Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Hello everyone in this blog! Here is the third post in our series showing how to deploy modern web applications on Red Hat OpenShift.

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

In the previous two posts, we explained how to deploy modern web applications in just a few steps and how to use the new S2I image along with a ready-made HTTP server image, like NGINX, with the help of chained builds for production deployment.

Today, we will show how to run a development server for your application on the OpenShift platform and sync it with your local file system, as well as discuss what OpenShift Pipelines are and how they can serve as an alternative to chained builds.

OpenShift as a Development Environment

Development Workflow

As mentioned in the first post, a typical development process for modern web applications involves a 'development server' that monitors changes in local files. When changes occur, it triggers a build of the application, and then the application is updated in the browser.

In most modern frameworks, such a 'development server' is built into the corresponding command line tools.

Local Example

First, let's see how this works with local application launches. We'll take the application React from previous articles, although the same workflow concepts apply across all other modern frameworks.
So, to start the 'development server' in our React example, we enter the following command:

$ npm run start

Then, we will see something like the following in the terminal window:

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

And our application will open in the default browser:

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Now, if we make changes to the file, the application should update in the browser.

OK, development in local mode is clear, but how do we achieve the same on OpenShift?

Development Server on OpenShift

If you remember, in the the previous post, we discussed the so-called run phase of the S2I image and saw that by default, our web application is served by the serve module.

However, if we take a closer look at the run script from that example, there is an environment variable $NPM_RUN, which allows you to execute your command.

For example, you can use the nodeshift module to deploy our application:

$ npx nodeshift --deploy.env NPM_RUN="yarn start" --dockerImage=nodeshift/ubi8-s2i-web-app

Note: The example above is presented in a condensed form to illustrate the general idea.

Here we added an environment variable NPM_RUN to our deployment, which tells the execution stage to run the yarn start command that starts the React development server inside our OpenShift pod.

If you check the log of the running pod, it will look something like this:

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Of course, this will all be meaningless until we can synchronize local code with the code that is also monitored for changes but lives on a remote server.

Synchronization of remote and local code

Fortunately, nodeshift makes synchronization easy, and to track changes, you can use the watch command.

So after we executed the command to deploy the development server for our application, we can confidently use the following command:

$ npx nodeshift watch

As a result, a connection will be made to the running pod that we created earlier, synchronization of our local files with the remote cluster will be activated, and the files on our local system will start being monitored for changes.

Therefore, if we now update the file src/App.js, the system will respond to these changes, copy them to the remote cluster, and start the development server, which will then update our application in the browser.

To complete the picture, let's show what these commands look like in full:

$ npx nodeshift --strictSSL=false --dockerImage=nodeshift/ubi8-s2i-web-app --build.env YARN_ENABLED=true --expose --deploy.env NPM_RUN="yarn start" --deploy.port 3000

$ npx nodeshift watch --strictSSL=false

The watch command is an abstraction over the oc rsync command; you can learn more about how it works. here.

This was an example for React, but the same method can be used with other frameworks; just configure the environment variable NPM_RUN accordingly.
 

OpenShift Pipelines

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Next, we will discuss a tool called OpenShift Pipelines and how it can be used as an alternative to chained builds.

What are OpenShift Pipelines?

OpenShift Pipelines is a cloud-oriented CI/CD system for continuous integration and delivery, designed to organize pipelines using Tekton. Tekton is a flexible Kubernetes-native CI/CD framework with open source, allowing for deployment automation across various platforms (Kubernetes, serverless, virtual machines, etc.) by abstracting from the underlying layer.

Certain knowledge of Pipelines is required to understand this article, so we strongly recommend starting with the official documentation..

Setting Up the Working Environment

To experiment with the examples in this article, you first need to prepare the working environment:

  1. Install and set up an OpenShift 4 cluster. We use CodeReady Containers (CRC) in our examples, and installation instructions can be found here.
  2. Once the cluster is ready, you need to install the Pipeline Operator. Don't worry, it's easy, the installation instructions here.
  3. Download Tekton CLI (tkn) here.
  4. Run the create-react-app command line tool to generate an application that will later be deployed (this is a simple application React).
  5. (Optional) Clone the repository to run the application locally using the command npm install and then npm start.

In the application repository, there will also be a k8s folder containing the Kubernetes/OpenShift YAML files used for deploying the application. There, you will find Tasks, ClusterTasks, Resources, and Pipelines that we will create in this the repository.

Let's get started

First, we need to create a new project in the OpenShift cluster for our example. We will call this project webapp-pipeline and create it with the following command:

$ oc new-project webapp-pipeline

This project name will appear in the code, so if you decide to name it something else, be sure to adjust the code in the examples accordingly. Starting from this point, we will go bottom-up: first creating all the components of the pipeline and only then the pipeline itself.

So, first things first…

Tasks

Let's create a couple of tasks that will help deploy the application within our pipeline. The first task – apply_manifests_task – is responsible for applying the YAML specifications of the Kubernetes resources (service, deployment, and route) located in the k8s folder of our application. The second task – update_deployment_task – is responsible for updating the already deployed image to the one created by our pipeline.

Don't worry if it's not very clear yet. In fact, these tasks are somewhat like utilities, and we'll go into more detail a bit later. For now, let's just create them:

$ oc create -f https://raw.githubusercontent.com/nodeshift/webapp-pipeline-tutorial/master/tasks/update_deployment_task.yaml
$ oc create -f https://raw.githubusercontent.com/nodeshift/webapp-pipeline-tutorial/master/tasks/apply_manifests_task.yaml

Then we will use the tkn CLI command to check that the tasks have been created:

$ tkn task ls

NAME                AGE
apply-manifests     1 minute ago
update-deployment   1 minute ago

Note: these are local tasks for your current project.

Cluster tasks

Cluster tasks are essentially the same as regular tasks. They are a reusable collection of steps that are combined in some way when a specific task is run. The difference is that a cluster task is available throughout the cluster. To see the list of cluster tasks that are automatically created when adding a Pipeline Operator, we will again use the tkn CLI command:

$ tkn clustertask ls

NAME                       AGE
buildah                    1 day ago
buildah-v0-10-0            1 day ago
jib-maven                  1 day ago
kn                         1 day ago
maven                      1 day ago
openshift-client           1 day ago
openshift-client-v0-10-0   1 day ago
s2i                        1 day ago
s2i-go                     1 day ago
s2i-go-v0-10-0             1 day ago
s2i-java-11                1 day ago
s2i-java-11-v0-10-0        1 day ago
s2i-java-8                 1 day ago
s2i-java-8-v0-10-0         1 day ago
s2i-nodejs                 1 day ago
s2i-nodejs-v0-10-0         1 day ago
s2i-perl                   1 day ago
s2i-perl-v0-10-0           1 day ago
s2i-php                    1 day ago
s2i-php-v0-10-0            1 day ago
s2i-python-3               1 day ago
s2i-python-3-v0-10-0       1 day ago
s2i-ruby                   1 day ago
s2i-ruby-v0-10-0           1 day ago
s2i-v0-10-0                1 day ago

Now let's create two cluster tasks. The first will generate an S2I image and send it to the OpenShift internal registry; the second will build our image based on NGINX, using the application we have built as the content.

Creating and sending the image

When creating the first task, we will repeat what we have already done in the previous article about related builds. Let's recall that we used the S2I image (ubi8-s2i-web-app) to 'build' our application, resulting in an image stored in the internal OpenShift registry. Now we will use this S2I web application image to create a DockerFile for our application, and then we will use Buildah to perform the actual build and push the resulting image to the internal OpenShift registry, as this is exactly what OpenShift does when you deploy your applications using NodeShift.

Wondering where we learned all this? From официальной версии official Node.js, we simply copied it and adjusted it for ourselves.

Now, let's create the cluster task s2i-web-app:

$ oc create -f https://raw.githubusercontent.com/nodeshift/webapp-pipeline-tutorial/master/clustertasks/s2i-web-app-task.yaml

We won't go into detail here, but we will focus on the OUTPUT_DIR parameter:

params:
      - name: OUTPUT_DIR
        description: The location of the build output directory
        default: build

By default, this parameter is set to build, which is where React places the compiled content. Other frameworks use different paths, for example in Ember it's dist. The output of our first cluster task will be an image containing the HTML, JavaScript, and CSS that we compiled.

Building an image based on NGINX

As for our second cluster task, it should build an image based on NGINX using the content of the application we have already compiled. Essentially, this is the part of the previous section where we discussed related builds.

To do this, we will create a cluster task webapp-build-runtime just like we did above:

$ oc create -f https://raw.githubusercontent.com/nodeshift/webapp-pipeline-tutorial/master/clustertasks/webapp-build-runtime-task.yaml

If you look at the code of these cluster tasks, you will see that the Git repository we are working with or the names of the images we are creating are not specified. We only define what exactly we are passing to Git or some image where the final image should be output. This is why these cluster tasks can be reused when working with other applications.

And here we gracefully transition to the next point...

Resources

So, as we just mentioned, since clustered tasks should be as general as possible, we need to create resources that will be used as input (Git repository) and output (final images). The first resource we need is Git, where our application is located, something like this:

# This resource is the location of the git repo with the web application source
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: web-application-repo
spec:
  type: git
  params:
    - name: url
      value: https://github.com/nodeshift-starters/react-pipeline-example
    - name: revision
      value: master

Here, PipelineResource has the type git. The key url in the params section specifies the specific repository and sets the master branch (this is optional, but we include it for completeness).

Now we need to create a resource for the image where the results of the s2i-web-app task will be saved, which is done like this:

# This resource is the result of running "npm run build",  the resulting built files will be located in /opt/app-root/output
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: built-web-application-image
spec:
  type: image
  params:
    - name: url
      value: image-registry.openshift-image-registry.svc:5000/webapp-pipeline/built-web-application:latest

Here, PipelineResource has the type image, and the value of the url parameter points to the internal OpenShift Image Registry, specifically the one located in the webapp-pipeline namespace. Don't forget to change this parameter if you are using another namespace.

And finally, the last resource we will need will also be of type image, and it will be the final NGINX image that will be used during deployment.

# This resource is the image that will be just the static html, css, js files being run with nginx
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: runtime-web-application-image
spec:
  type: image
  params:
    - name: url
      value: image-registry.openshift-image-registry.svc:5000/webapp-pipeline/runtime-web-application:latest

And again, note that this resource saves the image in the internal OpenShift registry in the webapp-pipeline namespace.

To create all these resources at once, we will use the create command:

$ oc create -f https://raw.githubusercontent.com/nodeshift/webapp-pipeline-tutorial/master/resources/resource.yaml

You can check that the resources have been created like this:

$ tkn resource ls

Pipeline

Now that we have all the necessary components, let's assemble them into a pipeline by running the following command:

$ oc create -f https://raw.githubusercontent.com/nodeshift/webapp-pipeline-tutorial/master/pipelines/build-and-deploy-react.yaml

But before we run this command, let's break down these components. The first is the name:

apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: build-and-deploy-react

Then in the spec section, we see the indication of the resources we created earlier:

spec:
  resources:
    - name: web-application-repo
      type: git
    - name: built-web-application-image
      type: image
    - name: runtime-web-application-image
      type: image

Next, we create the tasks that our pipeline should perform. First, it should execute the task we already created, s2i-web-app:

tasks:
    - name: build-web-application
      taskRef:
        name: s2i-web-app
        kind: ClusterTask

This task takes input (git resource) and output (built-web-application-image resource) parameters. We also pass it a special parameter so that it does not verify TLS, since we are using self-signed certificates:

resources:
        inputs:
          - name: source
            resource: web-application-repo
        outputs:
          - name: image
            resource: built-web-application-image
      params:
        - name: TLSVERIFY
          value: "false"

The next task is almost the same, but here we call the cluster task webapp-build-runtime that we have already created.

name: build-runtime-image
    taskRef:
      name: webapp-build-runtime
      kind: ClusterTask

As with the previous task, we pass the resource, but now it is built-web-application-image (the output of our previous task). And as output, we again specify the image. Since this task needs to run after the previous one, we add the runAfter field:

resources:
        inputs:
          - name: image
            resource: built-web-application-image
        outputs:
          - name: image
            resource: runtime-web-application-image
        params:
        - name: TLSVERIFY
          value: "false"
      runAfter:
        - build-web-application

The next two tasks are responsible for applying the YAML files for the service, route, and deployment, which reside in the k8s directory of our web application, as well as updating this deployment when new images are created. We defined these two cluster tasks back at the beginning of the article.

Start the pipeline

So, all parts of our pipeline are created, and we will start it with the following command:

$ tkn pipeline start build-and-deploy-react

At this stage, the command line is used interactively, and you need to select the appropriate resources in response to each prompt: for the git resource choose web-application-repo, then for the first image resource – built-web-application-image, and finally, for the second image resource – runtime-web-application-image:

? Choose the git resource to use for web-application-repo: web-application-repo (https://github.com/nodeshift-starters/react-pipeline-example)
? Choose the image resource to use for built-web-application-image: built-web-application-image (image-registry.openshift-image-registry.svc:5000/webapp-pipeline/built-web-
application:latest)
? Choose the image resource to use for runtime-web-application-image: runtime-web-application-image (image-registry.openshift-image-registry.svc:5000/webapp-pipeline/runtim
e-web-application:latest)
Pipelinerun started: build-and-deploy-react-run-4xwsr

Now let's check the status of the pipeline using the following command:

$ tkn pipeline logs -f

Once the pipeline starts and the application is deployed, we can retrieve the published route with the following command:

$ oc get route react-pipeline-example --template='http://{{.spec.host}}'

For better visibility, we can view our pipeline in Developer mode in the web console under Pipelines, as shown in Fig. 1.

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Fig. 1. Overview of running pipelines.

Clicking on the running pipeline displays additional details, as shown in Fig. 2.

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Fig. 2. Additional details about the pipeline.

After viewing the additional details, we can see the running applications in the Topology, as shown in Fig. 3.

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Fig. 3. Running pod.

Clicking on the circle in the upper right corner of the icon opens our application, as shown in Fig. 4.

Modern Applications on OpenShift, Part 3: OpenShift as a Development Environment and OpenShift Pipelines

Fig. 4. Running React application.

Conclusion

We have shown how to launch a development server for your application on OpenShift and synchronize it with the local file system. We also discussed how to simulate a chained-build template using OpenShift Pipelines. All example codes from this article can be found here.

Additional resources (EN)

Announcements of upcoming webinars

We are starting a series of Friday webinars on the native experience of using Red Hat OpenShift Container Platform and Kubernetes:

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster