Docker Basic 1

기존 물리적 머신(PM)에 비해 가상화 머신(VM)은 물리적인 제약에서 벗어나 서버 설치와 배포를 매우 간단하게 만들었습니다. 하지만 배포가 간단해졌다고 해도 VM은 실제 서비스 용도로 이용하기에는 성능 면에서 부족한 면이 있습니다. 당연한 이야기이지만 운영체제 위에 또 다른 운영체제를 운영한다는 것은 리소스 활용 면에서 비효율적인 면이 있습니다. 이런 단점을 극복하기 위해 나온 기술 중 하나가 LinuX Container(LXC) 입니다.

lxc-vm

위 그림은 컨테이너 방식과 기존의 가상화 방식의 차이점을 보여줍니다. 기존의 가상화 방식은 호스트 OS 위에 가상화를 통한 게스트 OS를 생성, 이 게스트 OS에 서비스를 실행합니다. 하지만 LXC는 호스트 OS 위에 다른 게스트 OS가 없이 호스트 OS에서 프로세스가 바로 실행됩니다. 기존의 프로세스와 다른 점은 각 프로세스는 독립된 공간에서 실행되며 이 공간을 컨테이너라고 합니다.

기존의 가상화에 비해 속도와 오버헤드, 이미지 용량 등 여러 면에서 개선된 점을 지니고 있으며 지금부터 소개해드릴 Docker 또한 이 LXC에서 출발한 오픈소스 프로젝트입니다.

docker

Docker(도커) 는 2013년 3월 출시한 오픈소스 프로젝트로 아마존, 구글, 마이크로소프트에서 공식 지원하는 현재 가장 이슈가 되고 있는 프로젝트 중 하나입니다.

초기 버전에는 LXC를 사용했으나 0.9 버전 이후 libcontainer를 독자적으로 개발하여 이용하고 있습니다.

2015년 7월 14일 1.7.1 버전을 릴리즈했고 현재는 Linux OS를 지원하며 이 외에 Mac OS X와 Windows에서 사용할 수 있습니다. 지원이 아닌 사용할 수 있다고 표현한 이유는 Mac과 Windows의 커널은 리눅스의 cgroups와 namespace 등의 기능이 없기 때문입니다. (Windows Server 2015 커널에서 공식 지원 예정) 또 리눅스에서 실행될 때와는 다르게 Windows와 Mac에서는 Virtual Box 위의 Boot2Docker라는 리눅스 이미지를 설치하고 실행됩니다.

따라서 도커를 테스트하기 위해서는 VirtualBox나 VMWare 등으로 Linux를 설치한 뒤에 도커를 실행하는 것이 올바른 방법입니다. 이 포스팅에서는 Ubuntu 14.04, Docker 1.7.1로 진행하겠습니다.

설치는 간단합니다.

이미지 001

https://get.docker.com 사이트는 단순히 설치 스크립트를 출력하는 사이트이며 해당 스크립크를 살펴보면 아시겠지만 각 리눅스 배포판에 따른 설치 방법이 나뉘어 있습니다. 데비안 계열의 리눅스는 https://apt.dockerproject.org/repo 에서 각 배포 버전 코드 네임에 맞는 패키지를 설치하게 되어 있습니다. 해당 저장소를 살펴보면 우분투는 precise(12.04) 부터 최신 배포판인 wily(15.10) 까지 지원함을 알 수 있습니다.

현 포스팅 작성 시점인 2015년 8월 현재는 1.7.1 버전이 설치됐습니다.

설치는 잘 됐으니 도커가 제대로 동작하는 지 확인해보겠습니다.

docker 명령어는 대부분 관리자 권한이 필요하므로 root로 작업을 하겠습니다.

이미지 002

출력된 결과 메세지를 보니 설치가 잘 된 듯 합니다. 메세지를 살펴보면 명령어의 실행 순서를 알 수 있습니다. 먼저 도커 클라이언트가 도커 데몬과 연결됩니다. 이후 run 명령어 뒤의 이미지 이름을 로컬 저장소에서 찾습니다. 물론 따로 받아놓은 이미지가 없으므로 docker hub라는 공식 저장소에서 해당 이미지를 찾습니다. 만약 이미지를 못 찾았다면 로컬과 저장소에서 찾지 못했다는 두개의 에러 메세지가 나옵니다. 이 hello-world 는 docker hub에 등록되어 있으므로 pull 명령어를 통해 이미지를 받아온 뒤 실행하게 됩니다.

이제 이 hello-world 이미지는 필요가 없으니 지우겠습니다.

이미지 003

docker rmi 명령어는 로컬 저장소의 이미지를 지우는 명령어입니다. 결과를 보니 에러로 인해 삭제가 실패했군요. 메세지를 보니 컨테이너 ID 5004b84e68a8 가 이 이미지를 쓰고 있다고 합니다. -f 옵션으로 강제로 삭제할 수 있지만 먼저 해당 컨테이너를 살펴보겠습니다.

이미지 004

docker ps 명령어는 현재 실행 중인 컨테이너 리스트를 보여주는 명령어입니다. -a 옵션을 붙이면 중지된 컨테이너 리스트도 같이 보여주게 됩니다. 이제 이 컨테이너를 지워보겠습니다.

이미지 005

보시는 바와 같이 컨테이너가 삭제됐습니다. 이제 다시 hello-world 이미지를 지워보겠습니다.

이미지 006

정상적으로 이미지도 삭제가 됐습니다. 이제 새로운 이미지를 받아와보겠습니다.

이미지 007

ubuntu 12.04 버전을 가지고 왔습니다. 이미지 이름 형식은 [이미지 이름]:[태그] 입니다. 이상한 부분은 분명 이미지 하나를 가지고 오도록 명령어를 쳤는데 명령어 실행 결과에서는 5개가 받아졌습니다. 이 부분을 확인해보겠습니다.

이미지 008

docker images 명령어는 현재 저장소에 저장된 이미지 리스트를 보여주는 명령어입니다. -a 옵션은 이미지가 빌드되는 과정에서 생성된 모든 이미지를 같이 보여줍니다. <none> 으로 보여지는 이미지들이 가장 위의 이미지를 빌드하는 과정에서 생성된 중간 이미지들입니다. 이제 받아온 이미지를 실행해보겠습니다.

이미지 009

63fd10249d47 ID를 가진 desperate_heisenberg 라는 이름의 컨테이너가 생성됐습니다. –name 옵션으로 따로 이름을 지정하지 않으면 무작위의 이름이 붙습니다. 이제 이 컨테이너로 접속해보겠습니다.

이미지 010

docker attach 명령어는 실행 중인 컨테이너에 접속하는 명령어입니다.

실행된 컨테이너로 들어가 /etc/issue 파일을 읽어보니 ubuntu 12.04 입니다. 우분투 14.04 위에 12.04을 올린 것입니다.

이 상태에서 apache를 설치해보겠습니다.

이미지 011

설치가 완료된 뒤 터미널에서 ctrl+ d나 exit 를 입력하면 컨테이너가 종료되며 호스트 OS로 나오게 됩니다. ctrl + p, ctrl + q 를 차례로 누르시면 컨테이너가 실행된 상태로 빠져나옵니다. 저는 실행된 상태로 나오겠습니다.

이제 bash 를 통하지 않고 docker 명령어로 컨테이너 안의 apache를 띄워보겠습니다.

이미지 012

docker exec 명령어로 bash 를 통하지 않고 외부에서 명령어가 실행됐습니다.

이제 apache를 설치한 컨테이너를 commit 을 통해 새로운 이미지로 만들어보겠습니다.

이미지 013

kollus 란 이름의 apache 태그가 달린 이미지가 생성됐습니다.

docker_images

위 그림은 각 이미지간의 의존성을 보여줍니다. 위 그림처럼 방금 커밋한 kollus:apache 이미지는 ubuntu:12.04 이미지에서 파생됐습니다. 즉 부모 이미지는 변경되지 않으며 변경된 부분만 kollus:apache 이미지에 쓰게 됩니다.

이제 이 이미지를 호스트 OS와 포트가 연결된 상태로 실행시켜보겠습니다.

이미지 014

호스트 OS의 8080 포트를 컨테이너의 80포트와 연결시켰습니다. 이제 호스트 OS IP의 8080포트로 브라우저로 접속해보겠습니다.

It works

아파치 기본 페이지가 잘 보입니다.

다음으로는 Dockerfile을 이용하여 자동으로 아파치를 설치하는 이미지를 만들어보겠습니다.

이미지 015

도커 파일 내용을 설명해드리겠습니다.

FROM : 어떤 이미지를 기반으로 할지를 정의합니다. 여기선 위에서 받아두었던 ubuntu:12.04 를 사용했습니다.

MAINTAINER : 이 도커 파일을 생성, 관리하는 사람을 입력합니다, 없어도 빌드에는 문제 없습니다.

RUN : 직접 실행될 명령어나 스크립트를 정의합니다. 여기서 apache를 설치했으며 설치 도중에는 결과 메세지만 출력되고 입력은 할 수 없으므로 -y 옵션을 붙였습니다.

CMD : 컨테이너가 실행된 후 실행할 명령어를 입력합니다. 여기서는 apache를 foreground 로 실행합니다. 주의해야 될 부분은 docker 홈페이지의 문서에도 나와 있지만 json 배열 형식을 따르므로 ‘ 가 아닌 ” 를 반드시 사용해야 합니다.

EXPOSE : 호스트와 연결할 포트 번호입니다. 여기서 주의해야 할 것은 외부에 노출이 되는 것이 아니라 단순히 호스트와 컨테이너의 사설 아이피를 연결만 한다는 것입니다. 이전 예제처럼 외부에 노출해야 될 포트는 docker run 의 -p 옵션을 사용합니다.

이미지 016

도커파일을 빌드로 실행합니다. RUN으로 정의한 명령어를 순차적으로 실행합니다.

빌드가 완료됐으면 다시 한번 빌드시켜보겠습니다.

이미지 017

처음 빌드한 속도와는 비교할 수 없이 빨리 빌드가 완료됐습니다. 출력 메세지를 살펴보면 Step 의 수는 같으나 실제로 수행은 하지 않고 cache 를 이용함을 알 수 있습니다. 이 예제는 구조가 간단하여 작성에 큰 어려움은 없으나 실제 서비스나 테스트 환경 구축은 이렇게 간단하지 않을 것입니다. 성공하기까지 도커파일 수정과 리빌드를 계속 반복해야 될 입장에선 다행스러운 일입니다.

캐시는 도커파일의 수정되기 전 라인까지 이용하며 step 3 을 수정했다면 실제 빌드는 step 3부터 이뤄집니다.

생성된 이미지를 호스트 OS의 포트와 연결해 실행해보겠습니다.

이미지 018

이전과 동일하게 웹브라우저로 접속해보면 아까와 동일한 아파치 기본 페이지가 나오게 됩니다.

이제 모든 컨테이너와 이미지를 삭제하겠습니다. 만약 실제 서비스 환경의 도커 파일을 만드셨다면 실패한 컨테이너 수가 상당히 많을 것이고 이것을 일일히 지우는 것도 상당히 고된 일입니다. 여기서 특정 컨테이너를 제외하고 삭제하는 명령어를 실행해보겠습니다.

# docker rm $(docker ps -a|grep -v [제외할 컨테이너 ID나 이름])

docker rm 뿐만이 start, stop  등에도 적용 가능합니다.

이제 도커 허브 사이트로 들어가보겠습니다.

https://registry.hub.docker.com/

dockerHub

도커 허브의 초기 페이지입니다. 도커 허브에는 각 오픈소스 프로젝트의 공식 이미지뿐만이 아니라 전 세계 이용자들이 등록한 이미지들도 같이 올라와 있습니다. 일단 초기 페이지에 보이는 nginx 저장소로 가보겠습니다.

간단한 nginx 의 소개와 이미지에 대한 사용법이 나와 있습니다. 설명은 제외하고 nginx 에 대한 도커 파일 페이지로 가보겠습니다.

이미지 019

위에서 작성한 도커파일 예제에 없는 구문이 몇개 있으므로 여기에 대해 먼저 설명하겠습니다.

일단 이 nginx 는 debian 8에서 동작함을 FROM에서 알 수 있습니다.

ENV : 환경 변수를 설정하는 구문입니다. 이 도커파일에서는 $NGINX_VERSION을 설정합니다.

VOLUME : HOST OS나 다른 컨테이너와 공유할 디렉토리를 지정합니다. CMD와 동일하게 json 형식을 이용하므로 “를 반드시 사용해야 하며 “volume1”, “volume2” 등으로 다수의 디렉토리를 지정 가능합니다.

공식 저장소에 있는 nginx 를 받아오겠습니다.

이미지 020

nginx:latest 를 받아왔습니다. 태그로 버전을 따로 지정하지 않는 한 가장 최신 버전을 받아옵니다. 받아올 수 있는 버전은 도커 허브의 tags 페이지를 보시면 확인 가능합니다.

받아온 nginx 이미지를 실행해보겠습니다.

# docker run -d -p 8080:80 –name nginx nginx

이전 예제와 동일하게 웹브라우저로 접속하면 nginx 의 기본 페이지가 보입니다.

이처럼 도커 허브에 필요한 이미지가 있다면 받아와 이용할 수 있습니다.

다음으로는 각 컨테이너 간의 통신을 예제와 함께 설명드리겠습니다.

이미지 021

도커를 설치하고 실행하게 되면 docker0 이라는 가상 네트워크 인터페이스가 생성됩니다. 컨테이너를 생성 시 특별한 옵션을 주지 않는 한 이 docker0 이라는 인터페이스를 통해 컨테이너가 통신을 하게 됩니다. 이 아이피 대역은 도커 시작 옵션으로 변경이 가능하며 bridge interface 를 추가로 생성하여 해당 인터페이스를 이용하게도 가능합니다.

먼저 도커 허브에 있는 이미지로 DB 서버를 설치해보겠습니다.

이미지 022

정상적으로 설치가 됐네요. 이제 웹을 설치해보겠습니다.

이미지 023

이제 호스트 IP의 8080으로 접속해보면 아파치의 기본 페이지가 보일 것입니다.

다른 예제에서 안나온 옵션을 먼저 설명드리겠습니다.

–link db:db : db 컨테이너와 현재 실행할 컨테이너를 별칭으로 연결합니다. 형식은 [컨테이너 명]:[별칭] 입니다.

-v /www:/var/www : 호스트 OS의 /www와 실행할 컨테이너의 /var/www 디렉토리를 연결합니다.

이제 다음으로 web 컨테이너에 워드프레스를 설치하겠습니다.

이미지 024

db 서버에 wordpress 데이터베이스 생성 및 사용자 생성을 진행합니다.

이미지 025

docker inspect 명령어는 컨테이너의 설정 정보를 보여주는 명령어입니다. 아무 옵션 없이 컨테이너 이름만 입력하면 모든 설정 값을 보여주며 형식은 json 입니다. -f 옵션으로 특정 옵션의 정보만 가지고 올 수 있습니다.

docker inspect 명령어로 web 컨테이너의 아이피 정보를 확인한 뒤 db 컨테이너에 접속 권한을 줬습니다.

wp_config

워드 프레스의 초기 설정 페이지가 보입니다. 모든 설정을 마치고 난 뒤 wp-login.php 에 관리자로 로그인하겠습니다.

wp_admin

워드 프레스의 초기 페이지가 나왔습니다. web 과 db 컨테이너의 통신이 정상적으로 이뤄졌습니다.

web 컨테이너의 hosts 파일을 읽어보겠습니다.

이미지 026

가장 아래 줄을 읽어보니 db 컨테이너가 hosts 파일에 세팅되어 있습니다. 사실 –link 옵션이 하는 일은 초기 컨테이너 생성 시 hosts 파일에 alias 를 추가해주는 일만 하며 이 세팅이 없어도 docker0 에 속한 컨테이너라면 아이피로 서로 통신이 가능합니다. 물론 수동으로 hosts 파일에 호스트를 추가해도 문제가 되지 않습니다.

마지막으로 도커 허브의 공식 이미지들의 취약점을 검사해보겠습니다. 기사 링크

도커 허브의 이미지를 사용할 때 주의해야 될 부분은 상당히 많은 수의 이미지들에 shellshock, poodle, heartbleed 등의 심각한 취약점이 존재한다는 것입니다. 비교적 최근에 발견된 이슈로 허브에 있는 이미지를 사용 시에는 주의가 필요합니다.

# git clone https://github.com/fermayo/docker-shellshocker.git

# cd docker-shellshocker

git hub에 있는 docker 취약점 검사 스크립트입니다.

base_images.txt 파일에 테스트할 도커 이미지 이름을 적습니다.

여기선 ubuntu:latest 부터 lucid까지 테스트해보겠습니다.

이미지 027

lucid, quantal, raring, saucy 이미지들에서 취약점이 발견됐습니다. 문제를 예방하기 위해서는 되도록이면 latest 버전으로 이미지를 사용하는 것이 좋지만 불가피하게 하위 버전의 이미지를 사용해야 한다면 반드시 취약점에 대한 패치를 적용해야 합니다.

도커의 설치부터 도커 허브 공식 이미지들의 취약점까지 설명을 드렸습니다. 다음 포스팅에서는 도커 네트워킹에 대해 설명을 드리겠습니다.

감사합니다.

Posted by 염지성@ 카테노이드 SE팀

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.