Docker + Nginx Problem?
-
Ich habe eine einfache fastapi-Applikation (hello world zu Testzwecken).
Die Applikation soll auf Port 8080 mit uvicorn ausgeführt werden:
uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
Nun nöchte ich, (zunächst sogar ohne SSL), dass ich die API übner eine subdomain erreichen kann, also
api.example.com
statt
example.com:8080
Hierfür nutze ich nginx:
server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_connect_timeout 60s; proxy_read_timeout 120s; } }
Soweit funktioniert das ganze auch:
api.example.com -> success
Nun möchte ich das ganze in Docker ausführen:
FROM python:3.10-slim-bullseye # Set the working directory to /api WORKDIR /api # Copy the requirements file to the container COPY requirements.txt . # Install the project dependencies RUN pip install --no-cache-dir -r requirements.txt # Copy the rest of the application code to the container COPY src/ . # Expose the port that the application will listen on EXPOSE 8080 # Define the command that will run the application CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "4"]
Ich starte mit
docker run -d -p 8080:8080 -v /home/user/certs:/api/certs:ro myapi:latest
Wenn ich das tue, kann ich die API unter ANgabe des Ports erreichen:
example.com:8080 -> success
Sobald ich aber die subdomain ansprechen möchte, bekomme ich Bad Gateway 502.
api.example.com -> Bad Gateway 502
Ich habe wenig AHnung, was da noch schieflaufen kann. Firewall ufw ist inaktiv.
-
@ravenheart_ggg sagte in Docker + Nginx Problem?:
proxy_pass http://localhost:8080;
da muss
http://app:8080
hin.
-
@Fragender :
Vielen Dank für die schnelle AntwortIch habe nun folgenden Block eingeführt:
server { listen 80; server_name api.example.com; location / { proxy_pass http://app:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_connect_timeout 60s; proxy_read_timeout 120s; } }
Leider erhalte ich dann mit
sudo nginx -t
nginx: [emerg] host not found in upstream "app" in /etc/nginx/sites-enabled/fast_api.conf:47 nginx: configuration file /etc/nginx/nginx.conf test failed
Der Docker-Container läuft, was muss ich sonst noch tun?
-
Das ist kompliziert, weil Docker ein eigenes Subnetz aufbaut und uvicorn wahrscheinlich auch:
https://docs.docker.com/compose/networking/
Du müsstest die IP des Subnetz herausfinden, unter der deine Anwendung auf deiner Maschine erreichbar ist.
-
geht zufällig
proxy_pass http://myapi:8080;
?
-
was sagt
netstat -tulpen
?
Vermutlich binded docker den port nicht an 0.0.0.0 (generic für alle network devices) sondern an eine der IP-Addressen (und nicht des lcoalhost aka loopback aka 127.0.0.1) des hosts.
Damit es mit localhost oder besser 127.0.0.1 funktioniert müsste es ausreichen den start des docker containers wie folgt zu ändern:
docker run -d -p 127.0.0.1:8080:8080 -v /home/user/certs:/api/certs:ro myapi:latest
Dadurch sagst du docker den port explizit an die ip-addresse des loopback devices zu binden.
Dadurch ist via 8080 die app nicht mehr von außen erreichbar aber du kannst dann in der proxy configuration dann http://127.0.0.1 nutzen
-
docker run -d -p 127.0.0.1:8080:8080 -v /home/user/certs:/api/certs:ro myapi:latest
Immer noch Bad Gateway
netstat:
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 0 3830253092 - tcp 0 0 0.0.0.0:8880 0.0.0.0:* LISTEN 0 4121481349 -
lsof:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME docker-pr 473846 root 4u IPv4 3830253092 0t0 TCP localhost:http-alt (LISTEN)
-
Generell sollte man aber Expose Port to Host ganz vermeiden, und nur für die Testzwecke verwenden.
Also, um das schrittweise auszuprobieren, wie es funktioniert, ist das ok. Hinterher dann (in Production) sollte man aber vermeiden.
-
@ravenheart_ggg mach doch mal
sudo netstat -tlpn
, um sehen zu können, was dort läuft.
-
netstat:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 474475/docker-proxy tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 473972/nginx: maste tcp 0 0 0.0.0.0:8880 0.0.0.0:* LISTEN 104772/sw-cp-server tcp 0 0 0.0.0.0:465 0.0.0.0:* LISTEN 992/master tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 1081/systemd-resolv tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 761/sshd: /usr/sbin tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN 992/master tcp 0 0 127.0.0.1:12346 0.0.0.0:* LISTEN 992/master tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 473972/nginx: maste tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 104772/sw-cp-server tcp 0 0 0.0.0.0:4190 0.0.0.0:* LISTEN 105385/dovecot tcp 0 0 127.0.0.1:12768 0.0.0.0:* LISTEN 106105/psa-pc-remot tcp 0 0 0.0.0.0:993 0.0.0.0:* LISTEN 105385/dovecot tcp 0 0 0.0.0.0:995 0.0.0.0:* LISTEN 105385/dovecot tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 1035/mariadbd tcp 0 0 0.0.0.0:110 0.0.0.0:* LISTEN 105385/dovecot tcp 0 0 0.0.0.0:143 0.0.0.0:* LISTEN 105385/dovecot tcp6 0 0 :::80 :::* LISTEN 473972/nginx: maste tcp6 0 0 :::8880 :::* LISTEN 104772/sw-cp-server tcp6 0 0 :::8081 :::* LISTEN 447849/apache2 tcp6 0 0 :::465 :::* LISTEN 992/master tcp6 0 0 :::21 :::* LISTEN 776/xinetd tcp6 0 0 :::22 :::* LISTEN 761/sshd: /usr/sbin tcp6 0 0 :::25 :::* LISTEN 992/master tcp6 0 0 :::443 :::* LISTEN 473972/nginx: maste tcp6 0 0 :::8443 :::* LISTEN 104772/sw-cp-server tcp6 0 0 :::4190 :::* LISTEN 105385/dovecot tcp6 0 0 :::993 :::* LISTEN 105385/dovecot tcp6 0 0 :::995 :::* LISTEN 105385/dovecot tcp6 0 0 :::106 :::* LISTEN 776/xinetd tcp6 0 0 :::110 :::* LISTEN 105385/dovecot tcp6 0 0 :::143 :::* LISTEN 105385/dovecot
-
docker ps
und danachdocker inspect <container ID>
mal aufrufen, das sollte auch einige Informationen liefern.und
proxy_pass http://127.0.0.1:8080;
würde auch nicht funktionieren?
-
@Fragender sagte in Docker + Nginx Problem?:
Generell sollte man aber Expose Port to Host ganz vermeiden, und nur für die Testzwecke verwenden.
Also, um das schrittweise auszuprobieren, wie es funktioniert, ist das ok. Hinterher dann (in Production) sollte man aber vermeiden.
Ich sehe kein Problem einen Port auf localhost zu exposen?
-
@ravenheart_ggg sagte in Docker + Nginx Problem?:
docker run -d -p 127.0.0.1:8080:8080 -v /home/user/certs:/api/certs:ro myapi:latest
Immer noch Bad Gateway
netstat:
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 0 3830253092 - tcp 0 0 0.0.0.0:8880 0.0.0.0:* LISTEN 0 4121481349 -
lsof:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME docker-pr 473846 root 4u IPv4 3830253092 0t0 TCP localhost:http-alt (LISTEN)
Öhm und wie sieht deine nginx configuration aus?
Und hast du überhaupt mal lokal getestet ob ein request an http://127.0.0.1:8080 das gewünschte zurückliefert?
-
Aktuell sieht meine Nginx-Konfiguration wie folgt aus:
server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name example.com www.example.com; ssl_certificate /home/user/certs/live/example.com/cert.pem; ssl_certificate_key /home/user/certs/live/example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location /api { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_connect_timeout 60s; proxy_read_timeout 120s; } }
# from host system curl 127.0.0.1:8080 curl: (52) Empty reply from server curl -k https://example.com:8080 {"message":"Hello, World!"}
-
Ich habe beim Ausführen von docker nun
0.0.0.0:8080 angegeben und siehe da, es funktioniert.
Leider habe ich nun keine AHnung mehr, was ich egentlich getan habe.
Kann mir das jemand erklären?Ist das ünerhaupt noch sicher?
-
@ravenheart_ggg sagte in Docker + Nginx Problem?:
Aktuell sieht meine Nginx-Konfiguration wie folgt aus:
server { listen 80; listen [::]:80; server_name example.com www.example.com; return 301 https://example.com$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name example.com www.example.com; ssl_certificate /home/user/certs/live/example.com/cert.pem; ssl_certificate_key /home/user/certs/live/example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location /api { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_connect_timeout 60s; proxy_read_timeout 120s; } }
# from host system curl 127.0.0.1:8080 curl: (52) Empty reply from server curl -k https://example.com:8080 {"message":"Hello, World!"}
Deine beiden aufrufe sind unterschiedlich in einem wichtigen Teil.
Einmal https und einmal httpjetzt ich klar was das problem ist. Deine App macht https schon.
Dann hätte auch eincurl -k https://127.0.0.1:8080
funktioniert.
Oder wird bei dir auch {"message":"Hello, World!"} ausgegeben wenn du
curl http://example.com:8080
aufrufst?
-
@ravenheart_ggg sagte in Docker + Nginx Problem?:
Ich habe beim Ausführen von docker nun
0.0.0.0:8080 angegeben und siehe da, es funktioniert.
Leider habe ich nun keine AHnung mehr, was ich egentlich getan habe.
Kann mir das jemand erklären?Ist das ünerhaupt noch sicher?
Dein Problem scheint zu sein, dass du nicht weist über welches protokoll deine App überhaupt daten liefert.
In beiden fällen ist es "gleich" sicher. Egal ob du jetzt über einen proxy gehst oder direkt.
Aber da du über nicht den port angegeben möchtest beim aufruf, brauchst du einen Proxy, da du ja schon einen Webserver auf port 443/80 für andere webseiten laufen hast.
-
Ja und eine "doppelte" ssl Verschlüsselung bringt auch nicht mehr Sicherheit. Es genügt, wenn deine Anwendung http (ohne s) liefert...
Und dann noch ein Rat: Wenn du alles in einem Docker-Compose-File machst, dann hättest du diese Probleme nicht
-
guckt doch einfach mal ins nginx logfile anstatt hier wild rumzuraten. optional mal die logs vom uvicorn container angucken.
wenn du den kram hinter nem TLS termination proxy laufen laesst, fehlt dir der
--proxy-headers
switch fuer uvicorn.
-
@Fragender sagte in Docker + Nginx Problem?:
Ja und eine "doppelte" ssl Verschlüsselung bringt auch nicht mehr Sicherheit. Es genügt, wenn deine Anwendung http (ohne s) liefert...
Sie wäre nicht mal "doppelt" denn die ssl verbindung vom client wird in nginx terminiert und nginx baut eine eigene ssl verbindung zum host auf der in proxy_pass angegeben ist.
In falle von @ravenheart_ggg wäre es dann
Client<---ssl--->Nginx<---ssl--->localhost:8080