Una din tehnicile de securitate recomandate pentru best practice in kubernetes este imutabilitatea containerelor. Prin imutabilitate se înțelege că un container nu va fi modificat pe tot timpul existenței sale: nu vor putea fi aplicate update-uri, patch-uri și nici nu va putea fi schimbată configurația sa.
Imutabilitatea face un deployment mai sigur și mai ușor de reprodus. Dacă este nevoie de un roll back, este foarte simplu să refacem deploymentul cu imaginea veche. Această abordare ne permite să lansăm aceeași imagine de container în mai multe medii de instalare, făcându-le cât mai identice posibil.
Containerele fiind imutabile, pentru actualizarea unei configurații este necesară lansarea unui nou container cu aceeași imagine, dar cu configurația actualizată.
În exemplele de mai jos am folosit clusterul de kubernetes instalat cu k3s în Oracle Cloud pe procesoare ARM.
Containere și poduri imutabile
Pentru a face un container imutabil trebuie ca sistemul său de fișiere să fie montat read-only (doar pentru citire).
Pașii pe care îi vom urma pentru a crea și testa imutabilitatea containerelor:
- vom crea un pod numit king cu 2 containere, c1 și c2, folosind imaginea bash:latest (trebuie să ne asigurăm că cele 2 containere vor rula)
- vom face imutabil containerul c2 al podului king
- vom face un deployment numit queen cu 3 replici folosind imaginea nginx:1.19.6
- vom face imutabil containerul deploymentului queen și vom remedia eventualele erori care apar (nginx are nevoie de niște căi în care să poată să scrie)
- imutabilitatea containerelor - verificare
1. Crearea unui pod numit king cu 2 containere, c1 și c2, folosind imaginea bash:latest
Vom crea definiția podului, apoi o vom edita conform cerințelor:
kubectl run king --image bash:latest --dry-run=client -oyaml > king.yaml
vim king.yaml
Fișierul va arăta ca mai jos:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: king
name: king
spec:
containers:
- image: bash:latest
name: c1
command: ["sh", "-c", "echo The app is running! && sleep 7200"]
resources: {}
- image: bash:latest
name: c2
command: ["sh", "-c", "echo The app is running! && sleep 7200"]
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Rulăm comanda kubectl
apply -f pe definiția podului și vom avea:
kubectl apply -f king.yaml
kubectl get po
$ kubectl get po
NAME READY STATUS RESTARTS AGE
king 2/2 Running 0 8s
2. Crearea unui container imutabil
Editam fișierul king.yaml și îi vom adăuga proprietatea readOnlyRootFilesystem în secțiunea securityContext. Fișierul final va arăta astfel:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: king
name: king
spec:
containers:
- image: bash:latest
name: c1
command: ["sh", "-c", "echo The app is running! && sleep 7200"]
resources: {}
- image: bash:latest
name: c2
command: ["sh", "-c", "echo The app is running! && sleep 7200"]
resources: {}
securityContext:
readOnlyRootFilesystem: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Ștergem podul vechi și replicăm noua definiție:
kubectl delete po king --force --grace-period=0
kubectl apply -f king.yaml
kubectl get po
3. Crearea unui deployment numit queen cu 3 replici folosind imaginea nginx:1.19.6
Vom crea deploymentul și-l vom exporta într-un fișier (vom avea nevoie să-l edităm), după care vom aplica fișierul manifest și vom vedea cele 3 replici rulând:
kubectl create deployment queen --image=nginx:1.19.6 --replicas=3 --dry-run=client -oyaml > queen.yaml
kubectl apply -f queen.yaml
kubectl get deploy
kubectl get po
$ kg po
NAME READY STATUS RESTARTS AGE
httpbin-5fc54dbfbc-vwnp5 1/1 Running 0 6d1h
king 2/2 Running 0 63m
queen-7fcff5b4d9-gh2c5 1/1 Running 0 58m
queen-7fcff5b4d9-78tcl 1/1 Running 0 58m
queen-7fcff5b4d9-h5k2d 1/1 Running 0 58m
4. Imutabilitatea containerului deploymentului queen
Vom aplica aceeași proprietate readOnlyRootFilesystem secțiunii securityContext, apoi vom reaplica deploymentul. Vom observa că noile poduri nu rulează. Verificăm logurile:
kubectl logs queen-7fcff5b4d9-h5k2d
Erorile ne arată că, din cauză că sistemul de fișiere este protejat la scriere, aplicația nginx nu va reuși să scrie în locurile în care dorește. Erorile vor fi explicite și au forma:
mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)open() "/var/run/nginx.pid" failed (30: Read-only file system)
nginx: [emerg] open() "/var/run/nginx.pid" failed (30: Read-only file system)
Vom crea câte un volum de tip emptyDir în cele două locații cerute de aplicația nginx, după care vom reaplica fișierul queen.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: queen
name: queen
spec:
replicas: 3
selector:
matchLabels:
app: queen
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: queen
spec:
containers:
- image: nginx:1.19.6
name: nginx
securityContext:
readOnlyRootFilesystem: true
resources: {}
volumeMounts:
- mountPath: /var/cache/nginx
name: cache-nginx
- mountPath: /var/run
name: run-nginx
volumes:
- name: cache-nginx
emptyDir: {}
- name: run-nginx
emptyDir: {}
status: {}
Observăm acum că podurile deploymentului queen rulează fără erori.
5. Verificare
Imutabilitatea containerelor e simplu de verificat: vom încerca să scriem câte un fișier test în fiecare director /tmp din cele 2 containere ale podului king și în câteva locații din containerul deploymentului queen:
kubectl exec king -c c1 -- touch /tmp/test ---> funcționează, căci filesystemul containerului c1 nu e read-only
kubectl exec king -c c2 -- touch /tmp/test ---> eroare, căci filesystemul containerului c2 e read-only
kubectl get deploy queen ---> vom observa cele 3 replici ale deploymentului
kubectl exec queen-7fcff5b4d9-78tcl -- touch /tmp/test ---> eroare
k exec queen-7fcff5b4d9-78tcl -- touch /var/cache/nginx/test ---> funcționează
Demonstrație:
Concluzii
Combinația dintre imutabilitate și statelessness (un container/pod nu-și păstrează starea de la o sesiune la alta, datele persistente de orice fel nu sunt stocate în interiorul containerului pentru a fi folosite de către sesiunea următoare, ci sunt stocate în afara sa) reprezintă punctul forte al infrastructurii bazată pe containere. Această combinație ne permite să automatizăm lansarea aplicațiilor și să creștem frecvența și fiabilitatea lansărilor.
Lasă un răspuns