raijin-server 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- raijin_server/__init__.py +5 -0
- raijin_server/cli.py +447 -0
- raijin_server/config.py +139 -0
- raijin_server/healthchecks.py +296 -0
- raijin_server/modules/__init__.py +26 -0
- raijin_server/modules/bootstrap.py +224 -0
- raijin_server/modules/calico.py +36 -0
- raijin_server/modules/essentials.py +29 -0
- raijin_server/modules/firewall.py +27 -0
- raijin_server/modules/full_install.py +131 -0
- raijin_server/modules/grafana.py +69 -0
- raijin_server/modules/hardening.py +47 -0
- raijin_server/modules/harness.py +47 -0
- raijin_server/modules/istio.py +13 -0
- raijin_server/modules/kafka.py +34 -0
- raijin_server/modules/kong.py +19 -0
- raijin_server/modules/kubernetes.py +187 -0
- raijin_server/modules/loki.py +27 -0
- raijin_server/modules/minio.py +19 -0
- raijin_server/modules/network.py +57 -0
- raijin_server/modules/prometheus.py +30 -0
- raijin_server/modules/traefik.py +40 -0
- raijin_server/modules/velero.py +47 -0
- raijin_server/modules/vpn.py +152 -0
- raijin_server/utils.py +241 -0
- raijin_server/validators.py +230 -0
- raijin_server-0.1.0.dist-info/METADATA +219 -0
- raijin_server-0.1.0.dist-info/RECORD +32 -0
- raijin_server-0.1.0.dist-info/WHEEL +5 -0
- raijin_server-0.1.0.dist-info/entry_points.txt +2 -0
- raijin_server-0.1.0.dist-info/licenses/LICENSE +21 -0
- raijin_server-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Instalacao completa e automatizada do ambiente produtivo."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, require_root
|
|
6
|
+
from raijin_server.modules import (
|
|
7
|
+
bootstrap,
|
|
8
|
+
essentials,
|
|
9
|
+
hardening,
|
|
10
|
+
network,
|
|
11
|
+
firewall,
|
|
12
|
+
kubernetes,
|
|
13
|
+
calico,
|
|
14
|
+
prometheus,
|
|
15
|
+
grafana,
|
|
16
|
+
loki,
|
|
17
|
+
traefik,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Ordem de execucao dos modulos para instalacao completa
|
|
22
|
+
INSTALL_SEQUENCE = [
|
|
23
|
+
("bootstrap", bootstrap.run, "Instalacao de ferramentas (helm, kubectl, containerd, etc.)"),
|
|
24
|
+
("essentials", essentials.run, "Pacotes essenciais e NTP"),
|
|
25
|
+
("hardening", hardening.run, "Seguranca do sistema (fail2ban, sysctl, auditd)"),
|
|
26
|
+
("network", network.run, "Configuracao de rede (IP fixo)"),
|
|
27
|
+
("firewall", firewall.run, "Firewall UFW"),
|
|
28
|
+
("kubernetes", kubernetes.run, "Cluster Kubernetes (kubeadm)"),
|
|
29
|
+
("calico", calico.run, "CNI Calico + NetworkPolicy"),
|
|
30
|
+
("prometheus", prometheus.run, "Monitoramento Prometheus"),
|
|
31
|
+
("grafana", grafana.run, "Dashboards Grafana"),
|
|
32
|
+
("loki", loki.run, "Logs centralizados Loki"),
|
|
33
|
+
("traefik", traefik.run, "Ingress Controller Traefik"),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def run(ctx: ExecutionContext) -> None:
|
|
38
|
+
"""Executa instalacao completa do ambiente produtivo."""
|
|
39
|
+
require_root(ctx)
|
|
40
|
+
|
|
41
|
+
typer.secho(
|
|
42
|
+
"\n" + "=" * 60,
|
|
43
|
+
fg=typer.colors.CYAN,
|
|
44
|
+
)
|
|
45
|
+
typer.secho(
|
|
46
|
+
" RAIJIN SERVER - Instalacao Completa Automatizada",
|
|
47
|
+
fg=typer.colors.CYAN,
|
|
48
|
+
bold=True,
|
|
49
|
+
)
|
|
50
|
+
typer.secho(
|
|
51
|
+
"=" * 60 + "\n",
|
|
52
|
+
fg=typer.colors.CYAN,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Mostra sequencia de instalacao
|
|
56
|
+
typer.echo("Sequencia de instalacao:")
|
|
57
|
+
for i, (name, _, desc) in enumerate(INSTALL_SEQUENCE, 1):
|
|
58
|
+
typer.echo(f" {i:2}. {name:15} - {desc}")
|
|
59
|
+
|
|
60
|
+
typer.echo("")
|
|
61
|
+
|
|
62
|
+
if not ctx.dry_run:
|
|
63
|
+
if not typer.confirm("Deseja continuar com a instalacao completa?", default=True):
|
|
64
|
+
typer.echo("Instalacao cancelada.")
|
|
65
|
+
raise typer.Exit(code=0)
|
|
66
|
+
|
|
67
|
+
total = len(INSTALL_SEQUENCE)
|
|
68
|
+
failed = []
|
|
69
|
+
succeeded = []
|
|
70
|
+
|
|
71
|
+
for i, (name, handler, desc) in enumerate(INSTALL_SEQUENCE, 1):
|
|
72
|
+
typer.secho(
|
|
73
|
+
f"\n{'='*60}",
|
|
74
|
+
fg=typer.colors.CYAN,
|
|
75
|
+
)
|
|
76
|
+
typer.secho(
|
|
77
|
+
f"[{i}/{total}] {name.upper()}: {desc}",
|
|
78
|
+
fg=typer.colors.CYAN,
|
|
79
|
+
bold=True,
|
|
80
|
+
)
|
|
81
|
+
typer.secho(
|
|
82
|
+
f"{'='*60}\n",
|
|
83
|
+
fg=typer.colors.CYAN,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
handler(ctx)
|
|
88
|
+
succeeded.append(name)
|
|
89
|
+
typer.secho(f"✓ {name} concluido com sucesso", fg=typer.colors.GREEN)
|
|
90
|
+
except KeyboardInterrupt:
|
|
91
|
+
typer.secho(f"\n⚠ Instalacao interrompida pelo usuario no modulo '{name}'", fg=typer.colors.YELLOW)
|
|
92
|
+
raise typer.Exit(code=130)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
failed.append((name, str(e)))
|
|
95
|
+
typer.secho(f"✗ {name} falhou: {e}", fg=typer.colors.RED)
|
|
96
|
+
|
|
97
|
+
if not ctx.dry_run:
|
|
98
|
+
if not typer.confirm("Continuar com os proximos modulos?", default=True):
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
# Resumo final
|
|
102
|
+
typer.echo("\n" + "=" * 60)
|
|
103
|
+
typer.secho("RESUMO DA INSTALACAO", fg=typer.colors.CYAN, bold=True)
|
|
104
|
+
typer.echo("=" * 60)
|
|
105
|
+
|
|
106
|
+
if succeeded:
|
|
107
|
+
typer.secho(f"\n✓ Modulos instalados com sucesso ({len(succeeded)}):", fg=typer.colors.GREEN)
|
|
108
|
+
for name in succeeded:
|
|
109
|
+
typer.echo(f" - {name}")
|
|
110
|
+
|
|
111
|
+
if failed:
|
|
112
|
+
typer.secho(f"\n✗ Modulos com falha ({len(failed)}):", fg=typer.colors.RED)
|
|
113
|
+
for name, error in failed:
|
|
114
|
+
typer.echo(f" - {name}: {error}")
|
|
115
|
+
|
|
116
|
+
if not failed:
|
|
117
|
+
typer.secho(
|
|
118
|
+
"\n✓ INSTALACAO COMPLETA COM SUCESSO!",
|
|
119
|
+
fg=typer.colors.GREEN,
|
|
120
|
+
bold=True,
|
|
121
|
+
)
|
|
122
|
+
typer.echo("\nProximos passos:")
|
|
123
|
+
typer.echo(" 1. Verifique o cluster: kubectl get nodes")
|
|
124
|
+
typer.echo(" 2. Acesse Grafana: kubectl port-forward svc/grafana 3000:80 -n observability")
|
|
125
|
+
typer.echo(" 3. Adicione workers: kubeadm token create --print-join-command")
|
|
126
|
+
else:
|
|
127
|
+
typer.secho(
|
|
128
|
+
f"\n⚠ Instalacao concluida com {len(failed)} erro(s). Verifique os logs.",
|
|
129
|
+
fg=typer.colors.YELLOW,
|
|
130
|
+
bold=True,
|
|
131
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Configuracao do Grafana via Helm com datasource e dashboards provisionados."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root, write_file
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run(ctx: ExecutionContext) -> None:
|
|
11
|
+
require_root(ctx)
|
|
12
|
+
typer.echo("Instalando Grafana via Helm...")
|
|
13
|
+
|
|
14
|
+
admin_password = typer.prompt("Senha admin do Grafana", default="admin")
|
|
15
|
+
ingress_host = typer.prompt("Host para acessar o Grafana", default="grafana.local")
|
|
16
|
+
ingress_class = typer.prompt("IngressClass", default="traefik")
|
|
17
|
+
tls_secret = typer.prompt("Secret TLS (cert-manager)", default="grafana-tls")
|
|
18
|
+
|
|
19
|
+
values_yaml = f"""adminPassword: {admin_password}
|
|
20
|
+
service:
|
|
21
|
+
type: ClusterIP
|
|
22
|
+
ingress:
|
|
23
|
+
enabled: true
|
|
24
|
+
ingressClassName: {ingress_class}
|
|
25
|
+
hosts:
|
|
26
|
+
- {ingress_host}
|
|
27
|
+
tls:
|
|
28
|
+
- secretName: {tls_secret}
|
|
29
|
+
hosts:
|
|
30
|
+
- {ingress_host}
|
|
31
|
+
persistence:
|
|
32
|
+
enabled: true
|
|
33
|
+
size: 10Gi
|
|
34
|
+
datasources:
|
|
35
|
+
datasources.yaml:
|
|
36
|
+
apiVersion: 1
|
|
37
|
+
datasources:
|
|
38
|
+
- name: Prometheus
|
|
39
|
+
type: prometheus
|
|
40
|
+
access: proxy
|
|
41
|
+
url: http://kube-prometheus-stack-prometheus.observability.svc:9090
|
|
42
|
+
isDefault: true
|
|
43
|
+
jsonData:
|
|
44
|
+
timeInterval: 30s
|
|
45
|
+
dashboards:
|
|
46
|
+
default:
|
|
47
|
+
kubernetes:
|
|
48
|
+
gnetId: 6417
|
|
49
|
+
revision: 1
|
|
50
|
+
datasource: Prometheus
|
|
51
|
+
node-exporter:
|
|
52
|
+
gnetId: 1860
|
|
53
|
+
revision: 27
|
|
54
|
+
datasource: Prometheus
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
values_path = Path("/tmp/raijin-grafana-values.yaml")
|
|
58
|
+
write_file(values_path, values_yaml, ctx)
|
|
59
|
+
|
|
60
|
+
helm_upgrade_install(
|
|
61
|
+
release="grafana",
|
|
62
|
+
chart="grafana",
|
|
63
|
+
namespace="observability",
|
|
64
|
+
repo="grafana",
|
|
65
|
+
repo_url="https://grafana.github.io/helm-charts",
|
|
66
|
+
ctx=ctx,
|
|
67
|
+
values=[],
|
|
68
|
+
extra_args=["-f", str(values_path)],
|
|
69
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Tarefas de hardening do sistema."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from raijin_server.utils import ExecutionContext, apt_install, apt_update, enable_service, require_root, run_cmd
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run(ctx: ExecutionContext) -> None:
|
|
11
|
+
require_root(ctx)
|
|
12
|
+
typer.echo("Aplicando hardening basico...")
|
|
13
|
+
|
|
14
|
+
apt_update(ctx)
|
|
15
|
+
apt_install(["fail2ban", "unattended-upgrades", "auditd"], ctx)
|
|
16
|
+
|
|
17
|
+
enable_service("fail2ban", ctx)
|
|
18
|
+
# Tenta iniciar auditd mas nao falha o fluxo se o kernel estiver sem auditing.
|
|
19
|
+
run_cmd(["systemctl", "enable", "--now", "auditd"], ctx, check=False)
|
|
20
|
+
if not ctx.dry_run:
|
|
21
|
+
status = subprocess.run(
|
|
22
|
+
["systemctl", "is-active", "auditd"],
|
|
23
|
+
capture_output=True,
|
|
24
|
+
text=True,
|
|
25
|
+
)
|
|
26
|
+
if status.returncode != 0 or status.stdout.strip() != "active":
|
|
27
|
+
typer.secho(
|
|
28
|
+
"auditd nao ficou ativo. Verifique se o kernel esta com auditing habilitado (audit=1 no boot).",
|
|
29
|
+
fg=typer.colors.YELLOW,
|
|
30
|
+
)
|
|
31
|
+
run_cmd(
|
|
32
|
+
["dpkg-reconfigure", "--priority=low", "unattended-upgrades"],
|
|
33
|
+
ctx,
|
|
34
|
+
env={"DEBIAN_FRONTEND": "noninteractive"},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Reforca parametros de rede em tempo de execucao. Ajuste conforme necessidade.
|
|
38
|
+
sysctls = [
|
|
39
|
+
"net.ipv4.conf.all.rp_filter=1",
|
|
40
|
+
"net.ipv4.conf.default.rp_filter=1",
|
|
41
|
+
"net.ipv4.conf.all.accept_redirects=0",
|
|
42
|
+
"net.ipv4.conf.default.accept_redirects=0",
|
|
43
|
+
"net.ipv4.conf.all.send_redirects=0",
|
|
44
|
+
"net.ipv4.ip_forward=0",
|
|
45
|
+
]
|
|
46
|
+
for param in sysctls:
|
|
47
|
+
run_cmd(["sysctl", "-w", param], ctx)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Instalacao do Harness Delegate via Helm."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, ensure_tool, require_root, run_cmd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run(ctx: ExecutionContext) -> None:
|
|
9
|
+
require_root(ctx)
|
|
10
|
+
ensure_tool("helm", ctx, install_hint="Instale helm para implantar o delegate.")
|
|
11
|
+
|
|
12
|
+
typer.echo("Instalando Harness Delegate via Helm...")
|
|
13
|
+
account_id = typer.prompt("Harness accountId")
|
|
14
|
+
org_id = typer.prompt("Org ID", default="default")
|
|
15
|
+
project_id = typer.prompt("Project ID", default="default")
|
|
16
|
+
delegate_name = typer.prompt("Delegate name", default="raijin-delegate")
|
|
17
|
+
namespace = typer.prompt("Namespace", default="harness-delegate")
|
|
18
|
+
delegate_token = typer.prompt("Delegate token", hide_input=True)
|
|
19
|
+
|
|
20
|
+
run_cmd(
|
|
21
|
+
["helm", "repo", "add", "harness", "https://app.harness.io/storage/harness-download/delegate-helm-chart/"],
|
|
22
|
+
ctx,
|
|
23
|
+
)
|
|
24
|
+
run_cmd(["helm", "repo", "update"], ctx)
|
|
25
|
+
|
|
26
|
+
cmd = [
|
|
27
|
+
"helm",
|
|
28
|
+
"upgrade",
|
|
29
|
+
"--install",
|
|
30
|
+
delegate_name,
|
|
31
|
+
"harness/harness-delegate-ng",
|
|
32
|
+
"-n",
|
|
33
|
+
namespace,
|
|
34
|
+
"--create-namespace",
|
|
35
|
+
"--set",
|
|
36
|
+
f"delegateName={delegate_name}",
|
|
37
|
+
"--set",
|
|
38
|
+
f"accountId={account_id}",
|
|
39
|
+
"--set",
|
|
40
|
+
f"delegateToken={delegate_token}",
|
|
41
|
+
"--set",
|
|
42
|
+
f"orgId={org_id}",
|
|
43
|
+
"--set",
|
|
44
|
+
f"projectId={project_id}",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
run_cmd(cmd, ctx, mask_output=True, display_override="helm upgrade --install <delegate> harness/harness-delegate-ng ...")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Instalacao do Istio usando istioctl."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, ensure_tool, require_root, run_cmd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run(ctx: ExecutionContext) -> None:
|
|
9
|
+
require_root(ctx)
|
|
10
|
+
ensure_tool("istioctl", ctx, install_hint="Instale o binario do Istio CLI.")
|
|
11
|
+
typer.echo("Instalando Istio (perfil raijin)...")
|
|
12
|
+
run_cmd(["istioctl", "install", "--set", "profile=raijin", "-y"], ctx)
|
|
13
|
+
run_cmd(["kubectl", "label", "namespace", "default", "istio-injection=enabled", "--overwrite"], ctx)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Deploy do Apache Kafka via Helm (Bitnami OCI)."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run(ctx: ExecutionContext) -> None:
|
|
9
|
+
require_root(ctx)
|
|
10
|
+
typer.echo("Instalando Kafka (Bitnami) via Helm OCI...")
|
|
11
|
+
|
|
12
|
+
replicas = typer.prompt("Numero de brokers", default="3")
|
|
13
|
+
disk_size = typer.prompt("Storage por broker", default="20Gi")
|
|
14
|
+
|
|
15
|
+
# Bitnami charts migraram para OCI. Usamos a referencia OCI diretamente.
|
|
16
|
+
chart_ref = "oci://registry-1.docker.io/bitnamicharts/kafka"
|
|
17
|
+
|
|
18
|
+
values = [
|
|
19
|
+
f"replicaCount={replicas}",
|
|
20
|
+
"zookeeper.enabled=true",
|
|
21
|
+
f"persistence.size={disk_size}",
|
|
22
|
+
"metrics.kafka.enabled=true",
|
|
23
|
+
"metrics.jmx.enabled=true",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
helm_upgrade_install(
|
|
27
|
+
release="kafka",
|
|
28
|
+
chart=chart_ref,
|
|
29
|
+
namespace="kafka",
|
|
30
|
+
repo=None,
|
|
31
|
+
repo_url=None,
|
|
32
|
+
ctx=ctx,
|
|
33
|
+
values=values,
|
|
34
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Configuracao do Kong via Helm."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run(ctx: ExecutionContext) -> None:
|
|
9
|
+
require_root(ctx)
|
|
10
|
+
typer.echo("Instalando Kong via Helm...")
|
|
11
|
+
helm_upgrade_install(
|
|
12
|
+
release="kong",
|
|
13
|
+
chart="kong",
|
|
14
|
+
namespace="kong",
|
|
15
|
+
repo="kong",
|
|
16
|
+
repo_url="https://charts.konghq.com",
|
|
17
|
+
ctx=ctx,
|
|
18
|
+
values=["ingressController.installCRDs=false"],
|
|
19
|
+
)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Preparacao de cluster Kubernetes com kubeadm."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from raijin_server.utils import (
|
|
9
|
+
ExecutionContext,
|
|
10
|
+
apt_install,
|
|
11
|
+
apt_update,
|
|
12
|
+
enable_service,
|
|
13
|
+
ensure_tool,
|
|
14
|
+
require_root,
|
|
15
|
+
run_cmd,
|
|
16
|
+
write_file,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _cleanup_old_repo(ctx: ExecutionContext) -> None:
|
|
21
|
+
"""Remove repo legado apt.kubernetes.io se existir para evitar erro 404."""
|
|
22
|
+
|
|
23
|
+
repo_file = Path("/etc/apt/sources.list.d/kubernetes.list")
|
|
24
|
+
if ctx.dry_run:
|
|
25
|
+
typer.echo("[dry-run] checando repos antigos em /etc/apt/sources.list.d/kubernetes.list")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
if repo_file.exists():
|
|
29
|
+
try:
|
|
30
|
+
content = repo_file.read_text()
|
|
31
|
+
if "apt.kubernetes.io" in content:
|
|
32
|
+
repo_file.unlink()
|
|
33
|
+
typer.echo("Repo antigo apt.kubernetes.io removido.")
|
|
34
|
+
except Exception:
|
|
35
|
+
typer.echo("Nao foi possivel ler/remover repo antigo apt.kubernetes.io (ok prosseguir).")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def run(ctx: ExecutionContext) -> None:
|
|
39
|
+
require_root(ctx)
|
|
40
|
+
typer.echo("Instalando e preparando Kubernetes (kubeadm/kubelet/kubectl)...")
|
|
41
|
+
|
|
42
|
+
# Verifica se cluster ja foi inicializado
|
|
43
|
+
kubeconfig_exists = Path("/etc/kubernetes/admin.conf").exists()
|
|
44
|
+
if kubeconfig_exists and not ctx.dry_run:
|
|
45
|
+
typer.secho("⚠ Cluster Kubernetes ja parece estar inicializado.", fg=typer.colors.YELLOW)
|
|
46
|
+
if not typer.confirm("Deseja continuar mesmo assim? (pode causar problemas)"):
|
|
47
|
+
typer.echo("Operacao cancelada pelo usuario.")
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
_cleanup_old_repo(ctx)
|
|
51
|
+
apt_update(ctx)
|
|
52
|
+
apt_install(
|
|
53
|
+
[
|
|
54
|
+
"apt-transport-https",
|
|
55
|
+
"ca-certificates",
|
|
56
|
+
"curl",
|
|
57
|
+
"gnupg",
|
|
58
|
+
"lsb-release",
|
|
59
|
+
"containerd",
|
|
60
|
+
],
|
|
61
|
+
ctx,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Novo repo oficial (pkgs.k8s.io) substitui apt.kubernetes.io, que nao tem Release em distros recentes.
|
|
65
|
+
repo_version = "v1.30"
|
|
66
|
+
key_url = f"https://pkgs.k8s.io/core:/stable:/{repo_version}/deb/Release.key"
|
|
67
|
+
key_tmp = Path("/tmp/kubernetes-release.key")
|
|
68
|
+
key_path = Path("/etc/apt/keyrings/kubernetes-apt-keyring.gpg")
|
|
69
|
+
repo_entry = f"deb [signed-by={key_path}] https://pkgs.k8s.io/core:/stable:/{repo_version}/deb/ /"
|
|
70
|
+
|
|
71
|
+
# Verifica se ja tem a chave instalada
|
|
72
|
+
if not key_path.exists() or ctx.dry_run:
|
|
73
|
+
typer.echo("Baixando chave GPG do Kubernetes (pkgs.k8s.io)...")
|
|
74
|
+
run_cmd(
|
|
75
|
+
[
|
|
76
|
+
"curl",
|
|
77
|
+
"-fsSL",
|
|
78
|
+
"--retry",
|
|
79
|
+
"3",
|
|
80
|
+
"--retry-delay",
|
|
81
|
+
"2",
|
|
82
|
+
"-o",
|
|
83
|
+
str(key_tmp),
|
|
84
|
+
key_url,
|
|
85
|
+
],
|
|
86
|
+
ctx,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
run_cmd(["mkdir", "-p", "/etc/apt/keyrings"], ctx)
|
|
90
|
+
|
|
91
|
+
if not ctx.dry_run:
|
|
92
|
+
if not key_tmp.exists() or key_tmp.stat().st_size == 0:
|
|
93
|
+
typer.secho("Falha ao baixar chave GPG do Kubernetes. Verifique conectividade.", fg=typer.colors.RED)
|
|
94
|
+
raise typer.Exit(code=1)
|
|
95
|
+
run_cmd(["gpg", "--yes", "--dearmor", "-o", str(key_path), str(key_tmp)], ctx)
|
|
96
|
+
if not key_path.exists() or key_path.stat().st_size == 0:
|
|
97
|
+
typer.secho("Chave GPG nao foi gravada corretamente.", fg=typer.colors.RED)
|
|
98
|
+
raise typer.Exit(code=1)
|
|
99
|
+
key_tmp.unlink(missing_ok=True)
|
|
100
|
+
else:
|
|
101
|
+
typer.echo("Chave GPG do Kubernetes ja existe, pulando download.")
|
|
102
|
+
|
|
103
|
+
repo_file = Path("/etc/apt/sources.list.d/kubernetes.list")
|
|
104
|
+
if not repo_file.exists() or ctx.dry_run:
|
|
105
|
+
run_cmd(
|
|
106
|
+
f'echo "{repo_entry}" | tee /etc/apt/sources.list.d/kubernetes.list',
|
|
107
|
+
ctx,
|
|
108
|
+
use_shell=True,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
apt_update(ctx)
|
|
112
|
+
apt_install(["kubelet", "kubeadm", "kubectl"], ctx)
|
|
113
|
+
|
|
114
|
+
# Hold das versoes para evitar upgrades automaticos
|
|
115
|
+
run_cmd(["apt-mark", "hold", "kubelet", "kubeadm", "kubectl"], ctx, check=False)
|
|
116
|
+
|
|
117
|
+
# Configura containerd com SystemdCgroup.
|
|
118
|
+
run_cmd(["mkdir", "-p", "/etc/containerd"], ctx)
|
|
119
|
+
|
|
120
|
+
# Verifica se config do containerd ja existe
|
|
121
|
+
containerd_config = Path("/etc/containerd/config.toml")
|
|
122
|
+
if not containerd_config.exists() or ctx.dry_run:
|
|
123
|
+
run_cmd("containerd config default > /etc/containerd/config.toml", ctx, use_shell=True)
|
|
124
|
+
|
|
125
|
+
# Aplica SystemdCgroup
|
|
126
|
+
run_cmd("sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml", ctx, use_shell=True)
|
|
127
|
+
run_cmd(["systemctl", "restart", "containerd"], ctx, check=False)
|
|
128
|
+
|
|
129
|
+
enable_service("containerd", ctx)
|
|
130
|
+
enable_service("kubelet", ctx)
|
|
131
|
+
|
|
132
|
+
# kubeadm exige ip_forward=1; sobrepoe ajuste de hardening para fase de cluster.
|
|
133
|
+
write_file(Path("/etc/sysctl.d/99-kubernetes.conf"), "net.ipv4.ip_forward=1\n", ctx)
|
|
134
|
+
run_cmd(["sysctl", "-w", "net.ipv4.ip_forward=1"], ctx, check=False)
|
|
135
|
+
|
|
136
|
+
# Prompts de configuracao
|
|
137
|
+
pod_cidr = typer.prompt("Pod CIDR", default="10.244.0.0/16")
|
|
138
|
+
service_cidr = typer.prompt("Service CIDR", default="10.96.0.0/12")
|
|
139
|
+
cluster_name = typer.prompt("Nome do cluster", default="raijin")
|
|
140
|
+
advertise_address = typer.prompt("API advertise address", default="0.0.0.0")
|
|
141
|
+
|
|
142
|
+
kubeadm_config = f"""apiVersion: kubeadm.k8s.io/v1beta3
|
|
143
|
+
kind: ClusterConfiguration
|
|
144
|
+
clusterName: {cluster_name}
|
|
145
|
+
kubernetesVersion: stable
|
|
146
|
+
controlPlaneEndpoint: {advertise_address}:6443
|
|
147
|
+
networking:
|
|
148
|
+
podSubnet: {pod_cidr}
|
|
149
|
+
serviceSubnet: {service_cidr}
|
|
150
|
+
apiServer:
|
|
151
|
+
extraArgs:
|
|
152
|
+
authorization-mode: Node,RBAC
|
|
153
|
+
timeoutForControlPlane: 4m0s
|
|
154
|
+
controllerManager: {{}}
|
|
155
|
+
scheduler: {{}}
|
|
156
|
+
---
|
|
157
|
+
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
|
158
|
+
kind: KubeProxyConfiguration
|
|
159
|
+
mode: ipvs
|
|
160
|
+
---
|
|
161
|
+
apiVersion: kubelet.config.k8s.io/v1beta1
|
|
162
|
+
kind: KubeletConfiguration
|
|
163
|
+
cgroupDriver: systemd
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
cfg_path = Path("/etc/kubernetes/kubeadm-config.yaml")
|
|
167
|
+
write_file(cfg_path, kubeadm_config, ctx)
|
|
168
|
+
|
|
169
|
+
ensure_tool("kubeadm", ctx, install_hint="Instale kubeadm (apt install kubeadm).")
|
|
170
|
+
run_cmd(["kubeadm", "config", "images", "pull"], ctx, check=False)
|
|
171
|
+
run_cmd(["kubeadm", "init", "--config", str(cfg_path), "--upload-certs"], ctx)
|
|
172
|
+
|
|
173
|
+
# Configura kubeconfig para root e sudoer.
|
|
174
|
+
run_cmd(["mkdir", "-p", "/root/.kube"], ctx)
|
|
175
|
+
run_cmd(["cp", "/etc/kubernetes/admin.conf", "/root/.kube/config"], ctx)
|
|
176
|
+
run_cmd("chown $(id -u):$(id -g) /root/.kube/config", ctx, use_shell=True)
|
|
177
|
+
|
|
178
|
+
sudo_user = os.environ.get("SUDO_USER")
|
|
179
|
+
if sudo_user:
|
|
180
|
+
user_home = Path(f"/home/{sudo_user}")
|
|
181
|
+
kube_dir = user_home / ".kube"
|
|
182
|
+
run_cmd(["mkdir", "-p", str(kube_dir)], ctx)
|
|
183
|
+
run_cmd(["cp", "/etc/kubernetes/admin.conf", str(kube_dir / "config")], ctx)
|
|
184
|
+
run_cmd(["chown", f"{sudo_user}:{sudo_user}", str(kube_dir / "config")], ctx)
|
|
185
|
+
|
|
186
|
+
typer.echo("Comando de join para workers:")
|
|
187
|
+
run_cmd(["kubeadm", "token", "create", "--print-join-command"], ctx, check=False)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Configuracao do Loki via Helm."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run(ctx: ExecutionContext) -> None:
|
|
9
|
+
require_root(ctx)
|
|
10
|
+
typer.echo("Instalando Loki Stack via Helm...")
|
|
11
|
+
|
|
12
|
+
values = [
|
|
13
|
+
"promtail.enabled=true",
|
|
14
|
+
"loki.persistence.enabled=true",
|
|
15
|
+
"loki.persistence.size=20Gi",
|
|
16
|
+
"loki.retentionPeriod=168h", # 7 dias
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
helm_upgrade_install(
|
|
20
|
+
release="loki",
|
|
21
|
+
chart="loki-stack",
|
|
22
|
+
namespace="observability",
|
|
23
|
+
repo="grafana",
|
|
24
|
+
repo_url="https://grafana.github.io/helm-charts",
|
|
25
|
+
ctx=ctx,
|
|
26
|
+
values=values,
|
|
27
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Deploy do MinIO via Helm."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from raijin_server.utils import ExecutionContext, helm_upgrade_install, require_root
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run(ctx: ExecutionContext) -> None:
|
|
9
|
+
require_root(ctx)
|
|
10
|
+
typer.echo("Instalando MinIO via Helm (modo standalone)...")
|
|
11
|
+
helm_upgrade_install(
|
|
12
|
+
release="minio",
|
|
13
|
+
chart="minio",
|
|
14
|
+
namespace="minio",
|
|
15
|
+
repo="minio",
|
|
16
|
+
repo_url="https://charts.min.io/",
|
|
17
|
+
ctx=ctx,
|
|
18
|
+
values=["mode=standalone"],
|
|
19
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Configuracao de rede (IP fixo) via Netplan."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from raijin_server.utils import ExecutionContext, require_root, run_cmd, write_file
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _is_wsl() -> bool:
|
|
12
|
+
if os.environ.get("WSL_DISTRO_NAME"):
|
|
13
|
+
return True
|
|
14
|
+
try:
|
|
15
|
+
data = Path("/proc/version").read_text().lower()
|
|
16
|
+
return "microsoft" in data or "wsl" in data
|
|
17
|
+
except OSError:
|
|
18
|
+
return False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run(ctx: ExecutionContext) -> None:
|
|
22
|
+
require_root(ctx)
|
|
23
|
+
typer.echo("Configurando IP fixo (Netplan)...")
|
|
24
|
+
|
|
25
|
+
wsl = _is_wsl()
|
|
26
|
+
if wsl:
|
|
27
|
+
typer.secho(
|
|
28
|
+
"Ambiente WSL detectado: netplan pode nao ter efeito ou quebrar rede. Use apenas para gerar arquivo.",
|
|
29
|
+
fg=typer.colors.YELLOW,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
iface = typer.prompt("Interface", default="ens18")
|
|
33
|
+
address = typer.prompt("Endereco CIDR", default="192.168.0.10/24")
|
|
34
|
+
gateway = typer.prompt("Gateway", default="192.168.0.1")
|
|
35
|
+
dns = typer.prompt("DNS (separe por virgula)", default="1.1.1.1,8.8.8.8")
|
|
36
|
+
|
|
37
|
+
dns_list = ",".join([item.strip() for item in dns.split(",") if item.strip()])
|
|
38
|
+
netplan_content = f"""network:
|
|
39
|
+
version: 2
|
|
40
|
+
renderer: networkd
|
|
41
|
+
ethernets:
|
|
42
|
+
{iface}:
|
|
43
|
+
dhcp4: false
|
|
44
|
+
addresses: [{address}]
|
|
45
|
+
gateway4: {gateway}
|
|
46
|
+
nameservers:
|
|
47
|
+
addresses: [{dns_list}]
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
target = Path("/etc/netplan/01-raijin-static.yaml")
|
|
51
|
+
write_file(target, netplan_content, ctx)
|
|
52
|
+
|
|
53
|
+
apply_now = typer.confirm("Aplicar netplan agora?", default=not wsl)
|
|
54
|
+
if apply_now:
|
|
55
|
+
run_cmd(["netplan", "apply"], ctx)
|
|
56
|
+
else:
|
|
57
|
+
typer.echo("Netplan nao aplicado (apenas arquivo gerado).")
|