mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #1371 from Dokploy/1345-domain-not-working-after-server-restart-or-traefik-reload
refactor(traefik): migrate from Docker Swarm service to standalone co…
This commit is contained in:
@@ -287,13 +287,19 @@ export const getServiceContainersByAppName = async (
|
||||
|
||||
export const getContainersByAppLabel = async (
|
||||
appName: string,
|
||||
type: "standalone" | "swarm",
|
||||
serverId?: string,
|
||||
) => {
|
||||
try {
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
const command = `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
|
||||
const command =
|
||||
type === "swarm"
|
||||
? `docker ps --filter "label=com.docker.swarm.service.name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
: type === "standalone"
|
||||
? `docker ps --filter "name=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`
|
||||
: `docker ps --filter "label=com.docker.compose.project=${appName}" --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'`;
|
||||
if (serverId) {
|
||||
const result = await execAsyncRemote(serverId, command);
|
||||
stdout = result.stdout;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
TRAEFIK_PORT,
|
||||
TRAEFIK_SSL_PORT,
|
||||
TRAEFIK_VERSION,
|
||||
TRAEFIK_HTTP3_PORT,
|
||||
getDefaultMiddlewares,
|
||||
getDefaultServerTraefikConfig,
|
||||
} from "@dokploy/server/setup/traefik-setup";
|
||||
@@ -542,22 +543,28 @@ export const installRClone = () => `
|
||||
export const createTraefikInstance = () => {
|
||||
const command = `
|
||||
# Check if dokpyloy-traefik exists
|
||||
if docker service ls | grep -q 'dokploy-traefik'; then
|
||||
if docker service inspect dokploy-traefik > /dev/null 2>&1; then
|
||||
echo "Migrating Traefik to Standalone..."
|
||||
docker service rm dokploy-traefik
|
||||
sleep 7
|
||||
echo "Traefik migrated to Standalone ✅"
|
||||
fi
|
||||
|
||||
if docker inspect dokploy-traefik > /dev/null 2>&1; then
|
||||
echo "Traefik already exists ✅"
|
||||
else
|
||||
# Create the dokploy-traefik service
|
||||
# Create the dokploy-traefik container
|
||||
TRAEFIK_VERSION=${TRAEFIK_VERSION}
|
||||
docker service create \
|
||||
docker run -d \
|
||||
--name dokploy-traefik \
|
||||
--replicas 1 \
|
||||
--constraint 'node.role==manager' \
|
||||
--network dokploy-network \
|
||||
--mount type=bind,src=/etc/dokploy/traefik/traefik.yml,dst=/etc/traefik/traefik.yml \
|
||||
--mount type=bind,src=/etc/dokploy/traefik/dynamic,dst=/etc/dokploy/traefik/dynamic \
|
||||
--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
|
||||
--label traefik.enable=true \
|
||||
--publish mode=host,target=${TRAEFIK_SSL_PORT},published=${TRAEFIK_SSL_PORT} \
|
||||
--publish mode=host,target=${TRAEFIK_PORT},published=${TRAEFIK_PORT} \
|
||||
--restart unless-stopped \
|
||||
-v /etc/dokploy/traefik/traefik.yml:/etc/traefik/traefik.yml \
|
||||
-v /etc/dokploy/traefik/dynamic:/etc/dokploy/traefik/dynamic \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p ${TRAEFIK_SSL_PORT}:${TRAEFIK_SSL_PORT} \
|
||||
-p ${TRAEFIK_PORT}:${TRAEFIK_PORT} \
|
||||
-p ${TRAEFIK_HTTP3_PORT}:${TRAEFIK_HTTP3_PORT}/udp \
|
||||
traefik:v$TRAEFIK_VERSION
|
||||
echo "Traefik version $TRAEFIK_VERSION installed ✅"
|
||||
fi
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
|
||||
import type { ContainerCreateOptions } from "dockerode";
|
||||
import { dump } from "js-yaml";
|
||||
import { paths } from "../constants";
|
||||
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
|
||||
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
||||
import type { FileConfig } from "../utils/traefik/file-types";
|
||||
import type { MainTraefikConfig } from "../utils/traefik/types";
|
||||
@@ -12,6 +11,8 @@ export const TRAEFIK_SSL_PORT =
|
||||
Number.parseInt(process.env.TRAEFIK_SSL_PORT!, 10) || 443;
|
||||
export const TRAEFIK_PORT =
|
||||
Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80;
|
||||
export const TRAEFIK_HTTP3_PORT =
|
||||
Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443;
|
||||
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2";
|
||||
|
||||
interface TraefikOptions {
|
||||
@@ -23,6 +24,7 @@ interface TraefikOptions {
|
||||
publishedPort: number;
|
||||
publishMode?: "ingress" | "host";
|
||||
}[];
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export const initializeTraefik = async ({
|
||||
@@ -30,113 +32,86 @@ export const initializeTraefik = async ({
|
||||
env,
|
||||
serverId,
|
||||
additionalPorts = [],
|
||||
force = false,
|
||||
}: TraefikOptions = {}) => {
|
||||
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
||||
const imageName = `traefik:v${TRAEFIK_VERSION}`;
|
||||
const containerName = "dokploy-traefik";
|
||||
const settings: CreateServiceOptions = {
|
||||
Name: containerName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
Image: imageName,
|
||||
Env: env,
|
||||
Mounts: [
|
||||
{
|
||||
Type: "bind",
|
||||
Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`,
|
||||
Target: "/etc/traefik/traefik.yml",
|
||||
},
|
||||
{
|
||||
Type: "bind",
|
||||
Source: DYNAMIC_TRAEFIK_PATH,
|
||||
Target: "/etc/dokploy/traefik/dynamic",
|
||||
},
|
||||
{
|
||||
Type: "bind",
|
||||
Source: "/var/run/docker.sock",
|
||||
Target: "/var/run/docker.sock",
|
||||
},
|
||||
],
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
EndpointSpec: {
|
||||
Ports: [
|
||||
{
|
||||
TargetPort: 443,
|
||||
PublishedPort: TRAEFIK_SSL_PORT,
|
||||
PublishMode: "host",
|
||||
},
|
||||
{
|
||||
TargetPort: 80,
|
||||
PublishedPort: TRAEFIK_PORT,
|
||||
PublishMode: "host",
|
||||
},
|
||||
...(enableDashboard
|
||||
? [
|
||||
{
|
||||
TargetPort: 8080,
|
||||
PublishedPort: 8080,
|
||||
PublishMode: "host" as const,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...additionalPorts.map((port) => ({
|
||||
TargetPort: port.targetPort,
|
||||
PublishedPort: port.publishedPort,
|
||||
PublishMode: port.publishMode || ("host" as const),
|
||||
})),
|
||||
],
|
||||
},
|
||||
|
||||
const exposedPorts: Record<string, {}> = {
|
||||
[`${TRAEFIK_PORT}/tcp`]: {},
|
||||
[`${TRAEFIK_SSL_PORT}/tcp`]: {},
|
||||
[`${TRAEFIK_HTTP3_PORT}/udp`]: {},
|
||||
};
|
||||
|
||||
const portBindings: Record<string, Array<{ HostPort: string }>> = {
|
||||
[`${TRAEFIK_PORT}/tcp`]: [{ HostPort: TRAEFIK_PORT.toString() }],
|
||||
[`${TRAEFIK_SSL_PORT}/tcp`]: [{ HostPort: TRAEFIK_SSL_PORT.toString() }],
|
||||
[`${TRAEFIK_HTTP3_PORT}/udp`]: [
|
||||
{ HostPort: TRAEFIK_HTTP3_PORT.toString() },
|
||||
],
|
||||
};
|
||||
|
||||
if (enableDashboard) {
|
||||
exposedPorts["8080/tcp"] = {};
|
||||
portBindings["8080/tcp"] = [{ HostPort: "8080" }];
|
||||
}
|
||||
|
||||
for (const port of additionalPorts) {
|
||||
const portKey = `${port.targetPort}/tcp`;
|
||||
exposedPorts[portKey] = {};
|
||||
portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }];
|
||||
}
|
||||
|
||||
const settings: ContainerCreateOptions = {
|
||||
name: containerName,
|
||||
Image: imageName,
|
||||
NetworkingConfig: {
|
||||
EndpointsConfig: {
|
||||
"dokploy-network": {},
|
||||
},
|
||||
},
|
||||
ExposedPorts: exposedPorts,
|
||||
HostConfig: {
|
||||
RestartPolicy: {
|
||||
Name: "always",
|
||||
},
|
||||
Binds: [
|
||||
`${MAIN_TRAEFIK_PATH}/traefik.yml:/etc/traefik/traefik.yml`,
|
||||
`${DYNAMIC_TRAEFIK_PATH}:/etc/dokploy/traefik/dynamic`,
|
||||
"/var/run/docker.sock:/var/run/docker.sock",
|
||||
],
|
||||
PortBindings: portBindings,
|
||||
},
|
||||
Env: env,
|
||||
};
|
||||
|
||||
const docker = await getRemoteDocker(serverId);
|
||||
try {
|
||||
if (serverId) {
|
||||
await pullRemoteImage(imageName, serverId);
|
||||
} else {
|
||||
await pullImage(imageName);
|
||||
}
|
||||
|
||||
const service = docker.getService(containerName);
|
||||
const inspect = await service.inspect();
|
||||
|
||||
const existingEnv = inspect.Spec.TaskTemplate.ContainerSpec.Env || [];
|
||||
const updatedEnv = !env ? existingEnv : env;
|
||||
|
||||
const updatedSettings = {
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ContainerSpec: {
|
||||
...(settings?.TaskTemplate as ContainerTaskSpec).ContainerSpec,
|
||||
Env: updatedEnv,
|
||||
},
|
||||
},
|
||||
};
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...updatedSettings,
|
||||
});
|
||||
|
||||
console.log("Traefik Started ✅");
|
||||
} catch (_) {
|
||||
try {
|
||||
await docker.createService(settings);
|
||||
} catch (error: any) {
|
||||
if (error?.statusCode !== 409) {
|
||||
throw error;
|
||||
const service = docker.getService("dokploy-traefik");
|
||||
await service?.remove({ force: true });
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
} catch (_) {}
|
||||
|
||||
const container = docker.getContainer(containerName);
|
||||
try {
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === "running" && !force) {
|
||||
console.log("Traefik already running");
|
||||
return;
|
||||
}
|
||||
console.log("Traefik service already exists, continuing...");
|
||||
|
||||
await container.remove({ force: true });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
console.log("Traefik Not Found: Starting ✅");
|
||||
|
||||
await docker.createContainer(settings);
|
||||
const newContainer = docker.getContainer(containerName);
|
||||
await newContainer.start();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -212,6 +187,9 @@ export const getDefaultTraefikConfig = () => {
|
||||
},
|
||||
websecure: {
|
||||
address: `:${TRAEFIK_SSL_PORT}`,
|
||||
http3: {
|
||||
advertisedPort: TRAEFIK_HTTP3_PORT,
|
||||
},
|
||||
...(process.env.NODE_ENV === "production" && {
|
||||
http: {
|
||||
tls: {
|
||||
@@ -267,6 +245,9 @@ export const getDefaultServerTraefikConfig = () => {
|
||||
},
|
||||
websecure: {
|
||||
address: `:${TRAEFIK_SSL_PORT}`,
|
||||
http3: {
|
||||
advertisedPort: TRAEFIK_HTTP3_PORT,
|
||||
},
|
||||
http: {
|
||||
tls: {
|
||||
certResolver: "letsencrypt",
|
||||
|
||||
Reference in New Issue
Block a user