openziti / ziti-sdk-py

Ziti SDK for Python
Apache License 2.0
73 stars 2 forks source link

How can I use zitify with fastAPI and uvicorn? #51

Closed chrda81 closed 1 year ago

chrda81 commented 1 year ago

Hi, I'm trying to zitify my fastAPI service. This is a minimal example:

import sys

import openziti
import uvicorn
from fastapi import FastAPI

app = FastAPI()
bind_opts = {}  # populated in main

@openziti.zitify(bindings={
    ':18080': bind_opts,
})
def run_app():
    config = uvicorn.Config("main:app", host="0.0.0.0", port=18080, log_level="info")
    server = uvicorn.Server(config)
    server.run()

@app.get("/")
async def hello_world():  # put application's code here
    return {"message": "Have some Ziti!"}

if __name__ == '__main__':
    bind_opts['ztx'] = sys.argv[1]
    bind_opts['service'] = sys.argv[2]
    run_app()

The service is running and responding to local requests, but it is not available on the ziti network. I tried the ziti-http-server example with the same identity & ziti service and everything is working. In the ZAC I can see, that for the identity both dots are green. Running my uvicorn example both dots staying grey, so the service is not zitified. Do you have any hints for me?

chrda81 commented 1 year ago

UPDATE: I changed the binding to match the exact host which was specified for uvicorn and then it works

@openziti.zitify(bindings={("0.0.0.0", 18080): bind_opts})
def run_app():
    ...

The client gets its answer, but on server side this exception is thrown:

INFO:     Started server process [3306534]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:18080 (Press CTRL+C to quit)
Exception in callback BaseSelectorEventLoop._accept_connection(<function Ser...x7f467da51620>, <openziti.dec...27.0.0.1', 0)>, None, <Server socke...0.0.1', 0)>,)>, 2048, None, None)
handle: <Handle BaseSelectorEventLoop._accept_connection(<function Ser...x7f467da51620>, <openziti.dec...27.0.0.1', 0)>, None, <Server socke...0.0.1', 0)>,)>, 2048, None, None)>
Traceback (most recent call last):
  File "/usr/lib/python3.11/asyncio/events.py", line 80, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/lib/python3.11/asyncio/selector_events.py", line 165, in _accept_connection
    conn, addr = sock.accept()
                 ^^^^^^^^^^^^^
  File "/home/serveradmin/openziti-sdk/venv/lib/python3.11/site-packages/openziti/zitisock.py", line 94, in accept
    fd, peer = zitilib.accept(self.fileno())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/serveradmin/openziti-sdk/venv/lib/python3.11/site-packages/openziti/zitilib.py", line 276, in accept
    check_error(clt)
  File "/home/serveradmin/openziti-sdk/venv/lib/python3.11/site-packages/openziti/zitilib.py", line 219, in check_error
    raise Exception(err, msg)
Exception: (11, 'unexpected error')
INFO:      - "GET / HTTP/1.1" 200 OK
scox-antillion commented 1 year ago

Hey, this seems like an extremely similar issue to what i'm seeing at the moment with my flask gunicorn application

the example of the binding

class Server(BaseApplication):
...
    bind_opts = {
        'ztx': ziti_path('identity.json'),
        'service': 'dashboard-loenserver'
    }

    @openziti.zitify(bindings={
        '0.0.0.0:5000': bind_opts,
    })
    def run_zitty(self):
        # self.bind_opts['ztx'] = sys.argv[1]
        # self.bind_opts['service'] = sys.argv[2]
        self.run()

and the app is running on the same ip and port

this is the error we are getting

[2023-10-31 16:59:56 +0000] [91556] [INFO] Worker exiting (pid: 91556)
[2023-10-31 16:59:56 +0000] [91509] [ERROR] Worker (pid:91556) exited with code 255
[2023-10-31 16:59:56 +0000] [91509] [ERROR] Worker (pid:91556) exited with code 255.
[2023-10-31 16:59:56 +0000] [91559] [INFO] Booting worker with pid: 91559
[2023-10-31 16:59:56 +0000] [91559] [ERROR] Exception in worker process
Traceback (most recent call last):
  File "/opt/loenserver/env/lib64/python3.11/site-packages/gunicorn/arbiter.py", line 609, in spawn_worker
    worker.init_process()
  File "/opt/loenserver/env/lib64/python3.11/site-packages/gunicorn/workers/base.py", line 142, in init_process
    self.run()
  File "/opt/loenserver/env/lib64/python3.11/site-packages/gunicorn/workers/sync.py", line 126, in run
    self.run_for_one(timeout)
  File "/opt/loenserver/env/lib64/python3.11/site-packages/gunicorn/workers/sync.py", line 70, in run_for_one
    self.accept(listener)
  File "/opt/loenserver/env/lib64/python3.11/site-packages/gunicorn/workers/sync.py", line 29, in accept
    client, addr = listener.accept()
                   ^^^^^^^^^^^^^^^^^
  File "/opt/loenserver/env/lib64/python3.11/site-packages/openziti/zitisock.py", line 119, in accept
    fd, peer = zitilib.accept(self.fileno())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/loenserver/env/lib64/python3.11/site-packages/openziti/zitilib.py", line 276, in accept
    check_error(clt)
  File "/opt/loenserver/env/lib64/python3.11/site-packages/openziti/zitilib.py", line 219, in check_error
    raise Exception(err, msg)
Exception: (22, 'unexpected error')
scox-antillion commented 1 year ago

we were also getting the same error with Exception: (11, 'unexpected error') at the end rather than 22 when the ziti binding was :5000 rather than 0.0.0.0:5000.

ekoby commented 1 year ago

I am looking into it. At the first glance gunicorn issue is different from uvicorn. gunicorn spawns worker processes and our underlying SDK does not handle that well