도커 컨테이너 만들기

이 장에서는 호스트 OS의 커널을 공유하면서 격리된 컴퓨터 자원을 제공하는 가상화 기술인 도커 컨테이너Docker Container를 만들어 봅니다.



이 장의 내용

  • 도커 컨테이너를 만들어 봅니다.


도커 컨테이너 만들기

검색된 이미지를 pull 옵션으로 로컬Local에 저장해 보겠습니다.

$ docker pull <Image_name:TAG>
$ docker pull ubuntu:14.04
Using default tag: latest
324d088ce065: Pull complete
2ab951b6c615: Pull complete
9b01635313e2: Pull complete
04510b914a6c: Pull complete
83ab617df7b4: Pull complete
Digest: sha256:b8855dc848e2622653ab557d1ce2f4c34218a9380cceaa51ced85c5f3c8eb201
Status: Downloaded newer image for ubuntu:latest


로컬에 저장된 도커 이미지Docker Image는 images 옵션을 통해 확인할 수 있습니다. 참고로 도커의 기본 이미지 저장 경로는 '/var/lib/docker/' 이며, $ docker info |grep 'Docker Root Dir' 명령으로 확인할 수 있습니다.

$ docker images
REPOSITORY          TAG IMAGE ID            CREATED SIZE
ubuntu              14.04 0b1edfbffd27       2 weeks ago 113MB


docker run 명령어를 사용하면 앞서 검색했던 리포지터리Repository로부터 이미지를 받아 컨테이너를 순식간에 만들 수 있습니다.

$ docker run -i -t ubuntu:14.04 /bin/echo hello
hello


이 명령은 우분투 컨테이너ubuntu container 가 hello를 실행하고 종료하는 명령입니다. 서버 하나 띄우는데 10초도 걸리지 않습니다.

이를 응용하면 일회성으로 수행해야 하는 배치batch 작업을 손쉽게 수행할 수 있습니다.

$ docker ps


docker ps 명령을 실행했는데 우분투 컨테이너가 없습니다. ps 명령만 입력하면 실행중인 컨테이너 목록만 나오기 때문입니다. -a 옵션을 추가하면 정지된 컨테이너도 함께 볼 수 있습니다.

$ docker ps -a
CONTAINER ID        IMAGE COMMAND             CREATED STATUS             PORTS NAMES
491da6daefba        ubuntu:14.04 "/bin/echo hello"   About a minute ago Exited (0) About a minute ago                       modest_gates



  [TIP] $ docker ps -a 명령 출력 결과에서 컬럼 의미

  • CONTAINER ID : 테스트한 결과 container id는 엄청 긴 16진수인데, ID 컬럼에는 그 일부인 12자리만 보입니다. Container ID는 $ docker inspect<Container-NAME>으로 확인할 수 있습니다.
  • IMAGE : 해당 컨테이너의 Image name입니다. image 내용은 커밋commit 명령으로 컨테이너의 변경사항을 이미지로 다시 생성하지 않는 이상 변하지 않습니다.
  • COMMAND : 컨테이너 수행 명령
  • CREATED : 컨테이너 생성 시간
  • STATUS : created, restarting, running, removing, paused, exited, dead의 상태 값 중 하나를 가집니다.
  • PORTS : 노출된 포트Exposed Ports
  • NAMES : 컨테이너 이름입니다. 여기서 재밌는 점은 run 혹은 create 명령에서 --name 옵션이 없으면 유명한 프로그래머의 이름이 랜덤으로 정의됩니다.


다음 명령으로 컨테이너를 하나 더 실행합니다.

$ docker run -i -t --name ubuntu-awskrug ubuntu:14.04


위 명령은 ubuntu:14.04 이미지로 ubuntu-awskrug란 이름의 컨테이너를 만들고 셸shell을 통해 attach하라는 뜻입니다. 그러면 프롬프트가 바뀌면서 컨테이너 내부로 들어가게 됩니다.

root@9a916dc0d839:/#


참고로, docker run 명령은 컨테이너를 생성한 후 명령어를 실행합니다.

우리가 docker run으로 컨테이너를 만들면 다음의 작업이 순차적으로 실행됩니다.

① 로컬 이미지가 없을 때 docker pull로 이미지를 받아옵니다.
② docker create로 컨테이너를 새로 생성합니다.
③ docker start로 컨테이너가 시작합니다.
④ -i -t 옵션을 주면 docker attach로 컨테이너 안에 들어갑니다.


컨테이너 안의 프로세스process는 백그라운드BackGround로 실행중이어야 하고 실행중인 프로세스가 하나도 없으면 컨테이너는 중지됩니다. 만약 컨테이너를 중지하지 않고 빠져나오려면 Ctrl + P + Q를 순서대로 입력합니다.

다시 컨테이너 안으로 들어가 봅니다. attach는 컨테이너 내부로 들어가는 명령어 입니다.

$ docker attach ubuntu-awskrug

혹은

$ docker exec -it ubuntu-awskrug /bin/bash


이러한 방식으로 관리자는 컨테이너 내부에 접근할 수 있습니다.

exit를 입력해 컨테이너에서 빠져나와 봅니다.

root@9a916dc0d839:/# exit



  [TIP] 만약 다음과 같은 에러가 발생하

unable to remount sys readonly: unable to mount sys as readonly max retries reached


/etc/sysconfig/docker 파일에서 다음과 같이 --exec-driver=lxc를 추가합니다.

$ sudo vi /etc/sysconfig/docker
# /etc/sysconfig/docker## Other arguments to pass to the docker daemon process# These will be parsed by the sysv initscript and appended# to the arguments list passed to docker -d
list passed to docker -d
other_args="--selinux-enabled --exec-driver=lxc"


도커 서비스를 재시작합니다.

$ sudo service docker restart



Docker run 명령의 주요 옵션  

Docker run 명령의 주요 옵션은 다음과 같습니다. 실습 시 사용하면서 하나씩 습득해 보겠습니다.

옵션

설명

비고

-i

--interactive=false로 표준 입력(stdin)을 활성화하며 컨테이너와 연결(-a : --attach)되어 있지 않더라도 표준 입력을 유지합니다.

보통 /bin/bash 명령을 이 옵션을 통해 사용합니다.

예) -i /bin/bash

만약 셸 프롬프트가 컨테이너 내부에 있고, 컨테이너를 종료하지 않고, 셸만 빠져나오고 싶으면 ctrl + p + q 를 차례로 누룹니다.


다시 컨테이너 셸로 들어가고 싶으면 # docker attach 컨테이너명 혹은 # docker exec -it 컨테이너명을 입력합니다.

-t

--tty=false 옵션으로, TTY mode(pseudo-TTY)  셸을 사용하려면 반드시 설정해야 합니다.


-a

--attach=[]의 약어로 컨테이너의 표준 입력(stdin), 표준 출력(stdout), 표준 에러(stderr)를 연결합니다.

예) --attach="[stdin]"


--add-host=[]

컨테이너의 /etc/hosts 에 호스트 이름과 IP 주소를 추가합니다.

예) --add-host=hostname:192.168.0.78


-c

--cpu-shares=1024의 약어로 기본 설정 값은 1024입니다.


cgroups : control gorups의 약어로, 프로세스 자원(CPU, Memory, Disk IN/OUT, Network 등) 사용을 제한하고 격리하는 리눅스 커널의 기능입니다.

예) --cpu-shares=2048


--cap-add=[]

앞서 -c 옵션에서 설명한 cgroups에서 제어하는 능력에 대한 옵션입니다. 정해진 List name에 맞게 설정해 특정 컨테이너가 특정 권한만 사용하게 할 수 있습니다. ALL을 지정하면 모든 capability를 사용할 수 있습니다.

예) --cap-add=NET_BIND_SERVICE

◇ Capabilities list 참조 
https://linux.die.net/man/7/capabilities


--cap-drop=[]

add와 반대로 특정 컨테이너에 특정 capability를 제외하는 옵션입니다.


--cidfile="경로"

특정 경로의 파일에 contatiner ID 를 기록합니다.

예를 들면 톰캣tomcat 등의 프로세스에서 특정 파일에 PID를 기록하는 방식과 유사합니다.


--cpuset=[]

특정 컨테이너를 특정 CPU 코어에 할당하는 옵션으로 잘 사용하지 않습니다.

예) --cpuset="0,1" 0번, 1번 CPU 코어만 사용

    --cpuset="0-7" 0~7번 CPU 코어, 즉 8개 코어를 사용


-m

--memory="num" 컨테이너의 메모리 제한을 설정하는 옵션입니다. 숫자 뒤에 b(byte), k(kilo), m(mega), g(giga)를 사용할 수 있습니다.

예) --memory="256m"


-d

--detach=false를 통해 Detached 모드로 컨테이너를 실행합니다. 보통 데몬 모드라고 부르며, 컨테이너가 백그라운드로 실행됩니다.


--device=[]

호스트의 디바이스를 컨테이너 내에서 사용할 수 있도록 연결하는 옵션입니다. <Host Device name>:<Container Device name> 형태로 사용합니다.

예) --device="/dev/sdf:/dev/sdf" 으로 설정하면 호스트에 연결된 /dev/sdf block device를 컨테이너에서도 사용할 수 있습니다.


--dns=[]

컨테이너에서 사용할 DNS 서버를 정의합니다.

예) --dns="8.8.8.8"


-e

--env=[]으로, 컨테이너 내 환경 변수를 정의할 때 사용합니다. 보통 ID, Password, PATH 등을 독립적인 각각의 컨테이너로 넘길 때 사용합니다.

# docker run -e 옵션에서도 환경 변수를 넘길 수 있지만, Dockerfile에서 초기 환경변수를 정의할 수도 있습니다.

예) -e userid=hsy


--env-file=[]

컨테이너의 환경 변수가 담긴 설정 파일을 지정함으로써 설정 파일 내 환경변수를 적용합니다.

예) --dnv-file="/etc/docker/contailner1_env"

# cat /etc/docker/contailner1_env

ENV PATH /home/path:$PATH

ENV userid hsy

ENV dbpass p@ssw0rd


--expose=[]

컨테이너 내에 포트를 Host에만 연결하고 외부에는 노출하지 않는 옵션입니다. 즉 --link된 컨테이너만 해당 포트로 접근할 수 있습니다.

예) --expose="3306"


-h

--hostname=[] 컨테이너의 호스트 이름을 정의합니다.

예) --hostname="hsy_test"


--link=[]

컨테이너끼리 Link로 연결합니다.

컨테이너의 IP는 유동 IP 성격을 띄고 있어 Link를 통한 연동을 권장합니다.

Link를 하면, 실행(run)되는 컨테이너 내 /etc/hosts 파일에 "IP name CONTAINER_ID" 형태로 삽입됩니다. 그리고 Link 대상이 되는 컨테이너의 IP가 변경되면 자동으로 /etc/hosts의 파일이 변경돼 연결이 유지됩니다.


--name=" "

컨테이너 이름을 정의합니다.


--net="Network_type"

컨테이너 생성 시 지원하는 네트워크 방식 중 대표적인 네 가지에 대해 설명합니다.

  1. bridge : 가장 많이 쓰이는 네트워크 방식으로 기본(default) 설정입니다. 도커를 구성할 때 docker0 가상 네트워크 인터페이스가 생기는데, 각각의 컨테이너는 이 디바이스를 바인딩해서 각각 사용함

  2. none : 네트워크를 사용하지 않습니다.

  3. container : 기존에 존재하는 다른 컨테이너의 네트워크 환경을 공유합니다.
    예) --net=container:컨테이너ID

  4. host : 컨테이너 내에서 호스트 네트워크를 그대로 사용합니다. 보안에 취약해서 권장하지 않습니다.


-rm

컨테이너 안의 프로세스가 종료되면 컨테이너를 자동으로 삭제합니다. 배치성 작업에 종종 설정하며 백그라우드 옵션인 -d와는 함께 사용하지 못합니다.


-P (대문자)

호스트에 연결된 컨테이너의 모든 포트를 외부에 노출합니다.


-p (소문자)

호스트에 연결된 컨테이너의 특정 포트를 외부에 노출합니다. 보통 웹서버 등 대외 서비스의 포트를 노출할 때 이용합니다.

예) -p <호스트 포트>:<컨테이너 포트>  -p 80:80

-p <호스트 IP>:<호스트 포트>:<컨테이너 포트> -p 10.0.0.16:80:80  호스트에 여러 개의 네트워크 인터페이스가 있을 때 사용합니다.


--privileged

컨테이너 안에서 호스트의 모든 리눅스 커널 기능을 사용하도록 하는 기능입니다.


--restart=" "

컨테이너 내의 프로세스 종료 시 재시작 정책을 정의합니다.



--u

컨테이너가 실행될 리눅스 사용자 계정 ID 혹은 UID를 정의 합니다.

예) --user=hsy


-v

데이터 볼륨을 정의합니다. 이 옵션으로 호스트와 공유할 디렉터리를 설정해 파일을 컨테이너에 저장하지 않고 호스트에 바로 저장할 수 있습니다.

기본값은 rw며, read only를 설명하려면 호스트 디렉터리 뒤에 :ro를 붙이면 됩니다.

예) --volume=[]

-v <호스트 디렉터리>:<컨테이너 디렉터리>:<rw, ro>  -v /home:/home:ro

-v <호스트 파일>:<컨테이너 파일> -v /var/run/docker.sock:/var/run/docker.sock


--volumes-from=[]

-v 옵션과 비슷한데, 예를 들면 # docker run -it -v /home:/home --name volume-container ubuntu /bin/bash 옵션으로 volume-container라는 이름의 컨테이너를 생성했을 때,

volume-container 생성 한 뒤 --volumes-from 옵션을 통해 volume-test라는 이름의 컨테이너를 생성합니다.

가령 # docker run -it --volumes-from volume-container --name volume-test ubuntu

volume-test 컨테이너 내에는 /home이라는 디렉터리가 보이며, host - volume-container - volume-test가 공유(share)하는 형태로 됩니다.

root@0c8b186592de:/home# ll /home

-rw-r--r-- 1 root root    0 May 3 01:42 container1

-rw-r--r-- 1 root root    0 May 3 01:46 container2


즉, 아래와 같은 그림의 형태로 볼륨을 공유할 수 있습니다.


-w

컨테이너 안의 프로세스가 실행될 디렉터리를 정의하는 옵션 입니다.

예) --workdir="/var/www"



이처럼 도커 컨테이너는 어떤 특수한 명령을 끝마친 후 끝낼 수도 있고(expire), 백그라운드에서 계속 실행할 수 있습니다.