feat: organize all services into separate stacks by dependency

This commit is contained in:
2025-11-22 17:51:58 +05:00
parent 06a316f1e6
commit a25c25afc4
30 changed files with 2513 additions and 386 deletions

View File

@@ -0,0 +1,47 @@
import * as fs from "fs";
import * as path from "path";
import { Release } from "@cdktf/provider-helm/lib/release";
import { Construct } from "constructs";
import { OnePasswordSecret } from "../../utils";
import { Providers } from "../../types";
type AuthentikServerOptions = {
providers: Providers;
name: string;
namespace: string;
};
export class AuthentikServer extends Construct {
constructor(scope: Construct, id: string, options: AuthentikServerOptions) {
super(scope, id);
const { kubernetes, helm } = options.providers;
new OnePasswordSecret(this, "secret-key", {
provider: kubernetes,
name: "authentik-secret-key",
namespace: options.namespace,
itemPath: "vaults/Lab/items/authentik-secret-key",
});
new OnePasswordSecret(this, "smtp", {
provider: kubernetes,
name: "authentik-smtp-token",
namespace: options.namespace,
itemPath: "vaults/Lab/items/smtp-token",
});
new Release(this, id, {
...options,
provider: helm,
repository: "https://charts.goauthentik.io",
chart: "authentik",
createNamespace: true,
values: [
fs.readFileSync(path.join(__dirname, "values.yaml"), {
encoding: "utf8",
}),
],
}).importFrom("homelab/authentik");
}
}

View File

@@ -0,0 +1,110 @@
global:
addPrometheusAnnotations: true
securityContext:
runAsUser: 1000
fsGroup: 1000
podLabels:
app: authentik
nodeSelector:
nodepool: worker
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: authentik
env:
- name: AUTHENTIK_SECRET_KEY
valueFrom:
secretKeyRef:
name: authentik-secret-key
key: password
- name: AUTHENTIK_EMAIL__USERNAME
valueFrom:
secretKeyRef:
name: authentik-smtp-token
key: authentik-username
- name: AUTHENTIK_EMAIL__PASSWORD
valueFrom:
secretKeyRef:
name: authentik-smtp-token
key: authentik-password
- name: AUTHENTIK_EMAIL__FROM
valueFrom:
secretKeyRef:
name: authentik-smtp-token
key: authentik-username
- name: AUTHENTIK_EMAIL__USE_TLS
value: "true"
- name: AUTHENTIK_POSTGRESQL__SSLMODE
value: verify-full
- name: AUTHENTIK_POSTGRESQL__SSLROOTCERT
value: "/opt/authentik/certs/ca.crt"
- name: AUTHENTIK_POSTGRESQL__SSLCERT
value: "/opt/authentik/certs/tls.crt"
- name: AUTHENTIK_POSTGRESQL__SSLKEY
value: "/opt/authentik/certs/tls.key"
- name: AUTHENTIK_REDIS__PASSWORD
valueFrom:
secretKeyRef:
name: valkey
key: password
volumes:
- name: ssl-bundle
projected:
sources:
- secret:
name: authentik-client-cert
items:
- key: tls.crt
path: tls.crt
- key: tls.key
path: tls.key
mode: 0600
- secret:
name: postgres-server-cert
items:
- key: ca.crt
path: ca.crt
volumeMounts:
- name: ssl-bundle
mountPath: /opt/authentik/certs
readOnly: true
authentik:
error_reporting:
enabled: false
email:
host: "smtp.protonmail.ch"
port: 587
postgresql:
host: postgres-cluster-rw
user: authentik
name: authentik
redis:
host: valkey
server:
replicas: 3
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: cloudflare-issuer
cert-manager.io/acme-challenge-type: dns01
cert-manager.io/private-key-size: "4096"
ingressClassName: traefik
hosts:
- auth.dogar.dev
tls:
- secretName: authentik-tls
hosts:
- auth.dogar.dev
worker:
replicas: 3
postgresql:
enabled: false
redis:
enabled: false

View File

@@ -0,0 +1 @@
export { GiteaServer } from "./server";

View File

@@ -0,0 +1,82 @@
import * as fs from "fs";
import * as path from "path";
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
import { Release } from "@cdktf/provider-helm/lib/release";
import { Construct } from "constructs";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { OnePasswordSecret } from "../../utils";
import { IngressRouteTcp } from "../../utils/traefik";
type GiteaServerOptions = {
providers: {
helm: HelmProvider;
kubernetes: KubernetesProvider;
};
name: string;
namespace: string;
r2Endpoint: string;
};
export class GiteaServer extends Construct {
constructor(scope: Construct, id: string, options: GiteaServerOptions) {
super(scope, id);
const { kubernetes, helm } = options.providers;
new OnePasswordSecret(this, "admin", {
provider: kubernetes,
name: "gitea-admin",
namespace: options.namespace,
itemPath: "vaults/Lab/items/gitea-admin",
});
new OnePasswordSecret(this, "oauth", {
provider: kubernetes,
name: "gitea-oauth",
namespace: options.namespace,
itemPath: "vaults/Lab/items/gitea-oauth",
});
new OnePasswordSecret(this, "smtp", {
provider: kubernetes,
name: "gitea-smtp-token",
namespace: options.namespace,
itemPath: "vaults/Lab/items/smtp-token",
});
new OnePasswordSecret(this, "r2", {
provider: kubernetes,
name: "gitea-cloudflare-token",
namespace: options.namespace,
itemPath: "vaults/Lab/items/cloudflare",
});
new Release(this, id, {
...options,
provider: helm,
repository: "https://dl.gitea.com/charts",
chart: "gitea",
createNamespace: true,
set: [
{
name: "gitea.config.storage.MINIO_ENDPOINT",
value: options.r2Endpoint,
},
],
values: [
fs.readFileSync(path.join(__dirname, "values.yaml"), {
encoding: "utf8",
}),
],
});
new IngressRouteTcp(this, "ssh-ingress", {
provider: kubernetes,
namespace: options.namespace,
entryPoint: "ssh",
serviceName: `${options.name}-ssh`,
servicePort: 22,
});
}
}

View File

@@ -0,0 +1,161 @@
global:
storageClass: longhorn
image:
rootless: false
service:
http:
annotations:
metallb.universe.tf/allow-shared-ip: gitea
ssh:
annotations:
metallb.universe.tf/allow-shared-ip: gitea
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: cloudflare-issuer
cert-manager.io/acme-challenge-type: dns01
cert-manager.io/private-key-size: 4096
className: traefik
hosts:
- host: git.dogar.dev
paths:
- path: /
pathType: Prefix
tls:
- secretName: gitea-tls
hosts:
- git.dogar.dev
gitea:
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "6060"
admin:
existingSecret: gitea-admin
metrics:
enabled: true
serviceMonitor:
enabled: true
config:
server:
ENABLE_PPROF: true
ENABLE_GZIP: true
LFS_START_SERVER: true
SSH_DOMAIN: git.dogar.dev
database:
DB_TYPE: postgres
HOST: postgres-cluster-rw
NAME: gitea
USER: gitea
SSL_MODE: verify-full
metrics:
ENABLED: true
cache:
ADAPTER: memory
session:
PROVIDER: db
PROVIDER_CONFIG: ""
queue:
TYPE: channel
storage:
STORAGE_TYPE: minio
MINIO_USE_SSL: true
MINIO_BUCKET_LOOKUP_STYLE: path
MINIO_LOCATION: auto
service:
DISABLE_REGISTRATION: true
oauth2_client:
ENABLE_AUTO_REGISTRATION: true
mailer:
ENABLED: true
PROTOCOL: smtp+starttls
SMTP_ADDR: smtp.protonmail.ch
SMTP_PORT: 587
FROM: git@dogar.dev
picture:
GRAVATAR_SOURCE: gravatar
oauth:
- name: "authentik"
provider: "openidConnect"
existingSecret: gitea-oauth
autoDiscoverUrl: "https://auth.dogar.dev/application/o/gitea/.well-known/openid-configuration"
iconUrl: "https://goauthentik.io/img/icon.png"
scopes: "email profile"
additionalConfigFromEnvs:
- name: GITEA__MAILER__PASSWD
valueFrom:
secretKeyRef:
name: gitea-smtp-token
key: gitea-password
- name: GITEA__PACKAGES__CHUNKED_UPLOAD_PATH
value: "/tmp/gitea-uploads"
- name: GITEA__PACKAGES__CHUNKED_UPLOAD_CONCURRENCY
value: "4"
- name: GITEA__STORAGE__MINIO_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: gitea-cloudflare-token
key: access_key_id
- name: GITEA__STORAGE__MINIO_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: gitea-cloudflare-token
key: secret_access_key
persistence:
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/daily-backup: "enabled"
enabled: true
size: 50Gi
accessModes:
- ReadWriteMany
deployment:
env:
- name: PGSSLMODE
value: verify-full
- name: PGSSLROOTCERT
value: /opt/gitea/.postgresql/root.crt
- name: PGSSLCERT
value: /opt/gitea/.postgresql/postgresql.crt
- name: PGSSLKEY
value: /opt/gitea/.postgresql/postgresql.key
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 6
memory: 6Gi
extraVolumes:
- name: ssl-bundle
projected:
sources:
- secret:
name: gitea-client-cert
items:
- key: tls.crt
path: postgresql.crt
- key: tls.key
path: postgresql.key
mode: 0600
- secret:
name: postgres-server-cert
items:
- key: ca.crt
path: root.crt
- name: gitea-temp
emptyDir: {}
extraInitVolumeMounts:
- name: ssl-bundle
mountPath: /opt/gitea/.postgresql
readOnly: true
extraContainerVolumeMounts:
- name: ssl-bundle
mountPath: /opt/gitea/.postgresql
readOnly: true
readOnly: true
- name: gitea-temp
mountPath: /tmp/gitea-uploads
postgresql-ha:
enabled: false
valkey-cluster:
enabled: false

94
utility-services/index.ts Normal file
View File

@@ -0,0 +1,94 @@
import * as path from "path";
import { DataKubernetesNamespaceV1 } from "@cdktf/provider-kubernetes/lib/data-kubernetes-namespace-v1";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
import { DataTerraformRemoteStateLocal, TerraformStack } from "cdktf";
import { Construct } from "constructs";
import { ValkeyCluster } from "./valkey";
import { GiteaServer } from "./gitea";
import { AuthentikServer } from "./authentik";
import { PostgresCluster } from "./postgres";
export class UtilityServices extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
const kubernetes = new KubernetesProvider(this, "kubernetes", {
configPath: "~/.kube/config",
});
const helm = new HelmProvider(this, "helm", {
kubernetes: {
configPath: "~/.kube/config",
},
});
const homelabState = new DataTerraformRemoteStateLocal(
this,
"homelab-state",
{
path: path.join(
__dirname,
"../cdktf.out/stacks/homelab/terraform.tfstate",
),
},
);
const namespaceName = homelabState.getString("namespace-output");
const namespaceResource = new DataKubernetesNamespaceV1(
this,
"homelab-namespace",
{
provider: kubernetes,
metadata: {
name: namespaceName,
},
},
);
const namespace = namespaceResource.metadata.name;
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
const valkeyCluster = new ValkeyCluster(this, "valkey-cluster", {
namespace,
provider: kubernetes,
name: "valkey",
});
const postgres = new PostgresCluster(this, "postgres-cluster", {
certManagerApiVersion: "cert-manager.io/v1",
name: "postgres-cluster",
namespace,
provider: kubernetes,
users: ["shahab", "budget-tracker", "authentik", "gitea"],
primaryUser: "shahab",
initSecretName: "postgres-password",
backupR2EndpointURL: `https://${r2Endpoint}`,
});
const authentik = new AuthentikServer(this, "authentik-server", {
providers: {
helm,
kubernetes,
},
name: "authentik",
namespace,
});
authentik.node.addDependency(valkeyCluster);
authentik.node.addDependency(postgres);
const gitea = new GiteaServer(this, "gitea-server", {
providers: {
helm,
kubernetes,
},
name: "gitea",
namespace,
r2Endpoint: r2Endpoint,
});
gitea.node.addDependency(authentik);
}
}

View File

@@ -0,0 +1,456 @@
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { Construct } from "constructs";
import { OnePasswordSecret } from "../../utils";
type PostgresClusterOptions = {
provider: KubernetesProvider;
name: string;
namespace: string;
users: string[];
primaryUser: string;
initSecretName: string;
certManagerApiVersion: string;
backupR2EndpointURL: string;
};
export class PostgresCluster extends Construct {
constructor(scope: Construct, id: string, options: PostgresClusterOptions) {
super(scope, id);
const { provider } = options;
const destinationPath = "s3://postgres-backups/";
const endpointURL = options.backupR2EndpointURL;
const barmanStoreName = "r2-postgres-backup-store";
const backupServerName = `${options.name}-backup`;
const barmanConfiguration = {
destinationPath,
endpointURL,
s3Credentials: {
accessKeyId: {
name: "barman-cloudflare-token",
key: "access_key_id",
},
secretAccessKey: {
name: "barman-cloudflare-token",
key: "secret_access_key",
},
region: {
name: "barman-cloudflare-token",
key: "AWS_REGION",
},
},
wal: {
compression: "gzip",
},
data: {
compression: "gzip",
},
};
new OnePasswordSecret(this, "barman-cloudflare-token", {
provider: options.provider,
name: "barman-cloudflare-token",
namespace: options.namespace,
itemPath: "vaults/Lab/items/cloudflare",
});
new Manifest(this, "r2-backup-store", {
provider,
manifest: {
apiVersion: "barmancloud.cnpg.io/v1",
kind: "ObjectStore",
metadata: {
namespace: options.namespace,
name: barmanStoreName,
},
spec: {
retentionPolicy: "15d",
configuration: {
...barmanConfiguration,
},
},
},
});
const { certManagerApiVersion } = options;
const certNames = {
server: "postgres-server-cert",
client: "postgres-client-cert",
};
const caNames = {
server: "postgres-server-ca",
client: "postgres-client-ca",
};
// Self-signed issuer for creating CA certificates
new Manifest(this, "selfsigned-issuer", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Issuer",
metadata: {
name: "selfsigned-issuer",
namespace: options.namespace,
},
spec: {
selfSigned: {},
},
},
});
// Server CA certificate
new Manifest(this, "server-ca-cert", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Certificate",
metadata: {
name: "server-ca",
namespace: options.namespace,
},
spec: {
isCA: true,
commonName: caNames.server,
secretName: caNames.server,
privateKey: {
algorithm: "ECDSA",
size: 384,
},
duration: "52560h", // 6 years
renewBefore: "8760h", // 1 year before expiration
issuerRef: {
name: "selfsigned-issuer",
kind: "Issuer",
group: "cert-manager.io",
},
},
},
});
// Issuer using the server CA
new Manifest(this, "server-ca-issuer", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Issuer",
metadata: {
name: `${caNames.server}-issuer`,
namespace: options.namespace,
},
spec: {
ca: {
secretName: caNames.server,
},
},
},
});
// Server certificate
new Manifest(this, "server-cert", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Certificate",
metadata: {
name: certNames.server,
namespace: options.namespace,
},
spec: {
secretName: certNames.server,
usages: ["server auth"],
dnsNames: [
"postgres-cluster-rw",
"postgres-cluster-rw.homelab.svc.cluster.local",
"postgres.dogar.dev",
],
duration: "4380h", // 6 months
renewBefore: "720h", // 30 days before expiration
issuerRef: {
name: `${caNames.server}-issuer`,
kind: "Issuer",
group: "cert-manager.io",
},
},
},
});
// Client CA certificate
new Manifest(this, "client-ca", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Certificate",
metadata: {
name: "client-ca",
namespace: options.namespace,
},
spec: {
isCA: true,
commonName: caNames.client,
secretName: caNames.client,
privateKey: {
algorithm: "ECDSA",
size: 256,
},
duration: "52560h", // 6 years
renewBefore: "8760h", // 1 year before expiration
issuerRef: {
name: "selfsigned-issuer",
kind: "Issuer",
group: "cert-manager.io",
},
},
},
});
// Issuer using the client CA
new Manifest(this, "client-ca-issuer", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Issuer",
metadata: {
name: `${caNames.client}-issuer`,
namespace: options.namespace,
},
spec: {
ca: {
secretName: caNames.client,
},
},
},
});
// Secret for client certificate
new Manifest(this, `${certNames.client}-secret`, {
provider,
manifest: {
apiVersion: "v1",
kind: "Secret",
metadata: {
name: certNames.client,
namespace: options.namespace,
labels: {
"cnpg.io/reload": "",
},
},
},
});
// Client certificate for streaming replica
new Manifest(this, "streaming-replica-cert", {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Certificate",
metadata: {
name: certNames.client,
namespace: options.namespace,
},
spec: {
secretName: certNames.client,
usages: ["client auth"],
commonName: "streaming_replica",
duration: "4380h", // 6 months
renewBefore: "720h", // 30 days before expiration
issuerRef: {
name: "postgres-client-ca-issuer",
kind: "Issuer",
group: "cert-manager.io",
},
},
},
});
// Client certificates for users
options.users.forEach(
(user) =>
new Manifest(this, `${user}-client-cert`, {
provider,
manifest: {
apiVersion: certManagerApiVersion,
kind: "Certificate",
metadata: {
name: `${user}-client-cert`,
namespace: options.namespace,
},
spec: {
secretName: `${user}-client-cert`,
usages: ["client auth"],
commonName: user,
duration: "4380h", // 6 months
renewBefore: "720h", // 30 days before expiration
issuerRef: {
name: "postgres-client-ca-issuer",
kind: "Issuer",
group: "cert-manager.io",
},
},
},
}),
);
new Manifest(this, "postgres-cluster", {
provider,
fieldManager: { forceConflicts: true },
manifest: {
apiVersion: "postgresql.cnpg.io/v1",
kind: "Cluster",
metadata: {
name: options.name,
namespace: options.namespace,
},
spec: {
instances: 3,
minSyncReplicas: 1,
maxSyncReplicas: 2,
primaryUpdateStrategy: "unsupervised",
certificates: {
serverCASecret: certNames.server,
serverTLSSecret: certNames.server,
clientCASecret: certNames.client,
replicationTLSSecret: certNames.client,
},
postgresql: {
parameters: {
archive_mode: "on",
archive_timeout: "60min",
checkpoint_timeout: "10min",
checkpoint_completion_target: "0.7",
dynamic_shared_memory_type: "posix",
full_page_writes: "on",
log_destination: "csvlog",
log_directory: "/controller/log",
log_filename: "postgres",
log_rotation_age: "0",
log_rotation_size: "0",
log_truncate_on_rotation: "false",
logging_collector: "on",
max_parallel_workers: "32",
max_replication_slots: "32",
max_worker_processes: "32",
max_slot_wal_keep_size: "256MB",
max_wal_size: "512MB",
min_wal_size: "128MB",
shared_memory_type: "mmap",
shared_preload_libraries: "",
ssl_max_protocol_version: "TLSv1.3",
ssl_min_protocol_version: "TLSv1.3",
wal_compression: "on",
wal_keep_size: "128MB",
wal_level: "replica",
wal_log_hints: "on",
wal_receiver_timeout: "5s",
wal_sender_timeout: "5s",
},
pg_hba: [
`hostssl all ${options.primaryUser} all cert`,
"hostssl sameuser all all cert",
],
},
plugins: [
{
name: "barman-cloud.cloudnative-pg.io",
isWALArchiver: true,
parameters: {
barmanObjectName: barmanStoreName,
serverName: backupServerName,
},
},
],
bootstrap: {
recovery: {
source: "clusterBackup",
},
},
externalClusters: [
{
name: "clusterBackup",
plugin: {
name: "barman-cloud.cloudnative-pg.io",
parameters: {
barmanObjectName: barmanStoreName,
serverName: backupServerName,
skipWalArchiveCheck: true,
},
},
},
],
managed: {
services: {
disabledDefaultServices: ["ro", "r"],
additional: [
{
selectorType: "rw",
serviceTemplate: {
metadata: {
name: "postgres-cluster",
superuser: true,
},
spec: {
type: "LoadBalancer",
},
},
},
],
},
roles: [
{
name: options.primaryUser,
inRoles: ["postgres"],
inherit: true,
disablePassword: true,
createdb: true,
createrole: true,
login: true,
ensure: "present",
},
],
},
storage: {
size: "10Gi",
storageClass: "longhorn",
},
walStorage: {
size: "2Gi",
storageClass: "longhorn",
},
},
},
});
new Manifest(this, "postgres-backup-job", {
provider,
manifest: {
apiVersion: "postgresql.cnpg.io/v1",
kind: "ScheduledBackup",
metadata: {
name: "postgres-cluster",
namespace: options.namespace,
},
spec: {
immediate: true,
// weekly midnight on Sunday
schedule: "* 0 0 * * 0",
backupOwnerReference: "self",
method: "plugin",
pluginConfiguration: {
name: "barman-cloud.cloudnative-pg.io",
parameters: {
barmanObjectName: barmanStoreName,
serverName: backupServerName,
},
},
cluster: {
name: options.name,
},
},
},
});
}
}

View File

@@ -0,0 +1,128 @@
import { DeploymentV1 } from "@cdktf/provider-kubernetes/lib/deployment-v1";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
import { Construct } from "constructs";
import { OnePasswordSecret } from "../../utils";
type ValkeyClusterOptions = {
provider: KubernetesProvider;
name: string;
namespace: string;
};
export class ValkeyCluster extends Construct {
constructor(scope: Construct, id: string, options: ValkeyClusterOptions) {
super(scope, id);
// Labels used by both Deployment and Service
const labels = { app: "valkey" };
const { provider, name, namespace } = options;
new OnePasswordSecret(this, "valkey-secret", {
provider,
name: "valkey",
namespace,
itemPath: "vaults/Lab/items/valkey",
});
new DeploymentV1(this, "valkey-deployment", {
provider,
metadata: {
name,
namespace,
labels,
},
spec: {
replicas: "1",
strategy: {
type: "RollingUpdate",
rollingUpdate: {
maxSurge: "1",
maxUnavailable: "0",
},
},
selector: { matchLabels: labels },
template: {
metadata: { labels },
spec: {
container: [
{
name: "valkey",
image: "docker.io/valkey/valkey:8.1.3",
port: [{ name: "client", containerPort: 6379 }],
env: [
{
name: "PASSWORD",
valueFrom: {
secretKeyRef: {
name: "valkey",
key: "password",
},
},
},
],
command: ["/bin/sh", "-c"],
args: ['exec valkey-server --requirepass "$PASSWORD"'],
readinessProbe: {
tcpSocket: [
{
port: "6379",
},
],
initialDelaySeconds: 5,
periodSeconds: 5,
timeoutSeconds: 3,
failureThreshold: 5,
},
livenessProbe: {
tcpSocket: [
{
port: "6379",
},
],
initialDelaySeconds: 20,
periodSeconds: 10,
timeoutSeconds: 5,
failureThreshold: 5,
},
resources: {
requests: {
cpu: "100m",
memory: "128Mi",
},
limits: {
memory: "512Mi",
},
},
},
],
},
},
},
});
new ServiceV1(this, "valkey-service", {
provider,
metadata: {
name,
namespace,
labels,
annotations: {
"external-dns.alpha.kubernetes.io/hostname": "valkey.dogar.dev",
"metallb.io/ip-allocated-from-pool": "pool",
},
},
spec: {
type: "LoadBalancer",
selector: labels,
port: [
{
name: "client",
port: 6379,
targetPort: "client",
},
],
},
});
}
}