Loading

第9章:Ingress

9.1 Ingress为弥补NodePort不足而生

1 单独用service暴露服务的方式,在实际生产环境中不太合适 ClusterIP 只能在集群内部访问。 NodePort 方式的话,测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。 LoadBalance 方式受限于云平台,且通常在云平台部署ELB还需要额外的费用。

2 ingress与ingress-controller

ingress对象: 指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。

ingress-controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。Ingress Controller会动态感知集群中的Ingress的规则变化,然后读取,动态生成Nginx的配置文件,最后注入到运行nginx的pod的中,然后会自动reload,配置生效。

3 k8s真正提供服务的是pod,为了负载均衡,为了使用域名, service 诞生了,然后 ingress 诞生了,那么Ingress的作用如下

(1) 帮助位于集群中的Service能够有一个对外可达的url,即让集群外的客户端也可以访问到自己。(对于这一点,NodePort类型的Service可以实现,只支持4层负载均衡,一个端口只能一个服务使用,端口需提前规划) (2) 做专业的负载均衡,毕竟Service的负载均衡还是很初级的

4 Ingress工作流程

如下图所示,流量到达外部负载均衡器(externalLB)后,首先转发至Service资源Ingres-nginx上,然后通过Ingress控制器基于Ingress资源定义的规则将客户端请求流量直接转发至与Service对应的后端Pod资源之上。这种转发机制会绕过Service资源(app Serviceapi Service),从而省去了由kube-proxy实现的端口代理开销。Ingress规则需要由一个Service资源对象辅助识别相关的所有Pod资源。如下Ingress通过app service资源去匹配后端的pod1pod2;这个app service只是起到一个辅助识别功能。

user -> 域名 -> node ip:80/443 -> ingress controller -> 域名分流 –> pod

ingress controller pod -> 获取service(apiserver)关联的pod -> 应用到本地nginx(提供七层负载均衡)

因为是DaemoSet方式部署的ingress controller, 所以除master节点外每个从node节点都会部署一个ingress controller的pod并监听80和443端口。

image-20210506234626157[4]

9.2 Pod与Ingress的关系

  • 通过Service相关联

  • 通过Ingress Controller实现Pod的负载均衡

    • 支持TCP/UDP 4层和HTTP 7层

pod-ingress[4]

9.3 Ingress Controller

为了使Ingress资源正常工作,集群必须运行一个Ingress Controller(负载均衡实现)。

所以要想通过ingress暴露你的应用,大致分为两步:

  1. 部署Ingress Controller

  2. 创建Ingress规则

整体流程如下:

ingress-controller[4]

Ingress Controller有很多实现,我们这里采用官方维护的Nginx控制器。

其他主流控制器:(1) Traefik: HTTP反向代理、负载均衡工具 (2) Istio:服务治理,控制入口流量

部署文档:https://github.com/kubernetes/ingress-nginx/blob/nginx-0.30.0/docs/deploy/index.md

# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml

注意事项:
- 镜像地址修改成国内的(国外的源无法连接):lizhenliang/nginx-ingress-controller:0.20.0
- 使用宿主机网络(提高网络转发性能):hostNetwork: true

# vim mandatory.yaml
......
spec:
      hostNetwork: true
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: lizhenliang/nginx-ingress-controller:0.20.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
......

# kubectl apply -f mandatory.yaml
# kubectl get pods -n ingress-nginx -o wide
NAME                             READY   STATUS   RESTARTS   AGE     IP           NODE
nginx-ingress-controller-9r25t   1/1     Running   0         5m16s   172.16.1.71   k8s-node1
nginx-ingress-controller-p8g66   1/1     Running   0         5m16s   172.16.1.72   k8s-node2

# 查看ingress-controller中的nginx配置文件
# kubectl exec -it nginx-ingress-controller-9r25t sh -n ingress-nginx
$ ls /etc/nginx/nginx.conf
/etc/nginx/nginx.conf
$

# 此时在任意Node上就可以看到该控制监听的80和443端口,80和443端口就是接收来自外部访问集群中应用流量,转发对应的Pod上
# netstat -natp |egrep ":80|:443"
tcp       0     0 0.0.0.0:80              0.0.0.0:*               LISTEN     83841/nginx: master
tcp       0     0 0.0.0.0:443             0.0.0.0:*               LISTEN     83841/nginx: master

9.4 Ingress

接下来,就可以创建ingress规则了。

在ingress里有三个必要字段:

  • host: 访问该应用的域名,也就是域名解析

  • serverName: 应用的service名称

  • serverPort: service端口

1、HTTP访问

# vim http.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: example-lc-ingress
spec:
  rules:
  - host: example.lc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80

# kubectl apply -f http.yaml

# kubectl get ingress
NAME                 HOSTS           ADDRESS   PORTS   AGE
example-lc-ingress   example.lc.com             80     47s

# 添加windows本地hosts解析
C:\Windows\System32\drivers\etc\hosts
172.16.1.71 example.lc.com

# 使用域名访问
http://example.lc.com/

注意:

(1) 生产环境:example.lc.com 域名是在你购买域名的运营商上进行解析,A记录值为K8S Node的公网IP(该Node必须运行了Ingress controller)。

(2) 测试环境:可以绑定hosts模拟域名解析("C:\Windows\System32\drivers\etc\hosts"),对应IP是K8S Node的内网IP。例如:

172.16.1.71 example.lc.com

2、HTTPS访问

# vim sslhttp.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: sslexample-lc-ingress
spec:
  tls:
  - hosts:
    - sslexample.lc.com
    secretName: sslexample-lc-com
  rules:
    - host: sslexample.lc.com
      http:
        paths:
        - path: /
          backend:
            serviceName: nginx
            servicePort: 80

# kubectl apply -f sslhttp.yaml

# kubectl get ingress
NAME                    HOSTS               ADDRESS   PORTS     AGE
sslexample-lc-ingress   sslexample.lc.com             80, 443   8s

里面用到了secret名为example-lc-com,用于保存https证书。这里使用cfssl工具自签证书用于测试,先下载cfssl工具:

# cat cfssl.sh
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl*
mv cfssl_linux-amd64 /usr/bin/cfssl
mv cfssljson_linux-amd64 /usr/bin/cfssljson
mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo

# cat certs.sh

cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing"
        }
    ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

cat > sslexample.lc.com-csr.json <<EOF
{
  "CN": "sslexample.lc.com",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing"
    }
  ]
}
EOF

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes sslexample.lc.com-csr.json | cfssljson -bare sslexample.lc.com

执行certs.sh脚本,生成证书:
# sh certs.sh
# ls *.pem
ca-key.pem ca.pem sslexample.lc.com-key.pem sslexample.lc.com.pem

将证书保存在secret里:

# kubectl create secret tls sslexample-lc-com --cert=sslexample.lc.com.pem --key=sslexample.lc.com-key.pem
# kubectl get secret
NAME                  TYPE                                 DATA   AGE
default-token-wc5fp   kubernetes.io/service-account-token   3     13d
sslexample-lc-com     kubernetes.io/tls                     2     16s

# kubectl describe secrets sslexample-lc-com

这样,ingress就能通过secret名称拿到要用的证书了,然后绑定本地hosts,就可以https访问了:https://sslexample.lc.com/

3、根据URL路由到多个服务

# vim url.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: url-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: foobar.lc.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: service1
          servicePort: 80
  - host: foobar.lc.com
    http:
      paths:
      - path: /bar
        backend:
          serviceName: service2
          servicePort: 80

工作流程:
foobar.lc.com -> 172.16.1.71 -> /foo   service1:80
                                 /bar   service2:80

4、基于名称的虚拟主机

# vim vhost.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.lc.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: bar.lc.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80

工作流程:
foo.lc.com --|                 |-> service1:80
                | 172.16.1.71 |
bar.lc.com --|                 |-> service2:80

9.5 Annotations对Ingress个性化配置

参考文档 :https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md

HTTP:配置Nginx常用参数

# vim nginx_parameter.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
     kubernetes.io/ingress.class: "nginx“
     nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
     nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
     nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
     nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
  rules:
  - host: example.lc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80

HTTPS:禁止访问HTTP强制跳转到HTTPS(创建https后通过http访问默认会跳转到https
# vim redirect_https.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: tls-example-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx“
    nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
  tls:
  - hosts:
    - sslexample.lc.com
    secretName: secret-tls
  rules:
    - host: sslexample.lc.com
      http:
        paths:
        - path: /
          backend:
            serviceName: nginx
            servicePort: 80

9.6 Ingress Controller高可用方案

如果域名只解析到一台Ingress controller,是存在单点的,挂了就不能提供服务了。这就需要具备高可用,有两种常见方案:

ingress-controller-ha[4]

左边:双机热备,选择两台Node专门跑Ingress controller,然后通过keepalived对其做主备。用户通过VIP访问。

固定ingress controller到两个node上(daemonset+nodeselector),user -> 域名 -> vip(keepalived) ha -> pod

右边:高可用集群(推荐),前面加一个负载均衡器,转发请求到后端多台Ingress controller。

固定ingress controller到两个node上(daemonset+nodeselector),user -> 域名 -> LB(nginx、lvs、haproxy)-> ingress controller -> pod


posted @ 2021-05-08 01:24  云起时。  阅读(256)  评论(0编辑  收藏  举报