Learn to use the Pack CLI for creating a container image from a Node.js web application source code.
In this lesson, you will learn how to use the Pack CLI to create a container image from your source code. We will use a simple Node.js web application as an example to demonstrate the process.
The main application file, index.js, starts a web server using Express. Below is the complete code:
Copy
Ask AI
console.log(process.env.NODE_PATH);const express = require('express');const app = express();const PORT = process.env.PORT || 8080;// Define a route for the root URLapp.get('/', (req, res) => { res.send('Hello, World!');});// Start the serverapp.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`);});
In addition to the application code, the project includes a package.json file that specifies the third-party dependencies required by the application (such as Express and UUID). When installed, these dependencies appear in the node_modules folder alongside your application code.
Before building the image, you need a builder. If your organization provides one, you can reference that; otherwise, you can use the Pack CLI command to get suggestions for pre-built default builders:
Copy
Ask AI
pack builder suggest
This command displays several builder options. For example, the output might look like:
Copy
Ask AI
pack builder suggestSuggested builders: gcr.io/buildpacks/builder:google-22 heroku/builder:24 paketo-buildpacks/builder-jammy-base paketo-buildpacks/builder-jammy-buildpackless-static paketo-buildpacks/builder-jammy-full paketo-buildpacks/builder-jammy-tinyTip: Learn more about a specific builder with: pack builder inspect <builder-image>
Each builder is distributed as a Docker image. In this demonstration, we will use the Google builder (gcr.io/buildpacks/builder:google-22) as it is the first option on the list.
Use the Pack CLI to create your container image. Here, we name the image “myapp”, specify the path to your Node.js application source code, and choose the builder:
After downloading the builder image, the build process advances through several stages:
Detection:
Each buildpack inspects the source code to decide whether it should execute. For the Node.js application, buildpacks check for files like package.json or package-lock.json. In this example, three out of five buildpacks detected that they needed to run.
Restoration:
Previous build metadata is restored. Although this step is useful for caching and speeding up subsequent builds, it is not the primary focus here.
Building:
During this stage, the builder installs Node.js (if it isn’t cached already) and executes npm install to populate the node_modules folder with your application’s dependencies. Here is an excerpt from the build log:
Timer: Detector ran for 175.639051ms and ended at 2024-11-04T02:34:08Z==> RESTORINGTimer: Restorer started at 2024-11-04T02:34:08ZRestoring metadata for "google.nodejs.npm:npm_modules" from cacheRestoring data for "google.nodejs.npm:npm_modules" from cacheTimer: Restorer ran for 72.040463ms and ended at 2024-11-04T02:34:08Z==> BUILDINGTimer: Builder started at 2024-11-04T02:34:08Z=== Node.js - Runtime ([email protected]) ===2024/11/04 02:34:08 [DEBUG] GET https://dl.google.com/runtimes/ubuntu2204/nodejs/version.json***** CACHE MISS: "nodejs"Installing Node.js v20.18.02024/11/04 02:34:09 [DEBUG] GET https://dl.google.com/runtimes/ubuntu2204/nodejs/nodejs-20.18.0.tar.gz***** CACHE HIT: "npm_modules"Running "npm install --quiet (NODE_ENV=production)"up to date, audited 67 packages in 81ms
Exporting:
The final stage involves assembling the image layers. The default process type is set to “web,” which will serve as the container’s startup command.
Copy
Ask AI
Adding label 'io.buildpacks.lifecycle.metadata'Adding label 'io.buildpacks.build.metadata'Adding label 'io.buildpacks.project.metadata'Setting default process type 'web'Timer: Saving myapp... started at 2024-11-04T02:34:17Z*** Images (cal0ad42c689): myappTimer: Saving myapp... ran for 4.676490745s and ended at 2024-11-04T02:34:21ZTimer: Exporter ran for 9.32536734s and ended at 2024-11-04T02:34:21ZTimer: Cache started at 2024-11-04T02:34:21ZReusing cache layer 'google.nodejs:npm:npm_modules'Timer: Cache ran for 141.314376ms and ended at 2024-11-04T02:34:21ZSuccessfully built image myapp
You can verify that the image was built successfully by running:
After the image is built, you can test it by creating and running a container. Since the application listens on port 8080, map it to port 8000 on your host machine:
Copy
Ask AI
docker run -d -p 8000:8080 myapp
To verify that the container is running, check the container list with:
Copy
Ask AI
docker ps
Finally, use an HTTP request to interact with the application:
Copy
Ask AI
curl localhost:8000
The expected output is:
Copy
Ask AI
Hello, World!
This confirms that the application image has been built correctly and the container is operational.
This command displays options for working with builders, buildpacks, stacks, extensions, and more. One particularly useful command is pack inspect, which provides detailed information about the image built by the buildpacks. For example:
Copy
Ask AI
pack inspect myapp
The output will include details such as the base image, applied buildpacks, and defined processes:
Copy
Ask AI
Inspecting image: myappREMOTE: (not present)LOCAL: Stack: google.22 Base Image: Reference: dd99c47037c93572a1ada64bdd4987ff7fa9408f859518f15fa435cadf3fc5b Top Layer: sha256:4064dc542d5199b1bd264ea33ddae9aed13f94c087c99851ccbc21e968f4084 Run Images: (none) Rebasable: true Buildpacks: ID VERSION HOMEPAGE google.nodejs.runtime 1.0.0 - google.nodejs.npm 1.1.0 - google.utils.label-image 0.0.2 - Processes: TYPE SHELL COMMAND ARGS WORK DIR web (default) node index.js /workspace
This output indicates that three buildpacks were applied: Node.js Runtime, Node.js NPM, and Google’s Util Label-Image. The defined process, “web,” sets the command node index.js within the /workspace directory.
Although some code blocks (such as the Node.js application code) appear multiple times in the process, they are presented only once in full to avoid redundancy.
If everything is set up correctly, you will see output similar to the following as the image is published:
Copy
Ask AI
***** CACHE MISS: "npm_modules"Installing application dependencies.---------------------------------------------------------------------Running "npm ci --quiet --no-fund --no-audit (NODE_ENV=production)"added 65 packages in 2sDone "npm ci --quiet --no-fund --no-audit (NODE_ENV=production)" (2.506255138s)=== Utils - Label Image ([email protected]) ===Timer: Builder ran for 6.336... and ended at 2024-11-04T08:17:36Z=== EXPORTINGno run metadata found at path '/cn/run.toml'Timer: Exporter started at 2024-11-04T08:17:37ZAdding layer 'google.nodejs.runtime:node'Adding layer 'buildpack:io.buildpacks.lifecycle:launch.sbom'Adding 1/1 app layer(s)Adding layer 'buildpack:io.buildpacks.lifecycle:launcher'Adding layer 'buildpack:io.buildpacks.lifecycle:config'Adding layer 'buildpack:io.buildpacks.lifecycle:process-types'Adding label 'io.buildpacks.metadata'Adding label 'io.buildpacks.project.metadata'Setting default process type='web'Timer: Saving sanjeevkt720/myapp... ran for 4.868953308s and ended at 2024-11-04T08:17:46Z**** Images (sha256:02579c9bc654798be821ec5d296ae89cfa76bcd345125849a5fd7b40152):sanjeevkt720/myappTimer: Cache ran for 195.424539msSuccessfully built image sanjeevkt720/myapp
Visit your Docker Hub repository to verify that the image has been successfully pushed.
This lesson has demonstrated how to use the Pack CLI to create, inspect, run, and publish a cloud-native application image built from source code. With these techniques, you can experiment with buildpacks to containerize your applications efficiently.For further reading, check out the following resources: