As we all take the journey towards aligning with GitOps Principles together, I am sure we can all remember when we initially used ArgoCD to deploy our first application to a Kubernetes cluster via GitOps. At Virtru, we loved how much easier GitOps made managing applications and how quickly it allowed us to move while keeping all of our environments continuously in sync.
We all know that after that first deployment, there is that rush and need to move quickly. Before you realize it, you have numerous components deployed across your clusters like:
It seems like every week, there is some new tool that fills a gap. Having all of these options is amazing, but introducing each adds to that growing list of components being managed.
Security is at Virtru’s core, and when you’re working at a company where that is the case, you need to make sure that any components that you’re leveraging are updated in a timely fashion. However, we also don't want to introduce any issues into the environment.
Let’s take a step back and think about what it takes to manage each component. For every component, you need to:
This quickly becomes a full-time responsibility for someone on the team. We needed to reduce some of the unnecessary toil on our team at Virtru.
Typically when managing dependencies you can turn to something like Dependabot. In our case we needed something that could manage more than your typical npm or go package versions. We needed to manage our helm chart versions defined within an ArgoCD Application along with our Terraform providers and modules.
That’s why we started to look out in the community to see if there was any existing technology that we could leverage or decide if we would have to build something on our own.
Lo and behold, we weren't the only ones thinking about this problem!
Renovate is a tool to manage automated dependency updates. It is open-source and integrates with various version control systems, including GitHub and GitLab. There are two ways to install Renovate: via GitHub application or self-hosted.
We wanted to have more control of how we run Renovate, and what version is deployed. This limited us to the self-hosted app. We wanted to run it in Kubernetes. Renovate is available as a container from DockerHub or you can also easily build your own container image by installing the application from NPM, which is the route we chose to have the most control over the Renovate configuration.
Renovate is deployed in the Virtru clusters as a CronJob. Our configuration is similar to the examples from the official documentation:
apiVersion: v1
kind: ConfigMap
metadata:
name: renovate-config
namespace: renovate-test
data:
config.json: |-
{
"repositories": ["<your-org>/<your-repo-1>, <your-org>/<your-repo-2>"]
}
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: renovate-bot
namespace: renovate-test
spec:
schedule: "@hourly"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- image: renovate/renovate:31.14.0
name: renovate-bot
env:
- name: RENOVATE_PLATFORM
value: "github"
- name: RENOVATE_AUTODISCOVER
value: "false"
- name: RENOVATE_BASE_DIR
value: "/tmp/renovate/"
- name: RENOVATE_CONFIG_FILE
value: "/opt/renovate/config.json"
- name: LOG_LEVEL
value: debug
- name: RENOVATE_TOKEN
valueFrom:
secretKeyRef:
key: github-token
name: renovate-secrets
volumeMounts:
- name: config-volume
mountPath: /opt/renovate/
- name: work-volume
mountPath: /tmp/renovate/
restartPolicy: Never
volumes:
- name: config-volume
configMap:
name: renovate-config
- name: work-volume
emptyDir: {}
This manifest assumes that Renovate namespace contains a Kubernetes secret called renovate-secrets, with key github-token and value of valid GitHub API token. The token permissions should be repo read/write. You can create the secret like so:
❯ kubectl create -n renovate secret generic renovate-secrets
--from-literal=github-token=<token>
Being as flexible as it is, Renovate is easy to start with. It offers repository auto-discovery. However, a larger organization would want to omit it, since this feature causes Renovate to run on every single repository that the GitHub API token has access to. An alternative to auto-discovery is the repository list directive in Renovate’s configuration file. Onboarding for each repository is also automated. Renovate submits pull requests with a default configuration file for this repository.
The most bare-bones configuration for a private GitHub repository looks like:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}
This instructs Renovate to use the default configuration, which is a great place to start.
You may also want to add labels to easily filter out PRs created by Renovate:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"labels": ["renovate"]
}
This will cause all the PRs to be labeled as “renovate.”
Ignoring certain paths can also be useful when a project has parts that aren’t ready to accept updates. For example, this is how you ignore everything inside “/argocd/sandbox” folder:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"ignorePaths": [
"argocd/sandbox/**"
]
}
Specifying custom package rules is crucial for any decent-sized repository. These rules instruct Renovate on how to apply a piece of configuration to a number of selected files/packages. The target can be selected based on a set of selectors, including path and manager.
Let’s talk more about “managers,” which is short for “package managers.” These include traditional package managers like npm, Gradle, and Bundle, as well as less traditional file formats, like Dockerfiles or ArgoCD manifests. The managers are used to detect and maintain files in a repository.
As a matter of fact, the minimum configuration for ArgoCD project requires usage of the custom package rules, because by default ArgoCD manager doesn’t define any file match patterns, so it won't match any files until the pattern is configured.
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"matchManagers": [
"argocd"
],
"matchPaths": [
"argocd/production/**",
"argocd/development/**",
"argocd/staging/**"
],
}
]
}
The setup from above will manage ArgoCD manifests in the project with the following structure:
└── argocd
├── development
├── production
└── staging
Custom rules inside the package rules list have higher precedence than global rules. This way, you can redefine global configuration inside the custom rules. For example, you can overwrite global label “green” for all ArgoCD files at path “argocd/production”:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"labels": ["green"],
"packageRules": [
{
"matchManagers": [
"argocd"
],
"matchPaths": [
"argocd/production/**"
],
"labels": ["red"]
},
{
"matchManagers": [
"argocd"
],
"matchPaths": [
"argocd/staging/**",
"argocd/development/**"
]
}
]
}
The previous example also illustrates how you can match based on multiple factors. In that case, it was managers and paths.
Sometimes, it is useful to group some of the objects that have been matched. This could be achieved using the group name directive. All matching updates sharing the groupName will be placed into the same branch and PR. Grouping all non-major version updates together can be done like so:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"matchManagers": ["argocd"],
"matchUpdateTypes": ["minor", "patch", "pin", "digest", "lockFileMaintenance", "rollback", "bump"],
"groupName": "argocd non-major updates"
},
{
"matchManagers": ["argocd"],
"matchUpdateTypes": ["major"]
}
]
}
Last, but not least, you can define a custom schedule for a repository. This setting was essential for us, and we ended up enabling it for every single renovate-managed repository. A repository-based schedule allowed us to run renovate every 15 minutes, without worrying about spinning up an excessive amount of CI/CD jobs or creating other undesired activity triggered by GitHub events.
Cron and Later syntaxes can be used to configure the schedule. There are also some presets that are supported. When a schedule is in use, Renovate will no longer create any new PRs. However, you need to disable the updateNotScheduled value in order to activity in the existing PRs.
Here is an example of monthly schedule with no updates to existing PRs:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"schedule": [
"before 3am on the first day of the month"
],
"updateNotScheduled": false
}
As you can see, Renovate brings a plethora of configuration options and provides the flexibility that any team might need.
Renovate has helped solve that first step in the lifecycle management process of having to check if there is a new release. By tightly coupling release notes to the pull requests, it makes it easier for the team to review what has changed.
Right now, everything after Renovate is still manual, but we plan to take it a few steps further:
Keep an eye out for more Virtru Technical Series posts from the Virtru Platform Engineering team as we continue to explore and implement these additional steps.
The Virtru engineering team includes developers and engineers dedicated to advancing data protection. With decades of combined experience across encryption, policy enforcement, key management, and other critical areas, this world-class team leads innovation in data-centric security.
View more posts by Virtru Platform Engineering TeamSee Virtru In Action
Sign Up for the Virtru Newsletter
Contact us to learn more about our partnership opportunities.