退役SEのつれづれ日記

定年退役SEが、つれづれなる想いをしたためています。
(旧名:『システムノヲニワソト』)

[docker][SoftwareDesign連載]応用がきくDockerイメージの作り方(series7)

2019-05-26 | Weblog
復習五回目。
今回は、「docker-compose で疎結合システムを構築する」です。

複数のコンテナを順序を考慮して(構築して)起動するための機能として、
docker-composeがあります。今回はこのdocker-compose機能をスタディーです。
(参考)
   「プログラマのためのDocker教科書第二版」・・p225
   「Dockerによるアプリケーション開発環境構築ガイド」・・p117
まずは、docker-composeコマンドの導入から。
[root@hogehoge hello]# docker-compose
bash: docker-compose: コマンドが見つかりません
[root@hogehoge hello]# curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(unam e -m)" -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100   617    0   617    0     0    658      0 --:--:-- --:--:-- --:--:--   658
  0 15.4M    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  3 15.4M    3  543k    0     0   196k      0  0:01:20  0:00:02  0:01:18  616k
 14 15.4M   14 2294k    0     0   609k      0  0:00:25  0:00:03  0:00:22 1221k
 26 15.4M   26 4214k    0     0   883k      0  0:00:17  0:00:04  0:00:13 1459k
 39 15.4M   39 6238k    0     0  1079k      0  0:00:14  0:00:05  0:00:09 1600k
 52 15.4M   52 8345k    0     0  1229k      0  0:00:12  0:00:06  0:00:06 1701k
 66 15.4M   66 10.2M    0     0  1340k      0  0:00:11  0:00:07  0:00:04 1969k
 79 15.4M   79 12.2M    0     0  1438k      0  0:00:10  0:00:08  0:00:02 2064k
 93 15.4M   93 14.3M    0     0  1507k      0  0:00:10  0:00:09  0:00:01 2106k
100 15.4M  100 15.4M    0     0  1538k      0  0:00:10  0:00:10 --:--:-- 2131k
[root@hogehoge hello]# chmod +x /usr/local/bin/docker-compose
[root@hogehoge hello]# docker-compose --vewrsion
docker-compose version 1.24.0, build 0aa59064

ケース1:Webサーバコンテナの単独起動の場合。
<<ファイル内容>>
----Dockerfile----
FROM python

COPY hello /
RUN chmod +x /hello
RUN pip install flask

CMD ["/hello"]

----hello----
A#!/usr/bin/env python

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!" 

if __name__ == '__main__':
    app.run(host='0.0.0.0')

----docker-compose.yml----
version: "3" 
services:
  web:
    build:
      context: web
    ports:
      - "80:5000" 
<<実行結果>>
[root@hogehoge hello]# docker-compose up
Building web
Step 1/5 : FROM python
 ---> a4cc999cf2aa
・・・
Step 5/5 : CMD ["/hello"]
 ---> Running in 6670e9d9ba6a
Removing intermediate container 6670e9d9ba6a
 ---> 66a7640fc252
Successfully built 66a7640fc252
Successfully tagged hello_web:latest
Recreating hello_web_1 ... 
Recreating hello_web_1 ... doneAttaching to hello_web_1
web_1  |  * Serving Flask app "hello" (lazy loading)
web_1  |  * Environment: production
web_1  |    WARNING: Do not use the development server in a production environment.
web_1  |    Use a production WSGI server instead.
web_1  |  * Debug mode: off
web_1  |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
^CGracefully stopping... (press Ctrl+C again to force)
Stopping hello_web_1   ... 

Stopping hello_web_1   ... done 

ケース2:2つの機能コンテナの起動と利用の場合。
<<ファイル内容>>
--- docker-compose.yml ---
version: "3" 
services:
  web:
    build:
      context: web
    ports:
      - "80:5000" 
    depends_on:
      - mysql
    environment:
      DOCKERTIPS7_MYSQL_HOSTNAME: mysql
      DOCKERTIPS7_MYSQL_PASSWD: password
    links:
      - mysql 

  mysql:
    image: mysql
    volumes:
      - "./mysql:/docker-entrypoint-initdb.d" 
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: web
--- Dockerfdile ---
FROM python

COPY hello /
RUN chmod +x /hello
RUN pip install flask PyMySQL cryptography

CMD ["/hello"]

--- hello ---
#!/usr/bin/env python

from flask import Flask
import pymysql.cursors
import os

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!" 

@app.route('/user/')
def user():
    connection = pymysql.connect(
        host=os.environ['DOCKERTIPS7_MYSQL_HOSTNAME'],
        user='root',
        password=os.environ['DOCKERTIPS7_MYSQL_PASSWD'],
        db='web',
        charset='utf8',
        cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = "SELECT name FROM user" 
            cursor.execute(sql)
            return ','.join([r['name'] for r in cursor.fetchall()])
    finally:
        connection.close()

if __name__ == '__main__':
    app.run(host='0.0.0.0')

ーーー user.sql ---
CREATE TABLE user (
    id INT(11) PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
INSERT user VALUES (1, 'Mr. Incredible');
<<実行結果>>
[root@hogehoge web]# docker-compose up --force-recreate --build
Creating network "db_default" with the default driver
Pulling mysql (mysql:)...
latest: Pulling from library/mysql
・・・
Status: Downloaded newer image for mysql:latest
Building web
Step 1/5 : FROM python
 ---> a4cc999cf2aa
・・・
Step 5/5 : CMD ["/hello"]
 ---> Running in b8f552a205da
Removing intermediate container b8f552a205da
 ---> 08ef9bba6572
Successfully built 08ef9bba6572
Successfully tagged db_web:latest
Creating db_mysql_1 ... 
Creating db_mysql_1 ... doneCreating db_web_1   ... 
Creating db_web_1   ... doneAttaching to db_mysql_1, db_web_1
mysql_1  | Initializing database
web_1    |  * Serving Flask app "hello" (lazy loading)
web_1    |  * Environment: production
web_1    |    WARNING: Do not use the development server in a production environment.
web_1    |    Use a production WSGI server instead.
web_1    |  * Debug mode: off
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
mysql_1  | 2019-05-12T11:45:08.368890Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release.
mysql_1  | 2019-05-12T11:45:08.379364Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.16) initializing of server in progress as process 29
mysql_1  | 2019-05-12T11:45:11.518787Z 5 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
mysql_1  | 2019-05-12T11:45:13.707803Z 0 [System] [MY-013170] [Server] /usr/sbin/mysqld (mysqld 8.0.16) initializing of server has completed
mysql_1  | Database initialized
mysql_1  | MySQL init process in progress...
mysql_1  | MySQL init process in progress...
mysql_1  | MySQL init process in progress...
mysql_1  | 2019-05-12T11:45:16.077439Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release.
・・・
mysql_1  | 2019-05-12T11:45:27.085831Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: '/var/run/mysqld/mysqlx.sock' bind-address: '::' port: 33060
web_1    | 172.19.0.1 - - [12/May/2019 11:46:29] "GET /user/ HTTP/1.1" 200 -
^CGracefully stopping... (press Ctrl+C again to force) ・・・・・[①]
Killing db_web_1    ... 
Killing db_mysql_1  ... 
Killing db_mysql_1  ... done
Killing db_web_1    ... done
<<別コンソールで参照>>
[root@hogehoge rluser]# curl http://localhost/user/
Mr. Incredible

ケース3:機能コンテナの起動と結合テストを実施する場合。
<<ファイル内容>>
--- docker-compose.yml ---
version: "3" 
services:
  web:
    build: web
    depends_on:
      - mysql
    environment:
      DOCKERTIPS7_MYSQL_HOSTNAME: mysql
      DOCKERTIPS7_MYSQL_PASSWD: password
    links:
      - mysql 

  mysql:
    image: mysql
    volumes:
      - "./mysql:/docker-entrypoint-initdb.d" 
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: web

  test:
    build: test
    depends_on:
      - web
    environment:
      DOCKERTIPS7_WEB_HOSTNAME: web
    links:
      - web
--- web/Docerfile ---
FROM python

COPY hello /
RUN chmod +x /hello
RUN pip install flask PyMySQL cryptography

CMD ["/hello"]
--- web/hello ---
#!/usr/bin/env python

from flask import Flask
import pymysql.cursors
import os

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!" 

@app.route('/user/')
def user():
    connection = pymysql.connect(
        host=os.environ['DOCKERTIPS7_MYSQL_HOSTNAME'],
        user='root',
        password=os.environ['DOCKERTIPS7_MYSQL_PASSWD'],
        db='web',
        charset='utf8',
        cursorclass=pymysql.cursors.DictCursor)
    try:
        with connection.cursor() as cursor:
            sql = "SELECT name FROM user" 
            cursor.execute(sql)
            return ','.join([r['name'] for r in cursor.fetchall()])
    finally:
        connection.close()

if __name__ == '__main__':
    app.run(host='0.0.0.0')
--- mysql/user.sql ---
CREATE TABLE user (
    id INT(11) PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
INSERT user VALUES (1, 'Mr. Incredible');

--- test/Dockerfile ---
FROM ubuntu

RUN \
  sed -ie 's!deb http://archive.ubuntu.com/ubuntu/ bionic!deb mirror://mirrors.ubuntu.com/mirrors.txt bionic!' /etc/apt/sources.list && \
  apt-get update && apt-get install -y wget gnupg && \
  sh -c "echo 'deb http://download.opensuse.org/repositories/home:/cabelo/xUbuntu_18.04/ /' > /etc/apt/sources.list.d/home:cabelo.list" && \
  wget -nv https://download.opensuse.org/repositories/home:cabelo/xUbuntu_18.04/Release.key -O Release.key && \
  apt-key add - < Release.key && \
  apt-get update && \
  apt-get install -y owasp-zap xvfb curl iputils-ping python-pip && \
  pip install --upgrade zapcli

COPY ./tests /tests
RUN chmod +x /tests/*

CMD ["run-parts", "--verbose", "--exit-on-error", "/tests"]

--- test/tests/0hello ---
#!/bin/sh -ex

ping -c 1 $DOCKERTIPS7_WEB_HOSTNAME

for i in `seq 1 10`; do
  curl -sf http://$DOCKERTIPS7_WEB_HOSTNAME:5000/ && exit 0
  sleep $i
done
exit 1

--- test/tests/10owasp-zap ---
#!/bin/sh -ex

ZAP_PATH=/usr/share/owasp-zap/ xvfb-run zap-cli -v quick-scan --self-contained -s xss,sqli --start-options '-config api.disablekey=true' http://$DOCKERTIPS7_WEB_HOSTNAME:5000/
<<実行結果>>
[root@hogehoge test]# docker c-compose up- --force -recreates  test
Creating network "test_default" with the default driver
Pulling mysql (mysql:)...
latest: Pulling from library/mysql

743f2d6c1f65: Pulling fs layer
3f0c413ee255: Pulling fs layer
aef1ef8f1aac: Pulling fs layer
f9ee573e34cb: Pulling fs layer
3f237e01f153: Pulling fs layer
f9da32e8682a: Pulling fs layer
4b8da52fb357: Pulling fs layer
3416ca8f6890: Pulling fs layer
786698c2d5de: Pulling fs layer
4ddf84d07bd1: Pulling fs layer
cd3aa23461b6: Pulling fs layer
9f287a2a95ad: Pulling fs layer3416c
・・・
121B/121B9f287a2a95ad: Pull completeDigest: sha256:711df5b93720801b3a727864aba18c2ae46c07f9fe33d5ce9c1f5cbc2c035101
Status: Downloaded newer image for mysql:latest
Building test
Step 1/5 : FROM ubuntu
 ---> 94e814e2efa8
・・・
Step 5/5 : CMD ["run-parts", "--verbose", "--exit-on-error", "/tests"]
 ---> Running in 5899132a75b1
Removing intermediate container 5899132a75b1
 ---> 7f395869d461
Successfully built 7f395869d461
Successfully tagged test_test:latest
WARNING: Image for service test was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating test_mysql_1 ... done
Creating test_web_1   ... done
Creating test_test_1  ... done
Attaching to test_test_1
test_1   | run-parts: executing /tests/0hello
test_1   | + ping -c 1 web
test_1   | PING web (172.25.0.3) 56(84) bytes of data.
test_1   | 64 bytes from test_web_1.test_default (172.25.0.3): icmp_seq=1 ttl=64 time=0.094 ms
test_1   |
test_1   | --- web ping statistics ---
test_1   | 1 packets transmitted, 1 received, 0% packet loss, time 0ms
test_1   | rtt min/avg/max/mdev = 0.094/0.094/0.094/0.000 ms
test_1   | + seq 1 10
test_1   | + curl -sf http://web:5000/
test_1   | Hello World!+ exit 0
test_1   | run-parts: executing /tests/10owasp-zap
test_1   | + ZAP_PATH=/usr/share/owasp-zap/ xvfb-run zap-cli -v quick-scan --self-contained -s xss,sqli --start-options -config api.disablekey=true http://web:5000/
test_1   | [INFO]            Starting ZAP daemon
test_1   | [DEBUG]           Starting ZAP process with command: /usr/share/owasp-zap/zap.sh -daemon -port 8090 -config api.disablekey=true.
test_1   | [DEBUG]           Logging to /usr/share/owasp-zap/zap.log
test_1   | [DEBUG]           ZAP started successfully.
test_1   | [INFO]            Running a quick scan for http://web:5000/
test_1   | [DEBUG]           Disabling all current scanners
test_1   | [DEBUG]           Enabling scanners with IDs 40012,40014,40016,40017,40018
test_1   | [DEBUG]           Scanning target http://web:5000/...
test_1   | [DEBUG]           Started scan with ID 0...
test_1   | [DEBUG]           Scan progress %: 0
test_1   | [DEBUG]           Scan #0 completed
test_1   | [INFO]            Issues found: 0
test_1   | [INFO]            Shutting down ZAP daemon
test_1   | [DEBUG]           Shutting down ZAP.
test_1   | [DEBUG]           ZAP shutdown successfully.
test_test_1 exited with code 0
[root@hogehoge test]#

[root@hogehoge rluser]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                 NAMES
3d6e6f00b598        test_test           "run-parts --verbose…"   3 minutes ago       Exited (0) 2 minutes ago                         test_test_1
d93851c312eb        test_web            "/hello"                 3 minutes ago       Up 3 minutes                                     test_web_1
ed5bb0337062        mysql               "docker-entrypoint.s…"   3 minutes ago       Up 3 minutes               3306/tcp, 33060/tcp   test_mysql_1

[root@hogehoge test]# ls
docker-compose.yml  mysql  test  web
[root@hogehoge test]# docker-compose down
Stopping test_web_1   ... done
Stopping test_mysql_1 ... done
Removing test_test_1  ... done
Removing test_web_1   ... done
Removing test_mysql_1 ... done
Removing network test_default

redmineのwebサーバとDBサーバなどはこの方法で運用できるのかもしれませんが、
今はbitnamiのパッケージを利用しているので直ぐに書き換えるのは難しいかな。

(参考)
1.
ソフトウェアデザイン-2019年4月号
2.
プログラマのためのDocker教科書第二版
3.
Dockerによるアプリケーション開発環境構築ガイド
コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« [thinkpad][t460s]少し環境を... | トップ | [docker]Docker meetup#31に... »