% ============================================ % STEP 06: GITEA INSTALLATION & SERVER-ÜBERSICHT % ============================================ \section{Gitea Installation und Server-Übersicht} \label{sec:step06} In diesem Schritt installieren wir \textbf{Gitea} – einen selbst gehosteten Git-Server mit Web-Oberfläche – als Ersatz für OneDev, das sich leider als fehlerhaft erwies. Am Ende dieses Kapitels hast du einen vollständigen Überblick über deinen Server: welche Container laufen, welche Ports offen sind, wo die Daten liegen und wie alles zusammenhängt. \subsection{Warum ein eigener Git-Server?} Bisher haben wir den Code manuell gebaut, als Docker-Image exportiert und per \texttt{scp} auf den Server kopiert – ein umständlicher Prozess, der bei jedem Update wiederholt werden muss. Ein eigener Git-Server mit CI/CD-Pipeline automatisiert diesen Ablauf: \begin{itemize} \item \textbf{Push = Deploy:} Code auf den Server pushen, Pipeline baut automatisch Docker-Images und startet Container neu \item \textbf{Versionierung:} Alle Änderungen sind nachvollziehbar, kein „das ging gestern noch"-Problem \item \textbf{Teamarbeit:} Mehrere Entwickler können gleichzeitig am Projekt arbeiten \item \textbf{Backup:} Das Repository liegt auf deinem Server, nicht bei GitHub/GitLab \end{itemize} \subsection{OneDev: Der gescheiterte Versuch} Ursprünglich wollten wir \textbf{OneDev} installieren – eine All-in-One-Lösung aus Git-Server, CI/CD-Pipeline, Issue-Tracker und Paket-Registry. OneDev bietet deutlich mehr Funktionen als Gitea, erwies sich jedoch als problematisch: \subsubsection{Installationsversuch mit Docker (Version \texttt{1dev/server:latest})} \begin{lstlisting}[language=YAML, caption={docker-compose.yml für OneDev}] services: onedev: image: 1dev/server:latest container_name: onedev restart: unless-stopped ports: - "6610:6610" - "6611:6611" volumes: - /var/run/docker.sock:/var/run/docker.sock - onedev_data:/opt/onedev volumes: onedev_data: \end{lstlisting} \textbf{Aufgetretene Probleme:} \begin{enumerate} \item \textbf{Container stoppt sofort:} Nach dem Start erschien in den Logs nur „INFO - Stopping application". Der Container beendete sich ohne Fehlermeldung. \item \textbf{Workaround mit fester Version:} Statt \texttt{latest} nutzten wir \texttt{1dev/server:11.6.13} – gleiches Problem. \item \textbf{Datenbank-Fehler:} Die entscheidende Fehlermeldung in \texttt{/opt/onedev/logs/server.log}: \begin{lstlisting}[language=Bash, caption={Kritischer Fehler beim OneDev-Start}] java.lang.IllegalStateException: EntityManagerFactory is closed at org.hibernate.internal.SessionFactoryImpl.validateNotClosed(...) \end{lstlisting} \textbf{Ursache:} OneDevs interne H2-Datenbank wurde beim ersten Start korrupt. Der Befehl \texttt{upgrade /opt/onedev} im Start-Script versuchte, eine nicht existierende Datenbank zu aktualisieren statt sie neu anzulegen. Selbst komplettes Löschen des Volumes und Neustart behoben das Problem nicht – die Logs wurden nach dem ersten Crash nicht mehr aktualisiert, der Container hing fest. \item \textbf{Versuch mit direkter Java-Installation:} Nachdem Docker scheiterte, luden wir die JAR-Datei herunter (57 Bytes – defekter Download) und stellten fest, dass Java nicht installiert war. Zu diesem Zeitpunkt war klar: Der Aufwand für OneDev steht in keinem Verhältnis zum Nutzen. \end{enumerate} \textbf{Fazit:} OneDev ist ein mächtiges Tool, aber die Docker-Images sind instabil. Für einen produktiven Einsatz wäre eine native Installation mit externer PostgreSQL-Datenbank empfehlenswert – für unsere Zwecke überdimensioniert. \subsection{Gitea: Die schlanke Alternative} \textbf{Gitea} ist ein in Go geschriebener Git-Server, der sich durch Einfachheit und geringe Ressourcenanforderungen auszeichnet. Im Vergleich zu OneDev: \begin{table}[h] \centering \caption{Vergleich OneDev vs. Gitea} \begin{tabular}{@{}p{3cm}ll@{}} \toprule \textbf{Merkmal} & \textbf{OneDev} & \textbf{Gitea} \\ \midrule Größe Image & $\sim$500 MB & $\sim$100 MB \\ Startzeit & 2–5 Minuten & $\sim$5 Sekunden \\ CI/CD & h Eingebaut & x Extern (z.B. Woodpecker CI) \\ Datenbank & H2 (intern, fehleranfällig) & SQLite3, PostgreSQL, MySQL \\ Komplexität & Hoch & Gering \\ Installation & Fehlgeschlagen & h In 10 Sekunden \\ \bottomrule \end{tabular} \end{table} \subsection{Gitea mit Docker installieren} \subsubsection{Schritt 1: OneDev rückstandslos entfernen} \begin{lstlisting}[language=Bash, caption={OneDev komplett löschen}] cd /opt/onedev docker compose down 2>/dev/null # Container stoppen cd / rm -rf /opt/onedev # Verzeichnis löschen docker system prune -af # Ungenutzte Images/Volumes entfernen \end{lstlisting} \texttt{docker system prune -af} löscht: \begin{itemize} \item Alle gestoppten Container \item Alle ungenutzten Netzwerke \item Alle ungenutzten Images \item Alle ungenutzten Build-Caches \end{itemize} Insgesamt wurden 806 MB Speicher freigegeben! \subsubsection{Schritt 2: Gitea-Verzeichnis und docker-compose.yml anlegen} \begin{lstlisting}[language=Bash, caption={Gitea-Verzeichnis vorbereiten}] mkdir -p /opt/gitea nano /opt/gitea/docker-compose.yml \end{lstlisting} \begin{lstlisting}[language=YAML, caption={docker-compose.yml für Gitea}] services: gitea: image: gitea/gitea:latest container_name: gitea restart: unless-stopped ports: - "3000:3000" # Web-UI - "2222:22" # SSH für Git-Operationen volumes: - gitea_data:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro volumes: gitea_data: \end{lstlisting} \textbf{Erklärung der Konfiguration:} \begin{itemize} \item \texttt{ports: "3000:3000"} – Die Web-Oberfläche ist auf Port 3000 erreichbar \item \texttt{ports: "2222:22"} – Git-Operationen per SSH laufen auf Port 2222 (weil Port 22 bereits vom Host-SSH belegt ist) \item \texttt{gitea\_data:/data} – Alle Gitea-Daten (Repositories, Konfiguration, Datenbank) liegen in diesem Volume \item \texttt{/etc/timezone:/etc/timezone:ro} – Übergibt die Zeitzone des Hosts an den Container (\texttt{ro} = read-only) \item \texttt{/etc/localtime:/etc/localtime:ro} – Übergibt die lokale Zeit \end{itemize} \subsubsection{Schritt 3: Container starten} \begin{lstlisting}[language=Bash, caption={Gitea starten}] cd /opt/gitea docker compose up -d \end{lstlisting} Ausgabe: \begin{lstlisting}[language=Bash, caption={Erfolgreicher Start}] [+] up 9/9 h Image gitea/gitea:latest Pulled h Network gitea_default Created h Volume gitea_gitea_data Created h Container gitea Started \end{lstlisting} \subsubsection{Schritt 4: Firewall öffnen} \begin{lstlisting}[language=Bash, caption={Port 3000 freigeben}] ufw allow 3000/tcp \end{lstlisting} \subsubsection{Schritt 5: Gitea im Browser einrichten} \textbf{URL:} \texttt{http://185.209.229.167:3000} Beim ersten Aufruf erscheint die Installationsseite. Folgende Einstellungen sind vorausgefüllt und können übernommen werden: \begin{itemize} \item \textbf{Datenbanktyp:} SQLite3 (einfachste Option, perfekt für kleine Teams) \item \textbf{Pfad:} \texttt{/data/gitea/gitea.db} \item \textbf{Server-Domain:} \texttt{185.209.229.167} \item \textbf{Gitea-Basis-URL:} \texttt{http://185.209.229.167:3000/} \item \textbf{SSH-Server-Port:} 22 \item \textbf{Gitea-HTTP-Listen-Port:} 3000 \end{itemize} \textbf{Wichtig:} Unter „Administratoreninstellungen" (ganz unten aufklappen) musst du einen Admin-Account anlegen: \begin{itemize} \item \textbf{Benutzername:} robert \item \textbf{Passwort:} [dein sicheres Passwort] \item \textbf{E-Mail:} robert@robre.de \end{itemize} Dann auf \textbf{„Gitea installieren"} klicken. Nach wenigen Sekunden ist Gitea einsatzbereit. \subsection{Vollständige Server-Übersicht} Nach diesem Schritt ergibt sich folgendes Gesamtbild deines Servers: \subsubsection{Laufende Docker-Container} \begin{table}[h] \centering \caption{Alle laufenden Container auf dem Server} \begin{tabular}{@{}llll@{}} \toprule \textbf{Container} & \textbf{Image} & \textbf{Ports} & \textbf{Aufgabe} \\ \midrule \texttt{gitea} & \texttt{gitea/gitea:latest} & 3000, 2222→22 & Git-Server mit Web-UI \\ \texttt{nginx-proxy} & \texttt{nginxproxy/nginx-proxy} & 80, 443 & Reverse Proxy \\ \texttt{acme-companion} & \texttt{nginxproxy/acme-companion} & – & SSL-Zertifikate (Let's Encrypt) \\ \texttt{fitness-web} & \texttt{fitness-web:latest} & 80 (intern) & React-Frontend \\ \texttt{fitness-api} & \texttt{fitness-api:latest} & 5000 (intern) & .NET 8 Backend \\ \bottomrule \end{tabular} \end{table} \subsubsection{Docker-Volumes (persistente Datenspeicher)} \begin{table}[h] \centering \caption{Volumes und ihre Inhalte} \begin{tabular}{@{}ll@{}} \toprule \textbf{Volume} & \textbf{Inhalt} \\ \midrule \texttt{gitea\_gitea\_data} & Gitea: Repositories, Datenbank, Konfiguration \\ \texttt{fitness\_fitness-data} & Fitness-App: SQLite-Datenbank (\texttt{/app/data}) \\ \texttt{fitness\_certs} & SSL-Zertifikate von Let's Encrypt \\ \texttt{fitness\_html} & Challenge-Dateien für Let's Encrypt \\ \texttt{fitness\_vhost} & Nginx-Konfiguration pro Virtual Host \\ \texttt{fitness\_acme} & ACME-Kontodaten \\ \bottomrule \end{tabular} \end{table} \subsubsection{Firewall (nur diese Ports sind offen!)} \begin{table}[h] \centering \caption{Geöffnete Ports} \begin{tabular}{@{}cll@{}} \toprule \textbf{Port} & \textbf{Dienst} & \textbf{Begründung} \\ \midrule 22 & SSH & Server-Verwaltung \\ 80 & HTTP & Fitness-App, leitet auf HTTPS um \\ 443 & HTTPS & Fitness-App (verschlüsselt), PWA-Pflicht! \\ 3000 & Gitea Web & Git-Server-Oberfläche \\ \bottomrule \end{tabular} \end{table} \subsubsection{Installierte Systempakete} \begin{itemize} \item \texttt{docker-ce}, \texttt{docker-ce-cli}, \texttt{docker-compose-plugin} – Docker-Plattform \item \texttt{fail2ban} – Bruteforce-Schutz für SSH \item \texttt{git} – Git (von Gitea genutzt) \end{itemize} Der Server ist bewusst schlank gehalten – keine überflüssigen Dienste, keine unnötigen Pakete. \subsubsection{Verzeichnisstruktur unter /opt} \begin{lstlisting}[language=Bash, caption={Projektverzeichnisse auf dem Server}] /opt/ containerd/ # Docker-Laufzeitumgebung fitness/ # docker-compose.yml fuer Fitness-App docker-compose.yml gitea/ # docker-compose.yml fuer Gitea docker-compose.yml \end{lstlisting} \subsection{Wie Gitea in die Infrastruktur passt} \textbf{Aktueller Datenfluss:} \textbf{Aktueller Datenfluss:} \begin{verbatim} Du (lokal) Server (testserver) --------- ------------------ git push ----------------> Gitea (Port 3000/2222) | | (spaeter: CI/CD-Pipeline) v Docker-Images bauen | v Container neustarten | v nginx-proxy (Port 80/443) | +-------+-------+ | | v v fitness-web fitness-api (Port 80) (Port 5000) \end{verbatim} \textbf{Was noch fehlt:} Die CI/CD-Pipeline in Gitea (z.B. mit Gitea Actions oder Woodpecker CI), die automatisch bei jedem Push Docker-Images baut und die Container aktualisiert. Das folgt in einem späteren Schritt. \subsection{Zusammenfassung} Nach diesem Schritt haben wir: \begin{itemize} \item OneDev aufgrund von Docker-Inkompatibilitäten verworfen \item Gitea erfolgreich mit Docker installiert (in unter 1 Minute!) \item Einen vollständigen Überblick über alle Container, Volumes, Ports und Verzeichnisse \item Die Grundlage für eine spätere CI/CD-Pipeline geschaffen \end{itemize} \textbf{Nützliche Befehle für die tägliche Verwaltung:} \begin{lstlisting}[language=Bash, caption={Server-Cockpit – wichtige Befehle}] # Alle laufenden Container docker ps # Alle Volumes docker volume ls # Firewall-Status ufw status verbose # Installierte Pakete apt list --installed | grep -E "docker|fail2ban" # Verzeichnisse unter /opt ls -la /opt/ \end{lstlisting}