Docker File
1. 이미지를 생성하는 법
개발한 애플리케이션을 컨테이너화를 할 때 가장 먼저 생각나는 방법은 아래와 같다.
1. 아무것도 존재하지 않는 이미지(우분투, centOS 등)로 컨테이너를 생성
2. 애플리케이션을 위한 환경을 설치하고 소스코드 등을 복사해 잘 동작하는 것을 확인
3. 컨테이너를 이미지로 커밋
위와 같은 방법은 애플리케이션이 동작하는 환경을 구성하기 위해 일일이 수작업으로 패키지를 설치하고 소스코드를 깃에서 복제하거나 호스트에서 복제해야한다. (너무 귀잖다.)
하지만 도커는 위와 같은 일련의 과정을 손쉽게 기록하고 수행할 수 있는 빌드 명령어를 제공한다. 완성된 이미지를 생성하기 위해 컨테이너에 설치해야 하는 패키지, 추가해야 하는 소스코드, 실행해야 하는 명령어와 셸 스크립트 등을 하나의 파일에 기록해 두면 도커는 이 파일ㅇㄹ 읽어 컨테이너에서 작업을 수행한 뒤 이미지로 만들어낸다.
이러한 작업을 기록한 파일의 이름을 Dockerfile 이라 하며 빌드 명령어는 Dockerfile을 읽어 이미지를 생성합니다. dockerfile을 사용하면 직접 컨테이너를 생성하고 이미지로 커밋해야 하는 번거로움을 덜 수 있을뿐더러 깃과 같은 개발 도구를 통해 애플리케이션의 빌드 및 배포를 자동화할 수도 있다.
즉 이미지 자체를 배포하는 대신 이미지를 생성하는 방법을 기록해 놓은 Dokcerfile을 배포할 수도 있다는 것이다.
Dockerfile을 작성하는 것은 이미지를 생성하는 방법을 기록하는 것뿐만 아니라 이미지의 빌드, 배포 측면에서도 매우 유리하다. 필요한 패키지 설치등을 명확히 할 수 있고 이미지 생성을 자동화 할 수 있으며, 쉽게 배포할 수 있다.
2. Dockerfile 작성
먼저 Dockerfile에서 쓰이는 명령어를 알아두자.
vi Dockerfile
FROM ubuntu:14.04
MAINTAINER kuwhawha
LABEL "purpose"="practice"
RUN apt-get update
RUN apr-get install apache2 -y
ADD test.html /var/www/html
WORKDIR /var/www/html
RUN ["/bin/bash", "-c", "echo hello >> test2.html"]
EXPOSE 80
CMD apachectl -DFOREGROUND
Dockerfile 은 기본적으로 한 줄이 하나의 명령어가 되고, 명령어를 명시한 뒤에 옵션을 추가하는 방식이다. 일반적으로 대문자로 표기한다.
FROM : 생성할 이미지의 베이스가 될 이미지를 뜻한다. Form 명령어는 Dokcer 작성할 때 반드시 한 번 이상 입력해야하며, 이미지 이름의 포맷은 docker run 명령어에서 이미지 이름을 사용했을 때와 같다.
MAINTAINER : 이미지를 생성한 개발자의 정보
LABLE : 이미지에 메타데이터를 추가한다. (이 값은 키:값 형태로 저장되며 여러개의 메타 데이터가 저장될 수 있다.)
RUN : 이미지를 만들기 위해 컨테이너 내부에서 명령어를 실행한다. 그 과정에서 별도의 입력이 불가능하기 때문에 뒤에 [Yes/NO] 같은 질문의 답을 미리 써주는 것이다.
RUN ["실행 가능한 파일", "명령줄 인자 1", "명령줄 인자2"] 이런식으로도 사용할 수 있다.
ADD : 파일을 이미지에 추가한다. 추가하는 파일은 Dockerfile이 위치한 폴더 context에서 가져온다.
WORKDIR : 명령어를 실행할 디렉토리를 나타낸다.
EXPOSE : Dockerfile의 빌드로 생성된 이미지에서 노출할 포트를 설정한다. 반드시 이 포트가 호스트의 포트와 바인딩 되는 것은 아니며, 단지 컨테이너의 사용할 포트를 나타내는 것뿐이다.
CMD : 컨테이너가 시작될 때마다 실행할 명령어를 설정하며 Dokerfile에서 한 번만 사용할 수 있다. 위의 명령어는 컨테이너를 생성할 때 별도의 커맨드를 입력하지 않아도 자동으로 아파치 웹 서버가 실행될 것이다. 입력은
["실행 가능한 파일", "명령줄 인자1", "명령줄 인자2"...] 이러한 형태로도 사용할 수 있다.
3. Dockerfile 빌드
1. 이미지 생성
앞에 만든 dockerfile 을 빌드해보자
docker build -t mybuild:0.0 ./
-t : 생성될 이미지의 이름을 설정한다.
build 명령어의 끝에는 Dockerfile이 저장된 경로를 입력한다.
-> 최종적으로 mybuild:0.0 이미지가 생성된다.
=> 가끔 설치 도중에 오류가 뜰 수 있다. 캐쉬 메모리 충돌이 일어났을 수 있으니 .
docker rm -f $(docker ps -a -q)
docker rmi $(docker images -q)
를 사용하고 재부팅 후 재 설치하면 문제없이 실행 될 것이다.
docker run -d -P --name myserver mybuild:0.0
을 사용해서 실행해보자.
이때 -P는 이미지에 설정된 EXPOSE의 모든 포트를 호스트에 연결하도록 설정하는 명령어이다. 그러므로 이 컨테이너가 호스트의 어떤 포트와 연결됐는지 확인해볼 필요가 있다.
docker port myserver, docker ps 를 사용해서 확인 할 수 있다.
Dockerfile에 이미지의 라벨을 purpose=practicefh 설정했으므로 docker images 명령어의 필터에 이 라벨을 적용할 수 있다.
docker images --filter "label=purpose=practive"
빌드 과정 )
이미지 빌드를 시작하면 가장 먼저 빌드 컨텍스트를 읽어 온다. 그 곳에는 이미지를 생성하는 데 필요한 각종 파일, 소스코드, 메타데이터 등을 담고 있고 Dockerfile에 위치한 디렉터리가 빌드 컨텍스트 된다.
빌드 컨텍스트는 Dockerfile에서 빌드될 이미지에 파일을 추가할 때 사용된다. (ADD, COPY)
컨텍스트는 build 명령어의 맨 마지막에 지정된 위치에 있는 파일을 전부 포함한다. (저장소의 파일과 서브 모듈까지 모두) -> 따라서 Dockerfile이 위치한 곳에는 이미지 빌드에 필요한 파일만 있는 것이 바람직하다.
이를 방지하기 위해 .dockerignore라는 파일을 작성하면 이 파일에 명시된 이름의 파일을 컨텍스르에서 제외 할 수 있다. 이 또한 Dockerfile이 존재하는 경로 기준으로 한다.
Dockerfile을 이용한 컨테이너 생성과 커밋
Build 명령어를 실행할 때 각각의 명령어에 따라 새로운 컨테이너가 하나씩 생성되며 이를 이미지로 커밋한다.
따라서 이미지의 빌드가 완성되면 Dockerfile의 명령어 수 만큼 레이어가 존재하게 되며, 중간에 컨테이너도 같은 수만큼 생성되고 삭제된다.
캐시를 이용한 이미지 빌드
한번 이미지 빌드를 마치고 난 뒤 다시 같은 빌드를 진행하면 이전의 이미지 빌드에서 사용했던 캐시를 사용한다.
Using cache 라는 출력과 함께 별도의 빌드 과정이 진행되지 않고 더 빠르게 이미지가 생성된다.
하지만 항상 유용한 것은 아니다. 리버전 관리가 일어나도 매번 빌드를 할 때마다 고정된 소스코드를 사용할 수 있기 때문이다. 이때 사용하는 명령어가--no-cache 옵션이다.
docker build --no-cache -t my_build:0.0
또는 사용할 이미지를 직접 지정할 수도 있다.
docker build --cache-from nginx -t my_ex_ng:0.0 .
멀티 스테이지를 이용한 Dockerfile 빌드하기
멀티 스테이지 빌드는 하나의 Dockerfile 안에 여러개의 From 이미지를 정의함으로써 빌드 완료 시 최종적으로 생성될 이미지의 크기를 줄이는 역활을 한다.
- 사용이유 : 실제 실행 파일의 크기는 매우 작지만 소스코드 빌드에 사용된 패키지 및 라이브러리가 불필요하게 이미지의 크기를 차지하고 있을 수 있기 때문에
즉, 컴파일이나 패키지 설치 과정에서 부수적으로 발생하는 불필요한 파일들은 배제하고, 애플리케이션 구동에 필요한 것만 선택하여 가져오므로 이미지를 경량화시킬 수 있다.
#---------------------------------------------------------------------------------------------------------------------------#
기타 Dockerfile 명령어.
ENV : Dockerfile 에서 사용될 환경변수를 지정한다. 이 환경변수는 이미지에도 저장되므로 빌드된 이미지로 컨테이너를 생성하면 이 환경변수를 사용할 수 있다. run 명령에서 -e 옵션을 사용하면 덮어 쓸 수 있다.
${env_name:-value} -> env_name이라는 환경변수가 설정되지 않았으면 value로 값을 지정한다. +의 경우에는 값이 설정돼 있으면 value를 값으로 사용하고 설정되지 않으면 빈 문자열을 사용한다.
VOLUME : 빌드된 이미지로 컨테이너를 생성했을 때 호스트와 공유할 컨테이너 내부의 디렉터리를 설정한다. ["/home/dir", "/home/dir2"] 이렇게 사용 한다.
ARG : build 명령어를 실행할 때 추가로 입력을 받아 Dockerfile 내에서 사용될 변수의 값을 설정합니다. ARG를 통해 입력 받지 않아도 기본값을 지정할 수 있다.
USER : USER로 컨테이너 내에서 사용될 사용자 계정의 이름이나 UID를 설정하면 그 아래의 명령어는 해당 사용자 권한으로 실행된다. 루트 권한이 필요하지 않다면 USER를 사용하는 것을 권장한다.
Onbuild : 빌드된 이미지를 기반으로 하는 다른 이미지가 Dockerfile로 생성될 때 실행할 명령어를 추가한다.
STOPSIGNAL : 컨테이너가 정지될 때 사용될 시스템 콜의 종류를 지정한다. 아무것도 설정하지 않으면 기본적으로 SIGTERM로 설정되지만 Dockerfile에 STOPSIGNAL을 정의해 컨테이너가 종료되는 데 사용될 신호를 선택할 수 있다. (SIGTERM, SIGKILL 등등)
HEALTHCHECK : HEALTHCHECK는 이미지로부터 생성된 컨테이너에서 동작하는 애플리케이션의 상태를 체크하도록 설정한다. 컨테이너 내부에서 동작 중이 애플리케이션의 프로세스가 종료되지는 않았으나 애플리케이션이 동작하고 있지 않은 상태를 방지기 위해 사용될 수 있다.
--interval => 주기
--timeout => 설정시간 초과하면 실패한 것으로 간주하고 --retrites의 횟수만큼 명령어 반복 -> 그것도 실패하면 unheathy 상태로 지정 docker ps 를 통해 상태 확인 가능
SHELL : Dockerfile에서 기본적으로 사용하는 셸은
리눅스 - "bin/sh -c"
윈도우 - "cmd /S /C"
하지만 SHELL 명령어로 SHELL ["/user/local/bin/node"] 이런식으로 변경 가능
COPY : 로컬 디렉터리에서 읽어 들인 컨텍스트로부터 이미지에 파일을 복사하는 역활을 한다. -> 형식은 ADD랑 동일
COPY ["test.html", "/home/"]
COPY는 로컬의 파일만 이미지에 추가할 수 있지만 ADD는 외부 URL 및 tar 파일에서도 파일을 추가할 수 있다는 점이 다르다.
ENTRYPOINT : 커맨드와 동일하게 컨테이너가 시작될 때 수행할 명령을 지정한다. 그러나ENTRYPOINT는 커맨드를 인자로 받아 사용할 수 있는 스크립트의 역활을 할 수 있다는 점에서 다르다.
Dockerfile 빌드할 때 주의할 점
1. 역슬래쉬로 나눠서 가독성을 높이자.
2. .dockerignore 파일을 작성해 불필요한 파일을 빌드 컨텍스트에 포함하지 않는다.
3. 빌드 캐쉬를 사용하여 기존에 사용했던 이미지 레이어를 재사용하자.
4. Dockerfile을 작성할 때 &&로 각 RUN 명령을 하나로 묶어 하나의 레이어로 만들자.
5. 도커 이미지 구조 활용하자.