Moderne applicaties op OpenShift, deel 2: chained builds

Hallo allemaal! Dit is de tweede post in onze serie waarin we laten zien hoe je moderne webapplicaties op Red Hat OpenShift kunt implementeren.

Moderne applicaties op OpenShift, deel 2: chained builds

In de vorige post hebben we kort de mogelijkheden van de nieuwe S2I (source-to-image) builder-afbeelding aangeraakt, die bedoeld is voor het bouwen en implementeren van moderne webapplicaties op het OpenShift-platform. Toen lag de focus op het snel implementeren van een applicatie, en vandaag bekijken we hoe we een S2I-afbeelding kunnen gebruiken als een 'schone' builder-afbeelding en deze kunnen combineren met gerelateerde builds op OpenShift.

Schone builder-afbeelding

Zoals we in het eerste deel hebben vermeld, hebben de meeste moderne webapplicaties een zogenaamde bouwfase, waarin meestal operaties zoals code-transpilatie, het samenvoegen van meerdere bestanden en minificatie plaatsvinden. De bestanden die uit deze operaties voortkomen – dat zijn statische HTML, JavaScript en CSS – worden in de output-map opgeslagen. De locatie van deze map hangt meestal af van de gebruikte build-tools, en voor React is dit de map .\/build (hierover komen we later meer terug).

Source-to-Image (S2I)

In deze post behandelen we helemaal niet het onderwerp 'wat is S2I en hoe gebruik je het' (hierover kun je meer lezen hier), maar het is belangrijk om een goed begrip te hebben van de twee fasen van dit proces, om te begrijpen wat de Web App Builder-afbeelding doet.

Assemblagefase

De assemblagefase lijkt in wezen sterk op wat er gebeurt wanneer je docker build uitvoert en uiteindelijk een nieuwe Docker-afbeelding ontvangt. Deze fase treedt op wanneer je een build op het OpenShift-platform start.

In het geval van de Web App Builder-afbeelding is het assemble script. standaard gebruikt de builder-afbeelding de constructie npm run build, maar deze kan worden overschreven via de omgevingsvariabele NPM_BUILD.

Zoals eerder vermeld, hangt de locatie van de kant-en-klare, samengevoegde applicatie af van welke tools worden gebruikt. In het geval van React bevindt dit zich in de map ./build, en voor Angular-applicaties is dat de map project_name/dist. En zoals in de vorige post al werd aangetoond, kan de locatie van de output-map, die standaard is ingesteld als build, worden overschreven via de omgevingsvariabele OUTPUT_DIR. Aangezien de locatie van de output-map verschilt van framework tot framework, hoeft u alleen de gegenereerde output te kopiëren naar de standaardmap in de afbeelding, namelijk naar /opt/apt-root/output. Dit is belangrijk voor het begrijpen van het vervolg van dit artikel, maar laten we nu snel de volgende fase bekijken – de uitvoeringsfase (run phase).

Uitvoeringsfase (run phase)

Deze fase komt in beeld wanneer voor een nieuwe afbeelding, die is gemaakt tijdens de assemblagefase, een docker run-oproep wordt gedaan. Dit gebeurt ook bij het implementeren op het OpenShift-platform. Standaard run script gebruikt serve module voor het serveren van statische inhoud, die in de hierboven genoemde standaard output-map ligt.

Deze methode is goed voor een snelle implementatie van applicaties, maar in feite is het niet aan te raden om statische inhoud op deze manier te serveren. Aangezien we daadwerkelijk alleen statische inhoud serveren, is de geïnstalleerde Node.js binnen onze afbeelding niet nodig – een webserver is voldoende.

Met andere woorden, tijdens de assemblage hebben we één ding nodig, tijdens de uitvoering iets anders. In zo'n situatie zijn gekoppelde builds (chained builds) handig.

Gekoppelde builds (chained builds)

Dit is wat erover wordt gezegd in gekoppelde builds in de OpenShift-documentatie:

«Twee builds kunnen aan elkaar worden gekoppeld, waarbij de ene een gecompileerde entiteit genereert en de andere deze entiteit in een aparte afbeelding plaatst, die wordt gebruikt om deze entiteit uit te voeren.»

Met andere woorden, we kunnen de Web App Builder-image gebruiken om onze build uit te voeren, en vervolgens de webserver-image, zoals NGINX, gebruiken om onze inhoud te serveren.

Op deze manier kunnen we de Web App Builder-image gebruiken als een 'schone' builder en tegelijkertijd een kleine runtime-image hebben.

Laten we dit nu bekijken met een specifiek voorbeeld.

Voor de oefening gebruiken we een eenvoudige React-applicatie, gemaakt met behulp van de commandoregeltool create-react-app.

Om alles samen te voegen zal ons helpen de OpenShift-sjabloonbestand.

Laten we dit bestand nader bekijken en beginnen met het gedeelte parameters.

parameters:
  - name: SOURCE_REPOSITORY_URL
    description: De bron-URL voor de applicatie
    displayName: Bron-URL
    required: true
  - name: SOURCE_REPOSITORY_REF
    description: De taknaam voor de applicatie
    displayName: Bron-tak
    value: master
    required: true
  - name: SOURCE_REPOSITORY_DIR
    description: De locatie binnen de bronrepo van de applicatie
    displayName: Bronmap
    value: .
    required: true
  - name: OUTPUT_DIR
    description: De locatie van de gecompileerde statische bestanden van uw webapps-bouwer
    displayName: Output-map
    value: build
    required: false

Het is allemaal vrij duidelijk hier, maar het is belangrijk om aandacht te besteden aan de parameter OUTPUT_DIR. Voor de React-applicatie uit ons voorbeeld hoeft u zich geen zorgen te maken, omdat React als output-map de standaardwaarde gebruikt, maar voor Angular of iets anders moet deze parameter op de juiste manier worden aangepast.

Laten we nu kijken naar het gedeelte ImageStreams.

- apiVersion: v1
  kind: ImageStream
  metadata:
    name: react-web-app-builder  // 1 
  spec: {}
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: react-web-app-runtime  // 2 
  spec: {}
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: web-app-builder-runtime // 3
  spec:
    tags:
    - name: latest
      from:
        kind: DockerImage
        name: nodeshift/ubi8-s2i-web-app:10.x
- apiVersion: v1
  kind: ImageStream
  metadata:
    name: nginx-image-runtime // 4
  spec:
    tags:
    - name: latest
      from:
        kind: DockerImage
        name: 'centos/nginx-112-centos7:latest'

Kijk naar de derde en vierde afbeeldingen. Ze zijn beide gedefinieerd als Docker-afbeeldingen, en het is duidelijk waar ze vandaan komen.

De derde afbeelding is de web-app-builder, en deze komt uit nodeshift/ubi8-s2i-web-app met tag 10.x op Docker hub.

De vierde afbeelding is de NGINX-afbeelding (versie 1.12) met tag latest op Docker hub.

Laten we nu kijken naar de eerste twee afbeeldingen. Beide zijn leeg bij de start en worden alleen tijdens de bouwfase aangemaakt. De eerste afbeelding, react-web-app-builder, wordt het resultaat van de assemblagefase, die de web-app-builder-runtime afbeelding en onze broncode combineert. Daarom hebben we '-builder' aan de naam van deze afbeelding toegevoegd.

De tweede afbeelding, react-web-app-runtime, is het resultaat van de combinatie van nginx-image-runtime en enkele bestanden van de react-web-app-builder afbeelding. Deze afbeelding zal ook worden gebruikt tijdens de implementatie en zal alleen de webserver en de statische HTML, JavaScript, CSS van onze applicatie bevatten.

Verwarrend? Laten we nu kijken naar de bouwconfiguraties en het zal iets duidelijker worden.

In onze sjabloon zijn er twee bouwconfiguraties. Hier is de eerste, en deze is vrij standaard:

  apiVersion: v1
  kind: BuildConfig
  metadata:
    name: react-web-app-builder
  spec:
    output:
      to:
        kind: ImageStreamTag
        name: react-web-app-builder:latest 
    source:   
      git:
        uri: ${SOURCE_REPOSITORY_URL}
        ref: ${SOURCE_REPOSITORY_REF}
      contextDir: ${SOURCE_REPOSITORY_DIR}
      type: Git
    strategy:
      sourceStrategy:
        env:
          - name: OUTPUT_DIR 
            value: ${OUTPUT_DIR}
        from:
          kind: ImageStreamTag
          name: web-app-builder-runtime:latest 
        incremental: true 
      type: Source
    triggers: 
    - github:
        secret: ${GITHUB_WEBHOOK_SECRET}
      type: GitHub
    - type: ConfigChange
    - imageChange: {}
      type: ImageChange

Zoals te zien is, zegt de regel met label 1 dat het resultaat van deze bouw zal worden opgeslagen in het beeld react-web-app-builder, dat we eerder in de sectie ImageStreams hebben gezien.

De regel met label 2 geeft aan waar de code vandaan komt. In ons geval is dit een git-repository, en de locatie, ref en contextdirectory zijn gedefinieerd door de parameters die we hierboven al hebben gezien.

De regel met label 3 hebben we eerder gezien in de sectie parameters. Deze voegt de omgevingsvariabele OUTPUT_DIR toe, die in ons voorbeeld gelijk is aan build.
De regel met label 4 zegt dat we het beeld web-app-builder-runtime willen gebruiken, dat we al hebben gezien in de sectie ImageStreams.

De regel met label 5 geeft aan dat we een incrementele build willen gebruiken, mits de S2I-image dit ondersteunt, wat het beeld Web App Builder doet. Bij de eerste uitvoering, na het voltooien van de assembly-fase, zal de afbeelding de map node_modules in een archiefbestand opslaan. Bij daaropvolgende uitvoeringen zal de afbeelding deze map gewoon weer uitpakken om de bouwtijd te verkorten.

En ten slotte, de regel met label 6 zijn slechts enkele triggers zodat de bouw automatisch wordt gestart, zonder handmatige tussenkomst, wanneer er iets verandert.

Over het algemeen is dit een vrij standaard bouwconfiguratie.

Laten we nu eens kijken naar de tweede bouwconfiguratie. Deze lijkt erg op de eerste, maar er is één belangrijk verschil.

apiVersion: v1
  kind: BuildConfig
  metadata:
    name: react-web-app-runtime
  spec:
    output:
      to:
        kind: ImageStreamTag
        name: react-web-app-runtime:latest 
    source: 
      type: Image
      images:                              
        - from:
            kind: ImageStreamTag
            name: react-web-app-builder:latest 
          paths:
            - sourcePath: /opt/app-root/output/.  
              destinationDir: .  
             
    strategy: 
      sourceStrategy:
        from:
          kind: ImageStreamTag
          name: nginx-image-runtime:latest
        incremental: true
      type: Source
    triggers:
    - github:
        secret: ${GITHUB_WEBHOOK_SECRET}
      type: GitHub
    - type: ConfigChange
    - type: ImageChange
      imageChange: {}
    - type: ImageChange
      imageChange:
        from:
          kind: ImageStreamTag
          name: react-web-app-builder:latest

Dus, de tweede configuratie is react-web-app-runtime, en die begint vrij standaard.

In de regel met label 1 is er niets nieuws – deze zegt gewoon dat de uitkomst van de build in de afbeelding react-web-app-runtime wordt geplaatst.

De regel met label 2, zoals in de vorige configuratie, geeft aan waar de broncode vandaan komt. Maar let op dat we hier zeggen dat deze uit een afbeelding komt. En wel uit de afbeelding die we zojuist hebben gemaakt – uit react-web-app-builder (vermeld in de regel met label 3). De bestanden die we willen gebruiken, bevinden zich binnen de afbeelding en hun locatie daar wordt bepaald in de regel met label 4, in ons geval is dat /opt/app-root/output/. Als je het je herinnert, daar worden de bestanden opgeslagen die zijn gegenereerd als resultaat van de build van onze applicatie.

De doelmap, ingesteld in de regel met label 5 – is gewoon de huidige catalogus (dit draait allemaal, laten we niet vergeten, binnen een soort magische machine genaamd OpenShift, en niet op jouw lokale computer).

De sectie strategy – regel met label 6 – lijkt ook op de eerste buildconfiguratie. Maar deze keer gaan we nginx-image-runtime gebruiken, dat we al in de sectie ImageStream hebben gezien.

Tenslotte is de regel met label 7 de triggersectie die deze build activeert elke keer wanneer de afbeelding react-web-app-builder verandert.

Verder bevat deze sjabloon een vrij standaard configuratie voor implementatie, evenals elementen die betrekking hebben op diensten en routes, maar we zullen daar niet dieper op ingaan. Let op dat de afbeelding die geïmplementeerd zal worden– de afbeelding react-web-app-runtime is.

Implementatie van de applicatie

Dus, nadat we naar de sjabloon hebben gekeken, laten we eens kijken hoe we deze kunnen gebruiken om de applicatie te implementeren.

We kunnen de clienttool OpenShift genaamd oc gebruiken om onze sjabloon te implementeren:

$ find . | grep openshiftio | grep application | xargs -n 1 oc apply -f

$ oc new-app --template react-web-app -p SOURCE_REPOSITORY_URL=https://github.com/lholmquist/react-web-app

De eerste opdracht op de afbeelding hierboven is een opzettelijk technische manier om de sjabloon /openshiftio/application.yaml te vinden.

De tweede opdracht maakt gewoon een nieuwe applicatie op basis van deze sjabloon.

Nadat deze opdrachten zijn uitgevoerd, zullen we zien dat we twee builds hebben ontvangen:

Moderne applicaties op OpenShift, deel 2: chained builds

En door terug te keren naar het overzichtsscherm, zien we de gestartte pod:

Moderne applicaties op OpenShift, deel 2: chained builds

Een klik op de link – en we worden doorgestuurd naar onze applicatie, die een standaard React App-pagina is:

Moderne applicaties op OpenShift, deel 2: chained builds

Aanvulling 1

Voor Angular-liefhebbers hebben we ook voorbeeld applicatie.

De sjabloon is hier hetzelfde, behalve de variabele OUTPUT_DIR.

Aanvulling 2

In dit artikel hebben we NGINX als webserver gebruikt, maar het is vrij eenvoudig om het te vervangen door Apache, gewoon door het sjabloonbestand aan te passen. NGINX afbeelding en een werkende opdracht krijgen. Apache afbeelding.

Conclusie

In het eerste deel van deze serie hebben we laten zien hoe je moderne webapplicaties snel kunt implementeren op het OpenShift-platform. Vandaag hebben we bekeken wat de Web App afbeelding doet en hoe deze kan worden gecombineerd met een lichte webserver zoals NGINX met behulp van gekoppelde builds (chained builds) om een productiegeschikte applicatie-build te organiseren. In het volgende, afsluitende artikel van deze serie, zullen we laten zien hoe je een ontwikkelserver voor je applicatie op OpenShift kunt draaien en de synchronisatie tussen lokale en externe bestanden kunt waarborgen.

Inhoud van deze artikelenserie

  • Deel 1: hoe moderne webapplicaties in slechts een paar stappen te implementeren;
  • Deel 2: hoe de nieuwe S2I afbeelding toe te passen samen met een bestaande HTTP-server afbeelding, zoals NGINX, met behulp van gekoppelde OpenShift builds voor het organiseren van productie-implementaties;
  • Deel 3: hoe een ontwikkelserver voor je applicatie op het OpenShift-platform te draaien en deze te synchroniseren met het lokale bestandssysteem.

Aanvullende bronnen

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster