TL;DR
- Mosquittoを使ってMQTTサーバを立てた際にコンテナ間でlocalhostで接続できないかと調査
- docker-composeでnetwork_modeを「host」にすることでホスト端末と同じIPアドレスにすることで可能とわかった
- GO言語のpaho.mqtt.golangライブラリを使って接続テストを実施
環境
- Windows10
- Docker Desktop 2.2.0.3
- docker-composeはDocker Desktopに同梱
- visual studio code 1.42.1[拡張機能Remote - Containers使用]
- Mosquitto 1.6.9
完成したリポジトリ
ファイル
docker-compose.tml
version: '3'
services:
api:
build:
dockerfile: Dockerfile
context: ./containers/api
volumes:
- ./containers/api/src:/go/api
tty: true
network_mode: "host"
mqtt:
build:
dockerfile: Dockerfile
context: ./containers/mqtt
ports:
- "1883:1883"
volumes:
- mosquittodata:/mosquitto/data
- mosquittolog:/mosquitto/log
tty: true
network_mode: "host"
volumes:
mosquittodata:
driver: "local"
mosquittolog:
driver: "local"
- apiコンテナとmqttコンテナにnetwork_modeで「host」を設定することでホスト端末と同じIPアドレスを使用する
main.go
package main
import (
"crypto/tls"
"flag"
"fmt"
"os"
"strconv"
"time"
MQTT "github.com/eclipse/paho.mqtt.golang"
)
// Que Strut.
type Que struct {
Server string
Sendtopic string
Resvtopic string
Qos int
Retained bool
Clientid string
Username string
Password string
Client MQTT.Client
Callback MQTT.MessageHandler
}
// Connect func .
func (q *Que) Connect() error {
connOpts := MQTT.NewClientOptions().AddBroker(q.Server).SetClientID(q.Clientid).SetCleanSession(true)
if q.Username != "" {
connOpts.SetUsername(q.Username)
if q.Password != "" {
connOpts.SetPassword(q.Password)
}
}
tlsConfig := &tls.Config{InsecureSkipVerify: true, ClientAuth: tls.NoClientCert}
connOpts.SetTLSConfig(tlsConfig)
client := MQTT.NewClient(connOpts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
return token.Error()
}
q.Client = client
if q.Callback != nil {
if token := q.Client.Subscribe(q.Resvtopic, byte(q.Qos), q.Callback); token.Wait() && token.Error() != nil {
return token.Error()
}
fmt.Printf("[MQTT] Subscribe to %s\n", q.Sendtopic)
}
fmt.Printf("[MQTT] Connected to %s\n", q.Server)
return nil
}
// Publish func .
func (q *Que) Publish(message string) error {
if q.Client != nil {
token := q.Client.Publish(q.Sendtopic, byte(q.Qos), q.Retained, message)
if token == nil {
return token.Error()
}
fmt.Printf("[MQTT] Sent to %s\n", q.Sendtopic)
}
return nil
}
// SetSubscribe - .
func (q *Que) SetSubscribe(callback MQTT.MessageHandler) error {
if callback != nil {
if token := q.Client.Subscribe(q.Resvtopic, byte(q.Qos), callback); token.Wait() && token.Error() != nil {
return token.Error()
}
fmt.Printf("[MQTT] Subscribe to %s\n", q.Sendtopic)
}
return nil
}
func onMessageReceived(client MQTT.Client, message MQTT.Message) {
fmt.Printf("[MQTT] Received to %s [Received Message: %s]\n", message.Topic(), message.Payload())
}
func main() {
hostname, _ := os.Hostname()
server := flag.String("server", "tcp://localhost:1883", "The full URL of the MQTT server to connect to")
sendtopic := flag.String("sendtopic", "MQTT/Client/Update/TEST", "Topic to publish the messages on")
resvtopic := flag.String("resvtopic", "MQTT/+/Update/#", "Topic to publish the messages on")
qos := flag.Int("qos", 0, "The QoS to send the messages at")
retained := flag.Bool("retained", false, "Are the messages sent with the retained flag")
clientid := flag.String("clientid", hostname+strconv.Itoa(time.Now().Second()), "A clientid for the connection")
username := flag.String("username", "", "A username to authenticate to the MQTT server")
password := flag.String("password", "", "Password to match username")
flag.Parse()
q := &Que{
Server: *server,
Sendtopic: *sendtopic,
Resvtopic: *resvtopic,
Qos: *qos,
Retained: *retained,
Clientid: *clientid,
Username: *username,
Password: *password,
Callback: onMessageReceived,
}
err := q.Connect()
if err != nil {
fmt.Println(err)
os.Exit(2)
}
if err := q.SetSubscribe(onMessageReceived); err != nil {
fmt.Println(err)
os.Exit(2)
}
for {
time.Sleep(5000 * time.Millisecond)
if err := q.Publish("test massage"); err != nil {
fmt.Println(err)
os.Exit(2)
}
}
}
- MQTTサーバの接続先に「tcp://localhost:1883」を指定しているが、お互いのコンテナがnetwork_modeで「host」を設定しているので接続できる
MQTT接続実行結果
実行結果
root@docker-desktop:/go/api# go run main.go
go: downloading github.com/eclipse/paho.mqtt.golang v1.2.0
go: downloading golang.org/x/net v0.0.0-20200320220750-118fecf932d8
[MQTT] Subscribe to MQTT/Client/Update/TEST
[MQTT] Connected to tcp://localhost:1883
[MQTT] Subscribe to MQTT/Client/Update/TEST
[MQTT] Sent to MQTT/Client/Update/TEST
[MQTT] Received to MQTT/Client/Update/TEST [Received Message: test massage]
[MQTT] Sent to MQTT/Client/Update/TEST
[MQTT] Received to MQTT/Client/Update/TEST [Received Message: test massage]
[MQTT] Sent to MQTT/Client/Update/TEST
[MQTT] Received to MQTT/Client/Update/TEST [Received Message: test massage]
まとめ
MQTTサーバをDockerで構築する際にサーバをlocalhostにできないかな、というので調査していてnetwork_mode使えばできることがわかったので記事にしてみました。