Red Hat Pipelines con Vite

  • Di
  • 2023-04-06 - 11 minuti
banner

Intro

OpenShift offre diverse opzioni per creare e distribuire applicazioni e tra questi ci sono:

  • templates: si tratta del modo più semplice per descrivere tutti gli oggetti necessari per rilasciare effettivamente un’applicazione in OpenShift (tipicamente, ma non esclusivamente, DeploymentConfig, Service, Route e forse BuildConfig) e avere il pieno controllo sulla loro configurazione;
  • Pipeline Jenkins: sì, puoi usare il buon vecchio Jenkins per implementare il tuo processo CI/CD su OpenShift.

In questo caso, vogliamo però realizzare una pipeline che permetta di utilizzare del codice sorgente disponibile su un repository qualsiasi e di avviare il deploy dell’applicazione all’interno del cluster in maniera del tutto automatica.

Per farlo, è sufficiente utilizzare le Red Hat OpenShift Pipelines, uno strumento potente tanto quanto complesso, che dietro le quinte utilizza Tekton, uno dei progetti open source più utilizzati in ambito CI/CD.

Andiamo per gradi: lo scenario per ripetere l’esperimento che verrà mostrato e che andremo a descrivere è abbastanza semplice:

  • Il codice sorgente per l’applicazione si trova in un repository GitHub pubblico (o privato);
  • L’immagine del container creata durante il processo di compilazione viene inviata a un registry di immagini (come Quay) pubblico (o privato);
  • Disponiamo di un cluster OpenShift attivo e funzionante da qualche parte, pronto per eseguire l’applicazione. Se non hai cluster a disposizione, puoi sempre utilizzare OpenShift Local!

Per il caso di esempio, useremo un’applicazione scritta in Vite.js (il repository è in fondo nelle risorse utili), ma è possibile utilizzare qualsiasi altro esempio che abbia un Dockerfile valido e utilizzabile su OpenShift!

Cos’è Tekton

Come descritto nel sito web del progetto open source Tekton:

Tekton è un framework open source potente e flessibile per la creazione di sistemi CI/CD, che consente agli sviluppatori di creare, testare e distribuire tra provider cloud e sistemi on-premise.

Red Hat OpenShift Pipelines è una soluzione CI/CD cloud-native completamente basata sulle risorse Kubernetes che utilizza i concetti e gli elementi costitutivi di Tekton per automatizzare le distribuzioni su più piattaforme astraendo i dettagli d’implementazione sottostanti. Tekton introduce una serie di Custom Resource Definitions (CRD) standard per la definizione di pipeline CI/CD portabili tra le distribuzioni Kubernetes.

Concetti

Concetti che definiscono la pipeline

  • Pipeline: la definizione della pipeline e le attività che dovrebbe svolgere
  • Task: un numero di passaggi riutilizzabile e liberamente accoppiato che esegue un’attività specifica (ad esempio, la creazione di un’immagine del container)

Concetti che gestiscono la pipeline

  • PipelineRun: l’esecuzione e il risultato dell’esecuzione di un’istanza di una pipeline, che include una serie di TaskRun
  • TaskRun: l’esecuzione e il risultato dell’esecuzione di un’istanza di un Task

Concetto per aiutare la pipeline a funzionare correttamente

  • PipelineResource: risorse di questo tipo servono a fornire gli strumenti per poter lavorare e che sono fondamentali lungo tutto il flusso di lavoro, per esempio, un repository Git o un’immagine da utilizzare.

Installazione dell’Operator

Prima ancora di pensare di utilizzare il framework OpenShift Pipelines, è necessario ovviamente installarlo nel
cluster; Red Hat fornisce un operatore per semplificare il processo di installazione.

Andiamo nella console Web di OpenShift e, attraverso la prospettiva amministrativa, seleziona OperatorsOperatorHub dal menu di navigazione a sinistra e quindi cerca l’operatore Red Hat Pipelines; a questo punto, fai clic sul riquadro e quindi sul successivo pulsante Installa.

Manteniamo le impostazioni predefinite e proseguiamo con l’installazione, attendiamo che sia completata e vedremo una nuova voce nel menù di sinistra della prospettiva Developer, relativa alle Pipelines:

Con questi pochi semplici passaggi abbiamo installato OpenShift Pipelines e ora possiamo procedere a utilizzare questo framework basato su Tekton per creare una pipeline per costruire e distribuire applicazioni, ma sii paziente: abbiamo ancora bisogno di un paio di passaggi preliminari per mettere le basi.

Configurazione di registry per le immagini e per il codice sorgente

L’immagine, una volta che sarà stata generata a partire dal codice sorgente, verrà pushata sia nel registry interno di OpenShift, sia su uno esterno, come forma di backup e/o rilascio: questo vuol dire che sarà necessario accedervi dal cluster con i giusti permessi di scrittura e lettura.

Nel caso di Quay.io, è sufficiente creare un account (gratuito), creare un repository e poi aggiungere un robot account ai permessi con i permessi di lettura e scrittura.

A questo punto, cliccando sull’account appena creato, selezioniamo la sezione relativa a Kubernetes e scarichiamo il Secret che contiene il pull secret che permette all’utente fittizio di collegarsi al repository e di operare:

Prima di eseguire le pipeline OpenShift, dobbiamo assicurarci che siano disponibili le autorizzazioni corrette per accedere al codice e al registry che ospita l’immagine, e il modo per farlo è configurare un ServiceAccount su OpenShift in modo appropriato e collegare i Secret contenenti le informazioni giuste.

Partiamo dal codice sorgente: se si trova su un repository pubblico, non dobbiamo compiere ulteriori passaggi, e possiamo proseguire. Se invece il repository è privato, possiamo creare un Secret che ci permetta di inserire le informazioni per accedere al codice con il seguente comando:

oc create secret generic <YOUR_GITHUB_SECRET_NAME> \
   --from-literal=username=<YOUR_GITHUB_USERNAME> \
   --from-literal=password=<YOUR_GITHUB_PERSONAL_ACCESS_TOKEN> \
   --type=kubernetes.io/basic-auth

#oc annotate secret <YOUR_GITHUB_SECRET_NAME> "tekton.dev/git-0=https://github.com"

Stesso discorso vale per il registry che ospiterà l’immagine: se il repository è pubblico, possiamo proseguire, altrimenti dovremo creare un Secret che ci permetta di inserire le informazioni per accedere in lettura e scrittura al registry con il seguente comando:

oc create secret docker-registry <YOUR_REGISTRY_SECRET_NAME> \
   --docker-server=<YOUR_REGISTRY_URL> \
   --docker-username=<YOUR_REGISTRY_USERNAME> \
   --docker-password=<YOUR_REGISTRY_PASSWORD>

Aggiungere i permessi

Proseguiamo con il ServiceAccount: quando installi l’operatore, viene creato un ServiceAccount chiamato pipeline per eseguire correttamente le attività e le pipeline di Tekton, quindi gli concederemo le autorizzazioni appropriate.

Aggiungiamo quindi i permessi che permetteranno al ServiceAccount pipeline di operare: con il primo comando, specifichiamo che questo “utente” potrà gestire e accedere a tutte le risorse e i container presenti nei nodi computazionali. Chiaramente, si tratta di un permesso piuttosto “ampio”, quindi dovrebbe essere utilizzato solo per attività seguite da chi amministra il cluster.

Il secondo comando permette di modificare le risorse nel progetto, ma solamente quelle applicative: non potrà quindi vedere o modificare i ruoli degli utenti o dei gruppi, e questo gli servirà per poter avviare Deployment, Service e molto altro.

Con il terzo e il quarto comando andiamo a far riferimento al Secret creato in precedenza relativo al robot account del registry e lo colleghiamo al Serviceaccount pipeline, così che abbia le informazioni per agire con quel repository.

oc adm policy add-scc-to-user privileged -z pipeline

oc adm policy add-role-to-user edit -z pipeline

oc secrets link pipeline <YOUR_REGISTRY_SECRET_NAME>

oc secrets link default <YOUR_REGISTRY_SECRET_NAME> --for pull

È necessario un ultimo passaggio preparatorio prima di poter effettivamente eseguire le pipeline OpenShift.

In Tekton ogni Task viene eseguito nel proprio container quindi, per consentire a diversi Task di condividere i dati (ad esempio: un’attività recupera il codice sorgente da un repository e un’altra attività crea l’applicazione dal codice sorgente), dobbiamo inserire un volume nell’equazione.

Andiamo quindi a definire una PVC utilizzando un oggetto simile a quello seguente, anche molto dipende dalla StorageClass e in generale dal provisioner utilizzato:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pipeline-build
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi

Definizione della pipeline

Dopo aver completato questi step di preparazione, ora possiamo creare la nostra pipeline. Puoi dare un’occhiata all’esempio nel file seguente: questo descrive quattro passaggi, eseguite in sequenza, che servono per ottenere il codice sorgente da un repository GitHub, creare un’immagine del container a partire da un Dockerfile, eseguirne il
push in un registry, avviare il deploy su OpenShift e infine esporre l’applicazione con una route per l’accesso esterno.

---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: vite-deploy-example
spec:
  workspaces:
    - name: source
  steps:
    - name: ls
      image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest
      workingDir: /workspace/source
      command: ["/bin/bash", "-c"]
      args:
        - |-
          echo Listing files
          pwd
          ls -la
          echo -----------------------------------          
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: vite-with-docker
spec:
  type: git
  params:
    - name: url
      value: https://github.com/serenasensini/Vite-with-Docker
    - name: revision
      value: master
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: vite-example-image
spec:
  type: image
  params:
    - name: url
      value: quay.io/ser_sensini/vite-example:0.0.1-build
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: vite-example
spec:
  params:
    - default: vite-example
      description: name of the application to deploy
      name: APP_NAME
      type: string
    - default: https://github.com/serenasensini/Vite-with-Docker
      description: url of the git repo for the code of deployment
      name: GIT_REPO
      type: string
    - default: main
      description: revision to be used from repo of the code for deployment
      name: GIT_REVISION
      type: string
    - default: quay.io/ser_sensini/vite-example:0.0.1-build
      description: image to be built from the code
      name: IMAGE
      type: string
    - default: '5173'
      description: Port on the container that the service should direct traffic to.
      name: SERVICE_PORT
      type: string
  tasks:
    - name: fetch-repository
      params:
        - name: url
          value: $(params.GIT_REPO)
        - name: revision
          value: $(params.GIT_REVISION)
        - name: subdirectory
          value: ''
        - name: deleteExisting
          value: 'true'
      taskRef:
        kind: ClusterTask
        name: git-clone
      workspaces:
        - name: output
          workspace: workspace
    - name: build-image
      params:
        - name: TLSVERIFY
          value: 'false'
        - name: IMAGE
          value: $(params.IMAGE)
      runAfter:
        - fetch-repository
      taskRef:
        kind: ClusterTask
        name: buildah
      workspaces:
        - name: source
          workspace: workspace
    - name: deploy
      params:
        - name: SCRIPT
          value: >-
            oc new-app --name=$(params.APP_NAME) --image=$(params.IMAGE)
            --as-deployment-config            
      runAfter:
        - build-image
      taskRef:
        kind: ClusterTask
        name: openshift-client
    - name: expose-route
      params:
        - name: SCRIPT
          value: oc expose service/$(params.APP_NAME) --port $(params.SERVICE_PORT)
      runAfter:
        - deploy
      taskRef:
        kind: ClusterTask
        name: openshift-client
  workspaces:
    - name: workspace

Scaviamo un po’ nell’anatomia della pipeline; all’inizio della sezione del codice, ci sono 2 aree:

  • workspace, che definisce un’area di lavoro che può essere associata a uno storage permanente una volta eseguita la pipeline;
  • params, dove sono definiti tutti i parametri che possono essere accettati dalla pipeline, con i loro valori di default, se presenti.
---
# ...
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: vite-example
spec:
  params:
    ...
  tasks:
    ...
  workspaces:
    - name: workspace

Analizziamo i Task uno a uno: quello chiamato fetch-repository, che puoi trovare nel codice sottostante, è responsabile della clonazione del repository del codice sorgente; ha i propri parametri, il cui significato dovrebbe essere abbastanza chiaro e una sezione workspaces che lega l’output alla cartella che è stato definita in precedenza tramite lo storage. Da notare inoltre che l’attività fa riferimento al clone git ClusterTask: i ClusterTask sono attività comunemente utili a livello di cluster implementate e rese disponibili da Red Hat OpenShift, in modo che tu possa semplicemente riutilizzarle senza reinventare la ruota.

...
tasks:
  - name: fetch-repository
    params:
      - name: url
        value: $(params.GIT_REPO)
      - name: revision
        value: $(params.GIT_REVISION)
      - name: subdirectory
        value: ''
      - name: deleteExisting
        value: 'true'
    taskRef:
      kind: ClusterTask
      name: git-clone
    workspaces:
      - name: output
        workspace: workspace
...

Il Task chiamato build-image è responsabile della creazione e del push dell’immagine nel registry, in base al valore del parametro $(params.IMAGE); nel caso di esempio questo è quay.io/ser_sensini/vite-example:0.0.1-build, quindi cercherà di eseguire il push al registry di Quay.io e creare un’immagine con il nome e il tag specificato.

Si basa su un ClusterTask che usa buildah, uno strumento molto simile a Podman, che costruisce l’immagine sulla base di un Dockerfile; notare la sezione runAfter che indica alla pipeline di eseguire l’attività solo dopo il completamento dell’attività fetch-repository.

...
tasks:
  - name: build-image
    params:
      - name: TLSVERIFY
        value: 'false'
      - name: IMAGE
        value: $(params.IMAGE)
    runAfter:
      - fetch-repository
    taskRef:
      kind: ClusterTask
      name: buildah
    workspaces:
      - name: source
        workspace: workspace
...

Il Task deploy è responsabile del rilascio e dell’avvio dell’immagine, creata nel passaggio precedente, in OpenShift. Si noti che è basato su un altro ClusterTask, openshift-client, che consente di utilizzare la riga di comando oc all’interno della pipeline; questo serve per permette l’utilizzo del comando oc new-app, che andrà a creare un Pod con il relativo controller DeploymentConfig a partire dall’immagine specificata.

tasks:
  - name: deploy
    params:
    - name: SCRIPT
      value: >-
        oc new-app --name=$(params.APP_NAME) --image=$(params.IMAGE)
        --as-deployment-config        
    runAfter:
      - build-image
    taskRef:
      kind: ClusterTask
      name: openshift-client

Infine l’attività expose-route è responsabile di creare una Route in OpenShift in modo che l’applicazione sia accessibile tramite browser con un protocollo HTTP.

tasks:
  - name: expose-route
    params:
    - name: SCRIPT
      value: oc expose service/$(params.APP_NAME) --port $(params.SERVICE_PORT)
    runAfter:
      - deploy
    taskRef:
      kind: ClusterTask
      name: openshift-client

Ora che abbiamo progettato e implementato una pipeline OpenShift, probabilmente vorremo utilizzarla, e possiamo farlo in un paio di modi: eseguire una pipeline tramite la riga di comando con la CLI di Tekton Pipelines o eseguire una pipeline utilizzando la GUI di OpenShift.

Rimaniamo sulla console di OpenShift, e selezioniamo la voce Pipelines dal menù di sinistra: una volta importato il file precedente, vedremo all’interno dell’elenco una nuova voce, relativa a quanto definito. Clicchiamo sul dettaglio della pipeline e clicchiamo su “Actions”, in alto a destra: questo ci permetterà di avviare la pipeline ed eseguire i diversi step previsti, che verranno mostrati con il relativo stato sul dettaglio di sinistra.

Avviando la pipeline, verrà creato un oggetto PipelineRun, che comportano una serie di TaskRun: ognuno di questi corrisponde a ogni Task descritto all’interno della Pipeline complessiva. Clicchiamo su “Start” tramite Actions e compiliamo i diversi campi, che corrispondono ai parametri definiti nella Pipeline: non ci dimentichiamo di associare al workspace il volume creato in precedenza, per permettere ai diversi Task di condividere i dati prodotti:

Per monitorare lo stato di esecuzione dei diversi Task, è possibile cliccare sui singoli riquadri di dettagli e vedere i log relativi ai singoli container.

Al termine dell’esecuzione, troveremo l’applicazione nella topologia del progetto, e potremo accedervi tramite la Route:

Questo naturalmente è un esempio molto semplice, ma può essere esteso ad altri casi d’uso, che coinvolgono Task più complessi, piuttosto che un’applicazione diversa o un tipo di risorse differenti; questo articolo vuole essere solo una panoramica di cosa fare per utilizzare il framework OpenShift Pipelines per costruire una pipeline!

Risorse utili

Post correlati

Partners

Community, aziende e persone che supportano attivamente il blog

Logo di Codemotion
Logo di GrUSP
Logo di Python Milano
Logo di Schrodinger Hat
Logo di Python Biella Group
Logo di Fuzzy Brains
Logo di Django Girls
Logo di Improove
Logo del libro open source
Logo di NgRome

Iscriviti alla newsletter

Per non perderti gli ultimi articoli e per vincere biglietti e gadget TheRedCode

Riceverai una volta al mese (o anche meno) gli articoli più interessanti pubblicati sul blog, e potrai provare a vincere un biglietto per uno dei prossimi eventi!

Andiamo!