Dockerfile 에서 RUN 을 통해 실행되는 모든 명령은

도커 컨테이너 및 이미지의 작업 디렉토리에서 실행되는데

여기서 말하는 작업 디렉토리는 컨테이너 파일 시스템의 루트 폴더

예를 들어 아래와 같은 Dockerfile 이 존재한다면

FROM node
RUN npm install

node 컨테이너의 / 위치에서 npm install 명령을 실행하게 됨

 

특정 위치에서 RUN 명령어를 실행하기 위해서는 WORKDIR 를 사용

이를 통해 작업 디렉토리를 바꿀 수 있음

예를 들어

FROM node
WORKDIR /app
COPY . ./
RUN npm install

 

COPY . ./ 는 "현재 Dockefile 이 위치해있는 곳의 모든 파일,디렉터리들" 을

"컨테이너 내 작업 디렉터리" 에 복사하는 의미의 명령어

 

RUN : Dockefile 을 통해 이미지를 생성할 때 수행 할 작업을 지시

ENTRYPOINT, CMD : Dockerfile 을 통해 이미지 생성할 때 수행되지 않고,

이미지가 만들어진 후 그 이미지로 컨테이너가 처음 시작될 때 컨테이너 내부에서 수행 할 작업을 지시

 

CMD 명령어는 특이하게 명령을 배열에 담아줘야 함

예를 들어 echo hello 라는 명령을 넣고싶다면

CMD ["echo", "hello"]

 

EXPOSE 명령어를 통해, 도커 컨테이너 실행시 -p 옵션을 통해 연결 가능한 (컨테이너 내) 포트를 문서화 할 수 있음

EXPOSE 80, 443

 

Dockerfile 을 만든 후 이미지로 만드는 방법은 다음과 같음

docker build -t [이미지 이름] [Dockerfile 위치] 
예) docker build -t eyeballs:latest .

 

ENTRYPOINT 와 CMD 를 자세히 알아봄.

 

위에 기술했듯이 ENTRYPOINT, CMD 둘 다

컨테이너가 처음 시작될 때(docker run 명령어로 실행시) 컨테이너 내부에서 수행 할 작업을 지시하는 명령어임

 

CMD 하나만 존재하는 Dockerfile 을 확인해보자

 

 

< Dockerfile >
FROM ubuntu:latest
CMD ["echo", "hello eyeballs!"]

위와 같은 Dockerfile 로 이미지를 만들어서

docker run 명령어로 실행하면

CMD 에 넣은 명령어가 실행되는 것을 볼 수 있음

 

이렇게 CMD 로 실행된 프로세스(echo)는 pid 1 번으로 실행되어 컨테이너의 생명 주기에 관여함

1번 프로세스가 살아있어야 컨테이너가 계속 살아있게 되고

만약 1번 프로세스가 죽게되면 컨테이너도 같이 stop 됨

 

실제로 docker stop 명령어가 컨테이너를 stop 하는 방법은,

pid 1번 프로세스에 SIG TERM 시그널(프로세스 종료 신호)을 보내는 거라고 함

위 Dockerfile 로 만든 컨테이너도 run 명령어를 날리자마자 echo 출력하고 곧바로 stop 되었다.

 

반대로,ENTRYPOINT 가 사용된 Dockerfile 은 어떻게 동작하는지 보자

 

< Dockerfile >
FROM ubuntu:latest
ENTRYPOINT ["echo", "hello eyeballs!"]

ENTRYPOINT 역시 docker run 명령어로 실행을 통해 곧바로 실행되는 것을 볼 수 있음

CMD 와 마찬가지로 ENTRYPOINT로 실행된 프로세스(echo)는 pid 1 번으로 실행되어 컨테이너의 생명 주기에 관여하게 됨

(docker run 명령어 이후 echo 출력하고 곧바로 stop 됨)

 

그럼 CMD 와 ENTRYPOINT 차이는?

ENTRYPOINT 명령어가 먼저 오고, CMD 가 ENTRYPOINT 명령의 파라미터를 넣어주는 역할을 한다고 함

docker run 을 통해 실행되는, 생명주기에 관여하게되는 pid 1번 프로세스는 ENTRYPOINT 의 명령어로 실행되고

CMD 명령어는 ENTRYPOINT 명령어에 이어 붙이는 파라미터, arguments 들을 넣어주는 역할

 

예를 들어보자

< Dockerfile >
FROM ubuntu:latest
ENTRYPOINT  ["top"]
CMD ["-b", "-c"]

docker run 을 실행하면

top -b -c 명령어가 실행됨

 

docker inspect 를 통해 확인해보면 다음과 같이 CMD, ENTRYPOINT 명령어가 들어있는 것을 볼 수 있음

sudo docker inspect myimage 
......
            "Cmd": [
                "-b",
                "-c"
            ],
.....
            "Entrypoint": [
                "top"
            ],
.....

 

추가) docker run 실행시 --entrypoint 옵션으로 pid 1번 프로세스 실행 변경이 가능함

 

참고 : https://asung123456.tistory.com/51


 

docker run 명령어는 attatched 모드로 컨테이너를 실행하고

docker start 혹은 restart 명령어는 detached 모드로 컨테이너를 실행

attatched 모드는 포그라운드에서 동작하면서 컨테이너 내의 출력 결과가 계속 출력되는 모드

detached 모드는 백그라우드에서 동작하며 사용자와 인터랙션이 없는 모드

 

docker run 명령어로 detached 모드를 사용하려면 -d 옵션을 붙이면 됨

docker run -d eyeballs

반대로 docker start 명령어로 attached 모드를 사용하려면 -a 옵션을 붙이면 됨

docker start -a eyeballs

detached 모드로 실행중인 컨테이너에 attached 모드로 붙으려면 docker attach 명령어 사용하면 됨

docker attach eyeballs

(docker run 실행했을 때 처럼 attached 모드로 붙을 수 있음)

 


 

docker container 의 로그를 지속적으로 보고 싶다면 -f 옵션 추가

docker logs -f eyeballs

 


 

도커가 run 되면 echo program(사용자 입력을 받고 그대로 출력하는 ) 파이썬 스크립트를 실행하는 이미지(echo-python)가 있다고 하자

이 이미지로 run 하면

docker run --name echo-python echo-python

사용자 입력을 받아야하는데 받지 못해서 에러가 남

 

사용자 입력을 받을 수 있도록 하려면 -it 옵션을 추가해야 함

docker run -it --name echo-python echo-python

 

  -i, --interactive                    Keep STDIN open even if not attached
  -t, --tty                            Allocate a pseudo-TTY (teletypewriter)

 

내가 흔히 사용하는, docker run -d image bash 와 docker exec -it container bash 두 단계를 거치지 않아도

docker 이미지 내 CMD 명령어를 통해 처음 실행하는 프로그램을 곧바로 사용 가능

 

이런 컨테이너가 stop 된 이후에 start 하게 되면

기본적으로 detached 모드가 되니 상호작용이 불가능

따라서 start 할 때 -a 옵션을 줘서 attached 모드로 실행하고, -i 옵션을 줘서 interactive 활성화 해야 함

docker stop echo-python
docker start -ai echo-python

 


 

현재 컨테이너로 사용되지 않는, 모든 이미지를 지우는 명령어

docker image --prune

  prune       Remove unused images


현재 stop 상태인 모든 container 를 지우는 명령어

docker container --prune

  prune       Remove all stopped containers

 


 

다운받은 Docker Image 의 상세 정보, 예를 들어

어떤 port 를 오픈할 수 있는지, 어떤 이미지를 base 로 삼고 있는지,

어떤 CMD 명령어를 실행하는지, entrypoint가 무엇인지 등을 알고 싶다면 inspect 명령어 사용

docker inspect [image]

 


 

Docker 컨테이너가 삭제되어도, 동일한 이미지로 다시 컨테이너를 띄웠을 때

데이터를 유지할 수 있는 방법은 volume 옵션을 사용하는 것

 

내가 알고있는 volume 은 host 의 path 와 연결하는 것인데,

docker 자체에서 알아서 path 를 정해서 host 어딘가에 데이터를 저장하는 방법을 제공할 수도 있음

 

예를 들어

docker run -v /home/eye/docker/:/app

위와 같은 volume 옵션을 준다면,

내 local host 머신의 /home/eye/docker 내부의 모든 파일, 디렉터리들이

docker container 내부의 /app 위치에 동기화 될 것이며

내가 host 머신에서 해당 파일, 디렉터리들에 직접 접근이 가능함

 

하지만 아래처럼 local path 가 아닌, volume 이름만 준다면

docker run -v myvolume:/app

docker 가 내 local host 머신 어딘가에 myvolume 에 해당하는 위치를 지정해두고

docker container 내부의 /app 에 저장되는 파일, 디렉터리들을 myvolume 위치에 동기화 시킬 것임

나는 host 머신에서 myvolume 위치에 접근이 불가능함

 

만약 아래처럼 이름조차 주지 않는다고 하더라도

docker run -v /app

 

docker 가 내 local host 머신 어딘가에 익명의 이름으로 path 를 지정해두고

docker container 내부의 /app 에 저장되는 파일, 디렉터리들을 동기화시킴

그래서 container 가 죽어도 파일/디렉터리들을 유지할 수 있음

 

위와 같이 내가 접근이 불가능한, 이름으로 volume 을 주고 그 목록을 보고자 한다면 아래 명령어 사용

docker volume ls

 

docker volume 을 삭제하려면 아래 명령어 사용

당연한 이야기지만 아래 명령어로 volume 을 삭제하면,

volume path 에 저장한 데이터들도 모두 삭제됨

docker volume rm [VOLUME NAME]

 

 

특정 이름/익명의 이름으로 추가된 volume 은 container 가 관리하게 되지만

내 local 머신의 path 와 container 내 path 를 잇는 volume 은 container 가 관리하지 않아

docker volume ls 명령어에 나타나지 않음

 


 

volume 을 사용할 때 주의할 점은,

Dockerfile RUN 명령어를 통해 실행되고 저장된 어떤 데이터를

Docker volume 으로 덮어써버릴 수 있다는 것임

 

예를 들어

< Dockerfile >
...
WORKDIR /app
RUN npm install
...

위와 같은 Dockerfile 은 npm install 명령어를 통해 /app 에 설치 데이터를 저장함 (정확히는 /app/node_modules 에 저장됨)

아래 명령어로 Docker run 을 하게되면

docker run -v /home/eye/myapps:/app ...

Container 내부 /app 위치에 저장되어있던 npm 설치 데이터들이 모두 덮어쓰여지게 됨

 

이런 경우를 위해, 덮어쓰지 말아야 하는 디테일한 path(/app/node_modules) 는 익명 volume 으로 처리하는 옵션을 추가

docker run -v /home/eye/myapps:/app -v /app/node_modules ...

위와 같이 지정해주면 docker container 내부의 /app/node_modules 가 첫번째 볼륨에 의해 덮어쓰여지지 않게 됨

 

 

 


 

docker volume 을 지정하면,

local 머신과 container 내부가 서로  공유하는 path 가 생기고

이 path 에 존재하는 파일들에 서로 read 와 write 가 가능함

local 머신에서도 파일을 읽고 쓸 수 있고

container 내부에서도 파일을 읽고 쓸 수 있다는 말임

 

그런데 나는 container 내부에서 파일을 읽기만 하고 쓰지는 못하도록 하고 싶음

즉, local 머신에서만 쓸 수 있도록 하고 싶음

그럴 땐 volume 옵션에 read only 성격을 추가하면 됨

 

docker run -v "/home/eye/myapps:/app:ro"

위와 같이 ro 옵션을 주면, container 내부에서는 /app 및 /app 이하 모든 path 에서 write 가 불가능하게 됨

 

volume 옵션이 두 가지 있다면,

좀 더 깊이있는 path 를 갖는 volume 옵션의 우선순위가 더 높음

예를 들어 다음과 같은 옵션이 있을 때

docker run -v "/home/eye/myapps:/app:ro" -v "/home/eye/myapps/modules:/app/modules"

 

/app:ro 보다 /app/modules 가 좀 더 깊이있기 때문에

/app/modules 의 우선순위가 높음

즉, container 에서는 /app path 에 있는 파일에 write 가 불가능하지만

/app/modules path 에 있는 파일에는 write 가 가능하다는 말임


 

docker container 가 관리하는 volume 의 자세한 내용을 보고 싶다면

(언제 생성되었고 이름은 무엇이고 local 머신 어디에 저장되어있는지... 등등) 

docker volume inspect [VOLUME 이름]

위 명령어로 mountpoint 가 나타나며

이는 docker container 의 가상머신 상의 경로라서

local 머신에서 찾을 수 없음

 


 

현재 사용되지 않는 모든 local volume 을 지우는 명령어

docker volume --prune

prune       Remove unused local volumes

 


 

Dockerfile 에서 COPY 명령어로 데이터를 container 내부로 복사할 때

복사하지 말아야 할 리스트인 .dockerignore 를 만들어서 복사하지 않도록 만들 수 있음

< .dockerignore >
Dockerfile
README.md
.git

 

 


 

docker container 내에 환경변수를 넣어줄 수 있음

여기서 말하는 환경변수는 linux 에서 사용하는 환경변수 그거임

 

예를 들어 아래와 같은 Dockerfile 을 docker run 해보면

< Dockerfile >
FROM ubuntu:latest
ENV MYNAME eyeballs

 

 

이렇게 container 내부 환경변수를 미리 지정할 수 있음

환경변수를 지정하는 방법은 총 3가지

 

1. Dockerfile 에 ENV 명령어로 지정

  위 예제대로 넣어줄 수 있음

 

2. docker run 명령어에 --env 혹은 -e 옵션으로 지정

  위 예제처럼 MYNAME 을 넣으려면 아래처럼 옵션 추가

docker run --env MYNAME=eyeballs ...
혹은
docker run -e MYNAME=eyeballs ...

 

3. docker run 명령어에 --env-file 로 환경변수가 저장된 파일를 지정

< .myenvfile >
docke run --env-file ./.myenvfile ...

 

만약 1번 방법과 2번 방법이 동시에 사용된다면?

2번 방법으로 들어간 환경변수가 더 높은 우선순위를 갖게 됨

만약 2번 방법으로 환경변수를 넣어줘야 하는데 넣지 못했다면,

1번 방법으로 들어간 환경변수가 대신 사용됨

즉, 1번 방법으로 들어간 환경변수는 default 값이 되는 것임

 

강의에서 "ENV MYNAME eyeballs" 처럼 설정했다가, 환경 변수가 default 로 적용되지 않아서

"ENV MYNAME=eyeballs" 처럼 등호를 넣어 다시 설정하니 되는 것을 보았음

1번 방법이 default 로써 사용되려면 등호를 넣어서 설정해야 하나 봄

 


 

ARG 명령어는 Dockerfile 내부에서만 사용 가능한 상수값

아래처럼 ARG 에 설정한 값은 ENV 등에서 사용이 가능함

FROM node
ARG DEFAULT_PORT=80
ENV PORT $DEFAULT_PORT

위와 같이 Dockerfile 내부에서 지정할 수 있고,

docker run 명령어에 옵션을 주어 지정도 가능

docker run --build-arg DEFAULT_PORT=8000 ...

ARG 가 지정되고나면 값은 고정되어버림

위와 같이 옵션을 통해 값을 지정하면, 이미지는 동일하나 ARG 값이 다른 여러 container 를 만들 수 있음

 

참고로 ARG 값은 CMD 명령어에서 사용 불가능함

ARG 명령어와 ARG 로 설정한 값을 사용하는 명령어는

Dockerfile 의 마지막에 넣어두는 게 좋음

ARG 값이 바뀌면 이미지를 빌드할 때 (업데이트가 된) ARG 명령어 이후의 모든 명령어를 다시 빌드하게 되기 때문

빌드를 다시 하지 않아도 되는 명령어들까지 빌드하게 되므로 리소스 및 시간이 낭비되기 때문

 


 

Docker container 내부에서, WWW (외부 인터넷) 에 존재하는 웹사이트 접근이나 API 요청하기 위해

따로 설정해야 하는 것은 없음. 그냥 가능함


Docker container 내부에서, container 가 실행되는 host 머신에 접근하려면

(localhost 가 아닌) host.docker.internal 로 접근하면 됨.

예를 들어 host 머신 위에 mongodb 가 실행중이고

Docker container 내부에서 host 머신의 mongodb 에 접근하려면 아래와 같은 방식으로 접근 (수도코드)

mongodb.connect('mongodb://host.docker.internal:27017/info')

 

Docker container 내부에서 host 머신으로 ping 을 날릴때도 이와 비슷함

ping host.docker.internal

 


 

Docker container 간 네트워크 연결이 가능함

예를 들어 AA container 와 BB container 가 존재하고

AA 가 BB 에게 ping 을 날리는 상황을 가정하자

 

AA container 내부에서 ping 을 실행하려면

BB container 의 ip 주소를 알아야 함

BB container 의 ip 주소는 docker container inspect BB 명령어 결과에서 볼 수 있음

결과중에 NetworkSettings.IPAddress 부분을 보면 BB container 의 ip 주소가 나타남

이렇게 얻은 ip 주소를 이용하여 AA container 는 ping 을 날릴 수 있음

 

위와 같이 ip 주소를 직접 얻어 사용하면

ip 주소를 사용하는 코드에서 하드코딩을 하게 되고

또 ip 주소가 바뀌면 하드코딩 또한 바뀌고 이미지 빌드도 다시 해야하고 실행도 다시 해야하기 때문에 아주 불편함

따라서, container 간 통신을 위해 ip 주소를 사용하는 대신,

하나의 network 로 container 들을 묶는 기술을 사용해야 함 (바로 아래서 설명)

 


 

하나의 docker network 를 만들고

그 network 에 container 들을 연결시켜두면

container 들끼리 자기들의 이름으로 통신이 가능하게 됨

 

아래 명령어를 통해 network 를 하나 만듦

network 의 이름은 간단하게 mynetwork 라고 하자

docker network create mynetwork

 

만든 network 는 아래 명령어로 확인이 가능

docker network ls

 

각 container 를 실행할 때 내가 만든 mynetwork 에 연결하여 실행

예를 들어 AA container, BB container 를 실행할 때 아래처럼 --network 옵션을 추가하여 연결

docker run --name AA --network mynetwork ...
docker run --name BB --network mynetwork ...

 

그럼 AA container 내부에서 BB container 의 이름(BB)으로 ping 을 날릴 수 있게 됨

ping BB

 

AA container 에서 Mongodb container 에 연결하려면 docker run 실행시 -p 옵션으로 포트를 열어야할까?

아님. -p 옵션은 host 머신에서 달라붙기 위해 오픈하는 포트 번호를 넣는 옵션일 뿐

container 간 연결에서는 -p 옵션을 넣지 않아도 container 간 포트가 열려있음


 

 

어떤 프로그램, 툴 의 공식 이미지는 '사용 방법'이 다 정해져서 나옴

예를 들어 mongodb 공식 이미지는 "mongo"이며

https://hub.docker.com/_/mongo 에서 확인할 수 있음

 

공식 이미지는, 만들어질 때 내부에서 프로그램을 어떻게 운용할지 정해둠

즉, pid 1번 프로세스는 무엇이며

로그는 어느 dir 에 저장하며

기본 port 는 무엇이며

환경변수에 따라 어떤 설정값이 추가되는지 등

그리고 그 '사용 방법'은 대개 docker image 에 안내되어있음

예를 들어 mongodb 공식 이미지의 경우

mongodb 프로세스가 데이터를 저장하는 dir 위치를 /data/db  로 설정해두었고

 

mongodb 의 root 사용자를 정의하려면

MONGO_INITDB_ROOT_USERNAME,  MONGO_INITDB_ROOT_PASSWORD 라는

두 가지 환경 변수를 사용해야 한다는 내용이 이미 image 내에 포함되어 있음

 

따라서 우리는 공식 이미지를 사용할 때, '사용 방법'에 따라 사용하면 됨

(우리가 '사용 방법'을 직접 추리할 수는 없는 노릇)

 

 


 

 

Docker Container 는 리소스(memory, cpu, io bps) 제한이 없음

호스트의 리소스를 제한 없이 사용함

따라서 Container 에서 리소스를 많이 사용하는 만큼

호스트 머신에서 동작하는 다른 프로세스에 영향이 끼침

심지어 Container 의, Container 에 의해 Out Of Memory 도 발생할 수 있음

 

Container 의 Memory 및 CPU 제한을 확인하려면 docker inspect 명령을 사용

아래 스샷은 리소스 제한이 없는 Container 의 Memory 와 Cpu 상태를 보여줌 (리소스 제한이 없어서 0으로 나타남)

 

 


 

 

Container 에 memory 제한을 주려면 다음과 같은 옵션을 사용

docker run -d -it --memory=1g ubuntu
docker run -d -it --memory=500m ubuntu

 

 

"Memory" 에 숫자가 나타나며, 숫자가 1gb 크기를 보여주는 것을 확인 가능

"MemorySwap" 을 따로 지정해주지 않으면 "Memory" 의 2배 크기만큼 잡힌다고 함

"MemorySwap" 을 따로 지정하려면 아래 옵션 추가

docker run -d -it --memory=1g --memory-swap=1g ubuntu
docker run -d -it --memory=1g --memory-swap=3.1g ubuntu

 

이 외에 --memory-swappiness, --kernel-memory, --oom-kill-disable 등의 옵션이 존재함

 


 

 

Container 에 cpu 제한을 주려면 다음과 같은 옵션을 사용

--cpus : Container 내부에서 제한하고 싶은 만큼의 cpu 점유율을 퍼센트 수치로 지정
host cpu 가 4개 존재하고, --cpus 에 0.5 를 넣으면
Container 에 2개(50%) 만큼의 cpu 성능 제한이 설정됨
docker run -d -it --cpus=0.5 ubuntu

 

--cpuset-cpus : host cpu 중 특정 cpu 만 사용하도록 지정
host 머신에서 htop 으로 볼 수 있는 cpu 의 index 를 넣으면
Container 는 넣은 index 에 해당하는 cpu 만 사용
index 는 0부터 시작 
docker run -d -it --cpuset-cpus=0 ubuntu #0번 index 에 해당하는 cpu 사용
docker run -d -it --cpuset-cpus=0,1 ubuntu #0번, 1번 index 에 해당하는 cpu 사용
docker run -d -it --cpuset-cpus=0-2 ubuntu #0번, 1번, 2번 index 에 해당하는 cpu 사용

 

 

이 외에 --cpu-period, --cpu-quata, --cpu-shares  등의 옵션이 존재함

 


 

리소스 제한 설정을 하지 않은 상태로 이미 올라간 Container 에

리소스를 제한하고 싶다면 update 명령어 사용

docker run -d -it --name eyeballs ubuntu
docker update --memory=1g eyeballs

 

참고로 부하 테스트를 진행할때 사용할 수 있는 stress 라는 명령어가 존재함

cpu 부하, memory 부하를 임의로 일으킬 수 있음

 

 

 

 


 

 

Docker 에 대한 내용이 너무 길어져서

Docker Compose 에 대한 내용은 다른 포스트로 올림

 

https://eyeballs.tistory.com/642

 

[Docker] Udmey Docker & Kubernetes : 실전 가이드 필기 - Docker Compose

Docker Compose 는 다수의 "docker build" 와 다수의 "docker run" 명령어를 대체하는 도구(orchestration 명령 모음) Docker compose 툴로 다수의 container 들을 한 번에 손쉽게 관리할 수 있음 하나의 Docker compose 를 사

eyeballs.tistory.com

 

 

 

 

+ Recent posts