What Does "Docker Native" Really Mean?

Wercker is a Docker-Native CI/CD Automation platform for Kubernetes & Microservice Deployments

Zachary Flower
Zachary Flower
April 28, 2017

Docker is an increasingly ubiquitous tool used by hobbyists and enterprises alike, but despite that popularity, many organizations fail to utilize the containerization platform to its full potential.

Typically, Docker is only used in one part of the product lifecycle. It facilitates atomic deployments, provides a repeatable development environment, hosts a non-legacy microservice, or runs the CI/CD pipeline; but, far less frequently, it is incorporated into every part of the application development workflow.

When first introduced to Docker, most organizations focus on the most immediate benefits it brings to one aspect of their workflow. Take the CI/CD process, for example. Platforms like Wercker use Docker to enable highly customizable testing environments, and maximize efficiency within their own stacks, but what about what happens before and after the test process? How can Docker be effectively used across the entire development to deployment workflow?

 

Parity, parity, parity

The biggest advantage to using Docker across the entire stack is cross-environment parity. Because of Docker's developer-friendly configuration protocol, ensuring a consistent server environment across individual developer machines and testing and production environments is incredibly straightforward.

As an example, take a look at the following Dockerfile from the Docker documentation.

 

FROM ruby:2.3.3

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /myapp

WORKDIR /myapp

ADD Gemfile /myapp/Gemfile
ADD Gemfile.lock /myapp/Gemfile.lock

RUN bundle install

ADD . /myapp

 

In a nutshell, this Dockerfile provisions an image with the bare minimum necessary to run a Ruby on Rails application, which can be mirrored exactly across every environment that needs it.

 

Docker, meet Wercker

Unfortunately, because many CI/CD platforms run on Docker, many of them have a policy against direct Dockerfile usage. As a result, platforms like Wercker have created safe wrappers (such as Wercker's wercker.yml file format) to facilitate a proper end-to-end Docker workflow.

Let's assume that the example Dockerfile above is representative of a Docker base image called (for lack of a better name) dockery/railsy. Rather than using a Dockerfile to build a new Docker image, Wercker can import a base box using the box command in the wercker.yml file:

box: dockery/railsy 

 

Then, any additional steps that need to be taken can be handled using Wercker's own step syntax:

 

 build:
 # The steps that will be executed on build
 steps:
   # A step that executes `npm install` command
   - npm-install
   # A step that executes `npm test` command
   - npm-test

   # A custom script step, name value is used in the UI
   # and the code value contains the command that get executed
   - script:
       name: echo nodejs information
       code: |
         echo "node version $(node -v) running"
         echo "npm version $(npm -v) running" 

 

The above code, when combined with the box directive outlined prior, runs some basic commands on the imported base box. While a pretty basic example, it illustrates how the base box will be provisioned, like any other Docker image.

 

Docker in development

While the parity described above may seem trivial on its own, when combined with a complex microservices architecture, Docker enables engineers to quickly spin up development environments with little more than a few commands. This can have an incredibly positive impact on new employee onboarding and shared knowledge across the organization.

To make this process even easier, the Wercker CLI has the ability to spin up a development environment using the same syntax outlined above. By defining a dev block in the pipeline, a Docker-based development environment can be spun up using only the wercker dev command.

 

 dev:
 steps:
   - npm-install
   - internal/watch:
       code: node app.js
       reload: true

 

Because development environments operate differently, but should use the same base image, this block would outline the bare minimum steps necessary to get a working development environment.

 

Docker in integration

Perhaps the most crucial place for environmental parity within an application is between the test and production environments. Integration (the CI part of CI/CD) involves automatically building and testing code with every change, so ensuring that the testing environment mimics the production environment is paramount. Running a test suite is effectively rendered pointless if a disparity between these two environments causes disparate test results.

This is where the build block shown above would come into play. Let's take a look at that block again:

 

 build:
 # The steps that will be executed on build
 steps:
   # A step that executes `npm install` command
   - npm-install
   # A step that executes `npm test` command
   - npm-test

   # A custom script step, name value is used in the UI
   # and the code value contains the command that get executed
   - script:
       name: echo nodejs information
       code: |
         echo "node version $(node -v) running"
         echo "npm version $(npm -v) running"

 

When the build step in the pipeline is run, all of the steps necessary to build and test an application are executed. If successful, the built image is preserved, which brings us directly to the next step—deployment.

 

Docker in deployment

The second half of the CI/CD coin is deployment. Rather than simply establishing similarity between the test and production environments, delivering a successfully built Docker image directly to a production environment after the test suite passes ensures absolute parity.

 

 deploy:
 steps:
   - internal/docker-push:
       username: $USERNAME
       password: $PASSWORD
       tag: latest
       repository: company/image
       registry: https://registry.hub.docker.com

 

Deployment of a Docker image with Wercker is accomplished with the internal/docker-push step. If successful, the provisioned image in the build block can be pushed directly up to a Docker image repository like Docker Hub. This allows you to launch the exact image that was successfully built, ensuring absolute parity between test and production environments.

 

Putting it all together

With Wercker's wercker.yml configuration file, an end-to-end Docker workflow can provide parity between all environments in the product development pipeline. From development to testing to production, being truly Docker native provides a level of productivity and reliability that is difficult to achieve with other solutions.

 

About the Author

Zachary Flower is a freelance web developer, writer, and polymath. He has an eye for simplicity and usability, and strives to build products with both the end user and business goals in mind. From building projects for the NSA to creating features for companies like Name.com and Buffer, Zach has always taken a strong stand against needlessly reinventing the wheel, often advocating for the use of well established third-party and open source services and solutions to improve the efficiency and reliability of a development project.

 

Like Wercker?

We’re hiring! Check out the careers page for open positions in Amsterdam, London and San Francisco.

As usual, if you want to stay in the loop follow us on twitter @wercker or hop on our public slack channel. If it’s your first time using Wercker, be sure to tweet out your #greenbuilds, and we’ll send you some swag!       

 

Topics: Containers, Tutorials