sagantaf

メモレベルの技術記事を書くブログ。

docker-composeでコンテナの内部IPアドレスを固定化してWebサイトを作る

はじめに

docker-composeでDockerコンテナの内部IPアドレスを固定化し、nginx + uwsgi + flask を使ってWebサイトの環境を構築します。

サイトの中身自体は hello world するだけの超簡単な内容にしてあるので、正直nginxを挟む必要は無いですが、コンテナが内部IPアドレスで通信できることを確かめるためにnginxを使っています。



環境

  • Ubuntu 16.04
  • docker 18.09.7
  • docker-compose 1.23.2

Dockerとdocker-composeのインストール方法は下記をご参考ください。

sagantaf.hatenablog.com

ちなみにDocker for Mac (macOS Catalina)でも変わらず作れます!


全体構成

nginxとuwsgiのコンテナを2つ作ります。 また、app_netをいう名前で内部ネットワークを作成します。

app_netのサブネットは172.30.0.0/24の範囲とし、nginxコンテナに172.30.0.2IPアドレスを、uwsgiコンテナに172.30.0.3IPアドレスを割り当てます。 172.30.0.1となっているbridgeは自動的に作成されます。

f:id:sagantaf:20200211000601p:plain


nginxコンテナ

nginxコンテナを構築するために、nginxの設定ファイルとDockerfileを作成します。

まずは好きなディレクトリで下記を実行します。

mkdir nginxuwsgi
cd nginxuwsgi/

nginx.conf

nginxディレクトリを作成し、その中にnginx.confを作成します。

(nginxは素人なので余計なパラメタ設定もあるかもしれません。。。)

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen 80;
        charset utf-8;
        location / {
            include uwsgi_params;
            uwsgi_pass 172.30.0.3:8765;
        }
    }
}

重要なのは、最後のservserの部分です。

    listen 80;
    charset utf-8;
    location / {
        include uwsgi_params;
        uwsgi_pass 172.30.0.3:8765;
   }

nginxコンテナの80ポートに来たアクセスを、172.30.0.3:8765(uwsgiコンテナ)に受け渡す設定となっています。

Dockerfile

続いては、同じnginxディレクトリの中にDockerfileを作成しますが、中身はとてもシンプルです。

FROM nginx

CMD ["nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"]

nginxがあらかじめインストールされたベースイメージを使っています。

またCMDで、先ほど作成したnginx.confを指定してnginxサーバを起動しています。

/etc/nginx/を指定していますが、後ほどdocker-compose.ymlを作る時に、ホストのnginx.confをコンテナにボリュームマウントします。


ここまでで、下記のようになっているはずです。

nginxuswgi
└──  nginx
    ├── Dockerfile
    └── nginx.conf


uwsgiコンテナ

uwsgiコンテナを構築するために、flaskのソースコード(app.py)、uwsgiの設定ファイル(uwsgi.ini)、Dockerfileを作成します。

flask(app.py)

uwsgiディレクトリを作成し、hello worldを表示するためのソースコードをapp.pyとして作成します。

# -*- coding: utf-8 -*-
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello, World!"

if __name__ == "__main__":
    app.run()

uwsgi.ini

続いて同じuwsgiディレクトリの中にuwsgiの設定ファイルを作成します。

[uwsgi]
wsgi-file = /var/www/app.py
callable = app
master = true
processes = 1
socket = :8765
chmod-socket = 666
vacuum = true
die-on-term = true
py-autoreload = 1

wsgi-file = /var/www/app.py でwebサーバとして起動する元となるソースコードを指定しています。/var/wwwとしていますが、nginxの時と同様、docker-compose.ymlにてホストのapp.pyをボリュームマウントします。

socket = :8765でリクエストを受け付けるポート番号を指定しています。先ほどnginx.confで8765ポートを指定したので、同じポート番号にする必要があります。

Dockerfile

最後にDockerfileです。(uwsgiディレクトリの中で作成します。)

FROM python:3.6

RUN pip install uwsgi flask
RUN mkdir /var/www/
WORKDIR /var/www/

CMD ["uwsgi", "--ini", "/var/www/uwsgi.ini"]

ベースはpython3.6があらかじめインストールされているイメージにしています。

またCMDで、先ほど作成したuwsgi.iniを指定してuwsgiサーバを起動しています。


ここまでで、下記のようになっているはずです。

nginxuswgi
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
└── uwsgi
    ├── Dockerfile
    ├── app.py
    └── uwsgi.ini


docker-compose

最後にdocker-compose.ymlを作成し、コンテナを起動します。

serviceでコンテナの設定を記述し、networksで内部ネットワークの設定を記述しています。

docker-compose.yml

version: "3.5"

services:
  nginx:
    build: ./nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "10080:80"
    networks:
      app_net:
        ipv4_address: 172.30.0.2

  uwsgi:
    build:
      context: ./uwsgi
    volumes:
      - ./uwsgi:/var/www/
    networks:
      app_net:
        ipv4_address: 172.30.0.3

networks:
  app_net:
    name: app_net
    driver: bridge
    ipam:
     driver: default
     config:
       - subnet: 172.30.0.0/24

networksでは、内部ネットワークのドライバとしてbridgeを指定し、サブネットとして172.30.0.0/24を指定しています。これで、172.30.0.2 ~ 172.30.0.254をコンテナの内部ネットワークとして利用できます。

serviceではnginxとuwsgiのコンテナの設定を記述しています。build:でそれぞれDockerfileの場所を指定することで、コンテナビルドごとdocker-composeで実施してしまっています。(事前にコンテナビルドしてイメージを作成しdocker-compose.ymlで指定する形式ではない。)

また、volumes:でそれぞれの設定ファイルをボリュームマウントさせてコンテナに渡しています。

networks.app_net.ipv4_addressの部分ではコンテナそれぞれのIPアドレスを指定しています。

コンテナの起動とアクセス確認

最終的な構成は以下のようになっているはずです。

nginxuswgi
├── docker-compose.yml
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
└── uwsgi
    ├── Dockerfile
    ├── app.py
    └── uwsgi.ini

では、コンテナを起動してみます。docker-compose.ymlが配置されている場所で下記コマンドを実行します。-dはバックグラウンド起動のオプションです。

$ docker-compose up -d
Creating network "app_net" with driver "bridge"
Creating nginxuswgi_nginx_1 ... done
Creating nginxuswgi_uwsgi_1 ... done

errorにならずdoneが表示されればOKです。

ブラウザでhttp://<ホストマシンのIPアドレス>:10080にアクセスしてみてください。

画面にHello, World!と表示されれば、正常にサーバが稼働し疎通ができています。

もし、エラーが発生した場合は、docker logs nginxuswgi_nginx_1などのコマンドを実行しログから原因を探ってみてください。

ネットワーク構成を確認

コンテナが問題なく起動している状態で、ネットワークの構成を確認してみます。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
2a94d4756996        app_net             bridge              local
f5e41246ea45        bridge              bridge              local
4920c3cc5a3e        host                host                local
6278bf6b8cc0        none                null                local

app_netが作成されていることがわかります。docker network inspect app_netでより詳細な情報を確認できます。サブネットや所属しているコンテナの情報まで表示されます。(出力が多いので省略)

また、ifconfigでブリッジが1つ作成されていることも確認できます。

$ ifconfig
br-2a94d4756996 Link encap:イーサネット  ハードウェアアドレス XX:XX:XX:XX:XX:XX
          inetアドレス:172.30.0.1  ブロードキャスト:172.30.0.255  マスク:255.255.255.0
          inet6アドレス: fe80::42:a6ff:XXXX:XXXX/64 範囲:リンク
          UP BROADCAST RUNNING MULTICAST  MTU:1500  メトリック:1
          RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
          TXパケット:44 エラー:0 損失:0 オーバラン:0 キャリア:0
          衝突(Collisions):0 TXキュー長:0
          RXバイト:0 (0.0 B)  TXバイト:5372 (5.3 KB)

(省略)


最後に掃除

起動したコンテナや作成したネットワークは下記のコマンドで削除できます。

docker-compose down
Stopping nginxflask_uwsgi_1 ... done
Stopping nginxflask_nginx_1 ... done
Removing nginxflask_uwsgi_1 ... done
Removing nginxflask_nginx_1 ... done
Removing network app_net


おわりに

dockerのネットワークについての基本を知りたい方はこちらをどうぞ!

sagantaf.hatenablog.com