Running old Ruby on Rails application with Docker
A few years ago, in 2016 I was developing Ruby on Rails applications. It was the beginning of my programming journey so I was not paying attention to containerization or even proper documentation. This made running those apps in 2022 a little bit harder.
As an example, I'll show the process based on my euro2016 repository. Looking at the Gemfile you can see that there are not many dependencies there - the process should be easier than a real commercial big application. The website was created using rails 4.1.8 which was released in 2014! Almost 10 years ago.
First try: running on host
Firstly I thought maybe it's possible to install and run the app directly on my computer (mac). I installed ruby and ran
bundle install. Sadly it was not so easy. Some old dependencies were causing problems.
euro2016 on master [?] via 💎 v2.6.8 ✗ bundle install An error occurred while installing json (1.8.3), and Bundler cannot continue. Make sure that gem install json -v '1.8.3' --source 'https://rubygems.org/' succeeds before bundling. In Gemfile: rails was resolved to 4.1.8, which depends on actionmailer was resolved to 4.1.8, which depends on actionpack was resolved to 4.1.8, which depends on actionview was resolved to 4.1.8, which depends on activesupport was resolved to 4.1.8, which depends on json
Having this error I tried to install the newer version of
json gem but new problems arrived. Foremost I wanted to run the app in the same state as it was before. Upgrading dependencies might change some behavior and I didn't want that.
After some googling, a few hours spend tinkering with the build process, and even changing versions there was no progress. As a next step I tried using rbenv. I thought installing an older version of ruby may solve some problems but installing the older version caused even more problems in the first place. Some dependency was not working because some os-package was updated (like libssl or something simillar). I didn't want to install old packages on my OS and here comes the Docker.
Second try: containerization with Docker
Using Docker for Ruby on Rails is pretty straightforward. You use
ruby image and that's it. As a first iteration I tried the basic
FROM ruby WORKDIR /root ADD Gemfile /root ADD Gemfile.lock /root RUN bundle install --without production
ruby:latest image here with some Ruby 3 version. Copying
Gemfile.lock and running
bundle install. So what was the problem with this approach?
#9 39.00 @package.dir_mode = options[:dir_mode] #9 39.00 ^^^^^^^^^^^ #9 39.04 An error occurred while installing rake (11.1.2), and Bundler cannot continue. #9 39.04 Make sure that `gem install rake -v '11.1.2'` succeeds before bundling. ------ executor failed running [/bin/sh -c bundle install --without production]: exit code: 5
Then I thought maybe the latest version of ruby is not the best. I didn't know what version of ruby I was using in 2016. I should have documented it and made my life easier. As a wild guess I tried ruby:1.9.3 and got the following error:
#0 34.15 Installing actionpack 4.1.8 #0 34.57 #0 34.57 Gem::InstallError: mime-types-data requires Ruby version >= 2.0. #0 34.58 An error occurred while installing mime-types-data (3.2016.0521), and Bundler #0 34.58 cannot continue. #0 34.58 Make sure that `gem install mime-types-data -v '3.2016.0521'` succeeds before #0 34.58 bundling. ------
Ok, some package required Ruby version >= 2.0. After a few more tries I managed to find the proper version which is Ruby 2.1.
So all dependencies were installed correctly. To simplify the process I created
docker-compose.yml which is used simply to build and start (serve) the application:
version: '3' services: rails-app: build: context: . dockerfile: Dockerfile image: euro2016-rails command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" ports: - '3000:3000' working_dir: /root volumes: - .:/root:cached
I tried to serve the application but the new problem (or a challenge) arrived:
nodejs. It will be easy, I have to add only
apt-get install nodejs to the
Dockerfile, right? Actually no.
=> ERROR [6/6] RUN apt-get install -y --force-yes nodejs 3.0s ------ > [6/6] RUN apt-get install -y --force-yes nodejs: #0 1.303 Reading package lists... #0 2.955 Building dependency tree... #0 2.957 Reading state information... #0 2.962 E: Unable to locate package nodejs ------ failed to solve: executor failed running [/bin/sh -c apt-get install -y --force-yes nodejs]: exit code: 100
This ruby:2.1 image uses some old Debian 8 underhood and there is no
nodejs in the debian repositories. After quick googling I found that on
nodesource there is a script for adding
nodejs to the repository. Final
Dockerfile looks like this:
FROM ruby:2.1 WORKDIR /root ADD Gemfile /root ADD Gemfile.lock /root RUN bundle install --without production RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - RUN apt-get install -y --force-yes nodejs RUN node --version RUN npm --version
Now I can run the application on any computer just having docker and running
docker compose up. It works! The next step is populating the database, adding demo accounts, and hosting it on cloud as a showcase.
For future projects I should:
- create a docker image for building/serving application
- document versions used
- os (e.g. Ubuntu 20.04)
- package managers, e.g. npm 2.1.3
- runtime versions, e.g. node 16.3.8