Hilfen zu Docker (Anfängerfragen)

internet

Top Contributor
Hallo zusammen,

ich bräuchte mal ein paar Start Tipps / Hilfen zu Docker. Vielleicht kann ja jemand (per Zitat) meine konkrete Fragen beantworten.
Hinweis: als "Mandant" betitel ich nun meine User / Kunden.

Generell möchte ich folgendes realisieren:
  • Wildfly Server mit Mysql Datenbank inkl. meiner Webapp (war - file).
  • Pro Mandant möchte ich gerne eine Docker Instanz laufen lassen inkl. eigener Datenbank (Schema)

Hier einige Fragen:

Allgemein
Wie starte ich denn allgemein?

Nach meiner Recherche:
a) Ich installiere Docker Desktop
b) Ich installiere dann dies:
https://hub.docker.com/r/bitnami/wildfly
https://hub.docker.com/_/mysql
c) Wie ich das WAR - File in den Wildfly bekomme, wird hier beschrieben ( https://hub.docker.com/r/bitnami/wildfly )

Datenbank:
a) Habe ich dann eine zentrale Datenbank, der zB auf dbserver.myapp.com läuft? Und dann pro Mandant darauf ein Datenbank Schema?
b) Oder eine Datenbank pro Docker Instanz?

Automatisierung:
- Mein Prozess ist im Prinzip nichts anderes, wie bei SaaS Applikationen, wie Zendesk etc.
1) User registriert sich
2) Docker Instanz wird erstellt / gestartet. Hier muss ich doch dann in dem Docker File immer eine Anpassung machen? Mindestens mal das richtige Schema angeben?
3) Datenbank bzw. Schema erstellt
4) User kann die App nun über user1.mydomain.com aufrufen
-> Wie realisiere ich das automatisiert? Welche Tools brauche ich dazu? Hat der User dann in einigen Sekunden Zugriff auf die App?

Administration / Tools:
- Zusätzlich suche ich ein kostenloses Programm, mit dem ich dann die Docker Instanzen managen kann.
-> Ich denke sowas wie "Portainer"? So wie ich gesehen habe ist "Portainer" aber nicht kostenlos?
  • Ich habe nicht die Erfahrung was diese Admin Tool alles können muss. Aber mal mindestens die Docker Instanzen stoppen / starten
  • Das Tool sollte über eine GUI verfügen und nicht nur über die Konsole etc. bedienbar sein

Ressourcen Verteilung / Speicher
- Das ist mir noch nicht ganz klar, wie das mit den Ressourcen funktioniert. Ich habe quasi pro Mandant ein Container (Docker Instanz), welches dann:
a) Wildfly (500 MB) b) Mysql (1000 MB) c) War - File (500 MB) (= Applikation) enthält.
-> In Summe wären das also 2000 MB. D.h. dann eben auch 2000 MB pro Kunde? Also bei 10 Kunden: 20.000 MB Speicherplatz?
Nun habe ich aber gelernt, dass das wohl nicht so sei?
Das Betriebssystem brauche ich ja nicht dazuzählen. Das läuft bei Docker ja anders, als bei einer VM. Hier nutzt jeder Docker Container das gleiche Betriebssystem.

Danke für jegliche Hilfe
 

Dukel

Top Contributor
Probiere es doch einfach aus.

Produktiv würde ich kein Docker Desktop nutzen. Das sollte dann auf richtigen Servern oder in der Cloud laufen.

Ob du eine Zentrale DB oder für jeden Mandanten eine eigene DB erstellen willst, musst du entscheiden. Beides hat Vor und Nachteile.

Als Docker Plattform ist meist (vor allem bei Cloud Providern) Kubernetes im Einsatz.

Mit den Ressourcen meinst du hier Disk oder Ram?

EDIT: Portainer ist bis 3 Nodes (Server) frei.
 

mihe7

Top Contributor
Vorab: sämtlich gezeigter Code ist ungetestet.

Docker Desktop funktioniert bei mir unter Wndows mehr schlecht als recht. Spaß macht mir Docker dagegen unter Linux.

a) Habe ich dann eine zentrale Datenbank, der zB auf dbserver.myapp.com läuft? Und dann pro Mandant darauf ein Datenbank Schema?
Wenn Du das möchtest, kannst Du das machen. Allerdings:
Pro Mandant möchte ich gerne eine Docker Instanz laufen lassen inkl. eigener Datenbank (Schema)
sieht das etwas anders aus :)

Docker bietet virtuelle Netzwerke, die voneinander isoliert sind. Der Clou: im Netzwerk sind die Container unter einem Namen erreichbar, der einstellbar ist.

Bash:
# Erstelle für meine Anwendung ein Netzwerk mit dem Namen app-321-net
# Standardmäßig wird der bridge-Treiber verwendet, also genau das, was wir wollen
docker network create app-321-net

# Starte einen Container mit dem Namen "app-321-mysql" und füge ihn unter dem Namen "db" zum Netzwerk "app-321-net" hinzu
docker run --name=app-321-mysql --network=app-321-net --hostname=db -eMYSQL_ROOT_PASSWORD=abc -d mysql:8

# Starte die Anwendung in einem Container mit dem Namen "app-321" und füge ihn zum Netzwerk "app-321-net" hinzu.
# Damit ist der Host "db" für den Client sichtbar.
# Beispielhaft einfach mal ein MySQL-Client, der auf den MySQL-Server "db" zugreift und eine Abfrage ausführt
docker run --name=app-321 --network=app-321-net mysql:8 mysql -uroot -pabc -hdb mysql -e "SELECT 5"

Mit docker-compose schreibst Du die Konfiguration letztlich in ein YAML-File und kannst dann einfach mit einem Befehl die komplette Anwendung starten:
Code:
docker-compose -f /pfad/zum/yaml up -d
Mit dem passenden YAML erzeugt der Befehl dann das Netzwerk und die entsprechenden Container. Beispiele findest Du unter https://hub.docker.com/_/mysql

2) Docker Instanz wird erstellt / gestartet. Hier muss ich doch dann in dem Docker File immer eine Anpassung machen? Mindestens mal das richtige Schema angeben?
Das Schema kannst Du konstant wählen, da Du ja unterschiedliche DB-Server hast.

Datenbank bzw. Schema erstellt
Wird im Dockerhub von MySQL gezeigt.

User kann die App nun über user1.mydomain.com aufrufen
Hierfür fehlen ein paar Kleinigkeiten. Zunächst einmal musst Du die Subdomain im Nameserver konfigurieren. Dann gehe ich einmal davon aus, dass der Anwender mit dem normalen HTTP(S)-Ports arbeiten soll. Hierfür benötigst Du einen Reverse Proxy, der die Anfragen auf dem HTTP(S)-Port entgegennimmt und an den betreffenden Container weiterleitet.

-> Wie realisiere ich das automatisiert? Welche Tools brauche ich dazu? Hat der User dann in einigen Sekunden Zugriff auf die App?
Du kannst z. B. beim Registrieren ein docker-compose-File generieren und docker-compose aufrufen. Darüber hinaus musst Du - wie oben erwähnt - den Nameserver konfigurieren. Wann der User dann Zugriff auf die Anwendung hat, hängt davon ab, wie schnell der Nameserver die Änderungen übernimmt und Deine Anwendung startet. Der Start der Anwendung dauert in etwa genauso lange wie ohne Docker.

Das ist mir noch nicht ganz klar, wie das mit den Ressourcen funktioniert
Ohne in Details zu gehen: ein Container basiert auf einem Image und ein Image ist read-only. Heißt: wenn Du 20 x Wildfly startest, existiert das Image trotzdem nur einmal auf der Platte. On top kommen die Änderungen, die im Betrieb auf die Platte geschrieben werden. Das kann im Container passieren (weg, wenn man den Container löscht) oder in einem gemountenten Volume.

Was RAM betrifft: klar, wenn Du 20 x Wildfly startest, brauchst Du im Grundsatz auch 20 x RAM dafür. Man kann Limits angeben. Hier ist ein Artikel, der ein paar Dinge näher beschreibt: https://eldermoraes.com/java-containers-what-i-wish-i-knew-before-i-used-it/
 

Oneixee5

Top Contributor
Automatisierung:
- Mein Prozess ist im Prinzip nichts anderes, wie bei SaaS Applikationen, wie Zendesk etc.
1) User registriert sich
2) Docker Instanz wird erstellt / gestartet. Hier muss ich doch dann in dem Docker File immer eine Anpassung machen? Mindestens mal das richtige Schema angeben?
3) Datenbank bzw. Schema erstellt
4) User kann die App nun über user1.mydomain.com aufrufen
-> Wie realisiere ich das automatisiert? Welche Tools brauche ich dazu? Hat der User dann in einigen Sekunden Zugriff auf die App?
Ich mache die Automatisierung mit Traefik. Traefik ist ein für Docker entwickelter Proxy. Traefik ist ein Tool mit riesigem Potential, hier kann ich nicht beschreiben was das alles kann, das wäre einfach zu viel. Ich mache hier mal ein kleines Beispiel mit einer Spring Boot Anwendung mit integrierter HSQLDB. Der Code ist ein abgewandeltes Projekt von Github und diente mir lange Zeit als CalDav- und CardDav-Server.
Bash:
tree carldav
carldav
├── app
│   ├── carldav.jar
│   ├── carldav.src.zip
│   └── Dockerfile
├── data
│   ├── config
│   │   └── application.properties
│   └── data
│       └── hsqldb
│           ├── carldav.data
│           ├── carldav.lck
│           ├── carldav.lobs
│           ├── carldav.log
│           ├── carldav.properties
│           ├── carldav.script
│           └── carldav.tmp
├── docker-compose.yml
└── readme
Der Ordner app enthält die Anwendung + Dockerfile:
Code:
FROM azul/zulu-openjdk-alpine:11-jre

# Running applications with user privileges helps to mitigate some risks
# make sure that the user has write access to possible volumes
RUN addgroup -S carldav && adduser -S carldav -G carldav
USER carldav:carldav

ARG JAR_FILE=*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Also das Basisimage ist hier: azul/zulu-openjdk-alpine:11-jre - uralt, aber ist nur ein Beispiel

Entscheidend für die Automatisierung ist hier docker-compose.yml:
Code:
version: '3'

services:
  carldav:
    build: ./app
    container_name: carldav
    ports:
      - 1984:1984
    expose:
      - 1984
    volumes:
      - /opt/dockers/carldav/data/config/application.properties:/config/application.properties
      - /opt/dockers/carldav/data/data:/data
    restart: always
    networks:
      web:
        ipv4_address: 172.25.0.9
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=web"
      - "traefik.http.services.carldav.loadbalancer.server.port=1984"
      - "traefik.http.routers.carldav.tls.certresolver=mytlschallenge"
      - "traefik.http.routers.carldav.entrypoints=websecure"
      - "traefik.http.routers.carldav.rule=Host(`my-domain.de`) && PathPrefix(`/directory`)"
networks:
  web:
    external: true
Der obere Teil legt einfach die Ports fest und bindet eine application.properties sowie einen Ordner für die DB-Daten ein, außerdem wird im Docker-Network eine feste IP vergeben. Das Network hat den Namen web und ist außerhalb konfiguriert.
Jetzt kommt die Magie: labels
Die Label sind einfach Anweisungen für Traefik. Wird der Container jetzt gestartet: docker compose up --detach
dann registriert Traefik das und stellt unter https://my-domain.de/directory die Anwendung zur Verfügung. Wenn nötig wird automatisch ein Zertifikat erzeugt oder verlängert. Das funktioniert genauso mit Subdomains, für die bräuchte man aber DNS oder so.

Das ist natürlich ein sehr einfaches Beispiel. Da geht noch viel mehr. Traefik kann Container auch auf Anforderung starten. Ich leite auch alle Anfragen durch eine Security-Chain, bei Bedarf auch durch ein zentrales Login-System, uvm.

Man bekommt auch ein automatisches Update und Deployment hin. Es gibt einen Docker-Image: containrrr/watchtower
damit kann man alle seine Images automatisch updaten. Da Traefik auch ein Loadbalancer ist, geht das auch unterbrechungsfrei.

Wenn Kubernetes zu teuer und zu aufwändig ist, dann ist Traefik die beste Lösung - finde ich.

docker-compose.yml könnte man auch als Template anlegen und automatisch ausfüllen in einen Ordner kopieren, starten und voiala: das Ding ist nach ein paar Sekunden online. Das Starten eines optimierten SpringBoot-Images dauert unter 0,2 Sekunden, noch schneller wird es nur mit Quarkus. Der erste Start dauert natürlich etwas länger, da dann die DB angelegt wird.

Wildfly Server mit Mysql Datenbank würde ich in getrennte Container packen - ein Container eine Aufgabe - Container erzeugen kaum Overhead, im Gegensatz zu VM's. Updates und Backupds werden dadurch einfacher. MySQL würde ich durch postgres:alpine erstzen, das hat weniger Grundlast und MySQL ist für Amateure.
 
Zuletzt bearbeitet:

internet

Top Contributor
Vielen Dank für die Antworten.

Ich habe mich nun mal intensiver mit Docker beschäftigt und wollte nun einfach mal meine Anwendung in einem Docker Container starten lassen. Nun habe ich aber ein Problem mit der Connection zu Datenbank (Mysql)... Diese läuft eben unter localhost.
Ich frage mich nun aber, ob mein Docker diesen "localhost" überhaupt kennt? Denn ich kann keine Verbindung aufbauen...

Hier mein Dockerfile:
Java:
FROM quay.io/wildfly/wildfly:26.1.3.Final-jdk11

RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent


COPY my-standalone.xml /opt/jboss/wildfly/standalone/configuration/
COPY myapp.war /opt/jboss/wildfly/standalone/deployments/
COPY module.xml /opt/jboss/wildfly/modules/system/layers/base/com/mysql/main/
COPY mysql-connector-java-8.0.12.jar /opt/jboss/wildfly/modules/system/layers/base/com/mysql/main/

EXPOSE 8080 9990

# Run with custom configuration
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c","my-standalone.xml"]

In der my-standalone.xml von Wildfly habe ich die Connection definiert:
Code:
 <subsystem xmlns="urn:jboss:domain:datasources:7.0">
            <datasources>
                <datasource jta="true" jndi-name="java:jboss/datasources/myapp" pool-name="myapp" enabled="true" use-java-context="true" use-ccm="true">
                    <connection-url>jdbc:mysql://localhost:3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false</connection-url>
                    <driver-class>com.mysql.cj.jdbc.Driver</driver-class>
                    <driver>mysql</driver>
                    <security>
                        <user-name>root</user-name>
                        <password>test</password>
                    </security>
                    <validation>
                        <validate-on-match>false</validate-on-match>
                        <background-validation>false</background-validation>
                    </validation>
                    <statement>
                        <share-prepared-statements>false</share-prepared-statements>
                    </statement>
                </datasource>
                <drivers>
                    <driver name="mysql" module="com.mysql">
                        <driver-class>com.mysql.cj.jdbc.Driver</driver-class>
                        <xa-datasource-class>com.mysql.cj.jdbc.MysqlXADataSource</xa-datasource-class>
                    </driver>
                </drivers>
            </datasources>
        </subsystem>

Ausführen tue ich:

Docker File starten / erstellen (im Verzeichnis ):
docker build -t mein_image_name .

Image starten:
docker run -p 8080:8080 mein_image_name
-> Wildfly startet aber ich erhalte dann diese Fehlermeldung:

Code:
21:57:06,149 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (ServerService Thread Pool -- 78) javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/myapp
21:57:06,155 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 78) MSC000001: Failed to start service jboss.persistenceunit."myapp.war#primary": org.jboss.msc.service.StartException in service jboss.persistenceunit."myapp.war#primary": javax.persistence.PersistenceException: [PersistenceUnit: primary] Unable to build Hibernate SessionFactory
    at org.jboss.as.jpa@26.1.3.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:199)
    at org.jboss.as.jpa@26.1.3.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:129)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at org.wildfly.security.elytron-base@1.19.1.Final//org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:664)
    at org.jboss.as.jpa@26.1.3.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:214)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
    at java.base/java.lang.Thread.run(Thread.java:829)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.JBossThread.run(JBossThread.java:513)
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: primary] Unable to build Hibernate SessionFactory
    at org.hibernate@5.3.28.Final//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1327)
    at org.hibernate@5.3.28.Final//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1253)
    at org.hibernate.jipijapa-hibernate5-3@26.1.3.Final//org.jboss.as.jpa.hibernate5.TwoPhaseBootstrapImpl.build(TwoPhaseBootstrapImpl.java:44)
    at org.jboss.as.jpa@26.1.3.Final//org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:171)
    ... 10 more
Caused by: org.hibernate.exception.GenericJDBCException: Unable to open JDBC Connection for DDL execution
    at org.hibernate@5.3.28.Final//org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
    at org.hibernate@5.3.28.Final//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
    at org.hibernate@5.3.28.Final//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
    at org.hibernate@5.3.28.Final//org.hibernate.resource.transaction.backend.jta.internal.DdlTransactionIsolatorJtaImpl.<init>(DdlTransactionIsolatorJtaImpl.java:62)
    at org.hibernate@5.3.28.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl.buildDdlTransactionIsolator(JtaTransactionCoordinatorBuilderImpl.java:46)
    at org.hibernate@5.3.28.Final//org.hibernate.tool.schema.internal.HibernateSchemaManagementTool.getDdlTransactionIsolator(HibernateSchemaManagementTool.java:175)
    at org.hibernate@5.3.28.Final//org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:94)
    at org.hibernate@5.3.28.Final//org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:183)
    at org.hibernate@5.3.28.Final//org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
    at org.hibernate@5.3.28.Final//org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
    at org.hibernate@5.3.28.Final//org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
    at org.hibernate@5.3.28.Final//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1250)
    ... 12 more
Caused by: java.sql.SQLException: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/myapp
    at org.jboss.ironjacamar.jdbcadapters@1.5.3.Final//org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:190)
    at org.jboss.as.connector@26.1.3.Final//org.jboss.as.connector.subsystems.datasources.WildFlyDataSource.getConnection(WildFlyDataSource.java:69)
    at org.hibernate@5.3.28.Final//org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
    at org.hibernate@5.3.28.Final//org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180)
    at org.hibernate@5.3.28.Final//org.hibernate.resource.transaction.backend.jta.internal.DdlTransactionIsolatorJtaImpl.<init>(DdlTransactionIsolatorJtaImpl.java:59)
    ... 20 more
Caused by: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/myapp
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:690)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.getManagedConnection(TxConnectionManagerImpl.java:440)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:789)
    at org.jboss.ironjacamar.jdbcadapters@1.5.3.Final//org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:182)
    ... 24 more
Caused by: javax.resource.ResourceException: IJ031084: Unable to create connection
    at org.jboss.ironjacamar.jdbcadapters@1.5.3.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createLocalManagedConnection(LocalManagedConnectionFactory.java:364)
    at org.jboss.ironjacamar.jdbcadapters@1.5.3.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.getLocalManagedConnection(LocalManagedConnectionFactory.java:371)
    at org.jboss.ironjacamar.jdbcadapters@1.5.3.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createManagedConnection(LocalManagedConnectionFactory.java:287)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreConcurrentLinkedDequeManagedConnectionPool.createConnectionEventListener(SemaphoreConcurrentLinkedDequeManagedConnectionPool.java:1328)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreConcurrentLinkedDequeManagedConnectionPool.getConnection(SemaphoreConcurrentLinkedDequeManagedConnectionPool.java:505)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.pool.AbstractPool.getSimpleConnection(AbstractPool.java:640)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:605)
    at org.jboss.ironjacamar.impl@1.5.3.Final//org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:624)
    ... 27 more
Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
    at com.mysql@8.0.12//com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:832)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:207)
    at org.jboss.ironjacamar.jdbcadapters@1.5.3.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory.createLocalManagedConnection(LocalManagedConnectionFactory.java:335)
    ... 34 more
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at com.mysql@8.0.12//com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
    at com.mysql@8.0.12//com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
    at com.mysql@8.0.12//com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
    at com.mysql@8.0.12//com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:167)
    at com.mysql@8.0.12//com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:91)
    at com.mysql@8.0.12//com.mysql.cj.NativeSession.connect(NativeSession.java:152)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:952)
    at com.mysql@8.0.12//com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:822)
    ... 38 more
Caused by: java.net.ConnectException: Connection refused (Connection refused)
    at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
    at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
    at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.base/java.net.Socket.connect(Socket.java:609)
    at com.mysql@8.0.12//com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:173)
    at com.mysql@8.0.12//com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:65)
    ... 41 more
 

Dukel

Top Contributor
Netzwerk (und später Persistenter Speicher) sind keine Einfachen Themen.
Es fängt dabei an, dass es mehrere Möglichkeiten gibt.

Deine Container müssen miteinander sprechen können. Mysql darf nicht auf Localhost lauschen.

Hier findest du z.B. weitere Informationen:
 

internet

Top Contributor
also aktuell läuft Mysql auf meinem Rechner einfach als ein Dienst.... Das heißt Docker kann damit nichts anfangen?

Es gibt ja auch die Option über: docker-compose.yaml ....
Wie wird / werden hier die "Dockerfiles" aufgerufen? Ich sehe hier keine Referenz auf den entsprechenden Pfad...

Aufrufen kann ich dies ja über:
docker-compose -f /pfad/zum/yaml up -d

Schaue ich mir das Beispiel mal an, ist mir nicht klar wie das Dockerfile von Wildfly und Oracle jeweils aufgerufen wird:
 

mihe7

Top Contributor
Diese läuft eben unter localhost.
Ich frage mich nun aber, ob mein Docker diesen "localhost" überhaupt kennt? Denn ich kann keine Verbindung aufbauen...
Ja, heißt dann host.docker.internal (s. https://docs.docker.com/desktop/net...ect-from-a-container-to-a-service-on-the-host)

Wie wird / werden hier die "Dockerfiles" aufgerufen? Ich sehe hier keine Referenz auf den entsprechenden Pfad...
Die Dockerfiles werden zum Erzeugen der Images verwendet. Docker-Compose ist für die Ausführung von Containern gedacht. Man kann zwar auch im YAML ein Dockerfile angeben und den Build über docker-compose durchführen, habe ich aber noch nie gemacht.

In docker-compose gibst Du das Image an, das zur Erzeugung des Containers verwendet werden soll, hier mal das Beispiel von MySQL auf Dockerhub:
https://hub.docker.com/_/mysql hat gesagt.:
YAML:
# Use root/example as user/password credentials
version: '3.1'

services:

  db:
    image: mysql
    # NOTE: use of "mysql_native_password" is not recommended: https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html#upgrade-caching-sha2-password
    # (this is just an example, not intended to be a production configuration)
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

Hier werden zwei Dienste spezifiziert: db und adminer. Für db wird das Image mysql verwendet, für adminer das gleichnamige Image. Da keine Version angegeben wird, wird :latest angenommen. BTW: im YAML wird kein Netzwerk konfiguriert, somit laufen beide im default-Netzwerk von docker.

Nehmen wir an, Du hast die Dienste mit docker-compose up -d gestartet. Jetzt gibts von MySQL ein Update auf Dockerhub, dessen Image Du mit docker pull mysql abrufst. Dann kannst Du Deine Container mit Hilfe eines erneuten docker-compose up -d aktualisieren. Das gleiche gilt auch, wenn Du Dein Anwendungs-Image neu baust: docker build erzeugt ein neues Image und mit docker-compose up -d wird der Container, der dieses Image verwendet, neu erstellt.

Nachtrag: der Kommentar von @Oneixee5 enthält bereits ein docker-compose-File... Da wird auch ein Netzwerk konfiguriert :)
 
Zuletzt bearbeitet:

internet

Top Contributor
Super, danke...
Bevor ich mich mich Portainer etc. beschäftige, will ich erstmal das docker-compose zum Laufen bringen.
Wenn ich das richtig verstehe, kann ich das docker-compose ja dann in Portainer laufen lassen (Begrifflichkeit wäre hier "Stack").

Fangen wir Schritt für Schritt an:

1) Aktuell scheint mir der Buildprozess (war - file) nicht zu funktionieren

Meine Dateistruktur ist:
1707377227315.png


Im Ordner "myapp":
1707377273136.png

Das Dockerfile sieht so aus:
Java:
FROM quay.io/wildfly/wildfly:26.1.3.Final-jdk11

RUN /opt/jboss/wildfly/bin/add-user.sh admin admin --silent


COPY my-standalone.xml /opt/jboss/wildfly/standalone/configuration/
COPY myapp.war /opt/jboss/wildfly/standalone/deployments/
COPY module.xml /opt/jboss/wildfly/modules/system/layers/base/com/mysql/main/
COPY mysql-connector-java-8.0.12.jar /opt/jboss/wildfly/modules/system/layers/base/com/mysql/main/

EXPOSE 8080 9990

# Run with custom configuration
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c","my-standalone.xml"]

Meine docker-compose.yaml:
Code:
version: "2"

services:
  app:
    container_name: "app"
    image: "myapp"
    environment:
      - WILDFLY_USER=admin
      - WILDFLY_PASS=adminPassword
      - DB_NAME=myapp
      - DB_USER=root
      - DB_PASS=test1234
      - DB_URI=jdbc:mysql://host.docker.internal:3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false
    build: ./myapp
    restart: always
    depends_on:
      - db
    volumes:
      - ~/deployments:/opt/jboss/wildfly/standalone/deployments
    ports:
      - "8080:8080" # application
      - "9990:9990" # admin console

  db:
    container_name: "db"
    image: "mysql:latest"
    environment:
      - MYSQL_DATABASE=myapp
      - MYSQL_USER=root
      - MYSQL_PASSWORD=test1234
      - MYSQL_ROOT_PASSWORD=test1234
    volumes:
      - ./workdir/db/init/:/docker-entrypoint-initdb.d/ # init database
      - ./workdir/db/data/:/var/lib/mysql/              # data storage
    ports:
      - "3307:3307"

networks:
    default:
        driver: bridge

Ich nutzte jetzt erstmal meinen MySQL Dienst auf dem Host (später soll dann die DB die in der docker-compose.yaml definiert wird, verwendet werden).

Start ich nun:
docker-compose -f docker-compose.yaml up -d

Wird beides richtig hochgefahren:
1707377489978.png

Gehe ich auf: http://localhost:8080/ ist der WildFly auch hochgefahren.
Rufe ich dann die App aber auf: http://localhost:8080/test.jsf -> Findet es die Seite nicht. Es scheint mir, dass das .war - File nicht deployed wird?

Was könnte das sein? Wenn ich nur das Dockerfile von der Applikation starte, dann funktioniert es und die Anwendung wird deployed.

2) Eine zweite Frage noch hinterher:
Ich betreibe für den Login einen Keyclock, welcher unter läuft (separater Prozess, der auf dem Host läuft).

Im Programmcode definiere ich diesen nur an einer Stelle (shiro.ini):
Code:
oidcConfig.baseUri = http://localhost:9009/auth
oidcConfig.discoveryURI = http://localhost:9009/auth/realms/gixx/.well-known/openid-configuration

Müsste ich dies statt localhost auch zu host.docker.internal im Programmcode definieren?
Oder kann ich dies irgendwie dem .yaml - File mitgeben, dass er dies berücksichtigen soll?
 

LimDul

Top Contributor
Das sieht schräg aus:

volumes:
- ~/deployments:/opt/jboss/wildfly/standalone/deployments
Das lokale Verzeichnis ~/deployments soll auf /opt/jboss/wildfly/standalone/deployments gemappt werden

COPY myapp.war /opt/jboss/wildfly/standalone/deployments/
Du kopierst in das Verzeichnis im Dockerfile was rein - was aber von außen gemappt werden soll. Das ist dann vermutlich nicht mehr sichtbar.

Du solltest dich für entweder oder entscheiden. Entweder das Volume mounten und das myapp.war auf dem Host nach ~/deployments kopieren oder nicht mounten und das war ins Dockerfile kopieren
 

internet

Top Contributor
Du solltest dich für entweder oder entscheiden. Entweder das Volume mounten und das myapp.war auf dem Host nach ~/deployments kopieren oder nicht mounten und das war ins Dockerfile kopieren
Nachdem ich das aus docker-compose.yaml herausgeworfen habe, funktioniert es :)
Java:
volumes:
      - ~/deployments:/opt/jboss/wildfly/standalone/deployments

Nun habe ich noch das Problem mit Keycloak...
BTW: http://0.0.0.0:9009/auth ist auch http://localhost:9009/auth
 

mihe7

Top Contributor
Ok, aber das heißt nun konkret? Was muss ich ändern?
Wo? :D

Der Aufruf in Deinem docker-compose-File:
Code:
/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c","my-standalone.xml"
ist ja okay.

Von Keycloak hast Du noch nichts gezeigt (vielleicht sehe ich es auch einfach bloß nicht). Lass Keycloak erstmal auf 0.0.0.0 lauschen, dann hast Du schon mal eine Fehlerquelle weniger.

Was ich bei Docker-Containern mache ist folgendes: die Anwendung selbst lasse ich auf 0.0.0.0 lauschen. Beim Portmapping des Docker-Containers gebe ich für Verbindungen, die von außen erreichbar sein sollen, einfach die Ports an (z. B. 80:80), für Verbindungen, die nur lokal verwendet werden sollen (Admingeschichten), gebe ich beim Portmapping 127.0.0.1:9000:9000 an.
 

internet

Top Contributor
Wo? :D

Der Aufruf in Deinem docker-compose-File:
Code:
/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0", "-c","my-standalone.xml"
ist ja okay.

Von Keycloak hast Du noch nichts gezeigt (vielleicht sehe ich es auch einfach bloß nicht). Lass Keycloak erstmal auf 0.0.0.0 lauschen, dann hast Du schon mal eine Fehlerquelle weniger.

Was ich bei Docker-Containern mache ist folgendes: die Anwendung selbst lasse ich auf 0.0.0.0 lauschen. Beim Portmapping des Docker-Containers gebe ich für Verbindungen, die von außen erreichbar sein sollen, einfach die Ports an (z. B. 80:80), für Verbindungen, die nur lokal verwendet werden sollen (Admingeschichten), gebe ich beim Portmapping 127.0.0.1:9000:9000 an.

Keycloak starte ich nicht über Docker etc.
das starte ich ganz normal über die Shell:

Java:
sh /mypath/Keycloak/keycloak-23.0.1/bin/kc.sh --verbose start-dev --http-enabled true --http-port 9009 --http-relative-path=/auth

Prinzipiell könnte ich Keycloak auch in einem Docker Container starten (sollte es dann auch in der Zukunft), aber das sollte dann nicht in der docker-compose-File sein, denn sonst hat jeder Kunde ja eine eigene Keycloak Instanz was kein Sinn macht...

Die Frage wäre dann wiederum wie die verschiedenen Container kommunizieren können?
 

KonradN

Super-Moderator
Mitarbeiter
Du kannst Ports weiterleiten. Dann hast Du einen Docker Port, der lokal oder remote verfügbar wird. Was @mihe7 bereits in #14 angesprochen hatte:
Was ich bei Docker-Containern mache ist folgendes: die Anwendung selbst lasse ich auf 0.0.0.0 lauschen. Beim Portmapping des Docker-Containers gebe ich für Verbindungen, die von außen erreichbar sein sollen, einfach die Ports an (z. B. 80:80), für Verbindungen, die nur lokal verwendet werden sollen (Admingeschichten), gebe ich beim Portmapping 127.0.0.1:9000:9000 an.

Und da spielt es dann keine Rolle, ob keycloak nun lokal oder in einem docker läuft: Es muss über einen Port des Host Systems erreichbar sein. Und diesen Port musst Du konfigurieren. Dazu kannst Du dann das angesprochene host.docker.internal nutzen oder wenn die IP generell erreichbar ist, dann auch die ip bzw. hostname.

Wenn die docker container zugriff auf das Netzwerk haben, dann könnten sie halt auch auf ein System wie auth.mydomain.com zugreifen. Dann ist egal, wo und wie es läuft - die Namensauflösung muss hat das richtige System benennen - also den Docker Host. Wenn es lokal läuft, dann wird der Port direkt bedient. Wenn es in einem docker container läuft, dann muss der Port entsprechend weiter gegeben werden.
 

internet

Top Contributor
ok, so ganz habe ich immer noch nicht verstanden, was ich nun tun sollte :)

Muss in der shiro.ini etwas geändert werden?
Java:
oidcConfig.baseUri = http://localhost:9009/auth
oidcConfig.discoveryURI = http://localhost:9009/auth/realms/myrealm/.well-known/openid-configuration

Ich hatte es mal geändert zu:
Code:
oidcConfig.baseUri = http://0.0.0.0:9009/auth
oidcConfig.discoveryURI = http://0.0.0.0:9009/auth/realms/myrealm/.well-known/openid-configuration

Das hat aber auch nichts gebracht.

Ansonsten habe ich ja schon diverse Ports angegeben im docker-compose, bpsw.
Code:
    ports:
      - "8080:8080" # application
      - "9990:9990" # admin console

Das Portmapping erfolgt doch beim Starten von Docker Compose? Oder kommt das auch noch in die docker-compose.yaml ?

Also dann sowas wie?
Code:
docker-compose -f /Users/myuser/Desktop/Docker/Test1/docker-compose.yaml up -d -p 9009:9009
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Also da einfach einmal einige Hinweise / Erläuterungen:


Das kann nicht funktionieren. Das ist ja in einem Docker Container. localhost oder 127.0.0.1 ist dann die Docker Instanz. In der Instanz läuft der keycloak aber doch nicht!

Das ist also wie mit den Häusern: localhost = aktuelles Haus.
Wenn Du aber nun etwas in ein anderes Haus (hier ein Wohnmobil = Docker Container) umziehst, dann ist es doch nicht mehr im Haus. Auch wenn das Wohnmobil in der Garage oder auf dem Grundstück steht: Jeder Zugriff, der sagt: "Es ist im Haus" schlägt fehl, denn es ist nicht mehr im Haus!

Dieses 0.0.0.0 ist keine gültige IP Adresse, die Du so ansprechen kannst! Das kannst Du nur verwenden, wenn Du eine Anwendung konfigurierst. Wenn eine Anwendung also einen Port öffnen soll, dann kannst Du definieren, auf welchen Ports etwas hören soll.

Vergleich mit dem Haus: Du kannst sagen, in welchen Zimmern etwas sein soll. Du kannst also sagen Wohnzimmer oder Küche. Aber hier kannst Du auch sagen: Alle Zimmer. Und dieses 0.0.0.0 ist nichts anderes als alle Zimmer.

Dieses alle Zimmer macht aber doch nur Sinn, wenn Du weisst, um welches Haus es sich handelt.

Wenn Du mich fragst, wohin du gehen sollst und ich sage "Alle Zimmer". Ja wohin gehst Du dann? Dein Haus? Mein Haus? Du musst ja genau sagen, wo sich Keycloak befindet. Daher 0.0.0.0 macht hier absolut keinen Sinn!

Dann die Portmappings:
Damit gibst Du an, wie Ports von außen erreicht werden können. Siehe dazu https://docs.docker.com/network/
Das bedeutet also, dass Zugriffe auf den Docker Host an den Container weiter gegeben werden können.

Aber Du willst ja jetzt den anderen Weg gehen. Du willst aus dem Docker Container auf den Docker Host zugreifen. Und das geht über host.docker.internal.

I want to connect from a container to a service on the host

The host has a changing IP address, or none if you have no network access. We recommend that you connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host.
 

internet

Top Contributor
Puh, das ist kompliziert :)

Also ich habe nun mal innerhalb der shiro.ini das geändert zu:
Java:
oidcConfig.baseUri = http://host.docker.internal:9009/auth
oidcConfig.discoveryURI = http://host.docker.internal:9009/auth/realms/myrealm/.well-known/openid-configuration
-> Würde für mich prinzipiell Sinn ergeben, aber geht auch nicht...

Nun versucht aber natürlich die Webapp beim Loginprozess (Keykloak) diese URL aufzurufen:
http://host.docker.internal:9009/auth/realms/myrealm/protocol/openid-connect/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%myapp%2Foauth%2Fcallback%3Fclient_name%3DKeycloakOidcClient&client_id=myapp-frontend&scope=openid

Nun findet er aber entsprechend diese Adresse nicht:
-> "Die Website ist nicht erreichbar"

Aber beim Aufruf der Webapp in meinem Browser bin ich ja auch nicht mehr wirklich im Docker Container, sondern im Docker Host...

In Keycloak wäre es noch diese Stelle, die ggf. angepasst werden muss, aber das glaube ich eher nicht:
1707398417076.png

Also etwas scheint immer noch nicht zu klappen
 

KonradN

Super-Moderator
Mitarbeiter
Wie startest Du keycloak? Ist ist da der Hostname gesetzt (-hostname Parameter bzw. keycloak.conf mit hostname Eintrag).
Und an welcher Stelle kommt die Fehlermeldung? Das ist beim Aufruf der Login-Seite vom Keycloak? Oder ist es nach der Anmeldung der Redirect?
(Müsste eigentlich ersteres sein denke ich mal. Da wäre die Frage wichtig, auf welchen Netzwerkdevices sich der keycloak bindet.)
 

internet

Top Contributor
Keycloak starte ich über die Shell:
Java:
sh /mypath/Keycloak/keycloak-23.0.1/bin/kc.sh --verbose start-dev --http-enabled true --http-port 9009 --http-relative-path=/auth

Fehler kommt bei der Aufruf der Login - Seite. Also ich komme erst gar nicht auf die Login - Seite von Keycloak.
Ich verwende Pac4j... Im Log sehe ich dann nur:

Code:
2024-02-08 15:04:28 Caused by: java.net.ConnectException: Connection refused (Connection refused)
2024-02-08 15:04:28     at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
2024-02-08 15:04:28     at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
2024-02-08 15:04:28     at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
2024-02-08 15:04:28     at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
 

LimDul

Top Contributor
Du hast jetzt eine Sitution, die etwas "schräg" ist. Du hast eine Keycloak Instanz, die unter localhost läuft, die aber remote erreichbar sein soll. Das sorgt natürlich für Probleme, weil der Hostname, mit dem deine Anwendung mit Keycloak kommuniziert ein ganz anderer ist, als der hostname, unter dem es für den User erreichbar ist. Bei allen Konfigurationen in deiner Anwendung, wo es um die redirect Url geht muss localhost rein, bei allen Konfigurationen wo es um die direkte Kommunikation geht muss host.docker.internal stehen.

Alternativen: Sorg dafür, dass dein Rechner einen einen resolvbaren Hostnamen bekommt und gib den an oder installier keycloak auch in einem docker container und sprich den entsprechend an.
 

internet

Top Contributor
oder installier keycloak auch in einem docker container und sprich den entsprechend an.
ok, danke für die Erklärung.

Ich habe nun Keycloak in einen seperaten Docker - Container gepackt, welcher nun auch läuft.
Wie schaffe ich nun aber eine Verbindung / Kommunikation zwischen den beiden 1. Container (App) und 2. Container (Keycloak) her?
Hier muss doch bestimmt etwas in der docker-compose.yaml (vermutlich in der von der Applikation) angepasst / hinzugefügt werden? Wenn ja, was?
 

Dukel

Top Contributor
Lies die Doku, die ich schon gepostet hatte. Du kannst z.B. Alias vergeben.

When these containers are joined to the user-defined bridge network, they can address each other by this name.
This means you don’t need to worry about keeping track of containers’ IP addresses, which can frequently change.

D.h. du vergibst die Aliase mysql, wildfly und keycloak und greifst dann via mysql:3306 auf MySql und via keyvloak:9009 auf Keycloak zu.
 

mihe7

Top Contributor
mache ich das nicht im docker-compose.yaml - file?
Damit definierst Du ein Netzwerk (entspricht dem docker network create aus Kommentar #3). Beim Container musst Du noch angeben, dass er sich in diesem Netzwerk befindet (entspricht dem --network Parameter aus #3). Wie das funktioniert: https://docs.docker.com/compose/networking/#specify-custom-networks

Danach kannst Du die Container mit ihrem Servicenamen ansprechen. In Deinem Beispiel aus #9 kannst Du von app aus auf die DB über den Hostnamen "db" zugreifen. Du würdest also statt
Code:
- DB_URI=jdbc:mysql://host.docker.internal:3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false
einfach
Code:
- DB_URI=jdbc:mysql://db3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false
schreiben können.
 

internet

Top Contributor
Damit definierst Du ein Netzwerk (entspricht dem docker network create aus Kommentar #3). Beim Container musst Du noch angeben, dass er sich in diesem Netzwerk befindet (entspricht dem --network Parameter aus #3). Wie das funktioniert: https://docs.docker.com/compose/networking/#specify-custom-networks

Danach kannst Du die Container mit ihrem Servicenamen ansprechen. In Deinem Beispiel aus #9 kannst Du von app aus auf die DB über den Hostnamen "db" zugreifen. Du würdest also statt
Code:
- DB_URI=jdbc:mysql://host.docker.internal:3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false
einfach
Code:
- DB_URI=jdbc:mysql://db3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false
schreiben können.
Und wie muss die Config denn dann in der Shiro.ini aussehen?

Java:
oidcConfig.baseUri = http://localhost:9009/auth
oidcConfig.discoveryURI = http://localhost:9009/auth/realms/myapp/.well-known/openid-configuration

Wenn ich eine andere Keycloak Docker Instanz habe:
Code:
services:
   keycloak:
    image: quay.io/keycloak/keycloak:23.0.1
    container_name: "keycloak"
    environment:
      - "KEYCLOAK_IMPORT=/opt/jboss/keycloak/imports/realm-export.json"
      - "KEYCLOAK_ADMIN=admin"
      - "KEYCLOAK_ADMIN_PASSWORD=admin"
    command:
      - "start-dev"
      - "--http-port=9009"
      - "--http-enabled=true"
      - "--http-relative-path=auth"
      - "--import-realm"
    volumes:
      - ./imports:/opt/jboss/keycloak/imports
    ports:
      - 9009:9009   
    restart: always
    networks:
      my_custom_net:

networks:
  my_custom_net:
    external: true

Muss ich das dann umändern in ?
Java:
oidcConfig.baseUri = http://keycloak:9009/auth
oidcConfig.discoveryURI = http://keycloak:9009/auth/realms/myapp/.well-known/openid-configuration
 

internet

Top Contributor
dann erhalte ich:

Java:
Caused by: java.io.FileNotFoundException: http://keycloak:9009/auth/realms/myapp/.well-known/openid-configuration

Also irgendwas passt da immer noch nicht...

Also ich habe zwei Docker Container:
a) Keycloak
b) Meine App + Mysql

Ich erstelle mir ein Network:
Code:
docker network create my_custom_net

a) Keycloak

docker-compose.yaml:
Code:
version: "3.8"

services:
   keycloak:
    image: quay.io/keycloak/keycloak:23.0.1
    container_name: "keycloak"
    environment:
      - "KEYCLOAK_IMPORT=/opt/jboss/keycloak/imports/realm-export.json"
      - "KEYCLOAK_ADMIN=admin"
      - "KEYCLOAK_ADMIN_PASSWORD=admin"
    command:
      - "start-dev"
      - "--http-port=9009"
      - "--http-enabled=true"
      - "--http-relative-path=auth"
      - "--import-realm"
    volumes:
      - ./imports:/opt/jboss/keycloak/imports
    ports:
      - 9009:9009   
    restart: always
    networks:
      - my_custom_net

networks:
  my_custom_net:
    external: true

b) Meine App + Mysql
Code:
version: "3.8"

services:
  app:
    container_name: "app"
    image: "myapp"
    environment:
      - WILDFLY_USER=admin
      - WILDFLY_PASS=adminPassword
      - DB_NAME=myapp
      - DB_USER=root
      - DB_PASS=test1234
      - DB_URI=jdbc:mysql://db:3306/myapp?serverTimezone=Europe/Berlin&useSSL=false
    build: ./myapp
    restart: always
    depends_on:
      - db
    ports:
      - "8080:8080" # application
      - "9990:9990" # admin console
    networks:
      - my_custom_net

  db:
    container_name: "db"
    image: "mysql:latest"
    environment:
      - MYSQL_DATABASE=myapp
      - MYSQL_USER=root
      - MYSQL_PASSWORD=test1234
      - MYSQL_ROOT_PASSWORD=test1234
    volumes:
      - ./workdir/db/init/:/docker-entrypoint-initdb.d/ # init database
      - ./workdir/db/data/:/var/lib/mysql/              # data storage
    ports:
      - "3307:3307" # expose MySQL port to host
    networks:
      - my_custom_net

networks:
  my_custom_net:
    external: true

Shiro.ini gibt es eine Config, die auf den Keycloak verweist. Hier habe ich aktuell:
Code:
oidcConfig.baseUri = http://localhost:9009/auth
oidcConfig.discoveryURI = http://localhost:9009/auth/realms/myapp/.well-known/openid-configuration

Das Problem ist aber offensichtlich, dass meine App nicht mit Keycloak kommunizieren kann.
Die App selbst ist deployed. Ich kann auch Seiten aufrufen, die nicht durch Keycloak "geschützt sind". Also Seiten, bei denen keine Authentifizierung notwendig ist...

Die Fehlermeldung ist bei Seiten mit Authentifizierung:
Code:
Caused by: java.net.ConnectException: Connection refused (Connection refused)

Ohne Docker läuft die App ohne Probleme...
 

internet

Top Contributor
Aber das ist doch das Thema, das wir im Thread schon hatten. Die app ist in einem container und damit ist localhost der docker container.
Du musst da aber doch den docker host ansprechen.
ja, auch das hier hat nichts gebracht:

Java:
oidcConfig.baseUri = http://host.docker.internal:9009/auth
oidcConfig.discoveryURI = http://host.docker.internal:9009/auth/realms/myrealm/.well-known/openid-configuration
 

KonradN

Super-Moderator
Mitarbeiter
Was nutzt Du auf dem Rechner? Linux? Windows? MacOS?

Es gibt bezüglich host.docker.internal einiges bezüglich Linux, wobei ich nicht weiss, ob das noch alles aktuell ist oder ob das veraltete Dinge sind.
Da findet sich aber teilweise die Information, dass man das entweder noch explizit konfiguriert oder das man die IP Adresse des Hosts direkt verwendet statt eben host.docker.internal.
 

internet

Top Contributor
Was nutzt Du auf dem Rechner? Linux? Windows? MacOS?

Es gibt bezüglich host.docker.internal einiges bezüglich Linux, wobei ich nicht weiss, ob das noch alles aktuell ist oder ob das veraltete Dinge sind.
Da findet sich aber teilweise die Information, dass man das entweder noch explizit konfiguriert oder das man die IP Adresse des Hosts direkt verwendet statt eben host.docker.internal.
MacOS...

In der standalone.xml von WildFly gehe ich auch über über host.docker.internal .

Hier funktioniert es wohl:
Java:
<connection-url>jdbc:mysql://host.docker.internal:3306/myapp?serverTimezone=Europe/Berlin&amp;useSSL=false</connection-url>
 

KonradN

Super-Moderator
Mitarbeiter
Also hier wundere ich mich etwas: Du hast in der connection URL den Port 3306. Im docker hast Du Port 3307?
Das funktioniert ggf, weil Du auf ein lokales mysql zugreifst und nicht auf den docker container?
 

internet

Top Contributor
Also hier wundere ich mich etwas: Du hast in der connection URL den Port 3306. Im docker hast Du Port 3307?
Das funktioniert ggf, weil Du auf ein lokales mysql zugreifst und nicht auf den docker container?
ja, das ist korrekt. Die DB im Dockerfile kann erstmal auch außen vor gelassen werden...
Ich greife aktuell noch auf die Datenbank von meinem Host zu, welche im Hintergrund als Dienst läuft..
 

KonradN

Super-Moderator
Mitarbeiter
Du hast die docker container in einem eigenen Netzwerk und nicht im "bridge" network, das sonst automatisch genommen wird. Das könnte evtl. hier das Problem sein. Aber da alles im gleichen docker network ist, solltest Du über die Namen zugreifen können. Also wenn der container app den container keycloak zugreifen will, dann sollte das doch über den Namen keycloak gehen.

Das wäre mein Verständnis, wobei ich zugeben muss, dass ich mit docker noch nicht so viel gemacht habe (außer dem üblichen, dass dann mal etwas in einem docker container auf dem Arbeitsplatz lief aber nichts mit Kommunikation zwischen den Docker Containern).

Siehe dazu evtl. https://www.tutorialworks.com/container-networking/
 

internet

Top Contributor
Mach mal ein docker network inspect my_custom_net

Was wird da dann ausgegeben?

Java:
[
    {
        "Name": "my_custom_net",
        "Id": "e76bd30d2e3d0130899d11718f5878f7ac844e4106063571224c8b2e6dcdeb53",
        "Created": "2024-02-13T10:45:43.494696222Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.22.0.0/16",
                    "IPRange": "172.22.240.0/20",
                    "Gateway": "172.22.240.0"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "aed5cc3307bcbd25d7948576adcac158f0467bfb660f7151ed7d38b36912e15a": {
                "Name": "app",
                "EndpointID": "c896f90f079b5ef53af7e05509557b6be034cbf55fe140b9ccb5cfcba8fc0af9",
                "MacAddress": "02:42:ac:16:f0:02",
                "IPv4Address": "172.22.240.2/16",
                "IPv6Address": ""
            },
            "fb1a36d65335ec8e254b2ffb01e4ecbbc2ce5525514aa2e47795a5b6a3f9d6d4": {
                "Name": "keycloak",
                "EndpointID": "b543b54f9ea3ad1f7b9ad28ae064923af6e788c35efc1cae594c5fabf9c526cd",
                "MacAddress": "02:42:ac:16:f0:01",
                "IPv4Address": "172.22.240.1/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
 

KonradN

Super-Moderator
Mitarbeiter
Wenn Du Docker Desktop auf dem Mac hast, dann öffne das mal und gehe auf den keycloak container und gehe da mal auf Terminal. Da gibst Du dann mal ein:
netstat -an | grep 9009

Dann sehen wir hoffentlich, wie Keycloak gebunden ist. Das wird hoffentlich auch auf 172.22.240.1 gebunden sein.

Und bei dem app container kannst Du mal ein ping keycloak ausprobieren...

Das wären so ein paar Schritte, die ich probieren würde als nächstes um dem Problem etwas mehr auf die Spur zu kommen.
 

internet

Top Contributor
Wenn Du Docker Desktop auf dem Mac hast, dann öffne das mal und gehe auf den keycloak container und gehe da mal auf Terminal. Da gibst Du dann mal ein:
netstat -an | grep 9009

Dann sehen wir hoffentlich, wie Keycloak gebunden ist. Das wird hoffentlich auch auf 172.22.240.1 gebunden sein.

Und bei dem app container kannst Du mal ein ping keycloak ausprobieren...

Das wären so ein paar Schritte, die ich probieren würde als nächstes um dem Problem etwas mehr auf die Spur zu kommen.
ähm wo meinst du genau...

Habe es so probiert, aber den Befehl gibt es nicht?
1707898104860.png
 

KonradN

Super-Moderator
Mitarbeiter
Dann sind diese Tools nicht Bestandteil deines docker images. Evtl. macht es Sinn, beim Bau des Docker Images auf ein Basis Image zu gehen, dass diesbezüglich etwas mehr bereit hält, damit Du am Anfang mehr Möglichkeiten hast, Dinge nachzuverfolgen.

Daher wäre die erste Frage: auf welchem Image setzt Du bei denen Images auf (also das FROM, das Du angeben müsstest)?
 

internet

Top Contributor
Dann sind diese Tools nicht Bestandteil deines docker images. Evtl. macht es Sinn, beim Bau des Docker Images auf ein Basis Image zu gehen, dass diesbezüglich etwas mehr bereit hält, damit Du am Anfang mehr Möglichkeiten hast, Dinge nachzuverfolgen.

Daher wäre die erste Frage: auf welchem Image setzt Du bei denen Images auf (also das FROM, das Du angeben müsstest)?
Also das Docker-compose File habe ich bei #32 gepostet... Sollte doch auch so passen?
 

KonradN

Super-Moderator
Mitarbeiter
Hier ein kleines Feedback:
Ich habe das etwas ausprobiert und es sieht soweit ok aus. Die Anwendungen, die ich mir im Container gewünscht hätte, sind leider nicht da und ich habe da auf die Schnelle keine Lösung, um die mit hinzu zu fügen.

Ist aber unwichtig, denn man sieht ja die Ausgabe von Keycloak und da findet sich dann, dass keycloak (wie erwartet) an 0.0.0.0 bindet - damit kann es da keine Problematik geben.

Die Ports sind auf dem Host selbst auch komplett gebunden (War ja auch so erwartet)

Was ich jetzt noch nicht gemacht habe, da mir etwas die Zeit fehlt: Man könnte einfach ein ubuntu Container hernehmen für ein paar Tests. Da sollten dann paar Basic Tools mit drinnen sein um dann sowas wie ein nslookup, ping und co machen zu können.

Ich sehe da derzeit kein Problem, aber ich hatte es ja schon erwähnt meine ich: Ich bin bei Docker auch recht unbedarft und habe da nicht wirklich viel mit gemacht. Nur einzelne Container, die dann für irgendwas da waren und nicht untereinander kommuniziert haben.
 

mrBrown

Super-Moderator
Mitarbeiter
@internet hast du einfach mal "keycloak" als Domain ausprobiert?

Späße mit host.docker.internal und localhost würde ich lassen, das mag manchmal funktionieren, macht auf Dauer aber mehr Probleme als es löst.
 

KonradN

Super-Moderator
Mitarbeiter
so wie in #39 von mir genannt. Der keycloak container sollte als keycloak erreichbar sein. Wichtig: Da gilt nicht der container name (container_name: "keycloak") sondern der Service Name (keycloak: nach services:).

Also einfach mal, was ich gemacht habe:
ubuntu container:
YAML:
version: "3.8"

services:
   ubuntu:
    image: ubuntu:latest
    container_name: "ubuntu"
    restart: always
    build: .
    tty: true   

    networks:
      - my_custom_net

networks:
  my_custom_net:
    external: true

Das dann gestartet und dann einfach im terminal die Befehle:
apt upgrade
apt install iputils-ping

Danach dann ein ping keycloak:
Code:
# ping keycloak
PING keycloak (172.18.0.2) 56(84) bytes of data.
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=1 ttl=64 time=0.149 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=4 ttl=64 time=0.042 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=5 ttl=64 time=0.062 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=6 ttl=64 time=0.056 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=7 ttl=64 time=0.055 ms
64 bytes from keycloak.my_custom_net (172.18.0.2): icmp_seq=8 ttl=64 time=0.400 ms

==> Das Ansprechend eines Services über den Service Namen funktioniert somit auf dem Mac mit Docker Desktop einwandfrei.

Das war erst einmal das Wichtige für mich. Aber was jetzt nicht geht, ist der Zugriff vom host aus. Da ist keycloak nicht bekannt.
Evtl. ist das Problem, dass Du genau überlegen musst, wer an welcher Stelle überhaupt zugreifen will.

Wenn deine app zugreifen will: keycloak:9009
Wenn Du von deinem Rechner aus zugreifen willst: localhost:9009

Das ist also wichtig zu wissen. Deine Fehlermeldung, die Du mal gebracht hast, deutete etwas auf einen Webbrowser hin. Daher kann das evtl. schon ein Problem sein.

Natürlich könntest Du in deiner Hosts Datei eintragen, dass "keycloak" auch 127.0.0.1 ist - dann würde der lokale Rechner auch auf keycloak:9009 zugreifen können...

Das wären jetzt so ein paar Dinge, die ich auf die Schnelle getestet habe bzw. paar Ideen, die gehen könnten. Ich habe da aber keine tiefe Analyse gemacht...
 

Ähnliche Java Themen

Neue Themen


Oben