Support Online
Skip to main content

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)

  1. 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)
  1. 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

  1. 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

  1. 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

  1. 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

  1. API is tested with Load Balancer IP address.

curl -X PUT -d "firstName=Ahmet&lastName=Yilmaz" http://<CLUSTER_IP>/person

Frequently Asked Questions (FAQ)

  1. Is there a fee to use Semaphore? Semaphore has a free tier for open source projects and small teams.

  2. Why should I use Kubernetes? Kubernetes is ideal for increasing the scalability of your application, reducing downtime, and managing resources efficiently.

  3. 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.

  4. 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.