osulyanov
12/3/2017 - 4:55 AM

Docker Dev

Docker Dev

FROM ruby:2.4

# PostgreSQL utils
# This is required if you use SQL format for your schema (then you need psql)
ENV PG_MAJOR 9.5
RUN echo 'deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main' $PG_MAJOR > /etc/apt/sources.list.d/pgdg.list
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -

RUN apt-get update && apt-get install -y build-essential

RUN apt-get install -y libpq-dev apt-utils postgresql-client-$PG_MAJOR

# Node installation
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y nodejs

RUN mkdir -p /app
WORKDIR /app

ENV LANG C.UTF-8
ENV BUNDLE_PATH /bundle
ENV BUNDLE_BIN /bundle/bin
ENV PATH /app/bin:/bundle/bin:$PATH

RUN gem install bundler

EXPOSE 3000

ENTRYPOINT ["bundle", "exec"]

CMD ["rails", "server", "-b", "0.0.0.0"]
# Example docker-compose for a sub-service
version: '3'
services:
  # If you have another app and want to share
  # some resources
  another_app:
    build:
      # path to another app root (and Dockerfile)
      context: '../another-app'
    image: another-app:1.1
    command: bundle exec sidekiq
    environment:
      - REDIS_URL=redis://redis:6379/
    volumes:
      - ../another-app:/app
      # Here we use services and volumes defined in the main config
      - bundle:/bundle
    depends_on:
      - redis
# Example docker-compose for a typical Rails+Webpack application
version: '3'
services:
  app: &app
    build: .
    # Update version every time your Dockerfile changes
    image: my_app:1.0
    volumes:
      - .:/app
  backend: &backend
    <<: *app
    volumes:
      - .:/app
      # Persist installed gems
      - bundle:/bundle
    environment:
      - REDIS_URL=redis://redis:6379/
      - DATABASE_URL=postgres://postgres:postgres@postgres:5432
      - ES_HOST=es
      - WEBPACK_URL=http://localhost:3100
    depends_on:
      - postgres
      - es
      - redis
  web:
    <<: *backend
    command: rails server -b 0.0.0.0
    ports:
      # Container:Host
      - '3000:3000'
  sidekiq:
    <<: *backend
    command: sidekiq -C config/sidekiq.dev.yml
  runner:
    <<: *backend
    entrypoint: /bin/bash
  webpack:
    <<: *app
    entrypoint: /bin/bash
    command: -c 'npm run webpack:development -- --host 0.0.0.0'
    ports:
      - '3100:3100'
  postgres:
    image: postgres:9.5
    volumes:
      # Persist postgres data
      - postgres:/var/lib/postgresql/data
    ports:
      - 5432
  es:
    image: elasticsearch:2.2
    volumes:
      # Persist elastic data
      - es:/usr/share/elasticsearch/data
    ports:
      - 9200
      - 9300
  redis:
    image: redis:3.2-alpine
    volumes:
      # Persist Redis data
      - redis:/data
    ports:
      - 6379
volumes:
  postgres:
  es:
  redis:
  bundle:

Installation

Go to the official website and choose your OS.

MacOS

Docker for Mac has a problem is the disk IO performance (see https://github.com/docker/for-mac/issues/77).

There is a fix (https://github.com/IFSight/d4m-nfs) which makes Docker use NFS instead of OSXFS.

NOTE: MacOS High Sierra <10.3.2 is likely to have problems with NFS (see discussion).

Follow the instructions:

  1. Update docker File Sharing preferences as explained at https://github.com/IFSight/d4m-nfs (i.e. only /tmp directory should remain)

  2. Quit Docker

  3. Clone repo:

git clone https://github.com/IFSight/d4m-nfs ~/d4m-nfs
  1. Put the following configuration in ~/d4m-nfs/etc/d4m-nfs-mounts.txt:
/Users:/Users
/Volumes:/Volumes
/private:/private
  1. Run the script:
~/d4m-nfs/d4m-nfs.sh

6*) Optional Add shortcut to run d4m-nfs:

echo "#\!/usr/bin/env bash\n/Users/`whoami`/d4m-nfs/d4m-nfs.sh" > /usr/local/bin/d4m
chmod +x /usr/local/bin/d4m

And then just run d4m to start Docker.

NOTE: it requires root access to modify /etc/exports, /etc/nfs.conf, etc.

That's it.

NOTE: now you must start Docker using the same script (~/d4m-nfs/d4m-nfs.sh or d4m) in order it to work correctly.

File Watchers

D4M-NFS doesn't support FS events, but there are the workarounds for popular text editors (instructions).

Usage

Add Dockerfile to build you application and docker-compose.yml to configure services (see examples below).

Basic commands:

# build app container
docker-compose build app

# run setup script (bundle install, db:migrate, db:seed)
docker-compose run runner -c ./bin/setup

# setup frontend dependencies
docker-compose run webpack -c 'npm install'

# run web app
docker-compose up web

# run sidekiq
docker-compose up sidekiq

# run the whole app
docker-compose up web webpack sidekiq

# run specs
docker-compose run web rspec

# simply launch bash within app directory
docker-compose run runner

Usually, the runner container is always running and you're working within it (install gems, run generators, tests, etc). So, docker-compose run runner is like vagrant ssh.

Using multiple configs

Sometimes it's useful to have separate docker-compose configurations (e.g. when working with microservices). You can re-use some services (e.g. databases) by passing several configuration files:

docker-compose -f docker-compose.yml -f docker-compose-service.yml up web webpack sidekiq another_app

Useful Aliases

alias dcr='docker-compose run --rm'
alias dcu='docker-compose up'
alias dcs='docker-compose stop'
alias dstats='docker stats --format "table {{.Name}}:\t{{.MemUsage}}\t{{.CPUPerc}}"'

Webpack Dev Server Configration

Webpack config:

devServer: {
  contentBase: paths.dest,
  clientLogLevel: 'warning',
  disableHostCheck: true,
  headers: { 'Access-Control-Allow-Origin': '*' },
  inline: true,
  overlay: true,
  port: 3100,
  publicPath: '/front/',
}

Rails helper:

def webpack_bundle_tag(bundle, options = {})
  host = Rails.env.development? ? ENV['WEBPACK_URL'] : compute_asset_host
  bundle_tag = javascript_include_tag("#{host}/front/#{Webpack.entry_path(bundle)}", options)

  if bundle == 'shared' && (manifest = Webpack.manifest)
    "#{javascript_tag "window.webpackManifest = #{manifest};"}\n#{bundle_tag}".html_safe
  else
    bundle_tag
  end
end