在 Docker Compose 中,服务之间的网络访问和与宿主机的交互是通过网络配置和端口映射实现的。以下是详细解释和实际示例:
一、服务间的相互访问
1. 默认网络行为
当使用 docker-compose up
启动服务时,Docker Compose 会自动创建一个默认网络(通常以项目名称为前缀),所有服务默认加入该网络,并通过服务名称(service name) 作为主机名互相访问。
示例配置:
version: "3.8"
services:
web:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- api
api:
image: my-api:latest
expose:
- "3000" # 仅在容器间暴露端口,不映射到宿主机
访问方式:
web
服务可以直接通过http://api:3000
访问api
服务(使用服务名称api
作为主机名)- 无需手动配置网络,Docker Compose 已自动处理
2. 自定义网络
如果需要更精细的控制(例如隔离部分服务),可以显式定义网络:
示例配置:
version: "3.8"
services:
web:
image: nginx:alpine
networks:
- frontend
ports:
- "80:80"
api:
image: my-api:latest
networks:
- frontend
- backend
expose:
- "3000"
database:
image: postgres:15
networks:
- backend
environment:
POSTGRES_PASSWORD: secret
networks:
frontend:
backend:
访问规则:
web
和api
在frontend
网络中,可以互相访问api
和database
在backend
网络中,可以互相访问web
无法直接访问database
(不在同一网络)
二、从宿主机访问服务
1. 通过端口映射(ports)
使用 ports
将容器端口映射到宿主机端口:
services:
web:
image: nginx:alpine
ports:
- "8080:80" # 宿主机的 8080 端口映射到容器的 80 端口
- 宿主机访问:
http://localhost:8080
或http://<宿主IP>:8080
- 其他容器访问:通过服务名
web:80
(无需端口映射)
2. 仅容器间访问(expose)
services:
api:
image: my-api:latest
expose:
- "3000" # 仅暴露给其他容器,宿主机无法直接访问
- 宿主机无法访问
api:3000
- 其他容器可通过
http://api:3000
访问
三、关键概念对比
配置项 | 作用范围 | 示例 | 典型用途 |
---|---|---|---|
ports | 宿主机 ↔ 容器 | - "80:80" | 允许外部访问服务 |
expose | 容器 ↔ 容器 | - "3000" | 内部服务间通信 |
networks | 容器间网络隔离 | 自定义网络名称 | 控制服务间通信权限 |
四、完整示例
version: "3.8"
services:
frontend:
image: nginx:alpine
ports:
- "80:80"
networks:
- public
backend:
image: node:18
expose:
- "3000"
networks:
- public
- private
environment:
DB_HOST: database
database:
image: postgres:15
networks:
- private
volumes:
- pg_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
networks:
public:
private:
volumes:
pg_data:
访问逻辑:
-
宿主机访问:
frontend
通过http://localhost:80
访问backend
和database
无法直接从宿主机访问(未配置ports
)
-
服务间访问:
frontend
可通过http://backend:3000
访问backend
backend
可通过postgres://database:5432
访问database
frontend
无法 直接访问database
(不在同一网络)
五、调试技巧
1. 查看容器IP
docker-compose exec <service_name> ip a
2. 测试容器间连通性
docker-compose exec frontend ping backend
3. 查看网络详情
docker network inspect <network_name>
六、常见问题
Q1:为什么服务间无法通过服务名访问?
- 确保服务在同一个网络
- 检查服务名称拼写(区分大小写)
- 确认目标服务已正确
expose
或ports
端口
Q2:如何从宿主机访问未映射端口的服务?
- 临时进入容器网络:
docker run --rm -it --network <project_name>_public nicolaka/netshoot curl http://backend:3000
Q3:如何让宿主机解析服务名称?
- 在 Docker Desktop 中可使用
host.docker.internal
作为宿主机别名 - Linux 环境下需手动配置路由或 DNS