Nowoczesne aplikacje na OpenShift, część 2: powiązane kompilacje chained builds

Cześć wszystkim! Oto drugi wpis z naszej serii, w której pokazujemy, jak wdrażać nowoczesne aplikacje internetowe na Red Hat OpenShift.

Nowoczesne aplikacje na OpenShift, część 2: powiązane kompilacje chained builds

W poprzednim poście nieco omówiliśmy możliwości nowego obrazu buildera S2I (source-to-image), który jest przeznaczony do budowania i wdrażania nowoczesnych aplikacji internetowych na platformie OpenShift. Wtedy interesował nas temat szybkiego wdrażania aplikacji, a dzisiaj przyjrzymy się, jak używać obrazu S2I jako „czystego” obrazu buildera i łączyć go z powiązanymi kompilacjami OpenShift.

Czysty obraz buildera

Jak wspomnieliśmy w pierwszej części, większość nowoczesnych aplikacji internetowych ma tak zwany etap budowy, na którym zazwyczaj przeprowadza się takie operacje jak transpilejsja kodu, konkatenacja kilku plików oraz minifikacja. Pliki powstałe w wyniku tych operacji – a więc statyczny HTML, JavaScript i CSS – są umieszczane w folderze output. Miejsce tego folderu zazwyczaj zależy od tego, jakie narzędzia budowania są używane, a dla React będzie to folder . /build (wrócimy do tego tematu dokładniej poniżej).

Source-to-Image (S2I)

W tym poście w ogóle nie poruszamy tematu „czym jest S2I i jak go używać” (więcej na ten temat można przeczytać tutaj), ale ważne jest, aby wyraźnie rozumieć dwa etapy tego procesu, aby zrozumieć, co właściwie robi obraz Web App Builder.

Etap assemblacji (assemble phase)

Etap assemblacji jest w gruncie rzeczy bardzo podobny do tego, co dzieje się, gdy uruchamiasz docker build i w wyniku tego otrzymujesz nowy obraz Docker. Odpowiednio, ten etap ma miejsce przy uruchomieniu budowy na platformie OpenShift.

W przypadku obrazu Web App Builder za instalację zależności Twojej aplikacji i uruchomienie budowy odpowiada skrypt assemble. Domyślnie obraz buildera używa konstrukcji npm run build, ale można ją nadpisać za pomocą zmiennej środowiskowej NPM_BUILD.

Jak mówiliśmy wcześniej, lokalizacja gotowej, już zbudowanej aplikacji zależy od używanych narzędzi. Na przykład, w przypadku React będzie to folder. /build, a dla aplikacji Angular – folder project_name /dist. I, jak już pokazano w poprzednim wpisie, lokalizację katalogu wyjściowego, który domyślnie jest ustawiony jako build, można nadpisać przez zmienną środowiskową OUTPUT_DIR. Ponieważ lokalizacja katalogu wyjściowego różni się w zależności od frameworka, po prostu kopiujesz wygenerowane wyjście do standardowego folderu w obrazie, a mianowicie do /opt /apt-root /output. To jest ważne dla zrozumienia dalszej części tego artykułu, a teraz szybko przejdźmy do następnego etapu – fazy uruchomienia (run phase).

Faza uruchomienia (run phase)

Ten etap zachodzi, gdy dla nowego obrazu, stworzonego w etapie składania, wywoływane jest docker run. Dzieje się to także podczas wdrażania na platformie OpenShift. Domyślnie skryptowi uruchamiającemu (run script) używa moduł serve do obsługi statycznych treści znajdujących się w wcześniej wspomnianym standardowym katalogu wyjściowym.

Ta metoda jest dobra do szybkiego wdrażania aplikacji, ale w rzeczywistości nie zaleca się obsługi statycznych treści w ten sposób. Ponieważ w rzeczywistości obsługujemy tylko statyczne treści, zainstalowany w obrazie Node.js nie jest potrzebny – wystarczy serwer WWW.

Mówiąc inaczej, podczas budowy potrzebujemy czegoś innego, a podczas wykonywania czegoś innego. W takiej sytuacji przydadzą się powiązane budowy (chained builds).

Powiązane budowy (chained builds)

Oto, co mówi się o powiązanych budowach w dokumentacji OpenShift:

„Dwie budowy można powiązać ze sobą, przy czym jedna z nich generuje skompilowany byt, a druga umieszcza ten byt w osobnym obrazie, który jest używany do uruchomienia tego bytu.”

Innymi słowy, możemy wykorzystać obraz Web App Builder do uruchomienia naszej budowy, a następnie użyć obrazu serwera WWW, tego samego NGINX, aby obsługiwać naszą treść.

W ten sposób możemy stosować obraz Web App Builder jako 'czysty' builder i jednocześnie mieć mały obraz runtime.

Teraz omówmy to na konkretnym przykładzie.

Do ćwiczeń użyjemy prostej aplikacji React, stworzonej za pomocą narzędzia wiersza poleceń create-react-app.

Zebrać wszystko w całość pomoże nam szablon pliku OpenShift.

Przyjrzyjmy się bliżej temu plikowi, zaczynając od sekcji parametrów.

parametry:
  - nazwa: SOURCE_REPOSITORY_URL
    opis: Adres URL źródłowego dla aplikacji
    displayName: Adres URL źródłowy
    wymagane: prawda
  - nazwa: SOURCE_REPOSITORY_REF
    opis: Nazwa gałęzi dla aplikacji
    displayName: Gałąź źródłowa
    wartość: master
    wymagane: prawda
  - nazwa: SOURCE_REPOSITORY_DIR
    opis: Lokalizacja w repozytorium źródłowym aplikacji
    displayName: Katalog źródłowy
    wartość: .
    wymagane: prawda
  - nazwa: OUTPUT_DIR
    opis: Lokalizacja skompilowanych plików statycznych z Twojego narzędzia do tworzenia aplikacji internetowych
    displayName: Katalog wyjściowy
    wartość: build
    wymagane: fałsz

Tutaj wszystko jest dość jasne, ale warto zwrócić uwagę na parametr OUTPUT_DIR. W przypadku aplikacji React z naszego przykładu nie ma się czym martwić, ponieważ React używa domyślnego ustawienia jako katalogu wyjściowego, ale w przypadku Angulara lub innych frameworków ten parametr będzie trzeba dostosować.

Teraz przyjrzyjmy się sekcji ImageStream.

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

Spójrz na trzeci i czwarty obraz. Oba zostały zdefiniowane jako obrazy Docker i widać wyraźnie, skąd pochodzą.

Trzeci obraz to web-app-builder, który pochodzi z nodeshift/ubi8-s2i-web-app z tagiem 10.x na Docker hub.

Czwarty obraz to obraz NGINX (wersja 1.12) z tagiem latest na Docker hub.

Teraz popatrzmy na dwa pierwsze obrazy. Oba zaczynają jako puste i są tworzone dopiero w fazie budowy. Pierwszy obraz – react-web-app-builder – będzie wynikiem etapu łączenia, który połączy obraz web-app-builder-runtime z naszym kodem źródłowym. Dlatego dodaliśmy do nazwy tego obrazu „-builder”.

Drugi obraz – react-web-app-runtime – będzie wynikiem połączenia nginx-image-runtime oraz pewnych plików z obrazu react-web-app-builder. Obraz ten również będzie używany podczas wdrożenia i będzie zawierał tylko serwer WWW oraz statyczny HTML, JavaScript, CSS naszej aplikacji.

Zawiłe? Spójrzmy teraz na konfiguracje budowy, aby stało się to trochę jaśniejsze.

W naszym szablonie znajduje się dwie konfiguracje budowy. Oto pierwsza z nich, która jest dość standardowa:

  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

Jak widzimy, linia z etykietą 1 mówi, że wynik tej budowy zostanie umieszczony w obrazie react-web-app-builder, który wcześniej widzieliśmy w sekcji ImageStream.

Linia z etykietą 2 mówi, skąd brać kod. W naszym przypadku jest to repozytorium git, a lokalizacja, ref i folder kontekstowy są określone przez parametry, które już widzieliśmy powyżej.

Linia z etykietą 3 – to już widzieliśmy w sekcji parametrów. Dodaje zmienną środowiskową OUTPUT_DIR, która w naszym przykładzie ma wartość build.
Linia z etykietą 4 mówi, aby używać obrazu web-app-builder-runtime, który już widzieliśmy w sekcji ImageStream.

Linia z etykietą 5 mówi, że chcemy korzystać z inkrementalnej budowy, jeżeli obraz S2I to wspiera, a obraz Web App Builder – wspiera. Przy pierwszym uruchomieniu, po zakończeniu etapu kompilacji, obraz zachowa folder node_modules w archiwum. Następnie przy kolejnych uruchomieniach obraz po prostu rozpakowuje ten folder, aby skrócić czas budowy.

I w końcu, linia z etykietą 6 – to tylko kilka wyzwalaczy, aby budowa uruchamiała się automatycznie, bez ręcznej interwencji, gdy cokolwiek się zmienia.

Ogólnie rzecz biorąc, to dość standardowa konfiguracja budowy.

Teraz przyjrzyjmy się drugiej konfiguracji budowy. Jest ona bardzo podobna do pierwszej, ale istnieje jedna ważna różnica.

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

Więc druga konfiguracja kompilacji – react-web-app-runtime, rozpoczyna się dość standardowo.

W linii oznaczonej jako 1 nie ma nic nowego – po prostu informuje, że wynik kompilacji jest umieszczany w obrazie react-web-app-runtime.

Linia oznaczona jako 2, jak w poprzedniej konfiguracji, wskazuje, skąd pobrać kod źródłowy. Zauważcie jednak, że mówimy, iż pochodzi z obrazu. A dokładniej z obrazu, który właśnie stworzyliśmy – z react-web-app-builder (wskazany w linii oznaczonej jako 3). Pliki, które chcemy użyć, znajdują się wewnątrz obrazu, a ich lokalizacja jest określona w linii oznaczonej jako 4, w naszym przypadku to /opt/app-root/output/. Jeśli pamiętacie, tam właśnie trafiają pliki generowane w wyniku kompilacji naszej aplikacji.

Folder docelowy, określony w linii oznaczonej jako 5 – to po prostu bieżący katalog (wszystko to, przypomnijmy, kręci się wewnątrz pewnej magicznej rzeczy o nazwie OpenShift, a nie na waszym lokalnym komputerze).

Sekcja strategy – linia oznaczona jako 6 – również przypomina pierwszą konfigurację kompilacji. Tylko tym razem zamierzamy użyć nginx-image-runtime, który już widzieliśmy w sekcji ImageStream.

Wreszcie linia oznaczona jako 7 – to sekcja triggerów, która aktywuje tę kompilację za każdym razem, gdy zmienia się obraz react-web-app-builder.

Poza tym, szablon ten zawiera całkiem standardową konfigurację wdrożenia, a także rzeczy odnoszące się do usług i tras, ale nie będziemy się w to zagłębiać. Zauważcie, że obraz, który będzie wdrażany – to obraz react-web-app-runtime.

Rozwój aplikacji

Zatem, po tym jak przyjrzeliśmy się szablonowi, zobaczmy, jak go użyć do wdrożenia aplikacji.

Możemy użyć narzędzia klienckiego OpenShift o nazwie oc, aby wdrożyć nasz szablon:

$ 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

Pierwsza komenda na zrzucie powyżej – to celowo inżynierski sposób na znalezienie szablonu ./openshiftio/application.yaml.

Druga komenda po prostu tworzy nową aplikację na podstawie tego szablonu.

Po tym, jak te komendy zostaną wykonane, zobaczymy, że mamy dwie kompilacje:

Nowoczesne aplikacje na OpenShift, część 2: powiązane kompilacje chained builds

A wracając do ekranu Overview, zobaczymy uruchomiony kontener:

Nowoczesne aplikacje na OpenShift, część 2: powiązane kompilacje chained builds

Kliknięcie w link – przeprowadzi nas do naszej aplikacji, która jest domyślną stroną aplikacji React App:

Nowoczesne aplikacje na OpenShift, część 2: powiązane kompilacje chained builds

Dodatek 1

Dla miłośników Angular również mamy przykład aplikacji.

Szablon jest taki sam, z wyjątkiem zmiennej OUTPUT_DIR.

Dodatek 2

W tym artykule użyliśmy NGINX jako serwera WWW, ale można go łatwo zamienić na Apache, po prostu zmieniając plik szablonu. obraz NGINX na obraz Apache.

Podsumowanie

W pierwszej części tej serii pokazaliśmy, jak szybko wdrażać nowoczesne aplikacje webowe na platformie OpenShift. Dziś przyjrzeliśmy się, co robi obraz aplikacji webowej i jak można go połączyć z czystym serwerem WWW typu NGINX za pomocą powiązanych budów (chained build), aby zorganizować bardziej odpowiednią do warunków produkcyjnych budowę aplikacji. W następnej, końcowej części tej serii pokażemy, jak uruchomić na OpenShift serwer deweloperski dla swojej aplikacji i zapewnić synchronizację plików lokalnych i zdalnych.

Spis treści tej serii artykułów

  • Część 1: jak wdrażać nowoczesne aplikacje webowe w zaledwie kilku krokach;
  • Część 2: jak zastosować nowy obraz S2I razem z istniejącym obrazem serwera HTTP, na przykład NGINX, używając powiązanych budów OpenShift, aby zorganizować wdrożenie produkcyjne;
  • Część 3: jak uruchomić na platformie OpenShift serwer deweloperski dla swojej aplikacji i zsynchronizować go z lokalnym systemem plików.

Dodatkowe zasoby

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster