Last active
September 21, 2022 00:43
-
-
Save kylev/71b61acf72711042d03b4b374aeb05f2 to your computer and use it in GitHub Desktop.
A complete Alpine docker-compose Rails workflow with PostgreSQL and Redis
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
version: "3" | |
services: | |
database: | |
image: postgres:11-alpine | |
volumes: | |
- postgres-data:/var/lib/postgresql/data | |
redis: | |
image: redis:5-alpine | |
volumes: | |
- redis-data:/data | |
web: | |
build: | |
context: . | |
args: | |
WITHOUT_BUNDLE: "yes" | |
environment: | |
REDIS_URL: redis://redis/ | |
DB_HOST: database | |
ports: | |
- "3000:3000" | |
volumes: | |
- bundle-cache:/usr/local/bundle | |
- .:/opt/app | |
depends_on: | |
- database | |
- redis | |
volumes: | |
bundle-cache: | |
postgres-data: | |
redis-data: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FROM ruby:2.6-alpine | |
# Additional runtime facilities provided by the OS. | |
RUN apk add --no-cache libxml2 libxslt postgresql-libs tzdata | |
# Tell bundler how to use the OS. | |
RUN gem install bundler -v '~> 1.17' && \ | |
bundle config build.nokogiri --use-system-libraries | |
WORKDIR /opt/app | |
RUN mkdir -p tmp/pids tmp/cache | |
ARG WITHOUT_BUNDLE="no" | |
ARG WITHOUT_GROUPS="development test" | |
COPY Gemfile Gemfile.lock ./ | |
# A single layer of the bundle installation. If we perform the bundle | |
# install, it is assumed to be final, so we rip out the development | |
# libraries. | |
RUN apk add --no-cache --virtual .bundler-installdeps \ | |
build-base git libxml2-dev libxslt-dev postgresql-dev && \ | |
if [ $WITHOUT_BUNDLE = 'no' ]; then \ | |
bundle install --jobs=3 --retry=3 --without=$WITHOUT_GROUPS && \ | |
rm -rf /usr/local/bundle/cache && \ | |
apk del .bundler-installdeps; \ | |
fi | |
COPY . ./ | |
ENTRYPOINT ["bundle", "exec"] | |
CMD ["rails", "server", "-b", "0.0.0.0"] | |
EXPOSE 3000 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.PHONY: build build-release bundle bundle-install bundle-update console db-setup db-migrate \ | |
down exec-shell logs up | |
default: bundle-install | |
build: # Build the docker image. | |
docker-compose build | |
build-release: # Build the docker image. | |
docker build --no-cache -t example-rails-app . | |
bundle: bundle-install # Alias of bundle-install | |
bundle-install: build # Install with bundler | |
docker-compose run --rm --entrypoint bundle web install --jobs=3 --retry=3 | |
bundle-update: build # Update with bundler | |
docker-compose run --rm --entrypoint bundle web update | |
console: # Start a rails console | |
docker-compose run --rm web rails c | |
db-setup: # Run rake db:setup | |
docker-compose run --rm web rake db:setup | |
db-migrate: # Run rake db:migrate | |
docker-compose run --rm web rake db:migrate | |
down: # Bring all services down | |
docker-compose down | |
exec-shell: # Exec a shell in the running web container. | |
docker-compose exec web sh | |
logs: # Show the logs from the running containers. | |
docker-compose logs | |
up: # Bring all services up | |
docker-compose up -d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The aim of this gist is to emulate a
rbenv
-like environment usingdocker-compose
. Its default docker build is highly optimized and small (138MB for my test rails 5.2 API project withnokogiri
andpg
gem dependencies).I used
make
to replace the native rails, rake, and bundler workflow. If you were to check out a project set up like this, rather than checkingrbenv
had the right version and runningbundle install
, you'd simply runmake bundle
, which establishes the entire development stack in docker. From there you'd use some of the usual rake targets (viamake db-seed
et. al.). To bring everything up and start working,make up
boots the app along side docker-compose managed Redis and PostgreSQL.The centerpiece of this approach is the Dockerfile. By default (and the make target
build-release
) it will generate an exceptionally small, Alpine Linux based Docker image with only the necessary production bundler groups, ready for deployment on ECS, Kubernetes, Heroku, or any other Docker deployment system.The central trick I learned about Alpine Linux while doing this was to install runtime requirements (like libxml2) separate from the development headers (in a package like libxml2-dev). I coalesced the adding development packages, bundle installing, and removal of the packages into a single
RUN
statement in order to to collapse the layer during the docker build. Leaving the development headers in place or removing them in a different layer will bloat the image by ~220MB.I chose to add a little complexity to the Dockerfile via ARGs to facilitate happy development and testing. Driving this as a developer with
make
uses bundler steps in the Docker build: we skip gem installation viaWITHOUT_BUNDLE
and run thebundle install
with thebundle-cache
mounted where bundler will place the gems. This means that changes to the Gemfile don't require a slow "build the whole image and bundle from scratch" step. Instead, a quicker differentialbundle install
of what isn't already onbundle-cache
takes a moments.For CI or other non-deployment docker scenarios, passing
--build-arg WITHOUT_GROUPS=none
to adocker build
will result in an "omnibus" whole-bundle build, ready to execute your test suite or be used in another developer workflow.