1-4. Dockerfile 작성: 사용자 정의 이미지 만들기

1. Dockerfile의 세계로: 사용자 정의 이미지의 시작

Docker는 컨테이너 기술을 통해 애플리케이션을 격리된 환경에서 실행할 수 있게 해줍니다. 이러한 컨테이너는 "이미지"를 기반으로 생성되며, 이미지는 애플리케이션 실행에 필요한 모든 것(코드, 런타임, 시스템 도구, 라이브러리 등)을 포함합니다. Dockerfile은 바로 이 이미지를 정의하는 레시피와 같습니다. Dockerfile을 통해, 개발자는 애플리케이션의 실행 환경을 정확하게 기술하고, 이를 재현 가능한 이미지로 빌드할 수 있습니다. 즉, Dockerfile은 컨테이너화된 애플리케이션의 청사진 역할을 수행합니다.

2. Dockerfile의 구성 요소: 명령어 탐구

Dockerfile은 일련의 명령어와 인수로 구성됩니다. 각 명령어는 이미지 빌드 과정에서 특정 작업을 수행하며, 최종적으로 사용자 정의 이미지를 생성합니다. Dockerfile의 핵심 명령어를 살펴보고, 각 명령어의 역할과 사용법을 이해해 보겠습니다.

1) FROM: 기반 이미지 선택

FROM 명령어는 Dockerfile의 가장 첫 번째 명령어이며, 이미지 빌드의 기반 이미지를 지정합니다. 기반 이미지는 새로운 이미지의 출발점이며, 운영 체제, 런타임 환경, 그리고 기본적인 도구들을 포함합니다.

FROM ubuntu:latest

위 예시에서 ubuntu:latest는 Ubuntu 운영 체제의 최신 버전을 기반 이미지로 사용함을 의미합니다. FROM 명령어를 통해 개발자는 특정 운영 체제, 프로그래밍 언어의 런타임 환경(예: Node.js, Python), 데이터베이스(예: MySQL) 등을 기반으로 선택하여, 애플리케이션 실행에 필요한 환경을 구축할 수 있습니다.

2) RUN: 명령어 실행

RUN 명령어는 이미지 빌드 과정에서 쉘(shell) 명령어를 실행합니다. 이는 패키지 설치, 파일 다운로드, 쉘 스크립트 실행 등 다양한 작업을 수행하는 데 사용됩니다.

RUN apt-get update && apt-get install -y --no-install-recommends nginx

위 예시에서는 apt-get update로 패키지 목록을 업데이트하고, apt-get install -y --no-install-recommends nginx로 Nginx 웹 서버를 설치합니다. RUN 명령어는 이미지 레이어에 영향을 미치며, 각 RUN 명령어의 결과는 새로운 이미지 레이어로 저장됩니다. 따라서, RUN 명령어를 효율적으로 사용하면 이미지 크기를 줄이고 빌드 시간을 단축할 수 있습니다. 예를 들어, 여러 개의 RUN 명령어를 && 연산자를 사용하여 하나의 명령어로 묶을 수 있습니다.

3) COPY: 파일 복사

COPY 명령어는 호스트 머신의 파일이나 디렉토리를 컨테이너 파일 시스템으로 복사합니다. 이 명령어는 애플리케이션 코드, 설정 파일, 또는 기타 필요한 리소스를 컨테이너 내부에 포함시키는 데 사용됩니다.

COPY ./app /app

위 예시에서 ./app 디렉토리의 내용을 컨테이너의 /app 디렉토리로 복사합니다. COPY 명령어는 상대 경로 또는 절대 경로를 모두 사용할 수 있으며, 와일드카드(예: *)를 사용하여 여러 파일을 한 번에 복사할 수도 있습니다. COPY는 빌드 컨텍스트에 포함된 파일만 복사할 수 있습니다.

4) ADD: 파일 및 URL 추가

ADD 명령어는 COPY와 유사하게 파일이나 디렉토리를 컨테이너로 복사합니다. 그러나 ADD는 URL로부터 파일을 다운로드하여 컨테이너에 추가하는 기능도 제공합니다.

ADD https://example.com/myfile.tar.gz /app/

ADD 명령어는 압축 파일을 자동으로 압축 해제하는 기능도 제공합니다. 하지만, ADDCOPY보다 더 많은 기능을 가지고 있기 때문에, 빌드 과정에서 캐싱을 방해할 수 있습니다. 일반적으로, URL로부터 파일을 다운로드하거나 압축 파일을 해제해야 하는 경우를 제외하고는 COPY를 사용하는 것이 좋습니다.

5) WORKDIR: 작업 디렉토리 설정

WORKDIR 명령어는 컨테이너 내에서 작업 디렉토리를 설정합니다. 이후의 RUN, COPY, CMD, ENTRYPOINT 명령어는 지정된 작업 디렉토리를 기준으로 실행됩니다.

WORKDIR /app

위 예시에서 작업 디렉토리를 /app으로 설정합니다. WORKDIR은 여러 번 사용할 수 있으며, 상대 경로 또는 절대 경로를 사용할 수 있습니다. WORKDIR을 사용하여, 애플리케이션 코드의 위치를 명확하게 지정하고, 관련 명령어들의 실행 경로를 단순화할 수 있습니다.

6) ENV: 환경 변수 설정

ENV 명령어는 컨테이너 내에서 환경 변수를 설정합니다. 환경 변수는 애플리케이션 설정, 데이터베이스 연결 정보 등 다양한 정보를 저장하는 데 사용되며, 애플리케이션이 실행될 때 환경에 따라 유연하게 설정값을 변경할 수 있도록 합니다.

ENV NAME=John
ENV VERSION=1.0

ENV 명령어를 통해 설정된 환경 변수는 RUN, CMD, ENTRYPOINT 명령어에서 사용될 수 있습니다. 또한, 컨테이너 실행 시 --env 또는 -e 옵션을 사용하여 환경 변수의 값을 재정의할 수 있습니다.

7) CMD: 컨테이너 실행 명령어 (default)

CMD 명령어는 컨테이너가 시작될 때 실행되는 기본 명령어를 지정합니다. CMD는 실행 가능한 명령어를 지정하며, 컨테이너 실행 시 명령어를 지정하지 않으면 CMD에서 정의된 명령어가 실행됩니다.

CMD ["/usr/local/bin/python", "app.py"]

위 예시에서는 Python 애플리케이션(app.py)을 실행하는 명령어를 지정합니다. CMD는 여러 번 정의될 수 있지만, Dockerfile에서는 마지막에 정의된 CMD 명령어만 유효합니다. CMD 명령어는 컨테이너 실행 시 인자를 전달받을 수 있으며, ENTRYPOINT 명령어와 함께 사용하여 유연한 컨테이너 실행을 구현할 수 있습니다.

8) ENTRYPOINT: 컨테이너 실행 명령어 (고정)

ENTRYPOINT 명령어는 컨테이너가 시작될 때 실행되는 명령어를 지정합니다. ENTRYPOINTCMD와 유사하지만, ENTRYPOINT에 지정된 명령어는 항상 실행되며, 컨테이너 실행 시 인자를 전달하여 실행할 수 있습니다.

ENTRYPOINT ["/usr/local/bin/python"]
CMD ["app.py"]

위 예시에서는 Python 인터프리터(python)를 ENTRYPOINT로 지정하고, app.pyCMD로 지정합니다. 컨테이너를 실행할 때 docker run <image_name> <argument> 형태로 인자를 전달하면, ENTRYPOINT에 지정된 명령어에 인자가 전달되어 실행됩니다. 예를 들어, docker run my_image argument1 argument2를 실행하면, /usr/local/bin/python argument1 argument2 명령어가 실행됩니다.

9) VOLUME: 볼륨 마운트 지점 설정

VOLUME 명령어는 컨테이너 내에서 볼륨을 마운트할 지점을 지정합니다. 볼륨은 컨테이너와 호스트 간의 데이터 공유를 위한 메커니즘으로, 컨테이너가 삭제되어도 데이터가 유지됩니다.

VOLUME ["/data"]

위 예시에서는 /data 디렉토리를 볼륨으로 설정합니다. 컨테이너를 실행할 때 -v 옵션을 사용하여 호스트 디렉토리를 볼륨에 마운트할 수 있습니다.

10) EXPOSE: 포트 노출

EXPOSE 명령어는 컨테이너가 특정 포트를 노출하도록 지정합니다. 이는 컨테이너 외부에서 해당 포트를 통해 컨테이너에 접근할 수 있도록 하는 역할을 합니다.

EXPOSE 80

위 예시에서는 80번 포트를 노출합니다. EXPOSE는 단순히 정보를 제공하는 역할만 하며, 실제로 포트를 외부로 연결하기 위해서는 컨테이너 실행 시 -p 옵션을 사용해야 합니다.

3. Dockerfile 작성 실전: 예시와 심화 학습

Dockerfile 명령어를 사용하여 실제로 사용자 정의 이미지를 빌드하는 과정을 살펴보겠습니다. 간단한 Python 웹 애플리케이션을 컨테이너화하는 예시를 통해 Dockerfile 작성법을 익히고, 더 나아가 Dockerfile 최적화 기법을 알아보겠습니다.

1) Python 웹 애플리케이션 컨테이너화

다음은 간단한 Python 웹 애플리케이션(app.py)과 requirements.txt 파일입니다.

# app.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, Docker!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)
# requirements.txt
Flask

이제 이 애플리케이션을 위한 Dockerfile을 작성해 보겠습니다.

# Dockerfile
FROM python:3.9-slim-buster

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

EXPOSE 8080

CMD ["python", "app.py"]

위 Dockerfile은 다음과 같은 과정을 거쳐 이미지를 빌드합니다.

  • FROM: Python 3.9-slim-buster 이미지를 기반으로 합니다.
  • WORKDIR: 작업 디렉토리를 /app으로 설정합니다.
  • COPY: requirements.txt를 컨테이너로 복사합니다.
  • RUN: pip를 사용하여 필요한 패키지를 설치합니다.
  • COPY: app.py를 컨테이너로 복사합니다.
  • EXPOSE: 8080 포트를 노출합니다.
  • CMD: python app.py 명령어를 실행합니다.

이 Dockerfile을 사용하여 이미지를 빌드하고 컨테이너를 실행해 보겠습니다.

docker build -t my-python-app .
docker run -p 8080:8080 my-python-app

위 명령어를 실행하면, 웹 브라우저에서 http://localhost:8080에 접속하여 "Hello, Docker!" 메시지를 확인할 수 있습니다.

2) Dockerfile 최적화

Dockerfile을 작성할 때, 이미지 크기를 줄이고 빌드 시간을 단축하기 위해 몇 가지 최적화 기법을 적용할 수 있습니다.

  • 멀티 스테이지 빌드: 여러 FROM 명령어를 사용하여, 빌드 단계와 런타임 단계를 분리합니다. 빌드에 필요한 도구는 빌드 단계에서만 사용하고, 런타임 환경에는 필요한 최소한의 파일만 포함시킵니다.

멀티 스테이지 빌드 설명 뒤

# 빌드 단계
FROM python:3.9-slim-buster AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .

# 런타임 단계
FROM python:3.9-slim-buster
WORKDIR /app
COPY --from=builder /app /app
EXPOSE 8080
CMD ["python", "app.py"]

위 예시에서는 builder라는 이름의 빌드 단계를 사용하여, 애플리케이션을 빌드하고 필요한 파일을 복사합니다. 런타임 단계에서는 빌드 단계에서 생성된 아티팩트만 복사하여 최종 이미지를 생성합니다.

  • --no-cache-dir 옵션 사용: pip install 명령어를 실행할 때, 캐시를 사용하지 않도록 --no-cache-dir 옵션을 사용합니다. 이렇게 하면 이미지 크기를 줄일 수 있습니다.
  • .dockerignore 파일 사용: .dockerignore 파일에 빌드 컨텍스트에서 제외할 파일과 디렉토리를 지정합니다. 불필요한 파일이 이미지에 포함되는 것을 방지하여 이미지 크기를 줄입니다.
  • 명령어 결합: 여러 RUN 명령어를 && 연산자를 사용하여 하나의 명령어로 결합합니다. 이렇게 하면 이미지 레이어 수를 줄일 수 있습니다.
  • 베이스 이미지 선택: 애플리케이션에 필요한 최소한의 패키지만 포함하는 슬림(slim) 또는 alpine과 같은 작은 베이스 이미지를 선택합니다.

4. Dockerfile 활용의 실제: 예시와 주의사항

Dockerfile은 다양한 애플리케이션을 컨테이너화하는 데 사용될 수 있습니다. 웹 서버, 데이터베이스, 백엔드 서비스 등 모든 종류의 애플리케이션을 Dockerfile을 사용하여 컨테이너화할 수 있습니다.

1) Nginx 웹 서버 컨테이너화

Nginx 웹 서버를 컨테이너화하는 Dockerfile 예시입니다.

FROM nginx:latest

COPY ./html /usr/share/nginx/html

위 Dockerfile은 Nginx 공식 이미지를 기반으로 하며, 호스트 머신의 ./html 디렉토리에 있는 정적 파일을 컨테이너의 /usr/share/nginx/html 디렉토리로 복사합니다.

2) 데이터베이스 컨테이너화

MySQL 데이터베이스를 컨테이너화하는 Dockerfile 예시입니다.

FROM mysql:latest

ENV MYSQL_ROOT_PASSWORD=password
ENV MYSQL_DATABASE=mydatabase
ENV MYSQL_USER=myuser
ENV MYSQL_PASSWORD=mypassword

COPY ./init.sql /docker-entrypoint-initdb.d/

위 Dockerfile은 MySQL 공식 이미지를 기반으로 하며, 환경 변수를 설정하고, 데이터베이스 초기화 스크립트(init.sql)를 컨테이너로 복사합니다.

3) 주의사항 및 트러블슈팅

Dockerfile을 작성할 때 몇 가지 주의사항을 기억해야 합니다.

  • 이미지 레이어 관리: 각 RUN 명령어는 새로운 이미지 레이어를 생성합니다. 이미지 레이어 수가 많아지면 이미지 크기가 커지고 빌드 시간이 길어집니다. 따라서, RUN 명령어를 효율적으로 사용하고, 불필요한 명령어를 제거해야 합니다.
  • 캐싱: Docker는 이미지 빌드 과정에서 캐싱을 활용하여, 이미 변경되지 않은 레이어를 재사용합니다. Dockerfile 명령어를 변경하면, 해당 명령어 이후의 모든 레이어가 다시 빌드됩니다. 따라서, Dockerfile 명령어를 변경할 때는 캐싱 메커니즘을 고려하여, 변경 사항이 필요한 레이어만 다시 빌드되도록 해야 합니다.
  • 권한: 컨테이너 내에서 파일을 생성하거나 수정할 때, 적절한 권한을 부여해야 합니다. 그렇지 않으면, 애플리케이션이 정상적으로 실행되지 않을 수 있습니다. USER 명령어를 사용하여, 컨테이너 내에서 실행되는 사용자를 변경할 수 있습니다.
  • 환경 변수: 환경 변수를 사용하여 애플리케이션 설정을 관리합니다. 환경 변수를 사용하면, 컨테이너 실행 시 다양한 설정을 적용할 수 있으며, 코드 변경 없이 다른 환경에서 애플리케이션을 실행할 수 있습니다.
  • 보안: Dockerfile을 작성할 때는 보안을 고려해야 합니다. 불필요한 패키지를 설치하지 말고, 최신 버전의 이미지를 사용하고, 컨테이너 내에서 사용되는 비밀번호와 키를 안전하게 관리해야 합니다.

5. 마치며: Dockerfile 마스터를 향하여

Dockerfile은 컨테이너 기반 애플리케이션 개발의 핵심 도구입니다. Dockerfile을 능숙하게 사용하면, 애플리케이션을 재현 가능하고 일관된 환경에서 실행할 수 있으며, 배포 과정의 효율성을 크게 향상시킬 수 있습니다.

본 가이드에서 Dockerfile의 기본 개념, 명령어, 작성법, 그리고 최적화 기법을 살펴보았습니다. 이러한 지식을 바탕으로, 다양한 애플리케이션을 컨테이너화하고, 개발, 테스트, 배포 과정을 자동화할 수 있습니다.

Docker에 대한 깊이 있는 이해는 컨테이너 기술의 활용 범위를 넓히고, 현대적인 애플리케이션 개발 환경을 구축하는 데 필수적입니다. 꾸준한 실습과 학습을 통해 Dockerfile 마스터의 길을 걷기를 바랍니다.

비슷한 글 추천

Comments (0)

No comments yet. Be the first to comment!