纲要引言微服务容器化的挑战与Docker Compose的定位容器网络模式none模式bridge桥接模式host模式容器间通信的三种方式宿主机端口绑定同一桥接网络下的 IP 直连--link与容器名解析实战手动部署go-zero微服务项目结构编写Dockerfile启动ETCD启动API与RPC服务常见问题与日志诊断使用Docker Compose统一编排安装Docker Compose编写docker-compose.yml自定义网络与固定 IP服务依赖与健康检查一键启动、停止与清理小结引言在微服务架构中一个应用通常由多个独立服务组成例如API网关、RPC服务、配置中心ETCD、缓存Redis、数据库MySQL等。如果每个服务都手动通过docker run启动并逐个管理网络、端口、依赖关系不仅效率低下而且极易出错。Docker Compose正是为解决这一问题而生——它允许通过一个 YAML 文件定义所有服务、网络和卷实现“一键编排”。本文将以go-zero框架生成的微服务为例从容器网络原理入手逐步演示如何手动部署多个容器并最终迁移到Docker Compose进行统一管理。文中的go-zero代码均基于go-zerolatest生成确保与最新版本兼容。容器网络模式理解Docker网络是进行多容器通信的前提。Docker提供三种基本网络模式none模式容器拥有独立的网络栈但Docker不会为其分配 IP、网卡等网络资源。此时容器完全隔离无法与外界通信适用于无需网络的特殊场景。bridge桥接模式默认Docker在宿主机上创建一个虚拟网桥通常为docker0所有未指定网络的容器默认接入该网桥。每个容器获得一个私有 IP宿主机通过 NAT 与容器通信。这是开发中最常用的模式。下图展示了桥接网络的工作原理NAT端口映射 8080:8080端口映射 6379:6379宿主机网卡 eth0docker0 虚拟网桥容器1: 172.17.0.2容器2: 172.17.0.3host模式容器直接使用宿主机的网络栈共享宿主机的 IP 和端口。这意味着容器内监听的端口会直接暴露在宿主机上省略了端口映射的步骤。但该模式容易与宿主机其他程序发生端口冲突不推荐在生产环境滥用。容器间通信的三种方式假设宿主机上运行了两个容器go-zero-api提供 HTTP 服务和redis缓存我们需要让它们互相访问。Host映射宿主机端口 8080API 容器Redis 容器方式一通过宿主机端口绑定访问将go-zero-api的 8080 端口映射到宿主机redis容器通过访问宿主机 IP 加端口的方式间接调用 API。示例# Redis 容器内执行curlhttp://192.168.1.100:8080/api/login该方式简单直接但调用方必须知道宿主机的 IP 和映射端口耦合度较高。方式二基于同一桥接网络 IP 直连当两个容器处于同一个bridge网络时可以直接使用对方的私有 IP 进行通信。查看网络信息dockernetwork inspect bridge输出会显示每个容器分配的 IP例如go-zero-api为172.17.0.2redis为172.17.0.3。在redis容器中直接访问curlhttp://172.17.0.2:8080/api/login这种方式依赖固定 IP但在容器重启后 IP 可能发生变化。方式三通过--link实现容器名解析在启动容器时使用--link参数可以将目标容器的名称或 ID 注入到当前容器的/etc/hosts中从而实现“通过容器名访问”。例如先启动redis再启动api并链接到redisdockerrun-d--nameredis redis:alpinedockerrun-d-p8080:8080--namego-zero-api--linkredis go-zero-api:latest此时在go-zero-api容器内部可以直接ping redis并解析到redis容器的 IP。注意--link是单向的且官方已推荐使用自定义网络替代。实战手动部署 go-zero 微服务假设我们已经用go-zerolatest生成了一个简单的微服务项目包含api服务和rpc服务它们都依赖ETCD作为服务注册中心。项目结构概览demo/ ├── api/ │ ├── Dockerfile │ └── api.go ├── rpc/ │ ├── Dockerfile │ └── rpc.go └── etcd/ └── Dockerfile为了使部署简单化我们只保留核心通信逻辑api通过ETCD发现rpc服务并调用它。为了纯容器化暂时注释掉数据库等非必要依赖。编写 API 服务的DockerfileFROM golang:1.21-alpine AS builder WORKDIR /app COPY . . RUN go build -o api-server api.go FROM alpine:latest WORKDIR /root/ COPY --frombuilder /app/api-server . COPY etc/api.yaml . EXPOSE 8080 CMD [./api-server]编写 RPC 服务的Dockerfile与 API 类似仅服务名不同FROM golang:1.21-alpine AS builder WORKDIR /app COPY . . RUN go build -o rpc-server rpc.go FROM alpine:latest WORKDIR /root/ COPY --frombuilder /app/rpc-server . COPY etc/rpc.yaml . EXPOSE 8081 CMD [./rpc-server]构建镜像dockerbuild-tgo-zero-api-fapi/Dockerfile.dockerbuild-tgo-zero-rpc-frpc/Dockerfile.启动 ETCD 容器首先确保ETCD正常运行因为它是服务注册发现的基础。dockerbuild-tetcd-fetcd/Dockerfile.dockerrun-d--nameetcd\-p2379:2379-p2380:2380\--networkmynet--ip192.168.10.20\etcd这里我们事先创建了一个自定义网络mynetdockernetwork create--subnet192.168.10.0/24 mynet启动 RPC 服务并指定 ETCD 地址dockerrun-d--namego-zero-rpc\--networkmynet\-eETCD_HOST192.168.10.20:2379\go-zero-rpc启动 API 服务dockerrun-d--namego-zero-api\-p8080:8080\--networkmynet\-eETCD_HOST192.168.10.20:2379\go-zero-api如果容器启动后立即退出或调用时报错务必查看日志定位问题dockerlogs go-zero-api常见错误包括ETCD地址配置错误、端口冲突或环境变量未正确传递。通过日志可以快速发现究竟是连接被拒绝还是配置项缺失。使用 Docker Compose 统一编排手动运行虽能工作但每次修改配置都需要停止容器、删除、重新构建镜像、再启动过程繁琐。Docker Compose允许将上述所有步骤固化在一个 YAML 文件中。安装 Docker Compose以 Linux 为例sudocurl-Lhttps://github.com/docker/compose/releases/latest/download/docker-compose-$(uname-s)-$(uname-m)-o/usr/local/bin/docker-composesudochmodx /usr/local/bin/docker-composedocker-compose--version编写docker-compose.yml在项目根目录创建该文件内容如下根据实际环境调整版本号和镜像名称version:3.8networks:go-zero-net:driver:bridgeipam:config:-subnet:192.168.10.0/24services:etcd:image:bitnami/etcd:latestcontainer_name:etcdports:-2379:2379-2380:2380networks:go-zero-net:ipv4_address:192.168.10.20environment:-ALLOW_NONE_AUTHENTICATIONyesvolumes:-./etcd/data:/bitnami/etcdgo-zero-rpc:build:context:./rpcdockerfile:Dockerfileimage:go-zero-rpc:latestcontainer_name:go-zero-rpcnetworks:-go-zero-netenvironment:-ETCD_HOST192.168.10.20:2379depends_on:-etcdgo-zero-api:build:context:./apidockerfile:Dockerfileimage:go-zero-api:latestcontainer_name:go-zero-apiports:-8080:8080networks:-go-zero-netenvironment:-ETCD_HOST192.168.10.20:2379depends_on:-etcd-go-zero-rpc上述配置做了几件重要的事自定义网络go-zero-net使用固定的子网并为etcd分配了静态 IPrpc和api会自动分配剩余地址。服务依赖depends_on确保etcd先于其他服务启动。但对于需要“等待服务就绪”的场景还需配合healthcheck这里简单起见未展开。构建策略若本地不存在对应镜像docker-compose up会自动根据build指令构建镜像避免手动执行docker build。数据卷etcd的数据目录映射到宿主机防止数据丢失。启动所有服务docker-composeup-d该命令会以后台方式启动所有定义的服务。使用docker-compose ps可以查看状态。查看服务日志docker-composelogs-fgo-zero-api停止并清理所有资源docker-composedown这条命令会停止容器、删除容器同时移除自动创建的网络彻底还原环境。小结本文从Docker网络的基础原理出发介绍了容器通信的三种模式并以go-zero微服务为例演示了从手动部署到Docker Compose统一编排的演进过程。Docker Compose通过声明式的配置文件极大简化了多容器应用的管理特别是对于服务依赖、网络划分、环境变量注入等高频操作它提供了清晰且可复用的解决方案。在实际开发中建议将docker-compose.yml与源代码一同纳入版本控制这样任何团队成员都能一键拉起完整的开发环境。后续还可以结合Docker Swarm或Kubernetes进行生产级编排但Docker Compose依然是本地开发和小规模部署的首选工具。