IBM Cloud Docs
Sviluppo di applicazioni

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
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:

  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 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.

  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 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 ClusterIPche 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.

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 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