ich betreibe eine recht kleine Discourse-Instanz (seit Jahren eigentlich, mit praktisch null Problemen): https://discuss.cubeisland.de/.
Ich nutze den standardmäßigen, launcher-basierten Bereitstellungsprozess auf einer dedizierten VM (auf meiner eigenen Hardware in einem Rechenzentrum). Das Einzige, was ich über die Jahre geändert habe, war die Migration auf eine extern gehostete, gemeinsam genutzte PostgreSQL-Datenbank.
Vor kurzem habe ich begonnen, Anwendungen von dedizierten VMs auf einen Docker-Swarm zu migrieren, als Vorbereitungsschritt für eine spätere Migration auf einen Kubernetes-Cluster, hauptsächlich um Ressourcen zu sparen und Teile der Infrastruktur „elastischer
Ich wollte das Gleiche wie du tun – wir betreiben Discourse auf Amazon ECS, daher mussten wir in der Lage sein, nur das Web-Image zu bauen und in eine Registry zu pushen. Ich wollte den Discourse-Build-Prozess nicht hacken, da wir so nah wie möglich an der unterstützten Installation bleiben möchten.
Stattdessen nutzen wir das normale launcher-Skript, um auf einem lokalen Rechner ein Setup mit zwei Containern zu erstellen, ignorieren dabei jedoch den Daten-Container und pushen den Web-Container in die Registry. Zur Laufzeit überschreiben wir die Postgres- und Redis-Verbindungsdetails über Umgebungsvariablen.
Das Bereitstellen des neuen Images ist ein dreistufiger Prozess:
Sichere Pre-Migrationen ausführen. Lass ECS diesen Befehl (mit dem neuen Image) ausführen:
SKIP_POST_DEPLOYMENT_MIGRATIONS=1 rake db:migrate
Das neue Image bereitstellen. Aktualisiere den ECS-Service.
Post-Migrationen ausführen. Lass ECS diesen Befehl ausführen:
SKIP_POST_DEPLOYMENT_MIGRATIONS=0 rake db:migrate
Es ist wahrscheinlich verschwenderisch, während des Image-Builds einen lokalen Daten-Container laufen zu lassen, aber dadurch können wir die standardmäßige web.template.yml verwenden, ohne uns Gedanken darüber machen zu müssen, welche Teile mit der Datenbank oder Redis kommunizieren.
Danke dafür! Ich habe auch überlegt, während des Image-Builds einfach eine PostgreSQL-Instanz hochzufahren und sie nach Abschluss des eigentlichen Builds wieder zu entfernen.
Ich habe mir endlich die Zeit genommen, das umzusetzen!
Ich habe den Image-Build mit einer GitLab-CI-Pipeline implementiert, die während des Builds PostgreSQL und Redis als Dienste startet und sie anschließend verwirft:
Jetzt muss ich nur noch die Bereitstellung mit den DB-Migrationen automatisieren.
Es scheint, dass dies endlich kaputtgegangen ist. Beim Upgrade von 3.0.6 auf 3.1.0 wurden keine DB-Migrationen durchgeführt. Das Ausführen des abschließenden bundle exec rake db:migrate innerhalb des laufenden Containers funktionierte jedoch, allerdings erst nach einem weiteren Neustart des Containers.
Sie müssen erneut migrieren, wenn das neue Image ohne diese Umgebung gestartet wurde. Es gibt eine Rake-Aufgabe, die dies erledigt, aber ich kann sie von meinem Handy aus nicht finden oder mich daran erinnern. Etwas wie ensure_post_migrations.
Soweit ich das beurteilen kann, sind mir keine Probleme aufgefallen. Ich folge hauptsächlich dem Beta-Release-Zweig, und soweit ich das beurteilen kann, sind die Migrationen bei jedem Schritt der 3.1.0.beta…-Serie korrekt verlaufen.
Ok, nachdem ich mir den Code angesehen habe, verstehe ich, was db:ensure_post_migrations tut. Es soll in derselben Rake-Ausführung vor db:migrate verwendet werden, um sicherzustellen, dass SKIP_POST_DEPLOYMENT_MIGRATIONS auf 0 gesetzt ist. Mein Skript stellt dies bereits sicher:
.gitlab-ci.yml:
./migrate.sh pre || echo "Redis läuft nicht während der Vorab-Migrationen, wird übersprungen..."
docker stack deploy --prune --resolve-image always -c "$STACK.yml" "$STACK"
./docker-stack-wait.sh -t 180 "$STACK"
./migrate.sh post
migrate.sh:
#!/usr/bin/env sh
if [ "$(docker ps -q --filter "label=com.docker.stack.namespace=${STACK}" --filter "label=com.docker.swarm.service.name=${STACK}_${DISCOURSE_REDIS_HOST}" | wc -l)" = "0" ]
then
echo "Kein Redis-Container gefunden, Migrationen können nicht ausgeführt werden!"
exit 1
fi
if [ "$1" = "pre" ]
then
skip_post=1
else
skip_post=0
fi
docker run \
--rm \
--name "discourse-migration-${DISCOURSE_DB_HOST}-${DISCOURSE_DB_NAME}" \
--network "${STACK}_discourse" \
--workdir /var/www/discourse \
-u discourse \
-e SKIP_POST_DEPLOYMENT_MIGRATIONS="$skip_post" \
-e LANG="${LANG}" \
-e DISCOURSE_DEFAULT_LOCALE="${DISCOURSE_DEFAULT_LOCALE}" \
-e DISCOURSE_HOSTNAME="${DISCOURSE_HOSTNAME}" \
-e DISCOURSE_DEVELOPER_EMAILS="${DISCOURSE_DEVELOPER_EMAILS}" \
-e DISCOURSE_SMTP_ADDRESS="${DISCOURSE_SMTP_ADDRESS}" \
-e DISCOURSE_SMTP_PORT="${DISCOURSE_SMTP_PORT}" \
-e DISCOURSE_DB_USERNAME="${DISCOURSE_DB_USERNAME}" \
-e DISCOURSE_DB_PASSWORD="${DISCOURSE_DB_PASSWORD}" \
-e DISCOURSE_DB_HOST="${DISCOURSE_DB_HOST}" \
-e DISCOURSE_DB_NAME="${DISCOURSE_DB_NAME}" \
-e DISCOURSE_REDIS_HOST="${DISCOURSE_REDIS_HOST}" \
"$DISCOURSE_IMAGE" \
bundle exec rake db:migrate
Es führt db:migrate mit SKIP_POST_DEPLOYMENT_MIGRATIONS=1 im neuen Image im Docker Swarm aus, während Discourse noch die alte Version ausführt. Dann stellt es das neue Image im Swarm bereit und wartet, bis es konvergiert ist. Am Ende führt es db:migrate erneut aus, aber mit SKIP_POST_DEPLOYMENT_MIGRATIONS=0.
Dies hat bei jeder Version seit über 2 Jahren zuverlässig funktioniert. Da es bei Ihnen @simonk funktioniert hat, haben Sie etwas grundlegend anders gemacht als mein Skript?
Nein, ich folge immer noch demselben Prozess, den ich hier oben beschrieben habe, der soweit ich das beurteilen kann, ungefähr derselbe ist wie deiner. Ich verwende zwar ein einfaches rake db:migrate anstelle von bundle exec rake db:migrate, aber ich kann mir nicht vorstellen, dass das einen großen Unterschied macht.
Ich habe noch nie Docker Stack oder Swarm verwendet. Gibt es eine Möglichkeit, dass irgendwo in deinen Skripten ein Fehler vorliegt, der dazu führen könnte, dass das migrate.sh-Skript das alte Image anstelle des neuen verwendet?
Das habe ich nicht explizit geprüft, ich werde es mir ansehen. Der Schwarm wird definitiv das neueste Image verwenden, aber vielleicht hat das CI-Skript aus irgendeinem Grund nicht das neueste verwendet.