第一个容器化应用

作为一个应用开发者,你需要了解Kubernetes 里面与开发者关系最密切的几个概念:

首先,是制作容器的镜像;

其次,你需要按照 Kubernetes 项目的规范和要求,将你的镜像组织为它能够“认识”的方式,然后提交上去。

这就是使用 Kubernetes 的必备技能:编写配置文件

在这篇文章中,我们来扮演一个应用开发者的角色,使用 Kubernetes 集群发布第一个容器化应用。

Kubernetes 跟 Docker 等很多项目最大的不同,就在于它不推荐你使用命令行的方式直接运行容器,而是希望你用 YAML 文件的方式,即:把容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用这样一句指令把它运行起来:

$ kubectl create -f 我的配置文件

这么做最直接的好处是,你会有一个文件能记录下 Kubernetes 到底做了什么?

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

一个 Kubernetes 的 API 对象的定义,大多可以分为 MetadataSpec 两个部分。

  • Metadata :存放的是对象的元数据
  • Spec :对象独有的定义,用来描述它所要表达的功能。

如何定义 Pod :

  • spec.replicas:指定副本个数为 2,Deployment 是一个定义多副本Pod的对象,负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。
  • spec.template: 定义 Pod 模版,描述要创建的 Pod 的细节,镜像(spec.containers.image)是 nginx:1.7.9,这个容器监听端口(containerPort)是 80。
  • Metadata : API 对象的“标识”,即元数据,它是从 Kubernetes 里找到这个对象的主要依据。常用的是Labels ,一组 key-value 格式的标签,Deployment 控制器对象通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。

我们可以这样理解两个”一致性”:

  • 容器镜像,保证 应用本身 在开发与部署环境里的一致性
  • YAML 文件,保证了应用的 部署参数 在开发与部署环境中的一致性

Demo

Kubernetes 有一个非常有意思的项目,叫 minikube,也就是启动一个最小的 local 的 Kubernetes 的一个环境。

minikube 我们推荐使用阿里云的版本,它和官方 minikube 的主要区别就是把 minikube 中所需要的 Google 上的依赖换成国内访问比较快的一些镜像,这样就方便了大家的安装工作;

如果大家不是 Mac 系统,其他操作系统请访问这个 链接,查看其它操作系统如何安装 minikube 沙箱环境。

启动:

minikube start

接下来,我们来做三件事情:

首先,查看 minikube 的 status,可以看到 kubelet masterkubectl 都是配置好的。

1
2
3
4
5
6
7
8
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
docker-env: in-use

接下来,我们利用 kubectl 来看一下这个集群中节选的状态,可以看到这个master 的节点已经是 running状态:

1
2
3
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 143d v1.22.1

就以这个为节点,下面我们尝试去看一下现在集群中 Deployment 这个资源:

1
2
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl get deployments
No resources found

创建一个 Deploment 的 YAML 文件 nginx-deployment.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ cat nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

第一步,我们提交一个 nginx 的 Deployment:

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
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Fri, 11 Feb 2022 19:37:32 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=nginx
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.7.9
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-5d59d67564 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 20s deployment-controller Scaled up replica set nginx-deployment-5d59d67564 to 2

可以看到:有一个 nginx-deployment 已经被生成了,它的 replicas 数目也是我们想要的、selector 也是我们想要的、它的 image 的版本也是 1.7.9。

第二步,对这个 Deployment 进行一次版本升级,将 Pod 的版本从 nginx 的镜像由 1.7.9 升级到 1.8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ cat nginx-update-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- containerPort: 80

如下看到:这里面 nginx 的 image 版本号从 1.7.9 升级到 1.8。

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
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl apply -f nginx-update-deployment.yaml
deployment.apps/nginx-deployment configured
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Fri, 11 Feb 2022 19:37:32 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 2
Selector: app=nginx
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.8
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-64c9d67564 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 15m deployment-controller Scaled up replica set nginx-deployment-5d59d67564 to 2
Normal ScalingReplicaSet 21s deployment-controller Scaled up replica set nginx-deployment-64c9d67564 to 1
Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-5d59d67564 to 1
Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-64c9d67564 to 2
Normal ScalingReplicaSet 16s deployment-controller Scaled down replica set nginx-deployment-5d59d67564 to 0
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-64c9d67564-6xpqs 1/1 Running 0 95s
nginx-deployment-64c9d67564-kkc6p 1/1 Running 0 92s

你可以通过 kubectl get 指令,查看两个 Pod 被逐一更新的过程:

1
2
3
4
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-64c9d67564-6xpqs 1/1 Running 0 95s
nginx-deployment-64c9d67564-kkc6p 1/1 Running 0 92s

第三步,尝试对 nginx 进行一次扩容,进行一次水平的伸缩,将 nginx 的 Pod 实例扩展到 4 个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ cat nginx-scale-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 4
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- containerPort: 80

如下看到: nginx 的 Pod 实例有 4 个,同时也可以看到 controller 又做了几次新的操作,这个 scale up 成功了。

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
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl apply -f nginx-scale-deployment.yaml
deployment.apps/nginx-deployment configured
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl describe deployment nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Fri, 11 Feb 2022 19:37:32 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 2
Selector: app=nginx
Replicas: 4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.8
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Progressing True NewReplicaSetAvailable
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-64c9d67564 (4/4 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 21m deployment-controller Scaled up replica set nginx-deployment-5d59d67564 to 2
Normal ScalingReplicaSet 6m50s deployment-controller Scaled up replica set nginx-deployment-64c9d67564 to 1
Normal ScalingReplicaSet 6m48s deployment-controller Scaled down replica set nginx-deployment-5d59d67564 to 1
Normal ScalingReplicaSet 6m48s deployment-controller Scaled up replica set nginx-deployment-64c9d67564 to 2
Normal ScalingReplicaSet 6m45s deployment-controller Scaled down replica set nginx-deployment-5d59d67564 to 0
Normal ScalingReplicaSet 5s deployment-controller Scaled up replica set nginx-deployment-64c9d67564 to 4

第四步,移除 Deployment。我们再去重新 get 这个 Deployment,也会显示这个资源不再存在,这个集群又回到了最开始干净的状态。

1
2
3
4
5
6
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl delete deployment nginx-deployment
deployment.apps "nginx-deployment" deleted
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl describe deployment nginx-deployment
Error from server (NotFound): deployments.apps "nginx-deployment" not found
GZ05032MLdeMacBook-Pro:~/workspace/k8s $ kubectl get deployments
No resources found

在实际使用 Kubernetes 的过程中,相比于编写一个单独的 Pod 的 YAML 文件,官方更推荐你使用一个 replicas=1 的 Deployment。为什么呢?

主要原因是当 Pod 所在的节点出故障的时候,replicas=1 的 Deployment 可以将 Pod 调度到其它健康的节点上,单独的 Pod只能在节点健康的情况下由Kubelet 保证其健康状况。

总结

docker run 这样的命令行操作,向 kubectl apply YAML 文件这样的声明式 API 的转变,是每一个容器技术学习者,必须要跨过的第一道门槛。

如果你想要快速熟悉 Kubernetes,请按照下面的流程进行练习:

  • 首先,在本地通过 Docker 测试代码,制作镜像;

  • 然后,选择合适的 Kubernetes API 对象,编写对应 YAML 文件(比如,Pod,Deployment);

  • 最后,在 Kubernetes 上部署这个 YAML 文件。

更重要的是,在部署到 Kubernetes 之后,接下来的所有操作,要么通过 kubectl 来执行,要么通过修改 YAML 文件来实现,就尽量不要再碰 Docker 的命令行了。

彦祖老师 wechat