도커 사용에 유용한

유틸리티와 팁

신명수 anarcher@gmail.com

2014-11-30

TOC

  • 도커 설치
  • 이미지 빌드
  • 컨테이너 실행

도커 설치

그냥 도커 설치이라면 각 배포판의 패캐징을 사용하면 되지만...

여러 서버에 간단하게 설치하면 무언가 도구가 필요하다

 

서버 설정 관리 도구 (server configuration management tool)

 

  • chef
  • ansible
  • puppet
  • saltstack
  • ...

Ansible을 이용한 Docker 설치

Ansible 특징 

 

  1. 모듈을 만들것이 아니라면 서버 구성에 yaml만 알면 된다. 
  2. ansible 모듈은 std in/out(JSON)이 되는 어떤 프로그램이라도 된다. 
  3. chef-server community edition같은 것은 없고 사야하는 ansible tower가 있다. T_T 
  4. http://deview.kr/2014/session?seq=15 

      (ansible 이해와 활용)

Ansible Docker 설치

$ansible-playbook -i hosts docker.yml
PLAY [cloudatcost] ************************************************************ 

GATHERING FACTS *************************************************************** 
ok: [a]
...
TASK: [Add docker apt repository key] ***************************************** 
ok: [a]
...
TASK: [Add docker apt repository] ********************************************* 
ok: [a]
...
TASK: [Install latest docker via apt] ***************************************** 
changed: [a]
...
TASK: [Start docker on boot] ************************************************** 
ok: [a]
...
PLAY RECAP ******************************************************************** 
a                          : ok=6    changed=1    unreachable=0    failed=0   
b                          : ok=6    changed=0    unreachable=0    failed=0   
c                          : ok=6    changed=0    unreachable=0    failed=0   

실행

docker.yml

tasks:
    - include: debian.yml
      when: "ansible_os_family == 'Debian'"
    
    - name: Docker group
      group: name=docker state=present
    
    - name: Start docker on boot
      service: name=docker state=started enabled=yes

debian.yml


- name: Add docker apt repository key
  apt_key: url=https://get.docker.io/gpg state=present

- name: Add docker apt repository
  apt_repository: repo='deb http://get.docker.io/ubuntu docker main' state=present

- name: Install latest docker via apt
  apt: pkg=docker.io state=latest update_cache=yes

이미지 빌드 

도커에서 이미지를 만드는 방법은 두가지 

 

- 컨테이너에서 docker commit 하기 

 

- Dockerfile로 이미지를 빌드하기 (추천)

Dockerfile

# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER  anarcher "anarcher@gmail.com"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hello container' > /usr/share/nginx/html/index.html
EXPOSE 80
$ sudo docker build -t="static_web" .

...
Step 2 : RUN apt-get update
 ---> Using cache
 ---> f1e96933a1b3
Step 3 : RUN apt-get install -y nginx
 ---> Using cache
 ---> f35da92bc69a
Step 4 : RUN echo 'Hello container2' >/usr/share/nginx/html/index.html
 ---> Running in 687148b94d31
 ---> 0f1b6efbb4ac
Removing intermediate container 687148b94d31
Step 5 : EXPOSE 80
 ---> Running in 07ece0129259
 ---> ded36d58c990
Removing intermediate container 07ece0129259
Successfully built ded36d58c990

Dockerfile 

위에서 아래로 빌드

캐쉬가 무효화되면 그 시점부터 모든 캐쉬를 무효화된다 

기본적으로 자주 안 변하고 오래 걸리는 것부터 

자주 변하고 짧은 시간을 사용하는 것은 (환경변수 설정 등) 을 나중에 

Step 3 : RUN apt-get install -y nginx
 ---> Using cache
 ---> f35da92bc69a
Step 4 : RUN echo 'Hello container2' >/usr/share/nginx/html/index.html
 ---> Running in 687148b94d31
 ---> 0f1b6efbb4ac
Removing intermediate container 687148b94d31
Step 5 : EXPOSE 80
 ---> Running in 07ece0129259
 ---> ded36d58c990
Removing intermediate container 07ece0129259
Successfully built ded36d58c990

Dockerfile

$docker history static_web
IMAGE               CREATED             CREATED BY                                      SIZE
134455b6ba22        5 minutes ago       /bin/sh -c #(nop) EXPOSE map[80/tcp:{}]         0 B
7814e0f4e451        5 minutes ago       /bin/sh -c echo 'Hello container' >/usr/share   16 B
f35da92bc69a        5 minutes ago       /bin/sh -c apt-get install -y nginx             18.46 MB
f1e96933a1b3        6 minutes ago       /bin/sh -c apt-get update                       20.65 MB
f2fb5e64006b        7 minutes ago       /bin/sh -c #(nop) MAINTAINER anarcher "anarch   0 B
86ce37374f40        4 days ago          /bin/sh -c #(nop) CMD [/bin/bash]               0 B
dc07507cef42        4 days ago          /bin/sh -c apt-get update && apt-get dist-upg   0 B
78e82ee876a2        4 days ago          /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.895 kB
3f45ca85fedc        4 days ago          /bin/sh -c rm -rf /var/lib/apt/lists/*          0 B
61cb619d86bc        4 days ago          /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   194.8 kB
5bc37dc2dfba        4 days ago          /bin/sh -c #(nop) ADD file:d11cc4a4310c270539   192.5 MB
511136ea3c5a        17 months ago                                                       0 B

build --no-cache 

모든 캐쉬를 사용하지 않고 다시 빌드하려면 --no-cache 옵션 사용

 

하지만 빌드시에 시간이 많이 걸리는 부분(예: apt-get update)등을 선택적으로 캐쉬 사용하고 싶다면...

$ docker build --no-cache -t="static_web" .

선택적인 캐쉬 사용

# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER  anarcher "anarcher@gmail.com"
ENV APT_UPDATED_AT  2014-11-30
RUN apt-get update
RUN apt-get install -y nginx=1.4.6-1ubuntu3.1
RUN echo 'Hello container' \
>/usr/share/nginx/html/index.html
EXPOSE 80

ENV APT_UPDATED_AT  2014-11-30

전체 패캐지 업데이트 하고 싶다면 날짜 변경 

RUN apt-get install -y nginx=1.4.6-1ubuntu3.1

패캐지 버젼 명시하기 

--no-cache 옵션을 사용하지 않고,

Base Image 

여러 애플리케이션에서 쓰는 공통의 이미지 

이미 언어별,플랫폼별 만들어서 공개된 이미지들이 많다 

Build 실패했을 때

Step 3 : RUN apt-get update
---> Running in 85130977028d
---> 997485f46ec4
Step 4 : RUN apt-get install -y ngin
---> Running in ffca16d58fd8
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package ngin
2014/06/04 18:41:11 The command [/bin/sh -c apt-get install -y 
ngin] returned a non-zero code: 100
$ docker run -t -i --rm 997485f46ec4 /bin/bash
dcge12e59fe8:/# #DEBUG 

사설 레지스트리 구성

https://hub.docker.com : docker inc에서 운영하는 레지스트리 서비스 

 

레지스트리(Registry) 자체는  오픈소스 프로젝트 
 https://github.com/docker/docker-registry

많은 경우 Nginx + registry 으로 구성 

docker container으로 사용가능 

docker run \
         -e SETTINGS_FLAVOR=s3 \
         -e AWS_BUCKET=mybucket \
         -e STORAGE_PATH=/registry \
         -e AWS_KEY=myawskey \
         -e AWS_SECRET=myawssecret \
         -p 5000:5000 \
         registry

컨테이너(Container) 실행

설정(Config)

환경변수  

docker run \
         -e SETTINGS_FLAVOR=s3 \
         -e AWS_BUCKET=mybucket \
         -e STORAGE_PATH=/registry \
         -e AWS_KEY=myawskey \
         -e AWS_SECRET=myawssecret \
         -p 5000:5000 \
         registry

설정 파일 

sudo docker run -p 5000:5000 -v /home/me/myfolder:/registry-conf 
  -e DOCKER_REGISTRY_CONFIG=/registry-conf/mysuperconfig.yml registry

컨테이너 재시작 정책

도커 1.2에 추가된 기능  --restart

 

--restart always 

--restart on-failure:5   #exit code 가 0이 아닌 경우 

--restart no  #default

docker run -d --restart always --name myredis redis
docker run -d --restart no --name myredis redis #default
docker run -d --restart on-failure:5 --name myredis redis

컨테이너 접속

docker exec 명령으로 실행중인 컨테이너에 커맨드를 실행
(>= 1.3) 

docker exec -it anarcher/fluentd /bin/bash 
#Output

aufs vs btrfs

file system corruption with devicemapper #7229

devicemapper와 aufs 둘다 발생하는 듯 (btrfs은 아님)

 

btrfs가 좀 더 잘 동작하는 것 같기도 (coreos은 btrfs 사용) 

btrfs는 파티션을 따로 설정해야 함

$ mkfs.btrfs /dev/xvdb 
$ mount /dev/xvdb /mnt 
$ mkdir -p /mnt/docker 
$ ln -sf /mnt/docker /var/lib/docker 
$ sed -i -e '/DOCKER_OPTS/s/.*/DOCKER_OPTS="-s=btrfs"/' /etc/default/docker
$ restart docker

데이터 볼륨 컨테이너

Data only container , Data volume container

# Dockerfile
FROM busybox
VOLUME /var/lib/mysql
CMD /bin/sh
$docker build -t mysql_datastore
$docker run -i -t -name mysql_data mysql_datastore

$docker run -d -volumes-from mysql_data me/awesome_mysql_image

애플리케이션 컨테이너를 삭제하고 생성하더라도 

데이터는 다른 컨테이너에서 유지된다 

데이터 볼륨 백업 

$ docker run -d --rm \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /your/host/backup/dir:/backup \
    --volumes-from backup1 \
    --volumes-from backup2 \
    -e AWS_ACCESS_KEY_ID=yourkeyid \
    -e AWS_SECRET_ACCESS_KEY=youraccesskey \
    -e AWS_DEFAULT_REGION=us-east-1 \
    -e S3_PATH=s3_bucketname_or_path \
    -e OLDFILE_PRESERVE_DAYS=14 \
    seapy/dock-trucker

컨테이너 로깅

도커(Docker)는 프로세스(Process)의 표준출력(stdout/err) 저장한다 

docker logs stdout/err을 출력 


docker logs -f --tail=0 fe24d # tail -f file.ext

stdout/err을 호스트 파일시스템에 저장해주기 때문에, 계속 쌓이기만 한다. 

       /var/lib/docker/containers/[containerID]/[containerID]-json.log

컨테이너 로깅

도커(Docker) 데몬(Daemon)에서 로깅 핸들링하도록 제안이 있는 상태

 Proposal: Logging drivers #7195 

docker -d --logging none
docker -d --logging default --logging-opt truncation=20mb
   --logging-opt rotation=1gb

호스트에 logrotate 설정

#logrotate.conf
/var/lib/docker/containers/*/*-json.log {
    daily
    compress
    rotate 5 
    size 10k
    copytruncate
}

중앙 집중식 로깅(centralized logging)

 호스트 머신에 쌓이는 컨테이너 로그를 로그 콜렉터로 수집

#fluentd
<source>
  type tail
  path /var/lib/docker/containers/*/*-json.log
  pos_file /var/log/fluentd-docker.pos
  time_format %Y-%m-%dT%H:%M:%S
  tag docker.*
  format json
</source>
<match docker.var.lib.docker.containers.*.*.log>
  type record_reformer
  container_id ${tag_parts[5]}
  tag docker.all
</match>
<match docker.all>
  type file
  path /var/log/docker/*.log
  format json
</match>

애플리케이션 로깅

Dockerized = stdout에만 쓴다?

많은 애플리케이션들이  파일에 로깅한다 예:/var/log/nginx/log

 

볼륨(volume)을 사용

docker run --name some-nginx -v <log-dir>:/var/log/nginx -d nginx

컨테이너 모니터링

cAdvisor  : 구글에서 만든 컨테이너 모니터링

 

  1. 리소스 사용을 모니터링 (CPU,Memory,IO) 
  2. InfluxDB 으로 매트릭을 전송하는 기능 
  3. 간단한 웹 대쉬보드와 API을 제공 
 docker run \                                                                                                                    
>   --volume=/:/rootfs:ro \
>   --volume=/var/run:/var/run:rw \
>   --volume=/sys:/sys:ro \
>   --volume=/var/lib/docker/:/var/lib/docker:ro \
>   --publish=8080:8080 \
>   --detach=true \
>   --name=cadvisor \
>   google/cadvisor:latest

컨테이너 모니터링

pharos : Distributed Docker Monitoring Tool

도커 네트워킹

컨테이너 링킹 (Container linking)

  1. 정적(static)이다 
  2. 부모-자식 관계이다 (단방향)
  3. 호스트안의 컨테이너간에만 가능하다
  4. 컨테이너를 삭제하고 새로운 이미지로 컨테이너를 실행하면연결(Linking)은 깨진다 
$ docker run -d --name db training/postgres
564cf1592267eccb013361cc14a0c413f2d5acd4f5f0ccc6a9473ca9faf9a4c1
$ docker run -d -P --name web --link db:db training/webapp python app.py
8aef00d59404f1ceb2cce74c75b738f0c7df12c6a31d27f1a7708404a4055eb6
$docker exec -it web env | grep DB
DB_PORT=tcp://172.17.0.42:5432
DB_PORT_5432_TCP=tcp://172.17.0.42:5432
DB_PORT_5432_TCP_ADDR=172.17.0.42
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_PROTO=tcp
DB_NAME=/web/db
DB_ENV_PG_VERSION=9.3

컨테이너 링킹

$ docker run -d --name db training/postgres
564cf1592267eccb013361cc14a0c413f2d5acd4f5f0ccc6a9473ca9faf9a4c1
$ docker run -d -P --name web --link db:db training/webapp python app.py
8aef00d59404f1ceb2cce74c75b738f0c7df12c6a31d27f1a7708404a4055eb6

$ docker exec -it db cat /etc/hosts
172.17.0.38	564cf1592267
$ docker exec -it web cat /etc/hosts | grep db
172.17.0.38	db

$ docker restart db

$ docker exec -it db cat /etc/hosts
172.17.0.40	564cf1592267
$ docker exec -it web cat /etc/hosts | grep db
172.17.0.40	db

$ docker rm -f db 

$ docker run -d --name db training/postgres                                                                                      
db20aae037686ddc0efda400cceedf04c1e471e530b5dcfd5491a55bf9e99779

$ docker exec -it db cat /etc/hosts
172.17.0.41	db20aae03768
$ docker exec -it web cat /etc/hosts
172.17.0.40	db

컨테이너 네트웍 포트 

도커 컨테이너의 네트웍 포트를 호스트 포트에 바인딩할수 있다. 

$ docker run -d -p 5000:5000 -p 80 --name web training/webapp python app.py 
3fe7e5cd9224f8ae56d73e8963943439d963d900f17d74d9d934b464d0d28ed0
$ docker port web
5000/tcp -> 0.0.0.0:5000
80/tcp -> 0.0.0.0:49164

고정 포트를 사용 할 때

새로운 이미지로 컨테이너를 실행하려면 기존의 컨테이너를

멈추고 시작해야 한다 

프록시(Proxy) 사용하기 

Automated Nginx Reverse Proxy for Docker

docker-gen 으로 컨테이너의 변화가 있을때 nginx 설정을 바꾸고 nginx reload을 실행

docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy

등록하고자 하는 컨테이너에 환경변수 VIRTUAL_HOST을 추가 해서 실행 

docker run -e VIRTUAL_HOST=foo.bar.com  ...
docker-gen -only-exposed -watch -notify "/etc/init.d/nginx reload" 
  templates/nginx.tmpl /etc/nginx/sites-enabled/default

Service discovery w/ Docker

synapse : AirBnB에 만든  Service discovery system  

개발에서 도커 사용 

fig.sh : Fast, isolated development environments using Docker.

  1. 기본적으로 한 호스트에서 있는 여러 컨테이너들을 yaml기반의 설정파일로 관리하는 도구.
  2. 특히 개발환경에서 여러 레이어(DB,APIServer,CacheServer 등등)를 하나의 호스트에서 사용할때 편리하다
web:
  build: .
  command: python app.py
  links:
   - db
  ports:
   - "8000:8000"
db:
  image: postgres

Fig을 프로덕션에서?

  1. DOCKER_HOST=tcp://[ip]:[port] ; fig [command]
  2. #527 : 각 서비스마다 다른 docker_host을 설정해서 여러 호스트에 띠울수 있도록 한 패치.
    1. 몇주째 머지되지 않았음
    2. 도커 핵데이 2014 우승작 :  Enable Fig to deploy to multiple Docker servers #608 

 

redis:
    image: redis
    docker_host: tcp://192.168.10.74:5555
redislocal:
    image: redis

감사합니다!

docker-2014-11-30-springsprout

By Myoungsoo Shin

docker-2014-11-30-springsprout

  • 5,177