Single Repository vs. Multiple Repositories
Single Repository (Monorepo) Approach:
fleet-infra/
├── clusters/
│ ├── production/
│ │ ├── flux-system/
│ │ ├── namespaces/
│ │ └── kustomization.yaml
│ └── staging/
│ ├── flux-system/
│ ├── namespaces/
│ └── kustomization.yaml
├── infrastructure/
│ ├── sources/
│ ├── monitoring/
│ ├── ingress/
│ └── cert-manager/
└── apps/
├── base/
│ ├── frontend/
│ ├── backend/
│ └── database/
└── overlays/
├── production/
└── staging/
Benefits:
- Single source of truth
- Atomic changes across multiple components
- Easier to understand the entire system
- Simplified CI/CD setup
Drawbacks:
- Can become large and unwieldy
- Potential permission issues
- May slow down Git operations
Multiple Repository Approach:
# Infrastructure Repository
infra-gitops/
├── clusters/
│ ├── production/
│ └── staging/
└── infrastructure/
├── monitoring/
├── ingress/
└── cert-manager/
# Team A Application Repository
team-a-apps/
├── base/
│ ├── frontend/
│ └── backend/
└── overlays/
├── production/
└── staging/
# Team B Application Repository
team-b-apps/
├── base/
│ └── api-service/
└── overlays/
├── production/
└── staging/
Benefits:
- Clear ownership boundaries
- Fine-grained access control
- Better scalability for large organizations
- Reduced repository size
Drawbacks:
- Coordination challenges across repositories
- More complex CI/CD setup
- Potential for drift between repositories
Best Practices for Repository Structure
Regardless of your approach, follow these best practices:
-
Clear Separation of Concerns
- Separate infrastructure from applications
- Separate cluster-specific from shared configurations
- Use distinct paths for different environments
-
Use Kustomize for Environment Variations
- Base configurations for shared elements
- Overlays for environment-specific changes
- Minimize duplication across environments
apps/ ├── base/ │ └── frontend/ │ ├── deployment.yaml │ ├── service.yaml │ └── kustomization.yaml └── overlays/ ├── production/ │ ├── replicas-patch.yaml │ └── kustomization.yaml └── staging/ ├── resources-patch.yaml └── kustomization.yaml
-
Standardize Naming Conventions
- Consistent file and directory naming
- Clear namespace strategies
- Descriptive resource names
-
Include Documentation
- README files explaining the purpose of components
- Architecture diagrams
- Dependency information
Implementing GitOps Workflows
With your tools and repository structure in place, let’s explore how to implement effective GitOps workflows.
Setting Up the GitOps Pipeline
A typical GitOps pipeline involves these components:
- Source Code Repositories: Where application code lives
- CI Pipeline: Builds, tests, and pushes container images
- Configuration Repositories: Where Kubernetes manifests live
- GitOps Operator: Syncs configuration to clusters
Example Workflow with GitHub Actions and Flux:
# .github/workflows/build-and-push.yml
name: Build and Push
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Update image tag in GitOps repo
uses: actions/checkout@v3
with:
repository: my-org/gitops-repo
token: ${{ secrets.PAT_TOKEN }}
path: gitops-repo
- name: Update image tag
run: |
cd gitops-repo
sed -i "s|image: ghcr.io/${{ github.repository }}:.*|image: ghcr.io/${{ github.repository }}:${{ github.sha }}|" apps/base/deployment.yaml
git config user.name "GitHub Actions"
git config user.email "[email protected]"
git add .
git commit -m "Update image to ${{ github.sha }}"
git push
Automated Image Updates with Flux
Flux can automatically update image tags in your Git repository:
# Image repository configuration
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
name: podinfo
namespace: flux-system
spec:
image: ghcr.io/stefanprodan/podinfo
interval: 1m0s
---
# Image policy for semantic versioning
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
name: podinfo
namespace: flux-system
spec:
imageRepositoryRef:
name: podinfo
policy:
semver:
range: 5.0.x
---
# Image update automation
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m0s
sourceRef:
kind: GitRepository
name: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
email: [email protected]
name: fluxcdbot
messageTemplate: '{{range .Updated.Images}}{{println .}}{{end}}'
push:
branch: main
update:
path: ./clusters/my-cluster
strategy: Setters
Progressive Delivery with Argo Rollouts
For advanced deployment strategies, consider Argo Rollouts:
# Progressive delivery with Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: frontend
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 20
- pause: {duration: 10m}
- setWeight: 40
- pause: {duration: 10m}
- setWeight: 60
- pause: {duration: 10m}
- setWeight: 80
- pause: {duration: 10m}
revisionHistoryLimit: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: frontend:v1
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
Multi-Environment GitOps
Most organizations need to manage multiple environments (development, staging, production). Here’s how to handle this with GitOps:
Environment Promotion Strategies
1. Path-Based Environments
Use different paths in the same repository for different environments:
environments/
├── dev/
│ ├── apps/
│ └── infrastructure/
├── staging/
│ ├── apps/
│ └── infrastructure/
└── production/
├── apps/
└── infrastructure/
2. Branch-Based Environments
Use different branches for different environments:
dev
branch for developmentstaging
branch for stagingmain
branch for production
3. Promotion through Pull Requests
graph LR
A[Dev Environment] -->|PR| B[Staging Environment]
B -->|PR| C[Production Environment]
Example Promotion Workflow:
# .github/workflows/promote-to-staging.yml
name: Promote to Staging
on:
workflow_dispatch:
inputs:
version:
description: 'Version to promote'
required: true
jobs:
promote:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: main
- name: Create promotion branch
run: git checkout -b promote-${{ github.event.inputs.version }}-to-staging
- name: Update version in staging
run: |
sed -i "s/tag: .*/tag: ${{ github.event.inputs.version }}/" environments/staging/apps/frontend/kustomization.yaml
git config user.name "GitHub Actions"
git config user.email "[email protected]"
git add .
git commit -m "Promote frontend ${{ github.event.inputs.version }} to staging"
git push -u origin promote-${{ github.event.inputs.version }}-to-staging
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
title: "Promote frontend ${{ github.event.inputs.version }} to staging"
body: "Automated promotion of frontend version ${{ github.event.inputs.version }} to staging environment."
branch: promote-${{ github.event.inputs.version }}-to-staging
base: main