在Kubernetes中,服务和Pod的IP地址仅在集群内部网络内部使用,对于集群的应用是不可见的。
为了使外部的应用能够访问集群内的服务,在Kubernetes目前提供了以下几种方案:
1)NodePort
2)LoadBalancer
3)Ingress
1)Ingress组成
Ingress 是反向代理规则,用来规定 HTTP/S 请求应该被转发到哪个 Service 上,比如根据请求中不同的 Host 和 url 路径让请求落到不同的 Service 上;
Ingress Controller 就是一个反向代理程序,它负责解析 Ingress 的反向代理规则,如果 Ingress 有增删改的变动,所有的 Ingress Controller 都会及时更新自己相应的转发规则,当 Ingress Controller 收到请求后就会根据这些规则将请求转发到对应的 Service;
2)Ingress工作原理
1)Ingress controller通过与Kubernetes api进行交互,动态的感知集群中Ingress规则的变化;
2)然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置;
3)再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中;
4)然后reload一下使配置生效。以此达到域名分别配置和动态更新的问题;
3) Ingress可以解决什么问题?
1)动态配置服务
如果是按照传统方式,当新增加一个服务时,我们可能需要在流量入口部署一台反向代理服务器指向我们新的K8s服务,而如果使用了Ingress,则只需配置好这个服务,当服务启动时,便会自动注册到Ingress中,不需要额外的操作;
2)减少不必要的端口映射
配置过k8s的都清楚, 第一步是要关闭防火墙的, 主要原因是k8s的很多服务会以NodePort方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅. 而Ingress可以避免这个问题, 除了Ingress自身服务可能需要映射出去, 其他服务都不用NodePort方式;
1)搭建registry私有仓库
搭建私有仓库的目的,仅仅是为了更快的获取镜像,如果网络稳定,也可跳过此步骤!
[root@master ~]# docker run -itd --name registry --restart=always -p 5000:5000 -v /registry:/var/lib/registry registry:2
#搭建registry私有仓库
[root@master ~]# vim /usr/lib/systemd/system/docker.service
#修改docker的配置文件,指向私有仓库
ExecStart=/usr/bin/dockerd --insecure-registry 192.168.10.52:5000
[root@master ~]# scp /usr/lib/systemd/system/docker.service root@node01:/usr/lib/systemd/system/
[root@master ~]# scp /usr/lib/systemd/system/docker.service root@node02:/usr/lib/systemd/system/
#将修改好的配置文件发送到Kubernetes集群中的各个节点
[root@master ~]# systemctl daemon-reload
[root@master ~]# systemctl restart docker
#所有节点都需重新启动服务
[root@master ~]# docker pull httpd
[root@master ~]# docker pull tomcat:8.5.45
[root@master ~]# docker tag httpd:latest 192.168.10.52:5000/httpd:v1
[root@master ~]# docker tag tomcat:8.5.45 192.168.10.52:5000/tomcat:v1
[root@master ~]# docker push 192.168.10.52:5000/httpd:v1
[root@master ~]# docker push 192.168.10.52:5000/tomcat:v1
#下载使用的镜像,并将其上传到私有仓库
2)创建用于测试的Pod
1)创建httpd服务及其service
[root@master ~]# vim httpd.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test-ns
#创建名称空间test-ns
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: httpd01
namespace: test-ns
spec:
replicas: 2
template:
metadata:
labels:
app: httpd-01
spec:
containers:
- name: httpd
image: 192.168.10.52:5000/httpd:v1
#使用httpd的镜像创建Deployment资源,副本数量为2个,并打标签为httpd-01
---
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
namespace: test-ns
spec:
selector:
app: httpd-01
ports:
- protocol: TCP
port: 80
targetPort: 80
#创建service资源对象与Deployment资源使用标签的方式进行关联
[root@master ~]# kubectl apply -f httpd.yaml
2)创建tomcat服务及其service
[root@master ~]# vim tomcat.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: tomcat01
namespace: test-ns
spec:
replicas: 2
template:
metadata:
labels:
app: tomcat-01
spec:
containers:
- name: tomcat
image: 192.168.10.52:5000/tomcat:v1
#使用tomcat的镜像创建Deployment资源,副本数量为2个,并打标签为tomcat-01
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-svc
namespace: test-ns
spec:
selector:
app: tomcat-01
ports:
- protocol: TCP
port: 8080
targetPort: 8080
#创建service资源对象与Deployment资源使用标签的方式进行关联
[root@master ~]# kubectl apply -f tomcat.yaml
3)确保以上资源对象成功创建
[root@master ~]# kubectl get pod -n test-ns #确保pod正常运行
NAME READY STATUS RESTARTS AGE
httpd01-84c6b66457-fkhf7 1/1 Running 0 3m2s
httpd01-84c6b66457-nlgmv 1/1 Running 0 3m2s
tomcat01-dcd778777-9sg2n 1/1 Running 0 2m20s
tomcat01-dcd778777-r62qd 1/1 Running 0 2m20s
[root@master ~]# kubectl get svc -n test-ns #确保service创建成功
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-svc ClusterIP 10.109.238.33 80/TCP 3m26s
tomcat-svc ClusterIP 10.104.198.78 8080/TCP 2m44s
[root@master ~]# curl -I 10.109.238.33:80
HTTP/1.1 200 OK
Date: Sun, 23 Aug 2020 00:55:25 GMT
Server: Apache/2.4.46 (Unix)
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
Content-Length: 45
Content-Type: text/html
[root@master ~]# curl -I 10.104.198.78:8080
HTTP/1.1 200
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 23 Aug 2020 00:55:32 GMT
#访问SVC的clusterIP+端口,确定可以访问到后端Pod
3)创建Ingress-controller资源对象
ingress-nginx资源所使用的镜像链接,
提取码:ltnx
#上传网盘提供的所有内容
[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
关于上面yaml文件中写入的“hostNetwork: true”具体解释:如果添加了此字段,意味着pod中运行的应用可以直接使用node节点的端口,这样node节点主机所在网络的其他主机,就可以通过访问该端口访问此应用。(类似于docker映射到宿主机的端口。)
[root@master ~]# kubectl get pod -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-ingress-controller-577ffd8c54-x6nqj 1/1 Running 0 2m10s 192.168.10.53 node01
4)创建Ingress资源对象
[root@master ~]# vim 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.123.com
http:
paths:
- path: /
backend:
serviceName: httpd-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: tomcat-svc
servicePort: 8080
#通过www.123.com 来访问到我们后端httpd容器提供的服务;
#通过www.123.com/tomcat 来访问我们后端tomcat提供的服务
[root@master ~]# kubectl apply -f ingress.yaml
ingress.extensions/test-ingress created
[root@master ~]# kubectl get ingresses. -n test-ns
NAME HOSTS ADDRESS PORTS AGE
test-ingress www.123.com 80 41m
其实,至此已经实现了我们想要的功能,前提是要配置DNS解析,或者直接修改client的hosts文件。
这里的ip是:
[root@master ~]# kubectl get pod -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-ingress-controller-577ffd8c54-x6nqj 1/1 Running 0 51m 192.168.10.53 node01
定义首页内容
[root@master ~]# kubectl get pod -n test-ns #查看pod名称
NAME READY STATUS RESTARTS AGE
httpd01-84c6b66457-fkhf7 1/1 Running 0 75m
httpd01-84c6b66457-nlgmv 1/1 Running 0 75m
tomcat01-dcd778777-9sg2n 1/1 Running 0 74m
tomcat01-dcd778777-r62qd 1/1 Running 0 74m
[root@master ~]# kubectl exec -it -n test-ns httpd01-84c6b66457-fkhf7 /bin/bash #进入容器修改首页内容
root@httpd01-84c6b66457-fkhf7:/usr/local/apache2# echo apache1 > htdocs/index.html
root@httpd01-84c6b66457-fkhf7:/usr/local/apache2# cat htdocs/index.html
apache1
[root@master ~]# kubectl exec -it -n test-ns httpd01-84c6b66457-nlgmv /bin/bash
root@httpd01-84c6b66457-nlgmv:/usr/local/apache2# echo apache2 > htdocs/index.html
root@httpd01-84c6b66457-nlgmv:/usr/local/apache2# cat htdocs/index.html
apache2
访问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.123.com 绑定所有node节点,包括master节点的IP了,很是灵活。
5)为Ingress-controller资源对象创建一个service资源对象
这里上面的网盘中有这个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
[root@master ~]# kubectl get svc -n ingress-nginx
#查看运行的service对象
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.110.148.71 80:30429/TCP,443:31382/TCP 6s
#可以看到service分别将80和443端口映射到了节点的30429和31382端口(随机映射的,也可以修改yaml文件指定端口)
至此,这个www.123.com 的域名即可和群集中任意节点的30429/31382端口进行绑定了。
测试如下(域名解析对应的IP可以是k8s群集内的任意节点IP):
还记得上面hosts里写的是
现在改为群集里的任意ip
6)创建基于虚拟主机的Ingress规则
如果需要将www.123.com 和www.321.com 都对应上后端的pod容器所运行的服务。应进行以下配置:
[root@master ~]# vim 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.123.com
http:
paths:
- path: /
backend:
serviceName: httpd-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: tomcat-svc
servicePort: 8080
- host: www.321.com #就是将原本的域名对应的svc服务复制一份,更改一下域名而已!
http:
paths:
- path: /
backend:
serviceName: httpd-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: tomcat-svc
servicePort: 8080
[root@master ~]# kubectl apply -f ingress.yaml
至此,即可实现访问www.123.com 和www.321.com 都可以访问到后端的httpd提供的页面(先要解决域名解析问题),如下:
Ingress-nginx资源的流程总结,如图:
从图中可以看出:后端有多个pod,pod与service进行关联,service又被ingress规则发现并动态写入到ingress-nginx-controller容器中,然后又为ingress-nginx-controller创建了一个Service映射到群集节点上的端口,来供client来访问。
在真正的生产环境中,创建Ingress Controller肯定使用的是DaemonSet资源类型,来保证Ingress Controller的高可用!
在上面的操作中,实现了使用ingress-nginx为后端所有pod提供一个统一的入口,那么,有一个非常严肃的问题需要考虑,就是如何为我们的pod配置CA证书来实现HTTPS访问?在pod中直接配置CA么?那需要进行多少重复性的操作?而且,pod是随时可能被kubelet杀死再创建的。当然这些问题有很多解决方法,比如直接将CA配置到镜像中,但是这样又需要很多个CA证书。
在上面的一系列流程中,关键的点就在于ingress规则,我们只需要在ingress的yaml文件中,为域名配置CA证书即可,只要可以通过HTTPS访问到域名,至于这个域名是怎么关联到后端提供服务的pod,这就是属于k8s群集内部的通信了,即便是使用http来通信,也无伤大雅。
配置如下:
为了简单起见,以下配置在上述环境下执行以下配置:
[root@master ~]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
#创建CA证书
[root@master ~]# ls | grep tls #确认产生了以下两个文件
tls.crt
tls.key
[root@master ~]# kubectl create secret tls tls-secret --key=tls.key --cert tls.crt
#创建secret资源对象
[root@master ~]# kubectl describe secrets tls-secret
#查看secret资源对象详细信息
Name: tls-secret
Namespace: default
Labels:
Annotations:
Type: kubernetes.io/tls
Data
====
tls.key: 1704 bytes
tls.crt: 1143 bytes
[root@master ~]# vim ingress.yaml
#对Ingresss资源对象进行修改
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: test-ns
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls: #在原本的基础上添加以下内容即可
- hosts:
- www.123.com
- www.321.com
secretName: tls-secret #填写须使用该证书的域名及证书的名称
rules:
- host: www.123.com
http:
paths:
- path: /
backend:
serviceName: httpd-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: tomcat-svc
servicePort: 8080
- host: www.321.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
[root@master ~]# kubectl describe ingresses. test-ingress -n test-ns
#查看Ingress-nginx的详细信息
Name: test-ingress
Namespace: test-ns
Address: 10.110.148.71
Default backend: default-http-backend:80 ()
TLS:
tls-secret terminates www.123.com,www.321.com
Rules:
Host Path Backends
---- ---- --------
www.123.com
/ httpd-svc:80 (10.244.1.2:80,10.244.2.2:80)
/tomcat tomcat-svc:8080 (10.244.1.3:8080,10.244.2.3:8080)
www.321.com
/ httpd-svc:80 (10.244.1.2:80,10.244.2.2:80)
/tomcat tomcat-svc:8080 (10.244.1.3:8080,10.244.2.3:8080)
访问时应注意433对应的端口进行访问!查看端口
[root@master ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.110.148.71 80:30429/TCP,443:31382/TCP 4h55m