介绍

这篇文章将学习基于 Kubernetes 的应用管理,包括 kubectl, pod, deployment, service, label 等等。针对每个内容都会给出 Reading List。注意,本节所有的操作都基于之前文章中创建的 Minikube 集群。

Kubectl CLI

看一下开始使用kubectl和kubernetes集群交互

kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.2", GitCommit:"092fbfbf53427de67cac1e9fa54aaa09a28371d7", GitTreeState:"clean", BuildDate:"2021-06-16T12:59:11Z", GoVersion:"go1.16.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.2", GitCommit:"52c56ce7a8272c798dbc29846288d7cd9fbae032", GitTreeState:"clean", BuildDate:"2020-04-30T20:19:45Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
WARNING: version difference between client (1.21) and server (1.18) exceeds the supported minor version skew of +/-1

kubectl 客户端的版本是 v1.21.2,kubernetes 集群的版本是 v1.18.0。同时,该命令还会输出 kubernetes 编译信息。


另外,可以看集群信息:

kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:45097
KubeDNS is running at https://127.0.0.1:45097/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

也可以使用kubectl cluster-info dump来查看更详细的信息。


kubectl 命令的基本格式是 kubectl <action> <resource>,其中 action 可以是 create, delete, get 等等,resource 你可以使用 kubectl api-resources 获得完整列表。

例如,可以通过 kubectl get nodes 获取节点信息,可以通过 kubectl describe nodes ${NODENAME} 获取节点详细信息。


kubectl通过读取配置文件信息与kubernetes交互,默认配置文件信息在~/.kube/config

cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /home/ec2-user/.minikube/ca.crt
    extensions:
    - extension:
        last-update: Sun, 20 Jun 2021 17:00:21 UTC
        provider: minikube.sigs.k8s.io
        version: v1.21.0
      name: cluster_info
    server: https://192.168.49.2:8443
  name: minikube
- cluster:
    certificate-authority-data: xxx
    server: https://127.0.0.1:45097
  name: kind-kind
contexts:
- context:
    cluster: minikube
    extensions:
    - extension:
        last-update: Sun, 20 Jun 2021 17:00:21 UTC
        provider: minikube.sigs.k8s.io
        version: v1.21.0
      name: context_info
    namespace: default
    user: minikube
  name: minikube
- context:
    cluster: kind-kind
    user: kind-kind
  name: kind-kind
current-context: kind-kind
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /home/ec2-user/.minikube/profiles/minikube/client.crt
    client-key: /home/ec2-user/.minikube/profiles/minikube/client.key
- name: kind-kind
  user:
    client-certificate-data: ...
    client-key-data: ...

这里有三个重要的顶级概念: clusters, userscontexts

我们使用的集群名为 minikube,其服务器地址为 https://127.0.0.1:45097,其认证证书位于 ${HOME}/.minikube/ca.crt

当我们使用该 kubeconfig 发送请求时,我们充当用户 minikube (确切地说,真正的用户名来自证书通用名,但是我们暂时跳过它)。

最后,context 是各种配置的组合,例如,存在两个 context,一个用于集群 minikube 以及用户 minikube,另一个用于集群 example 和用户 minikube,即这意味着用户 minikube 可以同时访问 minikubeexample 集群。

Node

节点(Node)是物理机或虚拟机,他们组成了 kubernetes 的资源池。Master 节点负责资源调度、集群状态控制等, Node 节点负责运行用户应用,承接负载。后续的文章会介绍具体的架构,现在只关心如何通过命令行根据 kubectl 与 kubernetes 接口交互管理节点。

首先启动minikube

minikube start

Node information

在 kubernetes 中,我们可以通过 kubectl get nodes 命令来获取所有节点信息:

kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
minikube   Ready    control-plane,master   20m   v1.20.7

以上输出表明当前集群有 1 个节点。注意,我们无法完全从命令行区分 master 节点和 node 节点。这里 minikube 节点即是 master 也是 node,也就是说,minikube 主机即负责调度和管理,也负责用户容器的运行时。一般在生产环境中,我们不会在 master 上运行用户容器。但是如果主机数量非常少的情况下可以考虑,前提是预留足够的资源给 master。

不承载用户容器的主机需要加上 SchedulingDisabled 的状态(通过 kubectl cordon <node name> 实现)

Node details

我们可以通过 kubectl describe nodes 来了解节点的详情。下面显示了 minikube 节点的详情,我们可以暂时不关心输出内容的细节。

kubectl describe nodes
Name:               minikube
Roles:              control-plane,master
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=minikube
                    kubernetes.io/os=linux
                    minikube.k8s.io/commit=76d74191d82c47883dc7e1319ef7cebd3e00ee11
                    minikube.k8s.io/name=minikube
                    minikube.k8s.io/updated_at=2021_06_20T17_00_21_0700
                    minikube.k8s.io/version=v1.21.0
                    node-role.kubernetes.io/control-plane=
                    node-role.kubernetes.io/master=
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Sun, 20 Jun 2021 17:00:18 +0000
Taints:             node.kubernetes.io/unschedulable:NoSchedule
Unschedulable:      true
Lease:
  HolderIdentity:  minikube
  AcquireTime:     <unset>
  RenewTime:       Sun, 20 Jun 2021 17:22:58 +0000
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Sun, 20 Jun 2021 17:20:47 +0000   Sun, 20 Jun 2021 17:00:13 +0000   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Sun, 20 Jun 2021 17:20:47 +0000   Sun, 20 Jun 2021 17:00:13 +0000   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Sun, 20 Jun 2021 17:20:47 +0000   Sun, 20 Jun 2021 17:00:13 +0000   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            True    Sun, 20 Jun 2021 17:20:47 +0000   Sun, 20 Jun 2021 17:00:36 +0000   KubeletReady                 kubelet is posting ready status
Addresses:
  InternalIP:  192.168.49.2
  Hostname:    minikube
Capacity:
  cpu:                8
  ephemeral-storage:  536858604Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             32938908Ki
  pods:               110
Allocatable:
  cpu:                8
  ephemeral-storage:  536858604Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             32938908Ki
  pods:               110
System Info:
  Machine ID:                 b77ec962e3734760b1e756ffc5e83152
  System UUID:                9332b662-9a17-4073-a4b6-74e52720f181
  Boot ID:                    136986f2-e8e7-4752-ad70-2483ef759155
  Kernel Version:             4.14.232-176.381.amzn2.x86_64
  OS Image:                   Ubuntu 20.04.2 LTS
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  docker://20.10.7
  Kubelet Version:            v1.20.7
  Kube-Proxy Version:         v1.20.7
PodCIDR:                      10.244.0.0/24
PodCIDRs:                     10.244.0.0/24
Non-terminated Pods:          (7 in total)
  Namespace                   Name                                CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                ------------  ----------  ---------------  -------------  ---
  kube-system                 coredns-74ff55c5b-2jcm5             100m (1%)     0 (0%)      70Mi (0%)        170Mi (0%)     22m
  kube-system                 etcd-minikube                       100m (1%)     0 (0%)      100Mi (0%)       0 (0%)         22m
  kube-system                 kube-apiserver-minikube             250m (3%)     0 (0%)      0 (0%)           0 (0%)         22m
  kube-system                 kube-controller-manager-minikube    200m (2%)     0 (0%)      0 (0%)           0 (0%)         22m
  kube-system                 kube-proxy-hn8kq                    0 (0%)        0 (0%)      0 (0%)           0 (0%)         22m
  kube-system                 kube-scheduler-minikube             100m (1%)     0 (0%)      0 (0%)           0 (0%)         22m
  kube-system                 storage-provisioner                 0 (0%)        0 (0%)      0 (0%)           0 (0%)         22m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                750m (9%)   0 (0%)
  memory             170Mi (0%)  170Mi (0%)
  ephemeral-storage  100Mi (0%)  0 (0%)
  hugepages-1Gi      0 (0%)      0 (0%)
  hugepages-2Mi      0 (0%)      0 (0%)
Events:
  Type    Reason                   Age                From        Message
  ----    ------                   ----               ----        -------
  Normal  NodeHasSufficientMemory  22m (x5 over 22m)  kubelet     Node minikube status is now: NodeHasSufficientMemory
  Normal  NodeHasNoDiskPressure    22m (x4 over 22m)  kubelet     Node minikube status is now: NodeHasNoDiskPressure
  Normal  NodeHasSufficientPID     22m (x4 over 22m)  kubelet     Node minikube status is now: NodeHasSufficientPID
  Normal  Starting                 22m                kubelet     Starting kubelet.
  Normal  NodeHasSufficientMemory  22m                kubelet     Node minikube status is now: NodeHasSufficientMemory
  Normal  NodeHasNoDiskPressure    22m                kubelet     Node minikube status is now: NodeHasNoDiskPressure
  Normal  NodeHasSufficientPID     22m                kubelet     Node minikube status is now: NodeHasSufficientPID
  Normal  NodeNotReady             22m                kubelet     Node minikube status is now: NodeNotReady
  Normal  NodeAllocatableEnforced  22m                kubelet     Updated Node Allocatable limit across pods
  Normal  NodeReady                22m                kubelet     Node minikube status is now: NodeReady
  Normal  Starting                 22m                kube-proxy  Starting kube-proxy.
  Normal  NodeNotSchedulable       33s                kubelet     Node minikube status is now: NodeNotSchedulable

Namespace

Kubernetes namespace 是用来构建虚拟的资源池;使用 kubernetes namespace,管理员可以将 kubernetes 划分成多个虚拟的区域,不同的项目或者团队可以使用不同的 namespace,达到了共享 kubernetes 集群资源的目的。此外, namespace 也被用来划分命名空间,即不同 namespace 里的资源可以取相同的名字,相同 namespace 内的资源不能重名。

假设resources文件夹下有一个ns.yaml文件,内容如下:

apiVersion: v1
kind: Namespace
metadata:
  name: test-lm

那么执行下面命令创建一个namespace

kubectl create -f resources/ns.yaml
namespace/test-lm created

通过kubectl get ns就可以看之前创建的namespace了

kubectl get ns
NAME              STATUS   AGE
default           Active   27m
kube-node-lease   Active   27m
kube-public       Active   27m
kube-system       Active   27m

这里 nsnamespace 的缩写。输出内容中的 default, kube-node-lease, kube-publickube-system,都是 kubernetes 默认创建的 namespace,用来放置系统相关的资源。

kubectl describe ns test-lm
Name:         test-lm
Labels:       <none>
Annotations:  <none>
Status:       Active

No resource quota.

No LimitRange resource.

Kubernetes Pod & Deployment

当我们有一个 kubernetes 集群之后,可以开始部署应用了。在 kubernetes 的世界里,Pod 是运行应用的载体。 Pod 是由多个容器组成、是 kubernetes 的最小调度单元、Pod 共享底层资源、由 kubernetes 来管理生命周期。然而,一般情况下,我们并不直接创建 Pod,而是通过 Deployment 来创建 Pod,由 Deployment 来负责创建、更新、维护其所管理的所有 Pods。

一旦我们通过 Deployment 创建 Pod,会有一个 Deployment 控制器不断监控所有 Pod 的状态。例如,如果 Pod 运行的机器宕机了,那么 Deployment 控制器会在另一台机器上重新启动一个 Pod。接下来,我们将部署一个应用,部署之后,集群将会达到下图所示的状态。

kubernetes_cluster

这里六边形方框代表一个 kubernetes 节点,正方体代表一个 Pod。后面,我们将通过 kubectl 来管理应用。

Create Deployment

我们可以通过 yaml 文件创建 Deployment,从而创建应用。

resources/deployment_ef.yaml文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: easy-flask
  labels:
    app: easy-flask
spec:
  replicas: 1
  selector:
    matchLabels:
      app: easy-flask
  template:
    metadata:
      labels:
        app: easy-flask
    spec:
      containers:
      - name: easy-flask
        image: qqliuming1995/easy-flask
        ports:
        - containerPort: 5000
tolerations:
  - key: "node-role.kubernetes.io/master"
    operator: "Equal"
    value: ""
    effect: "NoSchedule"

执行下列代码来部署deployment

kubectl taint node minikube node.kubernetes.io/unschedulable:NoSchedule-
kubectl apply -f resources/deployment_ef.yaml -n test-lm
deployment.apps/easy-flask created

由于minikube的master node可能不能部署pod,所以要先用uncordon来让master节点也可以部署pod。

kubectl get nodes
NAME       STATUS                     ROLES                  AGE   VERSION
minikube   Ready,SchedulingDisabled   control-plane,master   10h   v1.20.7

解锁部署限制

kubectl uncordon minikube

看一下部署结果

kubectl get pods -n test-lm
NAME                          READY   STATUS    RESTARTS   AGE
easy-flask-6495c89f66-td9cz   1/1     Running   0          9h
kubectl get deployment -n test-lm
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
easy-flask   1/1     1            1           9h

Logs

kubectl logs ef-7f998787bb-mpjdc -n test-lm
 * Serving Flask app 'run' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on all addresses.
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://172.17.0.5:5000/ (Press CTRL+C to quit)

连接 Pod

首先看一下pod的ip

kubectl get pods -n test-lm -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
easy-flask-6495c89f66-td9cz   1/1     Running   0          9h    172.17.0.3   minikube   <none>           <none>

由于 minikube 本身是运行在虚拟机中,因此我们需要登录虚拟机访问 easy-flask pod

minikube ssh
Last login: Sun Jun 20 17:52:58 2021 from 192.168.49.1

接下来连接一下pod,IP在之前kubectl get pods -n test-lm -o wide

curl 172.17.0.3:5000
Hello, World!

再查看一下日志

logs easy-flask-6495c89f66-td9cz -n test-lm
 * Serving Flask app 'run' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on all addresses.
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://172.17.0.3:5000/ (Press CTRL+C to quit)
172.17.0.1 - - [21/Jun/2021 03:20:02] "GET / HTTP/1.1" 200 -

在pod里执行代码

有时候,我们需要在 Pod 中执行命令,可以通过 kubectl exec

kubectl exec easy-flask-6495c89f66-td9cz -n test-lm -- ls -la
total 8
drwxr-xr-x    1 root     root            20 Jun 20 17:46 .
drwxr-xr-x    1 root     root            28 Jun 21 03:04 ..
-rw-r--r--    1 root     root           145 Jun 20 17:46 run.py
-rwxrwxr-x    1 root     root            42 Jun 20 17:39 run.sh

Kubernetes Service

接下来我们通过一连串的命令学习 kubernetes service。kubernetes service 有以下几个作用:

  • 提供固定的 IP。由于 Pod 可以随时启停,Pod IP 可能随时都会变化,例如上面 easy-flask pod 重启之后 IP 可能会变化。Service 为 Pods 提供的固定 IP,其他服务可以通过 Service IP 找到提供服务的 Pods。
  • 提供负载均衡。Service 由多个 Pods 组成,kubernetes 对组成 Service 的 Pods 提供的负载均衡方案,例如随机访问、基于 Client IP 的 session affinity。
  • 服务发现。集群中其他服务可以通过 Service 名字访问后端服务(DNS),也可以通过环境变量访问。

下图是 kubernetes Pods, Service 的典型关系。下图有两个 Deployment: A 和 B。其中 Deployment A 创建了一个 Pods(黄色),Deployment B 创建了三个 Pod(绿色)。我们可以创建两个 Service: A 和 B。 Service A 管理由 Deployment A 创建的 Pods,Service B 管理 Deployment B 创建的 Pods。可以看到, Service A 和 Service B 都有自己独立的 IP。无论他们所管理的容器如何变化, Service 的 IP 都不会变化。

创建service

与其他资源相同,可以通过 kubectl create -f 加文件名创建 Service。但类似 Deployment,kubernetes 提供了快捷命令让我们能快速创建 Service。

kubectl expose deployment easy-flask --port 5000 -n test-lm
service/easy-flask exposed

get service

通过 kubectl get service 命令可以查看 service 的详细信息:

kubectl get svc easy-flask -n test-lm
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
easy-flask   ClusterIP   10.98.62.84   <none>        5000/TCP   8s

可以看到,Service 具有一个固定的 IP 10.98.62.84。同样,通过 describe 可以看到更多详细的信息:

kubectl describe svc easy-flask -n test-lm
Name:              easy-flask
Namespace:         test-lm
Labels:            app=easy-flask
Annotations:       <none>
Selector:          app=easy-flask
Type:              ClusterIP
IP Families:       <none>
IP:                10.98.62.84
IPs:               10.98.62.84
Port:              <unset>  5000/TCP
TargetPort:        5000/TCP
Endpoints:         172.17.0.3:5000
Session Affinity:  None
Events:            <none>

其中,Endpoint 表明 Service 所选中的 Pod_IP:Pod_Port。我们可以查看 Pod 信息来验证:

kubectl get pods -o wide -n test-lm
NAME                          READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
easy-flask-6495c89f66-td9cz   1/1     Running   0          11h   172.17.0.3   minikube   <none>           <none>

Query service

创建 Service 后,我们可以在主机上直接访问该 Service。下面两条命令实际上访问的都是同一个后端。第一个命令通过 Service IP 访问,第二个命令通过 Pod IP 访问。

通过 Service IP 访问:

minikube ssh
curl 10.98.62.84:5000
Hello, World!

通过 Pod IP 访问:

minikube ssh
curl 172.17.0.3:5000
Hello, World!

上面的命令创建了一个名为 easy-flask 的 Service,并使用 5000 作为服务端口。这里,我们的 easy-flask容器监听的是容器的 5000 端口,该端口是 Pod IP 所监听的端口;我们可以在 Service 上使用不同的端口。例如,若我们想暴露的服务端口是 8080 端口,需要使用 port 和 targetPort 选项。

首先,删除已经创建的 Service:

kubectl delete svc easy-flask -n test-lm
service "easy-flask" deleted

之后,创建 Service:

kubectl expose deployment easy-flask --port 8080 --target-port 5000 -n test-lm
service/easy-flask exposed

尝试用 8080 端口访问服务

kubectl get svc easy-flask -n test-lm
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
easy-flask   ClusterIP   10.98.150.32   <none>        8080/TCP   39s
minikube ssh
curl 10.98.150.32:8080
Hello, World!

NodePort service

上述创建的 Service 只能被集群内部的节点和 Pod 访问,并不能被外部访问。我们可以通过两种方式暴露服务:NodePortLoadBalancerNodePort 通过在每个节点打开一个端口对外提供服务,LoadBalancer 通过创建一个外部负载均衡器(例如公有云负载均衡器)来对外提供服务。这里我们尝试使用 NodePort

首先,删除已有的 Service:

kubectl delete svc easy-flask -n test-lm
service "easy-flask" deleted
kubectl expose deployment easy-flask --port 5000 --type NodePort -n test-lm
service/easy-flask exposed

查看 Service 的细节:

kubectl get svc easy-flask -n test-lm
NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
easy-flask   NodePort   10.99.243.69   <none>        5000:30653/TCP   18s
kubectl describe svc easy-flask -n test-lm
Name:                     easy-flask
Namespace:                test-lm
Labels:                   app=easy-flask
Annotations:              <none>
Selector:                 app=easy-flask
Type:                     NodePort
IP Families:              <none>
IP:                       10.99.243.69
IPs:                      10.99.243.69
Port:                     <unset>  5000/TCP
TargetPort:               5000/TCP
NodePort:                 <unset>  30653/TCP
Endpoints:                172.17.0.3:5000
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

从以上输出可以看到,easy-flask 服务打开了节点的 30653 端口(每个节点),我们可以通过 NodeIP:NodePort 访问服务。

# 这次不需要进入minikube
curl $(minikube ip):30653
Hello, World!

Kubernetes Label

Service 通过 selectorlabel 来选取它所管理的 Pod,同样 Deployment 也是通过 selector 和 label 选取它所管理的 Pod。因为我们是通过 Deployment 创建的 Pod,因此 Deployment 的 selector 一定是匹配 Pod 的 label。如果我们想让 Service 选择与 Deployment 相同的 Pods,我们需要将 Service 的 selector 设为与 Deployment 相同。在上面的实验中,我们使用 kubectl expose deployment easy-flask 的时候,kubernetes 默认将 Service 的 selector 设置成与 Deployment 相同的 selector。下图对 label 做了详细的标注。

img

由上图可以看出:

Resourceselector
Deployment A, Service A, Pod Aapp=A
Deployment B, Service B, Pod Bapp=B

Label 可以在创建时添加,也可以在运行时添加或修改。在运行时修改会影响集群的状态,因为现有的 selector & label 结构会被改变。

View selector & label

从下面的输出可以看到,上述创建的 Deployment 和 Service 的 Selector 都是 app=easy-flask。Deployment和Pod 具有 Label app=easy-flask,因此他们都选中了 app=easy-flask 这个 Pod (只要 Pod label 的子集满足即可;这里的 pod-template-hash=6495c89f66 Label 是 kubernetes 自动创建)。

kubectl describe deployment easy-flask -n test-lm
Name:                   easy-flask
Namespace:              test-lm
CreationTimestamp:      Sun, 20 Jun 2021 17:49:57 +0000
Labels:                 app=easy-flask
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=easy-flask
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=easy-flask
  Containers:
   easy-flask:
    Image:        qqliuming1995/easy-flask
    Port:         5000/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   easy-flask-6495c89f66 (1/1 replicas created)
Events:          <none>
kubectl describe svc easy-flask -n test-lm
Name:                     easy-flask
Namespace:                test-lm
Labels:                   app=easy-flask
Annotations:              <none>
Selector:                 app=easy-flask
Type:                     NodePort
IP Families:              <none>
IP:                       10.99.243.69
IPs:                      10.99.243.69
Port:                     <unset>  5000/TCP
TargetPort:               5000/TCP
NodePort:                 <unset>  30653/TCP
Endpoints:                172.17.0.3:5000
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
kubectl describe pods easy-flask -n test-lm
Name:         easy-flask-6495c89f66-td9cz
Namespace:    test-lm
Priority:     0
Node:         minikube/192.168.49.2
Start Time:   Mon, 21 Jun 2021 03:03:53 +0000
Labels:       app=easy-flask
              pod-template-hash=6495c89f66
Annotations:  <none>
Status:       Running
IP:           172.17.0.3
IPs:
  IP:           172.17.0.3
Controlled By:  ReplicaSet/easy-flask-6495c89f66
Containers:
  easy-flask:
    Container ID:   docker://dc29608f015fd4eee74b1875de57f59171591b26f498fcf63ec72673618acea0
    Image:          qqliuming1995/easy-flask
    Image ID:       docker-pullable://qqliuming1995/easy-flask@sha256:6c3e5457a103147d540d46a020b40a5e454acae650dff724fbe7d46c78a61f4c
    Port:           5000/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 21 Jun 2021 03:04:00 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kms7g (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  default-token-kms7g:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-kms7g
    Optional:    false
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:          <none>

Label operations

kubectl 支持对资源的 label 进行管理,比如我们可以通过 -l 选项查看仅具有某个 label 的资源。

kubectl get pods -l app=easy-flask -n test-lm
NAME                          READY   STATUS    RESTARTS   AGE
easy-flask-6495c89f66-td9cz   1/1     Running   0          12h
kubectl get svc -l app=easy-flask -n test-lm
NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
easy-flask   NodePort   10.99.243.69   <none>        5000:30653/TCP   12m

当没有任何资源满足 label 时,输出为空:

kubectl get svc -l app=666 -n test-lm
No resources found in test-lm namespace.
kubectl label pod easy-flask-6495c89f66-td9cz test=v1 -n test-lm
pod/easy-flask-6495c89f66-td9cz labeled
kubectl describe pods easy-flask-6495c89f66-td9cz -n test-lm
Name:         easy-flask-6495c89f66-td9cz
Namespace:    test-lm
Priority:     0
Node:         minikube/192.168.49.2
Start Time:   Mon, 21 Jun 2021 03:03:53 +0000
Labels:       app=easy-flask
              pod-template-hash=6495c89f66
              test=v1
Annotations:  <none>
Status:       Running
IP:           172.17.0.3
IPs:
  IP:           172.17.0.3
Controlled By:  ReplicaSet/easy-flask-6495c89f66
Containers:
  easy-flask:
    Container ID:   docker://dc29608f015fd4eee74b1875de57f59171591b26f498fcf63ec72673618acea0
    Image:          qqliuming1995/easy-flask
    Image ID:       docker-pullable://qqliuming1995/easy-flask@sha256:6c3e5457a103147d540d46a020b40a5e454acae650dff724fbe7d46c78a61f4c
    Port:           5000/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 21 Jun 2021 03:04:00 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kms7g (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  default-token-kms7g:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-kms7g
    Optional:    false
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:          <none>

Kubernetes Deployment 操作

扩张

接下来学习 kubernetes Deployment 的另外两个操作:水平扩展应用和更新应用。下图中,Deployment A 有一个 Pod 在运行,Service A 管理该 Pod。

img

通过调整 Deployment 的副本数量,我们可以将 Pod 的数量调整到 4 个。与此同时,Service 会感知到同样 label 的 Pod 被扩容到了 4 个,会将流量导到所有 Pod(而不是只有最开始的 Pod)。

img

我们可以通过kubectl scale子命令将 Pod 数量扩容到四个:

kubectl scale deployments easy-flask --replicas=4 -n test-lm
deployment.apps/easy-flask scaled

查看一下deployment和pod

kubectl get deployments -n test-lm
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
easy-flask   4/4     4            4           13h
kubectl get pods -n test-lm
NAME                          READY   STATUS    RESTARTS   AGE
easy-flask-6495c89f66-6hzsn   1/1     Running   0          46s
easy-flask-6495c89f66-kb587   1/1     Running   0          46s
easy-flask-6495c89f66-lg9v9   1/1     Running   0          46s
easy-flask-6495c89f66-td9cz   1/1     Running   0          13h

查看 service

之前提到,Service 会感知到 Pods 的变化,在所有的 Pods 中负载均衡,我们可以通过 kubectl 查看。

kubectl describe service easy-flask -n test-lm
Name:                     easy-flask
Namespace:                test-lm
Labels:                   app=easy-flask
Annotations:              <none>
Selector:                 app=easy-flask
Type:                     NodePort
IP Families:              <none>
IP:                       10.99.243.69
IPs:                      10.99.243.69
Port:                     <unset>  5000/TCP
TargetPort:               5000/TCP
NodePort:                 <unset>  30653/TCP
Endpoints:                172.17.0.3:5000,172.17.0.6:5000,172.17.0.7:5000 + 1 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Service nginx 已经将后端 Endpoints 扩容到所有的 4 个 Pods。

收缩

我们也可以通过同样的命令缩容(kubectl scale)。Deployment 不会区分是扩容命令或是缩容命令,它只关心将实例的数量调整到指定的数量。

kubectl scale deployments easy-flask --replicas=2 -n test-lm
kubectl get pods -n test-lm
NAME                          READY   STATUS        RESTARTS   AGE
easy-flask-6495c89f66-6hzsn   1/1     Terminating   0          4m10s
easy-flask-6495c89f66-kb587   1/1     Terminating   0          4m10s
easy-flask-6495c89f66-lg9v9   1/1     Running       0          4m10s
easy-flask-6495c89f66-td9cz   1/1     Running       0          13h

Update deployment

接下来,我们将了解 kubernetes 如何进行应用更新。见下图,我们目前有四个运行中的应用:

img

当我们更新容器镜像时,kubernetes 会启动一个新 Pod 并关闭一个老 Pod。下图中,紫色的 Pod 为 kubernetes 新创建的 Pod,淡绿色 Pod 为老 Pod。Service 会停止向老 Pod 导流。

img

第一个 Pod 更新成功后,Deployment 会更新第二个 Pod。如下图所示,紫色两个 Pod 为 Deployment 创建的新 Pod。

img

最后,Deployment 将所有的 Pod 都更新完毕。

img

镜像更新

接下来,我们通过命令行了解 kubernetes 更新应用的过程。

kubectl set image deployments easy-flask easy-flask=qqliuming1995/easy-flask:new  -n test-lm
kubectl get pods -n test-lm
NAME                          READY   STATUS        RESTARTS   AGE
easy-flask-6495c89f66-lg9v9   1/1     Terminating   0          27m
easy-flask-6495c89f66-td9cz   1/1     Terminating   0          14h
easy-flask-6bdd99c4f4-2drtm   1/1     Running       0          24s
easy-flask-6bdd99c4f4-5brjd   1/1     Running       0          19s
curl $(minikube ip):30653
Hello, New World!

Deployment rollout

rollout 子命令可以用来查询部署的状态,以及回滚等操作。使用 kubectl rollout status 可以查询部署的状态。

kubectl rollout status deployment easy-flask -n test-lm
deployment "easy-flask" successfully rolled out

上面的状态说明之前部署的 Deployment 已经正常部署了。如果我们想要回滚到之前的版本,可以使用 kubectl rollout undo 命令。

首先,当前 是new hello world:

curl $(minikube ip):30653
Hello, New World!

接下来,使用回滚操作:

kubectl rollout undo deployment easy-flask -n test-lm
deployment.apps/easy-flask rolled back

再来看一下

curl $(minikube ip):30653
Hello, World!

Kubernetes Yaml/Json File

获取resource yaml

目前,我们都是通过 kubectl 提供的快捷命令来创建、管理资源。实际上,对于 kubernetes 而言,所有的操作都是以 yaml 文件为主。我们之前所使用的命令,只是方便用户快速修改 yaml 中经常需要修改的字段。

接下来,我们学习 kubernetes yaml/json 文件格式和使用方法。首先,kubernetes yaml 文件的基本格式如下代码所示(这里展示的是一个 Pod 的 yaml 文件,并且有部分裁剪)。

kubernetes yaml 整体分为 5 个部分:

  • apiVersion:kubernetes API 的分组
  • kind:操作的资源类型
  • metadata:资源的元数据,对于每种资源都是固定的,例如资源的名字,所处的 namespace, label 等
  • spec:用户对资源的 “说明书”,即用户对资源的各种配置信息
  • status:资源当前的状态,kubernetes 会尽最大努力使 spec 和 status 相匹配。

用户可以通过 kubectl get -o yaml 来获取已经部署的资源的 Yaml 文件,我们可以尝试获取之前通过 kubectl apply, kubectl expose 等命令部署的 Deployment 和 Service。

kubectl get pods easy-flask-6495c89f66-hp5lz -n test-lm -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2021-06-21T08:12:35Z"
  generateName: easy-flask-6495c89f66-
  labels:
    app: easy-flask
    pod-template-hash: 6495c89f66
  name: easy-flask-6495c89f66-hp5lz
  namespace: test-lm
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: easy-flask-6495c89f66
    uid: 24be92d6-ad65-4240-bce3-e25390c8d404
  resourceVersion: "39588"
  uid: 8a0256d5-d68b-46d1-8686-e9b4ecfd3c54
spec:
  containers:
  - image: qqliuming1995/easy-flask
    imagePullPolicy: Always
    name: easy-flask
    ports:
    - containerPort: 5000
      protocol: TCP
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-kms7g
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: minikube
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: default-token-kms7g
    secret:
      defaultMode: 420
      secretName: default-token-kms7g
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2021-06-21T08:12:35Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2021-06-21T08:12:39Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2021-06-21T08:12:39Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2021-06-21T08:12:35Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: docker://768e09cf6547ec1bcc9442c680396498258abb740741e959e37337aa1487a5d6
    image: qqliuming1995/easy-flask:latest
    imageID: docker-pullable://qqliuming1995/easy-flask@sha256:6c3e5457a103147d540d46a020b40a5e454acae650dff724fbe7d46c78a61f4c
    lastState: {}
    name: easy-flask
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2021-06-21T08:12:38Z"
  hostIP: 192.168.49.2
  phase: Running
  podIP: 172.17.0.7
  podIPs:
  - ip: 172.17.0.7
  qosClass: BestEffort
  startTime: "2021-06-21T08:12:35Z"
kubectl get svc easy-flask -n test-lm -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2021-06-21T05:43:22Z"
  labels:
    app: easy-flask
  name: easy-flask
  namespace: test-lm
  resourceVersion: "33106"
  uid: 71ed601e-8a78-4e5c-af40-c8706d20af39
spec:
  clusterIP: 10.99.243.69
  clusterIPs:
  - 10.99.243.69
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 30653
    port: 5000
    protocol: TCP
    targetPort: 5000
  selector:
    app: easy-flask
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

用yaml创建resource

用户可通过 kubectl create -f 创建 kubernetes 资源。用户不需要填入更多的信息,所有信息都已经在 yaml 文件中。在之前已经通过 yaml 文件创建过 namespace。这里我们创建一个 Pod。

假设resource/pod.yaml的内容为

apiVersion: v1
kind: Pod
metadata:
  name: yaml-flask
  namespace: test-lm
spec:
  containers:
  - name: yaml-flask
    image: qqliuming1995/easy-flask
    resources:
      requests:
        cpu: "0.1"
        memory: "100Mi"
      limits:
        cpu: "0.1"
        memory: "100Mi"

那么可以先创建pod

kubectl create -f resources/pod.yaml -n test-lm
kubectl get pods -n test-lm
NAME                          READY   STATUS    RESTARTS   AGE
easy-flask-6495c89f66-hp5lz   1/1     Running   0          106m
easy-flask-6495c89f66-xc5t5   1/1     Running   0          107m
yaml-flask                    1/1     Running   0          18s

更新resource yaml

假设新的yaml文件内容为

apiVersion: v1
kind: Pod
metadata:
  name: yaml-flask
  namespace: test-lm
spec:
  containers:
  - name: yaml-flask
    image: qqliuming1995/easy-flask:new
    resources:
      requests:
        cpu: "0.1"
        memory: "100Mi"
      limits:
        cpu: "0.1"
        memory: "100Mi"

更新一下设置

kubectl apply -f resources/pod.yaml -n test-lm
Warning: resource pods/yaml-flask is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
pod/yaml-flask configured

接下来再看一下image的信息,可以看到已经改成了qqliuming1995/easy-flask:new了

kubectl describe pods yaml-flask -n test-lm | grep "Image:" -C 5
IPs:
  IP:  172.17.0.6
Containers:
  yaml-flask:
    Container ID:   docker://41cc5b2e7a86592eed9536708e8f1d139c915776b31443cc0d32ccd756bf4d79
    Image:          qqliuming1995/easy-flask:new
    Image ID:       docker-pullable://qqliuming1995/easy-flask@sha256:bc28517fefb1d2a49648d1277e7819f2b4c89c748bdc8129042968235652b0d6
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 21 Jun 2021 10:02:37 +0000

kubernetes 同时支持在线编辑 yaml 文件:

kubectl edit pods yaml-flask -n test-lm
apiVersion: v1
kind: Pod
metadata:
  name: yaml-flask
  namespace: test-lm
spec:
  containers:
  - name: yaml-flask
    image: qqliuming1995/easy-flask:latest
    resources:
      requests:
        cpu: "0.1"
        memory: "100Mi"
      limits:
        cpu: "0.1"
        memory: "100Mi"

更改之后再describe,可以发现内容已经改回去了。

kubectl describe pods yaml-flask -n test-lm | grep "Image:" -C 5
IPs:
  IP:  172.17.0.6
Containers:
  yaml-flask:
    Container ID:   docker://41cc5b2e7a86592eed9536708e8f1d139c915776b31443cc0d32ccd756bf4d79
    Image:          qqliuming1995/easy-flask:latest
    Image ID:       docker-pullable://qqliuming1995/easy-flask@sha256:bc28517fefb1d2a49648d1277e7819f2b4c89c748bdc8129042968235652b0d6
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 21 Jun 2021 10:02:37 +0000

Kubernetes Events

Kubernetes events 显示了 kubernetes 集群中所有的事件。不同于其他资源,kubernetes events 并不是由用户创建的资源,而是由 kubernetes 系统组件创建,用以提示用户集群发生的各种事件。我们可以通过 kubectl get 命令来查询集群的事件。默认情况下,event 会有 TTL,超过 TTL 之后 kubernetes 会将事件删掉。

kubectl get events -n test-lm
LAST SEEN   TYPE     REASON      OBJECT           MESSAGE
10m         Normal   Scheduled   pod/yaml-flask   Successfully assigned test-lm/yaml-flask to minikube
10m         Normal   Pulling     pod/yaml-flask   Pulling image "qqliuming1995/easy-flask"
10m         Normal   Pulled      pod/yaml-flask   Successfully pulled image "qqliuming1995/easy-flask" in 1.863954507s
2m27s       Normal   Created     pod/yaml-flask   Created container yaml-flask
2m26s       Normal   Started     pod/yaml-flask   Started container yaml-flask
2m59s       Normal   Killing     pod/yaml-flask   Container yaml-flask definition changed, will be restarted
7m16s       Normal   Pulling     pod/yaml-flask   Pulling image "qqliuming1995/easy-flask:new"
7m14s       Normal   Pulled      pod/yaml-flask   Successfully pulled image "qqliuming1995/easy-flask:new" in 1.87977869s
2m29s       Normal   Pulling     pod/yaml-flask   Pulling image "qqliuming1995/easy-flask:latest"
2m27s       Normal   Pulled      pod/yaml-flask   Successfully pulled image "qqliuming1995/easy-flask:latest" in 1.879258203s

Event 与资源是相联系的,因此单独查询 Event 并不是非常有用,我们可以通过获取资源的详细信息来查看 Event 信息。例如, kubectl describe pod <pod name> 会返回 Pod 的 event 信息。

kubectl describe pod yaml-flask -n test-lm
Name:         yaml-flask
Namespace:    test-lm
Priority:     0
Node:         minikube/192.168.49.2
Start Time:   Mon, 21 Jun 2021 09:59:15 +0000
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           172.17.0.6
IPs:
  IP:  172.17.0.6
Containers:
  yaml-flask:
    Container ID:   docker://d73d6769a8d3081e00cf8220779ac4be13c7de43725e656782b283d24267466a
    Image:          qqliuming1995/easy-flask:latest
    Image ID:       docker-pullable://qqliuming1995/easy-flask@sha256:6c3e5457a103147d540d46a020b40a5e454acae650dff724fbe7d46c78a61f4c
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 21 Jun 2021 10:07:25 +0000
    Last State:     Terminated
      Reason:       Error
      Exit Code:    137
      Started:      Mon, 21 Jun 2021 10:02:37 +0000
      Finished:     Mon, 21 Jun 2021 10:07:22 +0000
    Ready:          True
    Restart Count:  2
    Limits:
      cpu:     100m
      memory:  100Mi
    Requests:
      cpu:        100m
      memory:     100Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kms7g (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  default-token-kms7g:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-kms7g
    Optional:    false
QoS Class:       Guaranteed
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 test-lm/yaml-flask to minikube
  Normal  Pulling    11m                    kubelet            Pulling image "qqliuming1995/easy-flask"
  Normal  Pulled     11m                    kubelet            Successfully pulled image "qqliuming1995/easy-flask" in 1.863954507s
  Normal  Pulling    8m11s                  kubelet            Pulling image "qqliuming1995/easy-flask:new"
  Normal  Pulled     8m9s                   kubelet            Successfully pulled image "qqliuming1995/easy-flask:new" in 1.87977869s
  Normal  Killing    3m54s (x2 over 8m41s)  kubelet            Container yaml-flask definition changed, will be restarted
  Normal  Pulling    3m24s                  kubelet            Pulling image "qqliuming1995/easy-flask:latest"
  Normal  Created    3m22s (x3 over 11m)    kubelet            Created container yaml-flask
  Normal  Pulled     3m22s                  kubelet            Successfully pulled image "qqliuming1995/easy-flask:latest" in 1.879258203s
  Normal  Started    3m21s (x3 over 11m)    kubelet            Started container yaml-flask

Kubernetes Pod Lifecycle

Pod 生命周期主要包括:

  • Pod Phase
  • Pod Condition
  • Restart Policy
  • Container probes

用户可以通过 kubectl describe pods 查看以上所有信息。Pod Phase 和 Pod Condition 比较简单,我们可以实时看到 kubernetes 的反馈。这里我们主要实践 Restart Policy 和 Container probes。

Restart policy

Restart Policy 指定的是当 Pod 内容器出错或执行完毕后,是否重启。

假设用的镜像是qqliuming1995/error-sample,yaml文件是resources/error-sample.yaml

apiVersion: v1
kind: Pod
metadata:
  name: error-sample
  labels:
    app: error-sample
spec:
  containers:
    - name: error-sample
      image: qqliuming1995/error-sample
      resources:
        requests:
          cpu: "0.1"
          memory: "100Mi"
        limits:
          cpu: "0.1"
          memory: "100Mi"

那么创建它

kubectl create -f resources/error-sample.yaml -n test-lm

看一下pod状态

kubectl get pods -n test-lm
NAME                          READY   STATUS              RESTARTS   AGE
easy-flask-6495c89f66-hp5lz   1/1     Running             0          129m
easy-flask-6495c89f66-xc5t5   1/1     Running             0          129m
error-sample                  0/1     RunContainerError   0          9s
yaml-flask                    1/1     Running             2          22m

现在,我们为该 Pod 添加 Restart Policy,使 kubernetes 不再不断重启 debian 容器,新的yaml文件是:

apiVersion: v1
kind: Pod
metadata:
  name: error-sample
  labels:
    app: error-sample
spec:
  restartPolicy: Never
  containers:
    - name: error-sample
      image: qqliuming1995/error-sample
      resources:
        requests:
          cpu: "0.1"
          memory: "100Mi"
        limits:
          cpu: "0.1"
          memory: "100Mi"

删除pod再次创建

kubectl delete pods error-sample -n test-lm
kubectl create -f resources/error-sample.yaml -n test-lm

从而得到以下结果:

kubectl get pods -n test-lm
NAME                          READY   STATUS               RESTARTS   AGE
easy-flask-6495c89f66-hp5lz   1/1     Running              0          131m
easy-flask-6495c89f66-xc5t5   1/1     Running              0          132m
error-sample                  0/1     ContainerCannotRun   0          5s
yaml-flask                    1/1     Running              2          25m

Container probes

Container probes 分为两种:LivenessProbe 和 ReadinessProbe。Liveness 检查应用是否依然健康无错,若有错,则 kubernetes 会根据 policy 重启或仅更新状态。ReadinessCheck 检查应用是否可以对外提供服务,若应用 Readiness 检查不通过,则 kubernetes 会将 Pod 从服务池中剔除。两者的使用方法都相同,这里我们来看看 Container probes。

假设resources/heath_prob.yaml文件的内容如下

apiVersion: v1
kind: Pod
metadata:
  namespace: test-lm
  name: health-flask
  labels:
    app: health-flask
spec:
  containers:
    - name: health-flask
      image: qqliuming1995/easy-flask
      resources:
        requests:
          cpu: "0.1"
          memory: "100Mi"
        limits:
          cpu: "0.1"
          memory: "100Mi"
      livenessProbe:
        httpGet:
          path: /
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5

那么创建一下

kubectl create -f resources/health_prob.yaml
.....
"qqliuming1995/easy-flask" in 1.846649627s
  Warning  Unhealthy  4s (x8 over 109s)   kubelet            Liveness probe failed: Get "http://172.17.0.8:8080/": dial tcp 172.17.0.8:8080: connect: connection refused

Kubernetes ConfigMap & Secret

ConfigMap 是 kubernetes 用来管理配置信息的资源类型。我们通过单独创建 ConfigMap,再将 ConfigMap 挂载到 Pod 内的方式分离配置和应用。我们通过一个实验来学习如何正确使用 ConfigMap。

创建 ConfigMap 可以通过 yaml 文件,也可以从文件直接创建。通过 yaml 文件的方式与创建其他资源类似。这里,我们采用文件的方式。假设 resources 目录下,有两个文件:game.propertiesui.properties。我们通过 kubectl 命令创建:

game.properties

enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

ui.properties

color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

那么就开始创建一下

kubectl create configmap game-config --from-file=resources/game.properties --from-file=resources/ui.properties -n test-lm

创建之后,通过 kubectl get configmap 来查看创建的 ConfigMap:

kubectl get configmap game-config -o wide -n test-lm
NAME          DATA   AGE
game-config   2      21s
kubectl describe configmap game-config -n test-lm
Name:         game-config
Namespace:    test-lm
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

Events:  <none>

查看详情

kubectl get configmap game-config -o yaml -n test-lm
apiVersion: v1
data:
  game.properties: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  creationTimestamp: "2021-06-21T11:06:31Z"
  name: game-config
  namespace: test-lm
  resourceVersion: "47109"
  uid: 111ebcf6-55e4-4d85-9cf3-393e8ce8434f

创建 ConfigMap 之后,我们可以创建 Pod 来使用该 ConfigMap:

resources/pod_configmap.yaml

apiVersion: v1
kind: Pod
metadata:
  namespace: test-lm
  name: pod-configmap
spec:
  restartPolicy: Never
  containers:
    - name: test-container
      image: qqliuming1995/easy-flask
      command: ["/bin/sh"]
      args: ["-c", "cat /etc/config/game.properties && cat /etc/config/ui.properties"]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
      resources:
        requests:
          cpu: "0.1"
          memory: "100Mi"
        limits:
          cpu: "0.1"
          memory: "100Mi"
  volumes:
    - name: config-volume
      configMap:
        name: game-config
kubectl create -f resources/pod_configmap.yaml -n test-lm
kubectl logs pod-configmap -n test-lm

这里我们看到了通过挂载文件的方式使用 configmap,kubernetes 同时也支持通过环境变量的方式使用 configmap。此外,Secret 的使用方式与 Configmap 类似,但内容会被加密。

总结

这章介绍了 Kubernetes deployment 的更多操作, Yaml 文件规范,事件,Pod 生命周期等,接下来会介绍其他应用相关模块,存储。

目前为止简单介绍了 Kubernetes command line, Pod, Deployment, Service 和 Label。这些都是 kubernetes 中最为核心的概念,接下来我们将深入了解 Deployent 的功能,然后开始涉及其他模块。本节暂无实验内容。

最后修改:2021 年 07 月 07 日 07 : 02 PM
如果觉得我的文章对你有用,请随意赞赏