Skip to content

Instantly share code, notes, and snippets.

@hopsoft
Last active May 17, 2023 19:58
Show Gist options
  • Save hopsoft/c27da1a9fda405169994a004957597b4 to your computer and use it in GitHub Desktop.
Save hopsoft/c27da1a9fda405169994a004957597b4 to your computer and use it in GitHub Desktop.
Dockerize your Rails app

Dockerize your Rails app

Dependencies

  • Bash
  • Docker

Quick Start

  1. Update Postgres and Redis connection configurations

    # config/database.yml
    host: postgres
    port: 5432
    username: postgres
    password: password
    # config/initializers/sidekiq.rb
    Sidekiq.configure_server do |config|
      config.redis = {url: "redis://redis_queue:6379/1"}
    end
    
    Sidekiq.configure_client do |config|
      config.redis = {url: "redis://redis_queue:6379/1"}
    end
    # config/environments/development.rb
    config.cache_store = :redis_cache_store, {
      url: "redis://redis_cache:6379/1"
    }
  2. Create redis configuration files

    Use the example files provided by Redis. https://raw.githubusercontent.com/redis/redis/6.0/redis.conf

    • config/redis-cache.conf - setup for LRU cache

      maxmemory 128mb
      maxmemory-policy allkeys-lru
      
    • config/redis-queue.conf

  3. Run the app and all it's services

    bin/docker/up

    Note: It's possible that some services may not start on first boot. Simply run bin/docker/restart if anything fails to start.

  4. Develop on the host OS using your preferred editor and tools

Containers

This setup will create the following Docker containers for your Rails application.

  • postgres - the primary database
  • redis_cache - the application cache
  • redis_queue - the background job queue
  • web - the rails web server
  • worker - the rails background job runner
  • webpack - the dev server that serves webpack assets
  • shell - a container dedicated to running scripts (bash commands, rails commands, etc...)

Shared Volumes

The docker containers mount the following volumes on the host. This ensures that data is preserved even when containers are destroyed.

  • .docker_volumes/postgres
  • .docker_volumes/redis_cache
  • .docker_volumes/redis_queue

Binstubs

  • bin/docker/up - starts the entire containerized environment

  • bin/docker/down - stops all services and removes containers, networks, volumes, and images

  • bin/docker/start - starts stopped containers

    bin/docker/start
    bin/docker/start web
    bin/docker/start webpack
    bin/docker/start worker
  • bin/docker/stop - stops containers without removing them

    bin/docker/stop
    bin/docker/stop web
    bin/docker/stop webpack
    bin/docker/stop worker
  • bin/docker/restart - restarts containers

    bin/docker/restart
    bin/docker/restart web
    bin/docker/restart webpack
    bin/docker/restart worker
  • bin/docker/attach - attach to container, useful for debugging with pry and byebug

    bin/docker/attach web
    bin/docker/attach webpack
    bin/docker/attach worker
    <CTRL-P><CTRL-Q>
  • bin/docker/exec - executes a command inside a container

    bin/docker/exec bash
  • bin/docker/tail - tail logs in a container

    bin/docker/tail web
    bin/docker/tail webpack
    bin/docker/tail worker
  • bin/docker/rails - executes a rails command inside a container

    bin/docker/rails c
    bin/docker/rails db:migrate
    bin/docker/rails credentials:edit    

Debugging

  1. Add a breakpoint to the project

    binding.pry
  2. Attach to the appropriate container to debug

    bin/docker/attach web
  3. Detach from the container when finished

    <CTRL-P><CTRL-Q>
#!/usr/bin/env bash
# These binstubs should be saved as individual files under bin/docker/
# bin/docker/up
mkdir -p .docker_volumes/postgres
mkdir -p .docker_volumes/redis_cache
mkdir -p .docker_volumes/redis_queue
mkdir -p .docker_volumes/bundle
docker-compose up -d $1
# bin/docker/down
docker-compose down $1
# bin/docker/start
docker-compose start $1
# bin/docker/stop
docker-compose stop $1
# bin/docker/restart
docker-compose restart $1
# bin/docker/attach
project=$(basename $(pwd))
docker attach $project-$1
# bin/docker/exec
project=$(basename $(pwd))
docker exec -it $project-shell $1 $2 $3 $4 $5
# bin/docker/tail
project=$(basename $(pwd))
docker logs -f $project-$1
# bin/docker/rails
bin/docker/exec bin/rails $1 $2 $3 $4 $5
version: "3.9"
networks:
main:
services:
postgres:
image: postgres:13
container_name: YOUR_PROJECT_NAME-postgres
restart: unless-stopped
environment:
POSTGRES_PASSWORD: password
networks:
- main
expose:
- 5432
volumes:
- ./log:/var/log:delegated
- ./.docker_volumes/postgres:/var/lib/postgresql/data:delegated
healthcheck:
test: pg_isready -U postgres
interval: 10s
timeout: 1s
retries: 10
redis_cache:
image: redis
container_name: YOUR_PROJECT_NAME-redis-cache
restart: unless-stopped
networks:
- main
expose:
- 6379
volumes:
- ./log:/var/log:delegated
- ./.docker_volumes/redis_cache:/data:delegated
- ./config/redis-cache.conf:/usr/local/etc/redis/redis.conf:cached
healthcheck:
test: redis-cli ping
interval: 10s
timeout: 1s
retries: 10
redis_queue:
image: redis
container_name: YOUR_PROJECT_NAME-redis-queue
restart: unless-stopped
networks:
- main
expose:
- 6379
volumes:
- ./log:/var/log:delegated
- ./.docker_volumes/redis_queue:/data:delegated
- ./config/redis-queue.conf:/usr/local/etc/redis/redis.conf:cached
healthcheck:
test: redis-cli ping
interval: 10s
timeout: 1s
retries: 10
shell:
build: .
image: YOUR_PROJECT_NAME
container_name: YOUR_PROJECT_NAME-shell
working_dir: /YOUR_PROJECT_NAME
command: /bin/bash -c "rm -f tmp/shell-ready.txt && yarn && bundle && bin/rails db:create db:migrate && touch tmp/shell-ready.txt && tail -f /dev/null"
environment:
BUNDLE_PATH: /bundle
EDITOR: /usr/bin/vim
networks:
- main
volumes:
- ./:/YOUR_PROJECT_NAME:cached
- ./.docker_volumes/bundle:/bundle:delegated
depends_on:
postgres:
condition: service_healthy
redis_cache:
condition: service_healthy
redis_queue:
condition: service_healthy
healthcheck:
test: test -f tmp/shell-ready.txt
interval: 30s
timeout: 1s
retries: 40
webpack:
image: YOUR_PROJECT_NAME
container_name: YOUR_PROJECT_NAME-webpack
working_dir: /YOUR_PROJECT_NAME
command: /bin/bash -c "bin/webpack-dev-server"
environment:
BUNDLE_PATH: /bundle
EDITOR: /usr/bin/vim
WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
networks:
- main
volumes:
- ./:/YOUR_PROJECT_NAME:delegated
- ./.docker_volumes/bundle:/bundle:cached
ports:
- 3035:3035
depends_on:
shell:
condition: service_healthy
healthcheck:
test: curl -I http://localhost:3035
interval: 20s
timeout: 1s
retries: 10
web:
image: YOUR_PROJECT_NAME
container_name: YOUR_PROJECT_NAME-web
working_dir: /YOUR_PROJECT_NAME
command: /bin/bash -c "rm -f tmp/pids/server.pid && bin/rails s --binding=0.0.0.0 --port=3000"
tty: true
stdin_open: true
environment:
BUNDLE_PATH: /bundle
EDITOR: /usr/bin/vim
WEBPACKER_DEV_SERVER_HOST: webpack
networks:
- main
volumes:
- ./:/YOUR_PROJECT_NAME:cached
- ./.docker_volumes/bundle:/bundle:delegated
ports:
- 3000:3000
depends_on:
webpack:
condition: service_healthy
healthcheck:
test: curl -I http://localhost:3000
interval: 20s
timeout: 1s
retries: 10
worker:
image: YOUR_PROJECT_NAME
container_name: YOUR_PROJECT_NAME-worker
working_dir: /YOUR_PROJECT_NAME
command: /bin/bash -c "bundle exec sidekiq -C config/sidekiq.yml"
tty: true
stdin_open: true
environment:
BUNDLE_PATH: /bundle
EDITOR: /usr/bin/vim
networks:
- main
volumes:
- ./:/YOUR_PROJECT_NAME:cached
- ./.docker_volumes/bundle:/bundle:delegated
depends_on:
shell:
condition: service_healthy
FROM ruby:3.0-alpine
RUN apk add --no-cache --update \
ack \
bash \
build-base \
curl \
git \
htop \
less \
libsass \
linux-headers \
nodejs \
npm \
postgresql-client \
postgresql-dev \
python2 \
redis \
tzdata \
vim \
yarn \
zsh
RUN gem install bundler
CMD tail -f /dev/null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment