xiaoxiao
发布于 2025-10-09 / 12399 阅读 / 2 评论 / 1111 点赞

[nginx]dns后端负载均衡

使用 Docker 模拟基于 DNS 的负载均衡环境

在现代微服务架构中,负载均衡是确保系统高可用性和性能的关键组件。本文将介绍如何使用 Docker 容器来模拟一个基于 DNS 的负载均衡环境,帮助我们理解服务发现和负载分发的工作原理。

环境依赖与前置说明

前置条件

  • 已安装 Docker,确保 Docker 服务正常运行(可通过 docker --version 验证版本)。

  • 无需安装 Docker Compose(本文使用原生 Docker 命令),但需确保当前用户拥有 Docker 操作权限(避免频繁使用 sudo)。

操作说明

  • 所有命令建议在同一工作目录下执行(如 ~/dns-lb-test),避免因路径混乱导致配置文件挂载失败。

  • 执行命令前可通过 pwd 确认当前目录,后续创建的配置文件(Corefile、api-01.conf 等)将保存在此目录。

环境概述

我们将创建一个由四个容器组成的测试环境:

IP

容器名称

作用

192.168.2.100

nginx-lb

负载均衡器

192.168.2.101

nginx-api-01

API 服务 1

192.168.2.102

nginx-api-02

API 服务 2

192.168.2.200

test-api-coredns

DNS 服务

工作原理

整个系统的运作流程如下:

客户端 → nginx-lb(8081端口)→ 向 CoreDNS(2053端口)查询 test-api.com → 
CoreDNS 返回 101/102 轮询结果 → nginx-lb 转发请求到对应 API 服务 → API 服务返回结果给客户端

这种基于 DNS 的负载均衡方式具有良好的可扩展性,当需要增加后端服务时,只需更新 DNS 记录即可。

实施步骤

1. 创建自定义网络

首先,我们需要创建一个自定义的 Docker 网络,以便为每个容器分配固定的 IP 地址:

# 创建子网为192.168.2.0/24的自定义网络,网关为192.168.2.1
docker network create --subnet=192.168.2.0/24 --gateway 192.168.2.1 test-network

2. 创建配置文件

接下来,我们需要为每个服务创建相应的配置文件。

CoreDNS 配置

CoreDNS 将作为我们的 DNS 服务器,负责将域名解析到后端服务:

cat > Corefile << EOF
.:2053 {
    hosts {
      ttl 120                  # DNS记录缓存时间(秒),影响负载均衡生效速度
      192.168.2.101 test-api.com  # 解析记录:test-api.com 对应 API 服务1
      192.168.2.102 test-api.com  # 解析记录:test-api.com 对应 API 服务2(轮询生效)
      fallthrough              # 当本地无匹配记录时,允许请求转发到公共DNS
    }
    cache 120                  # 缓存DNS解析结果120秒,减少重复解析压力
    forward . 114.114.114.114:53  # 转发非本地记录到公共DNS(114DNS)
    log                        # 开启解析日志,便于调试
}
EOF

API 服务配置

为两个 API 服务分别创建配置文件,返回各自的 IP 地址用于验证:

cat > api-01.conf << EOF
server {
    listen       8080;         # 监听8080端口
    server_name  localhost;

    location /api/ip {
        default_type application/json;
        return 200 '{"ip":"192.168.2.101"}';  # 返回当前容器IP
    }
}
EOF

cat > api-02.conf << EOF
server {
    listen       8080;
    server_name  localhost;

    location /api/ip {
        default_type application/json;
        return 200 '{"ip":"192.168.2.102"}';  # 返回当前容器IP
    }
}
EOF

负载均衡器配置

配置 Nginx 作为负载均衡器,通过 DNS 动态解析后端服务:

cat > proxy.conf << EOF
server {
    listen       8081;                 # 负载均衡器对外端口
    server_name  localhost;

    resolver 192.168.2.200:2053 ipv6=off;  # 指定使用自定义CoreDNS服务(IP:端口)
    resolver_timeout 15s;              # DNS解析超时时间

    set \$backend "test-api.com";      # 定义变量存储后端域名(实现动态解析)
    
    location /api/ {
        proxy_connect_timeout 5s;      # 与后端连接超时时间
        proxy_set_header Host \$host;  # 传递客户端请求的Host头
        proxy_set_header X-Real-IP \$remote_addr;  # 传递客户端真实IP
        rewrite ^/api/(.*)\$ /api/\$1 break;  # 保持路径一致性
        proxy_pass http://\$backend:8080;  # 转发到后端服务
    }
}
EOF

配置详解:

  1. 为什么使用变量方式引用域名?

    set $backend "test-api.com";
    proxy_pass http://$backend:8080;
    
    • 当使用变量时,Nginx 会在每次请求时动态解析域名

    • 直接写域名时,Nginx 仅在启动时解析一次并缓存结果,无法感知后端变化

    • 变量方式确保 DNS 轮询生效,适应后端服务动态增减

  2. resolver 指令的作用:强制 Nginx 使用指定的 CoreDNS 服务器(192.168.2.200:2053)解析域名,而非容器默认 DNS。

3. 启动容器

按以下顺序启动容器(确保依赖服务先就绪):

# 1. 启动CoreDNS服务(DNS解析核心)
docker run --name test-api-coredns -itd --network test-network --ip 192.168.2.200 \
  -v ./Corefile:/Corefile registry-dockerhub.xiaoxiao.space/coredns/coredns:1.13.1

# 2. 启动API服务1
docker run --name nginx-api-01 --network test-network --ip 192.168.2.101 \
  -v ./api-01.conf:/etc/nginx/conf.d/api-01.conf \
  -itd registry-dockerhub.xiaoxiao.space/library/nginx:1.29.2-trixie-perl

# 3. 启动API服务2
docker run --name nginx-api-02 --network test-network --ip 192.168.2.102 \
  -v ./api-02.conf:/etc/nginx/conf.d/api-02.conf \
  -itd registry-dockerhub.xiaoxiao.space/library/nginx:1.29.2-trixie-perl

# 4. 启动负载均衡器(依赖DNS和API服务)
docker run --name nginx-lb --network test-network --ip 192.168.2.100 \
  -v ./proxy.conf:/etc/nginx/conf.d/proxy.conf \
  -itd -p 8081:8081 registry-dockerhub.xiaoxiao.space/library/nginx:1.29.2-trixie-perl

注:已将镜像替换为公共仓库(coredns/corednsnginx),若使用私有仓库可替换为对应地址。

4. 验证配置

批量验证负载均衡效果

通过循环命令连续请求 10 次,直观观察轮询结果:

# 连续请求10次,查看返回的IP是否在101和102之间切换
for i in {1..10}; do curl http://localhost:8081/api/ip; echo; done

预期输出(IP 交替出现):

健康检查限制

基于 DNS 的负载均衡方案有一个重要限制:无法进行主动健康检查。当某个后端服务出现故障时:

  1. DNS 仍然会将域名解析到故障服务器的 IP 地址

  2. Nginx 会尝试向故障服务器转发请求

  3. 请求会在超时后失败,导致用户体验下降

  4. 故障期间部分请求会长时间无响应

超时配置优化

为缓解这一问题,建议合理配置超时时间:

nginx

location /api/ {
    # 连接超时设置
    proxy_connect_timeout 5s;
    # 请求发送超时
    proxy_send_timeout 10s;
    # 响应读取超时
    proxy_read_timeout 10s;
    # DNS 解析超时
    resolver_timeout 15s;
    
    rewrite ^/api/(.*)$ /api/$1 break;
    proxy_pass http://$backend:8080;
}

更好的解决方案

在生产环境中,建议使用具备健康检查功能的负载均衡方案,如:

  1. Nginx Plus 的主动健康检查

  2. HAProxy 的健康检查机制

  3. 服务网格(如 Istio)提供的高级负载均衡功能

  4. 云服务商提供的负载均衡器

扩展与优化

添加更多后端服务

要添加更多的后端服务,只需:

  1. 在 Corefile 中添加新的 DNS 记录(如 192.168.2.103 test-api.com

  2. 创建新的 API 服务配置文件(如 api-03.conf

  3. 启动新的容器并分配新的 IP 地址(如 --ip 192.168.2.103

改进 DNS 配置

可以考虑使用更高级的 CoreDNS 插件来实现加权轮询、健康检查等功能。

清理资源

测试完成后,清理所有资源(容器、网络、配置文件):

bash

# 停止并删除容器
docker stop nginx-lb nginx-api-01 nginx-api-02 test-api-coredns
docker rm nginx-lb nginx-api-01 nginx-api-02 test-api-coredns

# 删除自定义网络
docker network rm test-network

# 删除配置文件(可选,若需保留配置可跳过)
rm -f Corefile api-01.conf api-02.conf proxy.conf

总结

通过这个实验,我们成功地使用 Docker 搭建了一个基于 DNS 的负载均衡环境。这种方法的优势在于:

  1. 解耦性强:负载均衡器不需要知道具体的后端服务地址

  2. 易于扩展:新增后端服务只需更新 DNS 记录

  3. 灵活性高:可以轻松实现复杂的路由策略

但对于生产环境,需要注意其缺乏健康检查的限制,并考虑采用更完善的负载均衡解决方案。

对于理解和实践微服务架构中的服务发现与负载均衡机制非常有帮助。


评论