应用场景

  • docker 部署 Node 项目

  • 用 forever 守护进程工具管理 且将 logs 映射到主机目录 logs 文件夹(bind volume)

  • 用于项目开发容器:本地没有 node 开发环境且懒得安装

  • 主机项目文件更新,容器内 node 服务需 npx forever restartall 或者 npm run restart重启(restart = npx forever restartall)

部署过程

  • 安装 docker

  • docker pull node:tag 拉取 node 镜像(版本取决于实际项目)

  • 启动容器

    docker run -dp 8388:8388 \
    --name node \
    -w /app \
    --mount type=bind,src=/node/server/,target=/app \
    --mount type=bind,src=/node/logs,target=/logs \
    node:21.2.0 \
    /bin/bash -c "npm install &&  node /app/index.js && npm run dev"
    
    • --name 为该容器起一个别名,替代 <container ID>

    • -w 指定工作目录,容器内所有命令都将在该目录下执行

    • --mount type=bind,src=...,target=/app bind volume,将主机目录挂载到容器内,用于映射项目源文件、日志文件

    • -d 后台运行容器

    • -p 端口映射,将容器内 8388 端口映射到主机 8388 端口

在 npm run dev 的 dev 中,此脚本字段对应的命令为 npx dotenvx run --env-file=/app/.env.pack.dev -- forever start -c 'node --harmony' -l /logs/access.log -e /logs/err.log -a /app/index.js

  • 在容器中, 使用 forever 启动 node 应用时需要在命令中添加 -c 'node --harmony'

  • 执行非 node 命令,需要用 npx 执行(npx dotenvx runnpx forever list)

  • dotenvx run 为 dotenvx 库提供的命令行, 用于设置 env

1、启动容器时,执行了 npm install && node /app/index.js两条命令,若不执行node /app/index.js,则容器启动后会被迫停止 EXIT(0)
2、工具 forever、dotenvx 都预先存在于 package.json,npm install 后可直接使用, 也可手动在 node 容器中安装

docker(例如 node) 容器访问宿主机某端口服务

1、容器内的服务访问 localhost 和宿主机上非容器的服务访问 localhost 是不一样的, 比如都是访问 localhost:8288, 容器是在访问本身内部的 8288 端口服务,而不是访问宿主机的 8288 端口服务

2、请注意,本文只探讨容器访问宿主机,不涉及容器间的通信

3、那么如何从容器中访问到宿主机的网络?有以下两种解决办法,第一种亲测有效

使用 host 网络

  • Docker 容器运行的时候有 host、bridge、none 三种网络可供配置。默认是 bridge,即桥接网络,以桥接模式连接到宿主机;host 是宿主网络,即与宿主机共用网络;none 则表示无网络,容器将无法联网。

  • 容器使用 host 网络,那么容器的 localhost 就是宿主机的 localhost。

  • 在 docker 中使用 --network host 来为容器配置 host 网络(依照第二步部署过程中的 docker run, 给出示例命令):

    docker run -d \
    --name node \
    --network host \
    -w /app \
    --mount type=bind,src=/node/server/,target=/app \
    --mount type=bind,src=/node/logs,target=/logs \
    node:21.2.0 \
    /bin/bash -c "npm install &&  node /app/index.js && npm run dev"
    
  • 上面的命令中,没有必要像前面一样使用 -p 8388:8388 来映射端口,是因为本身与宿主机共用了网络,容器中暴露端口等同于宿主机暴露端口。

  • 总结: host 网络下 docker 容器与宿主机共用了网络,通用性好。但是,由于 host 网络没有 bridge 网络的隔离性好,使用 host 网络安全性不如 bridge 高。

使用宿主机 IP

在 Linux 下安装 Docker 的时候,会在宿主机安装一个虚拟网卡 docker0,我们可以使用宿主机在 docker0 上的 IP 地址来代替 localhost。

  • ip addr show docker0 查询宿主机 IP 地址

    $ ip addr show docker0
    3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
      link/ether 02:42:d5:4c:f2:1e brd ff:ff:ff:ff:ff:ff
      inet 172.17.0.1/16 scope global docker0
         valid_lft forever preferred_lft forever
      inet6 fe80::42:d5ff:fe4c:f21e/64 scope link
         valid_lft forever preferred_lft forever
    
  • 可以发现宿主机的 IP 是 172.17.0.1,那么将 node 容器内访问宿主机的 url 改为 http(s)://172.17.0.1:8288, 就可以访问到宿主机的 8288 端口,解决 502 Bad Gateway 错误。

但是,在 Windows 和 macOS 平台下并没有 docker0 虚拟网卡,这时候可以使用 host.docker.internal 这个特殊的 DNS 名称来解析宿主机 IP

  • request_url: http(s)://host.docker.internal:8288

  • 由此发现,不同系统下宿主机的 IP 是不同的,所以使用宿主机 IP,不能跨环境通用。

文章参考: Docker 容器访问宿主机网络