如何修改正在运行的 Docker 容器端口映射?这是从中文网络世界中搜集到的一道真实的大厂面试题。下面通过集中情况来讨论如何实现这个问题。

容器未启动?

如果你的容器还没有构建时,想添加端口映射时,你只需要在创建容器的时候添加 -p 参数,想添加几个端口映射就追加几个 -p 参数。类似于如下是启动elasticsearch实例的示例:

docker run --name elasticsearch -d \
-p 9200:9200 \
-p 9300:9300 \
-e "discovery.type=single-node" \
docker.elastic.co/elasticsearch/elasticsearch:7.10.1
docker容器暴露端口的形式有四种:
参数 说明
-P(大写P) 将指定的容器端口映射到宿主机所有地址的一个随机端口
-p : 将容器端口映射到指定的主机端口
-P ::(大写P) 将容器端口映射到主机指定ip的随机端口
-p :: 将容器端口映射到指定主机ip的指定端口

容器已启动?

方法一: 删除原有容器,重新建新容器

这个解决方案最为简单,把原来的容器删掉,重新建一个。当然这次不要忘记加上端口映射。

sean@sean-VMware-Virtual-Platform:~$ docker run -d -p 8080:80 --name webserver -v /data/web:/usr/share/nginx/html nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
c6a83fedfae6: Pull complete 
7f5898476db7: Pull complete 
45f552c78c31: Pull complete 
62a896bb4a21: Pull complete 
532b9a30583c: Pull complete 
41c49cbde6a6: Pull complete 
9da224fdd412: Pull complete 
35b039ba2bc5: Pull complete 
Digest: sha256:c04c18adc2a407740a397c8407c011fc6c90026a9b65cceddef7ae5484360158
Status: Downloaded newer image for nginx:alpine
258edd02bfcdf6c5a4ef554b8ee614bc7e7e4530384109ef1f3c37b1c972cfe2

优缺点:优点是简单快捷,在测试环境使用较多。缺点是如果是数据库镜像,那重新建一个又要重新配置一次,就会比较麻烦。

方法二:修改容器配置文件,重启docker服务

但是想修改或者添加端口时,先停止掉正在运行的容器。

以下内容都是以容器 id 为 258edd02bfcd 进行操作的,请务必将容器 id 换成你自己需要修改的容器 id。

docker ps
docker stop {容器的名称或者 id }  

比如:

sean@sean-VMware-Virtual-Platform:~$ docker stop 258edd02bfcd
258edd02bfcd
sean@sean-VMware-Virtual-Platform:~$ 

查看容器完整的 hash_of_the_container 数值:

docker inspect {容器的名称或者 id } | grep Id  

# 比如:  
sean@sean-VMware-Virtual-Platform:~$ docker inspect 258edd02bfcd|grep Id
        "Id": "258edd02bfcdf6c5a4ef554b8ee614bc7e7e4530384109ef1f3c37b1c972cfe2",

打开 hostconfig.json 配置文件

vim /var/lib/docker/containers/{hash_of_the_container}/hostconfig.json 

比如:

nano /var/lib/docker/containers/258edd02bfcdf6c5a4ef554b8ee614bc7e7e4530384109ef1f3c37b1c972cfe2/hostconfig.json  

如果你不想先查看完整的容器 hash_of_the_container 数值,你也可以直接先切换到当前容器相关目录中 cd /var/lib/docker/containers/{hash_of_the_container}* ,然后再去编辑 hostconfig.json 配置文件。

cd /var/lib/docker/containers/{hash_of_the_container}*  

比如

cd /var/lib/docker/containers/cbe26510c276*  

然后再去编辑 hostconfig.json 配置文件:

vim hostconfig.json  
修改 hostconfig.json 配置文件

在 hostconfig.json 配置文件中,找到 "PortBindings":{} 这个配置项,然后进行修改。这里的Nginx添加了将宿主机的 8080 端口映射到容器的 80 端口。

HostPort 对应的端口代表 宿主机 的端口。

建议容器使用什么端口,宿主机就映射什么端口,方便以后管理。当然,具体情况,具体分析。

"PortBindings":{
    "80/tcp":[
      {
        "HostIp":"","HostPort":"8080"
      }
    ]
}

只需要修改 "PortBindings": {} 中对应的"HostPort"的值,修改后的内容如下:

"PortBindings":{
    "80/tcp":[
      {
        "HostIp":"","HostPort":"8000"
      }
    ]
} 

最后重启 docker,然后查看容器相关配置信息是否已经修改完毕

systemctl restart docker  

查看容器相关配置信息:

docker inspect {容器的名称或者 id }  
# 比如:  
docker inspect 258edd02bfcd  

配置符合你的要求后,再次启动容器:

docker start {容器的名称或者 id }  
# 比如:  
docker start 258edd02bfcd  

优缺点:这个方法的优点是没有副作用,操作简单。缺点是需要重启整个docker服务,如果在同一个宿主机上运行着多个容器服务的话,就会影响其他容器服务。

方法三:利用docker commit新构镜像

docker commit:把一个容器的文件改动和配置信息commit到一个新的镜像。这个在测试的时候会非常有用,把容器所有的文件改动和配置信息导入成一个新的docker镜像,然后用这个新的镜像重起一个容器,这对之前的容器不会有任何影响。

停止docker容器

docker stop container01

commit该docker容器

docker commit container01 new_image:tag

用前一步新生成的镜像重新起一个容器

docker run --name container02 -p 80:80 new_image:tag

优缺点:这种方式的优点是不会影响统一宿主机上的其他容器,缺点是管理起来显得比较乱,没有第二种方法那么直观。