FROM

 

상세설명

docker 컨테이너를 어떤 docker 이미지로부터 생성할지 기술. 이것을 베이스 이미지라고 한다. 이 명령은 필수 명령 항목이다. 태그를 생략하면 베이스 이미지의 최신 버전이 적용된다. 고유한 이미지를 특정해서 명시하려면 다이제스트를 사용한다.

 

예시

FROM [이미지명]
FROM [이미지명]:[태그명] 
FROM [이미지명]@[다이제스트]

FROM centos:centos7
FROM tensorflow/tensorflow@sha256:27394k393.....

 

# 참고 : digest 값이란?

도커 허브에 이미지를 업로드하면 자동으로 부여되는 unique한 식별자. 이 값은 고유하므로 특정 이미지를 지정할 때 사용할 수 있다. 다이제스트 값을 확인하려면 다음 명령을 사용한다.

docker image ls --digest [이미지명]

 

LABEL

 

상세설명

Dockerfile로 생성할 이미지에 버전 정보나 작성자 정보, 코멘트 등을 제공하는데 사용한다.

 

 

예시

구문 : LABEL <키 명>=<값>

도커파일에 아래와 같이 명시할 수 있다.

LABEL maintainer="sungsikyung<ssking@live.co.kr>"
LABEL title="webapp"
LABEL version="10.1"
LABEL description="This test images thanks"

빌드 후, docker image inspect로 확인 docker image inspect --format="{{ .Config.Labels }}" 이미지명

map[description:This test images thanks maintainer:sungsikyung<ssking@live.co.kr title:webapp version:10.1]

 

 

SHELL

 

상세설명

쉘 형식으로 명령을 실행할 때의 기본 쉘을 설정한다. SHELL 명령을 지정하지 않았을 때 리눅스의 기본 쉘은 /bin/bash -c 이며, 윈도우는 cmd /S /C 이다. SHELL 명령을 지정하면, 그 쉘은 그 이후에 도커파일 안에서 쉘 형식으로 지정한 RUN, CMD, ENTRYPOINT 명령에서 유효해진다.

 

예시

구문 : SHELL ["쉘의 경로", "파라미터"]

SHELL ["/bin/bash", "-c"]
RUN echo hello

 

 

WORKDIR

 

상세설명

작업 디렉토리를 지정한다. 즉 도커파일에서 정의한 명령을 실행하기 위한 기본 디렉토리를 지정한다. 대상이 되는 명령은 다음과 같다. (RUN, CMD, ENTRYPOINT, COPY, ADD) 또한, WORKDIR 명령에서 지정한 디렉토리가 존재하지 않는다면 알아서 새로 만드므로 따로 미리 만들어 둘 필요는 없다. 이 명령은 도커파일 안에서 여러번 사용할 수 있다. 필요에 기본디렉토리를 바꿔가며 dockerfile 내용을 전개할 수 있다. 만약 상대 경로를 지정한 경우는 이전 WORKDIR 명령의 경로에 대한 상대 경로가 된다. 추가로, WORKDIR 명령에는 ENV 명령에서 지정한 환경변수를 사용할 수 있다.

 

예시

ENV로 지정한 환경변수를 사용하기

ENV DIRPATH /first
ENV DIRNAME second
WORKDIR $DIRPATH/$DIRNAME
RUN ["pwd"]

결과 : /first/second

 

 

USER

 

상세설명

이미지 실행이나 도커파일의 RUN, CMD, ENTRYPOINT 명령을 실행하기 위한 사용자를 지정한다. USER 명령에서 지정하는 사용자는 RUN 명령으로 미리 만들어 두어야 한다. 구문은 다음과 같다 : USER [사용자명/UID]

 

예시

RUN ["whoami"]     // 현재 root가 현재유저
RUN ["adduser", "test1"]    // 유저를 생성
USER test1   // 해당 유저로 로그인한다.
RUN ["whoami"]    // test1 유저 상태를 확인할 수 있다.

 

 

ENV

 

상세설명

도커파일 안에서 환경변수를 설정한다. ENV 명령으로 지정한 환경변수는 컨테이너 실행시의 docker container run 명령의 --env 옵션을 사용하면 변경할  있다. 구문 key value 형식과 key=value 형식 2가지가 있다.  

 

key value 형으로 지정하는 경우, 단일 환경변수에 하나의 값을 지정한다. 첫 번째 공백 앞을 key로 설정하면 그 이후는 모두 문자열로서 취급한다. 공백이나 따옴표와 같은 문자를 포함한 것도 문자로 취급한다. 이 명령을 실행하면 명령별로 도커 이미지 레이어를 만든다. 아래 예시는 ENV가 3줄이므로 3개의 도커 이미지 레이어가 생긴다.

 

예시 (ENV [key] [value] )

ENV myName "Sung si kyung"
ENV myOrder Gin Whisky Clvados
ENV myNickName clova

 

key=value 형으로 지정하는 경우, 한 번에 여러 개의 값을 설정할 때는 이 방식을 사용한다. 아래 예시처럼 ENV 후 한줄에 싹 다 쓸 수 있다. 이렇게 한줄에 하면 만들어지는 도커 이미지는 하나이다. 또한 변수 앞에 \를 추가하면 이스케이프 처리를 할 수 있다. 예를 들어 \$myName은 $myName이라는 리터럴로 치환할 수 있다.(기호를 단순한 문자로 바꾼다는 의미)

 

예시 (ENV [key] = [value])

ENV myName="Sunghyun Kim"/
    myOrder=Gin Whisky Clvados/
    myNickName sshkim

 

 

ARG

 

상세설명

도커파일 안에서 사용할 변수를 정의한다. 이 명령을 사용하여 변수의 값에 따라 생성되는 이미지의 내용을 바꿀 수 있다. 환경변수 ENV와 달리 이 변수는 도커파일 안에서만 사용할 수 있다. 또한, 해당 도커파일을 빌드할 때, --build-arg 옵션을 사용하여, 도커파일 내부에 정의된 변수에 기본값을 넣을 수 있다.

 

예시 (ARG 이름 [=기본값]) 

ARG YOURNAME="test1"
RUN echo $YOURNAME

 

예시대로 도커파일을 구성하고 빌드할 때 아무 옵션 없이 한다면 그대로 test1이 정의되겠지만, 아래와 같이 --build-arg 옵션을 사용하여 변수를 바꿀 수 있다. 원래 test1이었으나 yunhee로 바뀐다.

docker build. --build-arg YOURNAME=yunhee

 

 

< RUN, CMD, ENTRYPOINT >

 

이 세 명령어는 모두 명령을 실행하는 역할을 한다. 하지만 각기 특성이 있고 차이가 있어 함께 설명해야 이해가 빠르므로, 3개를 비교하여 함께 설명한다.  기본적으로 RUN은 이미지 작성, 즉 컨테이너 자체에 적용된 명령이며, CMD와 ENDPOINT는 만들어진 이미지를 바탕으로 생성된 컨테이너 위에서 실행하는 명령이라고 이해할 수 있다. 예를들어, 웹서버를 가동하기 위해 nginx를 설치하는것은 RUN으로 하지만, 설치한 nginx를 데몬으로서 컨테이너 안에서 작동시키려면 CMD를 사용한다.

 

세 명령어 모두 실행 방식 2가지를 가진다. shell 커맨드 방식, EXEC 방식 두가지이다.

 

1. 쉘커맨드 방식

내부적으로 shell 명령어를 호출(/bin/sh -c <command>)하여(/bin/sh -c "명령어" 는 그냥 쉘에서 명령어 치는거랑 동일한 효과이다), 사용자가 지정한 command를 호출한다. 즉 쉘을 한번 통해야 한다.

RUN command
CMD command param1 param2
ENTRYPOINT command param1 param2

<Example>
RUN apt-get install -y nginx
CMD echo "This is a test." 
ENTRYPOINT echo $HOME

 

2. EXEC 방식

shell방식처럼 내부적인 변환이 아니라, 사용자가 명확하게 실행파일을 명시하여 실행할 수 있다. 쉘을 통하지 않는다. 꼭 EXEC 형식이 필요한 경우는 /bin/bash가 없는 컨테이너인 경우, 다른 쉘을 이용하고 싶은 경우 RUN 명령에 쉘의 경로를 지정하는 방식으로 EXEC를 사용할 수 있다.

 

EXEC 형식에서는 실행할 명령을 JSON 배열로 지정한다.  또한 값에 문자열을 입력할 때는 싱글쿼테이션을 쓴다. SHELL 방식보다 EXEC 방식이 권고된다.

RUN ["executable", "param1", "param2"]
CMD [“executable”,”param1”,”param2”]
CMD [“param1”,”param2”] (ENTRYPOINT 인수 형식. 실행되는 명령은 없고 ENTRYPOINT가 실행될 때 파라미터 전달) 
ENTRYPOINT [“executable”, “param1”, “param2”]

<Example>
RUN ["apt-get", "install", "python3"]
RUN ["/bin/bash", "-c", "apt-get install -y nginx"]
RUN ["echo", "hello. this is jacob"]
CMD ["/bin/echo", "Hello world"]
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["/bin/echo", "Hello world"]
ENTRYPOINT ["nginx", "-g", "daemon off;"]

 

# 참고 : EXEC 방식에서 쉘 변수 사용

ENTRYPOINT ["/bin/echo", "Hello, $name"] 는 결과가 Hello, $name 이라고 나온다. EXEC 방식은 쉘을 통과하지 않으므로 쉘 변수를 읽을 수 없기 때문이다. 다음과 같이 쉘을 실행시키고 echo 명령을 수행하도록 하면 제대로 작동한다.

FROM ubuntu:latest 
ENV name John Dow   
["/bin/bash", "-c", "echo Hello, $name"]

 

CMD, ENTRYPOINT, RUN 을 어떻게 쓰는지 간단하게 정리하면 다음과 같다.

 

RUN : 패키지를 설치할 때

CMD : 컨테이너 실행 시점/환경에 따라 명령어를 다양하게 지정해야 하는 경우

ENTRYPINT : 컨테이너 실행 시 항상 수행해야 하는 명령어 지정. 웹서버, DB 등 프로세스가 항상 구동되어야 할 때 등.

 

관련하여 더 상세사항은 다음 링크를 참고한다. docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact

 

Dockerfile reference

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command...

docs.docker.com

 

RUN

 

상세설명

FROM 명령에서 지정한 베이스 이미지에 대해새로운 레이어에서 (즉 이미지가 생성됨) 명령어를 실행한다. 패키지 설치, 미들웨어 설정, 환경 구축등을 위한 명령을 실행하는 데 많이 사용된다. 도커파일 내에 기술된 순서에 따라 순차적으로 실행하므로 도커 이미지 실행시 명령어 한줄씩 실행되는 로그를 확인할 수 있다. RUN 명령의 개수는 제한이 없다. 

 

# 참고 : 주의사항

RUN에 명령어를 입력할 때 서로 관계있는 명령어는 &&를 사용하여 한 레이어에 수행해야 한다. 예를들어, 예시1보다는 예시2대로 수행해야 한다. 각 명령은 레이어별로 이미지가 생기므로, 예시1의 마지막줄 RUN은 apt-get update가 적용되지 않는다.

예시1 
FROM ubuntu:14.04 
RUN apt-get update 
RUN apt-get install -y curl

예시2

FROM ubuntu:14.04 

RUN apt-get update && apt-get install -y curl

# 참고 : 명령 줄바꿈

RUN 명령 실행시 \ 으로 줄바꿈이 가능하다. 가독성을 위해 사용된다.

RUN yum -y install\
           httpd\
           php

 

CMD

 

상세설명

컨테이너 실행 시 default로 실행되는 명령이나 파라미터를 설정한다. docker container run 실행 시 따로 명령어를 명시하지 않으면, CMD에 명시된 명령이 default로 실행된다. 만약 따로 명령어를 명시한다면, CMD로 기술한 내용은 무시되고, 실행 시 명시한 명령어가 작동된다. 또한 ENTRYPOINT 명령의 파라미터를 설정하는 역할도 수행한다. dockerfile에는 하나의 CMD를 기술할 수 있다. 만일 여러개를 쓴다면 마지막 명령만 유효하다. 

 

 

ENTRYPOINT

 

상세설명

CMD는 docker container run 실행 시 따로 명령어가 명시된다면 해당 명령어로 대체되지만, ENTRYPOINT는 컨테이너 실행 시 반드시 실행 명령에 포함된다. 또한 컨테이너 실행 시 따로 명령어를 명시하는 경우, ENTRYPOINT와 명령어가 동일하다면 옵션만 명시한 명령어대로 적용되고, 명령어가 다르다면 ENTRYPOINT의 내용과 컨테이너 실행시 명시한 내용이 한 줄에 다 쓰는 것처럼 되어 에러가 난다. (이해가 안될테니 아래 예시 참고. 내가썼는데도 이해가 안됨..)

 

예를들어, 엔드포인트가 ls -artl 이고 컨테이너 실행시 명령을 ls -al 로하면 ls -al로 실행되지만, 컨테이너 실행시 df -h 로 했다면 실제 명령이 ls -artl df -h 가 되어 에러가 나는 것이다.

 

추가로, container run 시 --entrypoint="실행명령" 옵션을 사용하여 dockerfile에 정의한 ENTRYPOINT를 변경할 수 있다. ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $name"] 가 포함된 도커 파일을 빌드한 후 컨테이너를 생성할 때 다음과 같이 수행했다면, (docker run --entrypoint="cat" test /etc/hosts) "echo Hello, $name"이 부분은 무시되고 cat /etc/hosts 명령이 실행된다.

 

ENTRYPOINT 명령에서 지정한 명령은 반드시 컨테이너에서 실행되는데, 실행 시 명령 인수를 지정하고 싶을 때 CMD와 함께 조합하여 사용되기도 한다. 예를들어, ENTRYPOINT 명령은 실행하고 싶은 명령 자체, 즉 꼭 실행하는 명령을 지정하고, CMD 명령은 ENTRYPOINT로 실행된 명령의 인수를 지정하면 컨테이너 실행시 디폴트 작동을 결정할 수 있다.

 

# 참고 : ENTRYPOINT와 CMD를 함께 사용하기

예시1 : 아무 인수 없이 docker container run 했을 때, "Hello world" 가 출력된다. 만약  docker container run 할 때 인수를 주면, "Hello 인수" 가 출력된다.

FROM ubuntu 
ENTRYPOINT ["/bin/echo", "Hello"] 
CMD ["world"] 

예시2 : 명령 없이 그대로 컨테이너를 실행하면, top -d 10 이 실행된다. docker container run -it sample -d 2 이렇게 실행하면, top -d 2 가 실행된다.

FROM ubuntu:16.04 
ENTRYPOINT ["top"] 
CMD ["-d", "10"] 

 

 

STOPSIGNAL

 

상세설명

docker container stop을 실행하면 도커 데몬은 컨테이너에게 signal을 보내서 중지시킨다. 기본적으로는 SIGTERM을 사용한다. (SIGTERM은 15이고, SIGKILL은 9이다) STOPSIGNAL은 이러한 컨테이너 종료 시그널을 변경할 수 있다. 크게 사용되지는 않는다.

 

구문 및 예시

STOPSIGNAL [시그널번호 또는 이름]
STOPSIGNAL SIGKILL

 

 

EXPOSE

 

상세설명

컨테이너의 공개 포트번호를 지정한다. 이 명령은 도커에게 실행중인 컨테이너가 LISTEN 하고 있는 네트워크를 알려준다. 이 명령 자체가 작성된 포트를 실행하여 리스닝시키는 것이 아니므로, 실제로 포트를 열려면 컨테이너 시작시 -p 또는 -P 옵션을 사용해야 한다. 

 

구문 및 예시

EXPOSE 포트넘버/프로토콜 (프로토콜을 지정하지 않으면 기본값은 tcp이다)
EXPOSE 8080/tcp    // 8080 TCP 포트를 공개

 

 

 

HEALTHCHECK

 

상세설명

컨테이너 안의 프로세스가 정상적으로 작동하고 있는지 체크하는 구문을 추가한다. 이러한 체크는 체크 간격, 체크 타임아웃 시간, 타임아웃 회수 등으로 해당 컨테이너의 상태를 체크할 수 있다. 헬스체크 결과는 docker container inspect 명령으로 상세하게 확인할 수 있고, 간단하게는 docker container ls 명령으로도 확인할 수 있다.

 

구문

HEALTHCHECK [옵션] CMD 실행할명령

 

옵션

옵션명

설명

기본값

--interval=n

헬스체크 간격

30초

--timeout=n

헬스체크 타임아웃

30초

--retries=N

타임아웃 횟수

3

 

예시

5분마다 가동 중인 웹서버의 메인페이지(http://localhost/)를 3초안에 표시할 수 있는지 없는지 확인하기 (도커파일까지 구축하는 상세 예시는  아래 전체 통합 예시 참조)

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1

 

* docker container inspect로 상태 확인

 

처음에는 이렇게 뜬다.

"Health": {
                "Status": "starting",
                "FailingStreak": 0,
                "Log": []
            }

안되면 아래와 같은 로그들이 남는다.

"Health": {
                "Status": "starting",
                "FailingStreak": 1,
                "Log": [
                    {
                        "Start": "2020-02-12T14:30:47.743974318+09:00",
                        "End": "2020-02-12T14:30:47.872516208+09:00",
                        "ExitCode": 1,
                        "Output": "/bin/sh: 1: curl: not found\n"
                    }
                ]

여기 보면 curl 명령이 없는것을  수있다직접 들어가서 설치 다시 기다려보자.

 "Health": {
                "Status": "healthy",
                "FailingStreak": 0,
                "Log": [
                    {
                        "Start": "2020-02-12T14:30:47.743974318+09:00",
                        "End": "2020-02-12T14:30:47.872516208+09:00",
                        "ExitCode": 1,
                        "Output": "/bin/sh: 1: curl: not found\n"
                    },
                    {
                        "Start": "2020-02-12T14:35:47.889708934+09:00",
                        "End": "2020-02-12T14:35:48.028551125+09:00",
                        "ExitCode": 0,
                        "Output": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r100   612  100   612    0     0  45566      0 --:--:-- --:--:-- --:--:-- 43714\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n"
                    }
                ]
            }

붉은색 부분이 홈페이지 코드이다. 이번엔 잘 나왔다. 시간도 칼같이 5분간격이다. 만약 옵션에 정한 일정 횟수가 실패한다면, status 부분에서 unhealthy를 확인할 수 있다.

 

 

ADD

 

상세설명

이미지에다가 호스트에 있는 파일이나 디렉토리, 원격 파일을 도커 이미지 안에 추가한다.(복사함)

 

구문

ADD 호스트파일경로 도커이미지파일경로

ADD ["호스트파일경로" "도커이미지파일경로"]

 

호스트 파일 경로에는 와일드카드, GO언어의 filepath.match 룰과 일치하는 패턴을 사용할 수 있다. 도커 이미지안의 파일은 절대경로로 지정하거나 WORKDIR 명령에서 지정한 디렉토리를 기점으로 상대경로로 지정한다. 호스트의 파일이 TAR 아카이브거나 압축포맷(gzip, bzip2 등)일 때는 디렉토리로 압축을 푼다. 단 원격 URL로 받은것은 압축이 풀리지 않는다.

 

# 참고 : 원격파일을 받는 경우

이미지에 추가할 파일이 원격파일 URL일 경우, 추가한 파일은 퍼미션이 600이 되며, 만일 취득한 원격 파일이 HTTP Last-Modified 헤더를 갖고 있다면 추가된 파일에서 mtime 의 값으로 사용된다. 원격파일URL을 받을 경우, 인증을 지원하지 않으므로 원격파일의 다운로드에 인증이 필요한 경우 RUN 명령에서 wget이나 curl 명령을 사용해야 한다. 또한 이미지 안의 파일 지정이 파일(마지막이 슬래시가 아님)일 때는 URL로부터 파일을 다운로드하여 지정한 파일명을 추가한다. 이미지 안의 파일 지정이 디렉토리(마지막이 슬래시) 일 때는 파일명은 URL로 지정한 것이 된다.

 

예시

호스트상의 host.html을 이미지 안에 /docker_dir/ 에 추가
ADD host.html /docker_dir/

hos로 시작하는 모든 파일 추가
ADD hos* /docker_dir/

[hos]+ 임의의 한 문자 룰에 해당하는 파일 추가
ADD hos?.txt /docker_dir/

원격 파일 추가
ADD http://www.wings.msn.to/index.php /docker_dir/web/

 

 

COPY

 

상세설명

이미지에 호스트상의 파일이나 디렉토리를 복사한다. ADD명령과매우 비슷한데, ADD는 원격파일의 다운로드나 아카이브의 압축해제 등의 기능이 있지만 COPY는 단순히 "복사" 만 한다. 따라서 단순히 이미지 안에 파일을 넣고싶을때는 COPY를 사용한다.

 

예시 및 구문

구문 : COPY 호스트파일경로 도커이미지파일경로

          COPY ["호스트파일경로" "도커이미지파일경로"]

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

이렇게 하면, 호스트의 /test/index.html 파일을 /usr/share/nginx/html/index.html로 넣는다.

 

 

 

VOLUME

 

상세설명

이미지에 볼륨을 할당한다. 지정한 이름의 마운트 포인트를 작성하며, 기본적으로 아래 링크에서 "호스트 디스크를 명시하지 않고 생성" 하는 방식으로 생성된다. 해당 링크 참고할 것.

2020/06/11 - [Docker Basic] - [Docker Basic] 16. Docker 데이터 저장 개념 / 기본 명령어 - volume

 

[Docker Basic] 16. Docker 데이터 저장 개념 / 기본 명령어 - volume

도커의 컨테이너안에 데이터를 저장할 수 있지만 여기에는 몇 가지 문제점이 존재한다. 1. 컨테이너가 삭제되면 데이터도 삭제된다. 2. 다른 프로세스에서 특정 컨테이너에 저장된 데이��

watch-n-learn.tistory.com

예시 및 구문

- 구문 : VOLUME ["/마운트포인트"]

- 예시도 위 링크를 참고하면 된다.

 

 

 

ONBUILD

 

상세설명

일단 해당 도커파일로 빌드를 한번 수행한 , 나온 이미지를 다른 dockerfile에서 참조하여 2번째 빌드를 실행할 발동하는 명령을 이미지 안에 설정하는 명령.  즉, 자신의 도커파일로부터 생성한 이미지를 베이스로 이미지로 한, 다른 도커파일을 빌드할 때 실행하고 싶은 명령을 기술하는 것이다. 예를 들어, 최초 도커파일에서 ONBUILD RUN echo "HELLO"를 기술했다면, 해당 도커파일을 빌드했을때는 아무일도 없지만, 빌드된 이미지를 갖고 누군가가 이미지를 새로 만들면 HELLO가 출력되게 된다. 한마디로 명령의 실행 타이밍을 늦출 수 있는 것. 

 

주요 실무 사용으로는, 예를들어 웹시스템 구축시 os설치 및 환경설정, 웹서버 설치, 각종 플로그인 설치 등의 인프라 환경 구축부분을 베이스 이미지로 만들고, 그 안에 onbuild 명령으로 이미지 안에 개발한 프로그램을 전개하는 명령 ADD나 COPY 등을 지정한다. 이렇게 하면, 최초 이미지는 인프라만 지정되어 있고, 그 다음 개발자가 해당 이미지를 갖고 어플리케이션 구축 부분을 코딩하고 아까 만든 베이스 이미지를 바탕으로 한 이미지를 작성한다. 이 이미지 안에는 프로그래밍이 끝난 업무 어플리케이션이 전개된다. 즉 "인프라 구축" "애플리케이션 전개 업무를 분할하는 것이다.

 

실제로 사용되는 예시로는, 웹서버용 이미지를 최초 빌드하는 사람이, 웹서버를 실행하는것은 그 이미지를 기반으로 다시 이미지를 만드는 사람들을 위해 지연시키고, 다른 환경만 세팅해서 최초 이미지를 만들고, 이후 그걸 가지고 여럿이 웹서버 개발을 수행할 때는 ONBUILD에 있는 웹서버 실행구문이 실행되게 하는 식으로 사용할 수 있다.

 

# 참고 : 개발 흐름의 이해

어플리케이션 개발 현장에서 한명이 모든것을 개발하는 일은 드물다. 여럿이 협업해서 진행하는 경우가 많다. 팀 멤버가 각각 도커파일을 작성하고 그걸로 이미지를 작성한다면 도커를 써도 어플리케이션 실행환경이 제각각이 될 수 있다.

 

그래서, 개발팀 안에 도거파일 작성 담당자를 정하고, 그 담당자가 OS/미들웨어의 설치나 설정, 라이브러리의 검증/도입을 수행하고 베이스가 되는 도커파일을 만든다. 다른 개발자들은 이 베이스 도커파일을 바탕으로 각자 개발한 소스코드를 전개하여 테스트를 하면, 팀 멤버 전원이 동일한 실행 환경에서 개발과 테스트를 할 수 있다.

 

베이스 도커파일을 생성하는 사람은 인프라와 애플리케이션 모두 정통한 엔지니어가 적임이다. 아무튼, 팀개발을 할 때는 개발환경의 베이스 도커파일을 바탕으로 만든 이미지를 공유하여 작업을 수행해야 한다. 또한 도커파일은 GIT 등을 사용하여 팀 내에서 공유하고 버전을 관리할 수 있다.

 

 

예시

ONBUILD ADD website.tar /var/www/html 포함된 도커파일로 이미지 webbase 만든다. 다른 사람이 webbase 가지고 FROM 사용하여 도커파일을 만들고 추가로 여러가지를 넣어서 빌드하면, ONBUILD ADD website.tar /var/www/html 실행되게 된다. (website.tar에는 웹페이지 파일이 들어있다) 예시는 실제로 수행해볼 없다. website.tar 파일이 따로 없기 때문. 예시로만 이해하면 된다

 

1. 베이스 이미지 작성

- 운영체제 설치, nginx 설치, 데몬실행 = 베이스 이미지

- 웹콘텐츠(website.tar)를 /var/www/html에 배치하는 명령 = ONBUILD

- 이것은 인프라 도커 엔지니어가 만든다고 가정한다. 따라서 아래 ONBUILD에 명시된 website.tar는 없다. (웹개발자가 만들겠지)

 

vi Dockerfile

FROM ubuntu:16.04
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install nginx
EXPOSE 80
ONBUILD ADD website.tar /var/www/html/
CMD ["nginx", "-g", "daemon off;"]

 

2. 빌드

docker build -t nginx-onbuild-base .

결과는 다음과 같다.

Sending build context to Docker daemon  2.048kB

Step 1/6 : FROM ubuntu:16.04

 ---> 005d2078bdfa

Step 2/6 : RUN apt-get -y update && apt-get -y upgrade

 ---> Using cache

 ---> 9d7da8a07ab7

(중략)

Step 3/6 : RUN apt-get -y install nginx

 ---> Running in f9fdd875097d

Step 4/6 : EXPOSE 80

 ---> Running in 03723856598b

Removing intermediate container 03723856598b

 ---> f2ac4d707760

Step 5/6 : ONBUILD ADD website.tar /var/www/html/

 ---> Running in 14214a0f1a97

Removing intermediate container 14214a0f1a97

 ---> ce0c1504d710

Step 6/6 : CMD ["nginx", "-g", "daemon off;"]

 ---> Running in 0ec31127997e

Removing intermediate container 0ec31127997e

 ---> 178e09c5e53e

Successfully built 178e09c5e53e

Successfully tagged nginx-onbuild-base:latest

[root@docker-basic test]#

 

docker images에서 해당 이미지를 확인할 수 있다.

onbuild가 잘 들어갔는지 확인하기 위해 docker image insperct 명령을 사용한다.

 

3. 웹 콘텐츠 추가 및 도커파일 생성

이것은 2번째로 빌드를 수행하는 것이다. 작업은 인프라 도커 엔지니어가 아니라, website.tar 라는 파일을 만든 웹개발자가 수행하는 작업이다. website.tar 파일을 준비하여 원하는 디렉토리에 둔다. 그리고 해당 디렉토리에 Dockerfile을 생성한다.

여기서 dockerfile의 내용은 단 한줄이다.

FROM nginx-onbuild-base

 

4. 웹 컨텐츠가 추가된 도커파일 빌드

docker build -t nginx-onbuild-web .

최종적으로 2개의 이미지가 생성된 것을 확인할 수 있다.

 

5. 컨테이너 생성 및 확인

2가지 이미지를 모두 사용하여 컨테이너를 생성한다. nginx-onbuild-base는 nginx 설정까지만 수행한 이미지이고, nginx-onbuild-web은 nginx 설정한 이미지에 onbuild로 웹 데이터까지 적용한 이미지이다.

docker container run -d -p 80:80 nginx-onbuild-base 
docker container run -d -p 88:80 nginx-onbuild-web 

 

* nginx-onbuild-base 접속 (80번 포트 사용)

* nginx-onbuild-web 접속 (88번 포트 사용)

nginx-onbuild-web의 도커파일 내용은 딱 FROM nginx-onbuild-base 만 명시했다. 그런데 nginx-onbuild-base에서 정의한 ONBUILD 옵션으로 인해 이렇게 웹데이터가 전개된 것을 확인할 수 있다.

 

 

 

 

 

 

실습 : dockerfile로 ubuntu에 apache httpd 올리기

1. dockerfile 생성

특정 디렉토리를 하나 만든 후, 거기에 아래 내용으로 도커파일을 생성한다.

vi test-dockerfile

FROM ubuntu:14.04
MAINTAINER "watch-n-learn <test@test.co.kr>"
LABEL "purpose"="practice"
RUN apt-get update && apt-get install apache2 -y
RUN apt-get update && apt-get install curl -y
ADD test.html /var/www/html
WORKDIR /var/www/html
RUN ["/bin/bash", "-c", "echo hello >> test.html"]
EXPOSE 80
CMD apachectl -D FOREGROUND
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://127.0.0.1/ || exit 1

 

2. 테스트용 html 파일 생성

아래 내용을 test.html 이라는 이름으로 도커파일이 있는 경로에 저장한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>"Hi~ Docker~!"</title>
</head>
<body>
    <h1>"Welcome to Docker World~~"</h1>
</body>

3.  빌드

도커파일과 html파일이 있는 경로에서 빌드 명령 수행

docker build -f test-dockerfile -t mybuild_html:0.0 .
[root@docker-basic webdocker]# docker build -f test-dockerfile -t mybuild_html:0.0 .
Sending build context to Docker daemon  3.072kB
Step 1/11 : FROM ubuntu:14.04
 ---> 6e4f1fe62ff1
Step 2/11 : MAINTAINER "watch-n-learn <test@test.co.kr>"
 ---> Running in 7a34515bca7b
Removing intermediate container 7a34515bca7b
 ---> e6101dd14cc4
Step 3/11 : LABEL "purpose"="practice"
 ---> Running in 8a576f8e8414
Removing intermediate container 8a576f8e8414
 ---> 65b7e94284f2
Step 4/11 : RUN apt-get update && apt-get install apache2 -y
 ---> Running in 0116b1fb59eb
Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
Ign http://archive.ubuntu.com trusty InRelease
Get:2 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:3 http://security.ubuntu.com trusty-security/main amd64 Packages [1032 kB]
Get:4 https://esm.ubuntu.com trusty-infra-security InRelease
Get:5 http://archive.ubuntu.com trusty-backports InRelease [65.9 kB]
Get:6 https://esm.ubuntu.com trusty-infra-updates InRelease
Hit http://archive.ubuntu.com trusty Release.gpg
Get:7 http://security.ubuntu.com trusty-security/restricted amd64 Packages [18.1 kB]
Get:8 https://esm.ubuntu.com trusty-infra-security/main amd64 Packages
Get:9 http://security.ubuntu.com trusty-security/universe amd64 Packages [378 kB]
Get:10 http://archive.ubuntu.com trusty-updates/main amd64 Packages [1460 kB]
Get:11 http://security.ubuntu.com trusty-security/multiverse amd64 Packages [4730 B]
Get:12 https://esm.ubuntu.com trusty-infra-updates/main amd64 Packages
Get:13 http://archive.ubuntu.com trusty-updates/restricted amd64 Packages [21.4 kB]
Get:14 http://archive.ubuntu.com trusty-updates/universe amd64 Packages [671 kB]
Get:15 http://archive.ubuntu.com trusty-updates/multiverse amd64 Packages [16.1 kB]
Get:16 http://archive.ubuntu.com trusty-backports/main amd64 Packages [14.7 kB]
Get:17 http://archive.ubuntu.com trusty-backports/restricted amd64 Packages [40 B]
Get:18 http://archive.ubuntu.com trusty-backports/universe amd64 Packages [52.5 kB]
Get:19 http://archive.ubuntu.com trusty-backports/multiverse amd64 Packages [1392 B]
Hit http://archive.ubuntu.com trusty Release
Get:20 http://archive.ubuntu.com trusty/main amd64 Packages [1743 kB]
Get:21 http://archive.ubuntu.com trusty/restricted amd64 Packages [16.0 kB]
Get:22 http://archive.ubuntu.com trusty/universe amd64 Packages [7589 kB]
Get:23 http://archive.ubuntu.com trusty/multiverse amd64 Packages [169 kB]
Fetched 13.7 MB in 8s (1620 kB/s)
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  apache2-bin apache2-data libapr1 libaprutil1 libaprutil1-dbd-sqlite3
  libaprutil1-ldap libxml2 sgml-base ssl-cert xml-core
Suggested packages:
  www-browser apache2-doc apache2-suexec-pristine apache2-suexec-custom ufw
  apache2-utils sgml-base-doc openssl-blacklist debhelper
The following NEW packages will be installed:
  apache2 apache2-bin apache2-data libapr1 libaprutil1 libaprutil1-dbd-sqlite3
  libaprutil1-ldap libxml2 sgml-base ssl-cert xml-core
0 upgraded, 11 newly installed, 0 to remove and 1 not upgraded.
Need to get 1898 kB of archives.
After this operation, 7526 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty-updates/main libxml2 amd64 2.9.1+dfsg1-3ubuntu4.13 [573 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ trusty/main sgml-base all 1.26+nmu4ubuntu1 [12.5 kB]
Get:3 http://archive.ubuntu.com/ubuntu/ trusty/main libapr1 amd64 1.5.0-1 [85.1 kB]
Get:4 http://archive.ubuntu.com/ubuntu/ trusty/main libaprutil1 amd64 1.5.3-1 [76.4 kB]
Get:5 http://archive.ubuntu.com/ubuntu/ trusty/main xml-core all 0.13+nmu2 [23.3 kB]
Get:6 http://archive.ubuntu.com/ubuntu/ trusty/main libaprutil1-dbd-sqlite3 amd64 1.5.3-1 [10.5 kB]
Get:7 http://archive.ubuntu.com/ubuntu/ trusty/main libaprutil1-ldap amd64 1.5.3-1 [8634 B]
Get:8 http://archive.ubuntu.com/ubuntu/ trusty-updates/main apache2-bin amd64 2.4.7-1ubuntu4.22 [845 kB]
Get:9 http://archive.ubuntu.com/ubuntu/ trusty-updates/main apache2-data all 2.4.7-1ubuntu4.22 [160 kB]
Get:10 http://archive.ubuntu.com/ubuntu/ trusty-updates/main apache2 amd64 2.4.7-1ubuntu4.22 [87.4 kB]
Get:11 http://archive.ubuntu.com/ubuntu/ trusty/main ssl-cert all 1.0.33 [16.6 kB]
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
dpkg-preconfigure: unable to re-open stdin:
Fetched 1898 kB in 5s (342 kB/s)
Selecting previously unselected package libxml2:amd64.
(Reading database ... 12097 files and directories currently installed.)
Preparing to unpack .../libxml2_2.9.1+dfsg1-3ubuntu4.13_amd64.deb ...
Unpacking libxml2:amd64 (2.9.1+dfsg1-3ubuntu4.13) ...
Selecting previously unselected package sgml-base.
Preparing to unpack .../sgml-base_1.26+nmu4ubuntu1_all.deb ...
Unpacking sgml-base (1.26+nmu4ubuntu1) ...
Selecting previously unselected package libapr1:amd64.
Preparing to unpack .../libapr1_1.5.0-1_amd64.deb ...
Unpacking libapr1:amd64 (1.5.0-1) ...
Selecting previously unselected package libaprutil1:amd64.
Preparing to unpack .../libaprutil1_1.5.3-1_amd64.deb ...
Unpacking libaprutil1:amd64 (1.5.3-1) ...
Selecting previously unselected package xml-core.
Preparing to unpack .../xml-core_0.13+nmu2_all.deb ...
Unpacking xml-core (0.13+nmu2) ...
Selecting previously unselected package libaprutil1-dbd-sqlite3:amd64.
Preparing to unpack .../libaprutil1-dbd-sqlite3_1.5.3-1_amd64.deb ...
Unpacking libaprutil1-dbd-sqlite3:amd64 (1.5.3-1) ...
Selecting previously unselected package libaprutil1-ldap:amd64.
Preparing to unpack .../libaprutil1-ldap_1.5.3-1_amd64.deb ...
Unpacking libaprutil1-ldap:amd64 (1.5.3-1) ...
Selecting previously unselected package apache2-bin.
Preparing to unpack .../apache2-bin_2.4.7-1ubuntu4.22_amd64.deb ...
Unpacking apache2-bin (2.4.7-1ubuntu4.22) ...
Selecting previously unselected package apache2-data.
Preparing to unpack .../apache2-data_2.4.7-1ubuntu4.22_all.deb ...
Unpacking apache2-data (2.4.7-1ubuntu4.22) ...
Selecting previously unselected package apache2.
Preparing to unpack .../apache2_2.4.7-1ubuntu4.22_amd64.deb ...
Unpacking apache2 (2.4.7-1ubuntu4.22) ...
Selecting previously unselected package ssl-cert.
Preparing to unpack .../ssl-cert_1.0.33_all.deb ...
Unpacking ssl-cert (1.0.33) ...
Processing triggers for ureadahead (0.100.0-16) ...
Setting up libxml2:amd64 (2.9.1+dfsg1-3ubuntu4.13) ...
Setting up sgml-base (1.26+nmu4ubuntu1) ...
Setting up libapr1:amd64 (1.5.0-1) ...
Setting up libaprutil1:amd64 (1.5.3-1) ...
Setting up xml-core (0.13+nmu2) ...
Setting up libaprutil1-dbd-sqlite3:amd64 (1.5.3-1) ...
Setting up libaprutil1-ldap:amd64 (1.5.3-1) ...
Setting up apache2-bin (2.4.7-1ubuntu4.22) ...
Setting up apache2-data (2.4.7-1ubuntu4.22) ...
Setting up apache2 (2.4.7-1ubuntu4.22) ...
Enabling module mpm_event.
Enabling module authz_core.
Enabling module authz_host.
Enabling module authn_core.
Enabling module auth_basic.
Enabling module access_compat.
Enabling module authn_file.
Enabling module authz_user.
Enabling module alias.
Enabling module dir.
Enabling module autoindex.
Enabling module env.
Enabling module mime.
Enabling module negotiation.
Enabling module setenvif.
Enabling module filter.
Enabling module deflate.
Enabling module status.
Enabling conf charset.
Enabling conf localized-error-pages.
Enabling conf other-vhosts-access-log.
Enabling conf security.
Enabling conf serve-cgi-bin.
Enabling site 000-default.
invoke-rc.d: policy-rc.d denied execution of start.
Setting up ssl-cert (1.0.33) ...
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
Processing triggers for libc-bin (2.19-0ubuntu6.15) ...
Processing triggers for sgml-base (1.26+nmu4ubuntu1) ...
Processing triggers for ureadahead (0.100.0-16) ...
Removing intermediate container 0116b1fb59eb
 ---> f923af6eea28
Step 5/11 : RUN apt-get update && apt-get install curl -y
 ---> Running in e5c191a1b974
Hit http://security.ubuntu.com trusty-security InRelease
Ign http://archive.ubuntu.com trusty InRelease
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://archive.ubuntu.com trusty-updates InRelease
Hit http://security.ubuntu.com trusty-security/restricted amd64 Packages
Hit http://archive.ubuntu.com trusty-backports InRelease
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://archive.ubuntu.com trusty Release.gpg
Hit https://esm.ubuntu.com trusty-infra-security InRelease
Hit http://security.ubuntu.com trusty-security/multiverse amd64 Packages
Hit http://archive.ubuntu.com trusty-updates/main amd64 Packages
Hit https://esm.ubuntu.com trusty-infra-updates InRelease
Hit http://archive.ubuntu.com trusty-updates/restricted amd64 Packages
Hit https://esm.ubuntu.com trusty-infra-security/main amd64 Packages
Hit http://archive.ubuntu.com trusty-updates/universe amd64 Packages
Hit https://esm.ubuntu.com trusty-infra-updates/main amd64 Packages
Hit http://archive.ubuntu.com trusty-updates/multiverse amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/main amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/restricted amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/universe amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/multiverse amd64 Packages
Hit http://archive.ubuntu.com trusty Release
Hit http://archive.ubuntu.com trusty/main amd64 Packages
Hit http://archive.ubuntu.com trusty/restricted amd64 Packages
Hit http://archive.ubuntu.com trusty/universe amd64 Packages
Hit http://archive.ubuntu.com trusty/multiverse amd64 Packages
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  libcurl3
The following NEW packages will be installed:
  curl libcurl3
0 upgraded, 2 newly installed, 0 to remove and 1 not upgraded.
Need to get 297 kB of archives.
After this operation, 878 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty-updates/main libcurl3 amd64 7.35.0-1ubuntu2.20 [173 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ trusty-updates/main curl amd64 7.35.0-1ubuntu2.20 [123 kB]
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
dpkg-preconfigure: unable to re-open stdin:
Fetched 297 kB in 2s (145 kB/s)
Selecting previously unselected package libcurl3:amd64.
(Reading database ... 12815 files and directories currently installed.)
Preparing to unpack .../libcurl3_7.35.0-1ubuntu2.20_amd64.deb ...
Unpacking libcurl3:amd64 (7.35.0-1ubuntu2.20) ...
Selecting previously unselected package curl.
Preparing to unpack .../curl_7.35.0-1ubuntu2.20_amd64.deb ...
Unpacking curl (7.35.0-1ubuntu2.20) ...
Setting up libcurl3:amd64 (7.35.0-1ubuntu2.20) ...
Setting up curl (7.35.0-1ubuntu2.20) ...
Processing triggers for libc-bin (2.19-0ubuntu6.15) ...
Removing intermediate container e5c191a1b974
 ---> d09fd3f9522b
Step 6/11 : ADD test.html /var/www/html
 ---> 8e09e7013f53
Step 7/11 : WORKDIR /var/www/html
 ---> Running in 5e93cc3c2b9f
Removing intermediate container 5e93cc3c2b9f
 ---> 4433d87dbe4e
Step 8/11 : RUN ["/bin/bash", "-c", "echo hello >> test.html"]
 ---> Running in e3d65669f651
Removing intermediate container e3d65669f651
 ---> f790c93359b9
Step 9/11 : EXPOSE 80
 ---> Running in 6d18dea6527b
Removing intermediate container 6d18dea6527b
 ---> d9a15c3ee4d6
Step 10/11 : CMD apachectl -D FOREGROUND
 ---> Running in 771d8762f994
Removing intermediate container 771d8762f994
 ---> 8d1ada8b3b91
Step 11/11 : HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://127.0.0.1/ || exit 1
 ---> Running in 3e22b7da57ee
Removing intermediate container 3e22b7da57ee
 ---> fcaea0bcbcbe
Successfully built fcaea0bcbcbe
Successfully tagged mybuild_html:0.0
[root@docker-basic webdocker]#

4. 컨테이너 생성

docker run -d -P --name myapacheserver mybuild_html:0.0

5. 테스트

호스트에서 아래와 같이 접속하면 처음 html파일에 정의한 내용 + dockerfile에 적용한 "hello" 값이 추가된 것을 확인할 수 있다.

 

 

+ Recent posts