Sviluppo di applicazioni
Sviluppa una configurazione per distribuire il carico di lavoro della tua applicazione su IBM Cloud® Kubernetes Service. Poiché Kubernetes è una piattaforma di orchestrazione dei contenitori estensibile che non richiede un linguaggio o un'applicazione specifici, puoi eseguire vari carichi di lavoro come le applicazioni senza stato, con stato e di elaborazione dati che vengono scritte nel linguaggio di tua scelta.
Specifica dei requisiti della tua applicazione nel file YAML
In Kubernetes, descrivi la tua applicazione in un file YAML che dichiara la configurazione dell'oggetto Kubernetes. Il server API Kubernetes elabora quindi il file YAML e memorizza la configurazione e lo stato richiesto dell'oggetto nell'archivio dati etcd. Il programma di pianificazione (scheduler) Kubernetes pianifica i tuoi carichi di lavoro sui nodi di lavoro all'interno del cluster, tenendo conto delle specifiche nel tuo file YAML, di eventuali politiche del cluster impostate dall'amministratore e della capacità disponibile del cluster.
Esaminare una copia del file YAML completo. Quindi, rivedi le seguenti sezioni per comprendere come poter migliorare la distribuzione della tua applicazione.
Vuoi ulteriori informazioni su come gli oggetti Kubernetes lavorano insieme per la tua distribuzione? Consulta Descrizione degli oggetti Kubernetes per le applicazioni.
Metadati di distribuzione di base
Utilizza la versione API appropriata per il tipo di oggetto Kubernetes che distribuisci. La versione dell'API determina le funzioni supportate per l'oggetto Kubernetes a tua
disposizione. Il nome che fornisci nei metadati è il nome dell'oggetto, non la sua etichetta. Si usa il nome quando si interagisce con l'oggetto, come ad esempio kubectl get deployment <name>
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
Serie di repliche
Per aumentare la disponibilità della tua applicazione, puoi specificare una serie di repliche nella tua distribuzione. In una serie di repliche, definisci il numero di istanze dell'applicazione che vuoi distribuire. Le serie di repliche vengono gestite e monitorate dalla tua distribuzione di Kubernetes. Se un'istanza dell'applicazione si interrompe, Kubernetes avvia automaticamente una nuova istanza per mantenere il numero specificato di istanze dell'applicazione.
spec:
replicas: 3
Etichette
Con le etichette, puoi contrassegnare diversi tipi di risorse del tuo cluster con la stessa coppia key: value
. Quindi, puoi specificare il selettore da
abbinare all'etichetta in modo che tu possa costruire su queste altre risorse. Se prevedi di esporre pubblicamente la tua applicazione, devi utilizzare un'etichetta che corrisponda al selettore specificato nel servizio. Nell'esempio, la
specifica di distribuzione utilizza il template che corrisponde all'etichetta app: wasliberty.
Puoi richiamare gli oggetti etichettati nel tuo cluster, ad esempio per vedere i componenti di staging
o production
. Ad esempio, elenca tutte le risorse con un'etichetta env: production
su tutti gli spazi
nomi presenti nel cluster. Nota: devi accedere a tutti gli spazi dei nomi per eseguire questo comando.
kubectl get all -l env=production --all-namespaces
- Per ulteriori informazioni sulle etichette, consultare la documentazione Kubernetes.
- Applica etichette ai nodi di lavoro.
- Per un esempio più dettagliato, vedi Distribuzione delle applicazioni a specifici nodi di lavoro utilizzando le etichette.
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
Affinità
Specificare l'affinità (colocazione) quando si desidera un maggiore controllo sui nodi worker su cui sono pianificati i pod. L'affinità interessa i pod solo al momento della pianificazione. Ad esempio, per diffondere la distribuzione tra i
nodi di lavoro anziché consentire ai pod di pianificare sullo stesso nodo, utilizza l'opzione podAntiAffinity
con i tuoi cluster standard. Puoi definire due tipi di anti-affinità pod: preferito o richiesto.
Per ulteriori informazioni, consultare la documentazione di Kubernetes sull'assegnazione dei Pod ai nodi.
- Anti - affinità richiesta
- È possibile distribuire solo il numero di repliche per cui si dispone di nodi worker. Ad esempio, se hai tre nodi di lavoro nel tuo cluster, ma definisci cinque repliche nel tuo file YAML, verranno distribuite solo tre repliche. Ciascuna replica è attiva su un nodo di lavoro diverso. Le due repliche rimanenti resteranno in sospeso. Se aggiungi un nodo di lavoro al tuo cluster, una delle repliche rimanenti viene distribuita automaticamente al nuovo nodo di lavoro. Se un nodo di lavoro non riesce, il pod non viene ripianificato perché è richiesta la politica di affinità. Per un esempio di YAML con required, vedere Liberty app con required pod anti-affinity.
- Anti - affinità preferita
- È possibile distribuire i pod sui nodi con capacità disponibile, il che offre una maggiore flessibilità per il carico di lavoro. Quando possibile, i pod vengono pianificati su diversi nodi di lavoro. Ad esempio, se hai tre nodi di lavoro con una capacità sufficiente nel tuo cluster, è possibile pianificare i cinque pod di replica tra i nodi. Tuttavia, se si aggiungono altri due nodi worker al cluster, la regola di affinità non obbliga i due pod aggiuntivi in esecuzione sui nodi esistenti a riprogrammare sul nodo disponibile.
- Affinità nodo di lavoro
- È possibile configurare l'installazione client in modo che venga eseguita solo su alcuni nodi worker, ad esempio bare metal. Per ulteriori informazioni, vedi Distribuzione delle applicazioni a specifici nodi di lavoro utilizzando le etichette.
Esempio di anti - affinità preferita
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
Immagine contenitore
Specifica l'immagine che vuoi utilizzare per i tuoi contenitori, l'ubicazione dell'immagine e la politica di estrazione dell'immagine. Se non si specifica il tag dell'immagine, per impostazione predefinita viene prelevata l'immagine con il
tag latest
.
Evita di utilizzare la tag latest per i carichi di lavoro di produzione. Potresti non aver verificato il tuo carico di lavoro con l'immagine più recente se utilizzi un repository pubblico o condiviso, ad esempio Docker Hub o IBM Cloud Container Registry.
Ad esempio, per elencare le tag di immagini IBM pubbliche:
- Passa alla regione del registro globale.
ibmcloud cr region-set global
- Elenca le immagini IBM.
ibmcloud cr images --include-ibm
L'impostazione predefinita di imagePullPolicy
è impostata su IfNotPresent
, che estrae l'immagine solo se non esiste localmente. Se vuoi che l'immagine venga estratta ogni volta che si avvia il contenitore, specifica
imagePullPolicy: Always
.
containers:
- name: wasliberty
image: icr.io/ibm/liberty:webProfile8
imagePullPolicy: Always
Porta per il servizio dell'applicazione
Seleziona una porta del contenitore su cui aprire i servizi dell'applicazione. Per vedere quale porta deve essere aperta, fai riferimento alle specifiche dell'applicazione o al Dockerfile. La porta è accessibile dalla rete privata, ma non
da una connessione di rete pubblica. Per esporre pubblicamente l'applicazione, devi creare un servizio NodePort, programma di bilanciamento del carico o Ingress. Utilizzi questo stesso numero di porta quando crei un oggetto Service
.
La porta 25 è bloccata per tutti i servizi in IBM Cloud.
ports:
- containerPort: 9080
Richieste e limiti di risorse
Gli amministratori del cluster si assicurano che i team che condividono un cluster non occupino più della loro giusta quota di risorse di calcolo (memoria e CPU) creando un oggetto ResourceQuota
per ogni namespace Kubernetes nel cluster. Se l'amministratore del cluster imposta una quota di risorse di calcolo, ogni contenitore all'interno del template di distribuzione
deve specificare le richieste e i limiti di risorse per la memoria e la CPU, altrimenti la creazione del pod non riesce.
- Verifica se è impostata una quota della risorsa per uno spazio dei nomi.
kubectl get quota --namespace=<namespace>
- Vedi quali sono i limiti di quota.
kubectl describe quota <quota_name> --namespace=<namespace>
Anche se non è impostata alcuna quota di risorsa, puoi includere richieste e limiti di risorse nella tua distribuzione per migliorare la gestione delle risorse del nodo di lavoro.
Se un contenitore supera il suo limite, potrebbe essere riavviato o avere esito negativo. Se un contenitore supera una richiesta, il suo pod potrebbe essere rimosso se il nodo di lavoro esaurisce quella risorsa che viene superata. Per ulteriori informazioni sulla risoluzione dei problemi, vedi Continui errori di riavvio o rimozione imprevista dei pod.
- Richiesta
- La quantità minima della risorsa che lo schedulatore riserva al contenitore per utilizzarla. Se la quantità è uguale al limite, la risorsa viene garantita. Se la quantità è inferiore al limite, la richiesta è comunque garantita, ma il programma di pianificazione può utilizzare la differenza tra la richiesta e il limite per soddisfare le risorse di altri contenitori.
- Limite
- La quantità massima di risorsa che il contenitore può consumare. Se la quantità totale di risorse che viene utilizzata tra i contenitori supera la quantità disponibile sul nodo di lavoro, è possibile rimuovere i contenitori per liberare spazio. Per evitare la rimozione, imposta la richiesta di risorse su un valore uguale al limite del contenitore. Se non è specificato alcun limite, il valore predefinito è la capacità del nodo di lavoro.
Per ulteriori informazioni, consultare la documentazione Kubernetes.
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
Analisi di attività e disponibilità
Per impostazione predefinita, Kubernetes invia il traffico ai pod della tua applicazione dopo l'avvio di tutti i contenitori nel pod e riavvia i contenitori quando si bloccano. Tuttavia, puoi impostare i controlli dell'integrità per migliorare la stabilità dell'instradamento del traffico di servizio.
Ad esempio, la tua applicazione potrebbe avere un ritardo di avvio. I processi dell'applicazione potrebbero iniziare prima che l'intera applicazione sia completamente pronta, il che può influire sulle risposte, soprattutto quando si esegue il ridimensionamento su più istanze. Con i controlli di integrità, puoi far sapere al tuo sistema se l'applicazione è in esecuzione e pronta a ricevere richieste. Impostando queste analisi, puoi anche aiutare ad evitare tempi di inattività quando esegui un aggiornamento continuo della tua applicazione. Puoi impostare due tipi di controlli dell'integrità: analisi di attività e di disponibilità.
- Probe di attività
- Impostare una sonda di liveness per verificare se il contenitore è in esecuzione. Se l'analisi ha esito negativo, il contenitore viene riavviato. Se il contenitore non specifica un'analisi di attività, l'analisi ha esito positivo perché presume che il contenitore sia attivo quando si trova nello stato Running.
- Probe di disponibilità
- Impostare una sonda di prontezza per verificare se il contenitore è pronto a ricevere richieste e traffico esterno. Se l'analisi non riesce, l'indirizzo IP del pod viene rimosso come indirizzo IP utilizzabile per i servizi che corrispondono al pod, ma il contenitore non viene riavviato. L'impostazione di una sonda di prontezza con un ritardo iniziale è particolarmente importante se la vostra applicazione richiede un po' di tempo per avviarsi. Prima del ritardo iniziale, l'analisi non si avvia, dando al contenitore il tempo di iniziare. Se il contenitore non fornisce un'analisi di disponibilità, l'analisi ha esito positivo perché presume che il contenitore sia attivo quando si trova nello stato Running.
Puoi configurare le analisi come comandi, richieste HTTP o socket TCP. Nell'esempio vengono utilizzate le richieste HTTP. Dai all'analisi di attività più tempo rispetto all'analisi di disponibilità. Per ulteriori informazioni, consultare la documentazione Kubernetes.
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
PDB (Pod Disruption Budget)
Per aumentare la disponibilità dell'applicazione, è possibile controllare il modo in cui l'applicazione reagisce alle interruzioni in base al tipo di disponibilità desiderato con un oggetto PodDisruptionBudget
.
Un PDB (pod disruption budget) può aiutarti a pianificare la modalità di funzionamento della tua applicazione durante le interruzioni volontarie, ad esempio quando inizi un riavvio diretto aggiornando la distribuzione dell'applicazione, o
durante le interruzioni involontarie, come un errore grave (panic) del kernel.
minAvailable
: È possibile specificare il numero o la percentuale di pod che devono essere ancora disponibili dopo un'interruzione.
maxUnavailable
- È possibile specificare il numero o la percentuale di pod che possono essere non disponibili dopo un'interruzione. L'esempio utilizza
maxUnavailable: 1
. selector
- Immettere l'etichetta per selezionare il set di pod a cui si applica
PodDisruptionBudget
. Nota che se hai utilizzato la stessa etichetta in altre distribuzioni di pod, il pod si applica anche a tali distribuzioni.
Per ulteriori informazioni, consultare la documentazione Kubernetes.
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
Esposizione del servizio dell'applicazione
Puoi creare un servizio che espone la tua applicazione. Nella sezione spec
, assicurati di far corrispondere i valori di port
e di etichetta con quelli che hai utilizzato nella distribuzione. Il servizio espone gli
oggetti che corrispondono all'etichetta, come app: wasliberty
nel seguente esempio.
- Per impostazione predefinita, un servizio utilizza
ClusterIP
che rende il servizio accessibile solo all'interno del cluster, ma non all'esterno. - Puoi creare un servizio NodePort, programma di bilanciamento del carico o Ingress per esporre pubblicamente l'applicazione. Questi servizi hanno due IP, uno esterno e uno interno. Quando il traffico viene ricevuto sull'IP esterno, viene inoltrato all'IP del cluster interno. Quindi, dall'IP del cluster interno, il traffico viene instradato all'IP del contenitore dell'applicazione.
- L'esempio utilizza
NodePort
per esporre il servizio all'esterno del cluster. Per ulteriori informazioni su come configurare l'accesso esterno, vedi Scelta di un servizio NodePort, LoadBalancer o Ingress.
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
Se hai un requisito per distribuire i pod hostNetwork
per ascoltare su porte specifiche o per utilizzare un hostPort
per esporre i tuoi pod dell'applicazione su una porta specifica sul nodo di lavoro, utilizza una
porta nell'intervallo 11000-11200
. IBM Cloud Kubernetes Service designa l'intervallo di porte 11000-11200
sui nodi di lavoro per questo scopo, per evitare conflitti con le porte locali e altre porte utilizzate da
IBM Cloud Kubernetes Service. Poiché i pod hostNetwork
e hostPorts
fanno riferimento a un particolare indirizzo IP del nodo di lavoro, i pod sono limitati alla sola esecuzione su tale nodo di lavoro. Se accade qualcosa
di imprevisto, come la rimozione del nodo worker o l'esaurimento delle risorse, il pod non può essere riprogrammato. Se vuoi esporre una porta del pod sul nodo di lavoro, prendi in considerazione di utilizzare invece un servizio NodePort
.
Per ulteriori informazioni, consultare la documentazione sulle best practice di Kubernetes.
Mappe di configurazione per le variabili di ambiente del contenitore
Le mappe di configurazione forniscono informazioni di configurazione non sensibili per i carichi di lavoro della tua distribuzione.
Il seguente esempio mostra come puoi fare riferimento ai valori della tua mappa di configurazione come variabili di ambiente nella sezione delle specifiche del contenitore del tuo file YAML di distribuzione. Facendo riferimento ai valori della tua mappa di configurazione, puoi disaccoppiare queste informazioni di configurazione dalla distribuzione per mantenere portatile la tua applicazione inserita in un contenitore.
- Come decidere se utilizzare un oggetto Kubernetes
ConfigMap
oSecret
per le variabili. - Per ulteriori modi di utilizzare configmap, consulta la documentazione Kubernetes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
...
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
Segreti per le variabili di ambiente del contenitore
I segreti forniscono informazioni di configurazione sensibili come, ad esempio, le password per i carichi di lavoro della tua distribuzione.
Il seguente esempio mostra come puoi fare riferimento ai valori del segreto come variabili di ambiente nella sezione delle specifiche del contenitore del tuo file YAML di distribuzione. Puoi anche montare il segreto come volume. Facendo riferimento ai valori del tuo segreto, puoi disaccoppiare queste informazioni di configurazione dalla distribuzione per mantenere portatile la tua applicazione inserita in un contenitore.
- Come decidere se utilizzare un oggetto Kubernetes
ConfigMap
oSecret
per le variabili. - Per creare un segreto, consulta la documentazione Kubernetes.
Per la gestione centralizzata di tutti i segreti tra i cluster e l'injection al runtime dell'applicazione, prova IBM Cloud Secrets Manager.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
...
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
Volumi persistenti per l'archiviazione del contenitore
I volumi persistenti (PV) si interfacciano con l'archiviazione fisica per fornire un'archiviazione dati persistente per i carichi di lavoro del tuo contenitore.
Il seguente esempio mostra come poter aggiungere l'archiviazione persistente alla tua applicazione. Per eseguire il provisioning dell'archiviazione persistente, devi creare un'attestazione del volume persistente (PVC, persistent volume claim)
per descrivere il tipo e la dimensione dell'archiviazione file che vuoi avere. Dopo aver creato il PVC, il volume persistente e lo storage fisico vengono creati automaticamente utilizzando il provisioning dinamico. Facendo riferimento alla
PVC nel file YAML di distribuzione, l'archiviazione viene montata automaticamente sul pod della tua applicazione. Quando il contenitore nel tuo pod scrive i dati nella directory del percorso di montaggio /test
, i dati vengono
memorizzati nell'istanza di archiviazione file NFS. Per le opzioni sugli altri tipi di archiviazione di cui puoi eseguire il provisioning, vedi Pianificazione di archiviazione persistente altamente disponibile.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi
Esempio di YAML di distribuzione completo
Il seguente esempio è una copia dello YAML di distribuzione discusso precedentemente sezione per sezione. Si può anche scaricare lo YAML da GitHub.
Per applicare lo YAML,
kubectl apply -f file.yaml [-n <namespace>]
YAML di esempio
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
containers:
- name: wasliberty
image: icr.io/ibm/liberty:latest
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
ports:
- containerPort: 9080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
---
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi