Mon second déploiement avec Kompose¶
Nous devons recréer un cluster k3d d'une maniere que le port interne 80 (ou le contrôleur ingress traefik écoute) soit exposé au système (en port 8180). Nous allons aussi configurer le cluster avec deux noeuds "worker" virtuels.
Puis changer de contexte et de namespace comme au début du tutoriel :
De Docker à k8s avec Kompose¶
Maintenant que nous avons vu toutes les étapes qui mènent au déploiement complet d'une application Jupyter, nous reprenons les 3 fichiers générés avec Kompose dans la partie du tutoriel sur Docker qui sont un déploiement, un service de type ClusterIP et un PersistentVolumeClaim. Nous les plaçons dans un répertoire à part afin de les appliquer ensemble :
Pour appliquer tous au cluster k8s :
Et vous devez obtenir :
NAME READY STATUS RESTARTS AGE
pod/jupyter-7b8bbbf8b7-brpjq 0/1 ErrImagePull 0 14s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/jupyter ClusterIP 10.43.169.51 <none> 8888/TCP 14s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/jupyter 0/1 1 0 14s
NAME DESIRED CURRENT READY AGE
replicaset.apps/jupyter-7b8bbbf8b7 1 1 0 14s
Il y a une erreur de type ErrImagePull
dans le pod cela est due au nom de l'image qu'il faut changer dans le yaml
(nous utilisons l'image de base de Jupyter).
...
spec:
containers:
- image: jupyter/base-notebook # <-- modifié
name: jupyter
env:
- name: JUPYTER_PORT
value: "8888"
...
!!! info Variable d'environnement
Il faut ajouter la variable d'environnement JUPYTER_PORT
pour que le conteneur fonctionne. L'initialisation standard y mettre mauvais valeur dans un cluster multi-node.
Ensuite réappliquer les manifests.
Comme lors du premier déploiement, il y a des ReplicaSet associé au déploiement. Il est possible de voir l'historique des updates de pod via :
k rollout history deployment jupyter
deployment.apps/jupyter
REVISION CHANGE-CAUSE
1 <none>
2 <none>
Volumes¶
Nous avons utilisé des nouveaux objets que sont les volumes/persistentVolumeClaim pour garder les données entre redémarrages de pod. Il est possible de les afficher avec :
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-8bb4749f-5b6c-45ab-b1a5-ef850ea39353 100Mi RWO Delete Bound tuto/jupyter-claim0 local-path <unset> 10s
Ce volume est créé à partir d'un PersistentVolumeClaim
qui déclare des caractéristiques d'un Volume
(il est possible de créer le volume manuellement, mais c'est peu pratique et n'est pas souvent utilisé)
k get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
jupyter-claim0 Bound pvc-901b9698-5ee0-47e6-b465-4bf5dabb448a 100Mi RWO local-path <unset> 16m
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
io.kompose.service: jupyter-claim0
name: jupyter-claim0
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
L'option clé de PVC
est StorageClass
(on utilise StorageClass
par défaut si manqué)
k get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 177m
Ici le seul SC
fournie avec k3d est de type local-path
. Les données de ces volumes sont stockés dans un dossier local de noeud. On peut avoir les SCs différents en fonction d'installation particulaire de k8s.
Autre option important est AccessMode
. Avec ReadWriteOnce
(RWO) le volume peut être attache à un seul noeud en mode read-write, ReadOnlyMany
(ROX) permet d'attacher le volume aux quelques noeuds en mode readonly et ReadWriteMany
(RWX) est utilisé si on a besoin d'écrire sur le volume à partir des différents noeuds en façon concourante.
Exposer l'application au port web¶
Avant, nous avons utilisé soit le service
du type NodePort
soit port-forwarding pour accéder l'application. Pour exposer l'application web (protocole HTTP/HTTPS) nous allons créer l'autre ressource k8s : Ingress
.
Créer le manifest :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jupyter
annotations:
ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jupyter
port:
number: 8888
Et l'appliquer :
Nous pouvons accéder à la définition d'ingress :
Name: jupyter
Labels: <none>
Namespace: tuto
Address: 172.19.0.2,172.19.0.3,172.19.0.4
Ingress Class: traefik
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
*
/ jupyter:8888 (10.42.2.6:8888)
Annotations: ingress.kubernetes.io/ssl-redirect: false
Events: <none>
L'application Jupyter est maintenant accessible :
Configuration d'application¶
apiVersion: v1
kind: ConfigMap
metadata:
name: initscript
data:
initpwd.py: |-
#!/usr/bin/env python
from jupyter_server.auth import passwd
import os
import json
if os.getenv('JUPYTER_PWD') is not None:
pwd = passwd(os.getenv('JUPYTER_PWD'))
cfg_dir = os.path.join(os.getenv('HOME'), '.jupyter')
os.makedirs(cfg_dir, exist_ok=True)
cfg_json = os.path.join(cfg_dir, 'jupyter_server_config.json')
with open(cfg_json, 'w') as fd:
json.dump({"IdentityProvider": {"hashed_password": pwd}}, fd)
apiVersion: v1
kind: Secret
metadata:
name: jupyter-pwd
type: Opaque
data:
password: dmVyeV9zZWN1cmVfanVweXRlcl9wd2Q=
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose --file compose.yaml convert
kompose.version: 1.35.0 (9532ceef3)
labels:
io.kompose.service: jupyter
name: jupyter
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: jupyter
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose --file compose.yaml convert
kompose.version: 1.35.0 (9532ceef3)
labels:
io.kompose.service: jupyter
spec:
containers:
- image: jupyter/base-notebook
name: jupyter
ports:
- containerPort: 8888
protocol: TCP
env:
- name: JUPYTER_PORT
value: "8888"
volumeMounts:
- mountPath: /home/jovyan/work/local
name: jupyter-claim0
- name: jupyter-confdir
mountPath: /home/jovyan/.jupyter
initContainers:
- image: jupyter/base-notebook
name: pwd-init
command:
- python
- /initpwd.py
volumeMounts:
- name: initscript
mountPath: /initpwd.py
subPath: initpwd.py
- name: jupyter-confdir
mountPath: /home/jovyan/.jupyter
env:
- name: JUPYTER_PWD
valueFrom:
secretKeyRef:
name: jupyter-pwd
key: password
restartPolicy: Always
volumes:
- name: jupyter-claim0
persistentVolumeClaim:
claimName: jupyter-claim0
- name: initscript
configMap:
name: initscript
- name: jupyter-confdir
emptyDir: {}