Kubernetes(k8s)Ingress原理
一、概述
Kubernetes 暴露服务的有三种方式,分别为 LoadBlancer Service、NodePort Service、Ingress。官网对 Ingress 的定义为管理对外服务到集群内服务之间规则的集合,通俗点讲就是它定义规则来允许进入集群的请求被转发到集群中对应服务上,从来实现服务暴漏。 Ingress 能把集群内 Service 配置成外网能够访问的 URL,流量负载均衡,终止SSL,提供基于域名访问的虚拟主机等等。
1)LoadBlancer Service
LoadBlancer Service 是 Kubernetes 结合云平台的组件,如国外 GCE、AWS、国内阿里云等等,使用它向使用的底层云平台申请创建负载均衡器来实现,有局限性,对于使用云平台的集群比较方便。
2)NodePort Service
NodePort Service 是通过在节点上暴漏端口,然后通过将端口映射到具体某个服务上来实现服务暴漏,比较直观方便,但是对于集群来说,随着 Service 的不断增加,需要的端口越来越多,很容易出现端口冲突,而且不容易管理。当然对于小规模的集群服务,还是比较不错的。
3)Ingress
Ingress 使用开源的反向代理负载均衡器来实现对外暴漏服务,比如 Nginx、Apache、Haproxy等。Nginx Ingress 一般有三个组件组成:
- ingress是kubernetes的一个资源对象,用于编写定义规则。
- 反向代理负载均衡器,通常以Service的Port方式运行,接收并按照ingress定义的规则进行转发,通常为nginx,haproxy,traefik等,本文使用nginx。
- ingress-controller,监听apiserver,获取服务新增,删除等变化,并结合ingress规则动态更新到反向代理负载均衡器上,并重载配置使其生效。
以上三者有机的协调配合起来,就可以完成 Kubernetes 集群服务的暴漏。
二、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工作流程
Ingress 的实现分为两个部分 Ingress Controller 和 Ingress .
截至目前,nginx-ingress 已经能够完成 7/4 层的代理功能(4 层代理基于 ConfigMap,感觉还有改进的空间);Nginx 的 7 层反向代理模式,可以简单用下图表示:
- Nginx 对后端运行的服务(Service1、Service2)提供反向代理,在配置文件中配置了域名与后端服务 Endpoints 的对应关系。
- 客户端通过使用 DNS 服务或者直接配置本地的 hosts 文件,将域名都映射到 Nginx 代理服务器。
- 当客户端访问 service1.com 时,浏览器会把包含域名的请求发送给 nginx 服务器,nginx 服务器根据传来的域名,选择对应的 Service,这里就是选择 Service 1 后端服务,然后根据一定的负载均衡策略,选择 Service1 中的某个容器接收来自客户端的请求并作出响应。
过程很简单,nginx 在整个过程中仿佛是一台根据域名进行请求转发的“路由器”,这也就是7层代理的整体工作流程了!
3)工作原理
对于 Nginx 反向代理做了什么,我们已经大概了解了。在 k8s 系统中,后端服务的变化是十分频繁的,单纯依靠人工来更新nginx 的配置文件几乎不可能,nginx-ingress 由此应运而生。Nginx-ingress 通过监视 k8s 的资源状态变化实现对 nginx 配置文件的自动更新,下面本文就来分析下其工作原理。
- nginx-ingress 模块在运行时主要包括三个主体:NginxController、Store、SyncQueue。
- Store 主要负责从 kubernetes APIServer 收集运行时信息,感知各类资源(如 ingress、service等)的变化,并及时将更新事件消息(event)写入一个环形管道。
- SyncQueue 协程定期扫描 syncQueue 队列,发现有任务就执行更新操作,即借助 Store 完成最新运行数据的拉取,然后根据一定的规则产生新的 nginx 配置,(有些更新必须 reload,就本地写入新配置,执行 reload),然后执行动态更新操作,即构造 POST 数据,向本地 Nginx Lua 服务模块发送 post 请求,实现配置更新。
- NginxController 作为中间的联系者,监听 updateChannel,一旦收到配置更新事件,就向同步队列 syncQueue 里写入一个更新请求。
三、安装Ingress-nginx
1)官方介绍
ingress-nginx v1.0 最新版本 v1.0
适用于 Kubernetes 版本 v1.19+ (包括 v1.19 )
Kubernetes-v1.22+ 需要使用 ingress-nginx>=1.0,因为networking.k8s.io/v1beta 已经移除
2)直接部署 ingress-nginx
直接部署比较简单,直接拉去 girhub 的文件就可以了,如果遇到长时间无响应,可以终止任务从新拉取。
拉取镜像
$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml
如果上面地址下载失败,可以使用我一下地址下载。如果使用官网的,可能需要换镜像地址,因为官网下载的镜像很大可能会失败。这里我使用的是下面的yaml文件创建ingress-controller
链接:https://pan.baidu.com/s/1fbqcy2bJMtmziJoXTbdK2g
提取码:8888
# 修改镜像地址
$ sed -i 's@k8s.gcr.io/ingress-nginx/controller:v1.0.0\(.*\)@willdockerhub/ingress-nginx-controller:v1.0.0@' deploy.yaml
$ sed -i 's@k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0\(.*\)$@hzde0128/kube-webhook-certgen:v1.0@' deploy.yaml
$ kubectl apply -f deploy.yaml
如果上面镜像还是下载失败,可以先下载镜像,地址如下:
链接:https://pan.baidu.com/s/1Rj5pDIGc3p3BjTU4PeIGFA
提取码:8888
下载完镜像,执行如下命令导入镜像:
$ docker image load -i hzde0128-kube-webhook-certgen-v1.0.tar
$ docker image load -i willdockerhub-ingress-nginx-controller-v1.0.0.tar
$ docker images
执行
$ kubectl apply -f ingress-nginx.yaml
检查安装
Completed 状态的是正常的,可以忽略。
$ kubectl get pod -n ingress-nginx
$ kubectl get svc -n ingress-nginx
$ kubectl get svc -n ingress-nginx
四、创建nginx应用
1)创建目录
$ mkdir -p /opt/ingress/nginx-test
$ cd /opt/ingress/nginx-test
2)创建nginx-Deployment-Service.yaml文件,内容如下:
$ cat << EOF > nginx-Deployment-Service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
minReadySeconds: 1
progressDeadlineSeconds: 60
revisionHistoryLimit: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: Always
ports:
- containerPort: 80
resources:
requests:
memory: "1Gi"
cpu: "80m"
limits:
memory: "1Gi"
cpu: "80m"
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
app: nginx
spec:
selector:
app: nginx
ports:
- name: nginx-port
protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF
3)部署 nginx应用
$ kubectl apply -f nginx-Deployment-Service.yaml
$ kubectl get svc -o wide|grep nginx-service
$ kubectl get pod -o wide|grep nginx-deployment-*
4)创建 ingress yaml文件,内容如下:
cat << EOF > nginx-Ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: ingress.nginx.com
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
EOF
创建
$ kubectl apply -f nginx-Ingress.yaml
$ kubectl get ingress|grep nginx
5)在 hosts 文件最后追加 ingress 节点的 IP 地址
$ kubectl get ingress|grep nginx
$ cat /etc/hosts
$ kubectl get svc -n ingress-nginx
6)验证
curl -I http://ingress.nginx.com:31473
五、使用 hostNetwork 的方式部署 ingress-nginx
每次部署 ingres-nginx 都随机一个 nodePort ,而使用 ingres-nginx 访问的时候也要以 域名:端口 的形式去访问,如何直接使用域名去访问呢?下面介绍另外一种安装方式。
1)创建目录
$ mkdir /opt/ingress/nginx-test/hostNetwork
$ cd /opt/ingress/nginx-test/hostNetwork
2)copy 一份deploy.yaml
$ cp /opt/ingress/deploy.yaml /opt/ingress/nginx-test/hostNetwork/
3)优化 ingress-nginx
1、使用 hostNetwork
默认 ingress-nginx 随机提供 nodeport 端口,开启 hostNetwork 启用80、443端口。
修改 Deployment 下面的 spec
参数如下:
hostNetwork: true # 新增
2、修改负载均衡问题
把 kind: Deployment 改为 kind: DaemonSet 模式,这样每台 node 上都有 ingress-nginx-controller pod 副本。
参数如下:
kind: Deployment # 注释
kind: DaemonSet # 新增
3、修改 ingressClass 问题
如果不关心 ingressClass 或者很多没有 ingressClass 配置的 ingress 对象,
添加参数 ingress-controller --watch-ingress-without-class=true 。
4、部署检查 ingress
$ kubectl apply -f deploy.yaml
发现除了master节点,其它node节点都有一个副本。
在其它节点上查看node
$ netstat -pntl |grep :443
$ netstat -pntl |grep :80
5、验证
# 直接访问,不通过nodePort
$ curl -I http://ingress.nginx.com
# 当然也可以通过nodePort访问
$ curl -I http://ingress.nginx.com:31827
六、 基于HTTPS的Ingress-nginx
1)创建CA证书
$ mkdir -p /opt/ingress/tls
$ cd /opt/ingress/tls
# 生成ca证书,参数可根据需求自定义
$ openssl genrsa -out tls.key 2048
$ openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Shanghai/L=Shanghai/O=DevOps/CN=ingress.nginx.com
PS:CN必须为访问的域名相同
创建完成后,会在当前目录下生成证书密钥(tls.crt)和证书key(tls.key)文件:
2)secret简介
secret 用于存储和管理一些敏感数据,比如密码,token,密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 的容器里挂载 Volume 的方式或者 环境变量 的方式访问到这些 Secret 里保存的信息。
Secret 可分为四种类型:
- kubernetes.io/dockerconfigjson : 用来存储私有docker registry的认证信息
- Opaque(generic:通用):从本地 file, directory 或者 literal value( literal:字面量) 创建一个 secret。对应的API对象为 Opaque([oʊˈpeɪk]),译为”不透明物”,等同于”敏感数据”。
- kubernetes.io/tls:创建一个 TLS secret。
- kubernetes.io/service-account-token:用于被serviceaccount引用。serviceaccout创建时Kubernetes会默认创建对应的secret。Pod如果使用了serviceaccount,对应的secret会自动挂载到Pod目录/run/secrets/ kubernetes.io/serviceaccount中。
Secret 示意图
3)创建 secret
上面了解了secret之后,就开始创建secret。自签证书生成后就可以用证书key和证书密钥创建 secret 了,这里为了方便,我用的是正规机构颁发的证书,如下所示:
$ cd /opt/ingress
$ ls tls/
1、通过命令行创建secret:
kubectl create secret tls ${CERT_NAME} --key ${KEY_FILE} --cert $
示例如下:
$ kubectl create secret tls tls-secret --key tls/tls.key --cert tls/tls.crt
$ kubectl get secret
2、通过yarml文件创建secret
yaml文件模板
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: dashboard
data:
tls.crt: base64 编码的 cert
tls.key: base64 编码的 key
type: kubernetes.io/tls
示例:
获取base64 编码的 cert和base64 编码的 key
$ cd /opt/secret
$ cat tls/tls.crt | base64 > tls/base64.tls.crt
$ cat tls/tls.key | base64 > tls/base64.tls.key
这种方式创建有问题,会有如下报错,后续再研究。
4)创建deployment,service,ingress资源
1)切到对应的目录下
$ cd /opt/ingress
2)创建命令空间
$ cat << EOF > ingress-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ingress-test
EOF
$ kubectl apply -f ingress-namespace.yaml
3)创建secret
$ kubectl create secret tls tls-secret --key tls/tls.key --cert tls/tls.crt -n ingress-test
$ kubectl get secret -n ingress-test
4)创建deployment,service
$ cat << EOF > create-deployment-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: ingress-test
spec:
replicas: 1
selector:
matchLabels:
app: test-web
template:
metadata:
labels:
app: test-web
spec:
containers:
- name: nginx
image: nginx:1.17.1
---
apiVersion: v1 #创建service,关联上述deployment
kind: Service
metadata:
name: web-svc
namespace: ingress-test
spec:
selector:
app: test-web
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
$ kubectl apply -f create-deployment-service.yaml
5)创建Ingress
$ cat << EOF > test-ingress.yaml
apiVersion: networking.k8s.io/v1 #创建ingress规则
kind: Ingress
metadata:
name: test-ingress
namespace: ingress-test
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls: #为域名颁发证书
- hosts:
- ingress.nginx.com
secretName: tls-secret
rules:
- host: ingress.nginx.com
http: #注意,此处字段为http,不支持https
paths:
- path: /index.html
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
EOF
$ kubectl apply -f test-ingress.yaml
$ kubectl get service -n ingress-test
$ kubectl describe ingress test-ingress -n ingress-test
6)通过ingress代理的443端口访问nginx服务
因为我们没有搭建dns服务器,所以需要在hosts文件将新的域名进行绑定(地址可以绑定集群中任意一台node)
$ kubectl get ingress -n ingress-test
在hosts文件中绑定域名:
192.168.0.114 ingress.nginx.com
7)https验证
# 查看ingress-nginx
$ kubectl get svc -n ingress-nginx
# 直接访问,-k:允许curl使用非安全的ssl连接并且传输数据(证书不受信)
$ curl -I -k https://ingress.nginx.com/
# 使用nodePort访问
$ curl -I -k https://ingress.nginx.com/31125
浏览器访问验证,https://ingress.nginx.com/
关于Kubernetes(k8s)Ingress的介绍就先到这里了,有疑问的小伙伴,欢迎给我留言哦~