도커 사용 후기

출처 : 

http://blog.naver.com/ktw5724/220899692550


2017년 1월 1일 일요일

도커로 배포 시스템을 구성하고 운영해 온지 이제 3달쯤 되어간다.
아직 개선해야 할 부분들이 많이 남아있긴 하지만 일단 지금까지의 과정들을 돌이켜보자.

"도커라는게 있다"라고 들어본 것은 몇년전이었는데 그냥 그런게 있나보다 하고 잊고 살고 있었다.
그러다가 개발중인 프로젝트의 배포 시스템을 구축해야 했고, 개발자 친구가 도커 얘기를 꺼내서 도커의 사용을 고민하게 되었다.
처음에는 일단 배포 시스템을 심플하게 구성하기 위해 도커를 사용하지 않으려고 했다.
장고로 리셋하기 전에 사용했던 프레임웍이 PHP Codeigniter였는데, 이때는 배포 방식이 "매우 심플"했다.
서버의 crontab에서 매분 git pull을 하는 방식이었던 것이다.
브랜치가 master와 dev 두개였고, 운영서버는 master를 향해 매분 git pull을 하고, 테스트서버는 dev를 향해 매분 git pull을 했다. 
변경한 코드를 dev에 push하면 테스트서버에 반영되고, dev에서 master로 merge하면 실서버에 배포되는 식이다.
다른 개발자 동료가 이 아이디어를 냈을때, 처음에는 뭔가 문제가 있을거라 생각하고 문제점을 찾으려고 애썼다.
그런데, 생각보다 별다른 문제가 없어서 당시 시스템에 적용했고, 실제로 별다른 문제가 없었다.
그래서 장고 배포 시스템도 이처럼 심플하게 배포할 수 있는 방법을 먼저 고민해 봤다.

그런데 심플한 배포 시스템의 구축에 몇가지 장애물들이 있었다.
1) 환경변수의 분리
aws secret key와 같은 값들은 보안 이슈로 소스 코드 내에 담지 않기로 했고, 운영/테스트/개발 환경에 따라 달라지는 값들도 소스코드 내에서 분기처리 하지 않고 환경변수로 받아서 서버 기동시 settings에 저장하기로 했다. 그래서 배포후 서버 기동 전에 해당 환경변수를 셋팅하기 위해 스크립트를 실행하는 과정이 추가되어야 했다.
2) 장고 + Gunicorn + Nginx 구조에서 서버 재시작 문제
장고의 내장 서버(python manage.py runserver)를 사용할 때는 소스코드가 변경되면 바로 서버가 재시작 되지만, 내장 서버는 개발환경용이지 운영환경용은 아니다. 그래서 장고에 Gunicorn과 Nginx를 붙였는데, 이 구조에서는 배포 후 서버 재기동이 필요했다.
3) db migrate 와 collectstatic
장고는 개발자가 서비스를 개발함에 있어서 필요한 것들을 최대한 자신의 컨텍스트 안에서 해결할 수 있도록 하고자 한다. db table을 생성하거나 column을 변경하거나 하는 것을 프로젝트의 models 안에서 정의한후 manage.py를 실행하여 db에 적용하고(python manage.py makemigrations 실행하여 마이그레이션 파일을 생성하고, python manage.py migrate 실행하여 db에 적용), 개발하면서 프로젝트에 추가된 이미지 파일들을, 프로젝트에서 지정한 운영환경용 파일서버(aws s3 등)에 반영하는 것도 manage.py에서  해준다.(python manage.py collectstatic 실행)
따라서, 배포시 서버 재시작 전에 db가 변경된 경우 migrate를 해줘야 하고 정적 파일이 변경/추가된 경우 collectstatic을 해줘야 한다.
4) pip install
라이브러리가 추가된 경우, 배포시 서버 재시작 전에 pip install 을 해줘야 한다.

이러한 문제들 때문에 당연히도 완전 심플했던 이전의 배포 방식은 고수할 수가 없었다.
위의 문제들을 고려해 보니 결국 배포 서버와 같은 배포의 주체가 필요하고 원격으로 대상 서버들이 필요한 동작들을 실행하도록 해야한다는 결론이 나왔다. 그래서 일단은 aws ec2 인스턴스에 우리 서비스가 돌아가는 운영환경을 만들고, 배포 스크립트를 작성한 후, 나의 개발 PC 또는 배포서버에서 원격으로 해당 스크립트를 실행하는 방식으로 구성해 보기로 했다.

그런데, 운영서버를 셋팅하는 과정이 생각보다 간단하지 않았다.
소스를 가져오기 위해 git을 설치해야했고, 환경변수를 셋팅하는 스크립트 파일을 소스와는 별도로 배포해줘야 했고, 정상적으로 서버가 실행되도록 하기 위해 추가적으로 리눅스 모듈들을 설치해줘야 했고, gunicorn과 nginx도 설치 및 설정을 해줘야 했다. 이 과정은 시행착오도 상당히 많았고 그래서 시간도 꽤 오래 걸렸다. 그래서 이 과정을 기록해 두기 위해 정리하다보니 문득 도커가 떠올랐다. 이 과정을 다시 되풀이해야 하는 상황이 되었을 때 또 일일이 이 과정들을 반복해야할까? OS 버전이 바뀌거나 하는 등의 변화가 생겼을때 잘 된다는 보장이 있을까? 이러한 생각을 하다보니, "이제 도커를 학습할 이유가 생겼다" 싶었다.

도커 학습을 위해 먼저 위 링크에 오픈된 책을 읽었다.
아주 대강 요약해 보면,
1) 이미지 생성과 배포에 특화된 기술이다.
2) 기존의 가상화 방식은 게스트OS와 호스트OS 사이에 하이퍼바이저와 같은 중간 단계가 있기 때문에 성능이 떨어지는데, 도커는 리눅스의 컨테이너 기술을 이용하여 성능 하락이 거의 없다.
3) git처럼 이미지의 버전 관리가 가능하고, 이미지 생성시 통째로 생성하는 것이 아니라 이전 이미지를 참조하여 바뀐 부분만 생성하기 때문에 기존의 이미지 생성 방식보다 효율적이다.
4) 이미지는 Dockerfile을 기반으로 생성되고, 이미지를 실행한 상태를 컨테이너라 한다.
5) 공개된 이미지 저장소로 DockerHub가 있고, 자체적으로 저장소를 구축할 수 있다. DockerHub는 GitHub과 마찬가지로 공개 저장소일때는 무료, 개인 저장소로 사용하려면 유료이다.

위의 책을 읽고 나서 대강 도커가 뭐구나 하는 감은 잡혔지만, github과 aws autoscaling을 사용하려는 상황에서 어떻게 구축해야 할지는 여전히 막막한 부분이 있었다. 그래서 책을 한권 사서 읽었다.

위 책을 통해서 추가로 알게된 내용들을 대강 요약해 보면,
1) 도커도 클라이언트/서버 모델이다.
2) 다양한 오케스트레이션 도구들이 있다.
3) 저장볼륨, CPU 등에 대해 다양하게 제한 및 설정을 할 수 있다.

이 책을 통해서 해답을 얻었다 싶었다. 도커의 클라이언트/서버 모델을 이용해서 autoscaling되는(확장 가능한) 시스템을 구현할 수 있도록 해주는 오케스트레이션 도구들이 있었다. 그 중에서 눈에 띈 것이 도커 스웜과 aws ecs(ec2 container service)였는데, 아무래도 현재의 상황이 aws ec2를 사용하고 aws autoscaling으로 서버 확장을 구현하려는 상황이기 때문에 aws ecs를 사용하는 것이 최선이라고 생각했다.

aws ecs 화면에 들어가서 튜토리얼을 시작하면, 도커 이미지 저장소 및 클러스터와 태스크 정의를 생성한다. 클러스터는 생성되는 ec2 인스턴스들의 그룹을 의미하고, 태스크 정의는 ec2의 생성 등을 컨트롤하는 작업 지시서와 같은 것이다. 뭔가 굉장히 복잡한데, 그래도 시키는대로 다하면 뭔가 꿀 같은 결과가 기다리고 있을 것이라 기대하면서 한 세트를 구성했다. 그리고 태스크를 실행하니, 오~ ec2가 생성되고 서버도 잘 뜬다. 그런데, 소스코드를 수정하고 재배포를 하려고 하니 어떻게 하는지를 확인할 수 없었다. 문서를 꽤나 꼼꼼히 읽어봤는데도 해당 내용을 찾지 못했고, 검색을 해보면 ecs 관련 문서가 별로 없어서 도움이 되지 않았다. 새로 빌드한 이미지를 push하면 aws에서 그것을 hook하여 배포를 다시 해줄 것이라 예상했지만, aws는 그렇게 자비롭지 않았다. 그래서 일단은 어떻게든 해보려고 현재 실행중인 태스크를 중지 시켜보니 자동으로 다시 태스크를 실행하면서 배포가 다시 되기는 했다. 하지만 이게 정상적인 방식은 아닌 듯 했고, 답답한 마음에 페이스북의 AWS 한국 사용자 그룹에도 관련 문의를 올려봤는데, 답변들이 신통치 않았다. 지금 이게 맞게 하는 방식이라는 것이었는데 신뢰가 가지 않았다. 태스크를 중지하면 실행중이던 ec2가 종료 되고, 새로 시작되는 태스크에 의해서 ec2가 다시 생성되었다. 그리고 autoscaling과 연계도 제대로 되지 않았다. 태스크는 ec2 한대를 생성하도록 되어 있는데 autoscaling에 의해 ec2가 2대가 된 상태에서 태스크를 종료하면 실행중이던 2대가 종료되고 1대가 새로 생성되었다. 이건 아니다 싶었다. 

도커 스웜을 써볼까도 생각해 봤지만 결국은 추가되는 개념이 최소화 되도록 하는게 좋겠다고 생각했다. 지금은 도커도 제대로 모르는데 그것의 확장 개념들까지 한방에 이해하려 하는 건 아무래도 부담스러웠다. 그래서 생각한 구조가 현재 운영중인 배포 구조이다.
크게 보면 매우 심플하고 이미 잘 알려진 방식이다.
1) 배포서버에서 배포할 도커 이미지를 생성한다.
2) 생성한 도커 이미지를 저장소에 push 한다.
3) 배포 대상 서버에서 도커 이미지를 저장소로부터 pull 한다.
4) 기존 컨테이너를 stop 하고 해당 이미지로 컨테이너를 run 한다.

그런데 구체적으로 쪼개 보면 풀어야할 문제들이 꽤 있었다.
1) 운영/테스트/개발 환경의 구분은 어떻게 할까?
 => 도커 이미지 자체는 환경과 독립적이고, 컨테이너를 실행할때 환경 구분값을 넘겨서 환경 변수 적용 스크립트를 실행한다.
2) 빌드된 이미지들은 어떻게 구분할까? 어느 시점에 빌드된 이미지인지 어떤 식으로 확인할 수 있을까?
 => 빌드할 도커 이미지의 태그를 github의 commit hash값으로 넣는다.
3) 도커 이미지 저장소는 어떻게 할까? DockerHub에서 비공개 저장소 사용하려면 유료인데..
 => aws ecs에서 제공하는 저장소를 사용한다.
4) auto scaling group에 있는 ec2 인스턴스들의 IP를 어떻게 실시간으로 알 수 있을까?
 => aws ec2 describe-instances 명령을 실행하여 autoscaling group의 인스턴스 정보들을 가져와서 사용한다.
5) pip install, migrate, collectstatic 은 매번 배포때마다 해야되는걸까? 시간이 너무 오래 걸리는데..
 => pip install은 라이브러리가 추가되었을때만 실행하면 되고, migrate는 DB 모델이 변경되었을때, collectstatic은 이미지/css/js 등이 변경 및 추가되었을때 실행하면 된다. 즉, 각각은 실행될 타이밍이 다르므로 배포시 선택하여 실행할 수 있도록 하면 된다.
6) 이미지를 생성해 보니, 서버 라이브러리 설치, 파이썬 라이브러리 설치, 소스코드 최신화 및 이미지에 복사 등을 하는데 시간이 너무 오래 걸린다.
 => 도커 이미지를 서버와 언어 설정만 된 이미지, 환경변수 적용 및 라이브러리 설치하는 이미지, 소스코드 적용하는 이미지로 구분하여 빌드한다. 서버 라이브러리 또는 환경변수 또는 파이썬 라이브러리가 변경된 경우가 아니라면 세번째 이미지만 빌드하면 된다.
7) rollback은 어떻게 할까?
 => 현재 컨테이너를 종료하고 이전 이미지로 컨테이너를 실행하면 된다.

이제 작업했던 과정을 좀 더 구체적으로 정리해 보자.

1) DockerHub에 python2.7.12, django, nginx를 셋팅한 이미지를 저장한다.
python, django, nginx 셋팅은 우리 회사의 서비스와는 독립적인 부분이기 때문에 개인 저장소에 올릴 필요가 없다고 판단하여 DockerHub 공개저장소에 저장했고, Dockerfile의 내용은 아래와 같다.

FROM ubuntu:latest

RUN apt-get update
RUN apt-get install -y apt-utils python2.7 python-pip python-dev build-essential libpq-dev libncurses5-dev libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk nginx git supervisor vim

2) 위의 이미지로부터 nginx 설정, 파이썬 라이브러리 설치, 환경변수 적용 및 배포를 위한 스크립트를 이미지로 카피하는 등의 과정을 통해 베이스 이미지를 빌드하여 aws ecs의 저장소에 저장했고, Dockerfile의 내용은 아래와 같다.

FROM tedkim/python27-django-nginx:latest

# nginx settings
RUN rm /etc/nginx/sites-available/default && \
    rm /etc/nginx/sites-enabled/default
COPY [my nginx.conf path] /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/[my nginx.conf name] /etc/nginx/sites-enabled/

# supervisor settings
RUN mkdir -p /var/log/supervisor
COPY [my supervisord.conf path] /etc/supervisor/conf.d/

# log settings
COPY [my log_files.yml path] /etc/
COPY [my remote_syslog path] /usr/local/bin/

# make directory and shell files copy
RUN mkdir -p [my source code path]
COPY [환경변수 적용하는 script path] [my source code path]
COPY [배포시 실행되는 script path] [my source code path]
COPY [배치작업 실행시 실행되는 script path] [my source code path]

# install dependencies
COPY requirements.txt [my source code path]
RUN pip install -r [my source code path]/requirements.txt

3) 위의 베이스 이미지로부터 소스코드 카피 및 배포 스크립트 실행을 하는 최종 이미지를 빌드하여 aws ecs의 저장소에 저장했고, Dockerfile의 내용은 아래와 같다.

FROM [aws ecs repository url]/[base image name]:[tag name]

# source code copy
COPY . [my source code path]

# nginx and gunicorn start by supervisor
EXPOSE 80
CMD /bin/bash [배포시 실행되는 script path]

4) 배포시 실행되는 script
# 도커 이미지 안에 카피된 script로서, 배포시 도커 컨테이너가 시작될때 호출된다.
# supervisord가 gunicorn 및 nginx를 실행시켜준다.

#!/bin/bash
remote_syslog
source [my source code path]/[환경변수 적용하는 script]
supervisord

5) 환경변수 적용하는 script
# 도커 이미지 안에 카피된 script로서, 배포시 도커 컨테이너에서 서버를 구동시키기 전에 호출된다.
# 내용은 대강 아래와 같은 형태이다.

#!/bin/bash

if [[ "$RUN_TYPE" == "" || "$1" != "" ]]; then
RUN_TYPE="$1"
fi

# RUN_TYPE: dev / test / prod
if [[ "$RUN_TYPE" == "dev" ]]; then
    export XXX=XXX
    ...
elif [[ "$RUN_TYPE" == "test" ]]; then
    export XXX=XXX
    ...
else
    export XXX=XXX
    ...
fi

export XXX=XXX
...

6) 배치작업 실행시 실행되는 script
# 배치작업은 배포서버 ec2 인스턴스의 crontab 에서 도커 컨테이너의 장고 command를 호출하는 방식을 사용한다.
docker exec [도커 이미지명] /bin/bash [my source code path]/[배치작업 실행시 실행되는 script] [장고 command]

# 배치작업 실행시 실행되는 script의 내용은 아래와 같다.
#!/bin/bash
cd [my source code path]
source [환경변수 적용하는 script] [RUN_TYPE]
python manage.py $1

7) 배포 명령 script
# 운영서버에 있는 script로서, 배포서버로부터 배포지시를 받는다. 내용은 아래와 같다.
#!/bin/bash
aws ecr get-login --region [리전명] | sh
docker tag [도커 이미지명:latest] [도커 이미지명:old]
docker pull [도커 이미지명:latest]
docker rm $(docker stop $(docker ps | grep '80->80' | grep -o '[0-9a-z]*' | head -n1))
docker run -d -p 80:80 -e RUN_TYPE=prod [도커 이미지명:latest]

8) 롤백 명령 script
# 운영서버에 있는 script로서, 배포서버로부터 롤백지시를 받는다. 내용은 아래와 같다.
#!/bin/bash
docker rm $(docker stop $(docker ps | grep '80->80' | grep -o '[0-9a-z]*' | head -n1))
docker tag [도커 이미지명:old] [도커 이미지명:latest]
docker run -d -p 80:80 -e RUN_TYPE=prod [도커 이미지명:latest]

9) 배포서버에서 배포시 작업 순서
9-1) 도커 이미지에 카피되는 설정 및 스크립트 파일이 변경되거나, 장고 프로젝트의 requirements.txt가 변경된 경우, 위 2)번의 Dockerfile로 베이스 이미지를 build하여 저장소에 push한다.
9-2) 위 3)번의 Dockerfile로 이미지를 build하여 저장소에 push한다.
9-3) RUN_TYPE에 따라 환경변수를 적용한다.
9-4) 정적 파일들이 추가 및 변경된 경우, python manage.py collectstatic 을 실행한다.
9-5) DB 모델 최신화를 위해, python manage.py migrate 를 실행한다.
9-6) 운영서버들의 배포 명령 script를 원격으로 실행한다.

도커를 사용하면서 "우와 도커 대박!" 했던 사례도 있었고, 반대로 상당히 아쉬웠던 사례도 있었다.

장점 사례 몇가지,

1) 롤백이 무지 쉽다. 위의 배포 명령 script와 롤백 명령 script를 보면, 현재 도커 이미지로 실행시킨 컨테이너를 내리고 이전 이미지로 컨테이너를 실행하면 된다. 물론, DB 모델이 변경된 경우는 상황이 다르긴 하다.

2) 로컬 개발환경 및 테스트 환경에 DB를 도커로 구성할 수 있다. DockerHub에서 적당한 DB 이미지를 찾아서, 로컬에서 docker pull 하여 사용해 보니 전혀 문제가 없었다. 그리고 상황에 따라 여러개의 도커 컨테이너를 start&stop 하여 사용할 수 있는데, 이게 상당히 유용하다. 여러개의 브랜치로 나눠서 개발하고 있는 경우 DB 스키마가 다를 수 있는데, 각각에 대응되는 DB 컨테이너를 docker start 하면 되는 것이다. 그리고 운영 DB에서 데이터를 주기적으로 백업하여 개발해야 하는 경우와 개발환경용의 특별한(?) 데이터를 남겨놔야 하는 경우가 공존하는 경우에도 유용하다.

3) 배포 자체는 도커 이미지를 컨테이너로 실행하는 과정으로 추상화 되고, 이미지를 어떻게 빌드하느냐는 독립적인 과정이기 때문에 이를 다양하게 활용할 수 있다. 특정 브랜치를 테스트용 도메인이 적용된 테스트 환경에 배포하거나, 아예 다른 서비스를 임시로 배포하는 것도 어렵지 않게 할 수 있다. 

4) 배포작업을 배포 서버 및 로컬 개발환경 등, 도커를 설치할 수 있는 곳이라면 어디서든 할 수 있다. 배포 서버는 정해진 배포 작업을 수행하고, 로컬에서는 위의 3)번에서 언급한 특수한 환경의 배포를 하는데에 활용하면 유용하다.

단점 사례 몇가지,

1) 빌드 시간이 다소 오래 걸린다. 이미지를 확장하는 관계로 쪼개고, 운영환경에 필요 없는 정적 파일들을 제외하는 등의 최적화를 했지만 그래도 상대적으로 도커를 사용하지 않았을 때보다는 빌드 시간이 오래 걸리는 듯 했다.

2) 이미지가 누적되면서 배포 서버의 용량이 문제가 되어 배포 서버 ec2 인스턴스의 사양을 t2 micro에서 t2 medium으로 올렸고, 주기적으로 오래된 이미지를 삭제해 주고 있다. 물론 도커가 이미지를 버전별로 통째로 저장하는게 아니라 변경된 레이어들을 저장하는 식으로 최적화했다고는 하나, 그래도 기본적으로 이미지 하나가 용량이 크고, git처럼 소스코드 레벨에서 관리하는 것이 아니라 Dockerfile에서의 실행문 단위로 변경된 내용들이 레이어가 되기 때문에, 도커를 사용하지 않았을 때보다 상대적으로 저장공간에 대한 부담이 있다.

3) 어쨌거나 도커 컨테이너는 가상환경에 준한다. 새 이미지를 빌드하여 컨테이너로 실행하면 기본적으로는 mac address가 바뀐다. 물론 컨테이너 실행할 때 mac address를 지정해 줄 수 있지만 이것도 auto scaling 등을 생각해보면 사용하기 상당히 부담스럽다. 이러한 특성 때문에 한가지 불편했던 점은, 로그 통합 수집 서비스로 PaperTrail을 사용하고 있는데, 배포를 할때마다 시스템 개수가 늘어났고 papertrail에서 무료로 허용하는 system 수가 제한되어 있었기 때문에 어느 순간부터 로그가 쌓이지 않는 문제가 발생했었다. 이것은 왠지 좋은 해결책이 있을 것 같기는 하지만 어쨌거나 현재로서는 불편한 부분이다.(이제 얼른 찾아보고 개선해 봐야겠다.)

[출처] 도커 사용 후기|작성자 김태우




====================

출처 : https://www.facebook.com/groups/korea.docker.user.group/


Kimsehwa 김세화님이 김태우님의 게시물을 공유했습니다.

@김태우 님
공유해주신 글 잘 읽었습니다. 
제가 도커로 레일즈환경 구축할때 고민했던 내용이랑 비슷해서 뭔가 기뻤습니다 : )

저도 부족하지만 조금 다른 구성을 했기때문에 내용을 공유합니다.

다른점.

1. aws ecs를 이용한 오토스켈링
문제가 있으셨던거 같은데
저희는 ecs로 오토스켈링 잘쓰고 있습니다..(tokyo region입니다.)

2. 빌드&배포 
단점 사례중 빌드속도 문제와 관련있는데

저희는 development환경에서 소스쪽 변경에 의한 빌드 횟수가 많아
소스 빌드와 인프라 빌드를 나눠버렸습니다.
Django는 어떨지 모르겠는데 rails에서 도커 빌드시 매번 bundle install 실행 하는건 어마어마 하게 시간이 걸려서..

그래서 레일즈 소스만 바뀌었을때는 capistrano로 소스만 바꿔치기하는 디플로이 형태를 취했습니다.
플로우는 이하와 같습니다.
capistrano로 소스 갱신 -> bundle install(cache) -> precompile(s3) ->db migrate(매번) -> unicornherder로 SIGNAL보내서 재기동

3. 환경변수 관리
환경변수를 소스에 두는것이 세큐리티상 안좋아서 aws s3에 통합관리하고 도커 기동시 이하와 같은 순으로 실행해서 환경변수를 적용했습니다.

s3에
env_unicorn_development
env_unicorn_staging
env_unicorn_production
환경별 환경변수 일람을 업로드


ecs task definition에서 RAILS_SUB_ENV라는 환경변수에다 개발환경을 정의

컨테너 실행시 aws s3 cp s3://xx/env_unicorn_$RAILS_SUB_ENV /etc/profiled.d/xx -> bash -lc "supervisord unicorn기동”

단점 사례 몇가지 피드백

1. 빌드 속도 느린문제 -> 도커다이어트를 한다. 배포서버 성능을 올린다. or 소스빌드/인프라 빌드를 나눈다.
2. 배포서버 용량문제 -> 배포서버 역할을 circleCI가 하고 있기때문에 용량이슈는 아직 없음. 
3. 로그관리 
PaperTrail은 안써봐서 잘모르겠습니다만
저희는 오토스켈링 환경에서 로그관리는 대수로 과금되는 형식보단 용량 과금 형식이 나을거 같아서 Logentries라는놈을 써서 로그통합 관리하고 있습니다.(로그내용을 호스트인스턴스랑 볼륨시켜놓고 호스트인스턴스에 에이전트 설치해서 로그수집)

그 외에도 logDriver를 이용해서 로그관리를 해주는법이 있습니다.

Dockerfile에서 수집할 로그를 이하와같이 /dev/stdout으로 날림
RUN ln -sf /dev/stdout /var/log/xx.log

task definition에서 logdriver를 지정해줘서
awslogs나td-agent드라이버가 있었습니다.

단점은 표준출력으로 나오는 로그를 전부 줍기때문에 쓸데없는 로그가 끼는것과
CloudWatchLogs로 로그수집하는거 굉장히 비싸네요..용량이 많으실때는 잘 고려해서 선택할 필요가 있을거 같습니다.

지금 다시 구축한다면 td-agent로 로그 수집 -> s3에보관 ->athena분석 환경으로 갈거 같습니다. : )


조휘철 bundle install 문제는 소스 리포지토리를 ADD하기전에 ADD Gemfile을 bundle install 전에 하시면 캐싱이 되는데 혹시 시도해 보셨는지요
Kimsehwa 김세화 네 : ) 처음엔 그렇게 했습니다.(staging,production환경은 아직도 그렇게 하구요)
development환경에서는 이하와 같은 상황이어서 source/infra 빌드를 나눴습니다
1. Gemfile이 하루에도 몇번이고 바뀜 
2.circleCI를 배포서버로 사용해서 docker build의 캐쉬가 안먹힘
3.개발자분들이 빠르게 git push한 결과를 빠르게 반영해시켜돌라는 요건이있었음
4.개발자분들이 capistrano에 적응되 계심

이건 좀 무논리인데 아무리 cache처리된다해도 똑같은 작업을 반복시키는게 썩 내키지 않더군요..
아직 구현은 안했는데 생각한거로는

후보1.

circleCI나jenkins에서 git hook으로 gemfile,gemfile.lock 파일 갱신을 캐치

circleCI에서 bundle install후 결과를 패키지화해서 s3에 업로드
컨테이너는 기동시 s3에 있는 최신의 bundle install package을 취득 

후보2.

aws efs를 이용해서 nfs로 관리.
성능이슈는 검증해봐야겠지만 NFS캐쉬기능을 이용하면 커버가능할지도
사례는 많이 없지만 있긴 하더군요..