Repository di Immagini in Locale
Scenario
- Sviluppiamo un applicativo
- Lo inseriamo in un'immagine
- Manteniamo l'immagine in un registry locale
- Lanciamo un pod che carica l'immagine dal registry locale
Quando si crea un pod, l’immagine del suo container è normalmente scaricata del registry del Docker Hub.
Vogliamo creare un’immagine locale, per motivi di confidenzialità, indipendenza ed economia.
Il registry locale deve essere visibile al cluster kubernetes.
- Servirà un cluster che lo permetta, tramite un opportuno plugin
- Occorrerà configurazione del cluster:
- nella costruzione
- a runtime
Sviluppo di Applicativo
Il programma di applicativo demo è un semplicissimo web server, scritto in linguaggio Go.
Ascoterà sulla porta 8888
e, quando contattato, invierà la stringa Hello, 世界
- Ciao Mondo in qualche lingua orientale.
Scaffolding
Prepariamo la locazione in cui sviluppare l'applicativo:
mkdir -p ~/demo/hello
cd ~/demo/hello
Programma
Si chiama main.go
:
vim main.go
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, 世界")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Running demo app. Press Ctrl+C to exit...")
log.Fatal(http.ListenAndServe(":8888", nil))
}
Inserimento Applicativo in Immagine
Non è necessario installare il software del compilatore Go. Si può far compilare il programma ad un contenitore, che in seguito viene scartato.
Usiamo un build a due fasi, con un Chained Dockerfile;
- viene compilato l'eseguibile
- l'eseguibile è inserito in un'immagine vuota
Il Dockerfile
è:
vim Dockerfile
FROM golang:1.17-alpine AS build
WORKDIR /src/
COPY main.go go.* /src/
ENV GO111MODULE="off"
RUN CGO_ENABLED=0 go build -o /bin/demo
FROM scratch
COPY --from=build /bin/demo /bin/demo
EXPOSE 8888
ENTRYPOINT ["/bin/demo"]
Ogni eseguibile compilato staticamente in Go include un mini sistema operativo che fornisce le funzionalità di scheduling e gestione della memoria.
Per questo nella seconda fase del build non c'è bisogno di partire da un sistema operativo di base e si può usare l'immagine di base
scratch
(vuota).
E' la direttiva di compilazione
CGO_ENABLED=0
che disabilita il link con le librerie dinamiche di sistema e produce un eseguibile linkato staticamente.
I linguaggio Go ha molti notevoli vantaggi quando si vuole produrre software da inserire in immagini Docker.
Build dell'Immagine
Si costruisce l'immagine myhello:
docker build -t myhello .
Verificarne l'avvenuta creazione con:
docker images
Nota
Il messaggio del comando docker build
avverte che build
è deprecato e verrà presto sostituito da buildx
.
Questo è un plugin di docker.
Per installarlo:
curl -L "https://github.com/docker/buildx/releases/download/v0.6.3/buildx-v0.6.3.linux-amd64" -o docker-buildx
mkdir -p ~/.docker/cli-plugins
mv docker-buildx ~/.docker/cli-plugins
chmod +x ~/.docker/cli-plugins/docker-buildx
docker buildx version
Il comando di build diventa allora:
docker buildx build -t myhello .
Registry Locale
Docker Hub offre l'immagine registry per un registry locale.
Occorre quindi un piccolo refactoring del progetto.
Refactoring del Progetto
Occorrono le seguenti attività:
- istanziare un contenitore docker per il registry
- creare il cluster con una opportuna patch che gli consenta di vedere il registry
- agganciare il registry alla rete del cluster
- creare un ConfigMap che documenti la presenza del registry
Il tutto è convenientemente raccolto in una procedura shell, copiata dalla documentazione del sito Kind, e leggermente modificata per il nostro progetto.
vim ~/registry.sh
#!/bin/sh
set -o errexit
# crea il contenitore del registry se non esiste
reg_name='kind-registry'
reg_port='5000'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
docker run \
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" \
-v $PWD/registry:/var/lib/registry registry:2
fi
# crea un cluster con il registry locale abilitato in containerd
cat <<EOF | kind create cluster --image kindest/node:v1.24.0 --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_name}:5000"]
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
# connette il registry alla rete del cluster se non connesso
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
docker network connect "kind" "${reg_name}"
fi
# Documenta il local registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF
Renderla eseguibile e lanciarla:
chmod +x ~/registry.sh
~/registry.sh
Check del cluster:
kubectl cluster-info --context kind-try
kubectl get nodes
Nostra immagine nel Registry Locale
Ora che abbiamo un registry locale, vi inseriamo l'immagine che abbiamo preparato, myhello
.
Il riferimento al registry che contiene un'immagine è parte del nome dell'immagine. Aggiungiamo quindi un altro nome:
docker tag myhello:latest localhost:5000/myhello:latest
Ora possiamo compiere il push dell'immagine al registry locale:
docker push localhost:5000/myhello:latest
Creazione di Pod
Un manifest ~/scripts/localdemo.yml
che compie il pull di un’immagine dal registry locale:
vim ~/scripts/localdemo.yml
kind: Pod
apiVersion: v1
metadata:
name: myhello-pod
labels:
zone: prod
version: v1
spec:
containers:
- name: myhello-ctr
image: localhost:5000/myhello
ports:
- containerPort: 8888
Creare il pod:
kubectl apply -f ~/scripts/localdemo.yml
Check, dopo un opportuno tempo di creazione:
kubectl get pods
kubectl get events
Il control-plane decide quale nodo avrà il pod. L’immagine del pod container è presa dal registry locale.