Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swagger doc page wont render when deployed in kubernetes #610

Closed
will-m-buchanan opened this issue Jul 26, 2024 · 4 comments
Closed

Swagger doc page wont render when deployed in kubernetes #610

will-m-buchanan opened this issue Jul 26, 2024 · 4 comments
Labels
question Further information is requested

Comments

@will-m-buchanan
Copy link

Ask a question
I have written a Flask app using flask-restx. The docs render properly when I run locally, but not when deployed in k8s.

I run locally via a __main__ if in my main file:

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8899, debug=True)

This runs fine, and the swagger docs are rendered properly at 127.0.0.1:8899/

In my k8s deployment, I use gunicorn --bind 0.0.0.0:8899 ... to run the app, I have a Service that maps 8899 to 8080 and an Ingress that runs my Service on the path "/my-app(/|$)(.*)"

This all seems to work such that when I hit the app's endpoints in postman (i.e. my-host.com/my-app/endpointx), I get the responses I'm looking for. However, when I navigate to my-host.com/my-app/, the expected swagger docs do not render. Instead I get a blank page.

Investigating the html, I see that the <head> block is rendered properly (so, for instance, the page has <title>My App<\title>, as defined in the code with

api = Api(app, title="My App" ...)

But within the <body> block, the div which – locally – contains the main content: <div id="swagger-ui">, is completely empty in the k8s deployment.

The last difference I've been able to find is in swagger.json. The swagger json exists at my-host.com/my-app/swagger.json1. The big difference I notice is that at the top level of the json, locally I have "basePath": "\/", whereas in k8s I have "basePath": "/",. All other paths are similarly escaped locally but not in k8s. e.g. local:

    "paths": {
        "\/path\/endpoint": {

k8s:

"paths": {"/path/endpoint": {

Any ideas what is going on?

1: when I navigate here in my browser the file renders in a single wrapped line, whereas locally http://127.0.0.1:8899/swagger.json is pretty-printed. This isn't a big deal that I need to solve I don't think, but it is a curious difference between the local run and the k8s deployment

@will-m-buchanan will-m-buchanan added the question Further information is requested label Jul 26, 2024
@peter-doggart
Copy link
Contributor

I think this is related to flask being behind a reserve proxy on k8 that isn't present in your development environment. Have you tried configuring Flask's ProxyFix? I think this is possibly the same issue as was discussed over on this issue, with a few alternative solutions proposed: #58

@will-m-buchanan
Copy link
Author

Thanks @peter-doggart. That issue was very helpful. What worked for me was a combination of solutions:

I added

app.wsgi_app = ProxyFix(app.wsgi_app, x_for=0, x_proto=0, x_host=0, x_port=0, x_prefix=1)

after defining app, then I also included

@api.documentation
def custom_ui():
    """Use a custom swagger UI endpoint.

    Updates specs_url to point to the custom endpoint.
    """
    return render_template("swagger-ui.html",
                           title=api.title,
                           specs_url=APPLICATION_ROOT + SWAGGER_SPEC)

@api.route(SWAGGER_SPEC)
@api.hide
class SwaggerDoc(Resource):
    """Endpoint for rendering JSON swagger spec in browser"""
    @api.doc(security=[])
    def get(self):
        """Modify apischema to customize swagger spec"""
        apischema = copy.copy(api.__schema__)
        apischema["basePath"] = APPLICATION_ROOT + "/"

        return apischema

as per @CpiliotisSTLA's suggestion. Finally, I added

nginx.ingress.kubernetes.io/x-forwarded-prefix: /my-app

to my Ingress' metadata.annotations, as per @rodrigocaus' comment. All that seemed to create a cocktail that worked for me!

@Joeylu-master
Copy link

@will-m-buchanan,
Where are the APPLICATION_ROOT and SWAGGER_SPEC definitions?

@will-m-buchanan
Copy link
Author

APPLICATION_ROOT is the root path named in the ingress (/my-app in my example's case), and SWAGGER_SPEC is the endpoint that the UI will call on to get the JSON that renders the API docs (/swagger-spec in my case)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants