I blogg #10 i serien Min reise mot CCIE Automation tar jeg steget fra Docker Compose til Kubernetes. Med utgangspunkt i Nautix-plattformen viser jeg hvordan deler av løsningen kan migreres til et lokalt Kubernetes-oppsett ved hjelp av kind, og hvordan dette dekker kravene i CCIE Automation Blueprint 4.3 – fra deklarative utrullinger og secrets-håndtering til ingress, helsesjekker og livssyklusstyring.
![<span id="hs_cos_wrapper_name" class="hs_cos_wrapper hs_cos_wrapper_meta_field hs_cos_wrapper_type_text" style="" data-hs-cos-general-type="meta_field" data-hs-cos-type="text" >[Min reise mot CCIE Automation #10] Fra Docker Compose til Kubernetes</span>](https://sicra.no/hs-fs/hubfs/two_guys_working_on_a_computer.jpg?width=1024&height=576&name=two_guys_working_on_a_computer.jpg)
(Denne artikkelen var tidligere en del av Bluetree.no. Siden Sicra og Bluetree har slått seg sammen, er nå innhold fra Bluetree overført til Sicra.)
[Min reise mot CCIE Automation #10] Fra Docker Compose til Kubernetes er del av en serie som dokumenterer min reise mot CCIE Automation. I forrige innlegg fokuserte jeg på sikker kodepraksis i Python. I dette innlegget tar jeg neste steg ved å migrere deler av plattformen fra Docker Compose til Kubernetes.
Frem til nå har Nautix kjørt ved hjelp av Docker Compose. Det fungerte godt i de tidlige fasene, men for Blueprint 4.3 – Package and deploy a solution by using Kubernetes, var det på tide å ta neste steg.
I dette innlegget viser jeg hvordan jeg migrerte deler av Nautix til Kubernetes, kjørt lokalt på utviklingslaptopen min, og hvordan dette mapper direkte til CCIE Automation blueprinten.
Docker Compose er utmerket for lokal utvikling, men Kubernetes gir deg:
Deklarative utrullinger
Innebygd helsesjekk og selvreparasjon
Innebygd håndtering av secrets og konfigurasjon
Service discovery og lastbalansering
En konsistent operasjonell modell på tvers av miljøer
For lokal utvikling valgte jeg kind (Kubernetes IN Docker).
Hvorfor kind?
Lettvekts og rask
Kjører fullstendig inne i Docker
Perfekt for lab og eksperimentering
Bruker ekte Kubernetes-API-er og verktøy
Forutsetninger
Docker Desktop (med WSL2-integrasjon)
kubectl
kind
Jeg opprettet clustret med eksplisitte port-mappinger slik at ingress-controlleren kunne nås fra laptopen min:
# kind-nautix.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
Opprett clustret:
kind create cluster --name nautix --config kind-nautix.yaml
Installer ingress-nginx:
kubectl apply -f \
https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
På dette tidspunktet hadde jeg et fullt fungerende Kubernetes-cluster kjørende lokalt.
Først opprettet jeg et dedikert namespace:
apiVersion: v1
kind: Namespace
metadata:
name: nautix
kubectl apply -f k8s/00-namespace.yaml
kubectl config set-context --current --namespace=nautix
Namespaces er et enkelt, men kraftig konsept – de gir isolasjon og gjør det langt enklere å resonere rundt ressurser.
I Docker Compose havner secrets ofte i .env-filer.
I Kubernetes er secrets førsteklasses objekter.
I stedet for å commite secrets til Git, opprettet jeg dem imperativt ved hjelp av kubectl:
kubectl create secret generic nautix-secrets \
--from-literal=VAULT_DEV_ROOT_TOKEN_ID=root-token \
-n nautix
Dette oppretter et innebygd Kubernetes Secret som trygt kan refereres av Deployments uten å lagre sensitive verdier i kildekode.
Inventory-tjenesten er et stateless Flask API, noe som gjør den til en perfekt kandidat for en Deployment.
Inventory Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: inventory
namespace: nautix
spec:
replicas: 1
selector:
matchLabels:
app: inventory
template:
metadata:
labels:
app: inventory
spec:
containers:
- name: inventory
image: nautix-inventory:dev
ports:
- containerPort: 8000
readinessProbe:
httpGet:
path: /healthz
port: 8000
initialDelaySeconds: 3
livenessProbe:
httpGet:
path: /healthz
port: 8000
initialDelaySeconds: 10
Inventory Service
apiVersion: v1
kind: Service
metadata:
name: inventory
namespace: nautix
spec:
selector:
app: inventory
ports:
- port: 8000
type: ClusterIP
Deploymenten håndterer pod-livssyklus og replikaer, mens Service gir et stabilt nettverkspunkt internt i clustret.
Vault kjører i dev-modus i denne laben, men jeg ønsket likevel å demonstrere volumer og persistent lagring.
PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vault-data
namespace: nautix
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Vault Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault
namespace: nautix
spec:
replicas: 1
selector:
matchLabels:
app: vault
template:
metadata:
labels:
app: vault
spec:
containers:
- name: vault
image: vault:1.8.7
ports:
- containerPort: 8200
env:
- name: VAULT_DEV_LISTEN_ADDRESS
value: "0.0.0.0:8200"
- name: VAULT_DEV_ROOT_TOKEN_ID
valueFrom:
secretKeyRef:
name: nautix-secrets
key: VAULT_DEV_ROOT_TOKEN_ID
volumeMounts:
- name: data
mountPath: /vault/file
volumes:
- name: data
persistentVolumeClaim:
claimName: vault-data
Vault Service
apiVersion: v1
kind: Service
metadata:
name: vault
namespace: nautix
spec:
selector:
app: vault
ports:
- port: 8200
type: ClusterIP
For å eksponere tjenestene eksternt brukte jeg host-basert ruting via Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nautix
namespace: nautix
spec:
ingressClassName: nginx
rules:
- host: inventory.nautix.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: inventory
port:
number: 8000
- host: vault.nautix.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vault
port:
number: 8200
Med riktige host-oppføringer på laptopen min kunne jeg nå nå:
http://inventory.nautix.local
http://vault.nautix.local
Med alle manifestene på plass:
kubectl apply -f k8s/
Verifiser:
kubectl get pods
kubectl get svc
kubectl get ingress
På dette tidspunktet kjørte både Inventory og Vault fullt ut inne i Kubernetes.
Det er her Kubernetes virkelig skinner.
Skalering
kubectl scale deploy inventory --replicas=3
kubectl get pods -l app=inventory
Logger
kubectl logs deploy/inventory
kubectl logs -f deploy/inventory
Selvreparasjon
kubectl delete pod <inventory-pod>
Podden blir automatisk opprettet på nytt – ingen manuell inngripen nødvendig.
I stedet for Docker Compose depends_on bruker Kubernetes readiness- og liveness-probes.
Readiness styrer om trafikk sendes til en pod
Liveness styrer når en pod skal restartes
Dette gjør plattformen langt mer robust som standard.
Ved å migrere selv en liten del av Nautix til Kubernetes, klarte jeg å demonstrere alle kravene i Blueprint 4.3 ved hjelp av reelle arbeidslaster:
Deklarative utrullinger
Sikker håndtering av secrets
Persistent lagring
Ingress-ruting
Helsesjekker
Skalering og livssyklushåndtering
Full kontroll via kubectl
[Min reise mot CCIE Automation #1] Introduksjon + bygging av en Python CLI-applikasjon
[Min reise mot CCIE Automation #2] Inventory REST API og mikrotjenestearkitektur
[Min reise mot CCIE Automation #3] Orchestration API og NETCONF
[Min reise mot CCIE Automation #9] Anvendelse av OWASP Secure Coding Practices



