heyer.systems

Entwickeln

Eigene App

Vom leeren Repo zur App mit Container, VM-Image und Update – in sechs Schritten.

Das Prinzip

Eine iac-App beschreibt nur ihren Sollzustand – als Ansible-Rolle: Pakete, Benutzer, Dateien, Konfiguration. Dazu kommt ein Steckbrief (iac.json) mit Ports, Diensten und Healthcheck. Alles andere übernimmt iac-core:

  • Container und VM anlegen, dazu die Einzeiler ct.sh, vm.sh und adopt.sh
  • das VM-Image bauen und testen, die CI
  • Firewall, Dienste starten, Healthcheck, update, Module

Deshalb besteht ein App-Repo nur aus wenigen Dateien:

iac-meineapp/
├── iac.json            Steckbrief
├── tasks/main.yml      der Sollzustand (Ansible)          ← das Wesentliche
├── handlers/main.yml   Neustarts
├── templates/, files/  Konfigurationsdateien, systemd-Units
├── tests/smoke.sh      Prüfungen, die nur diese App braucht
├── README.md, CHANGELOG.md, AGENTS.md (+ Verweise für KI-Werkzeuge)
└── .forgejo/workflows/ lint.yml, image.yml – unverändert aus der Vorlage

Ansible läuft dabei lokal im Gast – kein Controller, kein Inventory, kein SSH von außen.

1. Vorbereiten

mkdir -p ~/repositories/iac && cd ~/repositories/iac
git clone https://git.heyer.systems/iac/iac-core.git
git clone https://git.heyer.systems/iac/iac-example.git

Lokal gebraucht werden shellcheck, jq, ansible-core (Lint) und Docker (Testen ohne Proxmox). Lies vorher iac-example in der Reihenfolge aus seinem README: eine kleine App, in der jede Möglichkeit genau einmal vorkommt.

2. Gerüst anlegen

iac-core/tools/new-app.sh meineapp        # legt iac-meineapp/ an, überschreibt nie etwas

3. Steckbrief: iac.json

{
  "name": "meineapp",
  "title": "Meine App",
  "description": "Ein Satz für die Website.",
  "upstream": { "type": "debian", "repo": "meineapp" },
  "supports": ["lxc", "vm"],
  "defaults": {
    "lxc": { "cpu": 1, "ram": 512, "disk": 4 },
    "vm":  { "cpu": 1, "ram": 1024, "disk": 8 }
  },
  "ports": [{ "port": 80, "proto": "tcp", "description": "Web", "access": "public" }],
  "services": ["meineapp"],
  "health": { "url": "http://127.0.0.1/" },
  "access": "Web: http://{ip}/"
}

Aus ports entsteht die Firewall, services startet und prüft iac-core, health muss nach jeder Installation antworten. Alle Felder: App-Referenz.

4. Sollzustand: tasks/main.yml

Ganz normales Ansible – nur mit den eingebauten Modulen (ansible.builtin.*):

- name: Package meineapp
  ansible.builtin.apt:
    name: meineapp
    install_recommends: false

- name: Configuration
  ansible.builtin.template:
    src: config.yml.j2              # templates/config.yml.j2
    dest: /etc/meineapp/config.yml
    owner: root
    group: root
    mode: "0644"
  notify: Restart meineapp          # handlers/main.yml

Dazu der Handler in handlers/main.yml:

- name: Restart meineapp
  ansible.builtin.systemd_service:
    name: meineapp
    state: restarted
    daemon_reload: true
  when: not iac_build               # im Image-Build läuft kein systemd

Drei Regeln, dann passt es:

  1. Idempotent – ein zweiter Lauf ändert nichts. Mit Modulen statt command/shell bekommst du das geschenkt.
  2. Neustarts nur per notify – so unterbricht ein update ohne Änderung den Dienst nicht.
  3. Dienste startest du nicht selbst – das macht iac-core für alles in services.

Software von außerhalb Debians (GitHub-Release, eigene Download-Seite)? Die Rolle iac_version entscheidet, welche Version installiert wird: Versionen.

5. Testen

iac-core/tools/lint.sh iac-meineapp            # Tippfehler, falsche Felder, Ansible-Syntax
iac-core/tools/dev-container.sh iac-meineapp   # installieren + Smoke-Test, ohne Proxmox
iac-core/tools/dev-container.sh iac-meineapp   # zweiter Lauf: idempotent? (changed=1)

Hineinschauen: docker exec -it iac-dev-meineapp bash, Log in /var/log/iac/iac.log.

6. Veröffentlichen

  1. Repo iac-meineapp in der Organisation iac anlegen (öffentlich) und pushen.
  2. Die CI prüft jeden Push. Ein Push auf main baut außerdem das VM-Image, startet es in einer Test-VM und veröffentlicht es nur, wenn alle Prüfungen bestehen.
  3. Die Website liest stündlich alle öffentlichen iac-*-Repos ein. Danach gibt es die App-Seite mit Generator und die Einzeiler /meineapp/ct.sh und /meineapp/vm.sh.

Weiter: App-Referenz – alle Felder, Variablen, Hilfsrollen und häufige Fehler.