CKA시험 합격 이후 CKAD 시험을 앞두고 KodeKloud의 CKAD - Lightning lab-1의 풀이를 첫 게시글로 작성해보고자 한다.
Create a Persistent Volume called log-volume. It should make use of a storage class name manual. It should use RWX as the access mode and have a size of 1Gi. The volume should use the hostPath /opt/volume/nginx.
Next, create a PVC called log-claim requesting a minimum of 200Mi of storage. This PVC should bind to log-volume.
Mount this in a pod called logger at the location /var/www/nginx. This pod should use the image nginx:alpine.
RWO - ReadWriteOnce / ROX - ReadOnlyMany
RWX - ReadWriteMany / RWOP - ReadWriteOncePod 입니다.
우리는 앞으로 https://kubernetes.io/docs/를 자주 사용하게 될 것입니다. 해당 URL을 앞으로 docs라고 부르겠습니다. docs에 pv hostpath라고 검색하면 Configure a Pod to Use a PersistentVolume for Storage가 나옵니다. 해당 문서에 들어가서 오른쪽에 각 목차를 나타낸 곳을 보면 순서대로 Create a PersistentVolume, Create a PersistentVolumeClaim, Create a Pod가 있습니다. 해당 예시를 참고하여 문제를 풀어보도록 하겠습니다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
문제에서 name, storage class name, access mode, size, hostpath에 대한 언급이 있었기 때문에 해당 부분 변경해주도록 합니다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: log-volume
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
hostPath:
path: "/opt/volume/nginx"
다음은 pvc를 생성해보도록 하겠습니다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
문제에서 name, size, bind 되어야 할 pv의 이름을 알려주었습니다. 해당 부분 바꿔주도록 하겠습니다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: log-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
pvc는 생성할 때 연결되어야 할 pv의 정보와 같아야 합니다. storageClassName, accessModes를 pv와 동일하게 작성해줍니다.
마지막으로 Pod를 생성할 차례입니다.
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
문제에서는 Pod의 name, location 그리고 image에 대한 언급이 있었습니다. 그 외에도 pvc name, image, mountPath 정보 적용하도록 하겠습니다.
apiVersion: v1
kind: Pod
metadata:
name: logger
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: log-claim
containers:
- name: task-pv-container
image: nginx:alpine
volumeMounts:
- mountPath: "/var/www/nginx"
name: task-pv-storage
pv, pvc, pod 생성하고 bound 상태, pod 상태 확인해주면 되겠습니다.
We have deployed a new pod called secure-pod and a service called secure-service. Incoming or Outgoing connections to this pod are not working.
Troubleshoot why this is happening.
Make sure that incoming connection from the pod webapp-color are successful.
Important: Don't delete any current objects deployed.
이 문제에서 말하기를 secure-pod와 secuer-service를 배포하였으나 secure-pod로 들어가거나 나오는 연결이 안 된다고 합니다.
먼저 Service를 확인해보도록 하겠습니다.
# kubectl describe service sercure-service
Name: secure-service
Namespace: default
Labels: run=secure-pod
Annotations: <none>
Selector: run=secure-pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.101.127.155
IPs: 10.101.127.155
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.0.7:80
Session Affinity: None
Events: <none>
확인해보니 service는 secure-pod에 적용되어 있고 80/TCP로 설정되어 있습니다.
그럼, 이제 network policy를 확인해보겠습니다.
#k get networkpolicies.networking.k8s.io
NAME POD-SELECTOR AGE
default-deny <none> 13s
#k describe networkpolicies.networking.k8s.io default-deny
Name: default-deny
Namespace: default
Created on: 2023-01-17 02:44:55 -0500 EST
Labels: <none>
Annotations: <none>
Spec:
PodSelector: <none> (Allowing the specific traffic to all pods in this namespace)
Allowing ingress traffic:
<none> (Selected pods are isolated for ingress connectivity)
Not affecting egress traffic
Policy Types: Ingress
확인해보니 network policy는 default-deny가 있었고 default-deny는 따로 PodSelector, port 설정이 되어있지 않았습니다.
때문에 pod와 서비스가 연결이 되어있어도 통신이 되지 않았던 겁니다.
그럼 webapp-color와 secure-pod가 통신할 수 있도록 network policy를 생성해보도록 하겠습니다.
이번엔 network policy를 검색해서 network policies로 들어가서 The NetworkPolicy resource로 가줍니다.
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
저희는 secure-pod가 webapp-color의 신호를 받을 수 있게 하는 설정만 놔두고 싹 지워보도록 합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
run: secure-pod
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
name: webapp-color
ports:
- protocol: TCP
port: 80
label에 대한 정보는 아래와 같이 pod의 label을 확인하고 적어줍니다.
#k get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
secure-pod 1/1 Running 0 10m run=secure-pod
webapp-color 1/1 Running 0 10m name=webapp-color
이제 webapp-color가 secure-service를 80번 포트로 통신할 수 있는지 확인합니다.
# k exec -it webapp-color -- nc -z -v -w 1 secure-service 80
secure-service (10.110.11.172:80) open
위와 같이 nc명령어를 사용하여 service로 향한 포트가 열려있는 것이 확인하고 80번 포트가 허용되어있음을 알 수 있습니다.
추가로 nc(Netcat) 명령어는 포트가 열려있는지 확인하는 명령어입니다. 보통 telnet을 이용하여 포트 오픈을 확인하지만, net-tools가 안 깔려 있는 상황이거나 netcat이 설치된 상황에 대체하여 오픈 확인을 합니다.
-z 옵션 : 포트만 검색하도록 하는 옵션
-v 옵션 : 자세한 정보 제공
-w 옵션 : 뒤에 '-w 1'과 같이 숫자를 적어주면 최대 1초 동안 연결을 한다.
저는 vwxyz에서 xy만 빼는 거로 외웠습니다.
Create a pod called time-check in the dvl1987 namespace. This pod should run a container called time-check that uses the busybox image.
Create a config map called time-config with the data TIME_FREQ=10 in the same namespace.
The time-check container should run the command: while true; do date; sleep $TIME_FREQ;done and write the result to the location /opt/time/time-check.log.
The path /opt/time on the pod should mount a volume that lasts the lifetime of this pod.
이 문제는 다음과 같은 사냥의 pod, configmap을 생성하라고 합니다.
Pod
namespace: dvl1987 / pod: time-check / container: time-check / image: busybox / command: while true; do date; sleep $TIME_FREQ;done > /opt/time/time-check.log / mountPath: /opt/time
configmap
configmap: time-config / data: TIME_FREQ=10 / namespace: dvl1987
먼저 configmap부터 생성해주도록 하겠습니다.
docs에 configmap을 검색하고 configmaps로 들어가 ConfigMaps and Pods에 예시를 확인해줍니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# property-like keys; each key maps to a simple value
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# file-like keys
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
namespace 관련 내용을 추가하고 data 내용에 TIME_FREQ=10 내용을 추가해줍니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: time-config
namespace: dvl1987
data:
TIME_FREQ: "10"
다음은 Pod 예시를 봐볼까요?
apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: alpine
command: ["sleep", "3600"]
env:
# Define the environment variable
- name: PLAYER_INITIAL_LIVES # Notice that the case is different here
# from the key name in the ConfigMap.
valueFrom:
configMapKeyRef:
name: game-demo # The ConfigMap this value comes from.
key: player_initial_lives # The key to fetch.
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# You set volumes at the Pod level, then mount them into containers inside that Pod
- name: config
configMap:
# Provide the name of the ConfigMap you want to mount.
name: game-demo
# An array of keys from the ConfigMap to create as files
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"
다음 내용을 문제에서 준 내용대로 바꿔보겠습니다.
apiVersion: v1
kind: Pod
metadata:
name: time-check
namespace: dvl1987
spec:
containers:
- name: time-check
image: busybox
command: ["/bin/sh", "-c", "while true; do date; sleep $TIME_FREQ;done > /opt/time/time-check.log"]
env:
- name: TIME_FREQ
valueFrom:
configMapKeyRef:
name: time-config
key: TIME_FREQ
volumeMounts:
- name: config
mountPath: "/opt/time"
volumes:
- name: config
emptyDir: {}
Create a new deployment called nginx-deploy, with one single container called nginx, image nginx:1.16 and 4 replicas. The deployment should use RollingUpdate strategy with maxSurge=1, and maxUnavailable=2.
Next upgrade the deployment to version 1.17.
Finally, once all pods are updated, undo the update and go back to the previous version.
해당 문제는 deployment를 생성할 것을 요구하고 있다. 조건은 아래와 같다.
name: nginx-deploy / single container - name: nginx / image: nginx:1.16 / replicas: 4 / RollingUpdate strategy: maxSurge=1, maxUnavailable=2 / upgrade to 1.17 / undo the update and go back to the 1.16
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
문제에서 원하는 대로 yaml파일을 수정해봅니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
spec:
replicas: 4
selector:
matchLabels:
app: nginx-deploy
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- name: nginx
image: nginx:1.16
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 2
maxSurge와 maxUnavailable의 경우 해당 docs 문서 하단에 .spec.strategy.type==RollingUpdate, .spec.strategy.rollingUpdate.maxUnavailable, .spec.strategy.rollingUpdate.maxSurge
와 같이 어떤 위치에 적어주어야 하는지 나와 있기에 참고하여 작성하였습니다.
이후에는 deployment를 생성하고 이미지 업데이트 그리고 롤백하는 과정이 필요한데 롤백하기 위해서는 record가 필요합니다. 때문에 처음 생성할 때부터 --record를 붙여 생성해주도록 합니다
# k create -f dep.yaml --record
# k rollout history deployment nginx-deploy
deployment.apps/nginx-deploy
REVISION CHANGE-CAUSE
1 kubectl create --filename=dep.yaml --record=true
# kubectl set image deployment/nginx-deploy nginx=nginx:1.17 --record
# k rollout history deployment nginx-deploy
deployment.apps/nginx-deploy
REVISION CHANGE-CAUSE
1 kubectl create --filename=dep.yaml --record=true
2 kubectl set image deployment/nginx-deploy nginx=nginx:1.17 --record=true
# kubectl rollout undo deployment nginx-deploy --to-revision=1
deployment.apps/nginx-deploy rolled back
rollout undo 명령어를 통해 roll back을 하는데 revision을 명시해주어야 하므로 record를 하여 revision을 선택할 수 있도록 해야 합니다.
Create a redis deployment with the following parameters:
Name of the deployment should be redis using the redis:alpine image. It should have exactly 1 replica.
The container should request for .2 CPU. It should use the label app=redis.
It should mount exactly 2 volumes.
a. An Empty directory volume called data at path /redis-master-data.
b. A configmap volume called redis-config at path /redis-master.
c. The container should expose the port 6379.
The configmap has already been created.
문제에서 요구하는 내용은 다음과 같습니다.
name: redis / image: redis:alpine / replicas: 1 / request cpu: 0.2 / label: app=redis / mounted volume: 2 / volume(emptyDir) path: /redis-master-data / volume(configmap) path: /redis-master / container port: 6379
이제 아까와 같이 deployment 예시를 가져와서 수정해보도록 하겠습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
volumeMounts:
- name: data
mountPath: /redis-master-data
- name: redis-config
mountPath: /redis-master
resources:
requests:
cpu: "0.2"
volumes:
- name: data
emptyDir: {}
- name: redis-config
configMap:
name: redis-config
cpu는 pod cpu 검색하여서 어디에 넣는지 찾아보고 configmap은 pod configmap 검색해서 volumes에 어떻게 적는지 찾아봐서 작성하였다. configMap의 이름은 k get configmaps하여서 찾도록 합니다.