K8S CKA 学习笔记 (全)

K8S CKA学习笔记

一、环境

ssh登录node01(11.0.1.112),使用candidate帐号做题,密码为123,在node01上可以免密切换到其他节点,如 ssh master01 或 ssh node02,candidate帐号也可以免密切换到root,如 sudo -i

11.0.1.112
root/123456

切换到candidate帐号
su candidate

虚拟机和宿主机共享目录:

candidate@node01:~$ sudo -i
root@node01:~# find / -name macshare
/mnt/hgfs/macshare
root@node01:~# 
/mnt/hgfs/macshare

查看目录:

candidate@node01:~/k8sYaml$ pwd
/home/candidate/k8sYaml
candidate@node01:~/k8sYaml$ ls -l
total 32
-rw-r--r-- 1 candidate candidate 391 Feb 16 10:02 ingress-05.yaml
-rw-r--r-- 1 candidate candidate  52 Feb 16 10:02 namespace-dev.yaml
-rw-r--r-- 1 candidate candidate 333 Feb 16 10:02 networkpolicy.yaml
-rw-r--r-- 1 candidate candidate 176 Feb 16 10:02 nodeSelect-07.yaml
-rw-r--r-- 1 candidate candidate 210 Feb 16 10:02 pod-kucc-09.yaml
-rw-r--r-- 1 candidate candidate 233 Feb 16 10:02 pv-10.yaml
-rw-r--r-- 1 candidate candidate 195 Feb 16 10:02 pvc-11.yaml
-rw-r--r-- 1 candidate candidate 311 Feb 16 10:02 pvc-pod-11.yaml
candidate@node01:~/k8sYaml$ 

在vim中复制,可使用 :set paste 命令,要不然格式会乱。

二、学习路线

效率最高 学习路线
<1> 先从百度网盘下载所有资料。
<2> 根据《Windows模拟环境激活方法.pdf》或《苹果MAC模拟环境激活方法.pdf》操作。
<3> 观看”赠送的CKA题库讲解视频“,了解如何做题,学习kubectl -h的使用方法,不用死记硬背命令。
<4> 根据《CKA 1.24 题库(配合模拟环境使用).pdf》,以及在官网搜索关键字的方法,结合视频,在【模拟环境】里实操练习。
    所有题目做完后,恢复“CKA初始化”的快照,即可重复练习。
    使用模拟环境的时候,笔记本不能睡眠待机、断网,虚拟机不要关机、重启、挂起,这些都可能会导致认证失败(可能)。
    不用模拟环境的时候,回退关机状态的快照。
    如果你时间比较琐碎,比如做到第5题没时间继续做了,建议直接回退初始化快照。等下次有空了,则从初始化快照开机,接着继续做第6题。
<5> 参考《K8S 考试流程(报名、约考、注意).pdf》,报名、预约考试。
    考前可微信联系我,确认近期考试情况,以及是否变题等。

5、
注意:
<1> 每道题会提示你执行相应命令来切换k8s集群(kubectl config use-context xxx),如果没有切换集群,则会导致此题不得分。
<2> 有些题需要ssh到其他机器,或者sudo -i切换到root下,操作完成后,记得exit退回到初始的candidate@node-1或者candidate@node-1终端下。
<3> 考试前30分钟,点击“启动考试”。
<4> 新版考试平台,不能用自己的浏览器书签了。每道题给了几个链接可以参考,或者到K8S官网自己搜关键字。
<5> 考试需要身份证和第二有效证件。第二证件需要有你姓名的拼音,所以第二证件一般就是信用卡或者护照。
<6> yaml文件里的空格是有严格要求的,不要多,不要少,否则创建时会报错。vi编辑文件  先输入 :set paste 转换一下格式  再按 i ,再粘贴。
<7> 考试时,左边是考题,右边是Ubuntu桌面版。左边的考题里的蓝色字体,左键点一下选中,就自动复制了,就可以在右边shell终端里粘贴了。

6、
其他:
默认有两次考试机会,如果运气不好,赶上换新题了,为了给第二次补考留后路,最好将题目和yaml文件拍下来。
可以使用手机,从侧面拍摄电脑屏幕,因为摄像头仅能照到你的的半身照。
然后我可以针对新题,给你再免费做套模拟环境和解题答案。
以及可以根据提供新题的内容价值,提供折扣或者红包50至200不等!

三、真题练习

1、权限控制

命令补全:

source <(kubectl copletion bash)

创建集群角色:

# 查看命令帮助
kubectl create clusterrole -h

Usage:
  kubectl create clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename]
[--dry-run=server|client|none] [options]
# 创建集群
# --verb=create 设置权限,创建的权限
# --resource=deployments,statefulsets,daemonsets # 设置资源,必须加s
kubectl create clusterrole deployment-clusterrole --verb=create

查看config配置:

kubectl config view

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Context
为部署流水线创建一个新的 ClusterRole 并将其绑定到范围为特定的 namespace 的特定 ServiceAccount。

Task
创建一个名为 deployment-clusterrole 且仅允许创建以下资源类型的新 ClusterRole: 
Deployment
StatefulSet
DaemonSet

在现有的 namespace app-team1 中创建一个名为 cicd-token 的新 ServiceAccount。

限于 namespace app-team1 中,将新的 ClusterRole deployment-clusterrole 绑定到新的 ServiceAccount cicd-token。

答案:

# ①、创建clusterRole
kubectl create clusterrole deployment-clusterrole --verb=create --resource=deployments,statefulsets,daemonsets

# ②、创建 ServiceAccount
kubectl create serviceaccount cicd-token -n app-team1

# ③、绑定 
# 注意这里:clusterrolebinding -> rolebinding,之前搞错了,写成 clusterrolebinding 了,也不是 user,而是serviceaccount
# 题目中写了“限于 namespace app-team1 中”,则创建 rolebinding。没有写的话,则创 clusterrolebinding。
# kubectl create clusterrolebinding deployment-clusterrole-binding --clusterrole=deployment-clusterrole --user=cicd-token -n app-team1

Usage:
  kubectl create rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname]
[--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none] [options]

# 注意,--serviceaccount=app-team1:cicd-token 前边有加命名空间 app-team1:cicd-token
kubectl create rolebinding cicd-token-rolebinding --clusterrole=deployment-clusterrole --serviceaccount=app-team1:cicd-token -n app-team1

# ④、检查
kubectl describe rolebinding cicd-token-rolebinding -n app-team1

2、查看pod的CPU

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
通过 pod label name=cpu-loader,找到运行时占用大量 CPU 的 pod,
并将占用 CPU 最高的 pod 名称写入文件 /opt/KUTR000401/KUTR00401.txt(已存在)。

答案:

#Usage:
#  kubectl top pod [NAME | -l label] [options]

# ①、根据标签获取占用CPU的pod列表  kubectl top pod -l
kubectl top pod -l name=cpu-loader --sort-by=cpu -A

# ②、输出占用最大量的CPU到pod名称文件
echo "redis-test" > /opt/KUTR000401/KUTR00401.txt

3、配置网络策略NetworkPolicy(重)

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context hk8s

Task
在现有的 namespace my-app 中创建一个名为 allow-port-from-namespace 的新 NetworkPolicy。
确保新的 NetworkPolicy 允许 namespace echo 中的 Pods 连接到 namespace my-app 中的 Pods 的 9000 端口。
进一步确保新的 NetworkPolicy: 
不允许对没有在监听 端口 9000 的 Pods 的访问 
不允许非来自 namespace echo 中的 Pods 的访问

解析:

双重否定就是肯定,所以最后两句话的意思就是:

  • 仅允许端口为 9000 的 pod 方法。

  • 仅允许 echo 命名空间中的 pod 访问。

考点:

NetworkPolicy 的创建

答案

知识点:

Pod 有两种隔离: 出口的隔离和入口的隔离。它们涉及到可以建立哪些连接。 这里的“隔离”不是绝对的,而是意味着“有一些限制”。 另外的,“非隔离方向”意味着在所述方向上没有限制。这两种隔离(或不隔离)是独立声明的, 并且都与从一个 Pod 到另一个 Pod 的连接有关。

要允许从源 Pod 到目的 Pod 的连接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允许连接。 如果任何一方不允许连接,建立连接将会失败。

ingress规则(入口规则):允许外部的pod(三种形式)连接到该隔离pod;

egress规则(出口规则):允许隔离pod链接到 IP 下 5978 TCP 端口的连接;

参考链接:

https://kubernetes.io/zh-cn/docs/concepts/services-networking/network-policies/

策略文件名称:service/networking/networkpolicy.yaml

apiVersion: networking.k8s.io/v1

kind: NetworkPolicy

metadata:

  name: test-network-policy

  namespace: default

spec:

  podSelector:

    matchLabels:

      role: db

  policyTypes:

    - Ingress

    - Egress

  ingress:

    - from:

        - ipBlock:

            cidr: 172.17.0.0/16

            except:

              - 172.17.1.0/24

        - namespaceSelector:

            matchLabels:

              project: myproject

        - podSelector:

            matchLabels:

              role: frontend

      ports:

        - protocol: TCP

          port: 6379

  egress:

    - to:

        - ipBlock:

            cidr: 10.0.0.0/24

      ports:

        - protocol: TCP

          port: 5978

操作步骤:

# ①、查看所有ns的
kubectl get ns -A

# ②、查看所有ns的标签
kubectl get ns --show-labels

# ③、如果没有打标签,则手动打一个。如果有一个独特的标签label,则也可以直接使用。
kubectl label ns echo project=echo  # 给命名空间为echo的打一个标签为 project=echo

# ④、查看官网,拷贝demo进行修改,注意 :set paste 复制,防止yaml文件空格错误

vim networkpolicy-03.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-port-from-namespace-v2
  namespace: my-app
spec:
  podSelector:
    matchLabels: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              project: echo
      ports:
        - protocol: TCP
          port: 9000
# ⑤、执行
kubectl apply -f networkpolicy-03.yaml

# ⑥、检查
kubectl describe networkpolicy -n my-app

4、暴露服务 service

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
请重新配置现有的 deployment front-end 以及添加名为 http 的端口规范来公开现有容器 nginx 的端口 80/tcp。 创建一个名为 front-end-svc 的新 service,以公开容器端口 http。

配置此 service,以通过各个 Pod 所在的节点上的 NodePort 来公开他们。

考点:将现有的 deploy 暴露成 nodeport 的 service。

答案

参考链接

https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/deployment/

Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort 属性中引用这些名称。 例如,我们可以通过以下方式将 Service 的 targetPort 绑定到 Pod 端口:

示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc
操作
# ①、查看deployment信息,并记录SELECOTOR 的label标签,这里是 app=front-end
> kubectl get deployment front-end -o wide

# ②、更新deployment,添加端口信息
> kubectl edit deployment front-end

# 编辑内容:
    spec:
      containers:
      - image: vicuu/nginx:hello
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:   # 添加这行
        - containerPort: 80
          name: http
          protocol: TCP
        resources: {}

# ③、暴露对应端口
# Create a service for an nginx deployment, which serves on port 80 and connects to the containers on port 8000
# kubectl expose deployment nginx --port=80 --target-port=8000
# Usage:
# kubectl expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP|SCTP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type] [options]

kubectl expose deployment front-end --type=NodePort --port=80 --target-port=80 --name=front-end-svc

# ④、检查(正式环境有可能会出现这样的问题)
# 暴露服务后,检查service 标签是否正确,这个要与 deployment 的selector 标签一致的。
kubectl get deployment front-end -o wide 
kubectl get svc front-end-svc -o wide

# ⑤、如果暴露服务后,发现 service 的selector标签是空的 <none>,则需要进行编辑此 Service,手动添加标签。
kubectl edit svc front-end-svc 

# 加入标签:
spec:
  clusterIP: 10.108.131.235
  clusterIPs:
  - 10.108.131.235
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - nodePort: 30846
    port: 80
    protocol: TCP
    targetPort: 80
  selector:   # 添加
    app: front-end # 添加
  sessionAffinity: None
  type: NodePort

# ⑥、最后curl 检查
kubectl get svc -o wide

curl nodeIP:nodePort

candidate@node01:~/k8sYaml/v2$ curl 11.0.1.112:30846
Hello World ^_^

扩展:

以下是 type: NodePort 服务的一个示例清单,它指定了一个 NodePort 值(在本例中为 30007)。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
    - port: 80
      targetPort: 80
      # 可选字段
      # 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767)
      nodePort: 30007

5、创建Ingress(修改官网示例)

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
如下创建一个新的 nginx Ingress 资源:
名称: ping
Namespace: ing-internal
使用服务端口 5678 在路径 /hello 上公开服务 hello

可以使用以下命令检查服务 hello 的可用性,该命令应返回 hello: 
curl -kL <INTERNAL_IP>/hello

答案

Ingress.yaml

# 参考官方ingress demo
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80
操作:
# ①、复制官网ingress 示例,进行修改,注意题干,需要加入命名空间
# ②、编辑ingress.yaml 文件

# ③、部署
kubectl apply -f ingress-05.yaml 

# ④、验证
# kubectl get ingress -n ing-internal

candidate@node01:~/k8sYaml$ kubectl get ingress -n ing-internal
NAME      CLASS    HOSTS   ADDRESS   PORTS   AGE
ping-v2   <none>   *                 80      27s

# 通过以上打印,ingress获取不到IP,需要加上下边这行:
# kubernetes.io/ingress.class: "nginx"

# 添加之后再查看:
candidate@node01:~/k8sYaml$ kubectl get ingress -n ing-internal
NAME      CLASS    HOSTS   ADDRESS         PORTS   AGE
ping-v2   <none>   *       10.109.97.166   80      23m

# ⑤、测试
# curl -kL <INTERNAL_IP>/hello
curl -kL 10.109.97.166/hello

candidate@node01:~/k8sYaml$ curl -kL 10.109.97.166/hello
Hello World ^_^

Ingress-05.yaml文件

candidate@node01:~/k8sYaml/v2$ cat ingress-05.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ping-v2
  namespace: ing-internal
  annotations:
    kubernetes.io/ingress.class: "nginx"  # 【注意】1.23以上需要这行,否则ingress 服务获取不到IP
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /hello
        pathType: Prefix
        backend:
          service:
            name: hello
            port:
              number: 5678

6、扩容 deployment 副本数量

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
将 deployment presentation 扩展至 4 个 pods

答案

# 参考命令
# kubectl scale deployment -h
#  kubectl scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)[options]

# ①、查看deployment
kubectl get deployment

# ②、查看pod
kubectl get pods -l app=presentation
root@node01:~# kubectl get pods -l app=presentation
NAME                            READY   STATUS    RESTARTS        AGE
presentation-5f64bd874c-68z8p   1/1     Running   7 (7m45s ago)   218d

# ③、扩展
kubectl scale deployment presentation --replicas=4

# ④、查看扩展的pod
root@node01:~# kubectl get pods -l app=presentation
NAME                            READY   STATUS    RESTARTS        AGE
presentation-5f64bd874c-68z8p   1/1     Running   7 (9m14s ago)   218d
presentation-5f64bd874c-6v7sw   1/1     Running   0               6s
presentation-5f64bd874c-6vp8f   1/1     Running   0               6s
presentation-5f64bd874c-98w94   1/1     Running   0               6s
root@node01:~# 

candidate@node01:~$ kubectl get deployment presentation -o wide
NAME           READY   UP-TO-DATE   AVAILABLE   AGE    CONTAINERS   IMAGES              SELECTOR
presentation   4/4     4            4           218d   nginx        vicuu/nginx:hello   app=presentation
candidate@node01:~$ 

7、调度pod到指定节点

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s
Task
按如下要求调度一个 pod: 名称:nginx-kusc00401 Image:nginx
Node selector:disk=ssd

答案

参考

https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-pods-nodes/#add-a-label-to-a-node

操作
# ①、查看集群节点标签,确保node有这个labe
kubectl get nodes --show-labels | grep 'disk=ssd'

# ②、添加集群节点标签(如果该节点不存在该标签,则需要手动添加)
kubectl label nodes node01 disk=ssd

# ③、检查是否有这个pod,如果没有,则需要创建
kubectl get pods -A | grep  nginx-kusc00401

# ④、创建pod(拷贝官网案例,修改pod名称和镜像,删除多余的部分即可)
vim  pod-disk-ssd-07.yaml

# ⑤、部署pod
kubectl apply -f pod-disk-ssd-07.yaml

# ⑥、验证pod是否调度到 node1上
> kubectl get pod nginx-kusc00401 -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
nginx-kusc00401   1/1     Running   0          3m25s   10.244.196.178   node01   <none>           <none>

官网案例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

修改后的:

pod-disk-ssd-07.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-kusc00401
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disk: ssd

8、查看可用节点数量

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
检查有多少 nodes 已准备就绪(不包括被打上 Taint:NoSchedule 的节点),
并将数量写入 /opt/KUSC00402/kusc00402.txt

考点:检查节点角色标签,状态属性,污点属性的使用

答题

# ①、通过kubectl describe nodes 获取节点详细信息,然后再过滤出有污点的
candidate@node01:~/k8sYaml$ kubectl describe nodes | grep -i Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Taints:             <none>
Taints:             <none>
candidate@node01:~/k8sYaml$ 

# 可以看出master有 NoScheduler,2个node节点没有

# 参数说明: gerp 的 -i是忽略大小写,grep -v 是排除在外, grep -c 是统计查出来的条数
kubectl describe nodes | grep -i Taints | grep -vc NoSchedule

# ②、输出到文件
echo 2 > /opt/KUSC00402/kusc00402.txt

9、创建多容器的pod

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
按如下要求调度一个 Pod: 名称:kucc8
app containers: 2
container 名称/images:
nginx
consul

答案

# ①、参考官网示例,编写pod yaml示例
vim pod-kucc8-09.yaml

# ②、部署
kubectl apply -f pod-kucc8-09.yaml    

# ③、验证pod
kubectl get pod kucc8

candidate@node01:~/k8sYaml$ kubectl get pod kucc8
NAME    READY   STATUS    RESTARTS   AGE
kucc8   2/2     Running   0          33m

candidate@node01:~/k8sYaml$ vim pod-kucc8-09.yaml

apiVersion: v1
kind: Pod
metadata:
  name: kucc8
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  - name: consul
    image: consul
    imagePullPolicy: IfNotPresent

10、创建PV

考题

设置配置环境:
     [candidate@node-1] $ kubectl config use-context hk8s

Task
创建名为 app-config 的 persistent volume,容量为 1Gi,访问模式为 ReadWriteMany。 
volume 类型为 hostPath,位于 /srv/app-config

答案

# ①、解题思路,直接参考官网 存储卷 hostPath 配置示例,进行修改
# https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/
# https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

# ②、根据官网示例进行修改
vim pv-10.yaml 

# ③、创建
kubectl apply -f pv-10.yaml 

# ④、检查
candidate@node01:~/k8sYaml$ kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS      REASON   AGE
app-config   1Gi        RWX            Retain           Available                                      2m27s
pv01         10Mi       RWO            Retain           Available           csi-hostpath-sc            218d
candidate@node01:~/k8sYaml$ 

pv-10.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: app-config
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/srv/app-config"

扩展

卷(Volume)

Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像 的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。 任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。

hostPath 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 宿主上目录位置
      path: /data
      # 此字段为可选
      type: Directory
持久卷(PV)

持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。

11、创建PVC

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context ok8s

Task
创建一个新的 PersistentVolumeClaim: 名称: pv-volume
Class: csi-hostpath-sc
容量: 10Mi

创建一个新的 Pod,来将 PersistentVolumeClaim 作为 volume 进行挂载: 
名称:web-server
Image:nginx:1.16
挂载路径:/usr/share/nginx/html

配置新的 Pod,以对 volume 具有 ReadWriteOnce 权限。

最后,使用 kubectl edit 或 kubectl patch 将 PersistentVolumeClaim 的容量扩展为 70Mi,并记录此更改。

考点: pvc 的创建 class 属性的使用,--record 记录变更

答案

# ①、参考官方示例,创建 pvc
# https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

# 新建pvc-10.yaml文件
candidate@node01:~/k8sYaml$ kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS      REASON   AGE
app-config   1Gi        RWX            Retain           Available                                      2m27s
pv01         10Mi       RWO            Retain           Available           csi-hostpath-sc            218d
candidate@node01:~/k8sYaml$ vim pvc-11.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pv-volume-claim
spec:
  # 这个PV 官方已经建好了,可参考上述PV打印,访问模式为 RWO,STORAGECLASS 为 csi-hostpath-sc,所以这里不需要自己再建PV
  storageClassName: csi-hostpath-sc 
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
       storage: 10Mi

kubectl apply -f pvc-11.yaml

# ②、检查pvc
candidate@node01:~/k8sYaml$ kubectl get pvc
NAME              STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS      AGE
pv-volume-claim   Bound    pv01     10Mi       RWO            csi-hostpath-sc   5m12s

# ③、创建pod,关联pvc
vim pvc-pod-11.yaml

# ④、创建
kubectl apply -f pvc-pod-11.yaml

# ⑤、检查pod
candidate@node01:~/k8sYaml$ kubectl get pod web-server
NAME         READY   STATUS    RESTARTS   AGE
web-server   1/1     Running   0          10m

# ⑥、更改pvc大小,并记录过程。
# 模拟环境是 nfs 存储,操作时,会有报错忽略即可。考试时用的动态存储,不会报错的。
kubectl edit pvc pv-volume-claim --record

pvc-11.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pv-volume-claim
spec:
  storageClassName: csi-hostpath-sc
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
       storage: 10Mi

pvc-pod-11.yaml

apiVersion: v1
kind: Pod
metadata: 
  name: web-server
spec:
  volumes:
    - name: pv-storage
      persistentVolumeClaim:
        claimName: pv-volume-claim
  containers:
    - name: pvc-container
      image: nginx:1.16
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: pv-storage

12、查看pod日志

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Task
监控 pod foo 的日志并:
提取与错误 RLIMIT_NOFILE 相对应的日志行 
将这些日志行写入 /opt/KUTR00101/foo

考点:kubectl logs 命令

答案

# ①、查看 pod foo,确认该pod是否存在
kubectl get pods

# ②、记录
kubectl logs foo | grep "RLIMIT_NOFILE" > /opt/KUTR00101/foo

# ③、检查
candidate@node01:~/k8sYaml$ cat /opt/KUTR00101/foo
2023/02/21 03:16:26 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576

13、使用sidecar代理容器日志

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context k8s

Context
将一个现有的 Pod 集成到 Kubernetes 的内置日志记录体系结构中(例如 kubectl logs)。 
添加 streaming sidecar 容器是实现此要求的一种好方法。

Task
使用 busybox Image 来将名为 sidecar 的 sidecar 容器添加到现有的 Pod 11-factor-app 中。
新的 sidecar 容器必须运行以下命令:
/bin/sh -c tail -n+1 -f /var/log/11-factor-app.log
使用挂载在/var/log 的 Volume,使日志文件 11-factor-app.log 可用于 sidecar 容器。
除了添加所需要的 volume mount 以外,请勿更改现有容器的规格。

考题翻译成白话,就是:
添加一个名为 sidecar 的边车容器(使用 busybox 镜像),加到已有的 pod 11-factor-app 中。

确保 sidecar 容器能够输出 /var/log/11-factor-app.log 的信息。
使用 volume 挂载 /var/log 目录,确保 sidecar 能访问 11-factor-app.log 文件

答案

# ①、查看pod是否存在
kubectl get pod 11-factor-app

# ②、导出这个 pod 的yaml文件
kubectl get pod 11-factor-app -o yaml > varlog-13.yaml

# ③、备份yaml文件,防止改错了,回退
cp varlog-13.yaml varlog-13-bak.yaml

# ④、修改 varlog-13.yaml 文件
vim varlog.yaml # (完整请看后边)

# ⑤、删除原先的pod
kubectl delete pod 11-factor-app

# ⑥、查看删除
kubectl get pod 11-factor-app

# ⑦、新建pod
kubectl apply -f varlog-13.yaml 

# ⑧、检查
candidate@node01:~/k8sYaml$ kubectl get pod 11-factor-app
NAME            READY   STATUS    RESTARTS   AGE
11-factor-app   2/2     Running   0          92s

# ⑨、检查边车
kubectl logs 11-factor-app sidecar

# kubectl exec 11-factor-app -c sidecar -- tail -f /var/log/11-factor-app.log 
# kubectl exec 11-factor-app -c count -- tail -f /var/log/11-factor-app.log

varlog-13.yaml

candidate@node01:~/k8sYaml$ cat varlog-13.yaml 
apiVersion: v1
kind: Pod
metadata:
  annotations:
    cni.projectcalico.org/containerID: 7012305bd7cd526854df8779fac2460e18e7006b99fbc3272270d5c1fdb8defd
    cni.projectcalico.org/podIP: 10.244.196.175/32
    cni.projectcalico.org/podIPs: 10.244.196.175/32
  creationTimestamp: "2022-07-17T10:25:09Z"
  name: 11-factor-app
  namespace: default
  resourceVersion: "67724"
  uid: cc165dff-c835-47de-8b03-2d9a59ab8028
spec:
  containers:
  - args:
    - /bin/sh
    - -c
    - |
      i=0; while true; do
        echo "$(date) INFO $i" >> /var/log/11-factor-app.log;
        i=$((i+1));
        sleep 1;
      done
    image: busybox
    imagePullPolicy: IfNotPresent
    name: count
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-7m2k4
      readOnly: true
    - name: varlog    # 新加
      mountPath: /var/log   # 新加
  - name: sidecar  # 新加
    image: busybox   # 新加
    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/11-factor-app.log']   # 新加
    volumeMounts:   # 新加
      - name: varlog   # 新加
        mountPath: /var/log    # 新加
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: node01
  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: varlog   # 新加
    emptyDir: {}   # 新加
  - name: kube-api-access-7m2k4
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2022-07-17T10:25:09Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2023-02-21T03:15:23Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2023-02-21T03:15:23Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2022-07-17T10:25:09Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://ed4f3790c180bc98064e64582451a629c15a806a80e342c7f2829980a3547bc6
    image: docker.io/library/busybox:latest
    imageID: sha256:62aedd01bd8520c43d06b09f7a0f67ba9720bdc04631a8242c65ea995f3ecac8
    lastState:
      terminated:
        containerID: containerd://0d7c9f4c0be1c2360b470e9644b550e9153807d7487a7c38df9acc83969b745b
        exitCode: 255
        finishedAt: "2023-02-21T03:14:24Z"
        reason: Unknown
        startedAt: "2022-08-26T07:19:27Z"
    name: count
    ready: true
    restartCount: 7
    started: true
    state:
      running:
        startedAt: "2023-02-21T03:15:23Z"
  hostIP: 11.0.1.112
  phase: Running
  podIP: 10.244.196.175
  podIPs:
  - ip: 10.244.196.175
  qosClass: BestEffort
  startTime: "2022-07-17T10:25:09Z"
candidate@node01:~/k8sYaml$ 

14、升级集群

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context mk8s

Task
现有的 Kubernetes 集群正在运行版本 1.24.2。仅将 master 节点上的所有 Kubernetes 控制平面和节点组件升级到版本 1.24.3。 
确保在升级之前 drain master 节点,并在升级后 uncordon master 节点。

可以使用以下命令,通过 ssh 连接到 master 节点:
ssh master01
可以使用以下命令,在该 master 节点上获取更高权限: sudo -i

另外,在主节点上升级 kubelet 和 kubectl。
请不要升级工作节点,etcd,container 管理器,CNI 插件, DNS 服务或任何其他插件。

答案

# ①、理清思路,需要搞清楚升级步骤,需要升级哪些组件,参考官网哪一章节

# ②、查看集群节点
kubectl get nodes
NAME       STATUS   ROLES           AGE    VERSION
master01   Ready    control-plane   222d   v1.24.2
node01     Ready    <none>          222d   v1.24.2
node02     Ready    <none>          222d   v1.24.2

# cordon 停止调度,将 node 调为 SchedulingDisabled。新 pod 不会被调度到该 node,但在该 node 的旧 pod 不受影响。 
# drain 驱逐节点。首先,驱逐该 node 上的 pod,并在其他节点重新创建。接着,将节点调为 SchedulingDisabled。
# 所以 kubectl cordon master01 这条,可写可不写。但我一向做事严谨,能复杂,就绝不简单了事。所以就写了。

# ③、停止调度cordon 
kubectl cordon master01

# ④、将节点标记为不可调度并驱逐所有负载,准备节点的维护
# 参考官网命令:https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/
kubectl drain master01  --ignore-daemonsets

# kubectl get nodes
root@master01:~# kubectl get nodes
NAME       STATUS                     ROLES           AGE    VERSION
master01   Ready,SchedulingDisabled   control-plane   222d   v1.24.2
node01     Ready                      <none>          222d   v1.24.2
node02     Ready                      <none>          222d   v1.24.2

# ⑤、ssh到master01节点,切换到root账号
ssh master01
sudo -i

# ⑥、查看是否有更新资源,然后进行更新
apt-get update

> apt-cache show kubeadm | grep 1.24.3
Version: 1.24.3-00
Filename: pool/kubeadm_1.24.3-00_amd64_a185bea971069e698ed5104545741e561a7c629ebb5587aabc0b2abc9bf79af7.deb

# 7.1 安装kubeadm,再进行更新
apt-get install kubeadm=1.24.3-00

# 7.2 查看升级后的kubeadm版本号
kubeadm version

# 7.3 验证升级计划,此命令检查你的集群是否可被升级,并取回你要升级的目标版本。 命令也会显示一个包含组件配置版本状态的表格。
# 记住:在kubeadm 新版安装后执行此命令
kubeadm upgrade plan

# 7.4 排除etcd,升级其他的,提示时,输入y
kubeadm upgrade apply v1.24.3 --etcd-upgrade=false

# ⑧、升级kubelet
apt-get install -y kubelet=1.24.3-00
kubelet version

# ⑨、升级kubectl
apt-get install -y kubectl=1.24.3-00
kubectl version

# ⑩、退出master,退回到 node01,这里输入两个 exit
root@master01:~# exit
logout
candidate@master01:~$ exit
logout
Connection to master01 closed.
candidate@node01:~$ 

# 11、恢复 master01 的调度
kubectl uncordon master01

# 12、检查
candidate@node01:~$ kubectl get nodes
NAME       STATUS                     ROLES           AGE    VERSION
master01   Ready,SchedulingDisabled   control-plane   222d   v1.24.3
node01     Ready                      <none>          222d   v1.24.2
node02     Ready                      <none>          222d   v1.24.2
candidate@node01:~$ kubectl uncordon master01
node/master01 uncordoned
candidate@node01:~$ kubectl get nodes
NAME       STATUS   ROLES           AGE    VERSION
master01   Ready    control-plane   222d   v1.24.3
node01     Ready    <none>          222d   v1.24.2
node02     Ready    <none>          222d   v1.24.2
candidate@node01:~$ 

15、备份还原etcd

考题

设置配置环境
此项目无需更改配置环境。但是,在执行此项目之前,请确保您已返回初始节点。
[candidate@master01] $ exit  #注意,这个之前是在 master01 上,所以要 exit 退到 node01,如果已经是 node01 了,就不要再 exit 了。

Task
首先,为运行在 https://11.0.1.111:2379 上的现有 etcd 实例创建快照并将快照保存到 /var/lib/backup/etcd-snapshot.db (注意,真实考试中,这里写的是 https://127.0.0.1:2379)

为给定实例创建快照预计能在几秒钟内完成。 如果该操作似乎挂起,则命令可能有问题。用 CTRL + C 来取消操作,然后重试。 
然后还原位于/data/backup/etcd-snapshot-previous.db 的现有先前快照。

提供了以下 TLS 证书和密钥,以通过 etcdctl 连接到服务器。
CA 证书: /opt/KUIN00601/ca.crt
客户端证书: /opt/KUIN00601/etcd-client.crt 
客户端密钥: /opt/KUIN00601/etcd-client.key

答案

# ①、查看官方文档,熟悉相关命令,如 ETCDCTL_API=3, etcdctl -h 等;
ETCDCTL_API=3 etcdctl -h

# 列出 etcdctl 可用的各种选项。例如,你可以通过指定端点、证书等来制作快照,如下所示:
# ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
#  --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> \
#  snapshot save <backup-file-location>

# ②、设置(导出)系统环境变量
export ETCDCTL_API=3

# ③、备份快照,https://11.0.1.111:2379 为master 节点IP
etcdctl --endpoints=https://11.0.1.111:2379 --cacert="/opt/KUIN00601/ca.crt" --cert="/opt/KUIN00601/etcd-client.crt" --key="/opt/KUIN00601/etcd-client.key" snapshot save /var/lib/backup/etcd-snapshot.db

# ④、检查快照(非必须)
> etcdctl snapshot status /var/lib/backup/etcd-snapshot.db -wtable

Deprecated: Use `etcdutl snapshot status` instead.

+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| 7fcee8f3 |    73156 |       2586 |      16 MB |
+----------+----------+------------+------------+

# ⑤、还原,注意:这里还原的非刚才上边导出来的快照,是官方提供的快照
# ETCDCTL_API=3 etcdctl --endpoints 10.2.0.9:2379 snapshot restore snapshotdb  # 示例

# 根据题目,查看要还原的快照文件
# ls -l /data/backup/etcd-snapshot-previous.db

# 需要加证书及认证(需要加sudo权限)
sudo ETCDCTL_API=3 etcdctl --endpoints=https://11.0.1.111:2379 --cacert="/opt/KUIN00601/ca.crt" --cert="/opt/KUIN00601/etcd-client.crt" --key="/opt/KUIN00601/etcd-client.key" snapshot restore /data/backup/etcd-snapshot-previous.db

16、排查集群中故障节点

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context wk8s

Task
名为 node02 的 Kubernetes worker node 处于 NotReady 状态。 
调查发生这种情况的原因,并采取相应的措施将 node 恢复为 Ready 状态,确保所做的任何更改永久生效。

可以使用以下命令,通过 ssh 连接到 node02 节点: 
ssh node02

可以使用以下命令,在该节点上获取更高权限:
sudo -i

说明:没必要参考网址,记住先 restart 再 enable 就行

答案:

# ①、查看集群节点状态,通过 get nodes 查看异常节点,登录节点查看 kubelet 等组件的 status 并判断原因。 真实考试时,这个异常节点的 kubelet 服务没有启动导致的,就这么简单。
> kubectl get nodes

AME       STATUS   ROLES           AGE    VERSION
master01   Ready    control-plane   222d   v1.24.2
node01     Ready    <none>          222d   v1.24.2
node02     Ready    <none>          222d   v1.24.2

# ②、制造node02异常(考试时不需要,那个node就是异常的)
# 在 candidate@node01 上执行,执行完 a.sh 脚本后,等 3 分钟,node02 才会挂掉。
sh /opt/sh/a.sh

# ③、查看节点状态
candidate@node01:~$ kubectl get nodes
NAME       STATUS     ROLES           AGE    VERSION
master01   Ready      control-plane   222d   v1.24.2
node01     Ready      <none>          222d   v1.24.2
node02     NotReady   <none>          222d   v1.24.2   【挂掉了】

# ④、确定是node02挂了,切换到 node2节点
ssh node02
sudo -i

# ⑤、查看 kubelet组件状态
systemctl status kubelet

# ⑥、启动服务,并设置为开机启动
systemctl start kubelet
systemctl enable kubelet

# ⑦、查看状态
> systemctl status kubelet
> kubectl get nodes
NAME       STATUS   ROLES           AGE    VERSION
master01   Ready    control-plane   222d   v1.24.2
node01     Ready    <none>          222d   v1.24.2
node02     Ready    <none>          222d   v1.24.2

17、节点维护

考题

设置配置环境:
[candidate@node-1] $ kubectl config use-context ek8s

Task
将名为 node02 的 node 设置为不可用,并重新调度该 node 上所有运行的 pods。

考点: cordon 和 drain 命令的使用

答案

# ①、查看节点
> kubectl get nodes
NAME       STATUS   ROLES           AGE    VERSION
master01   Ready    control-plane   222d   v1.24.2
node01     Ready    <none>          222d   v1.24.2
node02     Ready    <none>          222d   v1.24.2

# ②、将node02设置为不可用
kubectl cordon node02

# ③、查看状态
candidate@node01:~$ kubectl get nodes
NAME       STATUS                     ROLES           AGE    VERSION
master01   Ready                      control-plane   222d   v1.24.2
node01     Ready                      <none>          222d   v1.24.2
node02     Ready,SchedulingDisabled   <none>          222d   v1.24.2

# ④、驱逐节点
kubectl drain node02  --ignore-daemonsets

# 但如果执行后,有跟测试环境一样的报错(如下截图),则需要加上--delete-emptydir-data --force,会强制将 pod 移除。
# kubectl drain node02  --ignore-daemonsets --delete-emptydir-data --force

# ⑤、检查
kubectl get node
kubectl get pods -A -o wide | grep node02 

# 正常已经没有 pod 在 node02 上了。但测试环境里的 ingress、flannel、kube-proxy 是 daemonsets 模式的,所以显示还在 node02 上,忽略即可。

18、yaml格式说明

https://digtime.cn/articles/440/gu-li-shang-cheng-ji-qun-83-k8s-ru-men-ji-ben-cao-zuo-ti-yan-ji-yaml-shi-yong


相关文章:
k8s官方文档

为者常成,行者常至