大规模场景下的多服务部署和管理是一件很难的事情。Docker Stack 为解决该问题而生,Docker Stack 通过提供期望状态、滚动升级、简单易用、扩缩容、健康检查等特性简化了应用的管理。
- Stack 能够在单个声明文件中定义复杂的多服务应用。Stack 还提供了简单的方式来部署应用并管理其完整的生命周期:初始化部署 -> 健康检查 -> 扩容 -> 更新 -> 回滚,以及其他功能!
- 在 Compose 文件中定义应用,然后通过 docker stack deploy 命令完成部署和管理。Compose 文件中包含了构成应用所需的完整服务栈。此外还包括了卷、网络、安全以及应用所需的其他基础架构。然后基于该文件使用 docker stack deploy 命令来部署应用。
- Stack 是基于 Docker Swarm 之上来完成应用的部署。因此诸如安全等高级特性,其实都是来自 Swarm。
- 从体系结构上来讲,Stack 位于 Docker 应用层级的最顶端。Stack 基于服务进行构建,而服务又基于容器。
1. Docker Stack部署应用
1.1 部署示例
示例应用 AtSea Shop,该示例托管在 Github 的 dockersamples/atsea-sample-shop-app。
- 该应用由 5 个服务、3 个网络、4 个密钥以及 3 组端口映射构成:
注:本章中服务一词,指的是 Docker 服务(由若干容器组成的集合,作为一个整体进行统一管理,并且在 Docker API 中存在对应的服务对象)。
1.1.1 Stack 简单分析
复制 Github 仓库,以获取全部源代码文件
$ git clone https://github.com/dockersamples/atsea-sample-shop-app.git
编写docker-stack.yml文件(该文件通常被称为 Stack 文件,在该文件中定义了应用及其依赖)在该文件整体结构中,定义了 4 种顶级关键字。
- version:代表了Compose文件格式的版本号。为了应用于Stack,需要3.0或者更高的版本。
- services:中定义了组成当前应用的服务都有哪些。
- networks:列出了必需的网络。
- secrets:定义了应用用到的密钥。
- 上图中的结构,Stack 文件由 5 个服务构成,分别为“reverse_proxy”“database”“appserver”“visualizer”“payment_gateway”。Stack 文件中包含 3 个网络,分别为“front-tier”“back-tier”“payment”。4 个密钥,分别为“postgres_password”“staging_token”“revprox_key”“revprox_cert” ```yaml version: “3.2”
services: reverse_proxy: database: appserver: visualizer: payment_gateway: networks: front-tier: back-tier: payment: secrets: postgres_password: staging_token: revprox_key: revprox_cert:
<a name="LfCbI"></a>
#### 1.1.2 Stack配置文件详解
> 1. Stack 文件就是 Docker Compose 文件,且version>=3.0。
> 1. Docker Stack 和 Docker Compose 的一个区别是,Stack 不支持构建。这意味着在部署 Stack 之前,所有镜像必须提前构建完成。
1. 网络
- Docker 根据某个 Stack 文件部署应用的时候,首先会检查并创建 `networks:` 关键字对应的网络。如果对应网络不存在,Docker 会以 `overlay` 网络方式进行创建。
```yaml
networks:
front-tier:
back-tier:
payment:
driver: overlay
driver_opts:
encrypted: 'yes'
- 文件中定义了 3 个网络:front-tier、back-tier 以及 payment。默认这些网络都会采用
overlay
驱动,新建对应的覆盖类型的网络。但是 payment 网络比较特殊,需要数据层加密。 - 默认情况下,覆盖网络的所有控制层都是加密的,如果需要加密数据层:
- 在 docker network create 命令中指定 -o encrypted 参数。
- 在 Stack 文件中的 driver_opts 之下指定 encrypted:’yes’。
- 密钥
secrets:
postgres_password:
external: true
staging_token:
external: true
revprox_key:
external: true
revprox_cert:
external: true
- 这4 个密钥都被定义为 external,这意味着在 Stack 部署之前,这些密钥必须存在。
- 服务
(1)reverse_proxy 服务
reverse_proxy 服务定义了镜像、端口、密钥以及网络:
reverse_proxy:
image: dockersamples/atseasampleshopapp_reverse_proxy
ports:
- "80:80"
- "443:443"
secrets:
- source: revprox_cert
target: revprox_cert
- source: revprox_key
target: revprox_key
networks:
- front-tier
secrets:
关键字中定义了两个密钥:revprox_cert 以及 revprox_key,必须在系统上已经存在。密钥以普通文件的形式被挂载到服务副本当中。文件的名称是 target 属性的值,其在 Linux 下的路径为/run/secrets
。- 本服务密钥中定义的内容会在每个服务副本中被挂载,具体路径为
/run/secrets/revprox_cert
和/run/secrets/revprox_key
。
(2)database 服务
database:
image: dockersamples/atsea_db
environment:
POSTGRES_USER: gordonuser
POSTGRES_DB_PASSWORD_FILE: /run/secrets/postgres_password
POSTGRES_DB: atsea
networks:
- back-tier
secrets:
- postgres_password
deploy:
placement:
constraints:
- 'node.role == worker'
environment:
关键字允许在服务副本中注入环境变量。在该服务中,使用了 3 个环境变量来定义数据库用户、数据库密码的位置(挂载到每个服务副本中的密钥)以及数据库服务的名称。(将三者作为密钥传递会更安全,因为这样可以避免将数据库名称和数据库用户以明文变量的方式记录在文件当中)deploy:
关键字定义了部署约束,这样保证了当前服务只会运行在 Swarm 集群的 worker 节点之上。- 部署约束是一种拓扑感知定时任务,是一种优化调度选择的方式。Swarm 目前允许如下几种调度方式:
- 节点ID,如
node.id==o2p4kw2uuw2a
- 节点名称,如
node.hostname==wrk-12
- 节点角色,如
node.role!=manager
- 节点引擎标签,如
engine.labels.operatingsystem==ubuntu16.04
- 节点自定义标签,如
node.labels.zone==prod1
- 节点ID,如
(3)appserver 服务
appserver:
image: dockersamples/atsea_app
networks:
- front-tier
- back-tier
- payment
deploy:
replicas: 2
update_config:
parallelism: 2
failure_action: rollback
placement:
constraints:
- 'node.role == worker'
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
secrets:
- postgres_password
- deploy-replicas:副本的数量
- update_config:滚动升级时的操作,每次更新 2 个副本(parallelism:2),升级失败以后回滚(failure_action: rollback)
- failure_action默认为 pause ,即服务升级失败后阻止其它副本的升级,还支持 continue
- restart_policy:容器异常退出的重启策略,当前策略为:如果某个副本以非 0 返回值退出(condition: on-failure),会立即重启当前副本,重启最多重试 3 次,每次最多等待 120s,每次重启间隔是 5s。
(4)visualizer 服务
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8001:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
update_config:
failure_action: rollback
placement:
constraints:
- 'node.role == manager'
stop_grace_period:
设置容器优雅停止时长(Docker 停止某个容器时,会给容器内 PID 为 1 的进程发送一个 SIGTERM 信号,容器内 PID 为 1 的进程有 10s 的优雅停止时长来执行清理操作)。volumes:
挂载提前创建的卷或者主机目录至某个服务副本中,本例中/var/run/docker.sock为Docker 的 IPC 套接字,Docker daemon 通过该套接字对其它进程暴露 API 终端,如果某个容器有该文件的访问权限,即允许该容器访问所有的 API 终端,并且可以查询及管理 Docker daemon。(生产环境严禁挂载该文件)
(5)payment_gateway 服务
payment_gateway:
image: dockersamples/atseasampleshopapp_payment_gateway
secrets:
- source: staging_token
target: payment_token
networks:
- payment
deploy:
update_config:
failure_action: rollback
placement:
constraints:
- 'node.role == worker'
- 'node.labels.pcidss == yes'
node.labels:
自定义节点标签,可以通过docker node update自定义,并添加至 Swarm 集群的指定节点。在本例中,payment_gateway 服务被要求只能运行在符合 PCI DSS标准(支付卡行业标准)的节点之上。为了使其生效,可以将某个自定义节点标签应用到 Swarm 集群中符合要求的节点之上。1.1.3 准备部署的环境
在讲解使用 Docker Stack 部署应用之前,有几个前置处理需要完成:
- Swarm 模式:应用将采用 Docker Stack 部署,而 Stack 依赖 Swarm 模式。
- 标签:某个 Swarm worker 节点需要自定义标签。
- 密钥:应用所需的密钥需要在部署前创建完成。
- 基于 Linux 的三节点 Swarm 集群搭建:
(1)初始化swarm
# 管理节点运行
$ docker swarm init
# 获取加入swarm的token
$ docker swarm json-token worker
# 加入swarm
wrk-1$ docker swarm join --token {token}
wrk-2$ docker swarm join --token {token}
# 查看swarm节点
$ docker node ls
(2)添加标签
- 为wrk-1添加pcidss标签,在实际操作中,添加该标签之前必须将某个 Docker 节点按 PCI 规范进行标准化。
```bash
将wrk-1节点加上pcidss标签
$ docker node update —label-add pcidss=yes wrk-1
$ docker node inspect wrk-1 … “Spec”: { “Labels”: { “pcidss”: “yes” } } …
- Node 标签只在 Swarm 集群之内生效。
(3)创建密钥
```bash
# 创建新的键值对
$ openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout domain.key -x509 -days 365 -out domain.crt
# 创建 revprox_cert、revprox_key 和 postgress_password 密钥
$ docker secret create revprox_cert domain.crt
$ docker secret create revprox_key domain.key
$ docker secret create postgres_password domain.key
# 创建 staging_token 密钥
$ echo staging | docker secret create staging_token -
# 列出所有密钥
$ docker secret ls
1.1.4 部署
使用命令
docker stack deploy -c <docker-stack.yml> <stack name>
完成部署。$ docker stack deploy -c docker-stack.yml seastack
2. 管理Stack
使用docker-stack.yml来管理Stack,虽然直接使用docker对容器或集群也可以操作,但是这样并不会修改stack文件的配置,此时再修改stack文件会再按照配置文件来调整。
修改好stack文件后,再重新部署,只需要运行:
$ docker stack deploy -c docker-stack.yml seastack
删除stack:
$ docker stack rm seastack