接着上篇继续了解 Kubernetes (以下简称 k8s) 的相关内容。

HPA(HorizontalPodAutoscaling)

每天不同时刻应用的资源使用率通常是不同的,例如中午和晚上是访问的高峰,凌晨那几个小时是低峰。怎么提高集群的整体资源利用率,让 Service 中的 Pod 个数自动调整呢?这就依赖了 k8s 提供的 HPA (Horizontal Pod Autoscaler) 资源对象,也就是「使 Pod 水平自动缩放」。

铺垫一下,在上篇文章中已经创建过一个 deployment:

❯ kubectl get deployment k8s-demo-deployment
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
k8s-demo-deployment   10/10   10           10          13h

k8s-demo-deployment里面有 10 个 Pod。怎么实现 Pod 的扩容缩容呢?最简单的方法是用kubectl scale:

# 缩容
❯ kubectl scale --replicas=5 -f deployment.yaml
deployment.apps/k8s-demo-deployment scaled

❯ kubectl get deployment k8s-demo-deployment
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
k8s-demo-deployment   5/5     5            5           13h

# 扩容
❯ kubectl scale --replicas=8 -f deployment.yaml
deployment.apps/k8s-demo-deployment scaled

❯ kubectl get deployment k8s-demo-deployment  # 期望8个Pod,现在只有5个可用
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
k8s-demo-deployment   5/8     8            5           13h
# 稍等几秒,8个都可用了
❯ kubectl get deployment k8s-demo-deployment
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
k8s-demo-deployment   8/8     8            8           13h

kubectl scale是传统运维惯用的手动的调整方法,接着我们看看怎么用 HPA,首先是先部署 metrics-server:

❯ git clone https://github.com/kubernetes-incubator/metrics-server.git
❯ cd metrics-server
❯ kubectl create -f deploy/1.8+/
# 这样建的Metrics Server有证书问题,需要编辑配置KUBE_EDITOR="emacsclient -t"  kubectl edit deploy -n kube-system metrics-server
# 在 spec.template.spec.containers.image 添加如下三行(不包含注释):
# args:
# - --kubelet-insecure-tls
# - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
# 接着可以看到它的状态是Running:
❯ kubectl -n kube-system get pods -l k8s-app=metrics-server
NAME                              READY   STATUS    RESTARTS   AGE
metrics-server-55857cd5dc-k59ln   1/1     Running   0          1m18s
# 可以看一下目前Pod的资源占用排名(下面列了前3)
❯ kubectl top pod |head -4
NAME                                   CPU(cores)   MEMORY(bytes)
k8s-demo                               0m           2Mi
k8s-demo-deployment-6bc5c77c85-5r5dm   0m           2Mi
k8s-demo-deployment-6bc5c77c85-6gbd7   0m           2Mi

接着修改 deployment.yaml,添加资源相关的修改:

...
    spec:
      containers:
      - name: k8s-demo-pod
        ...
        resources:  # resources部分都是新增的
          requests:
            memory: 64Mi
            cpu: 25m
          limits:
            memory: 128Mi
            cpu: 50m

这是因为默认情况下,Pod 运行没有限制 CPU 使用量和内存使用量。这意味着 Pod 能够使用该 Pod 运行节点的所有 CPU 和内存资源。必须要加上述配置限制,才能让下面的 HPA 有效果,应用配置:

❯ kubectl apply -f deployment.yaml --record=true
deployment.apps/k8s-demo-deployment configured

然后写一个 HPA 配置文件 (hpa.yaml):

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: k8s-demo-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: k8s-demo-deployment
  minReplicas: 6
  maxReplicas: 12
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 3
  - type: Resource
    resource:
      name: memory
      targetAverageValue: 300Mi

这里面设置了触发自动缩放的 2 种指标: CPU 阈值 3%,内存 300M,最小的 Pod 副本数为 10,最大为 15。HPA 会根据设定动态的增加或者减少 Pod 数量。

❯ kubectl create -f hpa.yaml
horizontalpodautoscaler.autoscaling/k8s-demo-hpa created
# 稍等几秒就可以看到HPA状态
❯ kubectl get hpa k8s-demo-hpa
NAME           REFERENCE                        TARGETS                    MINPODS   MAXPODS   REPLICAS   AGE
k8s-demo-hpa   Deployment/k8s-demo-deployment   2501017600m/300Mi, 0%/3%   6         12        10         25s

HPA 通过监控分析 RS 或者 Deployment 控制的所有 Pod 的负载变化情况来确定是否需要调整 Pod 的副本数量,上面的命令创建了一个关联资源 k8s-demo-deployment 的 HPA。可以看 TARGETS 下,由于没有流量 CPU 还是用的 0%,低于阈值 3%。

这里说一下,刚才应用 deployment.yaml 后会让副本数变回 10,但是由于 HPA 的自动缩放效果,它觉得目前很闲会缩容到 6 个副本数,稍等一会再看:

❯ kubectl get hpa k8s-demo-hpa
NAME           REFERENCE                        TARGETS                MINPODS   MAXPODS   REPLICAS   AGE
k8s-demo-hpa   Deployment/k8s-demo-deployment   2486272/300Mi, 0%/3%   6         12        6          2m14s

可以看到 REPLICAS 变成了 MINPODS 的数量 6! 接着要增大负载,引起 CPU 占用达到阈值以便提高 Pod,新开 2 个终端分别不断请求页面:

while true; do wget -q -O- http://127.0.0.1:31112/ > /dev/null; done

然后不断的刷新 HPA 状态,我截取了过程中 Pod 数量的变化:

❯ kubectl get hpa k8s-demo-hpa
NAME           REFERENCE                        TARGETS                MINPODS   MAXPODS   REPLICAS   AGE
k8s-demo-hpa   Deployment/k8s-demo-deployment   2510848/300Mi, 0%/3%   6         12        6          12m
...
❯ kubectl get hpa k8s-demo-hpa
NAME           REFERENCE                        TARGETS                     MINPODS   MAXPODS   REPLICAS   AGE
k8s-demo-hpa   Deployment/k8s-demo-deployment   2520405333m/300Mi, 24%/3%   6         12        6          12m
...
❯ kubectl get hpa k8s-demo-hpa
NAME           REFERENCE                        TARGETS                     MINPODS   MAXPODS   REPLICAS   AGE
k8s-demo-hpa   Deployment/k8s-demo-deployment   2520405333m/300Mi, 24%/3%   6         12        12         12m

❯ kubectl get pods |grep k8s-demo-deployment |grep Running | wc -l
12

可以看到由于不断的请求页面,CPU 负载超过设定的 3% 会一直加 Pod,直到达到 maxReplicas 的值。此时如果关掉刚才的 while true 循环进程并等待几分钟,副本数量将变回为 6。

内置的 HPA 操作由 CPU 或内存触发,还可以根据各种外部的和自定义指标来配置 HPA 以扩展 Pod。有需求的可以自行搜索。

当然,HPA 只是 k8s 自动缩放 (Autoscaling) 的方案之一,除此之外还有:

  • VPA (Vertical Pod Autoscaler)。既然有水平缩放,当然也有垂直缩放 (VPA),即根据容器资源使用情况自动设置 Pod 的 CPU、内存的请求值 (配置文件中的 resources.requests 项目),从而允许在节点上进行适当的调度,以便为每个 Pod 提供适当的资源。它既可以缩小过度请求资源的容器,也可以根据其使用情况随时提升资源不足的容量。具体的可以延伸阅读链接 2
  • CA (Cluster Autoscaler)。顾名思义,可以自动扩展和收缩 k8s 集群 Node,具体可以看延伸阅读链接 3

Job/CronJob

在日常开发中经常遇到跑一些临时脚本任务的需求,如果周期性的需要运行这些任务,会由 Crontab 调度任务,这个所谓的任务在 k8s 里面有专门的资源对象:

  • Job。仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束。
  • CronJob。定时任务,在指定的时间周期运行指定的任务。

先看一个 Job 例子 (job.yaml):

apiVersion: batch/v1
kind: Job
metadata:
  name: k8s-demo-job
spec:
  template:
    spec:
      containers:
      - name: python-38
        image: python:3.8-alpine
        command: ["python",  "-c", "import socket; print(socket.gethostbyname(socket.gethostname()))"]
      restartPolicy: Never
  backoffLimit: 4

这次用了一个新的镜像 (python:3.8-alpine), 如其名,容器自带 Python 解释器,command 是本次任务要执行的命令。backoffLimit 指定 job 失败后进行重试的次数,这个要配合restartPolicy: Never(RestartPolicy 是重启策略,Never 表示不重启,另外还支持 OnFailure: 失败才重启)

❯ kubectl create -f job.yaml
job.batch/k8s-demo-job created

❯ kubectl get pods --selector=job-name=k8s-demo-job  # 获得Job的Pod名字, selector是字段选择器
NAME                 READY   STATUS      RESTARTS   AGE
k8s-demo-job-krfnx   0/1     Completed   0          5s  # STATUS是完成的,因为这是一次性的

❯ kubectl logs k8s-demo-job-krfnx  # 查看Pod日志,可以看到代码中print语句输出打印到了日志里面
10.1.0.128

CronJob 的特点是「在给定时间点只运行一次」和「周期性地在给定时间点运行」,看一个例子来体会和 Job 的区别 (cronjob.yaml):

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: k8s-demo-cronjob
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: python-38
            image: python:3.8-alpine
            command: ["python",  "-c", "import socket; print(socket.gethostbyname(socket.gethostname()))"]
          restartPolicy: OnFailure

CronJob 和 Job 主要区别就是:

  • 多了 schedule (Linux crontab 格式一样,上例表示每分钟调用一次)
  • 使用 jobTemplate 表示 Job 里面的 spec.template 部分,相当于多嵌套一层

运行一下:

❯ kubectl create -f cronjob.yaml
cronjob.batch/k8s-demo-cronjob created

# 可以看到CronJob状态:
❯ kubectl get cronjob
NAME               SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
k8s-demo-cronjob   */1 * * * *   False     0        <none>          9s
# 等一会看Job:
❯ kubectl get jobs
NAME                          COMPLETIONS   DURATION   AGE
k8s-demo-cronjob-1571886360   1/1           3s         62s
k8s-demo-cronjob-1571886420   0/1           2s         2s  # 正好捕捉到第二个任务正在运行
k8s-demo-job                  1/1           32s        45m
❯ pod=$(kubectl get pods --selector=job-name=k8s-demo-cronjob-1571886360 --output=jsonpath={.items..metadata.name})
❯ kubectl logs $pod
10.1.0.143
❯ kubectl delete -f cronjob.yaml  # 不删掉会一直定时跑
cronjob.batch "k8s-demo-cronjob" deleted

每分钟都会出现一个k8s-demo-cronjob开头的 Job,这就是 CronJob。

服务发现 (Service Discovery)

由于 k8s 的调度机制,Pod 的 IP 不是固定的 (Pod 一直在经历动态创建和销毁)。如果其它 Pod 需要访问这个 Pod,要怎么知道这个 Pod 的 IP 呢?这就需要通过 Service 通过 VIP (虚拟 IP,ClusterIP) 访问 Pod 提供的服务:

❯ kubectl get svc k8s-demo-svc
NAME           TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-demo-svc   NodePort   10.97.121.169   <none>        80:31112/TCP   17h

我们可以通过命令行获得 VIP,但是在内部,应用 A 怎么知道应用 B 的 VIP?举个非常常见的场景:一个是 API 应用,一个是 DB 应用,两个应用都是通过 Deployment 进行管理的,并且都通过 Service 暴露出了端口提供服务,而 API 应用需要连接到 DB 应用,但在代码层面我们只知道 DB 应用的名称的 DB 对应的 Service 的名称,但是并不知道它的 VIP 地址,怎么办?

通过 k8s 的服务发现机制能容易地获得 Service 对应 IP 和 Port。k8s 支持 2 种基本的服务发现模式:环境变量和 DNS。

环境变量

想一下,k8s-demo-svc的集群 IP 和端口是怎么来的?当容器应用部署到集群时,其服务地址 (即 IP 和端口) 是由集群系统动态分配的。这是基于环境变量分配的。

当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。而 Pod 启动的时候,会通过环境变量设置所有服务相关 IP 和 Port 信息,为了让大家容易理解,基于 svc.yaml 新增 svc2.yaml,只改其中的 metadata.name 为k8s-demo2-svc,然后创建服务:

❯ kubectl create -f svc2.yaml
service/k8s-demo2-svc created

❯ kubectl get svc | grep k8s-demo
k8s-demo-svc    NodePort    10.97.121.169   <none>        80:31112/TCP   1d
k8s-demo2-svc   NodePort    10.104.26.124   <none>        80:32059/TCP   2m5s

现在有了 2 个 Service。大家还记不记得最开始创建的一个 Podk8s-demo标签也是app: k8s? 我们进入重新创建 Pod 就可以找到关于服务的 VIP 的环境变量:

❯ kubectl delete -f pod.yaml && kubectl create -f pod.yaml

❯ kubectl exec -it k8s-demo sh
/ # env |grep SERVICE
KUBERNETES_SERVICE_PORT=443
K8S_DEMO_SVC_SERVICE_HOST=10.97.121.169
K8S_DEMO_SVC_SERVICE_PORT=80
K8S_DEMO2_SVC_SERVICE_HOST=10.104.26.124
K8S_DEMO2_SVC_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_HOST=10.96.0.1

/ # wget -q -O- http://$K8S_DEMO_SVC_SERVICE_HOST:$K8S_DEMO_SVC_SERVICE_PORT
<h1>Hello Kubernetes!</h1>
/ # wget -q -O- http://$K8S_DEMO2_SVC_SERVICE_HOST:$K8S_DEMO2_SVC_SERVICE_PORT
<h1>Hello Kubernetes!</h1>

格式就是{SVCNAME}_SERVICE_HOST{SVCNAME**_SERVICE_PORT的变量,但注意 Service 的名称需大写,横线被转换成下划\ 线。当然进入重新创建 k8s-demo-deployment 的 Pod 也是可以的。理解了吧?服务相关的环境变量被「注入」了 Pod。

这里说一下为什么k8s-demo需要重建,这也是环境变量的最大问题:依赖的服务必须在 Pod 启动之前就存在,不然是不会出现在环境变量中的。之前的 Pod 创建时只有 k8s-demo2-svc 这个服务,所以环境变量是不全的

DNS

另外一种方案是用 DNS (域名解析系统),在 k8s 里面 DNS 是一种 Addon (附加组件,不是 k8s 集群必须安装,但强烈推荐安装), 目前可以选择 kube-dns 或 CoreDNS 为集群提供命名服务。从 v1.13 开始 CoreDNS 成为默认 DNS 服务,CoreDNS 的特点是效率更高,资源占用率更小,所谓本小节只介绍 CoreDNS 的使用。

DNS 服务会根据域名来访问相应的 IP,在 k8s 中就是根据相应的域名去获取相应的 Pod 的 IP,这样 API 应用根据域名就可以访问 DB 应用服务了。

看一下 k8s 系统空间 (kube-system) 下 CoreDNS 的 Pods:

❯ kubectl get pods -n kube-system |grep dns
coredns-584795fc57-2llbd                 1/1     Running   0          2d3h
coredns-584795fc57-586wz                 1/1     Running   0          2d3h

假设在 k8s 集群的命名空间 bar 中,定义了一个 Service foo,同在这个命名空间下的 Pod 可以简单地通过 DNS 查询 foo 来找到该 Service,其他命令空间里的 Pod 需要通过 DNS 查询 foo.bar 找到该 Service。

为了体验 DNS 的效果,先在另外一个命名空间创建个服务,首先是写命名空间配置 (namespace.yaml):

{
  "apiVersion": "v1",
  "kind": "Namespace",
  "metadata": {
    "name": "production",
    "labels": {
      "name": "production"
    }
  }
}

新加的命名空间叫做 production,创建它:

❯ kubectl create -f namespace.yaml
namespace/production created

❯ kubectl create -f svc.yaml -n production
service/k8s-demo-svc created

❯ kubectl create -f pod.yaml -n production
pod/k8s-demo created

❯ kubectl get svc --all-namespaces | grep k8s-demo
default       k8s-demo-svc     NodePort    10.97.121.169   <none>        80:31112/TCP             1d4h
default       k8s-demo2-svc    NodePort    10.104.26.124   <none>        80:32059/TCP             3h57m
production    k8s-demo-svc     NodePort    10.100.20.118   <none>        80:32292/TCP             10s

现在默认的空间下有 2 个服务,在 production 也有个叫做 k8s-demo-svc 的服务。现在进一个和上述服务都无关的新 Pod,通过域名的方式看看怎么访问其他 Service:

❯ kubectl run --rm -it --image=busybox --generator=run-pod/v1 test-pod # 命令式创建Pod,名字test-pod,镜像用的是busybox
# 普通的Service会生成 servicename.namespace.svc.cluster.local 格式的全限定域名(FQDN),会解析到Service对应的ClusterIP上
/ # wget -q -O- http://k8s-demo-svc.default.svc.cluster.local # k8s-demo-svc 是服务名,default 是默认的命令空间,之后的作为域后缀直接使用
<h1>Hello Kubernetes!</h1>
/ # wget -q -O- http://k8s-demo2-svc.default.svc.cluster.local # k8s-demo2-svc 是另外服务名
<h1>Hello Kubernetes!</h1>
/ # wget -q -O- http://k8s-demo-svc.production.svc.cluster.local # 另外命名空间下的服务
<h1>Hello Docker!</h1>  # 注意,pod.yaml用的是镜像是k8s-demo:0.1
/ # wget -q -O- http://k8s-demo-svc.default  # 相同命名空间可以省略域后缀
<h1>Hello Kubernetes!</h1>
/ # wget -q -O- http://k8s-demo-svc  # 相同命名空间下空间名字都可以省
<h1>Hello Kubernetes!</h1>
/ # wget -q -O- http://k8s-demo-svc.production
<h1>Hello Docker!</h1>

用 DNS 的方式就可以访问 k8s 集群里面任何你想要访问的 Service 资源了。

外部路由

一共有 4 种方案让服务可以被外部用户访问 (更具体的内容可以看延伸阅读链接 4)

ClusterIP

ClusterIP 是默认的 Service 类型,当 Service 创建时,会被分配一个唯一的 IP 地址(也称为 clusterIP),内部可以直接访问,外部用户可以用 port-forward 访问:

❯ kubectl expose deployment/k8s-demo-deployment --name=my-service  # expose可以在命令行创建服务
❯ kubectl get svc my-service
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
my-service   ClusterIP   10.104.250.157   <none>        80/TCP    8s
❯ kubectl run --rm -it --image=busybox --generator=run-pod/v1 test-pod
If you don't see a command prompt, try pressing enter.
/ # wget -q -O- http://10.104.250.157   # 内部访问
<h1>Hello Kubernetes!</h1>
❯ kubectl port-forward svc/my-service 7000:80
Forwarding from 127.0.0.1:7000 -> 80
Forwarding from [::1]:7000 -> 80

现在通过http://127.0.0.1:7000/就可以访问了,当然这种方案只适合在开发中的,因为维护线上转发关系非常麻烦。

NodePort

前面我们已经体验使用 NodePort 类型的 Service 把应用暴露给外部用户访问:

❯ kubectl get svc k8s-demo-svc
NAME           TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-demo-svc   NodePort   10.97.121.169   <none>        80:31112/TCP   1d5h

这样就可以通过http://127.0.0.1:31112/访问 k8s-demo-svc 服务了

LoadBalancer

LoadBalancer 类型的 Service 是在 NodePort 的基础上,借助云服务商 (如 AWS、Azure、DigitalOcean、阿里云等) 创建一个外部的负载均衡器,并将请求转发到节点的 NodePort 上:

这种方式首先要求服务器有公网 IP,我的云服务器也都在容器中,没有直接包含公网 IP,所以不能演示,下面只是本地体验一下:

❯ kubectl expose deployment/k8s-demo-deployment --name=lb-service --port=8000 --target-port=80 --type=LoadBalancer
service/lb-service exposed

❯ kubectl get svc lb-service
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
lb-service   LoadBalancer   10.104.247.244   localhost     8000:32097/TCP   3s

注意 EXTERNAL-IP 一栏是 localhost,和 NodePort 类型一样,现在打开http://127.0.0.1:8000/就可以访问了

Ingress

对于小规模的应用我们使用 NodePort 可以满足基本需求,但是当你的应用越来越多的时候,你就会发现维护大量的 NodePort 非常麻烦,这就引出了 Ingress。Ingress 不是一种 Service 类型,而是独立的资源对象,它包含路由到 Service 的规则集合,这样会将外部的请求转发到集群内不同的 Service 上,其实就相当于 Nginx (upstream)、Haproxy 等负载均衡代理服务器的作用,如图所示:

为了使 Ingress 正常工作,集群中必须运行 Ingress Controller: 可以理解是一个监听器,通过不断地与 k8s API 交互,实时的感知后端 Service、Pod 的变化,当得到这些变化信息后,Ingress Controller 结合 Ingress 的配置,更新反向代理负载均衡器,达到服务发现的作用。其实这点和服务发现工具 Consul consul-template 非常类似。Ingress Controller 可选择的非常多,具体列表的可以看延伸阅读链接 6 的官网地址,我选的是 Traefik , Traefik 是一款开源的 HTTP 反向代理与负载均衡工具,架构图如下:

我们安装它。这次搞起来非常的麻烦了,容我细细道来。我在 macOS 上安装 Kubernetes 是 上篇文章提到的方法一: Docker Desktop for Mac + Enable Kubernetes 方案,但是这样拿不到虚拟机的 IP (如果有同学知道怎么找请赐教~),后面就没法体验了,好消息是 Docker Desktop 支持切换 Context,所以可以用我上文提到的「命令行安装」方案安装和启动 Minikube,安装后自动设置 Context 为 minikube:

❯ kubectl config current-context
minikube
# 如果输出的是docker-desktop,可以执行`kubectl config set current-context minikube`

切换上下文后,原来启动的 Deployment、Service 都没有了,需要重新创建。要注意,k8s-demo 镜像也需要重新 build,需要这样:

eval $(minikube docker-env)
❯ docker build -t k8s-demo:0.2 .
❯ kubectl create -f deployment.yaml
error: unable to recognize "deployment.yaml": no matches for kind "Deployment" in version "apps/v1beta1"

我没有搜到使用 minikube 在 API 环境上和 docker-desktop 有什么区别,但需要修改 (deployment.yaml),看 diff 效果 (- 表示修改前,+ 表示修改后):

--- a/deployment.yaml
+++ b/deployment.yaml
@@ -1,8 +1,11 @@
-apiVersion: apps/v1beta1 # for versions before 1.6.0 use extensions/v1beta1
+apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: k8s-demo-deployment
 spec:
+  selector:
+    matchLabels:
+      app: k8s
   replicas: 10
   minReadySeconds: 3
   strategy:

这样就可以创建 Deployment 和 Service:

❯ kubectl create -f deployment.yaml
❯ kubectl create -f svc.yaml

接着安装 Traefik:

❯ git clone https://github.com/containous/traefik
❯ cd traefik
❯ gco -b v1.7 origin/v1.7  # 切到v1.7分支
❯ kubectl apply -f examples/k8s/traefik-rbac.yaml  
❯ kubectl apply -f examples/k8s/traefik-ds.yaml  # 使用Daemon Set版本
❯ curl $(minikube ip)
404 page not found

现在还没有配置路有规则,所以访问请求时 404,接着安装 Web UI,它就是一个 ingress:

❯ kubectl apply -f examples/k8s/ui.yaml  # Traefik的Web UI
❯ kubectl get ing -n kube-system
NAME             HOSTS                 ADDRESS   PORTS   AGE
traefik-web-ui   traefik-ui.minikube             80      49m
❯ echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts  # 写本地hosts,这样就可以通过域名的方式访问了
192.168.64.6 traefik-ui.minikube  # 192.168.64.6就是minikube虚拟机的IP

Minikube 和 Docker-desktop 的最大区别就是它有虚拟机 IP,运行的 Traefik 服务端口就绑定在这个 IP 上。接着访问http://traefik-ui.minikube就可以看到 Traefik 的 Web 页面:

ui.yaml就是一个基于虚拟机主机方式的 Ingress,看一下这部分的配置:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: traefik-web-ui
  namespace: kube-system
spec:
  rules:
  - host: traefik-ui.minikube
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-web-ui
          servicePort: web

所以当请求traefik-ui.minikube这个域名,就会基于这个配置去找traefik-web-ui服务 (也在 ui.yaml 配置的)。接着把我们的 k8s-demo-svc 服务也暴露出来 (ingress.yaml):

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: k8s-demo-ingress
spec:
  backend:
    serviceName: k8s-demo-svc
    servicePort: 80

创建并加 hosts 文件:

❯ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/k8s-demo-ingress created

❯ echo "$(minikube ip) k8s-demo.dongwm.com" | sudo tee -a /etc/hosts
192.168.64.6 k8s-demo.dongwm.com

现在访问http://k8s-demo.dongwm.com/就可以到达 k8s-demo-svc 服务了。访问 Traefik 的面板也可以看到新增的配置了:

项目源码

本文提到的全部源码可以在 mp 找到。

延伸阅读

  1. https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
  2. https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler
  3. https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler
  4. https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0
  5. https://kubernetes.io/docs/concepts/services-networking/ingress/
  6. https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers