docker固定IP容器构造

本文详细介绍了在CentOS7环境下安装并构建固定IP容器的过程,包括基于CentOS7安装docker、创建固定IP容器及遗留问题解决。重点在于通过shell命令实现docker容器的固定IP分配。

docker固定IP容器构造

本文将从安装docker开始计述,直到构建ambari平台,其间读者对某些命令不了解请自行查阅相关文档,大致内容如下:

  • 基于Centos7安装docker
  • 创建固定IP的容器
  • 遗留问题

安装docker

基于Centos7安装docker不需要使用amaza的repo,直接就可使用shell命令:

1.安装docker: yum install docker-io -y
(ps:在centos6.5上可能会出现这样的错误:
WARN[0000] You are running linux kernel version 2.6.32-431.el6.x86_64, which might be unstable running docker. Please upgrade your kernel to 3.8.0.
请升级内核,网上说的用命令:yum upgrade device-mapper-libs,是不行的,)

2.创建Dockerfile,内容如下:

    # 选择一个已有的os镜像作为基础,由于为做ambari进行hdp进行升级,这里我选用了centos6的环境作为容器环境;  
    FROM centos:centos6  

    # 镜像的作者  
    MAINTAINER moxuqiang "moxuqiang_dm@163.com"  

    # 安装openssh-server和sudo软件包,并且将sshd的UsePAM参数设置成no  
    RUN yum install -y openssh-server sudo  
    RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config  

    # 添加测试用户admin,密码admin,并且将此用户添加到sudoers里  
    RUN useradd admin  
    RUN echo "admin:admin" | chpasswd  
    RUN echo "admin   ALL=(ALL)       ALL" >> /etc/sudoers  

    # 下面这两句比较特殊,在centos6上必须要有,否则创建出来的容器sshd不能登录  
    RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key  
    RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key  

    # 启动sshd服务并且暴露22端口  
    RUN mkdir /var/run/sshd  
    EXPOSE 22  
    CMD ["/usr/sbin/sshd", "-D"] 

创建image: docker build -t centos6-ssh .
其中centos6-ssh是上面创建创镜像的名称,.表示Dockerfile路径;

创建可固定分配IP的容器

docker在创建容器的时候,如果不指定其网络格式,那么将默认使用桥接模式,但这种模式无法固定容器的IP地址,docker容器有四种网格模式,分别为:bridge,host,container,none;创建固定IP的容器必须使用none的模式;

创建容器命令

docker run -itd --net=none --privileged=true -v /home/docker/centos_1:/usr/hdp -v /home/docker/data/centos_1:/datas --hostname=hdp1.urun --name=centos_1 centos6-ssh

–privileged=true:这是因为在centos7才需要特别设置的,否则会出现挂载目无权限访问的情况;
-v: 从上面可以看到容器挂载了两个目录,也就是说挂载两个目录必须用两个-v来声明;
–hostname:声明容器主机名称;
–name:声明容器名称;

创建完容器后,为容器分配固定IP,使用如下脚本:

#!/bin/bash

resetIP()
{
    if [ `id -u` -ne 0 ];then
        echo '必须使用root权限'
        exit
    fi

    if [ $# != 2 ]; then
        echo "使用方法: $0 容器名字 IP"
        exit 1
    fi

    container_name=$1
    bind_ip=$2

    container_id=`docker inspect -f '{{.Id}}' $container_name 2> /dev/null`
    if [ ! $container_id ];then
        echo "容器不存在"
        exit 2
    fi
    bind_ip=`echo $bind_ip | egrep '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'`
    if [ ! $bind_ip ];then
        echo "IP地址格式不正确"
        exit 3
    fi

    container_minid=`echo $container_id | cut -c 1-10`
    container_netmask=`ip addr show docker0 | grep "inet\b" | awk '{print $2}' | cut -d / -f2`
    container_gw=`ip addr show docker0 | grep "inet\b" | awk '{print $2}' | cut -d / -f1`

    bridge_name="veth_$container_minid"
    container_ip=$bind_ip/$container_netmask
    pid=`docker inspect -f '{{.State.Pid}}' $container_name 2> /dev/null`
    if [ ! $pid ];then
        echo "获取容器$container_name的id失败"
        exit 4
    fi

    if [ ! -d /var/run/netns ];then
        mkdir -p /var/run/netns
    fi

    ln -sf /proc/$pid/ns/net /var/run/netns/$pid

    ip link add $bridge_name type veth peer name X
    brctl addif docker0 $bridge_name
    ip link set $bridge_name up
    ip link set X netns $pid
    ip netns exec $pid ip link set dev X name eth0
    ip netns exec $pid ip link set eth0 up
    ip netns exec $pid ip addr add $container_ip dev eth0
    ip netns exec $pid ip route add default via $container_gw

}

if [ $1 == "start"  ]; then
    for conf in  `cat host_ip.list` 
        do  
        name=`echo $conf |cut -d ":" -f 1`
        ip=`echo $conf |cut -d ":" -f 2`
        docker start $name
        resetIP $name $ip
        echo "start container:"$name "success in ip:"$ip
    done

elif [ $1 = "stop" ]; then
    for conf in  `cat host_ip.list`
        do
                name=`echo $conf |cut -d ":" -f 1`
                ip=`echo $conf |cut -d ":" -f 2`
                docker stop $name
                echo "stop container:"$name "success" 
    done
else
   echo "usage:container.sh start / stop"
fi

以上脚本需要配置容器:IP对,需要修改路径以及配置文件名称的请修改代码;

遗留问题

1.无法修改主机名称
2./etc/hosts的修改结果无法保存;

<think>我们面对的问题是:在.NET Core 8项目中,如何在Docker容器启动前获取容器内部对外端口映射和宿主机IP地址? 注意:用户强调的是“容器启动前”。然而,根据Docker的工作原理,在容器启动之前,我们无法获取到端口映射和IP地址,因为这些都是容器启动时由Docker守护进程动态分配的(除非我们指定了固定的端口)。但是,我们可以考虑在容器启动时(即应用程序启动过程中)获取这些信息。 实际上,有两种情况: 1. 在容器内部运行的应用程序,如何获取自己映射到宿主机的端口和宿主机IP? 2. 在容器启动之前(即构建镜像时)获取?这是不可能的,因为端口映射和网络配置是在容器运行时才确定的。 因此,我们只能考虑在容器启动后,应用程序启动时获取。 根据Docker的特性,我们可以通过以下方式: - 端口映射:在容器内部,应用程序可以通过环境变量或读取`/proc`文件系统获取端口映射信息(但Docker默认不会将映射信息通过环境变量注入到容器内部,除非使用特定的方式)。 - 宿主机IP:在容器内部,宿主机IP可以通过默认网关获得(因为宿主机是容器的网关)或者通过环境变量注入。 然而,值得注意的是,Docker官方并没有提供直接的方式让容器内的应用程序获取宿主机的IP地址和映射的端口。但我们可以通过一些变通方法。 方法一:使用环境变量手动注入 在运行容器时,我们可以通过`-e`参数或`environment`部分(在docker-compose中)设置环境变量,将宿主机IP和映射的端口传递进去。例如: docker run -e HOST_IP=192.168.1.100 -e HOST_PORT=8080 ... 方法二:在容器内部通过默认网关获取宿主机IP容器内,我们可以通过命令获取默认网关,这个网关就是宿主机的IP(在桥接网络模式下)。例如: host_ip=$(ip route | awk '/default/ { print $3 }') 方法三:获取映射的端口 在容器内部,应用程序可以读取环境变量`$PORT`(如果使用平台如Heroku会提供,但Docker默认不提供)。或者,我们可以通过Docker的“魔术”文件获取:`/proc/net/tcp`(但比较麻烦)。另外,在容器内部,应用程序监听的端口是固定的(比如80),但映射到宿主机的端口是随机的,那么容器内应用如何知道外部访问它的端口呢?实际上,容器内的应用通常不需要知道外部映射的端口,除非需要生成外部访问的URL。 如果确实需要,我们可以通过Docker的API来获取,但这样就需要容器内有访问Docker API的权限(不安全)。 方法四:在应用程序启动时,通过Docker的元数据服务(Docker在1.13+版本提供了元数据服务,但需要将宿主机的Docker socket挂载到容器内,同样有安全风险)。 鉴于上述,我们通常推荐方法一:在启动容器时通过环境变量注入宿主机IP和映射的端口(如果映射端口是随机的,那么就需要在启动脚本中动态获取并注入)。 然而,对于端口映射,如果我们使用的是docker run的随机端口映射(-P),那么宿主机的映射端口是在容器启动时由Docker随机分配的,我们可以在启动容器后通过`docker inspect`来获取,但在容器内部则无法直接通过环境变量获得(除非使用脚本在容器启动时查询并设置)。 但在docker-compose中,如果我们指定了端口映射,比如: ports: - "8080:80" 那么宿主机的端口就是8080,我们可以将其作为环境变量注入。 因此,针对用户的需求,我们可以这样设计: 步骤1:在docker-compose.yml中,将需要的信息通过环境变量传入容器 例如: environment: - HOST_IP=${HOST_IP} # 在宿主机的启动脚本中设置这个环境变量 - HOST_PORT=${HOST_PORT} # 同样,在宿主机启动容器前设置 但是这里的${HOST_PORT}需要是我们在docker-compose中映射的宿主机端口,我们可以固定写死,也可以使用变量。然而,如果端口映射是随机映射,那么我们在宿主机上无法提前知道端口号(除非在启动容器后获取并重新设置环境变量,这很复杂)。 步骤2:在应用程序(.NET Core 8)启动时,读取这些环境变量。 但是,如果用户不想在宿主机上设置这些环境变量,我们可以在容器内部通过脚本获取宿主机IP(通过默认网关)和映射端口(比较困难,因为容器内不知道外部映射的端口)。 关于映射端口的获取,有一个非官方的方法:在容器内部,我们可以通过`cat /proc/self/net/tcp`等文件分析出容器内端口对应的映射端口,但这个端口是宿主机上的内部映射端口(通常是高位的随机端口,而不是我们在docker-compose中指定的端口)。而且,如果我们在docker-compose中指定了端口映射,那么宿主机上的映射端口就是我们指定的端口,所以我们可以直接使用指定的端口(即我们在docker-compose中写死的那个端口号)作为环境变量传入。 因此,综合来看,最简单可靠的方式是: 1. 在docker-compose.yml中,我们为服务设置固定的端口映射(避免随机端口)。 2. 在docker-compose.yml中,使用环境变量部分来设置两个环境变量: environment: - HOST_PORT=8080 # 这里写死,和ports中的宿主机端口一致 - HOST_IP=host.docker.internal # 对于Windows和Mac的Docker,可以使用这个特殊域名指向宿主机,但在Linux上需要额外处理 注意:在Linux上,我们可以使用`host.docker.internal`这个主机名吗?默认是不支持的,但可以在运行容器时通过`--add-host`添加。在docker-compose.yml中,我们可以这样: extra_hosts: - "host.docker.internal:host-gateway" 这样,我们就可以在容器内部通过`host.docker.internal`来访问宿主机了。那么,我们就不需要知道宿主机的具体IP地址。 所以,我们可以这样配置: 在docker-compose.yml中: version: '3.8' services: webapp: image: your-dotnet8-image ports: - "8080:80" # 假设容器内监听80,宿主机端口8080 extra_hosts: - "host.docker.internal:host-gateway" environment: - HOST_PORT=8080 然后在.NET Core 8应用程序中,我们可以这样获取: string hostIp = "host.docker.internal"; // 直接使用这个域名 string hostPort = Environment.GetEnvironmentVariable("HOST_PORT") ?? "80"; // 默认80 这样,我们就可以构造外部访问的URL:$"http://{hostIp}:{hostPort}" 但是,如果用户需要获取实际的宿主机IP(比如在Linux网络中,可能需要用宿主机的真实IP),那么我们可以通过`extra_hosts`的方式将宿主机的IP映射到另一个主机名,但宿主机的IP在启动容器前可能是未知的(比如动态IP)。所以,我们更推荐使用`host.docker.internal`这个方案,因为它可以跨平台且不需要知道宿主机的具体IP。 总结:在容器启动前无法获取,但可以在容器启动时通过环境变量和额外的主机名配置来传递必要的信息。 因此,回答如下: </think>在 .NET Core 8 项目中,**无法在 Docker 容器启动前**获取容器对外端口映射和宿主机 IP,因为端口映射和网络配置是在容器启动时由 Docker 引擎动态分配的。但您可以通过以下方法在容器启动后(应用程序初始化阶段)获取这些信息: --- ### 解决方案:在容器启动后获取网络信息 #### 1. 获取宿主机 IP 地址 在容器内部,可通过默认网关推断宿主机 IP(宿主机通常作为容器的网关): ```csharp // .NET Core 8 代码 using System.Net.NetworkInformation; string GetHostIp() { var gateway = NetworkInterface .GetAllNetworkInterfaces() .Where(n => n.OperationalStatus == OperationalStatus.Up) .SelectMany(n => n.GetIPProperties().GatewayAddresses) .FirstOrDefault()?.Address?.ToString(); return gateway ?? "127.0.0.1"; // 网关通常是宿主机 IP } ``` #### 2. 获取对外端口映射 需通过 Docker SDK 或 Docker API 查询(需挂载 Docker Socket): ```csharp // .NET Core 8 代码(需安装 Docker.DotNet 包) using Docker.DotNet; async Task<int> GetMappedPort(string containerId, int internalPort) { using var client = new DockerClientConfiguration().CreateClient(); var inspect = await client.Containers.InspectContainerAsync(containerId); return inspect.NetworkSettings.Ports[$"{internalPort}/tcp"] ?.FirstOrDefault()?.HostPort ?? 0; } ``` --- ### 完整实现步骤 1. **配置 Docker Compose 挂载 Socket** ```yaml services: myapp: image: my-dotnet8-app volumes: - /var/run/docker.sock:/var/run/docker.sock # 挂载 Docker Socket ports: - "8080:80" # 映射示例 ``` 2. **在 .NET Core 8 启动代码中获取信息** ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // 获取宿主机 IP string hostIp = GetHostIp(); // 获取容器 ID (通过 /proc 文件系统) string containerId = File.ReadAllText("/proc/self/cgroup") .Split('\n') .FirstOrDefault(l => l.Contains("docker")) ?.Split('/').LastOrDefault() ?? string.Empty; // 查询端口映射 (需要容器 ID) int mappedPort = await GetMappedPort(containerId, 80); Console.WriteLine($"宿主机IP: {hostIp}, 映射端口: {mappedPort}"); ``` --- ### 关键注意事项 1. **安全性警告** 挂载 Docker Socket 会带来严重安全风险(容器获得宿主机 root 权限),仅限开发环境使用。生产环境应通过环境变量显式传递必要信息: ```yaml environment: - HOST_IP=192.168.1.100 - MAPPED_PORT=8080 ``` 2. **替代方案(无需 Docker SDK)** 通过容器元数据文件获取信息(Linux 系统): ```csharp // 获取宿主机 IP string hostIp = File.ReadAllLines("/etc/hosts") .LastOrDefault()?.Split('\t')[0] ?? ""; // 获取映射端口 (需安装 net-tools) // 使用 shell 命令: docker port <container-id> 80 ``` 3. **端口映射原理** Docker 端口映射实际通过 iptables 实现[^1],容器内无法直接感知外部映射,必须通过外部查询。 --- ### 生产环境推荐方案 ```mermaid graph TD A[Docker Compose] -->|注入环境变量| B[.NET Core 8 应用] C[启动脚本] -->|获取宿主机IP| A C -->|获取随机端口| A B -->|读取环境变量| D[获取 HOST_IP 和 PORT] ``` 启动脚本示例: ```bash #!/bin/bash # 启动前获取宿主机IP和随机端口 export HOST_IP=$(hostname -I | awk '{print $1}') export MAPPED_PORT=$(shuf -i 8000-9000 -n 1) docker-compose up -d ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值