The use of Appsody's tools and provided Stacks simplifies the steps and knowledge required to build "cloud packaged", "cloud native" and "cloud functions" based applications.
Appsody's nodejs Stack makes it easy to take any existing Node.js application and make it "cloud packaged".
The following tutorial, shows you how to take a sample Node.js application, and package it into a best-practise container image using the Appsody CLI and the nodejs Stack.
To follow this tutorial, you need to install both the Appsody CLI and Docker. You also need a Node.js application to cloud package.
Install the Appsody CLI by following the Installing Appsody guide for your platform.
You need a Node.js app to package. You can either use one you already have, or create a sample Express.js app by using the following steps:
npm install -g express-generator
mkdir nodejs-app cd nodejs-app
express
npm install npm start
This displays the following output:
Your sample application is now ready to use!
nodejs Appsody StackNew Appsody based applications are created by using appsody init <stack> <template>, where the stack is chosen by you from the stacks that are listed when you run appsody list. The init command downloads the most recent copy of the Appsody Stack, and populates the project directory with a template that provides a basic project structure.
In order to enable an existing application with a stack, the same steps can be followed, but passing the option none to appsody init, as a project structure is not required.
appsody init nodejs none
This provides output similar to the following:
Checking stack requirements... Docker requirements met Appsody requirements met Running appsody init... Downloading nodejs template project from https://github.com/appsody/stacks/releases/download/nodejs-v0.3.5/incubator.nodejs.v0.3.5.templates.simple.tar.gz Download complete. Do not unzip the template project. Only extracting .appsody-config.yaml file from /Users/kyemaloy/nodejs-app-2/nodejs.tar.gz Setting up the development environment Your Appsody project name has been set to nodejs-app-2 Pulling docker image docker.io/appsody/nodejs:0.3 Running command: docker pull docker.io/appsody/nodejs:0.3 0.3: Pulling from appsody/nodejs Digest: sha256:c0bc3aef3a2d5a631acfebc8fc33e2dfc230c37a0783d5ae2b6d61f93af1fe39 Status: Image is up to date for appsody/nodejs:0.3 docker.io/appsody/nodejs:0.3 Running command: docker run --rm --entrypoint /bin/bash docker.io/appsody/nodejs:0.3 -c "find /project -type f -name .appsody-init.sh" Successfully initialized Appsody project with the nodejs stack and no template.
As well as downloading the latest version of the Appsody Stack (in this case version 0.3), this has added a .appsody-config.yaml file to the project, which configures the versions of the Stack that the project will use.
At this point, your application has been enabled, which means you can now run, test and debug your application in a continuous containerized environment provided by the Appsody Stack, to build it into a optimized container image, and to deploy it to Kubernetes.
Now that your application has been enabled, you can use the run, test and debug commands to the Appsody CLI to work with your application in a continuous containerized environment provided by the Appsody Stack.
Appsody's run command provides a continuous development environment, where changes that are saved to your project cause your application to be restarted. This is similar to the experience you get from tools like nodemon, but this occurs inside the containerized environment. The only requirement is that your application's package.json file needs to include a start script.
The following steps show you how that works for your application:
Alternatively you can open a second terminal window so that you can edit the application files.
appsody run
As appsody run provides a continuous development environment, any code changes you make will cause an automatic restart of the application.
a) Open views > index.jade
b) Change the file contents to:
extends layout block content h1= title p Welcome to #{title}!!!!!!!
c) Save the file
Express: Welcome to Express!!!!!!!
You may need to refresh the page to see the change.
This provides a continuous, containerized development environment where you can almost instantly see any changes you make reflected in the application running in the browser.
As part of the development environment, both appsody run and appsody debug also provide a performance monitoring and profiling dashboard that uses the appmetrics-dash module. This feature is available during development only and it enables you to do analysis of your application.
This provides a display similar to the following, which provides data for:
HTTP request responsiveness and HTTP throughput
Performance of outbound HTTP requests and other requests
Event Loop latency showing the delay before tasks are scheduled.
CPU and Memory usage for the process and the system
Performance Monitoring and Analysis Dashboard
Click on the Profiling tab to open the profiling view
Click on the "Hamburger Menu" (the three horizontal lines in the top left-hand corner" and select Enable Profiling
This starts to collect function profiling data from the application, and to visualize it using a flame graph, which provides a visualization of the stacks and functions that are using most CPU time.
Flame Graph generated from Performance Profiling Data
As well as providing performance monitoring and profiling, you can also trigger Heap Dumps and Node Reports through the menu, and see an overview of your application in the Summary tab.
run environment by either:Using Ctrl-C in the terminal window where appsody run is executing
Running appsody stop from the project directory
Appsody's debug command provides the same continuous development environment that you get with the run environment. In addition, the application runs in debug and provides the correct configuration to enable remote access from a local debugger.
The following steps show you how that works for your application:
Alternatively you can open a second terminal window so that you can edit the application files.
apposody debug
Express: Welcome to Express!!!!!!!
As appsody debug provides a continuous development environment, any code changes you make will cause an automatic restart of the application. It also sets up the container to allow remote access from a debugger.
You can either use your favourite debugger for this, or use the one integrated into the Chrome Browser as follows:
Open the Chrome Browser
Enter chrome://inspect in the URL bar
chrome://inspect in the search bar
It displays a view similar to the following in the window:
Available targets to inspect with the Chrome Debugger
It opens a new panel containing the debugger, attached to your application running inside the continuous containerized debug environment.
You can now debug as you would normally. Additionally, any code change that you make to the project through changes to its source code with automatically stop and restart your application (after which you will need to restart the debugger connection).
Using Ctrl-C in the terminal window where appsody debug is executing
Running appsody stop from the project directory
Appsody's test command makes it possible to run any tests that your application has inside the containerized environment. To use this capability, your application needs to be provided with some tests, and your package.json file needs to include a test script that runs them.
The steps below show how that works for your application. As the sample application created from the Express Application Generate does not include any tests, you first need to add one.
package.json file with the following code. This will add devDependencies for chai, mocha and request, and add a test script: { "name": "test", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www", "test": "mocha" }, "dependencies": { "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", "http-errors": "~1.6.3", "jade": "~1.11.0", "morgan": "~1.9.1" }, "devDependencies": { "chai": "4.2.0", "mocha": "~6.1.0", "request": "2.88.0" } }
mkdir test cd test
const expect = require('chai').expect; const request = require('request'); const app = require('../app'); var http = require('http'); const PORT = process.env.PORT || '3000'; const server = app.listen(PORT); const url = "http://localhost:" + PORT; describe("Node.js Stack", function () { // Testing / responds with 200 OK describe('/ endpoint OK', function () { it('status', function (done) { request(url + '/', function (error, response, body) { expect(response.statusCode).to.equal(200); done(); }); }); }); }); // Stop the server after testing after(done => { server.close(done); });
npm install npm test
This responds with output similar to the following:
Node.js Stack / endpoint OK GET / 200 309.872 ms — 170 ✓ status (341ms) 1 passing (354ms)
appsody test
It responds with output that includes the following:
[Container] > [email protected] test /project/user-app [Container] > mocha [Container] [Container] [Container] [Container] Node.js Stack [Container] / endpoint OK [Container] GET / 200 320.079 ms — 170 [Container] null [Container] ✓ status (359ms) [Container] [Container] [Container] 1 passing (375ms)
Note that, unlike run and debug, test executes a single run of the tests rather than a continuous container.
When you are ready to build a deployable container image for your application, you can do that using appsody build. The build command creates a production-optimized image that is built using the regular “node” Official Docker image from the Node.js community and is then rebased on the communities “node-slim” image. The “node-slim” image is significantly smaller than the regular “node” image, making it easier and faster to deploy to a cloud. It is also more secure because it does not include packages such as compilers (which are required in the regular “node” image so that you can use packages with native add-ons).
The steps below show how to build the container image for your application, and then run it locally using Docker:
appsody build
This builds a container image for your application, using the name of your project as the "tag" (which is the name of the folder in which it resides). One of the final lines of the output gives the name of the image, usually just above the running command outputs:
Built docker image dev.local/nodejs-app
docker run --rm -it -p 3000:3000 dev.local/nodejs-app
This runs your container image, using the -p option to map port 3000 from the container to port 3000 on your machine, the -i option to run interactively (so you can use Ctrl-C to stop the container), the --rm option to remove the container when it's stopped, and the -t option to run the container image with our "tag" of nodejs-app.
Note that the Performance Dashboard is not present, as it was injected as a development-only feature.
docker run is executing.You now have a cloud package available in a container image that is ready to be deployed to any cloud that supports container images.
You can also use the appsody deploy command to deploy the image to any Kubernetes cluster.
This article covered how to take an existing Node.js application and make it "Cloud Packaged" using the nodejs Appsody Stack. Appsody also provides a nodejs-express stack with a preconfigured Express.js server. The nodejs-express stack includes cloud-native capabilities, such as liveness and readiness checks, metrics, and observability.
For more information on Appsody, join us on Slack, follow us on Twitter and star us on GitHub.