0

I've been tasked with maintaining a legacy Rails site: rails 3.2, ruby 2.4, and a few gems that depend on these legacy versions of rails and ruby.

I'm trying to put rails in one docker container, and the postgres db in another. I got this up and running just fine, so I'm confident I'm not completely useless.

Everything seems to run fine, including running docker-compose up -d --build, and then I exec into the rails container, bundle install, and then try to run rails s. Then I get the following:

    /usr/local/rvm/rubies/ruby-2.4.1/bin/ruby: warning: shebang line ends with \r may cause a problem
/usr/local/rvm/gems/ruby-2.4.1/gems/sinatra-1.4.6/lib/sinatra/base.rb:1225: warning: constant ::Fixnum is deprecated
/usr/local/rvm/gems/ruby-2.4.1/gems/sinatra-1.4.6/lib/sinatra/base.rb:1225: warning: constant ::Fixnum is deprecated
/usr/local/rvm/gems/ruby-2.4.1/gems/sinatra-1.4.6/lib/sinatra/base.rb:1225: warning: constant ::Fixnum is deprecated
/usr/local/rvm/gems/ruby-2.4.1/gems/builder-3.0.4/lib/builder/xchar.rb:111: warning: constant ::Fixnum is deprecated
/usr/local/rvm/gems/ruby-2.4.1/gems/sinatra-1.4.6/lib/sinatra/base.rb:1225: warning: constant ::Fixnum is deprecated
/usr/locfrom /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-3.2.22.5/lib/active_support/dependencies.rb:251:in `block in require'e -- guard/guard (LoadError)
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-3.2.22.5/lib/active_support/dependencies.rb:251:in `require'endency'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/bundler-1.16.0.pre.3/lib/bundler/runtime.rb:97:in `require''
        from /usr/local/rvm/gems/ruby-2.4.1/gems/bundler-1.16.0.pre.3/lib/bundler/runtime.rb:74:in `block in require'n require'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/bundler-1.16.0.pre.3/lib/bundler/runtime.rb:67:in `require'
        from /var/www/html/config/application.rb:13:in `<top (required)>'/bundler.rb:114:in `require'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-3.2.22.5/lib/rails/commands.rb:53:in `block in <top (required)>'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-3.2.22.5/lib/rails/commands.rb:50:in `<top (required)>'
        from script/rails:6:in `<main>''

Any ideas for how to move forward would be appreciated; I've been stuck on this for a long time now.

Included below are my Gemfile, docker-compose.yml, database.yml and the Dockerfiles for each Docker container:

docker-compose.yml:

version: '3'

services:
  web:
    build: 
      context: .
    ports:
      - "8080:3000"
      - "4443:443"
    volumes:
      - ./:/var/www/html
    environment:
      - ALLOW_OVERRIDE=true
    command: tail -f /dev/null
    container_name: hl_web
    links:
    - db
  db:
    image: postgres:9.3.17
    volumes:
      - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
      - "db-data:/var/lib/postgresql/data"
    container_name: hl_db

volumes:
  db-data:

database.yml

defaults: &defaults
  adapter: postgresql
  # template: template_postgis
  encoding: unicode
  pool: 5
  username: root
  password:
  host: db

development:
  <<: *defaults
  database: realty_development

test:
  <<: *defaults
  database: realty_test

staging:
  <<: *defaults
  database: realty_development

production:
  <<: *defaults
  database: realty_production

Dockerfile for rails:

# Latest Ubuntu LTS
FROM ubuntu:12.04

RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install --fix-missing curl git wget libpq-dev node libpq-dev vim

# Install mpapis
RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
RUN \curl -sSL https://get.rvm.io | bash -s stable --ruby

# Need to add RVM to the path
ENV PATH /usr/local/rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RUN /bin/bash -l -c 'source /etc/profile.d/rvm.sh'
RUN /bin/bash -l -c "rvm install 2.1.2"
RUN /bin/bash -l -c "rvm use 2.1.2"
RUN /bin/bash -l -c "gem install rails -v 4.2.2"

# Copy # Heroku installer to opt and run it
#COPY ./docker_config/install-ubuntu.sh /opt/install-ubuntu.sh
#WORKDIR /opt
#RUN sh install-ubuntu.sh

# Heroku installer
RUN /bin/bash -l -c 'echo "deb http://toolbelt.heroku.com/ubuntu ./" > /etc/apt/sources.list.d/heroku.list'
RUN \wget -O- https://toolbelt.heroku.com/apt/release.key | apt-key add -
RUN apt-get -y update
RUN apt-get install -y heroku-toolbelt

# Change back to project root and run bundle
#WORKDIR /var/www/html
#RUN /bin/bash -l -c "bundle install"

# make bundler a default gem
RUN echo bundler >> /usr/local/rvm/gemsets/global.gems
# source rvm in every shell
RUN sed -i '3i . /etc/profile.d/rvm.sh\n' ~/.profile
# setup some default flags from rvm (auto install, auto gemset create, quiet curl)
RUN echo "rvm_install_on_use_flag=1\nrvm_gemset_create_on_use_flag=1\nrvm_quiet_curl_flag=1" > ~/.rvmrc

# Copy over the site vhosts
# COPY ./docker_settings/apache_web.conf /etc/apache2/sites-available/000-asite.com.conf
# RUN ln -s /etc/apache2/sites-available/000-asite.com.conf /etc/apache2/sites-enabled/000-asite.com.conf
# RUN rm /etc/apache2/sites-enabled/000-default.conf

# # Link ssl
# RUN ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf

# # Enable modules
# RUN ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load
# RUN ln -s /etc/apache2/mods-available/ssl.load /etc/apache2/mods-enabled/ssl.load
# RUN ln -s /etc/apache2/mods-available/ssl.conf /etc/apache2/mods-enabled/ssl.conf
# RUN ln -s /etc/apache2/mods-available/socache_shmcb.load /etc/apache2/mods-enabled/socache_shmcb.load

# # Create some directories so Apache doesn't fallover
# RUN mkdir /var/www/html/public

# # Restart apache to load changes
# RUN service apache2 restart

# # We need to make some directories writable by apache before docker mounts it
# RUN chown -R www-data:www-data /var/www/html

# # Donwload composer. Install to /usr/bin
# WORKDIR /opt/composer
# RUN curl -sS https://getcomposer.org/installer | php
# RUN cp composer.phar /usr/bin/composer

# # make the work directory the web root dir
# WORKDIR /var/www/html
# 

# interactive shell by default so rvm is sourced automatically
#ENTRYPOINT /bin/bash -l

CMD ["/bin/bash -l"]

Dockerfile for postgres

# Latest Ubuntu LTS
FROM ubuntu:12.04

RUN apt-get -y update 
#&& apt-get -y upgrade
RUN apt-get -y install --fix-missing curl git wget libpq-dev node libpq-dev vim

#Install postgres
RUN locale-gen en_US.UTF-8

RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/postgresql.list'
RUN \wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN apt-get -y update
RUN apt-get -y install postgresql-9.3 postgresql-9.3-postgis-2.1
#RUN apt-get -y install postgis

RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf

# Expose the PostgreSQL port
EXPOSE 5432

# Add VOLUMEs to allow backup of config, logs and databases
VOLUME  ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]

RUN service postgresql start

#ENTRYPOINT /bin/bash
#CMD ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf", "start"]

Gemfile:

source 'https://rubygems.org'

gem 'rails', '~> 3.2.17'

# http://rack.github.com/
gem 'rack', '~> 1.4.5'

# To redirect trailing slashes to have no trailing slashes
gem 'rack-rewrite'

platforms :ruby do
  # Unicorn is a forking webserver
  gem 'unicorn'

  # Terminate out-of-control unicorns
  gem 'unicorn-worker-killer'
end

platforms :mswin do
  gem 'thin'
end

# dependencies for the redirector app:
gem 'sinatra-subdomain'
gem 'postrank-uri'

# Used for date queries
gem 'groupdate'

# Used to inject data into javascript DOM
gem 'gon'

# Used to connect to Amazon for sitemap_generator gem
gem 'fog'

# Used for background workers/concurrent tasks.
gem 'sidekiq'
gem 'sidekiq-failures'

# Next two gems are for sidekiq monitor
gem 'slim', '>= 1.1.0'
# if you require 'sinatra' you get the DSL extended to Object
gem 'sinatra', '>= 1.3.0', require: nil

# Used for long running jobs to display progress
gem 'ruby-progressbar'

# Cloudinary API - image processing and CDN
gem 'cloudinary'

# Performance monitoring / exception tracking
gem 'newrelic_rpm'
gem 'newrelic-rake'

# Convert an address into lat/lon coordinate pair
gem 'geocoder'

# Yesmail
gem 'yesmail', git: 'git://github.com/apartmentlist/yesmail.git'
gem 'yesmail2', git: 'git://github.com/apartmentlist/yesmail2.git'
gem 'log_mixin', git: 'git://github.com/apartmentlist/log_mixin.git'

# Captcha support through recaptcha
gem 'recaptcha', :require => 'recaptcha/rails'

# xml generation
# IMPORTANT: Put nokogiri before pg in your Gemfile
# https://github.com/sparklemotion/nokogiri/issues/742
# http://stackoverflow.com/questions/6802410/warning-nokogiri-was-built-against-libxml-version-2-7-8-but-has-dynamically-lo#answer-10912948
# http://stackoverflow.com/questions/11668604/mountain-lion-libxml-nokogiri
gem "nokogiri", ">= 1.6.7.rc"

# Postgres / Postgis gems
gem 'pg'

# Eventually, this gem needs upgrading as this branch is no longer supported
gem 'postgis_adapter', git: 'git://github.com/nofxx/postgis_adapter.git'

# HTTP/api stuff (lead posting)
gem 'httparty', '~> 0.10.0'

# HTTP library
gem 'faraday'

gem 'aws-sdk'

gem 'json'
gem 'psych', '~> 2.0.5'

# hiredis is a high performance redis driver that supports timeouts on a socket
gem 'hiredis'
gem 'redis', require: ['redis/connection/hiredis', 'redis']
gem 'redis-rails'

# Templates/Frontend
gem 'haml', '>= 3.0.0'
gem 'haml-rails'
gem 'kramdown'
gem 'haml-kramdown'

gem 'jquery-rails', '~> 2.1.4'
gem 'bourbon'
gem 'meta-tags', require: 'meta_tags'

# Detect mobile/tablet browsers
gem 'mobile-fu'

# P3P header generation so IE can load cookies in remote iFrames
gem 'rack-p3p'

# Timestamp scopes
gem 'timestamp_scopes', '~> 0.0.1'

# Twitter bootstrap
gem 'bootstrap-sass', '~> 2.3.2.1'
gem 'jquery-validation-rails'

# Pagination
gem "kaminari"

# Easy generation of sitemaps with upload to S3
gem 'sitemap_generator'

# Show the Rails environment on the Rails console prompt
gem 'marco-polo'

# Shorthand for update_all in batches
gem 'update_in_batches'

# Cleanup HTML to have a black or white list of tags
gem 'sanitize'

# A/B testing framework designed to work with Rails
gem 'split', require: 'split/dashboard'

# Used to include wordpress blog site that is hosted on a different server
gem "rack-reverse-proxy", require: "rack/reverse_proxy"

# Heroku specific
group :edge, :staging, :production do
  # Overwrite heroku's rails_log_stdout to support tagged logging
  gem 'rails_log_stdout', '~> 0.1.1'
end

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails', '3.2.5'
  gem 'coffee-rails'
  gem 'uglifier', '>= 1.0.3'
end

group :development, :test do
  gem 'awesome_print'
  gem 'quiet_assets'
  gem "rspec-rails", "~> 2.13.0"
  gem "capybara", "~> 2.1.0"
  gem "poltergeist", "~> 1.3.0"
  gem 'rspec-given'
  gem 'rr'
  gem 'shoulda-matchers'
  gem 'capybara-screenshot'
  gem 'factory_girl_rails'
  gem 'forgery'
  gem 'simplecov', :require => false
  gem 'simplecov-rcov', :require => false
  gem 'dotenv-rails'
  gem "rspec-instafail", "~> 0.2.4"
  gem "show_me_the_cookies"

  # Not removing these commented out gem lines so that we can enable profiling
  # in the near future per instructions at:
  # http://guides.rubyonrails.org/v3.2.13/performance_testing.html
  # enable next line for profiling
  # gem 'ruby-prof'
  # enable next 2 lines for profiling with rails 4
  # gem 'rails-perftest'
   gem 'test-unit'
end

group :development do
  gem 'annotate'
  gem 'foreman'
  gem 'guard'
  gem 'guard-bundler'
  gem 'guard-livereload'
  gem 'guard-pow'
  gem 'guard-rspec'
  gem 'guard-spork'
  gem 'spork-rails'
  gem 'growl'
  gem 'powify'
  gem 'yard'
  gem 'stack_rescue'
  # gem 'pry-rails'
  # The heroku toolbelt is required for the heroku rake tasks.
  # https://toolbelt.heroku.com/
  # gem 'heroku'
end
Jon Ohliger
  • 68
  • 1
  • 8

1 Answers1

3

There are multiple things wrong with your configuration including your choice of Ruby.

The error stems from using ruby 2.4 and rails 3.3. The highest version of ruby supported is 2.2 which was a question asked on stackoverflow. Here is the link to the original answer and here is the answer itself applied to your configuration.Look at this quote from this blog post:

Rails 3.2.22 includes all the commits from the 3-2-stable branch. This mean that now Rails 3.2 supports Ruby 2.2.

Rails 3.2 doesn't receive bug fixes anymore (only severe security fixes) I would not expect updates to the Rails 3.2 branch that ensure Ruby > 2.2 compatibility. Furthermore Rails 3.2 reached end of life when Rails 5.0 was released. So you should use ruby 2.2 and that's what I'll use to answer the question.

The first thing you need to understand when using docker, it is best practice to have a single foreground process per container. This is not a hard rule but when using a tool like docker-compose it is the intended practice. So in short you are definitively doing this the wrong way.

A second rule of thumb is to use official images whenever you can unless you have a really special case. And if you do have a case not covered by an official image, use a well maintained off-shoot based on an official image or base your modifications on top of an official image. For example in your case you would like to use postgis. The official postgres image does not include this but the mdillon/postgis image is built on the official postgres image, is well maintained and has gitter active for support, eliminating the need for your own image.

Another basic rule is to try and avoid execing into an image unless you are debugging it, I am not sure as to whether this config is or production or development but building a development environment seems to be what you would like to do so I will assume so.

One more thing that is inexplicable(hard to understand) is the disconnect between your docker-compose and your Dockerfiles. For example you define a postgres image of your own but use the official postgres:9.3.17. I understand you may be using an image you pre-built in your environment but this is not standard practice when using docker-compose. It is recommended to use your Dockerfile within compose if it is a custom image. This allows you to make changes on the fly, and prevents someone else from assuming that you use the official postgres image. Either way, the disconnect is confusing.

SOLUTION

Step one: choosing base images for your configuration.

  • For rails 3.2, ruby 2.2 we can use the ruby:2.2 image (Because I think that using rails 3 and ruby 2.4 is the source of the errors you got)
  • For postgres + postgis we can use the mdillon/postgis:9.3 image

Step two: writing dockerfiles

For the postgres/postgis setup there's no need the image can be used as is.

For the rails app, we need to do multiple things and there are somethings we do not need to do because of docker.

We need to:

  1. Install our gems and other dependencies

That's it. Seriously. What we DON'T need to do is:

  1. Install rvm, docker already isolates our app so it is pointless to have it installed and use gemsets.
  2. Install the heroku toolbelt. I honestly do not see why you need the heroku toolbelt and if you do, it can be placed in another container. If you are deploying to heroku, then it should be ideally separated from your app image.
  3. Install ruby 2.1.2 and rails 4. I don't understand why you do this. You clearly say you need rails 3.2 and ruby 2.2.
  4. Install apache, this should run on a different container or on the host if in production. During development you should not run an apache container.

Here is an example what should be in your rails Dockerfile. I've made the asumption that the Gemfile and Dockerfile are in the same folder. This should be the case since it is the only dockerfile

FROM ruby:2.2

# Set a working directory, I am partial to /src but choose whatever you like
WORKDIR /src

# Copy gemfile to pre-install gems (Allows us to quickly test if all is working well)
COPY Gemfile /src/Gemfile

# Install any library dependencies here like so after which you should
# install/update bundler then install gems from Gemfile
RUN apt-get -y update \
    && apt-get -y install curl \
                       git \
                       wget \
                       libpq-dev \
                       node \
                       libpq-dev \
                       vim \
                       /*whatever else you need*/ \
    && rm -rf /var/lib/apt/lists/* \  #  To preserve syntax highlighting */  
    && gem install bundler \
    && bundle install 

# Copy source files to image for flexibility later on, for example if you would like to push a bundled image to a registry or run it without compose
COPY . /src

# Define /src as a volume
VOLUME /src

# Use expose to show which port should be exposed to the host or other containers
EXPOSE 3000

# Run rails s in the foreground on container launch
CMD ["rails", "s", "-b 0.0.0.0", "-p 3000 "]

That's it for dockerfile compositions. This specific dockerfile be at the root of your source code. This is an example, you may still need to install other library dependencies like libpq but these can easily be install with the usual apt-get commands, before the gem installation as shown above just replace /whatever else you need/ with whatever else you may actually need.

Step three: Define docker-compose.yml

At this stage we want to do multiple things, some of which you did right initially:

  1. Link the rails and postgres conatiners. (Which you did)
  2. Mount a volume for the rails container, to allow changing the source as the rails server runs.(Tried to do)
  3. Mount a volume for the postgres conatiner to keep the data persistent (Which you did). Mounting a volume for the socket is also good, it allows you to postgres directly with the host.

Your compose file should then look really similar to the original.

version: '3'

volumes:
  # We'll define a volume that will store the data from the postgres databases:
  postgres-data:
    driver: local

services:
  # Our PostgreSQL service:
  postgres:
    image: mdillon/postgis:9.3

    ports:
      # We'll bind our host's port 5432 to postgres's port 5432, so we can use
      # our database IDEs with it:
      - 5432:5432

    volumes:
      # Mount the DB dumps folder into the container, to be able to create & access database dumps:
      - ./db/dumps:/db/dumps
      # Mount out tmp folder, we might want to have access to something there during development:
      - ./tmp:/tmp
      # We'll mount the 'postgres-data' volume into the location Postgres stores it's data:
      - postgres-data:/var/lib/postgresql/data
      # We'll mount the postgres socket just like before         
      - /var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock

    environment:
      POSTGRES_PASSWORD: password

  rails:
    build:
      context: .
      dockerfile: Dockerfile

    entrypoint: rails s -p 3000 -b 0.0.0.0

    volumes:
      # Mount our app code directory (".") into our app containers at the
      # "/src" folder:
      - .:/src    

    # Keep the stdin open, so we can attach to our app container's process and do things such as
    # byebug, etc:
    stdin_open: true

    # Enable sending signals (CTRL+C, CTRL+P + CTRL+Q) into the container:
    tty: true

    # Link to our postgres and redis containers, so they can be visible from our
    # app containers:
    links:
      # We'll include a link to the 'db' (postgres) container, making it visible from the container
      # using the 'db.local' hostname (which is not necessary, but I'm doing it here to
      # illustrate that you can play with this):
      - postgres:db.local

    environment:
      # Run the app in the 'development' environment:
      RACK_ENV: development
      RAILS_ENV: development

    depends_on:
      - postgres

With that config running docker-compose up should run seamlessly but before you do you should first configure your database.yml

Step four: database.yml

defaults: &defaults
  adapter: postgresql
  # template: template_postgis
  encoding: unicode
  pool: 5
  username: root
  password: password
  host: db.local

development:
  <<: *defaults
  database: realty_development

test:
  <<: *defaults
  database: realty_test

staging:
  <<: *defaults
  database: realty_development

production:
  <<: *defaults
  database: realty_production

That should be it. but in reality you only need to configure the development settings, since that's the environment defined in the docker-compose.yml, edit it if you wish.

CONCLUSION

Please read the docker and docker-compose documentation once again because all this is part of basic usage and is in several tutorials and repositories.

Clive Makamara
  • 2,845
  • 16
  • 31
  • Thanks Clive; that's a lot of really good information. I am indeed a Docker newb; this was handed to me as an initial foray into Docker, I think with the assumption that it would be an easy change to create a postgres container that would allow the database to persist. I think part of the clusterfuck here is that this is a codebase that we inherited (danger #1), and then we converted this from Vagrant to Docker (danger #2). This is indeed setting up a dev environment for myself and others. It may later be used as a Docker container in AWS, but that is not a concern right now. – Jon Ohliger Oct 23 '17 at 17:35
  • I'll let you know how this goes and get back to you. – Jon Ohliger Oct 23 '17 at 17:36