In this article I am going to touch Docker for containerization of Mule applications.
Intended audience: Any one who wants to understand how to containerize Mule applications and no prior knowledge is required to go through. But basic MuleSoft product knowledge is required to follow along.
Before going into containerization, I will briefly touch base with the definition and terms to follow the article.
Containers – Containers provides a mechanism to package an application with all its dependencies and provides isolation for the application environment. Application running in one container will not be affected by other applications. It can be executed on any machine, providing us the portability to transition from Development to Deployment seamlessly.
Docker – Docker is a technology that acts both as packaging format and a container runtime. Packaging is defined in a Docker file and container is created from the image created using the Docker file.
Docker file – A Docker file defines a set of instructions to build an image. The command used is:
docker build
A container is an instance of an image. The image can be deployed or transported to any platform since it is a self-contained and run as container
Docker container is created as below:
docker run
You can store the image in your local machine or a private registry like Azure Container Registry (ACR) or public storages like Docker Hub.
Note: This article deals with only the demonstration of creating an image and creating a container. In future articles, I will discuss more about the container registries. Also, note that since the runtime used is trial version, it is valid only for 30 days. For basic learning it is ok to use trial version. But, to work on your projects, get licensed runtime by contacting MuleSoft.
Pre-requisites:
- Download the free Mule runtime from MuleSoft’s website.
- Also, download the Anypoint Studio to develop applications.
- Install docker from the Docker’s website.
Step 1: Create a separate folder (For example, Docker4.3 in your local machine)
Step 2: Create a Dockerfile with no file extensions in the same folder. Use Visual Studio Code IDE which can detect a Dockerfile.
Step 3: Copy the Mule runtime zip file in the folder created in step 1.
Step 4: Open the Dockerfile and copy the following lines ( I have added comments for each instruction for easy understanding):
FROM java:openjdk-8-jdk
RUN rm -rf Mule
RUN mkdir Mule
#Add Mule runtime to the Docker container
RUN echo "Adding Mule 4.3.0 to Docker container"
ADD mule-ee-distribution-standalone-4.3.0.zip /Mule
#Add Work Directory
RUN echo "Adding Work Directory"
WORKDIR /Mule
#Extract and install the Mule runtime in the container
RUN echo "Extracting the zip file and installing the Mule runtime"
RUN unzip mule-ee-distribution-standalone-4.3.0.zip && \
rm mule-ee-distribution-standalone-4.3.0.zip
# Define volume mount points
VOLUME ["/Mule/mule-enterprise-standalone-4.3.0/logs", "/Mule/mule-enterprise-standalone-4.3.0/apps", "/Mule/mule-enterprise-standalone-4.3.0/domains"]
#Copy and deploy mule application in the Mule 4.3.0 runtime
RUN echo "Deploying mule application in runtime"
COPY simplehttp-1.0.0-SNAPSHOT-mule-application.jar mule-enterprise-standalone-4.3.0/apps/
RUN ls -ltr mule-enterprise-standalone-4.3.0/apps/
# Expose the port 8081 as needed
EXPOSE 8081
# Start Mule runtime
RUN echo "Starting Mule runtime 4.3.0"
CMD ["mule-enterprise-standalone-4.3.0/bin/mule"]
As specified int he Docker file, we need to create a sample Mule application and in my case it is “simplehttp-1.0.0-SNAPSHOT-mule-application.jar”.
Step 5: Develop a simple HTTP application that is exposed on port 8081 as below:
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core" xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd">
<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="f7f6140b-9749-469e-a9e8-99a850a52e60" >
<http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>
<flow name="simplehttpFlow" doc:id="1b65f4b8-bb63-4d6f-ae3a-964287b317a4" >
<http:listener doc:name="Listener" doc:id="4a626aaf-4e5d-44c5-bca9-a66cfb2dbc3a" config-ref="HTTP_Listener_config" path="/verify"/>
<logger level="INFO" doc:name="Logger" doc:id="4803e315-b3e1-4c6e-844f-9fbac6441769" message="Request recieved and sending response"/>
<ee:transform doc:name="Transform Message" doc:id="441e4a7e-77e5-4bbb-a901-a0f7ef28624a" >
<ee:message >
<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
'result':'success'
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
</mule>
This application when executed will return a JSON message.
Step 6: Build the jar by running packaging the application.
mvn clean package
Step 7: Copy the jar from the target folder of the application into the folder we created in the step 1.
Step 8: Now build the image by running the following command from the folder created in the step 1.
$ docker build -t ivaturia/simplehttp:latest
You should see the logs similar to the following:
[+] Building 10.3s (18/18) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.16kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/java:openjdk-8-jdk 0.6s
=> [internal] load build context 0.1s
=> => transferring context: 1.77MB 0.1s
=> [ 1/13] FROM docker.io/library/java:openjdk-8-jdk@sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c 0.0s
=> CACHED [ 2/13] RUN rm -rf Mule 0.0s
=> CACHED [ 3/13] RUN mkdir Mule 0.0s
=> [ 4/13] RUN echo "Adding Mule 4.3.0 to Docker container" 0.5s
=> [ 5/13] ADD mule-ee-distribution-standalone-4.3.0.zip /Mule 1.2s
=> [ 6/13] RUN echo "Adding Work Directory" 0.4s
=> [ 7/13] WORKDIR /Mule 0.0s
=> [ 8/13] RUN echo "Extracting the zip file and installing the Mule runtime" 0.4s
=> [ 9/13] RUN unzip mule-ee-distribution-standalone-4.3.0.zip && rm mule-ee-distribution-standalone-4.3.0.z 3.7s
=> [10/13] RUN echo "Deploying mule application in runtime" 0.4s
=> [11/13] COPY simplehttp-1.0.0-SNAPSHOT-mule-application.jar mule-enterprise-standalone-4.3.0/apps/ 0.0s
=> [12/13] RUN ls -ltr mule-enterprise-standalone-4.3.0/apps/ 0.4s
=> [13/13] RUN echo "Starting Mule runtime 4.3.0" 0.5s
=> exporting to image 2.0s
=> => exporting layers 1.9s
=> => writing image sha256:7dd9023c087a2cdac43dc226574efeb0ccd80ce79e3d88ea615dba178c6a07a3 0.0s
=> => naming to docker.io/ivaturia/simplehttp:latest
Step 9: Now run the application in the docker container by running the image as below:
docker run -it --rm -p 9091:8081 ivaturia/simplehttp:latest
Here I am mapping the port 8081 to 9091 so that I can make sure that I am hitting right URL after container is created.
docker run – runs the image and create the container, -it tells it to run in interactive shell in the container (pseudo TTY), -p tells to publish the port on the container 8081 to the port 9091 in the local machine, finally we have to specify the image created in the step 8.
Now you should see the logs as below that you normally see when the Mule application runs in the Anypoint Studio in the Console window.
Running in console (foreground) mode by default, use Ctrl-C to exit...
MULE_HOME is set to /Mule/mule-enterprise-standalone-4.3.0
MULE_BASE is set to /Mule/mule-enterprise-standalone-4.3.0
Running Mule Enterprise Edition...
--> Wrapper Started as Console
Java Service Wrapper Standard Edition 64-bit 3.5.37
Copyright (C) 1999-2018 Tanuki Software, Ltd. All Rights Reserved.
http://wrapper.tanukisoftware.com
Licensed to MuleSoft Inc. for Mule Runtime Enterprise Edition
Launching a JVM...
Starting the Mule Container...
WrapperManager: Initializing...
Valid license key --> Evaluation = true, Expiration Date = Mon Apr 26 00:00:00 UTC 2021, Contact Name = MuleSoft Support, Contact Email Address = support@mulesoft.com, Contact Telephone = support@mulesoft.com, Contact Company = MuleSoft, Contact Country = US, Entitlements =
**********************************************************************
* *
* ((((((((((((((((((( *
* ((((( ((((# *
* &((( *((( *
* &((( ((( *
* ((( (((% (((# ((( *
* (( ((((((( ((((((( ((( *
* (( ((((((((( ((((((((( (( *
* (( #((((((((((( #((((((((((( ((( *
* #(( (((((((((((((( (((((((((((((( (( *
* (( (((((( /((((((( ((((((( /(((((( (( *
* (( (((((( (((((((&((((((( (((((( (( *
* (( ((((( ((((((((((((* (((((( (( *
* (( (((((( *((((((((( (((((( (( *
* (( (((((( ((((((( (((((( ((( *
* (( (((((( (((((( (( *
* ((# ((((((# ((((((( (( *
* ((# (((((((( (((((((( ((( *
* ((( ((((((( ((((( ((. *
* (( ((( ((( #(( *
* /(( ((( *
* ((( &((( *
* *((((& &(((( *
* ((((((((((((((/ *
* *
* ___ ___ _ ______ _ _ *
* | \/ | | | | ___ \ | | (_) *
* | . . |_ _| | ___ | |_/ / _ _ __ | |_ _ _ __ ___ ___ *
* | |\/| | | | | |/ _ \ | / | | | '_ \| __| | '_ ` _ \ / _ \ *
* | | | | |_| | | __/ | |\ \ |_| | | | | |_| | | | | | | __/ *
* \_| |_/\__,_|_|\___| \_| \_\__,_|_| |_|\__|_|_| |_| |_|\___| *
* *
* *
* Mule Runtime and Integration Platform *
* Version: 4.3.0 Build: 5bb13483 *
* MuleSoft, Inc. *
* For more information go to *
* https://www.mulesoft.com/platform/soa/mule-esb-enterprise *
* *
* Server started: 3/27/21 9:27 PM *
* JDK: 1.8.0_111 (mixed mode) *
* JDK properties: *
* - java.vendor = Oracle Corporation *
* - java.vm.name = OpenJDK 64-Bit Server VM *
* - java.home = /usr/lib/jvm/java-8-openjdk-amd64/jre *
* OS: Linux (4.19.121-linuxkit, amd64) *
* Host: 0f09507d3c62 (172.17.0.2) *
* Mule services: *
* - mule-service-http-ee-1.6.0 *
* - mule-service-weave-ee-2.3.0 *
* - mule-service-scheduler-1.3.0 *
* - mule-service-soap-1.3.0 *
* - api-gateway-events-collector-service-1.1.0 *
* - mule-service-oauth-ee-1.0.0 *
* - api-gateway-contract-service-1.1.0 *
* Applied artifact patches: *
* - APIKIT-2576-1.3.7-1.3.8-1.0.jar *
* - APIKIT-2612-1.0.0-1.0.2-1.1.0-1.0.jar *
* - APIKIT-2612-1.1.1-1.0.jar *
* - APIKIT-2612-1.1.11-1.0.jar *
* - APIKIT-2612-1.1.12-1.2.0-1.2.2-1.0.jar *
* - APIKIT-2612-1.1.13-1.1.14-1.2.3-1.2.4-1.0.jar *
* - APIKIT-2612-1.1.15-1.1.16-1.0.jar *
* - APIKIT-2612-1.1.2-1.0.jar *
* - APIKIT-2612-1.1.3-1.0.jar *
* - APIKIT-2612-1.1.4-1.0.jar *
* - APIKIT-2612-1.1.5-1.1.6-1.0.jar *
* - APIKIT-2612-1.1.7-1.0.jar *
* - APIKIT-2612-1.1.8-1.1.10-1.0.jar *
* - APIKIT-2612-1.2.5-1.3.1-1.3.4-1.0.jar *
* - APIKIT-2612-1.3.0-1.0.jar *
* - APIKIT-2612-1.3.5-1.3.9-1.0.jar *
* - APIKIT-2613-1.2.0-1.2.1-1.0.jar *
* - APIKIT-2613-1.2.2-1.0.jar *
* - APIKIT-2613-1.2.3-1.2.4-1.3.0-1.0.jar *
* - APIKIT-2613-1.2.5-1.3.1-1.3.4-1.0.jar *
* - APIKIT-2613-1.3.5-1.3.6-1.0.jar *
* - APIKIT-2613-1.3.7-1.3.8-1.0.jar *
* - APIKIT-2613-1.3.9-1.0.jar *
* - SE-12551-1.0.0-1.0.1-1.0.jar *
* - SE-12551-1.1.0-1.1.15-2.0.jar *
* - SE-12551-1.2.0-1.3.0-1.0.jar *
* - SE-12551-1.3.1-1.3.4-1.0.jar *
* - SE-12562-1.0.0-1.5.5-1.0.jar *
* Mule system properties: *
* - mule.metadata.cache.expirationInterval.millis = 5000 *
* - mule.base = /Mule/mule-enterprise-standalone-4.3.0 *
* - mule.home = /Mule/mule-enterprise-standalone-4.3.0 *
* - mule.metadata.cache.entryTtl.minutes = 10 *
**********************************************************************
**********************************************************************
* Cluster configuration *
* *
* Cluster mode disabled *
**********************************************************************
**********************************************************************
* Started domain 'default' *
**********************************************************************
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ New app 'simplehttp-1.0.0-SNAPSHOT-mule-application' +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Initializing app 'simplehttp-1.0.0-SNAPSHOT-mule-application' +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
**********************************************************************
* Started DefaultSchedulerService *
* *
* Resolved configuration values: *
* *
* Pooling strategy: UBER *
* gracefulShutdownTimeout: 15000 ms *
* uber.threadPool.maxSize: 148 *
* uber.threadPool.threadKeepAlive: 30000 ms *
* *
* These can be modified by editing 'conf/scheduler-pools.conf' *
**********************************************************************
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Starting app 'simplehttp-1.0.0-SNAPSHOT-mule-application' +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
**********************************************************************
* Started app 'simplehttp-1.0.0-SNAPSHOT-mule-application' *
* Application plugins: *
* - Sockets : 1.2.1 *
* - HTTP : 1.5.24 *
**********************************************************************
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Mule is up and kicking (every 5000ms) +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
**********************************************************************
* - - + DOMAIN + - - * - - + STATUS + - - *
**********************************************************************
* default * DEPLOYED *
**********************************************************************
*******************************************************************************************************
* - - + APPLICATION + - - * - - + DOMAIN + - - * - - + STATUS + - - *
*******************************************************************************************************
* simplehttp-1.0.0-SNAPSHOT-mule-application * default * DEPLOYED *
*******************************************************************************************************
Now open Postman or any of your favorite tool and hit endpoint http://localhost:9091/verify and you should see the result as you got when you ran the program but this time on different port i.e. 9091 instead of 8081.
Now open new Bash window and type the command “docker images” and it will list our image with the IMAGE, ID and other details:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ivaturia/simplehttp latest 7dd9023c087a 18 minutes ago 1.33GB
To view the container, run the command “docker container ls” and it will list our container with the details:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0f09507d3c62 ivaturia/simplehttp:latest "mule-enterprise-sta…" 10 minutes ago Up 10 minutes 0.0.0.0:9091->8081/tcp clever_northcutt
Akkirajus-MacBook-Pro:~ Master$
If you want to delete image, first you need to remove the container and then you can remove the image. You cannot remove image while container is running.
Press ^c in the bash shell window where the container was running in the step 9 and you should see the following:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Disposing application 'simplehttp-1.0.0-SNAPSHOT-mule-application' +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Disposing domain 'default' +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
<-- Wrapper Stopped
Now if you run “docker container ls” it will not list the container as you have shutdown the container.
Commands nice to know:
To remove container; -f force deletion; ID is the id of the container,
docker rm -f container <ID>
To remove image,
docker rmi <ID>
Now this image is stored in my local computer and in order to make it available, we might need to publish to a public repository like docker hub. You need to register for the free account.
Next step, I am going to push the image to docker hub. Before that I need to make a small change to the image name i.e. ivaturia/simplehttp to akkiraju/simplehttp as my docker hub account name is “akkiraju” and if I try to push the old image, the push request will be denied with the following error:
denied: requested access to the resource is denied
In order to change the name of the image, we can clone the existing image with tag as <accountid>/<imagename>
$ docker tag ivaturia/simplehttp:latest akkiraju/simplehttp
Now run the following command to see if the new image is listed:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
akkiraju/simplehttp latest
Now let’s push the image to the Docker hub with the following command:
$ docker login
$ docker push ivaturia/simplehttp
Once the command is successful, login into the docker hub and check if the repository lists the new image. In my case, I can check the image as shown in the following figure:
Now the image is available in the repository and so we can remove the local image by using the command docker rmi <image>
If you ever want to download the image you can do so using the command docker pull <image>
Hope this article was useful to just to have a basic understanding of creating an image and container and access the endpoint and publish to docker hub from where others can pull the image and use it.