Kubernetes实战:部署多路Nginx网关及后端应用排错总结

项目目标: 在Kubernetes集群中部署多个独立的Web游戏应用(捕鱼、坦克、跳一跳),并通过一个统一的Nginx API网关对外提供服务,实现基于路径的路由。

核心组件与架构:

  1. 后端游戏应用 (buyu, tanke, tiaoyitiao):

    • 每个游戏是一个独立的Deployment,运行各自的Nginx实例(镜像是 harbor.oldboyedu.com/oldboyedu-games/...),用于提供静态游戏文件。
    • 每个游戏Nginx监听不同的内部端口(81, 82, 83)。
    • Nginx配置(如 buyu.conf)通过一个共享的ConfigMap (games-nginx-config) 提供,并使用 subPath 挂载到每个Pod的 /etc/nginx/conf.d/ 目录。
    • 每个游戏通过ClusterIP类型的Service (如 buyu-svc) 在集群内部暴露。
  2. API网关 (game-gateway):

    • 一个独立的Deployment,运行标准 nginx:1.26 镜像。
    • 其Nginx配置 (default.conf 来自 gateway-nginx-config ConfigMap) 作为反向代理,监听80端口。
    • 根据URL路径 (/buyu/, /tanke/, /tiaoyitiao/) 将请求路由到对应的后端游戏Service。
    • 通过NodePort类型的Service (gateway-svc) 对外暴露,NodePort为 8090

关键配置亮点与值得注意的点:

  1. 精巧的ConfigMap管理 (01-all-cm.yaml, 05-default.conf):

    • games-nginx-config: 将所有后端应用的Nginx配置文件集中在一个ConfigMap中,每个应用的配置作为ConfigMap的一个 data key。
    • subPath挂载: 在Deployment中,通过 volumeMountssubPath 字段,将ConfigMap中特定的key(如 buyu.conf)挂载为Pod内的单个文件。这避免了将整个ConfigMap作为一个目录挂载,使得配置管理更清晰、更细粒度。
      volumeMounts:
      - name: nginx-conf
        mountPath: /etc/nginx/conf.d/buyu.conf # 挂载为具体文件
        subPath: buyu.conf                   # 对应ConfigMap的key
      
    • gateway-nginx-config: 单独为网关Nginx定义ConfigMap,职责分离。
  2. 后端Nginx配置 (buyu.conf等):

    • alias指令: 用于提供静态文件服务,当 location 路径与文件系统路径不完全匹配时,aliasroot 更灵活。
    • URL标准化: location = /buyu { return 301 /buyu/; } 这样的配置确保用户访问不带尾部斜杠的路径时,会被重定向到带斜杠的路径,这对于 try_files 和相对路径的静态资源加载通常是必要的。
    • try_files: 实现优雅的前端路由回退,对于单页应用(SPA)非常有用。
  3. 网关Nginx配置 (05-default.conf):

    • 路径路由 (location /path/): 清晰地将不同路径的请求代理到不同的后端服务。
    • proxy_pass指令:
      • 使用了短服务名 (http://buyu-svc:81/...)。这依赖于Kubernetes的DNS服务,当Nginx Pod和目标Service在同一个命名空间时,DNS会自动补全搜索域进行解析。
      • proxy_pass 后的URL路径处理:proxy_pass http://buyu-svc:81/buyu/; 中的 /buyu/ 是重要的。它告诉Nginx将匹配 location 的部分(这里是 /buyu/)替换为 proxy_pass URL中的路径后,再追加剩余部分。例如,请求网关 GET /buyu/index.html 会被代理到 http://buyu-svc:81/buyu/index.html。如果 proxy_pass 的URL末尾没有路径(如 http://buyu-svc:81/;),则会将原始请求路径(匹配 location 后的部分)直接附加。

排错历程与核心学习点 (这部分是博客的精华!):

  1. Pod启动失败 (CrashLoopBackOff):

    • 首要操作: kubectl logs <pod-name> [-c <container-name>] 查看容器日志,以及 kubectl describe pod <pod-name> 查看Pod事件和状态。这是定位问题的起点。
  2. YAML语法与缩进错误:

    • 现象: kubectl apply -f <file.yaml> 时报错 error converting YAML to JSON: yaml: line X: did not find expected key
    • 原因: YAML对缩进非常敏感。ConfigMap中多行字符串(使用 |)的内容,其内部缩进相对于声明它的那一行。
    • 教训: 仔细检查YAML缩进,使用IDE的YAML插件或在线YAML校验器。
  3. Nginx resolver指令与集群DNS解析:

    • 问题现象: Nginx启动日志报错 host not found in resolver "kube-dns.kube-system.svc.cluster.local"
    • 关键发现: 集群的内部域名 (cluster domain) 不是默认的 cluster.local,而是自定义的 oldboyedu.com
    • 如何确认集群域名:
      • 在任一Pod中执行 cat /etc/resolv.conf,查看 search 行。
      • 检查Kubelet启动参数中的 --cluster-domain
      • 查看CoreDNS (或kube-dns) 的ConfigMap中 kubernetes 插件的配置。
    • resolver指令的正确配置:
      • 最佳实践: 直接使用kube-dns Service的ClusterIP作为resolver,如 resolver 10.200.0.10 valid=5s; (这里的IP是你的kube-dns Service IP)。这避免了对kube-dns服务名本身的依赖。
      • 使用FQDN: resolver kube-dns.kube-system.svc.oldboyedu.com valid=5s; (注意替换为你的实际集群域名)。
    • proxy_pass 与DNS: 当Nginx和后端服务在同一命名空间时,proxy_pass 使用短服务名(如 buyu-svc)通常无需在Nginx中显式配置 resolver,Nginx会使用Pod的系统DNS进行解析。如果显式配置了 resolver,Nginx会优先使用它。
  4. Kubernetes Service DNS解析规则:

    • 同命名空间: 可以直接使用短服务名 <service-name>
    • 跨命名空间: 需要使用 <service-name>.<namespace-name> 或完整的FQDN <service-name>.<namespace-name>.svc.<cluster-domain>
    • 验证: 在Pod内部使用 nslookup <service-name>[.<namespace-name>[.svc.<cluster-domain>]]ping <service-name>[.<namespace-name>]
  5. 镜像拉取策略 (imagePullPolicy):

    • 如果镜像标签不是 :latest (如 nginx:1.26),默认策略是 IfNotPresent。Kubernetes会先检查Node本地是否有该镜像,有则使用,无则拉取。
    • 如果本地有镜像但K8s仍然尝试拉取,请仔细核对Deployment中指定的镜像名和标签与本地 docker images 显示的是否完全一致
  6. 本地Docker镜像的跨节点使用:

    • 当无法从远程仓库拉取镜像时(如Docker Hub被墙),可以通过 docker save <image> -o <image.tar> 将镜像打包,scp到目标节点,再通过 docker load -i <image.tar> 导入。

最终成功运行的关键因素:

  • 修复了ConfigMap的YAML缩进。
  • 正确理解并配置(或在同命名空间场景下省略)了Nginx的resolver,并确保proxy_pass使用了可被正确解析的服务名。
  • 确保所有后端服务和网关Pod都正常运行且网络可达。

给其他学习者的建议:

  • 小步快跑,逐步验证: 不要一次性部署所有东西,先部署最小单元,验证通过后再扩展。
  • 日志和事件是你的朋友: kubectl logskubectl describe 是排错最重要的工具。
  • 理解K8s网络和DNS: 这是很多应用部署问题的根源。花时间学习Service、DNS解析、CNI等概念。
  • YAML很重要: 熟悉YAML语法,借助工具检查。
  • 文档和社区: 遇到问题时,查阅官方文档,搜索社区的类似问题。

FQDN

FQDN(Fully Qualified Domain Name,完全限定域名) 是互联网中用于唯一标识一个主机或服务的完整域名。以下是详细说明:


1. FQDN 的组成结构

一个标准的 FQDN 由多级标签组成,从具体到通用,格式为:

<主机名>.<子域名>.<二级域名>.<顶级域名>.

示例

www.example.com.
mail.server.example.com.
  • 末尾的 . 表示根域名(通常可省略)

2. 核心特点

特性 说明
绝对唯一性 全球DNS系统中能唯一确定一个节点
层级结构 从右向左解析,层级递减(com → example → server → mail)
完整路径 包含所有域名部分,不依赖上下文

3. 与普通域名的区别

对比项 FQDN 普通域名
完整性 host.subdomain.domain.tld. subdomain.domain.tld
末尾点号 可选但有明确意义 通常省略
使用场景 DNS解析、证书签发、服务器配置 一般网站访问

4. 技术应用场景

① Kubernetes 服务发现

# Service的FQDN格式:
<service-name>.<namespace>.svc.cluster.local

# 示例:访问集群内服务
curl http://buyu-svc.game.svc.cluster.local

② 服务器证书签发

  • SSL证书必须绑定FQDN(如 *.example.com

③ DNS解析

dig +short www.google.com  # 解析FQDN

④ 系统配置

# Linux主机名配置
hostnamectl set-hostname server1.example.com

5. 如何验证FQDN?

Linux/macOS

hostname -f  # 显示本机FQDN
nslookup <fqdn>  # 验证解析

Windows

[System.Net.Dns]::GetHostByName($env:computerName).HostName

Kubernetes Pod内

nslookup buyu-svc.game.svc.cluster.local

6. 常见问题

Q:为什么K8s服务要用svc.cluster.local后缀?
A:这是默认的集群域名(clusterDomain),可通过 kubeadm config 修改:

# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
networking:
  dnsDomain: "custom.domain"

Q:FQDN末尾的点号是否必须?
A:在DNS配置文件中必须(如 example.com.),浏览器和一般使用中可省略。


7. 生产环境建议

  1. 服务通信:始终使用FQDN(如 service.namespace.svc.cluster.local
  2. 证书管理:确保证书包含所有使用的FQDN
  3. 网络策略:按FQDN而非IP进行访问控制

通过FQDN可以实现精确的跨环境服务定位,是分布式系统的基石之一。

posted on 2025-05-24 22:00  Leo-Yide  阅读(67)  评论(0)    收藏  举报