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.

Esamina 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 utilizza il nome quando si interagisce con l'oggetto, 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, le specifiche di distribuzione utilizzano il modello corrispondente 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
selector:
  matchLabels:
    app: wasliberty
template:
  metadata:
    labels:
      app: wasliberty

Affinità

Specificare l'affinità (colocazione) quando si desidera un maggiore controllo sui nodi di lavoro su cui vengono 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 relativa all’assegnazione dei pod ai nodi.

Anti - affinità richiesta
È possibile distribuire solo il numero di repliche corrispondente al numero di nodi di lavoro disponibili. 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 file YAML con l'opzione “required”, consulta l'app Liberty con anti-affinità dei pod “required ”.
Anti - affinità preferita
È possibile distribuire i pod sui nodi con capacità disponibile, il che garantisce 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 di lavoro al cluster, la regola di affinità non impone che i due pod aggiuntivi in esecuzione sui nodi esistenti vengano riprogrammati sul nodo disponibile.
Affinità nodo di lavoro
È possibile configurare la distribuzione in modo che venga eseguita solo su determinati nodi di lavoro, come ad esempio i nodi 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 un tag immagine, per impostazione predefinita viene caricata l'immagine contrassegnata 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:

  1. Passa alla regione del registro globale.
    ibmcloud cr region-set global
    
  2. 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 quota loro spettante di risorse di calcolo (memoria e CPU), creando un oggetto “ ResourceQuota per ogni namespace Kubernetes presente 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.

  1. Verifica se è impostata una quota della risorsa per uno spazio dei nomi.
    kubectl get quota --namespace=<namespace>
    
  2. 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 di risorsa che lo scheduler riserva al container affinché possa 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 container 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 relativa a “ 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à
Configurare un test di integrità per verificare se il container è 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à
Configurare un test di disponibilità per verificare se il container è 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. Impostare un'interrogazione di disponibilità con un ritardo iniziale è particolarmente importante se l'avvio dell'app richiede un po' di tempo. 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 relativa a “ 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à della tua app, puoi controllare il modo in cui essa reagisce alle interruzioni in base al tipo di disponibilità che desideri, utilizzando 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 rimanere disponibili dopo il verificarsi di un'interruzione.

maxUnavailable
È possibile specificare il numero o la percentuale di pod che possono risultare non disponibili a seguito di un'interruzione del servizio. L'esempio utilizza maxUnavailable: 1.
selector
Inserisci l'etichetta per selezionare l'insieme di pod a cui si applica l' 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 relativa a “ 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, il che rende il servizio accessibile solo all'interno del cluster ma non al di fuori di esso.
  • 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 è necessario distribuire hostNetwork pod per l'ascolto su porte specifiche o utilizzare un hostPort per esporre i pod dell'app su una porta specifica sul nodo di lavoro, utilizzare una porta 11000-11200 nell'intervallo. IBM Cloud Kubernetes Service indica l'intervallo 11000-11200 di porte sui nodi di lavoro a questo scopo, per evitare conflitti con le porte locali e altre porte utilizzate IBM Cloud Kubernetes Service da. 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 si verifica un evento imprevisto, come la rimozione del nodo di lavoro 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 Kubernetes sulle best practice.

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.

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.

Per la gestione centralizzata di tutti i tuoi segreti nei cluster e l'iniezione durante l'esecuzione 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. Una volta creato il PVC, il volume persistente e lo spazio di archiviazione fisico vengono creati automaticamente tramite 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. È inoltre possibile scaricare il file YAML dall'indirizzo 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