Container-based software development is growing. Since it’s easy to replicate the environment, developers generally create applications on their desktop, and debug and test them locally. Later, they build and deploy the application to a Kubernetes cluster.
In this tutorial, I show you two ways to deploy an application to a Kubernetes cluster on IBM Cloud:
- Using a
kubectl
CLI without a DevOps pipeline - Using a Tekton Pipeline (which is a Kubernetes-style continuous integration and continuous delivery (CI/CD) pipeline)
Prerequisites
To complete this tutorial, you need to:
- Create an IBM Cloud account.
- Get an instance of Kubernetes Service on IBM Cloud, which should take approximately 20 minutes.
- Access a Kubernetes cluster through the
kubectl
CLI. To access the instructions, go to the IBM Cloud dashboard > [your cluster] > Access. - Create a namespace on the IBM Cloud container registry. To do so, go to your IBM Cloud dashboard and click Navigation > Kubernetes > Registry > Namespaces.
Configure the Git CLI. Clone the repository to your workstation using the following command:
git clone https://github.com/IBM/deploy-app-using-tekton-on-kubernetes.git
Estimated time
After your prerequisites are configured, this tutorial takes about 40 minutes.
Build and deploy an application on IBM Cloud Kubernetes Service using kubectl
To build and deploy an application on a Kubernetes cluster, perform the following steps:
- Write a Dockerfile for your application and build the container image using Dockerfile.
- Upload the built container image to the accessible container registry.
- Create a Kubernetes deployment using the container image and deploy the application to an IBM Cloud Kubernetes Service cluster using configuration (YAML) files.
For this tutorial, we have taken a simple Hello World! Node.js application to deploy on Kubernetes as shown. The following is code from our sample app; use one that you have on hand.
const app = require('express')()
app.get('/', (req, res) => {
res.send("Hello from Appsody!");
});
var port = 3300;
var server = app.listen(port, function () {
console.log("Server listening on " + port);
})
module.exports.app = app;
The Dockerfile
and the deployment configuration deploy.yaml
is available in the GitHub repository that you cloned earlier. The steps explained in this section help you to deploy your application to a cluster using CLIs.
1. Set up a deploy target
As a first step, you need to set the correct deploy target for the container image to upload to the accessible container registry. Depending on the region you created your cluster in, your image URL will be in the following format:
<REGION_ABBREVIATION>.icr.io/<YOUR_NAMESPACE>/<YOUR_IMAGE_NAME>:<VERSION>
The following command tells you the Registry API endpoint for your cluster. You can get the region abbreviation from the output.
ibmcloud cr api
To get a namespace, use the following command:
ibmcloud cr namespaces
For example, the deployment target for the US-South region is:
us.icr.io/test_namespace/sampleapp:1.0
2. Deploy the application
Run the following commands to deploy your application on a Kubernetes cluster:
cd ~/deploy-app-using-tekton-on-kubernetes/src
# Build and push it to IBM Cloud Container registry. Following command takes care of build and push to container registry and eliminates the overhead to run docker commands individually.
ibmcloud cr build -t us.icr.io/test_namespace/sampleapp:1.0 .
# Verify whether the image is uploaded to the container registry
ibmcloud cr images
# Update deploy target in deploy.yaml
sed -i '' s#IMAGE#us.icr.io/test_namespace/sampleapp:1.0# deploy.yaml
# Run deploy configuration
kubectl create -f deploy.yaml
# Verify output - pod and service should be up and running
kubectl get pods
kubectl get service
After successful deployment, your application is accessible at http://<public-ip-of-kubernetes-cluster>:32426/
where you can retrieve the public IP of a Kubernetes cluster from your IBM Cloud dashboard and the port 32426 is defined as nodePort
in deploy.yaml
.
The steps above showed you how to deploy onto a Kubernetes cluster using CLIs. If you change your application after deployment, you need to rerun the steps.
To build, test, and deploy applications faster and more reliably, you need to automate this entire workflow. Following a CI/CD methodology reduces the overhead of development and manual deployment processes, which can save you significant time and effort.
The next section of this tutorial explains the build and deploy approach using Tekton Pipelines.
Build and deploy an application on IBM Cloud Kubernetes Service using Tekton Pipeline
Tekton is a powerful and flexible Kubernetes-native open source framework for creating CI/CD systems. It allows you to build, test, and deploy across multiple cloud providers or on-premises systems by abstracting away the underlying implementation details.
Before I show you how to use Tekton Pipelines, here are some of the high-level concepts you need to understand:
The Tekton Pipeline project extends the Kubernetes API by five additional custom resource definitions (CRDs) to define pipelines:
- A task is an individual job and defines a set of build steps such as compiling code, running tests, and building and deploying images.
- A Taskrun runs the task you defined. With taskrun, it’s possible to execute a single task, which binds the inputs and outputs of the task.
- Pipeline describes a list of tasks that compose a pipeline.
- Pipelinerun defines the execution of a pipeline. It references the pipeline to run and which PipelineResource(s) to use as input and output.
- The PipelineResource defines an object that is an input (such as a Git repository) or an output (such as a Docker image) of the pipeline.
To automate the application’s build and deploy workflow using Tekton Pipelines, follow these steps:
1. Add the Tekton Pipelines component to your Kubernetes cluster
As a first step, add the Tekton Pipelines to your Kubernetes cluster using the following command:
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
The installation creates two pods which you can check using the following command. Make sure to wait until the pods are in a running state.
kubectl get pods --namespace tekton-pipelines
For more information on this, refer to the Tekton documentation. After completing these steps, your Kubernetes cluster is ready to run Tekton Pipelines. Let’s start by creating the definition of custom resources.
2. Create a PipelineResource
In this tutorial’s example, the source code of the application, Dockerfile, and deployment configuration is available in the GitHub repository that you cloned earlier.
To create the input PipelineResource to access the Git repository, do the following:
In the git.yaml
file, define the PipelineResource
for the Git repository by doing the following:
- Specify the resource
type
as Git. - Provide the Git repository URL as
url
. - Provivde
revision
as the name of the branch of the Git repository to be used.
The complete YAML file is available at ~/tekton-pipeline/resources/git.yaml
. Apply the file to the cluster as shown.
cd ~/tekton-pipeline
kubectl apply -f resources/git.yaml
3. Create tasks
A task defines the steps of the pipeline. To deploy an application to a cluster using source code in the Git repository, we define two tasks — build-image-from-source
and deploy-to-cluster
. In the task definition, the parameters used as arguments (args
) are referred to as $(inputs.params.<var_name>)
.
Define build-image-from-source
This task includes two steps:
- The
list-src
step lists the source code from the cloned repository. This is done to verify whether source code is cloned properly. - The
build-and-push
step builds the container image using Dockerfile and pushes the built image to the container registry. In this example, we use Kaniko to build and push the image. You can use Kaniko, buildah, podman, etc. Kaniko uses the Dockerfile name, its location, and destination to upload the container image as arguments.
All required parameters are passed through parameters. Apply the file to the cluster using the following command:
kubectl apply -f task/build-src-code.yaml
Define deploy-to-cluster
Now let’s deploy the application in a pod using the built container image, and make it available as a service to access from anywhere. This task uses the deployment configuration located as ~/src/deploy.yaml
.
This task includes two steps:
- The
update-yaml
step updates the container image URL in place ofIMAGE
in the deploy.yaml. - The
deploy-app
step deploys the application in a Kubernetes pod and exposes it as a service using~/src/deploy.yaml
. This step useskubectl
to create a deployment configuration on a Kubernetes cluster.
All required parameters are passed through parameters.
Apply the file to the cluster as:
kubectl apply -f task/deploy-to-cluster.yaml
4. Create a pipeline
A pipeline lists the tasks to be executed. It provides the input, output resources, and input parameters required by each task. If there is any dependency between the tasks, that is also addressed.
In the tekton-pipeline/resources/pipeline.yaml
:
- A pipeline uses the above mentioned tasks
build-image-from-source
anddeploy-to-cluster
. - The
runAfter
key is used here because the tasks need to be executed one after the other. - The PipelineResource (Git repository) is provided through the
resources
key.
All required parameters are passed through parameters. The parameters value is defined in the pipeline as $(params.imageUrl)
which is different than the args
in the task definition. Apply this configuration as:
kubectl apply -f pipeline/pipeline.yaml
5. Create PipelineRun
To execute the pipeline, you need a PipelineRun
resource definition, which passes all required parameters. PipelineRun
triggers the pipeline, and the pipeline, in turn, creates TaskRuns
and so on. In a similar manner, all parameters get substituted down to the tasks.
If a parameter is not defined in the PipelineRun
, then the default value gets picked up from the params
under spec
from the resource definition itself. For example, the pathToDockerfile
parameter is used in the task build-image-from-source
, but its value is not provided in pipeline-run.yaml
. Because of this, its default value defined in ~/tekton-pipeline/build-src-code.yaml
is used during the task execution.
In the PipelineRun
definition, tekton-pipeline/pipeline/pipeline-run.yaml
:
- References the pipeline
application-pipeline
created throughpipeline.yaml
. - References the PipelineResource
git
to use as input. - Provides the value of parameters under
params
which are required during the execution of the pipeline and the tasks. - Specifies a service account.
Note that through the pipeline, you can push images to the registry and deploy it to a cluster. You need to ensure that it has the sufficient privileges to access the container registry and the cluster. The credentials for the registry are provided by a service account. You need to define a service account before executing PipelineRun
.
Note: Do not apply the PipelineRun
file yet because you still need to define the service account for it.
6. Create a service account
To access the protected resources, set up a service account which uses secrets to create or modify Kubernetes resources. The IBM Cloud Kubernetes Service is configured to use IBM Cloud Identity and Access Management (IAM) roles. These roles determine the actions that users can perform on IBM Cloud Kubernetes.
Generate an API key
To generate an API key using IBM Cloud Dashboard, follow the instructions in the IBM Cloud documentation. You can also use the following CLI command to create the API key:
ibmcloud iam api-key-create MyKey -d "this is my API key" --file key_file.json
cat key_file.json | grep apikey
Copy the apikey
. You will use it in the next step.
Create secrets
To create a secret, use the following code. APIKEY
is the one that you created and REGISTRY
is the registry API endpoint for your cluster. An example would be us.icr.io
.
kubectl create secret generic ibm-cr-secret --type="kubernetes.io/basic-auth" --from-literal=username=iamapikey --from-literal=password=<APIKEY>
kubectl annotate secret ibm-cr-secret tekton.dev/docker-0=<REGISTRY>
This creates a secret named ibm-cr-secret
which is then used in the configuration file for the service account.
In the configuration file tekton-pipeline/pipeline/service-account.yaml
:
- The
ServiceAccount
resource uses the secretibm-cr-secret
. - As per the definition of a secret resource, the newly built secret is populated with an API token for the service account.
The next step is to define roles. A role can only be used to grant access to resources within a single namespace. You must include appropriate resources and apiGroups in rules or it will fail because of access issues. A role binding grants the permissions defined in a role to a user or set of users. It holds a list of subjects (users, groups, or service accounts) and a reference to the role being granted.
Apply this configuration as:
kubectl apply -f pipeline/service-account.yaml
7.Execute the pipeline
Before executing the PipelineRun
, modify the imageUrl
and the imageTag
in tekton-pipeline/pipeline/pipelinerun.yaml
. Refer to the Set up deploy target section above to decide on an image URL and tag. If the image URL is us.icr.io/test_namespace/sampleapp and the image tag is 1.0, then update the configuration file to:
sed -i '' s#IMAGE_URL#us.icr.io/test_namespace/sampleapp# pipeline/pipelinerun.yaml
sed -i '' s#IMAGE_TAG#1.0# pipeline/pipelinerun.yaml
Now, create the PipelineRun
configuration, like so:
kubectl create -f pipeline/pipeline-run.yaml
This creates a pipeline with the below message on your terminal:
pipelinerun.tekton.dev/application-pipeline-run created
Check the status of your new pipeline:
kubectl describe pipelinerun application-pipeline-run
You may need to rerun this command based on the status. It shows the interim status as:
Status:
Conditions:
Last Transition Time: 2020-07-09T08:43:53Z
Message: Tasks Completed: 0 (Failed: 0, Cancelled 0), Incomplete: 2, Skipped: 0
Reason: Running
Status: Unknown
Type: Succeeded
...
...
Events:
Type Reason Age From Message
---- ------ --- ---- -------
Normal Started 33s PipelineRun
Normal Running 33s PipelineRun Tasks Completed: 0 (Failed: 0, Cancelled 0), Incomplete: 2, Skipped: 0
Note the message where it tells you that “Tasks Completed: 0”.
Once the execution of your pipeline is complete, you should see the following as an output of the describe
command.
Events:
Type Reason Age From Message
---- ------ --- ---- -------
Normal Started 2m33s PipelineRun
Normal Running 2m33s PipelineRun Tasks Completed: 0 (Failed: 0, Cancelled 0), Incomplete: 2, Skipped: 0
Normal Running 57s PipelineRun Tasks Completed: 1 (Failed: 0, Cancelled 0), Incomplete: 1, Skipped: 0
Normal Succeeded 43s PipelineRun Tasks Completed: 2 (Failed: 0, Cancelled 0), Skipped: 0
In case of failure, it shows which task has failed. It also gives you the additional details to check logs. For details about a resource (for instance, the pipeline) use the kubectl describe
command to get more information.
kubectl describe <resource> <resource-name>
8. Verify your results
To verify whether the pod and service is running as expected, check the output of the following commands:
kubectl get pods
# Output should be something like this
NAME READY STATUS RESTARTS AGE
app-59dff7b655-7ggbt 1/1 Running 0 81s
application-pipeline-run-build-image-from-source-2m62g-pod-f4eb96 0/3 Completed 0 119s
application-pipeline-run-deploy-application-kg2jm-pod-89f884 0/3 Completed 0 89s
kubectl get service
# Output
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app NodePort xxx.xx.xx.xxx <none> 3300:32426/TCP 4m51s
After successful execution of PipelineRun
, the application is accessible at http://<public-ip-of-kubernetes-cluster>:32426/
, where you can retrieve the public IP of your Kubernetes cluster from your IBM Cloud dashboard, and the port 32426 is defined as nodePort
in deploy.yaml
.
Congratulations! You successfully deployed your application using a Tekton Pipeline. You should now understand the basics of Tekton Pipelines and how to get started on building your own. There are more features available, including webhooks and web-based dashboards. I suggest trying it out with IBM Cloud Kubernetes Service.
Next steps
This tutorial showed you how and why you should use Tekton Pipelines for deploying an application to Kubernetes. Tekton is included in IBM Cloud Pak for Applications, which offer a faster, more secure way to move your business applications to the cloud, in a container-enabled environment. Cloud Pak for Applications is built and supported on Red Hat OpenShift. Explore and try out more with the help of this developer guide.