Kubernetes之Service和Ingress

1.  Service资源及其实现模型

    1.  Service资源概述

        Service资源基于标签选择器将一组Pod定义成一个逻辑组合,并通过自己的IP地址和端口调度代理请求至组内的Pod对象之上,它向客户端隐藏了真实的处理用户请求的Pod资源,使得客户端的请求看上去就像是由Service直接处理并进行响应的一样

        Service对象的IP地址也称为Cluster IP,是一种虚拟IP地址,在Service对象创建后保持不变,并且能够被同一集群中的Pod资源访问。Service端口用于接收客户端请求并将其转发至其后端的Pod中应用的相应端口之上,这种代理机制称为端口代理

        

    2.   虚拟IP和服务代理

        一个Service对象就是工作节点上的一些iptables或ipvs规则,用于将到达Service对象IP地址的流量调度转发到相应的Endpoints对象指向的IP地址和端口之上          

        kube-proxy将请求代理至相应断电的方式有三种

        1.  userspace代理模型

            在kubernetes1.1版本之前,userspace是默认的代理模型

            在这种模型中,kube-proxy负责跟踪API Server上Service和Endpoints对象的变动,并据此调整Service资源的定义。

            对于每个 Service 对象,它会随机打开个本地端口(运行于用户 间的 kube-proxy 进程负责监昕),任何到达代理端口的连接请求都将被代理至当前 Service 资源后端Pod 对象上,至于会中哪个 Pod 对象取决于当前 Service 资源的调度方式,默认的调度算法轮询( round-robin  外,此类的 Servic巳对象还iptables 规则以捕获任何到达 ClusterIP 和端口的流

        2.  iptables代理模型

            在kubernetes1.2版本后开始,成为默认的类型

            对于每个 Service ,它都iptables 规则直接捕获到达 ClusterIP 和 Port 的流,并将其重定至当前 Service 的后端, 对于每个 Endpoints 对象, Service 资源会为其创建iptables 规则并关联至挑选的后端 Pod 资源,默认算法是随机调度( random

            iptables模型无须将流量在用户空间和内核空间来回切换,因而更加高效和可靠。

        3.  ipvs代理模型

            在kubernetes1.11版本后开始,成为默认的类型

            此种模型中, kube-proxy 跟踪 APIServer 上 Servic巳和 Endpoints 对象的变动,据此来调用 netlink 接口创建 ipvs 规则,并确保与 API Se凹er 中的变动保持步, 它与 iptables 规则的不同之处仅在于其请求流量的调度功能由ipvs 实现 ,余下的其他功能仍由 iptables完成

2.  Service资源的基础应用

    1.  Service存在的意义

        1.  防止Pod失联(服务发现)

        2.  定义一组Pod的访问策略(负载均衡)

        3.  支持ClusterIP,NodePort以及LoadBalancer三种类型  

    2.  创建Service资源

        1.  使用命令

            kubectl expose

        2.  使用yaml文件

            定义service资源对象,spec两个常用的内嵌字段:selector和ports            

kind: Service
   apiVersion: vl
metadata:
     name: myapp-svc
spec :
    selector:
       app: myapp
    ports:
    - protocol: TCP
      port: 80
      targetPort: 80

            Service 资源 myapp-svc 通过标签选择器关联至标签为“ app=myapp ”的各 Pod 对象,它会自动建名为 myapp - svc 的 Endpoints 资源对象,并自动配置一个 ClusterIP , 暴露的端口由 port 字段进行指定,后端各 Pod 对象的端口则由 targetPort 给出,也可以使用同 port 段的默认值          

    3.  查看service资源对象信息

        kubectl get svc myapp-svc

        kubectl get endpoints myapp-svc

    4.  Service会话粘性        

Service 资源还支持 Session affinity (粘性会话或会话粘性)机制,它能够将来自同 一个
客户端的请求始终转发至同一个后端的 Pod 对象,这意味着它会影响调度算法的流量分发
功用,进而降低其负载均衡的效果 。 因此,当客户端访问 Pod 中的应用程序时,如果有基
于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,那
么应该启用 session affinity 机制 。
Session affinity 的效果仅会在一定时间期限内 生效,默认值为 10 800 秒,超出此时长
之后,客户端的再次访问会被调度算法重新调度 。 另外 , Service 资源的 S巳ssion affinity 机
制仅能基于客户端 IP 地址识别客户端身份,它会把经 由同一个 NAT 服务器进行源地址转换
的所有客户端识别为同一个客户端,调度粒度粗糙且效果不佳,因此,实践中并不推荐使用
此种方法实现粘性会话 。 此节仅用于为读者介绍其功能及实现 。

3.  服务发现

    pod客户端中的应用如何得知某个特定Service资源的IP和端口?

    通过服务发现。

    1.  服务发现类型

        客户端发现

          由客户端到服务注册中心发现其依赖到的服务的相关信息,因此,它需要内置特定的服务发现程序和发现逻辑  

        服务端发现    

          这种方式需要额外用到一个称为中央路由器或服务均衡器的组件;服务消费者将请求发往中央路由器或者负载均衡器,由它们负责查询服务注册中心获取服务提供者的位置信息,并将服务消费者的请求转发给服务提供者

        常用的服务注册中心

          zookeeper,etcd分布式键值存储系统,它们只提供数据存储功能,更注重数据的一致性

          Eureka一可用性目的为先,可以在多种故障期间保持服务发现和服务注册的功能可用

          Consul除了服务发现的基础功能还有多数据中心的部署能力

        k8s使用的服务发现系统

          k8s1.3版本,kubeDNS

          k8s1.11版本,CoreDNS

    2.  服务发现方式

        1.  环境变量

            1.  kubernetes service环境变量

                在同一名称空间中创建的Pod对象都会自动拥有下面的变量

                {SVCNAME}_SERVICE_HOST

                {SVCNAME}_SERVICE_PORT

            2.  Docker Link形式的环境变量

[java@jar-wms-75dc8bbc7f-skz9c ansible]$ printenv | grep JAR
JAR_WMS_PORT=tcp://10.1.45.135:80
JAR_WMS_PORT_80_TCP_PORT=80
JAR_WMS_PORT_80_TCP_ADDR=10.1.45.135
JAR_WMS_PORT_80_TCP=tcp://10.1.45.135:80
JAR_WMS_PORT_80_TCP_PROTO=tcp 

        2.  DNS记录

            1.  拥有ClusterIP的service资源,需要具有以下的资源记录

                A记录

                SRV记录

                PTR记录

            2.  Headless类型的service资源,需要具有以下的资源记录            

                A记录

                SRV记录

                PTR记录

            3.  ExternalName类型的service资源,需要具有以下的资源记录

                CNAME记录

        3.  resolv.conf文件内容            

            nameserver 10.1.0.10  这是kube-dns的IP地址
            search default.svc.cluster.local svc.cluster.local cluster.local

            {NAMESPACE}.svc.{CLUSTER_DOMAIN}:如 default.svc.cluster.local
             SVC. {CLUSTER DOMAIN }:如 svc.cluster.local
            {CLUSTER_DOMAIN}:如 cluster.local
            {WORK_NODE_DOMAIN}:如 ilinux.io

4.  服务暴露

    service的IP仅在集群内可达,当一些服务需要暴露到外部网络中,就需要在集群的边缘为其添加一层转发机制,以实现将外部请求流量接入到集群的service资源之上,这种操作称为发布服务到外部网络中。

    1.  service类型

        1.  ClusterIP

            此地址仅在集群内部可达,而无法被集群外部的客户端访问,为默认的Service类型

        2.  NodePort

            在工作节点的IP地址上选择一个端口用于将集群外部的用户请求转发至目标Service的ClusterIP的Port,因此这种类型既可以像ClusterIP一样受到集群内客户端Pod的访问,也会接受集群外部客户端通过<NodeIP>:<NodePort>进行的请求。

            默认端口范围:30000~32767            

kind : Service
apiVersion : vl
metadata:
    name : myapp-svc-nodeport
spec .
    type: NodePort
    selector :
       app: myapp
    ports :
    - protocol : TCP
       port : 80
       targetPort : 80
       nodePort: 32223           

        3.  LoadBalancer

            这种类型构建在NodePort类型之上,其通过cloud provider提供的负载均衡器将服务暴露到集群外部。

            例如 Amazon 云计算环境中的 ELB 例即为此类的负载均衡设备此类型的优势在于,它能够把来自 于集群外部客户端的请求调度至有节点(或部分节点)的 NodePort 之上,而不是依赖于客户自行决定连接至哪个节点,从而避免了因客户端指定的节点故障而导致的服务不可用

            

kind : Service
apiVersion : vl
metadata:
    name : myapp-svc-nodeport
spec .
    type: LoadBalancer
    selector :
       app: myapp
    ports :
    - protocol : TCP
       port : 80
       targetPort : 80
       nodePort: 32223                                      

        4.  ExternalName

            此种型并非定义由 Kubemetes 集群提供的服务,而是把集群外部的某服务以 DNSCNAME记录的方式映射到集群内,从而让群内的 Pod 资源能够访问外部的 Service 的实现方式

apiVersion : vl
metadata :
    name : external-redis-svc
    namespace : default
spec:
    type : ExternalName
    externalName : redis.ilinux.io
    ports :
    - protocol : TCP
       port : 6379
       targetPort : 6379
       nodePort : 0
    selector : {}

5.  Headless类型的Service资源  

    客户端要求直接访问Service资源后端的所有pod资源,这时就应该向客户端暴露每个pod资源的IP地址,而不再是中间层Service对象的ClusterIP,这种类型的Service资源便称为Headless Service

    1.  创建Headless Service资源

kind : Service
apiVersion : v1
metadata :
     name : myapp-headless-svc
spec:
     clusterIP: None
     selector :
         app : myapp
     ports :
     -   port : 80
         targetPort : 80
         name : httpport

    2.  查看Headless资源的endpoints记录

        kubectl describe svc myapp-headless-svc

    3.  pod资源发现

        Headless Service通过标签选择器关联到所有pod资源的IP地址

        客户端向Headless Service对象发起的请求将直接接入到pod资源中的应用之上,而不再由Service资源进行代理转发,它每次接入的pod资源则由DNS服务器接收到查询请求时以轮询的方式返回的IP地址

6.  Ingress资源

    1.  Ingress为解决NodePort的痛点

        痛点

          1.  端口冲突

          2.  NodePort只支持四层

        pod与ingress的关联

          1.  通过service关联pod

          2.  基于域名访问

          3.  通过ingress controller实现pod的负载均衡

              支持TCP/UDP四层以及HTTP七层

    2.  Ingress和Ingress Controller

        一组基于DNS名称或URL路径将请求转发至指定的Service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布,这个就叫做Ingress

        能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器,这个控制器不是kube-controller-manager的一部分,它是一个附件,需要在集群上单独部署

    3.  创建Ingress资源

apiVersion : extensions/v1beta1
kind : Ingress
metadata :
     name : my-ingress
         annotations :
             kubernetes.io/ingress.class : "nginx"
spec:
   rules :
   - host : www.ilinux.io
     http :
        paths :
        - backend :
          serviceName : myapp-svc
          servicePort: 80  

    4.  Ingress资源类型

        1.  单Service资源型Ingress

apiVersion : extensions / v1beta1
kind : Ingress
metadata :
     name: my-ingress
spec :
    backend:
        serviceNarne: my-svc
        servicePort : 80

        2.  基于URL路径进行流量分发

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/cors-allow-credentials: 'true'
    nginx.ingress.kubernetes.io/cors-allow-headers: >-
      DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization
    nginx.ingress.kubernetes.io/cors-allow-methods: 'PUT, GET, POST, OPTIONS'
    nginx.ingress.kubernetes.io/cors-allow-origin: '*'
    nginx.ingress.kubernetes.io/enable-cors: 'true'
    nginx.ingress.kubernetes.io/service-weight: ''
  creationTimestamp: '2019-06-27T12:36:08Z'
  generation: 1
  name: http-back
spec:
  rules:
  - host: back.dev.zp.com
    http: &http_rules
      paths:
      - path: /adapter
        backend:
          serviceName: tomcat-adapter
          servicePort: 80
      - path: /backend
        backend:
          serviceName: tomcat-backend
          servicePort: 80 

        3.  基于主机名称的虚拟主机

apiVersion : extensions/v1beta1
kind : Ingress
metadata :
     name: test
spec :
    rules :
    - host: api.ik8s.io
      http :
         paths :
         - backend :
               serviceName: api
               servicePort : 80
    - host : wap.ik8s.io
       http :
           paths :
           - backend :
                 serviceName : wap
                 servicePort : 80    

        4.  TLS类型的Ingress资源

apiVersion : extensions/v1beta1
kind : Ingress
metadata:
     name : no-rules map
spec :
    tls :
    - secretName: ikubernetesSecret
    backend :
        serviceName : homesite
        servicePort : 80

    5.  部署Ingress Controller

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
data:

  # 获取真实IP地址
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  use-forwarded-headers: "true"

  # nginx-ingress前端还有代理,后端获取真实IP,前端代理务必配置,配置方法如下URL
  # https://a5pop.com/container/2020-06-04-nginx-ingress-real-ip.html
  # 如果ingress的前端代理不设置,后端会无法访问,报错broken header
  # proxy-real-ip-cidr: 10.127.0.10
  use-proxy-protocol: "true"

  # 客户端请求头的缓冲区大小 
  client-header-buffer-size: "512k"
  # 设置用于读取大型客户端请求标头的最大值number和size缓冲区
  large-client-header-buffers: "4 512k"
  # 读取客户端请求body的缓冲区大小
  client-body-buffer-size: "128k"
  # 代理缓冲区大小
  proxy-buffer-size: "256k"
  # 代理body大小
  proxy-body-size: "50m"
  # 服务器名称哈希大小
  server-name-hash-bucket-size: "128"
  # map哈希大小
  map-hash-bucket-size: "128"
  # SSL加密套件
  ssl-ciphers: "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
  # ssl 协议
  #ssl-protocols: "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 SSLv2 SSLv3"
  ssl-protocols: "TLSv1.2 TLSv1.3"
  # http重定向https
  #ssl-redirect: 'true'
  # 日志格式
  log-format-upstream: '[$the_real_ip] - $remote_user [$time_local] "$request" $status $body_bytes_sent $request_time "$http_referer" $host DIRECT/$upstream_addr $upstream_http_content_type "$http_user_agent" "$http_x_forwarded_for" $request_length [$proxy_upstream_name] $upstream_response_length $upstream_response_time $upstream_status $req_id'

---
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:
      - "extensions"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - "extensions"
    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: Deployment
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: 2
  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:
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.24.1
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: 1000m
              memory: 1000Mi
            requests:
              cpu: 30m
              memory: 50Mi
          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 -> 33
            runAsUser: 33
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
              #hostPort: 80
            - name: https
              containerPort: 443
              #hostPort: 443
          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         

            kubectl apply -f nginx-ingress.yaml

            kubectl get pods -n ingress-nginx -o wide  查看ingress控制器所在的节点服务器  

    6.  使用ingress暴露https应用

        1.  拿到域名的私钥和公钥  如果有购买,供应商会给你证书和私钥;如果没有就自己做一个自签名证书

        2.  kubectl create secret tls wzlinux-secret --cert=wzlinux.crt --key=wzlinux.key

        3.  查看创建好的secret

            kubectl describe secret wzlinux-secret

        4.  创建ingress资源                

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: wzlinux-ingress
spec:
  tls:
  - hosts:
    - www.wzlinux.com
    secretName: wzlinux-secret
  rules:
  - host: www.wzlinux.com
    http:
      paths:
      - path: /
        backend:
          serviceName: wzlinux-svc
          servicePort: 8080                                         

        5.  通过浏览器访问https://www.wzlinux.com  

        6.  查看ingress-controller的内容

            kubectl get pods -n ingress-nginx -o wide  获取ingress-controller的名称

            kubectl exec -it nginx-ingress-controller-2xdz8 bash -n ingress-nginx  登录到指定的ingress-controller中

            可以看到server模块中的test.zhen.net和test2.zhen.net的内容

  

  

            

    

            

                        

                                        

posted @ 2022-05-17 16:37  奋斗史  阅读(302)  评论(0)    收藏  举报