고급 Dockerfile 지시어
마지막 단계에서는 Docker 이미지를 더 안전하고 유지보수하기 쉬우며 사용하기 편리하게 만드는 추가적인 Dockerfile 지시어와 모범 사례를 살펴보겠습니다. 또한 각 단계의 프로세스를 검증하고 문제를 해결하는 방법에도 집중해 보겠습니다.
-
WebIDE 에서 Dockerfile을 다시 엽니다.
-
내용을 다음으로 교체합니다:
## 빌드 단계
FROM python:3.9-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
## 최종 단계
FROM python:3.9-slim
## 루트가 아닌 일반 사용자 생성
RUN useradd -m appuser
## 헬스체크를 위한 curl 설치
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
WORKDIR /app
## Python 버전과 site-packages 경로를 동적으로 결정
RUN PYTHON_VERSION=$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') && \
SITE_PACKAGES_PATH="/home/appuser/.local/lib/python${PYTHON_VERSION}/site-packages" && \
mkdir -p "${SITE_PACKAGES_PATH}" && \
chown -R appuser:appuser /home/appuser/.local
## 변수를 사용하여 site-packages와 바이너리 복사
COPY --from=builder /root/.local/lib/python3.9/site-packages "${SITE_PACKAGES_PATH}"
COPY --from=builder /root/.local/bin /home/appuser/.local/bin
COPY app.py .
ENV PATH=/home/appuser/.local/bin:$PATH
ENV ENVIRONMENT=production
## 애플리케이션을 실행할 사용자 설정
USER appuser
## ENTRYPOINT와 CMD 함께 사용
ENTRYPOINT ["python"]
CMD ["app.py"]
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:5000/ || exit 1
ARG BUILD_VERSION
LABEL maintainer="Your Name <your.email@example.com>"
LABEL version="${BUILD_VERSION:-1.0}"
LABEL description="Flask app demo with advanced Dockerfile techniques"
이 Dockerfile 에 도입된 새로운 개념들을 분석해 보겠습니다:
RUN useradd -m appuser: 컨테이너 내에 appuser라는 새 사용자를 생성합니다. 애플리케이션을 루트 (root) 가 아닌 사용자로 실행하는 것은 보안 모범 사례입니다. 이는 애플리케이션이 침해당했을 때 발생할 수 있는 잠재적 피해를 제한합니다. -m 플래그는 사용자의 홈 디렉토리를 생성합니다.
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*: HEALTHCHECK 지시어가 작동하는 데 필요한 curl 패키지를 설치합니다. 또한 이미지 크기를 줄이기 위해 설치 후 apt 캐시를 삭제합니다.
RUN PYTHON_VERSION=$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') && ...: 컨테이너 내의 Python 버전을 동적으로 파악하여 appuser를 위한 올바른 site-packages 디렉토리를 생성합니다. 또한 해당 디렉토리에 대해 사용자 권한을 올바르게 설정합니다.
COPY --from=builder /root/.local/lib/python3.9/site-packages "${SITE_PACKAGES_PATH}": builder 단계에서 설치된 Python 패키지들을 최종 이미지 내의 동적으로 결정된 site-packages 경로로 복사하여, appuser가 패키지를 사용할 수 있는 올바른 위치에 배치합니다.
COPY --from=builder /root/.local/bin /home/appuser/.local/bin: pip에 의해 설치된 실행 가능한 스크립트 (예: Flask CLI 등) 를 builder 단계에서 appuser의 로컬 bin 디렉토리로 복사합니다.
ENTRYPOINT ["python"]와 CMD ["app.py"]: 이 둘을 함께 사용하면 ENTRYPOINT는 컨테이너의 주 실행 파일 (여기서는 python) 을 정의하고, CMD는 해당 실행 파일에 전달할 기본 인자 (app.py) 를 제공합니다. 이 패턴은 유연성을 제공합니다. 사용자는 기본적으로 app.py를 실행할 수도 있고, CMD를 덮어써서 다른 Python 스크립트나 명령을 실행할 수도 있습니다.
HEALTHCHECK: 컨테이너의 상태 확인을 구성합니다. Docker 는 지정된 명령 (curl -f http://localhost:5000/) 을 주기적으로 실행하여 컨테이너의 건강 상태를 판단합니다. --interval=30s와 --timeout=3s는 각각 확인 간격과 타임아웃을 설정합니다. curl 명령이 실패 (0 이 아닌 종료 코드 반환) 하면 컨테이너는 "unhealthy" 상태로 간주됩니다.
ARG BUILD_VERSION: BUILD_VERSION이라는 이름의 빌드 인자를 정의합니다. 빌드 인자를 사용하면 빌드 시점에 Docker 이미지 내부로 값을 전달할 수 있습니다.
LABEL version="${BUILD_VERSION:-1.0}": Docker 이미지에 version 라벨을 설정합니다. BUILD_VERSION 빌드 인자를 사용하며, 빌드 시 값이 제공되지 않으면 기본값으로 1.0을 사용합니다 (:- 구문 활용).
- 이제 빌드 버전을 지정하여 새 이미지를 빌드합니다:
docker build -t advanced-flask-app-v2 --build-arg BUILD_VERSION=2.0 .
--build-arg BUILD_VERSION=2.0 플래그를 통해 이미지 빌드 과정에서 BUILD_VERSION 인자에 2.0이라는 값을 전달합니다. 이 값은 이미지의 version 라벨을 설정하는 데 사용됩니다.
- 빌드가 완료되면 이미지가 성공적으로 생성되었는지 확인합니다:
docker images | grep advanced-flask-app-v2
출력 목록에서 새 이미지 advanced-flask-app-v2와 해당 태그, 이미지 ID, 생성일, 크기를 확인할 수 있어야 합니다.
- 이제 새 이미지로 컨테이너를 실행합니다:
docker run -d -p 5002:5000 --name advanced-container-v2 advanced-flask-app-v2
이 명령은 컨테이너를 백그라운드에서 실행하고, 호스트의 5002 번 포트를 컨테이너의 5000 번 포트에 매핑하며, 이름을 advanced-container-v2로 지정합니다.
- 컨테이너가 실행 중인지 확인합니다:
docker ps | grep advanced-container-v2
컨테이너가 성공적으로 실행 중이라면 목록에 나타납니다. 만약 목록에 없다면 컨테이너가 종료되었을 수 있습니다. 중지된 컨테이너까지 포함하여 확인해 봅니다:
docker ps -a | grep advanced-container-v2
상태가 "Up"이 아니라면 로그를 확인하여 오류를 진단합니다:
docker logs advanced-container-v2
이 명령은 Flask 애플리케이션의 로그를 보여주며, 시작 시 발생한 문제나 런타임 오류를 파악하는 데 도움을 줍니다.
- 컨테이너가 정상 실행 중이라면, 시작될 때까지 잠시 기다린 후 건강 상태 (health status) 를 확인합니다:
docker inspect --format='{{.State.Health.Status}}' advanced-container-v2
잠시 후 (헬스체크가 최소 한 번 실행될 시간) "healthy"라는 출력이 나타나야 합니다. 처음에 "unhealthy"가 보인다면 30 초 (체크 간격) 정도 더 기다린 후 다시 실행해 보세요. 계속 "unhealthy" 상태라면 docker logs를 통해 Flask 애플리케이션에 문제가 있는지 확인하세요. 특별한 문제가 없다면 "unhealthy" 상태를 무시하고 진행해도 좋습니다.
- 빌드 버전 라벨이 올바르게 적용되었는지도 확인해 봅니다:
docker inspect -f '{{.Config.Labels.version}}' advanced-container-v2
출력 결과로 "2.0"이 나타나야 합니다. 이는 BUILD_VERSION 빌드 인자가 라벨 설정에 성공적으로 사용되었음을 증명합니다.
- 마지막으로 애플리케이션에 요청을 보내 테스트합니다:
curl http://localhost:5002
"Hello from production environment!" 메시지가 출력되어야 합니다. 이는 Flask 애플리케이션이 Docker 컨테이너 내부에서 정상적으로 작동하고 있으며 호스트의 5002 번 포트를 통해 접근 가능하다는 것을 의미합니다.
이러한 고급 기술들을 통해 더욱 안전하고 설정 가능하며 운영 환경에 적합한 Docker 이미지를 만들 수 있습니다. 루트가 아닌 사용자를 사용하면 보안이 강화되고, HEALTHCHECK는 컨테이너 오케스트레이션과 모니터링에 도움을 주며, 빌드 인자는 유연한 버전 관리를 가능하게 합니다.