Docker Swarm 零基礎入門

Docker Swarm 是 Docker 官方項目之一,提供 Docker 容器集羣服務,是 Docker 官方對容器雲生態進行支持的核心方案。使用它,用戶能夠將多個 Docker 主機封裝爲單個大型的虛擬 Docker 主機,快速打造一套容器雲平臺。node

Docker 1.12 Swarm mode 已經內嵌入 Docker 引擎,成爲了 docker 子命令 docker swarmmysql

Swarm mode 內置 kv 存儲功能,提供了衆多的新特性,好比:具備容錯能力的去中心化設計、內置服務發現、負載均衡、路由網格、動態伸縮、滾動更新、安全傳輸等。linux

概念

docker 中和 swarm 相關的命令有:nginx

  • docker swarm
  • dokcer node
  • docker service
  • docker stack
  • docker secret

Swarm 中一臺主機就是一個節點(node),節點分爲管理( manager)和工做(worker)節點。git

管理節點用於 Swarm 集羣的管理。一個 Swarm 集羣能夠有多個管理節點,但只有一個管理節點能夠成爲 leaderleader 經過 raft 協議實現。github

管理節點有內置 Raft 數據庫,它們用來存放配置等數據。並且它們之間的通訊都是加密的。web

工做節點是任務執行節點,管理節點將服務 (service) 下發至工做節點執行。管理節點默認也做爲工做節點。也能夠經過讓服務只運行在管理節點上,管理節點和工做節點只是它們的權限不一樣,工做節點就沒有管理節點那麼多權限,好比在工做節點上不能查看集羣中的容器。redis

有了 swarm 咱們就不用本身一個個的建立容器了,好比咱們有 3 個主機,咱們告訴 swarm 咱們要建立 5 個 nginx 容器,swarm 會本身幫咱們部署到不一樣主機上,好比那個主機部署一個那個部署兩個。sql

啓用 Swarm

$ docker swarm init
# 咱們執行這個命令,來啓用 swarm,咱們當前主機做爲管理節點
# 它還建立了 swarm 根證書,還建立了 join token,用來讓其餘節點加入這個 Swarm
# 還建立了 raft 數據庫。
Swarm initialized: current node (86pi89uue3q0eqiqpomna3ejy) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5aco0o1iqxbgjludw6m6rhl9qug60ylm8lj938hnyfy2cyt8d7-7jq47t76cohvtah61sab7rwcf 192.168.65.3:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
複製代碼

咱們能夠直接複製上面那條輸出的命令,讓其餘節點做爲 worker 加入這個 swarmdocker

如我咱們想讓加入的節點做爲 manager 加入這個 swarm 能夠執行:

$ docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5aco0o1iqxbgjludw6m6rhl9qug60ylm8lj938hnyfy2cyt8d7-ecmkdz7x0eswu3uyn0vij72dy 192.168.65.3:2377
複製代碼
$ docker node ls
# docker node 命令能夠用來管理咱們的節點,ls 表示列出集羣全部節點
複製代碼
$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS

# docker service 命令用來取代 docker run 命令
# 由於在 swarm 中咱們不關心容器的個個配置信息,並且也不會去其餘節點本身手動建立容器
# 咱們只須要拋出一個 任務(task) 由 Swarm 來協調
複製代碼

任務 (Task)是 Swarm 中的最小的調度單位,目前來講就是一個單一的容器。

服務 (Services) 是指一組任務的集合,服務定義了任務的屬性。服務有兩種模式:

  • replicated services 按照必定規則在各個工做節點上運行指定個數的任務。
  • global services 強制在每一個 node 上都運行一個且最多一個容器。
$ docker service create alpine ping baidu.com
# 建立一個容器,讓它來 ping 百度
$ docker service ls
ID                  NAME                  MODE                REPLICAS            IMAGE               PORTS
yjoejehnu4yx        nostalgic_zhukovsky   replicated          1/1                 alpine:latest
# 咱們發現 MODE 是 replicated(默認)
# REPLICAS 是 1/1 右邊是要運行,左邊是實際運行的數量
$ docker service ps nostalgic_zhukovsky
ID                  NAME                    IMAGE               NODE                    DESIRED STATE       CURRENT STATE           ERROR               PORTS
snpjphe14ztv        nostalgic_zhukovsky.1   alpine:latest       linuxkit-00155d01020f   Running             Running 4 minutes ago
# 查看這個 service 的任務(容器)
複製代碼
$ docker service update --replicas 3 nostalgic_zhukovsky
# docker service update 用來更新一個 service
# 這裏讓它運行 3 個任務
$ docker service ls
ID                  NAME                  MODE                REPLICAS            IMAGE               PORTS
yjoejehnu4yx        nostalgic_zhukovsky   replicated          3/3                 alpine:latest
$ docker service scale -d nostalgic_zhukovsky=10
# 咱們還能夠經過 scale 來擴大縮小咱們的服務
$ docker service ls
ID                  NAME                  MODE                REPLICAS            IMAGE               PORTS
yjoejehnu4yx        nostalgic_zhukovsky   replicated          7/10                alpine:latest
複製代碼
$ docker service scale nostalgic_zhukovsky=2
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS
584829ddf89f        alpine:latest       "ping baidu.com"    56 seconds ago      Up 49 seconds
8678f634f14b        alpine:latest       "ping baidu.com"    18 minutes ago      Up 18 minutes
$ docker rm -f 8678f634f14b
$ docker service ps yjoejehnu4yx
ID                  NAME                        IMAGE               NODE                    DESIRED STATE       CURRENT STATE            ERROR
snpjphe14ztv        nostalgic_zhukovsky.1       alpine:latest       linuxkit-00155d01020f   Running             Running 17 minutes ago
vk0ih9ds1kdm        nostalgic_zhukovsky.2       alpine:latest       linuxkit-00155d01020f   Running             Running 9 seconds ago
m4ylibww9l87         \_ nostalgic_zhukovsky.2   alpine:latest       linuxkit-00155d01020f   Shutdown            Failed 15 seconds ago    "task: non-zero exit (137)"
# 咱們發現咱們強制關閉一個容器,swarm 自動幫咱們新建了一個。
複製代碼

不像咱們本身啓動容器,使用 docker swarm 咱們只要說出本身任務就能夠了,swarm 會本身有沒有完成任務,比上面發現要運行兩個,卻發現一個被咱們刪了,它就會再建立一個容器。

多節點

建立 Swarm 集羣,咱們要建立多個 node,就須要多臺主機或虛擬機。

咱們能夠去 play with docker 網站來建立多個節點,這個網站是免費的,可是 4 個小時後就會銷燬你建立的全部虛擬機。

咱們還可使用 docker machine 幫咱們快速的在本地建立多臺 docker 虛擬機。

再或者去雲服務商那裏買幾臺雲主機,使用 這個腳本 快速安裝 docker。

docker machine

Docker Machine 是 Docker 官方編排(Orchestration)項目之一,負責在多種平臺上快速安裝 Docker 環境。它使用 Go 語言實現。

對於 windos 和 mac 安裝 docker 的時候就自帶了 docker machine。

對於 linux 可使用以下命令安裝。

$ sudo curl -L https://github.com/docker/machine/releases/download/v0.13.0/docker-machine-`uname -s`-`uname -m` > /usr/local/bin/docker-machine
$ sudo chmod +x /usr/local/bin/docker-machine
複製代碼
$ docker-machine create \
    --engine-registry-mirror https://dockerhub.azk8s.cn \
    -d virtualbox \
    node1
# 建立一個驅動是 virtualbox 類型的 docker 主機
# --engine-registry-mirror 用來指定鏡像加速器
# 驅動除了是 virtualbox 還能夠是 macOS 的 xhyve 驅動
# 它比 virtualbox 運行效率要高
複製代碼

若是是在 Windows 10 上執行上面那個命令就會報錯,由於 Windows 10 安裝 Docker for Windows 以後不能再安裝 VirtualBox,也就不能使用 virtualbox 驅動來建立 Docker Machine。

咱們可使用 hyperv 驅動,並且必須事先在 Hyper-V 管理器中新建一個 外部虛擬交換機 執行下面的命令時,使用 --hyperv-virtual-switch=MY_SWITCH 指定虛擬交換機名稱。

$ docker-machine create \
    --engine-registry-mirror https://dockerhub.azk8s.cn \
    --hyperv-virtual-switch PVS \
    -d hyperv \
    node1
# 而後咱們以管理員身份打開終端執行這條命令
# --hyperv-virtual-switch 後面是剛剛取的虛擬交換機名稱
# 咱們重複上面那個命令,分別建立 node1 node2 node3 3個節點
複製代碼

建立好 3 個 docker 主機後,咱們登陸上去建立 swarm 集羣。

$ docker-machine ssh node1
# 登陸到 node1
$ docker swarm init
# docker swarm join --token SWMTKN-1-1ve56x9y7784s0enqvhghnls4rbjazwxovz5me1cmqub9jlgwa-83msyrvzwpgrk46fsifmht8nj 192.168.1.168:2377
複製代碼

但後去 node2 和 node3 執行上面輸出的那條命令,而後再登陸到 node1。

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
2y3ih8gtvit225155x5u33690 *   node1               Ready               Active              Leader              18.09.7
q0ag3cuma8sqiszjlou2ttc3t     node2               Ready               Active                                  18.09.7
cx4yap7apqnii58xmmxs8mdo7     node3               Ready               Active                                  18.09.7
複製代碼

能夠看到這個 Swarm 中已經有 3 個節點了,其中 node1 是管理節點,剩下兩個是工做節點。

worker 節點沒有管理集羣,爲了方便咱們能夠複製 manager 的 token 讓另外兩個節點也成爲 manager。

或者在 node1 上執行:

$ docker node update --role manager node2
$ docker node update --role manager node3
複製代碼

網絡

在使用多節點以前,咱們先在本地執行一下,以下命令:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
rkfr0w8kpqvi        ingress             overlay             swarm
複製代碼

咱們能夠看到一個驅動類型是 overlay 類型的網絡,那是剛纔 swarm 幫咱們建立的。

要使用服務發現,須要相互通訊的 service 必須屬於同一個 overlay 網絡。自動建立的 ingress 沒有提供服務發現,必須建立本身的 overlay 網絡。

它是 Swarm 範圍橋接網絡,容器能夠跨主機互相訪問,就像它們在一臺主機上同樣。經過 overlay 網絡,主機與容器、容器與容器之間能夠相互訪問。

它只在一個 Swarm 內部,這樣就不會搞亂主機網絡配置。

使用

如今咱們使用 Swarm 來建立一個 drupal 網站(Drupal 是自由開源內容管理系統,用PHP語言寫成)。

登陸到 node1 首先建立一個本身 overlay 網絡

$ docker network create -d overlay drupal
複製代碼

而後建立一個 postgres service,用來存儲數據。

$ docker service create --name psql --network drupal -e POSTGRES_PASSWORD=mypass postgres
# --network 加入到咱們剛剛建立的那個網絡
# -e 添加環境變量,用來指定 postgres 密碼
複製代碼

而後就是 drupal service。

$ docker service create --name drupal --network drupal -p 80:80 drupal
$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
4c1rgorpz5hp        drupal              replicated          1/1                 drupal:latest       *:80->80/tcp
wyd2y3roqwh3        psql                replicated          1/1                 postgres:latest
$ docker service ps psql
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
v05c9pi98i4a        psql.1              postgres:latest     node2               Running             Running 10 minutes ago
$ docker service ps drupal
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
qz4443imlexv        drupal.1            drupal:latest       node3               Running             Running 6 minutes ago
# 能夠看到 postgres 運行在 node2,drupal 運行在 node3
複製代碼

咱們打開瀏覽器,輸入上面 node1 的 ip 地址。可使用 docker-machine lsdocker-machine ip node1 查看。

打開事後就能夠看到 drupal 頁面,快速填寫一下配置。

咱們能夠輸入一下 node2 和 node3 的 ip 到瀏覽器。

咱們發現一樣能夠訪問到 drupal 網站,雖然它只運行在 node2 上。

routing mesh

swarm 向外暴露端口,全部節點都參與進入 routing mesh 中。每一個節點都能接受暴露端口鏈接,即便 node 中沒有運行這個 service。routing mesh 會將請求路由到運行這個 service 上活躍的容器中,它在全部節點上作負載均衡。

當內部容器對容器通訊時,它們使用虛擬 IP (VIP)通訊,它是 Swarm 虛擬網絡中的私有 IP,它會讓請求分佈到全部服務任務中,好比咱們有 10 個 worker 容器,咱們無需作負載均衡,swarm 已經幫你作了。

當外部流量訪問監聽的端口時,全部 node 都會監聽該端口流量,而後它會將該流量負載均衡的路由到合適的容器。若是是不一樣 node 那麼就會經過虛擬網絡路由到容器,若是是同一個 node 上,那麼就會直接路由到容器的端口。

能夠看到每一個節點中都會有一個 load balancer。swarm 的負載均衡會把你的請求路由到一個任意節點的可用的容器上。routing mesh 在 swarm 節點的全部 IP 上監聽 published 端口。

若是在一個服務器端口上運行一個網站,咱們就會失去 swarm 的負載均衡,由於它是 TCP 層面的負載均衡而不是 DNS 層面的。這時候咱們就須要一個外部負載均衡,可使用 nginx 或 HAProxy。

滾動升級

滾動升級是一次只升級一部分副本,不一次性所有升級,它下降了應用更新的風險,若是某個副本更新失敗,整個更新將暫停,其餘副本則能夠繼續提供服務。在更新的過程當中,老是有副本在運行的,也保證了業務的連續性。

如今咱們把 nginx:1.16 版本升級到 nginx:1.17

$ docker service create --name web --replicas=3 nginx:1.16
# --replicas=3 啓用 3 個副本
複製代碼
$ docker service update --image nginx:1.17 web
# 升級到 1.17
# swarm 會中止一個容器,更新它,若是失敗就會暫停整個更新過程
$ docker service ps web
ID                  NAME                IMAGE               NODE                    DESIRED STATE       CURRENT STATE                 ERROR
           PORTS
nqch5ivpmmcd        web.1               nginx:1.17          linuxkit-00155d010229   Running             Running 57 seconds ago

uclfd0iev0sk         \_ web.1           nginx:1.16          linuxkit-00155d010229   Shutdown            Shutdown 58 seconds ago

ku2aomr1br3d        web.2               nginx:1.17          linuxkit-00155d010229   Running             Running about a minute ago

fpqp9mdfez9r         \_ web.2           nginx:1.16          linuxkit-00155d010229   Shutdown            Shutdown about a minute ago

ww6hxdsutlog        web.3               nginx:1.17          linuxkit-00155d010229   Running             Running about a minute ago

hvy2g68j9ah4         \_ web.3           nginx:1.16          linuxkit-00155d010229   Shutdown            Shutdown about a minute ago
複製代碼

Swarm 還有回滾功能,能夠經過 --rollback 快速恢復到更新以前的狀態。

$ docker service update --rollback web
# 也能夠寫成 docker serivice rollback web
複製代碼

--rollback 只能回滾到上一次執行 docker service update 以前的狀態,並不能無限制地回滾。

控制 Service 運行的節點

Swarm 會自動幫咱們把 Service 分配到合適的 node 上,性能高的 node 分配的 Service 就越多。除了 Swarm 自動分配,咱們也能夠手動分配。

咱們能夠給 node 設置 label,而後讓 Service 運行在指定 label 的 node 上。

$ docker node update --label-add key=value node1
$ docker node inspect node1 --pretty
...
Labels:
    - key = value
...
複製代碼

而後建立 Service

$ docker service create \
      --constraint node.labels.key==value \
      --replicas 3 \
      --name web \
      --p 80:80 \
      nginx
# --constraint 限制將 service 部署到指定的 node
$ docker service inspect web --pretty
...
Placement:Contraints: [node.labels.key==value]
...
複製代碼

咱們還能夠更新 Service constraint,讓它更換 node。

$ docker service update --constraint-rm node.labels.env==test web 
$ docker service update --constraint-add node.labels.env==prod web
複製代碼

若是咱們想 swarm 強制更新可使用 --force

$ docker service update --force web
複製代碼

Secret

咱們常常要向容器傳遞敏感信息,好比上面傳遞的 POSTGRES_PASSWORD=mypass 環境變量。密碼是以明文的形式寫在命令中,潛在的巨大的安全風險。

咱們能夠經過 Secret 安全地管理 Swarm 集羣中密碼、密鑰證書等敏感數據,並容許在多個 Docker 容器實例之間共享訪問指定的敏感數據。它最大支持 500KB 的字符串或二進制內容。

Secret 會被加密的保存在管理節點的硬盤上,被加密傳輸。只有被容許的容器才能查看 Secret,在容器中它只會被存在內存中,能夠在 /run/secrets/<secret_name | secret_alias> 訪問到。

Secret 能夠經過兩種方式建立,一種是文件另外一種是 stdin 建立。

$ docker secret create psql_user psql_user.txt
$ echo 'mypass' | docker secret create psql_pass -
複製代碼
$ docker service create --name psql --secret psql_user --secret psql_pass \
    -e POSTGRES_PASSWORD_FILE=/run/secrets/psql_pass \
    -e POSTGRES_USER_FILE=/run/secrets/psql_user postgres
複製代碼

--secret 用來指定 Service 能使用那個 secret

若是咱們如今刪除 Service 的 secret,可使用 --secret-rm

$ docker service update --secret-rm psql_pass psql
# 刪除 Service 的 secret。
# 容器會自動重建,由於 Service 是容器的一部分。
複製代碼

管理配置文件

Swarm 除了能夠幫咱們管理敏感數據,還能夠幫咱們管理配置文件。configsecret 命令的使用方法徹底一致。

$ docker config create redis.conf redis.conf
$ docker service create \
     --name redis \
     # --config source=redis.conf,target=/etc/redis.conf \
     --config redis.conf \
     -p 6379:6380 \
     redis:latest \
     redis-server /redis.conf
複製代碼

Stack

咱們每次啓動一個 service 的時候都要寫一堆命令行命令很是的笨拙冗長。

咱們能夠用 Stack 來簡化這個操做。Stack 是 Swarm 調用的抽象,和 docker-compose 同樣它也接收 docker compose 文件,用來定義 Services, Networks 和 Volumes 等。

咱們使用 docker stack deploy 而不是 docker service create,Stack 會幫咱們管理這些對象。

咱們能夠直接用 docker-compose.yml 文件,可是不能 build 鏡像,Swarm 只接收構建好的鏡像,新加了一個 deploy 字段。當使用 docker-compose 執行這個文件時,會忽略 deploy 字段。docker stack 中會忽略 build 字段,因此咱們能夠開發和發佈都使用一個 docker-compose.yml 文件。

version: '3.7'

services:
 db:
 image: mysql:5.7
 networks:
 - wordpress
 volumes:
 - db-data:/var/lib/mysql
 environment:
 MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
 MYSQL_DATABASE: wordpress
 MYSQL_USER: wordpress
 MYSQL_PASSWORD_FILE: /run/secrets/db_password
 secrets: # 指定 secret
 - db_root_password
 - db_password
 deploy: # 部署
 update_config: # 更新規則
 parallelism: 2 # 一次兩個
 delay: 10s # 更新延遲 10s,給 app 一個啓動時間
 restart_policy: # 重啓規則
 condition: on-failure
 placement:
 constraints: [node.role == manager] # 只部署在 manager 節點

 wordpress:
 depends_on:
 - db
 image: wordpress
 networks:
 - wordpress
 ports:
 - '80:80'
 environment:
 WORDPRESS_DB_HOST: db:3306
 WORDPRESS_DB_USER: wordpress
 WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
 secrets:
 - db_password
 deploy:
 replicas: 2
 labels: [APP=WORDPRESS] # 添加自定義 label
 restart_policy:
 condition: on-failure
 delay: 10s
 max_attempts: 3

 visualizer: # 可視化頁面,打開瀏覽器 8080 端口能夠看見效果
 image: dockersamples/visualizer:stable
 networks: 
 - wordpress
 ports:
 - "8080:8080"
 stop_grace_period: 1m30s
 volumes:
 - "/var/run/docker.sock:/var/run/docker.sock"
 deploy:
 placement:
 constraints: [node.role == manager]
volumes:
 db-data:

networks:
 wordpress:

secrets: 
 db_password:
 file: db_password.txt
 db_root_password:
 file: db_root_password.txt
複製代碼

而後咱們就能夠部署這個 stack

$ docker stack deploy -c docker_compose.yml wordpress
# -c 指定配置文件
# deploy 也能夠換成 up
$ docker stack ls
NAME                SERVICES            ORCHESTRATOR
wordpress           2                   Swarm
$ docker stack ps wordpress
ID                  NAME                    IMAGE               NODE                    DESIRED STATE       CURRENT STATE           
qk67so5btzln        wordpress_wordpress.1   wordpress:latest    linuxkit-00155d01022b   Running             Running 2 minutes ago
vmnk1gb7zl05        wordpress_db.1          mysql:latest        linuxkit-00155d01022b   Running             Running 2 minutes ago
s5x09e5q8fr9        wordpress_wordpress.2   wordpress:latest    linuxkit-00155d01022b   Running             Running 2 minutes ago
$ docker stack services wordpress
ID                  NAME                  MODE                REPLICAS            IMAGE               PORTS
kuro9uavayjq        wordpress_wordpress   replicated          2/2                 wordpress:latest    *:80->80/tcp
l667za415j6s        wordpress_db          replicated          1/1                 mysql:latest
$ docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
xnunueoy9t5m        wordpress_wordpress   overlay             swarm
$ docker secret ls
ID                          NAME                         DRIVER              CREATED             UPDATED
6wxq7esrrikmiq9rnhnl1uuzk   wordpress_db_password                            5 minutes ago       6 minutes ago
scvzglyfk4cjxfco1nytoajjz   wordpress_db_root_password                       5 minutes ago       6 minutes ago
複製代碼

經過上面能夠看到 stack 自動幫咱們建立 secret, network 等,咱們只須要一個命令就能夠了,若是咱們更新這個配置文件,咱們能夠再執行下面這個命令更新。

$ docker stack deploy -c docker-compose.yml wordpress
複製代碼

若是要中止這個 stack,能夠執行 rm 命令

$ docker stack rm wordpress
複製代碼

多配置文件

咱們能夠只是用一個 yaml 文件完成本地和生產環境開發,可是當咱們的應用變得複雜的時候,一個配置文件可能沒那麼好用,這時候咱們就可使用多配置文件。

好比咱們能夠新建以下 yaml 文件

docker-compose.yml
docker-compose.override.yml
docker-compose.test.yml
docker-compose.prod.yml
複製代碼

docker-compose.yml 做爲其餘配置文件的基礎,它會合併到其餘配置文件上。

docker-compose.override.yml 當執行 docker-compose up 的時候 docker-compose 會自動將 docker-compose.yml 和名爲 docker-compose.override.yml 合併成爲一個文件執行。

docker-compose.test.yml 用於 CI 環境,咱們能夠執行

$ docker-compose -f docker-compose.yml -f docker-compose.test.yml up -d
# 基本文件在前面
複製代碼

docker-compose.prod.yml 生產環境文件,咱們可使用 docker-compose 將它和 docker-compose.yml 合併成一個文件再交給 docker stack

$ docker-compose -f docker-compose.yml -f docker-compose.prod.yml config > out.yml
複製代碼

Docker 零基礎入門

Docker Compose 零基礎入門

Kubernetes 零基礎入門

相關文章
相關標籤/搜索