AB
The second part of our Node.js CI/CD pipeline series, exploring how to implement a continuous integration and deployment workflow using CircleCI.
In this tutorial, we will learn how to set up a Continuous Integration and Continuous Deployment (CI/CD) pipeline for a Node.js application using CircleCI. CircleCI offers a cloud-based CI/CD platform that automates the build, test, and deployment processes. This approach provides an alternative to Jenkins (which we covered in Part 1), with its own set of advantages, including easier setup and maintenance.
The sample code for this project is available at Clone Website GitHub Repository.
Before starting this project, make sure you have the following prerequisites:
Sign Up for CircleCI:
Add Your Project:
The heart of CircleCI is the configuration file that defines your pipeline. Create a file named .circleci/config.yml
in the root of your repository:
version: 2.1
jobs:
run_tests:
docker:
- image: cimg/node:18.16.0
steps:
- checkout
- run:
name: Install dependencies
command: npm install --save
- run:
name: Run tests
command: npm test
build_docker:
docker:
- image: cimg/node:18.16.0
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker Image
command: |
export TAG=0.2.<<pipeline.number>>
docker build -t amodh-2002/clone_website:$TAG .
docker tag amodh-2002/clone_website:$TAG amodh-2002/clone_website:latest
- run:
name: Push Docker Image
command: |
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
export TAG=0.2.<<pipeline.number>>
docker push amodh-2002/clone_website:$TAG
docker push amodh-2002/clone_website:latest
workflows:
version: 2
build_and_deploy:
jobs:
- run_tests
- build_docker:
requires:
- run_tests
Let’s break down this configuration:
run_tests:
build_docker:
The build_and_deploy
workflow defines the sequence of jobs:
To push images to Docker Hub, you need to securely store your Docker Hub credentials in CircleCI:
DOCKER_USERNAME
: Your Docker Hub usernameDOCKER_PASSWORD
: Your Docker Hub passwordAfter your image is built and pushed to Docker Hub, you’ll need to deploy it to your EC2 instance. You can do this manually or extend the CircleCI configuration to handle deployment.
SSH into your EC2 instance and run:
docker pull amodh-2002/clone_website:latest
docker stop nodejs-app || true
docker rm nodejs-app || true
docker run -d --name nodejs-app -p 80:80 amodh-2002/clone_website:latest
For a fully automated deployment, you can extend your CircleCI configuration to include a deployment job:
deploy_to_ec2:
docker:
- image: cimg/base:stable
steps:
- add_ssh_keys:
fingerprints:
- "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
- run:
name: Deploy to EC2
command: |
ssh -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST 'docker pull amodh-2002/clone_website:latest && \
docker stop nodejs-app || true && \
docker rm nodejs-app || true && \
docker run -d --name nodejs-app -p 80:80 amodh-2002/clone_website:latest'
To use this:
SSH_USER
: The username for SSH access (e.g., ubuntu
or ec2-user
)SSH_HOST
: Your EC2 instance’s public IP or DNSworkflows:
version: 2
build_and_deploy:
jobs:
- run_tests
- build_docker:
requires:
- run_tests
- deploy_to_ec2:
requires:
- build_docker
After setting up your configuration file and environment variables:
CircleCI offers several advantages compared to Jenkins:
Zero Maintenance: CircleCI is a cloud-based service, so you don’t need to maintain servers.
Faster Setup: Getting started with CircleCI is simpler and requires less configuration.
Built-in Docker Support: CircleCI has native support for Docker without requiring additional plugins.
Parallel Execution: Free accounts get multiple concurrent builds, which can speed up your pipeline.
Easy Configuration: YAML-based configuration that lives with your code.
Here are some best practices to optimize your CircleCI pipeline:
Add caching to speed up builds by reusing npm dependencies:
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- v1-dependencies-
- run: npm install
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
Use CircleCI orbs to simplify your config and reuse code:
version: 2.1
orbs:
node: circleci/[email protected]
docker: circleci/[email protected]
jobs:
build_and_test:
executor: node/default
steps:
- checkout
- node/install-packages
- run:
name: Run tests
command: npm test
Split your workflow into parallel jobs where possible to speed up execution:
workflows:
version: 2
build_and_deploy:
jobs:
- lint
- test_unit
- test_integration
- build_docker:
requires:
- lint
- test_unit
- test_integration
When implementing CI/CD pipelines with CircleCI, consider these security best practices:
Environment Variables: Always use environment variables for sensitive information.
Contexts: Use CircleCI contexts to share environment variables across projects.
Branch Protection: Set up branch protection rules in GitHub to require passing CI checks before merging.
Vulnerability Scanning: Add container scanning to your pipeline:
security_scan:
docker:
- image: aquasec/trivy
steps:
- run:
name: Scan Docker image
command: trivy image amodh-2002/clone_website:latest
After deploying your application, verify it’s running correctly:
docker logs nodejs-app
If you encounter authentication issues with Docker Hub:
If the automated deployment can’t connect to your EC2 instance:
SSH_USER
and SSH_HOST
environment variables are correctIf your tests are failing in CircleCI but pass locally:
In this tutorial, we’ve set up a comprehensive CI/CD pipeline for a Node.js application using CircleCI. We’ve covered:
CircleCI provides a powerful, easy-to-use platform for automating your development workflows. By implementing the practices covered in this tutorial, you can ensure your Node.js applications are built, tested, and deployed consistently and efficiently.
In the next part of this series, we’ll explore how to enhance our Jenkins pipeline from Part 1 with Trivy for container security scanning, taking our CI/CD process to the next level of security compliance.