ADD Deployment csomagoló queue worker és scheduler újraindítás a teendők közé
A 2026-08-17-i eset általánosítása: a d2d/e2e Docker Swarmban ugyanazt a kódot több service futtatja közös volume-ról, és a queue:work hosszan futó folyamat a betöltött osztályokat a memóriájában tartja. A fájlmásolás után a worker RÉGI kóddal dolgozta fel a jobot - a felület jónak látszott, a háttérfeladat némán a régi viselkedést hozta. - config: 'runtime' minta (app/, config/, database/, routes/, bootstrap/, composer.lock), mert a restart nem csak a jobok változásakor kell; targetenként a service nevek és a Swarm manager elérése - a felületen figyelmeztetés, a teendőkben a konkrét ssh + docker service update parancs - a restart szándékosan a cache ürítés UTÁN: a frissen induló worker különben a régi, cache-elt konfigot töltené be - a d2d schedulernél jelezzük, hogy 0 replikán fut (a restart üresjárat lehet), a t2t-nél pedig azt, hogy queue service még nincs, de a névkonvenció alapján t2t_queue lesz Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
4bb89ae6a1
commit
2965f231ce
@ -135,6 +135,30 @@ private function warnings(array $copied, array $deleted, int $excludedCount, ?ar
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->matching($copied, 'runtime') !== []) {
|
||||
$jobs = count($this->matching($copied, 'job'));
|
||||
$consoles = count($this->matching($copied, 'console'));
|
||||
|
||||
$detail = [];
|
||||
|
||||
if ($jobs > 0) {
|
||||
$detail[] = $jobs.' job';
|
||||
}
|
||||
|
||||
if ($consoles > 0) {
|
||||
$detail[] = $consoles.' konzol parancs';
|
||||
}
|
||||
|
||||
$warnings[] = [
|
||||
'level' => 'warning',
|
||||
'title' => 'Hosszan futó konténerek érintve'.($detail === [] ? '' : ' ('.implode(', ', $detail).')'),
|
||||
'body' => 'A queue worker és a scheduler külön konténerben fut, és a betöltött osztályokat a memóriájában '
|
||||
.'tartja - a fájlok felmásolása után is a RÉGI kóddal dolgozik, amíg újra nem indul. Ez némán rossz '
|
||||
.'viselkedést okoz (a job "elkészül", de nem azt csinálja, amit az új kód). A konkrét restart parancsok '
|
||||
.'a csomag _TEENDOK.md fájljában lesznek.',
|
||||
];
|
||||
}
|
||||
|
||||
if ($deleted !== []) {
|
||||
$warnings[] = [
|
||||
'level' => 'warning',
|
||||
|
||||
@ -298,6 +298,9 @@ private function writeInstructions(string $path, array $manifest, array $target)
|
||||
.' — mappa-összeolvasztás, NEM a teljes app mappa cseréje. A `_` kezdetű fájlokat ne másold fel.',
|
||||
];
|
||||
|
||||
/** @var array<int, string> $restartSteps */
|
||||
$restartSteps = [];
|
||||
|
||||
if ($manifest['deleted'] !== []) {
|
||||
$steps[] = sprintf(
|
||||
'Töröld a `_TORLENDO.txt`-ben felsorolt %d fájlt a célgépen. Nagyobb mennyiségnél a mellékelt '
|
||||
@ -336,9 +339,31 @@ private function writeInstructions(string $path, array $manifest, array $target)
|
||||
if (str_contains($warning['title'], 'Frontend')) {
|
||||
$steps[] = 'A fordított assetek NINCSENEK a csomagban (`.gitignore`) — `npm run build` után kézzel másold fel a `public/build` tartalmát.';
|
||||
}
|
||||
|
||||
// A restart lépéseket külön gyűjtjük: azoknak a cache ürítés UTÁN kell jönniük,
|
||||
// különben a frissen induló worker még a régi, cache-elt konfigot tölti be.
|
||||
if (str_contains($warning['title'], 'Hosszan futó')) {
|
||||
$commands = $this->targets->restartCommands($target['key']);
|
||||
|
||||
if ($commands !== []) {
|
||||
$restartSteps[] = 'Utolsó lépésként indítsd újra a hosszan futó konténereket. A queue worker és a '
|
||||
.'scheduler **a memóriájában tartja a betöltött kódot**, ezért a fájlmásolás önmagában NEM elég — '
|
||||
."enélkül a háttérfeladatok némán a régi viselkedést hozzák:\n\n"
|
||||
." ```bash\n ".implode("\n ", $commands)."\n ```";
|
||||
}
|
||||
|
||||
if ($target['services_note']) {
|
||||
$restartSteps[] = $target['services_note'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$steps[] = 'Ürítsd a cache-t: `php artisan config:clear && php artisan view:clear && php artisan route:clear`';
|
||||
|
||||
foreach ($restartSteps as $restartStep) {
|
||||
$steps[] = $restartStep;
|
||||
}
|
||||
|
||||
$steps[] = 'Ellenőrizd a kirakott verziót: `curl https://'.$target['domain'].'/deploy-version.json`'
|
||||
.' — a commitnak `'.substr((string) $manifest['to'], 0, 7).'` kell lennie.';
|
||||
|
||||
|
||||
@ -11,18 +11,25 @@
|
||||
class DeploymentTargets
|
||||
{
|
||||
/**
|
||||
* @return array<string, array{key:string, label:string, domain:string, remote_path:string, expected_branch:?string}>
|
||||
* @return array<string, array{key:string, label:string, domain:string, remote_path:string, expected_branch:?string, services:array<string, string>, services_note:?string}>
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
/** @var array<string, array<string, string>> $targets */
|
||||
/** @var array<string, array<string, mixed>> $targets */
|
||||
$targets = (array) config('deployment.targets', []);
|
||||
|
||||
$resolved = [];
|
||||
|
||||
foreach ($targets as $key => $target) {
|
||||
$resolved[$key] = array_merge(
|
||||
['label' => $key, 'domain' => '', 'remote_path' => '', 'expected_branch' => null],
|
||||
[
|
||||
'label' => $key,
|
||||
'domain' => '',
|
||||
'remote_path' => '',
|
||||
'expected_branch' => null,
|
||||
'services' => [],
|
||||
'services_note' => null,
|
||||
],
|
||||
$target,
|
||||
['key' => $key],
|
||||
);
|
||||
@ -32,7 +39,34 @@ public function all(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{key:string, label:string, domain:string, remote_path:string, expected_branch:?string}|null
|
||||
* A célkörnyezet hosszan futó service-einek újraindító parancsai.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function restartCommands(?string $key): array
|
||||
{
|
||||
$target = $this->find($key);
|
||||
|
||||
if (! $target || $target['services'] === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$user = (string) config('deployment.swarm.user', 'root');
|
||||
$host = (string) config('deployment.swarm.host');
|
||||
|
||||
return array_values(array_map(
|
||||
fn (string $service): string => sprintf(
|
||||
'ssh %s@%s "docker service update --force %s"',
|
||||
$user,
|
||||
$host,
|
||||
$service,
|
||||
),
|
||||
$target['services'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{key:string, label:string, domain:string, remote_path:string, expected_branch:?string, services:array<string, string>, services_note:?string}|null
|
||||
*/
|
||||
public function find(?string $key): ?array
|
||||
{
|
||||
|
||||
@ -98,6 +98,41 @@
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
],
|
||||
|
||||
/*
|
||||
| A queue worker és a scheduler hosszan futó PHP folyamat: a már betöltött
|
||||
| osztályokat a memóriájában tartja, ezért a fájlok felmásolása után is a RÉGI
|
||||
| kóddal dolgozik, amíg újra nem indul. Ez némán rossz viselkedést okoz (2026-08-17,
|
||||
| árlista végrehajtás: a job "elkészült" státuszt adott anélkül, hogy bármit írt volna),
|
||||
| ezért minden háttérben is futó kód változása restartot igényel - nem csak a jobok.
|
||||
*/
|
||||
'runtime' => [
|
||||
'app/*',
|
||||
'config/*',
|
||||
'database/*',
|
||||
'routes/*',
|
||||
'bootstrap/*',
|
||||
'composer.lock',
|
||||
],
|
||||
|
||||
/* Csak a figyelmeztetés szövegének pontosításához, a restart ettől függetlenül kell. */
|
||||
'job' => ['app/Jobs/*'],
|
||||
'console' => ['app/Console/*'],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Docker Swarm
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| A restart parancsokat a Swarm leader node-on keresztül kell kiadni
|
||||
| (ld. docs/cicd-context.md).
|
||||
|
|
||||
*/
|
||||
|
||||
'swarm' => [
|
||||
'host' => env('DEPLOYMENT_SWARM_HOST', '46.29.140.234'),
|
||||
'user' => env('DEPLOYMENT_SWARM_USER', 'root'),
|
||||
],
|
||||
|
||||
/*
|
||||
@ -121,12 +156,23 @@
|
||||
'domain' => 'd2d.emegrendeles.hu',
|
||||
'remote_path' => '/delirest/d2d.emegrendeles.hu/app/',
|
||||
'expected_branch' => 'test',
|
||||
// Ugyanabból az imageből, közös volume-ról több service is futtatja a kódot.
|
||||
// A web konténer (d2d-environment_app) kérésenként újratölti, ezért az nem kell ide.
|
||||
'services' => [
|
||||
'queue' => 'd2d-environment_queue',
|
||||
'scheduler' => 'd2d-environment_scheduler',
|
||||
],
|
||||
'services_note' => 'A d2d scheduler jelenleg 0 replikán fut — ha leskálázva marad, a restartja üresjárat.',
|
||||
],
|
||||
'e2e' => [
|
||||
'label' => 'e2e — t2t éles',
|
||||
'domain' => 'e2e.emegrendeles.hu',
|
||||
'remote_path' => '/delirest/test.t2t.emegrendeles.hu/app/',
|
||||
'expected_branch' => 'main',
|
||||
'services' => [
|
||||
'scheduler' => 't2t_scheduler',
|
||||
],
|
||||
'services_note' => 'A t2t-n még nincs queue worker service; ha létrejön, a névkonvenció alapján t2t_queue lesz, és azt is újra kell majd indítani.',
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@ -265,6 +265,40 @@ function deploymentCleanup(string $path): void
|
||||
deploymentCleanup($repository['path']);
|
||||
});
|
||||
|
||||
test('a teendők kérik a queue worker és a scheduler újraindítását, a cache ürítés után', function () {
|
||||
deploymentAllowEnvironment();
|
||||
$repository = deploymentTestRepository();
|
||||
[$first, , $third] = $repository['commits'];
|
||||
|
||||
$package = app(DeploymentPackageBuilder::class)->build('d2d', 'test', $first, $third);
|
||||
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
||||
|
||||
expect($teendok)->toContain('docker service update --force d2d-environment_queue')
|
||||
->and($teendok)->toContain('docker service update --force d2d-environment_scheduler')
|
||||
// A 0 replikás scheduler miatt tudni kell, hogy a restart üresjárat lehet.
|
||||
->and($teendok)->toContain('0 replikán fut');
|
||||
|
||||
// A restartnak a cache ürítés UTÁN kell jönnie: a frissen induló worker különben
|
||||
// a régi, cache-elt konfigot töltené be.
|
||||
expect(strpos($teendok, 'docker service update'))->toBeGreaterThan(strpos($teendok, 'config:clear'));
|
||||
|
||||
deploymentCleanup($repository['path']);
|
||||
});
|
||||
|
||||
test('a queue worker nélküli környezetnél is kiderül, mit kell majd újraindítani', function () {
|
||||
deploymentAllowEnvironment();
|
||||
$repository = deploymentTestRepository();
|
||||
[$first, , $third] = $repository['commits'];
|
||||
|
||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
||||
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
||||
|
||||
expect($teendok)->toContain('docker service update --force t2t_scheduler')
|
||||
->and($teendok)->toContain('t2t_queue');
|
||||
|
||||
deploymentCleanup($repository['path']);
|
||||
});
|
||||
|
||||
test('a teendők között szerepel a seeder futtatása a konkrét osztálynévvel', function () {
|
||||
deploymentAllowEnvironment();
|
||||
$repository = deploymentTestRepository();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user