Rails Applicatoin - Openshift 3.0 Deployment

Prepare helper scripts for application setup, app start and httpd config

Helper scripts can be stored as part of application code in directory scripts in project root directory.

Sample application setup script

# scripts/application_setup.sh

#! /bin/bash
bundle exec rake db:migrate
bundle exec rake db:seed

Sample applicaiton startup script

# scripts/appStartup.sh

#!/bin/bash
bundle exec rake assets:precompile

config-httpd
exec httpd -D FOREGROUND

Sample script to configure httpd.conf

# scripts/config_httpd.sh

#! /bin/sh

sed -i "s/RailsEnv RAILS_ENV/RailsEnv ${RAILS_ENV}/" /etc/httpd/conf/httpd.conf
echo "PassengerMinInstances 12" >> /etc/httpd/conf/httpd.conf
echo "PassengerMaxPoolSize 12" >> /etc/httpd/conf/httpd.conf
ruby -S passenger-config build-native-support

httpd.conf for passenger apache config will include VirtualHost and passenger configs ie. PassengerRoot, PassengerRuby etc.

Docker Image build with Rails application setup

Sample Dockerfile to build image with rails app. Dockerfile will reside in project root directory.

FROM centos/passenger-40-centos7
RUN mkdir -p /opt/app-root/bundle
COPY ./Gemfile /opt/app-root/bundle/Gemfile
COPY ./Gemfile.lock /opt/app-root/bundle/Gemfile.lock

# install sqlite3 on same app image as db, this should be run as seperate docker container service
RUN sudo apt-get install -y sqlite3 libsqlite3-dev

WORKDIR /opt/app-root/bundle
RUN scl enable rh-ruby25 'bundle install --deployment --without capistrano:development:test:int_test --jobs=4'
RUN chmod -R g+w /opt/app-root/bundle/

# Copy application code
COPY . /opt/app-root/src

WORKDIR /opt/app-root/src
# Ensure log directory writable, move in the bundle, 
# set app's default group as root, and put the app startup script in place
RUN chmod -R g+w /opt/app-root/src/log /opt/app-root/src/db /opt/app-root/src/config /opt/app-root/src/public
RUN mkdir -p /opt/app-root/src/tmp
RUN chmod 777 /opt/app-root/src/tmp /opt/app-root/src/log /opt/app-root/src/public
RUN ln -s /opt/app-root/bundle/.bundle /opt/app-root/src/.bundle
RUN mkdir -p /opt/app-root/src/vendor
RUN ln -s /opt/app-root/bundle/vendor/bundle /opt/app-root/src/vendor/bundle

COPY scripts/application_setup.sh /usr/bin/application_setup.sh
COPY scripts/appStartup.sh /usr/bin/appStartup.sh
COPY scripts/config_httpd.sh /usr/bin/config_httpd.sh

COPY scripts/httpd.conf /etc/httpd/conf/httpd.conf

CMD ["/usr/bin/appStartup.sh"]

Build Docker image using Dockerfile and push the image to docker image registry

Openshift Deployment config

Using this deployment template we can build a deployment on openshift, these template files can be part of source code in template direcotry of project root dir.

     oc process -f templates/deployment_config.yml | oc create -f -

Sample DeploymentConfig template

  # templates/deployment_config.yml

  apiVersion: v1
  kind: DeploymentConfig
  metadata:
    name: sample-rails-app
    labels:
      app: sample-rails-app
  spec:
    strategy:
      type: Recreate
      recreateParams:
        timeoutSeconds: 600
        mid:
          failurePolicy: Abort
          execNewPod:
            command:
            - application_setup.sh
            containerName: sample-rails-app
    triggers:
    - type: ConfigChange
    - type: ImageChange
    replicas: 1
    selector:
      app: sample-rails-app
      deploymentconfig: sample-rails-app
    template:
      metadata:
        name: sample-rails-app
        labels:
          app: sample-rails-app
          deploymentconfig: sample-rails-app
      spec:
        containers:
        - name: sample-rails-app
          image: 'docker.hub.com/railsapp/sample-rails-app:0.1'
          ports:
          - containerPort: 8080
          env:
          - name: RAILS_ENV
            value: dev
        restartPolicy: Always

Using service template a service can be created that will be mapped to deployment pods.

    oc process -f templates/service_config.yml | oc create -f -

sample Service template

  # templates/service_config.yml    
  apiVersion: v1
  kind: Service
  metadata:
    name: sample-rails-app
    labels:
      app: sample-rails-app
  spec:
    ports:
    - name: web
      port: 8080
      targetPort: 8080
    selector:
      app: sample-rails-app
      deploymentconfig: sample-rails-app

Using routes services will be exposed to external clients, service will be mapped to a route and route will be available to external world.

oc process -f templates/route_config.yml | oc create -f -

sample Route template

  # templates/route_config.yml
  apiVersion: v1
  kind: Route
  apiVersion: v1
  metadata:
    name: sample-rails-app
    labels:
      app: sample-rails-app
  spec:
    host: sample-rails-app.openshift-cluster.com
    to:
      kind: Service
      name: sample-rails-app