persist infrastructure and plattform

This commit is contained in:
Marcel Arndt 2025-04-10 17:31:33 +02:00
parent f81ae2f2e7
commit 98aeb82ff6
7 changed files with 230 additions and 35 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
.local/ .local/
.vscode/
.env
.DS_Store

View File

@ -0,0 +1,3 @@
config:
avicenna-infrastructure:hcloudToken:
secure: AAABAIi5Eso+isiy15TT2VC45yG73iLmuwhaDFi8180Gm0Vd9zdVSmh9HRPXQMlSU+NxzQDqOx5qbgqvMDiyew4AHkEgUJvJUA0cV0Mn1B4WeGlhzzYmbgdHeVobgv8i

View File

@ -22,8 +22,13 @@ const sshKey = new tls.PrivateKey('sshKey', {
export const privateKey = sshKey.privateKeyOpenssh; export const privateKey = sshKey.privateKeyOpenssh;
const env = pulumi.getStack();
if (!fs.existsSync(`./.local/${env}`)) {
fs.mkdirSync(`./.local/${env}`);
}
privateKey.apply((privKey) => privateKey.apply((privKey) =>
fs.writeFileSync('././local/private_key', privKey, { mode: '0600' }) fs.writeFileSync(`./.local/${env}/private_key`, privKey, { mode: '0600' })
); );
const hetznerSshKey = new hcloud.SshKey( const hetznerSshKey = new hcloud.SshKey(
@ -163,12 +168,11 @@ async function createSwarmCluster(nodeCount: number) {
return vm; return vm;
} }
const geniusceo = digitalocean.Domain.get( const doToken = config.getSecret('doToken');
'geniusceo', if (doToken) {
'genius.ceo', const geniusceo = digitalocean.Domain.get('geniusceo', 'genius.ceo');
);
const wwwSubDomain = geniusceo.id.apply( const wwwSubDomain = geniusceo.id.apply(
(id) => (id) =>
new digitalocean.DnsRecord('www', { new digitalocean.DnsRecord('www', {
domain: id, domain: id,
@ -176,9 +180,9 @@ const wwwSubDomain = geniusceo.id.apply(
value: swarmMaster.ipv4Address, value: swarmMaster.ipv4Address,
name: 'www', name: 'www',
}) })
); );
const noSubDomain = geniusceo.id.apply( const noSubDomain = geniusceo.id.apply(
(id) => (id) =>
new digitalocean.DnsRecord('noSub', { new digitalocean.DnsRecord('noSub', {
domain: id, domain: id,
@ -186,9 +190,9 @@ const noSubDomain = geniusceo.id.apply(
value: swarmMaster.ipv4Address, value: swarmMaster.ipv4Address,
name: '@', name: '@',
}) })
); );
const wildcardSubDomain = geniusceo.id.apply( const wildcardSubDomain = geniusceo.id.apply(
(id) => (id) =>
new digitalocean.DnsRecord('*', { new digitalocean.DnsRecord('*', {
domain: id, domain: id,
@ -196,7 +200,8 @@ const wildcardSubDomain = geniusceo.id.apply(
value: swarmMaster.ipv4Address, value: swarmMaster.ipv4Address,
name: '*', name: '*',
}) })
); );
}
const installAnsibleDepsCmd = new command.remote.Command( const installAnsibleDepsCmd = new command.remote.Command(
'installAnsibleDepsCmd', 'installAnsibleDepsCmd',
@ -213,7 +218,7 @@ const installAnsibleDepsCmd = new command.remote.Command(
const deployDockerStackCmd = new command.local.Command( const deployDockerStackCmd = new command.local.Command(
'deployDockerStackCmd', 'deployDockerStackCmd',
{ {
create: pulumi.interpolate`ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u root -i "${swarmMaster.ipv4Address}," --private-key ./private_key ansible/playbook.yml`, create: pulumi.interpolate`ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u root -i "${swarmMaster.ipv4Address}," --private-key ./.local/${env}/private_key ansible/playbook.yml`,
}, },
{ {
dependsOn: [installAnsibleDepsCmd], dependsOn: [installAnsibleDepsCmd],

View File

@ -0,0 +1,79 @@
version: '3.3'
networks:
data-hub_net:
traefik_public:
external: true
volumes:
pgdata:
services:
data:
image: thedevilisdero/avic-data-connector:latest
environment:
DATABASE_PORT: ${DATABASE_PORT}
DATABASE_HOST: ${DATABASE_HOST}
DATABASE_USERNAME: ${DATABASE_USERNAME}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
DATABASE_DATABASE: ${DATABASE_DATABASE}
DATABASE_URL: ${DATABASE_URL}
DISPO_LIVE_BASE_URL: ${DISPO_LIVE_BASE_URL}
DISPO_LIVE_USERNAME: ${DISPO_LIVE_USERNAME}
DISPO_LIVE_PASSWORD: ${DISPO_LIVE_PASSWORD}
DYFLEXIS_V0_BASE_URL: ${DYFLEXIS_V0_BASE_URL}
DYFLEXIS_V2_BASE_URL: ${DYFLEXIS_V2_BASE_URL}
DYFLEXIS_V0_API_KEY: ${DYFLEXIS_V0_API_KEY}
DYFLEXIS_V2_API_KEY: ${DYFLEXIS_V2_API_KEY}
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "http://monitoring_alloy:4318/v1/traces"
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: "http://monitoring_alloy:4318/v1/metrics"
OTEL_SERVICE_NAME: "avicenna.data-connector"
networks:
- data-hub_net
- traefik_public
deploy:
labels:
- "traefik.enable=true"
- "traefik.swarm.network=traefik_public"
- "traefik.http.routers.avic-data-con.rule=(Host(`avicenna.genius.ceo`) && PathPrefix(`/api/`)) || (Host(`avicenna.genius.ceo`) && Path(`/graphql`))"
- "traefik.http.routers.avic-data-con.entrypoints=https"
- "traefik.http.routers.avic-data-con.tls=true"
- "traefik.http.routers.avic-data-con.tls.certresolver=main"
- "traefik.http.services.avic-data-con.loadbalancer.server.port=3000"
web:
image: thedevilisdero/avic-app-dashboard:latest
networks:
- traefik_public
environment:
- COLLECTOR_SECRET
- OTLP_ENDPOINT
- OTLP_SERVICE_NAME
- GRAPHQL_ENDPOINT
- GRAPHQL_WS_ENDPOINT
- MS_AUTHENTICATION_CLIENT_ID
- MS_AUTHENTICATION_AUTHORITY
- MS_AUTHENTICATION_REDIRECT_URI
- MS_AUTHENTICATION_POST_LOGOUT_REDIRECT_URI
deploy:
labels:
- "traefik.enable=true"
- "traefik.swarm.network=traefik_public"
- "traefik.http.routers.avicenna-web.rule=Host(`avicenna.genius.ceo`)"
- "traefik.http.routers.avicenna-web.entrypoints=https"
- "traefik.http.routers.avicenna-web.tls=true"
- "traefik.http.routers.avicenna-web.tls.certresolver=main"
- "traefik.http.services.avicenna-web.loadbalancer.server.port=80"
db:
image: postgres:16.3
environment:
POSTGRES_HOST: ${DATABASE_HOST}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_DB: ${DATABASE_DATABASE}
PG_DATA: /var/lib/postgresql/data
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- data-hub_net

View File

@ -0,0 +1,104 @@
version: '3.3'
networks:
data-hub_net:
services:
# Automation
kestra-db:
image: postgres
volumes:
- ./.local/data/postgres:/var/lib/postgresql/data
env_file:
- .env
environment:
POSTGRES_DB: ${KESTRA_POSTGRES_DB}
POSTGRES_USER: ${KESTRA_POSTGRES_USER}
POSTGRES_PASSWORD: ${KESTRA_POSTGRES_PASSWORD}
networks:
data-hub_net:
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${KESTRA_POSTGRES_DB} -U $${KESTRA_POSTGRES_USER}"]
interval: 30s
timeout: 10s
retries: 10
kestra:
image: kestra/kestra:latest-full
pull_policy: always
# Note that this is meant for development only. Refer to the documentation for production deployments of Kestra which runs without a root user.
user: "root"
command: server standalone --worker-thread=128
networks:
data-hub_net:
volumes:
- ./.local/data/kestra:/app/storage
- /var/run/docker.sock:/var/run/docker.sock
- /tmp/kestra-wd:/tmp/kestra-wd
environment:
KESTRA_CONFIGURATION: |
datasources:
postgres:
url: jdbc:postgresql://kestra-db:5432/kestra
driverClassName: org.postgresql.Driver
username: kestra
password: k3str4
kestra:
server:
basic-auth:
enabled: false
username: "admin@kestra.io" # it must be a valid email address
password: kestra
repository:
type: postgres
storage:
type: local
local:
base-path: "/app/storage"
queue:
type: postgres
tasks:
tmp-dir:
path: /tmp/kestra-wd/tmp
url: http://localhost:8080/
ports:
- "8080:8080"
- "8081:8081"
depends_on:
kestra-db:
condition: service_started
avicenna-data:
image: thedevilisdero/avic-data-connector
ports:
- 3000:3000
env_file: .env
environment:
DATABASE_PORT: ${DATABASE_PORT}
DATABASE_HOST: ${DATABASE_HOST}
DATABASE_USERNAME: ${DATABASE_USERNAME}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
DATABASE_DATABASE: ${DATABASE_DATABASE}
DATABASE_URL: ${DATABASE_URL}
DISPO_LIVE_BASE_URL: ${DISPO_LIVE_BASE_URL}
DISPO_LIVE_USERNAME: ${DISPO_LIVE_USERNAME}
DISPO_LIVE_PASSWORD: ${DISPO_LIVE_PASSWORD}
DYFLEXIS_V0_BASE_URL: ${DYFLEXIS_V0_BASE_URL}
DYFLEXIS_V2_BASE_URL: ${DYFLEXIS_V2_BASE_URL}
DYFLEXIS_V0_API_KEY: ${DYFLEXIS_V0_API_KEY}
DYFLEXIS_V2_API_KEY: ${DYFLEXIS_V2_API_KEY}
networks:
- data-hub_net
avicenna-db:
image: postgres:16.3
env_file: .env
environment:
POSTGRES_HOST: ${DATABASE_HOST}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_DB: ${DATABASE_DATABASE}
PG_DATA: /var/lib/postgresql/data
volumes:
- ./.local/pgdata:/var/lib/postgresql/data
networks:
- data-hub_net