Kubenetes CKA 和 CKAD 认证备考指南(三)
原文:Kubernetes: Preparing for the CKA and CKAD Certifications
十二、持久卷
持久卷( PV )是由集群管理员提供的存储资源。供应可以是手动或自动的。
PV 申报由两部分组成:
-
其功能(访问模式、容量、存储类别、卷模式—文件系统或原始)
-
其实现(本地、NFS、云存储资源等。)
这些存储资源旨在通过使用PersistentVolumeClaims供 Pod 使用:Pod 将声明一个具有特定功能的持久卷,Kubernetes 将尝试找到一个与这些功能匹配的持久卷(独立于实现细节)。
如果可用,持久卷将被安装到部署 Pod 的节点的文件系统中,并最终暴露给 Pod。实现部分向 kubelet 说明如何将存储挂载到文件系统中。
创建 NFS 持久性卷
例如,我们将手动创建一个由 NFS 卷实施的 PV。
首先,在谷歌云中创建一个 NFS 卷:
$ gcloud filestore instances create nfs-server \
--project=$(gcloud config get-value project) \
--zone=$(gcloud config get-value compute/zone) \
--tier=STANDARD \
--file-share=name="vol1",capacity=1TB \
--network=name="kubernetes-cluster"
Waiting for [operation-...] to finish...done.
$ gcloud filestore instances describe nfs-server \
--project=$(gcloud config get-value project) \
--zone=$(gcloud config get-value compute/zone)
createTime: '2020-01-24T07:43:58.279881289Z'
fileShares:
- capacityGb: '1024'
name: vol1
name: projects/yourproject/locations/us-west1-c/instances/nfs-server
networks:
- ipAddresses:
- 172.25.52.106 # Note this IP address
network: kubernetes-cluster
reservedIpRange: 172.25.52.104/29
state: READY
tier: STANDARD
在 workers 上,安装 NFS 驱动程序,以便能够挂载 NFS 文件系统:
对每个工人重复这些步骤:
$ gcloud compute ssh worker-0
Welcome to worker-0
$ sudo apt-get -y update
$ sudo apt-get -y install nfs-common
您可以测试工作人员是否可以挂载文件系统:
$ gcloud compute ssh worker-0
Welcome to worker-0
$ sudo mkdir /mnt/nfs
$ sudo mount 172.25.52.106:/vol1 /mnt/nfs
$ ls /mnt/nfs
lost+found
$ sudo umount /mnt/nfs
现在,您可以定义 NFS 持久卷:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
# capabilities
accessModes:
- ReadWriteOnce
- ReadOnlyMany
- ReadWriteMany
capacity:
storage: 1Ti
volumeMode: Filesystem
# implementation
nfs:
path: /vol1
server: 172.25.52.106
访问模式
一般来说,存储系统可以被访问以进行读写操作,并且可以或不可以被多个客户端同时访问以进行这些操作,具体取决于存储系统使用的技术。
PersistentVolume accessModes字段指示底层存储系统在只读或读/写模式下的同时访问能力。定义了三个值:
-
ReadWriteOnce (RWO)
-
存储可由单个客户端进行读写操作。
-
readonlymny(rox)
-
多个客户端可以对存储进行只读操作。
-
读写多个 (RWX)
-
多个客户端可以对存储进行读写操作。
如果 PV 具有多能力,几个吊舱将能够同时使用这个 PV。请注意,对于要由几个 pod 使用的 PV,所有 pod 要求的访问模式必须相同;不可能一个 Pod 使用ReadOnlyMany模式,而另一个 Pod 在同一 PV 上使用ReadWriteMany。
声明一个持久卷
当一个 Pod 需要一个持久卷时,它必须申请一个。它不要求特定的持久卷,而是要求一系列功能。永久卷控制器将根据匹配的功能影响最佳的永久卷。
使用了PersistentVolumeClaim资源,其规范结构定义了这些字段:
-
accessModes -
请求的访问模式(参见“访问模式”)。
-
selector -
基于标签匹配特定持久性卷的标签选择器。
-
resources -
永久卷必须至少提供
resources.requests.storage,如果定义,最多提供resources.limits.storage。 -
storageClassName -
存储提供商可以定义不同的存储类名;您可以指定永久卷应属于哪个类。
-
volumeMode -
存储方式:文件系统或块。
继续前面的示例,您可以创建一个与调配的 NFS 持久性卷匹配的声明,确切地说,是一个可由多个客户端以读写模式访问的卷,具有至少 500 Gi 和最多 1.5 Ti 的存储:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pv-rwx-500g
spec:
storageClassName: ""
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Gi
limits:
storage: 1500Gi
现在,您可以部署两个单元,使用相同的存储、一个数据库系统和一个机箱:
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: pg
name: pg
spec:
serviceName: pg
selector:
matchLabels:
app: pg
template:
metadata:
labels:
app: pg
spec:
containers:
- image: postgres
name: pg
env:
- name: PGDATA
value: /var/lib/postgresql/data/db-files
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: pv-rwx-500g
readOnly: false
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: box
name: box
spec:
serviceName: box
selector:
matchLabels:
app: box
template:
metadata:
labels:
app: box
spec:
containers:
- image: busybox
name: box
volumeMounts:
- name: data
mountPath: /mnt
command:
- sh
- -c
- "sleep $((10**10))"
volumes:
- name: data
persistentVolumeClaim:
claimName: pv-rwx-500g
readOnly: false
如果您使用命令kubectl logs db-0检查数据库窗格的日志,您可以看到系统创建了一个新的数据库并将其文件存储在目录中:
/var/lib/postgresql/data/db-files
从box窗格中,您可以访问相同的文件系统:
$ kubectl exec -it box-0 sh
# ls /mnt/db-files/
PG_VERSION pg_multixact pg_tblspc
base pg_notify pg_twophase
global pg_replslot pg_wal
pg_commit_ts pg_serial pg_xact
pg_dynshmem pg_snapshots postgresql.auto.conf
pg_hba.conf pg_stat postgresql.conf
pg_ident.conf pg_stat_tmp postmaster.opts
pg_logical pg_subtrans postmaster.pid
清除
持久卷非常昂贵。当您不再需要 NFS 宗卷时,请记住将其删除:
$ gcloud filestore instances delete nfs-server \
--project=$(gcloud config get-value project) \
--zone=$(gcloud config get-value compute/zone)
使用自动配置的永久卷
通常,云中的 Kubernetes 引擎提供自动配置的持久卷。例如,在谷歌云 GKE 中,GCE 持久磁盘用于自动供应的持久卷。
首先,部署一个 Kubernetes 集群:
$ gcloud beta container clusters create "kluster" \
--project $(gcloud config get-value project) \
--zone $(gcloud config get-value compute/zone) \
--cluster-version "1.15.12-gke.2" \
--machine-type "g1-small" \
--image-type "COS" \
--disk-type "pd-standard" \
--disk-size "30" \
--num-nodes "1"
[...]
$ gcloud container clusters get-credentials kluster \
--zone $(gcloud config get-value compute/zone) \
--project $(gcloud config get-value project)
现在创建一个 PVC:
# auto-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: disk-rwo-10g
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
$ kubectl apply -f auto-pvc.yaml
persistentvolumeclaim/disk-rwo-10g created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
disk-rwo-10g Bound pvc-[...] 10Gi RWO standard 3s
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM \
STORAGECLASS REASON AGE
pvc-[...] 10Gi RWO Delete Bound default/disk-rwo-10g\
standard 3s
您可以看到,一旦创建了一个索赔,就创建了一个 PV 资源。
现在,您可以使用以下声明来部署 Pod:
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: box
name: box
spec:
serviceName: box
selector:
matchLabels:
app: box
template:
metadata:
labels:
app: box
spec:
containers:
- image: busybox
name: box
volumeMounts:
- name: data
mountPath: /mnt
command:
- sh
- -c
- "sleep $((10**10))"
volumes:
- name: data
persistentVolumeClaim:
claimName: disk-rwo-10g
readOnly: false
当您不再需要永久卷时,可以通过删除声明来释放它:
$ kubectl delete pvc disk-rwo-10g
persistentvolumeclaim "disk-rwo-10g" deleted
$ kubectl get pv
No resources found in default namespace.
清除
删除以前部署的 Kubernetes 集群:
$ gcloud beta container clusters delete "kluster" \
--project $(gcloud config get-value project) \
--zone $(gcloud config get-value compute/zone)
十三、多容器 Pod 设计模式
Pod 是 Kubernetes 集群中可部署的最小部分。一个 Pod 可以包含一个或多个容器。
当一个 Pod 包含几个容器时,这些容器共享网络和存储资源。
具有几个容器的单个容器必须小心使用。只有当容器紧密耦合,并且一个容器是主要容器,其他容器帮助第一个容器时,才应该使用这种模式。
Kubernetes 社区使用多容器 pod 描述了以下设计模式。
初始化容器
当您想要在主容器运行之前初始化一些资源或者等待一个特定的外部状态时,可以使用 Init 容器模式。
Pod 资源的规范包含一个initContainers字段。该字段包含容器定义的数组。
通过该字段定义的 Init 容器在主容器之前按顺序运行。如果 init 容器失败,Pod 会立即失败。
初始化存储器
在第一个例子中,一个 Init 容器从 Google Cloud Bucket 加载文件,并将这些文件存储在一个 volatile 卷中。主容器 nginx 服务器挂载相同的易失性卷,并为这些文件提供服务:
apiVersion: apps/v1
kind: Deployment
metadata:
name: website
spec:
selector:
matchLabels:
app: website
template:
metadata:
labels:
app: website
spec:
volumes:
- name: static-files
emptyDir:
sizeLimit: 20Mi
initContainers:
- name: copy-static-files
image: gcr.io/cloud-builders/gcloud
command:
- "bash"
- "-c"
- "gsutil cp -R $(SOURCE)/* /mnt/"
env:
- name: SOURCE
value: gs://my-gcp-project/my-bucket
volumeMounts:
- mountPath: /mnt
name: static-files
readOnly: false
containers:
- name: website
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: static-files
readOnly: true
等待其他服务的可用性
在第二个示例中,Init 容器测试后端服务是否可用,并在服务可用时成功终止,因此主容器可以认为后端在启动时正在运行:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
initContainers:
- name: wait-backend
image: busybox
args:
- sh
- -c
- "until wget http://backend/; do sleep 1; done"
containers:
- name: api
image: api
ports:
- containerPort: 80
边车容器
边车容器是与主容器在同一个 Pod 中运行的附加容器,用于在主容器执行期间辅助主容器。
它的目的不是由设计模式定义的;例如,它可以在将入站流量发送到主容器之前拦截入站流量以对其进行调整,或者它可以拦截出站流量以对其进行调整以适应接收方,或者它可以聚合主容器创建的日志以公开它们,这只是举几个例子。
下面的两个设计模式,适配器和大使,是 sidecar 容器模式的一个专门化。
适配器容器
适配器容器是一个边车容器,其目的是拦截到 Pod 的入站流量,并使其适应主容器所期望的协议。
使用适配器容器,您可以在不同的环境中重用主容器,而无需更改其接口。适配器容器将负责将调用者使用的协议转换为主容器使用的协议。
大使容器
大使容器是一个边车容器,其目的是代表主容器调用外部服务。
使用大使容器,您可以在不同的环境中重用主容器,而不需要让它知道与外部服务通信所需的所有协议,这些协议根据环境的不同可能具有不同的性质。
例如,考虑主容器需要从外部数据库访问数据。在开发环境中,您希望数据由内存中的数据库提供,类似于 SQLite。但是在生产环境中,数据必须由外部 PostgreSQL 数据库提供。由于大使容器模式,主容器将向其大使容器查询一些数据集,大使将根据环境进行专门化:部署在开发环境中的大使将从 SQLite 数据库查询数据,而部署在生产环境中的大使将从 PostgreSQL 数据库查询数据。
十四、可观察性
使用 Kubernetes 时,可观察性至关重要。Kubernetes 是由大量的移动部件组成的,你需要工具来了解这些不同部件之间发生了什么。
Kubernetes 级别的调试
首先,您必须注意到 Kubernetes 资源有两种类型:托管和非托管资源。托管资源是可识别的,因为它的定义包含一个spec部分和一个status部分。控制者负责管理这些资源;他们将阅读spec部分,将尽最大努力改变世界以反映这些规范,然后将在status部分报告状态。
其他资源,如ConfigMap、Secret、Volume、ServiceAccount、Role、RoleBinding等,都是非托管资源,它们的用途是包含系统其他元素使用的特定数据。
当您需要理解为什么您的应用不工作时,kubectl describe命令是首先使用的工具之一。使用这个命令,您可以观察所管理的 Kubernetes 资源的状态。
这个命令的输出通常从资源的元数据开始:名称、命名空间、标签和注释。元数据之后是规范和资源状态的混合,以人类可读的方式,通常以表格的形式。最后显示了附加到该资源的事件。
一个Event是 Kubernetes 资源,由控制器和其他 Kubernetes 元素用来记录信息。事件属于正常或警告类型,指示事件发生的时间、采取的操作、采取操作的原因、哪个控制器采取了操作并发出了事件、事件涉及的资源以及事件的可读描述。
阅读这些事件,您通常会发现集群中某些问题的根本原因,包括以下内容:
-
Pod 不可调度,因为节点没有足够的可用资源。
-
无法提取映像。
-
卷、配置映射或密码不可用。
-
容器的就绪或活动探测失败。
$ kubectl describe pods nginx-d9bc977d8-h66wf
Name: nginx-d9bc977d8-h66wf
Namespace: default
[...]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/ngi\
nx-d9bc977d8-h66wf to worker-0
Normal Pulling 10s kubelet, worker-0 Pulling image "nginx"
Normal Pulled 9s kubelet, worker-0 Successfully pulled image "nginx"
Normal Created 9s kubelet, worker-0 Created container nginx
Normal Started 9s kubelet, worker-0 Started container nginx
您还可以使用命令kubectl get events观察集群中发生的所有事件。您可以添加-w选项,使命令等待新事件(您可以使用 Ctrl-C 终止命令):
$ kubectl get events -w
LAST SEEN TYPE REASON OBJECT MESSAGE
<unknown> Normal Scheduled pod/nginx-d Successfully assigned default/nginx-d9bc977d8-h66wf to worker-0
4m20s Normal Pulling pod/nginx-d Pulling image "nginx"
4m19s Normal Pulled pod/nginx-d Successfully pulled image "nginx"
4m19s Normal Created pod/nginx-d Created container nginx
4m19s Normal Started pod/nginx-d Started container nginx
4m21s Normal Killing pod/nginx-d Stopping container nginx
4m21s Normal SuccessfulCreate rs/nginx-d Created pod: nginx-d9bc977d8-h66wf
在容器内部调试
可以使用命令kubectl exec从容器内部执行命令。这意味着容器包含调试工具。
下面的例子列出了一个nginx Pod 的单个容器的给定目录中的文件:
$ kubectl exec nginx-d9bc977d8-h66wf -- ls /usr/share/nginx/html
index.html
如果您想在容器内部运行一个交互式命令(一个 shell 或另一个与 TTY 交互的命令),您将需要指定--stdin和--tty标志(您可以将它们缩写为-it):
$ kubectl exec -it nginx-d9bc977d8-h66wf -- bash
# ls /usr/share/nginx/html
index.html
[ other commands ]
# exit
调试服务
Kubernetes 的一个重要职责就是暴露使用Service和Ingress资源的 pod。
在本例中,您将运行两个nginx副本,并通过服务使它们可用:
$ kubectl create deployment nginx --image=nginx
deployment.apps/nginx created
$ kubectl scale deployment/nginx --replicas=2
deployment.apps/nginx scaled
$ kubectl expose deployment nginx --port=80
service/nginx exposed
如果一切顺利,服务应该公开这两个 pod(在这种情况下,对服务的请求在这两个 pod 之间进行负载均衡)。为了确保服务公开两个后端,您可以检查Endpoints资源:
$ kubectl get endpoints nginx
nginx 10.244.43.43:80,10.244.43.45:80 6s
该信息也可通过kubectl describe service命令获得:
$ kubectl describe service nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP: 10.103.237.61
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.43.43:80,10.244.43.45:80
Session Affinity: None
Events: <none>
这些命令显示该服务公开了两个 IP 地址。您可以使用命令验证这些地址是否与两个nginxpod 的地址相匹配(注意-o wide选项将显示更多信息,包括 pod 的 IP 地址)
$ kubectl get pods -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP [...]
nginx-f89759699-b7lsq 1/1 Running 0 1m16s 10.244.43.45 [...]
nginx-f89759699-l4f7c 1/1 Running 0 1m38s 10.244.43.43 [...]
记录
容器必须将日志输出到标准输出(stdout)或标准错误(stderr)流中,这样日志才能在 Kubernetes 基础设施中可用。
您可以使用kubectl logs命令显示某个特定 pod 的日志:
$ kubectl logs nginx
127.0.0.1 - - [01/Feb/2020:17:05:58 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.58.0\
" "-"
或者一组吊舱,由它们的标签选择(使用--prefix来区分吊舱):
$ kubectl logs -l app=nginx --prefix
[pod/nginx-86c57db685-cqm6c/nginx] 127.0.0.1 - - [01/Feb/2020:17:45:14 +0000] "GET /\
HTTP/1.1" 200 612 "-" "curl/7.58.0" "-"
[pod/nginx-86c57db685-5f73p/nginx] 127.0.0.1 - - [01/Feb/2020:17:45:17 +0000] "GET /\
HTTP/1.1" 200 612 "-" "curl/7.58.0" "-"
logs命令的其他可用标志如下:
-
--follow(简称-f)允许跟随潮流;使用 Ctrl-C 停止。 -
--previous(简称为-p)允许查看以前的容器的日志,当一个容器崩溃并且你想查看导致它崩溃的错误时,这很有用。 -
--container=name(简称-c name)显示特定容器的日志,--all-containers显示所有容器的日志。 -
--timestamps在行首显示日志的时间戳。
节点级别的日志记录
默认情况下,Pod 的日志存储在运行 Pod 的节点中。当您使用kubeadm部署集群时,可以在/var/log/pods目录中找到日志。集群管理员负责为这些日志安装一些日志循环。
使用节点日志记录代理进行集群级日志记录
在节点级别收集的日志可以导出到外部日志后端(syslog、StackDriver、Elastic等)。).fluentd ( www.fluentd.org )是一个日志代理,您可以将它部署在每个节点上,收集日志并将它们发送到日志后端。您可以在节点系统上使用DaemonSet或专用服务来部署fluentd。
使用 Sidecar 将日志重定向到标准输出
如果您的应用不能将日志输出到stdout或stderr,而只能输出到文件,您可以运行一个 sidecar 容器来读取这些日志文件,并将它们传输到自己的stdout。通过这种方式,日志在节点级别变得可用,并且可以使用kubectl logs进行探索,并使用日志记录代理进行导出。
监视
您可以使用kubectl top命令来监控集群和 pod 的节点。
要使用这个命令,首先必须在集群上安装度量服务器。您可以按照第七章中的说明进行安装。
然后,您可以运行以下命令来获取每个节点和单元的 CPU 和内存使用情况:
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
controller 180m 18% 1391Mi 38%
worker-0 83m 8% 1294Mi 36%
$ kubectl top pods
NAME CPU(cores) MEMORY(bytes)
nginx-d9bc977d8-2bf2g 0m 2Mi
度量服务器和kubectl top命令只为您提供一组有限的短期度量。建议安装完整的监控解决方案。
用 Prometheus 监控
Prometheus (prometheus.io)是 CNCF 推出的一套完整的监控和警报解决方案。Prometheus 系统主要由
-
抓取和存储时间序列数据的服务器
-
用于检测应用代码的客户端库
-
用于导出主机指标的节点导出器
-
处理警报的警报管理器
服务器是一个有状态的应用,它会定期提取节点和应用组件,以收集包含指标的时间序列数据,并将其保存到数据库中。
在 Prometheus 提供的客户端库的帮助下,应用开发人员负责将指标暴露给 monitor。指标通常在/metrics端点公开。
集群管理员负责在集群节点上部署节点导出器,这些导出器将自动公开主机的指标。
警报管理器用于根据一个或多个收集的指标创建和配置警报。
Grafana 仪表板( www.grafana.com )是 Prometheus 的伴侣,它将帮助您以图形方式展示和探索您的指标。
十五、升级集群
升级 Kubernetes 集群分两个阶段完成。首先升级控制面板节点,然后升级工作节点。可以升级到下一个次要版本或同一次要版本的任何其他下一个修补程序版本。例如,当您的群集使用版本 1.18.6 时,您可以升级到 1.18.p(其中 p >= 7)和 1.19.x(无论 x 的值如何),但不能升级到 1.20.x。
由于控制面板和工作器在主机系统上运行,您还需要升级这些系统。您将看到如何准备集群,以便在不中断应用的情况下进行这些操作。
最后,您将了解如何备份和恢复集群证书和数据。
升级控制器
首先,安装想要的 kubeadm 版本:
$ gcloud compute ssh controller
Welcome to controller
$ sudo apt update && apt-cache policy kubeadm
$ sudo apt update && \
sudo apt-get install \
-y --allow-change-held-packages \
kubeadm=1.19.0-00
$ sudo apt-mark hold kubeadm
$ kubeadm version -o short
v1.19.0
排空控制器节点:
$ kubectl drain controller --ignore-daemonsets \
--delete-local-data
node/controller evicted
检查可能的升级计划:
$ sudo kubeadm upgrade plan
[...]
Components that must be upgraded manually after you have upgraded the control plane \
with 'kubeadm upgrade apply':
COMPONENT CURRENT AVAILABLE
Kubelet 3 x v1.18.6 v1.19.0
Upgrade to the latest stable version
:
COMPONENT CURRENT AVAILABLE
API Server v1.18.6 v1.19.0
Controller Manager v1.18.6 v1.19.0
Scheduler v1.18.6 v1.19.0
Kube Proxy v1.18.6 v1.19.0
CoreDNS 1.6.7 1.6.7
Etcd 3.4.3 3.4.3-0
You can now apply the upgrade by executing the following command:
kubeadm upgrade apply v1.19.0
显示了几种可能性:一种是升级到系列的最新版本,另一种是升级到使用了kubeadm版本的最新稳定版本。
在前面的屏幕中,仅显示升级到最新稳定版本的计划。
让我们开始升级:
$ sudo kubeadm upgrade apply v1.19.0
[...]
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.19.0". Enjoy!
再次使节点可调度:
$ kubectl uncordon controller
node/controller uncordoned
升级控制器上的kubelet和kubectl:
$ sudo apt-get update && \
sudo apt-get install \
-y --allow-change-held-packages \
kubelet=1.19.0-00 kubectl=1.19.0-00
$ sudo apt-mark hold kubelet kubectl
此时,控制器节点应该显示最新版本:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controller Ready master 13d v1.19.0
worker-0 Ready <none> 13d v1.18.6
worker-1 Ready <none> 13d v1.18.6
提升工人
对每个工人重复这些步骤。
首先,从您的计算机中清空节点:
$ kubectl drain worker-0 --ignore-daemonsets
node/worker-0 cordoned
node/worker-0 drained
然后,连接到节点并安装所需版本的 kubeadm:
$ gcloud compute ssh worker-0
Welcome to worker-0
$ sudo apt update && \
sudo apt-get install \
-y --allow-change-held-packages \
kubeadm=1.19.0-00
$ sudo apt-mark hold kubeadm
$ kubeadm version -o short
v1.19.0
升级节点:
$ sudo kubeadm upgrade node
[...]
[upgrade] The configuration for this node was successfully updated!
升级节点上的kubelet和kubectl:
$ sudo apt-get update && \
sudo apt-get install \
-y --allow-change-held-packages \
kubelet=1.19.0-00 kubectl=1.19.0-00
$ sudo apt-mark hold kubelet kubectl
从您的计算机上,再次使节点可调度:
$ kubectl uncordon worker-0
node/worker-0 uncordoned
升级所有工作节点后,您应该在群集的每个节点上获得最新版本:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controller Ready master 13d v1.19.0
worker-0 Ready <none> 13d v1.19.0
worker-1 Ready <none> 13d v1.19.0
升级操作系统
如果您需要重新启动集群节点的主机进行维护(例如,进行内核或硬件升级),您首先需要清空节点:
$ kubectl drain $NODENAME --ignore-daemonsets
node/nodename drained
排空节点将产生两种效果:
-
从该节点中逐出所有单元,由副本集控制的所有单元将在另一个节点上重新安排。
-
使此节点不可计划,以便在维护期间不会在此节点上计划新的 Pod。
现在,您可以安全地对操作系统或硬件进行维护操作。
维护结束后,您可以取消对节点的授权,使节点再次可调度:
$ kubectl uncordon $NODENAME
node/nodename uncordoned
备份群集
集群安装后备份文件/etc/kubernetes/pki/ca.crt和/etc/kubernetes/pki/ca.key。
使用以下命令定期创建etcd数据库的快照
$ kubectl exec -it -n kube-system etcd-controller \
sh -- -c "ETCDCTL_API=3 etcdctl snapshot save snapshot.db \
--cacert /etc/kubernetes/pki/etcd/server.crt \
--cert /etc/kubernetes/pki/etcd/ca.crt \
--key /etc/kubernetes/pki/etcd/ca.key"
Snapshot saved at snapshot.db
$ kubectl cp -n kube-system etcd-controller:snapshot.db snapshot.db
恢复集群
按照第一章中的步骤重新安装控制器,并在运行kubeadm init前停止。
将 ca.crt 和 ca.key 复制到/etc/kubernetes/pki 中,恢复良好的权限:
# ls -l /etc/kubernetes/pki/
total 8
-rw-r--r-- 1 root root 1025 Feb 1 10:43 ca.crt
-rw------- 1 root root 1675 Feb 1 10:43 ca.key
将 snapshot.db 放在/mnt 中,然后运行:
$ docker run --rm \
-v '/mnt:/backup' \
-v '/var/lib/etcd:/var/lib/etcd' \
--env ETCDCTL_API=3 \
'k8s.gcr.io/etcd-amd64:3.1.12' \
/bin/sh -c \
"etcdctl snapshot restore /backup/snapshot.db ; mv /default.etcd/member/ /var/lib/etcd/"
再次安装控制面板:
$ gcloud config set compute/zone us-west1-c # or your selected zone
Updated property [compute/zone].
$ KUBERNETES_PUBLIC_ADDRESS=$(gcloud compute instances describe controller \
--zone $(gcloud config get-value compute/zone) \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')
$ sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--ignore-preflight-errors=NumCPU \
--apiserver-cert-extra-sans=$KUBERNETES_PUBLIC_ADDRESS \
--ignore-preflight-errors=DirAvailable--var-lib-etcd
十六、命令行工具
库布特雷
kubectl是用于 Kubernetes 集群的命令行工具。您可以使用它来创建应用资源和集群资源,与正在运行的容器进行交互,以及管理集群。
kubectl completion
-
该命令输出要执行的 shell 代码,以使自动完成与
kubectl命令一起工作。它最简单的用法是" source 它的输出,这将使自动完成对当前 shell 会话可用: -
或者,如果使用
zsh外壳:
$ source <(kubectl completion bash)
- 运行
kubectl completion --help获取如何永久安装完井的说明。
$ source <(kubectl completion zsh)
管理 kubeconfig 文件
和其他 Kubernetes 程序在一个 kubeconfig 文件中获得连接到 Kubernetes 集群所需的凭证。默认情况下,在$HOME/.kube/config搜索该文件。可以通过使用--kubeconfig标志或定义KUBECONFIG环境变量来使用另一个文件。
KUBECONFIG的值是一个冒号分隔的配置文件路径列表,例如:
/etc/kubernetes/config:$HOME/.kube/config
当定义几个路径时,kubectl 将合并这些不同的文件(在内存中)并将结果作为一个单独的配置文件使用。
kubeconfig 文件由以下部分组成
-
集群信息列表(证书、API URL)
-
用户凭证列表
-
上下文的列表,每个上下文引用现有的集群、用户和默认名称空间
-
一个当前上下文,指向上下文列表中的特定上下文
kubectl config
-
get-clusters、set-cluster、delete-cluster编辑集群信息。 -
set-credentials编辑用户凭证。 -
get-contexts、set-context、delete-context、rename-context编辑上下文。 -
set和unset是编辑 kubeconfig 文件任何字段的通用子命令。 -
current-context获取当前上下文的名称。 -
use-context设置当前上下文的名称。 -
view查看命令可见的 kubeconfig 信息(取决于--kubeconfig标志和KUBECONFIG环境变量),特别有用的是查看合并几个配置文件的结果或用--minify获取当前上下文所需的信息。 -
此命令提供子命令来编辑集群、用户和上下文的列表,以及切换当前上下文:
通用命令
kubectl apply
- 大多数 kubectl 命令在命令式模式下使用。相反,
apply命令用于声明性模式,用于应用 YAML/JSON 模板,即用于根据资源在 YAML 或 JSON 文件中的定义向集群 API 声明资源。
kubectl get
- 用
-o yaml标志获取资源列表或它们的完整定义。
kubectl delete
- 删除一个或一组资源。
kubectl edit
- 在您的首选编辑器中交互编辑资源定义(您可以更改
EDITOR环境变量来更改它)。
kubectl create namespace
- 创建新的命名空间。
创建应用资源
创建和参数化工作负载
kubectl run
-
部署新的工作负载。这些命令的大多数形式都不推荐使用。
-
不推荐使用的方法是使用单个容器部署 Pod:
-
标志可用于设置容器的特定字段:映像拉取策略、环境变量、端口、资源请求和限制。
-
dry-run=client -o yaml有助于输出 Pod 的声明形式,稍后可以使用kubectl apply对其进行编辑和应用。
kubectl run podname --image=imagename
kubectl create deployment
-
以最简单的形式创建部署:
kubectl create deployment nginx --image=nginx。没有可用于参数化部署的标志。 -
dry-run=client -o yaml对于输出部署的声明形式很有用,可以在以后使用kubectl apply进行编辑和应用。 -
set和scale命令对于参数化部署非常有用。
kubectl create cronjob
-
给定映像和时间表,创建一个 cronjob。您还可以指定要传递给容器的命令和参数,这在使用像
busybox这样的通用映像时很有用: -
使用
man 5 crontab获取schedule标志的规格。 -
dry-run=client -o yaml对于输出 cronjob 的声明形式很有用,以后可以用kubectl apply编辑和应用它。
kubectl create cronjob pinghost --image=busybox \
--schedule="10 * * * *" \
-- sh -c 'ping -c 1 myhost'
kubectl create job
执行一项工作。有两种形式可用:
-
给定一个映像和一个可选命令,创建一个作业(与
create cronjob命令非常相似,但只针对一个作业): -
绕过 cronjob 的计划,从现有的 cronjob 创建作业:
kubectl create job pinghost --image=busybox \
-- sh -c 'ping -c 1 myhost'
kubectl create job pinghost --from=cronjob/pinghost
kubectl scale
- 将适用的资源(部署、副本集、状态集)扩展到给定数量的副本。通过类型/名称、标签选择器或文件来选择要缩放的资源。可以指定一些前提条件:当前副本的数量和资源的版本。
kubectl autoscale
- 为适用的资源(部署、复制集、状态集)创建自动缩放器。通过类型/名称或文件来选择要自动缩放的资源。重要的标志是
min、max和cpu-percent,以指示副本数量的限制和资源将被扩展的平均 CPU 百分比。
kubectl rollout
- 用于滚动更新和回滚适用资源(Deployment、DaemonSet、StatefulSet)的命令集。详见第五章“更新和回滚”一节。
配置工作负载
kubectl create configmap
创建一个ConfigMap资源,获取键/值对:
-
从环境样式文件(
--from-env-file):键和值将从文件中提取 -
From files (
--from-file):对于指定目录下的指定文件或所有文件,键将是文件名或指定键,值将是文件的内容:
# .env
key1=value1
key2=value2
- 从文字值(
--from-literal):
--from-file=dir --from-file=file1.txt --from-file=key=file2.txt
--from-literal=key1=value1 --from-literal=key2=value2
kubectl create secret
创建一个Secret资源。有三种形式可用:
-
create secret generic:非常类似于create configmap,从 env 样式的文件、文件和文字值中添加键/值对。 -
create secret docker-registry:创建一个用作imagePullSecrets的秘密(参见第十一章中的“使用私有 Docker 注册表”一节)。有两种形式可用,来自 Docker 配置文件(--from-file或通过指定注册表信息(--docker-server、--docker-username、--docker-password和--docker-email)。 -
create secret tls:给定存储在文件中的公钥/私钥对(--cert和--key),创建 TLS 秘密。
暴露 POD
kubectl create service
-
创建一个
Service资源。有四种形式可用,每种服务类型一种:-
create service clusterip创建一个集群 IP 服务,用--tcp标志:kubectl create service clusterip my-svc \ --tcp=8084:80指定端口
-
-
将添加一个选择器
app=my-svc,以匹配带有此标签的 pod。您可能需要对其进行编辑,以匹配所需窗格的标签。 -
可以使用
--clusterip=x.y.z.a为服务指定您自己的 IP,或者使用--clusterip=None创建一个无头服务。-
create service nodeport创建节点端口服务。除了clusterip变量之外,还可以用--node-port标志:kubectl create service nodeport my-svc \ --tcp=8084:80 --node-port=30080指定一个节点端口
-
create service loadbalancer创建一个负载均衡器服务,用--tcp标志:kubectl create service loadbalancer my-svc \ --tcp=8084:80指定集群 IP 端口
-
-
请注意,不可能为底层节点端口指定端口。
-
create service externalname创建外部名称服务:kubectl create service externalname my-svc \ --external-name=an-svc.domain.com
-
kubectl expose
- 公开一个适用的资源(服务、Pod、副本集、部署),创建一个新的
Service资源。
批准
kubectl create role
kubectl create clusterrole
kubectl create rolebinding
kubectl create clusterrolebinding
kubectl create serviceaccount
kubectl auth
所有这些命令都用于创建和验证授权。详见第十一章“授权”一节。
注释和标签
kubectl annotate
- 将元数据附加到任何类型的资源上,这些资源不是由 Kubernetes 使用,而是由工具或系统扩展使用。
kubectl label
- 编辑任何种类资源的标签,用作选择器。
与应用交互
kubectl attach
- 附加到已在现有容器中运行的进程,并显示其输出。
kubectl exec
- 在现有容器中执行新命令。
kubectl cp
- 将现有容器中的文件复制到本地计算机中。
源文件和目标文件的语法如下
-
/path/to/file对于本地计算机中的文件 -
pod:/path/to/file为pod的容器中的文件 -
namespace/pod:/path/to/file对于namespace的pod容器中的文件
-c container标志指示 Pod 的哪个特定容器是目标。
kubectl describe
- 显示资源的详细信息。
kubectl logs
- 打印 Pod 中容器的日志。
kubectl port-forward
-
将本地端口转发到 Pod:
-
type可以是一个 Pod,一个管理 Pod 的工作负载(复制集、部署等。),或者公开 Pod 的服务。 -
--address ip标志指定监听的本地地址,默认为127.0.0.1。
kubectl port-forward type/name local-port:remote-port
kubectl proxy
- 在本地机器上运行 Kubernetes API 服务器的代理。
kubectl top
- 显示节点或单元的资源(CPU/内存/存储)使用情况。
管理集群
kubectl taint
- 编辑节点污点。
kubectl uncordon
- 将节点标记为可调度。
kubectl cordon
- 将节点标记为不可调度。
kubectl drain
- 通过将节点标记为不可调度并从该节点中驱逐单元来准备维护节点。
kubectl certificate
- 批准/拒绝证书签名请求。
kubectl cluster-info
- 显示主服务器和服务的地址。
dump子命令转储所有适合调试和诊断集群问题的信息。
kubectl version
- 显示客户端和服务器的版本。
获取文档
kubectl api-versions
- 显示服务器上支持的 API 版本。
kubectl api-resources
- 显示服务器上支持的 API 资源。
kubectl explain
- 资源的列表和文档字段。
kubectl options
- 显示所有命令支持的通用标志。
kubectl help
- 显示关于 kubectl 的内嵌帮助。
Helm
Helm 是 Kubernetes 的包装经理。
Helm charts 是帮助您定义、安装和升级 Kubernetes 应用的包,这些图表存储在 Helm 存储库中。
您可以使用helm命令来搜索、安装、升级、回滚和卸载图表。
在你的开发机器上安装头盔
Helm 由单个二进制组成,helm。您可以通过手动下载这个二进制文件或者使用系统的软件包管理器来安装它。以下是针对不同操作系统下载和安装二进制文件的说明。
您可以在 https://github.com/helm/helm/releases 获得最新发布的版本。
Linux 操作系统
$ curl -LO https://get.helm.sh/helm-v3.3.0-linux-amd64.tar.gz
$ tar zxvf helm-v3.3.0-linux-amd64.tar.gz
$ sudo mv ./linux-amd64/helm /usr/local/bin/helm
# Test it is working correctly
$ helm version --short
v3.3.0+g8a4aeec
苹果
$ curl -LO https://get.helm.sh/helm-v3.3.0-darwin-amd64.tar.gz
$ tar zxvf helm-v3.3.0-linux-amd64.tar.gz
$ sudo mv ./linux-amd64/helm /usr/local/bin/helm
# Test it is working correctly
$ helm version --short
v3.3.0+g8a4aeec
Windows 操作系统
$ url -LO https://get.helm.sh/helm-v3.3.0-windows-amd64.zip
# Unzip the downloaded file,
# Move the helm binary into your PATH,
$ helm version --short
v3.3.0+g8a4aeec
安装图表
图表存储在 Helm 存储库中。您可以使用 Helm Hub ( https://hub.helm.sh/ )来发现新的存储库。
helm repo命令帮助您管理您有权访问的存储库。安装后,您可以看到您无权访问任何存储库:
$ helm repo list
Error: no repositories to show
您可以使用命令helm repo add添加新的存储库,例如:
$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories
$ helm repo list
NAME URL
Bitnami https://charts.bitnami.com/bitnami
现在,您可以从这个存储库中安装图表:
$ helm install my-wp-install bitnami/wordpress
[...]
稍后,当新版本发布时,您可以使用helm upgrade命令升级您的安装:
$ helm upgrade my-wp-install bitnami/wordpress
Release "my-wp-install" has been upgraded. Happy Helming!
[...]
您可以使用helm history管理已部署修订的历史记录:
$ helm history my-wp-install
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Sat Aug 10 10:39:04 2020 superseded wordpress-9.4.2 .4.1 Install complete
2 Sat Aug 15 10:46:54 2020 deployed wordpress-9.4.3 5.4.2 Upgrade complete
如有必要,您可以回滚到以前的版本:
$ helm rollback my-wp-install 1
Rollback was a success! Happy Helming!
$ helm history my-wp-install
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Sat Aug 10 10:39:04 2020 superseded wordpress-9.4.2 5.4.1 Install complete
2 Sat Aug 15 10:46:54 2020 superseded wordpress-9.4.3 5.4.2 Upgrade complete
3 Sat Aug 15 10:50:59 2020 deployed wordpress-9.4.2 5.4.1 Rollback to 1
您可以使用helm uninstall命令卸载该软件包:
$ helm uninstall my-wp-install
release "my-wp-install" uninstalled
创建您自己的图表
命令helm create用于创建你自己的包。当您运行此命令时,它将使用默认应用为您创建一个新的目录结构:
$ helm create my-nginx
Creating my-nginx
$ tree my-nginx
my-nginx
├─ charts
├─ Chart.yaml
├─ templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└─ values.yaml
Chart.yaml文件包含关于包的元数据。
templates目录包含 Kubernetes 清单,这些清单将用于部署组成应用的不同资源(部署、服务、入口、服务帐户和水平 Pod 自动缩放器)。如果您查看其中一个文件,可以看到这些清单是模板化的。
values.yaml文件包含用于个性化最终清单的值,最终清单将由模板创建。
Helm 使用 Go 模板引擎,你可以在 https://helm.sh/docs/chart_template_guide/ 找到相关语言的所有细节。
您可以随意删除templates目录和values.yaml中的文件,创建自己的模板和值文件,或者如果您的应用与默认应用相似,可以编辑它们。
当您编辑完模板和值后,您可以使用命令helm package打包您的图表:
$ helm package my-nginx/
Successfully packaged chart and saved it to: my-nginx-0.1.0.tgz
该命令创建一个包含模板的归档文件。您可以通过 Helm 存储库分发该图表,也可以使用以下命令在本地使用它
$ helm install test-my-nginx ./my-nginx-0.1.0.tgz
[...]
定制
与基于模板的 Helm 不同,Kustomize 基于定制。
当您希望在有限的环境中重用清单来部署应用时,此工具非常有用。
为此,首先定义包含所有环境的公共库的 base 清单;然后,通过将补丁和变形器应用于这些基本清单,创建覆盖来专门化每个环境的基本清单。
开发和生产环境示例
对于这种典型情况,我们将创建一个包含两种环境通用的基本清单的base目录,然后创建分别用于部署到开发和生产环境的dev和production目录。
我们将创建以下文件结构:
$ tree .
.
├─ base
│ ├─ deployment.yaml
│ ├─ kustomization.yaml
│ └─ service.yaml
├─ dev
│ ├─ kustomization.yaml
│ ├─ namespace.yaml
│ └─ resources-patch.yaml
└─ production
├─ kustomization.yaml
├─ namespace.yaml
└─ resources-patch.yaml
// base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
// base/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: ClusterIP
// base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
从base目录中,您可以使用以下命令:
$ cd base
$ kubectl apply -k .
service/nginx created
deployment.apps/nginx created
# clean
$ kubectl delete -k .
当使用带有-k标志的kubectl apply时,该命令将在命令行中指定的目录(这里是当前目录)中查找一个kustomization.yaml文件,并应用它。
一个kustomization.yaml文件包含不同类型的指令:
-
指向清单文件的
resources指令 -
将指定补丁应用到这些清单的补丁指令
-
修饰符指令将以一些特定的方式修改这些清单。
因此,当kubectl 将包含kustomization.yaml文件的目录应用于时,它将首先加载资源,然后修补和修改它们,最后像kubectl apply -f命令一样应用结果清单。
回到我们的例子,kustomization.yaml文件只包含一个resources指令;这相当于应用了两个文件deployment.yaml和service.yaml。
我们现在将为开发环境创建一个覆盖。以下是 kustomization 和相关文件:
// dev/kustomization.yaml
bases:
- ../base/
resources:
- namespace.yaml
namespace: dev
commonLabels:
env: dev
patchesStrategicMerge:
- resources-patch.yaml
// dev/namespace.yaml
kind: Namespace
metadata:
name: dev
// dev/resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
resources:
requests:
cpu: 100m
memory: 100Mi
在kustomization.yaml文件中,bases指令表明这个覆盖图是基于../base目录中的 kustomization 文件。
然后包含一些新的资源:这里是创建dev名称空间的清单。
接下来,应用一些修饰符:namespace将修改每个资源清单,将metadata.namespace值设置为dev,commonLabels将为每个资源清单添加env: dev标签。
最后,将应用在resources-patch.yaml中找到的补丁,将资源请求添加到部署中。
其他一些修改器也是可用的,包括:
-
namePrefix和nameSuffix将用给定的前缀或后缀修改每个资源清单的metadata.name。 -
commonAnnotations将为每个资源清单添加注释。 -
images是旧/新映像名称的映射,它将更改清单中的容器映像名称的值。 -
replicas将改变每个相关资源清单的副本数量。
作为一个练习,您可以使用生产环境的特定修补程序和修改器来创建生产环境的覆盖。
滚动配置更新
两个指令configMapGenerator和secretGenerator用于从kustomization.yaml文件生成配置图和密码。
当这些生成器创建 ConfigMap 或 Secret 时,它们会在其名称中添加一个随机后缀,并且从容器到该资源的引用会被修改为包含该后缀。这样,当您更新生成的配置映射或机密时,会创建一个新的配置映射或机密,更有趣的是,引用此资源的部署会被更新以更改配置映射/机密名称。将通过滚动更新推出新的 pod 来应用这一新配置。
例如,以下是 nginx 容器的清单,该容器从配置映射中提供静态文件:
// deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: static-files
configMap:
name: static-files
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: static-files
<!-- index.html -->
version 1
// kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: static-files
files:
- index.html
让我们用 Kustomize 来应用这些清单:
$ kubectl apply -k .
configmap/static-files-ck2b6hc6th created
deployment.apps/nginx created
当我们在configMapGenerator指令中将其命名为static-files时,我们可以看到创建的配置图被命名为static-files-ck2b6hc6th。此外,我们可以看到部署的 PodTemplate 中引用的配置图也是static-files-ck2b6hc6th,而不是指定的static-files:
$ kubectl get deployments.apps nginx -o jsonpath='{.spec.template.spec.volumes[0].configMap.name}'
static-files-ck2b6hc6th
现在,编辑 index.html 文件,然后再次应用kustomization.yaml文件:
$ echo 'version 2' > index.html
$ kubectl apply -k .
configmap/static-files-bg9hg69mh9 created
deployment.apps/nginx configured
$ kubectl get deployments.apps nginx -o jsonpath='{.spec.template.spec.volumes[0].configMap.name}'
static-files-bg9hg69mh9
$ kubectl rollout history deployment nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 <none>
2 <none>
您可以看到一个名为static-files-bg9hg69mh9的新配置图已经创建,部署已经更新以引用这个新配置图,并且出现了部署的首次展示。
请注意,您可以通过使用disableNameSuffixHash选项来禁用此行为:
[...]
configMapGenerator:
- name: static-files
files:
- index.html
options:
disableNameSuffixHash: true