feat(infrastructure): add basic terraform resources

This commit is contained in:
2025-09-19 23:32:00 +02:00
parent 68212b93b0
commit c8048d940d
20 changed files with 764 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
apiVersion: v2
name: maxscale-helm
version: 0.1.9
description: Helm chart for MaxScale related Kubernetes manifests

View File

@@ -0,0 +1,157 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: mariadb-repl
namespace: mariadb-operator
spec:
rootPasswordSecretKeyRef:
name: mariadb-secret
key: root-password
username: mariadb
passwordSecretKeyRef:
name: mariadb-secret
key: password
database: mariadb
storage:
size: 5Gi
storageClassName: longhorn
resizeInUseVolumes: true
waitForVolumeResize: true
volumeClaimTemplate:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: longhorn
replicas: 3
replicasAllowEvenNumber: true
podSpec:
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
seccompProfile:
type: RuntimeDefault
maxScale:
enabled: true
kubernetesService:
type: LoadBalancer
metadata:
annotations:
metallb.universe.tf/loadBalancerIPs: {{ .Values.metallb.maxscale_ip | default "" | quote }}
connection:
secretName: mxs-repl-conn
port: 3306
metrics:
enabled: true
tls:
enabled: true
replication:
enabled: true
primary:
podIndex: 0
automaticFailover: true
replica:
waitPoint: AfterSync
gtid: CurrentPos
replPasswordSecretKeyRef:
name: mariadb-secret
key: password
connectionTimeout: 10s
connectionRetries: 10
syncTimeout: 10s
syncBinlog: 1
probesEnabled: true
service:
type: LoadBalancer
metadata:
annotations:
metallb.universe.tf/loadBalancerIPs: {{ .Values.metallb.service_ip | default "" | quote }}
connection:
secretName: mariadb-repl-conn
secretTemplate:
key: dsn
primaryService:
type: LoadBalancer
metadata:
annotations:
metallb.universe.tf/loadBalancerIPs: {{ .Values.metallb.primary_ip | default "" | quote }}
primaryConnection:
secretName: mariadb-repl-conn-primary
secretTemplate:
key: dsn
secondaryService:
type: LoadBalancer
metadata:
annotations:
metallb.universe.tf/loadBalancerIPs: {{ .Values.metallb.secondary_ip | default "" | quote }}
secondaryConnection:
secretName: mariadb-repl-conn-secondary
secretTemplate:
key: dsn
affinity:
antiAffinityEnabled: true
tolerations:
- key: "k8s.mariadb.com/ha"
operator: "Exists"
effect: "NoSchedule"
podDisruptionBudget:
maxUnavailable: 33%
updateStrategy:
type: ReplicasFirstPrimaryLast
myCnf: |
[mariadb]
bind-address=*
default_storage_engine=InnoDB
binlog_format=row
innodb_autoinc_lock_mode=2
innodb_buffer_pool_size=1024M
max_allowed_packet=256M
#timeZone: Europe/Prague
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 1Gi
livenessProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 5
metrics:
enabled: true
tls:
enabled: true
required: true
suspend: false

View File

@@ -0,0 +1,18 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: Grant
metadata:
name: grant
spec:
mariaDbRef:
name: mariadb-repl
namespace: mariadb-operator
waitForIt: false
privileges:
- "ALL PRIVILEGES"
database: "*"
table: "*"
username: {{ .Values.user.name | default "user" }}
grantOption: true
host: {{ .Values.user.host | default "%" | quote }}
requeueInterval: 30s
retryInterval: 5s

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: mariadb-repl-0
namespace: mariadb-operator
spec:
selector:
app.kubernetes.io/instance: mariadb-repl
app.kubernetes.io/name: mariadb
statefulset.kubernetes.io/pod-name: mariadb-repl-0
ports:
- name: mariadb
port: 3306
targetPort: 3306
protocol: TCP
type: ClusterIP

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: mariadb-repl-1
namespace: mariadb-operator
spec:
selector:
app.kubernetes.io/instance: mariadb-repl
app.kubernetes.io/name: mariadb
statefulset.kubernetes.io/pod-name: mariadb-repl-1
ports:
- name: mariadb
port: 3306
targetPort: 3306
protocol: TCP
type: ClusterIP

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: mariadb-repl-2
namespace: mariadb-operator
spec:
selector:
app.kubernetes.io/instance: mariadb-repl
app.kubernetes.io/name: mariadb
statefulset.kubernetes.io/pod-name: mariadb-repl-2
ports:
- name: mariadb
port: 3306
targetPort: 3306
protocol: TCP
type: ClusterIP

View File

@@ -0,0 +1,32 @@
{{- if (.Values.phpmyadmin.enabled | default true) }}
apiVersion: v1
kind: ConfigMap
metadata:
name: phpmyadmin-config
namespace: mariadb-operator
data:
hosts-init-script.sh: |-
#!/bin/bash
echo "
/* Maximum number of databases displayed on one page */
$cfg['MaxDbList'] = 300;
$cfg['MaxNavigationItems'] = 300;
/* Additional servers */
$servers = [
{{- range $i, $e := until (int (3)) }}
'mariadb-repl-{{ $i }}',
{{- end }}
];
foreach ($servers as $server) {
$i++;
/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = 'cookie';
/* Server parameters */
$cfg['Servers'][$i]['host'] = $server;
$cfg['Servers'][$i]['port'] = '3306';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;
}
" >> /opt/bitnami/phpmyadmin/config.inc.php
{{- end }}

View File

@@ -0,0 +1,76 @@
{{- if (.Values.phpmyadmin.enabled | default true) }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: phpmyadmin
namespace: mariadb-operator
labels:
app: phpmyadmin
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: phpmyadmin
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: phpmyadmin
spec:
containers:
- env:
- name: DATABASE_ENABLE_SSL
value: "yes"
- name: DATABASE_HOST
value: "mariadb-repl"
- name: DATABASE_PORT_NUMBER
value: "3306"
- name: PHPMYADMIN_ALLOW_NO_PASSWORD
value: "false"
image: "docker.io/bitnami/phpmyadmin:5.2.2"
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /
port: http
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: phpmyadmin
ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: 8443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: http
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- mountPath: /docker-entrypoint-init.d/hosts-init-script.sh
name: config
subPath: hosts-init-script.sh
ip: 127.0.0.1
restartPolicy: Always
volumes:
- configMap:
defaultMode: 511
name: phpmyadmin-config
optional: false
name: config
{{- end }}

View File

@@ -0,0 +1,18 @@
{{- if (.Values.phpmyadmin.enabled | default true) }}
apiVersion: v1
kind: Service
metadata:
name: "phpmyadmin"
namespace: {{ .Values.namespace | default "mariadb-operator" | quote }}
labels:
app: "phpmyadmin"
spec:
clusterIP: None
ports:
- name: http
port: {{ .Values.phpmyadmin.servicePort | default 8080 }}
protocol: TCP
targetPort: {{ .Values.phpmyadmin.servicePort | default 8080 }}
selector:
app: "phpmyadmin"
{{- end }}

View File

@@ -0,0 +1,16 @@
apiVersion: k8s.mariadb.com/v1alpha1
kind: User
metadata:
name: mariadb-user
namespace: mariadb-operator
spec:
mariaDbRef:
name: mariadb-repl
namespace: mariadb-operator
waitForIt: false
host: {{ .Values.user.host | default "%" | quote }}
name: {{ .Values.user.name | default "user" }}
passwordPlugin: {}
passwordSecretKeyRef:
key: user-password
name: mariadb-secret

View File

@@ -0,0 +1,15 @@
# Default values for maxscale-helm.
# This file can be used to override manifest parameters.
user:
name: user
host: "%"
metallb:
maxscale_ip: ""
service_ip: ""
primary_ip: ""
secondary_ip: ""
phpmyadmin:
enabled: true

View File

@@ -0,0 +1,74 @@
terraform {
required_providers {
kubectl = {
source = "gavinbunney/kubectl"
version = "1.19.0"
}
helm = {
source = "hashicorp/helm"
version = "3.0.2"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.38.0"
}
}
}
resource "kubernetes_namespace" "mariadb-operator" {
metadata {
name = "mariadb-operator"
}
}
locals {
mariadb_secret_yaml = templatefile("${path.module}/mariadb-secret.yaml", {
password = var.mariadb_password
user_password = var.mariadb_user_password
root_password = var.mariadb_root_password
})
}
resource "kubectl_manifest" "secrets" {
yaml_body = local.mariadb_secret_yaml
depends_on = [ kubernetes_namespace.mariadb-operator ]
}
resource "helm_release" "mariadb-operator-crds" {
name = "mariadb-operator-crds"
repository = "https://helm.mariadb.com/mariadb-operator"
chart = "mariadb-operator-crds"
namespace = "mariadb-operator"
version = "25.8.4"
depends_on = [ kubectl_manifest.secrets ]
timeout = 3600
}
resource "helm_release" "mariadb-operator" {
name = "mariadb-operator"
repository = "https://helm.mariadb.com/mariadb-operator"
chart = "mariadb-operator"
depends_on = [ helm_release.mariadb-operator-crds, kubectl_manifest.secrets ]
namespace = "mariadb-operator"
timeout = 3600
}
resource "helm_release" "maxscale_helm" {
name = "maxscale-helm"
chart = "${path.module}/charts/maxscale-helm"
version = "0.1.9"
depends_on = [ helm_release.mariadb-operator-crds, kubectl_manifest.secrets ]
timeout = 3600
set = [
{ name = "user.name", value = var.mariadb_user_name },
{ name = "user.host", value = var.mariadb_user_host },
{ name = "metallb.maxscale_ip", value = var.maxscale_ip },
{ name = "metallb.service_ip", value = var.service_ip },
{ name = "metallb.primary_ip", value = var.primary_ip },
{ name = "metallb.secondary_ip", value = var.secondary_ip },
{ name = "phpmyadmin.enabled", value = tostring(var.phpmyadmin_enabled) },
]
}

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: mariadb-secret
namespace: mariadb-operator
labels:
k8s.mariadb.com/watch: ""
stringData:
password: ${password}
user-password: ${user_password}
root-password: ${root_password}

View File

@@ -0,0 +1,52 @@
variable "mariadb_password" {
description = "Password for standard MariaDB users (stored in Secret). Keep secure."
type = string
sensitive = true
}
variable "mariadb_root_password" {
description = "Root password for MariaDB (stored in Secret). Keep secure."
type = string
sensitive = true
}
variable "mariadb_user_name" {
description = "Application username to create and grant privileges to"
type = string
}
variable "mariadb_user_host" {
description = "Host (wildcard or specific) for the application user"
type = string
}
variable "maxscale_ip" {
description = "MetalLB IP for MaxScale service"
type = string
}
variable "service_ip" {
description = "MetalLB IP for general MariaDB service"
type = string
}
variable "primary_ip" {
description = "MetalLB IP for MariaDB primary service"
type = string
}
variable "secondary_ip" {
description = "MetalLB IP for MariaDB secondary service"
type = string
}
variable "phpmyadmin_enabled" {
description = "Whether to deploy phpMyAdmin auxiliary components"
type = bool
}
variable "mariadb_user_password" {
description = "Password for the application MariaDB user"
type = string
sensitive = true
}

View File

@@ -0,0 +1,70 @@
terraform {
required_providers {
kubectl = {
source = "gavinbunney/kubectl"
version = "1.19.0"
}
helm = {
source = "hashicorp/helm"
version = "3.0.2"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.38.0"
}
}
}
resource "kubernetes_namespace" "metallb-system" {
metadata {
name = "metallb-system"
labels = {
"pod-security.kubernetes.io/enforce" = "privileged"
}
}
}
resource "helm_release" "metallb" {
depends_on = [ kubernetes_namespace.metallb-system ]
name = "metallb"
repository = "https://metallb.github.io/metallb"
chart = "metallb"
namespace = "metallb-system"
version = "0.14.9"
timeout = 3600
}
resource "kubectl_manifest" "metallb_pool" {
depends_on = [ helm_release.metallb ]
yaml_body = yamlencode({
apiVersion = "metallb.io/v1beta1"
kind = "IPAddressPool"
metadata = {
name = "metallb-pool"
namespace = "metallb-system"
}
spec = {
addresses = [var.metallb_ip_range]
}
})
}
resource "kubectl_manifest" "metallb_l2_advertisement" {
depends_on = [ kubectl_manifest.metallb_pool ]
yaml_body = yamlencode({
apiVersion = "metallb.io/v1beta1"
kind = "L2Advertisement"
metadata = {
name = "l2-advertisement"
namespace = "metallb-system"
}
spec = {
ipAddressPools = ["metallb-pool"]
}
})
}

View File

@@ -0,0 +1,4 @@
variable "metallb_ip_range" {
description = "IP address range for MetalLB address pool"
type = string
}

View File

@@ -0,0 +1,35 @@
terraform {
required_providers {
kubectl = {
source = "gavinbunney/kubectl"
version = "1.19.0"
}
helm = {
source = "hashicorp/helm"
version = "3.0.2"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.38.0"
}
}
}
resource "kubernetes_namespace" "longhorn-system" {
metadata {
name = "longhorn-system"
labels = {
"pod-security.kubernetes.io/enforce" = "privileged"
}
}
}
resource "helm_release" "longhorn" {
depends_on = [ kubernetes_namespace.longhorn-system ]
name = "longhorn"
repository = "https://charts.longhorn.io/"
chart = "longhorn"
namespace = "longhorn-system"
version = "1.9.1"
timeout = 3600
}

61
tofu/main.tf Normal file
View File

@@ -0,0 +1,61 @@
terraform {
required_providers {
kubectl = {
source = "gavinbunney/kubectl"
version = "1.19.0"
}
helm = {
source = "hashicorp/helm"
version = "3.0.2"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.38.0"
}
}
}
provider "kubernetes" {
config_path = "./kubeconfig"
}
provider "kubectl" {
config_path = "./kubeconfig"
}
provider "helm" {
kubernetes = {
config_path = "./kubeconfig"
}
}
module "loadbalancer" {
source = "${path.module}/config/metallb"
depends_on = [ module.storage ]
metallb_ip_range = var.metallb_ip_range
}
module "database" {
source = "${path.module}/config/maxscale"
depends_on = [ module.storage, module.loadbalancer ]
mariadb_password = var.mariadb_password
mariadb_root_password = var.mariadb_root_password
mariadb_user_name = var.mariadb_user_name
mariadb_user_host = var.mariadb_user_host
mariadb_user_password = var.mariadb_user_password
maxscale_ip = var.metallb_maxscale_ip
service_ip = var.metallb_service_ip
primary_ip = var.metallb_primary_ip
secondary_ip = var.metallb_secondary_ip
phpmyadmin_enabled = var.phpmyadmin_enabled
}
module "storage" {
source = "${path.module}/config/storage"
}

65
tofu/variables.tf Normal file
View File

@@ -0,0 +1,65 @@
variable "metallb_ip_range" {
description = "IP address range for MetalLB address pool (e.g., 10.80.0.100-10.80.0.240)"
type = string
}
variable "mariadb_password" {
description = "Password for standard MariaDB users (stored in Secret). Keep secure."
type = string
sensitive = true
}
variable "mariadb_root_password" {
description = "Root password for MariaDB (stored in Secret). Keep secure."
type = string
sensitive = true
}
variable "mariadb_user_name" {
description = "Application username to create and grant privileges to"
type = string
default = "user"
}
variable "mariadb_user_host" {
description = "Host (wildcard or specific) for the application user"
type = string
default = "%"
}
variable "metallb_maxscale_ip" {
description = "MetalLB IP for MaxScale service"
type = string
default = ""
}
variable "metallb_service_ip" {
description = "MetalLB IP for general MariaDB service"
type = string
default = ""
}
variable "metallb_primary_ip" {
description = "MetalLB IP for MariaDB primary service"
type = string
default = ""
}
variable "metallb_secondary_ip" {
description = "MetalLB IP for MariaDB secondary service"
type = string
default = ""
}
variable "phpmyadmin_enabled" {
description = "Whether to deploy phpMyAdmin auxiliary components"
type = bool
default = true
}
variable "mariadb_user_password" {
description = "Password for the application MariaDB user"
type = string
sensitive = true
nullable = false
}