在Kubernetes中,服务和Pod的IP地址仅能够在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用可以访问集群内的服务,在Kubernetes中目前提供了如下几种方案:html
- NodePort
- LoadBalancer
- Ingress
NodePort,简单来讲,就是经过service这种资源对象,为后端pod提供一个统一的访问接口,而后将service的统一访问接口映射到群集节点上,最终实现client经过映射到群集节点上的端口访问到后端pod提供的服务。node
可是,这种方式有一个弊端,就是当新生成一个pod服务就须要建立对应的service将其映射到节点端口,当运行的pod过多时,咱们节点暴露给client端的端口也会随之增长,这样咱们整个k8s群集的危险系数就会增长,由于咱们在搭建群集之处,官方明确指出,必须关闭firewalld防火墙及清空iptables规则,如今咱们又暴露了那么多端口给client,安全系数可想而知。nginx
有没有更安全又简便的一种方法呢?答案是确定的,就是来利用Ingress这种资源对象来实现。git
- ingress-nginx-controller:根据用户编写的ingress规则(建立的ingress的yaml文件),动态的去更改nginx服务的配置文件,而且reload重载使其生效(是自动化的,经过lua脚原本实现);
- ingress资源对象:将Nginx的配置抽象成一个Ingress对象,每添加一个新的Service资源对象只需写一个新的Ingress规则的yaml文件便可(或修改已存在的ingress规则的yaml文件)
一、动态配置服务
若是按照传统方式, 当新增长一个服务时, 咱们可能须要在流量入口加一个反向代理指向咱们新的k8s服务. 而若是用了Ingress-nginx, 只须要配置好这个服务, 当服务启动时, 会自动注册到Ingress的中, 不须要而外的操做。
二、减小没必要要的端口映射
配置过k8s的都清楚, 第一步是要关闭防火墙的, 主要缘由是k8s的不少服务会以NodePort方式映射出去, 这样就至关于给宿主机打了不少孔, 既不安全也不优雅. 而Ingress能够避免这个问题, 除了Ingress自身服务可能须要映射出去, 其余服务都不要用NodePort方式github
一、ingress controller经过和kubernetes api交互,动态的去感知集群中ingress规则变化;
二、而后读取它,按照自定义的规则,规则就是写明了哪一个域名对应哪一个service,生成一段nginx配置;
三、再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中;
四、而后reload一下使配置生效。以此达到域名分别配置和动态更新的问题;web
注:docker
//运行registry私有库 [root@docker-k8s01 ~]# docker run -itd --name registry -p 5000:5000 --restart always registry //编辑配置文件,指定私有仓库 [root@docker-k8s01 ~]# vim /usr/lib/systemd/system/docker.service ExecStart=/usr/bin/dockerd -H unix:// --insecure-registry 192.168.171.151:5000 [root@docker-k8s01 ~]# vim /usr/lib/systemd/system/docker.service //将配置好的文件发放到其余两台节点 [root@docker-k8s01 ~]# scp /usr/lib/systemd/system/docker.service docker-k8s02:/usr/lib/systemd/system/ [root@docker-k8s01 ~]# scp /usr/lib/systemd/system/docker.service docker-k8s03:/usr/lib/systemd/system/ //在各个节点执行以下命令,重启docker使配置生效 [root@docker-k8s01 ~]# systemctl daemon-reload [root@docker-k8s01 ~]# systemctl restart docker [root@docker-k8s01 ~]# docker push 192.168.171.151:5000/httpd:v1 [root@docker-k8s01 ~]# docker push 192.168.171.151:5000/tomcat:v1
//建立名称空间test-ns [root@docker-k8s01 ~]# kubectl create ns test-ns //肯定建立成功 [root@docker-k8s01 ~]# kubectl get ns NAME STATUS AGE test-ns Active 6s
//建立httpd服务及servicevim
--- kind: Deployment apiVersion: extensions/v1beta1 metadata: name: web01 namespace: test-ns spec: replicas: 3 template: metadata: labels: app: httpd01 spec: containers: - name: httpd image: 192.168.171.151:5000/httpd:v1 --- apiVersion: v1 kind: Service metadata: name: httpd-svc namespace: test-ns spec: selector: app: httpd01 ports: - protocol: TCP port: 80 targetPort: 80 //执行yaml文件 [root@docker-k8s01 test]# kubectl apply -f httpd-01.yaml
//建立tomcat服务及service后端
--- kind: Deployment apiVersion: extensions/v1beta1 metadata: name: web02 namespace: test-ns spec: replicas: 3 template: metadata: labels: app: tomcat01 spec: containers: - name: tomcat image: 192.168.171.151:5000/tomcat:v1 --- apiVersion: v1 kind: Service metadata: name: tomcat-svc namespace: test-ns spec: selector: app: tomcat01 ports: - protocol: TCP port: 8080 targetPort: 8080 //执行yaml文件 [root@docker-k8s01 test]# kubectl apply -f tomcat-01.yaml
//肯定成功建立上述资源对象api
//肯定pod是正常运行状态 [root@docker-k8s01 test]# kubectl get po -n test-ns NAME READY STATUS RESTARTS AGE web01-6849f7dd96-cglhg 1/1 Running 0 8m25s web01-6849f7dd96-n57kx 1/1 Running 0 8m25s web01-6849f7dd96-r6m26 1/1 Running 0 8m25s web02-65b5798c6-8hzf9 1/1 Running 0 77s web02-65b5798c6-r7wrl 1/1 Running 0 77s web02-65b5798c6-zxx8h 1/1 Running 0 77s //肯定SVC已经成功建立 [root@docker-k8s01 test]# kubectl get svc -n test-ns NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc ClusterIP 10.107.178.33 <none> 80/TCP 8m8s tomcat-svc ClusterIP 10.110.129.210 <none> 8080/TCP 94s //访问SVC的clusterIP+端口,肯定能够访问到后端Pod [root@docker-k8s01 test]# curl -I 10.107.178.33:80 //访问httpd HTTP/1.1 200 OK Date: Sat, 12 Sep 2020 03:35:27 GMT Server: Apache/2.4.46 (Unix) //版本号也可看到 Last-Modified: Sat, 12 Sep 2020 03:05:22 GMT ETag: "11-5af15126f2080" Accept-Ranges: bytes Content-Length: 17 Content-Type: text/html [root@docker-k8s01 test]# curl -I 10.105.121.123:8080 //访问tomcat HTTP/1.1 200 Content-Type: text/html;charset=UTF-8 Transfer-Encoding: chunked Date: Sat, 12 Sep 2020 04:04:22 GMT #若是在上述访问测试中,没有访问到相应的pod,建议使用“kubectl describe svc”命令, #查看相应的service中的Endpoints列中有没有关联后端pod。
方法1:去gitlab搜索Ingress-nginx,点击“deploy”,再点击页面下的跳转连接,便可看到以下命令:
//这里先不要直接复制命令到终端先将yaml下载下来 [root@docker-k8s01 test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml //编辑其yaml文件 [root@docker-k8s01 test]# vim mandatory.yaml spec: //这是212行的spec字段 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: registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.29.0 //这里我替换为了阿里云的镜像 //上面是指定使用什么镜像,若须要更改镜像名称,改这里便可 //对docker-k8s02打上相应的标签,以便指定ingress-nginx运行在docker-k8s02上 [root@docker-k8s01 test]# kubectl label nodes docker-k8s02 Ingress=nginx //执行以下命令查看标签是否存在 [root@docker-k8s01 test]# kubectl get nodes docker-k8s02 --show-labels //切换到k8s02节点上执行以下命令,手动导入Ingress-nginx镜像(自行上传) [root@docker-k8s02 ~]# docker load < nginx-ingress-controller-0.29.0.tar //回到k8s01节点执行yaml文件 [root@docker-k8s01 test]# kubectl apply -f mandatory.yaml
关于上面yaml文件中写入的“hostNetwork: true”具体解释:若是使用此网络参数,那么pod中运行的应用程序能够直接使用Node节点端口,这样node节点主机所在的网络的其余主机,均可以经过访问到此应用程序。
肯定Ingress-nginx的容器运行正常
[root@docker-k8s01 test]# kubectl get pod -n ingress-nginx -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-ingress-controller-845fbb7b4b-5gtkc 0/1 ImagePullBackOff 0 70s 192.168.171.150 docker-k8s02 <none> <none>
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 //执行ingress规则的yaml文件 [root@docker-k8s01 test]# kubectl apply -f ingress.yaml //查看ingress规则资源对象 [root@docker-k8s01 test]# kubectl get ingresses -n test-ns NAME HOSTS ADDRESS PORTS AGE test-ingress www.test01.com 80 14s
其实,至此已经实现了咱们想要的功能,如今就能够经过www.test01.com 来访问到咱们后端httpd容器提供的服务,经过www.test01.com/tomcat 来访问咱们后端tomcat提供的服务,固然,前提是自行配置DNS解析,或者直接修改client的hosts文件。访问页面以下(注意:必定要本身解决域名解析的问题,若不知道域名对应的是哪一个IP,请跳过这两个图,看下面的文字解释):
//访问httpd服务
//访问Tomcat
在上面的访问测试中,虽然访问到了对应的服务,可是有一个弊端,就是在作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了,非常灵活。
刚才找到Ingress-nginx的yaml文件的页面,而后下拉页面,便可看到如下,能够根据k8s集群环境来选择适合本身的yaml文件,假如本身是在Azure云平台搭建的K8s集群,则选择复制Azure下面的命令便可,我这里是本身的测试环境,因此选择Bare-metal下面的yaml文件:
建立这个Service有两种方法,一是直接复制其web页面的命令到master节点上执行,二是将其连接地址复制到终端使用wget下载下来再执行,我选择第二种方法,由于我想看看里面的内容:
[root@docker-k8s01 test]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml //查看内容,不须要修改 [root@docker-k8s01 test]# 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 --- //执行yaml文件 [root@docker-k8s01 test]# kubectl apply -f service-nodeport.yaml service/ingress-nginx created //查看运行的service [root@docker-k8s01 test]# kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx NodePort 10.104.185.116 <none> 80:30483/TCP,443:32362/TCP 8s //能够看到service分别将80和443端口映射到了节点的30483和32362端口(随机映射的,也能够修改yaml文件指定端口)
至此,域名解析对应的IP能够是k8s群集内的任意节点IP
基于虚拟主机的Ingress规则
若是如今是另外一种需求,我须要将www.test01.com 和www.test02.com 都对应上我后端的httpd容器提供的服务,那么此时应该怎么配置?
//修改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 //从新执行yaml文件 [root@docker-k8s01 test]# kubectl apply -f ingress.yaml
访问www.test01.com:30483
访问www.test02.com:30483
上述示例的pod是如何一步一步可使client访问到的呢?
后端Pod---->service---->ingress规则---->写入ingress-nginx-controller配置文件并自动重载使之生效---->对ingress-nginx建立service---->实现client不管经过哪一个k8节点的IP+端口均可以访问到后端pod
在上面的操做中,实现了使用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@docker-k8s01 https]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc" //查看生成的两个文件 [root@docker-k8s01 https]# ls tls.crt tls.key //将生成的CA证书存储到etcd [root@docker-k8s01 https]# kubectl create secret tls tls-secret --key=tls.key --cert tls.crt //建立deploy、service、ingress资源对象 //编写yaml配置文件 kind: Deployment apiVersion: extensions/v1beta1 metadata: name: web03 spec: replicas: 2 template: metadata: labels: app: httpd03 spec: containers: - name: httpd3 image: 192.168.171.151:5000/httpd:v1 --- apiVersion: v1 kind: Service metadata: name: httpd-svc3 spec: selector: app: httpd03 ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress3 spec: tls: - hosts: - www.test03.com secretName: tls-secret rules: - host: www.test03.com http: paths: - path: / backend: serviceName: httpd-svc3 servicePort: 80 //执行yaml文件 [root@docker-k8s01 https]# kubectl apply -f httpd02.yaml
//确认建立的资源对象正常运行
//查看svc [root@docker-k8s01 https]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc3 ClusterIP 10.110.199.104 <none> 80/TCP 5m35s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 26d //查看pod [root@docker-k8s01 https]# kubectl get pod NAME READY STATUS RESTARTS AGE web03-c496b69f5-7288z 1/1 Running 0 6m23s web03-c496b69f5-hnmrg 1/1 Running 0 6m23s //查看ingress规则 [root@docker-k8s01 https]# kubectl describe ingresses Name: test-ingress3 Namespace: default Address: 10.104.185.116 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.8:80,10.244.2.8:80) //肯定关联到对应的service及后端pod
//https访问测试
访问https://www.test03.com