Docker 로 Heroku 에 배포하기
- 며칠 전 5월 5일, 유명 PaaS 인 Heroku 가 Docker 로 배포하는 도구를 발표했다. [1]
- 요즘 회사에서, 사내 개발자들을 위한 PaaS (Heroku 또는 AWS Beanstalk 와 비슷) 를 만들고 있고, Docker 에도 관심이 많기 때문에 한 번 살펴봤다.
- (내가 안 하면 아무도 안 해줄것 같기도 하고)
- 살펴본 결론은:
- 아무 Docker 이미지나 Heroku 에 올려서 돌릴 수 있는건 아니다.
- Heroku 가 자동으로 만들어 주는 Dockerfile 을 사용하거나.
FROM heroku/cedar:14
이미지를 사용해야 Heroku에 올릴 수 있다. (/app
아래에 파일들이 들어가는 조건도 있고)
ONBUILD
로 코드를 이미지에 포함시키기 때문에, 소스코드를 수정하면heroku docker:start
를 해줘야 한다.- 개발용으로 volume mount 를 하는 등의 다른 방법이 분명 있겠지?
- 장점은 (내 생각에는)
- 앱에 있는 바이너리 의존성 (e.g. imagemagick 같은것?) 을 buildpack 방식에서는 복잡하게 해결해야 했는데
- Docker 방식에서는 단순히 Dockerfile 에 의존성을 추가하고 PATH 를 추가하면 된다.
- 아무 Docker 이미지나 Heroku 에 올려서 돌릴 수 있는건 아니다.
heroku-docker 설치
$ heroku plugins:install heroku-docker
샘플 앱 clone
$ git clone https://github.com/heroku/node-js-getting-started.git
$ cd node-js-getting-started
docker:init - 현재 디렉터리의 언어에 따라 Dockerfile 생성
$ heroku docker:init
Wrote Dockerfile (node)
$ cat Dockerfile
FROM heroku/cedar:14
RUN useradd -d /app -m app
USER app
WORKDIR /app
ENV HOME /app
ENV NODE_ENGINE 0.12.2
ENV PORT 3000
RUN mkdir -p /app/heroku/node
RUN curl -s https://s3pository.heroku.com/node/v$NODE_ENGINE/node-v$NODE_ENGINE-linux-x64.tar.gz | tar --strip-components=1 -xz -C /app/heroku/node
ENV PATH /app/heroku/node/bin:$PATH
RUN mkdir -p /app/.profile.d
RUN echo "export PATH=\"/app/heroku/node/bin:/app/bin:/app/node_modules/.bin:\$PATH\"" > /app/.profile.d/nodejs.sh
RUN echo "cd /app/src" >> /app/.profile.d/nodejs.sh
EXPOSE 3000
ONBUILD RUN mkdir -p /app/src
ONBUILD COPY . /app/src
ONBUILD WORKDIR /app/src
ONBUILD RUN npm install
docker:init 내부 구현
- buildpack 과 동일한 방식으로 언어를 detect 한다.
- e.g.
package.json
가 존재하면 Node.js 로 detect 함. [2]
- e.g.
- platforms.detect(dir); [3]
- fs.writeFileSync(dockerfile, contents); [4]
- 현재 지원되는 언어는 node, python, ruby, scala [5]
heroku docker:init --template minimal
으로 최소한의 Dockerfile 을 만들 수 있음.- 여기서 생성되는 Dockerfile 이 [6] 이것.
heroku/cedar:14 도커 이미지 내부 구현
- 이미지는 Docker Registry 에 올라가 있음. [7]
- Cedar-14 는 ubuntu 14.04 LTS 를 기반으로 한 스택이고, 설치되는 ubuntu package 는 여기에. [8]
Dockerfile 은 아래와 같이 단순함. [9]
FROM ubuntu-debootstrap:14.04
COPY ./bin/cedar-14.sh /tmp/build.sh
RUN LC_ALL=C DEBIAN_FRONTEND=noninteractive /tmp/build.sh \
&& rm -rf /var/lib/apt/lists/*
- ubuntu-debootstrap 의 Docker Registry [10]
- 즉
heroku/cedar:14
는 용량이 작은 최소한의 ubuntu docker image 에 - cedar-14.sh 를 실행시킨다.
cedar-14.sh
의 코드 [13]
docker:start - 로컬에 컨테이너를 시작
$ heroku docker:start
building image...
Sending build context to Docker daemon 285.2 kB
Sending build context to Docker daemon
Step 0 : FROM heroku/cedar:14
Pulling repository heroku/cedar
66766a8c5395: Download complete
c40496787f7c: Download complete
f90b0e99841d: Download complete
ac0bf528748f: Download complete
Status: Downloaded newer image for heroku/cedar:14
---> 66766a8c5395
... docker 이미지 빌드 생략 ...
Successfully built d3e547511a82
starting container...
web process will be available at http://localhost:3000/
Node app is running on port 3000
docker:start 내부 구현.
- start.js [14]
- docker.js#runImage() [18]
run -w /app/src -p 3000:3000 --rm -it ${mountComponent} ${envArgComponent} ${imageId} sh -c "${command}" || true
[19]
컨테이너에 명령 실행
$ heroku docker:exec npm install --save --no-bin-links cool-ascii-faces
$ heroku docker:exec ls node_modules
docker:exec 내부 구현
docker:release - 배포
아래의 명령으로 배포 가능하다.
$ heroku docker:release
- 이것은 heroku 가 기존에 사용하던 Buildpack 기반의 빌드시스템을 사용하지 않음.
- 대신, Docker 이미지에 앱의 코드와 의존성을 포함하고, 로컬의 Docker 이미지를 직접 배포함.
docker:release 내부 구현
- Heroku 로 배포.
- 컨테이너를 올려서 /app 디렉터리를 압축한다.
- 이 압축파일에는 language 의 runtime 과 앱의 소스코드를 포함함.
release.js [22]
docker run -d ${imageId} tar cfvz /tmp/slug.tgz -C / --exclude=.git --exclude=.heroku ./app
docker wait ${containerId}
docker cp ${containerId}:/tmp/slug.tgz ${slugPath}
docker rm -f ${containerId}
slug.tgz