In this article we are going to cover Deploy nodejs Helm chart on EKS using GitHub Actions.
Table of Contents
Step #1:Create Amazon EKS cluster using eksctl:
Please follow below article to Create Amazon EKS cluster using eksctl.
How to Create Amazon EKS cluster using eksctl
Step #2:Create node.js Hello world application
In this step we need to create node.js code Hello world code using below code
Create package.json file
package.json
{
"name": "docker_web_app",
"version": "1.0.0",
"description": "Node.js on Docker",
"author": "First Last <[email protected]>",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
Create server.js file
'use strict';
const express = require('express');
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(PORT, HOST, () => {
console.log(`Running on http://${HOST}:${PORT}`);
});
Create nodejs Dockerfile
A Dockerfile for a Node.js application allows you to create a container image that includes all the necessary dependencies and configurations to run your Node.js application. Below is an example of a simple Dockerfile for a Node.js application:
# Use the official Node.js image as the base image
FROM node:18
# Set the working directory inside the container
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
# Copy package.json and package-lock.json to the working directory
COPY package*.json ./
# Install Node.js dependencies
RUN npm install
# If you are building your code for production
# RUN npm ci --omit=dev
# Bundle app source
COPY . .
# Expose the port on which your Node.js application will run
EXPOSE 8080
# Command to run your Node.js application
CMD [ "node", "server.js" ]
Create .dockerignore file
node_modules
npm-debug.log
Here’s a breakdown of each step:
FROM node:18
: This sets the base image for the container. In this case, we are using the official Node.js image with version 14. You can choose a different version based on your application’s requirements.WORKDIR /usr/src/app
: This sets the working directory inside the container to/usr/src/app
. It is a good practice to use a consistent location for your application files.COPY package*.json ./
: This copies thepackage.json
andpackage-lock.json
files from the host (where your Dockerfile is located) to the working directory inside the container.RUN npm install
: This installs the Node.js dependencies based on thepackage.json
file. It is executed during the image build process, ensuring the application’s dependencies are installed within the container.COPY . .
: This copies all the remaining application files from the host to the working directory inside the container.EXPOSE 3000
: This exposes port 3000 on the container. If your Node.js application runs on a different port, modify this value accordingly.CMD ["npm", "start"]
: This specifies the command that will be executed when the container starts. In this case, it runsnpm start
, which should be defined in yourpackage.json
as the start script for your Node.js application.
Step #3:Build and run Node.js docker image
Building your image:
Go to the directory that has your Dockerfile and run the following command to build the Docker image. The -t flag lets you tag your image so it’s easier to find later using the docker images command
Note: Before run this command you need to install docker and give some permission
sudo apt install docker.io
sudo usermod -aG docker $USER
sudo chmod 666 /var/run/docker.sock
To build the Docker image, navigate to the directory containing your Dockerfile and run the following command:
docker build . -t <your username>/node-app
Your image will now be listed by Docker:
docker images
Output:
ubuntu@ip-172-31-5-137:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
devopshint/node-web-app latest d832f1fd89ca 2 hours ago 1.1GB
node 18 d9ad63743e72 2 days ago 1.09GB
Run the image:
Running your image with -d runs the container in detached mode, leaving the container running in the background. The -p flag redirects a public port to a private port inside the container.
docker run -p 49160:8080 -d <your username>/node-app
To test your app, get the port of your app that Docker mapped:
docker ps
output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d17d04e0f42 devopshint/node-web-app "docker-entrypoint.s…" 2 hours ago Up 2 hours 0.0.0.0:49160->8080/tcp, :::49160->8080/tcp lucid_austin
Step #4:Run Docker image on browser
Now let access in browser so copy your instance ip and port number
Step #5:Push Node.js code and Docker on GitHub Repository:
Firstly let’s clone your repo using below command:
git clone <your-repo-HTTPS>
Copy your node.js and docker files and paste in your repo folder
Then push this code into your repo using below commands:
git add .
git commit -m "files added"
git push
Step #6:Add Secrets in GitHub Repository
In our repository we need to add two secrets here
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
Step #7:Create github Action workflow to build and push docker image to ECR
Let’s create workflow using below code:
name: Node js app deploy to EKS
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install kubectl
uses: azure/[email protected]
with:
version: 'v1.27.0' # default is latest stable
id: install
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-south-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push docker image to Amazon ECR
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: devops-img
IMAGE_TAG: latest
run: |
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
- name: Update kube config
run: aws eks update-kubeconfig --name sample
Step #8:Run GitHub Action workflow
Step #9:Install Helm on EKS cluster
Download the helm installation script using below command:
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
Add execute permissions to the downloaded script:
chmod +x get_helm.sh
Execute the installation script:
./get_helm.sh
Validate helm installation by executing the helm command:
helm
Check helm version:
helm version
Step #10:Create node-app helm chart
We will create a Helm chart node-app-chart
for the Node.js application. To create the Helm chart, run this Helm command:
helm create node-app-chart
- Charts folder
This folder contains any other dependencies for the node-app-chart
.
- Template folder
This folder contains all Kubernetes YAML or manifest files for creating Kubernetes objects such as Kubernetes Deployment, Kubernetes Service, Configmaps, and Secrets. In this guide, we will only require the deployment.yaml
and service.yaml
. We will modify these two files and add our configurations for deploying the Node.js application.
- Chart.yaml
This YAML file contains descriptive information about the Helm chart such as the chart name and version.
- Values.yaml
This YAML file contains the application configuration values that the other YAML files in the template folder will use. These values include the image name, container name, port mapping, number of pods, and the type of Kubernetes service.
Step #11:modify helm chart files
Lets modify values.yaml, deployment.yaml and service.yaml files using below code
values.yaml
# Default values for node-app.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: 908198849120.dkr.ecr.ap-south-1.amazonaws.com/devops-img
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: LoadBalancer
port: 80
targetPort: 8080
protocol: TCP
name: node-app
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
As you can see in image repository section we need to paste ECR URI
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "node-app.fullname" . }}
labels:
{{- include "node-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "node-app.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "node-app.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "node-app.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "node-app.fullname" . }}
labels:
{{- include "node-app.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: {{ .Values.service.protocol }}
name: {{ .Values.service.name }}
selector:
{{- include "node-app.selectorLabels" . | nindent 4 }}
Step #12:Push node-app folder to GitHub repo
After modifying files then we need to push this node-app folder to GitHub repository
firstly clone your repo then paste this node-app folder to your github repo folder then push this code on your repo using below commands
git add .
git commit -m "node-app added"
git push
Deploy nodejs Helm chart on EKS using GitHub Actions
Step #13:Update GitHub Action workflow
Now lets update our github workflow so here lets add new stage in GHA workflow
name: Node js app deploy to EKS
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install kubectl
uses: azure/[email protected]
with:
version: 'v1.27.0' # default is latest stable
id: install
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-south-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push docker image to Amazon ECR
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: devops-img
IMAGE_TAG: latest
run: |
docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
- name: Update kube config
run: aws eks update-kubeconfig --name sample
- name: Deploy nodejs helm chart to EKS
run: |
helm install nodeapp ./node-app
You can use Github repo Deploy nodejs Helm chart on EKS using GitHub Actions
Step #14:Check pods, deployment and service on EKS
After successfully run our workflow lets check pods, deployment and service using below command:
kubectl get pods
kubectl get deploment
kubectl get service
Output:
Now lets access our image in browser using Extername ip
Conclusion:
We have covered Deploy nodejs Helm chart on EKS using GitHub Actions.