Compare commits
4 Commits
4abf8bc6a9
...
f18e1c55e3
| Author | SHA1 | Date |
|---|---|---|
|
|
f18e1c55e3 | |
|
|
ed1eef9db0 | |
|
|
f17e210f3e | |
|
|
4c4e74ff8d |
|
|
@ -1,70 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AutoImportSettings">
|
|
||||||
<option name="autoReloadType" value="SELECTIVE" />
|
|
||||||
</component>
|
|
||||||
<component name="ChangeListManager">
|
|
||||||
<list default="true" id="009bc178-658e-4c81-9bb8-8d7bf6b8cbc6" name="Changes" comment="">
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/clusters/app-365zon/.terraform/terraform.tfstate" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/clusters/app-365zon/.terraform/terraform.tfstate" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/clusters/app-365zon/main.tf" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/clusters/app-365zon/main.tf" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/modules/mijn-365zon-nl/main.tf" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/modules/mijn-365zon-nl/main.tf" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/modules/minio/main.tf" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/modules/minio/main.tf" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/modules/minio/values.yaml.tftpl" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/modules/minio/values.yaml.tftpl" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/modules/minio/variables.tf" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/modules/minio/variables.tf" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/../infra/modules/mongodb/values.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/../infra/modules/mongodb/values.yaml" afterDir="false" />
|
|
||||||
</list>
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
||||||
</component>
|
|
||||||
<component name="Git.Settings">
|
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectColorInfo">{
|
|
||||||
"associatedIndex": 1
|
|
||||||
}</component>
|
|
||||||
<component name="ProjectId" id="2oqTXEtODybqnAKfjaqPi9uslRP" />
|
|
||||||
<component name="ProjectViewState">
|
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
|
||||||
<option name="showLibraryContents" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="PropertiesComponent"><![CDATA[{
|
|
||||||
"keyToString": {
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
|
||||||
"git-widget-placeholder": "main",
|
|
||||||
"last_opened_file_path": "/home/lamelos/Projects/fourlights/devops",
|
|
||||||
"node.js.detected.package.eslint": "true",
|
|
||||||
"node.js.detected.package.tslint": "true",
|
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
|
||||||
"node.js.selected.package.tslint": "(autodetect)",
|
|
||||||
"nodejs_package_manager_path": "npm",
|
|
||||||
"vue.rearranger.settings.migration": "true"
|
|
||||||
}
|
|
||||||
}]]></component>
|
|
||||||
<component name="SharedIndexes">
|
|
||||||
<attachedChunks>
|
|
||||||
<set>
|
|
||||||
<option value="bundled-js-predefined-d6986cc7102b-deb605915726-JavaScript-WS-243.22562.222" />
|
|
||||||
</set>
|
|
||||||
</attachedChunks>
|
|
||||||
</component>
|
|
||||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
||||||
<component name="TaskManager">
|
|
||||||
<task active="true" id="Default" summary="Default task">
|
|
||||||
<changelist id="009bc178-658e-4c81-9bb8-8d7bf6b8cbc6" name="Changes" comment="" />
|
|
||||||
<created>1731596143702</created>
|
|
||||||
<option name="number" value="Default" />
|
|
||||||
<option name="presentableId" value="Default" />
|
|
||||||
<updated>1731596143702</updated>
|
|
||||||
<workItem from="1731596144788" duration="1417000" />
|
|
||||||
<workItem from="1736261138378" duration="1228000" />
|
|
||||||
<workItem from="1736775177111" duration="7000" />
|
|
||||||
</task>
|
|
||||||
<servers />
|
|
||||||
</component>
|
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
|
||||||
<option name="version" value="3" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
#!/usr/bin/env -S deno run --allow-run --allow-read --allow-write
|
||||||
|
|
||||||
|
import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts";
|
||||||
|
|
||||||
|
const setupCluster = async (numMasters: number) => {
|
||||||
|
// Step 1: Create Low-Resource Profile (if not exists)
|
||||||
|
const profileExists = await Deno.run({
|
||||||
|
cmd: ["incus", "profile", "show", "low-resource"],
|
||||||
|
stdout: "null",
|
||||||
|
stderr: "null",
|
||||||
|
}).status().then((status) => status.success);
|
||||||
|
|
||||||
|
if (!profileExists) {
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["incus", "profile", "create", "low-resource"],
|
||||||
|
}).status();
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["incus", "profile", "set", "low-resource", "limits.cpu=1", "limits.memory=512MB"],
|
||||||
|
}).status();
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["incus", "profile", "device", "add", "low-resource", "root", "disk", "pool=default", "path=/"],
|
||||||
|
}).status();
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["incus", "profile", "device", "add", "low-resource", "eth-0", "nic", "network=incusbr0"],
|
||||||
|
}).status();
|
||||||
|
console.log("✅ Low-resource profile created.");
|
||||||
|
} else {
|
||||||
|
console.log("⏩ Low-resource profile already exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Launch VMs (if not already running)
|
||||||
|
for (let i = 1; i <= numMasters; i++) {
|
||||||
|
const vmName = `k3s-master${i}`;
|
||||||
|
const vmExists = await Deno.run({
|
||||||
|
cmd: ["incus", "list", vmName, "--format", "csv"],
|
||||||
|
stdout: "piped",
|
||||||
|
}).output().then((output) => new TextDecoder().decode(output).trim() !== "");
|
||||||
|
|
||||||
|
if (!vmExists) {
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["incus", "launch", "images:alpine/edge/cloud", vmName, "--profile", "low-resource"],
|
||||||
|
}).status();
|
||||||
|
console.log(`✅ VM ${vmName} launched.`);
|
||||||
|
} else {
|
||||||
|
console.log(`⏩ VM ${vmName} already exists.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Install k3sup (if not installed)
|
||||||
|
const k3supInstalled = await Deno.run({
|
||||||
|
cmd: ["which", "k3sup"],
|
||||||
|
stdout: "null",
|
||||||
|
stderr: "null",
|
||||||
|
}).status().then((status) => status.success);
|
||||||
|
|
||||||
|
if (!k3supInstalled) {
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["sh", "-c", "curl -sLS https://get.k3sup.dev | sh"],
|
||||||
|
}).status();
|
||||||
|
console.log("✅ k3sup installed.");
|
||||||
|
} else {
|
||||||
|
console.log("⏩ k3sup already installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Bootstrap First Master Node (if not already bootstrapped)
|
||||||
|
const firstMasterIP = await Deno.run({
|
||||||
|
cmd: ["incus", "list", "k3s-master1", "--format", "csv", "--columns", "n4"],
|
||||||
|
stdout: "piped",
|
||||||
|
}).output().then((output) => new TextDecoder().decode(output).trim().split(",")[1].split(" ")[0])
|
||||||
|
|
||||||
|
const kubeconfigExists = await Deno.stat("./kubeconfig").then(() => true).catch(() => false);
|
||||||
|
|
||||||
|
if (!kubeconfigExists) {
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["k3sup", "install", "--ip", firstMasterIP, "--user", "root", "--cluster"],
|
||||||
|
}).status();
|
||||||
|
console.log("✅ First master node bootstrapped.");
|
||||||
|
} else {
|
||||||
|
console.log("⏩ First master node already bootstrapped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: Join Additional Master Nodes (if not already joined)
|
||||||
|
for (let i = 2; i <= numMasters; i++) {
|
||||||
|
const vmName = `k3s-master${i}`;
|
||||||
|
const vmIP = await Deno.run({
|
||||||
|
cmd: ["incus", "list", vmName, "--format", "csv", "--columns", "n4"],
|
||||||
|
stdout: "piped",
|
||||||
|
}).output().then((output) => new TextDecoder().decode(output).trim().split(",")[1].split(" ")[0])
|
||||||
|
|
||||||
|
const joined = await Deno.run({
|
||||||
|
cmd: ["kubectl", "get", "nodes", vmName],
|
||||||
|
stdout: "null",
|
||||||
|
stderr: "null",
|
||||||
|
}).status().then((status) => status.success);
|
||||||
|
|
||||||
|
if (!joined) {
|
||||||
|
await Deno.run({
|
||||||
|
cmd: ["k3sup", "join", "--ip", vmIP, "--server-ip", firstMasterIP, "--user", "root"],
|
||||||
|
}).status();
|
||||||
|
console.log(`✅ VM ${vmName} joined the cluster.`);
|
||||||
|
} else {
|
||||||
|
console.log(`⏩ VM ${vmName} already joined the cluster.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("🚀 HA k3s cluster setup complete!");
|
||||||
|
};
|
||||||
|
|
||||||
|
await new Command()
|
||||||
|
.name("setup-k3s-cluster")
|
||||||
|
.version("0.1.0")
|
||||||
|
.description("Automate the setup of an HA k3s cluster using incus and k3sup")
|
||||||
|
.option("-m, --masters <numMasters:number>", "Number of master nodes", { default: 3 })
|
||||||
|
.action(({ masters }) => setupCluster(masters))
|
||||||
|
.parse(Deno.args);
|
||||||
Loading…
Reference in New Issue