This article will show how to deploy sample Daytrader7 application to an Amazon Elastic Kubernetes Service (EKS) using the WebSphere Liberty Operator (WLO). For more information about the WebSphere Liberty Operator, see blog.
Prerequisites
As part of the prerequisite steps, an EKS cluster has been created and proper permissions have been assigned to allow access to the cluster. That cluster will be referenced and remotely connected to from the local command line in this article. In order to allow interaction with AWS and the EKS cluster using the AWS Command Line Interface (AWS CLI), a few additional configuration commands might be needed.
Configure your AWS and EKS command line
Configure your AWS CLI
To configure some basic settings such as credentials and regions for interaction with the AWS CLI on the local environment, use the aws configure command.
# aws configure
AWS Access Key ID [None]: <access key id>
AWS Secret Access Key [None]: <secret access key>
Default region name [None]: <cluster's region>
Default output format [None]: <will default to json if nothing entered>
Create/update your EKS cluster configuration
Next create a kubeconfig file that stores credentials for the Kubernetes cluster on EKS.
# aws eks update-kubeconfig --name <cluster-name>
Creating an Ingress for the EKS cluster
Configure Nginx ingress controller and create an alias record
To expose applications externally later when they are installed on the cluster, install an NGINX ingress controller. There are other options that could also be explored for this such as a simple load balancer or another ingress like AWS Load Balancer Controller add-on.
The following command will install a basic NGINX ingress on the cluster.
# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/aws/deploy.yaml
Once the Nginx installation is complete, create an alias record (an ‘A’ record) that points to the load balancer that was created during the NGINX installation. This requires that the AWS account have a registered domain name - see help for aws route53domains list-domains command which can be used to verify existing registered domain names. Instructions provided at https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-creating.html can be used to create an alias record for the load balancer.
It is also possible to perform this by using the following commands. Ensure that jq is installed on the host if using these commands.
- Get the value for the ingress' external ip (reference in later commands as <ingress_ext_ip>)
kubectl -n ingress-nginx get svc ingress-nginx-controller --no-headers |awk '{print $4}'
- Get the value for the zone id (reference in later commands as <zone_id>) for the domain the record is to be created under - substitute for <domain name> when submitting this command
aws route53 list-hosted-zones-by-name --dns-name "<domain name>" --query "HostedZones[].Id" --output text | cut -d/ -f3
- Get the value for the hosted zone id (reference in later commands as <hosted_zone_id>) defined for the load balancer - substitute for <AWS_region> and <ingress_ext_ip> when submitting this command
aws elbv2 describe-load-balancers --region "<AWS_region>" | jq --arg name "<ingress_ext_ip>" -r '.LoadBalancers | .[] | select(.DNSName=="\($name)") | .CanonicalHostedZoneId'
- Use the values from above commands to run the aws route53 command to create the alias record - substitute for <zone_id>, <hosted_zone_id> and <ingress_ext_id> using the values derived from the above commands. For <FQDN> substitute a fully qualified domain name based on the domain name of the AWS account. For example if the account domain is example.com, perhaps the desired <FQDN> would be svtdemo.example.com. This FQDN will be used as the spec.route.host field setting in the WebSphereLibertyApplication deploy yaml file later in this blog when deploying the DayTrader application.
aws route53 change-resource-record-sets \
--hosted-zone-id <zone_id> \
--change-batch '
{
"Comment": "Creating Alias resource record set in Route53",
"Changes": [{
"Action" : "CREATE",
"ResourceRecordSet" : {
"Name": "'<FQDN>'",
"Type" : "A",
"AliasTarget": {
"HostedZoneId": "'<hosted_zone_id>'",
"DNSName": "'<ingress_ext_ip>'",
"EvaluateTargetHealth": false
}
}
}
]
}'
Install Cert-Manager in your EKS cluster
To allow the configuration of transport layer security (TLS) in WebSphere Liberty Operator and the NGINX ingress controller (which supports TLS termination), install Cert-manager. It will generate and manage the TLS certificates needed to secure the environment using, in this case, a ClusterIssuer resource configured to use the Let's Encrypt certificate authority. It is also possible to use an Issu
First, run the following command to install cert-manager. Please see https://cert-manager.io/docs/installation/supported-releases/ for newer supported releases of cert-manager and update the version if desired.
# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml
Verify the cert-manager installation using the following command and ensure the 3 resulting pods reach Running status:
# kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-99bb69456-46mwb 1/1 Running 0 103s
cert-manager-cainjector-ffb4747bb-b8hfc 1/1 Running 0 103s
cert-manager-webhook-545bd5d7d8-gv8mk 1/1 Running 0 102s
Next, to create the ClusterIssuer resource, run the following command, replacing EMAIL_ADDRESS with your valid email address. This email address will be included in the certificates and also used by Let’s Encrypt to send information and alerts regarding the certificates.
# cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: EMAIL_ADDRESS
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
podTemplate:
spec:
nodeSelector:
"kubernetes.io/os": linux
EOF
Run the script and verify the creation of the ClusterIssuer by running the following command:
# kubectl describe clusterissuer letsencrypt
Install the Operator Lifecycle Manager (OLM) in your EKS cluster
WebSphere Liberty Operator (WLO) can be installed with or without Operator Lifecycle Manager (OLM). Here the Operator SDK is used to install OLM. This SDK is a framework to make writing operators easier, but it also includes a command to install OLM on Kubernetes clusters. Follow the instructions at https://sdk.operatorframework.io/docs/installation/ to install the SDK on the local environment.
Then to install OLM to the Kubernetes cluster, run:
# operator-sdk olm install
Alternatively, OLM could have been installed using instructions found for an appropriate release at https://github.com/operator-framework/operator-lifecycle-manager/releases.
Install the WebSphere Liberty Operator
To install the WebSphere Liberty operator, there are two steps:
- Add IBM Operator Catalog to the cluster
- Install the WebSphere Liberty operator by creating a Subscription
Add IBM Operator Catalog to the cluster
Create a file named ibm_catalog_source.yaml with the following content:
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
name: ibm-operator-catalog
namespace: olm
spec:
displayName: IBM Operator Catalog
publisher: IBM
sourceType: grpc
image: icr.io/cpopen/ibm-operator-catalog:latest
grpcPodConfig:
securityContextConfig: restricted
updateStrategy:
registryPoll:
interval: 45m
Please note that the namespace in the CatalogSource is olm. This namespace was created during the install of OLM.
Run the following command to add this to the OLM catalog:
# kubectl apply -f ibm_catalog_source.yaml -n olm
Verify the catalog entry:
# kubectl get CatalogSources ibm-operator-catalog -n olm
Install the Websphere Liberty Operator by creating a subscription
To install the operator, create a subscription to the WebSphere Liberty Operator in the catalogsource just added. For this environment, the operator will be installed in AllNamespaces mode. For information on installing in other modes, refer to Installing the WebSphere Liberty operator without OLM.
To create the subscription, create a file named wlo-sub.yaml with the following content:
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: websphere-liberty-operator-subscription
namespace: operators
spec:
channel: v1.1
name: ibm-websphere-liberty
source: ibm-operator-catalog
sourceNamespace: olm
installPlanApproval: Automatic
Notice that the namespace for this subscription is operators. This namespace was created during the install of OLM and is associated with a “global Operators” group that watches all namespaces.
Run the following command to add this to the OLM catalog:
# kubectl apply -f wlo-sub.yaml
Verify that the operator is installed and PHASE is Succeeded:
# kubectl get csv -n operators
Install the Sample Daytrader Application
Now that the WebSphere Liberty Operator is successfully installed, the daytrader7 application can be installed.
There are 3 steps to consider:
- Build or use existing sample-daytrader7 application image
- Create DB2 database for daytrader7 application or use sample DB2 container
- Deploy the daytrader7 application
If there is a need to build the application image OR use the sample DB2 container, clone the application Git repo for sample-daytrader7 to the local machine.
# git clone https://github.com/WASdev/sample.daytrader7.git
Build or use existing sample-daytrader7 application image
A prebuilt image of the sample-daytrader7 application is available at 'docker.io/dguinan/sample-daytrader7:latest' .
To build the image if desired, complete the following steps:
- cd sample.daytrader7 and run 'mvn install' to compile and create application ear file.
- Build application image sample-daytrader7 with command 'podman build -t sample-daytrader7 -f Containerfile_db2 .'
- Upload the sample-daytrader7 image to a repo that your cluster can access such as Docker Hub or AWS Elastic Container Registry (ECR). For example,
- podman tag sample-daytrader7 docker.io/mydockerid/sample-daytrader7
- podman push docker.io/mydockerid/sample-daytrader7
Create DB2 for daytrader7 application
If a DB2 database is already available for the application, skip this setup and add that DB2 database information in spec.env of daytrader7-wldemo-deploy.yaml discussed in the next topic.
If there is no existing database, the cloned repo contains the configuration needed to deploy a sample DB2 container with a database for use with this sample daytrader7 application. The files and image used to create this container is for demonstration purposes only and is not supported nor recommended for production use.
To deploy this sample database container:
- Create a daytrader7 namespace
# kubectl create namespace daytrader7
- Deploy sample DB2 container to the cluster, by going to the 'sample.daytrader7/deploy' folder of the Git repo and applying files as follows:
# kubectl -n daytrader7 apply -f db2-role-n-sa.yaml
# kubectl -n daytrader7 apply -f db2-secret.yaml
# kubectl -n daytrader7 apply -f tradedb-service.yaml
# kubectl -n daytrader7 apply -f tradedb.yaml
- Verify that the DB2 pod is in the Running state
# kubectl -n daytrader7 get pods
NAME READY STATUS RESTARTS AGE
trade-db2-7fb8d89c86-m7lcx 1/1 Running 0 51s
Deploy the daytrader7 application
Create a WebSphereLibertyApplication yaml file using the sample daytrader7-wldemo-deploy.yaml file below. The following fields may require updating:
Link back to where FQDN was previously created.
- spec.applicationImage: location of the daytrader application image that was previously built such as 'docker.io/mydockerid/sample-daytrader7:latest' or prebuilt image 'docker.io/dguinan/sample-daytrader7:latest'
- spec.route.annotations.cert-manager.io/cluster-issuer: the Issuer or ClusterIssuer resource name - note that in earlier resource config, metadata.name was letsencrypt
- spec.route.host: the full hostname which is the FQDN generated when creating the alias record
Sample daytrader7-wldemo-deploy.yaml file
apiVersion: liberty.websphere.ibm.com/v1
kind: WebSphereLibertyApplication
metadata:
name: daytrader7
spec:
license:
accept: true
edition: IBM WebSphere Application Server
productEntitlementSource: Standalone
metric: Virtual Processor Core (VPC)
applicationImage: 'docker.io/dguinan/sample-daytrader7:latest'
pullPolicy: Always
replicas: 1
route:
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
host: svtdemo.example.com
certificateSecretRef: tls-secret
path: /daytrader
pathType: Prefix
expose: true
service:
port: 9443
type: ClusterIP
probes:
startup: {}
liveness: {}
readiness: {}
env:
- name: tradeDbHost
value: trade-db2
- name: tradeDbPort
value: "50000"
- name: tradeDbName
value: TRADEDB
- name: dbUser
value: db2inst1
- name: dbPass
valueFrom:
secretKeyRef:
name: db-credential
key: dbpw
resources:
requests:
cpu: 500m
memory: 1024Mi
limits:
cpu: 500m
memory: 2048Mi
Note: The WebSphereLibertyApplication custom resource that was created above was fairly basic, but please see WebSphereLibertyApplication custom resource documentation for other configurable parameters. Please note that if deploying an application on EKS that has been configured with the spec.autoscaling parameter, it may be necessary to also install the Kubernetes Metrics Server on your cluster for successful scaling to occur.
Deploy the daytrader7 application:
# kubectl -n daytrader7 apply -f daytrader7-wldemo-deploy.yaml
Verify the daytrader7 application is deployed and pods are in running state
# kubectl -n daytrader7 get wlapps
NAME IMAGE EXPOSED RECONCILED RESOURCESREADY READY AGE
daytrader7 docker.io/dguinan/sample-daytrader7:latest true True True True 89s
# kubectl -n daytrader7 get pod
NAME READY STATUS RESTARTS AGE
daytrader7-6fd857bdb-qqb4v 1/1 Running 0 99s
trade-db2-7fb8d89c86-m7lcx 1/1 Running 0 12m
The daytrader application should now be accessible using the Ingress URL and daytrader context root which is /daytrader. If the results of an ingress query produced the following modified response, access to the application would be https://svtdemo.example.com/daytrader.
# kubectl -n daytrader7 get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
daytrader7 <none> svtdemo.example.com abcxxxx-123xxx.elb.amazonaws.com 80, 443 114s