0%

docker 系列 - (6) Docker compose

1. Compose 簡介

一個環境的建置可以透過compose 方式來設計與配置。

docker-compose-intro

2. 基本語法結構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: "3.8" #compose 版本

services: # 容器
servicename: # 服務名字,這個名字也是内部 bridge網路可以使用的 DNS name
image: # 鏡像的名字
command: # 可選,如果設置,則會覆蓋默認鏡像裡的 CMD命令
environment: # 可選,相當於 docker run裡的 --env
volumes: # 可選,相當於docker run裡的 -v
networks: # 可選,相當於 docker run裡的 --network
ports: # 可選,相當於 docker run裡的 -p
servicename2:

volumes: # 可選,相當於 docker volume create

networks: # 可選,相當於 docker network create

以 Python Flask + Redis练习:为例子,改造成一个docker-compose文件

1
2
3
4
5
6
7
8
9
docker image pull redis
docker image build -t flask-demo .

# create network
docker network create -d bridge demo-network

# create container
docker container run -d --name redis-server --network demo-network redis
docker container run -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 5000:5000 flask-demo

docker-compose.yml 文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3.8"

services:
flask-demo:
image: flask-demo:latest
environment:
- REDIS_HOST=redis-server
networks:
- demo-network
ports:
- 8080:5000

redis-server:
image: redis:latest
networks:
- demo-network

networks:
demo-network: #創建一個deom-network網路,不填寫參數預設建立bridge type網路

3. docker-compose命令行基本使用

先將docker-compose.yml檔案放到上次練習的資料夾底下,並使用之前建立好的flask-demo鏡像檔。

1
2
3
4
5
root@host04:/home/kite/Desktop/flask_redis_practice# tree
.
├── app.py
├── docker-compose.yml
└── Dockerfile

3.1. docker-compose up

我們開始學習的第一個指令:

1
docker-compose up

我們會看到container 名稱會以資料夾名稱 + 鏡像名稱 + 數字(flask_redis_practice_redis-server_1) ,這個是跟後續擴容有關的設計,當然我們也可以在compose 檔設定自己定義的名稱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose up
Creating flask_redis_practice_flask-demo_1 ... done
Creating flask_redis_practice_redis-server_1 ... done
Attaching to flask_redis_practice_redis-server_1, flask_redis_practice_flask-demo_1
redis-server_1 | 1:C 07 Mar 2022 02:41:20.371 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-server_1 | 1:C 07 Mar 2022 02:41:20.371 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
redis-server_1 | 1:C 07 Mar 2022 02:41:20.371 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis-server_1 | 1:M 07 Mar 2022 02:41:20.372 * monotonic clock: POSIX clock_gettime
redis-server_1 | 1:M 07 Mar 2022 02:41:20.372 * Running mode=standalone, port=6379.
redis-server_1 | 1:M 07 Mar 2022 02:41:20.372 # Server initialized
redis-server_1 | 1:M 07 Mar 2022 02:41:20.372 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis-server_1 | 1:M 07 Mar 2022 02:41:20.372 * Ready to accept connections
flask-demo_1 | * Serving Flask app 'app.py' (lazy loading)
flask-demo_1 | * Environment: production
flask-demo_1 | WARNING: This is a development server. Do not use it in a production deployment.
flask-demo_1 | Use a production WSGI server instead.
flask-demo_1 | * Debug mode: off
flask-demo_1 | * Running on all addresses.
flask-demo_1 | WARNING: This is a development server. Do not use it in a production deployment.
flask-demo_1 | * Running on http://172.19.0.2:5000/ (Press CTRL+C to quit)
flask-demo_1 | 172.19.0.1 - - [07/Mar/2022 02:41:39] "GET / HTTP/1.1" 200 -
flask-demo_1 | 172.19.0.1 - - [07/Mar/2022 02:41:39] "GET /favicon.ico HTTP/1.1" 404 -
flask-demo_1 | 172.19.0.1 - - [07/Mar/2022 02:41:40] "GET / HTTP/1.1" 200 -

3.2. docker-compose up -d

ctrl + c退出互動模式後,如果想讓環境運行在後台,可加參數 -d

1
docker-compose up -d

執行如下:

1
2
3
4
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose up -d
Starting flask_redis_practice_redis-server_1 ... done
Starting flask_redis_practice_flask-demo_1 ... done
kite@host04:~/Desktop/flask_redis_practice$

3.3. docker-compose logs

這個模式下,如果還想看相關log我們可以使用docker-compose logsdocker-compose logs -f持續動態查看。

3.4. docker-compose ps

透過docker-compose ps查看運行的內容:

1
docker-compose ps
1
2
3
4
5
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------------------------------
flask_redis_practice_flask-demo_1 flask run -h 0.0.0.0 Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
flask_redis_practice_redis-server_1 docker-entrypoint.sh redis ... Up 6379/tcp

注意:這個指定一定要在docker-compose檔案所在的資料夾執行才有效

我們稍微看一下相關指令

1
2
3
4
5
6
7
8
kite@host04:~/Desktop/flask_redis_practice$ docker-compose
Options:
-f, --file FILE Specify an alternate compose file
(default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name
(default: directory name)
...
...

如果compose檔名稱要指定的話,加-f參數即可,跟dockerfile的概念一樣。另外也有看到一個 project-name這個就是我們剛剛說的container名稱會以資料夾名稱的命名方式,如果不喜歡也可以使用-p的指令方式指定。

3.5. docker-compose stop

如果想要停止compose也是可以做到的,這個方式很聰明,可以一次性將整個環境的容器一起停止處理:

1
docker-compose stop

如果去看docker container 、docker network 都會有compose 建立出來的容器與bridge

刪除沒有被使用的資源可以使用以下指令

1
docker system prune -f

3.6. docker-compose -p

我們也可以自定義專案名稱

1
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose -p myproject up -d

使用指令查看

1
2
3
4
5
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose -p myproject ps
Name Command State Ports
------------------------------------------------------------------------------------------------------------
myproject_flask-demo_1 flask run -h 0.0.0.0 Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
myproject_redis-server_1 docker-entrypoint.sh redis ... Up 6379/tcp

通過stop指令再批次停止

1
2
3
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose -p myproject stop
Stopping myproject_redis-server_1 ... done
Stopping myproject_flask-demo_1 ... done

3.7. docker-compose-rm

通過rm指令刪除

1
2
3
4
5
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose -p myproject rm
Going to remove myproject_redis-server_1, myproject_flask-demo_1
Are you sure? [yN] y
Removing myproject_redis-server_1 ... done
Removing myproject_flask-demo_1 ... done

3.8. docker-compose build

試著使用build方式來玩,,建立一個資料夾名稱為flask_web,將app.pyDockerfile移至flask_web底下:

1
2
3
4
5
6
kite@host04:~/Desktop/flask_redis_practice$ tree
.
├── docker-compose.yml
└── flask_web
├── app.py
└── Dockerfile

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from redis import Redis
import os
import socket

app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)


@app.route('/')
def hello():
redis.incr('hits')
return f"Hello Container World! I have been seen {redis.get('hits').decode('utf-8')} times and my hostname is {socket.gethostname()}.\n"

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FROM python:3.9.5-slim

RUN pip install flask redis && \
groupadd -r flask && useradd -r -g flask flask && \
mkdir /src && \
chown -R flask:flask /src

USER flask

COPY app.py /src/app.py

WORKDIR /src

ENV FLASK_APP=app.py REDIS_HOST=redis

EXPOSE 5000

CMD ["flask", "run", "-h", "0.0.0.0"]

修改docker-compose.yml 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "3.8"

services:
flask-demo:
build: ./flask_web
#image: flask-demo:latest
environment:
- REDIS_HOST=redis-server
networks:
- demo-network
ports:
- 8080:5000

redis-server:
image: redis:latest
networks:
- demo-network

networks:
demo-network:

我們添加了build: ./flask_web,預設會抓這個資料夾底下的Dockerfile名稱進行build image

compose build 指令玩一下:

1
2
3
4
5
6
7
8
9
10
11
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose build
redis-server uses an image, skipping
Building flask-demo
Sending build context to Docker daemon 3.072kB
Step 1/8 : FROM python:3.9.5-slim
---> c71955050276
Step 2/8 : RUN pip install flask redis && groupadd -r flask && useradd -r -g flask flask && mkdir /src && chown -R ...
...
Successfully built 626b99e2547c
Successfully tagged flask_redis_practice_flask-demo:latest
kite@host04:~/Desktop/flask_redis_practice$ sudo docker images

看一下image 清單,我們會發現image名稱會以當時的資料夾名稱為前綴:

1
2
3
kite@host04:~/Desktop/flask_redis_practice$ sudo docker images 
REPOSITORY TAG IMAGE ID CREATED SIZE
flask_redis_practice_flask-demo latest 626b99e2547c 36 seconds ago 129MB

3.9. docker-compose build 文件格式 - add image name

也可以在docker-compose文件中指定image名稱:

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "3.8"

services:
flask-demo:
build: ./flask_web
image: flask-kite:latest
environment:
- REDIS_HOST=redis-server
networks:
- demo-network
ports:
- 8080:5000

redis-server:
image: redis:latest
networks:
- demo-network

networks:
demo-network:

結果如下:

1
2
3
kite@host04:~/Desktop/flask_redis_practice$ sudo docker images 
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-kite latest 33a0bf4d2428 11 seconds ago 129MB

3.10. docker-compose build 文件格式 - context、dockefile name

先將Dockerfile名稱改為Dockerfile.dev

1
2
3
4
5
6
kite@host04:~/Desktop/flask_redis_practice$ tree
.
├── docker-compose.yml
└── flask_web
├── app.py
└── Dockerfile.dev

在docker-compose文件中指定dockerfile檔案名稱:

docker-compose.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: "3.8"

services:
flask-demo:
build:
context: ./flask_web
dockerfile: Dockerfile.dev
image: flask-kite:latest
environment:
- REDIS_HOST=redis-server
networks:
- demo-network
ports:
- 8080:5000

redis-server:
image: redis:latest
networks:
- demo-network

networks:
demo-network:

3.11. docker-compose pull

也可以使用docker-compose pull進行image build:

1
2
3
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose pull
Pulling flask-demo ... done
Pulling redis-server ... done

3.12. docker-compose –help

查找相關指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose --help
Define and run multi-container applications with Docker.

Usage:
docker-compose [-f <arg>...] [--profile <name>...] [options] [--] [COMMAND] [ARGS...]
docker-compose -h|--help

Options:
-f, --file FILE Specify an alternate compose file
(default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name
(default: directory name)
--profile NAME Specify a profile to enable
-c, --context NAME Specify a context name
--verbose Show more output
--log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
--ansi (never|always|auto) Control when to print ANSI control characters
--no-ansi Do not print ANSI control characters (DEPRECATED)
-v, --version Print version and exit
-H, --host HOST Daemon socket to connect to

--tls Use TLS; implied by --tlsverify
--tlscacert CA_PATH Trust certs signed only by this CA
--tlscert CLIENT_CERT_PATH Path to TLS certificate file
--tlskey TLS_KEY_PATH Path to TLS key file
--tlsverify Use TLS and verify the remote
--skip-hostname-check Don't check the daemon's hostname against the
name specified in the client certificate
--project-directory PATH Specify an alternate working directory
(default: the path of the Compose file)
--compatibility If set, Compose will attempt to convert keys
in v3 files to their non-Swarm equivalent (DEPRECATED)
--env-file PATH Specify an alternate environment file

Commands:
build Build or rebuild services
config Validate and view the Compose file
create Create services
down Stop and remove resources
events Receive real time events from containers
exec Execute a command in a running container
help Get help on a command
images List images
kill Kill containers
logs View output from containers
pause Pause services
port Print the public port for a port binding
ps List containers
pull Pull service images
push Push service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
top Display the running processes
unpause Unpause services
up Create and start containers
version Show version information and quit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose build --help
Build or rebuild services.

Services are built once and then tagged as `project_service`,
e.g. `composetest_db`. If you change a service's `Dockerfile` or the
contents of its build directory, you can run `docker-compose build` to rebuild it.

Usage: build [options] [--build-arg key=val...] [--] [SERVICE...]

Options:
--build-arg key=val Set build-time variables for services.
--compress Compress the build context using gzip.
--force-rm Always remove intermediate containers.
-m, --memory MEM Set memory limit for the build container.
--no-cache Do not use cache when building the image.
--no-rm Do not remove intermediate containers after a successful build.
--parallel Build images in parallel.
--progress string Set type of progress output (auto, plain, tty).
--pull Always attempt to pull a newer version of the image.
-q, --quiet Don't print anything to STDOUT

3.13. docker compose build -d –build

試著修改app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from redis import Redis
import os
import socket

app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)


@app.route('/')
def hello():
redis.incr('hits')
return f"Hello Container World!I'm kite. I have been seen {redis.get('hits').decode('utf-8')} times and my hostname is {socket.gethostname()}.\n"

在運行的過程中:

1
2
3
4
5
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------------------------------
flask_redis_practice_flask-demo_1 flask run -h 0.0.0.0 Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
flask_redis_practice_redis-server_1 docker-entrypoint.sh redis ... Up 6379/tcp

使用--build指令

1
docker compose build -d --build

這個指令會發現檔案有發生改變,就會決定重新build,並且會直接重啟與運行容器,不用經過compose stop、compose rm、compose build、 compose up 過程,非常方便,結果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose up -d --build
Building flask-demo
Sending build context to Docker daemon 3.072kB
Step 1/8 : FROM python:3.9.5-slim
---> c71955050276
Step 2/8 : RUN pip install flask redis && groupadd -r flask && useradd -r -g flask flask && mkdir /src && chown -R flask:flask /src
---> Using cache
---> bbf517f842c7
Step 3/8 : USER flask
---> Using cache
---> 7b1d7b6407b8
Step 4/8 : COPY app.py /src/app.py
---> 47b63bef74ff
Step 5/8 : WORKDIR /src
---> Running in 1da9b0391baf
Removing intermediate container 1da9b0391baf
---> ccbca4bf8c09
Step 6/8 : ENV FLASK_APP=app.py REDIS_HOST=redis
---> Running in 74c8fbb7fd95
Removing intermediate container 74c8fbb7fd95
---> cc12a2e1af00
Step 7/8 : EXPOSE 5000
---> Running in 201a42960e97
Removing intermediate container 201a42960e97
---> 3731d50ee226
Step 8/8 : CMD ["flask", "run", "-h", "0.0.0.0"]
---> Running in 3f4fea9f7156
Removing intermediate container 3f4fea9f7156
---> df6ac55a1b2d
Successfully built df6ac55a1b2d
Successfully tagged flask-kite:latest
flask_redis_practice_redis-server_1 is up-to-date
Recreating flask_redis_practice_flask-demo_1 ... done
kite@host04:~/Desktop/flask_redis_practice$

3.14. docker compose -d

目前comopose狀態:

1
2
3
4
5
6
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose ps
[sudo] password for kite:
Name Command State Ports
-----------------------------------------------------------------------------------------------------------------------
flask_redis_practice_flask-demo_1 flask run -h 0.0.0.0 Up 0.0.0.0:8080->5000/tcp,:::8080->5000/tcp
flask_redis_practice_redis-server_1 docker-entrypoint.sh redis ... Up 6379/tcp

3.14.1. add image

我們添加一個busybox ,修改docker-compose檔

docker-compose.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
version: "3.8"

services:
flask-demo:
build:
context: ./flask_web
dockerfile: Dockerfile.dev
image: flask-kite:latest
environment:
- REDIS_HOST=redis-server
networks:
- demo-network
ports:
- 8080:5000

redis-server:
image: redis:latest
networks:
- demo-network
busybox:
image: busybox:latest
command: sh -c "while true;do sleep 3600; done"
networks:
- demo-network

networks:
demo-network:

接著,我們再試著下docker-compose up -d,我們會發現有出現create busybox的訊息:

1
2
3
4
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose up -d
flask_redis_practice_flask-demo_1 is up-to-date
flask_redis_practice_redis-server_1 is up-to-date
Creating flask_redis_practice_busybox_1 ... done

3.14.2. remove image

再試著將剛剛的busybox移除 ,修改docker-compose檔

docker-compose.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3.8"

services:
flask-demo:
build:
context: ./flask_web
dockerfile: Dockerfile.dev
image: flask-kite:latest
environment:
- REDIS_HOST=redis-server
networks:
- demo-network
ports:
- 8080:5000

redis-server:
image: redis:latest
networks:
- demo-network
networks:
demo-network:

再執行一次docker-compose up -d

1
2
3
4
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose up -d
WARNING: Found orphan containers (flask_redis_practice_busybox_1) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
flask_redis_practice_flask-demo_1 is up-to-date
flask_redis_practice_redis-server_1 is up-to-date

會出現警告:

WARNING: Found orphan containers (flask_redis_practice_busybox_1) for this project. If you removed or renamed this service in your compose file, you can run this command with the –remove-orphans flag to clean it up.

照著指令操作,移除busybox的指令需要--remove-orphans,執行如下:

1
2
3
4
5
kite@host04:~/Desktop/flask_redis_practice$ sudo docker-compose up -d --remove-orphans
Removing orphan container "flask_redis_practice_busybox_1"
flask_redis_practice_flask-demo_1 is up-to-date
flask_redis_practice_redis-server_1 is up-to-date
kite@host04:~/Desktop/flask_redis_practice$

3.14.3. container 配置文件修改

通常container 配置文件(使用volume 掛載本地文件)修改之後,要重啟container。而在compose 可以透過docker compose restart指令重啟。

這個指令只是重啟,並不是重新創建喔。

3.15. 更新操作小總結

我們常在專案會時常更新,比如更新鏡像、配置文件、增加service、刪除service,最常用的有二種

  1. 刪除未使用的service

    1
    docker-compose up -d --remove-orphans
  2. container 配置文件修改

    1
    docker-compose restart
  3. image 進行修改

    1
    docker-compose -d --build

4. docker-compose網路(上)

暫補

5. docker-compose網路(下)

暫補

各別指定network

network default

network subnet