Docker – Przygotowanie i wdrożenie aplikacji Ionic

W tym artykule autor opisuje na bazie własnego doświadczenia jak poprawnie hostować aplikację Ionic przy użyciu Dockera.

Zagadnienia, jakie będą poruszane to:

  • Co to jest Ionic
  • Jak przygotować projekt aplikacji Ionic pod konteneryzację
  • Jak przygotować obraz dockerowy dla aplikacji Ionic
  • Jak uruchomić kontener dockerowy z aplikacją Ionic
  • Na co warto zwrócić uwagę podczas przygotowywania obrazu dockerowego dla aplikacji Ionic

Co to jest Ionic?

Ionic jest to narzędzie pozwalające na pisanie aplikacji hybrydowych przy użyciu technologii webowych. Aplikacje mogą być pisane w czystym JavaScripcie, ale też mogą być użyte najpopularniejsze frameworki takie jak: Angular, React, Vue.

W ekosystemie Ionica narzędziem do budowy aplikacji jest Capacitor, następca innego popularnego narzędzia do budowy aplikacji hybrydowych, czyli Apache Cordova. Poza tym Ionic to też zestaw gotowych komponentów używanych w aplikacjach mobilnych, zestaw interfejsów do połączenia aplikacji z częścią natywną (np. kamera, GPS, baza danych) i z różnymi usługami (np. Facebook, Firebase), ale i też gotowa usługa w chmurze do budowania, automatycznych aktualizacji, czy procesów CI.

Źródło obrazka: https://ionicframework.com/blog/announcing-capacitor-1-0/

Capacitor jako narzędzie do budowania aplikacji jest w stanie zbudować aplikację na iOSa, Androida, aplikacje desktopowe, ale też z racji, że użyte są technologie webowe, jest w stanie z tego samego kodu zrobić stronę internetową, czy aplikację PWA. Taką aplikację Ionic webową trzeba w jakiś sposób opublikować jako normalną stronę internetową. W tym przypadku może nam pomóc konteneryzacja, a dokładniej Docker.

Link do strony projektu: https://ionicframework.com/


Ionic i Docker — tworzenie projektu

Przejdziemy od utworzenia aplikacji w Ionicu, aż do zbudowania jej tak, aby była gotowa do osadzenia w obrazie dockerowym. Proces utworzenia bazowej aplikacji wygląda tak:

  1. Na samym początku musimy mieć zainstalowane środowisko NodeJS, które można pobrać ze strony: https://nodejs.org/en/download/
  2. Później instalujemy globalnie dla systemu CLI Ionica poleceniem:
    npm i ionic -g
  3. Tworzymy projekt z szablonu
    ionic start ionic-docker-example blank
  4. Podczas tworzenia projektu zostaniemy poproszeni o wybranie frameworku. Wybieramy Angulara.

Nie będziemy się skupiać nad strukturą projektu, ponieważ nie jest to cel tego postu. Skupimy się na przygotowaniu projektu tak, aby przyjął on zmienne środowiskowe podczas uruchamiania kontenera dockerowego. Standardowo konfiguracje zmienne dla różnych uruchomień w aplikacji z użyciem frameworka Angular są ustawiane w plikach environments.

W zależności, czy uruchamiamy produkcyjnie aplikację, czy tylko ją debugujemy, wybierany jest jeden z tych dwóch plików. W takim pliku możemy przechowywać np. adresy api. Jeśli jednak chcemy zmienić coś po skompilowaniu aplikacji, to niestety nie możemy zmienić tego w tych plikach, bo one też będą kompilowane. Przykładem jest sytuacja gdy będziemy chcieli zmienić adres api podczas uruchomienia kontenera.

Pliki, które znajdują się w folderze assets nie są kompilowane, więc możemy wykorzystać ten zasób w taki sposób:

Tworzymy dwa pliki env.js (do debugu) i env.template.js (do wersji produkcyjnej)

Treść pliku env.js:

(function (window) {
 window["env"] = window["env"] || {};

 window["env"]["apiUrl"] = "https://test.pl/api/";
})(this);

Treść pliku env.template.js:

(function(window) {
   window["env"] = window["env"] || {};

   // Environment variables
   window["env"]["apiUrl"] = "${API_URL}";
})(this);

Oba pliki rozszerzają obiekt window o konfigurację dla danej instancji. Różnią się tylko tym, że w pliku używanym produkcyjnie zmienne są przekazywane według szablonu, który będzie uzupełniany podczas uruchamiania kontenera. Jak to się dzieje, pokażę w dalszej części.

Następnie w obu plikach environment rejestrujemy w taki sposób naszą zmienną apiUrl:

oraz skrypt musi być też zarejestrowany w pliku index.html:

Na potrzeby testów aplikacji wyświetlimy wprowadzony adres api w naszej aplikacji. Po pierwsze musimy w pliku z logiką głównej i jedynej strony aplikacji (src/app/home/home.page.ts) przekazać jako pole:

import { Component } from '@angular/core';

import {environment} from '../../environments/environment';

@Component({
 selector: 'app-home',
 templateUrl: 'home.page.html',
 styleUrls: ['home.page.scss'],
})
export class HomePage {
 apiUrl = environment.apiUrl;
}

Później na widoku, w pliku html (src/app/home/home.page.html) pokazujemy ustawiony adres api:

<ion-header [translucent]="true">
 <ion-toolbar>
   <ion-title>
     Docker + Ionic
   </ion-title>
 </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
 <div id="container">
   Adres API: <strong> {{ apiUrl }}</strong>
 </div>
</ion-content>

Teraz już wystarczy tylko uruchomić testowo naszą aplikację w przeglądarce poleceniem ionic serve i przejść do strony: http://localhost:8100/. Zobaczymy wtedy zmienną ustawioną w pliku env.js:

Po weryfikacji poprawnie przekazanej zmiennej w przeglądarce kończymy proces w konsoli. Po stronie Ionica zostało nam już tylko zbudowanie produkcyjnie aplikacji. Wykonujemy to poleceniem:

ionic build –configuration=production

Po zbudowaniu aplikacji pojawi się katalog www, w którym pojawią się pliki, które zaraz wykorzystamy w obrazie dockerowym.


Przygotowanie obrazu dockerowego pod Ionica

Przygotowanie obrazu rozpoczynamy od utworzenia pliku Dockerfile w głównym katalogu projektu. Do uruchomienia aplikacji będziemy potrzebowali usługi hostującej strony www. Użyjemy do tego nginxa ustawiając obraz bazowy:

FROM nginx:1.21.6-alpine

Kopiujemy podczas przygotowania obrazu katalog www przygotowany przez Ionica poleceniem:

COPY www /usr/share/nginx/html

Przed uruchomieniem usługi nginx musimy jeszcze podmienić plik env.js plikiem env.template.js przekazując zmienne środowiskowe ustawiane podczas uruchomienia kontenera. Użyjemy do tego narzędzia envsubst, które jest w bazowym obrazie. Polecenie CMD, które jest uruchamiane podczas uruchomienia kontenera, wygląda więc w taki sposób:

CMD ["/bin/sh",  "-c",  "envsubst < 
/usr/share/nginx/html/assets/env.template.js > 
/usr/share/nginx/html/assets/env.js && exec nginx -g 'daemon off;'"]

Cały plik Dockerfile wygląda następująco:

FROM nginx:1.21.6-alpine
COPY www /usr/share/nginx/html
CMD ["/bin/sh",  "-c",  "envsubst < 
/usr/share/nginx/html/assets/env.template.js > 
/usr/share/nginx/html/assets/env.js && exec nginx -g 'daemon off;'"]

Gdy już plik Dockerfile mamy przygotowany, teraz wystarczy tylko zbudować obraz poleceniem:

docker build -t ionic-docker .


Ionic i Docker — uruchomienie aplikacji

Możemy teraz już uruchomić kontener z aplikacją Ionic na podstawie przygotowanego obrazu przekazując adres do api poleceniem:

docker run -p 8080:80 -d –name ionic-app –env API_URL=https://zdockera.pl/api ionic-docker  

Wybraliśmy na hoście port 8080, więc zweryfikować działanie aplikacji można przechodząc do strony http://localhost:8080/. Jak widać do aplikacji za pomocą zmiennej środowiskowej poprawnie został przekazany adres api:


Ionic i Docker — lepiej budować aplikację w obrazie, czy poza nim?

Aplikację Ionicową można budować zarówno poza obrazem, jak i w ramach obrazu dockerowego. Budowa w obrazie przenosi zalety konteneryzacji, takie jak budowanie zawsze przy takiej samej konfiguracji. W praktyce spowalnia to jednak przygotowanie takiego obrazu, bo musimy przygotować przy każdym budowaniu zestaw zależności potrzebny do zbudowania aplikacji.

W przypadku budowania aplikacji poza obrazem zależności te są przygotowywane raz, dzięki czemu przy wielokrotnym powielaniu proces ten jest wykonywany szybciej. Dodatkowo budowanie poza obrazem może być szybsze w przypadku jak aplikacja jest zrobiona z bibliotek też używanych w innych projektach i w tym samym momencie budowanych (np. przez CI). Po pierwszym zbudowaniu dla danego projektu każda z bibliotek jest cachowana i przy następnym projekcie wykorzystywana już bez ponownego budowania.

Podczas przygotowywania obrazu warto część związaną z kopiowaniem kodu zbudowanej aplikacji (COPY) robić jako ostatnie polecenie przed poleceniem wykonywanym już w kontenerze podczas uruchomienia (CMD). Kod aplikacji zawsze będzie się zmieniał, przez co polecenie, które pliki (COPY) zawsze będzie wykonywane, a polecenia wcześniejsze w pliku Dockerfile będą mogły być przez mechanizm Dockera cachowane i dzięki temu obraz będzie budował się też szybciej.


Podsumowanie

Do udostępniania aplikacji Ionic w formie strony www, lub PWA, możemy użyć Dockera. Podczas przygotowania takiej aplikacji musimy zadbać o poprawne przekazywanie zmiennych środowiskowych ustawianych podczas uruchomienia obrazu. Jeden ze sposobów opisałem w tym artykule. Obraz został przygotowany w minimalny sposób do uruchomienia aplikacji. 

W środowisku produkcyjnym na pewno trzeba zadbać o standardowe praktyki tworzenia obrazów (np. użycie non-root użytkownika). Przy pracy z Ioniciem można użyć też Dockera nie tylko do uruchamiania aplikacji, ale też np. do budowania aplikacji w CI. Innym przykładem użycia jest używanie specyficznej wersji CLI Ionica w ramach kontenera tak, aby później można było te CLI albo w łatwy sposób skasować, albo zmienić na inną wersję.

Adres do Githuba z kodem:

https://github.com/emiljuchnikowski/ionic-docker-example


Autor

Emil Juchnikowski — pracuje jako Architekt Oprogramowania. Swoją karierę rozpoczął jako .NET Developer ponad 10 lat temu. Teraz już z technologią .NET ma bardzo mało wspólnego. Rozwija się w technologiach JS, w różnych kierunkach: frontend Angular i Ionic, backend NodeJS i NestJS, bazy danych MongoDB. Poza tym zajmuje się też zagadnieniami DevOps, GitOps opartymi o konteneryzację w Kubernetes. Poza pracą lubi biegać na długie dystanse, oraz trenuje boks. Jego motto to: Jedynym ograniczeniem jest nasza wyobraźnia… a niektórzy twierdzą, że jeszcze czas i pieniądze.




.

2 thoughts on “Docker – Przygotowanie i wdrożenie aplikacji Ionic”

  1. Zmieniłem na:

    C:\Users\Acer\ionic-docker-example>docker run -p 8080:80 -d ionic-docker-example -env API_URL=https://zdockera.pl/api dockerfile

    wynik:
    Unable to find image ‘ionic-docker-example:latest’ locally
    docker: Error response from daemon: pull access denied for ionic-docker-example, repository does not exist or may require ‘docker login’: denied: requested access to the resource is denied.
    See ‘docker run –help’.

    dockerfile – to nazwa mojego dockerfile w katalogu głownym aplikacji
    ionic-docker-example – nazwa mojego przykladowego projektu

    Dopiero zaczynam z dockerrm wiec wybacz za moze glupie pytania

    1. Cześć Jarek!

      Wydaje mi się, iż nie zbudowałeś wcześniej obrazu dockerowego (komenda docker build . -t ionic-docker-example). PS. Ta kropka po ‘build’ to nie literówka. To określenie build contextu jako biezący katalog, z którego uruchamiasz proces budowania.

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *