开发的时候,给不同的 Docker 一个不同的出口网关,方便限制 Docker 公网 IP,保证更好的 AI 编程

很多时候,我们需要给每个 Docker 一个独立的 IP。
为何呢?因为这个 Docker 需要使用,比如 Codex。
那么我们可以这样创建我们的 docker-compose.yaml:

version: '3.6'               
services:
  linkeaseubuntu:
    image: linkease/desktop-ubuntu2-standard-amd64:latest
    hostname: linkeaseubuntu
    container_name: linkeaseubuntu
    working_dir: /projects/workspace-linkease-ubuntu
    #entrypoint: /projects/linkease-ubuntu-projects/docker/startup.sh
    privileged: true
    environment:
      - LANG=en_US.utf8
      - LANGUAGE=en_US:en
      - LC_ALL=en_US.utf8
      - START_DOCKER=false
      - PASSWORD=123456
    volumes:
      - "./data/projects:/projects"
      - "./data/home-linkeaseubuntu:/config"
      - "/var/run/docker.sock:/var/run/docker.sock"
    ports:
      - "3022:22"
      - "3001:3001"
      - "3002:3002"
      - "3003:3003"
    dns:
      - "223.5.5.5"
    devices: 
      - "/dev/fuse:/dev/fuse"
      - "/dev/dri:/dev/dri"
    security_opt: 
      - "apparmor=unconfined" 
    shm_size: 512m
    restart: always
    stdin_open: true
    tty: true
    networks:
      linkeaseubuntu_net:
        ipv4_address: 192.168.98.2
networks:
  linkeaseubuntu_net:
    ipam:
      driver: default
      config:
        - subnet: 192.168.98.0/24

然后我们可以这样设定 Docker 的网关,让我们这个 Docker 走到网关 192.168.3.2 这个地方去:

#!/bin/bash
set -e

# 配置变量
DOCKER_NETWORK="ycl-docker_linkeaseubuntu_net"
DOCKER_SUBNET="192.168.98.0/24"
CUSTOM_GATEWAY="192.168.3.2"
OUT_INTERFACE="enx68da73afc4a7"
ROUTE_TABLE_ID="201"
ROUTE_TABLE_NAME="docker_custom_route"

# 检查权限
if [ "$EUID" -ne 0 ]; then
    echo "请使用 sudo 运行此脚本"
    exit 1
fi

# 函数:检查命令是否存在
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# 函数:获取 Docker 网桥接口名称
get_docker_bridge_interface() {
    local network_name="$1"
    
    if ! command_exists docker; then
        echo "错误: Docker 未安装或未运行" >&2
        return 1
    fi

    # 方法1: 通过网络ID推断
    local network_id
    if network_id=$(docker network inspect "$network_name" --format '{{.Id}}' 2>/dev/null | cut -c1-12); then
        if [ -n "$network_id" ]; then
            echo "br-$network_id"
            return 0
        fi
    fi

    # 方法2: 通过IP路由查找
    local gateway_ip
    if gateway_ip=$(docker network inspect "$network_name" --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}' 2>/dev/null); then
        if [ -n "$gateway_ip" ]; then
            local bridge_if
            if bridge_if=$(ip route get "$gateway_ip" 2>/dev/null | grep -o "dev [^ ]*" | awk '{print $2}'); then
                if [ -n "$bridge_if" ] && [[ "$bridge_if" == br-* ]]; then
                    echo "$bridge_if"
                    return 0
                fi
            fi
        fi
    fi

    echo "" >&2
    return 1
}

# 函数:创建路由表配置
setup_route_table() {
    local table_id="$1"
    local table_name="$2"
    
    # 可能的 rt_tables 文件路径
    local rt_tables_paths=(
        "/etc/iproute2/rt_tables"
        "/usr/local/etc/iproute2/rt_tables"
    )
    
    local rt_file=""
    for path in "${rt_tables_paths[@]}"; do
        if [ -f "$path" ]; then
            rt_file="$path"
            break
        fi
    done
    
    if [ -z "$rt_file" ]; then
        echo "警告: 未找到 rt_tables 文件尝试创建..."
        rt_file="/etc/iproute2/rt_tables"
        mkdir -p "$(dirname "$rt_file")"
        touch "$rt_file"
    fi
    
    # 检查是否已存在
    if ! grep -q "^$table_id $table_name" "$rt_file" 2>/dev/null; then
        echo "添加路由表 $table_id $table_name 到 $rt_file"
        echo "$table_id $table_name" >> "$rt_file"
    else
        echo "路由表 $table_name 已存在"
    fi
}

# 函数:检查网络接口
check_interface() {
    local interface="$1"
    if ! ip link show "$interface" &>/dev/null; then
        echo "错误: 网络接口 $interface 不存在" >&2
        echo "可用的接口:" >&2
        ip -o link show | awk -F': ' '{print $2}' | grep -v lo
        return 1
    fi
    return 0
}

# 函数:检查网关可达性
check_gateway() {
    local gateway="$1"
    local interface="$2"
    
    if ping -c 1 -W 2 -I "$interface" "$gateway" &>/dev/null; then
        echo "网关 $gateway 可达"
        return 0
    else
        echo "警告: 网关 $gateway 不可达,但继续配置..." >&2
        return 1
    fi
}

# 主执行流程
echo "=== Docker 自定义网关配置脚本 ==="
echo "目标网络: $DOCKER_NETWORK"
echo "Docker 子网: $DOCKER_SUBNET"
echo "自定义网关: $CUSTOM_GATEWAY"
echo "出口接口: $OUT_INTERFACE"
echo "路由表: $ROUTE_TABLE_NAME ($ROUTE_TABLE_ID)"
echo

# 检查依赖
if ! command_exists ip; then
    echo "错误: iproute2 未安装" >&2
    exit 1
fi

if ! command_exists docker; then
    echo "错误: Docker 未安装" >&2
    exit 1
fi

# 检查网络接口
echo "检查网络接口..."
check_interface "$OUT_INTERFACE"

# 获取 Docker 网桥接口
echo "查找 Docker 网络接口..."
BRIDGE_INTERFACE=$(get_docker_bridge_interface "$DOCKER_NETWORK")

if [ -z "$BRIDGE_INTERFACE" ]; then
    echo "错误: 无法找到 Docker 网络 '$DOCKER_NETWORK' 的网桥接口" >&2
    echo "请检查:" >&2
    echo "1. Docker 服务是否运行: systemctl status docker" >&2
    echo "2. 网络是否存在: docker network ls" >&2
    echo "3. 网络详细信息: docker network inspect '$DOCKER_NETWORK'" >&2
    exit 1
fi

echo "使用网桥接口: $BRIDGE_INTERFACE"

# 检查网桥接口
check_interface "$BRIDGE_INTERFACE"

# 检查网关可达性
check_gateway "$CUSTOM_GATEWAY" "$OUT_INTERFACE"

# 设置路由表配置
echo "设置路由表..."
setup_route_table "$ROUTE_TABLE_ID" "$ROUTE_TABLE_NAME"

# 清理可能存在的旧规则
echo "清理旧规则..."
ip rule show | grep "from $DOCKER_SUBNET lookup $ROUTE_TABLE_NAME" | while read -r line; do
    echo "删除规则: $line"
    ip rule del ${line} 2>/dev/null || true
done

ip route flush table "$ROUTE_TABLE_NAME" 2>/dev/null || true

# 添加策略路由规则
echo "添加策略路由规则..."
if ! ip rule show | grep -q "from $DOCKER_SUBNET lookup $ROUTE_TABLE_NAME"; then
    ip rule add from "$DOCKER_SUBNET" table "$ROUTE_TABLE_NAME" pref 1000
    echo "已添加路由规则"
else
    echo "路由规则已存在,使用 replace 更新"
    ip rule del from "$DOCKER_SUBNET" table "$ROUTE_TABLE_NAME" 2>/dev/null || true
    ip rule add from "$DOCKER_SUBNET" table "$ROUTE_TABLE_NAME" pref 1000
fi

# 配置路由表
echo "配置路由表..."
ip route replace default via "$CUSTOM_GATEWAY" dev "$OUT_INTERFACE" table "$ROUTE_TABLE_NAME"
ip route replace "$DOCKER_SUBNET" dev "$BRIDGE_INTERFACE" scope link table "$ROUTE_TABLE_NAME" 2>/dev/null || true
ip route replace 192.168.9.0/24 dev "$OUT_INTERFACE" scope link table "$ROUTE_TABLE_NAME"

# 验证配置
echo -e "\n=== 验证配置 ==="
echo "1. 路由规则:"
ip rule show | grep "$ROUTE_TABLE_NAME" || echo "无相关路由规则"

echo -e "\n2. 路由表内容:"
ip route show table "$ROUTE_TABLE_NAME" || echo "路由表为空"

# 修正后的 Docker 网络信息获取部分
echo -e "\n3. Docker 网络信息:"
if docker network inspect "$DOCKER_NETWORK" &>/dev/null; then
    docker network inspect "$DOCKER_NETWORK" --format '名称: {{.Name}}
ID: {{.Id}}
驱动程序: {{.Driver}}'
    
    # 安全地获取子网和网关信息
    echo "子网和网关:"
    docker network inspect "$DOCKER_NETWORK" --format '{{range .IPAM.Config}}子网: {{.Subnet}}{{if .Gateway}} 网关: {{.Gateway}}{{end}}
{{else}}无IPAM配置{{end}}'
else
    echo "无法获取 Docker 网络信息"
fi

echo -e "检测到的网桥接口: $BRIDGE_INTERFACE"


1 个赞