Automate your Deployments with Docker & Watchtower

Automate your Deployments with Docker & Watchtower
When your first project finally runs - Midjourney AI

I have had my fair deal with a variety of weird and over complicated, but secure deployment processes. You don't wish to say it, but sometimes complication makes things secure, no pun intended. Nevertheless, I have found my personal favorite, that I want to share with you guys with examples. It usually goes like this:

Have a dockerizable application, and a private docker registry running.

This deployment method IMO is one of the cleanest and easiest I have ever had. I won't go into the details of dockerizing your application and also running a private docker registry as those information are well documented. But I will go into the details of setting up your pipeline for Gitlab and Github (you can use any pipeline you want of course), and how to setup Watchtower. So let's get going!

The Pipeline

You would want your pipeline to do 3 things:

  1. Build your application (Not for all applications needed)
  2. Dockerize and build your image
  3. Connect and push the image to your private docker registry

For your Gitlab project it would look something like this:

image: docker:latest

variables:
  IMAGE_NAME: registry.nexitor.io/my.image:$CI_COMMIT_REF_NAME

services:
    - docker:dind

BuildAndDeploy:
  script:
    - docker login registry.nexitor.io -u $REG_USER -p $REG_PASS
    - docker build -t $IMAGE_NAME .
    - docker push ${IMAGE_NAME}
  only:
    - staging
    - production

You can of course adjust the branches on which it will deploy, in this case we can use several instances of images differentiated by their tags. You can also dynamically set their names just like we do here with the tag, it's all up to you. Remember to set your project secrets in order to access them in your pipeline.

Let's go into an example with a Node project that you want to build outside of Docker, we will use Github Actions to that. This produces a little bit more pipeline code since we do all three steps in the pipeline:

name: Build Npm, Build Docker and push

on:
  push:
    branches: [ "master" ]

jobs:

  build_npm:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x]

    steps:
    - uses: actions/checkout@v3
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm install    
    - run: npm run build
    -
      name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
    - 
      name: Login to private registry
      uses: docker/login-action@v2
      with:
        registry: registry.nexitor.io
        username: ${{ secrets.REGISTRY_USERNAME }}
        password: ${{ secrets.REGISTRY_PASSWORD }}
    -
      name: Build and push
      uses: docker/build-push-action@v3
      with:
        context: .
        file: Dockerfile
        tags: registry.nexitor.io/my.image
        push: true

Watchtower

Now that we can automatically build and push our image onto our private registry by each Git Push or hg push...Mercurial.. cough, any merc fans out there?..

Anyways, got a bit drifted off there. Now comes the big question of how we run/replace the current running container with the new one?

And that's where Watchtower comes into the game. It's a tool to monitor and check for updated image versions an just restarts (actually replaces) your current running one with the new image and with the same configurations (ports, envs....).

To run a local docker Watchtower instance just run the following docker compose file:

version: '3'

services:
  watchtower:
    container_name: watchtower
    image: containrrr/watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_LABEL_ENABLE=true
      - WATCHTOWER_INCLUDE_RESTARTING=true
      - REPO_USER=tom_likes_jerry
      - REPO_PASS=hunter123
      - WATCHTOWER_INCLUDE_STOPPED=true
    command: --interval 30

As you can see, we want it to also connect to our private repository when an image with that host is given, that's why we pass him the credentials. Also we set the check interval to 30 so it checks each running container that has the watchtower label every 30 seconds for an updated image.

So for Watchtower to check your running containers you would need to label them as follows:

sudo docker run -d --name nexitor.image --label="com.centurylinklabs.watchtower.enable=true" registry.nexitor.io/my.image

you can also set that off and just check every running container but I do not recommend that as you don't want just random services getting restarted with different versions. For deactivating you would set the corresponding flag WATCHTOWER_LABEL_ENABLE to off.

Tadaaa!

Now you have your own fully automated deployment pipeline for your private services. I hope I could help you and maybe you also gained some knowledge. If you like my writing and want to read more, please consider subscribing and I will notify you when I release a new Post! I love sharing knowledge or discussing topics, so if you have topics you wish to know more about reach out to me! Thank you so much for reading this far!