很多时候,我们需要给每个 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"