Anleitung App Entwicklung
Das Ziel dieser Anleitung ist, eine ScaleIT-App zu entwickeln:
- Die App soll ein "Hello ScaleIT" als Ausgabe liefern
- Aufrufbar mit Web-Browser
- Die App soll heißen: "helloscaleit"
- Sie soll eine vollwertige ScaleIT App sein
- Sie soll aus dem App-Pool installierbar sein
Wenn die App dann erstellt wurde, sind folgende weiterführende möglicherweise Themen relevant:
Zurück zur App-Entwicklung. Folgende Schritte sind dazu erforderlich:
Inhaltsverzeichnis
- 1 Schritt 1: Die Vorbereitung
- 2 Schritt 2: Unser Programm
- 3 Schritt 3: Die Containerisierung
- 4 Schritt 4: Die Container-Automation
- 5 Schritt 5: Die Build-Automation
- 6 Schritt 6: Das Rancher-Template
- 7 Schritt 7: Das Registration Sidecar
- 8 Schritt 8: Veröffentlichung im App-Pool
- 9 Schritt 9: Installation der App in Rancher
Schritt 1: Die Vorbereitung
Das Handwerkszeug des App-Entwicklers ist:
- Linux als Betriebssystem
- Installierte Software: Docker, Docker-Compose, Make
- Als Editor empfehlen wir: Microsoft VisualCode
Die App-Entwicklung findet statt im Verzeichnis:
/home/meinname/de-ondics-helloscaleit
Wir legen darin eine Verzeichnisstruktur an wie unter Verzeichnisstrukur_einer_ScaleIT_App bschrieben.
Schritt 2: Unser Programm
Das eigentliche Programm soll hier als Beispiel ein kleines NodeJS-Programm (server.js) sein:
'use strict'; const express = require('express'); // Netzwerk-Erreichbarkeit const PORT = 8080; const HOST = '0.0.0.0'; // Hier eine Konstante, die per Environment-Variable // in docker-compose.yml geändert werden kann const TEXT = process.env.TEXT ? process.env.TEXT : "Hello ScaleIT"; // Webserver const app = express(); app.get('/', (req, res) => { res.send(TEXT+'\n'); }); app.listen(PORT, HOST); console.log(`ScaleIT-App läuft unter http://${HOST}:${PORT}`);
Dazu brauchen wir bei NodeJS üblich eine Datei package.json:
{ "name": "scaleit-app-helloscaleit", "version": "1.0.0", "description": "Erste ScaleIT-App in NodeJS", "author": "Ondics <info@ondics.de>", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "express": "^4.16.1" } }
Fertig. Die könnten wir nun schon testen, aber dazu brauchen wir die NodeJS-Laufzeitumgebung und die entsprechenden Bibliotheken (express,...). Die wollen wir nicht auf dem Linux-System installieren, sondern nur im Docker-Container vorhalten.
Schritt 3: Die Containerisierung
Nun muss unser programm in einen Docker-Container verpackt werden.
Dazu ist ein Dockerfile notwendig, das beschreibt, wie der Container gebaut werden soll:
FROM node:10-alpine # Hier ist unser Programm-Verzeichnis im Container WORKDIR /usr/src/app # Wir brauchen die NodeJS-Bibliotheks-Anforderungen COPY package*.json ./ RUN npm install # Für Produktionszwecke können die devDependencies wegbleiben # RUN npm ci --only=production # Jetzt muss der Programm-Code noch in den Container # wir kopieren am einfachsten das ganze Verzeichnis COPY . . # Der Port 8080 soll im Container von außen aufrufbar sein EXPOSE 8080 # Am Schluss folgt der eigentliche Programmstart CMD [ "node", "server.js" ]
Hin paar Hinweise zur Wahl des Basis-Docke-Images, auf dem unser Dockerfile aufbaut:
- Es kommt von https://hub.docker.com und ist frei verfügbar
- Wir nutzen Node 10, das reicht
- Das Image bau auf Alpine auf und ist sehr klein (24 MB), das ist gut für ScaleIT, sonst müssen Anwender ewig bei der App-Installation warten
Wir können nun den Container damit komplett erstellen, ausführen und wieder löschen:
$ docker build -t ondics/hello-scaleit-app . $ docker images $ docker run -p 49160:8080 -d clauss/node-web-app $ docker ps $ docker kill 2cf3bbf3eb2e && docker rm 2cf3bbf3eb2e
Wir haben nun drei Möglichkeiten, den laufenden Container zu testen:
1. Mit dem Browser
Mit Aufruf der Url http://0.0.0.0:49160 sollte unsere App "Hello ScaleIT" ausgeben.
2. Mit der Linux-Kommandozeile (im Terminalfenster):
curl ist ein tolles Tool, um Webseiten aufzurufen:
$ curl http://0.0.0.0:49160 Hello ScaleIT
3. Im Container selbst
Wir gehen zuerst in den Container hinein und rufen dann mit curl die Seite auf:
$ docker exec -it 2cf3bbf3eb2e /bin/sh # wget -O - -q http://0.0.0.0:8080 Hello world # exit
Zum Schluss vernichten wir den Container wieder und löschen das Image.
$ docker kill 2cf3bbf3eb2e && docker rm 2cf3bbf3eb2e && docker rmi ...
Schritt 4: Die Container-Automation
Die Docker-Befehle sind recht kompliziert, deswegen wurde Docker-Compose erfunden.
Wir bauen uns also eine Docker-Compose-Beschreibungsdatei docker-compose.yml:
version: "3.5" services: app: # Das Dockerfile im aktuellen Verzeichnis benutzen build: . # Mit den Env-Variablen wird unser programm parametrisiert environment: - TEXT=Hello ScaleIT, beschtes für Industre 4.0 # Der Port 8080 wird an den Port 49160 des PC's gebunden # und ist damit aufrufbar aus dem Browser, für curl, etc. ports: - 49160:8080
Fertig.
Damit kann man den Container noch leichter bauen und unser Programm noch leichter starten:
$ docker-compose build $ docker-compose up –d $ docker-compose ps $ docker logs -f $ docker-compose exec app /bin/sh # wget -O - -q http://0.0.0.0:8080 Hello ScaleIT, beschtes für Industre 4.0 # exit $ curl http://0.0.0.0:49160 Hello ScaleIT, beschtes für Industre 4.0 $ docker-compose down
Der letzte Befehl räumt alles auf, was mit dem Container zu tun hat.
Schritt 5: Die Build-Automation
Wir machen es uns noch leichter: Das uralte Unix-Werkzeug "make" muss dafür herhalten.
Hier ist ein kleines Makefile, das den Alltag des Entwicklers sehr erleichtert:
.PHONY: help build help: @echo "# ScaleIT App-make-Helferlein" @echo "# (C) Ondics, 2019" @echo Befehle: make ... @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) .DEFAULT_GOAL := help up: ## alle Services starten docker-compose up -d down: ## alle Services stoppen docker-compose down --remove-orphans build: ## alle Container bauen docker-compose build ps: ## was läuft? docker-compose ps logs: ## Alle Logs aller Container zeigen docker-compose logs -f -t
Der Aufruf der make-Befehle ist supereinfach:
Hilfe anzeigen:
$ make
Befehle ausführen, z.B.:
$ make build
Befehle können sogar hintereinander ausgeführt werden:
$ make build up logs
Einfacher geht's nicht!
Schritt 6: Das Rancher-Template
Mit dem Rancher-Template wird festgelegt, wie ein App gestartet wird, dazu zählen:
- Name und Versionsnummer der App (Datei
config.yml
) - App-Icon (Datei
catalogIcon-de-ondics-pacman.png
) - Informationen zur App und zur Version (Versionsdatei
0/README.md
) - Aufbau des Installations-Formulars (Versionsdatei
0/rancher-compose.yml
) - Docker-Compose zum Start der Container (Versionsdatei
0/docker-compose.yml.tpl
)
Die Dateien im Verzeichnis 0/...
sind Versionsdateien. Pro installierbarer Version wird ein neues Verzeichnis angelegt mit aufsteigender Nummerierung.
Viele Beispiele von Rancher-Templates sind verfügbar unter https://github.com/rancher/community-catalog/tree/master/templates
Datei config.yml
Beispiel:
name: ScaleIT-Pacman description: | ScaleIT Pacman - Das Spiel maintainer: Ondics GmbH license: "Ondics ScaleIT App License" version: 1.10.2 category: Spiel
Hinweise:
- Die
version
muss exakt mit der Version im jeweiligendocker-compose.yml.tpl
übereinstimmen - Als Maintainer wird im Rancher immer "Community Support" dargestellt. Das kann aktuell nicht geändert werden
Datei rancher-compose.yml
Beispiel:
.catalog: name: ScaleIT Pacman version: 1.10.2 description: "ScaleIT Datenfluss-Steuerung, App-Chains und Dashboards mit NodeRED" minimum_rancher_version: v1.6.0 questions: - variable: ssoproxy description: | Mit dem SSO-Proxy ist https und die Namensauflösung aktiviert. Der DNS muss korrekt eingerichtet sein. label: "ScaleIT SSO-Proxy benutzen?" required: true default: false type: boolean ...
Hinweise:
- Die Datei hat grundsätzlich das yml-Format (YAML). Mahr Infos zu YAML unter https://learn.getgrav.org/16/advanced/yaml
- Die Beschreibung der Rancher-Catalog-Spezifikation steht unter https://rancher.com/docs/rancher/v1.6/en/catalog/private-catalog/
- Texte können über mehrere Zeilen geschrieben werden, wenn sie mit einem
|
begonnen werden
Datei docker-compose.yml.tpl
Beispiel:
version: '2' services: de-ondics-pacman-app: image: registry.app-pool.scaleit-i40.de/r16/enterprise/de-ondics-pacman-app:1.9 restart: always labels: rap.host: ${DOMAINPREFIX} rap.sso_disabled: "true" {{- if eq .Values.ssoproxy "false"}} ports: - "${APP_DOMAIN_PORT}:80" {{- end}} de-ondics-pacman-registration: image: registry.app-pool.scaleit-i40.de/r16/enterprise/de-ondics-pacman-registration:1.3 restart: always environment: - APP_ID=de-ondics-pacman ...
Hinweise:
- Die Datei-Ending .tpl aktiviert die Variablen-Ersetzung von
{{...]]
in Rancher, z.B. if-then-Konstrukte - Die Version muss 2 sein
- Die Angaben unter
labels:
sind für die Namensadressierung in der EE-Version erforderlich - Die Angaben unter
ports:
sind für die Adressierung des Containers in der CE-Version erforderlich
Schritt 7: Das Registration Sidecar
Das Registration Sidecar ist teil der App. Es läuft in einem eigenen Container und hat folgende Aufgaben:
- Registrierung der App im ScaleIT App-Verzeichnis (ETCD)
- Entfernung der Registrierung, wenn die App gestoppt wird
Was wird alles über die App im App-Verzeichnis eingetragen?
- App-Name
- ScaleIT App-ID
- Url des App-Icons
- Beschreibung / Kurzbeschreibung der App
- Url zum App-Start
- Url zur App-API
- Url zur App-Administration
- Infos zur Rancher-Umgebung (Host-IP, Nutzung von SSO, Stackname)
Das Registration Sidecar kann Out-Of-The-Box genutzt werden. Es ist verfügbar unter https://github.com/scaleit-i40/registration
Hier ein Beispiel, wie es im Rancher-Template (docker-compose.yml.tpl) der App eingebaut werden kann:
services: # Domain-Container mit der App: de-ondics-pacman-app: image: . . . # Registration sidecar de-ondics-pacman-registration: image: registry.app-pool.scaleit-i40.de/ondics/enterprise/de-ondics-pacman-registration:1.1 restart: always environment: - APP_ID=de-ondics-pacman - APP_NAME=ScaleIT Pacman - APP_TITLE=ScaleIT Pacman - APP_SHORT_DESCRIPTION=ScaleIT Pacman - APP_DESCRIPTION=ScaleIT Pacman Web-Game - APP_DOMAIN_DESCRIPTION=ScaleIT Pacman - APP_CATEGORY=domainApp - APP_DOMAIN_PORT=${APP_DOMAIN_PORT} - APP_DOMAIN_PATH=/ - APP_DOMAIN_SERVICENAME= - APP_ICON_PORT=${APP_SIDECAR_WEBCONTENT_PORT} - APP_ICON_PATH=/icon.png - APP_ICON_SERVICENAME=webcontent - APP_API_PORT=${APP_SIDECAR_WEBCONTENT_PORT} - APP_API_PATH=/ - APP_API_SERVICENAME=webcontent - APP_ADMIN_PORT=${APP_DOMAIN_PORT} - APP_ADMIN_PATH=/ - APP_ADMIN_SERVICENAME= - HOST_IP=${HOST_IPADDR} - SSO_ACTIVATED=${ssoproxy} - SSO_APP_PREFIX=${DOMAINPREFIX} - STACK_NAME={{ .Stack.Name }}
Hinweise:
- Der Image-Name des Registration Sidecars muss der gleiche sein, der auch in den App-Pool gepuusht wurde.
- Die Rancher-Variablen
Vorlage:...
müssen als Rancher-Questions abgefragt werden. - Die Variable
Vorlage:.Stack.Name
ist eine von Rancher vordefinierte Variable und enthält den vom Benutzer bei der Installation vergebenen Stacknamen
Die Rancher-Questions für das Registration Sidecar können so spezifiziert werden:
questions: - variable: ssoproxy description: | Mit dem SSO-Proxy ist https und die Namensauflösung aktiviert. Der DNS muss korrekt eingerichtet sein. label: "ScaleIT SSO-Proxy benutzen?" required: true default: false type: boolean - variable: DOMAINPREFIX description: "Name für die App (für die URL)" label: "Name (für URL)" type: string required: true default: "pacman" - variable: HOST_IPADDR description: "Host-TCP/IP-Adresse, kann auch Domainname dieser ScaleIT-Instanz sein" label: "IP-Adresse dieser ScaleIT-Instanz (für ETCD,...)" type: string required: true - variable: APP_DOMAIN_PORT description: "Port dieser App (Voreinstellung: 51505)" label: "TCP-Port für diese App" type: int required: true default: 51505 - variable: APP_SIDECAR_WEBCONTENT_PORT description: "TCP-Port für statischen Content dieser App (Voreinstellung: 51506)" label: "TCP-Port für statischen Content dieser App" type: int required: true default: 51506
Hinweise dazu:
- Die Ports sind nur relevant, wenn SSO nicht verwendet wird, z.B. in der CE-Version. Sie sind trotzdem immer anzugeben, da eine App sowohl in der CE als auch in der EE Version laufen muss.
- Die Port-Nummern sind so zu vergeben, dass es keinen Konflikt mit anderen Apps gibt. Sonst kann eine App nicht korrekt installiert werden.
- Wenn die App in offizielle App-Pools aufgenommen werden soll, müssen die Port-Nummern vom App-Pool-Betreiber vergeben werden
Schritt 8: Veröffentlichung im App-Pool
Eine App kann in Rancher installiert werden, wenn
- das Rancher-Template der app in einem Git-Server zur Verfügung steht
- die Images aus einer Docker-Registry geladen werden können
Genau dafür gibt es den ScaleIT-App-Pool. Er umfasst beide Komponenten.
Es gibt folgende Arten von App-Pools:
- Lokaler App-Pool (CE): Er ist in jeder Community-Edition ScaleIT-Installation enthalten
- Lokaler App-Pool (EE): Er ist in jeder Enterprise-Installation als eigene App enthalten. Damit sind zusätzlich App-Exporte und App-Importe möglich
- Community App-Pool: Dieser ist in jeder CE-ScaleIT-Instanz konfiguriert
- Enterprise App-Pool: Dieser ist in jeder EE-ScaleIT-Instanz konfiguriert
Das nachfolgende Beispiel (Pacman-App) zeigt, wie eine App in den lokalen App-Pool (CE oder EE) gebracht wird. Hierzu erforderlich ist folgendes:
- Eine Datei
/docker-compose.yml
im Root-Verzeichnis der App - Ein Rancher-Template in der App uunter
/Resources/App-Pools/Rancher1.6/de-ondics-pacman
- Zugangsdaten zum lokalen App-Pool (Git und Docker Regisiry)
Datei /docker-compose.yml
Beispiel:
version: '2' services: de-ondics-pacman-app: build: DomainSoftware/pacman image: registry.app-pool.scaleit-i40.de/r16/enterprise/de-ondics-pacman-app:1.9 de-ondics-pacman-registration: build: PlatformSidecars/registration image: registry.app-pool.scaleit-i40.de/r16/enterprise/de-ondics-pacman-registration:1.3
Hinweise:
- Mit
build: DomainSoftware/pacman
wird das Domain-Image der App erstellt. Hierzu muss im Verzeichnis/DomainSoftware/pacman
ein entsprechendesDockerfile
liegen - Mit
image:
wird das erstellte Image so benannt, dass es im Rancher wieder geladen werden kann (es bekommt die Versionsnummer als Tag) - Environment-Variablen, Volumes, etc. sind nicht erforderlich, weil diese Images nicht lokal ausgeführt werden sollen, sondern erst durch Rancher (und dort gibt es dazu ein
docker-compose.yml.tpl
Die Images werden dann erstellt und in die Docker Registry gebracht:
$ docker-compose build $ docker-compose push
Rancher-Templates in App-Pool übertragen
Wenn die Rancher-Templates erstellt sind (Schritt 6), müssen sie in den App-Pool (Git-Server) übertragen bzw. dort aktualisiert werden.
Dieses sollte außerhalb des App-Verzeichnisses gemacht werden.
Beispiel für die erste App-Veröffentlichung:
$ cd /tmp $ git clone http://lokaler-app-pool.scaleit:1234/rancher-templates.git rancher-templates $ cd rancher-templates $ cp -rp /home/.../de-ondics-pacman . $ git config user.name myuser $ git config user.email myemail@ondics.de $ git add . $ git commit -m 'neue app de-ondics-pacman' -a $ git push origin master
Beispiel für App-Updates:
$ cd /tmp $ cd rancher-templates $ git pull origin master $ cp -rp /home/.../de-ondics-pacman . $ git add . $ git commit -m 'neue version von de-ondics-pacman' -a $ git push origin master
Hinweise:
- Die Zugangsdaten zum lokalen App-Pool stehen im Systemhandbuch der ScaleIT-Instanz
- Wenn eine Version gelöscht werden soll, muss das entsprechende Versions-Verzeichnis gelöscht werden
- Mit einem
Makefile
könnten diese Schritte einfacher ausgeführt werden
Schritt 9: Installation der App in Rancher
Folgende Schritte sind dazu erforderlich:
- Im Rancher-menü unter "Catalog" wird zunächst der lokale App-Pool ausgewählt
- Mit dem Button "Aktualisierung" (rechts oben) die neuesten Rancher-Templates aus dem Git-Server laden. Dann muss die neue/aktualisierte App angezeigt werden
- Die App auswählen
- Das Formular komplett ausfüllen. Die Daten für
docker-compose.yml.tpl
undrancher-compose.yml
können unten eingesehen werden (aufklappen) - Zum Abschluss die App über das Launchpad starten
Typische Fehler:
- Manifest kann nicht gefunden werden (wird rot angezeigt): Der Image-Namen oder das Image Tag (Versionsnummer) ist vermutlich falsch
- Die App startet und stoppt gleich wieder bzw. startet erneut: Das deutet auf einen Fehler im Dockerfile hin (in Rancher die Container-Logfiles anschauen)
- Die App kann per Url nicht aufgerufen werden: Stimmen die Rancher-Labels?
- Die App wird im Launchpad nicht angezeigt: Das Registration Sidecar funktioniert nicht richtig. Die Environment-Variablen müssen ggf. korrigiert werden
Zum Debugging eines einzelnen Containers kann dieser im Stack geöffent werden und dort die Logs angezeigt werden.
Tipp bei App-Updates:
Wenn nur der DomainContainer aktualisiert wurde, muss kein neues Rancher-Template erstellt werden, sondern es genügt, das Image in den lokalen App-Pool zu übertragen und dann im Rancher einen Container-Update auszuführen. Dann muss bei "Update Service" die Checkbox "Image vor der Erstellung immer laden" ausgewählt werden.
Fragen zur App-Entwicklung? Dafür haben wir ScaleIT I40-Forum erstellt unter https://forum.scaleit-i40.de