wip: swarm

This commit is contained in:
Andras Bacsai 2023-11-28 18:31:04 +01:00
parent c505a6ce9c
commit b4874c7df3
8 changed files with 172 additions and 130 deletions

View File

@ -11,6 +11,9 @@ class StopApplication
public function handle(Application $application)
{
$server = $application->destination->server;
if ($server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}" ], $server);
} else {
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
if ($containers->count() > 0) {
foreach ($containers as $container) {
@ -34,6 +37,8 @@ class StopApplication
instant_remote_process(["docker rm -f $name"], $application->destination->server, throwError: false);
}
}
}
}
}

View File

@ -14,8 +14,6 @@ class Dashboard extends Component
public function mount()
{
$this->servers = Server::ownedByCurrentTeam()->get();
ray($this->servers[1]);
ray($this->servers[1]->standaloneDockers);
$this->projects = Project::ownedByCurrentTeam()->get();
}
// public function getIptables()

View File

@ -126,7 +126,6 @@ class Select extends Component
{
$this->server_id = $server->id;
$this->standaloneDockers = $server->standaloneDockers;
ray($server);
$this->swarmDockers = $server->swarmDockers;
$this->current_step = 'destinations';
}

View File

@ -156,6 +156,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// Generate custom host<->ip mapping
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
if (!is_null($allContainers)) {
$allContainers = format_docker_command_output_to_json($allContainers);
$ips = collect([]);
if (count($allContainers) > 0) {
@ -175,6 +176,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->addHosts = $ips->map(function ($ip, $name) {
return "--add-host $name:$ip";
})->implode(' ');
}
if ($this->application->dockerfile_target_build) {
$this->buildTarget = " --target {$this->application->dockerfile_target_build} ";
@ -214,6 +216,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
if ($this->application->docker_registry_image_name && $this->application->build_pack !== 'dockerimage') {
$this->push_to_docker_registry();
if ($this->server->isSwarm()) {
$this->application_deployment_queue->addLogEntry("Creating / updating stack.");
$this->execute_remote_command(
[
"docker stack deploy --with-registry-auth --prune --compose-file {$this->configuration_dir}/docker-compose.yml {$this->application->uuid}"
],
);
}
}
$this->next(ApplicationDeploymentStatus::FINISHED->value);
$this->application->isConfigurationChanged(true);
@ -534,6 +544,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
private function rolling_update()
{
if ($this->server->isSwarm()) {
// Skip this.
} else {
if (count($this->application->ports_mappings_array) > 0) {
$this->execute_remote_command(
[
@ -556,8 +569,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->application_deployment_queue->addLogEntry("Rolling update completed.");
}
}
}
private function health_check()
{
if ($this->server->isSwarm()) {
// Implement healthcheck for swarm
} else {
if ($this->application->isHealthcheckDisabled()) {
$this->newVersionIsHealthy = true;
return;
@ -606,6 +623,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
}
}
}
private function deploy_pull_request()
{
$this->newVersionIsHealthy = true;
@ -849,21 +867,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
if ($this->pull_request_id !== 0) {
$labels = collect(generateLabelsApplication($this->application, $this->preview));
// $newHostLabel = $newLabels->filter(function ($label) {
// return str($label)->contains('Host');
// });
// $labels = $labels->reject(function ($label) {
// return str($label)->contains('Host');
// });
// ray($labels,$newLabels);
// $labels = $labels->map(function ($label) {
// $pattern = '/([a-zA-Z0-9]+)-(\d+)-(http|https)/';
// $replacement = "$1-pr-{$this->pull_request_id}-$2-$3";
// $newLabel = preg_replace($pattern, $replacement, $label);
// return $newLabel;
// });
// $labels = $labels->merge($newHostLabel);
}
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
$docker_compose = [
@ -906,6 +909,20 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
]
]
];
if ($this->server->isSwarm()) {
data_forget($docker_compose, 'services.' . $this->container_name . '.container_name');
data_forget($docker_compose, 'services.' . $this->container_name . '.expose');
data_forget($docker_compose, 'services.' . $this->container_name . '.restart');
data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit');
data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit');
data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness');
data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation');
data_forget($docker_compose, 'services.' . $this->container_name . '.cpus');
data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset');
data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares');
} else {
}
if ($this->server->isLogDrainEnabled() && $this->application->isLogDrainEnabled()) {
$docker_compose['services'][$this->container_name]['logging'] = [
'driver' => 'fluentd',

View File

@ -67,7 +67,7 @@ class Server extends BaseModel
{
$teamId = currentTeam()->id;
$selectArray = collect($select)->concat(['id']);
return Server::whereTeamId($teamId)->with('settings')->select($selectArray->all())->orderBy('name');
return Server::whereTeamId($teamId)->with('settings','swarmDockers','standaloneDockers')->select($selectArray->all())->orderBy('name');
}
static public function isUsable()
@ -87,6 +87,8 @@ class Server extends BaseModel
return $this->hasOne(ServerSetting::class);
}
public function addInitialNetwork() {
ray($this->id);
if ($this->id === 0) {
if ($this->isSwarm()) {
SwarmDocker::create([
@ -106,13 +108,13 @@ class Server extends BaseModel
} else {
if ($this->isSwarm()) {
SwarmDocker::create([
'name' => 'coolify',
'name' => 'coolify-overlay',
'network' => 'coolify-overlay',
'server_id' => $this->id,
]);
} else {
StandaloneDocker::create([
'name' => 'coolify',
'name' => 'coolify-overlay',
'network' => 'coolify',
'server_id' => $this->id,
]);
@ -452,7 +454,7 @@ class Server extends BaseModel
public function validateCoolifyNetwork($isSwarm = false)
{
if ($isSwarm) {
return instant_remote_process(["docker network create --driver overlay coolify-overlay >/dev/null 2>&1 || true"], $this, false);
return instant_remote_process(["docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true"], $this, false);
} else {
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
}

View File

@ -123,6 +123,9 @@ function instant_remote_process(Collection|array $command, Server $server, $thro
}
return excludeCertainErrors($process->errorOutput(), $exitCode);
}
if ($output === 'null') {
$output = null;
}
return $output;
}
function excludeCertainErrors(string $errorOutput, ?int $exitCode = null)

View File

@ -13,9 +13,9 @@ class SwarmDockerSeeder extends Seeder
*/
public function run(): void
{
SwarmDocker::create([
'name' => 'Swarm Docker 1',
'server_id' => 1,
]);
// SwarmDocker::create([
// 'name' => 'Swarm Docker 1',
// 'server_id' => 1,
// ]);
}
}

View File

@ -69,12 +69,28 @@
@endif
@if ($application->build_pack !== 'dockerimage' && $application->build_pack !== 'dockercompose')
<h3>Docker Registry</h3>
@if ($application->destination->server->isSwarm())
<div>Docker Swarm requires the image to be available in a registry. More info <a class="underline"
href="https://coolify.io/docs/docker-registries" target="_blank">here</a>.</div>
@else
<div>Push the built image to a docker registry. More info <a class="underline"
href="https://coolify.io/docs/docker-registries" target="_blank">here</a>.</div>
@endif
<div class="flex flex-col gap-2 xl:flex-row">
@if ($application->build_pack === 'dockerimage')
@if ($application->destination->server->isSwarm())
<x-forms.input required id="application.docker_registry_image_name" label="Docker Image" />
<x-forms.input id="application.docker_registry_image_tag" label="Docker Image Tag" />
@else
<x-forms.input id="application.docker_registry_image_name" label="Docker Image" />
<x-forms.input id="application.docker_registry_image_tag" label="Docker Image Tag" />
@endif
@else
@if ($application->destination->server->isSwarm())
<x-forms.input id="application.docker_registry_image_name" required label="Docker Image" />
<x-forms.input id="application.docker_registry_image_tag"
helper="If set, it will tag the built image with this tag too. <br><br>Example: If you set it to 'latest', it will push the image with the commit sha tag + with the latest tag."
label="Docker Image Tag" />
@else
<x-forms.input id="application.docker_registry_image_name"
helper="Empty means it won't push the image to a docker registry."
@ -86,6 +102,8 @@
label="Docker Image Tag" />
@endif
@endif
</div>
@endif
@ -140,8 +158,8 @@
@endif
@if ($application->build_pack === 'dockercompose')
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
<x-forms.textarea rows="10" readonly id="application.docker_compose" label="Docker Compose Content"
helper="You need to modify the docker compose file." />
<x-forms.textarea rows="10" readonly id="application.docker_compose"
label="Docker Compose Content" helper="You need to modify the docker compose file." />
{{-- <x-forms.textarea rows="10" readonly id="application.docker_compose_pr"
label="Docker PR Compose Content" helper="You need to modify the docker compose file." /> --}}
@endif