WD401-level7
1)Steps were taken to containerize the node-js application:
->step-1: Create a Docker file
I created a file named Docker file in your project directory and wrote the following instructions:

In above Docker file:
->The base image is defined as an official Node.js runtime based on the Alpine Linux distribution. The working directory inside the container is set to /app.
->The package.json file is copied from the local machine to the root directory in the container.The rest of the application code is copied to the /app directory in the container.
->Port 7000 is exposed for communication with the application.
->Two stages are defined: production and dev.
1) In the production stage, the environment is set to production, dependencies are installed, and the command to run the application (node index.js) is specified.
2)In the development stage, the environment is set to development, nodemon is installed globally, project dependencies are installed, and the command to run the application in development mode (npm run start) is specified
->step-2: Create docker-compose.yml
Created a file named docker-compose.yml in my project directory and wrote the following instructions.
version:'3.8'
services:
app:
build:
context: .
target: dev
image: online-voting:development
volumes:
- .:/app
ports:
- 7000:7000
env_file:
- .env
depends_on:
- db
db:
image: postgres:15
volumes:
- pg-dev-data:/var/lib/postgresql/data
env_file:
- .env
environment:
POSTGRES_USER: $DEV_USERNAME
POSTGRES_DB: $DEV_DATABASE
POSTGRES_PASSWORD: $DEV_PASSWORD
volumes:
pg-dev-data:
1)The Docker Compose configuration defines two services: app and db, where app represents the application container and db represents the PostgreSQL database container.
2)The app service builds an image using the Dockerfile in the current directory targeting the dev stage, mounts the current directory as a volume for live code updates, exposes port 7000 for communication, and loads environment variables from a .env file.
->step 3: Create docker-compose-prod.yml
I created a file named docker-compose-prod.yml in my project directory and wrote the following instructions:
version: '3.8'
services:
app:
build:
context: .
target: prod
image: online-voting:production
volumes:
- .:/app
ports:
-7000:7000
env_file:
- .env
depends_on:
- db
db:
image: postgres:15
volumes:
-pg-prod-data:/var/lib/postgresql/data
env_file:
- .env
environment:
POSTGRES_USER: $PROD_USERNAME
POSTGRES_DB: $PROD_DATABASE
POSTGRES_PASSWORD: $PROD_PASSWORD
volumes:
pg-prod-data:
1) The Docker Compose configuration sets up two services: app and db, where app is the container for the application and db is the PostgreSQL database container.
2) The app service builds an image targeting the prod stage from the Dockerfile, mounts the current directory as a volume for live code updates, exposes port 7000, and loads environment variables from a .env file. It also depends on the db service.
->step 4: Run Docker Compose
Now, I ran below Docker Compose command to build and run your containers:
For development:
docker-compose up -f docker-compose.yml
For production:
docker-compose up -f docker-compose-prod.yml
2)Environment Variable Configuration Documentation within the Docker container
PORT=7000
###### production ######
NODE_ENV=production
PROD_USERNAME=postgres
PROD_PASSWORD=mallik
PROD_DATABASE=vote_dev2
PROD_HOST=db
PROD_DIALECT=postgres
DATABASE_URL=postgres://postgres:mallik@db:5432/vote_dev2
###### development ######
DEV_USERNAME=postgres
DEV_PASSWORD=mallik
DEV_DATABASE=vote_dev2
DEV_HOST=db
DEV_DIALECT=postgres
Port Configuration
->PORT:
Specifying the port number 7000 on which the application will listen for incoming requests.
Production Environment Configuration:
-> Node Environment:
Specifying the environment mode in production.
->Database Connection:
Username: PROD_USERNAME
Password: PROD_PASSWORD
Database Name: PROD_DATABASE
Host: PROD_HOST
Dialect: PROD_DIALECT
URL: DATABASE_URL
Specifying the configuration parameters for connecting to the production database.
example as per our code:
Username: postgres
Password: ******
Database: vote_dev2
Host: db
Dialect: postgres
URL: postgres://postgres:mallik@db:5432/vote_dev2
Development Environment Configuration:
->Node Environment:
Specifying the environment mode in development.
->Database Connection:
Username: DEV_USERNAME
Password: DEV_PASSWORD
Database Name: DEV_DATABASE
Host: DEV_HOST
Dialect: DEV_DIALECT
Specifying the configuration parameters for connecting to the development database.
Example as per our code:
Copy code
Username: postgres
Password: *****
Database: vote_dev2
Host: db
Dialect: postgres
3) Docker Compose Configuration Documentation
Docker Compose is a tool for defining and running multi-container Docker applications. Below is the configuration from our application code for setting up services for both the Node.js application and the database using Docker Compose.
Application Service
Service Name: app
Image: Specifies the Docker image for the Node.js application.
Example: my-node-app
Environment Variables:
PORT: Specifies the port number on which the Node.js application will listen for incoming requests.
Example: 7000
NODE_ENV: Specifies the environment mode for the Node.js application.
Example: production or development
DATABASE_URL: Specifies the URL for connecting to the database.
Example: postgres://user:password@db_host:5432/database_name
Ports: Specifies the ports to expose for accessing the Node.js application externally.
Example: 7000:7000
Volumes: Specifies any volumes to mount for persisting data or sharing files between the host and the container.
Depends On: Specifies any dependencies that the service relies on, such as other services.
Example: db
Database Service
Service Name: db
Image: Specifies the Docker image for the database.
Example: postgres:latest
Environment Variables:
POSTGRES_USER: Specifies the username for accessing the database.
Example: postgres
POSTGRES_PASSWORD: Specifies the password for accessing the database.
Example: mallik
POSTGRES_DB: Specifies the name of the database.
Example: vote_dev2
Volumes: Specifies any volumes to mount for persisting data or sharing files between the host and the container.
Ports: Specifies the ports to expose for accessing the database externally.
Depends On: Specifies any dependencies that the service relies on, such as other services.
The below is the docker-compose.yml file from my application:
version: '3.8'
services:
app:
build:
context: .
target: dev
image: online-voting:development
volumes:
- .:/app
ports:
- 7000:7000
env_file:
- .env
depends_on:
- db
db:
image: postgres:15
volumes:
- pg-dev-data:/var/lib/postgresql/data
env_file:
- .env
environment:
POSTGRES_USER: $DEV_USERNAME
POSTGRES_DB: $DEV_DATABASE
POSTGRES_PASSWORD: $DEV_PASSWORD
volumes:
pg-dev-data:
4) CI/CD Pipeline Setup Documentation:
name: Auto tests For OnlineVotingApplication
on:
push:
branches:
- master
env:
PG_DATABASE: online_voting_DB
PG_USER: postgres
PG_PASSWORD: ${{ secrets.password_DB }}
jobs:
#Code validation
lint:
name: Validation code
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "14"
- name: Install Dependencies
run: npm ci
- name: Run ESLint
run: npx lint-staged
run-tests:
# Containers must run in Linux based operating
runs-on: ubuntu-latest
# Service containers to run with `container-job`
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres:11.7
# Provide the password for postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${{secrets.password_DB }}
POSTGRES_DB: online_voting_DB
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
# Downloads a copy of the code in your repository before running CI tests
- name: Check out repository code
uses: actions/checkout@v3
# Performs a clean installation of all dependencies in the `package.json` file
# For more information, see https://docs.npmjs.com/cli/ci.html
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
- name: Run the app
id: run-app
run: |
npm install
npx sequelize-cli db:drop
npx sequelize-cli db:create
npx sequelize-cli db:migrate
PORT=7000 npm start &
sleep 5
- name: Run integration tests
run: |
npm install cypress cypress-json-results
npx cypress run
build:
name: Dockerization
needs: [run-tests]
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Login to Docker desktop
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
- name: Docker Set up
uses: docker/setup-buildx-action@v3
- name: Building and updating to hub
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/megabyte:latest
deploy:
name: Automaic Deployment
needs: [build] #before deployment run
runs-on: ubuntu-latest
steps:
- name: Backend application deployment in Render
uses: johnbeynon/render-deploy-action@v0.0.8
with:
service-id: ${{ secrets.RENDER_SERVICEID}}
api-key: ${{ secrets.API_TOKEN }}
notifications-tests:
name: Test Notifications
needs: [run-tests] ## should run tests
runs-on: ubuntu-latest
if: ${{ always() }} ## runs or independent on previous jobs. i.e always show notifications
steps:
- name: Discord Notifications for Test Results
env:
DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
run: |
if [[ ${{ needs.run-tests.result }} == 'success' ]]; then
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Tes
t cases* completed successfully. \nCheck the logs for details: https://github.com/${{
github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
else
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Tes
t cases* failed. \nCheck the logs for details: https://github.com/${{ github.repository
}}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
fi
notifications-codevalidation:
name: Code validation Notifications
needs: [lint] ## should run
runs-on: ubuntu-latest
if: ${{ always() }} ## runs or independent on previous jobs. i.e always show notifications
steps:
- name: Discord Code validation for Results
env:
DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
run: |
if [[ ${{ needs.lint.result }} == 'success' ]]; then
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Co
de validation* completed successfully , No staged files found. \nCheck the logs for de
tails: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
' $DISCORD_WEBHOOKURL
else
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Cod
e validation* failed. \nCheck the logs for details: https://github.com/${{ github.repo
sitory }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
fi
notifications-dockerization:
name: Dockerization Notifications
needs: [build] ## should deployment
runs-on: ubuntu-latest
if: ${{ always() }} ## runs or independent on previous jobs. i.e notifications
steps:
- name: Dockerization Notifications for Dockerization Results
env:
DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
run: |
if [[ ${{ needs.build.result }} == 'success' ]]; then
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Doc
kerization of application* completed successfully. \nCheck the logs for details: https
://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_
WEBHOOKURL
else
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Doc
kerization of the application* failed. \nCheck the logs for details: https://github.
com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
fi
notifications-deploy:
name: Deployment Notifications
needs: [deploy] ## should deployment
runs-on: ubuntu-latest
if: ${{ always() }} ## runs or independent on previous jobs. i.e notifications
steps:
- name: Discord Notifications for Deployment Results
env:
DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
run: |
if [[ ${{ needs.deploy.result }} == 'success' ]]; then
curl -X POST -H 'Content-type: application/json' --data '{"content":" *De
ployment of application* completed successfully. \nCheck the logs for details: https:
//github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_
WEBHOOKURL
else
curl -X POST -H 'Content-type: application/json' --data '{"content":" *Dep
loyment of the application* failed. \nCheck the logs for details: https://github.com/$
{{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
fi
push-notification:
name: Push Notification
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- name: Discord Notifications for Push Event
env:
DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
run: |
curl -X POST -H 'Content-type: application/json' --data '{"content":"A new
push event has been triggered on the master branch of the repository. Check it out at:
https://github.com/${{ github.repository }}/commit/${{ github.sha }}"}' $DISCORD_WEBH
OOKURL
This CI/CD pipeline is set up to automate the testing, building, and deployment process for my application called OnlineVotingApplication. It uses GitHub Actions for automation and integrates various stages to ensure code quality, test coverage, Dockerization, and deployment.
Pipeline Structure
The pipeline consists of several stages, each responsible for a specific job:
1) Validation Code (using lint-staged): Validates the codebase using ESLint to ensure consistent code style and formatting.
2)Run Tests: Installs dependencies and runs unit tests using npm test. Runs the application, performs database setup (drop, create, migrate), and runs integration tests using Cypress.
3)Dockerization: Builds the Docker image for the application. And Pushes the image to Docker Hub.
4)Deployment: Deploys the application to a hosting platform. And Uses Render Deploy Action to deploy my application.
Environment Variables:
->Database Credentials:
PG_DATABASE: Online Voting Database name.
PG_USER: PostgreSQL username.
PG_PASSWORD: Encrypted secret for PostgreSQL password.
Error Handling Mechanisms:
->Discord webhooks are used to send notifications to the development team. Notifications are sent for each stage of the pipeline (code validation, tests, Dockerization, deployment).
->Different messages are sent based on the success or failure of each stage.
Overview on workflow:
This pipeline basically triggers on every push event to the master branch. Upon triggering, it starts with code validation, followed by tests, Dockerization, and deployment. Notifications are sent at each stage to inform the status about the pipeline's progress and outcome.
Comments
Post a Comment