Selon la documentation, dans Kubernetes, les ressources ConfigMap « vous permettent de découpler les artefacts de configuration du contenu des images afin de préserver la portabilité des applications conteneurisées. » Utilisées avec les pods Kubernetes, les configmaps peuvent être utilisées pour ajouter ou modifier dynamiquement les fichiers utilisés par les conteneurs.
Dans le cadre d’un programme d’installation de Kubernetes, notre équipe souhaitait déployer un serveur de fichiers léger sur le cluster Kubernetes afin de gérer les demandes d’entrée par défaut (chemin d’accès racine). De plus, nous avons pensé qu’il serait agréable de pouvoir modifier les fichiers index.html et CSS sans avoir à redéployer l’application.
Pour résoudre ce cas d’utilisation, nous avons décidé de construire une application Golang qui mapperait une partie de son système de fichiers à une ressource configmap de Kubernetes.
package mainimport (
“log”
“net/http”
)
func main()
{
fs := http.FileServer(http.Dir(“html”))
http.Handle(“/”, fs)log.Println(“Listening…”)
http.ListenAndServe(“:8080”, nil)
}
L’image du conteneur d’application est construite avec le Dockerfile ci-dessous. Il s’agit d’un fichier Docker en deux étapes qui effectue d’abord la construction de Golang dans un conteneur Alpine, puis copie le binaire compilé et le répertoire html vide dans une image finale basée sur le scratch.
# build stage
FROM golang:alpine AS builder
WORKDIR /usr/local/go/src
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# final stage
FROM scratch
WORKDIR /
COPY --from=builder /usr/local/go/src/main main
COPY html html
EXPOSE 8080
ENTRYPOINT ["/main"]
L’utilisation de conteneurs scratch avec des applications Golang est une méthode plus sûre et plus légère pour déployer des conteneurs Golang.
J’utilise make pour automatiser les opérations Docker. Ci-dessous se trouve le Makefile pour cette application.
VERSION ?= 0.0.1
NAME ?= "ingress-default"
AUTHOR ?= "Jimmy Ray"
PORT_EXT ?= 8080
PORT_INT ?= 8080
NO_CACHE ?= true
.PHONY: build run stop clean
build:
docker build -f scratch.dockerfile . -t $(NAME)\:$(VERSION) --no-cache=$(NO_CACHE)
run:
docker run --name $(NAME) -d -p $(PORT_EXT):$(PORT_INT) $(NAME)\:$(VERSION) && docker ps -a --format "{{.ID}}\t{{.Names}}"|grep $(NAME)
stop:
docker rm $$(docker stop $$(docker ps -a -q --filter "ancestor=$(NAME):$(VERSION)" --format="{{.ID}}"))
clean:
@rm -f main
DEFAULT: build
L’utilisation de make supprime la variabilité entre les tâches répétitives. Avec le Makefile ci-dessus, je peux construire et exécuter mon application dans Docker, avant de déployer l’application testée dans Kubernetes.
Pour cette solution, nous devons configurer un espace de noms, une carte de configuration, un déploiement, un service et un ingress Kubernetes. Pour ce faire, nous utilisons la méthode kubectl apply -f. Il s’agit d’un moyen déclaratif d’appliquer des changements aux ressources du cluster Kubernetes.
Voici le fichier YAML des ressources Kubernetes que nous allons modifier.
apiVersion: v1
kind: Namespace
metadata:
name: ingress-default
labels:
app: ingress-default
---
kind: ConfigMap
apiVersion: v1
metadata:
name: ingress-default-static-files
namespace: ingress-default
labels:
app: ingress-default
data:
index.html: |
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Cluster Ingress Index</title>
<link rel="stylesheet" href="main.css">
</head>
<body>
<table class="class1">
<tr>
<td class="class2">Kubernetes Platform</td>
</tr>
<tr>
<td class="class1">
<table class="class3">
<tr><td><h1>Cluster Ingress Index</h1></td></tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="class3">
<tr>
<td>
<h2>The following are links to this cluster's ingress resources:</h2>
</td>
</tr>
<tr>
<td class="class4">
<a href="https://<ROOT_INGRESS_PATH>" target="_blank">Root Ingress</a><br/>
<a href="https://<OTHER_INGRESS_PATH>" target="_blank">Other Ingress</a><br/>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
main.css: |
body {
background-color: rgb(224,224,224);
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 100%;
}
.class1 {
...
}
.class2 {
...
}
.class3 {
...
}
.class4 {
...
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ingress-default
name: ingress-default
namespace: ingress-default
spec:
selector:
matchLabels:
app: ingress-default
replicas: 1
template:
metadata:
labels:
app: ingress-default
name: ingress-default
spec:
containers:
- name: ingress-default
image: <IMAGE_REGISTRY_REPO_TAG>
imagePullPolicy: Always
resources:
limits:
cpu: 100m
memory: 10Mi
requests:
cpu: 100m
memory: 10Mi
volumeMounts:
- readOnly: true
mountPath: html
name: html-files
volumes:
- name: html-files
configMap:
name: ingress-default-static-files
---
kind: Service
apiVersion: v1
metadata:
name: ingress-default
namespace: ingress-default
labels:
app: ingress-default
spec:
selector:
app: ingress-default
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: default-ingress
namespace: ingress-default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.class: "nginx"
labels:
app: ingress-default
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: ingress-default
servicePort: 80
Comme vous pouvez le voir dans le YAML, le configmap ingress-default-static-files contient le contenu des fichiers index.html et main.css. En éditant ou en remplaçant cette configmap, nous pouvons changer ces fichiers servis par l’application Golang file server.
Dans le monde de Docker et Kubernetes, les volumes sont utilisés pour résoudre deux problèmes :
Pour notre solution, nous mappons les volumes dans nos conteneurs déployés à la ressource configmap. Dans l’extrait ci-dessous, le volume html-files est configuré pour être éventuellement utilisé par tous les conteneurs dans le pod. Le volume correspond aux données configurées dans la configmap ingress-default-static-files.
...volumes:
- name: html-files
configMap:
name: ingress-default-static-files...
Une fois le volume configuré au niveau du pod, nous configurons un montage de volume au niveau du conteneur. Ce montage de volume correspond au volume html-files, configuré dans le pod. Grâce à ce mappage, le conteneur d’application aura désormais accès aux deux fichiers de la carte de configuration - html/index.html et html/main.css.
...volumeMounts:
- readOnly: true
mountPath: html
name: html-files
Lorsque l’application Golang est lancée dans le cluster Kubernetes, la règle ingress-default entraîne la configuration d’une règle amont dans le contrôleur ingress NGINX. Le chemin résultant connectera le bord du cluster, via le contrôleur NGINX ingress, au service ingress-default. Ce service pointe vers le pod app du serveur de fichiers Golang. Lorsqu’il est exécuté, il sert l’application web par défaut sur le chemin racine du contrôleur ingress. S’il est nécessaire de modifier cette page web, il suffit de modifier/remplacer la ressource configmap.
L’un des principaux avantages de l’orchestration des conteneurs est qu’elle promet de supprimer le « travail lourd et indifférencié » qui serait nécessaire pour approvisionner et gérer plusieurs charges de travail conteneurisées. L’efficacité et la rapidité des déploiements d’applications et des changements d’état des clusters sont améliorées par l’utilisation des fonctions de configuration déclarative de Kubernetes, comme ConfigMap. En utilisant les ressources ConfigMap comme des volumes montés, avec des conteneurs en cours d’exécution, la configuration et le contenu peuvent être abstraits des conteneurs, ce qui réduit la nécessité de remanier les images et de redéployer les conteneurs.