Docker 编排概述
### 从手动到编排 你现在的流程是「单容器手工流」: 1. 导入镜像:`docker load -i py1.tar` 2. 启动容器:`docker run -itd --name kl ...` 3. 进容器装软件:`docker exec -it kl bash` 4. 打包成镜像:`docker commit` / `docker save` 这对**一个容器**没问题。但真实业务通常由多个容器协作: - 一个 Web 容器 + 一个数据库容器 + 一个缓存容器 - 它们之间有启动先后、网络互通、数据共享的需求 - 你要手动 `docker run` 三遍、手动建网络、手动记端口映射……很快就会乱 **Docker 编排(Orchestration)** 就是用一个「声明式配置文件」描述:要哪些容器、怎么连、按什么顺序起。然后**一条命令**把整套环境拉起来 / 停掉 / 销毁。核心解决的痛点: | 手工方式 | 编排方式 | |---------|---------| | 记住一长串 `docker run` 参数 | 写进配置文件,版本可追踪 | | 手动建网络、连容器 | 网络在文件里声明,自动互通 | | 一个挂了没人管 | 自动重启(self-healing) | | 想加机器只能重做 | 集群内调度、可扩缩容 | 常见的三种编排工具:**docker-compose**(单机多容器)、**Docker Swarm**(集群)、**Kubernetes**(行业标准)。下面分别介绍。
### docker-compose:单机多容器 - **定位**:在一台机器上,用**一个 YAML 文件**管理多个容器。 - **谁在用**:本地开发、单机部署、CI 环境,最常用、最轻量。 - **形态**:现代 Docker 已把它内置为 `docker compose` 子命令(v2 插件,注意没有横杠);老版本是独立的 `docker-compose`(v1,有横杠)。 核心思想就一句话:**把 `docker run` 后面那一长串参数,写成 `docker-compose.yml` 里的几行配置**。 ```yaml # docker-compose.yml 示意 services: web: image: nginx:latest ports: - "8080:80" db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: "123456" ``` 写好后: - `docker compose up -d` 一键启动 `web` 和 `db` 两个容器 - 它俩自动进入**同一个网络**,`web` 容器里直接用服务名 `db` 就能连到数据库 - `docker compose down` 一键全部停止并删除 > 本节后面(Compose 部署示例)会用空容器做一个完整可跑的例子。 适用场景:**单机**。如果你的服务要跨多台服务器,就该上 Swarm 或 K8s 了。
### Docker Swarm:Docker 自带的集群 - **定位**:Docker 原生内置的**多机集群**方案,直接集成在 Docker 引擎里,不需要额外装大套组件。 - **谁在用**:中小规模集群、不想上 K8s 的团队。注意:2023 年起官方把它转为**维护模式**,不再加新特性,但仍可用。 最小玩法(两台以上装好 Docker 的机器): ```bash # 在管理节点上初始化集群 docker swarm init --advertise-addr 192.168.1.10 # 它会输出一条 join 命令,拿到工作节点上执行即可加入集群 docker swarm join --token SWMTKN-xxx 192.168.1.10:2377 # 部署一个服务(自动在集群里调度到不同节点) docker service create --name myweb -p 8080:80 --replicas 3 nginx:latest ``` 特点: - 命令几乎和单机 `docker run` 一样(`docker service` / `docker stack`),学习成本低。 - 支持服务发现、滚动更新、用 `--replicas` 做副本数伸缩。 - 比 K8s 功能少(没有强大的 Operator、调度策略较简单)。 一句话:**多机、轻量、和 docker 命令几乎无缝**,但生态和扩展性不如 K8s。
### Kubernetes(K8s):行业标准 - **定位**:跨多机的大规模容器编排**事实标准**,最初来自 Google 内部 Borg 系统的开源。 - **谁在用**:中大型生产环境、云原生生态,几乎所有公有云都提供托管 K8s(AKS / EKS / GKE)。 核心概念: | 概念 | 类比 | 作用 | |------|------|------| | Pod | 一组共生死容器 | K8s 调度的最小单位,一个 Pod 里可放多个共享网络的容器 | | Deployment | 副本控制器 | 声明「我要 3 个副本」,掉了一个自动补上 | | Service | 稳定访问入口 | 给一组 Pod 一个固定名字和负载均衡,Pod 重建 IP 变了也不影响调用 | 同样部署 nginx(声明式 YAML,用 `kubectl` 操作): ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: myweb spec: replicas: 3 selector: matchLabels: app: myweb template: spec: containers: - name: nginx image: nginx:latest ``` 特点: - 自动扩缩容、滚动发布、自愈、配置 / 密钥管理、丰富的调度策略。 - **学习曲线陡**:概念多(Pod / Deployment / Service / Ingress / ConfigMap ...)、组件多(API Server / etcd / kubelet / kube-proxy)。 - 生态最丰富(Helm、Istio、Prometheus 等都围绕它)。 一句话:**大规模、生产级、生态最强**,但门槛也最高。
### 三者怎么选 | 维度 | docker-compose | Docker Swarm | Kubernetes | |------|----------------|--------------|------------| | 规模 | 单机 | 多机(中小集群) | 多机(大规模集群) | | 安装 | Docker 自带 | Docker 引擎内置 | 需独立搭建 / 托管 | | 学习成本 | 最低 | 低 | 高 | | 配置文件 | `docker-compose.yml` | 同 compose / `stack` | 各种 YAML + CRD | | 主要命令 | `docker compose` | `docker service/stack` | `kubectl` | | 高可用 / 自愈 | 弱 | 基础 | 强 | | 生态 | 一般 | 维护模式 | 极丰富 | | 典型场景 | 本地开发、单机服务 | 内部小集群 | 生产级云原生 | 选型建议: 1. **个人学习 / 本地开发 / 一台服务器跑几个服务** → 用 **docker-compose**(本节示例就是它)。 2. **几台机器要做高可用、又不想搞太复杂** → **Docker Swarm**,平滑、和现有 docker 命令几乎一致。 3. **生产环境、要弹性伸缩、团队要长期发展** → 上 **Kubernetes**,投入大但天花板高。 常见渐进路线:`compose`(跑通业务)→ `swarm`(先上多机)→ `k8s`(再要规模和生态)。从 compose 起步成本最低,也是本页后续示例的工具。
Compose 部署示例
### 目标与环境 我们用 docker-compose 部署一个**最小的多容器应用**:3 个几乎「什么都不做」的空容器,放在同一个网络里,能互相 ping 通。重点是把**整套流程**跑通。 > 为什么用「空容器」学?因为它剥离了业务,让你专注于编排本身:怎么写文件、怎么起、怎么停。空容器一旦能跑通,把里面的镜像换成 nginx / mysql / 你的 cent7,就是真实应用。 先确认 compose 可用: ```bash # 现代版本(v2 插件,推荐),注意 compose 没有横杠 docker compose version # 老版本(v1 独立脚本,带横杠) docker-compose -v ``` - 输出类似 `Docker Compose version v2.x` 就 OK。 - Docker Desktop 和较新的 Docker Engine 默认自带 `docker compose`(v2),不用单独装。 - 如果两个都没有:Linux 上可装 `docker-compose-plugin`,或 `pip install docker-compose`。 然后建一个干净的项目目录: ```bash mkdir -p ~/dkdemo && cd ~/dkdemo ``` 接下来在这个目录里建一个 `docker-compose.yml`。
### 第一步:编写 docker-compose.yml 在 `~/dkdemo` 目录下新建 `docker-compose.yml`(新版 compose 已不需要写 `version` 字段): ```yaml # ~/dkdemo/docker-compose.yml services: app1: image: busybox:latest container_name: myapp1 command: ["sleep", "infinity"] # 让空容器一直挂着不退出 networks: - mynet app2: image: busybox:latest container_name: myapp2 command: ["sleep", "infinity"] networks: - mynet app3: image: busybox:latest container_name: myapp3 command: ["sleep", "infinity"] networks: - mynet networks: mynet: driver: bridge ``` 逐段解释: - **`services`**:声明有哪些容器(这里 3 个:app1 / app2 / app3)。 - **`image`**:用 `busybox`(极小的镜像,约 1~2MB,适合做「空容器」)。你也可以换成自己导入的 `cent7`,或 `alpine`、`nginx`。 - **`container_name`**:给容器起固定名字,方便后续 `docker ps` / `exec`。 - **`command`**:**关键点**——容器只要「主进程退出」就会自动结束。busybox 默认起一个 shell 立刻退出,所以整个容器一启动就停了。用 `sleep infinity`(或 `tail -f /dev/null`)让它一直挂着,容器才「常驻」。 - **`networks`**:三个容器都加入同一个自定义网络 `mynet`。 - **`networks.mynet`**:声明这个网络用 bridge 驱动(默认的单机网络)。 一句话:同一 `mynet` 里的容器,可以**用服务名互相访问**(比如在 app1 里 `ping app2` 能通),不用记 IP。
### 第二步:启动、查看、停止 确保当前目录有 `docker-compose.yml`,然后: ```bash # 一键后台启动全部服务(-d = detach 后台运行) docker compose up -d # 看看当前这套服务跑了哪些容器 docker compose ps # 想看实时日志(-f 跟随) docker compose logs -f ``` `up -d` 之后,`docker compose ps` 应能看到 3 个容器都是 `running`。这时它们已经「部署起来了」,虽然啥都不做。 **停止 / 重启 / 销毁**——这是四个容易混的命令: | 命令 | 作用 | 容器还在吗 | 网络还在吗 | |------|------|-----------|-----------| | `docker compose stop` | 停止容器(发 SIGTERM) | 在(可重启) | 在 | | `docker compose start` | 把刚才 stop 的容器重新启动 | — | 在 | | `docker compose down` | 停止并**删除**容器和网络 | 删除 | 删除 | | `docker compose restart` | 重启容器(配置不变) | 在 | 在 | 记忆口诀: - **`stop` / `start`** 是「关机 / 开机」,容器本体保留,下次 `start` 还在。 - **`down`** 是「连锅端」,容器、网络都没了,只留 `docker-compose.yml` 文件,下次 `up -d` 重新创建。 - **`up -d`** 是幂等的:配置没改就啥也不做,改了就只重建变动的部分。 > 平时开发:改完配置 `docker compose up -d` 重新应用;完全不要了 `docker compose down`。
### 第三步:进容器看看,验证互通 容器起来了,怎么进去看?怎么验证它们确实在同一个网络、能互相访问? ```bash # 进入 app1 容器执行 sh(开一个交互式终端) docker compose exec app1 sh # 进入后,在 app1 里 ping app2 —— 直接用「服务名」做主机名 / # ping -c 3 app2 PING app2 (192.168.x.x): 56 data bytes # 能通说明同网可达 / # exit # 不进容器,直接执行一条命令也行 docker compose exec app3 ping -c 2 app1 # 看看 compose 这套服务里各容器的进程 docker compose top ``` 几个常用排查命令: ```bash # 看某个服务(比如 app2)的日志 docker compose logs app2 # 容器没起来 / 一直退出,先看日志和状态 docker compose ps -a docker compose logs app1 # 列出 compose 自动建的网络 docker network ls | grep mynet ``` 关键点:**同一网络里的容器用「服务名」互相访问**,这是 compose 相比手工 `docker run` 最大省心的地方——不用记 IP、不用每次 `--link`。等你把 busybox 换成 nginx + mysql,`web` 容器里直接 `mysql -h db` 就能连上数据库,靠的就是这个机制。
### 进阶:启动顺序与服务依赖 真实应用里,数据库没起来,Web 容器先起也没意义。compose 用 `depends_on` 表达**启动顺序**: ```yaml services: web: image: nginx:latest depends_on: - db # compose 会先启动 db,再启动 web ports: - "8080:80" db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: "123456" ``` 注意 `depends_on` 只保证**启动顺序**,不保证 db「已经能接受连接」。要等 db 真正就绪,加 `healthcheck` + `condition`: ```yaml services: web: image: nginx:latest depends_on: db: condition: service_healthy # 等 db 健康检查通过才起 web db: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: "123456" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 5s timeout: 3s retries: 10 ``` 其它常用字段速查: - **`volumes`**:挂载目录 / 数据卷,做持久化(容器删了数据还在)。例如 `volumes: ["./data:/var/lib/mysql"]`。 - **`ports`**:端口映射,格式 `"宿主机端口:容器端口"`,如 `"8080:80"`。 - **`environment`**:环境变量,注入配置(密码、开关)。 - **`restart`**:重启策略,`restart: always` 让容器挂了自动拉起。 - **`build`**:不想用现成镜像时,指向一个 Dockerfile 目录,compose 会自动构建。 到此你已经掌握 compose 的主干:**写 yml → `up -d` 起 → `down` 销毁**,其余字段都是围绕这套骨架加细节。
参考