컨테이너화의 힘: 도커와 쿠버네티스로 현대 애플리케이션 배포 마스터하기

1. 개념 소개

현대 소프트웨어 개발에서 "내 컴퓨터에서는 잘 되는데, 서버에서는 왜 안 될까요?"라는 질문은 악몽과도 같습니다. 개발 환경과 운영 환경의 미묘한 차이가 복잡한 버그로 이어지고, 이는 시간과 비용 낭비로 직결됩니다. 이러한 문제를 해결하기 위해 등장한 혁신적인 기술이 바로 컨테이너화이며, 그 중심에는 **도커(Docker)**와 **쿠버네티스(Kubernetes)**가 있습니다.
정의: 도커와 쿠버네티스
- 컨테이너(Container): 애플리케이션과 그 실행에 필요한 모든 것(코드, 런타임, 시스템 도구, 라이브러리 등)을 하나로 묶어 표준화된 방식으로 배포하고 실행할 수 있도록 만든 독립적인 실행 환경입니다. 마치 배송 컨테이너처럼, 안에 무엇이 들어있든 상관없이 표준화된 방식으로 운송하고 적재할 수 있게 해줍니다.
- 도커(Docker): 컨테이너 기술을 쉽고 효율적으로 사용할 수 있도록 도와주는 오픈소스 플랫폼입니다. 도커는 컨테이너 이미지를 생성하고, 컨테이너를 실행하며, 컨테이너를 관리하는 데 필요한 도구들을 제공합니다.
- 쿠버네티스(Kubernetes, K8s): 컨테이너화된 워크로드와 서비스를 관리하기 위한 이식성이 좋고, 확장 가능하며, 오픈소스인 플랫폼입니다. 수많은 컨테이너를 자동으로 배포하고, 스케일링하며, 관리하고, 복구하는 '오케스트레이션' 시스템의 대명사입니다.
탄생 배경: "내 컴퓨터에서는 잘 되는데..."와 가상 머신의 한계
과거에는 애플리케이션을 배포할 때, 각 서버에 필요한 운영체제와 런타임, 라이브러리 등을 수동으로 설치하고 구성해야 했습니다. 이는 환경 설정 오류, 의존성 충돌, 버전 불일치 등으로 인해 개발 환경과 운영 환경 간의 불일치를 초래하는 주범이었습니다. 이러한 문제를 해결하기 위해 **가상 머신(Virtual Machine, VM)**이 등장했습니다. 가상 머신은 하드웨어를 가상화하여 그 위에 독립적인 운영체제(Guest OS)를 올리는 방식입니다.
가상 머신은 환경 격리에는 효과적이었지만, 몇 가지 한계가 있었습니다.
- 높은 자원 소모: 각 VM마다 별도의 Guest OS를 실행해야 하므로 메모리와 CPU 자원 소모가 컸습니다.
- 느린 시작 시간: Guest OS 부팅에 시간이 걸려 애플리케이션 시작이 느렸습니다.
- 무거운 이미지 크기: VM 이미지는 GB 단위로 매우 커서 배포 및 관리가 번거로웠습니다.
마이크로서비스 아키텍처의 부상과 함께, 수많은 서비스들을 빠르고 가볍게 배포하고 관리해야 할 필요성이 증대되었고, 이러한 요구사항을 충족시키기 위해 컨테이너 기술이 각광받기 시작했습니다. 도커는 이러한 컨테이너 기술을 일반 개발자가 쉽게 사용할 수 있도록 대중화시켰고, 쿠버네티스는 수많은 도커 컨테이너를 효율적으로 관리하는 표준으로 자리매김했습니다.
왜 중요한가?
컨테이너화 기술은 현대 소프트웨어 개발 및 운영(DevOps)에서 필수적인 요소가 되었습니다.
- 개발 환경 일관성: 개발, 테스트, 운영 환경을 컨테이너로 동일하게 구성하여 "내 컴퓨터에서는 잘 되는데..." 문제를 근본적으로 해결합니다.
- 빠른 배포 및 확장: 가볍고 빠르게 컨테이너를 시작하고 종료할 수 있어, 서비스 배포 시간을 단축하고 트래픽 변화에 유연하게 대응할 수 있습니다.
- 자원 효율성: VM에 비해 훨씬 적은 자원으로 더 많은 애플리케이션을 실행할 수 있어 인프라 비용을 절감합니다.
- 유연한 마이크로서비스 아키텍처: 각 마이크로서비스를 독립적인 컨테이너로 패키징하여 개발, 배포, 확장을 독립적으로 수행할 수 있게 합니다.
- 장애 복구 및 고가용성: 쿠버네티스와 같은 오케스트레이터는 컨테이너 장애 시 자동으로 새로운 컨테이너를 시작하여 서비스 중단을 최소화합니다.
2. 핵심 원리 설명

컨테이너 vs 가상 머신: 핵심 차이점
가장 중요한 차이점은 운영체제 커널 공유 여부입니다.
- 가상 머신: 하이퍼바이저 위에서 각각의 Guest OS를 포함하여 완벽하게 격리된 환경을 제공합니다. 이는 높은 격리 수준을 보장하지만, 각 VM이 OS를 가지고 있어 무겁고 자원 소모가 큽니다.
- 컨테이너: 호스트 운영체제의 커널을 공유하며, 그 위에 애플리케이션 실행에 필요한 런타임과 라이브러리만을 격리된 공간에 포함합니다. Guest OS가 필요 없으므로 가볍고 빠르게 동작하며 자원 효율성이 높습니다.
| 특징 | 가상 머신 (VM) | 컨테이너 (Docker) | | :----------- | :----------------------------------- | :-------------------------------------- | | 격리 수준 | 하드웨어 가상화, OS 레벨 완벽 격리 | OS 커널 공유, 프로세스 레벨 격리 | | OS 포함 여부 | Guest OS 포함 | Guest OS 없음, 호스트 OS 커널 공유 | | 자원 소모 | 높음 (각 VM마다 OS 자원) | 낮음 (공유 커널, 필요한 라이브러리만) | | 시작 시간 | 느림 (OS 부팅 시간) | 빠름 (초 단위) | | 이미지 크기 | 무거움 (GB 단위) | 가벼움 (MB 단위) | | 이식성 | 높음 (하드웨어 종속성 적음) | 높음 (OS 커널이 같으면 어디든 실행 가능) |
도커의 핵심: 이미지와 컨테이너
도커는 크게 **이미지(Image)**와 **컨테이너(Container)**라는 두 가지 핵심 개념으로 동작합니다.
- 도커 이미지(Docker Image): 애플리케이션을 실행하는 데 필요한 모든 파일과 설정을 포함하는 읽기 전용 템플릿입니다. 즉, 애플리케이션의 "청사진" 또는 "설계도"입니다. 이미지는 여러 개의 레이어로 구성되어 효율적으로 저장되고 재사용됩니다. 예를 들어, 파이썬 기반 웹 애플리케이션 이미지는
Python 런타임 레이어,Flask 라이브러리 레이어,내 애플리케이션 코드 레이어등으로 구성될 수 있습니다. - 도커 컨테이너(Docker Container): 도커 이미지를 기반으로 실행되는 독립적인 프로세스입니다. 이미지가 정적인 청사진이라면, 컨테이너는 그 청사진을 바탕으로 실제로 동작하는 "실체"입니다. 컨테이너는 격리된 파일 시스템, 네트워크 인터페이스, 프로세스 공간 등을 가지고 있으며, 호스트 시스템과 분리되어 실행됩니다.
Dockerfile: 도커 이미지를 빌드하기 위한 명령어들을 모아 놓은 텍스트 파일입니다. 이 파일을 통해 어떤 OS 기반 위에, 어떤 파일을 복사하고, 어떤 라이브러리를 설치하며, 어떤 명령어로 애플리케이션을 실행할지 등을 명시합니다.
쿠버네티스의 역할: 컨테이너 오케스트레이션
도커가 개별 컨테이너를 만들고 실행하는 도구라면, 쿠버네티스는 수많은 컨테이너를 대규모로 관리하는 "지휘자" 또는 "항만 관리 시스템"입니다. 컨테이너 오케스트레이션은 컨테이너의 배포, 스케일링, 로드 밸런싱, 네트워크 구성, 자가 복구 등을 자동화하는 것을 의미합니다.
쿠버네티스의 주요 개념들은 다음과 같습니다:
- Pod: 쿠버네티스에서 배포할 수 있는 가장 작은 단위입니다. 하나 이상의 컨테이너와 스토리지, 네트워크 자원 등을 포함합니다. 파드 내의 컨테이너들은 네트워크와 스토리지를 공유합니다.
- Deployment: 파드의 집합을 관리하며, 선언된 상태(Desired State)를 유지합니다. 예를 들어, "이 애플리케이션의 파드를 항상 3개 유지해라"라고 설정하면, 쿠버네티스는 파드가 죽으면 자동으로 새로운 파드를 생성하여 3개를 유지합니다.
- Service: 파드 집합에 대한 네트워크 접근을 추상화하고 로드 밸런싱을 제공합니다. 파드가 생성되거나 삭제되어도 서비스는 항상 동일한 IP 주소와 포트로 접근할 수 있게 해줍니다.
- Node: 컨테이너를 실행하는 물리 또는 가상 머신입니다.
- Cluster: 여러 개의 노드로 구성된 쿠버네티스 시스템 전체를 의미합니다.
비유: 컨테이너화 기술을 이해하기 위한 가장 흔하고 강력한 비유는 배송 컨테이너입니다.
- 애플리케이션: 화물 (옷, 전자제품, 음식 등 다양한 종류)
- 도커 이미지: 표준화된 배송 컨테이너 (20피트, 40피트 등 규격) - 내용물이 무엇이든 상관없이 동일한 방식으로 다룰 수 있습니다.
- 도커 컨테이너: 실제로 배송 중인 컨테이너 - 이미지(규격)를 기반으로 생성되어 실행됩니다.
- 도커: 컨테이너를 만들고, 싣고 내리는 크레인 및 운반 도구
- 쿠버네티스: 항만 관리 시스템 - 수많은 컨테이너선(서버)이 드나들고, 수백, 수천 개의 컨테이너(애플리케이션)를 효율적으로 적재하고, 운반하고, 관리하며, 고장 나면 자동으로 대체하는 복잡한 시스템을 자동화합니다.
3. 코드 예제 2개 (Python)
예제 1: 간단한 Python Flask 웹 앱 도커라이징
간단한 Flask 애플리케이션을 만들고, 이를 도커 이미지로 빌드하여 컨테이너로 실행해 보겠습니다.
1. Flask 애플리케이션 (app.py)
# app.py
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
message = os.getenv("MESSAGE", "Hello from Docker!")
return f"<h1>{message}</h1>"
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
2. 의존성 파일 (requirements.txt)
Flask==2.3.2
3. Dockerfile 작성
# Dockerfile
# 1. Python 공식 이미지 중 3.9-slim 버전을 베이스 이미지로 사용합니다.
# 'slim' 태그는 불필요한 패키지를 줄여 이미지 크기를 최소화합니다.
FROM python:3.9-slim
# 2. 컨테이너 내부의 작업 디렉토리를 /app으로 설정합니다.
# 이후 모든 명령어는 이 디렉토리에서 실행됩니다.
WORKDIR /app
# 3. 호스트 시스템의 requirements.txt 파일을 컨테이너의 /app 디렉토리로 복사합니다.
COPY requirements.txt .
# 4. requirements.txt에 명시된 파이썬 패키지들을 설치합니다.
# --no-cache-dir: 캐시 파일을 저장하지 않아 이미지 크기를 줄입니다.
# -r: requirements 파일에서 패키지를 읽어 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# 5. 호스트 시스템의 현재 디렉토리(.)에 있는 모든 파일을 컨테이너의 /app 디렉토리로 복사합니다.
# app.py 파일이 이때 복사됩니다.
COPY . .
# 6. 애플리케이션이 5000번 포트를 사용함을 외부에 알립니다. (문서화 목적)
EXPOSE 5000
# 7. 컨테이너가 시작될 때 실행될 기본 명령어를 정의합니다.
# Flask 개발 서버를 0.0.0.0 (모든 네트워크 인터페이스)과 5000번 포트에서 실행합니다.
CMD ["python", "app.py"]
4. 도커 이미지 빌드 및 실행
터미널에서 app.py, requirements.txt, Dockerfile이 있는 디렉토리로 이동하여 다음 명령어를 실행합니다.
# 도커 이미지 빌드: 현재 디렉토리(.)의 Dockerfile을 사용하여 'my-flask-app'이라는 이름으로 이미지 생성
docker build -t my-flask-app .
# 도커 컨테이너 실행:
# -p 5000:5000: 호스트의 5000번 포트를 컨테이너의 5000번 포트에 연결 (포트 포워딩)
# -d: 백그라운드에서 컨테이너 실행 (detached mode)
# --name my-flask-container: 컨테이너에 'my-flask-container'라는 이름 부여
# my-flask-app: 사용할 이미지 이름
docker run -p 5000:5000 -d --name my-flask-container my-flask-app
# 환경 변수를 전달하여 실행해보기
docker run -p 5000:5000 -d --name my-flask-container-env -e MESSAGE="Hello from env var!" my-flask-app
# 실행 중인 컨테이너 목록 확인
docker ps
# 컨테이너 로그 확인
docker logs my-flask-container
# 컨테이너 중지 및 삭제
docker stop my-flask-container
docker rm my-flask-container
docker stop my-flask-container-env
docker rm my-flask-container-env
이제 웹 브라우저에서 http://localhost:5000에 접속하면 "Hello from Docker!" 메시지를 볼 수 있습니다.
예제 2: Docker Compose를 이용한 Flask 앱 + Redis 연동
이번에는 Redis를 캐시로 사용하는 Flask 애플리케이션을 만들고, Docker Compose를 사용하여 두 개의 서비스(Flask 앱, Redis)를 함께 관리해 보겠습니다.
1. Flask 애플리케이션 (app.py)
# app.py
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
# Redis 연결 정보를 환경 변수에서 가져오거나 기본값 사용
redis_host = os.getenv("REDIS_HOST", "redis")
redis_port = int(os.getenv("REDIS_PORT", 6379))
redis = Redis(host=redis_host, port=redis_port, decode_responses=True)
@app.route('/')
def hello():
# Redis에서 방문 횟수를 증가시키고 가져옵니다.
visits = redis.incr('visits')
return f"<h1>Hello from Flask! This page has been visited {visits} times.</h1>"
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
2. 의존성 파일 (requirements.txt)
Flask==2.3.2
redis==4.5.1
3. Dockerfile (예제 1과 동일)
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
4. Docker Compose 파일 (docker-compose.yml)
# docker-compose.yml
version: '3.8' # Docker Compose 파일 형식 버전
services:
web: # 웹 애플리케이션 서비스 정의
build: . # 현재 디렉토리의 Dockerfile을 사용하여 이미지 빌드
ports:
- "5000:5000" # 호스트의 5000번 포트를 컨테이너의 5000번 포트에 연결
environment: # 컨테이너에 전달할 환경 변수
- REDIS_HOST=redis # Redis 서비스의 호스트 이름을 'redis'로 설정 (서비스 이름과 동일)
depends_on: # web 서비스가 redis 서비스에 의존함을 명시
- redis
redis: # Redis 데이터베이스 서비스 정의
image: "redis:latest" # Docker Hub에서 공식 Redis 이미지를 사용
ports:
- "6379:6379" # Redis 기본 포트
# volumes: # 영속적인 데이터 저장을 위해 볼륨을 사용할 수 있습니다.
# - redis_data:/data # 호스트의 redis_data 볼륨을 컨테이너의 /data 디렉토리에 마운트
# volumes: # 볼륨 정의 (필요시 주석 해제)
# redis_data:
depends_on은 서비스 시작 순서를 보장하지만, Redis가 완전히 준비될 때까지 기다리지는 않습니다. 실제 운영 환경에서는 헬스 체크나 초기화 스크립트 등을 사용하여 의존성 준비 완료를 확인하는 것이 좋습니다.
5. Docker Compose 실행
app.py, requirements.txt, Dockerfile, docker-compose.yml이 모두 있는 디렉토리에서 다음 명령어를 실행합니다.
# Docker Compose로 모든 서비스 빌드 및 실행
docker-compose up --build -d
# 실행 중인 서비스 목록 확인
docker-compose ps
# 서비스 로그 확인 (예: web 서비스)
docker-compose logs web
# 서비스 중지 및 삭제 (네트워크, 볼륨도 함께 삭제)
docker-compose down
이제 http://localhost:5000에 접속하여 페이지를 새로고침할 때마다 방문 횟수가 증가하는 것을 확인할 수 있습니다. 이는 Flask 앱이 Redis 컨테이너에 성공적으로 연결되었음을 의미합니다.
4. 실무 적용 사례
컨테이너화 기술은 소프트웨어 개발의 거의 모든 단계에 혁신을 가져왔습니다.
- 개발 환경 구축: 팀원 모두가 동일한 개발 환경을 컨테이너로 공유하여 "내 컴퓨터에서는 잘 되는데..." 문제를 없앱니다. 새로운 개발자가 합류했을 때도, 컨테이너 환경을 한 번에 설정하여 온보딩 시간을 단축할 수 있습니다.
- CI/CD 파이프라인: Jenkins, GitLab CI, GitHub Actions 등 CI/CD 도구에서 애플리케이션을 컨테이너 이미지로 빌드하고, 테스트 컨테이너를 실행하며, 최종적으로 컨테이너 이미지를 레지스트리(Docker Hub, ECR 등)에 푸시한 뒤, 쿠버네티스 같은 오케스트레이터로 배포하는 자동화된 파이프라인을 구축할 수 있습니다.
- 마이크로서비스 아키텍처: 각 마이크로서비스를 독립적인 컨테이너로 패키징하여, 서비스별로 다른 기술 스택을 사용하거나 독립적으로 배포 및 확장하는 것이 가능해집니다. 쿠버네티스는 이러한 수많은 마이크로서비스 컨테이너를 효율적으로 관리하는 데 핵심적인 역할을 합니다.
- 클라우드 네이티브 개발: AWS, Azure, GCP와 같은 클라우드 공급자들은 컨테이너 기반의 서비스를 적극적으로 지원합니다 (예: AWS EKS, Azure AKS, Google GKE). 컨테이너화된 애플리케이션은 클라우드 환경에서 뛰어난 이식성과 확장성을 제공하며, 클라우드 자원을 더욱 효율적으로 사용할 수 있게 합니다.
- 배치 처리 및 잡(Job) 실행: 특정 작업을 수행하고 종료되는 배치성 스크립트나 잡(Job)도 컨테이너로 만들어 실행하면, 환경 구성에 대한 고민 없이 일관된 방식으로 작업을 수행할 수 있습니다.
5. 자주 하는 실수와 해결법
컨테이너 기술은 강력하지만, 잘못 사용하면 오히려 문제를 야기할 수 있습니다.
-
매우 큰 도커 이미지 크기:
- 문제: 불필요한 파일이나 패키지가 이미지에 포함되어 이미지 크기가 커지고, 빌드 및 배포 시간이 길어지며, 보안 취약점 노출 위험이 증가합니다.
- 해결법:
.dockerignore사용:.gitignore와 유사하게, 이미지 빌드 시 제외할 파일이나 디렉토리를 지정합니다. (예:.git,__pycache__,.env)- 멀티 스테이지 빌드(Multi-stage build): 빌드 시에만 필요한 도구나 의존성(예: 컴파일러)을 첫 번째 스테이지에서 사용하고, 최종 이미지에는 실제 실행에 필요한 최소한의 결과물만 복사하여 이미지 크기를 대폭 줄입니다.
slim또는alpine베이스 이미지 사용:python:3.9-slim이나node:16-alpine처럼 가벼운 베이스 이미지를 선택합니다.- 레이어 캐싱 활용: Dockerfile 명령어 순서를 잘 조정하여 자주 변경되지 않는 부분(예: 의존성 설치)을 앞쪽에 배치하여 빌드 속도를 높입니다.
-
보안 취약점:
- 문제: 컨테이너 내부에서
root권한으로 프로세스를 실행하거나, 이미지에 알려진 취약점이 있는 패키지를 포함하는 경우 보안 문제가 발생할 수 있습니다. - 해결법:
USER명령어 사용: Dockerfile에서USER명령어를 사용하여root가 아닌 일반 사용자 권한으로 애플리케이션을 실행합니다.- 이미지 취약점 스캔: Docker Scan, Clair, Trivy 등 도구를 사용하여 이미지에 포함된 패키지의 취약점을 정기적으로 스캔합니다.
- 최신 버전 유지: 베이스 이미지와 라이브러리를 최신 상태로 유지하여 알려진 취약점을 패치합니다.
- 문제: 컨테이너 내부에서
-
상태 관리 문제 (데이터 영속성):
- 문제: 컨테이너는 기본적으로 휘발성이므로, 컨테이너가 삭제되면 내부의 모든 데이터가 사라집니다. 데이터베이스, 로그 파일 등의 중요한 데이터가 손실될 수 있습니다.
- 해결법:
- 볼륨(Volume) 사용: 호스트 머신의 특정 디렉토리나 도커가 관리하는 볼륨을 컨테이너에 마운트하여 데이터를 영속적으로 저장합니다. Docker Compose 예제에서 주석 처리된
volumes부분을 참고하세요. - 외부 저장소 사용: 데이터베이스 서버, 클라우드 스토리지(S3, GCS) 등 컨테이너 외부에 데이터를 저장합니다.
- 볼륨(Volume) 사용: 호스트 머신의 특정 디렉토리나 도커가 관리하는 볼륨을 컨테이너에 마운트하여 데이터를 영속적으로 저장합니다. Docker Compose 예제에서 주석 처리된
-
환경 변수 및 민감 정보 관리 미흡:
- 문제: 데이터베이스 비밀번호, API 키 등 민감 정보를 Dockerfile에 직접 포함하거나, 컨테이너 환경 변수로 평문으로 노출하는 경우 보안 위험이 있습니다.
- 해결법:
- Docker Secrets 또는 Kubernetes Secrets 사용: 민감 정보를 암호화하여 안전하게 컨테이너에 전달하는 메커니즘을 사용합니다.
- 환경 변수 주입:
docker run -e KEY=VALUE또는 Docker Compose의environment섹션을 사용하여 환경 변수를 주입하되, 민감 정보는 별도 관리 시스템(HashiCorp Vault 등)과 연동을 고려합니다.
-
로깅 및 모니터링 부족:
- 문제: 컨테이너화된 애플리케이션은 분산 시스템의 특성을 가지므로, 개별 컨테이너의 로그를 확인하거나 전체 시스템의 상태를 파악하기 어려울 수 있습니다.
- 해결법:
- 표준 출력/오류로 로그 출력: 애플리케이션 로그를 파일로 저장하기보다는
stdout(표준 출력) 및stderr(표준 에러)로 출력하도록 설정합니다. 도커와 쿠버네티스는 이를 자동으로 수집합니다. - 중앙 집중식 로깅 시스템: ELK Stack (Elasticsearch, Logstash, Kibana) 또는 Grafana Loki와 같은 시스템을 구축하여 모든 컨테이너의 로그를 한곳에 모아 관리하고 분석합니다.
- 모니터링 도구 사용: Prometheus, Grafana, Datadog 등 도구를 사용하여 컨테이너 및 클러스터의 메트릭(CPU, 메모리 사용량 등)을 수집하고 시각화하여 시스템 상태를 지속적으로 모니터링합니다.
- 표준 출력/오류로 로그 출력: 애플리케이션 로그를 파일로 저장하기보다는
6. 더 공부할 리소스 추천
컨테이너 기술은 빠르게 발전하고 있으며, 그 깊이가 상당합니다. 다음 리소스들을 통해 지속적으로 학습하고 실습하는 것이 중요합니다.
- Docker 공식 문서: https://docs.docker.com/
- 가장 정확하고 최신 정보를 얻을 수 있는 자료입니다. Concepts, Dockerfile reference 등을 꼼꼼히 읽어보세요.
- Kubernetes 공식 문서: https://kubernetes.io/docs/
- Kubernetes의 모든 개념과 사용법이 상세히 설명되어 있습니다. Tutorials 섹션부터 시작하는 것을 추천합니다.
- "Docker / Kubernetes in Action" 류의 서적: 시중에 나와 있는 다양한 컨테이너 관련 서적들은 개념을 체계적으로 이해하고 실습하는 데 도움을 줍니다. 개인의 학습 스타일에 맞는 책을 선택하세요.
- 온라인 강의 플랫폼 (Udemy, Coursera, Pluralsight 등): 실습 위주의 강의는 컨테이너 기술을 빠르게 익히는 데 매우 효과적입니다. "Docker and Kubernetes for Developers"와 같은 제목의 강의를 찾아보세요.
- Cloud Provider별 컨테이너 서비스 문서:
- AWS EKS (Elastic Kubernetes Service): https://aws.amazon.com/eks/
- Azure AKS (Azure Kubernetes Service)
