Skip to content

Instantly share code, notes, and snippets.

@kkweon
Last active August 24, 2021 16:43
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save kkweon/2359da953f9b127b19c4146060797631 to your computer and use it in GitHub Desktop.
Save kkweon/2359da953f9b127b19c4146060797631 to your computer and use it in GitHub Desktop.

나만의 Docker Reference

Docker 타겟유저

다음 중 하나 이상 경험한 사람

빌드 문제

다른 컴퓨터에서 빌드 에러가 나는 경우

빌드툴이 발전하면서 많이 나아졌지만 그래도 다른 OS문제라던지 여전히 가끔씩 접하는 케이스이다.

해결법 Dockerfile 을 사용한다

DB사용

DB를 연결해야 되는데 내 컴퓨터를 사용하고 싶지 않은 경우

  • 웹개발을 할때 프로덕션과 동일한 환경으로 세팅해야 하지만 귀찮다
  • 이미 내 데이터베이스는 사용중이거나 혹은 귀찮다
  • 크롤링을 하면서 데이터베이스에 저장하고 싶은데 데이터베이스 설정하기가 귀찮다

    해결법 docker-compose 로 해결한다.

배포

배포 너무 귀찮다..

배포 환경에서 환경설정 등 여러가지로 설정을 잡아 주기 귀찮은 상황도 docker로 해결 할 수 있다.

해결법 docker stack deploy 를 사용한다

Scalability

서비스 scalability도 쉽게 했으면 좋겠다..

해결법 docker service scale frontend=10 한줄이면 된다.

Docker와 함께하는 개발 workflow

Docker로 개발하면 아래와 같은 프로세스로 개발을 하게 된다

  1. Dockerfile 작성
  2. docker-compose.yml 작성
  3. 개발
    • docker-compose up
  4. 배포
    • 이미지화 및 registry
    • docker stack deploy

Docker 연습 환경

http://labs.play-with-docker.com/

Dockerfile 작성

  • Dockerfile 은 docker image를 작성하는 간단한 스크립트이다
  • Dockerfile 로 image가 만들어지면 container로 실행이 가능하게 된다.
  • Virtualbox로 치면
    • image = iso파일
    • container = 실제 실행되는 가상환경
    • 물론 docker는 virtualization이 아니라 호스트의 커널을 사용해서 사실상 Native Application이다.

예시로 아래와 같은 Flask App이 있다.

from flask import Flask, render_template
import random

app = Flask(__name__)

# list of cat images
images = [
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26388-1381844103-11.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr01/15/9/anigif_enhanced-buzz-31540-1381844535-8.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26390-1381844163-18.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/10/anigif_enhanced-buzz-1376-1381846217-0.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr03/15/9/anigif_enhanced-buzz-3391-1381844336-26.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/10/anigif_enhanced-buzz-29111-1381845968-0.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr03/15/9/anigif_enhanced-buzz-3409-1381844582-13.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr02/15/9/anigif_enhanced-buzz-19667-1381844937-10.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26358-1381845043-13.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/9/anigif_enhanced-buzz-18774-1381844645-6.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr06/15/9/anigif_enhanced-buzz-25158-1381844793-0.gif",
    "http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr03/15/10/anigif_enhanced-buzz-11980-1381846269-1.gif"
]

@app.route('/')
def index():
    url = random.choice(images)
    return render_template('index.html', url=url)

if __name__ == "__main__":
    app.run(host="0.0.0.0")

프로젝트 구조는 다음과 같다.

.
├── app.py
├── requirements.txt
└── templates
    └── index.html

위 프로젝트로 Dockerfile 을 작성하면 다음과 같다.

# FROM: 베이스 이미지를 정해준다.
FROM python:3-alpine

# requirements.txt 파일 복사
COPY requirements.txt /code/
RUN pip install --no-cache-dir -r /code/requirements.txt

# app.py와 index.html 복사
COPY app.py /code/
COPY templates/index.html /code/templates/

EXPOSE 5000

WORKDIR /code

CMD ["python", "app.py"]

외울 필요는 없고 필요할때마다 Dockerfile Reference 에서 찾아보면 된다

몇가지 포인트는

FROM
베이스 이미지를 정해준다. 새로 만드는 것보다 official image 위를 이용하는게 좋고 alpine은 리눅스 종류 중 하나인데 용량이 5mb에 불과해 대부분 모든 공식 이미지들이 alpine을 지원한다.
EXPOSE
포트 5000을 열겠다는 소리이지만 실제로 아무 일도 하지 않는다. 사용자에게 포트 5000번에서 이 어플리케이션이 돌아간다고 알려주는 용도이다. 그렇지 않으면 포트 몇번을 연결해줘야 되는지 헷갈리기 때문이다. 실제로는 ENV 혹은 ARG 로 설정해준다.

이제 이걸 이미지로 빌드한다

docker build -t my_flask_app .
Sending build context to Docker daemon   7.68kB
Step 1/8 : FROM python:3-alpine
 ---> d26cf7d4701d
Step 2/8 : COPY requirements.txt /code/
 ---> Using cache
 ---> 459e0df94893
Step 3/8 : RUN pip install --no-cache-dir -r /code/requirements.txt
 ---> Using cache
 ---> 5a618308e9b8
Step 4/8 : COPY app.py /code/
 ---> Using cache
 ---> e35197a2ee91
Step 5/8 : COPY templates/index.html /code/templates/
 ---> Using cache
 ---> 734c689d69e3
Step 6/8 : EXPOSE 5000
 ---> Using cache
 ---> 0c4d7984da84
Step 7/8 : WORKDIR /code
 ---> Using cache
 ---> 89bfc8e45446
Step 8/8 : CMD python app.py
 ---> Using cache
 ---> 51fb98413e28
Successfully built 51fb98413e28
Successfully tagged my_flask_app:latest

빌드가 되었는지 확인을 한다

docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
my_flask_app        latest              51fb98413e28        20 seconds ago      98.7MB

image를 container로 실행은 다음과 같다.

docker container run --name flask --publish 8080:5000 -d my_flask_app
fee755b1f3b34bff0b8d8f676a696976cfe4b24b9555b7257428c56ab799424d

구) 커맨드인 docker run 으로 해도 되지만 신명령어는 docker <<context>> <<command>> 형태를 사용한다. 즉 여기서는 docker container run 인 것이다. 커맨드도 매번 --help 를 보면 된다.

docker container --help
Usage:	docker container COMMAND

Manage containers

Options:
      --help   Print usage

Commands:
  attach      Attach local standard input, output, and error streams to a running container
  commit      Create a new image from a container's changes
  cp          Copy files/folders between a container and the local filesystem
  create      Create a new container
  diff        Inspect changes to files or directories on a container's filesystem
  exec        Run a command in a running container
  export      Export a container's filesystem as a tar archive
  inspect     Display detailed information on one or more containers
  kill        Kill one or more running containers
  logs        Fetch the logs of a container
  ls          List containers
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  prune       Remove all stopped containers
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  run         Run a command in a new container
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop one or more running containers
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  wait        Block until one or more containers stop, then print their exit codes

Run 'docker container COMMAND --help' for more information on a command.

어쨌든 docker container run 이후 container id가 반환되었으면 성공적으로 실행된 것이다. 확인을 하면 동작중인것을 알 수 있다.

GET 127.0.0.1:8080
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 732
Server: Werkzeug/0.12.2 Python/3.6.3
Date: Sat, 07 Oct 2017 08:19:33 GMT

<html>
  <head>
    <style type="text/css">
      body {
        background: black;
        color: white;
      }
      div.container {
        max-width: 500px;
        margin: 100px auto;
        border: 20px solid white;
        padding: 10px;
        text-align: center;
      }
      h4 {
        text-transform: uppercase;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h4>Cat Gif of the day</h4>
      <img src="http://ak-hdl.buzzfed.com/static/2013-10/enhanced/webdr05/15/9/anigif_enhanced-buzz-26358-1381845043-13.gif" />
      <p><small>Courtesy: <a href="http://www.buzzfeed.com/copyranter/the-best-cat-gif-post-in-the-history-of-cat-gifs">Buzzfeed</a></small></p>
    </div>
  </body>
</html>

아래와 같이 실행중인 컨테이너를 확인할 수 있다.

docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
fee755b1f3b3        my_flask_app        "python app.py"     27 minutes ago      Up 27 minutes       0.0.0.0:8080->5000/tcp   flask

귀찮으니 force로 지운다

docker container rm -f flask

없어졌다.

docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

docker-compose.yml 작성

실제로 docker를 사용할때는 프로젝트를 시작하기전에 Dockerfiledocker-compose.yml 를 미리 만들어놓고 시작을 한다.

이제부터는 허접한 DevOps 라고 가정하고 다음과 같은 일을 해야 한다고 가정한다.

  • drupal 8.2 버전을 설치한다
  • drupal bootstrap theme을 설치한다
  • port는 8080을 이용한다.
  • database는 postgres 9.6 버전을 사용한다
  • postgres password는 qwer1234qwer 로 한다
  • 데이터는 container가 날아가도 보존하도록 한다

우선 가장 먼저 할일은 Dockerfile 을 만드는 일이다. 이 경우는 없어도 되긴 하지만 그냥 한다.

FROM drupal:8.2
RUN apt-get update                \
    && apt-get install -y git     \
    && rm -rf /var/lib/apt/lists*

WORKDIR /var/www/html/themes

RUN git clone --branch 8.x-3.x --single-branch --depth 1 https://git.drupal.org/project/bootstrap.git \
    && chown -R www-data:www-data bootstrap

WORKDIR /var/www/html

/var/www/html 이나 이런건 어떻게 알수 있는가? Dockerhub/drupal 에서 올려놓은 README 를 보면 된다.

docker-compose.yml 은 다음과 같다.

version: "3"
services:

  drupal:
    build: .
    image: custom-drupal
    depends_on:
      - db
    ports:
      - 8080:80
    volumes:
      - drupal-modules:/var/www/html/modules
      - drupal-profiles:/var/www/html/profiles
      - drupal-sites:/var/www/html/sites
      - drupal-themes:/var/www/html/themes

  db:
    image: postgres:9.6
    environment:
      POSTGRES_PASSWORD: qwer1234qwer
    volumes:
      - drupal-data:/var/lib/postgresql/data

volumes:
  drupal-data:
  drupal-modules:
  drupal-profiles:
  drupal-sites:
  drupal-themes:
  • yaml 포맷을 사용한다.
  • volumes은 실제 호스트에 볼륨을 사용해 콘테이너가 없어지더라도 데이터가 남아있게 된다.
    • 아무것도 작성하지 않으면 docker volume 을 사용한다.
  • environment 를 통해 환경변수를 설정할 수 있다.
    • 이런 secret들은 환경변수로 설정하는건 좋지 않다.
    • 실제로는 docker secret 을 사용한다.

자세한 키워드는 Docker Compose Reference 에서 확인한다.

이제 실행은 docker-compose up 을 사용한다. 먼저 help를 살펴본다.

docker-compose --help
Define and run multi-container applications with Docker.

Usage:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name (default: directory name)
  --verbose                   Show more output
  --no-ansi                   Do not print ANSI control characters
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the name specified
                              in the client certificate (for example if your docker host
                              is an IP address)
  --project-directory PATH    Specify an alternate working directory
                              (default: the path of the Compose file)

Commands:
  build              Build or rebuild services
  bundle             Generate a Docker bundle from the Compose file
  config             Validate and view the Compose file
  create             Create services
  down               Stop and remove containers, networks, images, and volumes
  events             Receive real time events from containers
  exec               Execute a command in a running container
  help               Get help on a command
  images             List images
  kill               Kill containers
  logs               View output from containers
  pause              Pause services
  port               Print the public port for a port binding
  ps                 List containers
  pull               Pull service images
  push               Push service images
  restart            Restart services
  rm                 Remove stopped containers
  run                Run a one-off command
  scale              Set number of containers for a service
  start              Start services
  stop               Stop services
  top                Display the running processes
  unpause            Unpause services
  up                 Create and start containers
  version            Show the Docker-Compose version information

이제 실행을 한다.

docker-compose up -d
docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
4d448b78b4fd        custom-drupal       "docker-php-entryp..."   19 seconds ago      Up 18 seconds       0.0.0.0:8080->80/tcp   drupal_drupal_1
4f2cf2a1722f        postgres:9.6        "docker-entrypoint..."   19 seconds ago      Up 18 seconds       5432/tcp               drupal_db_1

이제 로컬호스트:8080으로 가보면

GET 127.0.0.1:8080
HTTP/1.1 302 Found
Date: Sat, 07 Oct 2017 09:04:22 GMT
Server: Apache/2.4.10 (Debian)
X-Powered-By: PHP/7.1.5
Cache-Control: no-cache
Location: /core/install.php
Content-Length: 312
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="1;url=/core/install.php" />

        <title>Redirecting to /core/install.php</title>
    </head>
    <body>
        Redirecting to <a href="/core/install.php">/core/install.php</a>.
    </body>
</html>

drupal 설치로 연결되는 것을 볼 수 있다.

중요한 점은 db를 선택하는 시점에

  • postgreSQL 선택
  • DATABASE HOST는 docker-compose.yml 에서 지정해준 이름인 http://db 이다
  • USERNAME은 건드리지 않았기 때문에 postgres 기본값인 postgres 이다
  • PASSWORD는 qwer1234qwer 이다

실전으로 다음과 같은 architecture를 배포해야 되는 일인 경우,

https://image.ibb.co/iPKQ0b/architecture.png

docker-compose.yml 은 아래와 같다.

version: "3"
services:
  vote:
    image: dockersamples/examplevotingapp_vote:before
    depends_on:
      - redis
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
      restart_policy:
        condition: on-failure
    ports:
      - 80:80
    networks:
      - frontend

  redis:
    image: redis:3.2
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
    networks:
      - frontend

  worker:
    image: dockersamples/examplevotingapp_worker
    deploy:
      placement:
        constraints: [node.role == manager]
      replicas: 1
      labels: [APP=VOTING]
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
        window: 120s
    networks:
      - frontend
      - backend

  db:
    image: postgres:9.4
    volumes:
      - db-data:/var/lib/postgresql/data
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]
    networks:
      - backend

  result:
    image: dockersamples/examplevotingapp_result:before
    ports:
      - 5001:80
    networks:
      - backend
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
    depends_on:
      - db

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - 8080:8080
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

networks:
  frontend:
  backend:

volumes:
  db-data:

networks 설정

networks:
  frontend:
  backend:

이 부분은 docker network 로 네트워크가 저절로 생성 된후 각각의 콘테이너가 속하는 오버레이에서만 서로 통신이 가능하다.

docker network create --driver overlay frontend
docker network create --driver overlay backend

배포

실제 배포를 할 때는 docker swarm 을 이용해 여러대의 노드로 클러스터를 구성한다음

docker stack deploy -c docker-compose.yml voteapp

이전 docker-compose.yml 에서 deploy 에 들어간 옵션들이 docker stack deploy 를 할때 적용되는 옵션들인 것이다.

결론은

  • docker-compose 는 development 할 때 사용하는 명령어
  • docker stack deploy 는 실제 배포할 때 사용 되는 명령어
  • docker stack deploy 를 할 경우 Dockerfile로 빌드를 못한다
    • 배포 이전에 내 이미지를 만들어 registry 로 모든 노드에 접속할 수 있게 해준다.

registry 사용법

registry는 로컬 전용 dockerhub 라고 생각하면 된다. 스웜 클러스터에 자동으로 공유가 된다.

다음과 같이 사용한다.

docker service create --name registry -p 5000:5000 registry
docker tag my_app 127.0.0.1:5000/my_app
docker push 127.0.0.1:5000/my_app

이제 모든 노드에서 다음과 같이 내 이미지를 다운 받을 수 있다.

docker service create --name my_app 127.0.0.1:5000/my_app

secret 사용법

docker secret create psql_user psql_user.txt

혹은

echo "postgresuser" | docker secret create psql_user -

로 생성한다. (끝에 - 가 중요하다)

실제 컨테이너에서는 /run/secrets/psql_user 로 접근이 가능하다. 파일처럼 보이지만 파일이 아니라 메모리에 저장되어있다.

services:
  psql:
    image: postgres
    environment:
      POSTGRES_USER_FILE: /run/secrets/psql_user
      POSTGRES_PASSWORD_FILE: /run/secrets/psql_pass
    secrets:
      - psql_user
      - psql_pass


secrets:
  psql_user:
    file: ./psql_user.txt

  psql_pass:
    file: ./psql_pass.txt

으로 사용한다. 하지만 보안 이슈로 파일로 저장하기보다 external: true 세팅을 한 후 docker secret create 으로 생성한다.

secrets:
  psql_user:
    external: true

  psql_pass:
    external: true

docker-compose.yml 관리법

다음과 같은 파일로 관리를 한다.

docker-compose.yml
이미지만 설정되어있는 베이스 파일
docker-compose.override.yml
개발시 사용되는 config
docker-compose.prod.yml
실제 배포시 사용되는 config
docker-compose.test.yml
CI에서 사용되는 config

example

docker-compose.yml

version: '3.1'

services:

  drupal:
    image: drupal:latest

  postgres:
    image: postgres:9.6

docker-compose.override.yml

version: '3.1'

services:

  drupal:
    build: .
    ports:
      - "8080:80"
    volumes:
      - drupal-modules:/var/www/html/modules
      - drupal-profiles:/var/www/html/profiles
      - drupal-sites:/var/www/html/sites
      - ./themes:/var/www/html/themes

  postgres:
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/psql-pw
    secrets:
      - psql-pw
    volumes:
      - drupal-data:/var/lib/postgresql/data

volumes:
  drupal-data:
  drupal-modules:
  drupal-profiles:
  drupal-sites:
  drupal-themes:

secrets:
  psql-pw:
    file: psql-fake-password.txt

docker-compose.prod.yml

version: '3.1'

services:

  drupal:
    ports:
      - "80:80"
    volumes:
      - drupal-modules:/var/www/html/modules
      - drupal-profiles:/var/www/html/profiles
      - drupal-sites:/var/www/html/sites
      - drupal-themes:/var/www/html/themes

  postgres:
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/psql-pw
    secrets:
      - psql-pw
    volumes:
      - drupal-data:/var/lib/postgresql/data

volumes:
  drupal-data:
  drupal-modules:
  drupal-profiles:
  drupal-sites:
  drupal-themes:

secrets:
  psql-pw:
    external: true

docker-compose.test.yml

version: '3.1'

services:

  drupal:
    image: drupal
    build: .
    ports:
      - "80:80"

  postgres:
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/psql-pw
    secrets:
      - psql-pw
    volumes:
      - ./sample-data:/var/lib/postgresql/data
secrets:
  psql-pw:
    file: psql-fake-password.txt

Node.js good default

# if you're doing anything beyond your local machine, please pin this to a specific version at https://hub.docker.com/_/node/
FROM node:6

RUN mkdir -p /opt/app

# set our node environment, either development or production
# defaults to production, compose overrides this to development on build and run
ARG NODE_ENV=production
ENV NODE_ENV $NODE_ENV

# default to port 80 for node, and 5858 or 9229 for debug
ARG PORT=80
ENV PORT $PORT
EXPOSE $PORT 5858 9229

# check every 30s to ensure this service returns HTTP 200
HEALTHCHECK CMD curl -fs http://localhost:$PORT/healthz || exit 1

# install dependencies first, in a different location for easier app bind mounting for local development
WORKDIR /opt
COPY package.json /opt
RUN npm install && npm cache clean --force
ENV PATH /opt/node_modules/.bin:$PATH

# copy in our source code last, as it changes the most
WORKDIR /opt/app
COPY . /opt/app

# if you want to use npm start instead, then use `docker run --init in production`
# so that signals are passed properly. Note the code in index.js is needed to catch Docker signals
# using node here is still more graceful stopping then npm with --init afaik
# I still can't come up with a good production way to run with npm and graceful shutdown
CMD [ "node", "index.js" ]

phoenix

FROM ubuntu:latest

# Elixir requires UTF-8
RUN apt-get update && apt-get upgrade -y && apt-get install locales && locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

# update and install software
RUN apt-get install -y curl wget git make sudo \
    # download and install Erlang apt repo package
    && wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb \
    && dpkg -i erlang-solutions_1.0_all.deb \
    && apt-get update \
    && rm erlang-solutions_1.0_all.deb \
    # For some reason, installing Elixir tries to remove this file
    # and if it doesn't exist, Elixir won't install. So, we create it.
    # Thanks Daniel Berkompas for this tip.
    # http://blog.danielberkompas.com
    && touch /etc/init.d/couchdb \
    # install latest elixir package
    && apt-get install -y elixir erlang-dev erlang-dialyzer erlang-parsetools \
    # clean up after ourselves
    && apt-get clean


# install the Phoenix Mix archive
RUN mix archive.install --force https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
RUN mix local.hex --force \
    && mix local.rebar --force

# install Node.js (>= 8.0.0) and NPM in order to satisfy brunch.io dependencies
# See http://www.phoenixframework.org/docs/installation#section-node-js-5-0-0-
RUN curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - && sudo apt-get install -y nodejs

WORKDIR /code
COPY . /code
RUN mix deps.get && npm install && npm cache clean --force

EXPOSE 80 4000 4001
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment