容器互联 #

容器互联概述 #

为什么需要容器互联? #

text
┌─────────────────────────────────────────────────────┐
│                   容器互联需求                       │
├─────────────────────────────────────────────────────┤
│                                                     │
│  ┌─────────┐     ┌─────────┐     ┌─────────┐       │
│  │   Web   │────→│   API   │────→│Database │       │
│  └─────────┘     └─────────┘     └─────────┘       │
│                                                     │
│  需要解决:                                          │
│  - 容器如何发现彼此                                 │
│  - 容器如何通信                                     │
│  - 如何处理容器IP变化                               │
│  - 如何实现负载均衡                                 │
│                                                     │
└─────────────────────────────────────────────────────┘

容器互联方式 #

方式一:使用–link(已弃用) #

bash
# 创建数据库容器
docker run -d --name db mysql:8.0

# 使用--link连接
docker run -d --name web --link db:database nginx

# 在web容器中可以通过database访问db
docker exec web ping database

注意: --link已被弃用,推荐使用自定义网络。

方式二:使用自定义网络(推荐) #

bash
# 创建网络
docker network create mynetwork

# 运行容器
docker run -d --name db --network mynetwork mysql:8.0
docker run -d --name web --network mynetwork nginx

# 通过容器名通信
docker exec web ping db

方式对比 #

text
┌─────────────────────────────────────────────────────┐
│              --link vs 自定义网络                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  --link:                                            │
│  ✗ 已弃用                                           │
│  ✗ 只能单向连接                                     │
│  ✗ 需要重启容器更新                                 │
│  ✗ 不支持动态管理                                   │
│                                                     │
│  自定义网络:                                        │
│  ✓ 推荐方式                                         │
│  ✓ 双向通信                                         │
│  ✓ 动态连接/断开                                    │
│  ✓ 自动DNS解析                                      │
│  ✓ 支持网络别名                                     │
│                                                     │
└─────────────────────────────────────────────────────┘

服务发现 #

DNS解析 #

bash
# 创建网络
docker network create app-network

# 运行服务
docker run -d --name db --network app-network mysql:8.0
docker run -d --name redis --network app-network redis:alpine
docker run -d --name app --network app-network myapp

# 容器内DNS解析
docker exec app nslookup db
docker exec app nslookup redis

# 通过服务名连接
docker exec app ping -c 3 db
docker exec app ping -c 3 redis

网络别名 #

bash
# 设置网络别名
docker run -d \
  --name db \
  --network app-network \
  --network-alias mysql \
  --network-alias database \
  mysql:8.0

# 通过任意别名访问
docker run --rm --network app-network alpine ping mysql
docker run --rm --network app-network alpine ping database
docker run --rm --network app-network alpine ping db

Docker Compose服务发现 #

yaml
version: '3.8'

services:
  web:
    image: nginx
    networks:
      - frontend
    depends_on:
      - app

  app:
    image: myapp
    networks:
      - frontend
      - backend
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis

  db:
    image: mysql:8.0
    networks:
      - backend

  redis:
    image: redis:alpine
    networks:
      - backend

networks:
  frontend:
  backend:

容器间通信 #

HTTP通信 #

bash
# 创建网络
docker network create web-network

# 运行API服务
docker run -d \
  --name api \
  --network web-network \
  -p 8000:8000 \
  myapi

# 运行Web服务
docker run -d \
  --name web \
  --network web-network \
  -e API_URL=http://api:8000 \
  myweb

# Web可以通过http://api:8000访问API

数据库连接 #

yaml
# docker-compose.yml
version: '3.8'

services:
  app:
    image: myapp
    environment:
      - DB_HOST=db
      - DB_PORT=3306
      - DB_NAME=mydb
      - DB_USER=root
      - DB_PASSWORD=root
    depends_on:
      - db

  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=mydb

Redis连接 #

yaml
version: '3.8'

services:
  app:
    image: myapp
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      - redis

  redis:
    image: redis:alpine

负载均衡 #

使用Nginx负载均衡 #

yaml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app1
      - app2
      - app3

  app1:
    image: myapp
    networks:
      - app-network

  app2:
    image: myapp
    networks:
      - app-network

  app3:
    image: myapp
    networks:
      - app-network

networks:
  app-network:

nginx.conf配置 #

nginx
upstream backend {
    server app1:8000;
    server app2:8000;
    server app3:8000;
}

server {
    listen 80;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Docker Swarm负载均衡 #

bash
# 创建服务并扩展
docker service create \
  --name web \
  --replicas 3 \
  --publish 80:80 \
  nginx

# Swarm自动负载均衡

服务依赖 #

depends_on #

yaml
version: '3.8'

services:
  web:
    image: nginx
    depends_on:
      - app
      - db

  app:
    image: myapp
    depends_on:
      - db

  db:
    image: mysql:8.0

健康检查依赖 #

yaml
version: '3.8'

services:
  web:
    image: nginx
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mysql:8.0
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

等待脚本 #

bash
#!/bin/bash
# wait-for.sh

host="$1"
shift
cmd="$@"

until nc -z "$host" 3306; do
  echo "Waiting for $host..."
  sleep 1
done

echo "$host is available"
exec $cmd
yaml
# docker-compose.yml
services:
  app:
    image: myapp
    command: ["./wait-for.sh", "db:3306", "npm", "start"]
    depends_on:
      - db

网络安全 #

网络隔离 #

yaml
version: '3.8'

services:
  web:
    image: nginx
    networks:
      - frontend
    ports:
      - "80:80"

  app:
    image: myapp
    networks:
      - frontend
      - backend

  db:
    image: mysql:8.0
    networks:
      - backend

networks:
  frontend:
  backend:
    internal: true  # 隔离外部访问

访问控制 #

bash
# 创建隔离网络
docker network create --internal isolated-network

# 只有特定容器可以访问
docker run -d --name db --network isolated-network mysql
docker run -d --name app --network isolated-network myapp

调试技巧 #

测试连通性 #

bash
# 使用ping测试
docker exec app ping -c 3 db

# 使用curl测试HTTP
docker exec app curl -I http://api:8000

# 使用nc测试端口
docker exec app nc -zv db 3306

# 使用telnet测试
docker exec app telnet db 3306

查看DNS解析 #

bash
# 查看DNS配置
docker exec app cat /etc/resolv.conf

# 使用nslookup
docker exec app nslookup db

# 使用dig
docker exec app dig db

抓包分析 #

bash
# 在容器内抓包
docker exec app tcpdump -i eth0 port 3306

# 使用netshoot容器
docker run --rm --net container:app nicolaka/netshoot tcpdump -i eth0

小结 #

本节学习了Docker容器互联的方法:

  • 容器互联的两种方式
  • 服务发现和DNS解析
  • 容器间通信配置
  • 负载均衡实现
  • 服务依赖管理
  • 网络安全配置

下一步 #

接下来,让我们学习 Compose基础,了解Docker Compose的基本使用。