KCNA — Kubernetes and Cloud Native Associate
Finding Pods With Services, and Attaching Config and Storage
한국어 원문으로 표시합니다.
목표
Service 가 파드를 어떻게 찾는지, 못 찾으면 어떤 모습인지 직접 만들어 보고, ConfigMap/Secret 주입과 PVC 요청, 서비스 DNS 이름 규칙까지 손으로 확인합니다.
왜 중요한가
Service 관련 장애의 압도적 다수는 "서비스가 안 만들어졌다"가 아니라 "서비스는 만들어졌는데
뒤에 아무것도 없다" 입니다. 쿠버네티스는 셀렉터가 실제로 파드를 잡는지 검증해 주지 않기 때문입니다.
오타 하나로 엔드포인트가 0 건이 되어도 kubectl get svc 는 멀쩡해 보입니다.
그래서 이 실습에서는 일부러 그 상황을 만들어 봅니다. 한 번 손으로 만들어 본 실패는
현장에서 3분 만에 진단됩니다.
ConfigMap/Secret 은 12-factor 의 "설정은 환경에서"를 쿠버네티스가 구현한 형태입니다. 이미지는 어디서나 같고, 다른 건 주입되는 값이어야 합니다. 다만 Secret 은 이름과 달리 기본 설정에서 암호화되지 않는다는 점을 기억해 두세요 — 이건 KCSA 의 중요한 주제입니다.
PVC 는 '요청서'라는 비유가 전부입니다. 파드는 "NFS 서버 10.0.0.5 의 /export/data" 를 가리키지 않고 "1Gi 짜리 RWO 저장소"를 요청합니다. 이 간접 계층 덕분에 같은 매니페스트가 노트북 클러스터에서도 프로덕션에서도 돕니다.
단계
- 네임스페이스
kcna-net을 만들고 그 안에 Deploymentshop을 만듭니다 — 이미지nginx:1.27-alpine, replicas 2, 파드 템플릿 라벨app=shop. - Service
shop-svc를 만듭니다 — 타입ClusterIP, port 80, 셀렉터app=shop. 엔드포인트가 2 건 잡히는지 확인합니다. - Service
broken-svc를 같은 네임스페이스에 만듭니다 — port 80 이지만 셀렉터는 일부러app=shopp(p 하나 더). 엔드포인트가 0 건인 것을 확인하고, 이 서비스를 고치는 올바른 셀렉터를/root/kcna-net/diagnosis.txt에app=shop형태로 한 줄 저장합니다. - Service
shop-np를 만듭니다 — 타입NodePort, port 80, nodePort 는30080, 셀렉터app=shop. - ConfigMap
shop-config를 만듭니다(키APP_MODE, 값production). Secretshop-secret을 만듭니다(키API_KEY, 값은 아무 문자열). 그리고 파드shop-client를 만들어 환경변수APP_MODE는shop-config의APP_MODE키에서, 환경변수API_KEY는shop-secret의API_KEY키에서 가져오게 합니다. 이미지는nginx:1.27-alpine입니다. - PVC
shop-data를 만듭니다 — 요청 용량1Gi, accessModesReadWriteOnce, storageClassNamestandard. shop-svc의 정규화된 DNS 이름(FQDN)을/root/kcna-net/dns.txt에 한 줄로 저장하고, 같은 네임스페이스에 헤드리스 서비스shop-headless를 만듭니다 — 셀렉터app=shop, port 80, clusterIP 는 헤드리스로 지정.
참고
- 엔드포인트 확인은
kubectl get endpointslice -n kcna-net -l kubernetes.io/service-name=shop-svc -o yaml로 볼 수 있습니다.kubectl describe svc의 Endpoints 줄도 빠릅니다. - 5단계 파드의 env 항목은
valueFrom.configMapKeyRef/valueFrom.secretKeyRef를 씁니다. 각각name과key두 필드가 필요합니다. - 클러스터 도메인은 기본값
cluster.local입니다. - 흔한 실수 1: 3단계에서 오타 서비스를 만들자마자 고쳐 버리는 것. 이 단계는 틀린 상태 그대로 두어야 통과합니다.
- 흔한 실수 2: 6단계 PVC 가 Pending 이라고 잘못 만든 줄 알고 지웠다 다시 만드는 것. 동적 프로비저너가 없으면 Pending 이 정상이며, 채점은 요청 내용만 봅니다.
백엔드 디플로이먼트 세우기
네임스페이스 kcna-net 을 만들고 그 안에 Deployment shop 을 만듭니다 — 이미지 nginx:1.27-alpine, replicas 2, 파드 템플릿 라벨 app=shop.
서비스가 찾아갈 대상이 먼저 있어야 합니다. 파드 템플릿에 붙는 라벨이 나중에 셀렉터와 맞아야 하니 어떤 라벨이 붙는지 확인해 두세요.
ClusterIP 서비스와 엔드포인트 확인
Service shop-svc 를 만듭니다 — 타입 ClusterIP, port 80, 셀렉터 app=shop. 엔드포인트가 2 건 잡히는지 확인합니다.
kubectl expose 로 만들 수도 있고 YAML 로 써도 됩니다. 만든 뒤 엔드포인트가 실제로 잡혔는지 반드시 확인하세요 — 서비스가 만들어졌다는 것과 트래픽이 갈 곳이 있다는 것은 다른 얘기입니다.
셀렉터 오타를 일부러 재현하기
Service broken-svc 를 같은 네임스페이스에 만듭니다 — port 80 이지만 셀렉터는 일부러 app=shopp(p 하나 더). 엔드포인트가 0 건인 것을 확인하고, 이 서비스를 고치는 올바른 셀렉터를 /root/kcna-net/diagnosis.txt 에 app=shop 형태로 한 줄 저장합니다.
이 단계는 실패를 만드는 것이 목표입니다. 셀렉터 값에 글자 하나를 더 넣은 서비스를 만들고, 오류가 나지 않는다는 것과 엔드포인트가 0 건이라는 것을 둘 다 확인하세요. 진단 파일에는 이 서비스를 고칠 올바른 셀렉터를 key=value 한 줄로 적습니다.
NodePort 로 밖에서 들어오는 문 만들기
Service shop-np 를 만듭니다 — 타입 NodePort, port 80, nodePort 는 30080, 셀렉터 app=shop.
NodePort 범위는 기본적으로 30000-32767 입니다. 번호를 지정하지 않으면 임의로 배정되니, 채점되는 번호를 쓰려면 매니페스트에 명시해야 합니다.
ConfigMap 과 Secret 주입하기
ConfigMap shop-config 를 만듭니다(키 APP_MODE, 값 production). Secret shop-secret 을 만듭니다(키 API_KEY, 값은 아무 문자열). 그리고 파드 shop-client 를 만들어 환경변수 APP_MODE 는 shop-config 의 APP_MODE 키에서, 환경변수 API_KEY 는 shop-secret 의 API_KEY 키에서 가져오게 합니다. 이미지는 nginx:1.27-alpine 입니다.
kubectl create configmap 과 kubectl create secret generic 에 --from-literal 을 쓰면 빠릅니다. 파드 쪽은 env 항목마다 valueFrom 으로 configMapKeyRef / secretKeyRef 를 지정하는 형태입니다. 어떤 키를 어느 이름으로 노출할지 두 군데를 다 적어야 합니다.
PVC 로 저장소 요청서 쓰기
PVC shop-data 를 만듭니다 — 요청 용량 1Gi, accessModes ReadWriteOnce, storageClassName standard.
PVC 는 '요청서'입니다. 이 실습 클러스터에는 동적 프로비저너가 없어 Pending 에 머무를 수 있는데, 그것 자체가 정상이며 채점은 spec 을 봅니다. accessModes 는 배열이고 storageClassName 은 문자열입니다.
DNS 이름 규칙과 헤드리스 서비스
shop-svc 의 정규화된 DNS 이름(FQDN)을 /root/kcna-net/dns.txt 에 한 줄로 저장하고, 같은 네임스페이스에 헤드리스 서비스 shop-headless 를 만듭니다 — 셀렉터 app=shop, port 80, clusterIP 는 헤드리스로 지정.
서비스의 정규화된 DNS 이름은 네 조각으로 되어 있습니다. 그리고 헤드리스 서비스는 clusterIP 필드에 특정 값을 명시해 만듭니다 — 이 값은 kubectl expose 로는 지정하기 어려우니 YAML 을 쓰는 편이 낫습니다.