ingress和ingressController

ingress是一个API,是一个规则,是个虚拟的东西

kubernetes提供service我们常用两种方式ClusterIP和NodePort,另外还有LoadBalance类型和ExternalName类型(目前这两种没用过,不做阐述)。ClusterIP是在集群内部用service的IP加端口来访问服务;而NodePort可以跨主机访问,只要能ping通k8s节点就能访问而不必局限在本集群环境,使用集群内部的节点IP加端口来访问,这种方式非常方便,但当有几十上百的服务在集群中运行时,NodePort的端口管理就比较困难。k8s还提供了一种集群维度暴露服务的方式,也就是ingress。Ingress 不是一种服务类型,ingress可以简单理解为service的service,它充当集群的入口点。 它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下公开多个服务。

需要知道的是,ingress只是个规则,你必须具有 IngressController 才能满足 Ingress 的要求。仅创建 Ingress资源本身没有任何效果。下面是一个官网上的ingress示例的修改版,我把其中host可选项去掉了,如果没有host的话就适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了 host(例如 foo.bar.com),则 rules 适用于该 host.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

这个表示访问curl http://foo访问的是服务service1:4200端口,curl http://bar访问的是服务service2:8080端口。

Ingress Controller是需要用户自己实现的

Ingress是一个非常“极客”并需要DIY的产物,kubernetes只负责提供一个API定义,具体的IngressController需要用户自己实现!官方提供了Nginx Ingress Controller等其他几种Controller示例共开发者参考。实现一个IngressController大致框架是:List/Watch Kubernetes 的Service、Endpoints、Ingress对象,并根据这些信息刷新外部Load Balancer(例如nginx)的规则和配置。值得注意的是,如果想要通过域名访问Ingress,则需要用户自己配置域名和IngressIP的配置关系,例如host文件、自己的DNS(不是kube-dns). 下图是nginx官网给出的nginx-ingress-controller架构图

ingress和ingressController创建实例

Talk is cheap,show me the code.下面说一下我创建ingress和ingressController创建的过程,以及在这个过程中看到的一些思考和问题。

创建ingress

我使用如下YAML文件创建了一个ingress, 创建完之后除了可以kubectl get ingress有输出之外,不会有任何服务或者容器启动。只是创建了一个规则而已。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress 
spec:
  ingressClassName: nginx-ingress-controller 
  rules:
  - host: test-ko-mix-master-1 
    http:
      paths:
      - path: /index.html
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: nginx2
            port:
              number: 80

注意,这里的http前面没有横杠,这里没有namespace,ingressClassName我暂且理解为指明你想用哪个ingressController,如果你没有定义一个class,那么你的云provider会使用缺省的ic.用这个创建之后查看ingress,得到如下结果:

[root@test-ko-mix-master-1 20211109]# kubectl get ingress -A -o wide
NAMESPACE   NAME            CLASS                      HOSTS                  ADDRESS   PORTS   AGE
default     nginx-ingress   nginx-ingress-controller   test-ko-mix-master-1             80      2s

这里,ADDRESS一栏是空的(后面设定了service后会更新为service的IP),如果没有设定ingressClassName那么CLASS这一栏是none.

创建nginx-ingress-controller

这个nginx-ingress-controller是官方写的一个ic示例,如果你需要controller其他的资源需要自己去开发,这个资源可以是daemonset,也可以是deployment.启动的容器会自动拉起一个nginx进程并且会根据ingress的规则配置容器内nginx的配置。我
用如下的mandatory.yaml 文件来创建nginx-ingress-controller:

点击查看代码
[root@test-ko-mix-master-1 20211109]# cat mandatory.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: udp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress-serviceaccount
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: nginx-ingress-clusterrole
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses/status
    verbs:
      - update

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: nginx-ingress-role
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      # Defaults to "<election-id>-<ingress-class>"
      # Here: "<ingress-controller-leader>-<nginx>"
      # This has to be adapted if you change either parameter
      # when launching the nginx-ingress-controller.
      - "ingress-controller-leader-nginx"
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - get

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: nginx-ingress-role-nisa-binding
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nginx-ingress-role
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: nginx-ingress-clusterrole-nisa-binding
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nginx-ingress-clusterrole
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx

---

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
#  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      nodeSelector:
        kubernetes.io/hostname: test-ko-mix-master-1
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.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
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 101
            runAsUser: 101
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
              hostPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              hostPort: 443
              protocol: TCP
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown

---

apiVersion: v1
kind: LimitRange
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  limits:
  - min:
      memory: 90Mi
      cpu: 100m
    type: Container

找到创建出来的nginx-ingress-controller容器,kubectl exec -ti -n ingress-nginx pod/nginx-ingress-controller-vt5lq cat /etc/nginx/nginx.conf ,可以看到ingress设定的一些规则:

        ## start server test-ko-mix-master-1
        server {
                server_name test-ko-mix-master-1 ;
                
                listen 80  ;
                listen 443  ssl http2 ;
                
                set $proxy_upstream_name "-";
        ...
                location /foo {
                        
                        set $namespace      "default";
                        set $ingress_name   "nginx-ingress";
                        set $service_name   "";
                        set $service_port   "";
                        set $location_path  "/foo";
  

这个时候你外集群外节点去curl ngin-ingress-controller所在主机的80 端口可以得到404网页返回。另外,我们查看nginx-ingress-controller的log,发现它一直在报错:err services "ingress-nginx" not found.这个是说我们创建nginx-ingress-controller中启动参数制定了publish-service为ingress-nginx:

- /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

因此我们要创建一个nodeport类型的service,name是ingress-nginx,创建该service的YAML文件如下:

点击查看代码
[root@test-ko-mix-master-1 20211109]# cat service-nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

创建完ingress-nginx service之后再次查看ingress,ADDRESS栏位有了值,也就是service IP Address:

[root@test-ko-mix-master-1 20211109]# kubectl get ingress -A -o wide 
NAMESPACE   NAME            CLASS                      HOSTS                  ADDRESS           PORTS   AGE
default     nginx-ingress   nginx-ingress-controller   test-ko-mix-master-1   192.168.255.207   80      48m
[root@test-ko-mix-master-1 20211109]# kubectl describe ingress -n default     nginx-ingress 
Name:             nginx-ingress
Namespace:        default
Address:          192.168.255.207
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
  Host                  Path  Backends
  ----                  ----  --------
  test-ko-mix-master-1  
                        /index.html   nginx:80 (10.8.188.31:80)
                        /bar          nginx2:80 (10.8.188.36:80)
Annotations:            <none>
Events:                 <none>

正是有了这个nodeport类型的service我们才能在集群外的节点curl test-ko-mix-master-1:/index.html(这里test-ko-mix-master-1可以在/etc/hosts中配成集群的任何一个节点的IP)才能得到复杂的nginx容器的页面。否则只能把test-ko-mix-master-1在/etc/hosts中配成controller所在节点的IP才能访问(这是加nodePort或者80端口都可以访问,why?)。
因为我们ingress制定的url foo后端的service是nginx,所以我们要创建这个service及其后面的endpoints.否则nginx-ingress-controller会报错说找不到nginx这个service. 创建完成之后我的理解就是在集群群外的节点输入curl test-ko-mix-master-1 /foo 就访问到那边呢?nginx service对应的endpoint pod上吗?还是负载到nginx-ingress-controller里面的nginx进程上呢?根据官网的架构图理解应该是先到nginx-ingress-controller的容器,由它负载到后面的service,也就是nginx service对应的endpoints我看到过有的文档说流量不经过service由nginx-ingress-controller直接负载到service后面的endpoint也就是pod上,但是我没有找到controller所在主机的80监听端口也没有找到ipvs转发主机80端口的规则。那么我不敢妄评说controller的流量到底是怎么转发的。有知道的大神可以在评论区指教!
另外,我在集群主机上看到这样一条IPVS转发规则:

TCP  172.18.8.203:32629 rr
  -> 10.8.188.30:80               Masq    1      0          0      

意思就是访问本机(172.18.8.203)32629 端口的转发到10.8.188.30:80,其中10.8.188.30是nginx-ingress-controller 容器的IP Address.

ingressClass

当集群中存在多于一个的 Ingress Controller 时,就需要为 Ingress 指定对应的 Controller 是哪个,在 Kubernetes 1.18 之前,通常是在 Ingress 资源上通过 kubernetes.io/ingress.class 注解来指定使用的 Ingress Controller。虽然这个注解从未被正式定义过,但确是被各个 Ingress Controller 所广泛支持的。Kubernetes 1.18 起,正式提供了一个 IngressClass 资源,作用与 kubernetes.io/ingress.class 注解类似,例如:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: nginx-ingress-internal-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

其中重要的属性是 metadata.name 和 spec.controller,前者是这个 IngressClass 的名称,需要设定在 Ingress 中,后者是 Ingress Controller 的名称。Ingress 中的 spec.ingressClassName 属性,可以用来指定对应的 IngressClass,并进而由 IngressClass 关联到对应的 Ingress Controller,如:

kind: Ingress
metadata:
  name: spring-k8s
spec:
  ingressClassName: external-lb
  defaultBackend:
    service:
      name: spring-k8s
      port:
        number: 80

注意,spec.ingressClassName 与 metadata.annotations.kubernetes.io/ingress.class 的作用并不完全相同,因为 ingressClassName 字段引用的是 IngressClass 资源的名称,IngressClass 资源中,除了指定了 Ingress Controller 的名称之外,还可能会通过 spec.parameters 属性定义一些额外的配置。

题外话,正向代理和反向代理

  • 正向代理类似一个跳板机,代理访问外部资源。比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了。正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.
    正向代理的用途:
    (1)访问原来无法访问的资源,如google
    (2)可以做缓存,加速访问资源
    (3)对客户端访问授权,上网进行认证
    (4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
  • 反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端。
    反向代理的作用:
    (1)保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网
    (2)负载均衡,通过反向代理服务器来优化网站的负载

参考

posted @ 2021-11-10 17:35  JaneySJ  阅读(1075)  评论(0编辑  收藏  举报