Docker-Build mit Host Zugriff

[ChAoZ]

Rear Admiral
Registriert
Jan. 2010
Beiträge
5.239
Hallo Leute,

ganz einfach, ich habe ein leeres CakePHP Projekt und möchte es über Docker betreiben.
Dazu baue ich das Image mit docker-compose build

Während des Builds wird auch Composer runtergeladen und installiert (Dev-Image).
Nun würde ich gerne auch composer installdurchführen, also beim Build-Prozess.

Soweit kein Problem, nur landet Vendor eben nicht in meinem Localhost sondern im Image.
Für Prod-Image ist das korrekt, für Dev-Image nicht.

Wie kriege ich es also hin dass beim Build die Abhängigkeiten (Vendor) im Localhost landen und nicht im Image?


Dockerfile
Code:
FROM bitnami/php-fpm:8.1.6-debian-10-r14 as base-image

LABEL version="0.1" \
      maintainer="FooBar"

# system update
RUN apt-get update && apt-get upgrade -y && \
    rm -r /var/lib/apt/lists /var/cache/apt/archives

# set right timezone and locale
RUN echo Europe/Berlin > /etc/timezone

ENV TZ Europe/Berlin
ENV LANG de_DE.UTF-8

WORKDIR /app

#### DEV IMAGE ####
FROM base-image as dev-image

ENV APP_ENV dev

# install required system packages and dependencies
RUN install_packages \
    build-essential \
    autoconf

RUN pecl install \
    xdebug-3.1.4

# copy docker-php scripts because bitnami images doesn't know them
# https://github.com/docker-library/php/tree/master/8.1/buster/fpm
COPY config/docker/docker-php-* /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-php-*

RUN docker-php-ext-enable \
    xdebug

# install composer
RUN wget -O composer https://getcomposer.org/download/latest-stable/composer.phar \
   && chmod a+x composer \
   && mv composer /usr/local/bin/composer

# inject  config file
COPY config/php/$APP_ENV/xdebug.ini /opt/bitnami/php/etc/conf.d/xdebug.ini
COPY config/php/$APP_ENV/php.ini /opt/bitnami/php/lib/php.ini

docker-compose.yml
Code:
version: "3.8"
services:
  php:
    image: app-api:dev
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "9000:9000"
    environment:
      CAKE_ENV: dev
    links:
      - mariadb
    restart: unless-stopped

  apache:
    image: bitnami/apache:2.4.53-debian-10-r71
    ports:
      - "8080:8080"
    volumes:
      - ./config/apache/dev/vhost.conf:/vhosts/app-api.conf:ro
    depends_on:
      - php
    restart: unless-stopped

  mariadb:
    image: bitnami/mariadb:10.7.4-debian-10-r5
    ports:
      - "3306:3306"
    volumes:
      - ./tmp/mariadb:/bitnami/mariadb
      - ./config/mariadb/schema:/docker-entrypoint-initdb.d/
    environment:
      MARIADB_ROOT_PASSWORD: root
    restart: unless-stopped
 
Ein volume oder bind mount verwenden?
 
Einen Build machst du, wenn du ein fertiges Image bauen willst. Willst du entwickeln, brauchst du doch nur einen HTTP/PHP-Server und mountest dir die entsprechenden Ressourcen in den Container. Normalerweise klonst du das Projekt, lässt nen generischen HTTP/PHP Container laufen und mountest dir eben die Projektdaten in den Container, dass dieser darauf zugreifen kann ohne irgendwelche Images erstellen zu müssen. Das Bootstrapping und was da nicht noch alles dranhängt, musst du dann lokal bei dir selbst erledigen.

composer brauchst du im Container auch gar nicht. Sowas sollte durch Multistage Builds gleich komplett unterbunden werden, dass die Build Toolchain mit im Image auftaucht, außer es wird wirklich zur Runtime benötigt. Macht das Image teilweise auch gleich mal mehrere 100 MB schlanker.

Wenn du composer benötigst, es aber nicht lokal installieren willst, mach ein
Code:
$ docker run composer -v "$(PWD):/app" install
 
Was meinst du mit local host? Willst du composer in einem der images oder auf dem host haben?
 
NJay schrieb:
Ein volume oder bind mount verwenden?
Siehe mein docker-compose file.
Host (das Projekt Verzeichnis) wird bereits als Volume angegeben, wird aber beim Build komplett ignoriert.
Ergänzung ()

madmax2010 schrieb:
Was meinst du mit local host? Willst du composer in einem der images oder auf dem host haben?
Localhost ist eben meine lokale Maschine, da wo der Code und mein IDE liegt = Ubuntu.
Ergänzung ()

Yuuri schrieb:
Einen Build machst du, wenn du ein fertiges Image bauen willst.
Exakt. Mein Bitnami PHP 8 Image hat weder Composer installiert noch Xdebug (weitere Module oder Pakete wie SSH kommen noch), also nehme ich das Basis PHP Image, baue es für mich zurecht und erstelle mir daraus ein eigenes neues Image welches ich fürs Entwicklen einsetze, oder eben für Prod in der Zukunft (dann eben ohne Xdebug und ohne Composer).

Yuuri schrieb:
Willst du entwickeln, brauchst du doch nur einen HTTP/PHP-Server und mountest dir die entsprechenden Ressourcen in den Container. Normalerweise klonst du das Projekt, lässt nen generischen HTTP/PHP Container laufen und mountest dir eben die Projektdaten in den Container, dass dieser darauf zugreifen kann ohne irgendwelche Images erstellen zu müssen. Das Bootstrapping und was da nicht noch alles dranhängt, musst du dann lokal bei dir selbst erledigen.
Richtig, so mache ich das auch, es geht aktuell nur ums Build.
Würde eben gern mit einem Rutsch das Image bauen UND alle Abhängigkeiten installieren.

Aktuell baue ich erst das Image und erst dann installiere ich die Abhängigkeiten. Würde es gern vereinheitlichen.

Yuuri schrieb:
composer brauchst du im Container auch gar nicht. Sowas sollte durch Multistage Builds gleich komplett unterbunden werden, dass die Build Toolchain mit im Image auftaucht, außer es wird wirklich zur Runtime benötigt. Macht das Image teilweise auch gleich mal mehrere 100 MB schlanker.

Wenn du composer benötigst, es aber nicht lokal installieren willst, mach ein
Code:
$ docker run composer -v "$(PWD):/app" install
Bitte meine Files ansehen...
Ich setze bereits auf MultiStage UND Composer wird ausschließlich im Dev-Image installiert.

Ja ich weiß, ich könnte auch nur das Comoser Image nehmen und so alles regeln, das war früher so.
Es spricht überhaupt nichts dagegen im Dev Image Composer zu installieren.
 
Zuletzt bearbeitet:
[ChAoZ] schrieb:
Richtig, so mache ich das auch, es geht aktuell nur ums Build.
Würde eben gern mit einem Rutsch das Image bauen UND alle Abhängigkeiten installieren.
Das ist aber nicht Ziel eines Builds in diesem Prozess.

Wie gesagt: Du nimmst zum Entwickeln ein generisches PHP Image und linkst dann alles von dir Benötigte lediglich per Volume in den Container. Die Build Stage ist davon unberührt, es sei denn du willst spezielle Optionen setzen oder jedwede Module hinzufügen (xdebug, xhprof, spx, Swoole, ...) und willst hierbei nicht über Volumes die Configs zusätzlich setzen. Die Dependencies kommen hier aber nicht mit rein. Das geht alles zur Runtime beim Entwickeln in den Container.

Für ein Production Image kommt das natürlich mit in den Container, dafür hast du beim Deployment ja eben keine Dependencies mehr lokal liegen und mountest diese nicht mehr, weil sie bereits im Image zur Verfügung stehen.

Wenn du das in einem Rutsch bauen willst, mach dir ein Makefile Target o.ä., welches du bootstrap nennst, was dir deine Images baut und Dependencies installiert - ergo das Bootstrapping übernimmt.
[ChAoZ] schrieb:
Ich setze bereits auf MultiStage UND Composer wird ausschließlich im Dev-Image installiert.
Das ist kein Multistage Build nur weil mehr als ein FROM enthalten ist. Du baust dir ein Image und darauf folgend gleich das Nächste, welches das erste implizit enthält. Prinzipiell kannst du dir die zweite FROM-Klausel sparen, da du direkt aufeinander aufsetzt. Aber wie gesagt: Das brauchst du zum Entwickeln alles gar nicht und ist nur fürs Production Image relevant.

Und wie gesagt: Build Time != Runtime. Zur Build Time erstellst du das Image. Du kannst lokale Daten zur Build Time an das Image senden - auch Build Context genannt. Du kannst aber nicht aus dem Image raus. Bidirektional geht nur zur Runtime und damit über Volumes.
 
  • Gefällt mir
Reaktionen: Bitopium und madmax2010
Yuuri schrieb:
Wie gesagt: Du nimmst zum Entwickeln ein generisches PHP Image und linkst dann alles von dir Benötigte lediglich per Volume in den Container.
Nochmal: das Basis Image hat weder Xdebug noch benötige Extensions wie PDO oder eben Intl vorinstalliert. CakePHP setz Intl voraus... wie soll ich bitte ohne entwickeln?

Ich nehme das fertige Image, baue es für meine Zwecke um und betreibe es dann.
Was ist daran bitte unüblich?

Yuuri schrieb:
Die Build Stage ist davon unberührt, es sei denn du willst spezielle Optionen setzen oder jedwede Module hinzufügen (xdebug, xhprof, spx, Swoole, ...)
Sage ich doch.

Yuuri schrieb:
und willst hierbei nicht über Volumes die Configs zusätzlich setzen. Die Dependencies kommen hier aber nicht mit rein. Das geht alles zur Runtime beim Entwickeln in den Container.
Ist eine Möglichkeit Config zu injecten, muss man aber nicht gebe ich dir Recht.
Was spricht den dagegen?

Yuuri schrieb:
Für ein Production Image kommt das natürlich mit in den Container, dafür hast du beim Deployment ja eben keine Dependencies mehr lokal liegen und mountest diese nicht mehr, weil sie bereits im Image zur Verfügung stehen.

Wenn du das in einem Rutsch bauen willst, mach dir ein Makefile Target o.ä., welches du bootstrap nennst, was dir deine Images baut und Dependencies installiert - ergo das Bootstrapping übernimmt.
Das macht meine aktuelle Lösung auch nicht besser, nur über Bash Files statt Make.

Yuuri schrieb:
Das ist kein Multistage Build nur weil mehr als ein FROM enthalten ist. Du baust dir ein Image und darauf folgend gleich das Nächste, welches das erste implizit enthält. Prinzipiell kannst du dir die zweite FROM-Klausel sparen, da du direkt aufeinander aufsetzt. Aber wie gesagt: Das brauchst du zum Entwickeln alles gar nicht und ist nur fürs Production Image relevant.
Step by Step. Im aktuellen Stadium des Projektes gibt es da noch nichts, aber es kommt schon. Die Strukturen liegen dann schon mal vor.

Yuuri schrieb:
Und wie gesagt: Build Time != Runtime. Zur Build Time erstellst du das Image. Du kannst lokale Daten zur Build Time an das Image senden - auch Build Context genannt. Du kannst aber nicht aus dem Image raus. Bidirektional geht nur zur Runtime und damit über Volumes.
Danke, so Ähnlich habe ich es auch zig Seiten auch gelesen.
Ergo nicht möglich beim Build auf den Host zuzugreifen.
 
[ChAoZ] schrieb:
Was ist daran bitte unüblich?
Weil du deine Dependencies im Image installieren willst bzw. sie aus dem Image "raus holen" willst. Das passiert aber nur im Production Image, da du beim Deployment keine Volumes mehr für Code verwendest, sondern nur noch für die anfallenden Daten.

Zum Entwickeln wird ein generisches Image verwendet, wo das gesamte Projekt via Volume eingebunden wird und die Dependencies werden lokal (außerhalb des Images) verwaltet. Sonst müsstest du ja bei jeder kleinsten Änderung permanent das Image neu bauen.
[ChAoZ] schrieb:
Was spricht den dagegen?
Dass die Configs dann eben nicht im Image stecken, sondern dynamisch "nach Gutdünken" geladen werden. Damit hast du halt keine einheitliche Basis mehr und dem ominösem "hat bei mir noch funktioniert" kommen wir wieder näher. Container existieren ja aber u.a. dafür, dass eine einheitliche Basis geschaffen wird. Somit können die Dev Images zum Großteil identisch in der Config bestehen und man muss da keine Überraschungen mehr erleben, nur weil sich plötzlich Paket x.y.35 anders verhält als das lokal betriebene x.y.34 oder weil du lokal Ubuntu nutzt und ein Debian aufm Production Server komplett andere Software und Versionen vorhält.
[ChAoZ] schrieb:
Das macht meine aktuelle Lösung auch nicht besser, nur über Bash Files statt Make.
Ja das ist der übliche Weg. Genau dafür gibts dann eben beliebige Task Runner (gulp, grunt, SCons, Rake, sake, doit, Webpack, npm Scripts, composer Scripts, usw.), Build Tools, Script Helper und welche drölf Bezeichnungen es dafür gibt, die das erledigen.

Ich nutz bspw. ne stark erweiterte Form von https://github.com/stylemistake/runner - ein simpler Task Runner in bash. Das kann ich dann auch problemlos in den Containern selbst nutzen ohne mir groß Node, Python und Co. mit rein holen zu müssen. Und da bash sowieso zu gefühlt 99,999 % vorinstalliert ist, funktioniert der auch überall - im Container, außerhalb, im Docker Build Prozess, der CI Pipeline, ...
 
  • Gefällt mir
Reaktionen: madmax2010
Yuuri schrieb:
Weil du deine Dependencies im Image installieren willst bzw. sie aus dem Image "raus holen" willst. Das passiert aber nur im Production Image, da du beim Deployment keine Volumes mehr für Code verwendest, sondern nur noch für die anfallenden Daten.
Hier herrscht wohl ein leichtes Missverständnis, ich drücke mich scheinbar nicht deutlich genug aus. Ich stimme dir bei 9/10 Sätzen zu, genau so kenne ich es und arbeite genau so.

Bei Dependencies gebe ich dir Recht.
Bei PHP Extensions und (unix) Bibliotheken nicht.
Aber genau das wird hier vermischt.

Wenn das Image diese nicht mitbringt, installiere ich sie nach, ganz normales Vorgehen.
Konfiguration kann man während des Build-Prozesses injecten, muss man aber nicht. Je nach dem um welche Konfig es sich handelt und wie oft diese sich ändern. Kenne beide Varianten.

Was ich wollte war lediglich beim Docker-Build Prozess composer install durchführen, aber nicht IM Image sondern im Host. Und genau das gelingt mir eben nicht, weshalb ich hier um Hilfe bat und mir eigentlich geholfen wurde. Es geht schlicht nicht.

Yuuri schrieb:
Zum Entwickeln wird ein generisches Image verwendet, wo das gesamte Projekt via Volume eingebunden wird und die Dependencies werden lokal (außerhalb des Images) verwaltet. Sonst müsstest du ja bei jeder kleinsten Änderung permanent das Image neu bauen.
Stimme ich dir zu, hat aber nicht viel mit meinem ursprünglichem "Problem" zutun.

Yuuri schrieb:
Dass die Configs dann eben nicht im Image stecken, sondern dynamisch "nach Gutdünken" geladen werden. Damit hast du halt keine einheitliche Basis mehr und dem ominösem "hat bei mir noch funktioniert" kommen wir wieder näher.
Container existieren ja aber u.a. dafür, dass eine einheitliche Basis geschaffen wird. Somit können die Dev Images zum Großteil identisch in der Config bestehen und man muss da keine Überraschungen mehr erleben, nur weil sich plötzlich Paket x.y.35 anders verhält als das lokal betriebene x.y.34 oder weil du lokal Ubuntu nutzt und ein Debian aufm Production Server komplett andere Software und Versionen vorhält.
In meinem Beispiel wird die php.ini beim bauen einfach überschrieben mit den jeweiligen dev/prod Einstellungen. Muss man nicht machen, gebe ich dir Recht, spricht aber auch nichts dagegen wenn die php.ini im Team getestet und "abgenommen" wurde.

Dass ich Konfigs für andere Services per Volume einbinde, ist ja oben auch ersichtlich. Lässt sich auch für PHP ändern, kein Problem.

Danke für die Bestätigung dass es nicht geht.
 
Versuch doch mal folgendes. Anstatt dein riesen Image in einem Rutsch bauen zu wollen, starte doch einen Container von entsprechenden Base-Image mit einer interaktiven Shell und führe dort die entsprechenden Schritte aus deinem Dockerfile manuell aus und schau dir an was dort passiert. Kopieren den Sourcecode in den Container und führe deine build commands aus. Dadurch weißt du am Ende genau welche Schritte du brauchst für dein Dockerfile. So würde ich vorgehen...

Letztendlich wirst du wohl als Optimierung ein Multistage-Image erzeugen wollen.

Edit: Warum überhaupt bitnami Images? Was ist an den offiziellen Images verkehrt?
 
Tokolosh schrieb:
Hi. Danke für dein Input.

Bitnami wegen den Sys Admins, es muss nicht sein aber wir haben uns drauf geeinigt (weil Debian Basiert). Mein Wunsch war Alpine aber am Ende auch egal.

Das was du schreibst ist bereits von mir so umgesetzt. Nach dem Docker Build starte ich ein Container dieses Images mit -rm und binde das Projekt als Volume ein. Dort führe ich composer install durch. Klappt alles wunderbar.
 
Zurück
Oben