Docker中使容器互相通信但不暴露到互联网的解决方案

68次阅读
没有评论

问题描述

在使用Docker时,希望设置一个简单的环境,其中包括一个PostgreSQL容器和一个Node.js应用程序。他的主要目标是让PostgreSQL容器的端口不暴露到互联网,但允许Node.js后端通过端口3333接受请求。他已经尝试了一些方法,但遇到了问题。以下是他的当前docker-compose.yml文件:

version: "3"
services:
  redis:
    image: redis:latest
    ports:
      - 6379:6379
    command: ["redis-server", "/redis.conf"]
    network_mode: host
  database:
    image: "postgres"
    env_file:
      - ./database/database.env
    volumes:
      - database-data:/var/lib/postgresql/data
      - ./database/init-db.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - 5432:5432
    restart: always
    network_mode: host
  backend:
    depends_on: [database]
    build: ./server
    volumes:
      - ./files:/var/lib/files
    ports:
      - 3333:3333
    restart: always
    network_mode: host
volumes:
  database-data:

解决方案

在Docker中,你可以使用网络模式(network_mode)来控制容器之间的通信。用户尝试过使用network_mode: host来达到目的,但这会导致发布的端口被忽略,即使在容器内定义了端口,它们也会与主机上的相同端口一致。

但是,根据Docker Compose文档的默认行为,任何服务都可以通过服务名称与其他服务通信。因此,你不必使用network_mode: host,而是让Docker创建一个默认的网络。下面是一个示例docker-compose.yml文件,演示了如何实现这一目标:

version: '3.7'
services:
  nginx1:
    image: nginx
    ports:
      - '8080:80'
  nginx2:
    image: nginx

在上面的示例中,我们定义了两个服务nginx1nginx2nginx1通过端口8080在localhost和网络中对外提供服务。而nginx2则只是简单地使用了默认端口。

这两个服务仍然可以通过服务名称相互访问,而无需使用特殊的网络模式。例如,可以通过以下方式访问nginx1

$ curl -I http://nginx1:80
HTTP/1.1 200 OK
Server: nginx/1.19.10

同时,nginx2的端口也在主机上有相应的端口:

$ docker-compose ps
      Name                     Command               State                  Ports
-----------------------------------------------------------------------------------------------
nginx1_1   /docker-entrypoint.sh ngin ...   Up      0.0.0.0:8080->80/tcp,:::8080->80/tcp
nginx2_1   /docker-entrypoint.sh ngin ...   Up      80/tcp

可以看到,nginx1的端口8080被映射到了主机的8080端口上。

总结一下,通过使用Docker默认的网络,你可以轻松地让容器之间相互通信,而无需特殊的网络模式设置。

这里还有一种方法,可以使用links(新版本中使用networks),这种方式可以使得两个容器之间能够通信。你可以更新docker-compose.yml文件并添加links来将database服务(数据库容器)连接到backend服务中,然后在后端数据库配置中,使用database作为数据库主机。具体示例如下:

version: "3"
services:
  redis:
    image: redis:latest
    ports:
      - 6379:6379
    command: ["redis-server", "/redis.conf"]
  database:
    image: "postgres"
    env_file:
      - ./database/database.env
    volumes:
      - database-data:/var/lib/postgresql/data
      - ./database/init-db.sql:/docker-entrypoint-initdb.d/init.sql
    restart: always
  backend:
    depends_on: [database]
    build: ./server
    volumes:
      - ./files:/var/lib/files
    ports:
      - 3333:3333
    restart: always
    links:
      - "database"
volumes:
  database-data:

在这个示例中,我们将database服务(数据库容器)通过linksbackend服务连接起来,然后在后端数据库配置中,使用database作为数据库主机(这里的database是在compose.yaml文件中定义的服务名称)。这样就确保了backend服务可以访问database服务。

请注意,上述解决方案中的links在新版本的Docker中已经被networks取代,你可以根据实际情况使用适合的版本。

正文完