Möglichkeiten um Projekte "einfach" auf verschiedenen Server einzurichten

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Achso, verstehe, Danke.

Mit `docker inspect` kann ich mir die IP des Containers anzeigen lassen:

Code: Alles auswählen

            "Networks": {
                "docker_test_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "mariadb",
                        "27bff87bd78f"
                    ],
                    "MacAddress": "02:42:ac:12:00:03",
                    "DriverOpts": null,
                    "NetworkID": "f0ef795eeb80756106591e3b7f5e63837bf462f0476e0967c206a865cb6eeec3",
                    "EndpointID": "e42ba2bd95458d3574fc1da2b19cbefb18d01913f3d12a1528e93595ecc91ba9",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DNSNames": [
                        "docker_test_mariadb_1",
                        "mariadb",
                        "27bff87bd78f"
                    ]
                }
            }
        }
    }
]
Aber die 172.18.0.3 ist bei jedem `build` anders und ich finde keine Möglichkeit, wie ich die IP konfigurieren könnte.

Grüße
Dennis

@sparrow Sehe die Antwort erst jetzt, vor dem abschicken. Einmalig beim bauen der Container würde reichen. Der Hintergrund von der Aktion ist, dass ich meine Datenbank und die restliche Umgebung "überall" immer im aktuellen Entwicklungsstand habe. Wenn ich zu Hause etwas ändere, dann will ich woanders nicht manuell Datenbanken oder irgendwelche Konfigurationen anpassen, sondern ich baue die Container neu und habe alles aktuell.
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13703
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Die IP vom Container ist doch egal, der Port ist doch auf dem Rechner wo der Container *läuft* verfügbar. Du hast Doch auch die Webseite schon abgerufen. Da hast Du Dich doch auch nicht mit der Container-IP wo der Webserver drin läuft verbunden.
„Incorrect documentation is often worse than no documentation.“ — Bertrand Meyer
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Ne, die Webseite hat über `localhost:8080` funktioniert, aber die URL

Code: Alles auswählen

"mysql+pymysql://dennis:Bla2024!@localhost:3306/test"
oder

Code: Alles auswählen

"mysql+pymysql://dennis:Bla2024!@127.0.0.1:3306/test"
funktionieren nicht:

Code: Alles auswählen

docker_test_1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/create.py", line 643, in connect
docker_test_1  |     return dialect.connect(*cargs, **cparams)
docker_test_1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 621, in connect
docker_test_1  |     return self.loaded_dbapi.connect(*cargs, **cparams)
docker_test_1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test_1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 361, in __init__
docker_test_1  |     self.connect()
docker_test_1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 716, in connect
docker_test_1  |     raise exc
docker_test_1  | sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
docker_test_1  | (Background on this error at: https://sqlalche.me/e/20/e3q8)
Ich habe gerade noch mal explizit auf meinem Rechner nach gesehen, ob da vielleicht noch eine MariaDB-Instanz läuft, aber die ist inaktiv.

Wenn ich dich richtig verstehe, dann müsste ich über localhost:3306 Zugriff auf den Container haben, weil das ist ja der Port den ich angegeben habe und auf den hört der Container.(?)

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13703
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Das sollte eigentlich so funktionieren. Ist mariadb in dem Container auch entsprechend konfiguriert, dass da Verbindungen von aussen angenommen werden?
„Incorrect documentation is often worse than no documentation.“ — Bertrand Meyer
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Ich habe im Netz nach Beispielen für eine `docker-compose.yaml` gesucht und auch bei docker-hub nachgeschaut, aber nirgends finde ich eine Konfiguration, die sich groß von meiner unterscheidet bzw. die darauf hindeutet, dass jetzt andere Verbindungen zugelassen werden.

Wenn ich `docker-compose up` starte, dann werden ja die Logs ins Terminal geschrieben und er startet immer erst mit `docker_test` und erst danach mit `mariadb`. Also könnte es sein, dass da eine Verbindung aufgebaut werden soll, bevor der entsprechende Container läuft.(?)
Hab zwar verzweifelt die Struktur der `docker-compose.yaml` geändert, aber wenn die Container in der Reihenfolge, der Logs gestartet werden, dann hat sich nichts geändert.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
sparrow
User
Beiträge: 4401
Registriert: Freitag 17. April 2009, 10:28

Du kannst definieren, welche der Container auf die anderen angewiesen sind. Nennt sich depends_on.
Theoretisch kannst du das noch auskonkretisieren. Die Datenbank hat einen healthcheck und die darauf angewiesenen Container verwendet die condition service_healthy.

Hier ist das offizielle Beispiel für einen healthcheck in Docker Compose bei einem mariadb Container.
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Danke, ich habe das eingebaut und MaraiDB wird jetzt auch als erstes gestartet, aber der Fehler bleibt.

docker-compose.yaml:

Code: Alles auswählen

services:
  docker_test:
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      mariadb:
        condition: service_healthy

    platform: linux/amd64
    ports:
      - "5000:5000"
    volumes:
      - type: bind
        source: ./app
        target: /src/app

    command: >
      sh -c "python3 technical_data/fill_database.py &&
             gunicorn -w 4 -b 0.0.0.0:5000 --reload 'src.app:app'"
  mariadb:
    image: mariadb:11.2.6-jammy
    platform: linux/amd64
    ports:
      - "3306:3306"
    volumes:
      - ./test.sql:/docker-entrypoint-initdb.d/1.sql
    environment:
      - MYSQL_ROOT_PASSWORD=Blabliblub
      - MYSQL_PASSWORD=Bla2024!
      - MYSQL_USER=dennis
      - MYSQL_DATABASE=test
    healthcheck:
      test: [ "CMD", "healthcheck.sh", "--connect", "--innodb_initialized" ]
      start_period: 10s
      interval: 10s
      timeout: 5s
      retries: 3

Code: Alles auswählen

DATABASE_URL = "mysql+pymysql://dennis:Bla2024!@127.0.0.1:3306/test"

Code: Alles auswählen

mariadb-1      | 2024-11-29 20:32:12 0 [Note] InnoDB: Buffer pool(s) load completed at 241129 20:32:12
mariadb-1      | 2024-11-29 20:32:12 0 [Note] Server socket created on IP: '0.0.0.0'.
mariadb-1      | 2024-11-29 20:32:12 0 [Note] Server socket created on IP: '::'.
mariadb-1      | 2024-11-29 20:32:12 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
mariadb-1      | 2024-11-29 20:32:12 0 [Note] mariadbd: ready for connections.
mariadb-1      | Version: '11.2.6-MariaDB-ubu2204'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution
docker_test-1  | Traceback (most recent call last):
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 649, in connect
docker_test-1  |     sock = socket.create_connection(
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/socket.py", line 863, in create_connection
docker_test-1  |     raise exceptions[0]
docker_test-1  |   File "/usr/local/lib/python3.11/socket.py", line 848, in create_connection
docker_test-1  |     sock.connect(sa)
docker_test-1  | ConnectionRefusedError: [Errno 111] Connection refused
docker_test-1  | 
docker_test-1  | During handling of the above exception, another exception occurred:
docker_test-1  | 
docker_test-1  | Traceback (most recent call last):
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 146, in __init__
docker_test-1  |     self._dbapi_connection = engine.raw_connection()
docker_test-1  |                              ^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 3302, in raw_connection
docker_test-1  |     return self.pool.connect()
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 449, in connect
docker_test-1  |     return _ConnectionFairy._checkout(self)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 1263, in _checkout
docker_test-1  |     fairy = _ConnectionRecord.checkout(pool)
docker_test-1  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 712, in checkout
docker_test-1  |     rec = pool._do_get()
docker_test-1  |           ^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/impl.py", line 179, in _do_get
docker_test-1  |     with util.safe_reraise():
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
docker_test-1  |     raise exc_value.with_traceback(exc_tb)
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/impl.py", line 177, in _do_get
docker_test-1  |     return self._create_connection()
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 390, in _create_connection
docker_test-1  |     return _ConnectionRecord(self)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 674, in __init__
docker_test-1  |     self.__connect()
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 900, in __connect
docker_test-1  |     with util.safe_reraise():
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
docker_test-1  |     raise exc_value.with_traceback(exc_tb)
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 896, in __connect
docker_test-1  |     self.dbapi_connection = connection = pool._invoke_creator(self)
docker_test-1  |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/create.py", line 643, in connect
docker_test-1  |     return dialect.connect(*cargs, **cparams)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 621, in connect
docker_test-1  |     return self.loaded_dbapi.connect(*cargs, **cparams)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 361, in __init__
docker_test-1  |     self.connect()
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 716, in connect
docker_test-1  |     raise exc
docker_test-1  | pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 111] Connection refused)")
docker_test-1  | 
docker_test-1  | The above exception was the direct cause of the following exception:
docker_test-1  | 
docker_test-1  | Traceback (most recent call last):
docker_test-1  |   File "/MehrerSim/technical_data/fill_database.py", line 66, in <module>
docker_test-1  |     main()
docker_test-1  |   File "/MehrerSim/technical_data/fill_database.py", line 40, in main
docker_test-1  |     Base.metadata.create_all(engine)
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/sql/schema.py", line 5868, in create_all
docker_test-1  |     bind._run_ddl_visitor(
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 3252, in _run_ddl_visitor
docker_test-1  |     with self.begin() as conn:
docker_test-1  |   File "/usr/local/lib/python3.11/contextlib.py", line 137, in __enter__
docker_test-1  |     return next(self.gen)
docker_test-1  |            ^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 3242, in begin
docker_test-1  |     with self.connect() as conn:
docker_test-1  |          ^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 3278, in connect
docker_test-1  |     return self._connection_cls(self)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 148, in __init__
docker_test-1  |     Connection._handle_dbapi_exception_noconnection(
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2442, in _handle_dbapi_exception_noconnection
docker_test-1  |     raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 146, in __init__
docker_test-1  |     self._dbapi_connection = engine.raw_connection()
docker_test-1  |                              ^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 3302, in raw_connection
docker_test-1  |     return self.pool.connect()
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 449, in connect
docker_test-1  |     return _ConnectionFairy._checkout(self)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 1263, in _checkout
docker_test-1  |     fairy = _ConnectionRecord.checkout(pool)
docker_test-1  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 712, in checkout
docker_test-1  |     rec = pool._do_get()
docker_test-1  |           ^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/impl.py", line 179, in _do_get
docker_test-1  |     with util.safe_reraise():
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
docker_test-1  |     raise exc_value.with_traceback(exc_tb)
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/impl.py", line 177, in _do_get
docker_test-1  |     return self._create_connection()
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 390, in _create_connection
docker_test-1  |     return _ConnectionRecord(self)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 674, in __init__
docker_test-1  |     self.__connect()
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 900, in __connect
docker_test-1  |     with util.safe_reraise():
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
docker_test-1  |     raise exc_value.with_traceback(exc_tb)
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/base.py", line 896, in __connect
docker_test-1  |     self.dbapi_connection = connection = pool._invoke_creator(self)
docker_test-1  |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/create.py", line 643, in connect
docker_test-1  |     return dialect.connect(*cargs, **cparams)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 621, in connect
docker_test-1  |     return self.loaded_dbapi.connect(*cargs, **cparams)
docker_test-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 361, in __init__
docker_test-1  |     self.connect()
docker_test-1  |   File "/usr/local/lib/python3.11/site-packages/pymysql/connections.py", line 716, in connect
docker_test-1  |     raise exc
docker_test-1  | sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on '127.0.0.1' ([Errno 111] Connection refused)")
docker_test-1  | (Background on this error at: https://sqlalche.me/e/20/e3q8)
docker_test-1 exited with code 1
Ich weis schon gar nicht mehr, nach was ich noch suchen soll.


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
nezzcarth
User
Beiträge: 1709
Registriert: Samstag 16. April 2011, 12:47

Ich verwende docker/docker-compose immer so, dass ich die Container untereinander mit "links" verknüpfe; dann kann man sie mit ihrem Namen in den einzelnen, verknüpften Containern ansprechen. https://docs.docker.com/compose/how-tos ... containers

Zudem verwende ich wie gesagt ein Entrypoint Skript statt der von dir gewählten Variante, das auch Datenbankbefüllungen usw. übernehmen kann (wenn man sich auf Einzelinstanzen beschränkt).
Benutzeravatar
__blackjack__
User
Beiträge: 13703
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

127.0.0.1 im Container ist doch der Container *selbst*, in *dem* läuft doch keine MariaDB, die hat doch einen eigenen Container‽
„Incorrect documentation is often worse than no documentation.“ — Bertrand Meyer
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

danke für eure Antworten. Ich muss `mariadb` anstatt `127.0.0.1` eintragen, dann funktioniert die Verbindung, oh maaaan. Das habe ich in einem Kommentar auf SO gefunden.

Jetzt nur noch rausfinden, wie ich es anstelle, das nur beim bauen die Datenbank gefüllt wird und nicht bei jedem Containerstart.

Danke für eure Geduld
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
nezzcarth
User
Beiträge: 1709
Registriert: Samstag 16. April 2011, 12:47

Also vielleicht gibt es irgendwelche Tricks mit Multi-Stage-Builds etc., die ich nicht kenne, aber ich denke eigentlich nicht, dass das so geht, wie du es dir vorstellst. Beim Bauen wird gebaut, da läuft nichts, wo man indexieren könnte. MariaDB läuft ja erst, wenn der Container auf Basis des gebauten Images gestartet wird und dann kann man Daten einspielen. Der "klassische", generische Weg für Datenbank Preloading (nicht nur MariaDB), den ich kenne und auch in einigen offiziellen Images gesehen habe, ist, dass man beim Container Start ein Skript laufen lässt, das prüft, ob die Daten schon drin sind und sie nur indexiert, wenn das nicht der Fall ist.
Benutzeravatar
sparrow
User
Beiträge: 4401
Registriert: Freitag 17. April 2009, 10:28

@Dennis89: Nur um das noch einmal zu unterstreichen, was nezzcarth sagt: Beim Bauen nützt dir das nichts.

Wenn du es so wie im Augenblick machst, wirst du ab dem zweiten Containerstart ein Problem haben, weil du versuchst Dinge in die Datenbank zu schreiben, die bereits da sind.

Warum nützt es beim Bauen des Imags nichts?
Ich hatte bereits vorher geschrieben, dass das Dockerfile die Anweisungen sind, um eine definierte Umgebung zur Verfügung zu stellen. Diese definierte Umgebung ist dein "Image". Darin sollte aber tunlichst nur stehen, was nötig ist um diese Umgebung zu bauen.
Für die Datenbank nützt es dir auch nichts, weil es hoffentlich gar keine laufende Datenbank gibt, mit der zu diesem Zeitpunkt verbunden wird.

Um das auch noch einmal in Begriffen abzugrenzen:
Dein Dockerfile ist eine Beschreibung, aus der ein Image gebaut (build) wird. Dieses Image kann man durch die Welt schicken und auf einem beliebigen System, das die nötigen Voraussetzungen hat, starten. Mit dem Starten wird aus dem Image ein Container erzeugt und der ist dann, was läuft.
Ich bin mir nicht sicher, ob dir das klar ist: In der Regel baut man irgendwo das Image (zum Beispiel in der CI/CD-Pipeline und überträgt das dann in eine Container-Registry oder auf das Zielsystem um es dort zu starten.
Ich will mit der Erklärung nur ausschließen, dass du denkst, das Image würde auf dem Zielsystem gebaut werden.

Das Befüllen der Datenbank hat also nichts mit dem Bau deines Images zu tun. Ich kenne das auch so wie nezzcarth, dass es eine entrypoint.sh gibt, in der Dinge ausgeführt werden, wenn der Container gestartete wird. Und die endet dann im Start des "Daemons". In deinem Fall aus dem Start von gunicorn.
Du musst dich aber noch mit anderen Dingen beschäftigen. Was ist eigentlich, wenn sich die Datenbankstruktur ändert? Wenn ein Feld oder eine Tabelle dazu kommt?
Unter Django gibt es dafür Migrationen. Damit würde ich auch die Datenbefüllung durchführen. Und das könnte man in der entrypoint.sh machen. In wenig komplexen Umgebungen ist das nicht unüblich. Solch eine Mechanik müsste du nachbauen und vergleichbares im Flask/sqlAlchemy-Umfeld finden.
Benutzeravatar
__blackjack__
User
Beiträge: 13703
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

DB-Migration bei SQLAlchemy wird oft mit Alembic gemacht. Da gibt's wohl auch was für Flask: https://flask-alembic.readthedocs.io/
„Incorrect documentation is often worse than no documentation.“ — Bertrand Meyer
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für eure Erklärungen. Den Unterschied zwischen Image und laufenden Container habe ich dadurch soweit verstanden und jetzt auch hoffentlich verinnerlicht.
weil es hoffentlich gar keine laufende Datenbank gibt, mit der zu diesem Zeitpunkt verbunden wird.
Es gibt bis dahin keine laufende Datenbank.
Ich bin mir nicht sicher, ob dir das klar ist: In der Regel baut man irgendwo das Image (zum Beispiel in der CI/CD-Pipeline und überträgt das dann in eine Container-Registry oder auf das Zielsystem um es dort zu starten.
Ich will mit der Erklärung nur ausschließen, dass du denkst, das Image würde auf dem Zielsystem gebaut werden.
Das ist mir ganz und gar nicht klar. Mein Stand bzw. meine Vorgehensweise/Motivation ist folgendes. Mit Docker habe ich jetzt die Möglichkeit, dass ich auf verschiedensten Geräten nur Docker installieren muss und dann in Kürze meinen aktuellen Projektstand testen/zeigen kann ohne das ich erst Datenbanken/Webserver etc. einrichten/konfigurieren muss. Das war mein Wunsch bzw. dafür habe ich eine Lösung gesucht.
Der weitere Plan war, dass wenn das Projekt fertig ist, dann gibt es einen PC auf dem das laufen soll und da hätte ich dann ohne Docker alles eingerichtet.
Also Stand jetzt wird mein Image auf dem Zielsystem gebaut, da das Zielsystem unterschiedliche Geräte von mir sind.

Natürlich habe ich beim schreiben und beim lesen des Beitrags schon gemerkt, dass das was ich vorhabe nicht der "richtige" Weg ist. Ich habe ehrlich gesagt, Beiträge über CI/CP und Container-Registry noch nicht ganz hoch und runter gelesen, aber es scheint mir so, als setze ich das nicht in 3 Stunden einfach um. Ist das für eine mehr oder weniger private Entwicklung dennoch ratsam, dass ich mich damit beschäftige? Docker-Hub hat wohl eine kostenlose Version, die mit bis zu 3 Mitarbeiter verwendet werden kann. Aktuell fehlt mir nur noch etwas der Mehrwert, aber das liegt daran, dass ich das noch nicht verstanden habe.
Das Befüllen der Datenbank hat also nichts mit dem Bau deines Images zu tun. Ich kenne das auch so wie nezzcarth, dass es eine entrypoint.sh gibt, in der Dinge ausgeführt werden, wenn der Container gestartete wird. Und die endet dann im Start des "Daemons". In deinem Fall aus dem Start von gunicorn.
Okay, das kann ich umsetzen.
Was ist eigentlich, wenn sich die Datenbankstruktur ändert? Wenn ein Feld oder eine Tabelle dazu kommt?
Ich habe meine Tabelle über eine *.sql - Datei erstellt und die in `docker-compose.yaml' angegeben. Die Datei muss ich nur anpassen und ja in meinem Fall dann neu bauen.

Was das mit DB-Migration auf sich hat, muss ich auch erst noch recherchieren.

Puuh, da ist ja noch ganz schön was offen.


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
sparrow
User
Beiträge: 4401
Registriert: Freitag 17. April 2009, 10:28

@Dennis89: Der Titel des Threads ist ja, dass du möglichst "einfach" verteilen möchtest. Und das machst du über das Image. Nicht über das Bauen auf dem anderen System. Denn dafür musst du ja auch dort alles haben, was du zum Bauen brauchst. Und das kann von dem Abweichen, was du zum Starten eins Containers brauchst.

Dein Projekt an sich ist erst einmal dein Image. In deinem Fall also docker-test. Es ist nicht das docker-compose-File. Das Docker-Compose-File ist immer Abhängig von dem System, auf dem es läuft.
Folgendes Szenario für deine Software:
Für deine _lokale_ Entwicklung baust du dir ein Docker-Compose-File wie du es hast. Mit Datenbank, Webserver und deiner Applikation.
Und auf einem anderen System? Kommt darauf an, was dort zur Verfügung steht.

Du wirst einen Webserver und ein DBMS auf einem Produktivsystem nicht aus Docker laufen lassen wollen.
Die meisten root-Server-Anbieter bieten zumeist auch gleich eine Oberfläche oder ein CI-Tool an um Images zu importieren und zu konfigurieren. Da läuft dann Webserver und DBMS nativ auf der Maschine und die Konfiguration bindet dann deinen Container unter dessen internen Port an.
Bei den Cloud-Dienstleistern funktioniert das genauso. Du sagst "hier, Containr für Webapplikation", und was davor passiert (also den Webserver) kennst du nur vom Hörensagen. Und eine Datenbank startest du da auch nicht aus einem Compose-File, weil du die da auch als Service buchst.

Simpel wird das mit einer Registry. Du baust bei dir lokal (oder eben in der Pipeline) und legst es dann in die Registry.
Und dein Zielsystem kann von dort pullen. Also du baust da nicht. Du kopierst da nicht mal ein Image hin. Sondern du sagst dem Zielsystem nur, welches Image verwendet werden soll. Selbst wenn du jetzt ein zweites System hast, bei dem du das alles aus der Docker Compose hole wollen würdest, stehe da eben kein Build mehr sonder nur: image: docker-test:latest
Ein Traum.

Was sich dann je System unterscheidet bringt man per Umgebungsvariablen in den Docker-Container.
Deine Webapplication verbindet also nicht zu einer fest kodierten Datenbank sondern verwendet für den Aufbau des Connection-Strings Umgebungsvariablen. Und die kann man Containern (nicht Images!) beim Start mitgeben.
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

vielen Dank für die ausführliche Erklärung.
Das mit einer Registry hört sich super cool an. Aber ich glaub ich habe es nicht richtig verstanden. Ich baue das jetzt bei mir, wie du sagst, und dann lege ich es ab. Kann ich dafür mein Github-Repository verwenden oder muss das docker-hub sein?
Wenn ich das in der Registry habe, was mache ich dann? Wie bekomme ich es zum laufen und wie pflege ich meine Änderungen ein, die während der Entwicklung entstehen?

Ich finde hier in das Thema irgendwie gar keinen wirklichen Einstieg, auch wenn hier auf GitHub ziemlich viel steht. Kann das gar nicht beschreiben, aber das ist nicht greifbar zur Zeit.

Und zum Schluss vielleicht nochmal, ich brauche meine `docker-compose.yaml` - Datei um das Image zu bauen um das dann in die Registry zu schieben. Wenn sich jetzt meine Datenbank ändert, dann muss ich das wieder neu bauen und die neue Version hochladen. Das heißt für mich, dass ich auf den anderen Systemen auf denen ich die neuste Version will, anstatt `build` `pull` verwende und in beiden Fällen muss ich die Container starten? Im zweiten Fall wird nichts lokal gebaut.(?)


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
sparrow
User
Beiträge: 4401
Registriert: Freitag 17. April 2009, 10:28

Die Registry ist für Docker das was ein Package-Index für Python-Module ist.
Du kannst deine Module auch per Hand durch die Gegend tragen. Aber eigentlich willst du sie mit pip installieren. Und wenn du nicht PyPI verwenden möchtest, dann musst du dir einen eigenen Package Index hosten.
Genauso ist es mit Container-Registries. Du kannst die von Docker verwenden oder eine eigene verwenden.

Was du während der Entwicklung machst?
Was würdest du denn bei einem Python Modu machen?
Du entwickelst. Irgendwann hast du eine neue Version. Die baust du dann und pusht sie in die Registry. Fertig.
Clients können dann einfach das Image ziehen bzw. Updaten. In der Regel gibt man der neuesten Version immer den :latest Tag mit und verwendet das auf den Clients. Wenn man denn immer die neueste Version möchte.
Du verwendest das Image dann genauso wie du das Python und das Apache Image verwendet hast.

Ich glaube, du musst erst einmal sicher in dem Docker Thema an sich werden.
Du brauchst keine `docker-compose.yaml`-Datei um ein Image zu bauen. Compose ist nur die Möglichkeit mehrere Container in Abhängigkeit zueinander zu bauen bzw. zu starten. Es erleichtert dir das, was du sonst in einem Script selbt zusammenbasteln müsstest.,All das geht mit Docker auch ohne Compose. Zum Bauen brauchst du "docker build". Das macht dir aus einem Dockerfile ein Image. "docker create" baut einen Container aus dem Image, usw.

Bei einem Python-Modul würdest du auf dem Client in den requirements das Modul eintragen.
Und in diesem Fall würdest du das Image dort eintragen, wo du es verwendest. Zum Beispiel als Image in einem docker-compose-Fiel. Oder als source-Image einer Konfigurationsumgebung für Container, falls das Zielsystem so etwas hat.
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen und vielen Dank für die weitere Erklärung.
Ich glaube, du musst erst einmal sicher in dem Docker Thema an sich werden.
Das glaube ich auch, vorallem jetzt nach deinen Erklärungen merke ich, dass ich mir das falsch zusammen gereimt habe.
Ich werde mich weiterhin mit Docker beschäftigen, denn die Möglichkeiten hören sich schon sehr gut an. Wenn ich weiter gekommen bin, melde ich mich ziemlich sicher hier wieder. :mrgreen:

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1449
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

sagt mal bitte, muss ich beim importieren von eigenen Python-Module auch was beachten?

Code: Alles auswählen

.
├── app
│   ├── src
│   │   ├── app.py
│   │   ├── gas.py
│   │   ├── __init__.py
Dockerfile:

Code: Alles auswählen

FROM python:3.11-slim

ENV INSTALL_PATH=/Test
RUN mkdir -p $INSTALL_PATH
WORKDIR $INSTALL_PATH

COPY ./requirements.txt $INSTALL_PATH/
COPY ./app/src ${INSTALL_PATH}/src
COPY ./app/templates ${INSTALL_PATH}/templates
COPY ./technical_data ${INSTALL_PATH}/technical_data

RUN pip install --no-cache-dir -r requirements.txt
RUN rm -rf ~/.cache
RUN rm -rf /tmp/*

CMD ["gunicorn", "-b", "0.0.0.0:5000", "${INSTALL_PATH}.src.app:app"]
In `app.py` will ich `gas` importieren. Ich kopiere die ganze Ordner-Struktur mit `__init__.py` (ist leer) und wenn ich die Container laufen lasse, dann bekomme ich eine Fehlermeldung, dass das Modul `gas` nicht gefunden werden kann.
Ist das Arbeitsverzeichnis von Python irgendwie irgendwo anders bzw. wie kann ich das setzen?

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
sparrow
User
Beiträge: 4401
Registriert: Freitag 17. April 2009, 10:28

Du setzt das Arbeitsverzeichnis bereits mit WORKDIR.
Das ist quasi ein 'cd' wie man es aus der Shell/Eingabeaufforderung kennt.

Ich finde aber den Aufbau der Ordnerstruktur etwas merkwürdig.
'src' selbst ist ja ein Modul. Ich persönlich würde in einem Repository in 'src' den Quellcode vermuten (und darin die Module). Während in 'doc' die Dokumentation liegt, etc. Und im Auslieferungszustand wäre das Modul dann da, das vorher in src gelegen hat.

Zum eigentlichen Problem:
Du kannst jederzeit in den Container wechseln um zu sehen, wie die Verzeichnisstruktur aussieht. Wie wurde hier im Thread schon irgendwo erklärt (docker exec oder docker run mit dem expliziten Aufruf der Shell)
Wie würdest du es denn bei dir Lokal starten?
Am Ende des des Dockerfiles liegt deine "app.py" in '/Test/src'; dein Arbeitsverzeichnis ist '/Test'.
Ich finde den String, der die app.py für gunicoren bezeichnet, etwas seltsam. Ich kenne das nicht so, dass man dort einen Pfad voraus stellt.
Antworten