In a previous article I have written
on how easy it is to stand up a test environment of OpenShift. In this article
I will describe an example application from the sourcecode to the created image
and how this gets deployed. The steps are explained using manual steps, and how
OpenShift does it all automated. You will notice, at no point do you have to
write a Dockerfile
.
Preparation
For this article it is not necessary to have a working test environment, however
it does make things clearer. I would suggest you to use OpenShift Origin v1.3 on
CentOS 7. Although my previous article showed how to get it up and running on
Fedora 24, I experienced an issue with deployment not succeeding*.
The steps in the deployment article can be performed by replacing dnf
with
yum
.
Description of the example
The OpenShift project publishes several test applications on GitHub, of which one is a very simple Ruby applica8080. Please, have a look at: http://github.com/openshift/ruby-ex
You will see it consists of four files:
Gemfile
Gemfile.local
config.ru
README.md
The application itself is only described in config.ru
and the needed dependencies
are in Gemfile
.
Dependencies
To make the application work, we first need:
$ gem install bundler
This will install bundler
that can install dependencies as described in the
Gemfile
. This file describes:
source 'https://rubygems.org'
gem 'rack'
gem 'puma'
The first line says source
which points to a gem repository, and each line
starting wih gem
are bundled packages containing libraries for use in your
project. To install all of the required gems (dependencies) from the specified
sources:
$ bundle install
The file Gemfile.lock
is a snapshot of the Gemfile and is used internally.
config.ru
The application is specified in the file called config.ru
. If you open the
file you will see it contains route mappings, lines starting with map
, for
three urls:
/health
/lobster
/
/health
This is a commonly used to provide a simple health-check for applications that
are automatically deployed. It allows to quickly test if the application got
deployed. In projects I worked on, we also did quick dependency checks, such as
a configuration file exists, or another needed endpoint is available. In this
application it will respond with a HTTP status code 200 and returns 1
as
value.
/lobster
This is a test provided by rack. It shows an ASCII-art lobster. By adding a
variable to the URL querystring ?flip=left
the direction can be changed.
/
This is the mapping to a bare route. It shows a greeting message on how to use the application using OpenShift to trigger automated builds.
Rackup
Rack is an interface for using Ruby and Ruby frameworks with webservers. It
provides an application called rackup
to start the application:
$ bundle exec rackup -p 8080 config.ru
Using this command the webserver will bind to port 8080, according to the
description in the config.ru
file. To see what the mappings do, open:
Use the example with OpenShift
Deploying an application on OpenShift from source is very simple. A single command can do this. First have a look
$ oc new-app openshift/ruby-20-centos7~https://github.com/[username]/ruby-ex
But before we do, I will explain what this command does. Oversimplified OpenShift does two things:
- Build
- Deploy
Note: If you want to perform the command, go ahead. Please fork the repository
and change the [username]
in this command.
Build: source to image
OpenShift runs container images which are in the Docker format. It will run
the CMD
instruction for this. So, how does OpenShift know what to run?
Convention. Most frameworks have a standard way of doing things, and this is
as you noticed also the case with the Ruby example. The creation of the image
happens with a tool called source-to-image (S2I).
Source-to-Image (S2I) is a toolkit and workflow for building reproducible Docker images from source code. It uses a base image, and will layer the application on top, configures the runn command, which then results in a containter image for use.
$ s2i build https://github.com/[username]/ruby-ex openshift/ruby-20-centos7 ruby-ex
base image
The base image here is openshift/ruby-20-centos7. The source of this image can be found at the following GitHub repository: s2i-ruby-container
If you look at the Dockerfile
source, you will see
Software Collections is used to install a specific Ruby version.
In this case version 2.0. Software collections solves one of the biggest
complaints of using CentOS (or RHEL) as a basis as part of your delivery. It
allows you to use multiple versions of software on the same system, without
affecting system-wide installed packages.
The image also describes a label io.openshift.expose-services="8080:http"
which inidcate that the application on port 8080 will be exposed as HTTP
traffic. This also means the container does not need root privileges as the port
assignment is above 1024. The application itself will be installed into the
folder: /opt/app-root/src
.
Running this container can be done with:
$ docker run -p 8080:8080 ruby-ex
[1] Puma starting in cluster mode...
[1] * Version 3.4.0 (ruby 2.0.0-p645), codename: Owl Bowl Brawl
[1] * Min threads: 0, max threads: 16
[1] * Environment: production
[1] * Process workers: 1
[1] * Phased restart available
[1] * Listening on tcp://0.0.0.0:8080
[1] Use Ctrl-C to stop
[1] - Worker 0 (pid: 32) booted, phase: 0
Open the links as previously stated will yield the same results.
$ curl http://localhost:8080/health
The build process can be as simple as a copy for static content, to compiling Java or C/C++ code. For the purpose of this article I will not explain more about the S2I process, but this will certainly be explained in future articles.
New application
If we now look at the previous command again:
$ oc new-app openshift/ruby-20-centos7~https://github.com/[username]/ruby-ex
you can clear see the structure. The first element openshift/ruby-20-centos7
describes the S2I container image for Ruby as hosted at the Docker hub. The
second part is the source code path pointing to a git repository.
Please try the command now... OpenShift will create containers for each of the
stages used: build
, deploy
and the final running container. You can check
the containers using the command:
$ oc get pod
NAME READY STATUS RESTARTS AGE
ruby-ex-1-build 0/1 Completed 0 1m
Build stage
If you create this new application, a new container named ruby-ex-1-build
.
What happened is that the Source-to-image container got pulled which uses the
base image and layers the source code on top.
To see what happened, as with the previous command, you can see the build configuration:
$ oc logs bc/ruby-ex
Cloning "https://github.com/gbraad/ruby-ex" ...
Commit: f63d076b602441ebd65fd0749c5c58ea4bafaf90 (Merge pull request #2 from mfojtik/add-puma)
Author: Michal Fojtik <[email protected]>
Date: Thu Jun 30 10:47:53 2016 +0200
---> Installing application source ...
---> Building your Ruby application from source ...
---> Running 'bundle install --deployment' ...
Fetching gem metadata from https://rubygems.org/...............
Installing puma (3.4.0)
Installing rack (1.6.4)
Using bundler (1.3.5)
Cannot write a changed lockfile while frozen.
Your bundle is complete!
It was installed into ./bundle
---> Cleaning up unused ruby gems ...
Pushing image 172.30.108.129:5000/myproject/ruby-ex:latest ...
Pushed 0/10 layers, 10% complete
Pushed 1/10 layers, 34% complete
Pushed 2/10 layers, 49% complete
Pushed 3/10 layers, 50% complete
Pushed 4/10 layers, 50% complete
Pushed 5/10 layers, 50% complete
Pushed 6/10 layers, 61% complete
Pushed 7/10 layers, 71% complete
Pushed 8/10 layers, 88% complete
Pushed 9/10 layers, 99% complete
Pushed 10/10 layers, 100% complete
Push successful
The difference is that the resulting image will be placed in the myproject
namespace, and pushed to the local repository.
Deployment stage
After the image has been composed, OpenShift will run the container image on the scheduled node. What happens here can be checked with:
$ oc get pod
NAME READY STATUS RESTARTS AGE
ruby-ex-1-an801 1/1 Running 0 26s
ruby-ex-1-build 0/1 Completed 0 1m
This means that the build succeeded, the image got deployed and now runs in the
a container identified with ruby-ex-1-an801
. Note: The container
ruby-ex-1-deploy
is not shown here as only the logs are of importance.
The deployment configuration logs can be shown with:
$ oc logs dc/ruby-ex
[1] Puma starting in cluster mode...
[1] * Version 3.4.0 (ruby 2.0.0-p645), codename: Owl Bowl Brawl
[1] * Min threads: 0, max threads: 16
[1] * Environment: production
[1] * Process workers: 2
[1] * Phased restart available
[1] * Listening on tcp://0.0.0.0:8080
[1] Use Ctrl-C to stop
[1] - Worker 0 (pid: 32) booted, phase: 0
[1] - Worker 1 (pid: 35) booted, phase: 0
Events
To see the flow of execution, you can have a look at:
$ oc get events
This can be helpful if an error occured.
Verify
Now that the application has been deployed on OpenShift, we need to look up the IP address that has been assigned. For this we use:
$ oc get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ruby-ex 172.30.91.160 <none> 8080/TCP 21h
Now we can open the application as http://172.30.91.160:8080/
Conclusion
OpenShift allows you to run prebuilt images or applications based on source.
The Source-to-image tooling makes it possible to create reproducible images for
deployment of applications based on source. This tool itself is very helpful
and is certainly something I will be using, even outside the use of OpenShift.
There is no need to create or modify a Dockerfile
, which means that the
developer can focus on the development process.
If you want to know more about the automated builds, please have a look at the README of the Ruby example. In future articles more detailed descriptions about these topics will certainly be given. I hope this has been helpful. Please consider leaving feedback or tweet this article.