Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

nomad-programmer

[DevOps/Docker] 명령 및 데몬 실행 (RUN, CMD, ENTRYPOINT, ONBUILD) 본문

DevOps/Docker

[DevOps/Docker] 명령 및 데몬 실행 (RUN, CMD, ENTRYPOINT, ONBUILD)

scii 2020. 12. 1. 00:28

Docker 이미지를 만들려면 필요한 미들웨어를 설치하고 사용자 계정이나 디렉토리를 작성하는 등의 명령을 실행할 필요가 있다. 또한 이미지로부터 컨테이너를 생성했을 때, 서버 프로세스 등을 데몬으로서 작동시킬 필요도 있다.

명령 실행 (RUN 명령)

컨테이너에는 FROM 명령에서 지정한 베이스 이미지에 대해 '애플리케이션/미들웨어를 설치 및 설정한다' 라는 의미를 갖는다. '환경 구축을 위한 명령을 실행한다' 등과 같은 명령을 실행할 때는 RUN 명령을 사용한다.

Dockerfile을 작성할 때는 이 RUN 명령을 가장 많이 사용한다. RUN 명령으로 지정한 명령은 Docker 이미지를 생성할 때 실행된다.

RUN [실행하고 싶은 명령]

RUN 명령에는 다음 두 가지 기술 방법이 있다.

1. Shell 형식으로 기술

명령의 지정을 쉘에서 실행하는 형식으로 기술하는 방법이다. 예를 들어 apt 명령을 사용하여 Nginx를 설치할 때는 다음과 같이 기술한다.

RUN apt-get install -y nginx

이것이 Docker 컨테이너 안에서 /bin/sh -c를 사용하여 명령을 실행했을 때와 똑같이 작동한다. Docker 컨테이너에서 실행할 기본 쉘을 변경하고 싶을 때는 SHELL 명령을 사용한다.

2. Exec 형식으로 기술

Shell 형식으로 명령을 기술하면 /bin/sh 에서 실행되지만, Exec 형식으로 기술하면 쉘을 경유하지 않고 직접 실행한다. 따라서 명령 인수에 $HOME 과 같은 환경변수를 지정할 수 없다. Exec 형식에서는 실행하고 싶은 명령을 json 배열로 지정한다.

또한 다른 쉘을 이용하고 싶을 때는 RUN 명령에 쉘의 경로를 지정한 후 실행하고 싶은 명령을 지정한다. 예를 들어 /bin/bash 에서 apt 명령을 사용하여 Nginx를 설치하려면 다음과 같이 기술한다.

RUN ["/bin/bash", "-c", "apt-get install -y nginx"]

문자열을 인수로 지정할 때는 '(홑따옴표)를 사용한다. RUN 명령은 다음과 같이 Dockerfile에 여러 개 기술할 수 있다.

# 베이스 이미지 설정
FROM ubuntu:latest

# RUN 명령의 실행
RUN echo "shell 형식"
RUN ["echo", "exec 형식"]
RUN ["/bin/bash", "-c", "echo 'exec형식에서 bash쉘 사용'"]

RUN 명령은 기술된 내용에 따라 순차적으로 실행된다. docker build 명령으로 이 Dockerfile로부터 이미지를 생성하면 다음과 같은 로그가 출력된다.

Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM ubuntu:latest
latest: Pulling from library/ubuntu
da7391352a9b: Pull complete 
14428a6d4bcd: Pull complete 
2c2d948710f2: Pull complete 
Digest: sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
Status: Downloaded newer image for ubuntu:latest
 ---> f643c72bc252
Step 2/4 : RUN echo "shell 형식"
 ---> Running in a7cae6cea36b
shell 형식
Removing intermediate container a7cae6cea36b
 ---> 043ca0c4b018
Step 3/4 : RUN ["echo", "exec 형식"]
 ---> Running in 912e83948173
exec 형식
Removing intermediate container 912e83948173
 ---> 61fbd4108a67
Step 4/4 : RUN ["/bin/bash", "-c", "echo", "exec bash 형식"]
 ---> Running in 5f8b816de09d

Removing intermediate container 5f8b816de09d
 ---> 92631928ae6c
Successfully built 92631928ae6c
Successfully tagged test/test:latest

로그를 확인하면 Step이라고 출력되는 부분에서 Dockerfile에 기술한 명령이 한 줄씩 실행된다는 것을 알 수 있다. Dockerfile은 이 명령 한 줄마다 이미지를 생성한다.
이미지를 생성할 때 어떤 명령이 실행되는지를 확인하기 위해 docker history 명령을 실행해보자.

$ docker history test/test

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
92631928ae6c        19 minutes ago      /bin/bash -c echo exec bash 형식                  0B                  
61fbd4108a67        19 minutes ago      echo exec 형식                                    0B                  
043ca0c4b018        19 minutes ago      /bin/sh -c echo "shell 형식"                      0B                  
...

실행 결과를 확인하면 shell 형식으로 기술한 RUN 명령은 /bin/sh, Exec 형식으로 기술한 RUN 명령은 쉘을 통하지 않고 실행되는 것을 알 수 있다. 또한 shell을 명시적으로 지정하고 싶을 때는 Exec 형식을 사용하면 /bin/bash를 사용하여 명령이 실행되는 것을 알 수 있다.

/bin/sh를 경유하여 명령을 실행하고 싶을 때는 Shell 형식으로 기술하고, 그 외의 경우는 Exec 형식으로 기술하는 것이 좋다.

이미지의 레이어에 대하여

Dockerfile을 빌드하면 기술된 명령마다 내부 이미지가 하나씩 작성된다. 그래서 Dockerfile의 명령을 줄이는 방법이 몇 가지 고안되어 있다.

다음의 예에서는 4개의 레이어가 생성된다.

// RUN 명령을 여러 개 지정하는 경우
RUN yum -y install httpd
RUN yum -y install php
RUN yum -y install php-mbstring
RUN yum -y install php-pear

한편 다음의 예에서는 하나의 레이어만 생성된다.

// RUN 명령을 한 줄로 지정하는 경우
RUN yum -y install httpd php php-mbstring php-pear

이와 같이 한 줄에 쓸 수 있는 RUN 명령은 한 줄에 쓰는 것이 좋다. 또한 RUN 명령은 \ (이스케이프 문자)로 줄 바꿈을 할 수 있다. 줄 바꿈을하면 가독성이 향상되므로 명령을 쉽게 확인할 수 있다.

// RUN 명령의 줄 바꿈
RUN yum -y intall \
    httpd \
    php \
    php-mbstring \
    php-pear
    
// 또는 다음과 같이 할 수 있다.

RUN yum update \
  && yum -y install httpd \
  && pip install black \
  && echo "Done setup!"

데몬 실행 (CMD 명령)

RUN 명령은 이미지를 작성하기 위해 실행하는 명령을 기술하지만, 이미지를 바탕으로 생성된 컨테이너 안에서 명령을 실행하려면 CMD 명령을 사용한다.

이미지를 바탕으로 생성된 컨테이너 안에서 명령을 실행하려면 CMD 명령을 사용한다.

Dockerfile에는 하나의 CMD 명령을 기술할 수 있다. 만일 여러 개를 지정하면 마지막 명령만 유효하다.

예를 들어 웹 서버를 가동시키기 위해 Nginx를 설치하는 명령은 RUN 명령을, 설치한 Nginx를 데몬으로서 컨테이너 안에서 상시 작동시키기 위해서는 CMD 명령을 사용한다.

CMD [실행하고 싶은 명령]

CMD 명령은 다음 세 가지 기술 방법이 있다.

1. Exec 형식의 기술

RUN 명령의 구문과 똑같다. Nginx를 Foreground에서 실행할 때는 다음과 같이 기술한다. Exec 형식은 쉘을 호출하지 않는다. 인수는 json 배열로 지정한다.

// Exec 형식의 CMD 명령
CMD ["nginx", "-g", "daemon off;"]

2. Shell 형식으로 기술

RUN 명령의 구문과 똑같다. Nginx를 Foreground에서 실행할 때는 다음과 같이 기술한다. 쉘을 통해 실행하고 싶을 때 사용한다.

// Shell 형식의 CMD 명령
CMD nginx -g 'daemon off;'

3. ENTRYPOINT 명령의 파라미터로 기술

ENTRYPOINT 명령의 인수로 CMD 명령을 사용할 수 있다. 다음의 Dockerfile 예에서는 Ubuntu를 베이스 이미지로 하여 apt 명령으로 Nginx를 설치하고, Nginx를 데몬 실행한다. 또한 웹 서버로서 액세스하기 위해 EXPOSE 명령을 사용하여 80번 포트를 지정한다.

# 베이스 이미지 설정
FROM ubuntu:latest

# Nginx 설치
RUN apt-get -y update \ 
      && apt-get -y upgrade \
      && apt-get -y install nginx

# 포트 지정
EXPOSE 80

# 서버 실행
CMD ["nginx", "-g", "daemon off;"]

다음의 명령으로 Dockerfile로부터 cmd-sample이라는 이름의 Docker 이미지를 생성한다.

$ docker build -t cmd-sample .

생성한 이미지에서 컨테이너를 시작한다. CMD 명령을 사용하여 Nginx 데몬을 시작하도록 설정되어 있으므로 다음의 명령을 사용하여 80번 포트를 호스트 OS에 전송하면 컨테이너가 웹 서버로서 작동하고 있다는 것을 알 수 있다.

// 컨테이너 실행
$ docker container run -d -p 80:80 cmd-sample

데몬 실행 (ENTRYPOINT 명령)

ENTRYPOINT 명령에서 지정한 명령은 Dockerfile에서 빌드한 이미지로부터 Docker 컨테이너를 시작하기 때문에 docker container run 명령을 실행했을 때 실행된다.

ENTRYPOINT [실행하고 싶은 명령]

ENTRYPOINT 명령은 두 가지 기술 방법이 있다.

1. Exec 형식의 기술

RUN 명령의 구문과 똑같다. Nginx를 Foreground에서 실행할 때는 다음과 같이 기술한다.

// Exec 형식의 ENTRYPOINT 명령
ENTRYPOINT ["nginx", "-g", "daemon off;"]

2. Shell 형식의 기술

RUN 명령의 구문과 똑같다. Nginx를 Foreground에서 실행할 때는 다음과 같이 기술한다.

// Shell 형식의 ENTRYPOINT 명령
ENTRYPOINT nginx -g 'daemon off;'

ENTRYPOINT 명령과 CMD 명령의 차이는 docker container run 명령 실행 시의 동작에 있다.

CMD 명령의 경우, 컨테이너 시작 시에 실행하고 싶은 명령을 정의해도 docker container run 명령 실행 시에 인수로 새로운 명령을 지정한 경우 이것을 우선 실행한다.

ENTRYPOINT 명령에서 지정한 명령은 반드시 컨테이너에서 실행되는데, 실행 시에 명령 인수를 지정하고 싶을 때는 CMD 명령과 조합하여 사용한다. ENTRYPOINT 명령으로는 실행하고 싶은 명령 자체를 지정하고 CMD 명령으로는 그 명령의 인수를 지정하면, 컨테이너를 실행했을 때의 기본 작동을 결정할 수 있다.

다음의 예에서는 ENTRYPOINT 명령으로 top 명령을 실행한다. 그리고 CMD 명령으로 갱신 간격인 -d 옵션을 10초로 지정한다. 그러면 이 이미지로부터 작성된 컨테이너는 top 명령이 반드시 실행되는데, 이 때 CMD 명령에서 지정한 옵션을 사용하여 실행 시의 인수를 임의로 docker container run 명령 실행 시로 지정할 수 있다.

// ENTRYPOINT 명령과 CMD 명령을 조합한 예
# top 실행
ENTRYPOINT ["top"]
CMD ["-d", "10"]

이 Dockerfile을 바탕으로 sample이라는 이미지를 작성하고 docker container run 명령을 실행한 예는 다음과 같다.

// CMD 명령에서 지정한 10초 간격으로 갱신하는 경우
$ docker container run -it --rm sample

// 2초 간격으로 갱신하는 경우
$ docker container run -it --rm sample -d 2

CMD 명령은 docker container run 명령 실행 시에 덮어 쓸 수 있다는 구조 때문에 가능한 것이다.


빌드 완료 후에 실행되는 명령 (ONBUILD 명령)

ONBUILD 명령은 그 다음 빌드에서 실행할 명령을 이미지 안에 설정하기 위한 명령이다. 예를 들어 Dockerfile에 ONBUILD 명령을 사용하여 어떤 명령을 실행하도록 설정하여 빌드하고 이미지를 작성한다. 그리고 그 이미지를 다른 Dockerfile에서 베이스 이미지로 설정하여 빌드했을 때 ONBUILD 명령에서 지정한 명령을 실행시킬 수 있다.

ONBUILD [실행하고 싶은 명령]

ONBUILD 명령은 자신의 Dockerfile로부터 생성한 이미지를 베이스 이미지로 한 다른 Dockerfile을 빌드할 때 실행하고 싶은 명령을 기술한다.

구체적으로는 이 ONBUILD 명령을 사용하여 웹 애플리케이션의 실행 환경을 구축한다. 예를 들면 웹 시스템을 구축할 때 OS 설치 및 환경 설정이나 웹 서버 설치 및 각종 플러그인 설치 등과 같은 인프라 환경 구축과 관련된 부분을 베이스 이미지로 작성한다. 
이 때 ONBUILD 명령으로 이미지 안에 개발한 프로그램을 전개하는 명령 (ADD나 COPY 명령 등)을 지정한다. 

애플리케이션 개발자는 애플리케이션의 구축 부분을 코딩하고 이미 작성이 끝난 베이스 이미지를 바탕으로 한 이미지를 작성한다. 이 이미지 안에는 프로그래밍이 끝난 업무 애플리케이션이 전개된다.

Step 1. 베이스 이미지 작성

먼저 Ubuntu를 베이스 이미지로 하여 웹 서버의 실행 환경을 작성한다.

RUN 명령으로 Nginx를 설치하고, 데몬을 실행하도록 CMD 명령을 지정한다. 그리고 웹 콘텐츠를 /var/www/html 아래에 배치하는 명령을 ONBUILD 명령으로 지정한다.

// Dockerfile.base

# 베이스 이미지 설정
FROM ubuntu:latest

# Nginx 설치
RUN apt-get -y update && apt-get -y upgrade && apt-get install -y nginx

# 포트 지정
EXPOSE 80

# 웹 컨텐츠 배치
ONBUILD ADD website.tar /var/www/html/

# Nginx 실행
CMD ["nginx", "-g", "daemon off;"]

이 Dockerfile을 Dockerfile.base라는 이름으로 정하고, 다음의 명령으로 이미지를 빌드한다. 

docker build 명령을 사용하여 파일명을 지정할 때는 -f 옵션 다음에 파일명을 지정한다.

해당 디렉토리에 Dockerfile 이라는 이름의 파일이 없다면 다음과 같은 에러 메시지를 볼 수 있다.

unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /home/scii/workspace/docker/test/practice/Dockerfile: no such file or directory

그래서 Dockerfile 이라는 이름이 아니라면 -f 옵션을 붙여서 해당 파일이 Dockerfile이라고 지정해야 한다.

$ docker build -t web-base -f Dockerfile.base .

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

위의 명령으로 이미지를 만들었다면 container 시작해 내부를 살펴보도록 하자. 

$ docker container run -it --rm web-base ls /var/www/html/

index.nginx-debian.html

ls 명령의 결과를 보다시피 아직까지는 website.tar 파일이 존재하지 않는다. 따라서 ONBUILD 명령은 이렇게 만들어진 이미지를 가지고 다른 Dockerfile에서 Base Image로 사용할 때, 그 때 비로소 "ADD website.tar /var/www/html/" 명령이 실행된다. 이 명령으로 Base Image의 /var/www/html/ 디렉토리안에 website.tar 파일이 추가된다.

Step 2. 웹 컨텐츠 개발

웹 서버를 작동시키기 위한 인프라 실행 환경이 완성되었으므로 그 다음은 웹 컨텐츠를 구축한다. 샘플로서 HTML 파일이나 CSS 파일, 그림 파일 등을 website라는 이름으로 하여 tar 명령을 사용하여 하나로 모은다. 컨텐츠의 내용은 디렉토리 구성을 포함해도 상관 없다.

Step 3. 웹 서버용 이미지 작성

그 다음은 웹 서버 실행용 이미지를 작성한다. 이 이미지에는 Step 1. 에서 작성한 베이스 이미지를 FROM 명령으로 지정한다.

$ Dcoker 이미지 취득
FROM web-base

이 Dockerfile을 빌드하면 ONBUILD 명령에서 지정한 웹 컨텐츠를 이미지에 추가하는 처리가 실행되므로 같은 공간안에 website.tar 파일을 배치해 놓는다.

이 Dockerfile을 다음의 명령으로 빌드한다. 이로써 photoview-image 라는 이름의 이미지가 생성된다.

// 웹 서버용 이미지 빌드
$ docker build -t photoview-image .

Step 4. 웹 서버용 컨테이너 시작

Step 1. 에서 웹 서버를 작동시키기 위한 실행 환경과 Step 2. 에서 웹 컨텐츠의 전개가 되었으므로 생성된 이미지를 바탕으로 컨테이너를 시작한다.

// 웹 서버용 컨테이너 시작
$ docker container run -d -p 80:80 photoview-image

브라우저에서 localhost:80 주소로 액세스하면 로컬에 있는 애플리케이션이 전개되는 것을 알 수 있다.

이와 같이 인프라 구축과 관련된 이미지 작성과 애플리케이션 전개와 관련된 이미지 생성을 나눌 수 있다. 또한 이미지에 ONBUILD 명령이 설정되어 있는지 아닌지는 docker image inspect 명령으로 확인할 수 있다. 예를 들어 작성한 web-base라는 이미지에 ONBUILD 명령이 설정되어 있는지를 확인하려면 다음의 명령을 실행한다.

// 이미지의 상세 정보 확인
$ docker image inspect --format="{{ .Config.OnBuild }}" web-base

[ADD website.tar /var/www/html/]

ONBUILD 명령을 사용한 팀 개발의 예

애플리케이션 개발 현장에서는 한 사람이 모든 것을 개발하는 일은 드물며, 여러 사람이 팀으로 협력하면서 진행하는 경우가 많다. 팀 멤버가 각각 Dockerfile을 작성하고 이미지를 작성한다면 Docker를 도입했는데도 애플리케이션 실행 환경이 제각각이 될 우려가 있다.

그래서 개발팀 안에 실행 환경 Dockerfile을 작성할 담당자를 정한 후 그 담당자가 OS/미들웨어의 설치나 설정, 라이브러리의 검증이나 도입 등을 하고 베이스가 되는 Dockerfile을 만든다.

웹 컨텐츠 개발자는 이 베이스가 되는 Dockerfile을 바탕으로 각자 개발한 소스코드를 전개하여 테스트를 하면, 팀 멤버 전원이 똑같은 실행 환경에서 개발과 테스트를 진행할 수 있다.

베이스가 되는 Dockerfile을 작성하는 담당자는 인프라 기술과 애플리케이션 개발 기술 모두 정통한 엔지니어가 적임자이다. 팀 개발을 할 때의 개발 폴리시가 정해져 있는 경우는 그 룰 안에 베이스가 되는 Dockerfile을 바탕으로 하는 이미지로부터 생성한 컨테이너에서 테스트한다는 것을 정해두는 것도 좋다.

또한 Dockerfile은 Git을 사용하여 팀 내에서 공유하고 버전을 관리할 수 있으므로 설령 Dockerfile을 작성하는 담당자가 정해저 있어도 그 사람에게 의존해 버릴 가능성이 낮아진다.

개발팀 안에서 실행 환경 구축 흐름

Comments