[개발 정리] Docker 튜토리얼 및 정리 1편
mlops 를 키워드로 검색하면 가장 처음에 그리고 공통적으로 등장하는 친구가 있는데, 바로 도커이다. 도커가 뭘까 해서 설치해서 이것저것 해보다가, 좀 제대로 정리해야겠다 싶어서, 유튜브 freeCodeCamp.org 채널에 있는 도커 2시간 강의를 듣고 이해한 내용을 정리해본다. 추가적으로 글로 잘 정리된 블로그를 참고하였다.
도커는 컨테이너 기반의 오픈소스 가상화 플랫폼이다. 프로그램 별 독립된 환경을 제공해주고 이를 이미지화하여 어디든 옮기고 실행할 수 있게 하면서, 동일한 실행 환경을 보장한다. 비교 대상인 VM(virtual machine)과 비교하면서 좀 더 이해해보도록 하자.
Share the underlying OS Kernel
운영 체제 OS 는 OS kernel (ex. Linux) 과 software (ex. Ubuntu, Fedora, ..) 로 구성되어 있다. 같이 달아놓은 예시를 보면, 다양한 운영 체제가 같은 OS kernel을 공유하고 있음을 알 수 있는데, 도커 컨테이너 또한 OS kernel 을 공유한다. OS kernel 을 공유한다는 것은, 우리가 가지고 있는 서버의 OS kernel 이 Linux 일 경우, 이를 사용하는 소프트웨어인 ubuntu, centos 에 대한 도커 컨테이너들에 대해서만 실행이 가능함을 의미한다. 즉, 윈도우 서버에서 linux container 를 띄우는 것은, 실제로 linux container 를 띄우는 것이 아닌, 밑단에 linux VM 을 실행하는 것이라고 한다.
Docker vs VM
보다 직관적으로 이야기하면, VM은 OS 를 가상화하여 격리하고, Docker 는 프로세스를 격리한다. 이에 따라, 하나의 프로그램 실행에 대한 환경을 격리하기 위해서는, VM의 경우 각각 OS 를 따로 만들어주기 때문에, 리소스가 많이 들어가고 이에 따라 오버헤드가 발생하게 된다. 이에 반해, 도커는 VM 보다 낮은 수준의 격리를 지원하지만, 필요한 만큼의 프로세스와 메모리를 underlying OS kernel 에서 할당받기만 하면 되기 때문에, 사용 리소스가 상대적으로 훨씬 적다.
Container vs Image
- Image : 컨테이너를 어떻게 만들지에 대한 plan이자 template
- container : 실행 중인 이미지의 인스턴스, 격리된 환경과 프로세스를 가지고 있다.
Commands
- run : container 를 실행
docker run nginx
- ps : running container 를 확인
- a : 모든 instance 를 확인 (stopped, recently exited)
docker ps
docker ps -a
- stop : container 를 중지
docker stop {container_id|container_name}
- rm : container 를 삭제
docker rm {container_id|container_name}
- images : image 를 확인
docker images
- rmi : image 삭제
docker rmi {image_id|image_name}
- pull : image 다운로드
- docker run 을 실행할 때, 해당 이미지가 없는 경우, 자동으로 pull을 하고, run이 실행된다.
docker pull nginx
- exec : 커맨드 실행
docker exec {image_id|image_nm} {cmd}
- attach and detach :
- attach : 데몬화된 프로세스를 다시 tracking
- detach : 프로세스를 데몬화
docker run -d kodekloud/simple-webapp # start container instance docker attach {container_id_of_above}
- tag :
- version 을 ":" 과 함께 같이 달아준다.
- default 는 latest
docker run redis:4.0
- stdin :
- docker 는 기본적으로 표준 출력 (stdin) 을 지원해주지 않는다.
- -i : interactive 옵션을 통해 표준 입력을 받는다.
- -t : pseudo terminal 을 지원, 터미널의 프롬프트를 받을 수 있다. (ex. user's password : )
docker run -it {image_id}
- port mapping :
- docker container는 기본적으로 internal IP이기 때문에, docker host 하고만 통신이 가능하다. ex) 172.17.0.1
- -p : port 를 매핑해줌으로써, 외부에서 접근이 가능하게 해준다. ex) 192.168.1.5:80
docker run -p {mapped_port}:{internal_port} {image_name}
- volume mapping :
- 기본적으로 docker container는 격리된 file system을 가지고 있다.
- 이를 container 밖의 directory와 연결하기 위해서는 -v 옵션을 통해, volume mapping 을 사용하면 된다.
- 이는 내부적으로 외부 directory를 container 내부로 마운트시켜준다.
docker run -v {external_dir}:{internal_dir} mysql
- inspect :
- container 에 대한 자세한 부분을 json format으로 볼 수 있다.
docker inspect {container_id}
- logs :
- container의 표준 출력을 확인할 수 있다. (데몬화된 경우여도 가능)
docker logs {container_id}
- environment variable
- Python 의 os.environment[""] 의 값들을 외부에서 설정할 수 있다.
- 설정된 환경 변수는 inspect 커맨드로 확인할 수 있다.
docker run -e KEY=value image_name
Create My Own Images
도커 이미지를 생성하기 위해서는 Dockerfile 이라는 미리 정해진 이름의 configuration 파일을 사용한다. Dockerfile 에 쓰여지는 커맨드는 다양하지만 그 중 몇 가지만 알아보면 아래와 같다.
도커의 이미지 빌드 과정은 layered architecture 라는 계층적 구조를 띄고 있다. 이에 따라, Dockerfile 에 있는 커맨드의 각 행이 하나의 레이어를 형성하고 있으며, 이는 빌드 과정에서 에러가 발생해 디버깅하고 다시 실행할 경우, 빌드가 성공적으로 된 layer는 이전에 빌드된 것을 읽어서 바로 진행하고 남은 layer 들에 대한 것들만 새로 진행한다.
FROM -> start from a base OS or another image
RUN -> installs all dependencies
COPY -> copy source from externel to inside the container
ENTRYPOINT -> command will be run when the image is run as container
ex)
FROM Ubuntu
RUN apt-get update
RUN apt-get install python
RUN pip install flask
RUN pip install flask-mysql
COPY ./src_code
ENTRYPOINT FLASK_APP = ./src_code/app.py flask run
Conclusion
글을 쓰다보니, network 부분과 docker-compose 부분이 꽤나 복잡하고 좀 더 정리가 필요한 것 같아, 몇 개로 나눠서 블로깅을 진행하려 한다. 다른 포스트에는 network 부분과 orchestration, 그리고 docker 공식 docs 에 나와있는 몇 가지 팁들을 정리해보도록 하겠다.