Docker/Kubernetes 実践コンテナ開発入門@19日目

Docker/Kubernetes 実践コンテナ開発入門:書籍案内|技術評論社

今日は Nginx の構築をできるところまでやっていきます。

4.4 Nginx の構築

  • クライアントから受け取った HTTP リクエストを、リバースプロキシを用いてバックエンド Web に転送

  • Web から API 側へのリクエストを転送するためにも使用

  • リバースプロキシって?

  • git clone する

$ git clone https://github.com/gihyodocker/todonginx
  • trre でディレクトリ構造確認
$ tree .\todonginx\ /f
フォルダー パスの一覧:  ボリューム Windows
ボリューム シリアル番号は 6607-7B7E です
C:\USERS\SNYT45\DESKTOP\DOCKER_KUBERNETES 実践コンテナ開発入門\4-2\TODONGINX
│  Dockerfile
│
└─etc
    └─nginx
        │  nginx.conf.tmpl
        │
        └─conf.d
                log.conf
                public.conf.tmpl
                upstream.conf.tmpl
  • フロントエンドと API のそれぞれの前に置く Services が存在する。同じ Dockerfile から作成。
  • 今回は、API の前におく Nginx の Service を作成。

4.4.1 nginx.conf を構築する

  • ベースイメージとして nginx:1.13 を使用。

  • /etc/nginx/nginx.conf

    • Nginx の設定の起点となるファイル
  • Nginx の性能をチューニングする上で注目すべき項目

    • worker_processes
      • Nginx の worker プロセス数
    • worker_connections
      • worker プロセスが開ける最大コネクション数
    • keepalive_timeout
      • クライアントからの接続を維持する秒数
    • gzip
      • レスポンスの GZIP 圧縮を有効にするか
  • 性能に関わる値は固定値を定義ではなく、環境変数化したい

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;
  #tcp_nopush on;

  keepalive_timeout 65; # ← ③

  #gzip on; # ← ④

  include /etc/nginx/conf.d/*.conf;
}
  • 環境変数で制御できるように entrykit のテンプレート機能(tmpl ファイル)を使う

  • entrykit を使うと、コンテナ実行のタイミングでテンプレートと環境変数に設定された値からファイルを生成(render)できる

  • 次のように環境変数を埋め込む

{{ var "環境変数名" }}
{{ var "環境変数名" | default "デフォルト値" }}
  • Vue.js の”Mustache” 構文(二重中括弧)に似ている!

  • etc/nginx/nginx.conf.tmpl

    • nginx.conf を生成するためのテンプレートファイル
    • ⑤ の include によって/etc/nginx/conf.d/*.conf に合致するファイルをロードできる → 設定ファイルの追加は/etc/nginx/conf.d にファイルを追加していく
user  nginx;
worker_processes  {{ var "WORKER_PROCESSES" | default "1" }}; # ← ①

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

events {
    worker_connections  {{ var "WORKER_CONNECTIONS" | default "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;
    #tcp_nopush     on;

    keepalive_timeout  {{ var "KEEPALIVE_TIMEOUT" | default "65" }}; # ← ③

    gzip  {{ var "GZIP" | default "on" }}; # ← ④

    include /etc/nginx/conf.d/*.conf; # ← ⑤
}
  • ログ - etc/nginx/conf.d/log.conf

    • ログフォーマットの定義
  • バックエンドサーバの振り分け - etc/nginx/conf.d/upstream.conf.tmpl

    • プロキシ先は Nginx の proxy_pass ディレクティブに直接記述することも可能
    • upstream ディレクトリを定義するメリット
      • プロキシ先のサーバを複数指定してロードバランス
      • 障害時に Sorry サーバへプロキシできる設定をできる
  • ルーティング - etc/nginx/conf.d/public.conf.tmpl

    • HTTP リクエストのルーティングの設定
    • SERVER_PORT
      • Nginx を Listen するポート。デフォルト値として 80 を設定
      • location ディレクティブはバックエンドへのプロキシの設定
        • パスとして/を定義。全てバックエンドにプロキシする
        • proxy_pass は upstream ディレクティブで定義したので、http://backend とする
        • LOG_STDOUT の環境変数を利用し、値があれば標準出力に出力し、値がなければファイルに出力

コラム 環境変数を積極的に使う

  • Docker で挙動を変えたい部分は環境変数化し、デフォルト値を設定する癖をつけておこう

4.4.2 Nginx の Dockerle

FROM nginx:1.13

RUN apt-get update
RUN apt-get install -y wget
RUN wget https://github.com/progrium/entrykit/releases/download/v0.4.0/entrykit_0.4.0_linux_x86_64.tgz
RUN tar -xvzf entrykit_0.4.0_linux_x86_64.tgz
RUN rm entrykit_0.4.0_linux_x86_64.tgz
RUN mv entrykit /usr/local/bin/
RUN entrykit --symlink

# ① conf.dのデフォルトファイルを削除し、各種設定ファイルをコピー
RUN rm /etc/nginx/conf.d/*
COPY etc/nginx/nginx.conf.tmpl /etc/nginx/
COPY etc/nginx/conf.d/ /etc/nginx/conf.d/

# ② entrykitでテンプレートから設定ファイルを生成
ENTRYPOINT [ \
  "render", \
      "/etc/nginx/nginx.conf", \
      "--", \
  "render", \
      "/etc/nginx/conf.d/upstream.conf", \
      "--", \
  "render", \
      "/etc/nginx/conf.d/public.conf", \
      "--" \
]

CMD ["nginx", "-g", "daemon off;"]
  • entrykit の準備後は、① でコンテナの中が次のようなディレクトリ構造になる
└── etc
  └── nginx
    ├── conf.d
    │   ├── log.conf
    │   ├── public.conf.tmpl (render後はpublic.conf)
    │   └── upstream.conf.tmpl (render後はupstream.conf)
    └── nginx.conf.tmpl (render後はnginx.conf)
  • ② の ENTRYPOINT では、entrykit で render を実行したいファイルを列挙

  • render で指定するパスは.tmpl を付けずに完成後のパスとなるように指定

  • ch04/nginx:latest という名前でイメージを作成

$ docker image build -t ch04/nginx:latest .
[+] Building 29.0s (16/16) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                   0.1s
 => => transferring dockerfile: 713B                                                                                                                                   0.0s
 => [internal] load .dockerignore                                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                                        0.0s
 => [internal] load metadata for docker.io/library/nginx:1.13                                                                                                          3.3s
 => [1/11] FROM docker.io/library/nginx:1.13@sha256:b1d09e9718890e6ebbbd2bc319ef1611559e30ce1b6f56b2e3b479d9da51dc35                                                   8.8s
 => => resolve docker.io/library/nginx:1.13@sha256:b1d09e9718890e6ebbbd2bc319ef1611559e30ce1b6f56b2e3b479d9da51dc35                                                    0.0s
 => => sha256:b1d09e9718890e6ebbbd2bc319ef1611559e30ce1b6f56b2e3b479d9da51dc35 2.03kB / 2.03kB                                                                         0.0s
 => => sha256:e4f0474a75c510f40b37b6b7dc2516241ffa8bde5a442bde3d372c9519c84d90 948B / 948B                                                                             0.0s
 => => sha256:ae513a47849c895a155ddfb868d6ba247f60240ec8495482eca74c4a2c13a881 6.03kB / 6.03kB                                                                         0.0s
 => => sha256:f2aa67a397c49232112953088506d02074a1fe577f65dc2052f158a3e5da52e8 22.50MB / 22.50MB                                                                       7.2s
 => => sha256:3c091c23e29d0ddfc902b0be63b1a08a853ef39973f92fab39ad1727eac012bf 22.11MB / 22.11MB                                                                       7.2s
 => => sha256:4a99993b863683bef1c776732e14d2372f6ed52b48e94783f4a1b58af289db07 201B / 201B                                                                             0.8s
 => => extracting sha256:f2aa67a397c49232112953088506d02074a1fe577f65dc2052f158a3e5da52e8                                                                              0.7s
 => => extracting sha256:3c091c23e29d0ddfc902b0be63b1a08a853ef39973f92fab39ad1727eac012bf                                                                              0.3s
 => => extracting sha256:4a99993b863683bef1c776732e14d2372f6ed52b48e94783f4a1b58af289db07                                                                              0.0s
 => [internal] load build context                                                                                                                                      0.0s
 => => transferring context: 2.84kB                                                                                                                                    0.0s
 => [2/11] RUN apt-get update                                                                                                                                          5.0s
 => [3/11] RUN apt-get install -y wget                                                                                                                                 5.3s
 => [4/11] RUN wget https://github.com/progrium/entrykit/releases/download/v0.4.0/entrykit_0.4.0_linux_x86_64.tgz                                                      4.0s
 => [5/11] RUN tar -xvzf entrykit_0.4.0_linux_x86_64.tgz                                                                                                               0.4s
 => [6/11] RUN rm entrykit_0.4.0_linux_x86_64.tgz                                                                                                                      0.4s
 => [7/11] RUN mv entrykit /usr/local/bin/                                                                                                                             0.4s
 => [8/11] RUN entrykit --symlink                                                                                                                                      0.5s
 => [9/11] RUN rm /etc/nginx/conf.d/*                                                                                                                                  0.4s
 => [10/11] COPY etc/nginx/nginx.conf.tmpl /etc/nginx/                                                                                                                 0.0s
 => [11/11] COPY etc/nginx/conf.d/ /etc/nginx/conf.d/                                                                                                                  0.0s
 => exporting to image                                                                                                                                                 0.3s
 => => exporting layers                                                                                                                                                0.3s
 => => writing image sha256:e750c834a1dc3a8126f460d5f23cc89846eb2da07dfd862ca1407d33c2e703fb                                                                           0.0s
 => => naming to docker.io/ch04/nginx:latest
  • 先ほど作成したイメージを localhost:5000/ch04/nginx:latest というイメージとして作成
$ docker image tag ch04/nginx:latest localhost:5000/ch04/nginx:latest
  • registry に push
$ docker image push localhost:5000/ch04/nginx:latest
The push refers to repository [localhost:5000/ch04/nginx]
Get http://localhost:5000/v2/: dial tcp [::1]:5000: connect: connection refused

4.4.3 Nginx を通してアクセスできるようにする

  • todo-app.yml
    • 前章で構築した todo_app_api Service の前段に Nginx を配置
    • Nginx 側で entrykit を使ってコンテナ実行時の環境変数を用いて設定ファイルを生成するため、todo-app.yml で環境変数を設定するだけで済む
version: "3"
services:
  nginx:
    image: registry:5000/ch04/nginx:latest
    deploy:
      replicas: 2
      placement:
        constraints: [node.role != manager]
    depends_on:
      - api
    environment:
      WORKER_PROCESSES: 2
      WORKER_CONNECTIONS: 1024
      KEEPALIVE_TIMEOUT: 65
      GZIP: "on"
      BACKEND_HOST: todo_app_api:8080
      BACKEND_MAX_FAILS: 3
      BACKEND_FAIL_TIMEOUT: 10s
      SERVER_PORT: 80
      SERVER_NAME: todo_app_nginx
      LOG_STDOUT: "true"
    networks:
      - todoapp

  api:
    image: registry:5000/ch04/todoapi:latest
    deploy:
      replicas: 2
      placement:
        constraints: [node.role != manager]
    environment:
      TODO_BIND: ":8080"
      TODO_MASTER_URL: "gihyo:gihyo@tcp(todo_mysql_master:3306)/tododb?parseTime=true"
      TODO_SLAVE_URL: "gihyo:gihyo@tcp(todo_mysql_slave:3306)/tododb?parseTime=true"
    networks:
      - todoapp

networks:
  todoapp:
    external: true
  • todo_app の Stack を更新
$ docker container exec -it manager docker stack deploy -c /stack/todo-app.yml todo_app

と思いきや、PC を再起動したため最初からやり直す必要がありそう。。

  • registry、manager、worker コンテナ起動
$ docker-compose up
  • manager コンテナからサービス一覧確認。
    • docker swarm init からやり直す必要がありそうだ。。。
$ docker container exec -it manager docker service ls
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.
  • manager コンテナを Swarm の manager に設定
$ docker container exec -it manager docker swarm init
  • worker01~03 を Swarm クラスタに追加
$ docker container exec -it worker01 docker swarm join --token SWMTKN-1-1bt724fy34a1ghxypkxny8fhtz8jsp09z39nswag60bonxejyu-3nvmveic1vyx3vipzwmqesap3 172.18.0.3:2377
This node joined a swarm as a worker.

$ docker container exec -it worker02 docker swarm join --token SWMTKN-1-1bt724fy34a1ghxypkxny8fhtz8jsp09z39nswag60bonxejyu-3nvmveic1vyx3vipzwmqesap3 172.18.0.3:2377
This node joined a swarm as a worker.

$ docker container exec -it worker03 docker swarm join --token SWMTKN-1-1bt724fy34a1ghxypkxny8fhtz8jsp09z39nswag60bonxejyu-3nvmveic1vyx3vipzwmqesap3 172.18.0.3:2377
This node joined a swarm as a worker.
  • ノードに追加されているか確認
$ docker container exec -it manager docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
mc2re0d06joon80xef0qb2s4n     3a2de3f08dbe        Ready               Active                                  18.05.0-ce
bhhwcxzfqil5bmhjt53rh6ona     3cb697256825        Ready               Active                                  18.05.0-ce
w1nrjoj1qs03221ujay7c1a49     9372f731e1c1        Ready               Active                                  18.05.0-ce
1hb7chtd8iywklko5nt5m2ki6 *   e6eb10555c5c        Ready               Active              Leader              18.05.0-ce
  • サービス確認。まだ何もない。
$ docker container exec -it manager docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
  • ネットワーク確認。todoapp ネットワークがないことを確認。
$ docker container exec -it manager docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
6572733214ed        bridge              bridge              local
52a0ad425489        docker_gwbridge     bridge              local
25faa8a1e997        host                host                local
fjhzcrl5x3l1        ingress             overlay             swarm
2d7c7a1e8113        none                null                local
  • todoapp ネットワーク作成
$ docker container exec -it manager docker network create --driver=overlay --attachable todoapp
3j1wnc6fzh2niknr3e6ehusfg
  • MySQL スタックをデプロイ
$ docker container exec -it manager docker stack deploy -c stack/todo-mysql.yml todo_mysql
Creating service todo_mysql_slave
Creating service todo_mysql_master
  • MySQL コンテナに初期データ投入
$ docker container exec -it manager docker service ps todo_mysql_master --no-trunc --filter "desired-state=running" --format "docker container exec -it {{.Node}} docker container exec -it {{.Name}}.{{.ID}} bash"
docker container exec -it 9372f731e1c1 docker container exec -it todo_mysql_master.1.xbdp5ypk08ozt7ov4cna531ga bash

$ docker container exec -it 9372f731e1c1 docker container exec -it todo_mysql_master.1.xbdp5ypk08ozt7ov4cna531ga bash

$ docker container exec -it 9372f731e1c1 docker container exec -it todo_mysql_master.1.xbdp5ypk08ozt7ov4cna531ga init-data.sh

$ docker container exec -it 9372f731e1c1 docker container exec -it todo_mysql_master.1.xbdp5ypk08ozt7ov4cna531ga mysql -u gihyo -pgihyo tododb
mysql> SELECT * FROM todo LIMIT 1\G
*************************** 1. row ***************************
     id: 1
  title: MySQLのDockerイメージを作成する
content: MySQLのMaster、Slaveそれぞれで利用できるように、環境変数で役割を制御できるMySQLイメージを作成する
 status: DONE
created: 2020-11-01 17:50:03
updated: 2020-11-01 17:50:03
1 row in set (0.00 sec)

$ docker container exec -it manager docker service ps todo_mysql_slave --no-trunc --filter "desired-state=running" --format "docker container exec -it {{.Node}} docker container exec -it {{.Name}}.{{.ID}} bash"
docker container exec -it 3cb697256825 docker container exec -it todo_mysql_slave.1.4760xer3uihy2y4uu9x2odq75 bash
docker container exec -it 3a2de3f08dbe docker container exec -it todo_mysql_slave.2.khnlnjy7we87y9x84mw0q7jz4 bash

$ docker container exec -it 3cb697256825 docker container exec -it todo_mysql_slave.1.4760xer3uihy2y4uu9x2odq75 mysql -u gihyo -pgihyo tododb
mysql> SELECT * FROM todo LIMIT 1\G
*************************** 1. row ***************************
     id: 1
  title: MySQLのDockerイメージを作成する
content: MySQLのMaster、Slaveそれぞれで利用できるように、環境変数で役割を制御できるMySQLイメージを作成する
 status: DONE
created: 2020-11-01 17:50:03
updated: 2020-11-01 17:50:03
1 row in set (0.00 sec)
  • todo_app スタックをデプロイ
$ docker container exec -it manager docker stack deploy -c stack/todo-app.yml todo_app
Creating service todo_app_nginx
Creating service todo_app_api

$ docker container exec -it manager docker service logs -f todo_app_api
todo_app_api.1.s1aw04ptkdkt@9372f731e1c1    | 2020/11/01 17:54:50 Listen HTTP Server
todo_app_api.2.2ail031y5c0k@3a2de3f08dbe    | 2020/11/01 17:54:50 Listen HTTP Server
  • Nginx スタックをデプロイ(今回のメイン)
    • あれ、なんかだめっぽい。
$ docker container exec -it manager docker stack deploy -c stack/todo-app.yml todo_app
Updating service todo_app_api (id: kclcv92gfwdsjwjgt95xnubba)
Updating service todo_app_nginx (id: klx1x8racba49tdguglh0r2ur)
image registry:5000/ch04/nginx:latest could not be accessed on a registry to record
its digest. Each node will access registry:5000/ch04/nginx:latest independently,
possibly leading to different nodes running different
versions of the image.
  • todo_app_nginx がうまく起動していないなー
$ docker container exec -it manager docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                               PORTS
kclcv92gfwds        todo_app_api        replicated          2/2                 registry:5000/ch04/todoapi:latest
klx1x8racba4        todo_app_nginx      replicated          0/2                 registry:5000/ch04/nginx:latest
nlhixxweyeil        todo_mysql_master   replicated          1/1                 registry:5000/ch04/tododb:latest
cdt3g4mmmo6a        todo_mysql_slave    replicated          2/2                 registry:5000/ch04/tododb:latest
  • いったん削除。
$ docker container exec -it manager docker stack rm todo_app
Removing service todo_app_api
Removing service todo_app_nginx
  • そうか今回の章では前回の todo_app スタックを更新する形だから二重にデプロイしていたから変なエラーが出たのか。。これでよさそう。
$ docker container exec -it manager docker stack deploy -c stack/todo-app.yml todo_app
Creating service todo_app_api
Creating service todo_app_nginx
  • ただ、todo_app_nginx のレプリカが 0/2 なのは気になる・・・ダメそうだが、、とりあえず今日はここまで
$ docker container exec -it manager docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                               PORTS
nz210v1bx7ar        todo_app_api        replicated          2/2                 registry:5000/ch04/todoapi:latest
0s0ivsgitps7        todo_app_nginx      replicated          0/2                 registry:5000/ch04/nginx:latest
nlhixxweyeil        todo_mysql_master   replicated          1/1                 registry:5000/ch04/tododb:latest
cdt3g4mmmo6a        todo_mysql_slave    replicated          2/2                 registry:5000/ch04/tododb:latest

今日の学び

  • Swarm クラスタを構築中に PC を再起動するとやり直しが大変・・・副作用として、改めてコマンド再確認できた。
  • todo_app_nginx のレプリカは 0/2 なのは気になる・・・
  • entrykit を使えば nginx.tmpl から nginx.cong を生成できる。tmpl には環境変数を使える。
Hugo で構築されています。
テーマ StackJimmy によって設計されています。