Hands-On with FluxCD: A Practical Guide to GitOps for Beginners


Whether you're a developer, DevOps engineer, sysadmin, or platform engineer, you've heard of GitOps. GitOps is a methodology that extends the CI/CD model, allowing teams to manage infrastructure and application deployments using Git as a single source of truth. Using GitOps, however, might not be as straightforward as it seems. Some would even argue that its hidden complexity is not worth the hassle. GitOps with FluxCD can help navigate these complexities, as FluxCD simplifies continuous delivery through its built-in reconciliation engine and more.
In this beginner's guide, I aim to break down some of the complexity and deliver some tacit knowledge, allowing you to get hands-on with FluxCD. I'll show you the benefits of FluxCD, so you feel confident getting up and running. Also, you'll learn by doing an actual project!
What is FluxCD?
FluxCD is an open-source continuous delivery tool designed for Kubernetes. It works by continuously monitoring your Git repository and applying any changes to your Kubernetes cluster. With FluxCD, you can automate updates and rollbacks and even monitor your deployments.
Key Features of FluxCD
GitOps: FluxCD leverages Git as the single source of truth, enabling declarative management of infrastructure and application deployments.
Automated Deployments: FluxCD continuously monitors your Git repository and automatically updates your Kubernetes cluster with the desired state.
Syncing and Reconciliation: FluxCD ensures that the live state of your cluster matches the state defined in your Git repository.
Multi-Tenancy: FluxCD supports managing multiple environments, allowing you to use the same Git repository for staging, production, and more.
Integrations: FluxCD integrates well with tools like Helm, Prometheus, and others to provide a comprehensive solution for GitOps.
Core Components of FluxCD
Before we dive into the hands-on part, let's briefly discuss the core components of FluxCD:
Flux is the primary operator (control plane), continuously monitoring the Git repository and applying changes to the Kubernetes cluster.
The Helm Controller is a component of FluxCD that allows you to manage Helm charts in a GitOps fashion.
The Kustomize Controller supports managing Kubernetes manifests using Kustomize, enabling you to customize your deployments.
The Notification Controller handles notifications and alerts for your deployments.
The Source Controller monitors the source of the Kubernetes manifests (Git, Helm repositories, etc.) and makes them available for the other controllers.
Creating a GitHub Personal Access Token (PAT)
Since FluxCD uses Git as the single source of truth for deployments, the apparent prerequisite is a Git hosting service. Flux CLI creates a new GitHub repository for you, but it needs a personal access token (PAT) to do so.
Create a new PAT by going to the following GitHub link: https://github.com/settings/tokens/new
Select the checkmark box next to "repo," as shown below, then click "Generate Token." Copy the PAT, as you will use it for the next step!
Installing FluxCD
FluxCD can be installed using the flux
CLI tool. Here's how you can set it up on your Kubernetes cluster. For free access to a cluster, head to Killercoda.com and follow along!
Install the Flux CLI:
curl -s https://fluxcd.io/install.sh | sudo bash
Bootstrap FluxCD:
The bootstrap command installs the FluxCD components and configures them to reconcile your cluster state with a Git repository (replace
<github-username>
with your GitHub username).flux bootstrap github \ --owner=<github-username> \ --repository=my-flux-repo \ --branch=main \ --path=./clusters/my-cluster \ --personal=true \ --private=false
Verify the Installation:
After bootstrapping, you can verify that FluxCD is running correctly using the command
flux check --pre
which will check the health of the Flux components in your Kubernetes cluster.flux check --pre
Clone the Repo:
After running theflux bootstrap github
command, you should clone the GitHub repository that Flux created during the bootstrap process. This allows you to add your application files (e.g., Kubernetes manifests, Helm charts, or Kustomize files) to the repository. These files define the desired state of your applications, which Flux will then monitor and apply to your Kubernetes cluster (replace<github-username>
with your GitHub username).git clone https://github.com/<github-username>/my-flux-repo.git
Create your Application Directory
Inside the cloned repository, you will see the directory structure created by Flux. Typically, a specific directory is created to store the application manifests, helm charts or Kustomize configurations (e.g.apps/my-app
). Create the directory where your application files will go.mkdir -p my-flux-repo/apps/my-app
(Optional) Organize Your Repository
For larger projects, you can structure your repository into separate directories for staging, production, and other environments. The following is an example of this organizational pattern, which allows you to define shared configurations in the base directory and customize them for each specific environment in the overlays directory, without duplicating code.apps/ ├── my-app/ │ ├── base/ │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── overlays/ │ ├── staging/ │ └── production/ clusters/ ├── staging/ │ └── kustomization.yaml └── production/ └── kustomization.yaml
Building a Sample Microservices Application
In this example, we'll create a simple microservices-based application consisting of two services:
Frontend Service: A simple web server built using Node.js that serves static content.
Backend Service: A Python-based API service that provides some data to the frontend.
For most Kubernetes-based setups using FluxCD, the best practice is to store application code and deployment manifests in separate repositories. This adheres to GitOps principles, where your Git repository serves as the single source of truth for your cluster's desired state. Therefore, we’ll create a new application repository that contains our application code.
mkdir src && cd src
Frontend Service
The frontend service will be a basic Node.js application that serves an HTML page. Here's the code:
cat << EOF > index.js
// index.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('<h1>Welcome to the Frontend Service</h1>');
});
app.listen(port, () => {
console.log('Frontend service is running on http://localhost:${port}');
});
EOF
Create a Dockerfile for the frontend service:
cat << EOF > JS-Dockerfile
# JS-Dockerfile
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
EOF
Backend Service
The backend service will be a simple Python Flask application that returns some JSON data. Here's the code:
cat << EOF > app.py
# app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/data', methods=['GET'])
def get_data():
data = {
'message': 'Hello from the Backend Service!'
}
return jsonify(data)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
EOF
Create a Dockerfile for the backend service:
cat << EOF > Py-Dockerfile
# Dockerfile
FROM python:3.8-slim
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
EOF
Create the requirements.txt
file:
echo "flask" > requirements.txt
create a package.json
file:
# package.json
cat << EOF > package.json
{
"name": "flux-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"express": "^4.17.1"
},
"scripts": {
"test": ""
},
"author": "",
"license": "ISC"
}
EOF
Build the Container Images
We'll build the container images for both the front and backend apps using the Docker CLI. Use the following commands to build and tag the images using the two separate Dockerfile
files named JS-Dockerfile
and Py-Dockerfile
:
docker build -f JS-Dockerfile -t localhost:5000/my-frontend .
docker build -f Py-Dockerfile -t localhost:5000/my-backend .
Create a container registry locally:
docker run --name local-registry -d -p 5000:5000 registry
Push the container images to the local registry:
docker push localhost:5000/my-backend
docker push localhost:5000/my-frontend
Creating Kubernetes Manifests
Next, we'll create Kubernetes manifests for deploying these two services, placing them in the repo that we created and cloned at the beginning, in my-flux-repo/apps/my-app
. Use your favorite text editor to create these files in the my-app
directory.
cd $HOME/my-flux-repo/apps/my-app
Frontend Deployment Manifest
# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: default
labels:
app: frontend
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: localhost:5000/my-frontend:latest
ports:
- containerPort: 3000
Frontend Service Manifest
# frontend-service.yaml
apiVersion: v1
kind: Service
metadata:
name: frontend-service
namespace: default
spec:
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 3000
Backend Deployment Manifest
# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: default
labels:
app: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: localhost:5000/my-backend:latest
ports:
- containerPort: 5000
Backend Service Manifest
# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-service
namespace: default
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 5000
Setting Up FluxCD to Deploy the Application
Now that we have our application and Kubernetes manifests ready, let's set up FluxCD to deploy them.
Step 1: Create a Kustomization File
Also in the my-app
directory, create a kustomization.yaml
file. This file tells FluxCD to apply the resources defined in the specified YAML files.
The kustomization.yaml
file serves as a configuration file for Kustomize, a Kubernetes-native configuration management tool. It defines how Kubernetes manifests in that directory should be aggregated, customized, or patched before being applied to your cluster.
In a GitOps setup, such as with FluxCD, this file helps manage and deploy the application's resources by describing how the various Kubernetes YAML files in the my-app
directory (like frontend-deployment.yaml
, frontend-service.yaml
, etc.) should be combined and customized.
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- frontend-deployment.yaml
- frontend-service.yaml
- backend-deployment.yaml
- backend-service.yaml
- Link the Application Directory to FluxCD
In theclusters/my-cluster/flux-system/kustomization.yaml
file (already created from the bootstrap process), add a reference to your application'skustomization.yaml
file. This tells FluxCD to apply the resources defined inapps/my-app
. Edit theclusters/my-cluster/flux-system/kustomization.yaml
file and add thepath
to your application directory.
cd $HOME/my-flux-repo/clusters/my-cluster/flux-system
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
- ../../../apps/my-app/ # add this line
What Happens When FluxCD Reconciles It?
FluxCD Detects Changes:
- If you make a change in the Git repository (e.g., update
kustomization.yaml
or a resource file), FluxCD detects the change.
- If you make a change in the Git repository (e.g., update
FluxCD Applies the Kustomization:
FluxCD reads the
kustomization.yaml
file in theapps/my-app
directory.It processes the listed resources, applies the customizations (e.g., patches, labels), and reconciles the desired state with the cluster.
Resources Are Deployed:
- The aggregated and customized manifests are applied to the cluster, ensuring the application is deployed in the desired configuration.
Commit and Push Changes
After updating the kustomization.yaml
, commit and push the changes to your Git repository.
cd $HOME/my-flux-repo
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
git add .; git commit -m "add my-app to cluster configuration"; git push origin main
NOTE: When prompted for your GitHub password, paste in your GitHub PAT (the one we generated at the beginning).
Verify FluxCD Reconciliation
Wait for FluxCD to reconcile the changes or manually trigger it. Check the logs for updates:
Trigger Reconciliation:
flux reconcile kustomization flux-system -n flux-system
Check Logs:
kubectl logs -n flux-system deploy/kustomize-controller
Check Kustomization Status:
flux get kustomizations -n flux-system
Check that the app was created:
kubectl get all
Summary
In this guide, we walked through the end-to-end process of setting up a GitOps workflow using FluxCD, from bootstrapping a Flux-enabled cluster to deploying a sample microservices application. Along the way, you learned how FluxCD simplifies the continuous delivery process through reconciliation, Git-based change tracking, and modular infrastructure design.
Key Takeaways
Git as the Source of Truth: With FluxCD, your Git repository drives all changes in your Kubernetes cluster, ensuring consistency and auditability.
Modular Design: You can organize your manifests using
Kustomize
, separating base configurations from overlays and manage multiple environments efficiently.Declarative Deployment: All resources, from Deployments to Services, are declaratively described, version-controlled, and automatically applied via FluxCD.
End-to-End Automation: FluxCD monitors changes in Git and continuously reconciles your desired state with the actual state of your cluster, eliminating manual drift correction.
Hands-On Value: By deploying a working microservices application using Node.js and Flask, you’ve applied GitOps principles in a real-world scenario.
While GitOps can initially seem overwhelming, especially with the many moving parts of Kubernetes, CI/CD pipelines, and declarative tooling, FluxCD makes it approachable, scalable, and production-ready. The hands-on walkthrough you completed today is just the beginning.
Whether you’re just getting started or expanding GitOps across multiple environments, FluxCD provides a robust and secure foundation to manage your workloads the GitOps way.
If this guide helped demystify FluxCD, share it with your team or community! And if you’re ready for more advanced topics, like multi-tenancy, pull-based secrets management, and progressive delivery, stay tuned for our next post!
Subscribe to my newsletter
Read articles from KubeSkills directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
