docker

[Docker] Dockerfile

수수한개발자 2023. 10. 26. 16:30
728x90

IaC(Infrastructure as Code)가 필요한 이유

IaC(Infrastructure as Code) 코드형 인프라가 필요한 이유는 커맨드 기반의 인프라 구성시 사용자 실수 등의 인적 오류 가능성이 높다.

각종 환결 설정 정보와 설치 프로그램을 요구사항에 맞게 따져야 하며 설치 이후에도 잘못된 부분이 있다면 수정해야하고 재설치도 불가피하다.

이러한 수고로움을 하나의 이미지로 만들어 두고, 수정사항은 언제든 코드 변경이 용이하다면 개발 업무에만 집중할 수 있다.

"코드형 인프라"는 탄력성, 확장성, 반복성을 부여하게 되어 눈송이 서버가 아닌 동일한 환경을 보유한 서버(컨테이너)를 수십~수백대를 운영, 관리하게 해준다.

 

Dockerfile

Dockerfile은 Docker에서 동작하는 컨테이너의 구성 정보를 프로비저닝한 텍스트 template 파일이다.

  • 프로비저닝은 사용자의 요구에 맞게 시스템 자원을 할당, 배치, 배포해 두었다가 필요 시 시스템을 즉시 사용할 수 있는 상태로 미리 준비해 두는것
  • Dockerfile은 컨테이너에 설치해야 하는 패키지, 소스코드, 명령어, 환경변수 설정 등을 기록한 하나의 파일

Dockerfile은 애플리케이션 배포에 필요한 컨테이너 인프라를 코드 형태로 정의한 파일이다.

이러한 개념을 IaC라고 한다.

 

 

Dockerfile 명령어

FROM(필수, layer)

  • 생성하려는 이미지의 베이스 이미지 지정으로 hub.docker.com에서 제공하는 공식(오피션)이미지 권장하며 이미지 태그는 도커 허브에서 여러 태그가 버전 정보처럼 제공된다.
  • 이미지를 선택할 때 작은 크기의 이미지(slim)와 리눅스 배포판인 알파인(alpine) 이미지를 권장한다. 
  • 태그를 넣지 않으면 latest로 지정된다.

사용방법

FROM ubuntu:20.04

FROM python:3.9-slim-buster

FROm mongo:4.4.4-bionic

 

MAINTAINER

일반적으로 이미지를 빌드한 작성자 이름과 이메일을 작성한다.

 

사용방법

MAINTAINER jisu3268 <kimjisu3268@gmail.com>

 

LABEL(layer)

이미지의 작성 목적으로 버전, 타이틀, 설명, 라이선스 및 작성자 정보 등을 작성하여 이미지 관리에 도움이 된다.

하나 이상 작성 가능하다.

 

사용 방법

LABEL purpose = 'Nginx for webserver'

LABEL version = '1.0'

LABEL description = 'web service application using Nginx'

 

권장 사항

LABEL purpose = 'Nginx for webserver' \

             version = '1.0' \

             description = 'web service application using Nginx'

 

RUN(layer)

설정된 기본 이미지에 패키지 업데이트, 각종 패키지 설치, 명령 실행 등을 작성한다.

하나 이상 작성 가능하다.

 

사용 방법

RUN apt update

RUN apt -y install nignx

RUN apr -y install git

 

권장 사항

  • RUN 명령어의 개별 수를 줄여서 여러 설치 명령을 연결하면 이미지의 레이어 수 감소 됨
  • Autoremote, autoclean, rm -rf /var/lib/lists/* 을 사용하면 저장되어 있는 apt 캐시가 삭제되므로 이미지 크기가 감소

RUN apt update & apt install -y nginx \

git \

vim \

curl && \

apt clean -y && \

apt autoremove -y && \

rm -rf /var/lib/lists/* /var/tmp/*

 

 

CMD

생성된 이미지를 컨테이너로 실행할 때 실행되는 명령이고, ENTRYPOINT 명령문으로 지정된 커맨드에 디폴트로 넘길 파라미터를 지정할 때 사용한다. 여러 개의 CMD를 작성해도 마지막 하나만 처리된다. 일반적으로 이미지가 컨테이너 실행 시 애플리케이션 데몬이 실행되도록 하는 경우 유용하다.

 

사용 방법

[Shell 방식]

CMD apachectl -D FOREGROUND

 

[Exec 방식]

CMD ["/usr/sbin/apachectI", "-D", "FOREGROUND"]

CMD ["nginx", "=g", "daemon off;"]

CMD ["python", "app.py]

 

ENTRYPOINT

CMD와 마찬가지로 생성된 이미지가 컨테이너로 실행될 때 사용되지만, 다른 점은 컨테이너가 실행될 때 명령어 및 인자 값을 전달해야 실행 한다.

 

사용 방법

CMD와 유사하지만 인자값을 사용하는 경우에 유용

ENTRYPOINT ["npm", "start"]

ENRTYPOINT ["python", "runapp.py"]

 

사용 예

동일 환경 에 entrypoint.sh 쉘 스크립트를 이미지에넣고 실행 권한 설정 후 컨테이너 실행시 entrypoint.sh를 실행

ADD ./entrypoint.sh /entrypoin.sh

RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]

 

COPY(layer)

호스트 환경의 파일, 디렉터리 이미지 안에 복사하는 경우 작성한다.

단순한 복사 작업만 지원한다. 빌드 작업 디렉터리 외부의 파일은 COPY 할 수 없다.

복사가 필요한 명시적 작업인 경우는 ADD 보다는 COPY를 권장한다. COPY는 ADD 보다 더 예측 가능하고 오류가 덜 발생한다.

 

사용 방법

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

COPY ./runapp.py/

 

 

ADD(layer)

호스트 환경의 파일, 디렉터리를 이미지 안에 복사하는 경우 뿐만 아니라, URL 주소에서 직접 다운로드하여 이미지에 넣을 수 도 있고, 압축 파일(tar,tar.gz)인 경우에는 지정한 경로에 압축을 풀어서 추가한다. 단 빌드 작업 디렉터리 외부의 파일은 ADD할 수 없고 디렉터리 추가시에는 / 로 끝나야 한다.

 

사용 방법

ADD index.html /usr/share/nginx/html

ADD http://google.com /workspace/data/

ADD example.tar.gz /var/www/html

 

 

ENV(layer)

이미지 안에 각종 환경 변수를 지정하는 경우 작성한다. 애플리케이션 사용을 쉽게 하려면 사전에 구성되야 하는 환경 변수들이 있다. 예를 들어 자바 홈 디렉터리, 특정 실행파일의 경로를 보장하기 위해 절대 경로 지정을 위한 PATH 설정, 프로그램의 버전 등을 사전에 설정한다. 또한, 반복적인 표현이 사용되는 경우에도 환경 변수 설정을 권장한다. Dockerfile에서 ENV를 설정하면 RUN, WORKDIR등에서 환경 변수를 사용해 반복을 피할 수 있다.

 

사용 방법

ENV JAVA_HOME /usr/lib/jvm/java-8-oracle

ENV PATH /usr/local/nginx/bin:$PATH

 

사용 예

ENV JAVA_VERSION 17

 

EXPOSE

컨테이너가 호스트 네트워크를 통해 들어오는 트래픽을 리스닝하는 포트와 프로토콜을 지정하기 위해 작성한다. Nginx는 기본 포트로 HTTP 80번과 HTTPS 443번 포트를 사용하고, 컨테이너 모니터링 이미지로 사용하는 Cadvisor 컨테이너는 8080번 포트를 사용한다. 이미지 내에 애플리케이션이 사용하는 포트를 사전에 확인하고 호스트와 연결되도록 구성하는 경우 설정하고 docker run 사용시 -p 옵션을 통해 사용한다.

 

사용 방법

EXPOSE 80

EXPOSE 80/tcp

EXPOSE 443

EXPOSE 8080/udp

 

VOLUME

볼륨을 이미지 빌드에 미리 설정하는 경우 작성한다. 도커 컨테이너에서 사용된 파일과 디렉터리는 컨테이너 삭제와 함께 사라진다. 따라서 사용자 데이터 보존과 지속성을 위해 볼륨 사용을 권장한다. VOLUME으로 지정된 컨테이너의 경로는 볼륨의 기본 경로 /var/lib/docker와 자동으로 연결된다.

 

사용 방법

VOLUME /var/log

VOLUME /etc/nginx

HOST OS의 Volume 기본 경로와 컨테이너 내부의 /project와 연결

VOLUME ["/project"]

 

USER

컨테이너 기본 사용자는  root이다. 애플리케이션 권한 없이 서비스를 실행할 수 있다면 USER를 통해 다른 사용자로 변경한다.

 

사용 방법

RUN ["useradd", "jisu"]

USER jisu

RUN ["/bin/bash", "-c", "date"]

 

RUN groupadd -r mongodb&& \

useradd --no-log-init -r -g mongodb mongodb

 

WORKDIR

컨테이너 상에서 작업할 경로 전환을 위해 작성한다. 설정하면 RUN, CMD,ENTRYPOINT, COPY, ADD 명령문은 해당 디렉터리를 기준으로 실행한다. 지정한 경로가 없으면 자동 생성되고, 컨테이너 실행 이우 컨테이너에 접속 하면 지정한 경로로 연결된다.

 

사용 방법

WORKDIR /workspace

WORKDIR /usr/share/nginx/html

 

ARG

docker build 시점에서 변수 값을 전달하기 위해 "--build-arg=인자"를 정의하여 사용한다.

비밀 키, 계정 비밀번호 같은 민감한 정보 사용시 이미지에 그대로 존재하여 노출될 위험이 있으므로 이러한 정보는 주의해야 한다.

 

사용 방법

#Dockerfile에 ARG 변수를 정의하고

ARG db_name

# docker build 시 변수 값을 저장하면 이미지 내부로 인자가 전달된다.

~$ docker build --build-arg db_name=mydb.

#입력 받은 변수 값을 다음과 같이 명령에 사용한다.

CMD db_start.sh -h 127.0.0.1 -d ${db_name}

 

 

 

 

Dockerfile 최적화

도커 파일의 최적화를 위해 고려해야 될 사항

  • 빌드 시간
  • 이미지 크기
  • 재사용성
  • 보안
  • 유지보수성

컨테이너는 경량의 가상화 서비스를 목적으로 한다.

Docker에서 제공되는 base image들도 해당 이미지의 지향점에 필요한 프로그램, 라이브러리, 실행 파일만을 보유한다.

애플리케이션은 필요한 추가 구성 요소를 포함시키는 복잡성, 의존성 문제를 피해야 하고 언제든 빠른 컨테이너 배포를 위해

최소한의 설정과 구성을 권장한다.

이미지 내부에 불필요한 파일을 포함하지 않기 위해 .dockerignore 사용으로 빌드와 관련 없는 파일을 이미지 내부에 넣지 않는다.

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

 

 

최적화를 하기 위한 도커파일을 하나 생성하겠습니다.

 

#Step: 1 ubnuntu (base image)
FROM ubuntu:20.04

#Step: 2 Nginx package install
RUN apt-get -y update && apt-get install -y -q nginx

#Step: 3 source file copy
COPY index.html /var/www/html

#Step: 4 Nginx start (containcer execution)
CMD ["nginx", "-g", "daemon off."]

 

#.을 붙이면 현재 경로에 있는 dockerfile을 읽어서 수행한다.
docker build -t test-webserver:1.0 .

# image 확인
docker images | grep test

#image 실행
docker run -d --name=test-webserver -p 8081:80 test-webserver:1.0

 

 

1. 불필요한 파일 제거를 통한 이미지 경량화

3개의 도커파일을 만들어 줍니다.

dockerfile-myweb1

apt를 통해 설치된 패키지 파일들을 삭제하지 않고 이미지에 방치한다.

FROM ubuntu:14.04
MAINTAINER "jisu3268 <kimjisu3268@gmail.com>" LABEL "purpose"="webserver practice"
RUN apt update && apt install apache2 -y 
WORKDIR /var/www/html
COPY index.html .
EXPOSE 80
CMD apachectl -D FOREGROUND

dockerfile-myweb2

패키지 설치를 위해 다운로드한 패키지 잔여물을 autoremove, autoclean, rm 등으로 제거하여 경량화 한다.

FROM ubuntu:14.04
MAINTAINER "jisu3268 <kimjisu3268@gmail.com>"
LABEL "purpose"="webserver practice"
RUN apt-get update && apt-get install apache2 -y && \
apt-get clean autoclean && \
apt-get autoremove -y && \
rm -rfv /tmp/* /var/lib/apt/lists/* /var/tmp/*
WORKDIR /var/www/html
COPY index.html .
EXPOSE 80
CMD apachectl -D FOREGROUND

dockerfile-myweb3

가능하다면 base image를 alpine linux로 변경하여 이미지 크기를 줄인다.

FROM alpine
MAINTAINER "jisu3268 <kimjisu3268@gmail.com>"
LABEL "purpose"="webserver practice"
RUN apk update && apk add apache2
WORKDIR /var/www/html
COPY index.html .
EXPOSE 80
CMD ["/usr/sbin/httpd","-D","FOREGROUND"]

 

빌드

docker build -t myweb:1.0 -f dockerfile-myweb1 --no-cache .
docker build -t myweb:2.0 -f dockerfile-myweb2 --no-cache .
docker build -t myweb:3.0 -f dockerfile-myweb3 --no-cache .

 

docker images | grep myweb

 

알파인 리눅스를 사용하게되면 엄청나게 이미지 용량 차이가 나는것을 확인할 수 있다.

 

 

2. Multi-stage build를 통한 이미지 경량화

 

vi appstart.sh #!/bin/bash
echo "Best image build, multi stage build!"
chmod +x appstart.sh

 

vi dockerfile-app1
FROM ubuntu:14.04
RUN apt-get update -y && apt-get install nginx -y COPY appstart.sh /
RUN useradd jisu
USER jisu
ENTRYPOINT ["/appstart.sh"]

 

stage1

vi dockerfile-app2
FROM ubuntu:20.04 as v1-stage
RUN apt-get update && apt-get install nginx -y 
WORKDIR /app
COPY appstart.sh /app

 

stage2

FROM alpine:3.12.1
RUN addgroup -S appgroup && adduser -S jisu -G appgroup -h /home/jisu
COPY --from=v1-stage /app /home/jisu
USER jisu
ENTRYPOINT ["sh","/home/jisu/appstart.sh"]

 

 

3. ADD 기능으로 *.tar.gz으로 공유 받은 압축파일 이미지에 바로 적용하기

압축파일이 바로 빌드시 WORKDIR로 압축 해제되어 빌드 된다.

vi dockerfile-add
FROM ubuntu:14.04
MAINTAINER "jisu3268 <kimjisu3268@gmail.com>"
LABEL "purpose"="webserver practice"
RUN apt-get update && apt-get -y install apache2 \
										 vim \ 
                                         curl \
			&& rm -rf /var/lib/apt/lists/* \ 
            && rm -rf /etc/apt/sources.list.d/*
ADD webapp.tar.gz /var/www/html
WORKDIR /var/www/html
EXPOSE 80
CMD /usr/sbin/apache2ctl -D FOREGROUND

 

 

Java 환경 Image Build

# 자바 11
FROM adoptopenjdk:11-jre-hotspot
# 자바 17
# FROM openjdk:17-jdk-slim

WORKDIR /usr/src/app

# maven
COPY ./target/*.jar app.jar

#gradle
# COPY ./build/libs/*.jar app.jar

CMD ["java", "-jar", "app.jar"]

 

 

728x90