1. IaC & Dockerfile
IaC가 필요한 이유
- 명령어 기반으로 인프라를 구성할 때 사용자 실수와 같은 인적 오류 가능성이 높다.
- 이러한 오류 가능성을 줄이기 위해 인프라 구성을 코드로 관리할 수 있는 IaC(Infrastructure as Code)를 사용한다.
2. 최적의 Dockerfile 작성법
- 경량의 컨테이너 서비스 제공: 불필요한 파일이나 라이브러리를 최소화하여 경량 컨테이너를 제공한다.
- 레이어 최소화: Dockerfile의 레이어 수가 많아지면 빌드 시간이 늘어나고 이미지가 커질 수 있다. 불필요한 레이어를 줄여 효율을 높인다.
- 하나의 애플리케이션, 하나의 컨테이너: 단일 컨테이너에 하나의 애플리케이션만 실행하도록 구성하는 것이 좋다.
- 캐시 활용: 캐시 기능을 활용해 중복된 빌드 작업을 피할 수 있다.
- 디렉토리 단위 작업: 디렉토리별로 작업을 나누면 가독성이나 관리가 용이하다.
- 서버리스 환경에서 개발: 서버리스 환경에서 동작하도록 별도의 서버를 필요로 하지 않도록 한다.
3. Dockerfile 명령어
FROM
FROM
은 필수 항목이며, 이미지를 빌드할 때 사용할 베이스 이미지를 지정하는 명령어이다.- 일반적으로
hub.docker.com
에서 제공하는 다양한 베이스 이미지를 활용하는 것이 좋다. 특히 경량화된 이미지를 선호한다면,slim
이나Alpine
과 같은 리눅스 배포판을 사용하는 것을 권장한다.- 예:
FROM alpine:latest
Alpine
이미지는 매우 가볍고, 작은 용량을 요구하는 서비스나 빠른 시작 속도가 필요한 경우에 적합하다.
- 예:
MAINTAINER
- 이미지를 만든 작성자의 정보(이름, 이메일 주소)를 명시하는 명령어이다.
- 예:
MAINTAINER adam <itstudy@kakao.com>
이는 이미지의 소유자나 책임자가 누구인지 기록하여 추후에 문제가 발생했을 때 관련된 사람을 식별할 수 있게 한다.
- 예:
LABEL
- 이미지에 메타데이터(버전, 설명 등)를 추가하는 명령어이다. 여러 개의 라벨을 사용할 수 있으며, 이를 통해 이미지의 목적, 작성자, 버전 등의 정보를 기록할 수 있다.
- 예:
LABEL version="1.0" description="A PHP web application"
이 정보를 활용하면 나중에 이미지가 어떤 용도로 생성되었는지, 어떤 버전을 사용하는지 쉽게 파악할 수 있다.
- 예:
RUN
- 기본 이미지에 추가적으로 필요한 패키지나 설정을 실행하는 명령어이다. 패키지 설치, 업데이트 등 다양한 작업을 처리할 수 있으며, 여러 개의
RUN
명령을 사용할 수 있다.- 예:
RUN apt-get update && apt-get install -y nginx
RUN
명령은 하나의 명령이 하나의 레이어로 저장되므로, 명령어의 개수를 최소화하여 이미지 크기를 줄이는 것이 중요하다. 예를 들어, 여러 패키지를 한 번에 설치하는 방식으로 명령을 합칠 수 있다.
- 예:
CMD
- 컨테이너가 시작될 때 실행할 명령을 지정하는 명령어이다. 여러 개의
CMD
를 작성해도 마지막에 선언된 하나만 실행된다.- 예:
CMD ["python", "app.py"]
컨테이너 실행 시 기본적으로 실행될 명령을 지정하며, 일반적으로 애플리케이션을 실행하는 데몬 프로세스를 실행하는 데 유용하다.
- 예:
ENTRYPOINT
CMD
와 유사하게 컨테이너가 시작될 때 실행될 명령을 지정하지만, 필수적으로 실행해야 하는 명령을 지정할 때 사용된다.- 예:
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
ENTRYPOINT
는 컨테이너 실행 시 꼭 필요한 명령을 지정할 때 유용하다.CMD
는 기본 인자를 넘기는 용도로,ENTRYPOINT
는 반드시 실행해야 하는 명령을 지정하는 데 사용된다.
- 예:
COPY
- 호스트의 파일이나 디렉토리를 이미지 내부로 복사할 때 사용하는 명령어이다.
- 예:
COPY ./src /app/src
특정 파일이나 디렉토리를 이미지 빌드 과정에서 컨테이너 내에 복사한다.
- 예:
ADD
COPY
와 유사하지만, 추가적으로 URL에서 파일을 다운로드하여 이미지에 추가하거나, 압축 파일을 지정된 경로에 풀어서 추가할 수 있다.- 예:
ADD http://example.com/file.tar.gz /tmp/
URL을 통해 외부 파일을 가져와 압축을 풀고, 이미지에 포함시킬 수 있다.
- 예:
ENV
- 환경변수를 설정하는 명령어이다.
- 예:
ENV APP_ENV=production
이는 컨테이너 내부에서 애플리케이션이 실행될 때 참조할 수 있는 변수를 미리 설정하는 용도로 유용하다.
- 예:
EXPOSE
- 컨테이너 내부에서 외부로 노출할 포트를 지정하는 명령어이다.
- 예:
EXPOSE 80
해당 포트는 애플리케이션이 외부와 통신할 수 있도록 열려 있다.
- 예:
VOLUME
- 컨테이너 내부와 호스트 간의 데이터를 공유할 볼륨을 지정하는 명령어이다.
- 예:
VOLUME /data
이는 데이터를 영구적으로 저장하거나 컨테이너 간에 공유할 때 사용한다.
- 예:
USER
- 기본적으로
root
사용자로 실행되는 컨테이너에서 다른 사용자를 지정할 때 사용하는 명령어이다.- 예:
USER www-data
보안 상 이유로 권한이 낮은 사용자를 사용하는 것이 권장된다.
- 예:
WORKDIR
- 컨테이너 내부에서 작업 디렉토리를 설정하는 명령어이다. 이 디렉토리를 기준으로 모든 명령이 실행된다.
- 예:
WORKDIR /app
작업 디렉토리를 미리 설정하면, 이후의RUN
,CMD
,COPY
,ADD
명령이 모두 해당 디렉토리에서 실행된다.
- 예:
ARG
- 빌드 시점에 값을 전달하는 명령어이다.
- 예:
ARG version=1.0
빌드 과정에서 필요한 값을 동적으로 전달할 수 있다.--build-arg
옵션을 통해 값을 전달할 수 있다.
- 예:
HEALTHCHECK
- 컨테이너 내부의 프로세스 상태를 주기적으로 확인하는 명령어이다. 여러 개의
HEALTHCHECK
가 작성되었을 경우, 마지막에 선언된 것만 적용된다.- 예:
HEALTHCHECK --interval=1m --timeout=3s --retries=5 CMD curl -f http://localhost || exit 1
헬스체크 간격, 타임아웃, 재시도 횟수를 설정하여 프로세스가 정상적으로 작동하는지 확인할 수 있다.
- 예:
순서 및 빌드 캐시
FROM
은 가장 먼저 선언해야 하지만, 나머지 명령어들의 순서는 빌드 캐시 무효화와 관련이 있다. 캐시가 무효화되는 빈도가 적은 명령어부터 배치하는 것이 효율적이다.
Dockerfile 명령 순서
- Dockerfile의 순서는 명령어의 캐시 무효화와 관련이 있기 때문에, 자주 변경되지 않는 명령을 먼저 배치하는 것이 좋다.
4. Docker 이미지 빌드
docker build [옵션] 이미지이름[:태그] 경로 | URL | 압축파일
- -t: 이미지에 태그를 지정할 때 사용한다.
- -f: 다른 파일명을 사용하는 경우 Dockerfile을 지정한다.
5. Dockerfile이 필요한 이유
Dockerfile을 사용하지 않으면, 우분투 이미지를 이용해 컨테이너를 생성한 후 직접 컨테이너에 접속해 일일이 필요한 소프트웨어(예: 아파치 웹 서버, PHP)를 설치하고 웹 애플리케이션을 설정해야 한다. 이는 매번 반복 작업을 해야 한다는 단점이 있다.
반면, Dockerfile을 사용하면 이러한 설치 및 설정 과정을 자동화할 수 있다. 모든 명령어를 Dockerfile에 작성해두면 단 한 번의 명령어로 동일한 환경을 가진 컨테이너를 손쉽게 생성할 수 있다.
6. Dockerfile 작성 및 실행
- 작업 디렉토리 생성 및 이동
$ mkdir phpapp
$ cd phpapp
- Dockerfile 생성 및 작성: `vi Dockerfile`
FROM ubuntu:14.04
MAINTAINER "yeonsu <bestdustn@gmail.com>"
LABEL title "IaC PHP application"
RUN apt-get update && apt-get -y install apache2 php5 git curl ssh wget
# Apache2 환경 변수 설정
ENV APACHE2_RUN_USER www-data \
APACHE2_LOG_DIR /var/log/apache2 \
APACHE2_WEB_DIR /var/www/html \
APACHE2_PID_FILE /var/run/apache2/apache2.pid
# 기본 웹 페이지 및 PHP 파일 작성
RUN echo 'Hello Docker Application' > /var/www/html/index.html
RUN echo '<?php phpinfo(); ?>' > /var/www/html/index.php
EXPOSE 80
WORKDIR /var/www/html
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
- Dockerfile 빌드
현재 디렉토리에 Dockerfile 로 존재 → build
docker build -t myphpapp:1.0 .
- 컨테이너 생성 및 실행
docker run -dit -p 8101:80 --name phpapp1 myphpapp:1.0
7. 이미지 빌드 과정
이미지 빌드는 대화형이 아닌 자동화된 과정이다. 빌드가 시작되면 도커가 작성한 Dockerfile에 따라 자동으로 이미지를 생성하며, 사용자는 빌드 과정에 개입하지 않는다.
- Ubuntu 기반 이미지 생성 시 주의사항:
apt-get update
명령을 먼저 실행해야 패키지를 정상적으로 설치할 수 있다. 또한-y
옵션을 추가해 사용자 입력 없이 설치가 자동으로 진행되도록 해야 한다.
RUN apt-get update && apt-get install -y python
- .dockerignore 파일 사용: 빌드 시 제외하고 싶은 파일이나 디렉토리를
.dockerignore
파일에 명시해 빌드 컨텍스트에서 제외할 수 있다.
node_modules/
*.log
Python 개발 환경 설정 예시
- 디렉토리 생성 및 이동
mkdir python_lab
cd python_lab
- Dockerfile 작성
`nano Dockerfile` 명령을 통해 새로운 `Dockerfile`을 생성하고, 다음과 같은 내용을 작성한다:
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y python
이 때, `apt-get update` 명령을 누락하게 되면 패키지 설치가 실패할 수 있으므로 주의해야 한다.
- 이미지 빌드
docker build -t mypyapp:1.0 .
→ apt-get update
명령을 반드시 포함해야 패키지 설치에 실패하지 않음.
- Ubuntu 버전 14.04로 변경하여 빌드할 수도 있다.
FROM ubuntu:14.04
RUN apt-get update && apt-get install python
이미지 빌드 확인
이미지를 빌드하고 압축 파일이 해제된 후의 디렉토리를 확인할 수 있다.
- 이미지 용량 최적화: 불필요한 패키지와 파일을 삭제해 이미지 용량을 줄일 수 있다.
FROM ubuntu:latest
RUN apt-get update && apt-get -y install apache2 vim curl \
&& apt-get clean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN 명령을 하나로 합치고, 가능한 경우 alpine
기반 이미지를 사용해 성능을 최적화할 수 있다.
FROM python:3.9.2-alpine
COPY app.py /app
RUN pip install -r requirements.txt
CMD python /app/app.py
Dockerfile 작성 시 명령어들의 레이어 생성과 빌드 캐시의 동작을 고려해 효율적으로 작성하는 것이 중요하다.
빌드 캐시 사용을 위한 실습
- Dockerfile 생성
FROM ubuntu:latest
RUN apt-get update && apt-get install -y nginx curl vim
RUN echo 'Docker Container Application.' > /var/www/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
- 이미지 빌드
docker build -f Dockerfile -t webapp:1.0 .
- 이미지 빌드 (다른 태그 사용)
docker build -f Dockerfile -t webapp:2.0 .
- 동일한 내용을 가지고 두 번째 빌드를 하면 "cached"라는 문구가 보인다. 이는 빌드 과정에서 이전 단계의 캐시를 사용한 것이며, 빌드 속도를 향상시킨다. 이렇게 사용되는 캐시를 빌드 캐시라고 한다.
- 빌드 캐시는 동일한 명령어의 연속된 부분까지 적용되며, 그 이후부터는 캐시가 적용되지 않는다.
Dockerfile 최적화
- Python 설치 및
app.py
복사 후 실행하는 Dockerfile
FROM ubuntu:20.04
COPY app.py /app
RUN apt-get update && apt-get -y install python python-pip
RUN pip install -r requirements.txt
CMD python /app/app.py
- Dockerfile을 빌드할 때
RUN
,ADD
,COPY
명령어는 각각의 레이어를 생성한다. 반면에CMD
,LABEL
,ENV
,EXPOSE
등은 레이어를 생성하지 않는다. - 최적화 방법: 여러
RUN
명령어를 하나로 합치는 것이 좋다. 또한, 용량 최적화를 위해 Alpine과 같은 경량 이미지를 사용하는 것이 성능 향상에 유리하다.
FROM python:3.9.2-alpine
COPY app.py /app
RUN pip install -r requirements.txt
CMD python /app/app.py
- 변경 사항이 적은 명령어는 상단에 배치하여 빌드 캐시를 최대한 활용할 수 있도록 한다.
8. 다양한 방법의 Dockerfile 작성
- 쉘 스크립트를 이용한 환경 구성: 환경 변수들은
.sh
파일에 작성하고 이를 실행해 환경을 구성할 수 있다.- 디렉토리를 생성하고 프롬프트 이동
- Dockerfile 생성 및 작성
FROM ubuntu:latest
RUN apt-get update && apt-get -y install apache2
RUN echo 'Docker Container Application' > /var/www/html/index.html
RUN mkdir /webapp
RUN echo './etc/apache2/envvars' > /webapp/run_http.sh && \
echo 'mkdir -p /var/run/apache2' >> /webapp/run_http.sh
EXPOSE 80
CMD /webapp/run_http.sh
- ADD 명령어의 자동 압축 해제 기능 활용: 압축 파일을 Dockerfile에서 가져와 자동으로 해제할 수 있다.
sudo apt install git
git clone <https://github.com/brayanlee/webapp.git>
- 압축 파일이 있는 곳으로 프롬프트를 이동: `cd webapp`
- `ls` 명령으로 압축 파일이 있는지 확인
- Dockerfile이 저장될 디렉터리를 생성: `mkdir dockerfiles`
- Dockerfile 생성하고 작성: `nano dockerfiles/Dockerfile`
FROM ubuntu:latest
RUN apt-get update && apt-get -y install apache2 vim curl
ADD webapp.tar.gz /var/www/html
WORKDIR /var/www/html
EXPOSE 80
CMD /usr/sbin/apachectl -D FOREGROUND
- 이미지 빌드 후 컨테이너 실행
docker run -dit -p 8201:80 --name webapp8 webapp:8.0
- 압축된 파일이 해제되었는지 확인
docker exec -it webapp8 /bin/bash
ls
- 압축된 파일을 복사할 때는 개별 파일 복사보다 디렉토리 전체를 복사하거나 압축 파일 형태로 복사하는 것이 효율적이다.
- 기본 이미지를 사용하는 경우 불필요한 파일을 제거하여 이미지 크기를 줄일 수 있다.
apt-get clean
,apt-get autoremove
,rm -rf
명령어를 사용하여 임시 파일과 패키지 캐시를 삭제한다.
이전에 사용한 도커 파일을 수정(`nano dockerfiles/Dockerfile`)
FROM ubuntu:latest
RUN apt-get update && apt-get -y install apache2 vim curl && \
apt-get clean -y && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ADD webapp.tar.gz /var/www/html
WORKDIR /var/www/html
EXPOSE 80
CMD /usr/sbin/apachectl -D FOREGROUND
- 이미지 빌드 및 크기 확인
docker build -t webapp:9.0 -f ./dockerfiles/Dockerfile .
docker images
이전보다 이미지 크기가 줄어든 것을 확인할 수 있다.
9. Python Flask 애플리케이션 빌드
Flask 애플리케이션 디렉토리 생성 및 소스 코드 작성
mkdir py_flask && cd py_flask
mkdir app && nano app/py_app.py
from flask import Flask
py_app = Flask(__name__)
@py_app.route('/')
def python_flask():
return "<h1>Hello Flask</h1>"
if __name__ == "__main__":
py_app.run(host="0.0.0.0", port=9000, debug=True)
패키지 의존성 파일 생성: `app/requirements.txt` 생성
nano app/requirements.txt
Flask==1.1.2
- 가상 환경을 사용하는 것이 의존성 관리에 유리하다.
`pip freeze > requirements.txt` 명령을 이용하여 파일 생성
→ 가상환경인 경우, 가상환경에 있는 패키지들만 올라가지만, 가상환경이 없으면 깔려있는 모든 패키지들이 몽땅 다 올라간다.
따라서 되도록이면 python을 이용할 때 가상환경을 사용하는 것이 좋다.
현재 디렉토리에 Dockerfile을 만들고 작성
FROM python:3.8-alpine
RUN apk update && apk add --no-cache bash build-base python3-dev py-pip
ENV LIBRARY_PATH=/lib:/usr/lib
ENV FLASK_APP=py_app
ENV FLASK_ENV=development
WORKDIR /py_app
COPY ./app/ .
RUN pip install -r requirements.txt
EXPOSE 9000
ENTRYPOINT ["python"]
CMD ["py_app.py"]
이미지 빌드 및 확인
docker build -t py_flask:1.0 .
docker images
py_flask 이미지가 만들어졌는지 확인