exec 및 포트 포워딩을 이용한 디버깅

KubernetesBeginner
지금 연습하기

소개

이전 실습에서는 로그와 이벤트에 대해 다루었습니다. 로그와 이벤트는 문제 해결의 첫 번째 신호이지만, 때로는 실행 중인 컨테이너 내부를 직접 확인하거나 터미널에서 서비스에 일시적으로 접근해야 할 필요가 있습니다. 쿠버네티스는 이를 위해 kubectl exec와 kubectl port-forward라는 두 가지 유용한 도구를 제공합니다.

이번 실습에서는 소규모 웹 애플리케이션을 배포하고, kubectl exec를 사용하여 Pod 내부의 파일과 프로세스를 확인하며, kubectl port-forward를 사용하여 서비스에 대한 임시 로컬 연결을 생성해 보겠습니다. 실습 과정은 애플리케이션의 복잡성보다는 디버깅 기술 자체에 집중할 수 있도록 간결하게 구성되었습니다.

디버깅을 위한 네임스페이스 준비

이 단계에서는 exec 및 포트 포워딩 실습을 위한 깨끗한 네임스페이스를 준비합니다.

먼저 /home/labex/project 디렉토리에서 시작합니다. 애플리케이션 매니페스트는 이 디렉토리에 유지합니다.

mkdir -p /home/labex/project
cd /home/labex/project

쿠버네티스 API 서버에 접근 가능한지 확인합니다. 가상 머신이 막 시작되어 노드가 아직 준비되지 않았다면, 잠시 기다린 후 명령어를 다시 실행하세요.

kubectl get nodes --request-timeout=5s

예상 출력은 다음과 같습니다:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

클러스터 네트워킹 Pod 가 준비될 때까지 기다립니다. 포트 포워딩은 API 서버 연결을 사용하지만, 애플리케이션이 정상적으로 작동하려면 Pod 와 서비스가 모두 정상 상태여야 합니다.

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

예상 출력:

pod/calico-node-... condition met

ckad-prep-debug라는 이름의 네임스페이스를 생성하고 현재 네임스페이스로 설정합니다:

kubectl create namespace ckad-prep-debug
kubectl config set-context --current --namespace=ckad-prep-debug

현재 네임스페이스를 확인합니다:

kubectl config view --minify --output 'jsonpath={..namespace}'; echo

예상 출력:

ckad-prep-debug

소규모 웹 애플리케이션 배포

이 단계에서는 검사하고 접근할 간단한 애플리케이션을 배포합니다.

이 Pod 는 index.html 파일을 생성하고 8080 포트에서 BusyBox httpd를 실행합니다. debug-web이라는 서비스는 클러스터 내부의 Pod 를 80 포트로 노출합니다. kubectl port-forward는 Pod 뿐만 아니라 서비스도 대상으로 지정할 수 있기 때문에 서비스를 포함했습니다.

debug-web.yaml 파일을 생성합니다:

cat > debug-web.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: debug-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: debug-web
  template:
    metadata:
      labels:
        app: debug-web
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: web
        image: busybox:1.36
        command:
        - /bin/sh
        - -c
        - |
          mkdir -p /www
          echo "debug-web-ready" > /www/index.html
          httpd -f -p 8080 -h /www
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 1
          periodSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: debug-web
spec:
  selector:
    app: debug-web
  ports:
  - port: 80
    targetPort: 8080
EOF

매니페스트를 적용하고 배포가 완료될 때까지 기다립니다:

kubectl apply -f debug-web.yaml
kubectl rollout status deployment/debug-web --timeout=120s

예상 출력은 다음과 같습니다:

deployment.apps/debug-web created
service/debug-web created
deployment "debug-web" successfully rolled out

Pod 와 서비스를 확인합니다:

kubectl get pods -l app=debug-web
kubectl get service debug-web

두 개의 실행 중인 Pod 와 ClusterIP 서비스가 확인되어야 합니다:

debug-web-...   1/1   Running
debug-web       ClusterIP

exec 를 사용하여 실행 중인 컨테이너 검사

이 단계에서는 kubectl exec를 사용하여 애플리케이션 Pod 내부에서 명령어를 실행합니다.

kubectl exec는 Pod 가 실행 중이지만 컨테이너 내부의 상태를 확인해야 할 때 유용합니다. 이는 로그를 대체하는 도구가 아니며, 정상적인 배포 과정에서 임의로 수정하는 용도로 사용해서는 안 됩니다. 디버깅 워크플로우에서는 컨테이너 내부에서 파일, 프로세스, 환경 변수 및 로컬 네트워크 동작을 확인하는 데 도움을 줍니다.

Pod 이름 하나를 셸 변수에 저장합니다:

POD_NAME="$(kubectl get pods -l app=debug-web -o jsonpath='{.items[0].metadata.name}')"
echo "$POD_NAME"

예상 출력은 Pod 이름 하나입니다:

debug-web-...

컨테이너 내부에서 hostname을 실행합니다. 쿠버네티스는 Pod 이름을 기반으로 컨테이너의 호스트 이름을 설정하므로, 출력값은 Pod 이름과 일치해야 합니다.

kubectl exec "$POD_NAME" -- hostname

예상 출력:

debug-web-...

컨테이너 내부에서 실행 중인 프로세스 목록을 확인합니다:

kubectl exec "$POD_NAME" -- ps

예상 출력에는 httpd 프로세스가 포함되어야 합니다:

PID   USER     TIME  COMMAND
...   root     ...   httpd -f -p 8080 -h /www

웹 서버가 제공하는 파일을 읽어봅니다:

kubectl exec "$POD_NAME" -- cat /www/index.html

예상 출력:

debug-web-ready

마지막으로, 동일한 컨테이너 내부에서 웹 서버를 테스트합니다:

kubectl exec "$POD_NAME" -- wget -qO- http://127.0.0.1:8080

예상 출력:

debug-web-ready

포트 포워딩을 이용한 서비스 테스트

이 단계에서는 kubectl port-forward를 사용하여 터미널에서 서비스에 접근합니다.

포트 포워딩은 로컬 포트에서 클러스터 내의 Pod 나 서비스로 임시 터널을 생성합니다. 서비스 타입을 변경하거나, Ingress 를 생성하거나, 외부 접근을 허용할 필요 없이 빠르게 디버깅할 수 있어 유용합니다. kubectl port-forward 명령어는 중단할 때까지 계속 실행되므로, 이번 실습에서는 백그라운드에서 실행하고 테스트한 뒤 종료하겠습니다.

로컬 포트 18080에서 서비스 포트 80으로 포트 포워딩을 시작합니다. 터미널을 계속 사용할 수 있도록 출력은 /tmp/debug-web-port-forward.log 파일로 리다이렉트합니다.

kubectl port-forward service/debug-web 18080:80 > /tmp/debug-web-port-forward.log 2>&1 &
PF_PID=$!
sleep 3
cat /tmp/debug-web-port-forward.log

예상 출력은 다음과 같습니다:

Forwarding from 127.0.0.1:18080 -> 8080

로컬 포워딩 포트를 통해 요청을 보냅니다:

wget -qO- http://127.0.0.1:18080

예상 출력:

debug-web-ready

백그라운드 포트 포워딩 프로세스를 중단합니다. 로컬 포트가 계속 점유되지 않도록 정리하는 과정이 중요합니다.

kill "$PF_PID"
wait "$PF_PID" 2>/dev/null || true

포트 포워딩 로그에 연결 기록이 남았는지 확인합니다:

cat /tmp/debug-web-port-forward.log

예상 출력에는 다음 내용이 포함되어야 합니다:

Handling connection for 18080

요약

두 가지 실용적인 디버깅 도구를 실습했습니다. kubectl exec를 사용하여 실행 중인 컨테이너의 호스트 이름, 프로세스, 파일 및 로컬호스트 HTTP 응답을 확인했습니다. 또한 kubectl port-forward를 사용하여 ClusterIP 서비스에 대한 임시 로컬 연결을 생성하고, 터미널에서 테스트한 뒤 백그라운드 포워딩 프로세스를 깔끔하게 종료했습니다.