K8S修炼之路-1. 炼气期


K8S修炼之路-1. 炼气期

修炼须知及目标

本阶段为k8s修炼之路的开始阶段,不求学会太深奥的原理,只要明白为啥需要k8s以及k8s的基本使用即可。目标如下:

  • 安装 Kubernetes 环境
  • Kubernetes 基本概念和使用方法
    s

修炼内容

1. 修炼环境

1.1 kubectl

kubectl是一个命令行工具,用于和k8s集群中的资源进行交互,安装参考https://kubernetes.io/zh/docs/tasks/tools/install-kubectl-linux/

kubectl命令较多,使用

$ kubectl -h

查看帮助,并且在实战中学习。

1.2 minikube

minikube是一个极简版的k8s集群,运行在一个机器上,仅包含一个master节点和一个node节点。使用minikube省去了搭建k8s集群的步骤,是入门时使用k8s的非常方便的途径。安装参考https://minikube.sigs.k8s.io/docs/start/

安装完成后,使用

$ minikube start
😄  minikube v1.2.0 on darwin (amd64)
🔥  Creating virtualbox VM (CPUs=2, Memory=2048MB, Disk=20000MB) ...
🐳  Configuring environment for Kubernetes v1.15.0 on Docker 18.09.6
💾  Downloading kubeadm v1.15.0
💾  Downloading kubelet v1.15.0
🚜  Pulling images ...
🚀  Launching Kubernetes ...
⌛  Verifying: apiserver proxy etcd scheduler controller dns
🏄  Done! kubectl is now configured to use "minikube"

命令可以启动集群。

使用以下命令验证安装:

$ kubectl get nodes
NAME       STATUS   ROLES    AGE    VERSION
minikube   Ready    master   4m5s   v1.15.0

常用命令:

# 进入集群节点
minikube ssh

# 查看节点 IP
minikube ip

# 停止集群
minikube stop

# 删除集群
minikube delete

在这个过程中,不出意外应该会有很多意外,擅用google是修炼路上的重要辅助。

2. 操纵基本资源

2.1 kubectl 基本用法

kubectl version

使用以下命令查看k8s版本:

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.5", GitCommit:"c285e781331a3785a7f436042c65c5641ce8a9e9", GitTreeState:"clean", BuildDate:"2022-03-16T15:58:47Z", GoVersion:"go1.17.8", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.3", GitCommit:"816c97ab8cff8a1c72eccca1026f7820e93e0d25", GitTreeState:"clean", BuildDate:"2022-01-25T21:19:12Z", GoVersion:"go1.17.6", Compiler:"gc", Platform:"linux/amd64"}

可以看到,kubectl 客户端的版本是 v1.23.5,kubernetes 集群的版本是 v1.23.3。同时,该命令还会输出 kubernetes 编译信息。

kubectl cluster-info

使用以下命令查看集群信息:

$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.58.2:8443
CoreDNS is running at https://192.168.58.2:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

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

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

kubectl config file

kubectl通过读取配置文件与集群进行交互,默认配置文件路径是 ~/.kube/config,内容可能如下:

& cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /root/.minikube/ca.crt
    extensions:
    - extension:
        last-update: Mon, 23 May 2022 00:23:49 CST
        provider: minikube.sigs.k8s.io
        version: v1.25.2
      name: cluster_info
    server: https://192.168.58.2:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    extensions:
    - extension:
        last-update: Mon, 23 May 2022 00:23:49 CST
        provider: minikube.sigs.k8s.io
        version: v1.25.2
      name: context_info
    namespace: default
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /root/.minikube/profiles/minikube/client.crt
    client-key: /root/.minikube/profiles/minikube/client.key

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

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

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

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

2.2 Node

k8s是主从架构,master是主节点,node是从节点。节点node可以是物理机也可以是虚拟机,一个node包含若干个Pod。

Node information

查看有哪些节点:

$ kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
minikube   Ready    control-plane,master   8d    v1.23.3

这里 minikube 节点即是 master 也是 node。

Node details

查看节点详细信息:

$ kubectl describe nodes minikube

2.3 Namespace

namespace和docker中的类似,用于隔离资源,类似于一个分组。不同namespace中的资源名字可以相同,同一namespace下的资源不能同名。使用namespace达到了共享 kubernetes 集群资源的目的。

$ kubectl get ns
NAME                   STATUS   AGE
default                Active   8d
kube-node-lease        Active   8d
kube-public            Active   8d
kube-system            Active   8d
kubernetes-dashboard   Active   8d
tutorial               Active   2d4h

$ kubectl describe ns tutorial
Name:         tutorial
Labels:       kubernetes.io/metadata.name=tutorial
Annotations:  <none>
Status:       Active

No resource quota.

No LimitRange resource.

2.4 Pod & Deployment

通过image可以创建container,而pod就是container的运行环境。一个节点可以有多个pod,一个pod内可以有多个container,container之间共享底层资源。

在生产环境中,一般不直接操作pod,而是操作deployment,二者之间有啥区别?

通过创建deployment,可以更方便地管理pod,具体来说:

对于pod:

  1. 如果die了,那就die了。也就是说无容错能力,这也就是为什么生产环境不直接创建pod。
  2. 对于一个特定的创建pod的yml文件,只能创建一个pod。如果想创建多个相同配置的pod,也需要写多份pod的yml文件。

对于deployment:

  1. 可以定义我们预期的pod的状态,比如有几个pod。这样当一个pod die了,deplyment会自动创建一个新的pod,使整体情况达到我们的预期。

Create Deployment

通过以下命令创建deployment:

$ kubectl apply -f resources/deployment_nginx.yaml -n tutorial
deployment.apps/nginx-deployment created

在执行以上命令后,k8s在集群中寻找一台满足需求的机器运行节点,然后该节点上的 agent 启动该应用。

-n 代表指定 namespace

Get Deployment

$ kubectl get pods -n tutorial
NAME                     READY   STATUS    RESTARTS   AGE
nginx-646b46d648-hbwg2   1/1     Running   0          101s

Get Pods

$ kubectl get pods -n tutorial
NAME                     READY   STATUS    RESTARTS   AGE
nginx-646b46d648-hbwg2   1/1     Running   0          101s

更多信息:

$ kubectl get pods -n tutorial -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
nginx-646b46d648-hbwg2   1/1     Running   0          2m23s   172.17.0.11   minikube   <none>           <none>

Get Pod Logs

当我们部署要应用之后,可以通过 kubectl logs <pod name>kubectl exec <pod name> 与 Pod 交互。

$ kubectl logs nginx-646b46d648-hbwg2 -n tutorial

# -- 用于区分本地终端命令和容器中执行的命令
$ kubectl exec nginx-646b46d648-hbwg2 -n tutorial -- ls -l

#使用 ctrl + d 可以退出远程终端
$ kubectl exec -it nginx-646b46d648-hbwg2 -n tutorial bash

2.5 Service

kubernetes service 有以下几个作用:

  • 提供固定的 IP。由于 Pod 可以随时启停,Pod IP 可能随时都会变化,例如上面 nginx pod 重启之后 IP 可能不再是 172.17.0.11。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 都不会变化。

Create service

$ kubectl expose deployment nginx --port 80 -n tutorial
service "nginx" exposed

$ kubectl get svc nginx -n tutorial
NAME    TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.96.6.136   <none>        80/TCP    15s

可以看到,nginx这个svc,CLUSTER-IP是10.96.6.136,也就是说如果想访问这个svc下的pod,可以直接访问pod的ip或者这个svc的ip 10.96.6.136。

查看更多信息:

$ kubectl describe svc nginx -n tutorial
Name:              nginx
Namespace:         tutorial
Labels:            run=nginx
Annotations:       <none>
Selector:          run=nginx
Type:              ClusterIP
IP:                10.96.6.136
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         172.17.0.11:80
Session Affinity:  None
Events:            <none>

可以看到Endpoints这里,172.17.0.11:80是在这个svc下的pod的ip,验证一下:

$ kubectl get pods -o wide -n tutorial
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
nginx-646b46d648-hbwg2   1/1     Running   0          14m   172.17.0.11   minikube   <none>           <none>

关于几个port:

  • Port: service的port

  • TargetPort: 容器的port

  • NodePort: node的port

Query service

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

通过 Service IP 访问:

$ minikube ssh
$ curl 10.96.6.136
<!DOCTYPE html>
<html>
...
</html>

通过 Pod IP 访问:

$ minikube ssh
$ curl 172.17.0.11
<!DOCTYPE html>
<html>
...
</html>

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

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

$ kubectl delete svc nginx -n tutorial
service "nginx" deleted

之后,创建 Service:

$ kubectl expose deployment nginx --port 8080 --target-port 80 -n tutorial
service "nginx" exposed

尝试用 8080 端口访问服务

$ kubectl get svc nginx -n tutorial
NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
nginx   ClusterIP   10.98.125.20   <none>        8080/TCP   6s

$ minikube ssh
$ curl 10.98.125.20:8080
<!DOCTYPE html>
<html>
...
</html>

NodePort service

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

首先,删除已有的 Service:

$ kubectl delete svc nginx -n tutorial
service "nginx" deleted

通过 NodePort 暴露服务,注意这里使用了 --type NodePort

$ kubectl expose deployment nginx --port 80 --type NodePort -n tutorial
service "nginx" exposed

查看 Service 的细节:

$ kubectl get svc nginx -n tutorial
NAME    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx   NodePort   10.107.97.57   <none>        80:32542/TCP   5s
$ kubectl describe svc nginx -n tutorial
Name:                     nginx
Namespace:                tutorial
Labels:                   run=nginx
Annotations:              <none>
Selector:                 run=nginx
Type:                     NodePort
IP:                       10.107.97.57
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  32542/TCP
Endpoints:                172.17.0.11:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

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

$ curl $(minikube ip):32542
<!DOCTYPE html>
<html>
...
</html>

2.6 Label

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

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

$ kubectl describe deployment nginx -n tutorial
Name:                   nginx
Namespace:              tutorial
CreationTimestamp:      Fri, 28 Jun 2019 14:56:58 +0800
Labels:                 run=nginx
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               run=nginx
...

$ kubectl describe svc nginx -n tutorial
Name:                   nginx
Namespace:              tutorial
Labels:                 run=nginx
Annotations:            <none>
Selector:               run=nginx
...

$ kubectl describe pods nginx-646b46d648-hbwg2 -n tutorial
Name:           nginx-646b46d648-hbwg2
Namespace:      tutorial
Priority:       0
Node:           minikube/10.0.2.15
Start Time:     Fri, 28 Jun 2019 14:56:59 +0800
Labels:         pod-template-hash=646b46d648
                run=nginx

Label operations

# 通过 -l 指定标签
$ kubectl get pods -l run=nginx -n tutorial
NAME                     READY   STATUS    RESTARTS   AGE
nginx-646b46d648-hbwg2   1/1     Running   0          26m

# 给某个pod打标签
$ kubectl get pods -n tutorial
NAME                     READY   STATUS    RESTARTS   AGE
nginx-646b46d648-hbwg2   1/1     Running   0          26m

$ kubectl label pod nginx-646b46d648-hbwg2 app=v1 -n tutorial
pod/nginx-646b46d648-hbwg2 labeled

$ kubectl describe pods nginx-646b46d648-hbwg2 -n tutorial
Name:           nginx-646b46d648-hbwg2
Namespace:      tutorial
Priority:       0
Node:           minikube/10.0.2.15
Start Time:     Fri, 28 Jun 2019 14:56:59 +0800
Labels:         app=v1
                pod-template-hash=646b46d648
                run=nginx
Annotations:    <none>
Status:         Running
IP:             172.17.0.11
Controlled By:  ReplicaSet/nginx-646b46d648

2.7 Deployment Operations

k8s相比于docker的一个有点就是,弹性伸缩非常的方便。并且k8s自带更新策略,也就是说当一个deployment有多个pod时,k8s会按照一定地策略来对pod做更新。

下图中,Deployment A 有一个 Pod 在运行,Service A 管理该 Pod。

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

$ kubectl apply -f resources/deployment_nginx -n tutorial
$ kubectl expose deployment nginx --port 80 --name=nginx -n tutorial

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

$ kubectl get deployments -n tutorial
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           13s

$ kubectl scale deployments nginx --replicas=4 -n tutorial
deployment.extensions/nginx scaled

$ kubectl get deployments -n tutorial
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   4/4     4            4           49s

$ kubectl get pods -n tutorial -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
nginx-d6b94d6f6-lggxr   1/1     Running   0          28s   172.17.0.11   minikube   <none>           <none>
nginx-d6b94d6f6-lkr8m   1/1     Running   0          59s   172.17.0.8    minikube   <none>           <none>
nginx-d6b94d6f6-npntj   1/1     Running   0          28s   172.17.0.13   minikube   <none>           <none>
nginx-d6b94d6f6-rh42k   1/1     Running   0          28s   172.17.0.12   minikube   <none>           <none>

# svc也产生相应变化
$ kubectl describe service nginx -n tutorial
Name:              nginx
Namespace:         tutorial
Labels:            run=nginx
Annotations:       <none>
Selector:          run=nginx
Type:              ClusterIP
IP:                10.103.221.162
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         172.17.0.11:80,172.17.0.12:80,172.17.0.13:80 + 1 more...
Session Affinity:  None
Events:            <none>

同样可以进行缩容:

$ kubectl scale deployments nginx --replicas=2 -n tutorial
deployment.extensions/nginx scaled

$ kubectl get pods -n tutorial
NAME                    READY   STATUS    RESTARTS   AGE
nginx-d6b94d6f6-lkr8m   1/1     Running   0          4m58s
nginx-d6b94d6f6-rh42k   1/1     Running   0          4m27s

Update deployment

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

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

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


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

Update via setting image

$ kubectl set image deployments nginx nginx=cargo.caicloud.io/caicloud/nginx:1.9.3 -n tutorial
deployment.extensions/nginx image updated

$ kubectl get pods -n tutorial
NAME                     READY   STATUS              RESTARTS   AGE
nginx-d6b94d6f6-lkr8m    0/1     Terminating         0          5m58s
nginx-d6b94d6f6-rh42k    1/1     Running             0          5m27s
nginx-86d4667764-gxwkb   1/1     Running             0          4s
nginx-86d4667764-hr6r7   0/1     ContainerCreating   0          2s

分析一下上述命令,kubectl set image 将 Deployment 中的 nginx 镜像版本改为 1.9.3;运行该命令之后,发现 kubernetes 删掉了一个现有的 Pod,然后重新启动了两个新的 Pod(我们可以从一串数字中看出,”86d4667764” 是新 Pod 的 Hash 值,”d6b94d6f6” 是老 Pod 的 Hash 值)。等待一段时间后再次查询 Pods,发现所有新的 Pods 已经上线。整个过程中,我们都可以尝试去访问 nginx 服务,注意其版本的变化。

Deployment rollout

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

$ kubectl rollout status deployment nginx -n tutorial
deployment "nginx" successfully rolled out

首先,当前 nginx 处于 1.9.3 版本:

$ curl $(minikube ip):31658/version
<html>
...
<hr><center>nginx/1.9.3</center>
</body>
</html>

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

接下来使用回滚操作:

$ kubectl rollout undo deployment nginx -n tutorial
deployment.extensions/nginx rolled back

$ kubectl get pods -n tutorial
NAME                     READY   STATUS    RESTARTS   AGE
nginx-646b46d648-7c457   1/1     Running   0          9s
nginx-646b46d648-9wpdp   1/1     Running   0          6s

使用 rollout undo 之后,nginx 的版本回到了 set image 之前的版本:

$ curl $(minikube ip):31658/version
<html>
...
<hr><center>nginx/1.9.7</center>
</body>
</html>

2.8 Yaml/Json File

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

首先,kubernetes yaml 文件的基本格式如下代码所示(这里展示的是一个 Pod 的 yaml 文件,并且有部分裁剪)。kubernetes yaml 整体分为 5 个部分:apiVersion, kind, metadata, spec, status;其中 apiVersion 表明当前 kubernetes API 的分组;kind 表明当前操作的资源类型; metadata 是资源的元数据,对于每种资源都是固定的,例如资源的名字,所处的 namespace, label 等;spec 是用户对资源的 “说明书”,即用户对资源的各种配置信息;status 是资源当前的状态,kubernetes 会尽最大努力使 spec 和 status 相匹配。

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2019-06-28T08:16:45Z"
  generateName: nginx-646b46d648-
  labels:
  name: nginx-646b46d648-7c457
  namespace: tutorial
  ownerReferences:
  resourceVersion: "28549"
  selfLink: /api/v1/namespaces/tutorial/pods/nginx-646b46d648-7c457
  uid: e4312b93-9570-45a9-b981-8b26644f096c
spec:
  containers:
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: minikube
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  volumes:
status:
  conditions:
  containerStatuses:
  hostIP: 10.0.2.15
  phase: Running
  podIP: 172.17.0.8
  qosClass: Burstable
  startTime: "2019-06-28T08:16:46Z"

Get resource yaml

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

$ kubectl get pods nginx-646b46d648-7c457 -n tutorial -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2019-06-28T08:16:45Z"
  generateName: nginx-646b46d648-
...
spec:
  containers:
  - image: cargo.caicloud.io/caicloud/nginx:1.9.7
    imagePullPolicy: IfNotPresent
    name: nginx
    resources:
      limits:
        cpu: 200m
        memory: 512Mi
      requests:
        cpu: 100m
        memory: 256Mi
...
status:
  hostIP: 10.0.2.15
  phase: Running
  podIP: 172.17.0.8
  qosClass: Burstable
  startTime: "2019-06-28T08:16:46Z"
...
$ kubectl get svc nginx -n tutorial -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2019-06-28T08:13:20Z"
  labels:
    run: nginx
  name: nginx
  namespace: tutorial
  resourceVersion: "28252"
  selfLink: /api/v1/namespaces/tutorial/services/nginx
  uid: 4586f1da-59bf-4c9c-90fb-1d4974a0e04e
spec:
  clusterIP: 10.103.177.222
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 31658
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

通过修改yaml文件可以修改资源的信息,这里不过多展开,即用即查。

2.9 Events

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

$ kubectl get events -n tutorial
LAST SEEN   TYPE     REASON              OBJECT                        MESSAGE
12m         Normal   Scheduled           pod/nginx-646b46d648-7c457    Successfully assigned tutorial/nginx-646b46d648-7c457 to minikube
12m         Normal   Pulled              pod/nginx-646b46d648-7c457    Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine
12m         Normal   Created             pod/nginx-646b46d648-7c457    Created container nginx
12m         Normal   Started             pod/nginx-646b46d648-7c457    Started container nginx
50m         Normal   Scheduled           pod/nginx-646b46d648-8kbk9    Successfully assigned tutorial/nginx-646b46d648-8kbk9 to minikube
50m         Normal   Pulled              pod/nginx-646b46d648-8kbk9    Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine
50m         Normal   Created             pod/nginx-646b46d648-8kbk9    Created container nginx
50m         Normal   Started             pod/nginx-646b46d648-8kbk9    Started container nginx
49m         Normal   Killing             pod/nginx-646b46d648-8kbk9    Stopping container nginx
...

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

$ kubectl describe pod nginx -n tutorial
Name:         nginx
Namespace:    tutorial
Priority:     0
Node:         minikube/10.0.2.15
Start Time:   Fri, 28 Jun 2019 16:25:42 +0800
Labels:       <none>
...
Events:
  Type    Reason     Age                   From               Message
  ----    ------     ----                  ----               -------
  Normal  Scheduled  4m20s                 default-scheduler  Successfully assigned tutorial/nginx to minikube
  Normal  Pulled     3m22s                 kubelet, minikube  Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine
  Normal  Pulled     101s (x2 over 4m19s)  kubelet, minikube  Container image "cargo.caicloud.io/caicloud/nginx:1.9.3" already present on machine
  Normal  Created    101s (x3 over 4m19s)  kubelet, minikube  Created container nginx
  Normal  Started    101s (x3 over 4m18s)  kubelet, minikube  Started container nginx
  Normal  Killing    101s (x2 over 3m23s)  kubelet, minikube  Container nginx definition changed, will be restarted

2.10 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 内容器出错或执行完毕后,是否重启。下面的 Pod 使用了 debian 镜像,该镜像并不会长期运行,因此如果我们直接创建,kubernetes 会认为 Pod 出错。

$ kubectl create -f resources/debian.yaml -n tutorial
pod/debian created

注,若提示资源不足,可以删掉现有的 Deployment 或 Pod 资源。创建之后,等待 kubernetes 拉取镜像。几分钟后, kubernetes 提示 redis-django 进入 Crash 状态,且有多次重启:

$ kubectl get pods -n tutorial
NAME                     READY   STATUS             RESTARTS   AGE
debian                   0/1     CrashLoopBackOff   1          32s
nginx                    1/1     Running            2          14m
nginx-646b46d648-7c457   1/1     Running            0          23m
nginx-646b46d648-9wpdp   1/1     Running            0          23m

现在,我们为该 Pod 添加 Restart Policy,使 kubernetes 不再不断重启 debian 容器,从而得到以下结果:

$ kubectl delete pods debian -n tutorial
pod "debian" deleted

$ kubectl create -f resources/debian_never_restart.yaml -n tutorial
pod "debian" created

debian 容器变成 Completed 状态:

$ kubectl get pods -n tutorial
NAME                     READY   STATUS      RESTARTS   AGE
debian                   0/1     Completed   0          33s
nginx                    1/1     Running     2          15m
nginx-646b46d648-7c457   1/1     Running     0          24m
nginx-646b46d648-9wpdp   1/1     Running     0          24m

Container probes

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

打开 pod_health.yaml,可以看到里面定义了 livenessProbe。当我们运行创建该 Pod 的时候,kubernetes 就开始为我们监控该 Pod 的 liveness 信息。

$ kubectl delete pods nginx -n tutorial
pod "nginx" deleted

$ kubectl create -f resources/pod_health.yaml -n tutorial
pod "nginx" created

Pod 会一直处于 Running 状态:

$ kubectl get pods -n tutorial
NAME                     READY     STATUS    RESTARTS   AGE
nginx                    1/1     Running     0          7s
nginx-646b46d648-7c457   1/1     Running     0          29m
nginx-646b46d648-9wpdp   1/1     Running     0          29m

我们可以分别尝试将 livenessProbe 的 http 80 端口改为 8080,观察 Pod 的状态。

$ kubectl delete pods nginx -n tutorial
pod "nginx" deleted

$ kubectl create -f resources/pod_unhealth.yaml -n tutorial
pod "nginx" created

Pod 会首先处于 Running 状态,但是在经过一段时间之后,Pod 会变为 Crash 状态,事件里会汇报健康检查错误:

$ kubectl describe pod nginx -n tutorial
Name:         nginx
Namespace:    tutorial
Priority:     0
Node:         minikube/10.0.2.15
Start Time:   Fri, 28 Jun 2019 16:46:22 +0800
Labels:       app=nginx
Annotations:  <none>
Status:       Running
IP:           172.17.0.11
Containers:
  nginx:
    ...
    Liveness:     http-get http://:8080/ delay=5s timeout=1s period=5s #success=1 #failure=3
...
Events:
  Type     Reason     Age              From               Message
  ----     ------     ----             ----               -------
  Normal   Scheduled  14s              default-scheduler  Successfully assigned tutorial/nginx to minikube
  Normal   Pulled     13s              kubelet, minikube  Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine
  Normal   Created    13s              kubelet, minikube  Created container nginx
  Normal   Started    13s              kubelet, minikube  Started container nginx
  Warning  Unhealthy  3s (x2 over 8s)  kubelet, minikube  Liveness probe failed: Get http://172.17.0.11:8080/: dial tcp 172.17.0.11:8080: connect: connection refused

2.11 ConfigMap & Secret

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

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

$ kubectl create configmap game-config --from-file=resources/game.properties --from-file=resources/ui.properties -n tutorial
configmap/game-config created

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

$ kubectl get configmap game-config -o wide -n tutorial
NAME          DATA      AGE
game-config   2         2m

$ kubectl describe configmap game-config -n tutorial
Name:         game-config
Namespace:    tutorial
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 tutorial
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: "2019-06-28T08:49:20Z"
  name: game-config
  namespace: tutorial
  resourceVersion: "31335"
  selfLink: /api/v1/namespaces/tutorial/configmaps/game-config
  uid: 134781b7-5565-4037-b0b2-be42767255a0

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

$ kubectl create -f resources/pod_configmap.yaml -n tutorial
pod/pod-configmap created

查看:

$ kubectl get pods -n tutorial
NAME                     READY     STATUS             RESTARTS   AGE
pod-configmap            0/1       Completed          0          2m

$ kubectl logs pod-configmap -n tutorial
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

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

修炼感想

本阶段主要学会如何访问各种资源即可,切记不要试图去理解k8s的架构,因为太过庞大,贸然学习容易被反噬!切记!在炼气期所学,皆是一些花拳绣腿,如何做出一些动作。但是这些招式打出去软绵无力,毫无内功。在接下来的修炼之路上,要不断修炼内容,明白原理,这样在面对敌人时方可游刃有余。


文章作者: foursevenlove
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 foursevenlove !
  目录