使用 docker 时如何避免“端口冲突”?

Posted

技术标签:

【中文标题】使用 docker 时如何避免“端口冲突”?【英文标题】:How do I avoid 'port collision' when using docker? 【发布时间】:2018-06-25 04:54:15 【问题描述】:

我希望标题足够描述性。我正在尝试在 docker 中执行我的节点应用程序(使用 mongo 和 mysql)。我使用docker-compose 启动应用程序和docker-compose.yml 文件如下:

version: "3.3"
services:
  app:
    container_name: app
    restart: always
    build: .
    volumes:
      - ./:/app
    ports:
      - "3000:3000"
    links:
      - mongo
      - mysql
  mongo:
    container_name: mongo
    image: mongo
   ports:
      - "27017:27017"
  mysql:
    container_name: mysql
    image: mysql
    ports:
      - "3306:3306"

每当我尝试使用 docker-compose up 启动它时,我都会收到以下错误:

ERROR: for mysql  Cannot start service mysql: driver failed programming external connectivity on endpoint mysql (785b03daaa662bb3c344025f89fd28f49eabb43104b1c9a16ab425ab5120309f): Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use

ERROR: for mysql  Cannot start service mysql: driver failed programming external connectivity on endpoint mysql (785b03daaa662bb3c344025f89fd28f49eabb43104b1c9a16ab425ab5120309f): Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use
ERROR: Encountered errors while bringing up the project.

我做了一些研究,似乎gitlab-runner 正在使用 mysql 服务。我的理解是,如果我通过 docker 容器运行此设置,它们将与主机系统隔离,因此我不会有任何端口冲突。我要暴露的唯一端口是我的Dockerfile 中的端口 - 在我的情况下是 3000。我在我的docker-compose.yml 中遗漏了什么吗?还有什么问题?

【问题讨论】:

你必须把这个"3306:3306"改成XXXX:3306,其中XXXX是其他空闲的端口。 【参考方案1】:

在您的 docker-compose.yml 文件中,您通过在 ports 数组中声明它们来公开您主机网络空间上的 pod 的端口,例如:

ports:
  - "3306:3306"

如果您省略这部分配置,您的容器仍然可以私下相互访问,但端口不会在您的主机中绑定,避免您面临的端口冲突。

如果您需要为部分或全部服务向主机公开端口,则必须通过更改主机端的绑定端口自己处理冲突。例如,为了避免端口 3306 上的端口冲突,您可以简单地这样做:

ports:
  - "3307:3306"

【讨论】:

如果我只是删除端口部分,它仍然无法正常工作,但是会出现不同的错误。我假设这是我的代码的问题,但与问题无关。将其更改为“3307:3306”无论如何都可以。谢谢!【参考方案2】:

停止绑定到本地端口,让docker-compose 为您选择一个临时端口。在您的情况下,您的应用程序可以在没有任何帮助的情况下访问默认端口。如果您遵循12 Factor App 方法,请使用如下 sn-p 中的环境变量。

version: "3.3"
services:
  app:
    restart: always
    build: .
    volumes:
      - ./:/app
    ports:
      - 3000  # will be bound to an ephemeral port
    environment:
      MONGODB_URL: mongodb://mongo/db  # can reach port 27017 without help
  mongo:
    image: mongo
    ports:
      - 27017

这是通过环境变量或命令行标志使您的应用程序可配置的主要原因。

如果您需要从主机访问 docker 应用程序,您可以使用docker-compose port 获取临时端口。我经常使用如下的 shell 函数:

get_exposed_port()   # SERVICE PORT
    docker-compose port $1 $2 | cut -d: -f2

【讨论】:

请注意,通常没有必要公开 mongo 端口。默认情况下,同一网络中的服务可以相互访问; mongo 服务中的 ports 部分将端口导出到 host @FranklinYu - 正确。这就是为什么“app”容器可以在默认端口上连接到“mongo”的原因。您需要获取临时端口的唯一时间是从 docker 网络外部(例如,从 localhost)访问容器 这里是关于端口的简短语法文档,如果有人需要它:docs.docker.com/compose/compose-file/compose-file-v3/#ports【参考方案3】:

从 docker compose files 版本 3 开始提供的另一个选项是通过 environment variables 指定在主机上公开的不同端口。

所以一种方法可能是这样的:

version: "3.3"
services:
  mysql:
    container_name: mysql
    image: mysql
    ports:
      - "$MYSQL_PORT_NUMBER:-3306:3306"

然后,如果定义了环境变量,则将使用该值。否则,将使用默认的3306

版本 3 docker compose 文件的第二个功能,如果存在,它们将 read environment variables from a .env file。 .env 文件必须位于运行 docker-compose 的目录中。

最后,要调试所有这些,可以使用docker-compose config 发出变量扩展的 docker compose 文件。使用如下所示的.env 这样做:

MYSQL_PORT_NUMBER=3307

给出这个结果:

$ docker-compose -f mysql-port-conflict.yaml config
services:
  mysql:
    container_name: mysql
    image: mysql
    ports:
    - published: 3307
      target: 3306
version: '3.3'

这表明 MySQL 将在主机的端口 3307 上可用。当然,任何想要连接 MySQL 的应用程序也需要知道$MYSQL_PORT_NUMBER

HTH

【讨论】:

以上是关于使用 docker 时如何避免“端口冲突”?的主要内容,如果未能解决你的问题,请参考以下文章

cmd中如何解决端口冲突问题

应该如何为新服务选择默认 TCP/IP 端口?

如何解决端口冲突问题

Windows 8 上的 XAMPP 和 Skype 端口冲突 [重复]

解决端口冲突问题的一般思路

端口冲突问题