Files

325 lines
12 KiB
TeX
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
% ============================================
% 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 & 25 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}