问题描述
仅当在 Kubernetes Cluster 中部署 Ingress Controller 之后,我们定义的 Ingress 资源才能生效。当然 Ingress Controller 有很多实现,比如 NGINX Ingress Controller、Traefik Ingress Controller、HAProxy Ingress 等等(参考 Ingress Controllers | Kubernetes 页面);
针对我们的场景,我们使用 NGINX Ingress Controller 实现,这完全是基于我们具有很多 Nginx 使用经验,此外 Nginx 完全能满足我们的需要。即使日后 NGINX Ingress Controller 无法满足需求,我们亦可安装其他 Ingress Controller 实现,它们之间能够并存;
该笔记将记录:在 Kubernetes Cluster 中,如何部署 NGINX Ingress Controller 组件,以及相关问题处理;
解决方案
我们参照官方 Installation Guide 文档,并结合我们自身需求,我们整理出此文,所以该笔记是环境相关的,并不一定适合每种环境;
我们强烈建议参照官方文档,并结合自己的环境进行部署;
# 07/02/2022 该笔记是我们早期(12/09/2019)学习时的部署笔记,这里面里面存在许多不成熟的做法(比如 LoadBalancer 部分)。现在我们已经在集群中部署 MetalLB 组件,完全能够通过 LoadBalancer Service 来暴露 Nginx 服务,而不再需要使用 HostNetwork 这种做法。但是我们依旧保留该笔记,以用于日后可能会出现的某些特殊场景;
第一步、下载并且部署
我们为 Kubernetes v1.18.9 on Bare-metal 环境(自建 Kubernetes 集群);
如果是在 minikube、AWS、Azure 中部署,需要执行不同的命令,参考 Installation Guide 文档;
v1.4.0 ⇒ 1.25, 1.24, 1.23, 1.22
Helm Chart: 4.3.0
Manifest: https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.4.0/deploy/static/provider/cloud/deploy.yaml
官方提供 deploy.yaml 部署文件:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml kubectl apply -f deploy.yaml # 注意事项,因为 Deploymnet 的 nodeSelector: kubernetes.io/os: linux 设置, # 因此保证 lable 包含 kubernetes.io/os: linux 的节点存在于集群中,额外留意就好,一般无需设置; kubectl apply -f deploy.yaml
第二步、查看容器状态
查看 NGINX Ingress Controller 组件的状态:
# kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch NAMESPACE NAME READY STATUS RESTARTS AGE ingress-nginx ingress-nginx-admission-create-hq52n 0/1 Completed 0 6m52s ingress-nginx ingress-nginx-admission-patch-9xqhj 0/1 Completed 0 6m52s ingress-nginx ingress-nginx-controller-cdfb85746-5qqlp 1/1 Running 0 6m52s
第三步、关于 Ingress 访问
已知问题
在这之前,我们已经完成 Ingress 部署,但是还有个非常重要的问题:Ingress Controller 的本质作用是作为 WebServer 存在,接收 HTTP 与 HTTPS 请求,然后它根据 Ingress 资源的定义来完成请求的传发。但是 Ingress Controller 是以 Pod 运行的,它所监听的端口是内部网络可见,外部是无法直接访问的。即使通过 Service NodePort 暴露出来,也并非 80 443 端口,因此用户访问的时候,需要使用 http://hostname:nodePort/ 的形式来访问站点。很显然,这是不能接受的;
注意事项:我们这些的讨论都是基于 Bare-metal 部署的(即自建 Kubernetes 集群),其他部署环境也许会有类似问题;
解决方案
针对该问题,官方文档(Bare-metal considerations)提出以下解决方案:
1)MetalLB(纯软件解决方案)
2)NodePort
3)Host network
4)Using a self-provisioned edge(NodePort + LoadBalancer)
5)External IPs
篇幅原因,我们这里不再赘述,建议详细阅读官方文档;
我们的方案
针对我们的场景,方案 1 3 4 皆可,我们最终选择 4 方案(这里面有些不可抗力的存在)。而我们在测试环境中采用 3 方案,因为测试在内网,没有不是 LoadBalancer 服务。方案 1 其实是最好的,但是我们不想折腾了:-)
针对 4 方案,我们还有个需要考虑的问题:使用三层(TCP),还是七层(HTTP(s)):
最后,我们选择方案 4 的三层(TCP)做法。这么完全就是为了使用 Let’s Encrypt 免费证书。如果我们出现需要客户端正式地址的方案,我们将在部署七层(HTTP(s))。最后会形成“三层与七层并存,针对不用业务采用不同 LoadBalancer 线路”的做法;
实施方案
这里没有很多需要配置的东西,更多的是各种技术方案的取舍。唯一需要我们配置的就是:修改 Service 的 NodePort 为固定为 30080 30443 端口,然后配置 LoadBalancer 进行 80 443 的转发,这里不再展开;
在测试环境中
如果在测试环境中部署:
1)由于没有 Load Balancer 服务,所以将 Nginx Ingress Controller 运行在 hostNewwork 中:
# 修改 Pod 的 spec 配置,添加: ... template: spec: hostNetwork: true ...
2)还要将 Nginx Ingress Controller 从 Deployment 修改为 DaemonSet 类型,否则仅有某个节点的 80/443 能够访问;
3)针对 hostNetwork: true 设置,还需要修改 Pod 的 dnsPolicy 为 ClusterFirstWithHostNet 类型;
# TODO 我们有个问题未清楚:默认使用 ClusterFirst 类型,Ingress 依旧能够通过 Service 名称来访问 Service 服务;
还有个问题是关于 Ingress 的状态,但是在测试环境这个问题无关紧要;
第四步、部署测试资源
当部署 NGINX Ingress Controller 组件部署完成时,便能够定义 Ingress、Server、Deployment 资源进行验证;
定义资源文件并应用(./ingress-example.yaml):
kubectl apply -f ingress-example.yaml
然后,在浏览器中访问,以验证 Ingress 能够正常工作;
补充说明
部署 Ingress Controller 时,不建议使用 DaemonSet 类型,而应该使用 Deployment 类型,然后将 POD 示例固定在某个节点上,或者使用 NodePort 方式。为什么这样做?假如我们的集群有 1000 个节点时,在每个节点都运行 Ingress Controller 并不是最优的。如此大规模的集群,一般仅会有少数节点接受外部请求;
常见问题汇总
nginx-ingress-controller in pending state
当我们部署 deploy.yaml 后,ingress-nginx Pod 一直处于 Pending 状态。当我们部署自定义的 Deployment 进行测试时,该普通的 Deployment 也处于 Pending 状态;
当我们向集群添加新节点后,调度到新节点的 Pod 能够正常运行,而不是 Pending 状态。因此我们怀疑是节点存在问题,而且其他节点的 Docker 版本是 19.3 版本,而这个节点是 20.10 版本(都是自己埋的坑);
# 04/22/2021 因此我们尝试重启节点……然后问题得到解决(这并不算解决问题。也许是以为某些热修改的配置没有正确加载);
# 04/23/2021 今天这个问题又出现了,我们查看节点的 kubelet 日志,发现 CNI 插件相关的错误。该问题与 NGINX Ingress Controller 无关,因此这里不再讨论;
修改 Ingress 的 Address 参数
问题:在单台云服务器中,我们部署 Kubernetes 集群(单节点,即为控制平面,又作工作平面)。在云环境中,公网地址是通过 NAT 技术关联到服务器的,服务器本身仅有私网地址,所以 Ingress 资源中的 Address 字段为该私网地址。我们希望其为公网地址,以使其他集群组件能够正常工作(例如 External DNS)。
方案:通过修改 Ingress Controller Service 的 externalIPs 字段,来暗示 Nginx Ingress Controller 使用的地址。
参考文献
Accessing Kubernetes Pods from Outside of the Cluster
Deployments | Kubernetes
How to sign in kubernetes dashboard? – Stack Overflow
ingress is not listening on port 80 #4799
Installing the Ingress Controller
k8s 之 nginx-ingress、Daemonset 实现生产案例
NGINX Ingress Controller for Kubernetes
NGINX Ingress Controller/Installation Guide
NGINX Ingress Controller/Troubleshooting
Bare-metal considerations – NGINX Ingress Controller