본문 바로가기

ETC

[Docker] 도커 입문

0. 배경

우리가 사용하는 웹 어플리케이션은 서버에서 동작합니다. 서버는 우리가 작성한 코드가 실제로 실행되는 컴퓨터입니다. 작성한 코드를 실행하기 위해서는 다양한 작업을 해주어야 하는데 os 설치, 실행환경 세팅, 어플리케이션 코드(빌드) 다운로드, 라이브러리 다운로드 등등의 과정이 필요합니다. 

 

또한 서버를 운영하는데에도 여러 작업들이 필요합니다. 보안 결함이 없어야 하고, 필요할 때 새로운 코드나 라이브로리 추가가 가능해야 합니다. 만약 많은 수의 요청을 처리하는 서비스가 있다고 한다면 부하를 분산하고 운영을 원활하게 하기위해 많은 수의 서버를 운영해야 합니다. 이 여러대의 서버 컴퓨터의 운영을 쉽게 하기위해 스크립트를 작성해 사용합니다.

 

서비스를 운영하다보면 많은 요청이 들어옵니다. 예를들어

- ubuntu 대신 centos 서버를 사용하겠다.

- python3.8에서 python3.9로 업데이트하고 싶다.

- 새로운 서비스를 java로 만들어 보고 싶다.

 

서비스가 커지고, 서버가 늘어날수록 어플리케이션을 실행 시키기위한 조건 난이도가 기하급수적으로 상승합니다. 

각 상황에 따라 스크립트를 작성해주어야 하며, 모든 서버가 원하는 상태를 만들어 줘야 하기 때문입니다. 코드가 동작하지 않는 경우도 문제가 있겠지만 간헐적인 문제가 생기는 경우 이슈를 찾는데 매우 많은 시간이 필요해집니다. 

 

 

1. 도커란?

실제 서비스의 운영은 단순히 코드를 짜는 것 뿐만 아니라 프로그램을 실행시키는 것에도 있습니다. 서비스의 크기가 커지면 커질수록 서버 운영은 점점 복잡해집니다. 예를들어 보안적 이슈가 있을 때마다 모든 서버에 보안적인 업데이트를 해주거나 어플리케이션에서 새로운 기능이 필요할 때 (언어추가, 언어업데이트, 라이브러리 추가) 실행 환경을 만들어 주는 것도 일이었습니다. 이러한 문제점을 해결하기위해 서버를 운영하는 인프라 관리와 어플리케이션 작성을 분리해서 추상화를 한다는 것이 도커의 발상입니다. 실행환경, 실행하는 코드, 라이브러리, 설정 파일을 한 곳에 정의를 해두고 이를 도커 이미지라고 부르며 서버에서는 간단하게 이미지를 가지고 실행만 시킵니다. 

 

이러한 패러다임을 적용하기위해 세 가지 문제를 해결해야 합니다.

- 이미지를 생성하는 법 : 일관성 있게 이미지를 만들고, 어플리케이션을 실행하는데 필요한 모든 것을 설정할 수 있어야 한다.

- 이미지를 공유하는 법 : 이미지가 어디에 있든 잘 전달할 수 있게 만들어 필요한 곳에서 사용할 수 있다.

- 이미지를 실행하는 법 : 일관성있게 이미지를 실행할 수 있다. 

 

즉, 도커는 원하는 프로그램을 쉽게 실행하기 위한 플랫폼 입니다.

 

 

2. 도커의 구성요소와 장점

도커의 구성요소 :

- docker image : 도커 이미지는 어플리케이션을 실행하기위한 모든 것들이 저장된 파일입니다.

- docker container : 돜 컨테이너는 이미지를 실행합니다. 컨테이너는 프로세스의 독립성을 보장하기위해 각 컨테이너 끼리는 저장소와 네트워크가 분리되어 있습니다.

- docker registry : 도커 이미지를 쉽게 공유하는 저장곤간 입니다. 레지스트리에 이미지를 등록하고 다운 받는게 가능합니다.

- dockerd (docker daemon) : 도커 디는 도커 이미지를 레지스트리로부터 다운 받거나 올리거나, 이미지로부터 컨테이너를 실행하거나, 이미지를 새로 생성하거나 하는 도커 오브젝트들(이미지, 컨테이너, 볼륨, 네트워크) 등을 관리합니다. 이 때, docker API request를 클라이언트의 CLI로부터 요청 받습니다.  

https://docs.docker.com/get-started/overview/

 

도커 이전에도 개벌적인 어플리케이션을 원하는 환경에서 실행하는 기술들이 있었습니다. 비슷한 것으로 가상머신(VM) 기술이 있습니다.

VM과 Docker 비교

그림의 왼쪽에 보이는 기존의 가상화 기술(VMware, VirtualBox)는 Host OS를 설치한 다음에 하이퍼바이저를 설치하고 원하는 OS, Bin/Libs, 어플리케이션 코드를 받아서 실행하는 형태를 가지고 있습니다. 필요한 운영체제를 하이퍼 바이저를 거쳐서 동작하기 때문에 새로운 디스크롤 할당받거나 인터넷 통신을 하는 등 리소스를 사용할 때마다 대부분의 성능이 Host OS에서 실행하는 것보다 성능이 떨어집니다. 만약 여러 개의 OS를 생성해야 한다면 Gusest OS를 여러번 설치해야 하기 때문에 이미지의 크기가 커지는 제약이 있었습니다. 또한 시스템 자원을 가상화하고, 공간을 생성하는 하이퍼 바이저를 거치기 때문에 성능의 손실이 발생하였습니다. 비록 완벽하게 OS를 생성할 수 있다는 장점이 있었지만 성능손실이 매우 컸습니다. 

 

반면에 그림 오른쪽의 도커의 경우에는 Host OS에서 도커 엔진을 통해 직접 OS를 다운로드 받지 않아도 Binary나 Library를 다운 받아 바로 어플리케이션을 사용할 수 있습니다. 도커는 리눅스 자체 기능인 chroot, namespace, cgroup 기술을 사용해 프로세스 단위의 격리 환경을 만들 수 있기 때문에 성능 손실이 거의 없습니다. 따라서 OS를 별도로 가지지 않아 이미지의 크기가 매우 작아졌고 Host OS의 Kernel을 공유해 사용함으로 성능 손실이 거의 없습니다. 

 

3. 도커 설치하기

도커 다운 링크(https://www.docker.com/get-started)

git bash에서 docker 명령어나 docker ps 를 쳐서 정상적으로 설치가 된 것을 확인할 수 있습니다.

 

4. 도커로 어플리케이션 실행하기

docker run -d -p 80:80 docker/getting-started

브라우저의 localhost/tutorial/ 에서 위의 커맨드로 실행한 어필리케이션을 확인할 수 있습니다. 

 

public docker registry로 유명한 docker hub에 있는 docker/getting-started 라는 이미지를 로컬로 다운로드 받는 과정입니다. docker run이 실행되면 기본적으로 로컬 환경에 있는 docker image 목록에서 해당 이미지를 찾습니다. 만약 image를 찾지 못한 경우 docker registry에서 이 image를 가져옵니다. 

image를 가져온 후 docker run 커맨드를 통해 container를 생성합니다. 

 

docker run 기본 문법 
docker run [options] {image_name} [command]​

option은 docker run --help에서 쉽게 확인해 볼 수 있습니다. 

 

위에서 적용된 몇몇 대표적인 옵션에 대해 살펴보도록 하겠습니다. 

 

-d 옵션 : -d의 경우 detach option으로서 docker를 실행할 때 나오는 container의 실행 process를 백그라운드에서 진행시킵니다. 

-p : -p의 경우 publish 옵션으로서 실행시키려는 docker/getting-started 어플리케이션이 웹 서비스이다보니 80번 포트로 서비스를 제공하는데 제공된 서비스를 localhost에서도 접속을 해야 하다보니 단절된 네트워크를 연결해줍니다. -p [host port] : [container serving port]의 형태로 사용됩니다.

 

 

도커의 컨테이너를 조작하는 몇가지 명령어를 살펴 보도록 하겠습니다. 

도커 명령어 모음 
1. 현재 실행중인 컨테이너 모아 보기
docker ps​

2. 도커 컨테이너 멈추기

docker kill {container_id|container_name}
docker stop {container_id|container_name}

두 커맨드는 비슷하지만 다른 행동을 합니다. docker kill의 경우 SIGKILL 신호를 docker stop의 경우 SIGTERM 신호를 docker container에 전달합니다. SIGKILL의 경우 바로 삭제를 요청하고 SIGTERM은 프로세스의 정지를 의미합니다. SIGTERM의 경우 어플리케이션을 종료시킬 때 정리할 시간을 보장해줍니다. 

3. 중지된 컨테이너 모아보기

docker ps -a

docker stop으로 중지한 컨테이너를 볼 수 있습니다. 

4. 컨테이너 재실행 하기

docker restart { container_id | container_name }

중지된 도커 컨테이너를 재실행 시킬 수 있습니다. 


5. 실행중인 컨테이너에 명령어 적용하기

docker exec [options] {container_id|container_name} [command]

docker exec b5f166043ec1 ls # 목록 탐색 
docker exec -ti b5f166043ec1 sh # 쉘스크립트 실행하기
cat docker-entrypoint.sh # 화면에 보이기
exit


6. 도커 컨테이너 삭제하기

docker rm {container_id}


7. 도커 명령어 참고 링크 (https://docs.docker.com/engine/reference/commandline/cli/)

 

 

 

5. Flask 어플리케이션을 Docker로 실행하기