Now Reading
Rails on Docker · Fly

Rails on Docker · Fly

2023-01-26 10:32:37

Container ship carrying a train
Picture by Annie Ruygt

Rails 7.1 is getting an official Dockerfile, which ought to make it simpler to deploy Rails purposes to manufacturing environments that assist Docker. Consider it as a pre-configured Linux field that may work for many Rails purposes.

Which means you may begin seeing a Dockerfile within the challenge listing of much more Rails apps. When you’re not conversant in Docker, you would possibly open the file and see a number of issues that look acquainted like some bash instructions, however another issues could be new and overseas to you.

Let’s dive into what’s in a Dockerfile so its much less of a thriller, however first let’s take a look at how Fly.io makes use of Docker so that you higher perceive how Docker suits right into a Rails stack.

How Does Fly.io Use Docker?

Fun fact! Fly.io doesn’t actually run Docker in production—rather it uses a Dockerfile to create a Docker image, also known as an OCI image, that it runs as a Firecracker VM. What does that imply for you? Not a lot actually. For all sensible functions you may describe your purposes’ manufacturing machine in a Dockerfile and Fly.io transparently handles the remaining.

The wonderful thing about Dockerfiles is it makes standardizing manufacturing deployments potential, which for many builders means its simpler to deploy purposes to hosts that assist Docker, like Fly.io.

What’s a Dockerfile? It is a textual content file with a bunch of declarations and Linux instructions that describe what must be put in and executed to get an utility operating. This file is given to a bunch of fancy software program that configures a Linux distribution to the purpose the place it may run your utility.

You possibly can consider every command within the file as a “layer”. On the backside of the layer is a Linux distribution, like Ubuntu. Every command provides one other layer to the configuration till ultimately all of the packages, configurations, and utility code are within the container and your app can run. This layering is vital for caching instructions, which make deployments quick if finished accurately.

Let’s get into it.

A Closer Look at the Rails Dockerfile

At the time of this writing, the default Rails 7.1 Dockerfile looks like:

# Make sure it matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.0
FROM ruby:$RUBY_VERSION

# Install libvips for Active Storage preview support
RUN apt-get update -qq && 
    apt-get install -y build-essential libvips && 
    apt-get clean && 
    rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man

# Rails app lives here
WORKDIR /rails

# Set production environment
ENV RAILS_LOG_TO_STDOUT="1" 
    RAILS_SERVE_STATIC_FILES="true" 
    RAILS_ENV="production" 
    BUNDLE_WITHOUT="development"

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install

# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile --gemfile app/ lib/

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]

At the top of the file, we set the Ruby version.

# Make sure it matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.0
FROM ruby:$RUBY_VERSION

The version gets plugged into the FROM command, which ends up looking like FROM ruby:3.2.0. Where is ruby:3.2.0? It’s a Docker image that some neighborhood members have graciously configured for us that will get us a Linux distribution operating Ruby 3.2. That is not sufficient to run a Rails utility; we have to add a number of extra layers to the picture.

Subsequent up the Dockerfile installs Linux packages wanted to run sure Rails gems in Linux. libvibs is a local library used to resize photographs for ActiveSupport. Different packages could possibly be added right here, like a Postgres, MAQL, or SQLite shopper. Different gems might rely upon Linux packages too. For instance, a preferred XML parsing library, Nokogiri, will depend on libxml. These aren’t included on this record as a result of the ruby picture already consists of them.

# Set up libvips for Lively Storage preview assist
RUN apt-get replace -qq && 
    apt-get set up -y build-essential libvips && 
    apt-get clear && 
    rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man

apt-get is a Linux bundle supervisor that at all times seems unusual in a Dockerfile due to all of the command it does earlier than and after putting in a bundle. Let’s break it down line-by-line.

First, apt-get replace -qq tells Linux to obtain a manifest of all of the packages which are accessible for obtain from apt-get.

The second line is the one you care about and would possibly want to alter. apt-get set up -y build-essential libvips installs two packages and the -y routinely solutions “sure” when it asks when you’re certain you need to set up the packages.

Every part after that removes the manifest information and any non permanent information downloaded throughout this command. It’s a necessity to take away all these information on this command to maintain the scale of the Docker picture to a minimal. Smaller Dockerfiles imply sooner deployments.

Subsequent the working listing is ready.

# Rails app lives right here
WORKDIR /rails

This creates the ./rails folder contained in the docker picture. The entire traces within the Dockerfile after this are run from that listing and any information added to the picture are put in that listing. It is the equal of mkdir -p ./rails && cd ./rails.

Subsequent a number of surroundings variables are set.

# Set manufacturing surroundings
ENV RAILS_LOG_TO_STDOUT="1" 
    RAILS_SERVE_STATIC_FILES="true" 
    RAILS_ENV="manufacturing" 
    BUNDLE_WITHOUT="improvement"

What are these you ask?

  • RAILS_LOG_TO_STDOUT – Rails log output is shipped to STDOUT as an alternative of a file. STDOUT, or commonplace out, makes it potential for docker logs to view the output of no matter is operating on the container. The Twelve-Factor App has a good explanation of why logs needs to be written to STDOUT.
  • RAILS_SERVE_STATIC_FILES – This instructs Rails to not serve static information. It is at all times been really useful to have a server like nginx serve up photographs, CSS, JavaScripts, and different static information by a server that is not Ruby for higher efficiency.
  • RAILS_ENV – Instructs Rails in addition with manufacturing gems and with the configuration from config/environments/manufacturing.rb.
  • BUNDLE_WITHOUT – When you’ve ever regarded in your utility’s Gemfile, you may discover there’s gems tagged with the improvement group like web-console. These gems aren’t wanted or needed in a manufacturing surroundings as a result of they’d both sluggish issues down, not be used, or pose a safety danger. This command tells bundler to depart out all of the improvement gems.

Time to put in the gems! First Docker copies the Gemfile and Gemfile.lock from our workstation or CI’s server challenge listing into the containers ./rails listing (keep in mind the factor that was set by WORKDIR above? That is it!)

# Set up utility gems
COPY Gemfile Gemfile.lock ./
RUN bundle set up

bundle set up is run towards the Gemfiles that have been copied over. This installs the gems contained in the container, which we’ll want for our Rails utility to run.

One thing you could be asking your self, “why not copy the whole utility from my workstation after which run bundle set up?”. Nice query! Every “ALLCAPS” directive in a Dockerfile, like RUN, COPY, and many others. are “layers” that get cached. When you did not deal with copying the Gemfile and operating bundler as a separate layer, you’d need to run bundle set up each time you deployed Rails, even when you did not change the gem. That may take ceaselessly!

Making it a separate layer means you solely need to replace the bundle when the Gemfile modifications. In different phrases, when you solely change utility code, you’ll be able to skip operating bundle and bounce proper into the following layer, which saves a great deal of time between deploys.

Lastly the Rails utility code information are copied out of your pc or CI machine to the WORKDIR set above, which is ./rails within the picture.

# Copy utility code
COPY . .

Once we boot the Docker picture and the Rails server, we would like it to return on-line as shortly as potential so our deploys are sooner, so the picture copies over the bootsnap cache to make that occur.

# Precompile bootsnap code for sooner boot occasions
RUN bundle exec bootsnap precompile --gemfile app/ lib/

At this level all of the information wanted to run the server are copied over from the workstation to the Docker picture, excluding information listed in .dockerignorewhich usually embody the .git listing, log information, and many others.

Now its time to compile JavaScript, stylesheet, and picture property!

# Precompiling property for manufacturing with out requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 bundle exec rails property:precompile

It is a bit if a hack. Rails requires a secret key to maintain periods and different cryptographic Rails options safe, however for an asset compilation, together with the precise secret key will not be wanted and is subsequently a legal responsibility. As an alternative SECRET_KEY_BASE_DUMMY=1 is handed into the compilation activity to inform Rails, “ignore requiring a secret key”.

An important a part of this command is bundle exec rails property:precompile, which runs no matter compilation steps are wanted to minify and fingerprint property in order that they load shortly in manufacturing.

See Also

The ENTRYPOINT directive in Docker acts like a wrapper.

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

The most effective factor to do is take a look at the contents of the ENTRYPOINT script, which lives at ./bin/docker-entrypoint

#!/bin/bash

# If operating the rails server then create or migrate current database
if [ "${*}" == "./bin/rails server" ]; then
  ./bin/rails db:put together
fi

exec "${@}"

The script checks to see if the CMD, beneath, is operating ./bin/rails server. If its operating the server, it should be sure it runs a database migration earlier than it boots the appliance. If you do not need Rails to routinely run migrations while you deploy, you possibly can remark out or take away the ENTRYPOINT directive within the Dockerfile.

The very last thing in all Dockerfiles is easy methods to boot the appliance server.

# Begin the server by default, this may be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]

EXPOSE 3000 tells Docker the appliance will pay attention on port 3000, which is the default for bin/rails server, as you see in your Rails improvement surroundings.

Take It for a Spin

You can expect a lot of changes between now and when Rails 7.1 is released. For example, we’re currently exploring extracting Dockerfile generation out of railties and moving it into its own gem at https://rubygems.org/gems/dockerfile-rails. Which means you need to use this right this moment along with your present Rails challenge. If you wish to strive it out, first set up the gem to your Rails app.

$ bundle add dockerfile-rails

Then generate the Dockerfile with the dockerfile command.

Then checkout the Dockerfile that is now on the root of your challenge. You possibly can then deploy it by installing Fly.io, operating the next command, and following the directions.

Fly.io will ask you a number of questions and inside a couple of minutes, it’s best to see your Rails app operating in manufacturing.

There’s a Lot of Different Ways to Configure a Dockerfile

The official Rails Dockerfile will be a great starting place for most people, but as applications grow in complexity and the need to install additional packages arises, it might not be enough.

Fly.io has started putting together a collection of Dockerfile recipes in the Fly Rails Cookbooks. You may discover instance Dockerfiles for all types of various Rails deployments together with people who wants Node 19+ put in or for Rails API deployments.



Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top