Docker 기본 개념
https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html
Docker란? 기본적으로 SDP(Software Development Platform) 가상화 기술의 일종입니다.
철저히 packaged되고 가상적으로 container화된 환경에서 앱들을 개발 및 배포하기 용이하다 -> machine 등의 플랫폼 구별 없이 app들이 동일하게 구동된다. 쉽게 말해 VM의 일종인데 통상적인 Full virtualization보다 빠르고 가벼워 부담 없이 샌드박스 환경을 만들 수가 있습니다. 즉, 유동성이 확보되며, 가상 머신에 비해 많은 수의 container를 동시에 실행 가능하다는 말이죠. 내부적으로는 가상 머신과 달리 호스트와 리소스를 직접적으로 공유한다는 특징이 있습니다. 사실 호스트의 Linux 커널을 그대로 재활용합니다.
구체적으로는, 가상 머신은 Hypervisor라는 번역기를 통해 구동하나 docker는 중간자 없이 system kernel과 natively communicate 한다. 공간 소모도 적습니다. layered file system 사용하기 때문입니다.
container - docker에 의해 만들어진 각 sandbox 환경들을 뜻합니다(잘 알고 있는 가상머신 sandbox의 일종이므로 서로 독립적이며 호스트와도 독립된 하나의 공간, 페스 아이돌).
각 container는 MySQL db나 NodeJS app들과 같은 하나의 작업을 수행하면서 서로 네트워크를 통해 연결될 수도 있습니다.
DockerHub에서 원하는 종류의 pre-configured environment 를 담는 container Docker image를 찾을 수 있습니다.
Docker를 구동하기 위해
먼저 Dockerfile
이 필요합니다.
Dockerfile
은 단순한 문서 파일이며, Docker image(프로듀스 아이돌)의 속성을 기술합니다.
FROM
키워드로 base image를 선택한다.
base image는 DockerHub에서 사용할 컨테이너에서 찾을 수 있다.
ex)
FROM ubuntu:20.04
RUN
키워드는 일반적인 명령어를 실행하는 커맨드이다.
Dockerfile
이 만들어지면, docker build
명령어 를 통해 빌드 가능하다.
-t 플래그를 이용해 이미지에 이름을 붙일 수 있다.
ex)
docker build -t myDockerimage .
(마지막 argument는 Dockerfile
이 위치한 경로)
docker images
: 존재하는 image 들을 나열한다.
docker pull <이미지 이름>
: DockerHub 에서 pre-compiled 된 이미지를 내려받는다.
docker run <이미지 이름>
: 이미지로부터 새로운 컨테이너를 만들고, 실행한다.
docker run -it <이미지 이름> /bin/bash
: 컨테이너를 실행하고, bash shell을 실행한다.
-it
플래그는 interactive shell을 실행하는 명령어라, 컨테이너 실행과 함께 컨테이너 내부에서 작업하고 싶을 때 유용합니다.
docker container ls
: 실행 중인 컨테이너 목록을 출력한다.
Docker Build
Dockerfile
을 이용하여 도커 이미지 파일을 만드는 과정입니다.
Docker는 계층화된 파일 시스템에 기반하여 이미지를 구축합니다. 그리고 Docker의 이미지는 layer와 metadata로 구성되어 있죠. Dockerfile
에 적힌 각 command는 바로 이 layer를 생성하는 역할을 하는 것입니다. 참고로 docker inspect
명령어를 사용하면 도커 이미지가 어떤 layer로 구성되어 있는지 확인이 가능합니다.
Commands
Dockerfile
에 적히는 각 명령어는 기본적으로 이미지 빌드 시 순차적으로 실행됩니다.
FROM
: base 이미지를 설정합니다.WORKDIR
: 작업 디렉토리를 설정합니다.RUN
: 이미지 빌드 시, command를 실행합니다.ENTRYPOINT
: 컨테이너가 시작될 때 항상 실행될 command를 설정합니다(예:/bin/bash
).CMD
: 컨테이너가 시작될 때 실행할 default command 또는 parameter를 설정합니다.EXPOSE
: 컨테이너가 listening할 포트 및 프로토콜을 설정합니다.COPY/ADD
: 이미지 빌드 시, 컨테이너의 파일 시스템으로 파일 또는 디렉토리를 복사합니다.ENV
: 환경 변수를 설정합니다.ARG
: 빌드 시에만 넘겨 받는 argument를 설정합니다.
ENTRYPOINT
와 CMD
활용 방법
두 명령어는 함께 쓰이는 경우가 많은데, 주로 ENTRYPOINT
로는 명령어만을, 그리고 CMD
로 default parameter를 지정하는 경우가 많습니다. 예를 들면 ENTRYPOINT
로 python
을, CMD
로 실행할 app.py
를 지정하는 식으로 하면 docker run
으로 컨테이너를 만들고 실행할 때 실행될 파이썬 프로그램을 유연하게 지정이 가능합니다.
그냥 docker run <이미지 이름>
으로 실행하면 CMD
에 지정된 app.py
가 실행되고, docker run <이미지 이름> <다른 파이썬 파일>
로 실행하면 CMD
에 지정된 app.py
대신 다른 파이썬 파일이 실행됩니다.
RUN
vs CMD
RUN
은 보통 이미지 내부에 특정 소프트웨어를 설치하기 위해 사용됩니다. 예를 들어 pip
로 파이썬 패키지를 설치할 때, 다음과 같이 작성할 수 있죠.
RUN pip install numpy
RUN
은 이미지 빌드 시 항상 실행되며, Dockerfile
에 여러 개의 RUN 명령문이 선언 가능한 반면, CMD
는 컨테이너 실행 시에만 한 번의 실행 기회를 가지며, docker run
커맨드에서 인자를 넘기는 경우에는 실행되지 않을 수도 있습니다.
예시 Dockerfile
FROM busybox:1.34.1
RUN echo "cloud"
RUN echo "computing"
RUN echo "2024"
이렇게 Dockerfile을 작성했으면, 이제 이미지를 빌드해 봅시다.
docker build -t myimage:v1 .
-t
옵션은 이미지에 태그를 붙이는 역할을 합니다. myimage:v1
이라는 이름으로 이미지를 빌드하겠다는 의미입니다. .
은 Dockerfile이 있는 디렉토리를 가리킵니다.
docker build
시 --no-cache
옵션 활용
docker build
를 실행할 때, 이미지 빌드 시에 캐시를 사용하는데, 이 때 --no-cache
옵션을 사용하면 캐시를 사용하지 않고 무조건 새로 빌드합니다. 이 옵션은 이미지 빌드 시 캐시 문제로 인해 발생하는 문제를 해결할 때 유용합니다.
Docker Inspect
docker inspect
명령어로 이미지가 어떤 layer들로 구성되어 있는지 확인 가능합니다. Dockerfile
에 적힌 각 명령어가 하나의 layer를 구성한다고 이해해도 되는데, Docker는 용량을 절약하기 위해 이미지를 빌드할 때 intermediate container를 삭제하는 과정에서 layer가 삭제되는 경우가 있습니다. 즉 최종 생성되는 이미지에서 확인되는 layer 개수는 예상보다 적을 수도 있습니다.
Docker Volume
컨테이너를 생성 후, 컨테이너 내부에서 여러 작업들을 수행하면 기존 이미지가 담고 있던 내용과는 차이가 생길 수밖에 없습니다. 이 상태에서 컨테이너를 삭제해 버리면 복구가 불가능하죠. 이런 문제점을 해결하기 위해 Volume을 이용할 수 있습니다.
Volume을 사용하려면 docker run
실행 시 -v <호스트 내 경로>:<컨테이너 내부에서 마운트할 경로>
옵션을 사용해 호스트 폴더를 컨테이너 내부에 마운트하도록 설정하면 됩니다.
docker run -v /host/path:/container/path myimage
Docker Hub 사용하기
이미지를 만들었으면 이를 Docker Hub에 업로드하여 다른 사람들과 공유할 수 있습니다. Docker Hub는 Docker의 공식 레지스트리로, Docker 이미지를 저장하고 공유할 수 있는 서비스입니다. 이미지를 올리기 위해서는 이미지 이름을 다음의 형식으로 생성해야 합니다.
<Docker Hub 사용자 계정>/<이미지 이름>:<태그>
만약 위와 같은 형식으로 이미지 이름을 생성하지 않았다면, 다음과 같이 이미지 이름을 변경하면 됩니다.
docker tag <이미지 이름> <Docker Hub 사용자 계정>/<이미지 이름>:<태그>
이때, 태그를 지정하지 않으면 자동으로 latest
로 지정됩니다.
docker login
명령어로 Docker Hub에 로그인한 후, 빌드된 이미지를 push할 수 있습니다.
docker login
docker push <Docker Hub 사용자 계정>/<이미지 이름>:<태그>
docker pull
명령어로 Docker Hub에서 이미지를 다운로드할 수 있습니다.
docker pull <Docker Hub 사용자 계정>/<이미지 이름>:<태그>
Docker Commit
docker commit
명령어를 사용하면 이미 생성된 컨테이너의 현재 상태가 반영된 이미지를 만들 수 있습니다.
docker commit <컨테이너 ID> <새 이미지 이름>
참고로 컨테이너가 실행 중인 상태를 유지하면서 호스트 쉘로 나오고 싶다면 Ctrl + P 또는 Ctrl + Q를 입력하면 됩니다.
Ctrl + D 또는 exit
명령어로는 컨테이너가 종료됩니다.
Docker Networks
도커의 컨테이너는 외부와의 통신을 위해 2개의 네트워크 인터페이스를 함께 생성합니다.
-
eth0
: 컨테이너 내부 namespace에서 사용하는 인터페이스입니다. 통상적인 Linux OS의 네트워크 인터페이스이니 자연스럽게 컨테이너도 갖게 되는 것입니다. 그런데 이것만으로는 호스트 네트워크 자원에 접근할 수 없으니 격리된 샌드박스에서 외부와 통신할 수 없겠죠. -
vethXXX
: 호스트의 네트워크 네임스페이스와 컨테이너의 네임스페이스를 연결하는 가상 이더넷 인터페이스이며, 기본적으로 네트워크 브릿지docker0
에 바인딩됩니다.
docker0
브릿지는 veth
가상 인터페이스와 호스트의 네트워크 인터페이스를 이어주는 역할을 합니다.
또 컨테이너 내부 인터페이스인 eth0
은 veth
인터페이스와 연결되어 있고, 이를 통해 외부와 통신하게 됩니다.
docker0
에 연결된 bridge
라는 도커의 기본 네트워크는 172.17.0.0/16
서브넷을 가지므로 컨테이너 생성 시에는 이 대역 안에서 IP를 할당받게 됩니다.
docker0
은 호스트에서 접근 가능하며 ifconfig docker0
등으로 조회할 수 있는 네트워크 인터페이스입니다.
docker network ls
명령어로 현재 Docker에서 사용 가능한 네트워크를 확인할 수 있습니다.
네트워크 생성
docker network create
명령어로 기본 사용되는 docker0
브릿지가 아닌, 원하는 서브넷 주소 등을 설정 가능한 커스텀 네트워크 브릿지를 생성할 수 있습니다.
docker network create mynetwork
새 네트워크를 만들고 호스트에서 ifconfig
명령어로 확인해 보면 docker0
이 아닌 해당 네트워크가 사용하는 새 브릿지가 br-<네트워크 ID>
형식으로 생성된 것을 확인할 수 있습니다.
물론, 기본값인 bridge
드라이버를 사용한 네트워크를 생성했을 때의 이야기입니다. 호스트를 통해 외부와 통신하지 않고 컨테이너끼리만 통신이 가능한 네트워크를 생성하려면 none
드라이버를 사용하면 됩니다.
컨테이너를 네트워크에 연결
docker run
명령어 실행 시 --network
옵션을 사용하여 컨테이너를 특정 네트워크에 연결할 수 있습니다.
docker run --network mynetwork myimage