본문 바로가기

개인공부

[Microsoft Azure] 8. 컨테이너 기술의 이해 - 도커

이전에 포스팅한

MQTT AoI 알고리즘 프로젝트에서

도커를 통한 ansible 환경 배포를 진행한 적이 있었다.

그때 한번 도커를 경험해보았지만, 사실 완벽히 이해하고 사용한 것은 아니었다.

이번 시간에는 도커의 개념을 기초부터 꽉꽉 채워서 배워보겠다.


1. 컨테이너 기술의 입문

Docker란?

Docker는 애플리케이션과 그 실행에 필요한 모든 종속성을 컨테이너라는 표준화된 유닛으로 패키징하는 오픈소스 플랫폼이다.

핵심 흐름은 세 단계다:

Dockerfile → (docker build) → Docker Image → (docker run) → Docker Container

Dockerfile은 이미지를 만들기 위한 레시피다. base image, 설치할 패키지, 복사할 파일, 실행할 커맨드 등을 선언적으로 기술한다. Docker Image는 이 레시피대로 만들어진 읽기 전용 템플릿이고, Docker Container는 이미지를 실행한 인스턴스다.

OOP에 비유하면, Image는 Class이고 Container는 Instance다. 하나의 Image에서 여러 Container를 찍어낼 수 있다.

VM vs Container

VM과 컨테이너는 모두 격리된 환경을 제공하지만, 구조가 근본적으로 다르다.

구분 Virtual Machine Container
격리 수준 Hypervisor 위에 Guest OS 포함 Host OS 커널 공유
부팅 속도 수십 초 ~ 수 분 수백 ms
이미지 크기 수 GB (OS 포함) 수십 MB ~ 수백 MB
리소스 오버헤드 높음 (각 VM마다 OS 메모리 점유) 낮음 (커널 공유)
격리 강도 강함 (완전한 OS 분리) 상대적으로 약함 (namespace/cgroup 기반)

VM은 각각 Guest OS를 포함하므로 오버헤드가 크다. 컨테이너는 Host OS의 커널을 공유하면서 Linux namespace와 cgroup으로 프로세스 수준의 격리를 제공한다. 그래서 가볍고 빠르다.

단, 보안 격리 강도는 VM이 우위다. 멀티테넌트 환경에서 완전한 격리가 필요하면 VM을 선택해야 하고, 같은 팀이 운영하는 마이크로서비스들이라면 컨테이너가 합리적이다.

Docker Layer 구조

Docker Image는 여러 Layer의 스택으로 구성된다.

예를 들어 nginx 기반 웹 앱 이미지는 이런 구조다:

[web app source]  ← 내 앱 코드
[nginx]           ← nginx 설정
[Layer C]         ← ubuntu 패키지
[Layer B]         ← apt-get update
[Layer A]         ← base ubuntu

각 Layer는 읽기 전용이고, Dockerfile의 각 명령어(FROM, RUN, COPY 등)가 하나의 Layer를 생성한다. 컨테이너를 실행하면 이 읽기 전용 Layer 위에 R/W Layer 하나가 얹어진다. 컨테이너 안에서 파일을 수정하면 이 R/W Layer에 기록되고, 컨테이너를 삭제하면 R/W Layer도 사라진다.

이 구조의 장점은 Layer 공유다. ubuntu Layer가 동일하면 nginx 이미지와 web app 이미지가 같은 Layer를 재사용한다. 디스크 공간도 아끼고 pull 속도도 빨라진다.

Docker Image 경로

Docker Image의 전체 경로(Full Qualified Name)는 다음과 같은 구조다:

Registry / Namespace / Image Name : Tag
구성 요소 설명 예시
Registry 이미지가 저장된 서버 docker.io, ghcr.io, ECR URL
Namespace 소유자 구분 (조직 또는 사용자) library (공식), mycompany
Image Name 이미지 이름 nginx, my-app
Tag 버전 latest, 1.12.1, v2.0.0

공식 이미지는 docker.io/library/nginx:latest이고, 줄여서 nginx:latest로 쓴다. Private Registry를 운영하면 my-registry.com/myteam/my-app:1.0처럼 자체 경로를 사용한다.

실무에서 latest 태그는 절대 프로덕션에 쓰지 않는다. 어떤 버전이 배포되었는지 추적이 안 되기 때문이다. 반드시 명시적 버전 태그를 사용해야 한다.


2. Docker의 설치와 활용

주요 Docker CLI 명령어

Docker의 핵심 명령어들을 정리하면 다음과 같다.

명령어 설명 예시
docker pull Registry에서 Image 다운로드 docker pull ubuntu:18.04
docker images 로컬 Image 목록 조회 docker images
docker run 컨테이너 생성 및 실행 docker run -it --name demo1 ubuntu:18.04 /bin/bash
docker ps 실행 중인 컨테이너 목록 docker ps / docker ps -a (전체)
docker exec 실행 중인 컨테이너에 명령 실행 docker exec -it demo2 /bin/bash
docker logs 컨테이너 로그 확인 docker logs demo3 -f
docker stop 컨테이너 중지 docker stop demo3
docker rm 컨테이너 삭제 docker rm demo3
docker rmi 이미지 삭제 docker rmi ubuntu

docker run 주요 옵션

docker run은 가장 자주 쓰는 명령어인 만큼 옵션도 다양하다. 핵심 옵션만 정리하면:

docker run -it -d --name demo2 -p 8080:80 -v /host/path:/container/path ubuntu:18.04 /bin/bash
옵션 설명
-i Interactive 모드. stdin을 열어둔다
-t TTY 할당. 터미널을 연결한다
-d Detach 모드. 백그라운드에서 실행한다
--name 컨테이너에 이름을 부여한다
-p 포트 매핑. 호스트포트:컨테이너포트
-v 볼륨 마운트. 호스트경로:컨테이너경로

-it는 거의 세트로 쓴다. 컨테이너에 접속해서 터미널 작업을 할 때 필수다. -d는 서비스를 데몬처럼 띄울 때 사용한다. -it-d를 함께 쓰면 백그라운드로 실행하되 나중에 docker exec -it로 접속할 수 있는 상태가 된다.


3. Docker Image의 생성과 활용

Dockerfile 주요 명령어

Dockerfile은 Docker Image를 빌드하기 위한 스크립트다. 주요 명령어를 정리하면:

명령어 설명 예시
FROM Base Image 지정 FROM ubuntu:18.04
RUN 빌드 시점에 커맨드 실행 RUN apt-get update
COPY 호스트 파일을 이미지로 복사 COPY app.py /app/
WORKDIR 작업 디렉토리 설정 WORKDIR /home/demo
ENV 환경 변수 설정 ENV LANG ko_KR.UTF-8
EXPOSE 컨테이너 포트 선언 EXPOSE 8080
CMD 컨테이너 시작 시 실행할 명령 CMD ["python", "app.py"]

RUNCMD의 차이를 혼동하는 경우가 많다. RUN이미지 빌드 시점에 실행되고 Layer를 생성한다. CMD컨테이너 실행 시점에 실행되며, Dockerfile에서 마지막 CMD 하나만 유효하다.

Dockerfile 작성 예시

간단한 Dockerfile을 작성해보면:

# base image 설정
FROM ubuntu:18.04

# 패키지 업데이트
RUN apt-get update

# 컨테이너 시작 시 실행할 명령
CMD ["echo", "Hello FastCampus"]

이 Dockerfile로 이미지를 빌드하고 실행하는 과정:

# 이미지 빌드 (-t: 태그 지정, .: 현재 디렉토리를 빌드 컨텍스트로 사용)
docker build -t my-image:v1.0.0 .

# 빌드된 이미지 확인
docker images | grep my-image

# 컨테이너 실행
docker run my-image:v1.0.0
# 출력: Hello FastCampus

Docker Registry

Docker Image를 빌드했으면, 이를 다른 서버에서도 사용할 수 있도록 Registry에 push해야 한다.

Docker Hub가 기본 Public Registry이고, Private Registry도 직접 구축할 수 있다. AWS ECR, GCP Artifact Registry, Azure Container Registry 등 각 클라우드 벤더도 자체 Registry 서비스를 제공한다.

로컬에 Private Registry를 띄우는 것도 간단하다:

# Registry 컨테이너 실행
docker run -d -p 5000:5000 --name registry registry

# 이미지에 Registry 경로를 포함한 태그 부여
docker tag my-image:v1.0.0 localhost:5000/my-image:v1.0.0

# Registry에 push
docker push localhost:5000/my-image:v1.0.0

# Registry에 저장된 이미지 확인
curl -X GET http://localhost:5000/v2/_catalog
curl -X GET http://localhost:5000/v2/my-image/tags/list

실무에서는 CI/CD 파이프라인과 연동하여 코드 push → 이미지 빌드 → Registry push → 배포가 자동화되는 흐름을 구성한다. GitHub Actions + Docker Hub, GitLab CI + Harbor 같은 조합이 대표적이다.


실습 진행

도커를 설치하고 이미지 생성을 해보겠다.

VM 리소스 생성 및 도커 엔진 설치 코드 복사

 

VM에 도커 설치
도커 권한 주기
우분투 이미지 설치 및 실행

/#으로 루트 최고권한자로 우분투에 접속되는 것을 볼 수 있다.

모든 파일 확인 가능

 

로그를 찍는 이미지를 실행하고 로그를 확인
stop으로 이미지 중단 및 rm으로 삭제
도커 이미지 자체를 삭제

이제 도커파일로 이미지를 실제로 만들어보자

 

도커파일을 작성

 

이미지 생성 및 실행!!

실행이 무사히 완료되었다!!


정리

이번 주에는 컨테이너 기술의 핵심인 Docker를 다뤘다.

  • 컨테이너 vs VM: 컨테이너는 Host OS 커널을 공유하여 가볍고 빠르다. VM은 완전한 OS 격리를 제공하지만 오버헤드가 크다
  • Docker의 3요소: Dockerfile(레시피) → Image(템플릿) → Container(실행 인스턴스)
  • Layer 구조: 각 Dockerfile 명령이 하나의 읽기 전용 Layer를 생성하고, 컨테이너 실행 시 R/W Layer가 추가된다. Layer 공유로 디스크와 네트워크를 절약한다
  • Image 경로: Registry/Namespace/Name:Tag 구조. 프로덕션에서는 반드시 명시적 태그를 사용한다
  • 핵심 명령어: pull, build, run, exec, logs, stop, rm, rmi
  • Dockerfile: FROM, RUN, COPY, CMD, EXPOSE 등으로 이미지 빌드를 선언적으로 정의한다
  • Registry: 빌드한 이미지를 저장하고 배포하는 중앙 저장소. Docker Hub, ECR, ACR 등

다음 시간에는 Docker 컨테이너를 대규모로 관리하기 위한 오케스트레이션 도구, Kubernetes에 대해 알아본다.