Automate Docker with GitOps & Portainer

blog-post-img

Some of my smaller customers use a single Docker server to run an application test environment and/or to have developer tools like Jenkins, Artifactory, or SonarQube on-premise. But how to update the containers? This is where GitOps can come in handy.
But do we need Kubernetes, Flux, or ArgoCD to practice GitOps? There is an easier way. I’ll show you how to seamlessly integrate GitOps with Portainer, to automate deployments and simplify your development workflow.

What is GitOps?

GitOps is a branch of DevOps that focuses on using git repositories to manage infrastructure and application code deployments. The main difference between the two is that in GitOps, the git repository is the source of truth for the deployment state, while in DevOps, it is the application or server configuration files.

Source: https://about.gitlab.com/topics/gitops/

And what is Portainer?

Portainer is a container management tool available as a community or as a business edition. The business edition targets larger organizations but has some handy features for GitOps, like updating containers automatically from Git repositories. We use the business edition for the automatic updates but don’t worry it’s free for the first five nodes, and we only have one. To get a license key, you only have to register on their website. Below, you’ll see a screenshot of the administration UI:

The Reverse Proxy

Additionally, we are using Traefik as a reverse proxy. Traefik is very easy to configure; on the containers, we only have to set some labels that Traefik will pick up to configure the routing.

The GitOps Repository

In my current project, we use one repository that stores Docker Compose files for each application/tool we want to run as a container. The Docker Compose file looks relatively standard except for the labels and network for the Trafik reverse proxy.

version: "3.3"

services:
  toscaweb-develop:
    container_name: toscaweb-develop
    image: "organization/tosca:21.2.2472.develop"
    restart: always
    ports:
      - "127.0.0.1:9090:9090"
    networks:
      - traefik
    environment:
      - TZ="Europe/Zurich"
    labels:
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.toscaweb-develop.tls=true"

networks:
  traefik:
    external: true

Deploy with Portainer

To run a container based on a Docker Compose file from a Git repository, we define a Stack in Portainer. A stack is a collection of services, usually related to one application or usage. In our case, we only have one service per stack. The definition is straightforward in the Portainer UI.

To automatically update the stack choose “Automatic Update”. Then you can define if you want Portainer to poll the repository or if you want to use a webhook that you must add to the repository.

Jenkins Build

As the last step, we must update the Docker Compose File with the latest version in our Jenkins build. There are many ways to do that. We went with the simplest and wrote a script to clone/pull the repository, update the Docker Compose File with sed, commit and push the changes.

That’s it!

stage ('Update Docker Compose File') {
  steps {
    script {
      dir("gitops") {
        withCredentials([usernamePassword(credentialsId: 'build.user', 
                         passwordVariable: 'pass', usernameVariable: 'user')]) {
          sh '''
            if cd tools-gitops
            then
              git pull
            else
              git clone http://ds-git.ds.lan:3000/Tools/tools-gitops
            fi

            sed -i 's/"organization\\/tosca:.*"/"oranization\\/tosca:'${TAG}'"/g' toscaweb-develop.yml

            git commit -am "Updated toscaweb-develop.yml with ${TAG}"

            git push http://$user:$pass@ds-git.ds.lan:3000/Tools/tools-gitops
          '''
        }
      }
    }
  }
}

Take Control of Your Docker Environment

Portainer is an excellent tool for managing Docker. The automatic stack update enables simple GitOps that is enough for many use cases.

Currently, we run three applications this way, plus the developer tools Jenkins, Artifactory, SonarQube, and Selenium Grid. Thanks to the configuration of the stacks with Docker Compose and versioning in a Git repository, we achieve transparent traceability.

Stuck integrating GitOps with Portainer? Don’t hesitate to reach out for help!

Simon Martinelli
Follow Me