Panfactum LogoPanfactum
CI / CDRolling Deployments

Rolling Deployments

Objective

Create a service deployment that automatically updates with a newly built image for each commit pushed to your git repository.

Prerequisites

  • Review: Developing a First-party IaC Module

  • Complete: CI / CD Getting Started Guide

  • This guide assumes that you already have application code and a corresponding Dockerfile committed and pushed to your version control system.

Background

A common practice in software development is to have an environment such as development that is running the latest code on the primary integration branch of your git repository (e.g., main). We will demonstrate how to easily accomplish this using the Panfactum CI / CD system.

Build and Push Image from Dockerfile

First, you need to build and push an image based on the Dockerfile in your repository. We provide a submodule that will create a WorkflowTemplate that does this for you: wf_dockerfile_build.

Add this submodule to your CI / CD module that you created in the Getting Started guide. As an example of how this might look, you can reference our CI / CD module for this website.

Note that the images generated by the Workflow are tagged with the commit hash that was checked out for the build.

Before proceeding, ensure you have used the WorkflowTemplate to successfully create a build of your repo's Dockerfile.

Deploy the Image

Next, you must deploy your image using IaC. We provide an example of our website service as well as the terragrunt.hcl that we use to deploy it.

Notice that our module has an input called website_image_version which is set in terragrunt.hcl as follows:

website_image_version = run_cmd("--terragrunt-quiet", "pf-get-version-hash", "main", "https://github.com/panfactum/stack")

The run_cmd function is a Terragrunt feature that allows you to call an external command as your Terragrunt code is evaluated. The stdout from this call is then returned by the function.

We provide a command in the devenv called pf-get-version-hash that takes a git reference and a repository URL (using https://) and returns the commit hash that reference points to. By choosing main, our module will deploy the image corresponding to the latest commit on the main branch.

Before proceeding, ensure you can successfully deploy and run your service. Commit and push this new IaC so that we can pull it in the Workflows we create in the following step.

Combine WorkflowTemplates

Next, we must create a WorkflowTemplate the combines both the wf_dockerfile_build logic and the wf_tf_deploy logic (created in the Getting Started guide). This new WorkflowTemplate will reuse the existing WorkflowTemplates to first build the image and then deploy it.

This composition pattern is described in our guide for triggering Workflow. The full code for our website example can be found here, but the following snippets deserve an explanation:

Snippet 1

This code combines templates from the WorkflowTemplates created by the wf_dockerfile_build and wf_tf_deploy submodules and then runs them in sequence. The entrypoint output of each submodule refer to the name of the entry / root template of that WorkflowTemplate. 1

templates = [
    {
      name = "entry",
      dag = {
        tasks = [
          {
            name = "build-image"
            templateRef = {
              name = module.website_builder.name
              template = module.website_builder.entrypoint
            }
          },
          {
            name = "deploy-image"
            templateRef = {
              name = module.tf_deploy.name
              template = module.tf_deploy.entrypoint
            }
            depends = "build-image"
          }
        ]
      }
    }
]

Snippet 2

This code passes the git_ref and tf_apply_dir parameters to each reference template. For this Workflow, we want to build from the main branch and then apply only the pf_website module.

passthrough_parameters = [
    {
      name = "git_ref"
      value = "main"
    },
    {
      name = "tf_apply_dir"
      value = "packages/reference/environments/production/us-east-2/pf_website"
    }
]

Once you have created an analogous WorkflowTemplate in your IaC, deploy it. You should now see it in the Argo web dashboard. If you create a new Workflow from the WorkflowTemplate, it should successfully run a combined execution graph of both the build and deploy Workflow:

Example of a composed Workflow that both builds and deploys a container image

Connect to CI / CD Webhooks

Finally, you can now connect this new WorkflowTemplate to the Argo Sensor for your CI / CD pipeline. This will allow you to trigger a new Workflow from this WorkflowTemplate whenever you push to main (or whichever branch you want to use to automatically trigger deployments).

Here is an example of how we do this for our website.

Footnotes

  1. Yes, this is confusing. It is unclear why Argo would choose the word "template" to name the steps of WorkflowTemplates, but it is what it is. The maintainers offer some more explanation here.