In this post, I will show how we can integrate this auto-update mechanism in a Docker container on top of a CoreOS server.
The Rails Docker container is a custom container built from the well-known Phusion Passenger image. We just added some monitoring tools and the startup script (see below).
When the server starts, we pass data to it (through a Cloud Init user-data specification) that schedules the start of the container.
Here is an extract of the script responsible of the start of the Rails container:
- name: rails.service command: start enable: true content: | [Unit] Description=Bimeio Rails After=docker.service [Service] TimeoutStartSec=0 ExecStartPre=/bin/sh -c "/usr/bin/docker inspect bime/rails > /dev/null && /usr/bin/docker rm -f rails || true" ExecStartPre=/bin/sh -c "rm -f /home/core/rails || true" ExecStart=/bin/sh -l -c "/usr/bin/docker run --name rails --restart=always -p 80:80 -e RAILS_ENV=production bime/rails /sbin/my_init" ExecStop=/usr/bin/docker stop rails
This script declares a new service to Systemd named
rails and describes how to start and stop the Rails container. It also ensures that the service is started after the docker service necessary in order to create a new container.
rails service is started, we have a full docker container based on our image up and running on the server. Now let’s see how Rails is started.
In the init process of the container, there is a custom script called
startup.sh. Let’s have a look at the script:
#!/bin/bash cd /home/app/bimeio timestamp=`date +%s` mkdir -p /opt/releases/$timestamp cd /opt/releases/$timestamp export AWS_SECRET_ACCESS_KEY=... export AWS_ACCESS_KEY_ID=... aws s3 cp s3://.../rails.zip . unzip rails.zip rm rails.zip bundle install bundle exec rake db:migrate RAILS_ENV=production bundle exec whenever -w mkdir tmp cd /home/app rm -f bimeio ln -s /opt/releases/$timestamp bimeio chown app:app bimeio chown -R app:app /opt/releases/$timestamp touch bimeio/tmp/restart.txt
Basically, we create a new folder in the release folder that will contain the new Rails code. Then we retrieve the application source code, install the gems, run migrations and update the symbolic link to the new release. Touching
restart.txt notifies Passenger to reload the app based on the new code.
This script is the same as the one used by the auto-update mechanism, but is executed for the first time when the container starts.
Did you notice the
bundle exec whenever -w instruction?
This will install the cron jobs in our container. We use the whenever gem to install cron jobs based on a Ruby syntax:
job_type :rails_script, "source /etc/container_environment.sh && cd /home/app/bimeio && bin/rails runner -e :environment ':task' :output" every 1.minutes do rails_script 'RailsUpdater.check_update' end
As soon as the cron job is installed, the
RailsUpdater will check for a new version every minute.
With a solid auto-update mechanism and a range of different technologies, you can leverage an application based on Ruby on Rails and Docker that can be deployed almost anywhere* and automatically kept up to date.
As a SaaS provider, this technique allows us to choose our cloud provider wisely (AWS for now). As we are not dependent on the underlying infrastructure, it fits our philosophy for an Agile process from end to end.