K8s之Ingress-nginx原理及配置
K8s之Ingress-nginx原理及配置
前言
在Kubernetes中,服务和Pod的IP地址仅可以在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用能够访问集群内的服务,在Kubernetes中目前提供了以下几种方案:
* NodePort* LoadBalancer* Ingress
在之前的博文中介绍过NodePort,简单来说,就是通过service这种资源对象,为后端pod提供一个统一的访问接口,然后将service的统一访问接口映射到群集节点上,最终实现client通过映射到群集节点上的端口访问到后端pod提供的服务。
但是,这种方式有一个弊端,就是当新生成一个pod服务就需要创建对应的service将其映射到节点端口,当运行的pod过多时,我们节点暴露给client端的端口也会随之增加,这样我们整个k8s群集的危险系数就会增加,因为我们在搭建群集之处,官方明确指出,必须关闭firewalld防火墙及清空iptables规则,现在我们又暴露了那么多端口给client,安全系数可想而知。
一、Ingress-nginx介绍
1、Ingress-nginx组成
* ingress-nginx-controller:根据用户编写的ingress规则(创建的ingress的yaml文件), 动态的去更改nginx服务的配置文件,并且reload重载使其生效(是自动化的,通过lua脚本来实现);* ingress资源对象:将Nginx的配置抽象成一个Ingress对象,每添加一个新的Service资 源对象只需写一个新的Ingress规则的yaml文件即可(或修改已存在的ingress规则的yaml文件)
2、Ingress-nginx可以解决什么问题?
1)动态配置服务 如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指 向我们新的k8s服务. 而如果用了Ingress-nginx, 只需要配置好这个服务, 当服务启动 时, 会自动注册到Ingress的中, 不需要而外的操作。 2)减少不必要的端口映射 配置过k8s的都清楚, 第一步是要关闭防火墙的, 主要原因是k8s的很多服务会 以NodePort方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅. 而Ingress可以避免这个问题, 除了Ingress自身服务可能需要映射出去, 其他服务都不要 用NodePort方式
3、Ingress-nginx工作原理
1)ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化, 2)然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置, 3)再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中, 4)然后reload一下使配置生效。以此达到域名分别配置和动态更新的问题。
二、配置Ingress-nginx
1、搭建registry私有仓库
搭建私有仓库的目的,仅仅是为了更快的获取镜像,如果网络稳定,也可跳过此步骤!
//运行registry私有仓库 [root@master ~]# docker run -tid --name registry -p 5000:5000 --restart always registry[root@master ~]# vim /usr/lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd --insecure-registry 192.168.45.129:5000 //修改完保存退出即可//将修改后的文件发送到k8s群集中的其他节点 [root@master ~]# scp /usr/lib/systemd/system/docker.service root@node01:/usr/lib/systemd/system/docker.service 100% 1628 1.6KB/s 00:00 [root@master ~]# scp /usr/lib/systemd/system/docker.service root@node02:/usr/lib/systemd/system/docker.service 100% 1628 1.6KB/s 00:00 [root@master ~]# systemctl daemon-reload [root@master ~]# systemctl restart docker.service //重启docker服务[root@master ~]# docker pull httpd //下载相应所需的镜像[root@master ~]# docker pull tomcat:8.5.45 [root@master ~]# docker tag httpd:latest 192.168.45.129:5000/httpd:v1[root@master ~]# docker tag tomcat:8.5.45 192.168.45.129:5000/tomcat:v1[root@master ~]# docker push 192.168.45.129:5000/httpd:v1 [root@master ~]# docker push 192.168.45.129:5000/tomcat:v1 //将下载好的镜像,上传到私有仓库
2、创建namespace(也可跳过,使用默认的default名称空间也可以,但需要删除下面所有yaml文件中关于自定义的名称空间的配置字段)
[root@master ~]# kubectl create ns test-ns //创建名称空间test-ns[root@master ~]# kubectl get ns //确认创建成功
3、创建Deployment、Service资源对象
1)创建httpd服务及其service与之关联
[root@master test]# vim httpd.yaml //编写基于httpd服务的资源对象kind: DeploymentapiVersion: extensions/v1beta1metadata: name: web01 namespace: test-nsspec: replicas: 3 //副本数量为三个 template: metadata: labels: app: httpd01 spec: containers: - name: httpd image: 192.168.45.129:5000/httpd:v1 ---apiVersion: v1kind: Servicemetadata: name: httpd-svc namespace: test-nsspec: selector: app: httpd01 ports: - protocol: TCP port: 80 targetPort: 80 //创建service资源对象与Deployment资源使用标签的方式进行关联 [root@master test]# kubectl apply -f httpd.yaml //执行yaml文件
2)创建tomcat服务及其service
[root@master test]# vim tomcat.yaml //编写yaml文件如下kind: DeploymentapiVersion: extensions/v1beta1metadata: name: web02 namespace: test-nsspec: replicas: 3 template: metadata: labels: app: tomcat01 spec: containers: - name: tomcat image: 192.168.45.129:5000/tomcat:v1 ---apiVersion: v1kind: Servicemetadata: name: tomcat-svc namespace: test-nsspec: selector: app: tomcat01 ports: - protocol: TCP port: 8080 targetPort: 8080 [root@master test]# kubectl apply -f tomcat.yaml //执行yaml文件
3)确保以上资源对象成功创建
[root@master ~]# kubectl get pod -n test-ns //确定pod是正常运行状态NAME READY STATUS RESTARTS AGE web01-85674fbdd7-7h6cc 1/1 Running 0 9m3s web01-85674fbdd7-9l2zm 1/1 Running 0 9m3s web01-85674fbdd7-p9hfx 1/1 Running 0 9m3s web02-7f8f755bc7-4qk9b 1/1 Running 0 7m22s web02-7f8f755bc7-qhbnm 1/1 Running 0 7m22s web02-7f8f755bc7-zr56g 1/1 Running 0 7m22s [root@master ~]# kubectl get svc -n test-ns //确认SVC创建成功NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc ClusterIP 10.102.57.12 <none> 80/TCP 10m tomcat-svc ClusterIP 10.110.25.145 <none> 8080/TCP 8m23s [root@master ~]# curl -I 10.102.57.12:80 //访问httpdHTTP/1.1 200 OKDate: Sun, 23 Aug 2020 07:02:06 GMTServer: Apache/2.4.46 (Unix)Last-Modified: Mon, 11 Jun 2007 18:53:14 GMTETag: "2d-432a5e4a73a80"Accept-Ranges: bytesContent-Length: 45Content-Type: text/html[root@master ~]# curl -I 10.110.25.145:8080 //访问tomcatHTTP/1.1 200 Content-Type: text/html;charset=UTF-8Transfer-Encoding: chunkedDate: Sun, 23 Aug 2020 09:08:18 GMT//OK,以上表示内部访问是没有问题的 //如果在上述访问测试中,没有访问到相应的pod,建议使用“kubectl describe svc”命令, 查看相应的service中的Endpoints列中有没有关联后端pod。
4、创建Ingress-nginx资源对象
下载我提供的:镜像以及yaml配置文件提取码:jtff
方法一如下:
[root@master ~]# rz [root@master ~]# vim mandatory.yaml //上传我提供的yaml配置文件,并编辑 spec: //这是在212行的spec字段 hostNetwork: true //添加此行,表示使用主机网络 # wait up to five minutes for the drain of connections terminationGracePeriodSeconds: 300 serviceAccountName: nginx-ingress-serviceaccount nodeSelector: kubernetes.io/os: linux containers: - name: nginx-ingress-controller image: registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.29.0 //上面是指定使用什么镜像,由于镜像下载速度过慢,我换成国内阿里云的镜像了
这里附加一张图片避免出错:
[root@master ~]# kubectl apply -f mandatory.yaml //执行yaml文件[root@master ~]# kubectl get pod -n ingress-nginx -o wide //确定Ingress-nginx的容器运行正常 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-ingress-controller-86cdd68cf-hndtw 1/1 Running 0 10m 192.168.45.141 node01 <none> <none>
方法二如下:
[root@master ~]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.29.0/deploy/static/mandatory.yaml[root@master ~]# vim mandatory.yaml //对下载的yaml文件进行简单的修改 spec: //定位到212行,也就是该行 hostNetwork: true //添加该行,表示使用主机网络 # wait up to five minutes for the drain of connections terminationGracePeriodSeconds: 300 serviceAccountName: nginx-ingress-serviceaccount nodeSelector: Ingress: nginx //设置节点的标签选择器,指定在哪台节点上运行 containers: - name: nginx-ingress-controller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.29.0//该资源使用的镜像,该镜像下载较慢,可使用步骤开头提供的网盘链接![root@master ~]# kubectl label nodes node01 Ingress=nginx#对node01节点打相应的标签,以便指定Ingress-nginx运行在node01[root@master ~]# kubectl get nodes node01 --show-labels//查看node01的标签是否存在[root@node01 ~]# docker load < nginx-ingress.tar //node01节点导入镜像(也可自行下载)[root@master ~]# kubectl apply -f mandatory.yaml
注:以上的两种方法,自行选择,本人用的是第一种方法!!!
5、定义Ingress规则(编写ingress的yaml文件)
[root@master~]# vim ingress.yaml //编写yaml文件如下apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress namespace: test-ns annotations: nginx.ingress.kubernetes.io/rewrite-target: /spec: rules: - host: www.test01.com http: paths: - path: / backend: serviceName: httpd-svc servicePort: 80 - path: /tomcat backend: serviceName: tomcat-svc servicePort: 8080[root@master~]# kubectl apply -f ingress.yaml //执行ingress规则的yaml文件[root@master~]# kubectl get ingresses -n test-ns //查看ingresses规则资源对象NAME HOSTS ADDRESS PORTS AGE test-ingress www.test01.com 80 28s
注:其实,至此已经实现了我们想要的功能,现在就可以通过www.test01.com 来访问到我们后端httpd容器提供的服务,通过www.test01.com/tomcat 来访问我们后端tomcat提供的服务,当然,前提是自行配置DNS解析,或者直接修改client的hosts文件。访问页面如下(注意:一定要自己解决域名解析的问题,若不知道域名对应的是哪个IP,请跳过这两个图,看下面的文字解释):
解决域名解析的问题如下:
[root@master ~]# kubectl get pod -n ingress-nginx -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-ingress-controller-86cdd68cf-hndtw 1/1 Running 0 89m 192.168.45.141 node01 <none> <none>
在上面的访问测试中,虽然访问到了对应的服务,但是有一个弊端,就是在做DNS解析的时候,只能指定Ingress-nginx容器所在的节点IP。而指定k8s集群内部的其他节点IP(包括master)都是不可以访问到的,如果这个节点一旦宕机,Ingress-nginx容器被转移到其他节点上运行(不考虑节点标签的问题,其实保持Ingress-nginx的yaml文件中默认的标签的话,那么每个节点都是有那个标签的)。随之还要我们手动去更改DNS解析的IP(要更改为Ingress-nginx容器所在节点的IP,通过命令“kubectl get pod -n ingress-nginx -o wide”可以查看到其所在节点),很是麻烦。
有没有更简单的一种方法呢?答案是肯定的,就是我们为Ingress-nginx规则再创建一个类型为nodePort的Service,这样,在配置DNS解析时,就可以使用www.test01.com 绑定所有node节点,包括master节点的IP了,很是灵活。
6、为Ingress规则创建一个Service
在刚才获取Ingress-controller资源对象的yaml文件的页面,然后下拉页面,即可看到以下,可以根据k8s集群环境来选择适合自己的yaml文件。假如自己是在Azure云平台搭建的K8s集群,则选择复制Azure下面的命令即可,我这里是自己的测试环境,所以选择Bare-metal下面的yaml文件,如图:
[root@master ~]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.29.0/deploy/static/provider/baremetal/service-nodeport.yaml//如若下载失败,直接复制粘贴编辑即可 [root@master ~]# cat service-nodeport.yaml apiVersion: v1 kind: Service metadata: name: ingress-nginx namespace: ingress-nginx labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx spec: type: NodePort ports: - name: http port: 80 targetPort: 80 protocol: TCP - name: https port: 443 targetPort: 443 protocol: TCP selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx//编辑完,保存退出即可 [root@master ~]# kubectl apply -f service-nodeport.yaml //执行yaml文件[root@master ~]# kubectl get svc -n ingress-nginx //查看运行的serviceNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.108.48.248 <none> 80:32529/TCP,443:30534/TCP 11s//可以看到service分别将80和443端口映射到了节点的32529和30543端口(随机映射的,也可以修改yaml文件指定端口)
注:至此,这个www.test01.com 的域名即可和群集中任意节点的32529/30543端口进行绑定了。
测试如下(域名解析对应的IP可以是k8s群集内的任意节点IP):
至此,就实现了最初的需求!!
7、创建基于虚拟主机的Ingress规则
如果现在是另一种需求,我需要将www.test01.com 和www.test02.com 都对应上我后端的httpd容器提供的服务,那么此时应该怎么配置?
[root@master test]# vim ingress.yaml #修改ingress规则的yaml文件如下apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress namespace: test-ns annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: www.test02.com //增加这一段host配置 http: paths: - path: / backend: serviceName: httpd-svc //绑定和www.test01相同的service名字即可 servicePort: 80 - host: www.test01.com http: paths: - path: / backend: serviceName: httpd-svc servicePort: 80 - path: /tomcat backend: serviceName: tomcat-svc servicePort: 8080//增加完上述的host字段保存退出即可[root@master test]# kubectl apply -f ingress.yaml //重新执行yaml文件
至此,即可实现访问www.test01.com 和www.test02.com 都可以访问到后端的httpd提供的页面(自行解决域名解析问题=域名解析配置client hosts文件即可),如下:
总结上述示例的pod是如何一步一步可以使client访问到的,总结如下:
后端pod===》service====》ingress规则====》写入Ingress-nginx-controller配置文件并自动重载使更改生效===》对Ingress-nginx创建service====》实现client无论通过哪个K8节点的IP+端口都可以访问到后端pod
三、配置HTTPS
在上面的操作中,实现了使用ingress-nginx为后端所有pod提供一个统一的入口,那么,有一个非常严肃的问题需要考虑,就是如何为我们的pod配置CA证书来实现HTTPS访问?在pod中直接配置CA么?那需要进行多少重复性的操作?而且,pod是随时可能被kubelet杀死再创建的。当然这些问题有很多解决方法,比如直接将CA配置到镜像中,但是这样又需要很多个CA证书。
这里有更简便的一种方法,就拿上面的情况来说,后端有多个pod,pod与service进行关联,service又被ingress规则发现并动态写入到ingress-nginx-controller容器中,然后又为ingress-nginx-controller创建了一个Service映射到群集节点上的端口,来供client来访问。
在上面的一系列流程中,关键的点就在于ingress规则,我们只需要在ingress的yaml文件中,为域名配置CA证书即可,只要可以通过HTTPS访问到域名,至于这个域名是怎么关联到后端提供服务的pod,这就是属于k8s群集内部的通信了,即便是使用http来通信,也无伤大雅。
配置如下:
接下来的配置与上面的配置基本没什么关系,但是由于上面已经运行了Ingress-nginx-controller容器,所以这里就没有必要再运行了。只需要配置pod、service、ingress规则即可。
//创建CA证书(测试环境,自己创建吧) [root@master https]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"//当前目录下会生成两个文件,如下: [root@master https]# ls #确定当前目录下有这两个文件tls.crt tls.key //将生成的CA证书存储到etcd [root@master https]# kubectl create secret tls tls-secret --key=tls.key --cert tls.crt//创建deploy、service、ingress资源对象 [root@master https]# vim httpd03.yaml //编写yaml文件kind: DeploymentapiVersion: extensions/v1beta1metadata: name: web03spec: replicas: 2 template: metadata: labels: app: httpd03 spec: containers: - name: httpd3 image: 192.168.45.129:5000/httpd:v1 ---apiVersion: v1kind: Servicemetadata: name: httpd-svc3spec: selector: app: httpd03 ports: - protocol: TCP port: 80 targetPort: 80 ---apiVersion: extensions/v1beta1kind: Ingressmetadata: name: test-ingress3spec: tls: - hosts: - www.test03.com secretName: tls-secret //这里是指定的是etcd存储的CA证书名称 rules: - host: www.test03.com http: paths: - path: / backend: serviceName: httpd-svc3 servicePort: 80 [root@master https]# kubectl apply -f httpd03.yaml //执行yaml文件
确认创建的资源对象是否正常运行:
[root@master https]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc3 ClusterIP 10.96.4.68 <none> 80/TCP 9s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d [root@master https]# kubectl get podNAME READY STATUS RESTARTS AGE web03-b955f886b-gjbnr 1/1 Running 0 7m23s web03-b955f886b-w8cdn 1/1 Running 0 7m23s [root@master https]# kubectl describe ingresses. //查看ingress规则Name: test-ingress3 Namespace: defaultAddress: Default backend: default-http-backend:80 (<none>) TLS: tls-secret terminates www.test03.com Rules: Host Path Backends ---- ---- -------- www.test03.com / httpd-svc3:80 (10.244.1.5:80,10.244.2.5:80) //确定关联到对应的service及后端的pod
注:使用https://www.test03.com 进行访问(自行解决域名解析问题)
©著作权归作者所有:来自51CTO博客作者哦姓卞的原创作品,如需转载,请注明出处,否则将追究法律责任
it社区论坛 http://www.137zw.com/