关于Docker的一些个人理解与实践
最近由于在自己下载一些GitHub上面的项目尝试运行,为了防止出现安装了一大堆环境而互相冲突,或者依赖版本不兼容的情况,我使用了Docker。Docker是一个开源的应用容器引擎,它可以将应用及其依赖项打包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows、macOS上。这极大地简化了环境配置和部署过程。
容器端口冲突与通信问题
在项目实践中,我们有时会遇到一些端口冲突和容器间通信的问题。
例如,一个典型的Web应用场景:
- 前端服务(如Nginx、Web服务器)需要通过访问公网域名直接展示网页,所以通常需要将容器的80端口(HTTP)或443端口(HTTPS)映射到宿主机对应的端口上,以便外部用户可以直接访问。
- 后端API服务(如Java、Python应用)可能也需要监听一个端口(例如8080或80)来进行服务内部通信或作为其默认监听端口。
如果希望后端服务也能使用80端口进行通信,但宿主机的80端口已经被前端容器映射并占用,那么将第二个容器的80端口也映射到宿主机80端口显然是不可行的,会导致端口冲突。
同时,不同的Docker容器之间,如果希望它们能互相访问(通信),也需要配置正确的网络。
我们可以使用以下两种或更多方式来解决容器间通信以及宿主机端口占用的问题,使得前端和后端服务可以正常协作:
解决方案
1. 使用Docker自定义网络(推荐)
将两个或多个相关的Docker容器加入到一个自定义的桥接网络(Bridge Network)中。
优势:
- 容器可以直接使用服务名或容器名互相通信,无需通过映射到宿主机的端口或查询IP地址。
- 解决了宿主机80端口冲突的问题,因为容器间通信无需经过宿主机端口映射。
具体操作如下:
创建自定义网络:
1
docker network create my_custom_network
运行容器并指定网络:
- 前端容器(需要对外提供服务,因此需要映射宿主机端口):
1
2docker run -d --name frontend_web --network my_custom_network -p 80:80 frontend_image
# -p 80:80:将宿主机的80端口映射到容器的80端口,用于用户访问。 - 后端容器(不需要对外直接暴露,只需对前端容器暴露):
1
2docker run -d --name backend_api --network my_custom_network -p 8080:8080 backend_image
# 后端容器的8080端口可以随意选择,但不需要将其映射到宿主机,或可以映射到一个高位端口(如-p 12345:8080)用于测试,但不用于生产环境的前后端通信。
- 前端容器(需要对外提供服务,因此需要映射宿主机端口):
容器间通信:
在配置前端容器(如Nginx)时,可以直接使用后端容器的服务名(backend_api)和容器内部端口(8080)来进行通信和请求转发。- 例如,前端Nginx的配置中,代理地址可以是
http://backend_api:8080。
- 例如,前端Nginx的配置中,代理地址可以是
注意:frontend_web 和 backend_api 容器内部可以监听各自独立的80端口或其他端口,只要不冲突即可。这里的关键是宿主机只有一个80端口,因此只有一个容器可以映射到它。
2. 使用反向代理(如Nginx/Traefik)进行域名/路径匹配和请求转发
这个方案与第一个方案通常是结合使用的,尤其是在管理多个应用和域名时,或者当一个容器需要监听多个域名/路径的请求时。
具体操作:
- 部署一个专业的反向代理容器(如Nginx/Traefik),作为所有请求的唯一入口。
- 反向代理容器负责将宿主机的80/443端口映射出来,接收外部用户的所有请求。
- 反向代理通过域名匹配(Host Header)或路径匹配(URL Path)来判断请求应该发送给哪个后端服务容器。
- 将所有服务容器(包括前端应用、后端API)都加入到一个自定义网络(如
my_custom_network)中。 - 反向代理容器通过服务名和容器内部端口(如
backend_api:8080)将请求转发到对应的后端容器。
优势:
- 统一的入口,易于管理 SSL/TLS 证书、负载均衡、限流等。
- 彻底解决了宿主机端口冲突问题,因为所有外部请求只通过反向代理的80/443端口进入。
- 后端服务可以完全不对外暴露任何端口(不使用
-p进行宿主机端口映射),增强安全性。
总结:在现代Docker部署中,自定义网络是实现容器间通信的基础,而反向代理则是管理多服务入口和流量分发的常用手段。