Automating Docker Image Builds and Deployment with GitHub Actions and Watchtower
In this tutorial, we’ll walk through setting up a CI/CD pipeline for a simple Node.js application using GitHub Actions to build and push Docker images to a registry, and configuring Watchtower to automatically deploy these images.
This tutorial is designed for small personal pipelines and provides a straightforward way to automate Docker image builds and deployments using GitHub Actions and Watchtower.
1 — Run a Simple Node.js Application manually using docker
First, fork the repository from GitHub and create your own repository on which you will push the Node.js application.
- Navigate to the repository on GitHub.
- Click on the “Fork” button in the top-right corner to create your own copy of the repository.
- Clone your forked repository to your local machine:
git clone https://github.com/yourusername/tutorial-docker-actions-watchtower
cd tutorial-docker-actions-watchtower
For this tutorial, we will use Docker Hub, but you can use any other registry (even private ones). The repository will remain public, but you can also make it private and use access tokens for GitHub Actions to push new images and for WatchTower to pull them.
Ensure you have Docker installed and running on your machine and already have a Docker Hub Account.
Login to Docker Hub using this command and provide your username and password (For better security you can also login using a limited-privilege personal access token that you create on your docker hub account).
docker login
Build the Docker Image.
❗️ Change “username” with your docker hub username.
docker build -t username/app .
Once the image is built, let’s test it by running a container !
docker run -d --name app -p 3000:3000 username/app
When your container is running, open http://localhost:3000 in you web browser. The application should be running.
Now that we’ve tested our app, we will push the image to the registry.
docker push username/app
2 — Set Up Github Actions workflow
Create a .github/workflows
directory in your project and add a workflow file docker-build-push.yml
this file will hold all of the instruction to login into the repository, access token, build and push.
name: Image Build and Push
on:
push:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
buildkitd-flags: --debug
- name: Login to Docker registry
uses: docker/login-action@v2
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: raphaelderouelle/app:latest
We then have to create an access token on docker hub in `Account Settings > Security > Access Tokens`.
⚠️ Copy your access token and store it in a safe place, you won’t be able to access it again.
Next, hop back on your GitHub repository, navigate to Settings > Secrets and variables > Actions
and click on “New repository secret”.
Create 2 secrets :
REGISTRY_USERNAME
with the secret being your docker hub username.REGISTRY_PASSWORD
with the secret being the access token.
These secrets are used by the workflow file in order to login into your docker hub account.
3 — Setup Watch Tower
Watchtower is a tool to automatically update Docker containers.
This command will create a container for watchtower, we tell him to only watch our “app” container.
The --interval
tag is the interval between registry updates (in seconds). In our case, I set it to 2 min.
The --cleanup
tag exists so that the previous images versions are deleted from your machine.
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower my-node-app \
--interval 120 \
--cleanup
You can check that it’s running properly using logs.
docker logs watchtower
4 — Test the deployment pipeline
Modify public/index.html
to reflect the new version:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deployment Success</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>You've Successfully Deployed Version 1.0.1 ✅</h1>
</div>
</body>
</html>
Commit your changes and push the changes to GitHub:
git add .
git commit -m "Update version to 1.0.1"
git push origin main
You will be able to watch the GitHub actions workflow status inside the Actions tab on your repository.
At the end of the workflow, the latest image should be available on your Docker Hub registry.
WatchTower will automatically deploy the new image. We can check the logs again.
level=info msg="Note that the first check will be performed in 1 minute, 59 seconds"
level=info msg="Session done" Failed=0 Scanned=1 Updated=0 notify=no
level=info msg="Found new raphaelderouelle/app:latest image (17b2965cdce3)"
level=info msg="Stopping /app (0d65067b46e1) with SIGTERM"
level=info msg="Creating /app"
level=info msg="Removing image 98ee58a66697"
level=info msg="Session done" Failed=0 Scanned=1 Updated=1 notify=no
As we can see, our “app” container was updated. Now let’s open our browser to check.
Conclusion
You’ve set up a CI/CD pipeline using GitHub Actions to build and push Docker images to a registry and configured Watchtower to automatically update your running container at specified intervals. This ensures that any changes pushed to your GitHub repository are automatically deployed, providing a seamless workflow for continuous integration and deployment of personal applications.
ℹ️ This tutorial is designed for small personal pipelines and provides a straightforward way to automate Docker image builds and deployments using GitHub Actions and Watchtower. However, in an organizational setting, you would typically use Kubernetes with sophisticated deployment strategies to avoid downtime during updates. Tools like ArgoCD or FluxCD (GitOps) would be employed to sync your repository and manage the deployment process seamlessly. Additionally, it’s important to note that Watchtower has limitations, such as primarily supporting the “latest” tag, which might not be suitable for more complex versioning requirements.