本文目录导读:

- 使用 Docker API(推荐)
- 使用 iptables 动态修改(不重启容器)
- 使用 docker-compose 动态更新
- 高级脚本:完整端口管理工具
- 使用 Python + Docker SDK
- 使用建议
在 Shell 脚本中动态修改容器端口映射,主要有以下几种方法:
使用 Docker API(推荐)
通过更新容器配置
#!/bin/bash
# 方法1:重新创建容器(会丢失数据,除非使用卷)
update_port_mapping() {
local container_name="$1"
local old_port="$2"
local new_port="$3"
# 停止容器
docker stop "$container_name" 2>/dev/null
# 获取容器配置(不包括端口映射)
local config=$(docker inspect "$container_name" --format '{{json .Config}}' | \
jq 'del(.ExposedPorts)' | \
jq 'del(.PortSpecs)')
# 重新创建容器
docker rm "$container_name" 2>/dev/null
docker run -d \
--name "$container_name" \
-p "${new_port}:${old_port}" \
$(echo "$config" | jq -r '.Image') \
$(echo "$config" | jq -r '.Cmd[]' 2>/dev/null | xargs)
}
# 使用示例
update_port_mapping "myapp" 8080 9090
使用 iptables 动态修改(不重启容器)
#!/bin/bash
# 方法2:直接修改 iptables 规则(不重启容器)
modify_iptables_port() {
local container_ip="$1" # 容器IP地址
local old_port="$2"
local new_port="$3"
# 查找并删除旧的 DNAT 规则
iptables -t nat -L DOCKER --line-numbers -n | \
grep "${container_ip}:${old_port}" | \
awk '{print $1}' | \
sort -r | \
while read line; do
iptables -t nat -D DOCKER "$line"
done
# 添加新的 DNAT 规则
iptables -t nat -A DOCKER \
-p tcp --dport "${new_port}" \
-j DNAT --to-destination "${container_ip}:${old_port}"
# 更新防火墙规则
iptables -D DOCKER -p tcp --dport "${old_port}" -j ACCEPT 2>/dev/null
iptables -A DOCKER -p tcp --dport "${new_port}" -j ACCEPT
}
# 使用示例
container_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp)
modify_iptables_port "$container_ip" 8080 9090
使用 docker-compose 动态更新
#!/bin/bash
# 方法3:使用 docker-compose 重新部署
update_compose_port() {
local compose_file="$1"
local service_name="$2"
local old_port="$3"
local new_port="$4"
# 备份原文件
cp "$compose_file" "${compose_file}.bak"
# 修改端口映射
sed -i "s/${old_port}:${new_port}/${new_port}:${old_port}/g" "$compose_file"
# 重新创建服务
docker-compose -f "$compose_file" up -d "$service_name"
}
# 使用示例
update_compose_port "docker-compose.yml" "web" 8080 9090
高级脚本:完整端口管理工具
#!/bin/bash
# 完整的端口管理脚本
PORT_MANAGER_SCRIPT="/tmp/docker_port_manager.sh"
cat > "$PORT_MANAGER_SCRIPT" << 'SCRIPT'
#!/bin/bash
# 函数:显示当前端口映射
show_ports() {
local container="$1"
echo "当前容器 $container 的端口映射:"
docker port "$container"
}
# 函数:添加端口映射(不重启)
add_port_mapping() {
local container="$1"
local host_port="$2"
local container_port="$3"
# 获取容器网络设置
local network_mode=$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$container")
if [ "$network_mode" != "bridge" ]; then
echo "错误:仅支持 bridge 网络模式的容器"
return 1
fi
# 获取容器IP
local container_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
if [ -z "$container_ip" ]; then
echo "错误:无法获取容器IP"
return 1
fi
echo "添加端口映射:主机 $host_port -> 容器 $container_ip:$container_port"
# 添加 iptables 规则
iptables -t nat -A DOCKER \
-p tcp --dport "$host_port" \
-j DNAT --to-destination "${container_ip}:${container_port}"
iptables -A DOCKER \
-p tcp --dport "$host_port" \
-j ACCEPT
echo "端口映射添加成功"
}
# 函数:删除端口映射
remove_port_mapping() {
local container="$1"
local host_port="$2"
local container_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
if [ -z "$container_ip" ]; then
echo "错误:无法获取容器IP"
return 1
fi
echo "删除端口映射:主机端口 $host_port"
# 查找并删除对应的 iptables 规则
iptables -t nat -L DOCKER --line-numbers -n | \
grep "${host_port}" | \
grep "${container_ip}" | \
awk '{print $1}' | \
sort -r | \
while read line; do
iptables -t nat -D DOCKER "$line"
done
# 删除 ACCEPT 规则
iptables -D DOCKER -p tcp --dport "${host_port}" -j ACCEPT 2>/dev/null
echo "端口映射删除成功"
}
# 主逻辑
case "$1" in
show)
show_ports "$2"
;;
add)
add_port_mapping "$2" "$3" "$4"
;;
remove)
remove_port_mapping "$2" "$3"
;;
*)
echo "用法:$0 {show|add|remove} [参数]"
echo "示例:"
echo " $0 show container_name"
echo " $0 add container_name 8080 80"
echo " $0 remove container_name 8080"
;;
esac
SCRIPT
chmod +x "$PORT_MANAGER_SCRIPT"
echo "端口管理脚本已创建:$PORT_MANAGER_SCRIPT"
echo "使用方法:"
echo "$PORT_MANAGER_SCRIPT show container_name"
echo "$PORT_MANAGER_SCRIPT add container_name 8080 80"
echo "$PORT_MANAGER_SCRIPT remove container_name 8080"
使用 Python + Docker SDK
#!/usr/bin/env python3
# 保存为 docker_port_update.py
import docker
import sys
def update_port_mapping(container_name, old_host_port, new_host_port):
"""使用 Docker SDK 更新端口映射"""
client = docker.from_env()
try:
container = client.containers.get(container_name)
# 停止容器
print(f"停止容器: {container_name}")
container.stop()
# 获取当前配置
container_config = container.attrs['Config']
host_config = container.attrs['HostConfig']
# 更新端口映射
ports = host_config.get('PortBindings', {})
for container_port in list(ports.keys()):
bindings = ports[container_port]
for binding in bindings:
if str(binding.get('HostPort')) == str(old_host_port):
binding['HostPort'] = str(new_host_port)
print(f"端口映射更新: {old_host_port}:{container_port} -> {new_host_port}:{container_port}")
# 重新创建容器
container.remove()
new_container = client.containers.run(
image=container_config['Image'],
name=container_name,
ports=ports,
detach=True
)
print(f"容器 {container_name} 已重新创建")
return True
except Exception as e:
print(f"错误: {e}")
return False
if __name__ == "__main__":
if len(sys.argv) != 4:
print("用法: python docker_port_update.py <容器名> <旧端口> <新端口>")
sys.exit(1)
update_port_mapping(sys.argv[1], sys.argv[2], sys.argv[3])
使用建议
-
生产环境推荐:使用 Docker API 或 docker-compose 方式,避免直接修改 iptables
-
测试环境:可以使用 iptables 方式快速调试
-
数据持久化:确保使用数据卷存储重要数据,避免重建容器时丢失
-
安全性:
# 检查 iptables 规则 iptables-save > /tmp/iptables_backup.txt # 还原 iptables 规则 iptables-restore < /tmp/iptables_backup.txt
选择哪种方法取决于你的具体需求和环境限制。