Checklist¶
[[TOC]]
Dieser Leitfaden soll dabei unterstützen, einen eigenen Service mithilfe von ohMyHelm bereitzustellen.
Welche Services kann ich mit diesem Leitfaden bereitstellen?
- Pullen eigener Container aus einer Privaten Container Registry
- Einfache webservices mit/ohne persistenz mit/ohne Ingress
- Hinzufügen von Konfigurationen (Als Secrets / Configs)
- Bereitstellen der Secrets / Configs als Datei innerhalb des Pods
- Migrationen von Datenbanken mithilfe von Jobs/initContainer
- "Verstecken" der Anwendung in ein "Sidecar" mit vorgeschaltetem nginx. TODO
Bevor es losgeht¶
Erstelle einen Ordner checklist
Erstelle folgende dateien im Ordner checklist
:
Chart.yaml
values.yaml
Füge folgenden Inhalt in die Chart.yaml
apiVersion: v2
name: checklist
description: My first checklist
type: application
version: 0.1.0
appVersion: "1.16.0"
dependencies:
- name: ohmyhelm
alias: myservice
repository: https://gitlab.com/api/v4/projects/28993678/packages/helm/stable
version: 1.17.0
condition: myservice.enabled
Basics¶
enable the chart function¶
Füge folgenden Codeabschnitt in die values.yaml
damit die chart
funktion für unseren erster Service myservice
aktiviert wird.
myservice:
enabled: true
chart:
enabled: true
In den nächsten Schritten werden wir weitere Codeabschnitte zu unserer values.yaml
hinzufügen und unseren Service Stück für Stück aufbauen.
Welche art von Service möchtest du installieren (Deployment, Statefulset or Daemonset)?¶
Um die Entscheidung für den Anfang etwas zu erleichtern, hier ein paar Hinweise. HINWEIS: Deployments können auch mit Persistenzen ausgestattet werden. Wir möchten aber hier nicht zu tief ins Detail gehen, da es sonst den Rahmen dieses Leitfadens sprengen würde. Diese Liste dient lediglich als Orientierung.
- Deployment (Keine Persistenz, kann auf 1 bis N nodes laufen)
- Statefulset (Mit Persistenz, läuft auf einer node)
- Daemonset (Keine Persistenz, läuft einmal auf allen nodes)
myservice:
chart:
# Folgende auswahl ist möglich, wobei nur ein Eintrag benötigt wird. Siehe nächsten Code Abschnitt.
deployment: true
statefulset: false
daemonset: false
myservice:
chart:
# ...
statefulset: true
Wie soll dein Service heißen?¶
myservice:
chart:
# ...
fullnameOverride: "nodered"
Benötigt dein Service einen Dynamischen Port Range?¶
myservice:
chart:
# Das ist der Standardwert. Diesen Eintrag benötigen wir nur wenn wir ihn auf `true`setzen.
hostNetwork: false
Image Pullen¶
Um aus einer privaten Container Registry zu pullen, müssen in Kubernetes spezielle Secrets angelegt werden. Hierfür können wir den imageCredentials
helper verwenden.
# `imageCredentials` kann mehrere Container Registry Secrets anlegen und wird daher nur einmal in der values.yaml benötigt.
# Wenn ein Secret in einem anderen Namespace benötigt wird, muss man dies explizit angeben, Ansonsten werden die Secrets in dem Namepsace installiert, den man mit `helm upgrade --install -n "mychecklist"` angibt.
imageCredentials:
- name: my-container-registry
registry: https://mydockerregistry.example.com
username: "myname"
accessToken: "mytoken"
- name: my-second-container-registry
# Folgendes Secret wird im `dev` Namespace angelegt. Wenn es diesen Namespace nicht gibt, schlägt das Deployment fehl. Siehe `HELPER` namespaces.
namespace: dev
registry: https://my2dockerregistry.example.com
username: "myname"
accessToken: "mytoken"
Die image Secrets können nun im Service hinterlegt werden
myservice:
chart:
imagePullSecrets:
- name: my-container-registry
Aktiviere Prometheus Metriken für diesen Service¶
myservice:
chart:
# this will add an applabel with the servicename for prometheus
applabel: true
# the name in `chart.fullnameOverride` and `monitoring[0].name must match`
monitoring:
- enabled: true
name: nodered
release: prometheus
endpoints:
- port: http
interval: 15s
path: merics
scheme: http
container¶
Dies ist die Standardkonfiguration eines containers in ohMyHelm
myservice:
chart:
# ...
container:
# Add your container image here
image: mydockerregistry.example.com/mycusomservice:latest
# ohMyHelm default values. Remove unnecessary parts if you doesn't need them.
# When disabling ports: you should also diable the service. (See "## Service")
# When changing or adding new entries to ports (name, protocoll), remember to add them to your service too. (See "## Service")
ports:
- name: http
containerPort: 80
protocol: TCP
imageConfig:
pullPolicy: IfNotPresent
securityContext: {}
command: []
args: []
env: []
extraEnv: []
envFrom: []
Im nächsten Beispiel deaktivieren wir die Ports, da unser container nur ausgehend kommuniziert. Wir benötigen aber Environments, welche in configMaps und Secrets hinterlegt sind.
myservice:
chart:
# ...
container:
# Add your container image here
image: mydockerregistry.example.com/mycusomservice:latest
# Disable ports
ports: []
env:
- name: foo
value: "bar"
# Load a single environment from an config map. See next code block.
- name: DEBUGLEVEL
valueFrom:
configMapKeyRef:
name: anotherconfig
key: debuglevel
# Load a single secret to your environment. See next code block.
- name: SECRET
valueFrom:
secretKeyRef:
name: allsecrets
key: myservicesecret
# Load multiple environments from an config map. See next code block.
envFrom:
- configMapRef:
name: specialconfig
ConfigMaps und Secrets anlegen¶
Im vorherigen Beispiel referenzieren wir in den Environments auf ein Secret und eine ConfigMap. Diese können wir wie folgt anlegen.
HINWEIS: Wir können Secret und ConfigMaps auch als Dateien innerhalb eines Containers bereitstellen. Dieser Teil wird später im Leitfaden erwähnt.
myservice:
chart:
# Config CHART helper
configs:
- name: specialconfig
values:
BAR: foo
TEST: "1"
- name: anotherconfig
values:
debuglevel: WARN
config.yaml: |
number: "1"
foo:
bar:
- 1
- 2
- 3
# Secret CHART helper
secrets:
- name: allsecrets
values:
myservicesecret: "admin;-)"
mytoken: "******"
secret.file: |
somesensitive content.
FOO:BAR 1,2,3
liveness and readiness probe¶
Standardmäßig sind livenessProbe
und readinessProbe
nicht gesetzt.
Die konfiguration erfolgt wie in den Kubernetes docs beschrieben wird.
Beispiele:
myservice:
chart:
# ...
container:
# ...
livenessProbe:
initialDelaySeconds: 120
failureThreshold: 3
periodSeconds: 10
timeoutSeconds: 3
httpGet:
path: /healthz
port: http
myservice:
chart:
# ...
container:
# ...
readinessProbe:
initialDelaySeconds: 120
failureThreshold: 3
periodSeconds: 10
timeoutSeconds: 3
httpGet:
path: /healthz
port: http
Service¶
Dies ist das Kubernetes Service
Objekt.
Wenn Anpassungen an chart.container.port
vorgenommen wurden, muss targetPort
entsprechend angepasst werden.
myservice:
chart:
# ...
service:
# This is the default configuration. You don't need to add them.
type: ClusterIP
#clusterIP: 1.2.3.4
#selector:
# app.kubernetes.io/name: MyApp
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
Die Erstellung des Service
kann wie folgt deaktiviert werden
myservice:
chart:
# ...
service: []
initContainer¶
Dies ist die Standard Konfiguration des initContainer
.
Der initContainer
ist Standardmäßig deaktiviert und nutzt eine wait-for-job image ohmyhelm-job-helper
.
Anwendungsbereich für diesen initContainer
Datenbankmigration
Wird ein job
verwendet um eine Datenbankmigration durchzuführen, wartet der initContainer
solange bis der job
einen exitstatus 0
ausgibt. Anschließend beendet sich der initContainer und der eigentliche container im Pod startet.
Um dieses Feature zu nutzen, muss deer Wert yourjobnamehere
mit dem Namen in chart.fullnameOverride
ersetzt werden und job
und rbac
aktiviert werden. rbac
sorgt dafür, dass der initContainer
auf die Informationen des job
zugreifen darf.
Einschränkungen initContainer
:
- Man kann keinen Port hinzufügen
- Man kann keine Volumes mounten
myservice:
chart:
# ...
initContainer:
enabled: false
# Add your container image here
image: registry.gitlab.com/ayedocloudsolutions/ohmyhelm-job-helper:1.0.0
imageConfig:
pullPolicy: IfNotPresent
securityContext: {}
command: []
args:
- "job"
- "yourjobnamehere"
env: []
extraEnv: []
envFrom: []
job (container)¶
Dies ist die Standard Konfiguration des job
.
Der job
ist Standardmäßig deaktiviert. Wenn keine Image gesetzt wurde, wird die image aus chart.container.image
geladen. Mit args
können wir dem Container Parameter mitgeben. Bspw.: args: ['miration']
.
rbac
muss hinzugefügt werden, wenn derohmyhelm-job-helper
iminitContainer
verwendet wird.
Jobs werden normalerweise nicht gelöscht un bleiben bestehen. Dieses verhalten kann durch setzen von true
bei chart.job.removejob.enabled
angepasst werden.
myservice:
chart:
# ...
job:
enabled: false
# Add your container image here. If no image is set `chart.container.image` will be used.
#image:
imageConfig:
pullPolicy: IfNotPresent
securityContext: {}
command: []
args: []
env: []
extraEnv: []
envFrom: []
restartPolicy: Never
removejob:
enabled: false
ttlSecondsAfterFinished: 60
activeDeadlineSeconds: 1200
backoffLimit: 20
Role based access control¶
Dies ist die Standard Konfiguration des rbac
.
rbac
ist Standardmäßig deaktiviert.
myservice:
chart:
# ...
rbac:
enabled: false
roleRules:
- apiGroups: ["", "batch"]
resources: ["*"]
verbs: ["*"]
Create an ingress¶
Es gibt zwei arten von ingress
in ohMyHelm
ingress
Konfiguration des ingress abspec:
see Kubernetes docs ingressingressSimple
Kompakte Konfigurtion mit einem host und einem tls eintrag.
ingress¶
myservice:
chart:
# ...
ingress:
enabled: false
annotations: {}
tls:
- hosts:
- my.example.com
secretName: example-tls
# You can add multiple hosts / paths
hosts:
- host: my.example.com
http:
paths:
- path: /
# pathType Supported Kubernetes >= v1.19 [stable]
pathType: Prefix
backend:
serviceName: your-service-name-here
servicePort: http
simpleIngress¶
myservice:
chart:
# ...
ingressSimple:
enabled: false
annotations: {}
host: my.example.com
tlsSecretName: example-tls
pathType: Prefix
path: /
Beispiel mit annotations:
myservice:
chart:
# ...
ingressSimple:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: "letsencrypt-staging"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/configuration-snippet: |
rewrite ^(/myservice)$ $1/ redirect;
nginx.ingress.kubernetes.io/x-forwarded-prefix: "/myservice"
host: my.example.com
tlsSecretName: example-tls
pathType: Prefix
path: /myservice(/|$)(.*)
Persistenz einrichten¶
In einem Zukunftigen release wird dieser Part optimiert. Es wird lediglich eine
persistance
geben.
Je nach Auswahl des Typs unter chart.deployment
, chart.statefulset
oder chart.daemonset
muss das entsprechende *Volume
ausgewählt werden.
statefulsetVolume:
deploymentVolume:
(PVC (=PersistentVolumeClaim) muss seperat angelegt werden)daemonsetVolume:
(PVC muss seperat angelegt werden)- (Noch nicht implementiert. Fasst
*Volume
zusammen)persistance:
Die Konfiguration erfolgt unter:
*Volume.volumeClaimTemplates:
Sihe Kubernetes Docs*Volume.volumeMounts:
und*Volume.volumes:
Siehe Kubernetes Docs
statefulsetVolume¶
myservice:
chart:
# ...
statefulsetVolume:
volumeClaimTemplates: []
volumeMounts: []
volumes: []
Eine PersistentVolumeClaim hinzufügen:
myservice:
chart:
# ...
statefulsetVolume:
volumeClaimTemplates:
- metadata:
name: "database-data"
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
volumeMounts:
- name: database-data
mountPath: /app/database
subPath:
deploymentVolume und daemonsetVolume¶
Bei deploymentVolume
und daemonsetVolume
legt Kubernetes Standardmäßig keine PVC an. Hier muss mit dem manifests
helper diese Claim manuell erzeugt werden.
myservice:
chart:
# ...
daemonsetVolume:
volumeMounts: []
volumes: []
# Use mainfests helper to create the pvc
manifests: []
PVC für ein Deployment anlegen¶
myservice:
chart:
# ...
deploymentVolume:
volumeMounts:
- name: filesystem-data
mountPath: /app/filesystem
subPath:
volumes: []
- name: filesystem-data
persistentVolumeClaim:
claimName: filesystem-data-deployment
# Use mainfests helper to create the pvc
manifests:
- kind: PersistentVolumeClaim
apiVersion: v1
content:
metadata:
name: filesystem-data-deployment
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: filesystem-data-deployment
persistance (Nich implementiert)¶
Stadardmäßig wird bei einem statefulset
eine PVC
angelegt. Ein deployment
und ein daemonset
haben diese Funktion nicht.
Mit persistance:
kann man eine PVC
zu einem deployment
, daemonset
oder statefulset
hinzufügen.
Die Konfiguration erfolgt unter:
volumeClaimTemplates:
Siehe Kubernetes DocsvolumeMounts:
undvolumes:
Siehe Kubernetes Docs
Standard Konfiguration¶
myservice:
chart:
# ...
persistance:
volumeClaimTemplates: []
volumeMounts: []
volumes: []
Beispiel: daemon.json im container mit einer Configmap überschreiben¶
myservice:
chart:
# ...
persistance:
volumeClaimTemplates: []
volumeMounts:
- name: daemon-json
mountPath: /etc/docker/daemon.json
subPath: daemon.json
volumes:
- name: daemon-json
configMap:
name: daemon-json
configs:
- name: daemon-json
values:
daemon.json: |
{
"mtu": 1350
}
Beispiel: PVC hnzufügen¶
myservice:
chart:
# ...
persistance:
volumeClaimTemplates:
- metadata:
name: "database-data"
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
volumeMounts:
- name: database-data
mountPath: /app/database
subPath:
volumes: []
TODO replica, serviceaccount, etc¶
Dieser Abschnitt ist nich in Arbeit.
myservice:
chart:
# ...
replicaCount: 1
podAnnotations: {}
podSecurityContext: {}
serviceAccount:
create: true
annotations: {}
name: ""
resources: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
Das erstellte Chart installieren¶
helm dep update
helm upgrade --install -n "mychecklist" --create-namespace my-checklist .
Beispiele¶
Ein einfacher Service¶
- Pod und Service lauschen auf Port 80 (ohMyHelm Default)
- Wir benötigen ein Service Objekt (ohMyHelm Default)
- Registry ist öffentlich. (ohMyHelm Default)
Chart.yaml
apiVersion: v2
name: my-application
description: my simple apllication
type: application
version: 1.2.2
appVersion: "1.3.5"
dependencies:
- name: ohmyhelm
alias: myservice
repository: https://gitlab.com/api/v4/projects/28993678/packages/helm/stable
version: 1.15.2
condition: myservice.enabled
values.yaml
myservice:
enabled: true
chart:
enabled: true
deployment: true
fullnameOverride: "myservice"
container:
image: my-public-registry/my-image:1.3.5
Ein Komplexer Service¶
Unser Deployment benötigt ein volume mit "ReadWriteMany". Ein vorhandener CSI unterstütz dies aber nicht. Daher vewenden wir als CSI nfs-subdir-external-provisioner
und installieren einen nfs-server
ins Cluster.
WARNUNG: Dieses Szenario ist nicht für den Produktivbetrieb geeiget und dient lediglich als demonstration. Ein NFS Server sollte nicht im Cluster betrieben werden und die NFS Mounts sollten manuell und nicht durch
nfs-subdir-external-provisioner
erfolgen.
myservice:
- Pod und Service lauschen auf Port 80 (ohMyHelm Default)
- Registry ist NICHT öffentlich. imageCredentails müssen ngelegt werden( Siehe dependency
secretsconfigs
) - Wir benötigen eine Zentrale configMap for unsere environments (Siehe dependency
secretsconfigs
) - Wir benötigen volumes und eine PVC für unser Deployment. Volumes müssen in der lage sein
readWriteMany
operationen durchführen zu können, da wir einenreplicaCount
von 10 benötgien (Siehe dependencymyservice
)
nfs:
- we need multiple ports
securityContext
set toprivileged: true
- We need volumes and PVC for our statefulset
Chart.yaml
apiVersion: v2
name: my-application
description: myapplication needs ReadWriteMany
type: application
version: 1.2.3
appVersion: "1.3.5"
dependencies:
- name: secretsconfigs
alias: myservice
repository: https://gitlab.com/api/v4/projects/28993678/packages/helm/stable
version: 1.15.2
condition: secretsconfigs.enabled
- name: ohmyhelm
alias: myservice
repository: https://gitlab.com/api/v4/projects/28993678/packages/helm/stable
version: 1.15.2
condition: myservice.enabled
- name: ohmyhelm
alias: nfs
repository: https://gitlab.com/api/v4/projects/28993678/packages/helm/stable
version: 1.15.2
condition: nfs.enabled
- name: nfs-subdir-external-provisioner
repository: https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
version: 4.0.16
values.yaml
secretsconfigs:
imageCredentials:
- name: my-container-registry
registry: https://mydockerregistry.example.com
username: "myname"
accessToken: "mytoken"
# # ohMyHelm config Helper for a single config
# config:
# enabled: true
# name: myspecialconfig
# values:
# ENV: prod
# DEBUG: info
# START_WITH: "2022"
# # ohMyHelm config Helper for multiple configs
configs:
- name: myspecialconfig
values:
ENV: prod
DEBUG: info
START_WITH: "2022"
myservice:
enabled: true
chart:
enabled: true
deployment: true
replicaCount: 10
fullnameOverride: "myservice"
imagePullSecrets:
- name: my-registry
container:
image: my-registry/my-image:1.3.5
envFrom:
- configMapRef:
name: myspecialconfig
deploymentVolume:
volumeMounts:
- name: nfs-volume
mountPath: /data
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: data-service-nfs
# # ohMyHelm Chart config helper for multiple configs
# configs:
# - name: myspecialconfig
# values:
# ENV: prod
# DEBUG: info
# START_WITH: "2022"
# We need to add an PVC for the Deployment
manifests:
- kind: PersistentVolumeClaim
apiVersion: v1
content:
metadata:
name: data-service-nfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Gi
storageClassName: nfs-client
nfs:
enabled: true
chart:
enabled: true
statefulset: true
fullnameOverride: "nfs"
container:
image: k8s.gcr.io/volume-nfs:0.8
securityContext:
privileged: true
args:
- /app/data
ports:
- name: nfs
containerPort: 2049
- name: mountd
containerPort: 20048
- name: rpcbind
containerPort: 111
service:
type: ClusterIP
clusterIP: 10.43.249.55
ports:
- port: 2049
name: nfs
targetPort: nfs
- port: 20048
targetPort: mountd
name: mountd
- port: 111
targetPort: rpcbind
name: rpcbind
statefulsetVolume:
volumeMounts:
- name: data-nfs
mountPath: /app/data
volumeClaimTemplates:
- metadata:
name: "data-nfs"
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Gi
nfs-subdir-external-provisioner:
storageClass:
name: nfs-client
nfs:
server: 10.43.249.55
path: "/"
mountOptions:
- nolock
- nfsvers=4.2