Infra
Published on

도커에서 쿠버네티스까지: 실습으로 익히는 인프라 운영의 흐름

목차

  1. 들어가며: 다시 시작하는 인프라 공부
  2. 컨테이너와 오케스트레이션: 도커와 쿠버네티스의 관계
  3. 실습 1: 도커 컨테이너 빌드와 배포
  4. 쿠버네티스: 핵심 개념 정리 (Pod, Namespace, Deployment)
  5. 실습 2: 쿠버네티스 객체 배포와 운영 검증
  6. 마치며: 선언적 인프라가 주는 가치

1. 들어가며: 다시 시작하는 인프라 공부

도커와 쿠버네티스의 개념은 이전에 공부했지만, 실제로 직접 구축하고 운영해 본 경험은 부족했다. 실습까지 병행하려 했으나 여러 사정으로 공부를 미루게 되었고, 한동안 손을 놓게 되었다. (반성합니다...)

그러다 최근 업무에서 EKS(Amazon Elastic Kubernetes Service)와 Helm을 다루게 되면서, 잊고 있던 기본기를 다시 확실히 익혀야겠다는 생각이 들었다. 그래서 《 그림으로 이해하는 도커와 쿠버네티스 》 라는 귀여운 표지의 책을 읽으며 복습하고, 정리해보기로 했다.

기본적인 이론보다는 실제 동작 흐름에 집중하여 작성했다. 실습 환경은 Docker Desktop for Mac을 기준으로 하며, Apple Silicon 환경에서 Rosetta가 필요한 경우 아래 명령어로 설치할 수 있다.

softwareupdate --install-rosetta

2. 컨테이너와 오케스트레이션: 도커와 쿠버네티스의 관계

컨테이너의 기본적인 작업 흐름은 **이미지 작성(Build) -> 이미지 배포(Ship) -> 컨테이너 실행(Run)**으로 요약된다.

  • 도커(Docker): 하나의 머신에서 컨테이너를 관리하고, 이미지를 빌드하고 공유하는 기본적인 워크플로우를 지원하는 도구다.
  • 쿠버네티스(Kubernetes): **여러 대의 머신(클러스터)**으로 구성된 환경에서 수많은 컨테이너의 실행, 확장, 복구를 자동화하는 '오케스트레이션 엔진'이다.

즉, 도커가 애플리케이션을 담는 '표준화된 상자'를 만든다면, 쿠버네티스는 그 상자들을 효율적으로 쌓고 관리하는 '물류 시스템' 역할을 한다.

3. 실습 1: 도커 컨테이너 빌드와 배포

먼저 로컬에서 컨테이너를 빌드하고 실행한 뒤, 이를 원격 레지스트리에 공유하는 과정을 실습한다.

1) Build / Run: 컨테이너 생성과 실행

"Hello, Docker!"를 출력하는 간단한 셸 스크립트를 컨테이너에서 실행해 본다.

hello.sh

#!/bin/sh
echo "Hello, Docker!"
exec sleep infinity # 컨테이너가 즉시 종료되지 않도록 유지

Dockerfile

FROM alpine:3.23
WORKDIR /app
COPY hello.sh /app/hello.sh
RUN chmod +x hello.sh
CMD ["./hello.sh"]

이미지를 빌드하고 컨테이너를 실행한다.

docker build -t hello-docker:v1 .
docker run --name hello-container hello-docker:v1
Docker Desktop에서 실행 결과 확인

2) Ship: 이미지 공유 (Docker Hub)

작성한 이미지를 다른 호스트와 공유하기 위해 Docker Hub 레지스트리를 이용한다.

docker login
docker tag hello-docker:v1 parklego/hello-docker:v1
docker push parklego/hello-docker:v1

4. 쿠버네티스: 핵심 개념 정리

쿠버네티스 구조 및 흐름

쿠버네티스의 핵심은 선언적(Declarative) 관리다. 내가 원하는 이상적인 상태(Desired State)를 선언하면, 시스템이 이를 실현하고 유지해준다.

Pod: 최소 실행 단위

  • 파드는 하나 이상의 컨테이너를 담는 바구니와 같다.
  • 파드끼리는 각자의 고유 IP로 통신하며, 파드 내부의 컨테이너들은 localhost를 통해 서로 통신할 수 있다.
  • 보통 1 파드 = 1 컨테이너로 사용하지만, 로그 수집기 등을 함께 배포할 때 여러 컨테이너를 하나로 묶기도 한다.

Namespace: 논리적 격리

  • 클러스터 하나를 여러 개의 논리적인 단위로 나누어 사용하는 구역이다.
  • 개발(dev), 운영(prod) 등 용도에 따라 리소스를 분리하여 관리할 수 있으며, 이름 범위(Scope)를 한정짓는 역할을 한다.

Deployment: 복제와 상태 관리

  • 파드의 개수와 상태를 유지하는 관리자다.
  • 노드 장애나 파드 동작 불량 시 자동으로 새로운 파드를 실행하는 셀프 힐링(Self-healing) 기능과, 가동 중단 없이 업데이트하는 롤링 업데이트를 지원한다.

Context 관리

kubectl 명령어는 클러스터 내부가 아니라 내 로컬 PC에서 실행하는 명령어이다. 실무에서는 여러 개의 클러스터를 다루게 되는데, 이때 조종기가 어떤 클러스터를 가리키고 있는지 결정하는 것이 컨텍스트(Context)다.

# 현재 연결된 클러스터 확인
kubectl config current-context
# 클러스터 변경
kubectl config use-context [클러스터-이름]

⚠️ 주의: 중요한 작업을 하기 전에는 항상 현재 컨텍스트를 확인하는 습관이 중요하다.

5. 실습 2: 쿠버네티스 객체 배포와 운영 검증

이론을 바탕으로 로컬 환경(Docker Desktop + kind)에서 실제 배포와 통신 과정을 실습한다.

1) 네임스페이스 생성과 배포 (YAML)

리소스를 격리할 네임스페이스를 생성하고, 미리 빌드한 이미지를 사용하는 Deployment 파일을 적용한다.

kubectl create namespace dev-test

k8s/deployment.yaml

apiVersion: apps/v1
kind: Deployment # 리소스 종류 정의
metadata:
  name: hello-deployment
  namespace: dev-test
spec: # 상세 명세
  replicas: 3 # 항상 유지할 파드 개수
  selector:
    matchLabels:
      app: hello-app
  template:
    metadata:
      labels:
        app: hello-app
    spec:
      containers:
        - name: hello-container
          image: parklego/hello-docker:v1
          ports:
            - containerPort: 8080 # 내부 사용 포트
kubectl apply -f k8s/deployment.yaml # 설정 적용
kubectl get pods -n dev-test -o wide # 상태 확인

2) 내부 상태 확인 및 셀프 힐링 테스트

앱의 정상 동작 여부를 확인하고, 장애 발생 시 쿠버네티스의 대응을 직접 체감해 본다.

  • 로그 확인: kubectl logs [파드-이름] -n dev-test를 통해 "Hello, World!" 출력을 확인한다.
  • 셀프 힐링: kubectl delete pod [파드-이름] -n dev-test로 파드를 강제 삭제해 본다. Deployment가 즉시 새로운 파드를 생성하여 설정된 개수(3개)를 유지하는 것을 볼 수 있다.

3) 외부 연결 테스트 (Port-forward)

내 로컬 PC에서 파드 내부로 접근하기 위해 포트 포워딩을 사용한다. 웹 서버가 내장된 nginx 이미지를 활용해 브라우저에서 실제 접속을 확인한다.

# nginx 파드 생성 및 포트 포워딩 실행 (로컬 8091 -> 파드 80)
kubectl run nginx-test --image=nginx -n dev-test
kubectl port-forward pod/nginx-test 8091:80 -n dev-test

브라우저 주소창에 localhost:8091 입력 시 'Welcome to nginx!' 페이지가 나오면 성공이다.

6. 마치며: 선언적 인프라가 주는 가치

도커 이미지를 만드는 기초부터 쿠버네티스의 핵심 원리까지 전체적인 흐름을 정리해 보았다. 이번 실습을 통해 얻은 가장 큰 인사이트는 다음과 같다.

  • 인프라의 선언적 관리: '어떻게 실행할지'가 아니라 '어떤 상태가 이상적인지'를 선언함으로써 운영의 복잡성을 획기적으로 낮출 수 있다.
  • 안정성과 접근성: 셀프 힐링을 통해 시스템의 신뢰성을 확보하고, 포트 포워딩과 같은 도구로 내부 리소스에 안전하게 접근하는 법을 익혔다.

도커가 '애플리케이션을 담는 표준'이라면, 쿠버네티스는 그 표준을 '어떻게 안정적으로 운영할 것인가'에 대한 해답이다. 이번 실습은 향후 EKS나 Helm 같은 복잡한 도구를 다룰 때 단단한 기초가 되어줄 것이다.