1. 概述

Docker 的出现推动了容器化技术的普及,使得构建、交付和运行高度分布式的应用变得更加简单。我们通常使用 docker container 命令来管理独立的 Docker 容器。

Docker 最初主要面向开发者和开发周期。但随着平台的发展,它也通过一个简单而强大的编排器提供了编排服务。

在本文中,我们将先讨论容器化与编排的基本概念,然后深入分析 Docker 容器与编排服务之间的区别及其适用场景,帮助你在不同环境下做出更合适的选择。


2. 容器化与编排简介

将应用程序容器化,是软件开发周期中的关键一步。但部署和管理这些容器却可能迅速变得复杂。当我们只处理少量容器时,使用 Docker 手动管理仍可行。一旦出现故障,我们也能手动重启或更新。

但在现代架构中,一个应用通常被拆分成多个组件,每个组件都运行在各自的容器中。面对数十甚至上百个需要共存或跨多台服务器部署的容器时,手动管理很快变得不可持续。

这时就需要容器编排引擎(Container Orchestration Engine)来帮忙了。

容器编排工具可以自动化容器管理,尤其确保:

✅ 高效利用资源
✅ 高可用性
✅ 容错能力
✅ 透明的容器应用扩展

在这些工具中,Kubernetes 无疑是目前最广泛、最全面的选择。

Docker 自身也提供了集成的编排器:Docker Swarm 模式。它通过“服务”这一概念来编排容器,我们将在后续章节中详细介绍。


3. Docker 独立容器(Standalone Container)

在独立模式下运行容器,意味着直接在本地主机上启动容器,不依赖任何编排器。每个容器都是独立的,通过 Docker CLI 或 API 手动管理。

3.1. 部署

假设我们要部署一个名为 bael-api 的小型 REST 接口服务,用于展示最新的课程或文章。我们还有一个用于存储课程和文章元数据的数据库:

$ docker network create bael-network
$ docker volume create bael-db-data
$ docker container run -d --name db -v bael-db-data:/var/lib/mysql --network bael-network mysql
$ docker container run -d --name bael-api -p 8080:8080 --network bael-network bael-api

✅ 我们创建了一个自定义的 Docker 网络以确保容器间通信,并创建了一个持久化数据卷。接着使用 docker container run 启动两个容器:db 容器为内部服务,bael-api 容器则通过主机 8080 端口对外暴露。

3.2. 手动扩缩容

假设我们的 REST 接口访问量激增,单个容器已无法承受负载。我们可以手动复制多个 bael-api 容器进行横向扩展:

$ docker container run -d --name bael-api_1 -p 8081:8080 --network bael-network bael-api
$ docker container run -d --name bael-api_2 -p 8082:8080 --network bael-network bael-api

⚠️ 虽然现在有三个 API 实例在运行,但问题也随之而来:没有内置机制来实现多个容器之间的智能负载均衡

❌ 此时必须手动配置外部负载均衡器,如 TraefikHAProxy,才能实现请求的合理分发。

3.3. 手动更新

当需要更新 API 镜像时,流程也相当繁琐:

$ docker stop bael-api
$ docker rm bael-api
$ docker container run -d --name bael-api -p 8080:8080 --network bael-network bael-api:latest

✅ Docker 容器简单易用,但在面对高可用、自动扩缩容和滚动更新等需求时,很快就显得力不从心。

为了解决这些问题,Docker 引入了更高层次的抽象:Swarm 模式与服务(Service)


4. Docker 服务与 Swarm 编排

为了在生产环境中更高效地管理分布式应用,Docker 提供了一个高级特性:Docker Swarm 模式

Swarm 模式基于原生的 Docker SwarmKit 编排器,该编排器直接集成在 Docker 引擎中。它允许我们管理一个由多个 Docker 守护进程组成的集群,称为“Swarm 集群”。

我们可以通过初始化一个 Docker Swarm 集群来启用该模式,然后使用 docker service 命令进行管理。

4.1. 服务(Service)

从独立容器转向 Swarm 模式,意味着管理方式也发生了变化。

Service 是 Docker Swarm 的核心抽象。它定义了一个容器的期望状态。与容器不同,服务本身不会创建容器,而是描述一个需要长期维持的状态:

$ docker network create --driver overlay bael-network
$ docker service create --name bael-api --replicas 3 --update-delay 10s --update-parallelism 1 --publish 80:8080 --network bael-network bael-api:latest

✅ 我们创建了一个 overlay 网络以支持跨节点通信,并要求 Swarm 维持三个 bael-api 副本,通过主机 80 端口对外暴露。我们还设置了滚动更新策略:每次更新一个副本,间隔 10 秒。

Swarm 会自动完成以下操作:

✅ 将副本分配到可用节点
✅ 提供内置负载均衡
✅ 监控容器状态并在故障时重启
✅ 在不中断服务的情况下执行扩缩容或更新操作

每个副本由一个“任务”表示。Docker Swarm 中的服务实际上是一组由 Docker 自动编排的任务

你可以使用以下命令查看服务状态和任务:

$ docker service ls
$ docker service ps bael-api

4.2. 多服务栈(Multi-Service Stack)

我们可以使用 Docker Compose 的 YAML 文件来定义服务,并通过 stack 命令部署整个应用栈。Stack 是一组服务的集合,用于在 Swarm 模式下运行多容器应用

以下是一个名为 bael-stack.yml 的 Compose 文件示例:

version: "3.7"

services:
  db:
    image: mysql
    networks:
      - bael-network
    volumes:
      - bael-db-data:/var/lib/mysql

  bael-api:
    image: bael-api
    networks:
      - bael-network
    ports:
      - "80:8080"
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s

volumes:
  bael-db-data:

networks:
  bael-network:
    driver: overlay

✅ 使用该文件,我们可以通过以下命令部署整个应用栈:

$ docker stack deploy --compose-file bael-stack.yml bael-stack

我们部署了一个名为 bael-stack 的栈,包含两个服务:dbbael-api


5. 核心区别总结

特性 Docker 容器(Standalone) Docker 服务(Swarm)
作用范围 单一 Docker 主机 多节点集群(多主机)
部署方式 命令式(Imperative) 声明式(Declarative)
高可用性 不支持 自动管理
扩缩容 手动 原生支持
滚动更新 手动 自动支持
负载均衡 不支持 内置路由网格(Routing Mesh)
典型场景 本地开发、测试、简单负载 生产环境、高可用、跨主机部署

✅ 如果你的场景只涉及单台主机且不需要高级编排功能,使用 Docker Compose 配合容器就足够了。
✅ 但对于需要跨多主机部署、具备高可用性和自动扩缩容能力的生产环境,建议使用 Docker Swarm 模式


6. 总结

本文我们比较了 Docker 独立容器与 Swarm 模式下的服务之间的核心差异。通过了解这些区别,你可以更清晰地判断在不同场景下该使用哪种方式部署应用。

✅ Docker 容器适合本地开发和简单部署场景;
✅ 而 Docker 服务结合 Swarm 模式,则更适合生产环境,具备自动扩缩容、滚动更新、内置负载均衡等能力。

理解这些概念,有助于你更好地组织部署架构、优化资源使用并提升应用的稳定性。


原始标题:Docker Service vs. Docker Container: Understanding the Differences