Compare commits
No commits in common. "d05519992def8cd54020bd8a72d603fac9c6ac3c" and "a90539fcd79f4d66c6ad3087cb50f18a630ccf80" have entirely different histories.
d05519992d
...
a90539fcd7
11 changed files with 81 additions and 342 deletions
21
alpine.ts
21
alpine.ts
|
@ -1,4 +1,11 @@
|
||||||
import { mkdir, opendir } from "node:fs/promises";
|
import {
|
||||||
|
chmod,
|
||||||
|
copyFile,
|
||||||
|
mkdir,
|
||||||
|
opendir,
|
||||||
|
symlink,
|
||||||
|
writeFile,
|
||||||
|
} from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { cwd } from "node:process";
|
import { cwd } from "node:process";
|
||||||
import { Fstab } from "./fstab.js";
|
import { Fstab } from "./fstab.js";
|
||||||
|
@ -30,13 +37,13 @@ export class Alpine {
|
||||||
content: string,
|
content: string,
|
||||||
permissions?: { uid: number; gid: number }
|
permissions?: { uid: number; gid: number }
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const p = path.join(this.dir, filePath);
|
|
||||||
await this.mkdirP(path.dirname(filePath));
|
await this.mkdirP(path.dirname(filePath));
|
||||||
await sudoWriteFile(p, content);
|
await sudoWriteFile(path.join(this.dir, filePath), content);
|
||||||
if (permissions) {
|
if (permissions)
|
||||||
await sudoChown(p, `${permissions.uid}:${permissions.gid}`);
|
await sudoChown(
|
||||||
await sudoChmod(p, "600");
|
path.join(this.dir, filePath),
|
||||||
}
|
`${permissions.uid}:${permissions.gid}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
async writeExecutable(filePath: string, content: string): Promise<void> {
|
async writeExecutable(filePath: string, content: string): Promise<void> {
|
||||||
await this.writeFile(filePath, content);
|
await this.writeFile(filePath, content);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
import { Writable } from "node:stream";
|
||||||
import { join } from "node:path";
|
|
||||||
import { execFile } from "./better-api.js";
|
import { execFile } from "./better-api.js";
|
||||||
|
|
||||||
export async function reproRun(opts: {
|
export async function reproRun(opts: {
|
||||||
|
@ -7,7 +6,7 @@ export async function reproRun(opts: {
|
||||||
// cache/ and rootfs/
|
// cache/ and rootfs/
|
||||||
cwd: string;
|
cwd: string;
|
||||||
command: string;
|
command: string;
|
||||||
cache?: string[];
|
cache: string[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const run = execFile("repro-run", { cwd: opts.cwd });
|
const run = execFile("repro-run", { cwd: opts.cwd });
|
||||||
if (!run.child.stdin) throw false;
|
if (!run.child.stdin) throw false;
|
||||||
|
@ -22,29 +21,3 @@ export async function reproRun(opts: {
|
||||||
run.child.stderr?.pipe(process.stderr);
|
run.child.stderr?.pipe(process.stderr);
|
||||||
await run;
|
await run;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buildRepro<T>(
|
|
||||||
cacheName: string,
|
|
||||||
cacheKey: string,
|
|
||||||
buildScript: string,
|
|
||||||
run: (cwd: string) => Promise<void>,
|
|
||||||
getArtifacts: (cwd: string) => T
|
|
||||||
): Promise<T> {
|
|
||||||
const dir = join("cache", cacheName);
|
|
||||||
await mkdir(dir, { recursive: true });
|
|
||||||
const cacheKeyFile = join(dir, "version");
|
|
||||||
const output = getArtifacts(dir);
|
|
||||||
try {
|
|
||||||
if ((await readFile(cacheKeyFile, "utf-8")) === cacheKey) return output;
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
{
|
|
||||||
const buildScriptPath = join(dir, "build");
|
|
||||||
await writeFile(buildScriptPath, buildScript);
|
|
||||||
await chmod(buildScriptPath, 0o700);
|
|
||||||
}
|
|
||||||
await run(dir);
|
|
||||||
await writeFile(cacheKeyFile, cacheKey);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
11
index.ts
11
index.ts
|
@ -10,12 +10,10 @@ import { sudoChown, sudoChownToRunningUser } from "./helpers/sudo.js";
|
||||||
import { setupKernel } from "./kernel.js";
|
import { setupKernel } from "./kernel.js";
|
||||||
import { runQemu } from "./qemu.js";
|
import { runQemu } from "./qemu.js";
|
||||||
import { Runit } from "./runit/index.js";
|
import { Runit } from "./runit/index.js";
|
||||||
import { installFluentBit } from "./software/fluentbit";
|
|
||||||
import { setupForgejo } from "./services/forgejo/index.js";
|
import { setupForgejo } from "./services/forgejo/index.js";
|
||||||
import { setupDhcpcd } from "./services/dhcpcd.js";
|
import { setupDhcpcd } from "./services/dhcpcd.js";
|
||||||
import { setupNtpsec } from "./services/ntpsec.js";
|
import { setupNtpsec } from "./services/ntpsec.js";
|
||||||
import { setupGrafana } from "./services/grafana/index.js";
|
import { setupGrafana } from "./services/grafana/index.js";
|
||||||
import { setupLoki } from "./services/loki/index.js";
|
|
||||||
|
|
||||||
if (process.argv[2] === "generate-secrets") {
|
if (process.argv[2] === "generate-secrets") {
|
||||||
await generateForgejoSecretsFile();
|
await generateForgejoSecretsFile();
|
||||||
|
@ -37,19 +35,10 @@ if (process.argv[2] === "generate-secrets") {
|
||||||
const alpine = await Alpine.makeWorld({ dir: rootfsDir });
|
const alpine = await Alpine.makeWorld({ dir: rootfsDir });
|
||||||
|
|
||||||
await alpine.addPackages(["helix", "htop", "iproute2-ss", "socat"]);
|
await alpine.addPackages(["helix", "htop", "iproute2-ss", "socat"]);
|
||||||
await alpine.writeFile(
|
|
||||||
"/root/.ash_history",
|
|
||||||
`
|
|
||||||
socat tcp-listen:80,reuseaddr,fork tcp:localhost:3050 &
|
|
||||||
`,
|
|
||||||
{ uid: 0, gid: 0 }
|
|
||||||
);
|
|
||||||
await installFluentBit(alpine);
|
|
||||||
const runit = await Runit.setup(alpine);
|
const runit = await Runit.setup(alpine);
|
||||||
await setupDhcpcd(alpine, runit);
|
await setupDhcpcd(alpine, runit);
|
||||||
await setupNtpsec(alpine, runit);
|
await setupNtpsec(alpine, runit);
|
||||||
await setupForgejo(alpine, runit);
|
await setupForgejo(alpine, runit);
|
||||||
await setupLoki(alpine, runit);
|
|
||||||
await setupGrafana(alpine, runit);
|
await setupGrafana(alpine, runit);
|
||||||
const kernel = await setupKernel(alpine, kernelDir);
|
const kernel = await setupKernel(alpine, kernelDir);
|
||||||
|
|
||||||
|
|
|
@ -78,17 +78,20 @@ runit-init 6`
|
||||||
await runit.addService(
|
await runit.addService(
|
||||||
"getty-tty1",
|
"getty-tty1",
|
||||||
`#!/bin/sh
|
`#!/bin/sh
|
||||||
exec chpst -P getty 38400 tty1 linux`
|
exec chpst -P getty 38400 tty1 linux`,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
await runit.addService(
|
await runit.addService(
|
||||||
"getty-tty2",
|
"getty-tty2",
|
||||||
`#!/bin/sh
|
`#!/bin/sh
|
||||||
exec chpst -P getty 38400 tty2 linux`
|
exec chpst -P getty 38400 tty2 linux`,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
await runit.addService(
|
await runit.addService(
|
||||||
"getty-ttyS0",
|
"getty-ttyS0",
|
||||||
`#!/bin/sh
|
`#!/bin/sh
|
||||||
exec chpst -P getty 38400 ttyS0 linux`
|
exec chpst -P getty 38400 ttyS0 linux`,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
return runit;
|
return runit;
|
||||||
|
@ -97,13 +100,17 @@ exec chpst -P getty 38400 ttyS0 linux`
|
||||||
async addService(
|
async addService(
|
||||||
name: string,
|
name: string,
|
||||||
script: string,
|
script: string,
|
||||||
logScript?: string
|
log: boolean = true
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const runScriptPath = path.join("/etc/sv/", name, "/run");
|
const runScriptPath = path.join("/etc/sv/", name, "/run");
|
||||||
await this.alpine.sudoWriteExecutable(runScriptPath, script);
|
await this.alpine.sudoWriteExecutable(runScriptPath, script);
|
||||||
if (logScript) {
|
if (log) {
|
||||||
const logScriptPath = path.join("/etc/sv/", name, "/log/run");
|
const logScriptPath = path.join("/etc/sv/", name, "/log/run");
|
||||||
await this.alpine.sudoWriteExecutable(logScriptPath, logScript);
|
await this.alpine.sudoWriteExecutable(
|
||||||
|
logScriptPath,
|
||||||
|
`#!/bin/sh
|
||||||
|
exec logger -p daemon.info -t '${name}'`
|
||||||
|
);
|
||||||
await this.alpine.symlink(
|
await this.alpine.symlink(
|
||||||
`/run/runit/supervise.${name}.log`,
|
`/run/runit/supervise.${name}.log`,
|
||||||
path.join("/etc/sv/", name, "/log/supervise")
|
path.join("/etc/sv/", name, "/log/supervise")
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { buildRepro, reproRun } from "../../helpers/repro-run.js";
|
import { reproRun } from "../../helpers/repro-run.js";
|
||||||
|
|
||||||
const FORGEJO_VERSION = "v1.18.3-0";
|
const FORGEJO_VERSION = "v1.18.3-0";
|
||||||
|
|
||||||
// returns path to statically compiled binary
|
// returns path to statically compiled binary
|
||||||
export async function buildForgejo(): Promise<string> {
|
export async function buildForgejo(): Promise<string> {
|
||||||
return await buildRepro(
|
const dir = "cache/forgejo";
|
||||||
"forgejo",
|
await mkdir(dir, { recursive: true });
|
||||||
FORGEJO_VERSION,
|
const versionFile = join(dir, "version");
|
||||||
|
const output = join(dir, "rootfs/forgejo");
|
||||||
|
try {
|
||||||
|
if ((await readFile(versionFile, "utf-8")) === FORGEJO_VERSION)
|
||||||
|
return output;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
{
|
||||||
|
const buildScript = join(dir, "build");
|
||||||
|
await writeFile(
|
||||||
|
buildScript,
|
||||||
`#!/bin/sh -e
|
`#!/bin/sh -e
|
||||||
runprint() {
|
runprint() {
|
||||||
echo "==> $@"
|
echo "==> $@"
|
||||||
|
@ -23,9 +33,11 @@ cd forgejo
|
||||||
|
|
||||||
runprint env GOOS=linux GOARCH=amd64 LDFLAGS="-linkmode external -extldflags '-static' $LDFLAGS" TAGS="bindata sqlite sqlite_unlock_notify" make build
|
runprint env GOOS=linux GOARCH=amd64 LDFLAGS="-linkmode external -extldflags '-static' $LDFLAGS" TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||||
mv gitea /forgejo
|
mv gitea /forgejo
|
||||||
`,
|
`
|
||||||
(dir) =>
|
);
|
||||||
reproRun({
|
await chmod(buildScript, 0o700);
|
||||||
|
}
|
||||||
|
await reproRun({
|
||||||
cwd: dir,
|
cwd: dir,
|
||||||
command: "/src/build",
|
command: "/src/build",
|
||||||
cache: [
|
cache: [
|
||||||
|
@ -33,7 +45,8 @@ mv gitea /forgejo
|
||||||
"/home/repro/go",
|
"/home/repro/go",
|
||||||
"/home/repro/.npm",
|
"/home/repro/.npm",
|
||||||
],
|
],
|
||||||
}),
|
});
|
||||||
(dir) => join(dir, "rootfs/forgejo")
|
await writeFile(versionFile, FORGEJO_VERSION);
|
||||||
);
|
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { buildForgejo } from "./build.js";
|
import { buildForgejo } from "./build.js";
|
||||||
import { Alpine } from "../../alpine.js";
|
import { Alpine } from "../../alpine.js";
|
||||||
import { Runit } from "../../runit/index.js";
|
import { Runit } from "../../runit/index.js";
|
||||||
|
import { writeFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { loadForgejoSecretsFile } from "./secrets.js";
|
import { loadForgejoSecretsFile } from "./secrets.js";
|
||||||
import { sudoCopy } from "../../helpers/sudo.js";
|
import { sudoChown, sudoCopy, sudoWriteFile } from "../../helpers/sudo.js";
|
||||||
import { FluentBitParser, runitLokiLogger } from "../../software/fluentbit.js";
|
|
||||||
|
|
||||||
export async function setupForgejo(alpine: Alpine, runit: Runit) {
|
export async function setupForgejo(alpine: Alpine, runit: Runit) {
|
||||||
const bin = await buildForgejo();
|
const bin = await buildForgejo();
|
||||||
|
@ -20,8 +20,9 @@ export async function setupForgejo(alpine: Alpine, runit: Runit) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const secrets = await loadForgejoSecretsFile();
|
const secrets = await loadForgejoSecretsFile();
|
||||||
await alpine.writeFile(
|
const configPath = join(alpine.dir, "/etc/forgejo.conf");
|
||||||
"/etc/forgejo.conf",
|
await sudoWriteFile(
|
||||||
|
configPath,
|
||||||
`
|
`
|
||||||
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
|
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
|
||||||
|
|
||||||
|
@ -144,8 +145,9 @@ ALLOWED_HOST_LIST=external,loopback
|
||||||
REPO_INDEXER_ENABLED=true
|
REPO_INDEXER_ENABLED=true
|
||||||
REPO_INDEXER_EXCLUDE=**.mp4,**.jpg
|
REPO_INDEXER_EXCLUDE=**.mp4,**.jpg
|
||||||
`,
|
`,
|
||||||
entry
|
{ mode: 0o600 }
|
||||||
);
|
);
|
||||||
|
await sudoChown(configPath, `${entry.uid}:${entry.gid}`);
|
||||||
await runit.addService(
|
await runit.addService(
|
||||||
"forgejo",
|
"forgejo",
|
||||||
`#!/bin/sh
|
`#!/bin/sh
|
||||||
|
@ -164,7 +166,6 @@ export FORGEJO_WORK_DIR="$HOME"
|
||||||
cd "$HOME"
|
cd "$HOME"
|
||||||
|
|
||||||
exec chpst -u $USER:$USER /usr/local/bin/forgejo web --config /etc/forgejo.conf 2>&1
|
exec chpst -u $USER:$USER /usr/local/bin/forgejo web --config /etc/forgejo.conf 2>&1
|
||||||
`,
|
`
|
||||||
runitLokiLogger(FluentBitParser.Forgejo, "forgejo")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import assert from "node:assert";
|
import assert from "node:assert";
|
||||||
import { join } from "node:path";
|
|
||||||
import { Alpine } from "../../alpine.js";
|
import { Alpine } from "../../alpine.js";
|
||||||
import { Runit } from "../../runit/index.js";
|
import { Runit } from "../../runit/index.js";
|
||||||
import { FluentBitParser, runitLokiLogger } from "../../software/fluentbit.js";
|
|
||||||
import { loadGrafanaSecretsFile } from "./secrets.js";
|
import { loadGrafanaSecretsFile } from "./secrets.js";
|
||||||
|
|
||||||
const provisioningDir = "/etc/grafana/provisioning/";
|
|
||||||
|
|
||||||
// TODO: grafana-image-renderer?
|
// TODO: grafana-image-renderer?
|
||||||
// /etc/conf.d/grafana
|
// /etc/conf.d/grafana
|
||||||
// # To enable image rendering run
|
// # To enable image rendering run
|
||||||
|
@ -35,20 +31,6 @@ export async function setupGrafana(
|
||||||
});
|
});
|
||||||
await alpine.writeFile("/etc/grafana.ini", await genConfig(), user);
|
await alpine.writeFile("/etc/grafana.ini", await genConfig(), user);
|
||||||
|
|
||||||
await alpine.writeFile(
|
|
||||||
join(provisioningDir, "datasources/loki.yaml"),
|
|
||||||
`
|
|
||||||
apiVersion: 1
|
|
||||||
|
|
||||||
datasources:
|
|
||||||
- name: Loki
|
|
||||||
type: loki
|
|
||||||
access: proxy
|
|
||||||
url: http://localhost:3100
|
|
||||||
`,
|
|
||||||
user
|
|
||||||
);
|
|
||||||
|
|
||||||
await alpine.sudoWriteExecutable(
|
await alpine.sudoWriteExecutable(
|
||||||
"/usr/local/sbin/nulo-grafana-cli",
|
"/usr/local/sbin/nulo-grafana-cli",
|
||||||
`#!/bin/sh
|
`#!/bin/sh
|
||||||
|
@ -63,9 +45,17 @@ export GRAFANA_HOME=/var/lib/grafana
|
||||||
|
|
||||||
cd "$GRAFANA_HOME"
|
cd "$GRAFANA_HOME"
|
||||||
|
|
||||||
exec chpst -u grafana:grafana grafana-server -config /etc/grafana.ini -homepath /usr/share/grafana
|
install -o grafana -m755 -d \
|
||||||
`,
|
$GRAFANA_HOME/provisioning \
|
||||||
runitLokiLogger(FluentBitParser.Logfmt, "grafana")
|
$GRAFANA_HOME/provisioning/dashboards \
|
||||||
|
$GRAFANA_HOME/provisioning/datasources \
|
||||||
|
$GRAFANA_HOME/provisioning/notifiers \
|
||||||
|
$GRAFANA_HOME/provisioning/alerting \
|
||||||
|
$GRAFANA_HOME/provisioning/plugins
|
||||||
|
|
||||||
|
exec chpst -u grafana:grafana grafana-server -config /etc/grafana.ini -homepath /usr/share/grafana \
|
||||||
|
cfg:paths.provisioning="$GRAFANA_HOME"/provisioning
|
||||||
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +81,7 @@ data = /var/lib/grafana
|
||||||
plugins = /var/lib/grafana/plugins
|
plugins = /var/lib/grafana/plugins
|
||||||
|
|
||||||
# folder that contains provisioning config files that grafana will apply on startup and while running.
|
# folder that contains provisioning config files that grafana will apply on startup and while running.
|
||||||
provisioning = ${provisioningDir}
|
;provisioning = conf/provisioning
|
||||||
|
|
||||||
#################################### Server ####################################
|
#################################### Server ####################################
|
||||||
[server]
|
[server]
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { join } from "node:path";
|
|
||||||
import { buildRepro, reproRun } from "../../helpers/repro-run.js";
|
|
||||||
|
|
||||||
const LOKI_VERSION = "v2.7.3";
|
|
||||||
|
|
||||||
// returns path to statically compiled binary
|
|
||||||
export function buildLoki(): Promise<string> {
|
|
||||||
return buildRepro(
|
|
||||||
"loki",
|
|
||||||
LOKI_VERSION,
|
|
||||||
`#!/bin/sh -e
|
|
||||||
runprint() {
|
|
||||||
echo "==> $@"
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
runprint apk add --quiet git go make libc-dev bash
|
|
||||||
|
|
||||||
git config --global advice.detachedHead false
|
|
||||||
# TODO: cachear clon de repo
|
|
||||||
runprint git clone https://github.com/grafana/loki --branch '${LOKI_VERSION}' --depth 1 --single-branch
|
|
||||||
cd loki
|
|
||||||
|
|
||||||
runprint make -j1 GOMOD=readonly logcli loki
|
|
||||||
mv cmd/loki/loki /loki
|
|
||||||
`,
|
|
||||||
(dir) =>
|
|
||||||
reproRun({
|
|
||||||
cwd: dir,
|
|
||||||
command: "/src/build",
|
|
||||||
cache: [
|
|
||||||
"/home/repro/.cache/go-build",
|
|
||||||
"/home/repro/go",
|
|
||||||
"/home/repro/.npm",
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
(dir) => join(dir, "rootfs/loki")
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
import { join } from "node:path";
|
|
||||||
import { Alpine } from "../../alpine.js";
|
|
||||||
import { sudoCopy } from "../../helpers/sudo.js";
|
|
||||||
import { Runit } from "../../runit/index.js";
|
|
||||||
import { buildLoki } from "./build.js";
|
|
||||||
|
|
||||||
export async function setupLoki(alpine: Alpine, runit: Runit): Promise<void> {
|
|
||||||
const bin = await buildLoki();
|
|
||||||
await sudoCopy(bin, join(alpine.dir, "/usr/local/bin/loki"));
|
|
||||||
|
|
||||||
const user = await alpine.userAdd("loki");
|
|
||||||
|
|
||||||
// TODO: data
|
|
||||||
// await alpine.fstab.addTmpfs("/var/lib/grafana", {
|
|
||||||
// uid: user.uid,
|
|
||||||
// gid: user.gid,
|
|
||||||
// mode: "700",
|
|
||||||
// });
|
|
||||||
const configPath = "/etc/loki/loki-local-config.yaml";
|
|
||||||
await alpine.writeFile(
|
|
||||||
configPath,
|
|
||||||
`
|
|
||||||
auth_enabled: false
|
|
||||||
|
|
||||||
server:
|
|
||||||
http_listen_port: 3100
|
|
||||||
http_listen_address: 127.0.0.1
|
|
||||||
# grpc_listen_port: 9096
|
|
||||||
grpc_listen_address: 127.0.0.1
|
|
||||||
|
|
||||||
common:
|
|
||||||
instance_addr: 127.0.0.1
|
|
||||||
path_prefix: /tmp/loki
|
|
||||||
storage:
|
|
||||||
filesystem:
|
|
||||||
chunks_directory: /tmp/loki/chunks
|
|
||||||
rules_directory: /tmp/loki/rules
|
|
||||||
replication_factor: 1
|
|
||||||
ring:
|
|
||||||
kvstore:
|
|
||||||
store: inmemory
|
|
||||||
|
|
||||||
query_range:
|
|
||||||
results_cache:
|
|
||||||
cache:
|
|
||||||
embedded_cache:
|
|
||||||
enabled: true
|
|
||||||
max_size_mb: 100
|
|
||||||
|
|
||||||
schema_config:
|
|
||||||
configs:
|
|
||||||
- from: 2020-10-24
|
|
||||||
store: boltdb-shipper
|
|
||||||
object_store: filesystem
|
|
||||||
schema: v11
|
|
||||||
index:
|
|
||||||
prefix: index_
|
|
||||||
period: 24h
|
|
||||||
|
|
||||||
# ruler:
|
|
||||||
# alertmanager_url: http://localhost:9093
|
|
||||||
|
|
||||||
analytics:
|
|
||||||
reporting_enabled: false`,
|
|
||||||
user
|
|
||||||
);
|
|
||||||
|
|
||||||
// await alpine.sudoWriteExecutable(
|
|
||||||
// "/usr/local/sbin/nulo-grafana-cli",
|
|
||||||
// `#!/bin/sh
|
|
||||||
// cd /
|
|
||||||
// exec chpst -u grafana:grafana grafana-cli --homepath /usr/share/grafana --config /etc/grafana.ini "$@"`
|
|
||||||
// );
|
|
||||||
|
|
||||||
await runit.addService(
|
|
||||||
"loki",
|
|
||||||
`#!/bin/sh
|
|
||||||
exec chpst -u ${user.name}:${user.name} /usr/local/bin/loki -config.file='${configPath}'
|
|
||||||
`
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Alpine } from "../alpine.js";
|
import { Alpine } from "../alpine.js";
|
||||||
import { sudoWriteFile } from "../helpers/sudo.js";
|
import { sudoWriteFile } from "../helpers/sudo.js";
|
||||||
import { Runit } from "../runit/index.js";
|
import { Runit } from "../runit/index.js";
|
||||||
import { FluentBitParser, runitLokiLogger } from "../software/fluentbit.js";
|
|
||||||
|
|
||||||
export async function setupNtpsec(alpine: Alpine, runit: Runit) {
|
export async function setupNtpsec(alpine: Alpine, runit: Runit) {
|
||||||
await alpine.addPackages(["ntpsec"]);
|
await alpine.addPackages(["ntpsec"]);
|
||||||
|
@ -49,7 +48,6 @@ server gps.ntp.br nts iburst
|
||||||
"ntpsec",
|
"ntpsec",
|
||||||
`#!/bin/sh
|
`#!/bin/sh
|
||||||
exec ntpd --nice --nofork --panicgate
|
exec ntpd --nice --nofork --panicgate
|
||||||
`,
|
`
|
||||||
runitLokiLogger(FluentBitParser.Ntpsec, "ntpsec")
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
import { join } from "node:path";
|
|
||||||
import { Alpine } from "../alpine.js";
|
|
||||||
import { buildRepro, reproRun } from "../helpers/repro-run.js";
|
|
||||||
import { sudoCopy } from "../helpers/sudo.js";
|
|
||||||
|
|
||||||
const parsersPath = "/etc/fluent-bit/parsers.conf";
|
|
||||||
|
|
||||||
export async function installFluentBit(alpine: Alpine): Promise<void> {
|
|
||||||
const bin = await buildFluentBit();
|
|
||||||
await saveParsers(alpine);
|
|
||||||
await alpine.addPackages(["musl-fts", "yaml"]);
|
|
||||||
await sudoCopy(bin, join(alpine.dir, "/usr/local/bin/fluent-bit"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ## Script generators
|
|
||||||
|
|
||||||
// Returns a logScript to be used with runit for logging to Loki
|
|
||||||
export function runitLokiLogger(parser: FluentBitParser, name: string): string {
|
|
||||||
return `#!/bin/sh
|
|
||||||
exec chpst -u nobody:nobody /usr/local/bin/fluent-bit \
|
|
||||||
--parser='${parsersPath}' \
|
|
||||||
--input=stdin \
|
|
||||||
--prop=parser='${parser}' \
|
|
||||||
--output=loki \
|
|
||||||
--prop=labels='job=fluentbit,stream=${name}'
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ## Parsers
|
|
||||||
|
|
||||||
export enum FluentBitParser {
|
|
||||||
Json = "json",
|
|
||||||
Logfmt = "logfmt",
|
|
||||||
// Raw toma todo lo que haya en una línea y lo guarda en `message`. No recomendado.
|
|
||||||
Raw = "raw",
|
|
||||||
|
|
||||||
Forgejo = "forgejo",
|
|
||||||
Ntpsec = "ntpsec",
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveParsers(alpine: Alpine): Promise<void> {
|
|
||||||
// https://github.com/fluent/fluent-bit/blob/master/conf/parsers.conf
|
|
||||||
await alpine.writeFile(
|
|
||||||
parsersPath,
|
|
||||||
// https://rubular.com/
|
|
||||||
`
|
|
||||||
[PARSER]
|
|
||||||
name logfmt
|
|
||||||
format logfmt
|
|
||||||
|
|
||||||
[PARSER]
|
|
||||||
name raw
|
|
||||||
format regex
|
|
||||||
regex ^(?<message>.*?)$
|
|
||||||
|
|
||||||
[PARSER]
|
|
||||||
name forgejo
|
|
||||||
format regex
|
|
||||||
regex ^((?<time>\\d{4}\\/\\d{2}\\/\\d{2} \\d{2}:\\d{2}:\\d{2}?) )?((?<trace>.+:.+:.+\\(\\)?) \\[(?<level>.?)\\] )?(?<message>.*?)$
|
|
||||||
|
|
||||||
[PARSER]
|
|
||||||
name ntpsec
|
|
||||||
format regex
|
|
||||||
regex ^(?<time>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}) (?<process>.+\\[\\d+\\]): (?<component>\\w+): (?<message>.+)$
|
|
||||||
`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ## Build binary
|
|
||||||
|
|
||||||
function buildFluentBit(): Promise<string> {
|
|
||||||
const version = "v2.0.9";
|
|
||||||
return buildRepro(
|
|
||||||
"fluentbit",
|
|
||||||
version,
|
|
||||||
`#!/bin/sh -e
|
|
||||||
runprint() {
|
|
||||||
echo "==> $@"
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
runprint apk add --quiet \
|
|
||||||
make gcc g++ patch musl-dev openssl-dev linux-headers \
|
|
||||||
bison cmake flex musl-fts-dev gtest-dev yaml-dev zlib-dev
|
|
||||||
|
|
||||||
wget https://github.com/fluent/fluent-bit/archive/refs/tags/${version}.tar.gz -O- | tar zx
|
|
||||||
cd fluent-bit-*
|
|
||||||
|
|
||||||
patch --strip=1 <<'EOF'
|
|
||||||
--- a/lib/chunkio/src/CMakeLists.txt
|
|
||||||
+++ b/lib/chunkio/src/CMakeLists.txt
|
|
||||||
@@ -12,6 +12,7 @@
|
|
||||||
)
|
|
||||||
|
|
||||||
set(libs cio-crc32)
|
|
||||||
+set(libs \${libs} fts)
|
|
||||||
|
|
||||||
if(\${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
|
||||||
set(src
|
|
||||||
EOF
|
|
||||||
|
|
||||||
runprint cmake -B build \
|
|
||||||
-DFLB_CORO_STACK_SIZE=24576 \
|
|
||||||
.
|
|
||||||
runprint make -C build
|
|
||||||
|
|
||||||
ls build/bin/
|
|
||||||
|
|
||||||
mv build/bin/fluent-bit /fluent-bit
|
|
||||||
`,
|
|
||||||
(dir) =>
|
|
||||||
reproRun({
|
|
||||||
cwd: dir,
|
|
||||||
command: "/src/build",
|
|
||||||
cache: [],
|
|
||||||
}),
|
|
||||||
(dir) => join(dir, "rootfs/fluent-bit")
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in a new issue