$ ls ~yifei/notes/

Kubernetes 初探——服务

Posted on:

Last modified:

相关概念

Service. 如果一个 Deployment 对外(pod 外)提供服务的话,可以定义 Service 以通过 kube-proxy 和 DNS 等提供负载均衡暴露给外部。服务一共有三种类型:

  • ClusterIP. 服务暴露在集群内部的一个虚拟 IP,IP 生命周期和服务相同
  • NodePort. 暴露在所有 Node 上的服务端口,不建议在生产环境使用。
  • LoadBalancer,通过外部的 LB,暴露对应的服务。

另外还可以使用 Ingress Controller. Service 只是对集群内部暴露了服务,Ingress Controller 用于把 Service 再对外暴露,就相当于一个反向代理。

这时将创建一个 ClusterIP,需要特别注意的是,这个 ClusterIP 也是虚拟的,并没有和任何 pod 绑定,而是绑定到了服务上,可以理解为绑定到了这个服务对应的内部负载均衡上,并且是不变的。 即使你的 Deployment 中指定了多个副本,那么也只有这一个 clusterIP,pod 的创建和销毁也都不会影响到 clusterIP。

ClusterIP 是集群内部提供服务的默认方式。可以采用 kubelet proxy + ClusterIP 的方式临时提供对外服务。

NodePort 方式在每个 host 上都开一个端口来对外提供服务,适合一些临时性的服务。

ClusterIP + LoadBalancer

使用云厂商提供的 LoadBalancer 来提供服务,唯一的缺点是每一个服务会消耗一个 IP,比较贵。

除此之外,还可以使用 MetalLB 等等第三方的 LB。

Ingress + LB

Ingress 身是一个 Service,然后路由到不同的服务。实际上 Ingress 就是反向代理。一般情况下 Ingress 只能用来暴露 80 或者 443 端口的服务。如果需要暴露其他端口的服务,最好使用 Load Balancer 或者是 NodePort 型服务。

但是要暴露出去 Ingress 服务本身,可能还是要使用 NodePort 或者 LoadBalancer。

Internet -> Loadbalancer -> Ingress Controller -> Ingress Rules -> K8s-Services -> Replicas

Internet -> NodePort -> Ingress Controller -> Ingress Rules -> K8s-Services -> Replicas

在 K3S 中自带了一个 service lb,所以只要确保 ingress controller 第一个暴露出 loadbalancer 类型的 80 端口,就能自动通过 80 端口访问了。

Traefik Ingress Controller

Traefik 在 2.0 中不止支持了 K8S 默认的 ingress,还自己定义了一种 IngressRoute 类型的 CRD。 千万不要用这个 IngressRoute,他还不支持证书,简直就是废物。尽量不要使用 Traefik,bug 太多了。

nginx Ingress Controller

Nginx 毕竟是官方支持的 ingress,比较稳定,而且性能也比较好。

Cert Manager

Cert-Manager 用于管理 k8s 集群内部的证书。

Cert-Manager 中最重要的三个概念是:

  • Issuer, 只能在当前命名空间颁发证书
  • ClusterIssuer,可以在所有 namespace 颁发证书
  • Certificate, 颁发的证书

实战

安装 cert-manager

kubectl create namespace cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml
kubectl get pods -n cert-manager  # 可以看到几个在运行的 pod

安装一个 clusterissuer

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: <your>@<email>.<com> # 替换掉这个
    privateKeySecretRef:
      name: prod-issuer-account-key
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
      - http01:
          ingress:
            class: traefik
        selector: {}

然后,kubectl apply -f letsencrypt.yaml

安装一个服务

# 创建一个简单的 deployment,这里我们用了 rancher logos 这个服务
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rancher-logo-app
  namespace: default
spec:
  selector:
    matchLabels:
      name: rancher-logo-backend
  template:
    metadata:
      labels:
        name: rancher-logo-backend
    spec:
      containers:
        - name: backend
          image: rancher/logos:rancher
          ports:
            - containerPort: 80
---
# 创建一个 service,暴露了 80 端口,注意这里不需要暴露 443 端口
apiVersion: v1
kind: Service
metadata:
  name: rancher-logo-service
  namespace: default
spec:
  selector:
    name: rancher-logo-backend
  ports:
    # 这里的 port 可以做一个转换
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
---
# 创建一个 ingress,并在 annotation 中指定了使用 cluster-issuer
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rancher-logo-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: traefik  # 使用 traefik 做 ingress
    cert-manager.io/cluster-issuer: letsencrypt-prod  # 使用 cluster-issuer
spec:
  tls:
    - secretName: logos-ezly-tv-tls  # 指定证书对应的 secret
      hosts:
        - logos.ezly.tv
  rules:
  - host: logos.ezly.tv  # ingress 的配置
    http:
      paths:
        - path: /
          backend:
            service: rancher-logo-service  # 指向的 service
            port:
              number: 80

需要注意的是,https 证书的发放需要时间,可能得等 1 分钟左右才能从 https 协议访问。

服务治理有两种方式:

  • 一种是直接在服务中集成熔断降级等操作
  • 一种是使用 sidecar 模式,有公共组件来处理

两种模式的区别如图:

探针

Kubernetes 使用探针来确定服务当前的状态,以及是否需要重启和分配流量。

Kubernetes 中共有三类探针:

  1. Startup Probe,用来探测 Pod 是否启动完成;
  2. Liveliness Probe,用来探测 Pod 是否存活;
  3. Readiness Probe,用来探测 Pod 是否就绪,可否分配流量。

看起来我们似乎只需要一个 Liveliness Probe 或者 Readiness Probe 就够了,毕竟只有当容器存活的时候我们给他分配流量就够了,为什么要分三个呢?

为什么需要 Startup Probe?

假设你的容器启动很慢,比如说启动的时候需要编译字节码之类的,那么你需要定义一个 Startup Probe,否则在启动过程中就可能因为没有响应而被干掉重启了,从而陷入死循环。

为什么需要 Readiness Probe?

假设你的容器已经启动了,可以响应简单的 Liveliness Probe 了,但是程序的主要功能需要加载一个很大的文件,这时候还不能提供席上服务,那么你需要定义一个 Readiness Probe,在数据加载完成后再通过检测,这时候 Kubernetes 才会分配流量。

当一个 Pod 启动的时候,大概的流程是这样的:

  1. 如果定义了 Startup Probe,那么首先检查这个探针,直到这个探针成功的时候才开始检测 Liveliness Probe。
  2. 如果定义了 Liveliness Probe,那么当这个探针检测失败的时候,重启服务。
  3. 如果定义了 Readiness Probe,那么当这个探针成功的时候,给当前服务分配流量。

探针的实现方式

大概分为三种:

  1. exec,看 shell 命令返回是不是 0
  2. http get,看返回的状态码是不是正常的
  3. tcp port,看端口有没有打开

参考

  1. https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0
  2. https://stackoverflow.com/questions/45079988/ingress-vs-load-balancer
  3. https://sysadmins.co.za/https-using-letsencrypt-and-traefik-with-k3s/
  4. https://medium.com/@jmrobles/free-ssl-certificate-for-your-kubernetes-cluster-with-rancher-2cf6559adeba
  5. 这个教程有些问题,但是也有参考价值。https://community.hetzner.com/tutorials/howto-k8s-traefik-certmanager
  6. https://stackoverflow.com/questions/58553510/cant-get-certs-working-with-cert-manager
  7. Service Mesh 的本质、价值和应用探索
  8. Istio, K8S 的微服务支持
  9. 微服务之熔断、降级、限流
  10. 微服务化之服务拆分与服务发现
  11. https://sysadmins.co.za/https-using-letsencrypt-and-traefik-with-k3s/
  12. https://kubernetes.io/docs/concepts/services-networking/ingress/
  13. https://medium.com/flant-com/comparing-ingress-controllers-for-kubernetes-9b397483b46b
  14. https://www.servicemesher.com/blog/kubernetes-ingress-controller-deployment-and-ha/
  15. https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.