컨테이너와 Docker 개요
컨테이너가 무엇이고 Docker가 무엇을 자동화하는지, 개발 환경 일관성과 배포 재현성 관점에서 정리하는 입문 카드입니다.
docker image pull nginx:alpine
docker run -d --name web -p 8080:80 nginx:alpine
docker logs webCategory
Preparing references and filters for this topic. 이 주제의 레퍼런스와 필터를 준비하고 있습니다.
컨테이너가 무엇이고 Docker가 무엇을 자동화하는지, 개발 환경 일관성과 배포 재현성 관점에서 정리하는 입문 카드입니다.
docker image pull nginx:alpine
docker run -d --name web -p 8080:80 nginx:alpine
docker logs webDocker 입문에서 가장 자주 헷갈리는 image와 container의 차이를 정적 template과 실행 instance 관점에서 정리합니다.
docker image pull nginx:alpine
docker run -d --name web1 -p 8081:80 nginx:alpine
docker run -d --name web2 -p 8082:80 nginx:alpine
docker image ls nginx
docker ps --filter ancestor=nginx:alpineDocker CLI가 daemon과 통신해 image, container, network, volume 같은 object를 관리하는 구조를 정리합니다.
docker CLI / Docker Compose
| Docker API
v
dockerd (daemon)
|
+-- image / container / network / volume 관리
+-- registry에서 image pull·pushimage를 처음 실행할 때 가장 자주 쓰는 docker run option을 중심으로 이름, port, environment, 삭제 흐름을 정리합니다.
# server: 새 container 생성, background 실행, 이름과 port 지정
docker run --name web -d -p 8080:80 nginx:alpine
# application 설정: runtime environment를 file에서 전달
docker run --name api -d --env-file ./app.env my-api:1.2.3
# one-shot command: 종료 뒤 container를 자동 삭제
docker run --rm alpine:3.21 sh -c 'echo "health check"'
# image가 local에 없으면 pull, 있으면 local image 사용
docker run --pull=missing --name worker my-worker:1.2.3컨테이너를 생성한 뒤 상태를 확인하고 시작, 중지, 삭제하는 기본 생명주기 명령을 정리합니다.
# 새 container 생성과 실행
docker run --name web -d nginx:alpine
docker ps --filter name=web
# 같은 container를 멈췄다가 다시 시작
docker stop --time 30 web
docker start web
# 필요 없어진 stopped container만 삭제
docker stop web
docker rm web컨테이너 실행 시 환경 변수를 넘기는 기본 방법과 `--env-file`의 역할을 정리합니다.
# .env 파일 예시
# DB_HOST=db
# DB_PORT=5432
# APP_ENV=production
docker run --env-file .env -e PORT=4000 my-appFROM, WORKDIR, COPY, RUN, CMD 같은 핵심 지시어를 기준으로 Dockerfile이 이미지를 만드는 방식을 정리합니다.
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npm", "start"]Docker 빌드 속도와 이미지 효율을 좌우하는 build context와 레이어 캐시의 동작 원리를 정리합니다.
FROM node:22-alpine
WORKDIR /app
# 의존성 파일만 먼저 복사 — 소스 변경이 캐시에 영향 없음
COPY package.json package-lock.json ./
RUN npm ci
# 소스 코드는 나중에 — 자주 바뀌는 파일을 뒤에 둔다
COPY . .
RUN npm run build이미지 이름과 tag가 버전 식별과 배포 경로를 결정한다는 점을 중심으로 build, tag, push 흐름을 정리합니다.
VERSION=1.2.3
IMAGE=registry.example.com/team/my-app
docker build -t "${IMAGE}:${VERSION}" .
docker image tag "${IMAGE}:${VERSION}" "${IMAGE}:1.2"
docker push "${IMAGE}:${VERSION}"
docker push "${IMAGE}:1.2"빌드 도구와 런타임 이미지를 분리하는 multi-stage build의 핵심 목적과 패턴을 정리합니다.
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html컨테이너 시작 명령을 구성할 때 `ENTRYPOINT`와 `CMD`가 각각 어떤 역할을 맡는지 정리합니다.
FROM python:3.12-slim
WORKDIR /app
COPY . .
ENTRYPOINT ["python", "-m", "http.server"]
CMD ["8000"]빌드 시점 변수 ARG와 런타임 환경 변수 ENV를 어떻게 구분해야 하는지 정리합니다.
ARG NODE_VERSION=22
FROM node:${NODE_VERSION}-alpine
ENV NODE_ENV=production
ENV PORT=3000이미지 빌드가 느리고 예상보다 무거워질 때 자주 원인이 되는 build context와 .dockerignore의 역할을 정리합니다.
# .dockerignore
node_modules
.git
dist
.env
*.log
coverage
.DS_Store한 아키텍처만이 아니라 amd64와 arm64 같은 여러 플랫폼용 이미지를 함께 빌드하고 배포할 때 쓰는 buildx의 기본 흐름을 정리합니다.
# buildx 빌더 생성 (처음 한 번)
docker buildx create --name mybuilder --use
# amd64와 arm64를 동시에 빌드해 레지스트리에 푸시
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t myorg/myapp:1.0.0 \
--push .컨테이너 기본 실행 사용자를 root에서 별도 사용자로 바꾸는 `USER` 지시어의 위치와 파일 권한 설계 기준을 정리합니다.
FROM node:22-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --chown=app:app package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --chown=app:app . .
USER app
CMD ["node", "server.js"]Docker 이미지 tag가 움직일 수 있다는 점과 digest로 특정 이미지 버전을 고정할 때의 장단점을 정리합니다.
# 사람이 읽는 버전 tag
FROM node:22-alpine
# 검토한 특정 내용을 고정
FROM node@sha256:<reviewed-digest>Dockerfile에서 COPY와 ADD를 구분하고, local file 복사, tar 자동 해제, 원격 URL 사용 시 경계를 정리합니다.
COPY package.json package-lock.json ./
RUN npm ci
COPY src/ ./src/
COPY --from=build /app/dist/ /usr/share/nginx/html/
ADD archive.tar.gz /opt/archive/docker login/logout, Docker Hub device flow, self-hosted registry 주소, --password-stdin, credential store와 helper 설정 기준입니다.
# Docker Hub device flow
docker login
# self-hosted registry: host와 선택적 port만 지정
docker login registry.example.com
docker login registry.example.com:5000
# CI: secret 값은 표준 입력으로 전달
printf '%s' "$REGISTRY_TOKEN" | docker login registry.example.com \
--username "$REGISTRY_USER" --password-stdin
# 인증 제거
docker logout registry.example.comregistry 없이 Docker image를 tar archive로 옮길 때 docker save/load와 export/import의 차이, tag 보존, 검증 흐름을 정리합니다.
# image와 tag를 tar archive로 저장
docker image save --output my-app.tar myorg/my-app:1.2.3
# 다른 머신에서 image와 tag를 복원
docker image load --input my-app.tar
# 복원 tag와 실제 실행 확인
docker image inspect myorg/my-app:1.2.3 --format '{{json .RepoTags}}'
docker run --rm myorg/my-app:1.2.3 --version개발 편의성과 데이터 영속성 요구에 따라 bind mount와 volume 중 무엇을 고를지 판단하는 카드입니다.
# 명시적인 bind mount: source가 없으면 오류
docker run --mount type=bind,src="$(pwd)/src",dst=/app/src,readonly node:22-alpine
# Docker daemon이 관리하는 persistent volume
docker run -e POSTGRES_PASSWORD=example \
--mount type=volume,src=pgdata,dst=/var/lib/postgresql/data postgres:16
# host 메모리에만 존재하는 임시 mount
docker run --tmpfs /tmp:rw,size=64m my-apphost와 container 사이의 포트 연결, 그리고 container끼리의 통신에서 bridge network가 어떤 역할을 하는지 정리합니다.
# Docker daemon host의 모든 address에 TCP 8080 공개
docker run -d --name web -p 8080:80 nginx:alpine
# daemon host의 loopback에서만 TCP 8080 공개
docker run -d --name admin -p 127.0.0.1:8080:80 nginx:alpine
# UDP를 명시적으로 공개
docker run -d --name dns -p 5353:53/udp my-dns
# container끼리만 통신할 DB는 publish하지 않음
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=password postgres:17컨테이너끼리 이름으로 통신하려면 named network와 Docker의 서비스 발견 방식을 함께 이해해야 합니다.
docker network create appnet
docker run -d --name db --network appnet \
-e POSTGRES_USER=app -e POSTGRES_PASSWORD=password -e POSTGRES_DB=app postgres:17
docker run -d --name api --network appnet \
-e DATABASE_URL=postgres://app:password@db:5432/app my-api
# api 안의 application은 host=db, port=5432로 연결
docker exec api printenv DATABASE_URLDocker named volume 데이터를 tar로 백업하고 새 volume으로 복구할 때의 안전한 순서와 검증 기준을 정리합니다.
# 1. application write를 멈추거나 DB의 일관된 backup을 먼저 생성
docker compose stop db
# 2. volume을 read-only로 붙여 tar archive 생성
docker run --rm \
--mount type=volume,src=app_pgdata,dst=/data,readonly \
--mount type=bind,src="$PWD",dst=/backup \
alpine tar czf /backup/pgdata.tgz -C /data .
# 3. 새 volume에만 먼저 복원
docker volume create app_pgdata_restore
docker run --rm \
--mount type=volume,src=app_pgdata_restore,dst=/data \
--mount type=bind,src="$PWD",dst=/backup,readonly \
alpine tar xzf /backup/pgdata.tgz -C /data여러 docker run 명령을 흩어 놓는 대신 compose.yaml 하나로 app, DB, volume, network를 선언하는 기본 흐름을 정리합니다.
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:password@db:5432/app
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: password
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 20
start_period: 10s
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Compose에서 서비스 시작 순서를 제어하되, 실행 순서와 준비 완료 상태는 다르다는 점을 healthcheck와 함께 정리합니다.
services:
web:
build: .
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5환경별 차이를 Compose 파일 분리로 관리하는 기본 패턴을 정리합니다.
# 왼쪽에서 오른쪽 순서로 파일을 병합한다.
docker compose -f compose.yaml -f compose.dev.yaml up -d
# 실제 적용 결과를 먼저 확인한다.
docker compose -f compose.yaml -f compose.dev.yaml config
# 운영 환경도 파일 조합을 명시한다.
docker compose -f compose.yaml -f compose.prod.yaml up -d선택적 서비스 기동을 위한 Compose profiles 기능의 기본 목적과 사용 흐름을 정리합니다.
services:
app:
build: . # profile 없음 → 항상 실행
adminer:
image: adminer
profiles:
- debug # --profile debug 할 때만 실행
prometheus:
image: prom/prometheus
profiles:
- monitoring # --profile monitoring 할 때만 실행Compose가 project name으로 컨테이너, 네트워크, 볼륨 이름을 격리하는 방식과 CI·브랜치별 충돌을 피하는 기준을 정리합니다.
docker compose -p myapp-dev up -d
COMPOSE_PROJECT_NAME=myapp-ci-123 docker compose up -d
docker compose ls
docker compose -p myapp-dev downDocker Compose의 ${VAR} 치환, .env 파일, --env-file, environment/env_file과 컨테이너 환경 변수의 차이를 구분합니다.
services:
web:
image: "webapp:${TAG:-latest}"
ports:
- "${WEB_PORT:-8080}:80"
environment:
- DEBUG=${DEBUG:-false}실행 중인 container 내부를 확인하고, shell에 들어가고, 설정과 port mapping을 읽는 기본 디버깅 흐름을 정리합니다.
# 1. exit·restart·log부터 확인
docker ps -a --filter name=app
docker logs --tail 100 app
# 2. 실행 설정과 실제 mount·port를 읽음
docker inspect --format '{{json .State}}' app
docker inspect --format '{{json .NetworkSettings.Ports}}' app
# 3. PID 1이 running일 때만 내부 command 실행
docker exec app env
docker exec -it app sh실험 후 남은 중지 컨테이너, dangling 이미지, 미사용 네트워크와 볼륨을 안전하게 정리하는 기본 카드를 정리합니다.
# 삭제 대상은 명령 전 목록으로 확인한다.
docker system df -v
docker ps -a
docker image ls --filter dangling=true
docker volume ls
# 확인 프롬프트를 읽고 미사용 리소스를 정리한다.
docker system prunecontainer가 host 자원을 무제한 쓰지 않도록 docker run 단계에서 메모리와 CPU 제한을 거는 기본 패턴을 정리합니다.
# 실행 가능한 상한: memory 512 MiB, CPU 최대 1.5 core 분량
docker run -d --name api --memory=512m --cpus=1.5 my-app:1.2.3
# physical memory 512 MiB, memory+swap 합계도 512 MiB
docker run -d --name api --memory=512m --memory-swap=512m my-app:1.2.3
# 종료 뒤 OOM kill 여부와 실제 설정 확인
docker inspect --format '{{.State.OOMKilled}} {{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' api컨테이너 재시작 정책을 통해 장애 후 동작을 어느 수준까지 자동화할 수 있는지 정리합니다.
docker run -d --restart unless-stopped my-app컨테이너가 단순히 떠 있는 상태와 실제로 요청을 받을 준비가 된 상태가 왜 다른지, healthcheck를 어떤 기준으로 설계하는지 정리합니다.
HEALTHCHECK --interval=30s --timeout=3s --retries=3 --start-period=10s \
CMD curl -f http://localhost:3000/health || exit 1비밀번호와 API 키 같은 민감한 값을 이미지에 굳히지 않고 안전하게 전달하는 기본 원칙을 정리합니다.
# compose.yaml
services:
app:
image: my-app
secrets:
- db_password
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txtDocker CLI가 어떤 daemon을 대상으로 명령을 실행하는지 context로 전환하고, remote daemon을 다룰 때 필요한 보안 경계를 정리합니다.
# 현재 target 확인
docker context ls
docker context inspect default
# SSH endpoint를 가진 context 생성
docker context create staging --docker host=ssh://deploy@staging.example.com
# 일회성 명령은 context를 명시
docker --context staging container ls --all
# 필요할 때만 shell 전체의 기본 context 전환
docker context use stagingcontainer가 언제 생성, 시작, 종료, 제거되었는지 Docker daemon event stream으로 확인하는 방법을 정리합니다.
# 특정 container의 최근 lifecycle만 확인
docker events --since 10m \
--filter type=container \
--filter container=app
# 모든 container lifecycle을 JSON Lines로 기록
docker events --filter type=container --format '{{json .}}'
# exit state와 application output을 같은 시점으로 대조
docker inspect --format 'exit={{.State.ExitCode}} oom={{.State.OOMKilled}}' app
docker logs --tail 100 appcontainer와 host 사이에서 파일을 복사할 때 경로 해석, 권한, 중지 container 처리 기준을 정리합니다.
mkdir -p ./out
# host -> container
docker cp ./config.json app:/etc/app/config.json
# running 또는 stopped container -> host
docker cp app:/var/log/app.log ./app.log
# directory 자체를 이미 존재하는 ./out 안에 복사
docker cp app:/var/log ./out
# directory의 내용만 이미 존재하는 ./out 안에 복사
docker cp app:/var/log/. ./outcontainer의 CPU, memory, network, disk I/O, PID 사용량을 docker stats로 확인하고 한계값 판단에 연결하는 방법을 정리합니다.
# running container의 live stream
docker stats api worker
# 현재 값 한 번만 기록
docker stats --no-stream --format \
'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.PIDs}}'
# memory limit 및 OOM 종료 이력과 함께 확인
docker inspect --format '{{.State.OOMKilled}} {{.HostConfig.Memory}}' apidocker logs가 stdout/stderr를 어떻게 보여 주는지와 json-file, local, 원격 logging driver 선택 시 확인할 지점을 정리합니다.
# 최근 출력 확인과 live follow
docker logs --tail 100 app
docker logs --follow --timestamps app
# daemon 기본 driver와 container 실제 driver 확인
docker info --format '{{.LoggingDriver}}'
docker inspect --format '{{.HostConfig.LogConfig.Type}}' app
# 현재 시점 이후만 확인
docker logs --since 10m app