- Số lượng câu hỏi: 17
- Thời gian: 2 giờ
Phần lớn trong phần này chủ yếu liên quan đến kiến trúc của cluster cũng như các thức install cluster.
Chú trọng các thành phần chính như sau:
- kubelet
- kube-apiserver
- kube-scheduler
- etcd
- controller-manager
Ngoài ra cũng phân cần phân biệt giữa cách hoạt động của static pod, pod và process.
Các bài thi sẽ không bao gồm việc setup 1 cluster mới mà thay vào đó là sẽ là các task upgrade version cho 1 hoặc nhiều nodes.
Lưu ý trình tự upgrade các node từ master cho đến các node. Cần đọc kĩ yêu cầu của task để làm đúng chính xác node nào cần upgrade và các thành phần upgrade là gì
Master Node
Nếu đề yêu cầu là upgrade master node. Ta sẽ action như sau:
- Step 1: Check xem version
kubeadm
hiện tại của master node - Step 2: Tiến hành unschedule node và allocate pod scheduled on master node.
basenode$ kubect drain master01 --ignore-daemonsets
- Step 3: Tiến hành update package kubeadm
basenode$ ssh master01
master01$ sudo apt update # update package repo
master01$ sudo apt install kubeadm=<version> # Version mà task yêu cầu upgrade
# eg task yeu cau upgrade version 1.21.1
master01$ sudo apt install kubeadm=1.21.1-00
- Step 4: Planing cho việc upgrade và tiến hành upgrade cho node
master01$ kubeadm upgrade plan
# When the above command is done, run the following command to upgrade the components of k8s on master node.
master01$ kubeadm upgrade v1.21.1
- Step 5: Update kubelet và các package liên quan (nếu có)
master01$ sudo apt install kubelet=1.21.1-00 kubectl=1.21.1-00
- Step 6: Restart kubelet
master01$ systemctl restart kubelet
- Step 7: Back to basenode and uncordon node
basenode$ kubectl uncordon master01
Worker
On the worker node, we easy to upgrade k8s
- Step 1: Drain node. Example the node name is worker01
basenode$ kubectl drain node01 --ignore-daemonsets
- Step 2: Upgrade kubeadm
basenode$ ssh node01
worker01$ sudo apt update && sudo apt install kubeadm=1.21.0-00
- Step 3: Run kubeadm to upgrade node
worker01$ kubeadm upgrade node
- Step 4: Upgrade kubelet (and related package ) and restart kubelet
worker01$ apt install kubelet=1.21.0-00
worker01$ systemctl restart kubelet
# Some task also require upgrade kubectl. Please careful.
worker01$ apt install kubectl=1.21.0-00
- Step 5: Uncordon node
# Back to basenode
basenode$ kubectl uncordon worker01
Some Tips
# Generate and print join command for worker
kubeadm token create --print-join-command
ETCD Backup
Etcd DB khá quan trọng trong hệ thống k8s. Do đó nên cẩn trọng trong việc backup và restore etcd. Nếu làm không cẩn thận, nó sẽ làm cho các công việc bạn làm trước đó bị ảnh hưởng. Ví dụ như sai etcd của cluster k8s chính có thể làm mất hết các result mà bạn đã làm trước đó.
Phần lớn tỉ trọng etcd là 7-10% do đó bạn có thể cân nhắc by-pass qua vấn đề này
Tuy nhiên, tôi không khuyến khích bạn bỏ qua chỉ để pass CKA mà nên hiểu rõ để áp dụng thực tế.
Đầu tiên cần kiểm tra xem, etcd
nào mà đề yêu cầu bạn cần backup và restore.
Thông thường để tránh ảnh hưởng đến các kết quả của bạn, đề thi sẽ bắt bạn thực hiện backup và restore trên một host độc lập. Tạm gọi là basenode.
Kế đến, kiểm tra etcd
đang run dưới dạng process
hay một static pod
bằng cách.
* Kiểm tra basenode có đang chạy kubelet ko. Và manifest nó đang là đường dẫn gì? Và trong đường dẫn manifest có static pod `etcd` hay không?
* Sử dụng `ps -ef | grep etcd` để check nếu etcd sử dụng process.
Lời khuyên: backup lại file static pod hoặc lưu lại command xuất hiện trong ps để thuận tiện trong việc restore.
export ECTDCTL_API=3
etcdctl --cacert=<file_ca_crt> --key=<file_server_key> --cert=<file_server_crt> --endpoints https://127.0.0.1:2379 snapshot save /opt/backup-etcd
# Lưu ý endpoint phải đúng với etcd cần backup.
# /opt/backup-etcd: thay bằng path mà đề yêu cầu backup
Restore
export ECTDCTL_API=3
etcdctl snapshot restore <path_file> --data-dir=<dir_extract_file_backup>
# Path file: file mà đề yêu cầu cần restore
# data-dir: data-dir thực ra là optional của của command restore. Tuy nhiên nên sử dụng nó để dễ dàng cho việc restore
# Sau đó chỉ cần sửa lại thu mục data dir trong etcd là đc
- Nếu etcd run là static-pod
api: v1
kind: Pod
metadata:
...
...
spec:
....
volumes:
- hostPath:
path: <dir_extract_file_backup> # change here
type: DirectoryOrCreate
name: etcd-data
- Nếu etcd run là process Chỉ biến môi trường ETCD_DATA_DIR trong systemd đúng với path vừa extract
- Nếu etcd run là docker
docker run -v <path_extract>:/var/lib/etcd:rw quay.io/coreos/etcd:v3.4.13 --env-file=/etc/etcd.env /usr/local/bin/etcd
Sheduling
Việc schedule cho pod
được thực hiện bởi kube-scheduler
. Tuy nhiên sẽ các trường hợp đề sẽ yêu cầu bạn manual scheduling khi kube-scheduler không hoạt động.
- Đề yêu cầu bạn tạm thời disable static pod kube-schedule rồi tạo 1 pod và schedule nó lên một node được đặt tên là node01.
// $ mv /etc/kubenetes/manifests/kube-schedulers.yaml /opt
// $ kubectl -n kube-system get pod # chắc rằng pod đã tạm thời bị disable
// $ kubectl run pod --image=nginx --dry-run=client -o yaml > pod.yaml
// # edit file pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
nodeName: node01 // Name of node where the pod is located at
containers:
- image: nginx
name: pod
- Taints và tolerations: Mặc định khi khởi tạo cluster bằng kubeadm thì master node sẽ được thiết lập thuộc tính taints.
$ kubectl describe node master | grep -i taints # Use option "-i" is search pattern without case sensitive
Taints: node-role.kubernetes.io/master:NoSchedule
Khi masster node có thuộc tính taints nghĩa là kube-scheduler sẽ không lập lịch các pod bình thường vào node này. Để lập lịch pod có thể được locate lên master ta action như sau:
apiVersion: v1
kind: Pod
metadata:
name: pod
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: pod
image: nginx
-
NodeSelector
Node selector sử dụng để assign pod đến một một tập các nodes. Có rất nhiều cách để làm thực hiện, trong đó sử dụng
labels selectors
để action.Tạo 1 pod
pod1
chạy với trên node đang có GPU.Giả sử node01 đang có GPU nhưng chưa được set labels để dễ detect.
# Set labels GPU=true for node 01 kubectl labels nodes node01 gpu=true
Tạo 1 pod với thuộc tính nodeSelector:
// pod1.yaml apiVersion: v1 kind: Pod metadata: name: podd1 spec: nodeSelector: gpu: true containers: - name: pod1 image: nginx
Nodeselector trên pod.Specs có format là một kiểu map[string]string. Do đó không thể sử dụng cùng 1 loại key cho nodeSelector
Ví dụ hạ tầng node có 3 loại disk được phân theo labels: disk=sata, disk=ssd và disk=sas. Thì nodeSelector không thể giải quyết bài toán shedule cho pod nếu yêu cầu pod có thể chạy trên disk là sata or disk là sas.
Các điều kiện trong nodeSelector được thực hiện bằng
AND
operators. -
Affinity và AntiAffinity
Tính năng affinity và antiAffitiny giải quyết hạn chế của nodeSelector.
- Cung cấp nhiều quy tắc so sánh hơn so với nodeSelector (nodeSelector chỉ hỗ trợ
AND
operator) - Cung cấp thêm một cơ chế matching là
soft
. Nghĩa là nếu pod có những thuộc tính màkube-scheduler
không đáp ứng được, thì pod này vẫn được schedule. Như nodeSelector làhard
matching. Nếu không có node nào đáp ứng đúng những labels củaPOD
cần select thìpod
sẽ ở trạng thái pending đến khi nào có 1 node thỏa điều kiện - Ngoài ra với antiAffinity:
pod
sẽ được hạn chế schedule trên các labels của cácpods
đang chạy. Ví dụ như khi bạn triển khai deployment một application nào đó với replicas là 6. Tuy nhiên hiện tại chỉ có 5 node available. Nếu như không có thuộc tính antiAffinity thì sẽ có 1 node sẽ chạy ít nhất là 2 pod (phụ thuộc workload CPU)
Example:
apiVersion: v1 kind: Pod metadata: name: pod-1 spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disk operator: In values: - sata - sas containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0 // Với config nodeAffinity này, pod chỉ được schedule trên những node có labels disk là sata hoặc là sas
Operator support: In, NotIn, Exists, DoseNotExists, Gt, Lt
Nếu cùng sử dụng cả hai
nodeSelector
vànodeAffinity
, thì node ứng cử viên phải đáp ứng cả hai điều kiện.Nếu có nhiều
nodeSelectorTerms
trongnodeAffinity
thì pod sẽ được scheduled trên lên node thỏa 1 trong những điều kiện củanodeSelectTerms
Nếu có nhiều
matchExpressions
trongnodeSelectorTerms
thì pod sẽ được schedule lên node thỏa tất cả điều kiện củamatchExpressions
podAffinity/podAntiAffinty
Hai thuộc tính này cho phép bạn giới hạn các node mà các
pod
của bạn bạn đủ điều kiện để schedule dựa trên labels củapod
đang chạy trên node.Để dễ hiểu ta xem ví dụ dưới đây
apiVersion: apps kind: Deployment metadata: name: deploy-v1 labels: id: deploy-v1 spec: replicas: 2 selector: matchLabels: id: deploy-v1 template: metadata: labels: id: deploy-v1 spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: id operator: In values: - deploy-v1 topologyKey: "kubernetes.io/hostname" containers: - name: nginx image: nginx
Với manifest như thế này thì pod sẽ không được schedule trên chính các node mà đã chưa pod deploy-v1-xxxx-xxxx đang chạy.
Ví dụ với replicas là 2, định nghĩa thứ tự mỗi pod là pod-1 và pod-2. Và hệ thống hiện tại chỉ có 1 worker A đang available. Đầu tiên pod-1 sẽ được schedule trên worker A. Lúc đo pod đang có labels
id=deploy-v1
. Sau đó pod-2 sẽ tiếp tục schedule, tuy nhiên khi check điều kiệnaffinity
thì worker A không thỏa điều kiện dó đã có pod-1 đang chạy trên đó có labels match với điều kiện của podAntiAffinityTham khảo thêm link để biết thêm về namespaceSelector cũng như hiểu được 4 cơ chế scheduler:
- requiredDuringSchedulingIgnoredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution
- requiredDuringSchedulingRequiredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution
Phần này sẽ không có trong đề thi CKA tuy nhiên cũng là một kiến thức bạn nên cần nắm vũng.
- Cung cấp nhiều quy tắc so sánh hơn so với nodeSelector (nodeSelector chỉ hỗ trợ
Workloads
- Scale:
kubectl scale <resource> <name> --replicas=<number_replicas> [--record=true]
Resouce available:
- Deployment
- Statefulset
- ReplicaSet
Options: record=true. It allows record the command in Annotations
- tip:
Trong bài thi, nếu đề yêu cầu bạn scale một applications trong một namespace nào đó. Không chỉ rõ resource source nào thì có thể check bằng command như sau:
kubectl -n <namespace> get all | grep -i "<pattern_you_wanna_search">
- Create manifest for daemonset app-1 quickly:
kubectl create deployment app-1 -n namespace1 --image=nginx --dry-run=client -o yaml > pod-1.yaml
Edit the manifest
// pod-1.yaml
apiVersion: apps
kind: DaemonSet # change Deployment to DaemonSet
metadata:
labels:
app: app-1
name: app-1
namespace: namespace-1
spec:
# replicas: 1 remove line
selector:
matchLabels:
app: app-1
# strategy: {} remove line
template:
metadata:
labels:
app: app-1
spec:
containers:
- image: nginx
name: nginx
resources: {}
- Update image for deployment app-1 and record
kubectl set image deployment app-1 <name_container>=<version> --record=true
Eg: kubectl set image deployment app-1 nginx=1.21 --record=true
- Undo revision
kubectl rollout undo <resource> <name> --to-revision=<revision>
Eg: kubectl rollout undo daemonset ingress-nginx --to-version=1
- Set limit/request resouce
apiVersion: v1
kind: Pod
metadata:
name: pod-1
spec:
containers:
- name: pod-1
image: busybox
command: ["sleep", "4800"]
resources:
requests:
memory: 1M
cpu: 100m
limits:
memory: 10M
cpu: 1
Quick command create pod
with request/limit
kubectl run pod-1 --image=busybox --limits=cpu=1,memory=10M --requests=cpu=100m,memory=1M --command -- sleep 4800
Set a deployments nginx container cpu limits to "100m" and memory to "256Mi":
kubectl set resources deployment nginx -c=nginx --limits=cpu=100m,memory=256Mi
Set the resource request and limits for all containers in nginx:
kubectl set resources deployment nginx --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi
Ngoài ra, trong phần này bạn cần nắm rõ nguyên tắc hoạt động của các extensions như Deployment, StatefulSet cũng như DaemonSets. Hiểu và biết vận dụng vào các trường hợp nhất định.
-
Understand host networking configuration on the cluster nodes
-
Understand connectivity between Pods
-
Understand ClusterIP, NodePort, LoadBalancer service types and endpoints
-
Know how to use Ingress controllers and Ingress resources
-
Know how to configure and use CoreDNS
-
Choose an appropriate container network interface plugin
-
Create services with nodePort for deployment nginx
kubectl expose deployment nginx --name=<service-name> --port=80 --target-port=80 --type=NodePort
-
Create services with clusterIP for pod nginx
kubectl expose pod nginx --name=<service-name> --port=80 --target-port=80
-
Create networkpolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: <name_network_policy>
# namespace: namespace | if task required
spec:
podSelector: {} # must have this spec. If network policy apply for all pods in a namespace. Fill {}. If apply for invidiual pod, fill matchLabels: and labels of pod.
# podSelector:
# matchLabels:
# app: db
policyTypes:
- Ingress
ingress:
- from:
podSelector:
matchLabels:
app: front-end
ports:
- port: 80
protocol: TCP
-
Ingress Controller
Create ingress services ingress-app-1 for service service-1 in
namespace-1
namespace with prefix path /helloapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-app-1 namepsace: namespace-1 spec: rules: - http: paths: - path: /hello pathType: Prefix backend: service: name: service-1 port: number: 80
Phần này khá dễ, chủ yếu đọc kĩ đề và lựa chọn đúng storage classes, đúng accessMode, đặt đúng tên PV hoặc PVC lúc config
Chỉ một lưu ý đối với đề có sidecar container thì thường khái niệm đang chạy chỉ đến là multi container on pod. Lúc này ta sử dụng emptyDir để tạo ra volume sharing giữa các container nhưng không persistent
// kubectl run multi-container --image=busybox --dry-run=client -o yaml > multi-container.yaml
// Edit multi-container.yaml be like
---
apiVersion: v1
kind: Pod
metadata:
name: multi-container
labels:
run: multi-container
spec:
volumes:
- name: shared-data
emptyDir: {}
container:
- image: busybox
name: container1
command: ["/bin/sh"]
args: ["-c", "while true; do echo $date > /vol/date.log; done"]
volumeMounts:
- name: shared-data
mountPath: /vol/
- image: busybox
name: container1
command: ["/bin/sh"]
args: ["-c", "tail -f /vol/date.log"]
volumeMounts:
- name: shared-data
mountPath: /vol/
// run: kubectl -f multi-container.yaml create
Nếu đề có yêu cầu expands capacity của PVC mà có record thì ta thực hiện như sau:
# Expand capacity for pvc example-pvc to 100Gi
$ kubectl edit pvc example-pvc --record=true
# Find line capacity and edit number to 100Gi.
Troubleshoot thì có khá rộng. Tuy nhiên trong phạm vi cert thì phần lớn chú trọng vào các điểm như sau:
- kubelet đã start hay chưa? Có startup on-boot chưa
- kubectl scheduler đang work properly?
- kubectl-apiserver đang work properly?
Theo tôi tìm hiểu phần lớn các lỗi của các component trên đều có những cái sai như sau:
- Binary path của kubelet, kube-scheduler hay kube-apiserver bị sai.
- Path certificate bị sai
- Image name của container kubec-schuduler hay apiserver trong static pod bị sai.
Cố gắng đọc kỹ yêu cầu của đề. Maybe chỉ đơn giản là lệnh start services của kubelet và enable services kubelet là bạn đã hoàn thành câu trả lời.
-
*Role&ClusterRole
Chứa tập hợp các rules đại diện cho một số quyền nào đó. Rules này chỉ có tính chất thêm vào chứ ko chặn quyền.
- Role hoạt động trong 1 name namespace cụ thể
- Cluster role: hoạt động trong non-namespaced.
Role
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: default name: pod-reader rules: - apiGroups: [""] # "" indicates the core API group resources: ["pods"] verbs: ["get", "watch", "list"] // fast command with kubectl $ kubectl create role pod-reader -n default --verb=get,watch,list --resource=pod
ClusterRole
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: create-deployment rules: - apiGroups: ["apps","extensions"] resources: ["deployments"] verbs: ["create"] // fast command $ kubectl create clusterrole create-deployment --verb=create --resource=deployments
Tip
- Resorce: lower case and end with
s
. Example: pods
, deployements
, secrets
, v.v...
*RoleBinding/Cluster role binding
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: pod-reader namespace: default subjects: - kind: User name: dave # Name is case sensitive apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: pod-reader apiGroup: rbac.authorization.k8s.io
RoleBinding có thể tham chiếu đến ClusterRole để thiết lập quyền cho namespace nhất định.
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: create-deployment namespace: staging subjects: - kind: User name: dave # Name is case sensitive apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: create-deployment apiGroup: rbac.authorization.k8s.io
apiVersion: rbac.authorization.k8s.io/v1 # This cluster role binding allows anyone in the "manager" group to read secrets in any namespace. kind: ClusterRoleBinding metadata: name: read-secrets-global subjects: - kind: Group name: manager # Name is case sensitive apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: secret-reader apiGroup: rbac.authorization.k8s.io
Command
# With user $ kubectl rolebinding pod-reader --role=pod-reader -n default --user=dave # With services account $ kubectl rolebinding pod-reader --role=pod-reader -n default --serviceaccount=default:admin $ kubectl rolebinding create-deployment --clusterrole=create-deployment -n staging --user=dave $ kubectl clusterrolebinding read-secrets-global --clusterrole=secret-reader --group=manager # Check $ kubectl auth can-i <verb> <resource> [-n namespace ] --as <user> $ kubectl auth can-i <verb> <resource> --as system:serviceaccount:<namespace>:<service_account_name>
Cluster | Members | CNI | Description |
---|---|---|---|
k8s | 1 master, 2 worker | flanel | K8s cluster |
hk8s | 1master, 2 worker | calico | k8s cluster |
bk8s | 1master, 1 worker | flannel | k8s cluster |
wk8s | 1master, 2 worker | flanel | k8s cluster |
ek8s | 1master, 2 worker | flanel | k8s cluster |
ik8s | 1master, 1 base node | loopback | k8s cluster - missing worker node |
Trong quá trình làm exam, các cheat sheet sau sẽ giúp cho việc thực hiện sẽ được nhanh hơn
# 1. Use kubectl alias, which means you always run kubectl just with k
alias k=kubectl
# 2. This way you can just run k run pod1 --image=nginx $do
export do="--dry-run=client -o yaml"
# 3. Sometime you need to wait few seconds to kill pods, force killing them is better, create a shortcut to do it faster `k delete pod <podname> $fk`
export fk="--force --grace-period=0"
# 4. Dont delete and recreate pods, do replace of the yaml
k replace -f <yaml-file> --force
# 5. Adapt ~/.vimrc to be able to apply tab on multiple selected lines(I did not do it myself)
set shiftwidth=2
# 6. Enable autocompletion(After you use the alias, autocompletion will be disabled, use this to reenable it)
source <(kubectl completion bash)
and then
complete -F __start_kubectl k
-
Nếu bạn có dự định thi chứng chỉ CKA thì nên đăng ký liền. Vì khi đăng ký, CNCF sẽ cho bạn 2 session simulator trên killer.sh. Mỗi session sẽ được 36 tiếng khi bạn active nó. Bạn hãy tận dụng triệt đẽ 2 session này để tập làm quen cũng như nhuần nhuyễn các câu lệnh.
-
Theo kinh nghiệm thì bạn nên active session đầu tiên khi bạn có vài kiến thức cơ bản. Cố gắng làm các task đó mà không cần nhìn vào đáp án. Session thứ 2 bạn nên active trước ngày thi 1->2 ngày để cũng cố lại kiến thức.
-
CKA sẽ cho bạn 1 lần retake nếu first attempt của bạn bị failed. Nên đừng quá lo lắng. Nhưng theo những gì tôi tìm hiểu về CKA này thì bạn nên đăng ký tiếp lần 2 vì có khả năng bạn sẽ làm lại bộ đề giống như first attempt. Tuy nhiên đây chỉ là những trick giúp bạn pass CKA chứ ko giúp bạn có nhiều kiến thức về k8s.