即便有了Docker Compose,項目的部署仍然存在問題,由於Docker Compose只能把項目全部的容器部署在同一臺機器上,這在生產環境下是不現實的。html
Docker Compose通常只適用於開發環境,而對於生產環境下的項目部署,咱們須要用到Docker Swarm。前端
Docker Swarm是Docker官方提供的一套容器編排系統,它將一組Docker主機虛擬成一個單獨的虛擬Docker主機。node
架構以下:mysql
swarm是一系列節點的集合,而節點能夠是一臺裸機或者一臺虛擬機。一個節點能扮演一個或者兩個角色,manager或者worker。web
manager Docker Swarm集羣須要至少一個manager節點,節點之間使用Raft consensus protocol進行協同工做。 一般,第一個啓用docker swarm的節點將成爲leader,後來加入的都是follower。當前的leader若是掛掉, 剩餘的節點將從新選舉出一個新的leader。每個manager都有一個完整的當前集羣狀態的副本,能夠保證manager的高可用。 worker worker節點是運行實際應用服務的容器所在的地方。理論上,一個manager節點也能同時成爲worker節點,但在生產環境中, 咱們不建議這樣作。worker節點之間,經過control plane進行通訊,這種通訊使用gossip協議,而且是異步的。
多個tasks組成一個service,多個services組成一個stack。redis
task 在Docker Swarm中,task是一個部署的最小單元,task與容器是一對一的關係。 service swarm service是一個抽象的概念,它只是一個對運行在swarm集羣上的應用服務,所指望狀態的描述。 它就像一個描述了下面物品的清單列表同樣: 服務名稱 使用哪一個鏡像來建立容器 要運行多少個副本 服務的容器要鏈接到哪一個網絡上 須要映射哪些端口 stack stack是描述一系列相關services的集合,能夠經過在一個YAML文件中來定義一個stack,相似於docker-compose。
對於單主機網絡,全部的容器都運行在一個docker主機上,他們之間的通訊通常使用本地的bridge network便可。sql
而對於swarm集羣,針對的是一組docker主機,須要使用docker的overlay network。docker
前面有講過,docker run
僅適用於單機啓動單個容器;docker compose
僅適用於單機啓動多個容器;docker service
僅適用於集羣啓動單個service的容器。數據庫
即便在生產環境下使用docker service
來部署項目,也是沒法接受的。當項目足夠複雜時,會有更多關聯的service須要建立,經過docker service
手動建立多個service,效率也是十分低下的。ubuntu
所以,Docker Stack應運而生,Docker Stack適用於集羣啓動多個service的容器,在集羣中能夠相似Docker Compose那樣經過YAML文件直接部署項目。
只不過Docker Stack忽略了build
指令,不能像Docker Compose那樣在docker-compose.yml
中引用Dockerfile構建新鏡像,它須要鏡像是預先已經構建好的。因此Docker Compose更適合於開發場景,而Docker Stack更適用於生產環境。
單機 | 集羣 |
---|---|
docker run | docker service create |
docker-compose up | docker stack deploy |
docker stack
命令:使用docker stack再也不須要安裝docker-compose,直接使用docker stack deploy
命令。
docker stack deploy project_name --compose-file docker-compose.yml
固然,後面跟着的YAML文件名不必定是docker-compose.yml
,能夠自定義。
deploy
指令:指定與service的部署和運行相關的配置。只在使用docker stack deploy
部署時生效,而且會被docker-compose up
和docker-compose run
忽略。
version: "3.7"services: redis: image: redis:alpine deploy: replicas: 6 update_config: parallelism: 2 delay: 10s restart_policy: condition: on-failure
endpoint_mode
指令:爲鏈接到集羣的外部客戶端指定服務發現方法。
endpoint_mode: vip(默認配置) 爲service分配一個虛擬IP(vip),做爲客戶端在網絡上訪問service的前端。 Docker在客戶端和service可用的工做節點之間路由請求,而客戶端不知道有 多少節點參與服務或它們的IP地址或端口。 endpoint_mode: dnsrr DNS循環(dnsrr)服務發現不使用單個虛擬IP。Docker爲服務設置DNS條目, 針對service名稱的DNS查詢將返回一個IP地址列表,客戶端將直接鏈接到其中之一。
deploy: mode: replicated replicas: 2 endpoint_mode: dnsrr
labels
指令:爲服務指定標籤。這些標籤僅在service上設置,而不在service的任何容器上設置。
deploy: labels: com.example.description: "This label will appear on the web service"
要在容器上設置標籤,請在deploy
以外使用labels
。
version: "3.7"services: web: image: web labels: com.example.description: "This label will appear on all containers for the web service"
mode
指令:mode有global
和replicated
兩種,默認mode是replicated
。當爲global
時,每一個集羣節點僅容許有一個service的容器,不容許有副本;當爲replicated
時,每一個集羣節點能夠有指定數量的service的容器。
version: "3.7"services: worker: image: dockersamples/examplevotingapp_worker deploy: mode: global
deploy: mode: replicated replicas: 5
placement
指令:指定約束和偏好的設置。node.role == manager
表示只在manager節點建立service的容器。
deploy: placement: constraints: - node.role == manager - engine.labels.operatingsystem == ubuntu 14.04 preferences: - spread: node.labels.zone
replicas
指令:若是mode爲replicated
,則replicas
可指定在任何給定時間應該運行service的容器數量。
deploy: mode: replicated replicas: 6
resources
指令:配置資源限制。limits
限制最大資源,reservations
爲預留資源(始終可用)。
deploy: resources: limits: cpus: '0.50' memory: 50M reservations: cpus: '0.25' memory: 20M
restart_policy
指令:配置service的容器退出時是否重啓以及如何重啓。
condition 重啓條件:none、on-failure、any,默認是any delay 延遲,兩次重啓嘗試之間等待的時間,默認爲0s max_attempts 最大嘗試次數,在放棄以前嘗試從新啓動容器的次數,默認爲永不放棄) window 等待時間,判斷一次重啓是否成功以前的等待時間,默認爲當即判斷
deploy: restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s
rollback_config
指令:配置在更新失敗時應如何回滾服務。
parallelism 並行數量,每次回滾的容器數量。若是設置爲0,則全部容器同時回滾。 delay 延遲,每一個容器組兩次回滾之間等待的時間,默認爲0s failure_action 若是回滾失敗該繼續或暫停,默認是暫停 monitor 每次任務更新後檢測失敗的持續時間,默認爲0s max_failure_ratio 回滾期間的容錯率,默認爲0 order 回滾期間的操做順序。stop-first表示舊任務在啓動新任務以前中止; start-first表示新任務首先啓動,正在運行的任務暫時重疊。默認是stop-first
deploy: rollback_config: parallelism: 2 delay: 10s order: stop-first
update_config
指令:配置如何更新服務。
parallelism 並行數量,每次更新的容器數量。若是設置爲0,則全部容器同時更新。 delay 延遲,每一個容器組兩次更新之間等待的時間,默認爲0s failure_action 若是更新失敗該繼續或暫停,默認是暫停 monitor 每次任務更新後檢測失敗的持續時間,默認爲0s max_failure_ratio 更新期間的容錯率,默認爲0 order 更新期間的操做順序。stop-first表示舊任務在啓動新任務以前中止; start-first表示新任務首先啓動,正在運行的任務暫時重疊。默認是stop-first。 注意:只支持v3.4或更高版本
version: "3.7"services: vote: image: dockersamples/examplevotingapp_vote:before depends_on: - redis deploy: update_config: parallelism: 2 delay: 10s order: stop-first
下面使用Docker Stack部署wordpress項目,該項目包含兩個service:web和mysql。
role | ip | hostname |
---|---|---|
manager | 192.168.30.128 | test1 |
worker1 | 192.168.30.129 | test2 |
worker2 | 192.168.30.130 | test3 |
# mkdir /wordpress && cd /wordpress# vim docker-compose.yml
version: "3.7"services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: 123456789 MYSQL_DATABASE: wordpress volumes: - db_data:/var/lib/mysql networks: - my-network deploy: mode: global placement: constraints: - node.role == manager web: depends_on: - db image: wordpress ports: - "80:80" environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_PASSWORD: 123456789 networks: - my-network deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 3 delay: 10s order: stop-first volumes: db_data: networks: my-network: driver: overlay
# docker stack deploy wordpress --compose-file docker-compose.yml
# docker stack lsNAME SERVICES ORCHESTRATOR wordpress 2 Swarm# docker stack ps wordpress ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS qgi46v9jcgkh wordpress_db.q1n9ztahdj489pltf3gl5pomj mysql:5.7 test1 Running Running 18 seconds ago o848jlfflzfv wordpress_web.1 wordpress:latest test1 Running Running 13 seconds ago 2e0yxcr3man2 wordpress_web.2 wordpress:latest test2 Running Running about a minute ago nsvlklmt2ce4 wordpress_web.3 wordpress:latest test3 Running Running 55 seconds ago# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a42ca591eb8c wordpress:latest "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 80/tcp wordpress_web.1.o848jlfflzfv2p3zgl15ubq3a 3f35d12a5bce mysql:5.7 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 3306/tcp, 33060/tcp wordpress_db.q1n9ztahdj489pltf3gl5pomj.qgi46v9jcgkh70xwjtnu83t1i
當前集羣中有兩個service,其中web有3個副本,分別在集羣各節點上。
打開瀏覽器,分別訪問192.168.30.128
,192.168.30.129
,192.168.30.130
。
能夠看到,經過swarm集羣的任意節點均可以訪問到wordpress界面,這與以前使用Docker Service部署wordpress項目一致。
相比之下,使用Docker Stack與使用Docker Service部署項目的差異,就相似於使用Docker Compose與使用Docker Run部署服務的差異。
在生產環境下,使用Docker Stack是沒問題的,但把項目的一些敏感信息(如數據庫密碼)寫在YAML文件中則是咱們不想看到的。可使用Docker Secret解決該問題。
用戶名密碼 SSH key TLS認證 任何不想讓別人看到的數據
secret存在於Swarm Manager節點的Raft database裏 secret能夠分配給一個service,該service就能看到這個secret 在容器內部secret看起來像文件,但其實是在內存中
docker secret
命令:create 從一個文件或標準輸入的內容建立secret inspect 顯示一個或多個secret的詳細信息 ls 列出全部的secret rm 刪除一個或多個secret
# vim db_root_password123456789# docker secret create -l mysql_root_password password db_root_passwordpa6lx5kt52wtg3md8r1zhtmo2# docker secret lsID NAME DRIVER CREATED UPDATED pa6lx5kt52wtg3md8r1zhtmo2 password 9 seconds ago 9 seconds ago# docker secret inspect password [ { "ID": "pa6lx5kt52wtg3md8r1zhtmo2", "Version": { "Index": 552 }, "CreatedAt": "2019-10-25T03:47:23.552004564Z", "UpdatedAt": "2019-10-25T03:47:23.552004564Z", "Spec": { "Name": "password", "Labels": { "mysql_root_password": "" } } }]# rm -f db_root_password
能夠看到,咱們根據db_root_password
這個文件的內容來建立一個名爲password
的secret,並設置標籤爲mysql_root_password
。
注意:在建立完secret後,最好刪除對應的secret文件。
# docker service create --name busybox --secret password busybox sh -c "while true; do sleep 3600; done"fefw5tlp44quh2fg01vn0k1ei overall progress: 1 out of 1 tasks 1/1: running [==================================================>] verify: Service converged# docker service lsID NAME MODE REPLICAS IMAGE PORTS fefw5tlp44qu busybox replicated 1/1 busybox:latest # docker service ps busybox ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS tpzod80hckns busybox.1 busybox:latest test3 Running Running 44 seconds ago
192.168.30.130
# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4ba00cff43c1 busybox:latest "sh -c 'while true; …" About a minute ago Up About a minute busybox.1.tpzod80hcknsm6yce4mms61mz# docker exec -it busybox.1.tpzod80hcknsm6yce4mms61mz sh/ # cd /run/secrets//run/secrets # lspassword /run/secrets # cat password 123456789
能夠看到,這與以前的db_root_password
文件的內容一致。經過--secret
能夠給一個service指定多個secret。
# cd /wordpress# vim docker-compose.yml
version: "3.7"services: db: image: mysql:5.7 secrets: - password environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/password MYSQL_DATABASE: wordpress volumes: - db_data:/var/lib/mysql networks: - my-network deploy: mode: global placement: constraints: - node.role == manager web: depends_on: - db image: wordpress ports: - "80:80" secrets: - password environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_PASSWORD_FILE: /run/secrets/password networks: - my-network deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 3 delay: 10s order: stop-first volumes: db_data: networks: my-network: driver: overlaysecrets: password: external: true#secrets:# password:# file: ./db_root_password
與前面不使用secret的docker-compose.yml
文件對比,上面的docker-compose.yml
文件中增長了secrets
指令,並指定secret所在路徑,external: true
表示外部已經建立好secret。
一般來講,建議事先建立好secret,而後在YAML文件中指定便可,不然也能夠取消上面docker-compose.yml
文件的註釋內容來建立secret。
# docker stack deploy wordpress --compose-file docker-compose.yml
# docker stack lsNAME SERVICES ORCHESTRATOR wordpress 2 Swarm# docker stack ps wordpress ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 6ozs7o5eit0i wordpress_db.q1n9ztahdj489pltf3gl5pomj mysql:5.7 test1 Running Running 4 seconds ago aj4mis52w7tv wordpress_web.1 wordpress:latest test3 Running Starting 38 seconds ago r3cn3bs4edwu wordpress_web.2 wordpress:latest test1 Running Running 9 seconds ago mt81de7ihoc4 wordpress_web.3 wordpress:latest test2 Running Starting 34 seconds ago
# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e25a893106d5 mysql:5.7 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp, 33060/tcp wordpress_db.q1n9ztahdj489pltf3gl5pomj.6ozs7o5eit0i299hlkaedk8nr 12ce1facb2bb wordpress:latest "docker-entrypoint.s…" 2 minutes ago Up About a minute 80/tcp wordpress_web.2.r3cn3bs4edwunnykn9t83xqvm# docker exec -it wordpress_db.q1n9ztahdj489pltf3gl5pomj.6ozs7o5eit0i299hlkaedk8nr bashroot@e25a893106d5:/# cat /run/secrets/password 123456789 root@e25a893106d5:/# mysql -uroot -p123456789mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 Server version: 5.7.28 MySQL Community Server (GPL)Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases;+--------------------+| Database |+--------------------+| information_schema || mysql || performance_schema || sys || wordpress |+--------------------+ 5 rows in set (0.00 sec)
打開瀏覽器,分別訪問192.168.30.128
,
這說明wordpress鏈接mysql沒有問題,咱們配置的secret也就沒有問題。
由於在生產環境中使用的是Docker Swarm集羣,那麼如何在項目運行中更新service呢?這是咱們須要解決的問題。
以上面經過Docker Stack部署的項目wordpress爲例,更新mysql版本。
# docker exec -it wordpress_db.q1n9ztahdj489pltf3gl5pomj.6ozs7o5eit0i299hlkaedk8nr bashroot@e25a893106d5:/# mysql --versionmysql Ver 14.14 Distrib 5.7.28, for Linux (x86_64) using EditLine wrapper
當前版本爲5.7.28,循環查看mysql版本
root@e25a893106d5:/# sh -c "while true; do mysql --version && sleep 1; done"
新開一個窗口,
# docker service update wordpress_db --image mysql:5.7.27 --update-delay 0s --update-parallelism 1wordpress_db overall progress: 1 out of 1 tasks q1n9ztahdj48: running [==================================================>] verify: Service converged
# docker exec -it wordpress_db.q1n9ztahdj489pltf3gl5pomj.c1f6ea9fpg9hg5wauihkj0ba4 bashroot@5a13c556e1d4:/# mysql --versionmysql Ver 14.14 Distrib 5.7.27, for Linux (x86_64) using EditLine wrapper
能夠看到,mysql版本已經更新爲5.7.27。
固然,這個例子並很差,由於當前只有一個mysql容器,更新時原容器直接中止運行並刪除,通常對數據庫容器的更新也不會如此草率,好在這裏只是演示如何進行service的更新。
另外,Docker Stack對多個service同時更新仍是經過docker stack deploy
命令進行的,只不過事先更改好YAML文件便可。