文章目录
- [0 背景与架构](#0 背景与架构)
-
- [0.1 为什么需要ingress-nginx](#0.1 为什么需要ingress-nginx)
- [0.2 环境架构](#0.2 环境架构)
- [0.3 镜像拉取方案](#0.3 镜像拉取方案)
- [1 安装前准备](#1 安装前准备)
-
- [1.1 下载ingress-nginx部署文件](#1.1 下载ingress-nginx部署文件)
- [1.2 替换镜像地址](#1.2 替换镜像地址)
- [1.3 修复DNS解析问题](#1.3 修复DNS解析问题)
-
- [1.3.1 问题现象](#1.3.1 问题现象)
- [1.3.2 修复方法](#1.3.2 修复方法)
- [2 安装ingress-nginx](#2 安装ingress-nginx)
-
- [2.1 应用部署文件](#2.1 应用部署文件)
- [2.2 验证部署](#2.2 验证部署)
- [3 测试Ingress](#3 测试Ingress)
-
- [3.1 部署测试应用](#3.1 部署测试应用)
- [3.2 验证Ingress](#3.2 验证Ingress)
- [3.3 从宿主机访问](#3.3 从宿主机访问)
- [4 常见问题与解决](#4 常见问题与解决)
-
- [4.1 镜像拉取失败(ImagePullBackOff)](#4.1 镜像拉取失败(ImagePullBackOff))
- [4.2 DNS解析问题](#4.2 DNS解析问题)
- [4.3 EXTERNAL-IP一直pending](#4.3 EXTERNAL-IP一直pending)
- [5 总结](#5 总结)
- 感谢阅读
0 背景与架构
0.1 为什么需要ingress-nginx
在 《06_在k8s集群中安装MetalLB实现LoadBalancer》 中,我们通过 MetalLB 让裸金属集群的 LoadBalancer Service 获得了外部 IP。但 MetalLB 只解决了四层负载均衡 的问题,当我们需要基于域名或路径进行七层路由 (例如 foo.com 转发到 foo-service,bar.com 转发到 bar-service),就需要用到 Kubernetes 的 Ingress 资源。
然而,Ingress 本身只是一个声明式的规则对象,它需要 Ingress Controller 来真正解析这些规则并转发流量。ingress-nginx 是 Kubernetes 社区最流行的 Ingress Controller 实现,它以 Nginx 作为数据平面,能够动态监听 Ingress 资源变化并自动生成 Nginx 配置。
0.2 环境架构
| IP | 虚拟机名称 | 角色 | 说明 |
|---|---|---|---|
| 192.168.56.101 | rockylinux10-1 | control-plane | k8s master 节点 |
| 192.168.56.102 | rockylinux10-2 | worker | k8s worker 节点 |
| 192.168.56.103 | rockylinux10-3 | worker | k8s worker 节点 |
0.3 镜像拉取方案
由于国内网络限制,我们采用以下镜像源:
| 原始仓库 | 替换地址 | 用途 |
|---|---|---|
| registry.k8s.io | k8s.m.daocloud.io | DaoCloud 的 k8s 镜像代理 |
1 安装前准备
1.1 下载ingress-nginx部署文件
根据官方的文档 https://kubernetes.github.io/ingress-nginx/deploy/ 可知:
If you don't have Helm or if you prefer to use a YAML manifest, you can run the following command instead:
bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/cloud/deploy.yaml
由于虚拟机内直接访问 raw.githubusercontent.com 会超时,我们在 Mac 宿主机 上下载 YAML 文件,然后传输到虚拟机。
在 Mac 上执行:
bash
curl -L -o ingress-nginx-deploy.yaml https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/cloud/deploy.yaml
传输到 master 节点:
bash
scp ingress-nginx-deploy.yaml root@192.168.56.101:/root/
在 master 节点上创建目录并移动文件:
bash
# 在 rockylinux10-1 上执行
mkdir -p /root/ingress-nginx
mv /root/ingress-nginx-deploy.yaml /root/ingress-nginx/
cd /root/ingress-nginx
1.2 替换镜像地址
ingress-nginx-deploy.yaml 中的镜像默认来自 registry.k8s.io,国内拉取会超时。根据 DaoCloud 镜像加速 https://gitee.com/daocloud/public-image-mirror 的说明,将 registry.k8s.io 替换为 k8s.m.daocloud.io:
bash
sed -i 's/registry.k8s.io/k8s.m.daocloud.io/g' ingress-nginx-deploy.yaml
验证替换结果:
bash
grep "image:" ingress-nginx-deploy.yaml
期望输出:
yaml
image: k8s.m.daocloud.io/ingress-nginx/controller:v1.15.1@sha256:594ceea76b01c592858f803f9ff4d2cb40542cae2060410b2c95f75907d659e1
image: k8s.m.daocloud.io/ingress-nginx/kube-webhook-certgen:v1.6.9@sha256:01038e7de14b78d702d2849c3aad72fd25903c4765af63cf16aa3398f5d5f2dd
image: k8s.m.daocloud.io/ingress-nginx/kube-webhook-certgen:v1.6.9@sha256:01038e7de14b78d702d2849c3aad72fd25903c4765af63cf16aa3398f5d5f2dd
注意:
kube-webhook-certgen镜像出现了两次,分别用于admission-create和admission-patch两个 Job,这是正常的。
1.3 修复DNS解析问题
1.3.1 问题现象
完成镜像地址替换后,执行 kubectl apply 时 Pod 报 ErrImagePull:
text
failed to pull image "k8s.m.daocloud.io/ingress-nginx/kube-webhook-certgen:v1.6.9@sha256:..."
failed to resolve reference "...": dial tcp: lookup m.daocloud.io on 192.168.1.1:53: no such host
但此时在节点上执行 ping k8s.m.daocloud.io 却可以成功解析。这是因为 glibc 和 Go 的 DNS 解析器行为不同 :ping 使用 glibc 解析器,遇到 NXDOMAIN 会继续尝试下一个 DNS;而 containerd 使用 Go 解析器,遇到 NXDOMAIN 直接放弃。节点的 /etc/resolv.conf 中 192.168.1.1 无法解析公网域名,containerd 在第一个 DNS 就失败了。
1.3.2 修复方法
在 所有 K8s 节点 (rockylinux10-1、10-2、10-3)上,为 NAT 网卡 enp0s9 配置可解析公网域名的 DNS:
bash
# 配置 DNS,将公共 DNS 放在最前面
nmcli connection modify "enp0s9" \
ipv4.dns "114.114.114.114 8.8.8.8 192.168.1.1 192.168.0.1" \
ipv4.ignore-auto-dns yes
# 重启网卡使配置生效
nmcli connection down "enp0s9" && nmcli connection up "enp0s9"
验证 DNS 配置:
bash
cat /etc/resolv.conf
期望输出:
text
# Generated by NetworkManager
nameserver 114.114.114.114
nameserver 8.8.8.8
nameserver 192.168.1.1
# NOTE: the libc resolver may not support more than 3 nameservers.
# The nameservers listed below may not be recognized.
nameserver 192.168.0.1
nameserver fd17:625c:f037:3::3
说明 :
114.114.114.114和8.8.8.8已经排在最前面,containerd 使用 Go 解析器时会优先用它们解析公网域名,因此无需重启 containerd ,直接执行kubectl apply即可成功拉取镜像。
2 安装ingress-nginx
2.1 应用部署文件
在 master 节点(rockylinux10-1)上执行:
bash
cd /root/ingress-nginx
kubectl apply -f ingress-nginx-deploy.yaml
2.2 验证部署
在 master 节点(rockylinux10-1)上执行:
bash
kubectl get all -n ingress-nginx
期望输出(刚执行完 kubectl apply 几秒后的状态):
text
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-psjcd 0/1 Completed 0 8s
pod/ingress-nginx-admission-patch-hjxn2 0/1 Completed 0 6s
pod/ingress-nginx-controller-6c9bd4845d-dfcvr 1/1 Running 0 10s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.1.47.212 192.168.56.240 80:31922/TCP,443:32639/TCP 10s
service/ingress-nginx-controller-admission ClusterIP 10.1.192.23 <none> 443/TCP 10s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 10s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-6c9bd4845d 1 1 1 10s
NAME COMPLETIONS DURATION AGE
job.batch/ingress-nginx-admission-create 1/1 5s 8s
job.batch/ingress-nginx-admission-patch 1/1 4s 6s
关键点说明:
ingress-nginx-admission-create和ingress-nginx-admission-patch是两个 Job,COMPLETIONS 为1/1,对应的 Pod 状态为Completed,表示它们已经成功执行完成。ingress-nginx-controller的 Pod 状态为Running,Service 类型为LoadBalancer,EXTERNAL-IP由 MetalLB 分配。- 这两个 Job 生成的 Pod 会在执行完成后保留一段时间,随后会被自动清理,因此过一段时间再执行
kubectl get all可能就看不到它们了。届时可以用kubectl get events确认它们曾经执行成功。
查看 Job 相关的 events 记录的命令:
bash
kubectl get events -n ingress-nginx | grep ingress-nginx-admission
3 测试Ingress
3.1 部署测试应用
创建文件 kiada-ingress.yaml:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kiada
spec:
replicas: 2
selector:
matchLabels:
app: kiada
template:
metadata:
labels:
app: kiada
spec:
containers:
- name: kiada
# 这个镜像要自己用 k8s in action 相关的代码打镜像
image: m.daocloud.io.localcache.registry:55000/kiada:0.5
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: kiada
spec:
selector:
app: kiada
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kiada-ingress
spec:
ingressClassName: nginx
rules:
- host: kiada.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kiada
port:
number: 80
应用:
bash
kubectl apply -f kiada-ingress.yaml
3.2 验证Ingress
bash
kubectl get ingress
期望输出:
text
NAME CLASS HOSTS ADDRESS PORTS AGE
kiada-ingress nginx kiada.example.com 192.168.56.240 80 30s
3.3 从宿主机访问
由于 kiada.example.com 是虚构域名,需要在 Mac 的 /etc/hosts 中添加解析,将域名指向 Ingress Controller 的外部 IP。
在 Mac 上执行:
bash
echo "192.168.56.240 kiada.example.com" | sudo tee -a /etc/hosts
192.168.56.240就是前面通过 MetalLB 分配给ingress-nginx-controller的EXTERNAL-IP,可以通过kubectl get svc -n ingress-nginx查看实际值。
然后在 Safari 浏览器 中访问 http://kiada.example.com,应该能看到 Kiada 页面。
注意:Chrome 浏览器可能因安全策略无法访问,推荐使用 Safari。
4 常见问题与解决
4.1 镜像拉取失败(ImagePullBackOff)
现象 :Pod 处于 ImagePullBackOff,错误信息包含 no such host。
排查:
bash
kubectl describe pod -n ingress-nginx <pod-name> | grep -E "Image:|Failed"
解决 :确认 registry.k8s.io 已替换为 k8s.m.daocloud.io,并检查所有节点的 DNS 配置是否正确。
4.2 DNS解析问题
现象 :ping 能通,但 containerd 拉取镜像时报 no such host。
原因 :glibc 和 Go DNS 解析器对 NXDOMAIN 的处理不同。
解决 :参考第 1.3 节,在所有节点上通过 nmcli 配置公共 DNS。
4.3 EXTERNAL-IP一直pending
现象 :ingress-nginx-controller 的 EXTERNAL-IP 始终为 <pending>。
原因:裸金属集群缺少 LoadBalancer 实现。
解决 :参考 《06_在k8s集群中安装MetalLB实现LoadBalancer》,安装 MetalLB 并配置 IP 地址池。
5 总结
通过本文,我们完成了以下工作:
- 下载了 ingress-nginx 部署文件:从 Mac 宿主机下载并传输到 master 节点
- 替换了镜像地址 :将
registry.k8s.io替换为k8s.m.daocloud.io - 修复了 DNS 解析问题 :理解了 glibc 和 Go 解析器的差异,通过
nmcli配置公共 DNS - 成功安装了 ingress-nginx:验证了 Pod、Service 和 IngressClass 正常创建
- 测试了 Ingress 路由 :通过
/etc/hosts解析,验证了域名路由功能