Oracle JET Web Applications – Automating Build, Package and Deploy (to Application Container Cloud) using a Docker Container

0

The essential message of this article is the automation for Oracle JET application of the flow from source code commit to a running application on Oracle Application Container Cloud, as shown in this picture:

image

I will describe the inside of the “black box” (actually light blue in this picture) where the build, package and deploy are done for an Oracle JET application.

The outline of the approach: a Docker Container is started in response to the code commit. This container contains all tooling that is required to perform the necessary actions including the scripts to actually run those actions. When the application has been deployed (or the resulting package is stored in an artifact repository) the container can be stopped. This approach is very clean – intermediate products that are created during the build process simply vanish along wih the container. A fresh container is started for the next iteration.

Note: the end to end build and deploy flow takes about 2 to 3 minutes on my environment. That obviously would be horrible for a simple developer round trip, but is actually quite acceptable for this type of ‘formal’ release to the shared cloud environment. This approach and this article are heavily inspired by this article (Deploy your apps to Oracle Cloud using PaaS Service Manager CLI on Docker) on Medium by Abhishek Gupta (who writes many very valuable articles, primarily around microservices and Oracle PaaS services such as Application Container Cloud).

Note: this article focuses on final deployment of the JET application to Application Container Cloud. It would however be quite simple to modify (in fact to simplify)the build container to not deploy the final ZIP file to Application Container Cloud, but instead push the file to an artifact repository or deploy to some other type of runtime platform. It would not be very hard to take the ZIP file and create a fresh Docker Container with that file that can be deployed on Kubernetes Cluster or any Docker runtime such as Oracle Container Cloud.

The sources – including a sample JET Application – are in this GitHub repo: https://github.com/lucasjellema/webshop-portal-soaring-through-the-cloud-native-sequel .

The steps I describe in this article are:

  • preparation of the Docker Container that will do the build-package-deploy actions
  • preparation of the Oracle JET application – to be turned from a locally run, developer only client side web application into a stand-alone runnable enterprise web app with server side platform (Node with Express)
  • creation of the build script that will run inside the container and orchestrate the actions by the available tools to take the source all the way to the cloud
  • putting it all together

 

1. Preparation of the Docker Container that will do the build-package-deploy actions

The first step is the composition of the Docker Container. For this step, I have made good use of Abhishek’s article and the dockerfile he proposes in that article. I complemented Abhishek’s Dockerfiles with the tooling required for building Oracle JET applications.

A visual presentation of what the Docker Container will contain – and the steps made to put it together – is shown below:

image

Note: it is fun to bake Docker Container Images completely through a Docker file – and it is certainly convenient to share the instructions for creating a Docker Container image in the form of a Docker file. However, when the steps are complex to automated through a Docker file, there is a simple alternative: build as much of the container as you can through a Docker file. Then run the container and complete it through manual steps. Finally, when the container does what you need it to do, you can commit the state of the container as your reusable container image. And perhaps at this point, you can try to extend the Docker file with some of the manual steps, if you feel that maintaining the image will be a frequently recurring task.

The Docker build file that I finally put together is included below. The key steps:

  • the container is based on the “python:3.6.2-alpine3.6” image; this is done mainly because the PSM (Oracle PaaS Service Manager command line tool requires a Python runtime environment)
  • the apk package manager for Alpine Linux is used several times to add required packages to the image; it adds curl, zip, nodejs, nodejs-npm, bash, git and openssh
  • download and install the Oracle PSM command line tool (a Python application)
  • set up PSM for the target identity domain and user
  • install the Oracle JET Command Line tool that will be used for building the JET web application
  • copy the script build-app.sh that will be executed to run the end-to-end build-package-deploy flow

 

# extended from https://medium.com/oracledevs/quick-start-docker-ized-paas-service-manager-cli-f54eaf4ebcc7
# added npm, ojet-cli and git

FROM python:3.6.2-alpine3.6

ARG USERNAME
ARG PASSWORD
ARG IDENTITY_DOMAIN
ARG PSM_USERNAME
ARG PSM_PASSWORD
ARG PSM_REGION
ARG PSM_OUTPUT


WORKDIR "/oracle-cloud-psm-cli/"

RUN apk add --update curl && \
    rm -rf /var/cache/apk/*

RUN curl -X GET -u $USERNAME:$PASSWORD -H X-ID-TENANT-NAME:$IDENTITY_DOMAIN https://psm.us.oraclecloud.com/paas/core/api/v1.1/cli/$IDENTITY_DOMAIN/client -o psmcli.zip && \
	pip3 install -U psmcli.zip 

COPY psm-setup-payload.json
RUN psm setup -c psm-setup-payload.json

RUN apk add --update nodejs nodejs-npm
RUN apk add --update zip

RUN npm install -g @oracle/ojet-cli

RUN apk update && apk upgrade &&  apk add --no-cache bash git openssh

COPY build-app.sh .

CMD ["/bin/sh"]

Use this command to build the container:

docker build –build-arg USERNAME=”your ACC cloud username” –build-arg PASSWORD=”the ACC cloud password” –build-arg IDENTITY_DOMAIN=”your identity domain” –build-arg PSM_REGION=”us” –build-arg PSM_OUTPUT=”json” -t psm-cli .

assuming that this command is run in the directory where the docker file is located.

This will create a container and tag it as image psm-cli. When this command completes, you can find the container image by running “docker images”. Subsequently, you can run a container based on the image: “docker run –rm -it psm-cli”

     

    2. Preparation of the Oracle JET application

    When developing a JET (4.x) application, we typically use the Oracle JET CLI – the command line tool that helps us to quickstart a new application, create composite components, serve the application locally as we are developing it to a browser with instant update of any file changes. The JET CLI is also used to build the application for release. The result of this step is the complete set of files needed to run the JET application in the browser. In order to actually offer the JET application to end users, it has to be served from a ‘web serving’ platform component – such as nginx or a backend in Python, Java or Node. Frequently, the JET application will require some server side facilities that the backend that serves the static JET application resources can also provide. For that reason, I select a JET serving backend that I can easily leverage for these serverside facilities; for me, this is currently Node.

    In order to create a self running JET application for the JET application built in the pipeline discussed in this article, I have added a simple Node & Express backend.

    I have used npm to create a new Node application (npm init jet-on-node). I have next created directory bin and the file www. This file is main entrypoint into the node application that serves the JET application; it delegates most work to module app that is loaded from file app.js in the root of this Node application, path /jet-on-node .

     

    SNAGHTML8be0161

    All static resources that the browser can access (including the JET application) go into the folder /jet-on-node/public. Module app defines – through Express – that requests for public resources (requests not handled by one of the URL path handlers) are taken care – by serving resources from the directory /public. Module app can handle other HTTP requests – for example from the JET application – and it could also implement the backend for Server Sent Events or WebSockets. Currently is handles the REST GET request to path “/about” that returns some key data for the application:

    SNAGHTML8d17d8a

    The dependencies for the jet-on-node application are defined in package.json during the build process of the final application, we will use “npm install” to add the server side required node modules.

    At this point, we have extended our code base with a simple landing platform for the JET application that can serve the application at runtime. All that remains is to take all content under the /web directory and copy it to the jet-on-node/public folder. Then we can run the application using “npm start” in directory jet-on-node. This will execute the start script in file package.json – which is defined as “node ./bin/www”.

     

    3. Creation of the build script that will run inside the container and
    orchestrate the actions

    The JET build container is available. The JET application is available from a Git repository (in my example in GitHub). A number of steps are now required to go to a running application on Application Container Cloud. The first steps are shown below:

     

    image

    1. Clone the Git repo that contains the JET application (or pull the latest sources or a specific tag)

    2. Install all modules required by the JET application – by running npm install

    3. Use the Oracle JET command line utility to build the application for release: ojet build –release

    After this step, all run time artifacts – including the JET libraries – are in the /web directory. These next steps turn these artifacts into a running application:

    4. Copy the contents of /web to /jet-on-node/public

    5. Install the modules required for the server side Node application by running npm install in directory jet-on-node

    6. Create a single zip file for all artifacts in the /jet-node directory – that includes both the JET application and its server side backend Node application. This zip-file is the release artifact for the JET application. As such, it can be pushed to an artifact repository or deployed to some other platform.

    7. Engage psm command line interface (Oracle PaaS Service Manager CLI) to perform deployment of the zip file to the Application Container Cloud for which psm already as configured during the creation of the build container.

    Note: the files manifest.json and deployment.json in the root of jet-on-node provide instructions to PSM and Application Container Cloud regarding the run time settings for this application – including the runtime version of Node, the command for starting the application, the runtime memory per instance and the number of instances as well as the values of environment variables to be passed to the application.

    image

    The shell-script build-app.sh (you may have to explicitly make this script executable, using “chmod u+x build-app.sh”) performs the steps described above (although perhaps not in the optimal way – feel free to fine tune and improve and let me know about it).

    #git clone https://github.com/lucasjellema/webshop-portal-soaring-through-the-cloud-native-sequel
    # cd webshop-portal-soaring-through-the-cloud-native-sequel
    
    git pull
    wait
    
    npm install
    wait
    ojet build --release
    wait
    cp -a ./web/. ./jet-on-node/public
    wait
    cd jet-on-node
    wait
    npm install
    wait
    zip -r webshop.zip .
    wait
    cd /oracle-cloud-psm-cli/webshop-portal-soaring-through-the-cloud-native-sequel/jet-on-node
    
    psm accs push -n SoaringWebshopPortal -r node -s hourly -d deployment.json -p webshop.zip
    

    The end-to-end flow through the build container during the release of the latest version of the JET application can now be depicted like this:

    image

     

    4. Putting it all together

    I will now try to demonstrate how this all works together. In order to do so, I will go through these steps – and illustrate them with screenshots:

    • make a change in the JET application
    • commit and push the change (to GitHub)
    • run the Docker build container psm-cli
    • run the script build-app.sh
    • wait for about three minutes (check the output in the build container and the application status in the ACC console)
    • access the updated Web Application

    The starting point for the application:

    SNAGHTML8fbfc34

    1. Make a change

    The word Shopping Basket – next to the icon – seems superfluous, I will remove that. And I will increase the version number, from v1.2.0 to v1.2.1.

    image

     

      2. commit and push the change (to GitHub)

      image

      The change is accepted in GitHub:

      image

       

      3. Run the Docker build container psm-cli

      Run the Docker Quickstart Terminal (I am on Windows) and perform: “docker run –rm -it psm-cli”

      image

       

      At this point, I lack a little bit of automation. The manual step I need to take (just the first time round) is to clone the JET application’s Git repository:

      git clone https://github.com/lucasjellema/webshop-portal-soaring-through-the-cloud-native-sequelCloning

      and to move to the created directory

      cd webshop-portal-soaring-through-the-cloud-native-sequel/

      and to make the file build-app.sh executable:

      chmod u+x build-app.sh

      image

      Note: As long the container keeps running, I only have to run “git pull” and “./build-app.sh” for every next update to the JET application. The next step would be to configure a web hook that is triggered by the relevant commit in the GitHub repository.

       

      4. run the script build-app.sh

      ./build-app.sh

      image

      wait for about three minutes (check the output in the build container

      image

      SNAGHTML91173de

      and the application status in the ACC console)

      SNAGHTML90fc3cd

      SNAGHTML90fe186

       

      5. access the updated Web Application

      image

      As you can see, after committing and pushing the change, I only had to run a simple command line command to get the application fully rebuilt and redeployed. After stopping the Docker container, no traces remain of the build process. And I can easily share the container image with my team members to build the same application or update to also build other or additional JET applications.

       

      Resources

      The inspirational article by Abhishek Gupta: https://medium.com/oracledevs/quick-start-docker-ized-paas-service-manager-cli-f54eaf4ebcc7

      The sources – including a sample JET Application – are in this GitHub repo: https://github.com/lucasjellema/webshop-portal-soaring-through-the-cloud-native-sequel .

      Oracle JET Command Line Interface: https://github.com/oracle/ojet-cli

      Docs on the Oracle PSM (PaaS Service Manager) CLI: https://docs.oracle.com/en/cloud/paas/java-cloud/pscli/abouit-paas-service-manager-command-line-interface.html

      Node & Express Tutorial Part 2: Creating a skeleton website: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website

      Serving Public Files with Express – https://expressjs.com/en/starter/static-files.html

      Documentation for Oracle Application Container Cloud: https://docs.oracle.com/en/cloud/paas/app-container-cloud/dvcjv/getting-started-oracle-application-container-cloud-service.html

       

        About Author

        Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director and Oracle Developer Champion. Solution architect and developer on diverse areas including SQL, JavaScript, Docker, Machine Learning, Java, SOA and microservices, events in various shapes and forms and many other things. Author of the Oracle Press books: Oracle SOA Suite 11g Handbook and Oracle SOA Suite 12c Handbook. Frequent presenter on community events and conferences such as JavaOne, Oracle Code and Oracle OpenWorld.

        Leave a Reply

        This site uses Akismet to reduce spam. Learn how your comment data is processed.