本文由ilanniweb微信公众号提供友情赞助,首发于烂泥行天下
IT技术分享QQ群:571981257
最近一直在学习有关k8s相关的知识,中间的坑比较多,今天就把有关ingress的安装介绍下。
有关k8s的安装,我这边使用的kubeasz这个项目进行安装的。kubeasz是一个基于二进制方式、利用ansible-playbook实现自动化部署k8s的项目,项目地址为:
https://github.com/gjmzj/kubeasz
如果觉得下载比较慢的话,也可以从可下面这个地址下载:
https://gitee.com/ilanni/kubeasz
在部署过程使用到的二进制文件,由于是在百度网盘中,众所周知的原因,下载速度就不需要多说了。为了提高下载速度,我这边把已经下载镜像文件上传到了阿里云的cdn上,下载地址如下:
http://share.ilanni.com/f/15323800-325473895-6f8200
由于其中的镜像会和kubeasz的镜像进行更新,为了保持上述连接的稳定,所以该链接会先跳转到一个页面,下载其中的一个文件,然后该文件中有实际的镜像下载地址。
这其中包含k8s相关的镜像,以及harbor的离线安装包。
K8s集群对外暴露服务的方式目前只有三种:loadblancer、nodeport、ingress。前两种熟悉起来比较快,而且使用起来也比较方便,在此就不进行介绍了。
下面详细讲解下ingress这个服务,ingress由两部分组成:ingress controller和ingress服务。
其中ingress controller目前主要有两种:基于nginx服务的ingress controller和基于traefik的ingress controller。
而其中traefik的ingress controller,目前支持http和https协议。由于对nginx比较熟悉,而且需要使用TCP负载,所以在此我们选择的是基于nginx服务的ingress controller。
但是基于nginx服务的ingress controller根据不同的开发公司,又分为k8s社区的ingres-nginx和nginx公司的nginx-ingress。
在此根据github上的活跃度和关注人数,我们选择的是k8s社区的ingres-nginx。
k8s社区提供的ingress,github地址如下:
https://github.com/kubernetes/ingress-nginx
nginx社区提供的ingress,github地址如下:
https://github.com/nginxinc/kubernetes-ingress
有关nginx社区提供的ingress使用方式,我们会在后续的文章中介绍。
ingress具体的工作原理如下:
ingress contronler通过与k8s的api进行交互,动态的去感知k8s集群中ingress服务规则的变化,然后读取它,并按照定义的ingress规则,转发到k8s集群中对应的service。
而这个ingress规则写明了哪个域名对应k8s集群中的哪个service,然后再根据ingress-controller中的nginx配置模板,生成一段对应的nginx配置。
然后再把该配置动态的写到ingress-controller的pod里,该ingress-controller的pod里面运行着一个nginx服务,控制器会把生成的nginx配置写入到nginx的配置文件中,然后reload一下,使其配置生效。以此来达到域名分配置及动态更新的效果。
为了本次试验,我们需要使用到httpd:alpine、tomcat:alpine和nginx-ingress-controller:0.21.0这三个镜像。
拉取镜像,使用如下命令:
docker pull httpd:alpine
docker pull tomcat:alpine
拉取nginx-controller镜像如下:
docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0 --实际用了这个
或者
docker pull registry.cn-hangzhou.aliyuncs.com/ilanni/nginx-ingress-controller:0.21.0
镜像拉取完毕后,我们开始进行其他操作。
为了进行本次试验,我们首先要从头配置相关的信息。
k8s中是通过namespace进行资源隔离的,所以在此我们先要创建一个namespace,命名为ilanni-namespace。使用如下命令:
cat > ilanni_namespace.yml << “EOF”
apiVersion: v1
kind: Namespace
metadata:
name: ilanni-namespace
labels:
name: ilanni-namespace
EOF
执行如下命令:
kubectl apply -f ilanni-namespace.yml –record
查询刚刚创建的namespace,使用如下命令:
kubectl get ns
通过上图,我们可以很明显的看到ilanni-namespace,这个namespace已经创建成功。
当然也可以,直接使用kubectl create ns ilanni-namespace命令进行创建。
Namespace创建完毕后,我们就可以在这个namespace下创建service。使用如下命令:
cat > ilanni-httpd-service.yml << “EOF”
apiVersion: v1
kind: Service
metadata:
name: ilanni-httpd-svc
namespace: ilanni-namespace
spec:
type: NodePort
ports:
– name: http-port
nodePort: 28080
port: 80
targetPort: 80
selector:
app: ilanni-httpd-dm
EOF
在ilanni-httpd-service.yml这个k8s的yml文件中,我们在ilanni-namespace这个namespace中,定义一个名为ilanni-httpd-svc的Service,并且使用NodePort这个service类型,使用28080端口对外提供访问端口。
同时这个yml文件中,我们也使用selector来选择后端的Deployment,在此我们选择的是ilanni-httpd-dm,这个Deployment。
现在执行kubectl apply命令使该文件生效,如下:
kubectl apply -f ilanni-httpd-service.yml –record
kubectl get svc -n ilanni-namespace -o wide
通过上图,我们可以很明显的看出ilanni-httpd-svc这个service分配的IP地址为10.68.184.223.
注意:上述IP地址k8s集群内部的地址。外部是无法访问的。
以上service创建完毕后,我们就要创建deployment。
现在我们来创建deployment,命令如下:
cat > ilanni-httpd-deployment.yml << “EOF”
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ilanni-httpd
namespace: ilanni-namespace
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
#由于replicas为2,则整个升级,pod个数在1-3个之间
rollingUpdate:
##滚动升级时会先启动1个pod
maxSurge: 1
##滚动升级时允许的最大Unavailable的pod个数
maxUnavailable: 0
template:
metadata:
labels:
app: ilanni-httpd-dm
spec:
##k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒
terminationGracePeriodSeconds: 60
restartPolicy: Always
containers:
– name: ilanni-httpd
image: httpd:alpine
imagePullPolicy: Always
EOF
在ilanni-httpd-deployment.yml这个k8s的yml文件中,我们在ilanni-namespace这个namespace中,定义一个名为ilanni-httpd的Deployment,并且定义了一个ilanni-httpd-dm的labels。
这样ilanni-httpd-svc这个Service就和ilanni-httpd这个Deployment关联起来了。
同时在这个yml文件中,我们也定义了该pod的发布策略,以及副本集个数。
现在执行kubectl apply命令使该文件生效,如下:
kubectl apply -f ilanni-httpd-deployment.yml –record
kubectl get deploy -n ilanni-namespace -o wide
上述截图中,可以很明显的看出我们通过yml文件定义的相关信息。
Deployment部署完毕后,相应的pod也正常启动。现在我们来看下pod的运行情况如下,使用如下命令:
kubectl get pod -n ilanni-namespace -o wide
通过上图,我们可以很明显的看出目前有两个ilanni-httpd pod运行在192.168.123.6和192.168.123.7节点,而且pod里面的IP地址也分别是172.20.3.18和172.20.1.15。
这里面需要注意下每个pod所被分配的IP地址,是根据该pod所在node节点cni0网卡的IP地址段来分配的。
我们现在可以查看192.168.123.7这个node节点的cni0网卡的IP地址段,如下:
通过上图,我们很明显的看出在192.168.123.7上的cni0网卡的IP地址段为172.20.1.1/24,那么该node节点上的pod说分配的ip地址也是172.20.1.1/24网段的。
当然这里面牵涉到有关k8s网络部分,暂时不做详细讲解。因为这块我也是在学习中,后续小伙伴们可以多多交流。
Pod查看完毕后,我们再切换到node节点上,查看具体的docker容器。使用如下命令:
docker ps
通过上图,我们可以很明显的看出每个pod在node节点上都会启动两个docker容器:一个pod本身的docker容器,一个是pod运行时所使用的网络docker容器。
我们再来查看下,该node上所启动的端口,使用如下命令:
netstat -tunlp
通过上图,我们可以很明显的看出node节点已经监听了我们在ilanni-httpd-service.yml文件中配置的ilanni-httpd-svc的NodePort对外的映射端口28080。
现在我们要从外部访问ilanni-httpd-svc,使用对外映射的端口28080,如下:
http://192.168.123.6:28080/
通过上图,我们可以很明显的看出,ilanni-httpd-svc这个service已经正常对外提供服务,说明我们以上的操作都是正常的。
上述有关namespace、service、deployment的创建,我们都是各自使用一个文件的,也可以合成到一起,作为一个yml文件。如下:
cat > httpd-deployment.yml << “EOF”
apiVersion: v1
kind: Namespace
metadata:
name: ilanni-namespace
labels:
name: ilanni-namespace
—
apiVersion: v1
kind: Service
metadata:
name: ilanni-httpd-svc
namespace: ilanni-namespace
spec:
type: NodePort
ports:
– name: http-port
nodePort: 28080
port: 80
targetPort: 80
selector:
app: ilanni-httpd-dm
—
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ilanni-httpd
namespace: ilanni-namespace
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
#由于replicas为2,则整个升级,pod个数在1-3个之间
rollingUpdate:
##滚动升级时会先启动1个pod
maxSurge: 1
##滚动升级时允许的最大Unavailable的pod个数
maxUnavailable: 0
template:
metadata:
labels:
app: ilanni-httpd-dm
spec:
##k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒
terminationGracePeriodSeconds: 60
restartPolicy: Always
containers:
– name: ilanni-httpd
image: httpd:alpine
imagePullPolicy: Always
EOF
因为在本次试验中,我们还使用到另外一个pod tomcat,所以在此也把tomcat相关的yml文件列出来,如下:
cat > tomcat-deployment.yml << “EOF”
apiVersion: v1
kind: Service
metadata:
name: ilanni-tomcat-svc
namespace: ilanni-namespace
spec:
type: NodePort
ports:
– name: http-port
nodePort: 28181
port: 8080
targetPort: 8080
selector:
app: ilanni-tomcat-dm
—
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ilanni-tomcat
namespace: ilanni-namespace
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
#由于replicas为2,则整个升级,pod个数在1-3个之间
rollingUpdate:
#滚动升级时会先启动1个pod
maxSurge: 1
#滚动升级时允许的最大Unavailable的pod个数
maxUnavailable: 0
template:
metadata:
labels:
app: ilanni-tomcat-dm
spec:
#k8s将会给应用发送SIGTERM信号,可以用来正确、优雅地关闭应用,默认为30秒
terminationGracePeriodSeconds: 60
restartPolicy: Always
containers:
– name: ilanni-tomcat
image: tomcat:alpine
imagePullPolicy: Always
EOF
文章写了这么多,还没有引出我们今天的主角ingress是不是有点疑问。
那么,下面我们就来正式引入。
在第4.6章节中,我们介绍了访问httpd服务的方法,我们是通过kubectl get pod -n ilanni-namespace -o wide命令查看httpd的pod运行在192.168.123.6和192.168.123.7这两个node节点上。
同时,我们也使用NodePort方式使ilanni-httpd-svc的service对外映射28080端口。
那么问题来了,如果我们有10个node节点,那么此时httpd的pod会在哪一个node节点上运行呢?如果我们再次使用相关命令查看到该pod运行节点,那么下次、下下次呢?
为了我们无感知的访问后端应用,那么我们就可以使用ingress了。
有关ingress的具体工作过程,可以查看第一、二章节。
注意:service、deployment、ingress是区分namespace的。
现在我们开始安装和配置ingress-controller,如下。
k8s社区提供的ingress,github地址如下:
https://github.com/kubernetes/ingress-nginx/tree/master/deploy/static
在此我们需要下载mandatory.yaml文件,使用如下命令:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
上面可能无法下载:所以用国内的 gitee
wget https://gitee.com/mirrors/ingress-nginx/raw/nginx-0.25.0/deploy/static/mandatory.yaml
hostNetwork网络,这是一种直接定义Pod网络的方式。
如果在Pod中使用hostNetwork:true配置网络,那么Pod中运行的应用程序可以直接使用node节点的端口,这样node节点主机所在网络的其他主机,都可以通过该端口访问到此应用程序。
启用hostNetwork网络,我们只需在mandatory.yaml文件添加hostNetwork: true即可。如下:
我们可以通过mandatory.yaml文件,查看ingress-controller默认的监听端口,如下:
当然我们也可以很明显的看出,nginx-ingress-controller默认负载的是http协议。
如果是要负载TCP协议的话,我们需要单独另外配置,这个我会在后期的文章中进行介绍。
nginx-ingress-controller会随意选择一个node节点运行pod,为此需要我们把nginx-ingress-controller运行到指定的node节点上。
首先需要给需要运行nginx-ingress-controller的node节点打标签,在此我们把nginx-ingress-controller运行在192.168.123.7这个node节点上。
给192.168.123.7这个node节点打标签,使用如下命令:
kubectl label node 192.168.123.7 nginx=nginx
kubectl get nodes –show-labels
为了让nginx-ingress-controller运行到指定的node节点上,还需要我们修改mandatory.yaml文件。
mandatory.yaml文件中只需要增加如下内容:
nodeSelector:
nginx: “nginx”
默认情况ingress nginx是没有添加service资源的,不添加service资源也是可以的,但是在ingress nginx的pod日志里面会报找不到相应的service。如下:
添加ingress service文件,使用如下命令:
cat > mandatory-service.yaml << “EOF”
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: NodePort
ports:
– name: http
port: 80
targetPort: 80
– name: https
port: 443
targetPort: 443
selector:
app: ingress-nginx
EOF
mandatory.yaml文件修改完毕后,我们现在来应用该文件,使用如下命令:
kubectl apply -f mandatory.yaml
kubectl apply -f mandatory-service.yaml
查看nginx-ingress-controller运行的node节点,使用如下命令:
kubectl get pod -n ingress-nginx -o wide
通过上图,我们很明显的看出nginx-ingress-controller确实运行在192.168.123.7这个node节点上。
查看创建的namespace,使用如下命令:
kubectl get ns
查看创建的cm, deploy ,pod,如下:
kubectl get cm,deploy,pod -n ingress-nginx -o wide
查看创建的service,如下:
kubectl get svc -n ingress-nginx -o wide
登录到192.168.123.6这个node节点上,进行查看端口如下:
netstat -tunlp
通过上图,我们可以很明显的看出,192.168.123.6这个node节点,已经监听了80和443端口。
其他node节点也是如此进行操作查看即可。
现在我们进入nginx-ingress-controller的pod里面,查看下nginx的具体配置,使用如下命令:
kubectl exec -it -n ingress-nginx nginx-ingress-controller-fb47ff79d-bqh5p bash
当然我们也可以不进入pod进行查看,命令如下:
kubectl exec -it -n ingress-nginx nginx-ingress-controller-65755c776f-drztq — cat nginx.conf
通过上图,我们可以很明显的看出nginx-ingress-controller,实质是nginx与lua脚本的集成,这点和openresty比较类似。
nginx-ingress-controller创建完毕后,我们再来创建针对具体service的ingress。
现在我们针对ilanni-namespace下的ilanni-httpd-svc和ilanni-tomcat-svc这两个service进行负载。
具体的ingrees文件如下:
cat > ingress_example.yml << “EOF”
apiVersion: v1
kind: List
items:
– apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ilanni-ingress
namespace: ilanni-namespace
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
– host: ingress.ilanni.com
http:
paths:
– path: /
backend:
serviceName: ilanni-httpd-svc
servicePort: 80
– path: /tomcat
backend:
serviceName: ilanni-tomcat-svc
servicePort: 8080
EOF
上图中,我们定义了ingress.ilanni.com这个域名,根目录/访问的是ilanni-httpd-svc这个service的80端口,注意该端口也是ilanni-httpd-svc后端pod的容器端口。
子目录tomcat访问的是ilanni-tomcat-svc这个service的8080端口,注意该端口也是ilanni-tomcat-svc后端pod的容器端口。
应用该文件,使用如下命令:
kubectl apply -f ingress_example.yml
kubectl get ing -n ilanni-namespace
查看ingress的相关信息,如下:
kubectl get ing -n ilanni-namespace
kubectl describe ing -n ilanni-namespace ilanni-ingress
通过上图,我们已经很明显的看出我们定义的ingress规则。
现在我们来访问ingress定义的域名,如下:
通过以上两张图,我们可以很明显的看出,我们自定义的ingress规则已经生效。
访问ingress.ilanni.com根目录会访问把请求分发到ilanni-httpd-svc这个service。
访问ingress.ilanni.com子目录tomcat会访问把请求分发到ilanni-tomcat-svc这个service。
注意:上述域名我们把它解析到192.168.123.7这个node节点上的。
为什么这样操作?就是因为在5.4章节中,我们把nginx-ingress-controller运行到了该节点上。
如果我们后续要做ingress的高可用的话,要可以通过这种方式。把nginx-ingress-controller运行到指定的几个node节点上,然后再把这几个node节点加入到lb中,然后对应的域名解析到该lb即可实现ingress的高可用。
现在我们来查看nginx-ingress-controller的nginx配置文件,如下:
kubectl exec -it -n ingress-nginx nginx-ingress-controller-65755c776f-drztq — cat nginx.conf
通过上图,我们可以很明显的看出ingress果真是修改nginx的配置文件。
现在来查看nginx-ingress-controller的pod的日志,使用如下命令:
kubectl logs -f -n ingress-nginx nginx-ingress-controller-788fd6764d-6fjwq
通过上图,我们可以很明显的,刚刚我们的访问被分发到后端对应的service上。
现在我们再来查看对应service的后端pod日志,如下:
kubectl logs -f -n ilanni-namespace ilanni-httpd-84c7fdd8b-db882
到此有关ingress的http负载已经完毕了。在此所有使用的yml文件可以点我下载。