공공부하자개발 · 영어 학습 노트
리눅스
리눅스 운영RHEL 에서 배포·운영하기0/13 완료
  • 01셸 기본과 파일 시스템
  • 02텍스트 처리 (grep·sed·awk)
  • 03프로세스·서비스 관리 (systemd)
  • 04자바 애플리케이션 배포
  • 05Tomcat·nginx 배포·재기동
  • 06로그·디스크 운영 (logrotate·cron)
  • 07네트워크 진단 (ss·curl·firewalld)
  • 08셸 스크립트 (운영 자동화)
  • 09장애 대응 (jstack·jmap·OOM killer·FD)
  • 10보안 기초 (ssh 강화·SELinux·감사 로그)
  • 11컨테이너 기초 (podman)
  • 12백업·복구 (설정·릴리스·DB 덤프)
  • 13Docker 실무
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 리눅스 운영 › 13 / 13

Docker 실무

Dockerfile·다단계 빌드·실행 옵션·Compose·레지스트리·폐쇄망 반입 (podman 대응)
섹션 7진행 0 / 13
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

2. 핵심 원리

2.1 Docker 구조

Docker 는 클라이언트(docker 명령), 데몬(dockerd), 이미지, 컨테이너, 레지스트리로 이루어집니다. 클라이언트가 데몬에 명령을 보내면 데몬이 이미지를 받아 컨테이너로 실행합니다.

Windows 의 Docker Desktop 은 이 데몬을 WSL2 안 가상 리눅스에서 돌립니다. 클라이언트만 Windows 에 있고 실제 컨테이너는 리눅스 커널 위에서 실행됩니다.

구성 요소 역할
클라이언트 docker 명령
데몬 이미지·컨테이너 관리
이미지 실행 환경 스냅샷
컨테이너 이미지를 실행한 프로세스
레지스트리 이미지 저장소

2.2 podman 과의 차이

podman 은 데몬이 없어(daemonless) 클라이언트가 직접 컨테이너를 실행합니다. Docker 는 데몬이 항상 떠 있어야 하고, 데몬이 root 권한으로 동작하는 구조가 기본입니다.

RHEL 서버는 데몬 없는 구조와 rootless 실행을 선호해 podman 을 기본으로 채택했습니다. 반면 개발 PC 는 Docker Desktop 의 GUI 와 생태계가 편해 Docker 를 그대로 씁니다. 명령어는 대부분 호환됩니다.

항목 Docker podman
데몬 필요 없음
기본 권한 root 데몬 rootless 가능
systemd 연동 별도 설정 quadlet 내장
Compose docker compose podman-compose
명령 문법 기준 거의 동일

alias docker=podman 을 서버에 걸어 두면 이 레슨의 명령을 podman 서버에서도 그대로 시도할 수 있습니다. 단 Compose 파일의 healthcheck 문법 등 일부 차이는 확인이 필요합니다.

2.3 Dockerfile 핵심 명령

FROM 으로 베이스 이미지를 정하고, COPY 로 파일을 넣고, RUN 으로 빌드 시점 명령을 실행합니다. ENTRYPOINT 는 컨테이너 시작 시 실행할 명령입니다.

명령 뜻
FROM 베이스 이미지
WORKDIR 작업 폴더
COPY 파일 복사
RUN 빌드 시점 실행
USER 실행 사용자
EXPOSE 문서용 포트
ENTRYPOINT 시작 명령

2.4 레이어 캐시 순서

Dockerfile 의 각 줄은 레이어가 되고, 위에서부터 순서대로 캐시됩니다. 자주 바뀌지 않는 줄(베이스 이미지, 의존성 설치)을 위에, 자주 바뀌는 소스나 jar 를 아래에 두면 재빌드가 빨라집니다.

소스 코드를 먼저 복사하고 의존성을 나중에 설치하면, 코드 한 줄만 바뀌어도 의존성 설치 레이어까지 다시 실행됩니다. Maven·Gradle 프로젝트는 의존성 목록 파일을 먼저 복사해 캐시를 살리는 방식을 씁니다.

2.5 다단계 빌드

빌드 도구(JDK, Maven)가 들어간 무거운 이미지로 컴파일하고, 실행에는 가벼운 JRE 이미지만 남기는 방식이 다단계 빌드입니다. FROM ... AS build 로 이름 붙인 단계에서 COPY --from=build 로 결과물만 가져옵니다.

이 레슨의 Dockerfile 은 eclipse-temurin:21-jdk 로 Main.java 를 컴파일하고, eclipse-temurin:21-jre 에 Main.class 만 복사합니다. JDK 이미지는 JRE 이미지보다 100MB 이상 크므로 최종 이미지가 그만큼 가벼워집니다.

2.6 베이스 이미지 선택

eclipse-temurin 은 공식 OpenJDK 배포판이고, ubi9/openjdk 는 RHEL 계열 운영에 맞춰져 있습니다. alpine 계열은 이미지가 매우 작지만 musl libc 를 써서 glibc 를 가정한 일부 네이티브 라이브러리와 충돌할 수 있습니다.

베이스 특징
eclipse-temurin 표준, glibc
ubi9/openjdk-21 RHEL 인증, 운영 친화
alpine 계열 초경량, musl libc 주의

태그는 항상 버전을 고정합니다. eclipse-temurin:21-jre 처럼 메이저 버전까지만 적어도, latest 보다 훨씬 예측 가능합니다. 완전히 고정하려면 eclipse-temurin:21.0.4_7-jre 처럼 빌드 번호까지 적습니다.

2.7 .dockerignore

.dockerignore 는 빌드 컨텍스트에서 제외할 파일을 지정합니다. .git, 빌드 산출물, 문서 파일을 제외하면 빌드 컨텍스트 전송이 빨라지고 이미지에 불필요한 파일이 섞이지 않습니다.

.gitignore 와 문법은 같지만 별개 파일입니다. 두 파일의 목적이 다르므로 하나로 합치지 않고 따로 관리합니다.

2.8 실행 옵션과 로그

docker run 의 주요 옵션은 -d(백그라운드), -p(포트), -v(볼륨), -e(환경 변수), --name, --memory, --restart 입니다. 로그는 컨테이너 안 파일이 아니라 표준 출력(stdout)으로 남기는 것이 원칙입니다.

애플리케이션이 stdout 에 로그를 쓰면 docker logs 로 어디서든 확인할 수 있습니다. 기본 로그 드라이버인 json-file 은 크기 제한이 없어 디스크를 채울 수 있으니 --log-opt max-size=10m 같은 제한을 둡니다.

옵션 뜻
-d 백그라운드 실행
-p host:container 포트 연결
-v host:container 볼륨 연결
-e KEY=VAL 환경 변수
--memory 메모리 한도
--restart 재시작 정책
--log-opt max-size 로그 파일 크기 제한

2.9 JVM 메모리와 컨테이너 한도

--memory 256m 으로 컨테이너 메모리를 제한했을 때 JVM 이 이를 모르면 호스트 전체 메모리 기준으로 힙을 잡아 OOM 이 납니다. JDK 10 이후는 cgroup 한도를 인식하므로 -XX:MaxRAMPercentage=75 로 한도의 비율만 지정하면 됩니다.

한도를 넘겨 커널이 프로세스를 강제 종료하면 종료 코드 137(128+9, SIGKILL)이 남습니다. docker inspect 의 OOMKilled 필드로 원인이 메모리 초과였는지 확인할 수 있습니다.

2.10 PID 1 과 완전 종료

컨테이너 안 PID 1 은 신호를 기본 방식으로 처리하지 않습니다. ENTRYPOINT 를 ["java", ...] 형태의 exec 형식으로 쓰면 java 가 직접 PID 1 이 되어 SIGTERM 을 정상적으로 받습니다.

sh -c "java ..." 형태로 쓰면 셸이 PID 1 이 되어 신호를 자바에 전달하지 않을 수 있습니다. docker stop -t 10 은 SIGTERM 을 보내고 10초 기다린 뒤 SIGKILL 하므로, 그 안에 완전 종료가 끝나야 종료 코드 143(128+15) 으로 깔끔히 끝납니다.

2.11 Compose 로 여러 컨테이너 묶기

Compose 는 app, web, db 처럼 여러 컨테이너를 하나의 파일로 정의하고 함께 띄웁니다. depends_on 에 condition: service_healthy 를 주면 앱이 완전히 준비된 뒤에야 다음 서비스가 시작합니다.

healthcheck 는 컨테이너 안에서 주기적으로 상태를 확인하는 명령입니다. profiles 로 개발용 DB 처럼 선택적으로만 띄울 서비스를 묶고, .env 파일로 비밀번호 같은 값을 코드 밖에 둡니다.

항목 역할
depends_on.condition 시작 순서 제어
healthcheck 컨테이너 상태 확인
volumes 데이터 보존
.env 환경 값 분리
profiles 선택적 서비스 그룹

2.12 레지스트리와 폐쇄망 반입

이미지 태그는 앱이름:버전-git해시 형태로 지어 어떤 커밋으로 만들었는지 추적합니다. docker push 로 사내 레지스트리(registry:2 컨테이너로 직접 운영 가능)에 올립니다.

인터넷이 없는 서버는 docker save 이미지 | gzip > app.tar.gz 로 파일을 만들고, USB 나 내부망으로 옮긴 뒤 docker load < app.tar.gz 로 등록합니다. 전달 파일은 sha256sum 체크섬을 함께 넘겨 위·변조 여부를 확인합니다.

핵심 원리
  • 2.1 Docker 구조
  • 2.2 podman 과의 차이
  • 2.3 Dockerfile 핵심 명령
  • 2.4 레이어 캐시 순서
  • 2.5 다단계 빌드
  • 2.6 베이스 이미지 선택
  • 2.7 .dockerignore
  • 2.8 실행 옵션과 로그
  • 2.9 JVM 메모리와 컨테이너 한도
  • 2.10 PID 1 과 완전 종료
  • 2.11 Compose 로 여러 컨테이너 묶기
  • 2.12 레지스트리와 폐쇄망 반입
이전 섹션1 왜 배우는가2 / 7다음 섹션3 코드 예제