Add step 03: Docker, Deployment & First Live App
- Introduced a new section on building Docker images and deploying the fitness app. - Added detailed explanations of Docker concepts, including images, containers, and volumes. - Included three Dockerfiles for the backend and frontend, with line-by-line explanations. - Updated main.tex to include step_03.tex. - Modified main.toc to reflect new sections and subsections. - Updated main.fls and main.log to include new font inputs and log entries related to the new content. - Adjusted font configurations for FiraMono and FiraSans in main.fls. - Updated PDF and synctex files to reflect changes in the document structure.
This commit is contained in:
+40
-3
@@ -69,12 +69,49 @@
|
||||
\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Firewall aktivieren}{10}{subsubsection.2.5.3}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {21}Firewall aktivieren}{10}{lstlisting.21}\protected@file@percent }
|
||||
\BKM@entry{id=19,dest={73756273656374696F6E2E322E36},srcline={179}}{5C3337365C3337375C3030305A5C303030755C303030735C303030615C3030306D5C3030306D5C303030655C3030306E5C303030665C303030615C303030735C303030735C303030755C3030306E5C30303067}
|
||||
\BKM@entry{id=20,dest={73656374696F6E2E33},srcline={5}}{5C3337365C3337375C303030445C3030306F5C303030635C3030306B5C303030655C303030725C3030302D5C303030495C3030306D5C303030615C303030675C303030655C303030735C3030305C3034305C303030625C303030615C303030755C303030655C3030306E5C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C303030415C303030705C303030705C3030305C3034305C303030645C303030655C303030705C3030306C5C3030306F5C303030795C303030655C3030306E}
|
||||
\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.4}Konfiguration überprüfen}{11}{subsubsection.2.5.4}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {22}Firewall-Status mit Details anzeigen}{11}{lstlisting.22}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {23}Erwartete Firewall-Ausgabe (gekürzt)}{11}{lstlisting.23}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Zusammenfassung}{11}{subsection.2.6}\protected@file@percent }
|
||||
\BKM@entry{id=21,dest={73756273656374696F6E2E332E31},srcline={10}}{5C3337365C3337375C303030575C303030615C303030735C3030305C3034305C303030695C303030735C303030745C3030305C3034305C303030445C3030306F5C303030635C3030306B5C303030655C303030725C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C303030775C303030615C303030725C303030755C3030306D5C3030305C3034305C3030306E5C303030755C303030745C3030307A5C303030655C3030306E5C3030305C3034305C303030775C303030695C303030725C3030305C3034305C303030655C303030735C3030303F}
|
||||
\BKM@entry{id=22,dest={73756273656374696F6E2E332E32},srcline={27}}{5C3337365C3337375C303030445C303030695C303030655C3030305C3034305C303030645C303030725C303030655C303030695C3030305C3034305C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C303030655C303030735C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C}
|
||||
\BKM@entry{id=23,dest={73756273756273656374696F6E2E332E322E31},srcline={29}}{5C3337365C3337375C303030425C303030615C303030635C3030306B5C303030655C3030306E5C303030645C3030302D5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C303030655C3030303A5C3030305C3034305C303030615C303030705C303030705C303030735C3030302F5C303030615C303030705C303030695C3030302F5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C30303065}
|
||||
\@writefile{toc}{\contentsline {section}{\numberline {3}Docker-Images bauen und App deployen}{12}{section.3}\protected@file@percent }
|
||||
\newlabel{sec:step03}{{3}{12}{Docker-Images bauen und App deployen}{section.3}{}}
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}Was ist Docker und warum nutzen wir es?}{12}{subsection.3.1}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Die drei Dockerfiles im Detail}{12}{subsection.3.2}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsubsection}{\numberline {3.2.1}Backend-Dockerfile: \texttt {apps/api/Dockerfile}}{12}{subsubsection.3.2.1}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {24}Dockerfile für das .NET Backend}{12}{lstlisting.24}\protected@file@percent }
|
||||
\BKM@entry{id=24,dest={73756273756273656374696F6E2E332E322E32},srcline={64}}{5C3337365C3337375C303030465C303030725C3030306F5C3030306E5C303030745C303030655C3030306E5C303030645C3030302D5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C303030655C3030303A5C3030305C3034305C303030615C303030705C303030705C303030735C3030302F5C303030775C303030655C303030625C3030302F5C303030445C3030306F5C303030635C3030306B5C303030655C303030725C303030665C303030695C3030306C5C30303065}
|
||||
\@writefile{toc}{\contentsline {subsubsection}{\numberline {3.2.2}Frontend-Dockerfile: \texttt {apps/web/Dockerfile}}{13}{subsubsection.3.2.2}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {25}Dockerfile für das React Frontend}{13}{lstlisting.25}\protected@file@percent }
|
||||
\BKM@entry{id=25,dest={73756273756273656374696F6E2E332E322E33},srcline={99}}{5C3337365C3337375C3030304E5C303030675C303030695C3030306E5C303030785C3030302D5C3030304B5C3030306F5C3030306E5C303030665C303030695C303030675C303030755C303030725C303030615C303030745C303030695C3030306F5C3030306E5C3030303A5C3030305C3034305C303030615C303030705C303030705C303030735C3030302F5C303030775C303030655C303030625C3030302F5C3030306E5C303030675C303030695C3030306E5C303030785C3030302E5C303030635C3030306F5C3030306E5C30303066}
|
||||
\BKM@entry{id=26,dest={73756273656374696F6E2E332E33},srcline={130}}{5C3337365C3337375C303030445C303030615C303030735C3030305C3034305C303030425C303030615C303030635C3030306B5C303030655C3030306E5C303030645C3030303A5C3030305C3034305C303030505C303030725C3030306F5C303030675C303030725C303030615C3030306D5C3030302E5C303030635C303030735C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C}
|
||||
\@writefile{toc}{\contentsline {subsubsection}{\numberline {3.2.3}Nginx-Konfiguration: \texttt {apps/web/nginx.conf}}{14}{subsubsection.3.2.3}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {26}Nginx-Konfiguration mit Reverse Proxy}{14}{lstlisting.26}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}Das Backend: Program.cs im Detail}{15}{subsection.3.3}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {27}Vollständige Program.cs}{15}{lstlisting.27}\protected@file@percent }
|
||||
\BKM@entry{id=27,dest={73756273656374696F6E2E332E34},srcline={186}}{5C3337365C3337375C303030445C303030655C303030725C3030305C3034305C303030415C303030505C303030495C3030302D5C303030435C3030306C5C303030695C303030655C3030306E5C303030745C3030303A5C3030305C3034305C303030635C3030306C5C303030695C303030655C3030306E5C303030745C3030302E5C303030745C303030735C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C}
|
||||
\BKM@entry{id=28,dest={73756273656374696F6E2E332E35},srcline={226}}{5C3337365C3337375C303030445C303030615C303030735C3030305C3034305C303030465C303030725C3030306F5C3030306E5C303030745C303030655C3030306E5C303030645C3030303A5C3030305C3034305C303030415C303030705C303030705C3030302E5C303030745C303030735C303030785C3030305C3034305C303030695C3030306D5C3030305C3034305C303030445C303030655C303030745C303030615C303030695C3030306C}
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.4}Der API-Client: client.ts im Detail}{16}{subsection.3.4}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {28}Manueller API-Client}{16}{lstlisting.28}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.5}Das Frontend: App.tsx im Detail}{16}{subsection.3.5}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {29}Hauptkomponente mit Workout-Liste und Formular}{16}{lstlisting.29}\protected@file@percent }
|
||||
\BKM@entry{id=29,dest={73756273656374696F6E2E332E36},srcline={265}}{5C3337365C3337375C303030495C3030306D5C303030615C303030675C303030655C303030735C3030305C3034305C303030625C303030615C303030755C303030655C3030306E}
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.6}Images bauen}{17}{subsection.3.6}\protected@file@percent }
|
||||
\BKM@entry{id=30,dest={73756273656374696F6E2E332E37},srcline={287}}{5C3337365C3337375C303030495C3030306D5C303030615C303030675C303030655C303030735C3030305C3034305C303030655C303030785C303030705C3030306F5C303030725C303030745C303030695C303030655C303030725C303030655C3030306E5C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C303030615C303030755C303030665C3030305C3034305C303030645C303030655C3030306E5C3030305C3034305C303030535C303030655C303030725C303030765C303030655C303030725C3030305C3034305C3030306B5C3030306F5C303030705C303030695C303030655C303030725C303030655C3030306E}
|
||||
\BKM@entry{id=31,dest={73756273656374696F6E2E332E38},srcline={311}}{5C3337365C3337375C303030435C3030306F5C3030306E5C303030745C303030615C303030695C3030306E5C303030655C303030725C3030305C3034305C303030615C303030755C303030665C3030305C3034305C303030645C303030655C3030306D5C3030305C3034305C303030535C303030655C303030725C303030765C303030655C303030725C3030305C3034305C303030735C303030745C303030615C303030725C303030745C303030655C3030306E}
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.7}Images exportieren und auf den Server kopieren}{18}{subsection.3.7}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {30}Images exportieren und kopieren}{18}{lstlisting.30}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.8}Container auf dem Server starten}{18}{subsection.3.8}\protected@file@percent }
|
||||
\@writefile{lol}{\contentsline {lstlisting}{\numberline {31}Docker-Netzwerk, Volume und Container anlegen}{18}{lstlisting.31}\protected@file@percent }
|
||||
\BKM@entry{id=32,dest={73756273656374696F6E2E332E39},srcline={345}}{5C3337365C3337375C303030415C303030755C303030665C303030675C303030655C303030745C303030725C303030655C303030745C303030655C3030306E5C303030655C3030305C3034305C303030505C303030725C3030306F5C303030625C3030306C5C303030655C3030306D5C303030655C3030305C3034305C303030755C3030306E5C303030645C3030305C3034305C3030304C5C3030305C3336365C303030735C303030755C3030306E5C303030675C303030655C3030306E}
|
||||
\BKM@entry{id=33,dest={73756273656374696F6E2E332E3130},srcline={372}}{5C3337365C3337375C3030305A5C303030755C303030735C303030615C3030306D5C3030306D5C303030655C3030306E5C303030665C303030615C303030735C303030735C303030755C3030306E5C30303067}
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.9}Aufgetretene Probleme und Lösungen}{19}{subsection.3.9}\protected@file@percent }
|
||||
\@writefile{toc}{\contentsline {subsection}{\numberline {3.10}Zusammenfassung}{20}{subsection.3.10}\protected@file@percent }
|
||||
\global\@namedef{scr@dte@section@lastmaxnumwidth}{11.00392pt}
|
||||
\global\@namedef{scr@dte@subsection@lastmaxnumwidth}{19.9919pt}
|
||||
\global\@namedef{scr@dte@subsubsection@lastmaxnumwidth}{28.39188pt}
|
||||
\global\@namedef{scr@dte@subsection@lastmaxnumwidth}{24.81589pt}
|
||||
\global\@namedef{scr@dte@subsubsection@lastmaxnumwidth}{28.41586pt}
|
||||
\@writefile{toc}{\providecommand\tocbasic@end@toc@file{}\tocbasic@end@toc@file}
|
||||
\gdef \@abspage@last{11}
|
||||
\gdef \@abspage@last{20}
|
||||
|
||||
+11
-6
@@ -1,7 +1,7 @@
|
||||
# Fdb version 4
|
||||
["pdflatex"] 1778074526.98351 "/home/computer/projects/fitness-app/LateX/main.tex" "main.pdf" "main" 1778074529.108 0
|
||||
["pdflatex"] 1778081248.77398 "/home/computer/projects/fitness-app/LateX/main.tex" "main.pdf" "main" 1778081251.03276 0
|
||||
"/etc/texmf/web2c/texmf.cnf" 1776891072.07073 475 c0e671620eb5563b2130f56340a5fde8 ""
|
||||
"/home/computer/projects/fitness-app/LateX/main.tex" 1778074523.06229 8392 bdb3148a64fa8b2e535471367f7e4f1e ""
|
||||
"/home/computer/projects/fitness-app/LateX/main.tex" 1778081236.97865 8413 41aad5c5d32e893cafb487728bf379a6 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc" 1570828436 2978 6d777d1174162fa94ff58f36782f4570 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc" 1570828436 3385 21a7e8c8dac3c39de5acda2c56e7bd7e ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_iln36p.enc" 1570828436 3071 cfa92ee28d698dd9275559d9d1c3a233 ""
|
||||
@@ -22,6 +22,8 @@
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1136768653 1120 8b7d695260f3cff42e636090a8002094 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1--base.tfm" 1556836886 1324 0a7092c8d43950fc7585219358b7afd2 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1.tfm" 1556836886 1376 464dde77446a9d55bb77e0f8000924a5 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1--base.tfm" 1559682857 1544 e1d8938f0433eb7b9914e10941e55e1c ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1.tfm" 1559682857 1596 ad99e5db8e2fbbe207fd41ae14f51134 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1--base.tfm" 1556836886 1308 9050f92394f218407f72f188eb4d1a33 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm" 1556836886 1360 dfbacd55e2269d2ff422e3de3884feab ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Bold-osf-t1--base.tfm" 1558644978 1796 11e21ab836516246ef24a10a351d2541 ""
|
||||
@@ -32,10 +34,12 @@
|
||||
"/usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-ts1.tfm" 1554498137 1456 8ee3e51b7984f3df81900715b34ebdeb ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1248133631 32569 5e5ddc8df908dea60932f3c484a54c0d ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Bold.pfb" 1559682857 205973 90710d15f8d1d4d4f6bdd622521b398f ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Oblique.pfb" 1559682857 271356 1b50b6cf73a83edb93bda1aa45a1679d ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Regular.pfb" 1559682857 192962 8eef27b162fb2864c33843b9ad6cdbc2 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Bold.pfb" 1554498137 485112 0bd86946afd04160ee1229d86cd76902 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Regular.pfb" 1554498137 457228 86b22575fa05027949aa646d89908746 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Bold-tosf-t1.vf" 1556836886 1692 8b219f0f8fd0e6dfdce79d58d9f68088 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Oblique-tosf-t1.vf" 1559682857 1696 f96e3873fa4905e557b6ed829aef1f0f ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf" 1556836886 1696 af65436ccdde59c952e95f8cc457dbb5 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Bold-osf-t1.vf" 1558644978 1724 1ac6e230aa57cf8ba8f965fbceffd874 ""
|
||||
"/usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-t1.vf" 1558644978 1724 e5cbcafbef1b8b346466340ef078339c ""
|
||||
@@ -130,12 +134,13 @@
|
||||
"/usr/share/texmf/web2c/texmf.cnf" 1707919699 40399 f2c302f7d2af602abb742093540a5834 ""
|
||||
"/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map" 1776891108.46284 5472669 54eaf61a88b6b7896ebd0dac973cb29c ""
|
||||
"/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1776891271 8211336 7fc26d317f030a4855527787ba3b41d3 ""
|
||||
"main.aux" 1778074529.0082 14507 a445efc066d6f1fab05528a5ef320e35 "pdflatex"
|
||||
"main.out" 1778074528.61242 0 d41d8cd98f00b204e9800998ecf8427e "pdflatex"
|
||||
"main.tex" 1778074523.06229 8392 bdb3148a64fa8b2e535471367f7e4f1e ""
|
||||
"main.toc" 1778074529.0122 1951 c589379c8507d6fcc5ff4c6e2179786a "pdflatex"
|
||||
"main.aux" 1778081250.91843 23846 804a4d87b04ab7403c0abdcb70108b86 "pdflatex"
|
||||
"main.out" 1778081250.30135 0 d41d8cd98f00b204e9800998ecf8427e "pdflatex"
|
||||
"main.tex" 1778081236.97865 8413 41aad5c5d32e893cafb487728bf379a6 ""
|
||||
"main.toc" 1778081250.92443 3404 2187def0eadc57fc64da16e547916901 "pdflatex"
|
||||
"step_01.tex" 1778074119.30934 10807 dd7fc11a20ecebed2f07638ceddcf838 ""
|
||||
"step_02.tex" 1778074524.52004 9161 30219f0c68c4ae118067f27c09a123fb ""
|
||||
"step_03.tex" 1778081246.2535 17575 0dde7ed301abaec40a3b2d19357b160b ""
|
||||
(generated)
|
||||
"main.aux"
|
||||
"main.log"
|
||||
|
||||
+17
-7
@@ -230,11 +230,18 @@ INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm
|
||||
OUTPUT main.toc
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Bold-osf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Bold-osf-t1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-t1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc
|
||||
INPUT ./step_01.tex
|
||||
INPUT ./step_01.tex
|
||||
INPUT step_01.tex
|
||||
@@ -242,18 +249,11 @@ INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-ts1.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-t1.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-ts1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-ts1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Regular-tosf-t1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraSans-Regular-osf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraSans-Regular-osf-t1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Regular-tosf-t1.vf
|
||||
@@ -264,10 +264,20 @@ INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1-
|
||||
INPUT ./step_02.tex
|
||||
INPUT ./step_02.tex
|
||||
INPUT step_02.tex
|
||||
INPUT ./step_03.tex
|
||||
INPUT ./step_03.tex
|
||||
INPUT step_03.tex
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Bold-tosf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Bold-tosf-t1--base.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1.tfm
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/vf/public/fira/FiraMono-Oblique-tosf-t1.vf
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/fira/FiraMono-Oblique-tosf-t1--base.tfm
|
||||
INPUT main.aux
|
||||
INPUT ./main.out
|
||||
INPUT ./main.out
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Bold.pfb
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Oblique.pfb
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Regular.pfb
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Bold.pfb
|
||||
INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Regular.pfb
|
||||
|
||||
+67
-29
@@ -1,4 +1,4 @@
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) (preloaded format=pdflatex 2026.4.22) 6 MAY 2026 15:35
|
||||
This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) (preloaded format=pdflatex 2026.4.22) 6 MAY 2026 17:27
|
||||
entering extended mode
|
||||
restricted \write18 enabled.
|
||||
file:line:error style messages enabled.
|
||||
@@ -615,13 +615,26 @@ File: mt-cmr.cfg 2013/05/19 v2.2 microtype config. file: Computer Modern Roman (
|
||||
|
||||
{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_iln36p.enc}]
|
||||
Package tocbasic Info: character protrusion at toc deactivated on input line 239.
|
||||
(./main.toc)
|
||||
(./main.toc
|
||||
LaTeX Font Info: Trying to load font information for T1+FiraMono-TOsF on input line 24.
|
||||
(/usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
File: T1FiraMono-TOsF.fd 2019/10/10 (autoinst) Font definitions for T1/FiraMono-TOsF.
|
||||
)
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/n' in size <12> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/regular/n' tried instead on input line 24.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/n' will be
|
||||
(Font) scaled to size 10.79993pt on input line 24.
|
||||
Package microtype Info: Loading generic protrusion settings for font family
|
||||
(microtype) `FiraMono-TOsF' (encoding: T1).
|
||||
(microtype) For optimal results, create family-specific settings.
|
||||
(microtype) See the microtype manual for details.
|
||||
)
|
||||
\tf@toc=\write4
|
||||
\openout4 = `main.toc'.
|
||||
|
||||
[2
|
||||
|
||||
] (./step_01.tex
|
||||
{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc}] (./step_01.tex
|
||||
LaTeX Font Info: Trying to load font information for TS1+FiraSans-OsF on input line 21.
|
||||
(/usr/share/texlive/texmf-dist/tex/latex/fira/TS1FiraSans-OsF.fd
|
||||
File: TS1FiraSans-OsF.fd 2019/10/10 (autoinst) Font definitions for TS1/FiraSans-OsF.
|
||||
@@ -634,18 +647,6 @@ Package microtype Info: Loading generic protrusion settings for font family
|
||||
(microtype) `FiraSans-OsF' (encoding: TS1).
|
||||
(microtype) For optimal results, create family-specific settings.
|
||||
(microtype) See the microtype manual for details.
|
||||
LaTeX Font Info: Trying to load font information for T1+FiraMono-TOsF on input line 21.
|
||||
(/usr/share/texlive/texmf-dist/tex/latex/fira/T1FiraMono-TOsF.fd
|
||||
File: T1FiraMono-TOsF.fd 2019/10/10 (autoinst) Font definitions for T1/FiraMono-TOsF.
|
||||
)
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/n' in size <12> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/regular/n' tried instead on input line 21.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/n' will be
|
||||
(Font) scaled to size 10.79993pt on input line 21.
|
||||
Package microtype Info: Loading generic protrusion settings for font family
|
||||
(microtype) `FiraMono-TOsF' (encoding: T1).
|
||||
(microtype) For optimal results, create family-specific settings.
|
||||
(microtype) See the microtype manual for details.
|
||||
Package hyperref Info: bookmark level for unknown lstlisting defaults to 0 on input line 35.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/n' in size <10> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/regular/n' tried instead on input line 35.
|
||||
@@ -657,7 +658,7 @@ LaTeX Font Info: Font shape `T1/FiraSans-OsF/regular/n' will be
|
||||
(Font) scaled to size 10.0pt on input line 36.
|
||||
[3
|
||||
|
||||
{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc}{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d4q673.enc}] [4]
|
||||
{/usr/share/texlive/texmf-dist/fonts/enc/dvips/fira/fir_d67aat.enc}] [4]
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/b/n' in size <10> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/bold/n' tried instead on input line 140.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/bold/n' will be
|
||||
@@ -709,7 +710,44 @@ Overfull \hbox (3.891pt too wide) in paragraph at lines 77--78
|
||||
[]\T1/FiraSans-OsF/regular/n/12 (-20) HTTP-Verbindungen kön-nen von An-grei-fern ver-än-dert wer-den (Man-in-the-Middle)
|
||||
[]
|
||||
|
||||
[9] [10]) [11] (./main.aux)
|
||||
[9] [10]) (./step_03.tex [11]
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/b/n' in size <12> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/bold/n' tried instead on input line 29.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/bold/n' will be
|
||||
(Font) scaled to size 10.79993pt on input line 29.
|
||||
[12] [13]
|
||||
Overfull \hbox (74.52446pt too wide) in paragraph at lines 125--126
|
||||
[]\T1/FiraMono-TOsF/regular/n/12 location / \T1/FiraSans-OsF/regular/n/12 (-20) ^^U An-fra-gen an die Haupt-sei-te $\OMS/cmsy/m/n/12 !$ \T1/FiraSans-OsF/regular/n/12 (-20) lie-fert React-Dateien aus \T1/FiraMono-TOsF/regular/n/12 /usr/share/nginx/html
|
||||
[]
|
||||
|
||||
|
||||
Overfull \hbox (36.2292pt too wide) in paragraph at lines 126--127
|
||||
[]\T1/FiraMono-TOsF/regular/n/12 location /api/ \T1/FiraSans-OsF/regular/n/12 (-20) ^^U An-fra-gen an \T1/FiraMono-TOsF/regular/n/12 /api/* $\OMS/cmsy/m/n/12 !$ \T1/FiraSans-OsF/regular/n/12 (-20) lei-tet sie an das Ba-ckend (\T1/FiraMono-TOsF/regular/n/12 fitness-api:5000\T1/FiraSans-OsF/regular/n/12 (-20) )
|
||||
[]
|
||||
|
||||
[14] [15]
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/m/it' in size <10> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/regular/it' tried instead on input line 214.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/it' in size <10> not available
|
||||
(Font) Font shape `T1/FiraMono-TOsF/regular/sl' tried instead on input line 214.
|
||||
LaTeX Font Info: Font shape `T1/FiraMono-TOsF/regular/sl' will be
|
||||
(Font) scaled to size 8.99994pt on input line 214.
|
||||
|
||||
Overfull \hbox (5.87708pt too wide) in paragraph at lines 220--221
|
||||
[]\T1/FiraMono-TOsF/regular/n/12 API_BASE = \T1/FiraSans-OsF/regular/n/12 (-20) ^^U Kei-ne ab-so-lu-te URL! Statt-des-sen re-la-ti-ve Pfa-de wie \T1/FiraMono-TOsF/regular/n/12 /api/workouts\T1/FiraSans-OsF/regular/n/12 (-20) .
|
||||
[]
|
||||
|
||||
[16] [17] [18]
|
||||
Overfull \hbox (10.69511pt too wide) in paragraph at lines 341--342
|
||||
[]\T1/FiraMono-TOsF/regular/n/12 -v fitness-data:/app/data \T1/FiraSans-OsF/regular/n/12 (-20) ^^U Bin-det das Vo-lu-me \T1/FiraMono-TOsF/regular/n/12 fitness-data \T1/FiraSans-OsF/regular/n/12 (-20) in den Container-
|
||||
[]
|
||||
|
||||
|
||||
Overfull \hbox (0.99844pt too wide) in paragraph at lines 361--362
|
||||
[]\T1/FiraSans-OsF/regular/n/12 (-20) Ursache: Im Cli-ent stand \T1/FiraMono-TOsF/regular/n/12 const API_BASE = "http://192.168.178.189:5107"\T1/FiraSans-OsF/regular/n/12 (-20) .
|
||||
[]
|
||||
|
||||
[19]) [20] (./main.aux)
|
||||
***********
|
||||
LaTeX2e <2023-11-01> patch level 1
|
||||
L3 programming layer <2024-01-22>
|
||||
@@ -718,18 +756,18 @@ Package rerunfilecheck Info: File `main.out' has not changed.
|
||||
(rerunfilecheck) Checksum: D41D8CD98F00B204E9800998ECF8427E;0.
|
||||
)
|
||||
Here is how much of TeX's memory you used:
|
||||
19308 strings out of 474222
|
||||
335876 string characters out of 5748733
|
||||
2134975 words of memory out of 5000000
|
||||
41036 multiletter control sequences out of 15000+600000
|
||||
686653 words of font info for 181 fonts, out of 8000000 for 9000
|
||||
19894 strings out of 474222
|
||||
343688 string characters out of 5748733
|
||||
2200975 words of memory out of 5000000
|
||||
41353 multiletter control sequences out of 15000+600000
|
||||
692737 words of font info for 213 fonts, out of 8000000 for 9000
|
||||
1141 hyphenation exceptions out of 8191
|
||||
108i,10n,107p,10941b,2145s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Bold.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Regular.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Bold.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Regular.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb>
|
||||
Output written on main.pdf (11 pages, 219584 bytes).
|
||||
108i,10n,107p,10941b,2229s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
||||
</usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Bold.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Oblique.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraMono-Regular.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Bold.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/fira/FiraSans-Regular.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb>
|
||||
Output written on main.pdf (20 pages, 300891 bytes).
|
||||
PDF statistics:
|
||||
292 PDF objects out of 1000 (max. 8388607)
|
||||
265 compressed objects within 3 object streams
|
||||
115 named destinations out of 1000 (max. 500000)
|
||||
44697 words of extra memory for PDF output out of 51595 (max. 10000000)
|
||||
651 PDF objects out of 1000 (max. 8388607)
|
||||
609 compressed objects within 7 object streams
|
||||
336 named destinations out of 1000 (max. 500000)
|
||||
45833 words of extra memory for PDF output out of 51595 (max. 10000000)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -245,6 +245,7 @@
|
||||
% ============================================
|
||||
\input{step_01.tex}
|
||||
\input{step_02.tex}
|
||||
\input{step_03.tex}
|
||||
|
||||
% Weitere Kapitel folgen hier:
|
||||
% \input{step_02.tex}
|
||||
|
||||
@@ -18,4 +18,18 @@
|
||||
\contentsline {subsubsection}{\numberline {2.5.3}Firewall aktivieren}{10}{subsubsection.2.5.3}%
|
||||
\contentsline {subsubsection}{\numberline {2.5.4}Konfiguration überprüfen}{11}{subsubsection.2.5.4}%
|
||||
\contentsline {subsection}{\numberline {2.6}Zusammenfassung}{11}{subsection.2.6}%
|
||||
\contentsline {section}{\numberline {3}Docker-Images bauen und App deployen}{12}{section.3}%
|
||||
\contentsline {subsection}{\numberline {3.1}Was ist Docker und warum nutzen wir es?}{12}{subsection.3.1}%
|
||||
\contentsline {subsection}{\numberline {3.2}Die drei Dockerfiles im Detail}{12}{subsection.3.2}%
|
||||
\contentsline {subsubsection}{\numberline {3.2.1}Backend-Dockerfile: \texttt {apps/api/Dockerfile}}{12}{subsubsection.3.2.1}%
|
||||
\contentsline {subsubsection}{\numberline {3.2.2}Frontend-Dockerfile: \texttt {apps/web/Dockerfile}}{13}{subsubsection.3.2.2}%
|
||||
\contentsline {subsubsection}{\numberline {3.2.3}Nginx-Konfiguration: \texttt {apps/web/nginx.conf}}{14}{subsubsection.3.2.3}%
|
||||
\contentsline {subsection}{\numberline {3.3}Das Backend: Program.cs im Detail}{15}{subsection.3.3}%
|
||||
\contentsline {subsection}{\numberline {3.4}Der API-Client: client.ts im Detail}{16}{subsection.3.4}%
|
||||
\contentsline {subsection}{\numberline {3.5}Das Frontend: App.tsx im Detail}{16}{subsection.3.5}%
|
||||
\contentsline {subsection}{\numberline {3.6}Images bauen}{17}{subsection.3.6}%
|
||||
\contentsline {subsection}{\numberline {3.7}Images exportieren und auf den Server kopieren}{18}{subsection.3.7}%
|
||||
\contentsline {subsection}{\numberline {3.8}Container auf dem Server starten}{18}{subsection.3.8}%
|
||||
\contentsline {subsection}{\numberline {3.9}Aufgetretene Probleme und Lösungen}{19}{subsection.3.9}%
|
||||
\contentsline {subsection}{\numberline {3.10}Zusammenfassung}{20}{subsection.3.10}%
|
||||
\providecommand \tocbasic@end@toc@file {}\tocbasic@end@toc@file
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
% ============================================
|
||||
% STEP 03: DOCKER, DEPLOYMENT & ERSTE LIVE-APP
|
||||
% ============================================
|
||||
|
||||
\section{Docker-Images bauen und App deployen}
|
||||
\label{sec:step03}
|
||||
|
||||
In diesem Schritt bringen wir unsere Fitness-App vom lokalen Entwicklungsrechner auf den Server und machen sie weltweit erreichbar. Dafür nutzen wir \textbf{Docker} – eine Container-Plattform, die Anwendungen in standardisierten, isolierten Umgebungen verpackt und ausführt.
|
||||
|
||||
\subsection{Was ist Docker und warum nutzen wir es?}
|
||||
|
||||
Docker funktioniert wie ein \textbf{Versandkarton für Software}. Stell dir vor, du verschickst ein zerbrechliches Paket: Du packst es in einen genormten Karton, der überall auf der Welt gleich behandelt wird – egal ob in Deutschland, Japan oder Brasilien. Docker macht dasselbe mit Software:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Image} = Der Bauplan des Kartons (inkl. Inhalt). Ein Image enthält das Betriebssystem, alle Abhängigkeiten und die Anwendung selbst.
|
||||
\item \textbf{Container} = Der tatsächliche laufende Karton. Ein Container ist eine laufende Instanz eines Images.
|
||||
\item \textbf{Volume} = Ein separater Speicherort, der den Container überlebt. Wie ein externer USB-Stick, den man an den Karton anschließt.
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Konkret für unser Projekt:}
|
||||
\begin{itemize}
|
||||
\item \texttt{fitness-api:latest} – Image mit .NET 8 Backend
|
||||
\item \texttt{fitness-web:latest} – Image mit React Frontend + Nginx
|
||||
\item \texttt{fitness-data} – Volume für die SQLite-Datenbank (überlebt Container-Neustarts)
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Die drei Dockerfiles im Detail}
|
||||
|
||||
\subsubsection{Backend-Dockerfile: \texttt{apps/api/Dockerfile}}
|
||||
|
||||
\begin{lstlisting}[language=Dockerfile, caption={Dockerfile für das .NET Backend}]
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["apps/api/Api.csproj", "apps/api/"]
|
||||
RUN dotnet restore "apps/api/Api.csproj"
|
||||
COPY . .
|
||||
WORKDIR /src/apps/api
|
||||
RUN dotnet publish "Api.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/publish .
|
||||
EXPOSE 5000
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
ENTRYPOINT ["dotnet", "Api.dll"]
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Zeile für Zeile erklärt:}
|
||||
\begin{enumerate}
|
||||
\item \texttt{FROM ... AS build} – Startet mit dem .NET 8 SDK Image (enthält Compiler, Tools). Der Alias \texttt{build} erlaubt später darauf zuzugreifen.
|
||||
\item \texttt{WORKDIR /src} – Setzt das Arbeitsverzeichnis im Container auf \texttt{/src}.
|
||||
\item \texttt{COPY ["apps/api/Api.csproj", "apps/api/"]} – Kopiert NUR die Projektdatei. Dadurch cached Docker diesen Schritt: Solange sich \texttt{Api.csproj} nicht ändert, wird der Cache verwendet $\rightarrow$ schnellere Builds!
|
||||
\item \texttt{RUN dotnet restore} – Lädt alle NuGet-Pakete herunter (Entity Framework, NSwag, Swagger usw.).
|
||||
\item \texttt{COPY . .} – Kopiert den gesamten restlichen Quellcode.
|
||||
\item \texttt{WORKDIR /src/apps/api} – Wechselt ins Backend-Verzeichnis.
|
||||
\item \texttt{RUN dotnet publish} – Kompiliert die Anwendung im Release-Modus in den Ordner \texttt{/app/publish}.
|
||||
\item \texttt{FROM ... AS final} – Startet ein NEUES, schlankeres Image (nur ASP.NET Runtime, kein SDK). Das spart Speicher!
|
||||
\item \texttt{COPY --from=build ...} – Kopiert die kompilierte Anwendung aus dem Build-Image.
|
||||
\item \texttt{EXPOSE 5000} – Dokumentiert, dass der Container auf Port 5000 lauscht.
|
||||
\item \texttt{ENV ASPNETCORE\_URLS=http://+:5000} – Sagt .NET, es soll auf Port 5000 auf ALLEN Netzwerkschnittstellen lauschen.
|
||||
\item \texttt{ENTRYPOINT ["dotnet", "Api.dll"]} – Startet die Anwendung beim Container-Start.
|
||||
\end{enumerate}
|
||||
|
||||
\subsubsection{Frontend-Dockerfile: \texttt{apps/web/Dockerfile}}
|
||||
|
||||
\begin{lstlisting}[language=Dockerfile, caption={Dockerfile für das React Frontend}]
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY apps/web/package.json apps/web/
|
||||
RUN npm install -g pnpm && pnpm install --no-frozen-lockfile
|
||||
COPY apps/web/ apps/web/
|
||||
WORKDIR /app/apps/web
|
||||
RUN pnpm run build
|
||||
|
||||
FROM nginx:stable-alpine
|
||||
COPY --from=build /app/apps/web/dist /usr/share/nginx/html
|
||||
COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Zeile für Zeile erklärt:}
|
||||
\begin{enumerate}
|
||||
\item \texttt{FROM node:22-alpine} – Leichtgewichtiges Node.js 22 Image (Alpine Linux = nur $\sim$5 MB statt $\sim$180 MB bei Ubuntu).
|
||||
\item \texttt{COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./} – Kopiert die Monorepo-Konfiguration aus dem Root. \texttt{pnpm-workspace.yaml} ist nötig, damit pnpm die Workspace-Struktur erkennt.
|
||||
\item \texttt{COPY apps/web/package.json apps/web/} – Kopiert die Frontend-Paketliste an ihren Platz.
|
||||
\item \texttt{RUN npm install -g pnpm \&\& pnpm install} – Installiert pnpm global und dann alle Abhängigkeiten.
|
||||
\item \texttt{COPY apps/web/ apps/web/} – Kopiert den restlichen Frontend-Code.
|
||||
\item \texttt{WORKDIR /app/apps/web} – Wechselt ins Frontend-Verzeichnis.
|
||||
\item \texttt{RUN pnpm run build} – Baut das Frontend mit Vite (erzeugt \texttt{dist/}).
|
||||
\item \texttt{FROM nginx:stable-alpine} – NEUES schlankes Image mit Nginx (Webserver).
|
||||
\item \texttt{COPY --from=build ... /usr/share/nginx/html} – Kopiert den Build-Output in Nginx's Standard-Webverzeichnis.
|
||||
\item \texttt{COPY apps/web/nginx.conf ...} – Unsere eigene Nginx-Konfiguration.
|
||||
\item \texttt{EXPOSE 80} – HTTP-Port.
|
||||
\item \texttt{CMD ["nginx", "-g", "daemon off;"]} – Startet Nginx im Vordergrund (Container bleibt am Leben).
|
||||
\end{enumerate}
|
||||
|
||||
\subsubsection{Nginx-Konfiguration: \texttt{apps/web/nginx.conf}}
|
||||
|
||||
\begin{lstlisting}[language=Bash, caption={Nginx-Konfiguration mit Reverse Proxy}]
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://fitness-api:5000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection keep-alive;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Erklärung:} Dies ist ein \textbf{Reverse Proxy}. Nginx nimmt alle Anfragen entgegen und entscheidet, wohin sie weitergeleitet werden:
|
||||
\begin{itemize}
|
||||
\item \texttt{location /} – Anfragen an die Hauptseite $\rightarrow$ liefert React-Dateien aus \texttt{/usr/share/nginx/html}
|
||||
\item \texttt{location /api/} – Anfragen an \texttt{/api/*} $\rightarrow$ leitet sie an das Backend (\texttt{fitness-api:5000}) weiter
|
||||
\item \texttt{try\_files \$uri \$uri/ /index.html} – Sorgt dafür, dass Reacts Client-Side-Routing funktioniert (z.B. \texttt{/workouts/123} wird an React weitergegeben, nicht als 404 beantwortet)
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Das Backend: Program.cs im Detail}
|
||||
|
||||
\begin{lstlisting}[language=CSharp, caption={Vollständige Program.cs}]
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
var dbPath = Path.Combine("/app/data", "fitness.db");
|
||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseSqlite($"Data Source={dbPath}"));
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddOpenApiDocument();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
db.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseOpenApi();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.MapPost("/api/workouts", async (Workout workout, AppDbContext db) => { ... });
|
||||
app.MapGet("/api/workouts", async (AppDbContext db) => ...);
|
||||
app.MapGet("/api/workouts/{id:guid}", async (Guid id, AppDbContext db) => ...);
|
||||
app.MapPut("/api/workouts/{id:guid}", async (Guid id, Workout input, AppDbContext db) => ...);
|
||||
app.MapDelete("/api/workouts/{id:guid}", async (Guid id, AppDbContext db) => ...);
|
||||
|
||||
app.MapGet("/", () => "H Fitness API läuft!");
|
||||
app.Run();
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Die NuGet-Pakete in der .csproj-Datei:}
|
||||
\begin{itemize}
|
||||
\item \texttt{Microsoft.EntityFrameworkCore.Sqlite} (8.0.14) – EF Core für SQLite
|
||||
\item \texttt{Microsoft.EntityFrameworkCore.Design} (8.0.14) – EF Core Tools für Migrationen
|
||||
\item \texttt{NSwag.AspNetCore} (14.1.0) – OpenAPI/Swagger-Generator
|
||||
\item \texttt{Swashbuckle.AspNetCore} (6.6.2) – Swagger UI (die schöne Oberfläche)
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Wichtige Details:}
|
||||
\begin{itemize}
|
||||
\item \texttt{Path.Combine("/app/data", "fitness.db")} – Im Docker-Container liegt die Datenbank im Volume-Ordner \texttt{/app/data}. Das stellt sicher, dass die Daten erhalten bleiben, auch wenn der Container gelöscht und neu erstellt wird.
|
||||
\item \texttt{db.Database.EnsureCreated()} – Erstellt die Datenbank-Tabellen automatisch beim Start, falls sie noch nicht existieren. Erspart uns manuelle Migrationen im Produktivbetrieb.
|
||||
\item Die CRUD-Endpunkte sind \textbf{Minimal API Endpoints} – .NET 8's leichtgewichtige Alternative zu Controllern.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Der API-Client: client.ts im Detail}
|
||||
|
||||
\begin{lstlisting}[language=TypeScript, caption={Manueller API-Client}]
|
||||
const API_BASE = "";
|
||||
|
||||
export interface Workout {
|
||||
id?: string;
|
||||
name: string;
|
||||
date: string;
|
||||
durationMinutes: number;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export const fitnessApi = {
|
||||
async getWorkouts(): Promise<Workout[]> {
|
||||
const res = await fetch(`/api/workouts`);
|
||||
return res.json();
|
||||
},
|
||||
|
||||
async createWorkout(workout: Workout): Promise<Workout> {
|
||||
const res = await fetch(`/api/workouts`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(workout),
|
||||
});
|
||||
return res.json();
|
||||
},
|
||||
|
||||
// Weitere Methoden: getWorkout, updateWorkout, deleteWorkout...
|
||||
};
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Erklärung:}
|
||||
\begin{itemize}
|
||||
\item \texttt{API\_BASE = ""} – Keine absolute URL! Stattdessen relative Pfade wie \texttt{/api/workouts}. Der Browser sendet die Anfrage dann an dieselbe Domain, auf der die Seite gehostet ist. Nginx leitet sie an das Backend weiter.
|
||||
\item \texttt{interface Workout} – TypeScript-Interface für Typsicherheit. Stellt sicher, dass wir keine falschen Felder an die API senden.
|
||||
\item \texttt{fetch()} – Native Browser-API für HTTP-Anfragen. Kein Axios, kein jQuery nötig!
|
||||
\item Die Methoden geben direkt das geparste JSON zurück.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Das Frontend: App.tsx im Detail}
|
||||
|
||||
\begin{lstlisting}[language=TypeScript, caption={Hauptkomponente mit Workout-Liste und Formular}]
|
||||
function App() {
|
||||
const [workouts, setWorkouts] = useState<Workout[]>([]);
|
||||
const [form, setForm] = useState<Workout>({ ... });
|
||||
|
||||
const loadWorkouts = async () => {
|
||||
const data = await fitnessApi.getWorkouts();
|
||||
setWorkouts(data);
|
||||
};
|
||||
|
||||
useEffect(() => { loadWorkouts(); }, []);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
await fitnessApi.createWorkout({ ...form });
|
||||
loadWorkouts();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-950 text-white p-4 max-w-md mx-auto">
|
||||
{/* Formular */}
|
||||
<form onSubmit={handleSubmit}>...</form>
|
||||
{/* Workout-Liste */}
|
||||
{workouts.map(w => ( ... ))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Erklärung:}
|
||||
\begin{itemize}
|
||||
\item \texttt{useState} – Reacts State-Management für die Workout-Liste und das Formular.
|
||||
\item \texttt{useEffect} – Lädt die Workouts einmalig beim ersten Rendern der Komponente.
|
||||
\item \texttt{handleSubmit} – Wird beim Absenden des Formulars aufgerufen. Verhindert den Standard-Seiten-Reload (\texttt{e.preventDefault()}), sendet das Workout an die API und lädt die Liste neu.
|
||||
\item Tailwind CSS-Klassen wie \texttt{bg-gray-950}, \texttt{text-white}, \texttt{p-4} gestalten die App im Dark-Mode.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Images bauen}
|
||||
|
||||
Am Anfang war das Frontend-Image fehlerhaft. Hier die drei wichtigsten Fixes:
|
||||
|
||||
\textbf{Fehler 1: \texttt{pnpm-lock.yaml} nicht gefunden}
|
||||
\begin{itemize}
|
||||
\item Ursache: Dockerfile suchte in \texttt{apps/web/}, aber die Datei liegt im Root.
|
||||
\item Lösung: \texttt{COPY pnpm-lock.yaml ./} (vom Root kopieren).
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Fehler 2: \texttt{--frozen-lockfile} schlug fehl}
|
||||
\begin{itemize}
|
||||
\item Ursache: pnpm-Lockfile war nicht aktuell mit der Root-\texttt{package.json}.
|
||||
\item Lösung: \texttt{--no-frozen-lockfile} verwenden, damit pnpm fehlende Pakete nachinstalliert.
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Fehler 3: TypeScript Compiler (\texttt{tsc}) nicht gefunden}
|
||||
\begin{itemize}
|
||||
\item Ursache: \texttt{tsc -b} benötigt TypeScript als Abhängigkeit, die im Container fehlte.
|
||||
\item Lösung: Build-Script von \texttt{"tsc -b \&\& vite build"} auf \texttt{"vite build"} geändert. Vite führt den TypeScript-Check beim Dev-Server durch – im Produktions-Build reicht die reine Vite-Kompilierung.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Images exportieren und auf den Server kopieren}
|
||||
|
||||
\begin{lstlisting}[language=Bash, caption={Images exportieren und kopieren}]
|
||||
# Images als tar-Datei speichern
|
||||
docker save fitness-api:latest fitness-web:latest -o fitness-images.tar
|
||||
|
||||
# Auf den Server kopieren (scp = Secure Copy über SSH)
|
||||
scp fitness-images.tar testserver:/root/
|
||||
|
||||
# Auf dem Server importieren
|
||||
ssh testserver
|
||||
docker load -i /root/fitness-images.tar
|
||||
docker images | grep fitness
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Befehle erklärt:}
|
||||
\begin{itemize}
|
||||
\item \texttt{docker save} – Exportiert Docker-Images in eine portable tar-Datei.
|
||||
\item \texttt{scp} – Secure Copy: Kopiert Dateien verschlüsselt über SSH.
|
||||
\item \texttt{testserver:/root/} – Der Alias aus unserer \texttt{\textasciitilde/.ssh/config}. Die Datei landet im \texttt{/root/}-Verzeichnis des Servers.
|
||||
\item \texttt{docker load -i} – Importiert Images aus einer tar-Datei in Docker.
|
||||
\item \texttt{docker images} – Listet alle lokal verfügbaren Docker-Images auf.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Container auf dem Server starten}
|
||||
|
||||
\begin{lstlisting}[language=Bash, caption={Docker-Netzwerk, Volume und Container anlegen}]
|
||||
# Volume für die Datenbank (überlebt Container-Neustarts)
|
||||
docker volume create fitness-data
|
||||
|
||||
# Netzwerk (damit Backend und Frontend kommunizieren können)
|
||||
docker network create fitness-net
|
||||
|
||||
# Backend starten
|
||||
docker run -d \
|
||||
--name fitness-api \
|
||||
--network fitness-net \
|
||||
-v fitness-data:/app/data \
|
||||
-p 5000:5000 \
|
||||
fitness-api:latest
|
||||
|
||||
# Frontend starten
|
||||
docker run -d \
|
||||
--name fitness-web \
|
||||
--network fitness-net \
|
||||
-p 80:80 \
|
||||
fitness-web:latest
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Optionen erklärt:}
|
||||
\begin{itemize}
|
||||
\item \texttt{-d} – Detached Mode: Container läuft im Hintergrund (gibt die Konsole frei).
|
||||
\item \texttt{--name fitness-api} – Gibt dem Container einen festen Namen (sonst vergibt Docker Zufallsnamen).
|
||||
\item \texttt{--network fitness-net} – Bindet den Container in unser Docker-Netzwerk ein. Container im selben Netzwerk können sich über ihren Namen erreichen (z.B. \texttt{fitness-api}).
|
||||
\item \texttt{-v fitness-data:/app/data} – Bindet das Volume \texttt{fitness-data} in den Container-Pfad \texttt{/app/data} ein. Alles, was im Container unter \texttt{/app/data} gespeichert wird, landet tatsächlich im Volume und überlebt.
|
||||
\item \texttt{-p 5000:5000} – Port-Mapping: Leitet Port 5000 des Hosts (Server) an Port 5000 des Containers weiter.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Aufgetretene Probleme und Lösungen}
|
||||
|
||||
\textbf{Problem 1: Nginx konnte Host "backend" nicht auflösen}
|
||||
\begin{itemize}
|
||||
\item Ursache: In \texttt{nginx.conf} stand \texttt{proxy\_pass http://backend:5000}, aber der Backend-Container heißt \texttt{fitness-api}.
|
||||
\item Lösung: \texttt{backend} $\rightarrow$ \texttt{fitness-api} in \texttt{nginx.conf} ändern, Image neu bauen.
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Problem 2: Frontend lud Assets mit Pfad \texttt{/app/...}}
|
||||
\begin{itemize}
|
||||
\item Ursache: In \texttt{vite.config.ts} war \texttt{base: "/app/"} für PWA-Zwecke gesetzt.
|
||||
\item Lösung: Geändert auf \texttt{base: "/"}.
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Problem 3: API-Anfragen gingen an lokale IP \texttt{192.168.178.189}}
|
||||
\begin{itemize}
|
||||
\item Ursache: Im Client stand \texttt{const API\_BASE = "http://192.168.178.189:5107"}.
|
||||
\item Lösung: Geändert auf relative Pfade (\texttt{/api/workouts}), sodass Nginx die Anfragen per Reverse Proxy ans Backend weiterleitet.
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Problem 4: Doppeltes \texttt{/api} im Pfad}
|
||||
\begin{itemize}
|
||||
\item Ursache: Client hatte \texttt{API\_BASE = "/api"} und Endpunkte begannen ebenfalls mit \texttt{/api}.
|
||||
\item Ergebnis: Anfragen gingen an \texttt{/api/api/workouts}.
|
||||
\item Lösung: API-Base auf leeren String gesetzt und Endpunkte mit \texttt{/api/} beginnen lassen.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Zusammenfassung}
|
||||
|
||||
Nach diesem Schritt ist die Fitness-App produktiv auf dem VPS im Einsatz:
|
||||
|
||||
\begin{itemize}
|
||||
\item Die App ist unter \texttt{http://185.209.229.167} weltweit erreichbar
|
||||
\item Workouts werden persistent in einer SQLite-Datenbank gespeichert
|
||||
\item Docker-Volumes stellen sicher, dass Daten Container-Neustarts überleben
|
||||
\item Das Backend läuft auf .NET 8, das Frontend auf Nginx
|
||||
\item Ein Reverse Proxy (Nginx) leitet API-Anfragen intern an das Backend weiter
|
||||
\end{itemize}
|
||||
Reference in New Issue
Block a user