Kubernetes实战:部署多路Nginx网关及后端应用排错总结
项目目标: 在Kubernetes集群中部署多个独立的Web游戏应用(捕鱼、坦克、跳一跳),并通过一个统一的Nginx API网关对外提供服务,实现基于路径的路由。
核心组件与架构:
-
后端游戏应用 (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) 在集群内部暴露。
- 每个游戏是一个独立的Deployment,运行各自的Nginx实例(镜像是
-
API网关 (game-gateway):
- 一个独立的Deployment,运行标准
nginx:1.26镜像。 - 其Nginx配置 (
default.conf来自gateway-nginx-configConfigMap) 作为反向代理,监听80端口。 - 根据URL路径 (
/buyu/,/tanke/,/tiaoyitiao/) 将请求路由到对应的后端游戏Service。 - 通过NodePort类型的Service (
gateway-svc) 对外暴露,NodePort为8090。
- 一个独立的Deployment,运行标准
关键配置亮点与值得注意的点:
-
精巧的ConfigMap管理 (
01-all-cm.yaml,05-default.conf):games-nginx-config: 将所有后端应用的Nginx配置文件集中在一个ConfigMap中,每个应用的配置作为ConfigMap的一个datakey。subPath挂载: 在Deployment中,通过volumeMounts的subPath字段,将ConfigMap中特定的key(如buyu.conf)挂载为Pod内的单个文件。这避免了将整个ConfigMap作为一个目录挂载,使得配置管理更清晰、更细粒度。volumeMounts: - name: nginx-conf mountPath: /etc/nginx/conf.d/buyu.conf # 挂载为具体文件 subPath: buyu.conf # 对应ConfigMap的keygateway-nginx-config: 单独为网关Nginx定义ConfigMap,职责分离。
-
后端Nginx配置 (
buyu.conf等):alias指令: 用于提供静态文件服务,当location路径与文件系统路径不完全匹配时,alias比root更灵活。- URL标准化:
location = /buyu { return 301 /buyu/; }这样的配置确保用户访问不带尾部斜杠的路径时,会被重定向到带斜杠的路径,这对于try_files和相对路径的静态资源加载通常是必要的。 try_files: 实现优雅的前端路由回退,对于单页应用(SPA)非常有用。
-
网关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_passURL中的路径后,再追加剩余部分。例如,请求网关GET /buyu/index.html会被代理到http://buyu-svc:81/buyu/index.html。如果proxy_pass的URL末尾没有路径(如http://buyu-svc:81/;),则会将原始请求路径(匹配location后的部分)直接附加。
- 使用了短服务名 (
- 路径路由 (
排错历程与核心学习点 (这部分是博客的精华!):
-
Pod启动失败 (
CrashLoopBackOff):- 首要操作:
kubectl logs <pod-name> [-c <container-name>]查看容器日志,以及kubectl describe pod <pod-name>查看Pod事件和状态。这是定位问题的起点。
- 首要操作:
-
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校验器。
- 现象:
-
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插件的配置。
- 在任一Pod中执行
resolver指令的正确配置:- 最佳实践: 直接使用
kube-dnsService的ClusterIP作为resolver,如resolver 10.200.0.10 valid=5s;(这里的IP是你的kube-dnsService 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会优先使用它。
- 问题现象: Nginx启动日志报错
-
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>]。
- 同命名空间: 可以直接使用短服务名
-
镜像拉取策略 (
imagePullPolicy):- 如果镜像标签不是
:latest(如nginx:1.26),默认策略是IfNotPresent。Kubernetes会先检查Node本地是否有该镜像,有则使用,无则拉取。 - 如果本地有镜像但K8s仍然尝试拉取,请仔细核对Deployment中指定的镜像名和标签与本地
docker images显示的是否完全一致。
- 如果镜像标签不是
-
本地Docker镜像的跨节点使用:
- 当无法从远程仓库拉取镜像时(如Docker Hub被墙),可以通过
docker save <image> -o <image.tar>将镜像打包,scp到目标节点,再通过docker load -i <image.tar>导入。
- 当无法从远程仓库拉取镜像时(如Docker Hub被墙),可以通过
最终成功运行的关键因素:
- 修复了ConfigMap的YAML缩进。
- 正确理解并配置(或在同命名空间场景下省略)了Nginx的
resolver,并确保proxy_pass使用了可被正确解析的服务名。 - 确保所有后端服务和网关Pod都正常运行且网络可达。
给其他学习者的建议:
- 小步快跑,逐步验证: 不要一次性部署所有东西,先部署最小单元,验证通过后再扩展。
- 日志和事件是你的朋友:
kubectl logs和kubectl 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. 生产环境建议
- 服务通信:始终使用FQDN(如
service.namespace.svc.cluster.local) - 证书管理:确保证书包含所有使用的FQDN
- 网络策略:按FQDN而非IP进行访问控制
通过FQDN可以实现精确的跨环境服务定位,是分布式系统的基石之一。
浙公网安备 33010602011771号