Published on: September 20, 2023
22 min read
Follow this comprehensive tutorial to learn how to create and use feature flags in your software development environment.
Developers typically use advanced techniques like canary, blue/green, and incremental deployments to reduce risk when practicing progressive delivery, a facet of continuous delivery (CD). In this tutorial, we will show you how to use feature flags, another progressive delivery option developers can use to test while in production.
Progressive delivery is the ability to test in production while controlling your audience of who can exercise or see updates to an application with a high level of granularity. This approach can also be thought of as developer experimentation.
Feature flags enable you to choose what to deploy and who to deploy to in production. They allow you to define the audience for your application updates as well as the fashion in which they will be served.
Feature flags help stakeholders reduce risk, allowing them to do controlled testing of features and separate feature delivery from customer launch.
The following are benefits of GitLab's feature flags.
This is what you need for this tutorial:
brew install fluxcd/tap/flux
)kubectl
connectivity to your Kubernetes cluster from a local Terminal window on your desktopThis tutorial is based on a fictitious application, which is a simplified inventory system. The goal of this tutorial is to show you how to create, configure, and implement a feature flag using GitLab.
Note: This tutorial is for learning purposes and not meant to deploy a production-ready architecture. Also, to keep the number of steps low, masked variables and sealed secrets are not being used throughout this tutorial.
Here is how to install Flux and GitLab agent for Kubernetes.
pat-for-flux
. Leave the expiration date with its default (it should be 30 days from its creation) and select the API scope for your PAT. Click on the Create personal access token button to create your PAT. Copy and save the value of your PAT; you will need it at a later step.
Creating a personal access token
Creating group "hn"
Creating project "flux-config"
kubectl
access to your Kubernetes cluster, export your PAT by entering the following command:export GITLAB_TOKEN=
<replace with your PAT value>
kubectl
access to your Kubernetes cluster, bootstrap Flux by executing the following command:Note: Make sure to replace <your path>
with whatever precedes your group “hn”. For example, it could be --owner=tech-marketing/sandbox/hn
, or if your group “hn” is at the very top level of your GitLab workspace, it would be --owner=hn
.
flux bootstrap gitlab \
--owner=<your path>/hn \
--repository=flux-config \
--branch=main \
--path=clusters/my-cluster \
--deploy-token-auth
Flux bootstrap output
The “flux-config” project should now contain new directories and files as shown below:
Project flux-config post flux bootstrap process
Note: Make sure to replace <your path>
with whatever precedes your group “hn”. For example, it could be - id: tech-marketing/sandbox/hn
or if your group “hn” is at the very top level of your GitLab workspace, it would be - id: hn
.
ci_access:
groups:
- id: <your path>/hn
Commit this file to main by clicking on the Commit changes button and ensuring that the target branch is “main”.
Creating the GitLab agent for Kubernetes configuration manifest
Registering the GitLab agent for Kubernetes
The agent access token to save
At this moment, you will see the agent listed and its Connection status will be “Never connected”.
Agent registered but not connected yet
apiVersion: v1
kind: Namespace
metadata:
name: gitlab
Manifest for the gitlab namespace
Commit this file to main by clicking on the Commit changes button and ensuring that the target branch is “main”.
Note: You can check that the namespace was created in your cluster by executing this command from a Terminal:
kubectl get ns
Flux created gitlab namespace
Note: Make sure to replace <your-agent-access-token-here>
with your Agent access token you saved earlier.
apiVersion: v1
kind: Secret
metadata:
name: gitlab-agent-token-initial
type: Opaque
stringData:
values.yaml: |-
config:
token: "<your-agent-access-token-here>"
Manifest for agent token secret created on local desktop
kubectl apply -f secret.yaml -n gitlab
Note: You can check that the secret was created in your cluster by executing this command from a Terminal:
kubectl get secrets -n gitlab
Applying the agent token secret to the Kubernetes cluster
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
labels:
app.kubernetes.io/component: agentk
app.kubernetes.io/created-by: gitlab
app.kubernetes.io/name: agentk
app.kubernetes.io/part-of: gitlab
name: gitlab-agent
namespace: gitlab
spec:
interval: 1h0m0s
url: https://charts.gitlab.io
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: gitlab-agent
namespace: gitlab
spec:
chart:
spec:
chart: gitlab-agent
sourceRef:
kind: HelmRepository
name: gitlab-agent
namespace: gitlab
interval: 1h0m0s
values:
replicas: 1
config:
kasAddress: "wss://kas.gitlab.com"
valuesFrom:
- kind: Secret
name: gitlab-agent-token-initial
valuesKey: values.yaml
Creating the manifest for the GitLab agent for Kubernetes
Commit this file to main by clicking on the Commit changes button and ensuring that the target branch is “main”.
Note: In a few seconds, you can check that the GitLab agent for Kubernetes was created in your cluster by executing this command from a Terminal (the pod name should start with “gitlab-agent”):
kubectl get pods -n gitlab
Agentk running in the Kubernetes cluster
Leave the rest of the fields with their defaults.
Importing mysql project into group "hn"
Creating the deploy token for "mysql" project for Flux to interact with it
Copy and save the username and password for the newly created deploy token; you will need them at a later step.
Creating the deploy token for "mysql" project for Flux to interact with it
Note: Make sure to replace <your path>
with the missing partial path to the project “mysql”,
flux create secret git mysql-flux-deploy-authentication \
--url=https://gitlab.com/<your path>/hn/mysql \
--namespace=default \
--username=<your-deploy-token-username> \
--password=<your-deploy-token-password>
Note: You can check that the secret was created in your cluster by executing this command from a Terminal:
kubectl -n default get secrets mysql-flux-deploy-authentication
Creating secret for the deploy token for "mysql" project in the Kubernetes cluster
Selecting Web IDE from the dropdown menu
Navigate to directory "clusters/my-cluster" in the Web IDE
Note: Replace <your path>
with the missing partial path to the project “mysql”
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: mysql
namespace: default
spec:
interval: 1m0s
ref:
branch: main
secretRef:
name: mysql-flux-deploy-authentication
url: https://gitlab.com/<your path>/hn/mysql
Creating mysql-manifests-source.yaml file in the Web IDE
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: mysql-source-kustomization
namespace: default
spec:
interval: 1m0s
path: ./
prune: true
sourceRef:
kind: GitRepository
name: mysql
namespace: default
targetNamespace: default
Creating mysql-manifests-kustomization.yaml file in the Web IDE
Clicking on the Source Control icon and committing to main in the Web IDE
Then press the Continue button to confirm that you want to commit your changes to the default branch:
Clicking on the Source Control icon and committing to main in the Web IDE
Note: You can check that the GitLab agent for Kubernetes was created in your cluster by executing this command from a Terminal:
kubectl get pods -l app=mysql
You can check the persistent volume by executing this command from a Terminal:
kubectl describe pvc mysql-pv-claim
Verifying that mysql pod and its associated persitent volume claim are up and ready
Head to "mysql" project and select Build > Pipelines from the left vertical navigation menu
Clicking on the Run pipeline button to run the project "mysql" pipeline
The "mysql" pipeline and its two manual jobs
Note: The clear_db job should only be used if you’d like to erase all of the database resources created by the create_and_load_db job. The clear_db should only be used AFTER a failed run of the create_and_load_db job.
Now that we have the database ready to go, let’s set up the project that we will use for the creation of the feature flags.
Note 1: Make sure to create and set the KUBE_CONTEXT and KUBE_NAMESPACE variable in group “hn” and to these values:
variable | value |
---|---|
KUBE_CONTEXT | <your path> /hn/flux-config:k8s-agent |
KUBE_NAMESPACE | my-apps |
For example, in my case <your path>
was “tech-marketing/sandbox/hn/flux-config:k8s-agent”. In your case, it will be different. If <your path>
is at the root of your GitLab workspace, then it would be empty so the value of KUBE_CONTEXT would be “hn/flux-config:k8s-agent”.
Adding variable KUBE_CONTEXT in group "hn"
Adding variable KUBE_NAMESPACE in group "hn"
Note 2: As an FYI, when uncommenting the GitLab managed apps in the “helmfile.yaml” file, there will not be one for Prometheus. So, you will only uncomment the lines for ingress and cert-manager.
Uncommenting lines for ingress and cert-manager in file "helmfile.yaml"
Note 3: When the pipeline for project “cluster-management” runs, you will notice that the job “sync” is a manual job. You will need to click on its Play (right arrow next to its name) button to run it. Wait until the “sync” job completes successfully before continuing.
Job "sync" is manual so you need to press on the Play button next to its name
Note 4: Once the pipeline finishes, for your convenience, here is the command you need to run from a Terminal window to get the external IP address of your cluster:
kubectl --namespace gitlab-managed-apps get services -o wide -w ingress-ingress-nginx-controller
Running
kubectl
command to get the ingress IP address to the cluster
Create and set a variable KUBE_INGRESS_BASE_DOMAIN
in group “hn” and set it to the external IP address of your cluster and append the suffix “.nip.io” to it.
Addding variable KUBE_INGRESS_BASE_DOMAIN in group "hn"
Leave the rest of the fields with their defaults.
Importing project "prodmgr" into group "hn"
include:
template: Auto-DevOps.gitlab-ci.yml
variables:
K8S_SECRET_TF_VAR_dbusername: "sasha"
K8S_SECRET_TF_VAR_dbpassword: "password"
TEST_DISABLED: "true"
CODE_QUALITY_DISABLED: "true"
LICENSE_MANAGEMENT_DISABLED: "true"
BROWSER_PERFORMANCE_DISABLED: "true"
LOAD_PERFORMANCE_DISABLED: "true"
SAST_DISABLED: "true"
SECRET_DETECTION_DISABLED: "true"
DEPENDENCY_SCANNING_DISABLED: "true"
CONTAINER_SCANNING_DISABLED: "true"
DAST_DISABLED: "true"
REVIEW_DISABLED: "true"
CODE_INTELLIGENCE_DISABLED: "true"
CLUSTER_IMAGE_SCANNING_DISABLED: "true"
POSTGRES_ENABLED: "false"
STAGING_ENABLED: "true"
INCREMENTAL_ROLLOUT_MODE: "manual"
Click on the Commit changes button ensuring that the Target branch is main.
Creating an Auto-DevOps-based pipeline for project "prodmgr"
To deploy the application to production, click on the rollout 100% Play button
At this point, you have a running application in the staging and production environments in your Kubernetes cluster. Let’s start creating a feature flag.
Creating user list named "prods-in-alphabetical-order-userlist”
Adding users to user list "prods-in-alphabetical-order-userlist”
In the Strategies section of the New feature flag window, there should already be sub-sections for Type and Environments.
Defining the feature flag with its strategies for strating and production environments
In order for developers to instrument their code for this feature flag, you need to share with them the following information:
Copy and save the values for the feature flag API URL and Instance ID
Variable Key | Variable Value | Variable Type | Environment Scope | Flag - Protect variable | Flag - Mask variable |
---|---|---|---|---|---|
K8S_SECRET_UNLEASH_URL | <saved API URL value> | Variable | All (default) | unchecked | unchecked |
K8S_SECRET_UNLEASH_INSTANCE_ID | <saved Instance ID value> | Variable | All (default) | unchecked | unchecked |
Adding variable K8S_SECRET_UNLEASH_URL to project "prodmgr"
Adding variable K8S_SECRET_UNLEASH_INSTANCE_ID to project "prodmgr"
These two variables contain values that will be passed to your application (via the K8S_SECRET_ keyword) so that it can make use of the feature flags defined and managed by GitLab.
In order for your application to be able to use feature flags, you need to instrument your application with our Feature Flags framework. Let's see how you do this in the sample Java application.
In this example, we are using the Java client for Unleash but if you’re using a different programming language then you need to use the client library for your language. To get all the supported languages, refer to the Unleash documentation or Unleash open source project.
src/main/java/csaa/jspring/ProductManager
.// Uncomment block below to instrument Feature Flag
Uncomment all the code blocks under each of the lines indicated above.
Partial view of AppController.java file with uncommented code blocks
Now that the application is running in the staging and production environments, let’s see the feature flag in action.
Now let's check how the feature flag is working.
Michael gets the feature flag that orders the list of product names in alphabetical order
Thomas does not get the feature flag because the product names are not ordered in alphabetical order
The steps above demonstrate that the feature flag strategy for staging successfully worked.
Note: Remember to click on the "Open live environment" button for the production environment. Once you log out from each user, remember to close the browser tab to ensure that the session closes.
Username | Password |
---|---|
[email protected] | pa33w0rd |
[email protected] | pa33w0rd |
[email protected] | pa33w0rd |
[email protected] | pa33w0rd |
[email protected] | pa33w0rd |
[email protected] | pa33w0rd |
Your final count should consist of three users being served the feature and three not, matching the strategy that was set for the production environment.
As changes are made to feature flags, you can track them from the audit events window.
Note: A Premium GitLab subscription is needed for viewing Audit events.
Audit events is an auditable list of actions that have been taken againt resources
This auditing allows you to identify when and who made changes to feature flags. It can also help preempt out-of-compliance scenarios and streamline audits to avoid penalties, providing an opportunity to optimize cost, and lower risk of unscheduled production outages.
Now you know how to create and use feature flags to lower your deployment risk.
Photo by Liam Desic on Unsplash