Kubernetes in Action 笔记 —— 部署第一个应用

Minikube & kubectl

Minikube 是一个能够在本地环境搭建 Kubernetes 集群的工具,支持 Windows、Linux 和 MacOS 等平台,由 Kubernetes 社区进行维护。
它通常在 Linux 虚拟机中运行 Kubernetes。如果宿主机是基于 Linux 的系统,也可以通过 Docker 实现。
即为了运行 Minikube,需要先安装 Hypervisor 比如 Virtualbox;对于 Linux 系统,也可以直接使用 Docker。

具体的安装配置步骤可以参考官方文档 Getting Started Guide

kubectl 是一个命令行工具,能够向 Kubernetes 集群发送命令并执行,支持的功能包括部署应用、查询和管理资源、查看日志等。
安装步骤可参考官方文档 Install Tools

kubectl

部署应用

通常情况下,部署应用时要准备一个 JSON 或者 YAML 文件,里面包含对该应用的所有组件的描述信息,再把该描述文件应用到 Kubernetes 集群。
从演示的角度来看,也可以通过单行命令的方式部署简单的应用。

创建 deployment

可以使用 kubectl create deployment 命令部署应用。

1
2
$ kubectl create deployment kubia --image=luksa/kubia:1.0
deployment.apps/kubia created

其中 kubia 表示创建的 deployment 对象的名称,luksa/kubia:1.0 指代需要使用的容器镜像。

kubia 对象的存在告诉 Kubernetes luksa/kubia:1.0 容器必须运行在集群中。它定义了一种用户期待的状态,而 Kubernetes 负责确保实际的状态一定会满足该期望。

kubectl get deployment 命令可以列出当前集群中存在的所有 deployment 对象及其状态。

1
2
3
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
kubia 0/1 1 0 5m17s

Pods

容器并不是 Kubernetes 中部署的最小单位。不同于直接部署独立的容器,Kubernetes 实际上会部署一组相互关联的容器,称为 pod
pod 包含一组一个或一个以上关系密切的容器实例,同时运行在同一个工作节点上,并共享特定的 Linux 命名空间。
同一个 pod 中的容器共享相同的网络和 UTS 命名空间,因而共享同样的网络接口、IP 地址、端口空间和主机名等。也可以在描述文件中定义其他需要共享的命名空间。

pods

每个 pod 都有自己的 IP、机器名、进程、网络接口以及其他资源。同一个 pod 中的容器都会将自己看作是 pod 中唯一运行的容器,它们并不能看到其他容器中的进程。

创建 Deployment 对象后就表示已经部署了 pod,Kubernetes 会基于 Deployment 对象创建一个或多个 pod。
可以使用 kubectl get pods 来列出系统中的 pod:

1
2
3
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubia-767f9bc59d-77d2z 1/1 Running 0 41m

如果某些 issue 导致 pod 运行失败,或者单纯想查看更多 pod 相关的信息,可以使用 kubectl describe pod 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
$ kubectl describe pod
Name: kubia-767f9bc59d-77d2z
Namespace: default
Priority: 0
Node: minikube/192.168.49.2
Start Time: Tue, 14 Dec 2021 11:16:58 +0800
Labels: app=kubia
pod-template-hash=767f9bc59d
Annotations: <none>
Status: Running
IP: 172.17.0.3
IPs:
IP: 172.17.0.3
Controlled By: ReplicaSet/kubia-767f9bc59d
Containers:
kubia:
Container ID: docker://e9bd5cf8f2eb15959c08bc8f154742b7194030d8ce0f9e6290cd80fc21b48692
Image: luksa/kubia:1.0
Image ID: docker-pullable://luksa/kubia@sha256:a961dc8f377916936fa963508726d77cf77dcead5c97de7e5361f0875ba3bef7
Port: <none>
Host Port: <none>
State: Running
Started: Tue, 14 Dec 2021 11:26:25 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-n9n9b (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-n9n9b:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 11m default-scheduler Successfully assigned default/kubia-767f9bc59d-77d2z to minikube
Normal Pulling 11m kubelet Pulling image "luksa/kubia:1.0"
Normal Pulled 115s kubelet Successfully pulled image "luksa/kubia:1.0" in 9m24.3380576s
Normal Created 113s kubelet Created container kubia
Normal Started 113s kubelet Started container kubia

输出的最后就包含 pod 创建和启动时触发的一系列事件(Events)。

Pods 的创建流程
  • 运行 kubectl create deployment 命令,向 Kubernetes API Server 发送 HTTP 请求,创建一个新的 Deployment 对象
  • 之后 Kubernetes 创建一个新的 Pod 对象,该 Pod 对象被分配给某个工作节点
  • 工作节点上的 Kubelet agent 得知新的 Pod 对象被创建,且分配给了自己。于是 Kubelet 控制 Docker 拉取特定的镜像并创建、运行容器

Deployment object to a running container

向外部暴露应用

应用已经成功运行了,接下来就是控制它如何被外部访问。每个 pod 都会获得一个专属的 IP 地址,但该地址是只有集群内部可见的。为了使 pod 能够从外部访问,还需要创建一个 Service 对象。

Service 对象有好几种类型,其中一种 LoadBalancer 会生成一个外部的负载均衡器,令服务能够从集群外部访问。
可以使用 kubectl expose 命令创建 Service:

1
2
$ kubectl expose deployment kubia --type=LoadBalancer --port 8080
service/kubia exposed

使用 kubectl get svc 命令查看当前系统中存在的 Service:

1
2
3
4
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d
kubia LoadBalancer 10.106.111.39 <pending> 8080:30077/TCP 2m28s

创建 LoadBalancer 服务时,正常情况下 Kubernetes 会访问云服务提供商,令其创建负载均衡器并获取公共 IP。
Minikube 是本地模拟的集群环境,因而无法完成上述操作。kubia Service 的 EXTERNAL-IP 会一直处于 状态。

在没有获取到外部 IP 的情况下,minikube 可以使用下面的方法获取服务的 url:

1
2
3
4
5
6
7
8
9
$ minikube service kubia --url
🏃 Starting tunnel for service kubia.
|-----------|-------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|-------|-------------|------------------------|
| default | kubia | | http://127.0.0.1:39529 |
|-----------|-------|-------------|------------------------|
http://127.0.0.1:39529
❗ Because you are using a Docker driver on linux, the terminal needs to be open to run it.

打开一个新的命令行窗口,可以成功访问上面的 url:

1
2
$ curl http://127.0.0.1:39529
Hey there, this is kubia-767f9bc59d-77d2z. Your IP is ::ffff:172.17.0.1.

LoadBalancer 的创建流程

Service object to LoadBalancer

横向扩展应用

在容器中部署应用的一个主要好处就是,横向扩展应用变得非常简单和直观。
可以使用下列命令扩展 kubia 应用,令其同时运行 3 个实例副本。

1
2
3
4
5
$ kubectl scale deployment kubia --replicas=3
deployment.apps/kubia scaled
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
kubia 3/3 3 3 3h58m

此时共有 3 个 pod 实例运行:

1
2
3
4
5
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kubia-767f9bc59d-77d2z 1/1 Running 0 3h59m
kubia-767f9bc59d-rvsdq 1/1 Running 0 3m56s
kubia-767f9bc59d-sfn42 1/1 Running 0 3m56s

可以加上 -o wide 选项获取更详细的 pods 信息,比如 IP、运行的节点等:

1
2
3
4
5
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kubia-767f9bc59d-77d2z 1/1 Running 0 4h 172.17.0.3 minikube <none> <none>
kubia-767f9bc59d-rvsdq 1/1 Running 0 5m31s 172.17.0.5 minikube <none> <none>
kubia-767f9bc59d-sfn42 1/1 Running 0 5m31s 172.17.0.4 minikube <none> <none>

再次访问 Service 的 URL,可以看到多次访问返回的信息并不一样,可以证实后台提供服务的 pod 并不是同一个,而是 3 个 pod 轮流接收请求并提供服务:

1
2
3
4
5
6
7
8
$ curl http://127.0.0.1:39529
Hey there, this is kubia-767f9bc59d-sfn42. Your IP is ::ffff:172.17.0.1.
$ curl http://127.0.0.1:39529
Hey there, this is kubia-767f9bc59d-77d2z. Your IP is ::ffff:172.17.0.1.
$ curl http://127.0.0.1:39529
Hey there, this is kubia-767f9bc59d-sfn42. Your IP is ::ffff:172.17.0.1.
$ curl http://127.0.0.1:39529
Hey there, this is kubia-767f9bc59d-rvsdq. Your IP is ::ffff:172.17.0.1.

负载均衡架构示意图:
Load balancing

总结

  • 部署应用可以使用 kubectl create deployment 命令,暴露应用使用 kubectl expose deployment 命令,横向扩展应用使用 kubectl scale deployment 命令。
  • 应用部署的基本单位不是容器而是 pod,一个 pod 可以包含一个或多个相互关联的容器。
  • Deployments、Services、Pods 和 Nodes 都是 Kubernetes 对象/资源。可以使用 kubectl get 命令获取这些对象的列表,或者使用 kubectl describe 命令获取对象的详细信息。
  • Deployment 对象负责部署指定数量的 pods。Services 对象则可以令这些 pods 能够通过一个单一的 IP 地址访问。
  • Service 在集群内部提供负载均衡。如果指定其类型为 LoadBalancer,则 Kubernetes 会请求云服务提供商令应用可以通过公共地址访问。

参考资料

Kubernetes in Action, Second Edition