
In diesem Artikel werde ich ĂŒber meine Erfahrungen beim âVerpackenâ einer Laravel-Anwendung in einen Docker-Container sprechen, damit Frontend- und Backend-Entwickler lokal damit arbeiten können und das Starten in der Produktion so einfach wie möglich war. AuĂerdem fĂŒhrt CI automatisch statische Code-Analysatoren, phpunit
Tests und Build-Images aus.
"Und was ist eigentlich KomplexitĂ€t?" - Sie können sagen, und Sie werden teilweise recht haben. Tatsache ist, dass sich in der russisch- und englischsprachigen Gemeinschaft ziemlich viele Diskussionen mit diesem Thema befassen, und ich wĂŒrde fast alle untersuchten Themen bedingt in die folgenden Kategorien einteilen:
- "Ich verwende Docker fĂŒr die lokale Entwicklung. Ich habe Laradock eingesetzt und kenne die Probleme nicht." Cool, aber was ist mit dem Automatisierungs- und Produktionsstart?
- "Ich sammle einen Container (Monolith) basierend auf
fedora:latest
(~ 230 MB), lege alle Dienste (Nginx, DB, Cache usw.) darin ab und fĂŒhre alles im Supervisor aus." Auch ausgezeichnet, leicht zu starten, aber was ist mit der Ideologie von "einem Container - einem Prozess"? Was ist mit Balancing und Prozessmanagement? Wie groĂ ist das Bild? - "Hier sind einige KonfigurationsstĂŒcke, die mit AuszĂŒgen aus Sh-Skripten gewĂŒrzt, magische Env-Werte hinzugefĂŒgt und verwendet werden." Danke, aber was ist mit mindestens einem lebenden Beispiel, das ich gabeln und voll spielen könnte?
Alles, was Sie unten lesen, ist eine subjektive Erfahrung, die nicht vorgibt, die ultimative Wahrheit zu sein. Wenn Sie ErgÀnzungen oder Hinweise auf Ungenauigkeiten haben - willkommen zu Kommentaren.
FĂŒr Ungeduldige - ein Link zum Repository , Klon, mit dem Sie die Laravel-Anwendung mit einem Befehl starten können. Es ist auch nicht schwierig, es auf demselben Rancher auszufĂŒhren, die Container korrekt zu "verknĂŒpfen" oder die Lebensmittelversion docker-compose.yml
als Ausgangspunkt zu verwenden.
Theoretischer Teil
Welche Werkzeuge werden wir in unserer Arbeit verwenden und worauf werden wir uns konzentrieren? ZunĂ€chst mĂŒssen wir auf dem Host installiert werden:
docker
- 18.06.1-ce
Zeitpunkt des Schreibens habe ich Version 18.06.1-ce
docker-compose
- Es bewĂ€ltigt das VerknĂŒpfen von Containern und das Speichern der erforderlichen Umgebungswerte. Version 1.22.0
make
- Sie werden vielleicht ĂŒberrascht sein, aber es passt perfekt in den Kontext der Arbeit mit Docker
Sie können docker
auf debian
Systemen mit dem Befehl curl -fsSL get.docker.com | sudo sh
curl -fsSL get.docker.com | sudo sh
, aber docker-compose
besser mit pip
zu installieren, da die neuesten Versionen in den Repositorys gespeichert sind ( apt
Regel weit zurĂŒck).
Damit ist die Liste der AbhÀngigkeiten vervollstÀndigt. Was Sie verwenden, um mit dem Quellcode zu arbeiten - phpstorm
, netbeans
oder the dead vim
- liegt ganz bei Ihnen.
Als nÀchstes folgt eine spontane QualitÀtssicherung im Zusammenhang mit (ich habe keine Angst vor diesem Wort) Bilddesign:
F: Grundbild - welches ist besser zu wÀhlen?
A: Derjenige, der "dĂŒnner" ist, ohne Schnickschnack. Auf der Grundlage von alpine
(~ 5 Mb) können Sie sammeln, was Ihr Herz begehrt, aber höchstwahrscheinlich mĂŒssen Sie mit der Zusammenstellung von Diensten aus der Quelle spielen. Als Alternative - jessie-slim
(~ 30 Mb) . Oder verwenden Sie diejenige, die in Ihren Projekten am hÀufigsten verwendet wird.
F: Warum ist das Bildgewicht wichtig?
A: Verringerung des Verkehrsaufkommens, Verringerung der Wahrscheinlichkeit eines Fehlers beim Herunterladen (weniger Daten - weniger Wahrscheinlichkeit), Verringerung des verbrauchten Ortes. Die Regel "Schwerkraft ist zuverlÀssig" (© "Snatch") funktioniert hier nicht sehr gut.
F: Aber mein Freund %friend_name%
sagt, dass ein "monolithisches" Bild mit all-all-AbhÀngigkeiten der beste Weg ist.
A: Lass uns einfach zĂ€hlen. Die Anwendung hat 3 AbhĂ€ngigkeiten - PG, Redis, PHP. Und Sie wollten testen, wie es sich in Bundles verschiedener Versionen dieser AbhĂ€ngigkeiten verhĂ€lt. PG - Versionen 9.6 und 10, Redis - 3.2 und 4.0, PHP - 7.0 und 7.2. Falls jede Sucht ein eigenes Bild ist - Sie benötigen 6 davon, die Sie nicht einmal sammeln mĂŒssen -, ist alles fertig und liegt auf hub.docker.com
. Wenn aus ideologischen GrĂŒnden alle AbhĂ€ngigkeiten in einem Container "verpackt" sind, mĂŒssen Sie ihn mit Stiften wieder zusammensetzen ... 8 Mal? opcache
Sie nun die Bedingung hinzu, dass Sie noch mit opcache
spielen opcache
. Im Falle einer Zerlegung ist dies einfach eine Ănderung der Tags der verwendeten Bilder. Ein Monolith ist einfacher zu betreiben und zu warten, aber es ist der Weg ins Nirgendwo.
F: Warum ist der Supervisor im Container böse?
A: Weil PID 1
. Wenn Sie nicht viele Probleme mit Zombie-Prozessen haben möchten und bei Bedarf flexibel âKapazitĂ€t hinzufĂŒgenâ können, versuchen Sie, einen Prozess pro Container auszufĂŒhren. Eine besondere Ausnahme ist nginx
mit seinen Arbeitern und php-fpm
, die die FĂ€higkeit haben, Prozesse zu produzieren, dies aber php-fpm
mĂŒssen (auĂerdem sind sie nicht schlecht darin, auf SIGTERM
zu reagieren und ihre Arbeiter ganz richtig zu "töten"). Wenn Sie alle DÀmonen als Supervisor starten, sind Sie mit ziemlicher Sicherheit zu Problemen verurteilt. In einigen FÀllen ist es zwar schwierig, darauf zu verzichten, aber dies sind bereits Ausnahmen.
Nachdem wir uns fĂŒr die wichtigsten AnsĂ€tze entschieden haben, fahren wir mit unserer Anwendung fort. Es muss in der Lage sein:
web|api
- Geben Sie mit nginx
statisch an und generieren Sie mit fpm
dynamischen Inhaltscheduler
- FĂŒhren Sie den nativen Taskplaner ausqueue
- Verarbeitet Jobs aus Warteschlangen
Ein Basissatz, der bei Bedarf erweitert werden kann. Kommen wir nun zu den Bildern, die wir sammeln mĂŒssen, damit unsere Anwendung âabhebtâ (ihre Codenamen sind in Klammern angegeben):
PHP + PHP-FPM
( App ) - die Umgebung, in der unser Code ausgefĂŒhrt wird. Da die Versionen von PHP und FPM fĂŒr uns gleich sind, sammeln wir sie in einem Bild. So ist die Verwaltung mit Konfigurationen einfacher und die Zusammensetzung der Pakete ist identisch. NatĂŒrlich - FPM- und Anwendungsprozesse werden in verschiedenen Containern ausgefĂŒhrtnginx
( nginx ) - das wĂŒrde sich nicht um die Lieferung von Konfigurationen und optionalen Modulen fĂŒr nginx
kĂŒmmern - wir werden ein separates Bild damit sammeln. Da es sich um einen separaten Dienst handelt, verfĂŒgt er ĂŒber eine eigene Docker-Datei und einen eigenen Kontext- Quellen der Anwendung ( Quellen ) - Die Quelle wird unter Verwendung eines separaten Abbilds geliefert, wobei das
volume
mit ihnen in einem Container mit der App bereitgestellt wird. Das Basis-Image ist alpine
, im Inneren befinden sich nur Quellen mit installierten AbhÀngigkeiten, die mithilfe von Webpack-Assets gesammelt wurden (Build-Artefakte).
Andere Entwicklungsdienste werden in Containern gestartet und von hub.docker.com
. In der Produktion hingegen werden sie auf separaten Servern in Clustern ausgefĂŒhrt. Wir mĂŒssen der Anwendung nur noch mitteilen (ĂŒber die Umgebung), an welchen Adressen / Ports und mit welchen Details sie angeklopft werden mĂŒssen. Noch cooler ist es, Service-Discovery fĂŒr diesen Zweck zu verwenden, aber nicht um diese Zeit.
Nachdem ich mich fĂŒr den theoretischen Teil entschieden habe, schlage ich vor, mit dem nĂ€chsten Teil fortzufahren.
Der praktische Teil
Ich schlage vor, Dateien im Repository wie folgt zu organisieren:
. âââ docker # - â âââ app â â âââ Dockerfile â â âââ ... â âââ nginx â â âââ Dockerfile â â âââ ... â âââ sources â âââ Dockerfile â âââ ... âââ src # â âââ app â âââ bootstrap â âââ config â âââ artisan â âââ ... âââ docker-compose.yml # Compose- âââ Makefile âââ CHANGELOG.md âââ README.md
Sie können sich mit der Struktur und den Dateien vertraut machen, indem Sie auf diesen Link klicken.
Um einen Dienst zu erstellen, können Sie den folgenden Befehl verwenden:
$ docker build \ --tag %local_image_name% \ -f ./docker/%service_directory%/Dockerfile ./docker/%service_directory%
Der einzige Unterschied besteht in der Zusammenstellung des Bildes mit den Quellen - dafĂŒr muss der Zusammenstellungskontext (extremes Argument) auf ./src
.
Die Regeln fĂŒr die Benennung von Bildern in der lokalen Registrierung empfehlen die Verwendung der Regeln, die docker-compose
standardmĂ€Ăig verwendet: %root_directory_name%_%service_name%
. Wenn das Projektverzeichnis my-awesome-project
heiĂt und der Dienst my-awesome-project_redis
ist es besser, den my-awesome-project_redis
(lokal) fĂŒr my-awesome-project_redis
zu wÀhlen.
Um den Erstellungsprozess zu beschleunigen, können Sie dem Docker anweisen, den Cache des zuvor zusammengestellten Images zu verwenden. Dazu wird die --cache-from %full_registry_name%
. Daher wird der Docker-Daemon vor dem Starten einer bestimmten Anweisung in der Docker-Datei nachsehen - hat sich diese geĂ€ndert? Wenn nicht (der Hash konvergiert), wird die Anweisung ĂŒbersprungen, wobei die bereits vorbereitete Ebene aus dem Bild verwendet wird, die Sie als Cache verwenden sollen. Dieses Ding ist nicht schlecht, also wird es den Prozess neu aufbauen, besonders wenn sich nichts geĂ€ndert hat :)
ENTRYPOINT
die ENTRYPOINT
Skripte zum Starten von Anwendungscontainern.
Das Image der Umgebung zum Starten der Anwendung (App) wurde unter BerĂŒcksichtigung der Tatsache erfasst, dass sie nicht nur in der Produktion funktioniert, sondern auch lokal. Entwickler mĂŒssen effektiv mit ihr interagieren. Das Installieren und Entfernen von composer
AbhĂ€ngigkeiten, das AusfĂŒhren von Komponententests, Endprotokollen und die Verwendung bekannter Aliase ( php /app/artisan
â art
, composer
â c
) sollte ohne Probleme sein. DarĂŒber hinaus werden damit auch unit
Tests und statische Code-Analysatoren (in unserem Fall phpstan
) auf CI ausgefĂŒhrt. Aus diesem Grund enthĂ€lt die xdebug
Datei beispielsweise die xdebug
Installationszeile, das Modul selbst ist jedoch nicht aktiviert (es wird nur mit CI aktiviert).
Auch fĂŒr composer
das Paket hirak/prestissimo
, was die Installation aller AbhÀngigkeiten erheblich hirak/prestissimo
.
WÀhrend der Produktion hÀngen wir den Inhalt des Verzeichnisses /src
aus dem Image mit den darin enthaltenen Quellen (Quellen) in das Verzeichnis /app
. FĂŒr die Entwicklung "rollen" wir das lokale Verzeichnis mit Anwendungsquellen ( -v "$(pwd)/src:/app:rw"
).
Und hier liegt eine KomplexitĂ€t: Dies sind Zugriffsrechte auf Dateien , die aus dem Container erstellt werden. Tatsache ist, dass die im Container ausgefĂŒhrten Prozesse standardmĂ€Ăig vom Stammverzeichnis ( root:root
), den von diesen Prozessen erstellten Dateien (Cache, Protokolle, Sitzungen usw.) ausgehen und daher nichts âlokalâ mit ihnen zu tun haben Sie können dies tun, ohne sudo chown -R $(id -u):$(id -g) /path/to/sources
auszufĂŒhren.
Verwenden Sie als eine Lösung fixuid , aber diese Lösung ist unkompliziert. Der beste Weg schien mir, USER_ID
lokale USER_ID
und ihre GROUP_ID
im Container USER_ID
und Prozesse mit diesen Werten zu starten . StandardmĂ€Ăig wurde durch Ersetzen der Werte 1000:1000
(die Standardwerte fĂŒr den ersten lokalen Benutzer) der Aufruf $(id -u):$(id -g)
, und Sie können sie $ USER_ID=666 docker-compose up -d
jederzeit ĂŒberschreiben ( $ USER_ID=666 docker-compose up -d
) oder .env
die Docker-Compose-Datei in die .env
Datei ein.
Wenn php-fpm
lokal gestartet wird php-fpm
vergessen php-fpm
nicht, den opcache
zu deaktivieren - andernfalls gibt es eine Menge "Ja, was zur Hölle!" Sie werden zur VerfĂŒgung gestellt.
FĂŒr eine "direkte" Verbindung zu Redis und Postgres habe ich zusĂ€tzliche Ports "out" ( 15432
bzw. 15432
) 15432
, sodass es im Prinzip keine Probleme gibt, "zu verbinden und zu sehen, was und wie es wirklich ist".
Ich behalte den Container mit der Codenamen- app
( --command keep-alive.sh
), um bequem auf die Anwendung zugreifen zu können.
Hier einige Beispiele zur Lösung alltÀglicher Probleme mit docker-compose
:
Bedienung | Befehl ausfĂŒhren |
---|
Installieren Sie das composer Paket | $ docker-compose exec app composer require package/name |
Phpunit ausfĂŒhren | $ docker-compose exec app php ./vendor/bin/phpunit --no-coverage |
Installieren Sie alle KnotenabhÀngigkeiten | $ docker-compose run --rm node npm install |
Installieren Sie das Knotenpaket | $ docker-compose run --rm node npm i package_name |
Starten eines Live-Wiederaufbaus von Assets | $ docker-compose run --rm node npm run watch |
Sie finden alle Startdetails in der Datei docker-compose.yml .
Choi make
lebendig!
Nach dem zweiten Mal wird es langweilig, jedes Mal dieselben Befehle einzugeben. Da Programmierer von Natur aus faule Wesen sind, sollten wir uns mit ihrer "Automatisierung" befassen. Das sh
einer Reihe von Skripten ist eine Option, aber nicht so attraktiv wie ein einzelnes Makefile
, zumal seine Anwendbarkeit in der modernen Entwicklung stark unterschÀtzt wird.
Das vollstÀndige russischsprachige Handbuch finden Sie unter diesem Link .
Mal sehen, wie der make
Lauf im Stammverzeichnis des Repositorys aussieht:
[user@host ~/projects/app] $ make help Show this help app-pull Application - pull latest Docker image (from remote registry) app Application - build Docker image locally app-push Application - tag and push Docker image into remote registry sources-pull Sources - pull latest Docker image (from remote registry) sources Sources - build Docker image locally sources-push Sources - tag and push Docker image into remote registry nginx-pull Nginx - pull latest Docker image (from remote registry) nginx Nginx - build Docker image locally nginx-push Nginx - tag and push Docker image into remote registry pull Pull all Docker images (from remote registry) build Build all Docker images push Tag and push all Docker images into remote registry login Log in to a remote Docker registry clean Remove images from local registry --------------- --------------- up Start all containers (in background) for development down Stop all started for development containers restart Restart all started for development containers shell Start shell into application container install Install application dependencies into application container watch Start watching assets for changes (node) init Make full application initialization (install, seed, build assets) test Execute application tests Allowed for overriding next properties: PULL_TAG - Tag for pulling images before building own ('latest' by default) PUBLISH_TAGS - Tags list for building and pushing into remote registry (delimiter - single space, 'latest' by default) Usage example: make PULL_TAG='v1.2.3' PUBLISH_TAGS='latest v1.2.3 test-tag' app-push
Er ist sehr gut in sĂŒchtig machenden Zielen. Um beispielsweise docker-compose run --rm node npm run watch
zu starten ( docker-compose run --rm node npm run watch
), muss die Anwendung " docker-compose run --rm node npm run watch
" werden - Sie mĂŒssen nur das up
Ziel als abhĂ€ngig angeben - und Sie mĂŒssen sich nicht darum kĂŒmmern, dies zu vergessen, bevor Sie watch
aufrufen - make
sich alles fĂŒr Sie tun. Gleiches gilt zum Beispiel fĂŒr das AusfĂŒhren von Tests und statischen Analysatoren, bevor Ănderungen vorgenommen werden. FĂŒhren Sie einen make test
und die ganze Magie wird fĂŒr Sie geschehen!
Sie mĂŒssen sich --cache-from
nicht darum kĂŒmmern, Bilder zusammenzustellen, herunterzuladen, --cache-from
und so --cache-from
alles anzugeben?
Sie können den Inhalt des Makefile
unter diesem Link anzeigen.
Autoteil
Fahren wir mit dem letzten Teil dieses Artikels fort - dies ist die Automatisierung des Aktualisierungsprozesses von Bildern in der Docker-Registrierung. Obwohl in meinem Beispiel GitLab CI verwendet wird, um die Idee auf einen anderen Integrationsdienst zu ĂŒbertragen, denke ich, dass dies durchaus möglich sein wird.
ZunÀchst bestimmen wir die Benennung der verwendeten Bild-Tags:
Tag-Name | Ziel |
---|
latest | Bilder aus der Hauptniederlassung gesammelt. Der Status des Codes ist der neueste, aber noch nicht bereit, in die Version einzusteigen |
some-branch-name | Bilder, die auf dem Brunch some-branch-name Zweignamens gesammelt wurden. Auf diese Weise können wir Ănderungen in jeder Umgebung "einfĂŒhren", die nur im Rahmen eines bestimmten Brunchs implementiert wurden, noch bevor sie mit dem master Light zusammengefĂŒhrt werden. Es reicht aus, die Bilder mit diesem Tag zu "strecken". Und - ja, die Ănderungen können sowohl den Code als auch die Bilder aller Dienste im Allgemeinen betreffen! |
vX.XX | Eigentlich die Freigabe der Anwendung (zum Bereitstellen einer bestimmten Version) |
stable | Alias ââfĂŒr das Tag mit der neuesten Version (zum Bereitstellen der neuesten stabilen Version) |
Die Veröffentlichung erfolgt durch Veröffentlichung eines Tags in einem vX.XX
Format.
Um den Build zu beschleunigen, wird das Caching der Verzeichnisse ./src/vendor
und ./src/node_modules
+ --cache-from
fĂŒr den docker build
und besteht aus den folgenden Schritten:
KĂŒnstlername | Ziel |
---|
prepare | Die Vorbereitungsphase - die Zusammenstellung von Bildern aller Dienste auĂer dem Bild mit der Quelle |
test | Testen der Anwendung (AusfĂŒhren von phpunit , statische Code-Analysatoren) anhand von Bildern, die in der Vorbereitungsphase gesammelt wurden |
build | Installieren aller composer AbhĂ€ngigkeiten ( --no-dev ), Zusammenstellen von assets webpack und webpack Images mit dem Quellcode einschlieĂlich der empfangenen Artefakte ( vendor/* , app.js , app.css ) |

Die Baugruppe auf dem Hauptzweig erzeugt push
mit den latest
und master
Tags
Im Durchschnitt dauern alle Phasen der Montage 4 Minuten , was ein ziemlich gutes Ergebnis ist (die parallele AusfĂŒhrung von Aufgaben ist unser Alles).
Unter diesem Link können Sie sich mit dem Inhalt der Konfiguration ( .gitlab-ci.yml
) des Kollektors vertraut machen.
Anstelle einer Schlussfolgerung
Wie Sie sehen können, ist die Organisation der Arbeit mit einer PHP-Anwendung (am Beispiel von Laravel
) mit Docker nicht so schwierig. Als Test können Sie das Repository tarampampam/laravel-in-docker
und alle Vorkommen von tarampampam/laravel-in-docker
durch Ihr eigenes ersetzen - probieren Sie alles "live" aus.
FĂŒr den lokalen Start - fĂŒhren Sie nur 2 Befehle aus:
$ git clone https://gitlab.com/tarampampam/laravel-in-docker.git ./laravel-in-docker && cd $_ $ make init
Ăffnen Sie dann http://127.0.0.1:9999
in Ihrem bevorzugten Browser.
... die Gelegenheit nutzen
Im Moment arbeite ich am TL-Projekt "Autocode" und wir suchen talentierte PHP-Entwickler und Systemadministratoren (EntwicklungsbĂŒro befindet sich in Jekaterinburg). Wenn Sie sich als Erster oder Zweiter betrachten, schreiben Sie unseren HR-Brief mit dem Text "Ich möchte ein Entwicklungsteam sein, Lebenslauf:% link_on_summary%" an die E-Mail- hr@avtocod.ru
. Wir helfen hr@avtocod.ru
beim Umzug.