Contents

将端口映射分配给正在运行的Docker容器

1. 概述

如果我们希望从我们的主机与 Docker 容器通信,那么我们需要一个端口映射。有时,我们可能会启动一个容器而没有映射我们稍后需要的端口。

在本教程中,我们将讨论 Docker 中端口映射的重要性。一旦启动 Docker 容器,我们将研究添加新端口映射的不同方法。

2. 为什么我们使用端口映射

端口映射用于访问在Docker 容器内运行的服务。我们打开一个主机端口,让我们可以访问 Docker 容器内相应的开放端口。然后,向主机端口发出的所有请求都可以重定向到 Docker 容器中。

端口映射使容器内的进程可以从外部访问。

在运行新的 Docker 容器时,我们可以使用*-p*选项在docker run 命令中分配端口映射:

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

上面的命令启动一个httpd容器并将主机的端口 81 映射到该容器内的端口 80。

默认情况下,httpd服务器侦听端口 80。所以现在,我们可以使用主机上的端口 81 访问应用程序:

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

**不必为所有 Docker 容器执行端口映射。**通常,我们会避免打开主机端口,以保持容器的服务私有或仅对同一 Docker 网络中的同级容器可见。

3. 将新端口映射分配给正在运行的容器的方法

让我们考虑一个在启动 Docker 容器时忘记进行端口映射的情况。我们无法从主机上的 TCP/IP 连接访问服务。 有三种方法可以解决这个问题:

  • 通过停止现有容器并使用相同的原始 Docker 映像重新启动一个新容器来重新开始
  • 提交现有容器并从提交的 Docker 映像重新启动一个新容器,保持我们尝试访问的容器的状态
  • 通过操作 Docker 配置文件添加新的端口映射

让我们深入研究这些解决方案。

4. 重新启动容器

这是一个简单的解决方案,我们删除正在运行的 Docker 容器并使用相同的 Docker 映像重新启动它。但这一次,我们添加了忘记添加到旧容器中的端口映射。

这是最简单、最直接的方法,但并不适合所有情况。考虑一个例子,我们启动 Docker 容器(没有任何端口映射)并在其中执行一些操作,比如安装一些包、启动进程、更新文件等。在所有这些更改之后,我们意识到缺少端口映射。现在,如果我们使用相同的旧 Docker 映像启动一个新的 Docker 容器,我们将丢失所有这些更改。

在这种情况下,下一个方法来救援。但是,我们应该注意,将Docker 卷 用于我们的重要数据以方便替换容器是一种很好的做法。

5. 从 Docker Commit 重新启动

我们可以提交旧的 Docker 容器来创建一个新的 Docker 镜像,并使用它来启动一个打开正确端口的新容器,而不是从零启动一个新容器。

由于我们提交的是旧的 Docker 容器,因此第一个容器的持久状态将在新启动的容器中可用。 让我们试试这个。首先,我们将停止 Docker 容器并使用docker commit 命令创建其镜像:

$ docker stop httpd-container
httpd-container
$ docker commit httpd-container httpd-image
sha256:33da33fcad051c90ac9b7dea9b2dbda442767e05ddebd8d6db8ac6893ef4ef40

接下来,我们将删除容器并使用我们刚刚创建的映像重新启动容器。这次我们需要确保在run命令中添加/更新端口映射:

$ docker rm httpd-container
httpd-container
$ docker run -d -p 83:80 --name httpd-container httpd-image
dd2535c477ad74e80b3642abca9055efacb89eaf14572b91f91bf20cd3f0cbf3

现在,我们有了一个带有正确端口映射的新容器,可以从前一个停止的地方开始。

6. 在 Flight 中重新配置 Docker

在我们目前讨论的两种方法中,我们删除了现有的 Docker 容器并启动了一个新容器。尽管容器的工作方式相同,但它的元数据与之前的完全不同

现在让我们看看如何改为修改现有的 Docker 容器。

首先,让我们运行一个没有任何端口映射的新 Docker 容器:

$ 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

docker run命令返回完整的 docker 容器ID,长度为 64 个字符。或者, 也可以使用docker inspect 命令找到此ID

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

稍后我们将使用该 ID 来查找 Docker 配置文件路径。

6.1. 停止 Docker 容器和 Docker 服务

重新配置正在运行的容器的第一步是停止它。这可以使用docker stop命令完成:

$ docker stop httpd-container
httpd-container

由于我们将更新 Docker 容器的配置文件,我们还需要停止 Docker 服务本身。我们应该注意,这将关闭所有 Docker 容器,直到我们完成

$ systemctl stop docker

6.2. 查找配置文件

所有与 Docker 容器、镜像、卷和网络相关的配置文件都可以在*/var/lib/docker*目录中找到。此目录在 Windows 和 macOS 的情况下会有所不同。

我们将主要关注两个 Docker 容器配置文件*——hostconfig.jsonconfig.v2.json*。这些文件存在于以下目录中:

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

这里,ID表示我们在第 6 节开始计算的完整 Docker 容器ID。它的值是:

a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7

因此,在我们的例子中,配置文件存在于以下目录中:

$ ls /var/lib/docker/containers/a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7/
a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7-json.log  checkpoints  config.v2.json  hostconfig.json  hostname  hosts  mounts  resolv.conf  resolv.conf.hash

6.3. 更新配置文件

**现在让我们首先更新hostconfig.json文件。**我们必须在这个 JSON 文件中搜索PortBindings键。此字段包含该特定容器的所有端口映射。

由于我们在运行 Docker 容器时没有添加任何端口映射,因此httpd-containerPortBindings键将为空 :

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

现在让我们通过更新JSON 文件中的PortBindings将主机端口 82 分配给 Docker 容器httpd-container的端口 80:

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

映射端口后,我们需要config.v2.json文件中公开 Docker 容器的 80 端口。我们应该在Config键中添加ExposedPorts 字段(如果还没有) :

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

通过这样做,我们通知 Docker 守护进程容器正在侦听该网络接口内的端口 80。 通过将值作为逗号分隔的键值对传递,可以公开多个端口

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

6.4. 验证更改

是时候验证我们迄今为止所做的更改了。 首先,让我们启动 Docker 服务:

$ systemctl start docker

现在,让我们启动 docker 容器httpd-container并使用docker ps 命令验证端口映射:

$ docker start httpd-container
httpd-container
[[email protected]](/cdn_cgi/l/email_protection):~# 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

在这里,我们可以看到端口映射已经更新。我们现在可以使用 82 端口从 Docker 环境外部访问httpd服务:

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

在这种方法中,容器 ID 和其他与容器相关的元数据被保留,因为我们不会替换原始 Docker 容器。

6.5. 更新正在运行的容器的端口映射

我们还可以使用相同的过程修改任何 Docker 容器的现有端口映射。唯一的区别是hostconfig.json文件中的PortBindings键不会为空。我们只需要更新我们正在使用的端口号。

如果我们在端口映射中更新 Docker 容器 TCP 端口,那么我们还需要在config.v2.json文件中公开相同的端口。

最后,我们启动 Docker 服务和 Docker 容器以使更改生效。