1. 概述

当我们需要从宿主机访问 Docker 容器提供的服务时,就需要配置端口映射(Port Mapping)。有时候,我们可能在启动容器时忘记映射某个后续需要用到的端口。

在本文中,我们将探讨 Docker 中端口映射的重要性,并介绍几种在容器启动后添加新端口映射的方法。

2. 为什么使用端口映射

端口映射用于从宿主机访问 Docker 容器内部运行的服务。我们通过将宿主机的一个端口与容器内的某个端口进行绑定,使对宿主机端口的请求被转发到容器中。

端口映射使得容器内部的服务可以从外部访问。

启动一个新的 Docker 容器时,可以在 docker run 命令中使用 -p 参数来配置端口映射:

$ docker run -d -p 81:80 --name httpd-container httpd

上面的命令会启动一个名为 httpd-container 的容器,并将宿主机的 81 端口映射到容器的 80 端口。

httpd 默认监听 80 端口,因此我们可以通过宿主机的 81 端口访问它:

$ curl http://localhost:81
<html><body><h1>It works!</h1></body></html>

并非所有容器都必须配置端口映射。 有时我们为了安全或隔离性,不对外暴露容器服务,只允许同一 Docker 网络中的其他容器访问。

3. 为运行中的容器添加端口映射的几种方式

假设我们在启动容器时忘了配置端口映射,这时就无法通过宿主机的 TCP/IP 协议访问该容器的服务。

解决这个问题主要有三种方式:

✅ 重新启动容器:

  • 停止当前容器
  • 使用原镜像重新启动,并加上新的端口映射

✅ 提交容器为新镜像后重启:

  • 将当前容器提交为镜像
  • 使用新镜像启动容器,并配置端口映射

✅ 修改容器配置文件:

  • 停止容器和 Docker 服务
  • 修改容器的 hostconfig.jsonconfig.v2.json 文件
  • 重启 Docker 服务并启动容器

接下来我们逐一介绍这几种方法。

4. 重新启动容器

最直接的方式是删除当前容器并重新启动一个新容器,使用原始镜像并添加端口映射。

这是最简单、最安全的做法,但不适用于已有大量配置或数据改动的容器

比如我们启动了一个容器后,进行了若干操作(如安装软件、修改配置等),此时再重新启动原镜像会导致这些改动丢失。

这时候就需要使用第二种方法。

5. 提交容器为镜像后重启

与其从零开始启动新容器,我们可以将当前容器保存为一个新镜像,再用这个新镜像启动容器并配置端口映射。

这样可以保留原容器的状态和改动。

操作步骤如下:

  1. 停止容器并提交为新镜像:
$ docker stop httpd-container
httpd-container
$ docker commit httpd-container httpd-image
sha256:33da33fcad051c90ac9b7dea9b2dbda442767e05ddebd8d6db8ac6893ef4ef40
  1. 删除旧容器并用新镜像启动:
$ docker rm httpd-container
httpd-container
$ docker run -d -p 83:80 --name httpd-container httpd-image
dd2535c477ad74e80b3642abca9055efacb89eaf14572b91f91bf20cd3f0cbf3

现在,我们有了一个保留了原容器状态并配置了端口映射的新容器。

6. 动态修改 Docker 配置

前两种方式都涉及删除容器并重新创建,虽然功能一致,但容器 ID 和元数据会改变

如果我们希望保留容器 ID 和其他元数据,可以考虑直接修改容器的配置文件。

6.1. 停止容器和服务

首先,我们启动一个未配置端口映射的容器:

$ docker run -d --name httpd-container httpd 
a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7

查看容器状态:

$ docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED             STATUS          PORTS       NAMES
a0ed1c9fc60c   httpd     "httpd-foreground"   1 second ago        Up 1 second     80/tcp      httpd-container

获取容器完整 ID:

$ docker inspect --format="{{.Id}}" httpd-container
a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7

然后停止容器和 Docker 服务:

$ docker stop httpd-container
httpd-container

$ systemctl stop docker

⚠️ 注意:停止 Docker 服务会停止所有容器,操作完成后记得重启。

6.2. 找到配置文件

Docker 容器的配置文件位于:

/var/lib/docker/containers/<容器ID>/

对于本例,路径为:

/var/lib/docker/containers/a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7/

我们重点关注两个文件:

  • hostconfig.json:包含端口绑定信息
  • config.v2.json:包含容器的配置信息,包括暴露的端口

6.3. 修改配置文件

首先,编辑 hostconfig.json,找到 PortBindings 字段:

{
  ...
  "PortBindings": {},
  ...
}

将其修改为:

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

接着,编辑 config.v2.json,确保 Config 节点下包含 ExposedPorts 字段:

{
  "Config": {
    ...
    "ExposedPorts": {
      "80/tcp": {}
    },
    ...
  }
}

如果需要暴露多个端口,可以写成:

{
  "Config": {
    ...
    "ExposedPorts": {
      "80/tcp": {},
      "82/tcp": {},
      "8080/tcp": {}
    },
    ...
  }
}

6.4. 验证修改

重启 Docker 服务并启动容器:

$ systemctl start docker
$ docker start httpd-container

查看端口映射是否生效:

$ docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED             STATUS          PORTS                               NAMES
a0ed1c9fc60c   httpd     "httpd-foreground"   1 hours ago         Up 1 seconds    0.0.0.0:82->80/tcp, :::82->80/tcp   httpd-container

测试访问:

$ curl http://localhost:82
<html><body><h1>It works!</h1></body></html>

✅ 成功!端口映射已更新,且容器 ID 和元数据保持不变。

6.5. 修改已有端口映射

如果要修改已有端口映射,也可以用相同方式:

  • 修改 hostconfig.json 中的 PortBindings
  • 如果修改了容器监听的端口,记得在 config.v2.json 中更新 ExposedPorts
  • 重启 Docker 服务和容器

7. 总结

本文介绍了三种为已启动的 Docker 容器添加或修改端口映射的方法:

方法 是否保留容器状态 是否保留容器 ID 推荐场景
重新启动容器 简单、干净,适用于无状态容器
提交为镜像后重启 容器有状态改动时推荐
修改配置文件 需保留容器 ID 和元数据时使用

选择哪种方式取决于你的具体需求。如果你只是想快速解决问题,建议使用前两种方法;如果必须保留容器 ID,可以使用第三种方法。


💡 踩坑提醒:修改配置文件方式虽然保留了容器元数据,但操作过程较为复杂,容易出错,建议只在必要时使用。同时操作前务必备份配置文件,避免误操作导致容器无法启动。


原始标题:Assigning a Port Mapping to an Existing Docker Container