Deploy ACE on CP4I using Jenkins
Overview
This is a continuation of my previous story (link)on how to deploy MQ on CP4I using Jenkins. Deploying ACE is more complex than MQ because to me, ACE is really an integration application where you need to compile into a deployable unit called Broker Archive (BAR) file before you can deploy to an integration server.
Therefore, a typical CI/CD pipeline of deploying ACE applications would look very similar to any other applications pipeline. The diagram shows the System Context diagram of the pipeline for the purpose of this article.
Our pipeline above has the following stages:
- Git Checkout — This stage check out the source code for App Connect Enterprise (ACE) from a Git repository
- Build Bar File — Package the ACE application artifacts into a BAR file. To do so, we need to run this using a Docker image that is installed with ACE Toolkit.
- Upload Bar File — Upload the BAR file into a file repository, e.g. JFrog Artifactory.
- Deploy Integration Server — Deploy an integration server that is configured to point to the file location of the previous BAR file.
- Unit Test — Use a curl command to test the API endpoint of the integration application.
Required components/systems
The following software is needed to implement this pipeline. I will not write in detail how to install the various software; I will just provide references on how to do this.
- Jenkins — see my previous article on installing Jenkins on the same OpenShift cluster of the CP4I.
- JFrog Artifactory — We use this for artifact management. I installed it in the OpenShift cluster using the IBM Cloud Native Toolkit.
- Docker Registry — I used the default OpenShift image registry.
- GitHub — I used the public GitHub.com. I have two repositories, one contains the source code of the integration application and another contains the utility artifacts like scripts and templates.
- OpenShift cluster — installed with CP4I and ACE dashboard. OCP is v4.8.26. CP4I is v2021.4.1.
Create build and deploy images
I used 3 images for the pipeline.
- jnlp-slave image which is located here
- oc-deploy image that has these tools — openshift-client, kubectl, curl, jq, git, sed, wget. This image is used in the stages-Upload Bar File, Deploy Integration Server, and Unit Test. The Dockerfile is located here.
- ace-buildbar image that has the ACE toolkit and configured with libraries to run mqsicreatebar (e.g. libgtk2.0–0 libxtst6 xvfb libswt-gtk-4-java libswt-gtk-4-jni). The Dockerfile is located here.
You need to build the image, tag, and push to the OpenShift image registry. The following are the instructions for building the ace-buildbar image. You can follow the same instructions for the oc-deploy image.
// Build the image
docker build -t ace-buildbar:12.0.4.0-ubuntu -f Docker.ace-12.0.4-buildbar .// Create an external route for the image registry
oc patch configs.imageregistry.operator.openshift.io/cluster --patch '{"spec":{"defaultRoute":true}}' --type=merge// Get the hostname of the image registry
oc get route -n openshift-image-registry default-route -ojson | jq -r .spec.host// Get the hostname into variable HOST
HOST=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}')// Login to OpenShift image registry
docker login -u $(oc whoami) -p $(oc whoami -t) $HOST// Tag the image
docker tag ace-buildbar:12.0.4.0-ubuntu $HOST/jenkins/ace-buildbar:12.0.4.0-ubuntu// Push the image into the OpenShift image registry
docker push $HOST/jenkins/ace-buildbar:12.0.4.0-ubuntu
The image can now be pulled by containers within the OpenShift cluster via the following tag.
image-registry.openshift-image-registry.svc:5000/jenkins/ace-buildbar:12.0.4.0-ubuntu
Set up Artifactory credentials in Jenkins
- Create Global credentials (kind: Username with password) to store the username and password for JFrog Artifactory. I used the name artifactory_credentials in the Jenkinsfile to reference the username/password securely.
Set up Artifactory credentials (BarAuth) for ACE
We use the spec.barURL in the integration server to retrieve the Bar file from JFrog Artifactory. To do that we need to set up the BarAuth configuration that contains both the credentials and CA certificate.
Here is the IBM ACE documentation on Configuration and BarAuth.
Create a secret for the certificate
- Get the certificate for the JFrog Artifactory endpoint.
openssl s_client -connect artifactory-tools.itzroks-3100015379-x94hbr-6ccd7f378ae819553d37d5f2ee142bd6-0000.au-syd.containers.appdomain.cloud:443
2. Create a secret using the following YAML (artifactory-cacert-secret.yaml).
kind: Secret
apiVersion: v1
metadata:
name: artifactory-cacert-secret
namespace: ace
data:
ca.crt: '{{ base64 encoded of the certificate }}'
tls.crt: ''
tls.key: ''
type: kubernetes.io/tls
3. Note: Use the following instruction to get the ca.crt value
cat artifactory-ca.crt | base64
4. Finally, run the following command to create the secret.
oc apply -f artifactory-cacert-secret.yaml
Create a BarAuth configuration
- Create a BarAuth JSON file, see here. Populate the values as follows.
{
"authType":"BASIC_AUTH",
"credentials": {
"username":"{{username}}",
"password":"{{password}}",
"caCertSecret":"artifactory-cacert-secret"
}
}
2. Create a BarAuth secret using the JSON file in the namespace you want to deploy the ACE integration server.
oc create secret generic bar-auth-secret --from-file=configuration=bar-auth.json --namespace=ace
3. Finally, create the ACE Configuration object referencing the secret name, the YAML file here.
oc create -n ace -f bar-auth-config.yaml// bar-auth-config.yaml
apiVersion: appconnect.ibm.com/v1beta1
kind: Configuration
metadata:
name: bar-auth-config
namespace: ace
spec:
description: Stores bar auth
secretName: bar-auth-secret
type: barauth
Set up Jenkins pipeline
- Create a pipeline that points to our source code Git repository. The Git repository contains a Jenkinsfile that defines the entire Pipeline script.
Upload and retrieve files from JFrog Artifactory
- The easiest way to know how to use upload and retrieve is to go to the JFrog Artifactory UI and click on the Set Me Up button (top right).
2. I have written two scripts to do this — in this folder. To upload a file, we need to calculate the checksum and include it in the HTTP header.
CHECKSUM=`shasum -a 1 $ARTIFACTORY_BAR_FILE | awk '{ print $1 }'`-H 'X-Checksum-Sha1:${CHECKSUM}'
Anatomy of the Jenkinfiles
The following pipeline is implemented in the Jenkinsfile that is conveniently located together with the source code Git repository.
Git Checkout stage: This stage check out the source code and utility files (scripts and templates) from their respective Git repositories. The script files and template file (integration server YAML file) are copied into the source code folder.
Build Bar File stage: This stage uses the ace-buildbar container to create the Bar file. The runs the following commands:
- It runs the X virtual frame buffer to enable you to run graphical applications without a display. It is needed for the mqsicreatebar command because it uses some SWT libraries.
- Run the mqsiprofile to configure the environment for ACE toolkit.
- Append the build number to the Bar file name.
- Run mqsicreatebar to generate the Bar file.
Xvfb -ac :99 &
export DISPLAY=:99export LICENSE=acceptpwd
source /opt/ibm/ace-12/server/bin/mqsiprofile
cd $PROJECT_DIRBAR_FILE="${BAR_NAME}_${BUILD_NUMBER}.bar"
mqsicreatebar -data . -b $BAR_FILE -a $APP_NAME -cleanBuild -trace -configuration .ls -lha
Build Bar File stage: This stage uses the ace-buildbar container to create the Bar file. The runs the following commands:
- Run the script upload-barfile-to-artifactory.sh that does precisely that.
set -ecd $PROJECT_DIRls -lhaecho "Calling upload-barfile-to-artifactory.sh ${ARTIFACTORY_HOST} ${ARTIFACTORY_REPO} ${ARTIFACTORY_BASE_PATH} "${BAR_NAME}_${BUILD_NUMBER}.bar" ${ARTIFACTORY_USER} ${ARTIFACTORY_PASSWORD}"./upload-barfile-to-artifactory.sh ${ARTIFACTORY_HOST} ${ARTIFACTORY_REPO} ${ARTIFACTORY_BASE_PATH} "${BAR_NAME}_${BUILD_NUMBER}.bar" ${ARTIFACTORY_USER} ${ARTIFACTORY_PASSWORD}
Deploy Integration Server stage: This stage deploys the ACE integration server by fetching the Bar file from JFrog Artifactory. The commands are as follows:
- Create the integration-server.yaml file from the template file, but substitute the variables.
- Run an oc command to deploy the integration server, using the YAML file.
set -e
cd $PROJECT_DIR
BAR_FILE="${BAR_NAME}_${BUILD_NUMBER}.bar"
cat integration-server.yaml.tmpl
sed -e "s/{{NAME}}/$SERVER_NAME/g" \
-e "s/{{NAMESPACE}}/$NAMESPACE/g" \
-e "s/{{ARTIFACTORY_HOST}}/$ARTIFACTORY_HOST/g" \
-e "s/{{ARTIFACTORY_PORT}}/$ARTIFACTORY_PORT/g" \
-e "s/{{ARTIFACTORY_REPO}}/$ARTIFACTORY_REPO/g" \
-e "s/{{ARTIFACTORY_BASE_PATH}}/$ARTIFACTORY_BASE_PATH/g" \
-e "s/{{BAR_FILE}}/$BAR_FILE/g" \
-e "s/{{CONFIGURATION_LIST}}/$CONFIGURATION_LIST/g" \
-e "s/{{ACE_VERSION}}/$ACE_VERSION/g" \
-e "s/{{ACE_LICENSE}}/$ACE_LICENSE/g" \
-e "s/{{REPLICAS}}/$REPLICAS/g" \
integration-server.yaml.tmpl > integration-server.yaml
cat integration-server.yaml
oc apply -f integration-server.yaml
The sample copy of the integration-server.yaml file is as follows. It configures the following files
- metadata.name — with the integration server name.
- spec.barURL — with artifactory host, artifactory port, artifactory repository, bar file name.
- spec.configurations — with configurationList variable.
apiVersion: appconnect.ibm.com/v1beta1
kind: IntegrationServer
metadata:
name: books
namespace: ace
spec:
license:
accept: true
license: L-APEH-C79J9U
use: CloudPakForIntegrationNonProduction
pod:
containers:
runtime:
resources:
limits:
cpu: 300m
memory: 350Mi
requests:
cpu: 300m
memory: 300Mi
adminServerSecure: true
enableMetrics: true
createDashboardUsers: true
barURL: https://artifactory-tools.itzroks-3100015379-x94hbr-6ccd7f378ae819553d37d5f2ee142bd6-0000.au-syd.containers.appdomain.cloud:443/artifactory/generic-local/cp4i/Books_14.bar
configurations: [ bar-auth-config ]
router:
timeout: 120s
designerFlowsOperationMode: disabled
service:
endpointType: http
version: '12.0'
replicas: 1
logFormat: basic
Unit Test stage: This stage performs a simple unit test using a curl command.
HOSTNAME=$(oc get route -n ace books-http -ogo-template --template='{{.spec.host}}')
curl -k http://${HOSTNAME}/api/v1/books | jq -r .
Sample output:
[
{
"Id": "1",
"Title": "Steve Jobs",
"ISBN": "1451648537",
"Author": "Walter Isaacson",
"Published": "Nov 24, 2011",
"Language": "English",
"Formats": [
"Hardcover",
"Paperback",
"Audiobook CD",
"Audible"
]
},
{
"Id": "2",
"Title": "Beautiful Whale",
"ISBN": "1419703846",
"Author": "Bryant Austin, Sylvia Earle",
"Published": "May 02, 2013",
"Language": "English",
"Formats": [
"Hardcover"
]
},
{
"Id": "3",
"Title": "A Game of Thrones (A Song of Ice and Fire, Book 1)",
"ISBN": "9780553103547",
"Author": "George R. R. Martin",
"Published": "Sep 01, 1996",
"Language": "English",
"Formats": [
"Kindle",
"Hardcover",
"Paperback",
"Audiobook CD",
"Audible"
]
}
]
Conclusion
Finally, a video showing how everything hangs together.
Disclaimer:
All opinions expressed here are very much my own and not of IBM. All codes/scripts/artifacts are provided as-is with no support unless otherwise stated.