-
-
Save natevick/96c22b18b6cb31fa458fafba32fa000f to your computer and use it in GitHub Desktop.
default: &default | |
adapter: postgresql | |
encoding: unicode | |
pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %> | |
username: <%= ENV.fetch("DATABASE_USER", 'postgres') %> | |
host: <%= ENV.fetch("DATABASE_HOST", 'localhost') %> | |
port: 5432 | |
development: | |
<<: *default | |
database: docker_development | |
test: | |
<<: *default | |
database: docker_test | |
production: | |
<<: *default | |
database: docker_production | |
username: docker | |
password: <%= ENV['DOCKER_DATABASE_PASSWORD'] %> |
version: '3.7' | |
services: | |
rails: | |
build: | |
context: ./docker/ruby | |
args: | |
- RUBY_VERSION=2.6.5 | |
- BUNDLE_JOBS=15 | |
- BUNDLE_RETRY=2 | |
- NODE_VERSION=12 | |
- INSTALL_PG_CLIENT=true | |
- UID=500 | |
- GID=500 | |
environment: | |
- DATABASE_USER=postgres | |
- DATABASE_HOST=postgres | |
command: bundle exec rails server -p 3000 -b '0.0.0.0' | |
entrypoint: docker/ruby/entrypoint.sh | |
volumes: | |
- .:/app:cached | |
- gems:/gems | |
- node_modules:/app/node_modules | |
- packs:/app/public/packs | |
- rails_cache:/app/tmp/cache | |
ports: | |
- "3000:3000" | |
user: ruby | |
tty: true | |
stdin_open: true | |
depends_on: | |
- postgres | |
postgres: | |
image: postgres:11 | |
environment: | |
- POSTGRES_HOST_AUTH_METHOD=trust | |
volumes: | |
- postgres:/var/lib/postgresql/data | |
volumes: | |
gems: | |
postgres: | |
node_modules: | |
packs: | |
rails_cache: |
ARG RUBY_VERSION=2.6 | |
FROM ruby:$RUBY_VERSION | |
ARG DEBIAN_FRONTEND=noninteractive | |
ARG NODE_VERSION=11 | |
RUN curl -sL https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - | |
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ | |
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list | |
RUN apt-get update && apt-get install -y \ | |
build-essential \ | |
nodejs \ | |
yarn \ | |
locales \ | |
git \ | |
netcat \ | |
vim \ | |
sudo | |
ARG UID | |
ENV UID $UID | |
ARG GID | |
ENV GID $GID | |
ARG USER=ruby | |
ENV USER $USER | |
RUN groupadd -g $GID $USER && \ | |
useradd -u $UID -g $USER -m $USER && \ | |
usermod -p "*" $USER && \ | |
usermod -aG sudo $USER && \ | |
echo "$USER ALL=NOPASSWD: ALL" >> /etc/sudoers.d/50-$USER | |
ENV LANG C.UTF-8 | |
ENV BUNDLE_PATH /gems | |
ENV BUNDLE_HOME /gems | |
ARG BUNDLE_JOBS=20 | |
ENV BUNDLE_JOBS $BUNDLE_JOBS | |
ARG BUNDLE_RETRY=5 | |
ENV BUNDLE_RETRY $BUNDLE_RETRY | |
ENV GEM_HOME /gems | |
ENV GEM_PATH /gems | |
ENV PATH /gems/bin:$PATH | |
ARG INSTALL_PG_CLIENT=false | |
RUN if [ "$INSTALL_PG_CLIENT" = true ]; then \ | |
apt-get install -y postgresql-client \ | |
;fi | |
RUN mkdir -p "$GEM_HOME" && chown $USER:$USER "$GEM_HOME" | |
RUN mkdir -p /app && chown $USER:$USER /app | |
WORKDIR /app | |
RUN mkdir -p node_modules && chown $USER:$USER node_modules | |
RUN mkdir -p public/packs && chown $USER:$USER public/packs | |
RUN mkdir -p tmp/cache && chown $USER:$USER tmp/cache | |
USER $USER | |
RUN gem install bundler |
#! /bin/bash | |
set -e | |
: ${APP_PATH:="/app"} | |
: ${APP_TEMP_PATH:="$APP_PATH/tmp"} | |
: ${APP_SETUP_LOCK:="$APP_TEMP_PATH/setup.lock"} | |
: ${APP_SETUP_WAIT:="5"} | |
# 1: Define the functions to lock and unlock our app container's setup | |
# processes: | |
function lock_setup { mkdir -p $APP_TEMP_PATH && touch $APP_SETUP_LOCK; } | |
function unlock_setup { rm -rf $APP_SETUP_LOCK; } | |
function wait_setup { echo "Waiting for app setup to finish..."; sleep $APP_SETUP_WAIT; } | |
# 2: 'Unlock' the setup process if the script exits prematurely: | |
trap unlock_setup HUP INT QUIT KILL TERM EXIT | |
# 3: Wait for postgres to come up | |
echo "DB is not ready, sleeping..." | |
until nc -vz postgres 5432 &>/dev/null; do | |
sleep 1 | |
done | |
echo "DB is ready, starting Rails." | |
# 4: Specify a default command, in case it wasn't issued: | |
if [ -z "$1" ]; then set -- bundle exec rails server -p 3000 -b 0.0.0.0 "$@"; fi | |
# 5: Run the checks only if the app code is executed: | |
if [[ "$3" = "rails" ]] | |
then | |
# Clean up any orphaned lock file | |
unlock_setup | |
# 6: Wait until the setup 'lock' file no longer exists: | |
while [ -f $APP_SETUP_LOCK ]; do wait_setup; done | |
# 7: 'Lock' the setup process, to prevent a race condition when the | |
# project's app containers will try to install gems and set up the | |
# database concurrently: | |
lock_setup | |
# 8: Check if dependencies need to be installed and install them | |
bundle install | |
yarn install | |
# 9: Run migrations or set up the database if it doesn't exist | |
# Rails >= 6 | |
bundle exec rails db:prepare | |
# Rails < 6 | |
# bundle exec rake db:migrate 2>/dev/null || bundle exec rake db:setup | |
# 10: 'Unlock' the setup process: | |
unlock_setup | |
# 11: If the command to execute is 'rails server', then we must remove any | |
# pid file present. Suddenly killing and removing app containers might leave | |
# this file, and prevent rails from starting-up if present: | |
if [[ "$4" = "s" || "$4" = "server" ]]; then rm -rf /app/tmp/pids/server.pid; fi | |
fi | |
# 12: Replace the shell with the given command: | |
exec "$@" |
The entrypoint does a check for Gemfile changes on every docker-compose up
, but if there are no changes it moves on.
Cool man. It works now. Thank you so much for your help and for these files
Glad it works and helps!
Great Gist, even two years on ;-) I also love the blog on hint.io. But allow me one question: In entrypoint.sh, you call unlock_setup
followed by while [ -f $APP_SETUP_LOCK ]; do wait_setup; done
-- but if I am not mistaken, the call to unlock_setup
will basically render the locking mechanism ineffective? It seems to me that this function call is erroneous, or am I missing something?
@bovender Thanks for the kind words! I have gone back and forth on that section of the entrypoint with my team over the years. I honestly believe we landed there because if a setup.lock file did not get cleaned up for some reason when the entrypoint was run again it would stall the starting of the app. Feel free to experiment with it to see what works best in your situation.
Thanks!
They are cached in a docker volume.