Docker 기초

StrayCat·2026년 3월 13일

Docker란 무엇인가 — 컨테이너 기술 핵심 개념 정리

본 포스팅은 Docker의 핵심 개념과 주요 명령어를 정리한 학습 노트다.
CI/CD, 클라우드 배포와 같은 현대 개발 파이프라인을 이해하려면 Docker는 사실상 필수 기술이 되었다.


1. Docker가 왜 필요한가

개발을 하다 보면 이런 상황을 한 번쯤 겪게 된다.

"내 로컬에서는 잘 되는데, 서버에 올리면 왜 안 되지?"

이 문제의 본질은 환경 불일치다. 개발자 로컬 PC, QA(품질 보증) 환경, 실제 배포 서버가 서로 다른 OS, 다른 라이브러리 버전, 다른 설정을 가지고 있기 때문에 발생하는 고전적인 문제다.

Docker는 이 문제를 컨테이너(Container) 라는 개념으로 해결한다.

애플리케이션과 그 실행에 필요한 모든 것을 하나의 패키지로 묶어버리는 것이다.
그 패키지를 어디서 실행하든 동일하게 동작한다.


2. Docker의 핵심 구성 요소

Docker를 처음 접할 때 헷갈리는 개념이 몇 가지 있다. 각각의 역할을 명확하게 이해해두는 것이 중요하다.

이미지 (Image)

이미지는 읽기 전용 템플릿이다. 애플리케이션 코드, 런타임(Runtime, 프로그램이 실행되는 환경), 라이브러리, 환경 변수, 설정 파일 등이 모두 포함된다.

흔히 쓰는 비유로는 "붕어빵 틀"이다. 이미지 자체가 실행되는 것이 아니라, 이미지를 기반으로 컨테이너를 찍어낸다.

컨테이너 (Container)

컨테이너는 이미지를 실행한 상태다. 이미지가 정적인 설계도라면, 컨테이너는 그 설계도로 실제로 돌아가는 인스턴스(Instance, 실제로 동작하는 하나의 실체)다.

핵심은 격리(Isolation) 다. 컨테이너는 각각 독립된 공간에서 실행되기 때문에, 하나의 서버에서 여러 컨테이너를 동시에 돌려도 서로 영향을 주지 않는다.

Dockerfile

Dockerfile은 이미지를 만들기 위한 스크립트 파일이다. 어떤 베이스 이미지를 쓸 것인지, 어떤 명령어를 실행할 것인지, 어떤 파일을 복사할 것인지 등을 순서대로 기술한다.

# 베이스 이미지 지정 (어떤 OS/런타임 기반인지)
FROM openjdk:17-slim

# 빌드 결과물을 컨테이너 안으로 복사
COPY build/libs/app.jar /app/app.jar

# 컨테이너 시작 시 실행할 명령어
CMD ["java", "-jar", "/app/app.jar"]

Docker Hub

Docker Hub는 이미지를 저장하고 공유하는 중앙 레지스트리(Registry, 이미지 저장소) 다. GitHub이 코드를 위한 저장소라면, Docker Hub는 이미지를 위한 저장소라고 보면 된다.

docker pull postgres 같은 명령어로 공식 이미지를 바로 가져올 수 있다.

볼륨 (Volume)

컨테이너는 기본적으로 상태를 저장하지 않는다(Stateless). 컨테이너가 삭제되면 그 안의 데이터도 함께 사라진다.

볼륨은 이 문제를 해결하기 위한 데이터 영속성(Persistence) 메커니즘이다. 컨테이너 외부에 데이터를 저장하므로, 컨테이너가 삭제되거나 재시작되어도 데이터는 유지된다.

PostgreSQL 같은 데이터베이스 컨테이너를 띄울 때 볼륨 없이 쓰면 재시작할 때마다 데이터가 날아가는 참사가 생기므로 반드시 사용해야 한다.

네트워크 (Network)

여러 컨테이너가 서로 통신하려면 네트워크 설정이 필요하다. Docker는 세 가지 주요 네트워크 드라이버를 제공한다.

종류설명용도
Bridge기본값. 동일 호스트 내 컨테이너 간 통신단일 서버에서 여러 컨테이너 연결
Host컨테이너가 호스트의 네트워크를 직접 사용네트워크 성능이 중요한 경우
Overlay여러 호스트에 걸친 컨테이너 연결Kubernetes, Docker Swarm 등 클러스터 환경

별도로 지정하지 않으면 모든 컨테이너는 Bridge 네트워크에서 실행된다.


3. Docker vs 가상 머신 (VM)

Docker를 처음 배울 때 가장 먼저 나오는 비교가 바로 "VM이랑 뭐가 달라?"다.

아키텍처 차이

가상 머신(VM) 은 하이퍼바이저(Hypervisor, 물리 하드웨어 위에 가상 OS를 올리는 소프트웨어) 위에 각각 독립된 OS를 올린다. 즉, VM 3개를 돌리면 OS도 3개다.

Docker 컨테이너는 호스트 OS의 커널(Kernel, OS의 핵심 코어)을 공유한다. OS 없이 애플리케이션만 격리해서 실행한다.

핵심 비교표

항목Docker 컨테이너가상 머신 (VM)
시작 시간수 초 이내수 분
리소스 사용매우 적음 (MB 단위)많음 (GB 단위)
격리 수준보통 (커널 공유)강함 (완전 OS 분리)
이식성매우 높음낮음
OS 다양성제한적 (Linux 기반)어떤 OS도 가능

추가내용

2026년 현재, 컨테이너가 클라우드 네이티브(Cloud-native)와 마이크로서비스 워크로드에서 지배적인 위치를 차지하고 있지만, VM은 레거시 시스템과 강력한 보안 격리가 필요한 환경에서 여전히 핵심적인 역할을 한다.

실제 개발 환경에서 이 둘은 경쟁 관계가 아니라 보완 관계로 쓰이는 경우가 많다. 이미 구축된 레거시 위주의 기업 환경에서는 VM 기반 인프라가 그대로 유지되면서, 신규 서비스만 컨테이너로 전환하는 방식이 현실적이다.

2026 트렌드 포인트: Kata Containers, AWS Firecracker 같이 VM 수준의 보안 격리와 컨테이너의 경량성을 동시에 제공하는 기술들이 주목받고 있다. "둘 중 하나"가 아닌 "둘의 장점만 취하는" 방향으로 업계가 움직이는 것이다.


4. Docker를 언제 쓰는가

개념만 알면 부족하다. 어떤 상황에서 Docker를 써야 하는지 감을 잡아두는 것이 중요하다.

일관된 개발 환경이 필요할 때
"내 PC에서는 됐는데"라는 말이 나오지 않게 하려면, 모든 팀원이 동일한 컨테이너 위에서 개발하면 된다.

마이크로서비스 아키텍처(MSA)를 도입할 때
각 서비스를 독립적인 컨테이너로 분리해 배포하고 관리하기에 Docker는 최적의 도구다.

CI/CD 파이프라인을 구축할 때
CI/CD(Continuous Integration / Continuous Deployment, 코드 변경 시 자동으로 빌드·테스트·배포하는 자동화 파이프라인)와 Docker는 매우 잘 맞는다. 코드 푸시(Push)만 하면 자동으로 이미지 빌드 → 테스트 → 배포로 이어지도록 설정할 수 있다.

리소스를 효율적으로 쓰고 싶을 때
VM보다 훨씬 적은 메모리와 CPU로 동일한 수의 애플리케이션을 운용할 수 있다.


5. 핵심 Docker 명령어 정리

이론보다 명령어를 자주 쳐봐야 손에 익는다. 자주 쓰는 것들을 정리했다.

이미지 관련

# Docker Hub에서 이미지 다운로드
docker pull postgres

# 현재 디렉토리의 Dockerfile로 이미지 빌드 (-t: 이름과 태그 지정)
docker build -t myapp:latest .

# 로컬에 저장된 이미지 목록 확인
docker images

# 이미지 삭제
docker rmi myapp:latest

컨테이너 실행 및 관리

# 컨테이너 실행
# -d: 백그라운드(detached) 실행 — 터미널을 블로킹하지 않음
# -p 8080:80: 호스트 8080 포트 → 컨테이너 80 포트로 포트 포워딩
docker run -d -p 8080:80 myapp:latest

# 실행 중인 컨테이너 목록 확인
docker ps

# 중지된 컨테이너 포함 전체 목록 확인
docker ps -a

# 컨테이너 내부 쉘 접속
# -i: 표준 입력(STDIN) 유지 (키보드 입력 받을 수 있게)
# -t: 가상 터미널(tty) 할당
docker exec -it 컨테이너_아이디 /bin/bash

# 컨테이너 중지
docker stop 컨테이너_아이디

# 컨테이너 재시작
docker start 컨테이너_아이디

# 컨테이너 삭제 (중지 후 삭제해야 함)
docker rm 컨테이너_아이디

컨테이너 아이디는 전체를 다 입력할 필요 없다. 앞의 몇 글자만 식별 가능한 만큼만 입력해도 된다. (docker stop a3b 처럼)

네트워크 관련

# 네트워크 생성
docker network create mynetwork

# 네트워크 목록 확인
docker network ls

# 네트워크 삭제
docker network rm mynetwork

볼륨 관련

# 볼륨 생성
docker volume create myvolume

# 볼륨 목록 확인
docker volume ls

# 볼륨 삭제
docker volume rm myvolume

6. 실습 — PostgreSQL 컨테이너 실행해보기

이제 실제로 PostgreSQL 데이터베이스 컨테이너를 띄워보자.

이미지 받기

# Docker Hub에서 최신 PostgreSQL 공식 이미지 다운로드
docker pull postgres

컨테이너 실행

docker run -d --name postgres-sample \
  -p 5433:5432 \                                         # 호스트 5433 → 컨테이너 5432 포트 매핑
  -e POSTGRES_USER=admin1 \                              # 환경 변수로 DB 사용자 설정
  -e POSTGRES_PASSWORD=admin2 \                          # 환경 변수로 DB 비밀번호 설정
  -e PGDATA=/var/lib/postgresql/data/pgdata \            # PostgreSQL 데이터 저장 경로 지정
  -v ${로컬_바인딩_폴더}:/var/lib/postgresql/data:z \    # 호스트 폴더를 컨테이너 볼륨으로 마운트
  postgres

옵션 설명:

  • -e: 환경 변수(Environment Variable) 설정. 컨테이너 내부에서 설정값을 주입할 때 사용
  • -v: 볼륨 마운트. 호스트_경로:컨테이너_경로 형식. 데이터를 호스트에 저장해 컨테이너 삭제 후에도 유지
  • :z: SELinux(보안 강화 리눅스) 환경에서 호스트 폴더에 대한 읽기/쓰기 권한 부여 옵션

맥(Mac)에서 Permission denied 에러가 날 경우

  • Rancher Desktop 사용 시: Virtual Machine > Volumes 에서 Mount Type을 9p, Security Model을 Mapped-xattr로 변경
  • Docker Desktop 사용 시: General 탭에서 virtoFS를 oxsfs (Legacy)로 변경

💡 심화 — Bitnami PostgreSQL 이미지와 복제(Replication) 구성

공식 PostgreSQL 이미지(postgres)는 기능적으로 충분하지만, 복제(Replication) 구성이 필요한 경우에는 Bitnami 이미지(bitnami/postgresql)가 훨씬 편리하다.

복제(Replication)란, 마스터(Master) DB가 처리한 모든 쓰기 작업을 슬레이브(Slave) DB가 실시간으로 따라가며 동기화하는 구조다.
마스터 장애 시 슬레이브가 대체하거나, 읽기 트래픽을 슬레이브로 분산해 부하를 줄이는 목적으로 사용된다.

공식 이미지와의 핵심 차이는 아래 한 줄로 정리된다.

공식 postgres 이미지는 복제 관련 환경 변수를 아예 지원하지 않는다.

Bitnami 이미지는 POSTGRESQL_REPLICATION_MODE 같은 환경 변수 몇 개만 설정하면 마스터/슬레이브 스트리밍 복제(Streaming Replication) 클러스터를 바로 구성할 수 있다.

마스터 컨테이너 실행

docker run -d --name postgresql-master \
  -e POSTGRESQL_REPLICATION_MODE=master \           # 이 컨테이너를 마스터로 지정
  -e POSTGRESQL_REPLICATION_USER=repl_user \        # 복제 전용 사용자 생성
  -e POSTGRESQL_REPLICATION_PASSWORD=repl_pass \    # 복제 전용 사용자 비밀번호
  -e POSTGRESQL_USERNAME=myuser \                   # 일반 접속 사용자
  -e POSTGRESQL_PASSWORD=mypassword \               # 일반 접속 비밀번호
  -e POSTGRESQL_DATABASE=mydb \                     # 초기 생성할 데이터베이스 이름
  bitnami/postgresql:latest

슬레이브 컨테이너 실행

docker run -d --name postgresql-slave \
  --link postgresql-master:master \                   # 마스터 컨테이너와 연결
  -e POSTGRESQL_REPLICATION_MODE=slave \              # 이 컨테이너를 슬레이브로 지정
  -e POSTGRESQL_MASTER_HOST=master \                  # 마스터 호스트명 (link 별칭)
  -e POSTGRESQL_MASTER_PORT_NUMBER=5432 \             # 마스터 포트
  -e POSTGRESQL_REPLICATION_USER=repl_user \          # 마스터에서 만든 복제 사용자
  -e POSTGRESQL_REPLICATION_PASSWORD=repl_pass \      # 복제 사용자 비밀번호
  -e POSTGRESQL_PASSWORD=mypassword \                 # pg_hba.conf 생성에 필요
  bitnami/postgresql:latest

슬레이브는 시작 시 자동으로 마스터에 접속해 초기 데이터를 복제한 뒤, 이후 변경 사항을 실시간으로 스트리밍 받는다.

주의사항

  • 마스터는 읽기/쓰기 모두 가능, 슬레이브는 읽기 전용이다. 쓰기 요청을 슬레이브로 보내면 오류가 발생한다.
  • --link 옵션은 Docker의 레거시 기능이다. 실제 개발 환경에서는 아래처럼 Docker Compose + 사용자 정의 네트워크 방식이 권장된다. (다음 챕터에서 다룬다)
  • 현재 Bitnami 공식 문서 및 Helm 차트에서는 master/slave 용어 대신 primary/readReplica로 명칭이 변경되었다. 환경 변수 키 이름은 동일하지만, 문서 읽을 때 이 점을 인지해두자.

Docker Compose로 구성하는 방법 (미리보기)

docker run 두 번으로 구성하는 것보다, Compose 파일 하나로 선언하는 방식이 훨씬 관리하기 쉽다.

version: "3"
services:
  postgresql-master:
    image: bitnami/postgresql       # Bitnami 이미지 사용
    environment:
      - POSTGRESQL_REPLICATION_MODE=master
      - POSTGRESQL_REPLICATION_USER=repl_user
      - POSTGRESQL_REPLICATION_PASSWORD=repl_pass
      - POSTGRESQL_USERNAME=myuser
      - POSTGRESQL_PASSWORD=mypassword
      - POSTGRESQL_DATABASE=mydb

  postgresql-slave:
    image: bitnami/postgresql
    depends_on:
      - postgresql-master            # 마스터가 먼저 뜬 뒤 슬레이브 시작
    environment:
      - POSTGRESQL_REPLICATION_MODE=slave
      - POSTGRESQL_MASTER_HOST=postgresql-master
      - POSTGRESQL_MASTER_PORT_NUMBER=5432
      - POSTGRESQL_REPLICATION_USER=repl_user
      - POSTGRESQL_REPLICATION_PASSWORD=repl_pass
      - POSTGRESQL_PASSWORD=mypassword

이 구성이 가능한 것만 알아두고, 상세 내용은 Docker Compose 챕터에서 이어서 다루겠다.


7. 추가 내용

Rootless 컨테이너 (보안 중심 트렌드)

네임스페이스(Namespaces), cgroup, seccomp 프로파일, rootless 컨테이너 등이 컨테이너 보안을 한층 강화하는 방향으로 널리 쓰이고 있다.

루트리스 컨테이너(Rootless Container)는 root 권한 없이 컨테이너를 실행하는 방식으로, 보안 취약점 노출을 줄인다.

최근 컨테이너 보안은 단순 격리를 넘어서 rootless 기본 실행, 네임스페이스 강화, 공급망 보안 같은 방향으로 나아가고 있다.

Docker Compose

새로운 서비스를 배포하려 할 때 문서에는 거의 반드시 Docker Compose 파일이나 Helm 차트(Helm Chart, Kubernetes용 패키지 관리 파일)가 포함되어 있다. 컨테이너가 소프트웨어의 표준 배포 형식이 된 것이다.

여러 컨테이너를 한꺼번에 정의하고 관리하는 Docker Compose는 단순한 편의 도구가 아니라, 실제 개발 환경의 기본 도구로 자리잡았다. (다음 챕터에서 포스팅 예정)

현재 컨테이너 생태계는 Docker 도구(Docker Engine/Compose) 와 Kubernetes, container runtimes, Podman, containerd 등 다양한 형태로 분화돼 있다.

Kubernetes는 컨테이너 오케스트레이션의 사실상 표준이며 Docker CLI를 직접 사용하지 않는 클러스터(Kubernetes에서는 dockershim 제거 이후)도 많다.

Podman/Buildah/containerd 같은 도구는 Docker Desktop의 대안 또는 보완으로 성장했고, Docker CLI와 호환 가능한 경우도 있지만 루트리스/데몬리스 아키텍처를 기반으로 한 보안·운영 장점이 있어 점점 채택이 늘고 있다.

Alpine 이미지 사용 권고

FROM python:3.9 대신 FROM python:3.9-slim 혹은 FROM python:3.9-alpine처럼, 경량 베이스 이미지를 쓰는 것이 이미지 크기를 줄이고 보안 취약점을 최소화하는 방법으로 널리 권장된다.

Podman — Docker의 대안

Docker Desktop이 개인 외 사용에 유료화된 이후, Podman(포드맨)이 rootless 지원과 데몬(Daemon, 백그라운드 상주 프로세스) 불필요 등의 이유로 대안으로 주목받고 있다. 다만 이미지 호환성(OCI 표준 준수)이 유지되므로 기존 Dockerfile 자산은 그대로 쓸 수 있다.


정리

Docker는 단순한 개발 편의 도구가 아니다. 현대 소프트웨어 배포 방식의 근간이 되는 기술이다.

처음엔 이미지/컨테이너 구분도 헷갈리고, 명령어도 낯설게 느껴지지만 직접 docker run을 치고, 컨테이너 안에 exec으로 들어가보면 금방 손에 익는다.

다음 단계는 여러 컨테이너를 한 번에 정의하는 Docker Compose다.


참고 자료

profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글