Kubernetes 服务发布进阶
一、基本概念
service 的作用体现在两个方面,对集群内部,它不断跟踪 pod 的变化,更新 endpoint(端点)中对应 pod 的对象,提供了 ip 不断变化的 pod 的服务发现机制;对集群外部,他类似负载均衡器,可以在集群内外部对 pod 进行访问。
在 Kubernetes 中,Pod 的 IP 地址和 service 的 ClusterIP 仅可以在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用能够访问集群内的服务,Kubernetes 目前提供了以下几种方案:
NodePort:将 service 暴露在节点网络上,NodePort 背后就是 Kube-Proxy,Kube-Proxy 是沟通 service 网络、Pod 网络和节点网络的桥梁。测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort 的端口管理就是个灾难。因为每个端口只能是一种服务,端口范围只能是 30000 - 32767。
LoadBalancer:通过设置 LoadBalancer 映射到云服务商提供的 LoadBalancer 地址。这种用法仅用于在公有云服务提供商的云平台上设置 Service 的场景。受限于云平台,且通常在云平台部署 LoadBalancer 还需要额外的费用。在 service 提交后,Kubernetes 就会调用 CloudProvider 在公有云上为你创建一个负载均衡服务,并且把被代理的 Pod 的 IP 地址配置给负载均衡服务做后端。
externalIPs: service 允许为其分配外部 IP,如果外部 IP 路由到集群中一个或多个 Node 上,Service 会被暴露给这些 externalIPs。通过外部 IP 进入到集群的流量,将会被路由到 Service 的 Endpoint 上。
Ingress:只需一个或者少量的公网 IP 和 LB,即可同时将多个 HTTP 服务暴露到外网,实现七层反向代理。可以简单理解为 service 的 service,它其实就是一组基于域名和 URL 路径,把用户的请求转发到一个或多个 service 的规则。
注意:
目前比较流行的 IngressController 有 ingress-nginx(由 Kubernetes 官方维护 )、nginx-ingress(由 Nginx 官方维护,注意和 Ingress-nginx 的区别 )、Traefik、Istio 等。
1、Ingress 组成
(1) ingress:
ingress 是一个 API 对象,通过 yaml 文件来配置,ingress 对象的作用是定义请求如何转发到 service 的规则,可以理解为配置模板。
ingress 通过 http 或 https 暴露集群内部 service,给 service 提供外部 URL、负载均衡、SSL/TLS 能力以及基于域名的反向代理。ingress 要依靠 ingress - controller 来具体实现以上功能。
(2) ingress - controller:
ingress - controller 是具体实现反向代理及负载均衡的程序,对 ingress 定义的规则进行解析,根据配置的规则来实现请求转发。
ingress - controller 并不是 k8s 自带的组件,实际上 ingress - controller 只是一个统称,用户可以选择不同的 ingress - controller 实现,目前,由 k8s 维护的 ingress - controller 只有 google 云的 GCE 与 ingress - nginx 两个,其他还有很多第三方维护的 ingress - controller,具体可以参考官方文档。但是不管哪一种 ingress - controller,实现的机制都大同小异,只是在具体配置上有差异。
一般来说,ingress - controller 的形式都是一个 pod,里面跑着 daemon 程序和反向代理程序。daemon 负责不断监控集群的变化,根据 ingress 对象生成配置并应用新配置到反向代理,比如 ingress - nginx 就是动态生成 nginx 配置,动态更新 upstream,并在需要的时候 reload 程序应用新配置。
ingress - controller 才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到 ingress - controller,而 ingress 对象是用来告诉 ingress - controller 该如何转发请求,比如哪些域名、哪些 URL 要转发到哪些 service 等等。
2、Ingress 工作过程
用户访问一个业务的流程如下:
(1) 用户在浏览器中输入域名
(2) 域名解析至业务的入口 IP (一般为外部负载均衡器,比如阿里的 SLB 或者 DMZ 的网关)。
(3) 外部负载均衡器反向代理至 kubernetes 的入口 (一般为 Ingress, 或者通过 NodePort 暴露的
服务等)。
(4) Ingress 根据自身的配置找到对应的 Service, 再代理到对应的 Service 上。
(5) 最后到达 Service 对应的某一个 Pod 上。
可见,在一般情况下,Ingress 主要是一个用户 kubernetes 集群业务的入口。是业务能够正常提供服务的核心,所以在生产环境中,推荐使用单独的服务器作为 Ingress Controller。Controller 可以使用 Traefik、Istio、Nginx、HaProxy 等作为 Ingress Controller。因为相对于其他 IngressController, 管理人员更熟悉 Nginx 或者 HaProxy 等服务,所以本章主要讲解 Ingress Nginx 的安装与常用配置,这也是 kubernetes 官方提供的 Ingress Controller。
3、Ingress的工作原理
(1) ingress-controller 通过和 kubernetes API Server 交互,动态的去感知集群中 ingress 规则变化
(2) 然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个 service,生成一段 nginx 配置
(3) 再写到 nginx-ingress-controller 的 pod 里,这个 ingress-controller 的 pod 里运行着一个 Nginx 服务,控制器会把生成的 nginx 配置写入 /etc/nginx.conf 文件中
(4) 然后 reload 一下使配置生效。以此达到域名区分配置和动态更新的作用。
二、安装 Ingress Nginx Controller
由于 Ingress Controller 相当于 kubernetes 集群中服务的 “大门”,因此在生产环境中,一定要保障 Controller 的稳定性和可用性。为了提高 Ingress Controller 的可用性,我们一般采用单独的服务器作为 Controller 节点,以此保障 Ingress Controller 的 Pod 资源不会被其他服务的 Pod 影响。
Ingress Nginx 官方提供了多种部署方式,本节课将采用 Helm 的方式进行安装,并且将 Ingress Controller 安装在 k8s-node01 节点。
1、下载并安装helm
# 下载 helm 安装包
wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz
# 解压安装包
tar zxvf helm-v3.9.4-linux-amd64.tar.gz
# 将 helm 可执行文件移动到系统可执行路径下,完成安装
mv linux-amd64/helm /usr/local/bin/
2、下载并修改 Ingress Controller 参数
# 添加 ingress-nginx 的 Helm Chart 仓库
# 后续可通过这个仓库拉取 ingress-nginx 相关的 Chart 包
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx# 更新本地 Helm 仓库索引
# 确保能获取到仓库中最新的 Chart 信息,包括版本、元数据等
helm repo update# 从 ingress-nginx 仓库拉取指定版本(4.7.1)的 ingress-nginx Chart 包到本地
# 拉取后是.tgz 压缩包格式,方便离线部署或自定义配置
helm pull ingress-nginx/ingress-nginx --version 4.7.1# 解压刚刚拉取的 ingress-nginx Chart 压缩包
# 解压后得到包含模板、values.yaml 等文件的目录,用于自定义部署配置
tar xvf ingress-nginx-4.7.1.tgz# 使用 vim 编辑器打开解压后目录里的 values.yaml 文件
# 可在此文件中修改 ingress-nginx 部署的各项参数(如镜像版本、资源限制、配置等)
vim ingress-nginx/values.yaml
3、修改配置文件
(1) 将 Controller 的 registry 仓库地址修改为国内的
controller:name: controllerimage:chroot: falseregistry: registry.cn-hangzhou.aliyuncs.comimage: tanzu/controllertag: "v1.6.4"#digest: sha256:15be4666c53052484dd2992efacf2f50ea77a78ae8aa21ccd91af6baaa7ea22f#digestChroot: sha256:0de01e2c316c3ca7847ca13b32d077af7910d07f21a4a82f81061839764f8f81
(2) 修改 opentelemetry 镜像地址
opentelemetry:enabled: falseimage: registry.cn-hangzhou.aliyuncs.com/tanzu/opentelemetry:v20230107containerSecurityContext:allowPrivilegeEscalation: false
(3) 将 admissionWebhook 的镜像地址修改为国内的
patchWebhookJob:securityContext:allowPrivilegeEscalation: falseresources: {}patch:enabled: true
image:registry: registry.cn-hangzhou.aliyuncs.comimage: tanzu/kube-webhook-certgentag: v20220916-gd32f8c343#digest: sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227bpullPolicy: IfNotPresent
(4) 修改 hostNetwork 的值为 true
设置为 true 时,该 Pod 将与其所在节点共享网络命名空间。
(5) dnsPolicy 设置为 ClusterFirstWithHostNet
kubernetes 可以在 pod 级别通过 dnspolicy 字段设置 DNS 策略。目前支持的 DNS 策略如下:
Default:继承 pod 所在宿主机的域名解析设置。
ClusterFirst:将优先使用 kubernetes 环境的 dns 服务(如 coreDNS 提供的域名解析服务),将无法解析的域名转发到系统配置的上游 DNS 服务器。
ClusterFirstWithHostNet:适用与以 hostNetwork 模式运行的 pod。
None:忽略集群的 DNS 配置,需要手工通过 dnsConfig 自定义 DNS 配置。这个选项在 1.9 版本中开始引入,到 1.10 版本时升级为 Beta,到 1.14 版本时达到稳定版本。
(6) nodeSelector 添加 ingress: "true"
nodeSelector:ingress: truekubernetes.io/os: linux
(7) 修改kind的类型为DaemonSet
kind: DaemonSet
4、部署ingress
(1) 打标签
ku label node k8s-node01 ingress=true
(2) 创建ns
ku create ns ingress-nginx
(3) 安装ingress-nginx
cd ingress-nginx/
helm install ingress-nginx -n ingress-nginx .
(4) 卸载ingress-nginx
helm uninstall ingress-nginx -n ingress-nginx
(5) 查看
ku get pod -o wide -n ingress-nginx
三、Ingress-Nginx使用
1、创建一个命名空间
ku create ns study-ingress
2、创建Nginx的Pod
vim nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: webappnamespace: study-ingress
spec:replicas: 2selector:matchLabels:app: webapptemplate:metadata:labels:app: webappspec:containers:- name: webappimage: nginx:1.7.9ports:containerPort: 80protocol: TCP
ku create -f nginx-deployment.yaml
3、创建Service
vim webapp-service.yaml
apiVersion: v1
kind: Service
metadata:name: nginxnamespace: study-ingress
spec:ports:- protocol: TCPport: 80targetPort: 80selector:app: webapp
ku create -f webapp-service.yaml
4、查看Pod和Service的状态
ku get pod -o wide -n study-ingressku get svc -o wide -n study-ingress
5、编辑ingress配置文件并通过名称选中指定的Service
vim web-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: nginx-ingressnamespace: study-ingress
spec:ingressClassName: nginxrules:- host: nginx.test.comhttp:paths:- backend:service:name: nginxport:number: 80path: /pathType: ImplementationSpecific
有 3 种支持的 path 类型:
ImplementationSpecific:对于这种 path 类型,匹配取决于 IngressClass。可以将其视为一个单独的 pathType 或者将其认为和 Prefix 或者 Exact 路径类型一样。
Exact:精确匹配 URL 路径,并且区分大小写
Prefix:根据 URL 中的,被 / 分割的前缀进行匹配。匹配区分大小写并且按照元素对路径进行匹配。path 元素指的是路径中由分隔符分隔的标签列表。即只要用户请求的路径以指定的路径开始即可。
注意:如果路径的最后一个元素是请求路径中最后一个元素的子字符串,那么这个是不匹配的。【举例:/foo/bar 匹配 /foo/bar/baz,但是不匹配 /foo/barbaz】
6、创建ingress
ku create -f web-ingress.yaml
7、查看ingress
ku get ingress -o wide -n study-ingress
8、编辑客户端hosts文件
9、访问测试
创建的 Ingress 绑定的域名为 nginx.test.com,由于本书的 IngressController 是以 hostNetwork 模式部署的,因此将域名解析至 IngressController 所在的节点即可。
如果 IngressController 上层还有一层网关,解析至网关 IP 即可。接下来通过域名 nginx.test.com 即可访问 Web 服务器
四、Ingress Nginx 实现域名重定向 Redirect
当一个服务需要更换域名时,并不能对其直接更改,需要一个过渡的过程。在这个过程中,需要将旧域名的访问跳转到新域名,此时可以使用 Redirect 功能。待旧域名无访问时,再停止旧域名。
在 Nginx 作为代理服务器时,Redirect 可用于域名的重定向,比如访问 old.com 被重定向到 new.com。Ingress 可以更简单地实现 Redirect 功能。接下来用 nginx.redirect.com 作为旧域名,baidu.com 作为新域名进行演示。
1、编辑ingress文件
vim redirect.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.comname: nginx-redirectnamespace: study-ingress
spec:ingressClassName: nginxrules:- host: nginx.redirect.comhttp:paths:- backend:service:name: nginxport:number: 80path: /pathType: ImplementationSpecific
2、编辑客户端hosts文件
3、创建ingress
ku create -f redirect.yaml
4、访问测试
在客户端的 hosts 文件添加域名 nginx.redirect.com,IP 地址为 k8s - node01 节点的 IP 地址。
使用域名 nginx.redirect.com 访问网站,打开的是 baidu.com,说明跳转成功。
五、Ingress Nginx 实现前后端分离 Rewrite
现在大部分应用都是前后端分离的架构,也就是前端用某个域名的根路径进行访问,后端接口采用 /api 进行访问,用来区分前端和后端。或者同时具有很多个后端,需要使用 /api - a 到 A 服务,/api - b 到 B 服务,但是由于 A 和 B 服务可能并没有 /api - a 和 /api - b 的路径,因此需要将 /api - x 重写为 “/”,才可以正常到 A 或者 B 服务,否则将会出现 404 的报错。此时可以通过 Rewrite 功能达到这种效果。
1、编辑deployment文件
vim nginx-backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: backend-apinamespace: study-ingress
spec:replicas: 2selector:matchLabels:app: webapptemplate:metadata:labels:app: webappspec:containers:- name: webappimage: nginx:1.7.9ports:- containerPort: 80protocol: TCP
2、创建deployment
ku create -f nginx-backend-deployment.yamlku get pod -o wide -n study-ingress
3、编辑Service文件
vim webapp-backend-service.yaml
apiVersion: v1
kind: Service
metadata:name: backend-apinamespace: study-ingress
spec:ports:- protocol: TCPport: 80targetPort: 80selector:app: webapp
4、创建并查看Service
ku create -f webapp-backend-service.yamlku get svc -o wide -n study-ingress
5、编辑ingress
vim rewirte.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/rewrite-target: /$2name: backend-apinamespace: study-ingress
spec:ingressClassName: nginxrules:- host: nginx.test.comhttp:paths:- backend:service:name: backend-apiport:number: 80path: /api-a(/|$)(.*)pathType: ImplementationSpecifi
备注:
annotations: ##用来定义 ingress 控制器的行为,具体行为如下nginx.ingress.kubernetes.io/rewrite-target: /$2
##用于重写 URL,使得传入的请求在转发给后端服务之前,其路径部分会被修改。本案例中当用户访问
nginx.test.com/api-a/xxxx 的时候,都被重定向为 nginx.test.com/api-a
nginx.test.com/api-a 指向了 service 名称为 backend-api 的后端 podpath: /api-a(/|$)(.*): 这个路径使用正则表达式来匹配请求。这里有两个捕获组:
第一个捕获组 (/$|$) 匹配 / 或者以 api-a 结尾。
第二个捕获组 (.*) 匹配任意字符串,表示/api-a 之后的路径部分,此处表示任意路径名称。
$2 指的是匹配第二个捕获组。
6、创建并查看ingress
ku create -f rewirte.yaml
ku get ingress -o wide -n study-ingress
7、访问测试
六、Ingress Nginx 实现 SSL 配置
生产环境对外的服务一般需要配置 HTTPS 协议,使用 Ingress 也可以非常方便地添加 HTTPS 的证书。
由于是学习环境,并没有权威证书,因此需要使用 OpenSSL 生成一个测试证书(如果是生产环境,那么证书为在第三方公司购买的证书,无须自行生成)。
1、生成私有证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginx.test.com"
参数 | 作用说明 |
---|---|
openssl req | 调用 OpenSSL 的 req 子命令,用于处理证书请求相关操作,常用来生成证书签名请求(CSR)或自签名证书 |
-x509 | 表示要生成一个自签名的 X.509 证书,而不是仅仅生成证书签名请求 |
-nodes | “no des” 的意思,生成私钥时不使用 DES(数据加密标准)等加密算法对私钥进行加密存储,这样私钥文件(tls.key)以明文形式保存(方便测试等场景,生产环境若需更高安全,一般不建议这样用,可结合合适加密方式 ) |
-days 365 | 指定证书的有效天数,这里设置为 365 天,即证书自生成起 1 年内有效 |
-newkey rsa:2048 | 同时生成一个新的私钥和一个证书签名请求(因结合 -x509 实际是直接生成自签名证书 ),私钥算法用 RSA,密钥长度为 2048 位 |
-keyout tls.key | 指定生成的私钥文件名为 tls.key,用于后续服务器配置等使用私钥的场景 |
-out tls.crt | 指定生成的自签名证书文件名为 tls.crt,证书包含公钥等信息,用于客户端验证等 |
-subj "/CN=nginx.test.com" | 设置证书的主题信息,这里仅指定了通用名称(Common Name,CN)为 nginx.test.com,表示该证书绑定的域名,实际应用中可根据需求补充组织(O)、组织单元(OU)等更多主题字段 |
2、创建证书的(保险箱)secret
ku create secret tls ca-secret --cert=tls.crt --key=tls.key -n study-ingress
3、编辑ingress文件
vim ingress-ssl.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:creationTimestamp: nullname: nginx-ingress-ssl
spec:ingressClassName: nginx-sslrules:- host: nginx.test.comhttp:paths:- backend:service:name: nginxport:number: 80path: /pathType: ImplementationSpecifictls:- hosts:- nginx.test.comsecretName: ca-secret
4、创建ingress
ku create -f ingress-ssl.yaml
5、访问测试
七、ingress-nginx实现基本认证
1、安装httpd-tools
dnf -y install httpd-tools
2、使用htpasswd创建用户
htpasswd -c auth zhangsan
3、创建账户的保险箱secret
ku create secret generic basic-auth --from-file=auth -n study-ingress
4、编辑ingress
vim ingress-with-auth.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/auth-realm: Please Input Your Username and Passwordnginx.ingress.kubernetes.io/auth-secret: basic-authnginx.ingress.kubernetes.io/auth-type: basicname: ingress-with-authnamespace: study-ingress
spec:ingressClassName: nginxrules:- host: auth.test.comhttp:paths:- backend:service:name: nginxport:number: 80path: /pathType: ImplementationSpecific
配置项 | 说明 |
---|---|
nginx.ingress.kubernetes.io/auth - secret | 密码文件的 secret 名称,值为 auth |
nginx.ingress.kubernetes.io/auth - type | 认证类型,值为 basic |
host: auth.test.com | 客户端要访问的域名,需注意不要使用前面的域名,或者把前面的 ingress 都删掉 |
5、创建ingress
ku create -f ingress-with-auth.yaml