sts -250rrです。 今回は ラクス Advent Calendar 2018 に投稿した記事、「 開発環境を作るためにDockerを使った話 」の続きになります。 qiita.com はじめに Dockerネットワークを作成してコンテナ間通信 開発環境(Go)・PostgreSQLコンテナの起動 docker-compose.yml Dockerfile 1_createdb.sql main.go 今回のポイント Goのパッケージインポート PostgreSQLコンテナで初期DB構築を行う 実は・・・ まとめ はじめに 前回の記事 ではDockerで開発環境を作ってみました。 が、このままではあまりにもチープな構成に感じます。。。 今回は コンテナ間通信 を使って別コンテナに作成したDBのデータを取得できるような構成を作っていくことを目標とします。 目標とする形は以下のようなイメージです。 Dockerネットワークを作成してコンテナ間通信 コンテナ間通信を実現するために、Dockerネットワークを作成します。 Dockerネットワークはコンテナ名を指定することでアプリ用(開発環境用)のコンテナからDBコンテナに接続することができるようになります。 まずはDockerネットワークを作っていきます。 $ docker network create my-network cbe89848f313a1b7766780903f79da2ff3a83bbe962f930c1f3caf9136fbac6f $ docker network ls NETWORK ID NAME DRIVER SCOPE 39d283374d44 bridge bridge local a3d0faef3da4 host host local cbe89848f313 my-network bridge local 7b1a7347c6b9 none null local 以下のコマンドでDockerネットワークの詳細を確認することができます。 $ docker network inspect my-network [ { "Name": "my-network", "Id": "cbe89848f313a1b7766780903f79da2ff3a83bbe962f930c1f3caf9136fbac6f", "Created": "2018-12-09T02:41:41.8124636Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.21.0.0/16", "Gateway": "172.21.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ] 作成したてのDockerネットワークなので何も登録されておらず、Containers部分が空っぽです。 コンテナ起動時にこのDockerネットワークを指定することで、同一のネットワークにコンテナが作成されるため、コンテナ間通信ができるようになります。 ※細かい部分は詰められていません。 開発環境(Go)・ PostgreSQL コンテナの起動 コンテナを起動して、開発環境コンテナのGoプログラムから PostgreSQL コンテナのDBに接続出来るような環境構築を行なっていきます。 今回もdocker-composeを利用していくので ディレクト リ構成・ファイルを以下のように作成します。 <任意のディレクトリ> |-- init | |-- 1_createdb.sql |-- Dockerfile |-- docker-compose.yml |-- main.go 今回、 main.go から PostgreSQL コンテナのDBに接続するために各設定ファイルやDBの初期構築が必要になります。 各ファイルは以下のように作成します。 docker-compose.yml version: '3' services: postgres: image: postgres environment: POSTGRES_USER: postgres # user POSTGRES_PASSWORD: postgres # pass ports: - "5432:5432" volumes: - ./db:/docker-entrypoint-initdb.d networks: - my-network app: build: . ports: - "8080:8080" networks: - my-network volumes: test_db: external: false networks: my-network: external: true Dockerfile #ベースのDockerイメージをgolangで指定 FROM golang:latest EXPOSE 5000 #ワークディレクトリを設定する WORKDIR /go #ホストのディレクトリを/go配下にコピー ADD . /go #GOPATHの設定 ENV GOPATH $GOPATH:$HOME/go RUN go get github.com/jinzhu/gorm RUN go get github.com/lib/pq #main.goを実行 CMD ["go", "run", "main.go"] 1_createdb. sql create database testdb; main.go main.goはこちらを参考にさせていただきました。 実行するとDBに登録されているIDと名前を出力します。 https://qiita.com/rky_1011/items/9772d92b5fe8cb3b82b0 qiita.com package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/lib/pq" ) type User struct { ID int64 `gorm:"primary_key" json:"id"` Name string `json:"name"` } type Users []User func main() { db, err := gorm.Open("postgres", "host=172.24.0.3 user=postgres port=5432 password=postgres dbname=testdb sslmode=disable") if err != nil { panic(err) } defer db.Close() db.AutoMigrate(User{}) var user = User{Name: "testname"} db.NewRecord(user) db.Create(&user) db.Save(&user) var users = Users{} db.Find(&users) fmt.Println(users) } さて、ファイルが揃ったら、 docker-compose build 、 docker-compose up を実行すれば、 main.go の実行結果が得られます。 (略) postgres_1 | 2018-12-09 06:32:23.787 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" postgres_1 | 2018-12-09 06:32:23.829 UTC [58] LOG: database system was shut down at 2018-12-09 06:32:23 UTC postgres_1 | 2018-12-09 06:32:23.851 UTC [1] LOG: database system is ready to accept connections app_1 | [{1 testname}] app_app_1 exited with code 0 postgres_1 | や app_1 | といった形でコンテナごとの出力結果が得られています。 app_1 | [{1 testname}] と出力されているのでなんだかいけてそうな気がします。 これだけではピンとこないので PostgreSQL コンテナに入ってDBを確認してみると、 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f9a9ed9b3fc8 postgres "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:5432->5432/tcp app_postgres_1 $ docker exec -it app_postgres_1 bash root@f9a9ed9b3fc8:/# psql -U postgres testdb psql (11.1 (Debian 11.1-1.pgdg90+1)) Type "help" for help. testdb=# select * from users; id | name ----+---------- 1 | testname (1 rows) PostgreSQL コンテナの内容と開発環境コンテナのmain.go実行時の内容が一致するので正しく値を取得できているようです。 最後にDockerネットワークの状態を確認します。 docker network inspect my-network [ { "Name": "my-network", "Id": "e77a834d8aff49e316e7ab153666232eab35f7fe4350b18be19da1d608125c4b", "Created": "2018-12-09T06:25:02.1986525Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.24.0.0/16", "Gateway": "172.24.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "f9a9ed9b3fc8b67232edb76ec4202e0b66ec776dbd0665db7f9ee11341088571": { "Name": "app_postgres_1", "EndpointID": "6dfd276d55c4352668364de97a588ca5072bf5ee647b8b9dbe5cbdc04d8603f1", "MacAddress": "02:42:ac:18:00:03", "IPv4Address": "172.24.0.3/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} } ] Go側のコンテナは実行完了と同時に停止してしまうので、 Containers には表示されていませんが、Postgresコンテナが my-network 内に含まれていることがわかります。 色々複雑でしたが、コンテナ間通信ができる開発環境、無事完成です。 今回のポイント Goのパッケージインポート ローカルでGoを書く時にも必要なことですが、軽く詰まりました。 main.goの中で Github からパッケージをインポートしていますが、事前にGOPATHを設定したり、go getでGitリモー トリポジ トリをダウンロードしておかなければならなかったようです。 Dockerコンテナでもこれは同様なのでDockerfile上でENVやRUNで必要な事前処理を行っていますが、詰まったのはRUNでgo getを実行する部分です。 はじめはCMDで実行していましたが、main.go実行時にこうなります。 app_1 | main.go:6:4: cannot find package "github.com/jinzhu/gorm" in any of: app_1 | /usr/local/go/src/github.com/jinzhu/gorm (from $GOROOT) app_1 | /go/src/github.com/jinzhu/gorm (from $GOPATH) app_1 | /go/src/src/github.com/jinzhu/gorm app_1 | main.go:7:4: cannot find package "github.com/lib/pq" in any of: app_1 | /usr/local/go/src/github.com/lib/pq (from $GOROOT) app_1 | /go/src/github.com/lib/pq (from $GOPATH) app_1 | /go/src/src/github.com/lib/pq app_app_1 exited with code 1 パッケージ見つからないよ。という旨のエラーですね。 調べてみたらRUNとCMDでこんな違いがありました。 qiita.com 深くまで追えていませんがイメージ作成時に実行しておかないとmain.goの実行時にはDLが完了できないとかなんでしょうか? PostgreSQL コンテナで初期DB構築を行う PostgreSQL コンテナをただ作るのみではもちろんDBの作成はしていないので、接続に失敗します。かといって毎回コンテナを起動するたびに手動で作成するのはDockerの利点を潰してしまっていますよね。 そんなことをしなくて良いようにDockerhubの公式のイメージには便利機能がありました。 If you would like to do additional initialization in an image derived from this one, add one or more . sql , . sql .gz, or *.sh scripts under /docker-entrypoint-initdb.d (creating the directory if necessary). /docker-entrypoint-initdb.d という ディレクト リに .sql や .sh のファイルを配置しておけば起動時に実行してくれるみたいです。 ということで、 docker-compose.yml の volumes: で init 以下の 1_createdb.sql を docker-entrypoint-initdb.d に配置し、 createdb を実行していました。 実は・・・ 必要なファイルが揃えば、さも簡単に実行できるかのように書いていますが、1度目の実行は ほぼ必ず 失敗します。 というのも1度目の実行ではDockerネットワーク上の PostgreSQL コンテナに割り振られるIPがわからないためです。。。 main.goの中で接続先のDBのあるIPを指定しているので、その部分でコケます。 多分何かしらいい方法があるんだと思いますが、現時点では見つからず。。。 良い方法があれば教えていただきたいです。。。 まとめ 今回はDockerネットワークを使ってGoアプリとDBを接続してみました。 アプリのコンテナとDBを分けることができ、なんだかそれらしくなってきたような気がします。 Dockerやdocker-composeがよしなにやってくれる分、上手くいかなかった時にはどこに問題があるのかを見つけるのが大変です。 その分出来上がってしまえば作り直しや複製が簡単にできてしまうので、便利であることに変わりはないですね。 個人的には、「実は・・・」でお伝えした部分はなんとか解決したいものです。。。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com