将端口映射分配给正在运行的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.json和config.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-container的PortBindings键将为空 :
{
...
...
"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 容器以使更改生效。