在现代应用系统的架构设计与部署中,单一容器的模式已难以满足实际生产需求。虽然通过 Docker 部署单个容器应用方式简单,适合开发和测试场景,但面向生产环境的系统通常由多个协作的微服务组件组成,每个服务承担着不同的业务角色。 例如,Web 前端作为用户入口,数据库承担持久化存储,API 网关负责服务之间的请求路由与安全控制等。这些服务共同构建成一个功能完整的“应用社区”。
在这种多服务协作的架构下,若采用传统的手工操作手段进行部署与管理,不仅工作量巨大,且难以保障整个系统的一致性和可维护性。 因此,我们需要更为系统化和自动化的工具,帮助实现面向云原生应用的集群化部署与高效管理。Docker Stacks 正是为此场景设计,能够有效支持大型微服务应用的声明式部署与自动化运维。

Docker Stack 是由多个相互关联的服务(Service)组成的应用集合,它以声明式方式管理应用生命周期,为高复杂度的分布式系统提供一致性、可维护性和高可用性保障。
在 Stack 的管理范式下,用户只需专注于核心“蓝图”(即 Docker Compose 文件,通常命名为 compose.yaml)的编写与维护,描述系统中各项服务的组成、镜像、资源分配、网络配置及依赖关系等。
Docker Swarm 作为底层集群调度引擎,接管了实际的服务编排、资源调度、安全隔离等工作,实现蓝图到实际系统状态的自动化映射。
通过这种声明式的基础架构管理方式,开发与运维人员能够专注于业务架构设计,无需关心底层系统的实现细节。无论是初始部署、服务扩缩容、版本升级还是异常回滚,均可通过 Stack 的自动化机制高效完成,显著提升应用的可管理性与可靠性。
总结来说,对于面向生产环境、具备一定规模与复杂度的应用系统,Docker Stacks 能够提供集群级别的专业部署与运维支持,是现代云原生架构不可或缺的基础组件。
如果你在我们之前的学习中详细掌握了 Docker Compose,那么恭喜你,学习 Docker Stacks 对你来说会非常轻松。 从架构上看,Stack 位于 Docker 应用层级的顶端,它建立在服务(Services)之上,而服务又建立在容器(Containers)之上。
现在,让我们来详细解读这张“蓝图”——compose.yaml 文件。这份文件不仅仅是配置,它本身就是一份极佳的“活文档”,清晰地记录了应用的完整架构。通常,它包含三大核心部分。
在 compose.yaml 中,networks 字段定义了应用所需的网络。这就像社区的道路系统。
为了让社区内的各个建筑(服务)能够跨越不同的地块(Swarm 节点)进行通信,我们通常需要使用 overlay 网络,它就像是连接整个社区的高速公路。
我们还可以对这条高速公路进行加密,确保“道路”上运输的信息是私密的,不会被窃听。
|networks: counter-net: driver: overlay driver_opts: encrypted: 'yes'
此外,我们还需要为社区开设一个对外的大门,让外部的访客可以访问。这就是端口映射(port mapping),它能将 Swarm 集群节点上的某个端口(例如 5001)的流量,引导至社区内部“商店”服务的指定端口(例如 8080)。
很多应用都需要一个地方来持久化存储数据。在我们的社区里,volumes 字段就扮演了“公共仓库”的角色。
这个仓库是独立于任何一栋建筑(容器)的。即使某天“商店”需要翻新重建(容器被删除和替换),存放在仓库里的货物(数据)也依然安然无恙。新的“商店”建好后,可以直接接入这个仓库,继续使用里面的数据。
|volumes: counter-vol: services: redis: # ... 其他配置 ... volumes: - type: volume source: counter-vol target: /app
services 是整个蓝图中最核心、最激动人心的部分。这里定义了构成我们应用社区的各种“功能建筑”。
让我们来看一个名为 web-fe 的“商店”服务的例子。它的定义看起来可能很长,但每个配置项都有其清晰的用途。
让我们亲手来建造一个应用社区吧。这意味着我们的 Docker 节点需要先组成一个施工团队,也就是配置成 Swarm 模式。
首先,我们需要在管理节点上初始化一个 Swarm 集群。这个过程非常简单。
|$ docker swarm init Swarm initialized: current node (lhma...w4nn) is now a manager.
然后,根据提示,在其他工作节点上执行 docker swarm join 命令,让它们加入这个施工团队。
当施工队准备就绪后,我们就可以拿出我们的 compose.yaml 蓝图,下达施工命令了。
我们使用 docker stack deploy 命令,它需要两个主要参数:用 -c 指定蓝图文件,以及为我们即将创建的社区(Stack)起一个名字。
|# 假设我们的社区叫 'ddd' $ docker stack deploy -c compose.yaml ddd Creating network ddd_counter-net Creating service ddd_web-fe Creating service ddd_redis
你会发现,Docker 会自动在你定义的所有资源(如网络、服务)名称前,加上你给社区起的名字作为前缀。 这有助于在同一个 Swarm 集群中隔离和管理多个不同的应用社区。
命令下达后,施工队就开始忙碌起来了。我们可以用几个命令来视察进度。docker stack ls 会列出当前 Swarm 上所有的社区,
而 docker stack ps <社区名字> 则能提供某个社区内所有建筑(服务副本)的详细状态。
|$ docker stack ps ddd NAME IMAGE NODE DESIRED STATE CURRENT STATE ddd_redis.1 redis:alpine mgr1 Running Running 4 mins ddd_web-fe.1 nigelpoulton/ddd... wrk1 Running Running 4 mins ddd_web-fe.2 nigelpoulton/ddd... wrk2 Running Running 4 mins ...
docker stack ps 是排查问题的起点。如果某个服务启动失败,这里会显示错误信息,告诉你问题出在哪里。
一个社区建成后,并非一成不变。我们可能需要扩建“商店”以应对节假日的人流高峰,或是对“商店”进行翻新,推出新的产品。 这里最重要的原则是:始终坚持“声明式”管理。
切记:不要使用 docker service scale 这样的命令去手动修改线上服务的状态。这种“命令式”的操作就像是绕过规划图纸,在社区里搞违章建筑。它会让你实际运行的环境和你的“蓝图”不再一致,造成管理上的混乱和灾难。
正确的做法永远是:修改你的蓝图 (compose.yaml),然后重新部署它。
让我们来看一个例子。假设我们想把 web-fe 商店的数量从4家增加到10家,并使用一个更新版本的应用镜像 swarm-appv2。
首先,我们修改 compose.yaml 文件:
|services: web-fe: image: nigelpoulton/ddd-book:swarm-appv2 # <-- 更新镜像 # ... deploy: replicas: 10 # <-- 增加副本数量 # ...
然后,保存文件,并再次执行部署命令:
|$ docker stack deploy -c compose.yaml ddd Updating service ddd_redis (id: ozljsazuv7mmh14ep70pv43cf) Updating service ddd_web-fe (id: zbbplw0hul2gbr593mvwslz5i)
Docker 非常智能,它会比较新旧蓝图的差异,只对发生变化的部分进行更新。它会按照你在 update_config 中设定的策略,平滑地、滚动地将旧的建筑替换成新的,确保在整个升级过程中,你的服务始终可用。
当一个应用社区完成了它的历史使命,我们可以用 docker stack rm 命令来将它完整地拆除。
|$ docker stack rm ddd Removing service ddd_redis Removing service ddd_web-fe Removing network ddd_counter-net
请注意,这个命令非常干脆,它不会向你二次确认。执行后,服务和网络等资源会被立即删除。
值得一提的是,之前创建的“公共仓库”(Volume)默认不会被删除。这是因为 Docker 认为数据是宝贵的,需要被长期保留,所以将它与应用的生命周期解耦。如果你确认不再需要这些数据,需要手动去删除 Volume。
总而言之,Stacks 是 Docker 官方提供的、用于部署和管理云原生微服务应用的最佳方案。 它要求你切换到一种“声明式”的思维模式:你不再是亲力-亲为的建筑工人,而是一位高瞻远瞩的城市规划师。 你通过维护一份作为“唯一事实来源”的蓝图文件,来管理应用的整个生命周期——从部署、扩容、升级到最终的下线。