復習五回目。
今回は、「docker-compose で疎結合システムを構築する」です。
複数のコンテナを順序を考慮して(構築して)起動するための機能として、
docker-composeがあります。今回はこのdocker-compose機能をスタディーです。
(参考)
「プログラマのためのDocker教科書第二版」・・p225
「Dockerによるアプリケーション開発環境構築ガイド」・・p117
まずは、docker-composeコマンドの導入から。
ケース1:Webサーバコンテナの単独起動の場合。
ケース2:2つの機能コンテナの起動と利用の場合。
ケース3:機能コンテナの起動と結合テストを実施する場合。
redmineのwebサーバとDBサーバなどはこの方法で運用できるのかもしれませんが、
今はbitnamiのパッケージを利用しているので直ぐに書き換えるのは難しいかな。
(参考)
1.
ソフトウェアデザイン-2019年4月号
2.
プログラマのためのDocker教科書第二版
3.
Dockerによるアプリケーション開発環境構築ガイド
今回は、「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によるアプリケーション開発環境構築ガイド