Troubles with postgres while building docker container
I am trying to lauch DJANGO + POSTGRES inside a single container with DOCKERFILE. I have the folowing dockerfile which basicly installs all the python dependencies and postgresql. Then it lauches the script to create db and user (presented below).
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN apt update && apt -y install postgresql postgresql-contrib
RUN service postgresql start && su -c "bash config.db.bash" postgres
WORKDIR /app/psql
RUN python manage.py makemigrations && python manage.py migrate #Error
EXPOSE 8000
CMD service postgresql start && python manage.py runserver 0.0.0.0:8000
Here is the script "config.db.bash"
#!/bin/bash
psql <<-INPUT
CREATE USER docker;
ALTER USER docker WITH PASSWORD 'password';
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker
INPUT
requirements.txt
asgiref==3.7.2
Django==5.0.2
sqlparse==0.4.4
tzdata==2024.1
psycopg2
Here is the DJANGO`s settings.py -> Database config (psycopg_2 module installed)
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "docker",
"USER": "docker",
"PASSWORD": "password",
"HOST": "localhost",
"PORT": 5432,
}
}
It works just fine until this line
RUN python manage.py makemigrations && python manage.py migrate
Then docker says that error occured during this operation. Log basicly says that DJANGO cant connect to postgress DB.
> [9/9] RUN python manage.py makemigrations && python manage.py migrate:
3.377 /usr/local/lib/python3.12/site-packages/django/core/management/commands/makemigrations.py:160: RuntimeWarning: Got an error checking a consistent migration history performed for database connection 'default': connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused
3.377 Is the server running on that host and accepting TCP/IP connections?
3.377 connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address
3.377 Is the server running on that host and accepting TCP/IP connections?
3.377
3.377 warnings.warn(
3.384 No changes detected
3.748 Traceback (most recent call last):
3.748 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 275, in ensure_connection
3.748 self.connect()
3.748 File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.748 return func(*args, **kwargs)
3.749 ^^^^^^^^^^^^^^^^^^^^^
3.749 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 256, in connect
3.749 self.connection = self.get_new_connection(conn_params)
3.749 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.749 File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.749 return func(*args, **kwargs)
3.750 ^^^^^^^^^^^^^^^^^^^^^
3.750 File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/base.py", line 277, in get_new_connection
3.750 connection = self.Database.connect(**conn_params)
3.750 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.750 File "/usr/local/lib/python3.12/site-packages/psycopg2/__init__.py", line 122, in connect
3.751 conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
3.751 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.751 psycopg2.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused
3.751 Is the server running on that host and accepting TCP/IP connections?
3.751 connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address
3.751 Is the server running on that host and accepting TCP/IP connections?
3.751
3.751
3.751 The above exception was the direct cause of the following exception:
3.751
3.751 Traceback (most recent call last):
3.751 File "/app/psql/manage.py", line 22, in <module>
3.751 main()
3.751 File "/app/psql/manage.py", line 18, in main
3.751 execute_from_command_line(sys.argv)
3.751 File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
3.752 utility.execute()
3.752 File "/usr/local/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
3.752 self.fetch_command(subcommand).run_from_argv(self.argv)
3.752 File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 413, in run_from_argv
3.752 self.execute(*args, **cmd_options)
3.752 File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 459, in execute
3.753 output = self.handle(*args, **options)
3.753 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.753 File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 107, in wrapper
3.753 res = handle_func(*args, **kwargs)
3.753 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.753 File "/usr/local/lib/python3.12/site-packages/django/core/management/commands/migrate.py", line 100, in handle
3.754 self.check(databases=[database])
3.754 File "/usr/local/lib/python3.12/site-packages/django/core/management/base.py", line 486, in check
3.754 all_issues = checks.run_checks(
3.754 ^^^^^^^^^^^^^^^^^^
3.754 File "/usr/local/lib/python3.12/site-packages/django/core/checks/registry.py", line 88, in run_checks
3.754 new_errors = check(app_configs=app_configs, databases=databases)
3.754 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.754 File "/usr/local/lib/python3.12/site-packages/django/core/checks/model_checks.py", line 36, in check_all_models
3.755 errors.extend(model.check(**kwargs))
3.755 ^^^^^^^^^^^^^^^^^^^^^
3.755 File "/usr/local/lib/python3.12/site-packages/django/db/models/base.py", line 1610, in check
3.756 *cls._check_constraints(databases),
3.756 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.756 File "/usr/local/lib/python3.12/site-packages/django/db/models/base.py", line 2460, in _check_constraints
3.757 connection.features.supports_nulls_distinct_unique_constraints
3.757 File "/usr/local/lib/python3.12/site-packages/django/utils/functional.py", line 47, in __get__
3.757 res = instance.__dict__[self.name] = self.func(instance)
3.757 ^^^^^^^^^^^^^^^^^^^
3.757 File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/features.py", line 141, in is_postgresql_15
3.757 return self.connection.pg_version >= 150000
3.757 ^^^^^^^^^^^^^^^^^^^^^^^^^^
3.757 File "/usr/local/lib/python3.12/site-packages/django/utils/functional.py", line 47, in __get__
3.757 res = instance.__dict__[self.name] = self.func(instance)
3.757 ^^^^^^^^^^^^^^^^^^^
3.757 File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/base.py", line 438, in pg_version
3.758 with self.temporary_connection():
3.758 File "/usr/local/lib/python3.12/contextlib.py", line 137, in __enter__
3.758 return next(self.gen)
3.758 ^^^^^^^^^^^^^^
3.758 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 691, in temporary_connection
3.758 with self.cursor() as cursor:
3.758 ^^^^^^^^^^^^^
3.758 File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.759 return func(*args, **kwargs)
3.759 ^^^^^^^^^^^^^^^^^^^^^
3.759 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 316, in cursor
3.759 return self._cursor()
3.759 ^^^^^^^^^^^^^^
3.759 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 292, in _cursor
3.759 self.ensure_connection()
3.760 File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.760 return func(*args, **kwargs)
3.760 ^^^^^^^^^^^^^^^^^^^^^
3.760 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 274, in ensure_connection
3.760 with self.wrap_database_errors:
3.760 File "/usr/local/lib/python3.12/site-packages/django/db/utils.py", line 91, in __exit__
3.760 raise dj_exc_value.with_traceback(traceback) from exc_value
3.760 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 275, in ensure_connection
3.761 self.connect()
3.761 File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.761 return func(*args, **kwargs)
3.761 ^^^^^^^^^^^^^^^^^^^^^
3.761 File "/usr/local/lib/python3.12/site-packages/django/db/backends/base/base.py", line 256, in connect
3.761 self.connection = self.get_new_connection(conn_params)
3.761 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.761 File "/usr/local/lib/python3.12/site-packages/django/utils/asyncio.py", line 26, in inner
3.761 return func(*args, **kwargs)
3.762 ^^^^^^^^^^^^^^^^^^^^^
3.762 File "/usr/local/lib/python3.12/site-packages/django/db/backends/postgresql/base.py", line 277, in get_new_connection
3.762 connection = self.Database.connect(**conn_params)
3.762 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.762 File "/usr/local/lib/python3.12/site-packages/psycopg2/__init__.py", line 122, in connect
3.762 conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
3.763 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3.763 django.db.utils.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused
3.763 Is the server running on that host and accepting TCP/IP connections?
3.763 connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address
3.763 Is the server running on that host and accepting TCP/IP connections?
3.763
------
Dockerfile:17
--------------------
15 | WORKDIR /app/psql
16 |
17 | >>> RUN python manage.py makemigrations && python manage.py migrate
18 |
19 | EXPOSE 8000
Before looking for help I tried several ways to solve this problem by myself.
I tried to start/restart psql server or start it again with the commands
RUN service postgresql startRUN service postgresql restartServer tried to start/restart and after long time of waiting it throwed timeout error.
I tried to modify DJANGO settings.py -> Database config. Changed "localhost" to "0.0.0.0" and to "127.0.0.1". Then I tried to specify PORT as a string. -> Didnt work.
I tried to run
RUN service postgresql statusIt said that after this command in dockerfile...
RUN service postgresql start && su -c "bash config.db.bash" postgres... server went ofline somehow! And as I mentioned before when I was trying to start it again timeout error occured!
Im looking for solution using DOCKERFILE and not Docker-Compose. Also I dont want to use DIFFERENT BASE IMAGE in DOCKERFILE.
Let me start early: The Dockerfile is used to build the container, meaning installing everything, setting up directory structure etc. The Dockerfile is not supposed to run services already. This you typically want to have when you start the container. Every intermediate command in your Dockerfiles requires a "start and end", so does the RUN command at that place. But how can the database run alongside successfully in a container that is not even fully build yet?
Knowing that the database can not run where you are trying it to run it is easy to figure out why the migration command fails. You are attempting to connect to a database that is not currently running.
So how to mitigate? Disclaimer: What I am telling you now is not best practice at all. You typically want to run one process per container. One container for one task. This slowly goes to the direction to docker compose. Anyways to answer your question:
You would want to run a processmanager like supervisord. Remember, one container one main process. In this case this main process would be supervisord (which handles then your postgres and development server).
Meaning at the end of your Dockerfile you want to start an entrypoint script. This has to
- Start up the database
- Do the migrations
- Start the development server.
This is a lot to set up. But be considerative about the work you put in. Because even after setting it up as I said it is not a good solution. When the container dies, your database is gone. You need to setup a volume, somewhere. The development server is called development server for a reason. Meaning when you move to production you need a third service aswell (nginx for example to handle your staticfiles). This would introduce even a third service.
I do not know your reasoning behind the "all in one container approach" and I do not recommend it but the answer to your question is a service like supervisord.