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 零基础入门

相关文章
相关标签/搜索