Sanic app name xxx already in use while running in Docker

Hello, I encountered a strange situation while running the Sanic application (in version 22.9.1) in Docker. I have prepared a sample code (python code is based on the examples that I found in the documentation) to reproduce the issue.

Dockerfile

FROM python:3.11.0-buster

COPY app app
COPY run_app.py run_app.py
COPY requirements.txt  requirements.txt

RUN ["python", "-m", "pip", "install", "-r", "requirements.txt"]

EXPOSE 8000
CMD ["python", "run_app.py"]

requirements.txt

aiofiles==22.1.0
httptools==0.5.0
multidict==6.0.2
sanic==22.9.1
sanic-routing==22.8.0
ujson==5.6.0
uvloop==0.17.0
websockets==10.4

app/__init __.py

from sanic import Request, Sanic, json

def attach_endpoints(app: Sanic):
    @app.get("/")
    async def handler(request: Request):
        return json({"app_name": request.app.name})

def create_app() -> Sanic:
    app = Sanic('testApp')
    attach_endpoints(app)
    return app

run_app.py

from sanic import Sanic
from sanic.worker.loader import AppLoader

from app import create_app

if __name__ == "__main__":
    loader = AppLoader(factory=create_app)
    app = loader.load()
    app.prepare(port=8000)
    Sanic.serve(primary=app, app_loader=loader)

While running created container, I am getting a Sanic Exception

sanic.exceptions.SanicException: Sanic app name "testApp" already in use.

I have noticed that adding dev=True to the prepare method suppresses this issue

app.prepare(port=8000, dev=True)

I do not see this exception when I run the app in the terminal on my machine
(System Version: macOS 12.6 Kernel Version: Darwin 21.6.0 Python Version: 3.11.0).

On the other hand when I modify the code in run_app.py to as follows

from app import create_app

if __name__ == "__main__":
    app = create_app()
    app.run(host='0.0.0.0', port=8000)

I am able to run the app in Docker, but I am getting Sanic Exception when I execute it in terminal.

sanic.exceptions.SanicException: Sanic app name 'testApp' not found.
App instantiation must occur outside if __name__ == '__main__' block or by using an AppLoader.

Is this expected behavior? How should I run Sanic applications in Docker?
Thank you in advance.

Dropping run_app.py file and running application via Sanic CLI in Docker

FROM python:3.11.0-buster

COPY app app
COPY run_app.py run_app.py
COPY requirements.txt  requirements.txt

RUN ["python", "-m", "pip", "install", "-r", "requirements.txt"]

EXPOSE 8000
CMD ["python", "-m", "sanic", "--factory", "app:create_app", "--port", "8000", "--host", "0.0.0.0"]

Results in the same exception

sanic.exceptions.SanicException: Sanic app name "testApp" already in use.

I do not see this issue when I downgrade Sanic to version 22.6.2, but I would rather use the newest version of Sanic.

Sorry I have not gotten back to you yet. I will try and take a look tonight.

I need to look into this some more, but changing it to

from app import create_app

if __name__ == "__main__":
    app = create_app()
    app.run()

This works for me from docker or local machine.

Update…

Okay, so I have not 100% nailed down why there is a diff in the debian container versus on my Linux machine. But, I believe that this must be related to fork v spawn. Usually we lean on spawn and only use fork when (1) it is Linux and (2) there is no auto reload. So, that explains why having dev=True makes a difference for you.

But, it does not explain why I do not see that behavior on a Linux box not in Docker.

Ok. I think I figured it out so that the docker behavior makes sense now. The reason this has not come up for me before (and why I am assuming that you are experiencing this locally while I am not) is that I was in an env with sanic-testing which suppressed that issue. :man_facepalming:

This is definitely NOT expected behavior and is a bug. Or, at least is something that we need to account for. I am wondering if maybe we should always just use spawn. This might be safer.

I decided to go a different route. Since usually someone will be using a factory method to return a newly constructed app instance I am going to add a feature into the app loader to clear out any existing apps that exists. This is a fairly low-level toggle, but it might be necessary for someone that is doing something custom. See: https://github.com/sanic-org/sanic/pull/2624

1 Like

Hello @ahopkins, thank you for investigating this issue :slight_smile:

1 Like