Shell脚本如何动态修改容器端口映射

wen 实用脚本 2

本文目录导读:

Shell脚本如何动态修改容器端口映射

  1. 使用 Docker API(推荐)
  2. 使用 iptables 动态修改(不重启容器)
  3. 使用 docker-compose 动态更新
  4. 高级脚本:完整端口管理工具
  5. 使用 Python + Docker SDK
  6. 使用建议

在 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])

使用建议

  1. 生产环境推荐:使用 Docker API 或 docker-compose 方式,避免直接修改 iptables

  2. 测试环境:可以使用 iptables 方式快速调试

  3. 数据持久化:确保使用数据卷存储重要数据,避免重建容器时丢失

  4. 安全性

    # 检查 iptables 规则
    iptables-save > /tmp/iptables_backup.txt
    # 还原 iptables 规则
    iptables-restore < /tmp/iptables_backup.txt

选择哪种方法取决于你的具体需求和环境限制。

抱歉,评论功能暂时关闭!