Binary Deployments with OpenShift 3

Binary Deployments

OpenShift 3 offers a S2I (Source to Image) process to build applications where you can supply source code (your application source) and choose a builder image (a technology docker image such as JBoss EAP). S2I will build your application first and then layer your application on the top of the builder image to create an application docker image. This application docker image is then added to the repository and deployed as your application. This is all good for a complete DevOps model implemented by default within OpenShift.

Some of the OpenShift users want to build their application outside of OpenShift, perhaps using a build management tool that they already use, and use the resultant war file to deploy into OpenShift. We used to call this binary deployment in OpenShift v2 and I am using the same name here. This article discusses the approaches for binary deployment of the application war files that are built outside of OpenShift into the OpenShift 3 environment.

There are a few ways to deploy your war file into OpenShift 3 environment and you can choose whatever works best for your situation. OpenShift 3 being so very flexible, there may be additional ways to achieve this. I encourage the readers to list those in the comments as you discover more ways to do the same.

Note: I have packaged these examples and posted on github so that you can try them out. Hence, the tone of the rest of this blog is set like a tutorial or a lab exercise. In order to run these examples, you will need an OpenShift 3 environment and the OC CLI installed on your box. You should have connected to your OpenShift 3 environment using oc login. You can change the hostnames based on the specifics of your setup.

Docker Build Method

As the first approach we will discuss the docker build method. In this process we won’t use S2I but instead use a docker file to deploy a .war file into OpenShift. Let us look at the example below.

Here I am deploying a previously created petstore.war file. I have a git repository for my application that only has a Dockerfile with the following contents:

FROM registry.access.redhat.com/jboss-eap-6/eap-openshift:6.4

EXPOSE 8080 8888

RUN curl https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war -o $JBOSS_HOME/standalone/deployments/ROOT.war

All this does is use the jboss-eap-6 image provided by OpenShift and add my .war file to the /deployments/ folder. When you create an application using this Dockerfile, OpenShift invokes a docker build to create your application image and deploy it. Lets look at the steps:
 
Step 1:  Let us start by creating a new project. I am naming it petstore.

$ oc new-project petstore

 

Step 2: Create a new application within that project using the Dockerfile. You will see below that I am pointing to my git repository while creating the application. Since my repository contains a Dockerfile, OpenShift understands that it has to do a Docker Build. I am naming my application petstore again here.

$ oc new-app https://github.com/VeerMuchandi/dockrbld-bin-dep.git  --name=petstore

imagestreams/petstore
buildconfigs/petstore
deploymentconfigs/petstore
services/petstore
A build was created - you can run `oc start-build petstore` to start it.
Service "petstore" created at 172.30.10.221 with port mappings 8080->8080, 8888->8888.

You will notice that the above step also created a service with the name petstore.

If you get a warning message in the above step such as the one shown below, you can ignore it.

Could not find an image match for "registry.access.redhat.com/jboss-eap-6/eap-openshift:6.4". Make sure that a Docker image with that tag is available on the OpenShift node for the build to succeed.

 

Step 3: Now let’s add a route that exposes this service with the url petstore.testv3.osecloud.com:

$ oc expose service petstore --hostname=petstore.testv3.osecloud.com

NAME       HOST/PORT                      PATH      SERVICE    LABELS

petstore   petstore.testv3.osecloud.com             petstore

 

Step 4: Next we will start a build by issuing a start-build command as shown below. It also displays the name assigned to the running build.

$ oc start-build petstore

petstore-1

 

You can watch the build logs as the build is in progress. You will notice that OpenShift runs what we listed in the Dockerfile. It gets the builder image for jboss-eap-6 from registry, downloads the .war file and adds it to the $JBOSS_HOME/standalone/deployments folder. It then creates a new application image and pushes into the docker registry configured in OpenShift.

$ oc build-logs petstore-1

I0710 16:53:55.480823       1 cfg.go:50] Problem accessing /root/.dockercfg: stat /root/.dockercfg: no such file or directory

I0710 16:53:57.621261       1 docker.go:69] Starting Docker build from petstore/petstore-1 BuildConfig ...

I0710 16:53:57.625443       1 tar.go:133] Adding to tar: /tmp/docker-build051447222/Dockerfile as Dockerfile

Step 0 : FROM registry.access.redhat.com/jboss-eap-6/eap-openshift:6.4

---> 85765d60ad46

Step 1 : EXPOSE 8080 8888

---> Running in 41ca38853cfb

---> 113b97247c5d

Removing intermediate container 41ca38853cfb

Step 2 : RUN curl https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war -o $JBOSS_HOME/standalone/deployments/ROOT.war

---> Running in f0424ebe8bb6

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

>Dload  Upload   Total   Spent    Left  Speed

100 6011k  100 6011k    0     0   307k      0  0:00:19  0:00:19 --:--:-- 1459k

---> 63908bcc750f

Removing intermediate container f0424ebe8bb6

Step 3 : ENV OPENSHIFT_BUILD_NAME "petstore-1" OPENSHIFT_BUILD_NAMESPACE "petstore" OPENSHIFT_BUILD_SOURCE "https://github.com/VeerMuchandi/dockrbld-bin-dep.git"

---> Running in c16f2fc92da8

---> 292ebddba482

Removing intermediate container c16f2fc92da8

Successfully built 292ebddba482

I0710 16:54:51.511380       1 cfg.go:46] PUSH_DOCKERCFG_PATH=/var/run/secrets/openshift.io/push/.dockercfg

I0710 16:54:51.511826       1 cfg.go:64] Using serviceaccount user for Docker authentication

I0710 16:54:51.511847       1 docker.go:84] Using Docker authentication provided

I0710 16:54:51.511862       1 docker.go:87] Pushing 172.30.246.7:5000/petstore/petstore image ...

I0710 16:55:14.031293       1 docker.go:91] Successfully pushed 172.30.246.7:5000/petstore/petstore

 

Step 5: Once the build is successful, you’ll notice that OpenShift deploys the application as a pod and your pod starts running. You can see that the build pod is complete with ExitCode of 0 and the application pod is running.

$ oc get pods

NAME               READY     REASON       RESTARTS   AGE

petstore-1-build   0/1       ExitCode:0   0          7m

petstore-1-mtc63   1/1       Running      0          51s

You can watch the logs for your pod with the command oc logs petstore-1-mtc63 if you wish to. You can see that the ROOT.war gets deployed.
 

Step 6: Now if you type in your url in the browser your favorite petstore app will be shown. You can initialize the database and using petstore if you want to try it out!

 

S2I with Customized Assemble Script

The Source to Image process uses the assemble STI script that builds the application artifacts from a source and places them into appropriate directories inside the image. We can customize this assemble script if we need to. The approach in this section uses a small change to this assemble script to deploy our .war file.

Here are two such customized assemble scripts for jbosseap and jbossews:

https://github.com/VeerMuchandi/eap6-bin-deploy
https://github.com/VeerMuchandi/ews-bin-deploy

As we are deploying only a .war file and not any code, there is no need to add any code here. You will notice that we have a .sti/bin directory in each repository and an assemble script posted in there. I just added one step to this script to copy the .war file to the deployments folder.

curl -o ${DEPLOY_DIR}/ROOT.war -O ${WAR_FILE_URL}

It is as simple as that. This script uses an environment variable to pass the .war file url. So you can deploy your own warfile using this script instead of what we are testing in the steps below. Now let us try to use this script to deploy our favorite petstore .war file.
 

Step 1: Let us first clean up our petstore project created earlier. If you are directly jumping to this approach create a new project instead.

$ oc project petstore

This will change you to use petstore project.

$ oc delete all --all

This will delete everything in the project.
 

Step 2: Create the application.

This time we will use JBossEWS. We are asking it to use jboss-webserver3-tomcat8-openshift builder image and pointing to the git repository that has the custom assemble script for jbossews. Let us name this application petstore again.

$ oc new-app jboss-webserver3-tomcat8-openshift~https://github.com/VeerMuchandi/ews-bin-deploy.git --name=petstore

imagestreams/petstore

buildconfigs/petstore

deploymentconfigs/petstore

services/petstore

A build was created - you can run `oc start-build petstore` to start it.

Service "petstore" created at 172.30.76.144 with port mappings 8080->8080, 8443->8443.

 

Note: You can follow the same steps to do binary deployment using the JBossEAP image in which case you would use https://github.com/VeerMuchandi/eap6-bin-deploy.git as the GitURL.
 

Step 3: Let us add a route to this application just like before with the URL petstore.testv3.osecloud.com:

$ oc expose service petstore --hostname=petstore.testv3.osecloud.com

NAME       HOST/PORT                      PATH      SERVICE    LABELS

petstore   petstore.testv3.osecloud.com             petstore

 

Step 4: Edit Build Configuration.

In the custom assemble script we passed an environment variable. But we did not assign any value to that yet. We need to tell our build configuration on which .war file to use to deploy. Now we will edit the build configuration to add an environment variable WAR_FILE_URL that points to where your .war file is. Your build management tool may have placed your .war file in an artifact repository and you can point to that. In this example, we are using the petstore war file that I posted on github.

Note: OpenShift does an automatic build and deploy within minutes after your application is created. So by the time you are here, a build may have already started. Don’t worry. There is no harm. Let it go. We will start a build again after we edited the build configuration.

Run the following command to get your build configuration:

$ oc get bc

NAME       TYPE      SOURCE

petstore   Source    https://github.com/VeerMuchandi/ews-bin-deploy.git

Now let us edit the build configuration using oc edit. I prefer editing it as json as I don’t have to worry about the number of spaces in a yaml.

$ oc edit bc -o json

This will open up your build configuration. Use it as a ‘vi’ editor, find the sourceStrategy section and add the environment variable. Your sourceStrategy section should look as shown below. Here you can replace the .war file URL with your own if you want to deploy your application instead of petstore.

Note: Be careful about closing the braces and don’t forget to add a comma at the end of the from section.

"sourceStrategy": {
   "from": {
      "kind": "ImageStreamTag",
      "namespace": "openshift",
      "name": "jboss-webserver3-tomcat8-openshift:latest"
       },
        "env": [
            {
              "name": "WAR_FILE_URL",
               "value": "https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war"
            }
        ]
}  

You can save and exit from the editor.

Step 5: Once the build configuration is changed start a new build. As said before, a build may have already run once. Ignore that.

$ oc start-build petstore

petstore-2

 

You can watch the build logs just like before. You will notice that the S2I build process uses the custom assemble script to download the tomcat builder image, it downloads the .war file, builds an application image by layering it on the top of tomcat builder image and pushes it into the registry.

$ oc build-logs petstore-2

I0729 10:28:00.649417       1 cfg.go:50] Problem accessing /root/.dockercfg: stat /root/.dockercfg: no such file or directory

I0729 10:28:00.659164       1 sti.go:77] Creating a new S2I builder with build config: "Builder Image:\t\tregistry.access.redhat.com/jboss-webserver-3/tomcat8-openshift:3.0-137\nSource:\t\t\thttps://github.com/VeerMuchandi/ews-bin-deploy.git\nOutput Image Tag:\t172.30.162.74:5000/petstore/petstore\nEnvironment:\t\tOPENSHIFT_BUILD_SOURCE=https://github.com/VeerMuchandi/ews-bin-deploy.git,WAR_FILE_URL=https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war,OPENSHIFT_BUILD_NAME=petstore-2,OPENSHIFT_BUILD_NAMESPACE=petstore\nIncremental Build:\tdisabled\nRemove Old Build:\tdisabled\nForce Pull:\t\tdisabled\nQuiet:\t\t\tdisabled\nLayered Build:\t\tdisabled\nDocker Endpoint:\tunix:///var/run/docker.sock\n"

I0729 10:28:00.667657       1 docker.go:187] Pulling image registry.access.redhat.com/jboss-webserver-3/tomcat8-openshift:3.0-137

I0729 10:28:31.555155       1 sti.go:83] Starting S2I build from petstore/petstore-2 BuildConfig ...

I0729 10:28:31.555267       1 sti.go:112] Building 172.30.162.74:5000/petstore/petstore

I0729 10:28:31.754121       1 clone.go:26] Cloning into /tmp/sti046720555/upload/src

I0729 10:28:34.213739       1 docker.go:180] Image registry.access.redhat.com/jboss-webserver-3/tomcat8-openshift:3.0-137 available locally

I0729 10:28:34.213862       1 docker.go:267] Image contains io.s2i.scripts-url set to 'image:///usr/local/sti'

I0729 10:28:34.213982       1 download.go:56] Using image internal scripts from: image:///usr/local/sti/assemble

I0729 10:28:34.214024       1 download.go:56] Using image internal scripts from: image:///usr/local/sti/run

I0729 10:28:34.218809       1 docker.go:180] Image registry.access.redhat.com/jboss-webserver-3/tomcat8-openshift:3.0-137 available locally

I0729 10:28:34.218875       1 docker.go:267] Image contains io.s2i.scripts-url set to 'image:///usr/local/sti'

I0729 10:28:34.218994       1 download.go:56] Using image internal scripts from: image:///usr/local/sti/save-artifacts

I0729 10:28:34.219036       1 sti.go:182] Using assemble from upload/src/.sti/bin

I0729 10:28:34.219067       1 sti.go:182] Using run from image:///usr/local/sti

I0729 10:28:34.219103       1 sti.go:182] Using save-artifacts from image:///usr/local/sti

I0729 10:28:34.219148       1 sti.go:120] Clean build will be performed

I0729 10:28:34.219174       1 sti.go:123] Performing source build from https://github.com/VeerMuchandi/ews-bin-deploy.git

I0729 10:28:34.219198       1 sti.go:130] Building 172.30.162.74:5000/petstore/petstore

I0729 10:28:34.219226       1 sti.go:326] Using image name registry.access.redhat.com/jboss-webserver-3/tomcat8-openshift:3.0-137

I0729 10:28:34.219279       1 sti.go:330] No .sti/environment provided (no evironment file found in application sources)

I0729 10:28:34.219896       1 tar.go:133] Adding to tar: /tmp/sti046720555/upload/scripts/assemble as scripts/assemble

I0729 10:28:34.235592       1 docker.go:315] Both scripts and untarred source will be placed in '/tmp'

I0729 10:28:34.235682       1 docker.go:350] Creating container using config: {Hostname: Domainname: User: Memory:0 MemorySwap:0 CPUShares:0 CPUSet: AttachStdin:false AttachStdout:true AttachStderr:false PortSpecs:[] ExposedPorts:map[] Tty:false OpenStdin:true StdinOnce:true Env:[OPENSHIFT_BUILD_NAME=petstore-2 OPENSHIFT_BUILD_NAMESPACE=petstore OPENSHIFT_BUILD_SOURCE=https://github.com/VeerMuchandi/ews-bin-deploy.git WAR_FILE_URL=https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war] Cmd:[/bin/sh -c tar -C /tmp -xf - && /tmp/scripts/assemble] DNS:[] Image:registry.access.redhat.com/jboss-webserver-3/tomcat8-openshift:3.0-137 Volumes:map[] VolumesFrom: WorkingDir: MacAddress: Entrypoint:[] NetworkDisabled:false SecurityOpts:[] OnBuild:[] Labels:map[]}

I0729 10:28:35.592048       1 docker.go:357] Attaching to container

I0729 10:28:35.604127       1 docker.go:414] Starting container

I0729 10:28:36.094139       1 docker.go:424] Waiting for container

E0729 10:28:36.437997       1 sti.go:414] cp: cannot stat '/tmp/src/*': No such file or directory

E0729 10:28:36.438208       1 sti.go:414] cp: cannot stat '/tmp/src/*': No such file or directory

I0729 10:28:36.440131       1 sti.go:388]  Veer - Custom code for binary deployment

I0729 10:28:36.440239       1 sti.go:388] https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war

E0729 10:28:36.679885       1 sti.go:414]   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

E0729 10:28:36.680047       1 sti.go:414]                                  Dload  Upload   Total   Spent    Left  Speed

% Total    % Received % Xferd  A0     0    0     0    0     verage Speed   Time    Time     Time  Current

E0729 10:28:36.680188       1 sti.go:414]                                  Dload  Upload   Total   Spent    Left  Speed

0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     00   0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0  100 6011k  100 6011k    0     0  3182k      0  0:00:01  0:00:01 --:--:-- 3182k

E0729 10:28:38.567714       1 sti.go:414]      0  3182k      0  0:00:01  0:00:01 --:--:-- 3182k

I0729 10:28:38.866795       1 docker.go:430] Container exited

I0729 10:28:38.866859       1 docker.go:436] Invoking postExecution function

I0729 10:28:38.866919       1 sti.go:216] No .sti/environment provided (no evironment file found in application sources)

I0729 10:28:38.867063       1 docker.go:469] Committing container with config: {Hostname: Domainname: User: Memory:0 MemorySwap:0 CPUShares:0 CPUSet: AttachStdin:false AttachStdout:false AttachStderr:false PortSpecs:[] ExposedPorts:map[] Tty:false OpenStdin:false StdinOnce:false Env:[OPENSHIFT_BUILD_NAME=petstore-2 OPENSHIFT_BUILD_NAMESPACE=petstore OPENSHIFT_BUILD_SOURCE=https://github.com/VeerMuchandi/ews-bin-deploy.git WAR_FILE_URL=https://raw.githubusercontent.com/VeerMuchandi/ps/master/deployments/ROOT.war] Cmd:[/usr/local/sti/run] DNS:[] Image: Volumes:map[] VolumesFrom: WorkingDir: MacAddress: Entrypoint:[] NetworkDisabled:false SecurityOpts:[] OnBuild:[] Labels:map[]}

I0729 10:28:49.529515       1 sti.go:246] Successfully built 172.30.162.74:5000/petstore/petstore

I0729 10:28:49.613093       1 cleanup.go:23] Removing temporary directory /tmp/sti046720555

I0729 10:28:49.613147       1 fs.go:99] Removing directory '/tmp/sti046720555'

I0729 10:28:49.616846       1 cfg.go:46] PUSH_DOCKERCFG_PATH=/var/run/secrets/openshift.io/push/.dockercfg

I0729 10:28:49.617207       1 cfg.go:64] Using serviceaccount user for Docker authentication

I0729 10:28:49.617250       1 sti.go:131] Using provided push secret for pushing 172.30.162.74:5000/petstore/petstore image

I0729 10:28:49.617266       1 sti.go:134] Pushing 172.30.162.74:5000/petstore/petstore image ...

I0729 10:30:39.375996       1 sti.go:138] Successfully pushed 172.30.162.74:5000/petstore/petstore

 

Step 6: Right after the build is complete, OpenShift initiates the deploy process and you will see the application running. If you get the list of pods you will note the application pod running.

$ oc get pods

NAME               READY     REASON       RESTARTS   AGE

petstore-1-build   0/1       ExitCode:0   0          14m

petstore-2-build   0/1       ExitCode:0   0          7m

petstore-2-uxh4v   1/1       Running      0          4m

 

Step 7: Now if you try the url for the application in the browser and petstore comes up.

Other Options

There are a couple of other options that you may choose based on your preference, such as

  • You can build a docker image for your application completely outside of OpenShift and deploy it in OpenShift. In such a case OpenShift does handle the build at all; it just runs your application docker image.
  • Include your .war file in a `pom.xml` that pulls the .war file and places in the deployments folder. In this case, you will need to create a git repository that holds the `pom.xml` and use that repository to create your application in openshift using `oc new-app`. I contemplated that angle, and ended up using the above custom assemble script approach as the custom assemble script that we saw above can be used with any .war file.

Summary

In this article we have seen two ways to deploy your .war file into OpenShift. Other options are also possible.

Categories
Java, OpenShift Container Platform, OpenShift Dedicated, OpenShift Origin, Products, Technologies, Tomcat
Tags
, , , , , ,
  • John Wild

    Thanks Veer! This is hands down my favorite post thus far. I have been a bit fuzzy on some of the newer concepts like building routes, exposing services, and doing Source-to-Image, but this seems like a very compelling way to deploy applications.

    Very simple to see your explanation. Do you have a video on building a new Source-to-Image from scratch? This would be helpful. Our team also would like to look at WordPress in OpenShift, wondering if you could do a video on this topic.

    It would also be nice to maybe like to see something on allocating resources to users or for development teams. The replications you have mentioned still seems fuzzy to me on how this corresponds to matching machine resources based on load.

    Thanks!
    John

  • Sandeep

    Hi Veer,

    Can you please explain more about this option

    You can build a docker image for your application completely outside of OpenShift and deploy it in OpenShift. In such a case OpenShift does handle the build at all; it just runs your application docker image.”

    My deployment phase fails …it repeatedly restarts the docker. Can you please explain in detail

    Sandeep

    • Veer Muchandi

      Sandeep, How are you creating this app?
      You should use oc new-app to create an app from a docker image.

      • Sandeep

        Hi Veer,
        Thanks for the reply .The docker image has to be pushed to Openshift registry? Cant we use a private repo?

        Sandeep