更新日期:2022-03 调整 nginx-controller 版本; 更新 ingress 资源 apiversion。

0. 背景和环境

Service 对集群之外暴露服务的主要方式有两种:NotePort和LoadBalancer,但是这两种方式,都有一定的缺点:

  • NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显,另外实际业务是通过域名访问的,根据不同的域名跳转到不同端口服务。
  • LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持

基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。

node1 master Ubuntu 20.04(ARM64) 192.168.50.11
node2 worker Ubuntu 20.04(ARM64) 192.168.50.12
node3 worker Ubuntu 20.04(ARM64) 192.168.50.13
node4 Storage Ubuntu 20.04(ARM64) 192.168.50.147

1. Ingress 和 Ingress Controller

什么是 Ingress?
ingress 是 kubernetes 中的一种 API 资源,需要使用 ingress controller 来实现 ingress。
image.png
什么是 Ingress Controller?
为了使 Ingress 正常工作,集群中必须运行 Ingress controller。 这与其他类型的控制器不同,其他类型的控制器通常作为 kube-controller-manager 二进制文件的一部分运行,在集群启动时自动启动。 你需要选择最适合自己集群的 Ingress controller 或者自己实现一个。

一个最简单的 ingress

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: test-ingress
  5. spec:
  6. rules:
  7. - http:
  8. paths:
  9. - path: /testpath
  10. backend:
  11. serviceName: test
  12. servicePort: 80

如果没有配置 Ingress controller 就将其 POST 到 API server 不会有任何用处。
总结一下:
ingress 是 kubernetes 中的一个对象,作用是定义请求如何转发到service的规则
ingress controller 是具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如Nginx, Contour, Haproxy等等

2. Ingress Controller 工作原理

a. ingress controller 通过与 k8s api 进行交互,动态感知 k8s 集群中 ingress 服务规则的变化,然后读取它,并按照定义的 ingress 规则,转发到 k8s 集群中对应的 service。
b. ingress 规则写明了哪个域名对应 k8s 集群中的哪个 service,然后再根据 ingress-controller 中的 nginx 配置模板,生成一段对应的 nginx 配置。
c. 然后再把该配置动态的写到 ingress-controller 的 pod 里,该 ingress-controller 的 pod 里面运行着一个 nginx 服务,控制器会把生成的 nginx 配置写入到 nginx 的配置文件中,然后 reload 一下,使其配置生效, 以此来达到域名配置及动态更新的效果。
处理 http 请求的过程需要经过service 吗?
通过Ingress可从集群外访问K8s资源,访问流程如下:

  • 从dns获取域名对应的ip。
  • 将请求发送到该IP,Ingress Controller收到请求。
  • Ingress Controller根据转发规则,找到对应到Service及其背后的Endpoint。
  • Ingress Controller随机挑选一个Pod将请求转发给该Pod。

    3. 部署 Ingress Controller

    Deployment 和 DaemonSet 部署的区别
    使用 deployment,并且 replicate 为 1 时,这样将会在某一台节点上启动对应的 nginx-ingress-controller pod。外部流量访问至该节点,由该节点负载分担至内部的 service。测试环境考虑防止单点故障,改为 DaemonSet ,配合亲和性部署在指定节点上启动 nginx-ingress-controller pod,确保有多个节点启动 nginx-ingress-controller pod,后续将这些节点加入到外部硬件负载均衡组实现高可用性。

    3.1 使用 Deployment 方式部署

    3.1.1准备配置文件

    wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/1.21/deploy.yaml
    

    3.1.2 调整配置文件

    # 修改Pod 网络类型,暴露 nginx-ingress-controller pod 的服务端口(80)
    sed -i '/dnsPolicy:/i\      hostNetwork: true' deploy.yaml
    # 国内镜像加速
    sed -i 's/k8s.gcr.io/lank8s.cn/' deploy.yaml
    

    3.1.3 创建 Nginx Controller 相关资源

    kubectl apply -f deploy.yaml
    

    3.1.4 检查

    ```shell $ kubectl get pods -n ingress-nginx NAME READY STATUS RESTARTS AGE ingress-nginx-admission-create-666gb 0/1 Completed 0 7m17s ingress-nginx-admission-patch-7t5jj 0/1 Completed 3 7m17s ingress-nginx-controller-7cd586cc64-2k2qt 1/1 Running 0 7m17s

$ kubectl get svc -n ingress-nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller NodePort 10.96.63.215 80:31808/TCP,443:32081/TCP 7m27s ingress-nginx-controller-admission ClusterIP 10.96.98.53 443/TCP 7m26s

<a name="DH02V"></a>
#### 3.1.5 绑定 Node 节点(可选)
目前 ingress-controller 会被调度器分配到某一个节点,如果需要指定一台独立的 node 节点运行 ingress-controller,就需要 node 节点打标签。
```shell
# 获取节点标签
kubectl get nodes --show-labels
# 给指定的节点打标签
kubectl label nodes 节点_IP nginx=nginx

image.png
调整 deployment 中节点选择器的标签
image.png
更新配置

kubectl apply -f deploy.yaml

检查

kubectl get pods -n ingress-nginx

$ kubectl get pods -n ingress-nginx -o wide
NAME                                        READY   STATUS              RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create-666gb        0/1     Completed           0          28m   10.244.104.3    node2   <none>           <none>
ingress-nginx-admission-patch-7t5jj         0/1     Completed           3          28m   10.244.135.2    node3   <none>           <none>
ingress-nginx-controller-767f5dcbf4-tmrdw   0/1     ContainerCreating   0          15m   192.168.50.12   node2   <none>           <none>
ingress-nginx-controller-7cd586cc64-2k2qt   1/1     Running             0          28m   192.168.50.13   node3   <none>           <none>

$ kubectl get pods -n ingress-nginx -o wide
NAME                                        READY   STATUS        RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create-666gb        0/1     Completed     0          29m   10.244.104.3    node2   <none>           <none>
ingress-nginx-admission-patch-7t5jj         0/1     Completed     3          29m   10.244.135.2    node3   <none>           <none>
ingress-nginx-controller-767f5dcbf4-tmrdw   1/1     Running       0          16m   192.168.50.12   node2   <none>           <none>
ingress-nginx-controller-7cd586cc64-2k2qt   0/1     Terminating   0          29m   192.168.50.13   node3   <none>           <none>

$ kubectl get pods -n ingress-nginx -o wide
NAME                                        READY   STATUS      RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create-666gb        0/1     Completed   0          30m   10.244.104.3    node2   <none>           <none>
ingress-nginx-admission-patch-7t5jj         0/1     Completed   3          30m   10.244.135.2    node3   <none>           <none>
ingress-nginx-controller-767f5dcbf4-tmrdw   1/1     Running     0          16m   192.168.50.12   node2   <none>           <none>

3.2 使用 DaemonSet 方式部署

3.2.1 准备配置文件

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/1.21/deploy.yaml
#https://github.com/kubernetes/ingress-nginx/blob/nginx-0.30.0/deploy/static/mandatory.yaml
#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
#https://github.com/kubernetes/ingress-nginx/blob/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml

3.2.2 调整配置文件

在没有可用的外部负载均衡器并且不使用 NodePorts 的设置中,可以配置 ingress-nginx Pod 使用它们运行的主机的网络,而不是专用的网络命名空间。 这种方法的好处是 NGINX Ingress 控制器可以将端口 80 和 443 直接绑定到 Kubernetes 节点的网络接口,而无需 NodePort 服务强加的额外网络转换。
image.png

# 修改为 DaemonSet 类型
sed -i 's/Deployment/DaemonSet/' deploy.yaml
#修改 Pod 网络
#template:
#  spec:
#    hostNetwork: true
sed -i '/dnsPolicy:/i\      hostNetwork: true' deploy.yaml

由于镜像在 k8s.gcr.io,所有需要替换国内可以访问的镜像地址。

sed -i 's/k8s.gcr.io/lank8s.cn/' deploy.yaml

安全考虑:启用此选项会将 NGINX Ingress 控制器每个系统守护进程暴露给任何网络接口上 ,包括主机的回环接口。请仔细评估这可能对您的系统安全产生的影响。

3.2.3 创建Nginx Controller 相关资源

kubectl apply -f deploy.yaml

3.2.4 检查


$ k get pod -n ingress-nginx -o wide
NAME                                   READY   STATUS      RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
ingress-nginx-admission-create-sknfj   0/1     Completed   0          27s   10.244.104.6    node2   <none>           <none>
ingress-nginx-admission-patch-w2djb    0/1     Completed   1          27s   10.244.135.5    node3   <none>           <none>
ingress-nginx-controller-kptlw         1/1     Running     0          27s   192.168.50.13   node3   <none>           <none>
ingress-nginx-controller-s2plx         1/1     Running     0          27s   192.168.50.12   node2   <none>           <none>


$ k get svc -n ingress-nginx        
NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.96.199.16   <none>        80:31687/TCP,443:32298/TCP   58s
ingress-nginx-controller-admission   ClusterIP   10.96.238.5    <none>        443/TCP                      58s

3.2.5 绑定 Node 节点(可选)

#添加亲和性属性
#增加亲和性部署,有 custom/ingress-controller-ready 标签的节点才会部署该 DaemonSet
kubectl label node 192.168.50.12 custom/ingress-controller-ready=true
kubectl label node 192.168.50.13 custom/ingress-controller-ready=true

在 yaml 中增加 nodeSelector 指定标签并更新资源即可。由于 true 是比较特殊的字符,因此需要加双引号。
image.png

4. 测试应用

4.1 创建测试应用

创建测试应用kubectl apply -f tomcat-nginx.yaml

cat > tomcat-nginx.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: tomcat-pod
  template:
    metadata:
      labels:
        app: tomcat-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.5-jre10-slim
        ports:
        - containerPort: 8080

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: default
spec:
  selector:
    app: nginx-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: default
spec:
  selector:
    app: tomcat-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
EOF

检查

$ kubectl get pods
NAME                                 READY   STATUS    RESTARTS   AGE
nginx-deployment-5ffc5bf56c-5vfwl    1/1     Running   0          8h
nginx-deployment-5ffc5bf56c-km5cm    1/1     Running   0          8h
tomcat-deployment-7db86c59b7-6qxfs   1/1     Running   0          8h
tomcat-deployment-7db86c59b7-9d92t   1/1     Running   0          8h

$ kubectl get svc
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP    3d9h
nginx-service    ClusterIP   None         <none>        80/TCP     8h
tomcat-service   ClusterIP   None         <none>        8080/TCP   8h

4.2 创建 ingress http 规则

4.2.1 使用 networking.k8s.io/v1 版本

cat > ingress-http-v1.yaml << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
  - host: nginx.kubernetes.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80
  - host: tomcat.kubernetes.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: tomcat-service
            port:
              number: 8080
EOF

更多 ingress 的配置,参考官网文档:

4.2.2 使用 networking.k8s.io/v1beta1 版本[1.22+将被弃用]

cat > ingress-http-v1beta1.yaml << EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-http
  namespace: default
spec:
  rules:
  - host: nginx.kubernetes.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.kubernetes.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
EOF

4.2.3 创建 ingress https 规则

4.2.3.1 创建 SSL 证书和 Secret 资源

申请证书

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=kubernetes.com"

创建 Secret 资源

kubectl create secret tls tls-secret --key tls.key --cert tls.crt
# kubectl get secrets

4.2.3.2 使用 networking.k8s.io/v1 版本
cat > ingress-https-v1.yaml << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-https
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
    - nginx.kubernetes.com
    - tomcat.kubernetes.com
    secretName: tls-secret # 指定秘钥
  rules:
  - host: nginx.kubernetes.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-service
            port:
              number: 80
  - host: tomcat.kubernetes.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: tomcat-service
            port:
              number: 8080
EOF

验证

$ kubectl get ingress                             
NAME            CLASS    HOSTS                                        ADDRESS   PORTS     AGE
ingress-https   <none>   nginx.kubernetes.com,tomcat.kubernetes.com             80, 443   70s

$ kubectl describe ing ingress-https
Name:             ingress-https
Namespace:        default
Address:          
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
  tls-secret terminates nginx.kubernetes.com,tomcat.kubernetes.com
Rules:
  Host                   Path  Backends
  ----                   ----  --------
  nginx.kubernetes.com   
                         /   nginx-service:80 (10.244.104.5:80,10.244.135.4:80)
  tomcat.kubernetes.com  
                         /   tomcat-service:8080 (10.244.104.4:8080,10.244.135.3:8080)
Annotations:             nginx.ingress.kubernetes.io/rewrite-target: /
Events:                  <none>

4.2.3.3 使用networking.k8s.io/v1beta1 版本
cat > ingress-https-v1beta1.yaml << EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-https
  namespace: default
spec:
  tls:
  - hosts:
    - nginx.kubernetes.com
    - tomcat.kubernetes.com
    secretName: tls-secret # 指定秘钥
  rules:
  - host: nginx.kubernetes.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.kubernetes.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
EOF

验证【略】

4.3 验证 ingress

kubectl get pods -n ingress-nginx -o wide
kubectl get ingress [-o yaml]

4.5 配置访问

配置 host 文件,指定测试域名和 ingress Controller 绑定的 Node IP后,测试访问域名

192.168.50.12 nginx.kubernetes.com
192.168.50.12 tomcat.kubernetes.com

image.png

curl -I -k https://nginx.kubernetes.com
curl -I -k https://tomcat.kubernetes.com

5. ingress 实验

6. 参考文档

https://www.jianshu.com/p/52889bc8571d
https://blog.csdn.net/weixin_44729138/article/details/105978555
https://jimmysong.io/kubernetes-handbook/concepts/ingress.html
https://www.yuque.com/polaris-docs/container/cw8t2e
https://www.yuque.com/woshinianshaodexihuan/zt9p71/hd8u0y
https://kubernetes.io/zh/docs/concepts/services-networking/ingress/
https://www.cnblogs.com/varden/p/15126662.html
https://www.cnblogs.com/varden/p/15127374.html
https://www.icode9.com/content-4-984126.html