로컬 애플리케이션 이미지 빌드 및 실행

DockerBeginner
지금 연습하기

소개

이전 배포 실습에서는 레지스트리나 클러스터 캐시에 이미 존재하는 이미지를 사용했습니다. 애플리케이션 개발자는 로컬 이미지 워크플로우를 이해하는 것 또한 중요합니다. 즉, 애플리케이션 파일로부터 이미지를 빌드하고, 해당 이미지를 Kubernetes 노드에서 사용할 수 있게 만든 다음, 배포 (Deployment) 가 이를 사용하도록 설정하는 과정입니다.

이번 실습에서는 Minikube 를 사용하여 소규모 웹 애플리케이션 이미지를 로컬에서 빌드하고, imagePullPolicy: Never를 사용하여 배포한 뒤, 롤링 업데이트를 위해 애플리케이션을 새로운 이미지 태그로 다시 빌드해 보겠습니다. 이는 애플리케이션 아티팩트를 이를 실행하는 Kubernetes 워크로드에 연결하는 일반적인 CKAD 스타일의 작업을 반영합니다.

로컬 이미지 작업 공간 준비

이 단계에서는 깨끗한 네임스페이스를 준비하고 이미지 빌드 경로를 사용할 수 있는지 확인합니다.

/home/labex/project에서 시작합니다. 이 디렉토리에 애플리케이션 파일, 이미지 빌드 파일 및 Kubernetes 매니페스트를 생성할 것입니다.

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

Kubernetes API 서버에 접근 가능한지 확인합니다. VM 이 막 시작되어 노드가 아직 준비되지 않았다면, 몇 초 기다린 후 명령어를 다시 실행하세요.

kubectl get nodes --request-timeout=5s

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

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

클러스터 네트워킹 Pod 가 준비 (Ready) 상태가 될 때까지 기다립니다. 애플리케이션은 서비스를 통해 테스트되므로 네트워킹이 정상 상태여야 합니다.

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

예상 출력:

pod/calico-node-... condition met

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

kubectl create namespace ckad-prep-local-image
kubectl config set-context --current --namespace=ckad-prep-local-image

네임스페이스를 확인하고 Minikube 이미지 명령어를 점검합니다. minikube image build 명령어는 Minikube 프로필을 위해 직접 이미지를 빌드하므로, 레지스트리에 푸시하지 않고도 Pod 에서 바로 사용할 수 있습니다.

kubectl config view --minify --output 'jsonpath={..namespace}'; echo
minikube image build --help | sed -n '1,4p'

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

ckad-prep-local-image
Build a container image...

첫 번째 로컬 이미지 빌드

이 단계에서는 소규모 웹 애플리케이션을 생성하고 local-message-app:v1으로 빌드합니다.

이 이미지는 BusyBox httpd를 사용하여 정적 파일 하나를 서비스합니다. 파일 내용은 눈에 보이는 애플리케이션 버전이므로, 나중에 Kubernetes 에서 어떤 이미지 태그가 실행 중인지 확인할 수 있습니다.

애플리케이션 디렉토리와 첫 번째 웹 페이지를 생성합니다:

mkdir -p local-message-app/www
cat > local-message-app/www/index.html <<'EOF'
local-image-v1
EOF

Dockerfile을 생성합니다. COPY 명령어는 로컬의 www/index.html 파일을 이미지 내부로 복사하며, CMD는 컨테이너가 실행될 때 포트 8080에서 BusyBox 웹 서버를 시작합니다.

cat > local-message-app/Dockerfile <<'EOF'
FROM busybox:1.36
COPY www/index.html /www/index.html
EXPOSE 8080
CMD ["httpd", "-f", "-p", "8080", "-h", "/www"]
EOF

활성 Minikube 프로필을 위해 이미지를 빌드합니다. -t 옵션은 이미지에 로컬 태그를 부여하며, 마지막 경로는 빌드 컨텍스트 디렉토리입니다.

minikube image build --profile labex-v135 -t local-message-app:v1 ./local-message-app

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

naming to docker.io/library/local-message-app:v1 done

Minikube 에 알려진 이미지 목록을 나열하고 새로운 태그를 확인합니다:

minikube image ls --profile labex-v135 | grep local-message-app

예상 출력:

docker.io/library/local-message-app:v1

로컬 이미지 배포

이 단계에서는 Kubernetes 에서 로컬 이미지를 실행합니다.

local-message-app:v1은 원격 레지스트리에 푸시되지 않았으므로, 배포 시 imagePullPolicy: Never를 사용합니다. 이는 kubelet 에게 네트워크에서 이미지를 가져오려 하지 말고 노드에 이미 존재하는 이미지를 사용하도록 지시합니다.

local-image-app.yaml을 생성합니다:

cat > local-image-app.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: local-message-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: local-message-app
  template:
    metadata:
      labels:
        app: local-message-app
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: web
        image: local-message-app:v1
        imagePullPolicy: Never
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 1
          periodSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: local-message-app
spec:
  selector:
    app: local-message-app
  ports:
  - port: 80
    targetPort: 8080
EOF

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

kubectl apply -f local-image-app.yaml
kubectl rollout status deployment/local-message-app --timeout=120s

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

deployment.apps/local-message-app created
service/local-message-app created
deployment "local-message-app" successfully rolled out

클라이언트 Pod 를 생성하고 서비스를 테스트합니다:

kubectl run image-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --for=condition=Ready pod/image-client --timeout=120s
kubectl exec image-client -- wget -qO- http://local-message-app

예상 출력:

local-image-v1

새로운 로컬 이미지 빌드 및 롤아웃

이 단계에서는 애플리케이션을 두 번째 이미지 태그로 다시 빌드하고 배포를 해당 태그로 롤링 업데이트합니다.

디스크의 파일을 변경한다고 해서 실행 중인 이미지가 바뀌지는 않습니다. 새로운 이미지 태그를 빌드하고 배포를 업데이트하여 Kubernetes 가 해당 태그로 새로운 Pod 를 시작하도록 해야 합니다. 이렇게 하면 버전 변경 사항을 명확히 확인할 수 있으며, 변경 가능한 로컬 이미지 콘텐츠에 의존하는 것을 방지할 수 있습니다.

웹 페이지 내용을 버전 2 로 교체합니다:

cat > local-message-app/www/index.html <<'EOF'
local-image-v2
EOF

두 번째 이미지 태그를 빌드합니다:

minikube image build --profile labex-v135 -t local-message-app:v2 ./local-message-app

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

naming to docker.io/library/local-message-app:v2 done

배포 이미지를 업데이트하고 롤아웃이 완료될 때까지 기다립니다:

kubectl set image deployment/local-message-app web=local-message-app:v2
kubectl rollout status deployment/local-message-app --timeout=120s

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

deployment.apps/local-message-app image updated
deployment "local-message-app" successfully rolled out

이미지 태그와 애플리케이션 응답을 모두 확인합니다:

kubectl get deployment local-message-app -o jsonpath='{.spec.template.spec.containers[0].image}'; echo
kubectl exec image-client -- wget -qO- http://local-message-app

예상 출력:

local-message-app:v2
local-image-v2

요약

Minikube 프로필을 위한 소규모 애플리케이션 이미지를 빌드하고, imagePullPolicy: Never를 사용하여 배포한 뒤, Kubernetes 서비스를 통해 테스트했습니다. 그 후 애플리케이션을 새로운 이미지 태그로 다시 빌드하여 배포를 롤링 업데이트했습니다. 핵심 워크플로우는 Pod 가 실행되기 전에 노드에서 이미지를 사용할 수 있도록 준비하는 것입니다.