Docker

매번 반복되는 지루한 일상(환경 구축)은 가라. Docker.

작년 여름에 Docker를 전해 들었다.
‘거 참 괜찮네.’란 생각이 들었지만 직접 써본 건 바닥에 낙엽이 깔리고 나서였다.
그래도 이제는 손에 좀 익었기에 Docker에 대해 몇 자 적어본다.

소프트웨어 개발을 대략적인 과정은 다음과 같다.
환경 구축(개발) -> 개발 -> 환경 구축(테스트) -> 테스트 -> 환경 구축(배포) -> 배포
Ax -> B -> Ay -> C -> Az -> D

환경 구축이라는 작업이 반복적으로 이루어진다.
환경을 구축하는 것은 중요하지만, 어지간히 귀찮아서 여러 번 다시 하기 싫은 일이다.
그래서 예로부터 이런 환경 설정을 쉽게 도와주는 도구들이 개발자를 도왔다.
윈도즈 사용자라면 지금 환경을 통째로 구워서 어디서나 같은 작업 환경을 금세 되돌릴 수 있는(예를 들면 게임과 애드온이라든가...) 노턴 고스트라는 도구를 익히 들어봤을 것이다. 웹 개발자라면 APM(Apache + Php + Mysql) 환경 구축을 돕는 LAMP, WAMP, MAMP라는 녀석들과 가깝게 지냈을 것이다. 자바와 루비, 파이썬, 노드JS 등도 인기가 많아서 호스팅 업체에서는 이들을 위한 환경을 미리 구축하고는 OO호스팅, XX호스팅이라며 상품을 만들어 팔기도 한다. 그렇지만 수많은 개발 언어(http://en.wikipedia.org/wiki/List_of_programming_languages)중에 별로 인기가 없는 언어로 자신만의 환경을 구축하려면? 그리고 이런 환경을 다른 머신에 또 구축하려면? 우공(愚公)이 산을 옮기듯 삽질을 아주 여러 번 해야 한다. 이건 너무 불공평하다. 지금은 민주주의 시대인데, 소수 언어 사용자도 편리할 권리가 있잖은가? 그래서 Vagrant(https://www.vagrantup.com/)라는 멋진 녀석이 나왔다. Vagrant를 이용해서 환경을 한 번 구축해 두면, 다른 머신에서 언제나 꺼내 쓸 수 있다. 다만 여전히 문제가 있었으니 가상머신에 종속되기 때문에 덩치가 크고, 자원을 많이 잡아먹는다. 그러니 넉넉지 못한 환경에서 vagrant를 돌리면 만족스러운 성능을 기대하기 어렵다. 이에 반해 Docker는 LXC(http://en.wikipedia.org/wiki/LXC)를 통해 kernel cgroup 과 namespacing을 이용하니 훨씬 가볍다. 다만 이는 리눅스 시스템에서 사용할 때 이야기고, 애플 OS X나 마이크로소프트 Windows에서는 boot2docker(https://github.com/boot2docker/boot2docker)등의 도움을 받아야 한다. Vagrant + Docker도 썩 괜찮은 조합이라고 한다.

Docker를 써보자.

설치

https://docs.docker.com/installation/ 문서를 참조한다.

Arch 리눅스

sudo pacman -S docker
sudo systemctl enable docker

Arch 리눅스에서 sudo 없이 docker를 사용하고 싶다면 아래 커맨드를 실행한다.

https://wiki.archlinux.org/index.php/Docker

gpasswd -a <user> docker

Ubuntu 리눅스

문서(https://docs.docker.com/installation/ubuntulinux/) 에 따르면
Ubuntu-maintained Package와 Docker-maintained Package가 있다.
Ubuntu-maintained Package를 설치하면 버전이 낮아서 Dockerfile에서 설정한 ENV를 WORKDIR에서 인식하지 못하는 문제가 발생한다.

Ubuntu 리눅스에서 sudo 없이 docker를 사용하고 싶다면 아래 커맨드를 실행한다.

groupadd docker
gpasswd -a <user> docker
service docker.io restart

그리고 로그아웃하고 다시 로그인한다.(재부팅)

설치 오류 해결

Your kernel does not support cgroup swap limit

/etc/default/grub
GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

$ sudo update-grub
그리고 재부팅한다.


error: cannot run ssh: No such file or directory fatal: unable to fork

경로 문제이다. 경로(path)에 다음을 추가한다.
/nvm/{nodeJS version x.xx.xx}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin


Docker를 익히는데 도움이 되는 문서

http://blog.nacyot.com/articles/2014-01-27-easy-deploy-with-docker/
http://forum.docker.co.kr/t/docker-docker-howto/68
https://www.docker.com/
http://dockerbook.com/
https://coderwall.com/p/2es5jw/docker-cheat-sheet-with-examples
https://github.com/wsargent/docker-cheat-sheet

자주 쓰는 Docker 명령어

Dockerfile 빌드(build)

Dockerfile이 있는 디렉토리에서 실행한다. --no-cache는 캐쉬를 사용하지 않는 옵션이다.
docker build -t "<tag : user/repository>" --no-cache .
사용 예)
docker build -t "dorajistyle/flask-canjs-i18n-boilerplate" --no-cache .

entry point 덮어쓰기

주로 이미지나 컨테이너에서 bash 쉘을 실행하기 위해 쓴다. -it는 STDIN을 허용하는 pty를 여는 옵션이다.
docker exec -it <container-id> <command>
docker run -it <image-id> <command>
docker run -it --entrypoint <command> <image-id>

cannot execute binary file 오류가 뜨면 아래 커멘드를 쓴다.

docker run -it --entrypoint <command> <image-id> -s

이미지 실행

--publish, -p 옵션은 컨테이너의 포트를 호스트포트로 넘겨준다. 6060:8080이면 호스트에서 6060포트로 접속하면 컨테이너의 8080포트로 연결된다.
--name 옵션은 컨테이너에 이름을 붙여준다.
--rm 옵션은 실행된 컨테이너가 중지되면 컨테이너를 자동으로 지워준다.
-d 옵션은 데몬으로 실행한다.
docker run --publish <host-port>:<container-port> --name <container-name> --rm <image-name>
docker run -dp 6060:3001 <host-port>:<container-port> <image-name>

사용 예)
docker run --p 6060:5050 --name fcib --rm dorajistyle/flask-canjs-i18n-boilerplate
docker run -dp 6060:3001 my-image

실행중인 컨테이너 중지

docker stop <container-id>

이미지에 태그 달기

docker tag "user/tag"

docker.io에 이미지 등록하기

docker허브에 등록한뒤에 로그인하고 push하면 된다. image-tag는 / 형식으로 쓴다.
docker login
docker push <image-tag>

쓰지 않는 컨테이너와 이미지 지우기.

docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q) && docker images --no-trunc| grep none | awk '{print $3}' | xargs -r docker rmi

docker ps의 -a옵션은 모든 컨테이너를 보여주는 것이고, -q옵션은 컨테이너의 다른 정보 없이 id만 보여주라는 것이다.
위 커멘드는 3 부분으로 나뉜다.
docker stop $(docker ps -a -q) // 모든 컨테이너 중지
docker rm $(docker ps -a -q) // 모든 컨테이너 삭제
docker images --no-trunc| grep none | awk '{print $3}' | xargs -r docker rmi // 알수 없는 이름의 모든 이미지를 지운다.

컨테이너 IP주소 받아오기

docker inspect <container-id>
docker inspect -f '{{ .NetworkSettings.IPAddress }}' <container-id>
docker inspect <container-id> | grep IPAddress | cut -d '"' -f 4
docker run <image-id> ip -4 -o addr show eth0


Dockerfile 잘 쓰기

  • 캐쉬를 잘 활용한다.
  • 태그를 쓴다.
  • base이미지로 작은 것을 쓴다. (ubuntu 보다는 debian)
  • 공통된 작업은 묶어서 한다. (예 : RUN apt-get install A B C D E F)
  • 용도에 맞게 base이미지를 만들어서 활용한다. (RoR용 base, 파이썬용 base, Golang용 base등)

Docker 빌드 자동화

Docker허브를 이용하면 github.com이나 bitbucket.org의 저장소가 변경될때마다 자동으로 빌드되도록 할 수 있다.
http://docs.docker.com/userguide/dockerrepos/

Docker Remote API의 웹 인터페이스

https://github.com/crosbymichael/dockerui

CI(Continuous Integration) 도구

https://github.com/drone/drone
https://github.com/Strider-CD/strider

Docker를 쓰면서 궁금했던 점이 두 가지 있다. 하나는 'Dockerfile에서 Private Repository를 clone하려면 어떻게 해야 할까?' 이고, 또 다른 하나는 '확장은 어떻게 해야 할까?'이다.

Private 저장소를 Dockerfile에서 불러오기.

스텍오버플로우 질문을 찾아보니 아래 처럼 하면 된다고 한다.

# private key를 복사한다.
ADD id_rsa /root/.ssh/id_rsa
# known_hosts 파일을 만든다.
RUN touch /root/.ssh/known_hosts
# bitbuckets키(다른 저장소를 이용한다면, 다른 저장소의 키)를 known_hosts에 추가한다.
RUN ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts

위 방법은 Dockerfile과 id_rsa가 같은 경로에 있어야만 한다.
단일 Dockerfile만으로는 방법이 없을까?
RUN command안에 키를 넣어 버리는 것이다. Dockerfile이 유출되면 Private Key도 노출된다는 단점이 있지만, 이는 위의 방법도 마찬가지다. Dockerfile과 Private key 파일이 함께 있을테니까. 그래도 보안을 위해서 Dockerfile에 사용할 키는 저장소에서 read권한만 가진 배포용 권한만 주는것이 좋다.
아래처럼 넣어주면 된다.
RUN mkdir -p /root/.ssh && str="-----BEGIN RSA PRIVATE KEY-----blahblahblah-----END RSA PRIVATE KEY-----" && echo | sed "i$str" > /root/.ssh/id_rsa && \
echo "ssh-rsa blapublahpublah" > /root/.ssh/id_rsa.pub && \
chmod 600 /root/.ssh/id_rsa && \
printf "Host bitbucket.org\n\tStrictHostKeyChecking no\n" >> /root/.ssh/config


Docker를 이용한다면 확장은 어떻게 해야 할까?

문서(http://www.centurylinklabs.com/auto-loadbalancing-with-fig-haproxy-and-serf/)에서
http://www.fig.sh/http://www.haproxy.org/ 그리고 https://www.serfdom.io/를 이용한 로드벨런싱을 설명하고 있다.
그리고 문서(http://stackoverflow.com/questions/18285212/how-to-scale-docker-containers-in-production)에 따르면, 확장을 돕는 다양한 서비스가 나와있으니, 구미에 맞는 서비스를 이용하면 되겠다.
아마존의 Elastic Beanstalk에서도 Docker를 지원하므로, AWS에 익숙하다면 이를 이용하면 편리하다. 이 내용은 추후에 다시 다루겠다.


확장과 로드밸런싱을 돕는 도구들


참고자료



by


Tags : , , , ,

  • 재미있게 읽으셨나요?
    방랑자의 이야기.
    월풍도원에선 기부를 받습니다.