背景
通過前幾章節,我們知道:
- docker build 可以創建一個自定義鏡像;
- docker run 可以啟動一個容器;
而實際項目中,特別是微服務化之后,運維需要面對的不單單是一個鏡像一個容器,而是幾十乃至上千。如果通過手工敲命令去創建一個個容器,不科學也太慢。如果遇到機器更新換代或者重啟,又得重新敲一遍,這樣下去我想遲早腦袋頭發都要掉光。
還好docker提前想到了這點,為我們准備了相應的工具:
- 容器編排:docker compose
- 集群編排:swarm mode
還有一個很時尚的工具 Kubernetes,它是Google根據自身十幾年經驗打造的。
容器編排:docker compose
Compose 是由Python 編寫,定義和運行多個 Docker 容器的工具。通過一個 docker-compose.yml 模板文件(YAML 格式)來定義應用服務,簡單的命令批量創建和啟動定義的所有服務。
重要概念:
- 服務 (service):一個應用容器,實際上可以運行多個相同鏡像的實例。
- 項目 (project):由一組關聯的應用容器組成的一個完整業務單元。
一個項目可以由多個服務(容器)關聯而成,Compose 面向項目進行管理。
特性:
-
使用不同的項目名稱可以在一個主機構建不同組應用環境。比如開發主機上的不同項目。
默認項目名稱為yml文件所在目錄名稱,通過使用-p projectname 可以自定義項目名稱。創建的鏡像和容器名稱前綴會帶上項目名稱。 -
保存已經創建容器里頭掛載的數據。運行 docker-compose up 的時候,如果監測到有運行的容器,它會自動把就容器掛載的數據復制到新的容器。
-
Compose會緩存創建容器的配置,重啟service時只會重建有更改的容器。
-
yml模板文件支持可變參數,使用參數可以創建不同的應用。
db: image: "postgres:${POSTGRES_VERSION}"
YML模板文件常用配置:
-
version:compose文件格式版本號,最新版為3。
Compose file format Docker Engine release 3.8 19.03.0+ 3.7 18.06.0+ 3.6 18.02.0+ 3.5 17.12.0+ 3.4 17.09.0+ 3.3 17.06.0+ 3.2 17.04.0+ 3.1 1.13.1+ 3.0 1.13.0+ 2.4 17.12.0+ 2.3 17.06.0+ 2.2 1.13.0+ 2.1 1.12.0+ 2.0 1.10.0+ 1.0 1.9.1.+ -
services:服務父節點。
-
通過鏡像創建容器的docker-compose.yml文件
version: "3" services: #容器唯一標識 webapp: #鏡像名稱 image: examples/web ports: - "80:80" volumes: - "/data" -
通過DockerFile編譯創建容器的docker-compose.yml文件
version: '3' services: #容器唯一標識 webapp: build: #DockerFile文件地址 context: ./dir #DockerFile文件名稱 dockerfile: Dockerfile-alternate
安裝:
- Mac/Windows安裝:Docker Desktop for Mac/Windows 自帶 docker-compose 二進制文件,安裝 Docker 之后可以直接使用。
- Linux安裝:
$ sudo curl -L https://github.com/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose $ sudo chmod +x /usr/local/bin/docker-compose
常見命令:
- up: 創建並啟動配置的所有容器
- down: 停止並刪除容器、鏡像、網絡、存儲盤
- ps:列出服務(service)所有的容器
D:\docker\03-compose>docker-compose ps Name Command State Ports --------------------------------------------------------------------- 03-compose_nbaspnetcore_1 dotnet aspnetcoreapp.dll Exit 0 03-compose_nbginx_1 /bin/sh -c /bin/bash Exit 0 - images:列出服務(service)所有的鏡像
D:\docker\03-compose>docker-compose images Container Repository Tag Image Id Size -------------------------------------------------------------------------------------- 03-compose_nbaspnetcore_1 03-compose_nbaspnetcore latest 73ae1ad12839 211.9 MB 03-compose_nbginx_1 03-compose_nbginx latest 61ed0bb949f6 403.8 MB - rm:刪除停止的容器
D:\docker\03-compose>docker-compose ps Name Command State Ports ----------------------------------------------------------------------------------------------------------- 03-compose_nbaspnetcore_1 dotnet aspnetcoreapp.dll Up 0.0.0.0:6067->443/tcp, 0.0.0.0:6066->80/tcp 03-compose_nbginx_1 /bin/sh -c /bin/bash Exit 0 D:\docker\03-compose>docker-compose rm Going to remove 03-compose_nbginx_1 Are you sure? [yN] y Removing 03-compose_nbginx_1 ... done D:\docker\03-compose>docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------------------------------- 03-compose_nbaspnetcore_1 dotnet aspnetcoreapp.dll Up 0.0.0.0:6067->443/tcp, 0.0.0.0:6066->80/tcp - build:編譯或者再編譯服務
- create: 創建服務
- start:開啟服務
- stop:停止服務
- restart:重啟服務
實例
下面用 Python 來建立一個能夠記錄頁面訪問次數的 web 網站。

-
新建composetest文件夾,在該目錄中編寫 app.py 文件。
import time import redis from flask import Flask app = Flask(__name__) cache = redis.Redis(host='redis', port=6379) def get_hit_count(): retries = 5 while True: try: return cache.incr('hits') except redis.exceptions.ConnectionError as exc: if retries == 0: raise exc retries -= 1 time.sleep(0.5) @app.route('/') def hello(): count = get_hit_count() return 'Hello World! 該頁面已被訪問 {} 次。\n'.format(count) -
在文件夾中,添加 Dockerfile 文件
FROM python:3.7-alpine WORKDIR /code ENV FLASK_APP app.py ENV FLASK_RUN_HOST 0.0.0.0 RUN apk add --no-cache gcc musl-dev linux-headers RUN pip install redis flask COPY . . CMD ["flask", "run"]- 通過Python 3.7 基礎鏡像創建.
- 設置工作目錄為 /code.
- 設置FLask命令的環境變量.
- 安裝gcc 加速編譯MarkupSafe與SQLAlchemy.
- 安裝python依賴的包.
- 復制當前目錄內容到鏡像目錄.
- 設置容器默認啟動命令flask run.
-
在文件夾外部創建 docker-compose.yml 文件
version: '3' services: webtest: build: ./composetest ports: - 5000:5000 redis: image: redis:alpine- 創建一個web容器,開放5000端口.
- 從Docker Hub拉取一個Redis鏡像創建容器.
-
運行Compose項目。(項目名稱為plana)
D:\docker\03-compose>docker-compose -p plana up Creating network "plana_default" with the default driver Building webtest Step 1/8 : FROM python:3.7-alpine ---> 16f919b9ecd5 Step 2/8 : WORKDIR /code ---> Using cache ---> 3cd944ab4513 Step 3/8 : ENV FLASK_APP app.py ---> Using cache ---> 2aa9917f4c9f Step 4/8 : ENV FLASK_RUN_HOST 0.0.0.0 ---> Using cache ---> db1028a91223 Step 5/8 : RUN apk add --no-cache gcc musl-dev linux-headers ---> Using cache ---> 13a0ed97a7dd Step 6/8 : RUN pip install redis flask ---> Using cache ---> 1807263196e2 Step 7/8 : COPY . . ---> Using cache ---> 553b6c68eace Step 8/8 : CMD ["flask", "run"] ---> Using cache ---> ad92e1924ad5 Successfully built ad92e1924ad5 Successfully tagged plana_webtest:latest WARNING: Image for service webtest was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating plana_redis_1 ... done Creating plana_webtest_1 ... done Attaching to plana_webtest_1, plana_redis_1 redis_1 | 1:C 29 Apr 2020 12:59:36.795 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo redis_1 | 1:C 29 Apr 2020 12:59:36.795 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=1, just started redis_1 | 1:C 29 Apr 2020 12:59:36.795 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf redis_1 | 1:M 29 Apr 2020 12:59:36.800 * Running mode=standalone, port=6379. redis_1 | 1:M 29 Apr 2020 12:59:36.800 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. redis_1 | 1:M 29 Apr 2020 12:59:36.800 # Server initialized redis_1 | 1:M 29 Apr 2020 12:59:36.801 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. redis_1 | 1:M 29 Apr 2020 12:59:36.802 * Ready to accept connections webtest_1 | * Serving Flask app "app.py" webtest_1 | * Environment: production webtest_1 | WARNING: This is a development server. Do not use it in a production deployment. webtest_1 | Use a production WSGI server instead. webtest_1 | * Debug mode: off webtest_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) -
執行結果。(需要指定項目名稱)
D:\docker\03-compose>docker-compose ps Name Command State Ports ------------------------------ D:\docker\03-compose>docker-compose images Container Repository Tag Image Id Size ---------------------------------------------- D:\docker\03-compose>docker-compose -p plana images Container Repository Tag Image Id Size ------------------------------------------------------------------ plana_redis_1 redis alpine 3661c84ee9d0 29.8 MB plana_webtest_1 plana_webtest latest ad92e1924ad5 219.7 MB D:\docker\03-compose>docker-compose -p plana ps Name Command State Ports --------------------------------------------------------------------------------- plana_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp plana_webtest_1 flask run Up 0.0.0.0:5000->5000/tcp
集群編排:swarm mode
Swarm 是使用 SwarmKit 構建的 Docker 引擎內置(原生)的集群管理和編排工具。
Docker 1.12開始 Swarm mode 已經內嵌入 Docker 引擎,成為了 docker 子命令 docker swarm。
Swarm mode 內置 kv 存儲功能,提供了眾多的新特性,使得 Docker 原生的 Swarm 集群具備與 Mesos、Kubernetes 競爭的實力。比如:
- 具有容錯能力的去中心化設計
- 內置服務發現
- 負載均衡
- 路由網格
- 動態伸縮
- 滾動更新
- 安全傳輸等
常見概念
-
節點
節點為作用在Swarm集群中的一個實例化Docker引擎。雖然可以在一台物理主機上部署多個節點,但是生產環境中都是跨物理主機進行。
節點可以加入已經存在的swarm集群或者將自己初始化為一個swarm集群。
分為:管理(manager)節點,工作(worker)節點。
- 管理節點:
執行docker swarm 命令管理 swarm 集群的節點。
一個 Swarm 集群可以有多個管理節點,但只有一個管理節點可以成為 leader,leader 通過 raft 協議實現。
管理節點下發任務(Task) 給工作節點,通過工作節點通知給它的任務(Task)執行狀態來管理每個工作節點。 - 工作節點:
接收和執行來自管理節點的任務(task)。管理節點默認也作為工作節點。你也可以通過配置讓服務只運行在管理節點。
- 管理節點:
-
服務(Service)與任務(Task)

- 任務(Task):
Swarm 中的最小的調度單位,目前來說就是一個單一的容器。一個任務分配給一個節點之后,哪怕執行失敗都無法轉移到其他節點。 - 服務(Service):
運行於管理或者工作節點上的一組任務屬性定義。它是Swarm的中心結構,也是與Swarm交互的主要角色。
與Docker-compose中的服務類似,包含鏡像與命令等。
服務有兩種模式:- replicated services:按照一定規則在各個工作節點上運行指定個數的任務。
- global services:每個工作節點上運行一個任務
- 任務(Task):
使用實例
使用play-with-docker實驗。
-
初始化管理節點
$ docker swarm init Error response from daemon: could not choose an IP address to advertise since this system has multiple addresses on different interfaces (192.168.0.48 on eth0 and 172.18.0.53 on eth1) - specify one with --advertise-addr因為有多個網絡地址需要指定
$ docker swarm init --advertise-addr 192.168.0.48 Swarm initialized: current node (uas5ayulwp28ldrcr3bscmitr) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-17khjeqkfmjswynhda1i0dfqw4hy5io2006xktyvav272ccfie-6xomx3u1mvm7pacj0ymheyu6e 192.168.0.48:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.查看節點列表
$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION uas5ayulwp28ldrcr3bscmitr * node1 Ready Active Leader 19.03.4 -
加入工作節點
$ docker swarm join --token SWMTKN-1-17khjeqkfmjswynhda1i0dfqw4hy5io2006xktyvav272ccfie-6xomx3u1mvm7pacj0ymheyu6e 192.168.0.48:2377 This node joined a swarm as a worker.在兩個主機執行以上命令之后,去管理節點查看節點列表
$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION uas5ayulwp28ldrcr3bscmitr * node1 Ready Active Leader 19.03.4 2rum5pm81t76tjv91rld1n8vu node3 Ready Active 19.03.4 u4tbyt95nhy7l6vj3z013r8wz node5 Ready Active 19.03.4 -
服務部署
(命令都是在管理節點執行。)-
新建服務
在集群中創建nginx服務。$ docker service create --replicas 3 -p 80:80 --name nginx nginx:latest stieixvmvfipi4w6qh49t9es0 overall progress: 3 out of 3 tasks 1/3: running 2/3: running 3/3: running verify: Service converged -
查看服務
查看服務列表:
$ docker service ls ID NAME MODE REPLICAS IMAGE PORTS stieixvmvfip nginx replicated 3/3 nginx:latest *:80->80/tcp查看服務詳細:
$ docker service ps nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS xgc5u5m5gl9p nginx.1 nginx:latest node3 Running Running 2 minutes ago y76pttbz9pkh nginx.2 nginx:latest node5 Running Running 2 minutes ago oti754af3izt nginx.3 nginx:latest node1 Running Running 2 minutes ago瀏覽器中輸入管理節點或者工作節點IP地址都可以查看nginx界面。
管理節點:
工作節點1:

工作節點2:

-
服務伸縮
- 業務訪問量少時,可以減少服務運行容器數。
查看服務詳情:$ docker service scale nginx=2 nginx scaled to 2 overall progress: 2 out of 2 tasks 1/2: running 2/2: running verify: Service converged$ docker service ls ID NAME MODE REPLICAS IMAGE PORTS stieixvmvfip nginx replicated 2/2 nginx:latest *:80->80/tcp - 業務訪問量多時,可以增加服務運行容器數。
$ docker service scale nginx=3 nginx scaled to 3 overall progress: 3 out of 3 tasks 1/3: running 2/3: running 3/3: running verify: Service converged
- 業務訪問量少時,可以減少服務運行容器數。
-
刪除服務
$ docker service rm nginx nginx查看服務:
$ docker service ls ID NAME MODE REPLICAS IMAGE PORTS
-
-
通過compose文件部署服務
docker service create 一次只能部署一個服務,使用 docker-compose.yml 可以一次啟動多個關聯的服務- 創建docker-compose.yml文件
version: "3" services: nginx: image: nginx:latest ports: - 80:80 deploy: mode: replicated replicas: 3 - 部署服務
yml文件通過使用docker stack部署。$ docker stack deploy -c docker-compose.yml nginx Creating network nginx_default Creating service nginx_nginx - 查看服務
$ docker stack ls NAME SERVICES ORCHESTRATOR nginx 1 Swarm$ docker service ls ID NAME MODE REPLICAS IMAGE PORTS 92n4xd254vu1 nginx_nginx replicated 3/3 nginx:latest *:80->80/tcp$ docker service ps nginx_nginx ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS p6d3xibx9swx nginx_nginx.1 nginx:latest node1 Running Running 2 minutes ago v0taw0rnr9v3 nginx_nginx.2 nginx:latest node3 Running Running 2 minutes ago 2z3sbkdo16oi nginx_nginx.3 nginx:latest node5 Running Running 2 minutes ago - 移除服務
$ docker stack down nginx Removing service nginx_nginx Removing network nginx_default
- 創建docker-compose.yml文件
