How to use ConfigCat with GitOps
GitOps is a framework that combines the practices of DevOps and Git and applies them to infrastructure management. It is not a tool or platform but a set of standards for managing IT infrastructure using Git. Within this realm, feature flags, like those from ConfigCat, can play a crucial role in streamlining workflows, managing releases, and enabling dynamic configurations.
Let’s dive into how ConfigCat and GitOps work together and why this combination is worth considering for your infrastructure and development processes.
GitOps: A Quick Refresher
GitOps is primarily focused on managing the infrastructure that software applications depend on, such as virtual machines, operating systems, software packages, networking, etc.
In the early days, setting up infrastructure meant physically handling servers. Fast forward to today, tools like Ansible, AWS CloudFormation, and Terraform have made it possible to define infrastructure as code.
Terraform, for example, enables teams to manage their entire setup with a simple YAML
file stored in a Git repository, aligning infrastructure with software code.
But what are the benefits of this?
- Git processes - All the advantages of using Git, such as merge requests, commit history, and the ability to undo changes, can be applied to your infrastructure.
- Fewer mistakes - You are less likely to make errors with your infrastructure because you can set rules that require approval from experienced engineers through multiple reviews.
- Centralization - There is a single reliable place to find information, keeping your infrastructure neat and organized.
- Handling changes is straightforward - Only the modified parts of the infrastructure code will be implemented when your GitOps configs get executed.
- CI/CD perks - Infrastructure changes can be automated using tasks set in a CI/CD pipeline.
While these are all strong indicators to consider using GitOps, modern software often requires new features and updates that rely on feature flags. Let us look at how we can integrate them into GitOps.
The role of feature flags in GitOps
Feature flags are boolean values linked to specific features or code blocks in your code. Based on their state, features or code blocks in a program can be toggled on or off. They are platform agnostic and can work in programming languages and frameworks like GitOps. Integrating feature flags with GitOps brings all the benefits of using feature flags into your GitOps workflows. Let us see how this is possible.
To start, consider using a feature flag provider like ConfigCat. It is a developer-centric feature flag service with a free plan with complete feature set, unlimited team size, and awesome support.
Let’s see how you can use ConfigCat in GitOps workflows with two popular tools: Terraform and Kubernetes.
Integrating ConfigCat with Terraform
Terraform lets you define your infrastructure as code, making it easy to include feature flags as part of your setup. Here’s an example:
Say you have a simple Flask API that exposes two endpoints. Endpoint one returns a list of users, and endpoint two returns the sum of users:
@app.route('/users', methods=['GET'])
def get_users():
return jsonify(users)
@app.route('/user_count', methods=['GET'])
def get_user_count():
total_users = len(users)
return jsonify({"total_users": total_users})
If using a local Docker setup as your infrastructure, your Terraform file might look like this:
provider "docker" {
host = "unix:///var/run/docker.sock"
}
resource "docker_image" "flask_api" {
name = "flask-api:latest"
build = "."
force_remove = true
}
resource "docker_container" "flask_api" {
name = "flask-api-container"
image = docker_image.flask_api.latest
ports {
internal = 5000
external = 5000
}
}
Now, let's say that you want to use a feature flag to toggle the endpoint that returns the sum of users when the flag is on as shown below:
@app.route('/user_count', methods=['GET'])
def get_user_count():
isUserCountEndpointEnabled = configcat_client.get_value('YOUR-FEATURE-FLAG-KEY')
if isUserCountEndpointEnabled:
total_users = len(users)
return jsonify({"total_users": total_users})
else:
return "Sorry this endpoint is not available"
You can also include the infrastructure for your feature flag alongside the Flask API infrastructure or in a separate file using ConfigCat’s feature flag provider in Terraform:
# Setup your provider
provider "configcat" {
basic_auth_username = var.configcat_basic_auth_username
basic_auth_password = var.configcat_basic_auth_password
}
# Create a Feature Flag
resource "configcat_setting" "setting" {
config_id = data.configcat_configs.my_configs.configs[0].config_id
key = "isUserCountEndpointEnabled"
name = "User count endpoint"
hint = "Returns the total number of users"
setting_type = "boolean"
order = 0
}
# Set a value to the Feature Flag created above
resource "configcat_setting_value" "setting_value" {
environment_id = data.configcat_environments.my_environments.environments[0].environment_id
setting_id = configcat_setting.setting.id
value = "false"
}
You can read more about it here and see code examples here.
Like many other providers and resources that Terraform supports, your feature flag infrastructure is also idempotent, so if the same configurations are executed again in your GitOps workflow, it won't mess things up. It only changes what needs changing.
If things go unexpected, you can use ConfigCat’s Public Management API or the dashboard to turn off the buggy feature without touching your source code or editing your terraform file.
Using ConfigCat with Kubernetes
Kubernetes is an orchestration platform for containers. It works similarly to Docker in sharing and deploying containerized applications but at a larger scale without the limitations of Docker. In the context of GitOps, Kubernetes focuses on container-based infrastructure setups.
When you combine Kubernetes and feature flags, you can dynamically adjust your container infrastructure on the fly, such as toggling a specific network configuration, the type of image a particular container should use, etc.
For example, you may want to toggle between using a node 18 or a node 20 image for a container based on the value of a feature flag in your Kubernetes infrastructure script. To do this, you can find a common ground that both Kubernetes and the feature flag support. You could opt for Kubernetes python client SDK, where you configure your Kubernetes resources in a .py
file. With that set, you can install ConfigCat’s Python SDK and use conditional if-else statements to control specific parts of your container infrastructure.
Here's a code example:
from kubernetes import client, config
from kubernetes.client.rest import ApiException
from os import path
import yaml
import configcatclient
def main():
# 1. Create the ConfigCatClient instance with your ConfigCat SDK Key
configcat_client = configcatclient.get('YOUR-CONFIGCAT-SDK-KEY')
# Load the kube config file
config.load_kube_config()
# Create the path to the deployment yaml file
deployment_file = path.join(path.dirname(__file__), "deployment.yaml")
# Check if the feature flag is enabled using the ConfigCatClient instance
is_my_feature_flag_enabled = configcat_client.get_value('YOUR-FEATURE-FLAG-KEY', False)
# Load the deployment yaml file and create/update the deployment
with open(deployment_file) as f:
dep = yaml.safe_load(f)
k8s_apps_v1 = client.AppsV1Api()
deployment_name = dep['metadata']['name']
namespace = "default"
try:
# Check if the deployment already exists
k8s_apps_v1.read_namespaced_deployment(name=deployment_name, namespace=namespace)
print("Deployment already exists.")
except ApiException as e:
if e.status == 404:
# If the deployment does not exist, create it
resp = k8s_apps_v1.create_namespaced_deployment(body=dep, namespace=namespace)
print("Deployment created. status='%s'" % str(resp.status))
else:
print("Error: %s" % e)
# Update the deployment image based on the feature flag value
image = "backend-with-node20" if is_my_feature_flag_enabled else "backend-with-node18"
status = "enabled" if is_my_feature_flag_enabled else "disabled"
print(f"Feature flag is {status}. Using image: {image}")
# Patch the deployment with the new image if the feature flag is enabled
if is_my_feature_flag_enabled:
patch = {
"spec": {
"template": {
"spec": {
"containers": [
{"name": "backend-container", "image": image}
]
}
}
}
}
resp = k8s_apps_v1.patch_namespaced_deployment(
name="backend-deployment",
namespace="default",
body=patch,
)
# Run the main function when the script is executed
if __name__ == '__main__':
main()
You can find the complete code for the above here
Best practices for using ConfigCat with GitOps
While you can mix and match multiple technologies to use ConfigCat feature flags in your GitOps workflow, some best practices are worth considering:
Design and planning
When beginning your GitOps journey, take small steps until all your infrastructure resources are automated and in Git. Ideally, your repository strategy should serve as a single source of truth for all your infrastructure. If this is not possible, then have your infrastructure defined per project. Some teams may decide on mono-repos or multi-repos, but whether you choose mono-repos or multi-repos is up to your team and organization preference.
Configuration and Management of Feature Flags in GitOps
When managing your feature flags resources and infrastructure, there are a few pointers to lean towards:
-
Prevent feature flag clutter - If your software development follows a methodology like Agile or similar, your code base can become cluttered with feature flags. A handy solution is to create a task in your CI/CD pipeline to scan for usage references to your feature flags using ConfigCat's CLI tool. You can read how to set this up here. A good practice here is to define an internal policy for removing feature flags after the features they control become stable.
-
Keep it minimal - Provision only the feature flags resources you need in Terraform to keep your configurations clean and readable.
-
Avoid using ConfigCat's public API for feature flag evaluation - Though it might seem convenient to incorporate it into your GitOps workflow, it is generally discouraged. You can use a supported API client for flag evaluation, particularly in the context of Kubernetes mentioned earlier.
Conclusion
GitOps applies DevOps principles using Git as the source of truth for infrastructure. Because GitOps is a framework, you can combine multiple tools and technologies to achieve your GitOps goal. We looked at two of them, Terraform and Kubernetes, and examined how to use these tools to integrate feature flags in your GitOps workflow. Lastly, we covered some best practices when deciding on your repository setup and GitOps workflow. If you want to learn more, here are some resources worth checking out:
-
ConfigCat Feature Flags:
-
Kubernetes:
-
Terraform:
-
ConfigCat's Public API:
Want to give feature flags a try? Get started with ConfigCat for free here. Deploy any time, release when confident. I have been using their forever free plan in all my side projects.
Keep up with the latest updates by following ConfigCat on Facebook, X, LinkedIn, and GitHub.