docker

docker-composeとネットワーク、コンテナのホスト名の割り当てなどを理解する

Railsなどサーバーサイド開発でDockerを使っているなら、docker-composeを使っている人は多いだろう。docker-compose.ymlでRails環境とDB環境(これらをサービスと読ぶ)を定義し、docker-compose upで、2つのコンテナを起動するのはよくあることだ。

よくあるケースなのでググればすぐ出てくるし、Dockerのネットワーク構成についてあまり理解してなくても問題なかったりする。しかし、Railsから別コンテナのSFTPサーバーにファイルをアップロードしたいとかになると、ググってもあまり出てこず、Dockerのネットワークについて理解しておかないとハマるかもしれない。

docker-compose.ymlで宣言したサービスは、docker-compose upなどでコンテナ起動すると、同一のネットワークに所属することになる(Dockerネットワークと呼ぶ)。そして、それぞれホスト名が割り当てられる。そのホスト名はサービス名、もしくはdocker-compose.ymlで指定したコンテナ名となる。

このようなdocker-compose.ymlを定義があるとする。実際に動作検証はしていない。あくまで例。

docker-compose.yml
version: '2'
services:
  db:
    image: mysql:5.6
    container_name: my_db
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3316:3306'

  sftp:
    image: atmoz/sftp
    container_name: my_sftp
    volumes:
      - ./tmp/upload:/home/foo/upload
    ports:
      - '2222:22'
    command: foo:pass:1001

  rails:
    build:
      context: .
      dockerfile: Dockerfile-rails
    command: bundle exec rails server -p 3000 -b '0.0.0.0'
    container_name: my_rails
    environment:
      RAILS_ENV: development
    ports:
      - '3000:3000'
    links:
      - db
      - sftp

docker-compose upなどで3つのコンテナが起動される。

サービス名 コンテナ名
db my_db
sftp my_sftp
rails my_rails

このコンテナ名およびサービス名がホスト名となる。

Railsのconfig/database.ymlには以下のように書いていることだろう。これは同一ネットワーク内でdbというホスト名でアクセスできるからだ。

default: default
〜〜〜
  host: db
  port: 3306
〜〜〜

注目すべきはポート番号。ここでは3306を指定している。docker-composeのportsで'3316:3306'のように定義してあるが、前者の3316はホストマシンからアクセスする際のポートである。Dockerコンテナが所属するネットワーク内では3316ではアクセスできない。つまりRaisは後者の3306でDBへアクセスする。

あなたが実際に使っているホストマシンで、MySQLやSFTPのデフォルトのポートは使いたくないだろう。競合の可能性があるからだ。だから、portsの前者で指定するポートは、他と重複しないものを指定し、後者はDockerネットワーク内なのでデフォルトのポートを指定しても問題ないということになる。

Railsでsftpコンテナへアクセスする際には、以下のようにすればいい。

hostname = "sftp" # my_sftpでも良い
username = "foo"
option = {
  password: "pass"
  port: 22
}
Net::SFTP.start(hostname, username, option) do |sftp|
〜〜〜何らかの処理〜〜〜
end

ここでポートに22を指定しているのも先程と同様の理由である。sftpサービスはportsを2222:22で定義している。つまりホストマシンからは2222でログインでき、Dockerネットワーク内では22でログインできる。

Dockerネットワークを詳しく見る

以下のコマンドを打つと、Dockerネットワークの一覧を見ることが出来る。

$ docker network list

その中に、"{docker-compose.ymlが配置されているフォルダ名}+_default"という名前のネットワークがあるはずだ。(この名前はフォルダ名のハイフンやドットは省略されているかもしれない)

例えばそれがmyapp_defaultだったとしよう。次に以下のように打つ。

$ docker network inspect myapp_default

ずらずらと出力がなされるが、Containersの項目に割り当てらたIPアドレスなどを確認できるはずだ。

        "Containers": {
            "4238*******************************0": {
                "Name": "myapp_sftp",
                "EndpointID": "c11*******************************40",
                "MacAddress": "02:**:**:**:00:02",
                "IPv4Address": "172.22.0.2/16",
                "IPv6Address": ""
            },
            "75*******************************06": {
                "Name": "myapp_db",
                "EndpointID": "cf*******************************be",
                "MacAddress": "02:**:**:**:00:03",
                "IPv4Address": "172.22.0.3/16",
                "IPv6Address": ""
            },
            "dc*******************************2e": {
                "Name": "myapp_rails",
                "EndpointID": "41*******************************6e",
                "MacAddress": "02:**:**:**:00:04",
                "IPv4Address": "172.22.0.4/16",
                "IPv6Address": ""
            }
        },

もちろんこのIPアドレスは動的に割り当てられるのでプログラムから指定してはならない。固定であるホスト名で指定するべきである。

このようにDockerネットワークについて理解しておくと、コンテナからコンテナへ接続できないなどでハマった時に原因を特定できるかもしれない。