Guide to Deploying Node.js Application to Kubernetes Using Semaphore CI/CD
What will you learn in this guide?
In this comprehensive guide, you'll learn how to build a Node.js application from scratch, package it with Docker, and automatically deploy it to a Kubernetes cluster using Semaphore CI/CD. You'll discover how to manage automated testing and deployment processes every time you update your code, eliminating manual deployment processes.
Preliminary Preparations
Before you start, make sure you have the following:
- GitHub account and a computer with Git installed
- Docker Hub account
- Semaphore CI/CD account (can be logged in via GitHub)
- Kubernetes Cluster and PostgreSQL Database running on GenixNode or similar provider
- Have Node.js and kubectl installed on your local machine
Step 1: Preparing Infrastructure Services
Let's start by creating the basic building blocks on which our application will run.
Database: Create a PostgreSQL cluster from your cloud panel. Define database addressbook_db and user addressbook_user.
Kubernetes Cluster: Create the Kubernetes cluster where the application will run. At least 3 nodes are recommended.
Note: Keep database connection information in a safe place.
Step 2: Writing the Node.js Application
We will develop a simple address book API.
mkdir addressbook && cd addressbook
npm init -y
npm install --save sequelize pg express body-parser
- These commands create the project structure and install the necessary libraries.
Database Connection (database.js)
- The application connects to the database via environment variables.
const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DB_SCHEMA || 'postgres',
process.env.DB_USER || 'postgres',
process.env.DB_PASSWORD || '',
{
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
dialect: 'postgres',
dialectOptions: {
ssl: process.env.DB_SSL == "true"
}
});
const Person = sequelize.define('Person', {
firstName: { type: Sequelize.STRING, allowNull: false },
lastName: { type: Sequelize.STRING, allowNull: true }
});
module.exports = { sequelize: sequelize, Person: Person };
- Application Server (app.js)
- REST API routes are defined.
var express = require('express');
var bodyParser = require('body-parser');
var db = require('./database');
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/person/:id", function(req, res) {
db.Person.findByPk(req.params.id)
.then(person => res.status(200).send(JSON.stringify(person)))
.catch(err => res.status(500).send(JSON.stringify(err)));
});
app.put("/person", function(req, res) {
db.Person.create({
firstName: req.body.firstName,
lastName: req.body.lastName,
id: req.body.id
}).then(person => res.status(200).send(JSON.stringify(person)))
.catch(err => res.status(500).send(JSON.stringify(err)));
});
app.get("/all", function(req, res) {
db.Person.findAll().then(persons => res.status(200).send(JSON.stringify(persons)));
});
var server = app.listen(process.env.PORT || 3000, function() {
console.log("Uygulama port 3000 üzerinde çalışıyor");
});
Step 3: Writing and Running Tests
- Testing is mandatory for CI/CD processes.
npm install --save-dev jest
npm run test
- This step verifies that database operations are working correctly.
Step 4: Creating Semaphore CI Pipeline
- Create a Semaphore project and link the GitHub repo.
version: v1.0
name: Addressbook
agent:
machine:
type: e1-standard-2
os_image: ubuntu1804
blocks:
- name: Install dependencies
task:
jobs:
- name: npm install
commands:
- checkout
- nvm use
- npm install
- name: Tests
task:
jobs:
- name: Unit test
commands:
- checkout
- nvm use
- npm install
- sem-service start postgres
- npm run test
Step 5: Creating a Docker Image
- The application is converted to a Docker image.
FROM node:10.16.0-alpine
COPY package*.json ./
RUN npm install
COPY *.js ./
EXPOSE 3000
CMD [ "npm", "run", "start" ]
Step 6: Deploy to Kubernetes
- The application is run with Kubernetes Deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: addressbook
spec:
replicas: 3
selector:
matchLabels:
app: addressbook
template:
metadata:
labels:
app: addressbook
spec:
containers:
- name: addressbook
image: ${DOCKER_USERNAME}/addressbook:${SEMAPHORE_WORKFLOW_ID}
Step 7: Testing the Application
- API is tested with Load Balancer IP address.
curl -X PUT -d "firstName=Ahmet&lastName=Yilmaz" http://<CLUSTER_IP>/person
Frequently Asked Questions (FAQ)
-
Is there a fee to use Semaphore? Semaphore has a free tier for open source projects and small teams.
-
Why should I use Kubernetes? Kubernetes is ideal for increasing the scalability of your application, reducing downtime, and managing resources efficiently.
-
When does the "LoadBalancer" IP address occur? After applying the deployment.yml file, it may take a few minutes for your cloud provider (Ex: GenixNode) to assign an IP.
-
Is it safe to store database passwords in deployment.yml? No. An environment variable is used in this guide for simplicity, but it is recommended that you use Kubernetes Secrets in a production environment.
Result
With this guide, your Node.js application was automatically deployed to Kubernetes using Semaphore CI/CD. Code updates now automatically trigger test, build and deployment processes.
You can use the GenixNode infrastructure for managed Kubernetes experience.

