如何通过DNS子域名访问Docker Swarm集群中的容器?
实现Docker Swarm容器的子域名访问(容器名作为子域名)
当然可以实现!核心思路是Wildcard DNS解析 + Swarm-aware反向代理,反向代理会自动发现Swarm中的服务,把对应子域名的流量转发到目标容器。下面是具体的实现步骤,我会用最适合Swarm生态的Traefik来做演示——它自带服务发现,能自动适配Swarm的动态服务部署。
第一步:配置Wildcard DNS记录
首先要让所有*.example.com的子域名都解析到你的物理机公网IP。登录你的域名服务商后台,添加一条A记录:
- 主机记录:
* - 记录值:你的物理机公网IPv4地址
- TTL:选默认或短一点(比如5分钟)方便调试
这样不管是mysqldb.example.com还是webapp.example.com,都会指向你的服务器。
第二步:准备Swarm网络
创建一个Swarm overlay网络,让Traefik和你的服务能跨节点通信(单节点Swarm也适用):
docker network create --driver=overlay traefik-public
第三步:部署Traefik反向代理
Traefik是专门为容器化环境设计的反向代理,原生支持Swarm模式的服务自动发现。下面是生产可用的docker-compose配置(保存为traefik.yml):
version: '3.8' services: traefik: image: traefik:v2.10 command: - "--providers.docker=true" - "--providers.docker.swarmMode=true" - "--providers.docker.exposedbydefault=false" # 只处理显式声明的服务 - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" # 自动申请Let's Encrypt证书(可选,用于HTTPS) - "--certificatesresolvers.myresolver.acme.httpchallenge=true" - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" - "--certificatesresolvers.myresolver.acme.email=your-email@example.com" # 用于证书过期提醒 - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" # 可选:Traefik仪表盘,生产环境建议限制内网访问 - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" # 只读权限,安全起见 - "./letsencrypt:/letsencrypt" # 存储证书 networks: - traefik-public deploy: placement: constraints: - node.role == manager # 部署在Swarm管理节点 labels: # 配置仪表盘的子域名访问 - "traefik.enable=true" - "traefik.http.routers.traefik.entrypoints=websecure" - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" - "traefik.http.routers.traefik.tls.certresolver=myresolver" - "traefik.http.services.traefik.loadbalancer.server.port=8080" networks: traefik-public: external: true
执行部署命令:
docker stack deploy -c traefik.yml traefik
第四步:配置你的服务(以MySQL为例)
现在部署你的mysqldb服务,通过Traefik标签指定子域名。注意MySQL是TCP协议,所以要配置TCP路由(和HTTP服务的配置略有不同):
保存为mysql.yml:
version: '3.8' services: mysqldb: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD=your-secure-password # 替换成你的密码 volumes: - mysql-data:/var/lib/mysql # 持久化数据 networks: - traefik-public deploy: labels: - "traefik.enable=true" # 配置TCP路由,用SNI匹配子域名(TCP没有HTTP Host头) - "traefik.tcp.routers.mysqldb.entrypoints=websecure" - "traefik.tcp.routers.mysqldb.rule=HostSNI(`mysqldb.example.com`)" - "traefik.tcp.routers.mysqldb.tls=true" - "traefik.tcp.routers.mysqldb.tls.certresolver=myresolver" # 指向MySQL容器的3306端口 - "traefik.tcp.services.mysqldb.loadbalancer.server.port=3306" volumes: mysql-data: networks: traefik-public: external: true
部署服务:
docker stack deploy -c mysql.yml mysqldb
测试访问
用MySQL客户端连接:
mysql -h mysqldb.example.com -P 443 -u root -p --ssl-mode=REQUIRED
输入密码后就能访问容器里的MySQL了。
对于HTTP服务的配置(比如Nginx)
如果是Web服务,配置会更简单,用HTTP路由即可:
deploy: labels: - "traefik.enable=true" - "traefik.http.routers.webapp.entrypoints=websecure" - "traefik.http.routers.webapp.rule=Host(`webapp.example.com`)" - "traefik.http.routers.webapp.tls.certresolver=myresolver" - "traefik.http.services.webapp.loadbalancer.server.port=80"
注意事项
- 防火墙配置:确保物理机开放80、443端口(比如
ufw allow 80/tcp、ufw allow 443/tcp)。 - 安全建议:
- 不要直接把MySQL等数据库暴露到公网,建议搭配VPN或IP白名单限制访问。
- 生产环境关闭Traefik的
api.insecure=true(如果配置了),改用HTTPS访问仪表盘并限制内网IP。
- 动态适配:如果你的服务缩放、重启,Traefik会自动更新路由配置,不需要手动修改。
内容的提问来源于stack exchange,提问作者Vladyslav Baidak
相关产品推荐
相关产品推荐

