March 13, 2015


Updating Old Production Applications with Docker

Software Development Startups Co-Building

When working on continuously expanding projects over long periods of time, software developers often come across code that must be updated in order for the project to progress. If the code is outdated, preparing the environment and launching certain applications become problematic. Docker is an open-source, virtual environment tool primarily used on Linux and OS6 applications that allows the developer to more quickly update and deploy apps inside various virtual software containers. These containers do not require separate operating systems. They access the Linux kernel’s features, meaning several containers can be run simultaneously by a single server.

 

Docker further speeds up deployment by providing consistent development and production environments. All members of the team can use the same system libraries, language run-times, and OS. It supports multiple language versions, including Python, Ruby, Java, and Node, meaning developers can run a script inside a Docker image, rather than installing various languages and running scripts separately. Docker’s “Copy-on-Write” mechanism duplicates any requested code in a standard UNIX pattern. Developers can also begin running a container in less than 0.1 seconds, and the entire container requires less than 1 MB of disc-space. These are significant improvements over standard VMs.

 

To help developers take advantage of these many benefits, this article shows how to update an old production application with Docker. The app uses a non-standard Sphinx build, wkhtmltopdf, NPM, and Resque. They will initially be kept within the application and then moved to their own containers.

 

The goals are to:

  • Keep 100% forward compatibility with devs not using Docker.
  • Make the app use environment variables for configuration.
  • Build Sphinx, install Node.js, and wkhtmltopdf in a basic container.
  • Keep the app and Resque in the same container.
  • Simplify the Procfile.
  • Create a base Docker-composed configuration file and extend it for development and CI envs.

 

Step 1: Setup

 

Here is what you’ll need to install Docker and its NFS plug-in for OSX Native, OSX Brew and VirtualBox, or Ubuntu.

 

For OSX Native: https://docs.docker.com/docker-for-mac/

 

For OSX via Brew and VirtualBox:

 

 

For Ubuntu: (install docker) (install docker-compose)

 

Now that you have Docker installed, you can run the application on it. Docker apps are configured via a Dockerfile, which defines how the container is built.

 

Step 2: Unify File Configuration

 

Assume the app you are updating has hard-coded config files under the version control. When running the app in Docker, the secrets will no longer be stored inside the image.

 

When modifying this file to unify the configuration, Hash#fetch helps return default value unless it already exists in the ENV variable:

 

 

 

 

Step 3: Run Dockerfile

 

This app uses light-weighted Ruby: 2.1 as the base image and changes BUNDLE_PATH to get cache via volume (https://docs.docker.com/engine/reference/builder):

 

 

 

Step 4: Install NPM and Node.js via Node Version Manager

 

At this point, you are explicitly specifying the Node libraries. As mentioned, the application is using a non-standard Sphinx build. Therefore, you can’t use already existing Docker images for this purpose:

 

 

Step 5: Containerize the App

 

This step is a temporary solution to keep Sphinx within the application. When you are preparing for production release, Sphinx must be moved to a separate container:

 

 

During this step, you also need to clean up extraneous data to reduce the image size:

 

 

Step 6: Mount Bundled Gems Folder Inside Container

 

Docker supports mount points called “volumes” which let you access data from either the native host or another container. In this case, you can mount your bundled gems folder inside the container:

 

 

(To learn more about BUNDLE_PATH and why gems must be installed as the last step, see: https://medium.com/@fbzga/how-to-cache-bundle-install-with-docker-7bed453a5800)

 

Step 7: Install Node Packages

 

When using Docker to perform this step, the NPM is invoked with root privileges, so it will change the UID to the user account or to the UID specified by the user config, which, in this case, defaults to nobody.

 

Start by setting the unsafe-perm flag to run scripts with root privileges:

 

 

(For more details on running scripts with root privileges, see: https://docs.npmjs.com/misc/scripts#user)

 

During the last part of this step, you add the app’s code, expose the internal 3000 port, and process any incoming commands thought bin/docker-entrypoint.sh:

 

 

Step 8: Create docker-compose.yml File

 

Before executing any code in the app’s container, you need to make sure that the old PID files have been deleted and the database is available:

 

 

Now you’ll use wait-for-mysql.sh to hold the execution until the DB is ready:

 

 

Next, you need to integrate the app with Mysql, Mongo, Redis, volumes and expose the env variables.

 

As an additional step, you can create docker-compose-base.yml which will be extended in the compose file for dev and ci/prod.

 

Many of the images on Dockerhub accept env variables for configuration. In this case, you want to config mysql’s root user and expose the data in the application container, as well as in the Redis and Mongo URLs:

 

 

The docker-compose.yml includes the application service, along with the images for Mysql, Redis and Mongo:

 

 

 

At this point, you have completed all the steps needed to create your new Docker container and can move on to starting and running the application.

 

Step 9: Start and Run the Application

 

Your entry point:

 

 

Point your browser to: http://localhost:3000 or http://192.168.99.100:3000.

 

The Docker default machine’s IP is 192.168.99.100. To make sure your IP is correct, run:

 

 

If you want to bash to the app container or run custom command, don’t forget to enable and map services to the host by passing the “service-ports” option to RUN command:

 

 

Here’s what you need to run the specs:

 

 

Step 10: Prepare to Release the App to Production

 

  1. Move applications logs to STDOUT/STDERR. During the updating process, logs will be saved to the container’s default file system.
  2. Integrate this STDOUT/STDERR log file into a centralized logging system. This is usually a 3rd party service.
  3. Move Sphinx, Node.js, Resque, wkhtmltopdf and all schedule-based tasks and processes to their own containers.
  4. Create a compose file for CI and prod.

 

Containerizing outdated apps with Docker is becoming increasingly popular due to its many benefits. It speeds up the entire development process while improving portability, transparency, and security.

Software Development Startups Co-Building

Latest Insights in Software Development

The Rise of Kotlin – Moving Away from Java for Android Development

Kotlin is a programming language for the Java Virtual Machine that’s able to be used in any scenarios that currently…

Introducing our Sphere Heroes Program – Artem Korenev – Employee of the month

At Sphere, employee recognition is a key component of our corporate culture. We believe in celebrating the successes of our…

Write For Sphere

Are you a writer with tech expertise? Then we want to hear from you! Here are a few guidelines for…

View All Articles arrow

We are here to help:

checkmarkto become a customer checkmarkto become an investor checkmarkto send a media inquiry checkmarkto join our team checkmarkto simply say ‘hi’
Get in Touch