Skip to content
Back to Blog
1 min read

GitOps Practices: Declarative Infrastructure and Application Delivery

I wrote “GitOps Practices: Declarative Infrastructure and Application Delivery” to share practical, production-minded guidance on this topic.

The GitOps Principles

  1. Declarative: Desired state is expressed declaratively
  2. Versioned and Immutable: Git stores the canonical desired state
  3. Pulled Automatically: Software agents pull the desired state
  4. Continuously Reconciled: Agents continuously reconcile actual state

Setting Up Flux for GitOps

# Bootstrap Flux on AKS cluster
flux bootstrap github \
    --owner=myorg \
    --repository=infrastructure \
    --branch=main \
    --path=clusters/production \
    --personal
# clusters/production/flux-system/gotk-components.yaml
# This is auto-generated by bootstrap

# clusters/production/apps/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./apps/production
  prune: true
  validation: client
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: api-gateway
      namespace: production
  timeout: 5m

# clusters/production/infrastructure/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: infrastructure
  namespace: flux-system
spec:
  interval: 1h
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./infrastructure/production
  prune: true
  validation: client
  dependsOn:
    - name: cert-manager

Application Deployment with GitOps

# apps/base/api-service/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service
  labels:
    app: api-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-service
  template:
    metadata:
      labels:
        app: api-service
    spec:
      containers:
        - name: api
          image: myregistry.azurecr.io/api-service:v1.2.3
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
          env:
            - name: ASPNETCORE_ENVIRONMENT
              value: "Production"

# apps/base/api-service/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  - hpa.yaml

# apps/production/api-service/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
  - ../../base/api-service
patches:
  - patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
    target:
      kind: Deployment
      name: api-service
images:
  - name: myregistry.azurecr.io/api-service
    newTag: v1.2.3

Image Automation with Flux

# Automatically update images when new versions are available
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
  name: api-service
  namespace: flux-system
spec:
  image: myregistry.azurecr.io/api-service
  interval: 5m
  secretRef:
    name: acr-credentials\n\n## Takeaways\n\n*Add a concise, personal takeaway and recommended next steps here.*\n
Michael John Pena

Michael John Pena

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.