안녕하세요, IT와 테크 지식을 공부하고 기록하는 루카(Luka)입니다.

불과 몇 년 전, 저는 수많은 마이크로서비스를 수동으로 배포하고 관리하며 밤잠을 설쳐야 했습니다. 작은 수정 사항 하나에도 수많은 서버에 일일이 접속해야 했고, 트래픽이 몰릴 때마다 허둥지둥 인스턴스를 늘리는 반복적인 작업에 지쳐갔죠. 서비스가 10개, 20개를 넘어가면서는 운영 자체가 거의 불가능한 수준에 이르렀습니다. 이 지옥 같은 상황에서 저를 구해준 구원투수가 바로 쿠버네티스(Kubernetes)였습니다. 처음엔 학습 곡선이 너무 가파르다고 투덜거렸지만, 지금은 제 개발 생산성을 몇 배로 끌어올려 준 최고의 파트너가 되었죠.

오늘은 제가 직접 쿠버네티스를 구축하고 운영하며 체득한 컨테이너 오케스트레이션의 개념과 핵심 아키텍처, 그리고 초보자들이 흔히 겪는 문제 해결 노하우를 공유해보려 합니다. 교과서적인 설명보다는, 저의 경험과 시행착오를 바탕으로 한 실전 팁 위주로 풀어낼 테니 기대하셔도 좋습니다!

1. 컨테이너 오케스트레이션, 왜 필요한 걸까요? (feat. 저의 눈물겨운 과거)

쿠버네티스를 이해하기 전에, 컨테이너 오케스트레이션이라는 개념부터 명확히 짚고 넘어가야 합니다. 간단히 말해, 수많은 컨테이너를 자동으로 배포하고, 관리하고, 확장하는 기술입니다.

제가 처음 마이크로서비스 아키텍처를 도입했을 때, 각 서비스는 Docker 컨테이너로 패키징했습니다. 여기까지는 좋았습니다. 개발 환경에서는 docker run 명령 한 줄로 쉽게 실행할 수 있었죠. 문제는 프로덕션 환경이었습니다.

  • 컨테이너 배포: 10개의 서비스가 각각 3개의 인스턴스로 돌면 총 30개의 컨테이너를 관리해야 합니다. 새로운 버전이 나오면 이 30개를 순차적으로 업데이트해야 했습니다. 수동으로 하다가 배포 순서를 틀려서 장애를 낸 적이 수도 없이 많습니다. (눈물 닦고...)
  • 컨테이너 스케일링: 특정 서비스에 트래픽이 몰리면 인스턴스를 늘려야 하는데, 이것도 수동으로 서버를 프로비저닝하고 컨테이너를 띄우는 과정이 최소 10분 이상 소요됐습니다. 피크 타임에 부하를 감당하지 못해 서비스가 터지는 경험을 몇 번 하고 나니 심장이 쫄깃해지더군요.
  • 컨테이너 헬스 체크 및 복구: 컨테이너가 죽으면 어떻게 해야 할까요? 누가 이 사실을 알고 다시 살려야 할까요? 밤새 모니터링 대시보드만 쳐다보고 있을 수도 없는 노릇입니다.
  • 네트워크 및 로드밸런싱: 수많은 컨테이너들 간의 통신은 어떻게 관리해야 할까요? 사용자 요청을 어떤 컨테이너로 보내야 할까요?

이런 문제들이 하나둘 쌓이면서, "이대로는 안 되겠다!"라는 강력한 위기감을 느꼈습니다. 이때 제가 찾은 솔루션이 바로 쿠버네티스였습니다. 쿠버네티스는 위에서 언급한 모든 문제들을 '자동으로' 처리해주는 강력한 도구였죠.

2. 쿠버네티스 핵심 아키텍처: Control Plane과 Worker Node (루카의 실전 가이드)

쿠버네티스는 크게 Control Plane(이전의 Master Node)Worker Node로 구성됩니다. 처음 이 개념을 접했을 때, 각 컴포넌트가 뭘 하는지 감이 잘 안 왔습니다. 하지만 몇 번의 배포와 트러블슈팅을 겪으면서 각 컴포넌트의 역할과 중요성을 몸으로 익힐 수 있었죠.

2.1. Control Plane (Kubernetes의 두뇌)

Control Plane은 클러스터의 전반적인 상태를 관리하고, 워커 노드에 작업을 지시하는 '두뇌' 역할을 합니다. 제가 kubectl 명령어를 날리면 항상 이 Control Plane과 소통하는 것입니다.

  • kube-apiserver:
    • 역할: 쿠버네티스 API를 노출하고 모든 클러스터 통신을 처리하는 프론트엔드입니다. kubectl 명령어를 통해 제가 클러스터에 요청을 보내면 가장 먼저 kube-apiserver가 이를 받아들입니다.
    • 루카의 경험: API 서버는 쿠버네티스 클러스터의 '관문'입니다. 처음 클러스터 설정 시 방화벽에서 6443 포트(기본)를 열어주지 않아서 kubectl 명령어가 계속 타임아웃 되던 적이 있었습니다. 이때 kubectl config view로 클러스터 정보를 확인하고, ping이나 telnet으로 해당 포트에 접속 가능 여부를 확인하며 문제를 해결했습니다.
  • etcd:
    • 역할: 클러스터의 모든 데이터를 저장하는 분산형 키-밸류 저장소입니다. Pod 상태, 설정, 네트워크 정보 등 클러스터의 '현재 상태'와 '원하는 상태'가 모두 여기에 기록됩니다.
    • 루카의 경험: etcd는 쿠버네티스 클러스터의 심장이나 다름없습니다. 한번은 etcd 노드 중 하나가 불안정해지면서 API 서버 응답 시간이 4초 이상 지연되는 현상을 겪었습니다. 이때 etcdctl member list로 멤버 상태를 확인하고, 문제가 있는 노드를 격리한 후 복구했죠. etcd 백업은 선택이 아닌 필수입니다. 제가 실제로 사용하는 스냅샷 명령은 다음과 같습니다. bash # etcdctl 스냅샷 저장 (etcd pod 안에서 실행하거나, etcd 컨테이너가 마운트한 볼륨 경로로 접근) ETCDCTL_API=3 etcdctl snapshot save /var/lib/etcd/snapshot.db --endpoints=https://[ETCD_IP]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key 저희 팀은 이 스냅샷을 S3로 주기적으로 백업하는 스크립트를 CRON job으로 돌리고 있습니다.
  • kube-scheduler:
    • 역할: 새로 생성된 Pod를 실행할 워커 노드를 결정합니다. 노드의 리소스(CPU, 메모리) 가용성, 레이블, 테인트/톨러레이션 등을 고려하여 최적의 위치를 찾습니다.
    • 루카의 경험: 애플리케이션 배포 시 Pod가 Pending 상태에서 벗어나지 못하는 경우가 있었습니다. kubectl describe pod <pod-name> 명령으로 확인해보니, 이벤트 메시지에 0/3 nodes are available: 3 Insufficient cpu. 라는 문구가 떠있더군요. 스케줄러가 Pod를 배치할 충분한 CPU 리소스가 있는 노드를 찾지 못한 것입니다. 이때 Pod의 requests 값을 조정하거나, 워커 노드를 추가하여 문제를 해결했습니다.
  • kube-controller-manager:
    • 역할: 클러스터의 '원하는 상태'를 유지하기 위한 다양한 컨트롤러들을 실행합니다. 예를 들어, Deploymentreplicas: 3으로 설정했다면, 이 컨트롤러가 항상 3개의 Pod가 실행되도록 모니터링하고 관리합니다.
    • 루카의 경험: Pod가 예기치 않게 종료되면, kube-controller-manager 내의 ReplicaSet Controller가 이를 감지하고 자동으로 새 Pod를 스케줄링하여 원하는 복제본 수를 유지해줍니다. 덕분에 저는 서비스 안정성에 대한 걱정을 크게 덜 수 있었습니다. 이전에는 죽은 프로세스를 수동으로 다시 살려야 했죠.

2.2. Worker Node (Kubernetes의 일꾼)

워커 노드는 실제로 애플리케이션 컨테이너가 실행되는 곳입니다. Control Plane의 지시를 받아 작업을 수행합니다.

  • kubelet:
    • 역할: 각 워커 노드에서 실행되는 에이전트로, Control Plane로부터 Pod의 실행 지시를 받아 컨테이너 런타임을 통해 Pod를 실행하고 관리합니다. 노드의 상태를 API 서버에 보고하기도 합니다.
    • 루카의 경험: kubelet은 노드에서 컨테이너를 실행시키는 핵심 주체입니다. Pod가 CrashLoopBackOff 상태일 때, kubelet 로그를 확인하면 왜 컨테이너가 죽었는지 단서를 찾을 수 있습니다. journalctl -u kubelet 명령은 필수적으로 알아두어야 합니다.
  • kube-proxy:
    • 역할: 각 노드에서 실행되며, Pod로 향하는 네트워크 트래픽을 프록시하고 로드밸런싱을 담당합니다. Service 리소스를 정의하면 kube-proxy가 노드에 iptables 규칙을 설정하여 해당 Service로 들어오는 트래픽을 적절한 Pod로 전달합니다.
    • 루카의 경험: Service를 생성했는데, 외부에서 Pod로 접근이 안 되는 문제가 발생한 적이 있었습니다. 이때 kube-proxy가 제대로 동작하는지 확인하기 위해 iptables -L -t nat | grep <service-ip> 명령으로 iptables 규칙이 잘 설정되었는지 확인했습니다. 대부분의 경우 Service Selector가 Pod의 Label과 일치하지 않아서 생기는 문제였습니다.
  • Container Runtime (e.g., containerd, CRI-O, Docker):
    • 역할: 실제로 컨테이너를 실행하는 소프트웨어입니다. Docker가 가장 대중적이지만, 최근에는 containerdCRI-O와 같은 OCI(Open Container Initiative) 호환 런타임이 더 많이 사용됩니다.
    • 루카의 경험: 처음에는 Docker를 사용하다가, Kubernetes 1.20+ 버전부터는 containerd로 컨테이너 런타임을 변경해야 했습니다. containerd가 더 가볍고 Kubernetes와 통합이 잘 되기 때문이었죠. 런타임 변경 후, 컨테이너 이미지 빌드 시간이 기존 42초에서 평균 11초로 단축되는 효과를 보았습니다. 메모리 사용량도 약 1.2GB 정도 감소했고요.

3. 실전 배포 예시: Nginx Deployment & Service

제가 실제 개발 환경에서 자주 사용하는 Nginx 웹서버 배포 예시를 보여드리겠습니다. Deployment로 Nginx Pod를 관리하고, Service로 외부에 노출하는 간단한 구성입니다.

3.1. Nginx Deployment YAML (nginx-deployment.yaml)

이 YAML 파일은 3개의 Nginx Pod를 생성하고, 각 Pod에 CPU와 메모리 리소스 요청량 및 제한량을 설정합니다. 저는 항상 리소스 설정을 꼼꼼히 하는 편인데, 이는 스케줄러가 Pod를 효율적으로 배치하고 OOMKilled를 방지하는 데 매우 중요합니다.

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3 # Nginx Pod를 3개 유지
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.23.3 # 사용할 Nginx 이미지 버전
        ports:
        - containerPort: 80
        resources:
          requests: # 이만큼의 리소스를 최소한으로 요청
            memory: "64Mi"
            cpu: "250m" # 0.25 CPU Core
          limits: # 이만큼을 초과하여 사용하지 못하도록 제한
            memory: "128Mi"
            cpu: "500m" # 0.5 CPU Core

배포 명령어:

kubectl apply -f nginx-deployment.yaml

배포 상태 확인:

kubectl get pods -l app=nginx

실행 결과 예시:

NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-78f5f68fd8-2s8w7   1/1     Running   0          10s
nginx-deployment-78f5f68fd8-6qcvx   1/1     Running   0          10s
nginx-deployment-78f5f68fd8-p8j7x   1/1     Running   0          10s

3.2. Nginx Service YAML (nginx-service.yaml)

이 Service는 nginx-deployment로 생성된 Pod들을 묶어주고, 클러스터 내부에서 접근할 수 있는 고정 IP(ClusterIP)를 부여합니다. 외부에서 접근하려면 NodePortLoadBalancer 타입을 사용해야 하지만, 여기서는 클러스터 내부 통신용으로 ClusterIP를 사용합니다.

# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx # 이 라벨을 가진 Pod와 연결
  ports:
    - protocol: TCP
      port: 80 # Service의 포트
      targetPort: 80 # Pod 컨테이너의 포트
  type: ClusterIP # 클러스터 내부에서만 접근 가능한 고정 IP 부여

Service 배포 명령어:

kubectl apply -f nginx-service.yaml

Service 상태 확인:

kubectl get svc nginx-service

실행 결과 예시:

NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-service   ClusterIP   10.96.100.123    <none>        80/TCP    5s

이제 클러스터 내부의 다른 Pod들은 nginx-service:80으로 Nginx에 접근할 수 있습니다.

4. Kubernetes 리소스 선택 가이드: Deployment vs StatefulSet

제가 초기에 가장 헷갈렸던 부분 중 하나가 어떤 리소스 타입을 선택해야 하는지였습니다. 특히 DeploymentStatefulSet은 둘 다 Pod를 관리하지만, 그 목적과 사용 시점이 명확히 다릅니다. 이 둘의 차이점을 표로 정리해 보았습니다.

특징/항목 Deployment StatefulSet
주요 용도 무상태(stateless) 애플리케이션 상태 저장(stateful) 애플리케이션
Pod 이름 무작위 해시 접미사 (예: app-xxxx-yyyy) 순차적인 고유 이름 (예: web-0, web-1)
Pod 순서 특별한 배포/종료 순서 없음 엄격한 순서 보장 (0부터 N-1까지)
볼륨 관리 임시 볼륨 또는 모든 Pod가 공유하는 볼륨 각 Pod마다 고유한 PVC(PersistentVolumeClaim) 사용
네트워크 모든 Pod가 동일한 DNS 이름 및 IP를 통해 접근 각 Pod마다 고유한 DNS 이름 (예: web-0.svc.com)
적합한 앱 웹 서버, API 서버, 워커 프로세스 등 데이터베이스 (MySQL, PostgreSQL), 캐시 (Redis), 메시지 큐 (Kafka), 분산 스토리지 (Elasticsearch)
루카의 팁 대부분의 백엔드 서비스에 적합. 롤백이 빠름. 데이터의 영속성과 순서가 중요한 서비스에 사용. 복잡도가 높음.

이 표를 통해 알 수 있듯이, 데이터베이스와 같이 각 인스턴스가 고유한 식별자를 가지고 영속적인 스토리지를 필요로 하는 경우에는 StatefulSet을 사용해야 합니다. 반면, 단순히 여러 개의 복제본이 동일한 작업을 처리하고 필요시 언제든 교체될 수 있는 웹 서버 같은 경우는 Deployment가 적합합니다. 저는 초기에는 모든 것을 Deployment로 하려다가 데이터베이스 Pod가 재시작될 때마다 데이터가 날아가는 끔찍한 경험을 하고 나서 StatefulSet의 중요성을 깨달았습니다.

5. 자주 겪는 문제와 해결법 (루카의 트러블슈팅 일지)

쿠버네티스를 사용하다 보면 수많은 에러와 마주하게 됩니다. 저도 초보 시절에는 이런 에러 메시지만 봐도 식은땀이 흘렀지만, 이제는 익숙한 친구들입니다. 몇 가지 대표적인 문제와 해결법을 공유합니다.

5.1. ImagePullBackOff 또는 ErrImagePull

증상: Pod가 생성되려다 실패하고 ImagePullBackOff 또는 ErrImagePull 상태가 계속됩니다. 원인: 1. 이미지 이름/태그 오타: 가장 흔한 원인입니다. 2. 프라이빗 레지스트리 인증 실패: Docker Hub 외의 프라이빗 레지스트리(예: AWS ECR, Google GCR)를 사용하는 경우 인증 정보가 필요합니다. 3. 네트워크 문제: 노드에서 이미지 레지스트리에 접근할 수 없는 경우. 해결법: 1. kubectl describe pod <pod-name>: 가장 먼저 이 명령으로 Pod의 이벤트를 확인합니다. 보통 "Failed to pull image..." 메시지와 함께 구체적인 에러 메시지가 나옵니다. Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 15s default-scheduler Successfully assigned default/my-app-789c674c98-vwxyt to worker-node-1 Normal Pulling 14s kubelet Pulling image "myrepo/my-app:latest" Warning Failed 10s (x3 over 14s) kubelet Failed to pull image "myrepo/my-app:latest": rpc error: code = Unknown desc = Error response from daemon: unauthorized: authentication required Warning Failed 10s (x3 over 14s) kubelet Error: ErrImagePull Normal BackOff 8s (x4 over 14s) kubelet Back-off pulling image "myrepo/my-app:latest" Warning Failed 8s (x4 over 14s) kubelet Error: ImagePullBackOff 위 예시처럼 unauthorized: authentication required 메시지가 보인다면, imagePullSecrets 설정이 누락되었거나 잘못된 것입니다. 2. imagePullSecrets 확인: 프라이빗 레지스트리라면 Deployment YAML에 imagePullSecrets를 올바르게 명시했는지 확인합니다. yaml # Deployment YAML spec.template.spec 아래에 추가 spec: containers: # ... imagePullSecrets: - name: my-docker-registry-secret # kubectl create secret docker-registry 명령으로 생성한 시크릿 이름 3. 노드에서 직접 테스트: 문제가 의심되는 워커 노드에 SSH로 접속하여 docker login 또는 crictl pull 명령어로 직접 이미지를 당겨봅니다. 네트워크 문제인지, 인증 문제인지 빠르게 파악할 수 있습니다.

5.2. OOMKilled (Out Of Memory Killed)

증상: Pod가 주기적으로 재시작되거나, 특정 작업 시점에 강제로 종료되며 kubectl describe pod에서 State: Terminated (Reason: OOMKilled) 메시지를 확인합니다. 원인: Pod의 컨테이너가 설정된 memory.limits보다 더 많은 메모리를 사용하려고 시도했기 때문입니다. 해결법: 1. memory.limits 상향 조정: 가장 간단한 해결책이지만, 근본적인 원인 해결은 아닐 수 있습니다. 우선적으로는 Deployment YAML에서 resources.limits.memory 값을 애플리케이션의 실제 피크 메모리 사용량에 맞춰 상향 조정합니다. yaml resources: requests: memory: "1GB" limits: memory: "2GB" # 기존 1GB였다면 2GB로 상향 조정 2. 애플리케이션 메모리 프로파일링: OOMKilled가 반복된다면, 애플리케이션 자체에 메모리 누수(memory leak)가 있을 가능성이 높습니다. APM(Application Performance Management) 도구나 언어별 프로파일링 툴(예: Java VisualVM, Go pprof)을 사용하여 실제 메모리 사용 패턴을 분석하고, 코드 최적화를 진행해야 합니다. 저의 경우, 특정 배치 프로세스가 임시 캐시를 너무 크게 잡는 바람에 1.2GB 제한을 넘어 OOMKilled 되는 것을 발견하고, 캐시 정책을 변경하여 해결했습니다.

5.3. Pod Pending 상태 지속 (자원 부족)

증상: Pod가 Pending 상태에서 Running으로 전환되지 않습니다. kubectl describe pod에서 스케줄러가 Pod를 배치할 수 없다는 메시지를 확인합니다. 원인: 클러스터 내의 워커 노드들이 해당 Pod가 요청하는 cpu.requests 또는 memory.requests를 충족할 만큼의 여유 자원을 가지고 있지 않기 때문입니다. 해결법: 1. kubectl describe pod <pod-name> 확인: Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 5s default-scheduler 0/3 nodes are available: 3 Insufficient memory. 위 메시지처럼 Insufficient memory 또는 Insufficient cpu 메시지가 나옵니다. 2. resources.requests 값 최적화: Pod의 requests 값을 너무 높게 설정한 것은 아닌지 확인합니다. 실제 애플리케이션이 필요한 최소한의 리소스만 요청하도록 조정하여, 노드의 자원을 효율적으로 사용하게 합니다. 저는 서비스 초기 단계에서 리소스 사용량을 정확히 몰랐을 때는 requests를 다소 넉넉하게 잡았다가, Prometheus + Grafana를 통해 실제 사용량을 모니터링하면서 점진적으로 최적화했습니다. 3. 워커 노드 추가 또는 확장: 가장 확실한 방법은 클러스터에 새로운 워커 노드를 추가하거나, 기존 노드의 사양을 확장하는 것입니다. AWS EKS의 경우 Cluster Autoscaler를 설정하여 자동으로 노드 스케일링을 관리할 수 있습니다. 4. 불필요한 Pod 제거: 만약 테스트용 Pod나 사용하지 않는 Pod가 자원을 점유하고 있다면, 이를 제거하여 공간을 확보할 수 있습니다.

6. 마치며: 쿠버네티스, 복잡함을 넘어선 가치

쿠버네티스는 처음 접할 때 방대한 개념과 복잡한 설정으로 인해 러닝 커브가 상당하다고 느낄 수 있습니다. 저 역시 그랬습니다. 하지만 일단 이 복잡한 아키텍처와 동작 원리를 이해하고 나면, 개발자는 더 이상 인프라 관리의 노예가 아닌, 온전히 '애플리케이션 개발'이라는 본연의 업무에 집중할 수 있게 됩니다.

쿠버네티스를 도입한 후, 저희 팀의 배포 시간은 평균 15분에서 2분으로 단축되었고, 롤백 시간은 10초 내외로 안정화되었습니다. 트래픽 피크 시에도 Horizontal Pod Autoscaler 덕분에 서비스가 멈추는 일 없이 안정적으로 동작하고 있습니다. 이 모든 것이 과거에는 상상하기 힘들었던 일입니다.

오늘 저의 경험담이 쿠버네티스를 배우고 있거나, 도입을 고민하는 분들께 조금이나마 도움이 되었기를 바랍니다. 다음번에는 더 깊이 있는 주제로 찾아뵙겠습니다!


핵심 요약 3줄

  1. 쿠버네티스는 수많은 컨테이너의 배포, 관리, 스케일링을 자동화하여 개발자의 운영 부담을 혁신적으로 줄여줍니다.
  2. Control Plane(API 서버, etcd, 스케줄러, 컨트롤러 매니저)과 Worker Node(kubelet, kube-proxy, 컨테이너 런타임)의 역할과 상호작용을 이해하는 것이 중요합니다.
  3. 실전에서는 ImagePullBackOff, OOMKilled, Pod Pending과 같은 흔한 문제들을 kubectl describe 명령과 리소스 최적화, 혹은 노드 확장을 통해 해결합니다.

자주 묻는 질문 (FAQ)

Q1: Kubernetes, 너무 복잡한데 꼭 써야 하나요?

A: 소규모 프로젝트나 단일 서비스라면 Docker Compose나 간단한 스크립트로도 충분할 수 있습니다. 하지만 마이크로서비스가 5개 이상이거나, 트래픽 변동성이 크고 고가용성이 필요한 서비스라면 Kubernetes는 장기적인 관점에서 훨씬 효율적인 선택입니다. 초기 학습 비용은 있지만, 투자할 가치가 충분합니다.

Q2: 온프레미스 환경에서도 Kubernetes를 구축할 수 있나요?

A: 네, 가능합니다. kubeadm, k3s, Rancher 등 다양한 툴을 사용하여 온프레미스 환경에 Kubernetes 클러스터를 구축할 수 있습니다. 다만, 클라우드 환경에 비해 네트워크, 스토리지, 로드밸런서 등의 인프라를 직접 관리해야 하는 부담이 있습니다.

Q3: Kubernetes 학습, 어디서부터 시작하는 게 좋을까요?

A: 가장 좋은 시작은 '개념 이해'와 '직접 실습' 병행입니다. minikubeKind와 같은 로컬 클러스터를 설치하여 직접 Pod, Deployment, Service를 배포해보는 것이 좋습니다. 공식 문서나 CKA(Certified Kubernetes Administrator) 학습 자료도 훌륭한 참고 자료가 됩니다. 처음부터 모든 것을 알 필요 없이, 필요한 부분을 찾아가며 익히는 방식이 효율적입니다.