✔️ Dockerfile로 이미지 빌드
- 현재 디렉토리에 도커 파일이 있어야 한다.
- 현재 디렉토리를 기준으로 하위 디렉토리의 내용까지 모두 이미지로 만들기 때문에 root에서 작업하지 않는다.
- 도커 파일 작성시 Instruction은 관습적으로 대문자로 작성한다.
✔️ Instruction
✔️ FROM
Base Image
FROM <image>
FROM <image>[:<tag>]
FROM <image>[@<digest>]
✔️ RUN
RUN - execute command, 이미지를 빌드하는 중에 실행할 명령어를 지정한다.
Shell Form
RUN yum install httpd
→ /bin/sh -c yum install httpd
Exec Form
RUN ["yum", "install", "httpd"]
→ exec yum install httpd
exec는 명령 X
FROM에서 Base Image를 지정하고 ubuntu 이미지를 가지고 apache를 만들었다면
ubuntu에 exec로 접속하여 apt 명령을 사용해 apache를 설치했던 내용이 RUN이 되는 것이다.
exec form에서의 exec()는 c언어 함수이자 shell의 기능인데 exec ls 하면 ls가 실행이 된다.
exec()는 기본함수로서 뒤에 있는 명령어를 실행해주는 함수이다.
그냥 ls를 하면 shell에서 실행한 것이고 exec ls 는 shell을 사용하지 않고 실행한 것이다.
Docker에서 /bin/sh -c와 exec는 각각 다음과 같은 역할을 한다.
- /bin/sh -c: Docker 컨테이너 내부에서 실행할 명령어를 지정하는 옵션이다.
이 옵션을 사용하면 컨테이너 내부에서 셸을 실행하고, 해당 셸에서 지정한 명령어를 실행한다.
이 옵션은 새로운 프로세스를 실행하므로, 컨테이너 내부에서 실행 중인 다른 프로세스와는 별개로 실행된다. - exec: Docker 컨테이너 내부에서 실행 중인 프로세스에 명령어를 전달하는 옵션이다.
이 옵션을 사용하면, 이미 실행 중인 프로세스에 명령어를 전달하여 추가적인 작업을 수행할 수 있다.
이 옵션은 이미 실행 중인 프로세스와 연결되므로, 해당 프로세스가 종료되면 명령어도 함께 종료된다.
즉, /bin/sh -c는 새로운 프로세스를 실행하고 그 안에서 명령어를 실행하는 것이고, exec는 이미 실행 중인 프로세스에 명령어를 전달하는 것이다.
따라서, 각각의 사용 목적에 따라 적절한 옵션을 선택하여 사용해야 한다.
/bin/sh -c | exec | |
목적 | 컨테이너 내부에서 실행할 명령어 지정 | 컨테이너 내부에서 실행 중인 프로세스에 명령어 전달 |
동작 방식 | shell 실행 후 shell에서 지정한 명령어 실행 | 이미 실행 중인 프로세스에 명령어 전달 |
종료 방식 | 다른 프로세스와는 별개로 실행 | 프로세스와 연결되어 프로세스 종료시 명령어도 종료 |
apache2ctl -DFOREGROUND 를 실행하고자 할 때 다음과 같이 사용할 수 있다.
RUN apache2ctl -DFOREGROUND # Shell Form -> /bin/sh -c apache2ctl -DFOREGROUND
RUN {"apache2ctl", "-DFOREGROUNT"} # Exec Form -> exec apache2ctl -DFOREGROUND
이 두 명령어는 동일하다.
✔️ CMD
기본 실행 어플리케이션
Shell Form
CMD /usr/sbin/httpd -DFOREGROUND
→ /bin/sh -c yum install httpd
Exec Form(선호)
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
✔️ CMD에서의 Shell과 Exec Form의 차이
결론적으로 CMD 또는 ENTRYPOINT 에서는 Shell form을 사용하면 안된다.
작동에는 크게 문제가 없을 수 있으나 stop 시에는 항상 비정상 종료가 된다.
shell은 자신의 하위 프로세스에게 시그널을 전달하지 않는다.
직접 확인해보자
vagrant@docker ~ docker image inspect httpd
"Cmd": [
"httpd-foreground"
],
위는 httpd 이미지의 실행 방식이다.
vagrant@docker ~ docker image inspect myweb:ubuntu
"Cmd": [
"/bin/sh",
"-c",
"/usr/sbin/apache2ctl -DFOREGROUND"
],
ubuntu Base Image로 직접 커스텀한 이미지의 실행 방식이다.
vagrant@docker ~ docker stop 9f
vagrant@docker ~ docker inspect 9f
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2022-05-10T05:28:07.184285978Z",
"FinishedAt": "2022-05-10T05:28:45.29422324Z"
},
프로세스를 정상적으로 종료시키면 위와 같이 리턴 코드(ExitCode)가 0이 된다.
우리가 사용하던 터미널을 닫게되면 15번 Signal이 전송된다.
kill -l 명령을 통해 확인해보면 15번 Signal은 SIGTERM : 정상 종료 시그널 이다.
자주 사용하던 systemctl stop 명령과 docker stop 명령도 15번 시그널을 전송시키는 것이다.
vagrant@docker ~ docker run -d myweb:ubuntu
50c69526ddf7f72cb41f73106895de9ffaf552d633041715e7bc47ea26d63525
vagrant@docker ~ docker stop 50
50
vagrant@docker ~ docker start 50
50
vagrant@docker ~ time docker stop 50
50
docker stop 50 0.01s user 0.02s system 0% cpu 10.180 total
vagrant@docker ~ docker inspect 50
[
{
"Id": "50c69526ddf7f72cb41f73106895de9ffaf552d633041715e7bc47ea26d63525",
"Created": "2022-05-10T05:29:38.71648141Z",
"Path": "/bin/sh",
"Args": [
"-c",
"/usr/sbin/apache2ctl -DFOREGROUND"
],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 137, #
"Error": "",
"StartedAt": "2022-05-10T05:29:56.719743633Z",
"FinishedAt": "2022-05-10T05:30:15.794646317Z"
},
vagrant@docker ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
50c69526ddf7 myweb:ubuntu "/bin/sh -c '/usr/sb…" 4 minutes ago Exited (137) 3 minutes ago quirky_wescoff
9fcac0681612 httpd "httpd-foreground" 5 minutes ago Exited (0) 4 minutes ago modest_aryabhata
커스텀한 myweb:ubuntu 이미지를 컨테이너로 띄우고 15번 Signal을 전송시켜 컨테이너를 중지해본다.
ExitCode가 0이 아닌 137-128 = 9 임을 알 수 있다.
Ctrl + c 가 아닌 직접 stop 명령을 사용하여 종료 시그널을 보냈는데 왜 ExitCode가 0이 아닐까 ?
httpd 이미지에 15번 시그널을 전송시키면 httpd가 받아 정상 종료시킨다.
하지만 /bin/sh -c ~ 는 shell이 받는다. 이때는 shell이 apache를 실행 중이다.
shell에게 15번 시그널을 전송시키면 이를 무시하고 apache에게 종료 신호를 전달하지 않는다.
shell은 받은 15번 시그널을 무시하고 도커는 10초 동안 응답이 오지 않으면 9번 시그널을 전송시킨다.
그냥 프로세스를 강제로 죽이게된다. 9번 시그널은 어떤 프로세스도 무시할 수 없다.
따라서 ExitCode가 9가 되는 것이다.
다시 정리해보자면,
CMD를 사용할 때는 종료 시그널을 shell이 대신받아 이미지에 전달하지 않기 때문에, 도커가 kill signal을 전송시켜 프로세스가 강제로 죽는 것이다.
✔️ LABEL
LABEL <key>=<value> <key>=<value> <key>=<value> ...
설명을 붙이기 위해 사용, 검색을 위해 사용한다.
✔️ EXPOSE
외부에 노출할 포트 및 프로토콜
EXPOSE <port> [<port>/<protocol>...]
기본은 TCP며 UDP 프로토콜을 사용하고 싶다면 반드시 명시해야한다.
✔️ ENV
쉘 환경 변수
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
✔️ ADD/COPY
도커 호스트 -> 컨테이너 파일 복사
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
COPY <SRC> <DEST>
ADD는 SRC로 URL 지정 가능
✔️ ENTRYPOINT
Docker 컨테이너가 시작될 때 실행할 기본 명령어를 지정하는 지시어이다.
ENTRYPOINT를 사용하면 Docker 이미지를 실행할 때 기본적으로 실행될 실행 파일이나 스크립트를 지정할 수 있다.
예를 들어, Dockerfile에서 'ENTRYPOINT'를 다음과 같이 지정하면,
ENTRYPOINT ["python", "app.py"]
이미지를 실행할 때 'app.py' 파일이 실행된다.
이때 'python' 명령어가 컨테이너 내부의 기본 실행 파일로 설정된다.
이러한 설정으로 인해, 'docker run' 명령어를 사용할 때 실행 파일을 지정하지 않아도 기본적으로 'app.py' 파일이 실행된다.
또한, ENTRYPOINT와 함께 CMD 지시어를 함께 사용하여 실행 파일에 전달할 인수를 지정할 수 있다.
이렇게 하면, Docker 이미지를 실행할 때 CMD에 지정한 인수가 ENTRYPOINT 명령에 전달된다.
예를 들어, Dockerfile에서 ENTRYPOINT와 CMD를 다음과 같이 지정하면
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8080"]
이미지를 실행할 때 --port 8080 인수가 app.py 파일에 전달된다.
따라서, docker run 명령어를 사용할 때 --port 옵션을 직접 지정하지 않아도 기본적으로 8080 포트가 사용된다.
CMD의 명령으로 사용
X | X | 허용X |
X | abc | abc |
xyz | X | xyz |
xyz | abc | xyz abc |
실행할 것이 ls -l이라고 하면
entrypoint : ls → 명령어에 사용
cmd : -l → argument 또는 옵션에 사용
만약에 나중에 ls -F 를 실행하고 싶다면 cmd만 교체하면 된다.
✔️ USER
USER <user>[:<group>]
USER <UID>[:<GID>]
컨테이너 내부는 기본적으로 루트 권한을 사용하고 있어 보안상으로 좋지 않다.
✔️ VOLUME
자동으로 마운트할 볼륨 마운트 포인트 지정
VOLUME /myvol
✔️ WORKDIR
작업 디렉토리RUN
, CMD
, ENTRYPOINT
, ADD
, COPY
에 영향을 미침
WORKDIR /usr/local
✔️ ONBUILD
ONBUILD <INSTRUCTION>
빌드를 할 때마다 실행할 명령어를 지정할 때 사용한다.
이미지 내에 적용시키면 항상 INSTRUCTION이 실행된다.
✔️ STOPSIGNAL
default는 SIGTERM이다.
다른 시그널로도 중지시키고 싶으면 그 시그널로도 할 수 있다.
✔️ HEALTHCHECK
어플리케이션에 문제가 발생했을 때 healthcheck하기 위한 매커니즘으로 restart 정책과 연관이 있다.
healthcheck를 위한 명령어를 따로 지정할 수 있다.
ex) apache의 경우 curl 명령으로 응답이 돌아오는지를 체크할 수 있고 만약 응답이 돌아오지 않으면 재시작을 하도록 할 수 있다.
✔️ 사용 예시
Dockerfile
FROM ubuntu:focal
RUN apt update; apt install -y apache2
COPY index.html /var/www/html/index.html
EXPOSE 80/tcp
CMD ["/usr/sbin/apache2ctl", "-DFOREGROUND"]
# CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] # 이렇게 사용 가능
yes or no의 stdin을 기다려 주지 않으므로 -y
로 모든 대화형태를 차단해야 한다.
✔️ Layer(레이어)
RUN
, ADD
, COPY
명령어 한 줄 마다 레이어를 만든다.
RUN apt update
RUN apt install -y apache2
RUN은 실행할 때마다 layer가 생성된다.
RUN apt update; apt install -y apache2
명령어를 나열하면 하나의 레이어만을 사용할 수 있다.
✔️ Linux Timezone 설정
우분투 이미지에는 Timezone이 설정되어 있지 않다.
centos 이미지는 자동으로 default timezone인 UTC time이 설정된다.
apache를 설치할 때 Timezone 정보를 필요로 하기 때문에 Timezone 관련 질문을 대화형 방식으로 물어보게 된다.
우회하는 방법
ENV DEBIAN_FRONTEND=noninteractive
환경 변수를 추가한다.
Timezone을 설정하는 방법
ls -l /etc/localtime
/etc/localtime 이라는 타임은 심볼릭 링크로 걸려있다.
vagrant@docker ~ ls -l /etc/localtime
lrwxrwxrwx 1 root root 27 Apr 29 21:48 /etc/localtime -> /usr/share/zoneinfo/Etc/UTC
vagrant@docker ~ cd /usr/share/zoneinfo
vagrant@docker /usr/share/zoneinfo ls
vagrant@docker /usr/share/zoneinfo cd Asia
vagrant@docker /usr/share/zoneinfo/Asia ls
vagrant@docker /usr/share/zoneinfo/Asia ls Seoul
Seoul
vagrant@docker /usr/share/zoneinfo/Asia
Seoul을 localtime에 심볼릭 링크를 걸어줘야 한다.
vagrant@docker /usr/share/zoneinfo/Asia sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
vagrant@docker /usr/share/zoneinfo/Asia ls -l /etc/localtime
lrwxrwxrwx 1 root root 30 May 10 15:54 /etc/localtime -> /usr/share/zoneinfo/Asia/Seoul
vagrant@docker /usr/share/zoneinfo/Asia sudo timedatectl
Local time: Tue 2022-05-10 15:54:37 KST
Universal time: Tue 2022-05-10 06:54:37 UTC
RTC time: Tue 2022-05-10 06:54:36
Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no
vagrant@docker /usr/share/zoneinfo/Asia
/etc/localtime 이 가리키고 있는 파일이 해당 리눅스 시스템의 timezone이 되는 것이다.
'DevOps > Docker' 카테고리의 다른 글
[Docker] ubuntu 이미지로 httpd 서비스 이미지 만들기 ( + Timezone 세팅) (0) | 2022.05.11 |
---|---|
[Docker] Shell(쉘) 변수 (0) | 2022.05.11 |
[Docker] 이미지 레이어(Layer) (0) | 2022.05.11 |
[Docker] docker commit 명령을 이용하여 이미지 빌드하기 (0) | 2022.05.10 |
[Docker] Netfilter의 역할 (0) | 2022.05.10 |
영차영차 성장 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!