使用docker-compose 注册服务到 Nacos,同时保证应用服务本身端口不暴露,是完全可行且推荐的最佳实践。

核心思路是:利用 Docker 的内部 DNS,让服务通过容器名在内部网络中进行通信。Nacos 注册的是容器名和内部端口,而不是宿主机的 IP 和端口。

最佳实践方案

假设我们有一个 user-service 应用,运行在容器内的 8080 端口,我们需要将它注册到 Nacos,但不对宿主机暴露。

1. 关键的 Docker Compose 配置

docker-compose.yml

version: '3.8'

services:
  # Nacos Server - 不对外暴露
  nacos:
    image: nacos/nacos-server:v2.2.0
    container_name: nacos-server
    environment:
      - MODE=standalone
      - NACOS_AUTH_ENABLE=true
    networks:
      - microservice-net
    # 注意:没有 ports 映射,Nacos 管理界面也无法从外部访问
    # 如果需要临时管理,可以按下面说明添加 127.0.0.1 绑定

  # 你的业务应用 - 绝不暴露端口!
  user-service:
    build: ./user-service
    container_name: user-service
    environment:
      # 关键配置:告诉Spring Cloud应用如何连接Nacos
      - SPRING_CLOUD_NACOS_SERVER-ADDR=nacos:8848 # 使用容器名'nacos'
      - SPRING_CLOUD_NACOS_DISCOVERY_IP=user-service # 强制注册的IP为容器名
      - SPRING_CLOUD_NACOS_DISCOVERY_PORT=8080 # 注册容器内的端口
    networks:
      - microservice-net
    # 关键:没有 ports 映射,外部无法直接访问
    depends_on:
      - nacos

  # API网关 - 它是唯一的入口,可以暴露
  api-gateway:
    build: ./api-gateway
    container_name: api-gateway
    ports:
      - "80:8080" # 唯一暴露的端口
    environment:
      - SPRING_CLOUD_NACOS_SERVER-ADDR=nacos:8848
    networks:
      - microservice-net
    depends_on:
      - nacos
      - user-service

networks:
  microservice-net:
    driver: bridge
2. 关键的应用配置(Spring Cloud Application)

在你的 user-serviceapplication.ymlbootstrap.yml 中,进行如下关键配置:

spring:
  application:
    name: user-service # 服务名
  cloud:
    nacos:
      discovery:
        server-addr: ${SPRING_CLOUD_NACOS_SERVER-ADDR:nacos:8848} # 从环境变量读取,默认用容器名
        ip: ${SPRING_CLOUD_NACOS_DISCOVERY_IP:} # 强制指定注册的IP地址
        port: ${SPRING_CLOUD_NACOS_DISCOVERY_PORT:8080} # 强制指定注册的端口
        # 以下是关键中的关键!
        register-enabled: true   # 允许注册
        enabled: true            # 开启服务发现
        # 确保不使用主机名和外部IP
        prefer-ip-address: false # 非常重要!设为false,否则会尝试注册宿主机IP

环境变量的作用:

  • SPRING_CLOUD_NACOS_DISCOVERY_IP=user-service:这强制 Spring Cloud 向 Nacos 注册时,使用的 IP 地址是容器名 user-service。这样,其他服务(如网关)从 Nacos 获取到的实例地址就是 http://user-service:8080,这个地址在 Docker 内部网络中是可以通过 DNS 解析的。
  • SPRING_CLOUD_NACOS_DISCOVERY_PORT=8080:确保注册的是容器内部的实际端口。
3. 工作原理流程图

在这里插入图片描述

  1. 注册user-service 启动后,通过环境变量配置,向 nacos:8848 注册自己,注册的地址是容器名 user-service 和内部端口 8080
  2. 发现api-gateway 也连接到 nacos:8848,查询名为 user-service 的实例列表。
  3. 获取地址:Nacos 返回实例地址:user-service:8080
  4. 内部路由api-gateway 直接向 http://user-service:8080 发起请求。Docker 的网络 DNS 会自动将容器名 user-service 解析为其在内部网络中的 IP 地址,通信成功。
4. 如何验证和管理?

问题: Nacos 本身没暴露端口,如何查看服务注册情况?

解决方案: 有以下几种安全的方式:

  1. 临时暴露 Nacos UI(仅用于调试)
    修改 nacos 服务的配置,添加仅绑定到本地回环的端口映射:

    nacos:
      image: nacos/nacos-server:v2.2.0
      ports:
        - "127.0.0.1:8848:8848" # 仅宿主机可通过 localhost:8848 访问
      # ... 其他配置不变
    

    然后你可以在宿主机上通过 http://localhost:8848/nacos 访问控制台。生产环境中应移除此映射

  2. 使用命令行工具进入容器内部查看

    # 进入nacos容器执行命令
    docker compose exec nacos curl http://localhost:8848/nacos/v1/ns/service/list
    # 进入网关容器测试是否能解析user-service
    docker compose exec api-gateway ping user-service
    docker compose exec api-gateway curl http://user-service:8080/actuator/health
    
  3. 通过已暴露的网关进行间接验证
    如果你的网关有健康检查接口,可以通过访问网关来间接验证整个链路是否通畅。

    curl http://localhost:80/actuator/gateway/routes # 查看网关路由信息
    curl http://localhost:80/user/api/v1/hello # 测试通过网关访问user-service
    

结论

要保证注册到 Nacos 的应用不暴露端口,只需做到以下几点:

  1. 不在 Compose 文件中为业务服务配置 ports
  2. 通过环境变量 SPRING_CLOUD_NACOS_DISCOVERY_IP 强制指定注册的 host 为容器名
  3. 确保 spring.cloud.nacos.discovery.prefer-ip-address=false,防止注册宿主机IP。
  4. 所有服务(Nacos、应用、网关)连接到同一个自定义 Docker 网络
  5. 让 API 网关作为唯一的对外入口
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐