14、Ingress

14、 服务发布Ingress

image-20240509131830500

1. 传统架构和K8s架构访问

img

在没有使用Kubernetes的传统架构中,部署一个应用并使用域名进行发布的流程如下:

  1. 部署应用至宿主机,启动应用并暴露一个端口号。
  2. 通过Nginx或其他代理工具配置域名,并代理至后端应用。
  3. 在域名厂商进行域名解析,设置DNS记录至Nginx或其他代理工具(Nginx的前面可能还有其他代理服务器,在此不进行说明)的服务器上,之后就能通过域名访问我们的应用。

在使用Kubernetes时,部署一个应用并使用域名进行发布的流程如下:

  1. 部署应用至Kubernetes集群,容器内定义了程序启动的方式。
  2. 配置ClusterIP类型的Service,通过Selector链接到容器内的应用。
  3. 配置Ingress链接到该应用的Service,之后将域名解析到Ingress(Ingress前面可能还有其他代理服务器)对应的宿主机上即可。

2. 为什么使用Ingress

可以使用 NodePort 和 LoadBlancer类型的 Service 可以把应用暴露给外部用户使用,除此之外,Kubernetes 还为我们提供了一个非常重要的资源对象可以用来暴露服务给外部用户,那就是Ingress。对于小规模的应用我们使用 NodePort 或许能够满足我们的需求,但是当你的应用越来越多的时候,你就会发现对于 NodePort 的管理就非常麻烦了,这个时候使用 Ingress 就非常方便了,可以避免管理大量的端口。

3. 了解Ingress

Ingress就是实现用域名的方式访问应用。Ingress实现的方式有很多,比如Nginx、HAProxy、Treafik等,就Nginx而言,和上述提到的传统服务架构用Nginx类似。Ingress 控制器在每个符合条件的宿主机上部署一个 Pod,这个 Pod 里面运行的就是 Nginx 进程,里面的实现逻辑和宿主机部署 Nginx 的方式并无太大区别。

Ingress 其实就是从 Kuberenets 集群外部访问集群的一个入口,将外部的请求转发到集群内不同的 Service 上,其实就相当于 nginx、haproxy 等负载均衡代理服务器,可能你会觉得我们直接使用 nginx 就实现了,但是只使用 nginx 这种方式有很大缺陷。

每次有新服务加入的时候怎么改 Nginx 配置?不可能让我们去手动更改或者滚动更新前端的 Nginx Pod 吧?那我们再加上一个服务发现的工具比如 consul 如何?貌似是可以,对吧?Ingress 实际上就是这样实现的,只是服务发现的功能自己实现了,不需要使用第三方的服务了,然后再加上一个域名规则定义,路由信息的刷新依靠 Ingress Controller 来提供。

Ingress Controller 可以理解为一个监听器,通过不断地监听 kube-apiserver,实时的感知后端 Service、Pod 的变化,当得到这些信息变化后,Ingress Controller 再结合 Ingress 的配置,更新反向代理负载均衡器,达到服务发现的作用。

4. Ingress Controller安装

4.1 Helm安装

下载地址:https://github.com/helm/helm/releases ,点击 Linux amd64 下载压缩包到节点上。

img

解压压缩包

[root@k8s-master01 ~]# tar xf helm-v3.14.4-linux-amd64.tar.gz
[root@k8s-master01 ~]# cd linux-amd64/
[root@k8s-master01 linux-amd64]# ls
helm  LICENSE  README.md

在解压目中找到helm程序,移动到需要的目录中。

mv /root/linux-amd64/helm /usr/local/bin/helm

出现以下版本信息代表安装成功

[root@k8s-master01 ~]# helm version
version.BuildInfo{Version:"v3.14.4", GitCommit:"81c902a123462fd4052bc5e9aa9c513c4c8fc142", GitTreeState:"clean", GoVersion:"go1.21.9"}

基础命令

下载一个包:helm pull
创建一个包:helm create
安装一个包:helm install
查看:helm list
查看安装参数:helm get values
更新:helm upgrade
删除:helm delete

4.2 添加仓库并拉取软件包

Helm 仓库是一个存储和分发 Helm 软件包的地方。Helm 是一个用于 Kubernetes 集群中应用程序的包管理器,它允许用户轻松部署、管理和升级应用程序。

Helm软件包是预先打包好的 Kubernetes 应用程序,包括应用程序的 Kubernetes 清单文件和任何其他相关的资源,如配置文件、依赖项等。通过将这些软件包存储在 Helm 仓库中,用户可以轻松地共享和下载这些软件包,从而加速应用程序的部署和管理过程。

Helm 仓库通常分为两种类型:官方仓库和第三方仓库。官方仓库是由 Helm 维护的,提供了广泛的官方软件包供用户使用。第三方仓库则是由其他组织或个人创建和维护的,提供了更多的自定义和特定领域的软件包。

添加仓库并更新仓库信息到本地

helm repo add stable https://charts.helm.sh/stable
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

添加完成后再次查看仓库列表

[root@k8s-master01 ~]# helm repo list
NAME    URL
stable  https://charts.helm.sh/stable
ingress-nginx   https://kubernetes.github.io/ingress-nginx

接下来可以通过helm search命令查看ingress-nginx仓库有哪些可用的Chart,也可以直接搜索软件的名字,比如搜索ingress-nginx相关的Chart,格式:helm search repo ingress-nginx

Tips:由于此仓库和helm包名都一样,两个搜索一样,没区别。

  • NAME:Chart的名字。
  • CHART VERSION:Chart的版本。
  • APP VERSION:Chart内应用的名称。
  • DESCRIPTION:Chart的描述文件。

img

可以使用helm pull将某个Chart下载至本地,比如将上述检索的Kafka下载至本地。

#拉取最新版本
[root@k8s-master01 ~]# helm pull ingress-nginx/ingress-nginx

#拉取特定版本(先查历史版本号)
[root@k8s-master01 ~]# helm search repo ingress-nginx -l | head -n 6
NAME                            CHART VERSION   APP VERSION     DESCRIPTION
ingress-nginx/ingress-nginx     4.7.1           1.8.1           Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx     4.7.0           1.8.0           Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx     4.6.1           1.7.1           Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx     4.6.0           1.7.0           Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx     4.5.2           1.6.4           Ingress controller for Kubernetes using NGINX a...

[root@k8s-master01 ~]# helm pull ingress-nginx/ingress-nginx --version 4.5.2
[root@k8s-master01 ~]# ll ingress-nginx-4.5.2.tgz
-rw-r--r-- 1 root root 46009 Aug 30 14:48 ingress-nginx-4.5.2.tgz

Tips:k8s与Helm包相对应的版本查询:(https://github.com/kubernetes/ingress-nginx)

4.3 阿里云同步gcr镜像

将helm包进行解压并进入到该目录

[root@k8s-master01 ~]# tar xf ingress-nginx-4.5.2.tgz
[root@k8s-master01 ~]# cd ingress-nginx/

筛选出两个gcr镜像地址和版本(Controller 和 admissionWebhook 的镜像地址),进行阿里云同步gcr镜像,最后推到本地Harbor仓库。

[root@k8s-master01 ingress-nginx]# grep -A 5 "registry:" values.yaml
        registry: registry.k8s.io
        image: ingress-nginx/controller
        ## for backwards compatibility consider setting the full image url via the repository value below
        ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
        ## repository:
        tag: "v1.6.4"
--
                registry: registry.k8s.io
                image: ingress-nginx/kube-webhook-certgen
                ## for backwards compatibility consider setting the full image url via the repository value below
                ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
                ## repository:
                tag: v20220916-gd32f8c343
--

#拼接如下:
registry.k8s.io/ingress-nginx/controller:v1.6.4
registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343

echo "FROM registry.k8s.io/ingress-nginx/controller:v1.6.4" > ingress-nginx-controller-v1.6.4
echo "FROM registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343" > kube-webhook-certgen-v20220916-gd32f8c343

阿里云同步gcr镜像

image-20240509141553892

image-20240509142731809

Harbor主机上进行拉取镜像,然后推到本地Harbor仓库。

#拉取阿里云同步的镜像
docker pull registry.cn-hangzhou.aliyuncs.com/hpj/ingress:kube-webhook-certgen-v20220916-gd32f8c343
docker pull registry.cn-hangzhou.aliyuncs.com/hpj/ingress:ingress-nginx-controller-v1.6.4

#打上tag
docker tag registry.cn-hangzhou.aliyuncs.com/hpj/ingress:kube-webhook-certgen-v20220916-gd32f8c343 10.0.0.138:5000/library/kube-webhook-certgen:v20220916-gd32f8c343
docker tag registry.cn-hangzhou.aliyuncs.com/hpj/ingress:ingress-nginx-controller-v1.6.4 10.0.0.138:5000/library/controller:v1.6.4

#推送到Harbor仓库
docker push 10.0.0.138:5000/library/controller:v1.6.4
docker push 10.0.0.138:5000/library/kube-webhook-certgen:v20220916-gd32f8c343

4.4 修改value.yaml配置文件

需要进行修改地方:

  • 镜像的image地址改为本地仓库
  • 镜像的 digest 值注释
  • hostNetwork 设置为 true
  • dnsPolicy 设置为 ClusterFirstWithHostNet
  • nodeSelector 添加 ingress: "true" 部署至指定节点
  • 类型更改为 kind: DaemonSet
  • 将 ingress nginx 设置为默认的 ingressClass

修改镜像地址为本地仓库

[root@k8s-master01 ingress-nginx]# grep -A 5 "registry:" values.yaml
        registry: 10.0.0.138:5000
        image: library/controller
        ## for backwards compatibility consider setting the full image url via the repository value below
        ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
        ## repository:
        tag: "v1.6.4"
--
                registry: 10.0.0.138:5000
                image: library/kube-webhook-certgen
                ## for backwards compatibility consider setting the full image url via the repository value below
                ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
                ## repository:
                tag: v20220916-gd32f8c343

镜像的 digest 值注释

image-20240509144947068

image-20240509145008492

hostNetwork 设置为 true

image-20240509145047044

dnsPolicy 设置为 ClusterFirstWithHostNet

image-20240509145130028

nodeSelector 添加 ingress: "true" 部署至指定节点

image-20240509154631910

类型更改为 kind: DaemonSet

image-20240509145258377

将 ingress-nginx 设置为默认的 ingressClass

image-20240509145417173

4.5 打上Label标签并安装Ingress

#对需要部署Ingress的节点打上Label,DaemonSet部署方式就会在符合nodeSelector的节点上进行部署
kubectl label node k8s-node02 ingress=true

#创建给Ingress使用的命名空间
kubectl create ns ingress-nginx

#安装Ingress到ingress-nginx命名空间中
helm install ingress-nginx -n ingress-nginx .

image-20240509202838661

image-20240509203002688

5. 了解Ingress的yaml文件

5.1 yaml样例

创建Ingress和创建其他资源类似,也是使用YAML格式的文件。创建一个简单的Ingress代码如下:

v1版本

apiVersion: networking.k8s.io/v1  #版本大于1.22,必须选择v1,小于1.22是v1beta1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  ingressClassName: nginx #ingressClassName:指定ingress controller的名字,一个集群中可以有多个controller,如果是ingress-nginx,controller名字为nginx。(可通过这个命令查看kubectl get ingressclass)
  rules:  #一个Ingress可以配置多个rule。
  - host: www.zzb.com #一般都会配置对应的域名。
    http:
     paths: #域名的location配置,同一个host可以配置多个path。比如有一个路径格式为xxx.com/abc,可以配置path为/abc,每个路径都有一个对应的backend,对应到应用的Service和Port。
      - path: /
        pathType: ImplementationSpecific #pathType:路径的匹配方式,目前有ImplementationSpecific、Exact和Prefix方式。
        backend: #描述Service和Port的组合,对Ingress匹配主机和路径的HTTP与HTTPS请求将被发送到对应的后端。
          service:        #代理到那个Service
            name: nginx   #Service
            port:
              number: 80  #Service暴露的端口号

v1beta1版本

apiVersion: networking.k8s.io/v1beta1  #版本大于1.22,必须选择v1,小于1.22是v1beta1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"  #指定ingress controller的名字,可通过这个命令查看kubectl get ingressclass
spec:
  rules:
  - host: www.zzb.com 
    http:
     paths:
     - path: /abc
       pathType: ImplementationSpecific
       backend:
         serviceName: service1
         servicePort: 4200
     - path: /bar
       pathType: Prefix
       backend:
         serviceName: service2
         servicePort: 8080

5.2 配置详解

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.zzb.com
    http:
     paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: zzb-nginx
            port:
              number: 80
      - path: /abc
        pathType: ImplementationSpecific
        backend:
          service:
            name: abc-nginx
            port:
              number: 80

Ingress Rules对应spec.rules字段,一个Ingress可以配置多个rule。

  • host:可选,一般都会配置对应的域名。

  • paths:域名的location配置,同一个host可以配置多个path。比如有一个路径格式为xxx.com/abc,可以配置path为/abc,每个路径都有一个对应的backend,对应到应用的Service和Port。

  • pathType:路径的匹配方式,目前有ImplementationSpecific、Exact和Prefix方式。

    • ImplementationSpecific:这种类型的路由匹配根据Ingress Controller来实现,可以当作一个单独的类型,也可以当作Prefix和Exact。ImplementationSpecific是1.18版本引入Prefix和Exact的默认配置。
    • Exact:精确匹配,比如配置的path为/bar,那么/bar/将不能被路由。
    • Prefix:前缀匹配,基于以“/”分隔的URL路径。比如path为/abc,可以匹配到/abc/bbb等,比较常用的配置。
  • backend:描述Service和Port的组合,对Ingress匹配主机和路径的HTTP与HTTPS请求将被发送到对应的后端。

  • backend.service.name:代理的Service名字。

  • backend.service.port:代理的Service端口配置。

  • backend.service.port.number:数字型的端口号配置,比如80。

  • backend.service.port.name:名字型的端口号配置,比如web,表示Port的名字。

6. 使用Ingress

6.1 初试暴露某个Pod的Web服务

创建一个namespace

kubectl create ns study-ingress

假如公司有一个Web服务的容器,需要为其添加一个域名,此时可以使用Ingress实现该功能。下面创建一个简单的Nginx模拟Web服务。

kubectl create deployment zzb-nginx --image=10.0.0.138:5000/library/nginx --replicas=1 -n study-ingress

然后创建该Pod的 Service

kubectl -n study-ingress expose deployment zzb-nginx --port 80

--port:指定服务暴露的端口号。

--target-port:指定服务转发流量到的目标容器端口(如果不指定,则默认与 --port 相同)。

image-20240509155920581

将Pod中的index.html覆写,然后测试一下Service。

[root@k8s-master01 ingress-nginx]# kubectl get pod -n study-ingress -owide
NAME                         READY   STATUS    RESTARTS   AGE     IP               NODE         NOMINATED NODE   READINESS GATES
zzb-nginx-66f7d69694-8mk22   1/1     Running   0          3m41s   172.16.85.253    k8s-node01   <none>           <none>

[root@k8s-master01 ingress-nginx]# kubectl exec -n study-ingress zzb-nginx-66f7d69694-8mk22 -- bash -c 'echo "k8s-node01" > /usr/share/nginx/html/index.html'

image-20240509204319062

创建 Ingress 指向上面创建的 Service

[root@k8s-master01 ~]# cat web-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: zzb-ingress
  namespace: study-ingress
spec:
  rules:
  - host: www.zzb.com
    http:
      paths:
      - backend:
          service:
            name: zzb-nginx
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific

创建并查看资源

[root@k8s-master01 ingress-nginx]# kubectl apply -f web-ingress.yaml
ingress.networking.k8s.io/zzb-ingress created
[root@k8s-master01 ingress-nginx]# kubectl get ingresses.networking.k8s.io -n study-ingress
NAME          CLASS   HOSTS         ADDRESS   PORTS   AGE
zzb-ingress   nginx   www.zzb.com             80      14s
[root@k8s-master01 ingress-nginx]# kubectl describe ingresses.networking.k8s.io -n study-ingress zzb-ingress

image-20240509204540169

在宿主机上添加host记录(C:\Windows\System32\drivers\etc),指向部署Ingress的节点IP。(使用controller svc的ip+域名)

[root@k8s-master01 ingress-nginx]# kubectl get pod -n ingress-nginx -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE         NOMINATED NODE   READINESS GATES
ingress-nginx-controller-sdcrw   1/1     Running   0          74m   10.0.0.108   k8s-node02   <none>           <none>

10.0.0.108 www.zzb.com

在宿主机通过访问域名测试

image-20240509205337146

理解整个步骤,首先就是安装Ingress Controller

[root@k8s-master01 ingress-nginx]# kubectl get ingressclasses.networking.k8s.io
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       76m

然后就是创建deployment,接着暴露端口的话,就会创建一个Service,通过selector选择带有app: zzb-nginx标签的Pod。

[root@k8s-master01 ingress-nginx]# kubectl get deployments.apps -n study-ingress
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
zzb-nginx   1/1     1            1           20m
[root@k8s-master01 ingress-nginx]# kubectl get pod -n study-ingress
NAME                         READY   STATUS    RESTARTS   AGE
zzb-nginx-66f7d69694-fzgr4   1/1     Running   0          20m


[root@k8s-master01 ingress-nginx]# kubectl get svc -n study-ingress -oyaml
apiVersion: v1
items:
- apiVersion: v1
  kind: Service
  metadata:
    creationTimestamp: "2024-05-09T12:37:13Z"
    labels:
      app: zzb-nginx
    name: zzb-nginx
    namespace: study-ingress
    resourceVersion: "43441"
    uid: 1472257f-217a-449b-8192-ecbe9de7dfea
  spec:
    clusterIP: 192.168.245.91
    clusterIPs:
    - 192.168.245.91
    internalTrafficPolicy: Cluster
    ipFamilies:
    - IPv4
    ipFamilyPolicy: SingleStack
    ports:
    - port: 80
      protocol: TCP
      targetPort: 80
    selector:
      app: zzb-nginx
    sessionAffinity: None
    type: ClusterIP
  status:
    loadBalancer: {}
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

Tips:上面通过kubectl -n study-ingress expose deployment zzb-nginx --port 80进行暴露端口,自动Service名称与deployment名称一样,方便识别!最好创建Ingress时,在yaml文件中也最好用该名称,容易辨别!

最后创建Ingress进行暴露某个Web服务,也能看到该Ingress指向的Service,正是我们需要暴露的Web服务的Service。(此处创建时没有用Deployment和Service相同名称,不建议!)

image-20240509210245697

6.2 配置不配置域名访问服务

在上面的基础上,配置一个新的ingress

[root@k8s-master01 yaml]# cat no-host-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nohost-ingress
  namespace: study-ingress
spec:
  rules:
  - http:
      paths:
      - backend:
          service:
            name: zzb-nginx
            port:
              number: 80
        path: /nohost
        pathType: ImplementationSpecific

Tips:去掉host,然后配置好path进行匹配。

创建并查看Ingress

[root@k8s-master01 ingress-nginx]# kubectl get ingress -n study-ingress
NAME             CLASS   HOSTS         ADDRESS   PORTS   AGE
nohost-ingress   nginx   *                       80      4m14s
zzb-ingress      nginx   www.zzb.com             80      70m

创建测试目录和文件

kubectl exec -n study-ingress zzb-nginx-66f7d69694-fzgr4 -- bash -c 'mkdir /usr/share/nginx/html/nohost/ && echo "k8s-node01" > /usr/share/nginx/html/nohost/index.html'

访问测试

image-20240509215644240

image-20240509215700358

6.3 配置域名重定向Redirect

在 Nginx 作为代理服务器时,Redirect 可用于域名的重定向,比如访问 old.com 被重定向到 new.com。Ingress 可以更简单的实现 Redirect 功能,下面用 old.zzb.com 作为旧域名,baidu.com 作为新域名进行演示。配置重定向功能只需要填加一个annotations即可,key为nginx.ingress.kubernetes.io/permanent-redirect,值为目标URL:https://www.baidu.com

Tips:当一个服务需要更换域名时,并不能对其直接更改,需要一个过渡的过程。在这个过程中,需要将旧域名的访问跳转到新域名,此时可以使用Redirect功能。待旧域名无访问时,再停止旧域名。

[root@k8s-master01 yaml]# cat redirect-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: redirect-ingress
  namespace: study-ingress
  annotations:
    nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com
spec:
  rules:
  - host: old.zzb.com
    http:
      paths:
      - backend:
          service:
            name: zzb-nginx
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific

Tips:也可以在annotations下设置跳转代码,格式:nginx.ingress.kubernetes.io/permanent-redirect-code: '308'

创建Ingress并查看

[root@k8s-master01 yaml]# kubectl create -f redirect-nginx.yaml

[root@k8s-master01 yaml]# kubectl get ingress -n study-ingress
NAME               CLASS   HOSTS            ADDRESS   PORTS   AGE
redirect-ingress   nginx   old.zzb.com             80      2m31s

在节点上添加host记录后进行curl测试,代码301,已进行跳转。

[root@k8s-master01 yaml]# curl -I old.zzb.com
HTTP/1.1 301 Moved Permanently
Date: Thu, 31 Aug 2023 06:09:08 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://www.baidu.com

在宿主机上添加host记录后浏览器访问

10.0.0.108  www.zzb.com old.zzb.com

image-20240509231636234

image-20240509232202659

10.0.0.108(为controller地址)

6.4 配置前后端分离Rewrite

现在大部分应用都是前后端分离的架构,也就是前端用某个域名的根路径进行访问,后端接口采用/api进行访问,用来区分前端和后端。或者同时具有很多个后端,需要使用/api-a到A服务,/api-b到B服务,但是由于A和B服务可能并没有/api-a和/api-b的路径,因此需要将/api-x重写为“/”,才可以正常到A或者B服务,否则将会出现404的报错。

先拉取测试镜像,推送到本地仓库。

docker pull registry.cn-beijing.aliyuncs.com/dotbalo/nginx:backend-api
docker tag registry.cn-beijing.aliyuncs.com/dotbalo/nginx:backend-api 10.0.0.138:5000/library/nginx:backend-api
docker push 10.0.0.138:5000/library/nginx:backend-api

创建一个应用模拟后端服务

kubectl create deploy backend-api --image=10.0.0.138:5000/library/nginx:backend-api -n study-ingress

创建 Service 暴露该应用

kubectl expose deploy backend-api --port 80 -n study-ingress

查看该 Service 的地址,并且通过/api-a 访问测试。

image-20240509235830239

直接访问根路径是可以的

image-20240509235926767

通过 Ingress Nginx 的 Rewrite 功能,将/api-a 重写为“/”,配置示例如下:

[root@k8s-master01 yaml]# cat rewrite-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: backend-api
  namespace: study-ingress
spec:
  rules:
  - host: www.zzb.com
    http:
      paths:
      - backend:
          service:
            name: backend-api
            port:
              number: 80
        path: /api-a(/|$)(.*)
        pathType: ImplementationSpecific

以上path中的正则表达式是匹配以/api-a开头或该字符串结尾,也就是/api-a/或/api-a为一组。后面的(.*)就表示一个分组,用来匹配任意长度的字符串。请看下面例子,所以如果提供A服务的后端为svc-a,它的服务提供路径是 “/” 。此时如果直接访问http://svc-a/api-a/abc/123是无法访问的,所以通过正则匹配出abc/123进行Rewrite路径,此时链接就会成为http://svc-a/abc/123,则能够正常访问。

47b5e85e-a9bf-420e-b1e4-457463f7df95

image-20240510002509544

创建ingress并查看

kubectl create -f rewrite-nginx.yaml

image-20240510003416500

重新进行访问www.zzb.com/api-a测试,就能顺利代理到后端。

image-20240510002502086

6.5 配置错误代码重定向

如果访问一些不存在的路径或域名,就会抛出404的异常页面。对于生产环境,这个提示并不友好,会暴露Nginx的版本号,我们可以利用Nginx的错误代码重定向功能,将某些错误代码(比如404、403、503)重定向到一个固定的页面。

如果是采用Helm安装的Ingress Controller,直接更改values.yaml,之后执行helm upgrade即可(如果是静态文件安装,需要更改ingress-nginx的ConfigMap文件)。

拉取defaultbackend镜像(可以自己开发一个界面更友好的封装到镜像)

docker pull registry.cn-beijing.aliyuncs.com/dotbalo/defaultbackend-amd64:1.5
docker tag registry.cn-beijing.aliyuncs.com/dotbalo/defaultbackend-amd64:1.5 10.0.0.138:5000/library/defaultbackend-amd64:1.5
docker push 10.0.0.138:5000/library/defaultbackend-amd64:1.5

下面修改 values.yaml 如下图所示位置:

image-20240510010401294

更新 ConfigMap

    config:
      apiVersion: v1
      client_max_body_size: 20m
      custom-http-errors: "404,415,503"

image-20240510010320649

更新 Release

helm upgrade ingress-nginx -n ingress-nginx .

image-20240510010516970

更新完成后访问一个不存在的页面,比如之前定义的www.zzb.com。访问一个不存在的页面123,就会跳转到Error Server中的页面,且不会暴露版本号。

image-20240510010636187

6.6 配置https加密访问

生产环境对外的服务一般需要配置HTTPS协议,使用Ingress也可以非常方便地添加HTTPS的证书。由于是实验环境,并没有权威证书,因此需要使用OpenSSL生成一个测试证书(如果是生产环境,那么证书为在第三方公司购买的证书,无须自行生成):

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=www.zzb.com"

创建tls类型的secret

[root@k8s-master01 yaml]# kubectl create secret tls ca-secret --cert=tls.crt --key=tls.key -n study-ingress
secret/ca-secret created
[root@k8s-master01 ingress-nginx]# kubectl get secret -n study-ingress
NAME                  TYPE                                  DATA   AGE
ca-secret             kubernetes.io/tls                     2      5s
default-token-7xbl5   kubernetes.io/service-account-token   3      5h29m

为Ingress添加TLS配置:

[root@k8s-master01 yaml]# cat zzb-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: zzb-ingress
  namespace: study-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.zzb.com
    http:
      paths:
      - backend:
          service:
            name: zzb-nginx
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - www.zzb.com
    secretName: ca-secret

上面参数说明:

  • hosts:证书所授权的域名列表
  • secretName:证书的Secret名字

Tips:1.22+版本的k8s需要配置ingressClassName

接下来更新该Ingress

[root@k8s-master01 yaml]# kubectl replace -f zzb-nginx.yaml
ingress.networking.k8s.io/zzb-ingress replaced
[root@k8s-master01 yaml]# kubectl get ingress -n study-ingress zzb-ingress
NAME          CLASS   HOSTS         ADDRESS   PORTS     AGE
zzb-ingress   nginx   www.zzb.com             80, 443   24s

使用curl进行测试,域名已经被重定向到https。

image-20240510011835377

image-20240510011421017

6.7 配置访问基本认证

有些网站可能需要通过密码来访问,对于这类网站可以使用Nginx的basic-auth设置密码访问,具体方法如下,由于需要使用htpasswd工具,因此需要安装httpd:

yum install httpd -y

使用htpasswd创建zzb用户的密码

[root@k8s-master01 yaml]# htpasswd -c auth zzb
New password: 123456
Re-type new password: 123456
Adding password for user zzb
[root@k8s-master01 yaml]# cat auth
zzb:$apr1$NgU16QZV$CikrBRY4BZgvFseIqMSVZ/

基于之前创建的密码文件创建Secret

kubectl create secret generic basic-auth --from-file=auth -n study-ingress

image-20240510013938821

修改yaml,使Ingress增加密码认证的功能。

  • nginx.ingress.kubernetes.io/auth-type:认证类型,可以是basic和digest。
  • nginx.ingress.kubernetes.io/auth-secret:密码文件的Secret名称。
  • nginx.ingress.kubernetes.io/auth-realm:需要密码认证的消息提醒。
[root@k8s-master01 yaml]# cat zzb-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: zzb-ingress
  namespace: study-ingress
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    nginx.ingress.kubernetes.io/auth-realm: Please Input Your Username and Password
spec:
  ingressClassName: nginx
  rules:
  - host: www.zzb.com
    http:
      paths:
      - backend:
          service:
            name: zzb-nginx
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - www.zzb.com
    secretName: ca-secret

更新该Ingress

kubectl replace -f zzb-nginx.yaml

宿主机访问测试

image-20240510014257868

6.8 配置区分手机端和PC端

开发一个网页或者应用时,往往会适配计算机端和手机端,通常会将移动客户端访问的页面重定向到移动端的服务上,大家也有可能经常见到m.xxx.com此类的域名,基本都属于移动端服务。Nginx可以通过一个请求的请求头判断客户端来源,并将其路由到指定的服务上。本节将演示把来自移动端的访问重定向到移动端服务,计算机端访问保持默认即可。

部署手机端应用和PC端应用

#手机端部署并暴露端口,配置ingress资源
kubectl create deploy phone --image=registry.cn-beijing.aliyuncs.com/dotbalo/nginx:phone -n default
kubectl expose deploy phone --port 80 -n default
kubectl create ingress phone --rule=m.test.com/*=phone:80 -n default

#PC端部署并暴露端口,配置ingress资源
kubectl create deploy laptop --image=registry.cn-beijing.aliyuncs.com/dotbalo/nginx:laptop -n default
kubectl expose deploy laptop --port 80 -n default
kubectl create ingress laptop --rule=www.test.com/*=laptop:80 -n default

书写Ingress的yaml

[root@k8s-master01 yaml]# cat laptop-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/server-snippet: |
      set $agentflag 0;
              if ($http_user_agent ~* "(Android|iPhone|Windows Phone|UC|Kindle)" ){
                set $agentflag 1;
              }
              if ( $agentflag = 1 ) {
                return 301 http://m.test.com;
              }
  name: laptop
  namespace: default
spec:
  rules:
  - host: www.test.com
    http:
      paths:
      - backend:
         service:
           name: laptop
           port:
             number: 80
        path: /
        pathType: ImplementationSpecific

nginx.ingress.kubernetes.io/server-snippet: |: 这个注解设置一个自定义的 Nginx 配置片段。在这个配置片段中,根据请求的 User-Agent 进行了一些判断和重定向操作。下面的 Nginx 配置片段用于判断请求的 User-Agent 是否匹配指定的设备类型,如果匹配,则将请求重定向到http://m.test.com

部署Ingress

 kubectl replace -f laptop-ingress.yaml

宿主机添加两条host记录(www.test.com和m.test.com),然后浏览器访问测试,直接访问www.test.com没问题!然后再通过浏览器开发者工具模拟移动设备访问,成功自动跳转手机端!

image-20240510015419957

image-20240510015413543

6.9 配置黑/白名单

有些网页可能只需要指定用户访问,比如公司的ERP只能公司内部访问,此时可以使用白名单限制访问的IP。有些网页不允许某些IP访问,比如一些有异常流量的IP,此时可以使用黑名单禁止该IP访问。

配置黑名单

配置黑名单禁止某一个或某一段IP,需要在Nginx Ingress的ConfigMap中配置,比如将10.0.0.138(多个配置使用逗号分隔)添加至黑名单:

image-20240510021925157

在滚动更新之前,先测试10.0.0.138是否可以访问http://www.test.com,方便后面验证,观察到可以正常访问

image-20240510022026512

更新 nginx-ingress-controller,会reload新的配置。

helm upgrade ingress-nginx -n ingress-nginx .

image-20240510022252562

重新在10.0.0.138主机上进行访问测试,已经被禁止访问了。在宿主机访问,正常!

image-20240510022114823

配置白名单

白名单表示只允许某个IP访问,直接在YAML文件中配置即可(也可以通过ConfigMap配置),比如只允许10.0.0.138访问,只需要添加一个nginx.ingress.kubernetes.io/whitelist-source-range注释即可:

[root@k8s-master01 yaml]# cat zzb-nginx.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: zzb-ingress
  namespace: study-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    nginx.ingress.kubernetes.io/auth-realm: Please Input Your Username and Password
    nginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.138
spec:
  ingressClassName: nginx
  rules:
  - host: www.zzb.com
    http:
      paths:
      - backend:
          service:
            name: zzb-nginx
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - www.zzb.com
    secretName: ca-secret

Tips:禁止访问是全局的,但在单独的yaml配置中加入白名单,它就可以访问该Ingress资源。

更新Ingress

kubectl replace -f zzb-nginx.yaml

此时10.0.0.138能够解除之前全局禁止访问,能够访问www.zzb.com,但宿主机和其他节点访问就会报403了。但是10.0.0.138的主机依然访问不了其他站点。

由于之前的yaml有配置用户名和密码所以需要使用到-k(跳过ssl认证) -u(指定用户名:密码)

image-20240510023835727

image-20240510024125681

6.10 实现蓝绿、灰度/金丝雀发布

6.10.1 背景

现如今,越来越多的应用采用了微服务架构,这也导致了应用数量相比传统模式更多,管理更加复杂,发布更加频繁,如果直接将新版本上线发布给全部用户。一旦遇到线上事故(或BUG),对用户的影响极大,解决问题周期较长,甚至有时不得不回滚到前一版本,严重影响了用户体验。为了保证整体系统的稳定,风险降到最低,我们可以采用蓝绿发布与灰度/金丝雀发布等不同的发布方式。

6.10.2 蓝绿发布

蓝绿发布,提供了一种零宕机的部署方式,是一种以可观测的方式发布应用的方式,目的减少发布过程中停止时间。在保留老版本的同时部署新版本,将两个版本同时在线,新版本和老版本相互热备,通过切换路由权重的方式(非0即100)实现应用的不同版本上线或者下线,如果有问题可以快速地回滚到老版本。这样做的好处是无需停机,并且风险较小。

img

先创建模拟测试环境的 Namespace 和服务,然后进行创建v1版本和v2版本。

#创建命名空间
kubectl create ns canary

#创建v1版本
kubectl create deploy canary-v1 --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/canary:v1 -n canary
kubectl expose deploy canary-v1 --port 8080 -n canary

#创建v2版本
kubectl create deploy canary-v2 --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/canary:v2 -n canary
kubectl expose deploy canary-v2 --port 8080 -n canary

查看Service并进行curl

[root@k8s-master01 ingress-nginx]# kubectl get svc -n canary
NAME        TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)    AGE
canary-v1   ClusterIP   192.168.254.132   <none>        8080/TCP   34s
canary-v2   ClusterIP   192.168.100.150   <none>        8080/TCP   14s
[root@k8s-master01 ingress-nginx]#
[root@k8s-master01 ingress-nginx]# curl 192.168.254.132:8080
<h1>Canary v1</h1>
[root@k8s-master01 ingress-nginx]#
[root@k8s-master01 ingress-nginx]# curl 192.168.100.150:8080
<h1>Canary v2</h1>

创建Ingress并查看

kubectl create ingress canary-v1 --rule=canary.com/*=canary-v1:8080 -n canary

[root@k8s-master01 ingress-nginx]# kubectl get ingress -n canary
NAME        CLASS   HOSTS        ADDRESS   PORTS   AGE
canary-v1   nginx   canary.com             80      12s

宿主机添加host记录后访问

image-20240510031507636

下面创建一个新的ingress资源,来实现蓝绿发布。

  • nginx.ingress.kubernetes.io/canary: "true"表示启用了金丝雀发布策略。
  • nginx.ingress.kubernetes.io/canary-weight: "100":表示流量到新版本的比例为 100%,即将全部流量都到新版本,也就是蓝绿发布。
[root@k8s-master01 ingress-nginx]# cat canary-v2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "100"
  name: canary-v2
  namespace: canary
spec:
  ingressClassName: nginx
  rules:
  - host: canary.com
    http:
      paths:
      - backend:
          service:
            name: canary-v2
            port:
              number: 8080
        path: /
        pathType: ImplementationSpecific

创建Ingress,并进行测试。

[root@k8s-master01 ingress-nginx]# kubectl apply -f canary-v2.yaml
ingress.networking.k8s.io/canary-v2 created
[root@k8s-master01 ingress-nginx]# kubectl get ingress -n canary
NAME        CLASS   HOSTS        ADDRESS   PORTS   AGE
canary-v1   nginx   canary.com             80      14m
canary-v2   nginx   canary.com             80      72s

#测试命令
for i in {1..20}; do curl http://canary.com/; done;

image-20240510031956107

6.10.3 灰度/金丝雀发布

需要上线新版本v2应用,又不想直接替换成新版本v2应用,而是希望将20%的流量切换新版本。待运行一段时间稳定后,可将所有流量从老版本v1应用切换到新版本v2应用中,再平滑地将老版本v1应用下线。

img

修改canary-v2.yaml,修改为 nginx.ingress.kubernetes.io/canary-weight: "20" (服务权重流量切分),则表示流量到新版本的比例为 20%。

[root@k8s-master01 ingress-nginx]# cat canary-v2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "20"
  name: canary-v2
  namespace: canary
spec:
  ingressClassName: nginx
  rules:
  - host: canary.com
    http:
      paths:
      - backend:
          service:
            name: canary-v2
            port:
              number: 8080
        path: /
        pathType: ImplementationSpecific

更新Ingress并进行测试

kubectl replace -f canary-v2.yaml

#测试命令
for i in {1..20}; do curl http://canary.com/; done;

image-20240510032427056

6.10.4 基于客户端来源IP的流量切分

假设线上已运行了一套对外提供的七层 demo 应用,此时开发了一些新的功能,需要上线新版本 demo 应用,又不想直接替换成新版本 demo 应用,而是只希望公司内部人员能访问到新版本 demo 应用中,进行测试验证新版本 demo 应用,非公司内部人员访问还是访问到老版本应用中。等公司内部人员测试验证通过并稳定后,可将所有流量从老版本 demo 应用切换到新版本 demo 应用中,再平滑地将老版本 demo 应用下线。

#在ingress的yaml文件中加入下面内容即可
annotations:
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-by-header: "X-Forwarded-For"
  # 假设客户端来源IP为123.456.789.123
  nginx.ingress.kubernetes.io/canary-by-header-value: "123.456.789.123"
6.10.5 基于客户端请求头的流量切分

假设线上已运行了一套对外提供的七层 demo 应用,此时开发了一些新的功能,需要上线新版本demo应用,但是又不想直接替换成新版本 demo 应用,而是希望将请求头包含user=zzb的客户端请求转发到新版本demo应用中,进行验证测试新版本demo应用,等测试验证通过并稳定后,可将所有流量从老版本 demo 应用切换到新版本 demo 应用中,再平滑地将老版本 demo 应用下线。

#在ingress的yaml文件中加入下面内容即可
annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    # 请求头为user
    nginx.ingress.kubernetes.io/canary-by-header: "user"
    # 请求头user的值为zzb时,请求才会被路由到新版本服务中。
    nginx.ingress.kubernetes.io/canary-by-header-value: "zzb"

7. 环境清理

7. 环境清理

清除命名空间canary的所有deploy,svc,ingress

kubectl delete deploy,svc,ingress -n canary --all

清除命名空间canary的所有deploy,svc,ingress

kubectl delete deploy,svc,ingress -n study-ingress --all

删除命名空间

kubectl delete ns study-ingress canary

注:本篇学习笔记内容参考杜宽的《云原生Kubernetes全栈架构师》,视频、资料文档等,大家可以多多支持!还有YinJayChen语雀k8s训练营“我为什么这么菜”知乎博主等资料文档,感谢无私奉献!

posted @ 2024-07-01 02:46  zzbao  阅读(204)  评论(0)    收藏  举报