Build great JRuby apps with Docker
Docker is an exciting piece of technology. You can build a ready to use Linux containers by just just writing all your dependencies / packages declared in an easy to read text file.
Since a lot of sites have Docker introduction covered we will start right off with a Dockerfile to setup a JRuby + Rails app. You can put this file in your Rails project so that it is version managed like the rest of the code.
FROM phusion/baseimage:0.9.13
# Set correct environment variables.ENV HOME /root
# Use baseimage-docker's init system.CMD ["/sbin/my_init"]
#Install Oracle JDK 8 and Node.jsRUN sudo add-apt-repository ppa:webupd8team/javaRUN sudo add-apt-repository ppa:chris-lea/node.js && sudo apt-get update#This absurdity exists so that JDK is installed without prompting for licenseRUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections
RUN sudo apt-get install -y --no-install-recommends oracle-java8-installerRUN sudo apt-get install oracle-java8-set-defaultRUN sudo apt-get install -y nodejsRUN sudo apt-get autoremove -y && apt-get clean
ENV JRUBY_VERSION 1.7.15#Get JRubyRUN curl http://jruby.org.s3.amazonaws.com/downloads/$JRUBY_VERSION/jruby-bin-$JRUBY_VERSION.tar.gz | tar xz -C /opt
ENV PATH /opt/jruby-$JRUBY_VERSION/bin:$PATH
RUN echo gem: --no-document >> /etc/gemrc
RUN gem update --systemRUN gem install bundler
#Add userRUN addgroup --gid 9999 appRUN adduser --uid 9999 --gid 9999 --disabled-password --gecos "Application" appRUN usermod -L appRUN mkdir -p /home/app/my_appADD . /home/app/my_appRUN chown -R app:app /home/app/
USER app
#JRuby optionsENV PATH /opt/jruby-$JRUBY_VERSION/bin:$PATHRUN echo compat.version=2.0 > /home/app/.jrubyrcRUN echo invokedynamic.all=true >> /home/app/.jrubyrc
#Get Rails runningWORKDIR /home/app/my_appENV RAILS_ENV stagingRUN bundle exec rake db:resetRUN bundle install --deployment --without test development###################################################################Remove these lines when using fig since fog will start the server##################################################################RUN bundle exec rake db:resetEXPOSE 3000ENTRYPOINT bundle exec rails server
The file is pretty self explanatory. To get the craziness that is called Oracle Java 8 working, you also need an additional Rails initializer in your project with the following code which I plucked from StackOverflow -
require 'java'java_import 'java.lang.ClassNotFoundException'
begin security_class = java.lang.Class.for_name('javax.crypto.JceSecurity') restricted_field = security_class.get_declared_field('isRestricted') restricted_field.accessible = true restricted_field.set nil, falserescue ClassNotFoundException => e # Handle Mac Java, etc not having this configuration setting $stderr.print "Java told me: #{e}n"end
To get a Docker image from this file run -
sudo docker build --rm=true --no-cache=false -t rockyj/app_name .
Now you have a Docker image that you can run as a container -
sudo docker run -tid -p 3000:3000 --name app_name --link db:db rockyj/app_name
This will get you a running Rails app as long as you use a DB like sqlite. To run this on PostgreSQL we need another container running Postgres and we need to link the app and db containers together.
To do this we will use Fig which makes our lives super easy. First let us write a Dockerfile for our Postgres image -
FROM phusion/baseimage:0.9.13
# Set correct environment variables.ENV HOME /root
# Use baseimage-docker's init system.CMD ["/sbin/my_init"]
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update -qq
RUN apt-get install wget -qq
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main 9.3" > /etc/apt/sources.list.d/pgdg.list
RUN gpg --keyserver keys.gnupg.net --recv-keys ACCC4CF8
RUN gpg --export --armor ACCC4CF8|apt-key add -
RUN apt-get update
# If you use another language, change the package. e.g. language-pack-es# and run update-locale with language variable.RUN apt-get install language-pack-en -y
RUN update-locale LANG=en_US.UTF-8 LC_MESSAGES=POSIX
RUN apt-get install postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3 -y
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/
USER postgres
RUN /etc/init.d/postgresql start &&\ psql --command "ALTER USER postgres with password 'foobar';"
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
EXPOSE 5432
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
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"]
Build the Docker image for this as well -
sudo docker build --rm=true --no-cache=false -t rockyj/postgres .
Now with fig, let us link the two containers, in RAILS_PROJECT/fig.yml -
db: image: rockyj/postgres ports: - "5432"web: image: rockyj/app_name command: bundle exec rails s ports: - "3000:3000" links: - db
With fig installed, now all you need to run is -
sudo fig buildsudo fig up#in another terminal windowsudo fig run web bundle exec rake db:createsudo fig run web bundle exec rake db:migrate#when you want to stopsudo fig stop
That is it! You now have two containers, running Rails and PostgreSQL talking to each other. The Rails container runs on JDK8 and the latest JRuby (with invoked-dynamics on). Finally, as some homework you can persist the data of the Postgres container so that it survives restarts (using Docker volumes). Enjoy super easy JRuby+Rails deployments with Docker.