Docker 服务与 Docker 容器的区别

1、概览

Docker 推动了容器化技术的普及,简化了高度分布式应用的创建、交付和运行流程。我们通常使用 docker container 命令管理独立的 Docker 容器。

Docker 最初聚焦于开发者和开发周期,但现已演进为通过简洁强大的编排器提供编排服务。

本文将带你了解容器化技术及编排器(orchestrator)的必要性,继而分析 Docker 容器与编排服务的区别及其适用场景。

2、容器化与编排技术解析

将应用容器化为自治单元是软件开发周期的关键步骤。然而,随着容器数量增长,部署和管理将面临重大挑战:

  • 少量容器:通过 Docker 手动管理仍可行,故障时可手动重启或更新。
  • 现代架构:应用通常拆分为多个组件,每个组件部署为独立容器。
  • 大规模部署:当数十甚至数百个容器需跨多服务器协同工作时,手动管理将不可行。

此时需引入容器编排引擎。

容器编排工具可自动化容器管理,主要保障以下特性:

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

在这些工具中,Kubernetes 无疑是应用最广泛且功能最全面的解决方案。

Docker 也提供了内置编排器 Docker Swarm 模式,该模式通过 services(服务)概念实现 Docker 容器的编排。

3、Docker 独立容器

以独立模式运行容器指直接在本地主机启动容器,无需编排器参与。每个容器相互隔离,通过 Docker CLI 或 API 手动管理。

3.1、部署

假设我们需要部署 bael-api(一个小型 REST Web 服务,用于查询最新或即将发布的课程和教程),同时配置存储课程元数据的数据库:

$ 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 API 获得巨大流量导致单容器无法有效承载。为应对负载增长,可通过手动复制 (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 实例,但仍面临新挑战:如何实现实例间的智能负载均衡。

使用 docker container run 单独运行容器时,系统没有内置的跨容器流量分发机制。要实现负载均衡,需额外配置 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 模式和服务(services)概念引入了更高层次的抽象。

4、Docker 服务与 Swarm 编排

为简化生产环境中分布式应用的管理,Docker 提供了称为 Docker Swarm 模式的高级功能。该模式基于原生 Docker SwarmKit 编排器(直接集成于 Docker 引擎),用于管理由多个 Docker 守护进程组成的集群(称为 swarm)。

启用 Swarm 模式需先初始化 Docker Swarm 集群,之后即可使用 docker service 命令进行管理。

4.1、服务

从独立容器迁移至 Swarm 模式时,相关概念也同步演进:

服务(Service)是 Docker Swarm 的核心抽象单元,用于定义容器的目标状态。与 Docker 容器不同,Docker 服务并不直接创建容器,而是声明需要持续维持的状态:

$ 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 网络以实现跨节点容器通信,并要求 Docker Swarm 维持三个 API 副本,通过主机的 80 端口对外服务。同时使用 -update-parallelism 参数配置逐实例 滚动更新(Rolling Update 策略,每次成功更新间隔 10 秒。

Docker Swarm 自动执行以下操作:

  • 跨可用节点分发副本。
  • 提供内置负载均衡。
  • 监控容器状态并在故障时重启。
  • 实施零中断的扩展或更新。

每个副本由 task(任务)表示。因此,Docker Swarm 服务对应一组活动任务,由 Docker 自动编排。

可通过 docker service ls 查看服务状态,或使用 docker service ps 检查创建的任务。

4.2、多服务栈

我们可以通过 Compose YAML 文件使用 stack 创建服务。stack 是服务集合,用于在 Docker Swarm 模式下定义和运行多容器应用。

以下是将第 3 节部署方案转换为 bael-stack.yml 文件的示例:

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

这种方式简化了多服务管理与连接。deploy 配置段 支持自定义服务行为。

接下来通过以下命令运行 stack:

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

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

5、核心差异

以下是独立 Docker 容器与编排 Docker 服务的核心差异总结:

维度 独立 Docker 容器 编排 Docker 服务
范围 单 Docker 主机 多节点集群
部署方式 命令式 声明式
高可用性 无自动管理 自动维护
扩展能力 手动操作 原生支持
滚动更新 手动实施 自动管理
负载均衡 无内置支持 通过路由网格(routing mesh)内置自动实现
典型场景 本地开发/测试/简单负载 生产环境/可扩展性/容错需求

若应用场景仅限于单主机且无需高级编排功能,使用 Docker Container 配合 Compose 即可满足需求。对于需要跨多主机的扩展性、容错能力和编排功能的生产环境,推荐采用 Docker Swarm。

6、总结

本文介绍了独立容器与 Docker 原生编排器,通过 Docker 服务突破了 Docker 容器和 Docker Swarm 方案的局限性。

理解这一区别有助于:

  • 优化部署架构。
  • 提高资源利用率。
  • 增强应用可靠性。

Ref:https://www.baeldung.com/ops/docker-service-vs-docker-container