Search code examples
dockercontainers.net-8.0dotnet-aspire.net-9.0

Avoid container image from exiting in net aspire


I am using builder.AddPostgres().WithImage(...) but the image has this behaviour of exiting if I don't use -it when doing docker run

this is the way the documentation says to use this image:

docker run \
    -it \
    -v postgresml_data:/var/lib/postgresql \
    -p 5433:5432 \
    -p 8000:8000 \
    ghcr.io/postgresml/postgresml:2.7.12 \
    sudo -u postgresml psql -d postgresml

this is how I add postgres but does not seed to work, the item is show in the dashboard as exited and in docker if I press the play button it just start and exit in less than a minute, which is the correct way to keep the container running.

    postgres = builder.AddPostgres("lcommerce-dependecy-postgres", port: 5432)
        .WithImage("postgresml/postgresml")
        .WithImageTag("2.9.3")
        .WithImageRegistry("ghcr.io")
        .WithHttpEndpoint(name: "endpoint-55432-5432",
            port: 8000, targetPort: 8000, isProxied: true)
        .WithContainerRuntimeArgs("-it", "--gpus=all");

net aspire dashboard showing postgres and other resources depending on ti in exited status

I tried to keep the container running using .WithContainerRuntimeArgs("-it", "--gpus=all") but keep exiting. (by default net aspire adds postgres user and password I already tested another image pgvector/pgvector and it works fine but this one does no exit by default)

UPDATE

YOU CAN USE

.WithContainerRuntimeArgs("-i", "--gpus=all")
.WithArgs("sudo", "-u", "postgresml", "psql", "-d", "postgresml")

BUT BEST IS

(as the answer suggest):

.WithContainerRuntimeArgs("--gpus=all")
.WithArgs( "sleep","infinity")

THE CORRECT IS

Create a custom image with the extension and needed modules and set the database server as the main persistent process instead of using a workaround (as the answer mentioned)


Solution

  • That image's Dockerfile is publicly available. It has a couple of significant design issues. Most notably, it does not run the database server as the main container process, but instead depends on some other process running to "keep the container alive", something you should never need to actually do. A typical hackaround for this is to run a meaningless sleep command. This probably looks something like

    postgres = builder.AddPostgres("lcommerce-dependecy-postgres", port: 5432)
      .WithArgs("sleep", "infinity")
      ...;
    

    What's happening internally? The image declares

    ENTRYPOINT ["bash", "/app/entrypoint.sh"]
    

    (typically you should just run script, not bash script) without a default CMD. The entrypoint.sh calls service postgresql start (service doesn't usually work in Docker and a container normally only runs one process), runs several sudo -u postgresml commands (sudo is hard to use and is almost always unnecessary in Docker) and finally ends with

    exec "$@"
    

    Docker passes the CMD as arguments to the ENTRYPOINT, but in the default setup CMD is empty. That runs exec with no arguments, which does nothing, and the script exits. Since the script is the main container process, when the script exits, the container exits too.

    The Docker Hub postgres image page points at the PostGIS image as a more typical way of building a PostgreSQL image plus an extension. Download the extension proper (the PostgresML Dockerfile uses APT to get it, which should be just fine), put the relevant SQL CREATE EXTENSION commands into a script in /docker-entrypoint-initdb.d, and let the normal first-time initialization set up the extension. For this particular extension you'd need to add CUDA on top of a PostgreSQL image rather than vice versa, but the standard database image setup is kind of involved and it could be better to reuse it.