feat: Implement MariaDB and backup job modules with configuration and secrets management

This commit is contained in:
2025-04-27 19:12:07 +02:00
parent 761c183ce2
commit 654cf500f9
14 changed files with 5 additions and 74 deletions
+2 -2
View File
@@ -78,7 +78,7 @@ resource "kubernetes_secret" "gitea_mail_credentials" {
# Setup MariaDB database
#
module "gitea_database" {
source = "./modules/k8s_mariadb"
source = "../modules/k8s_mariadb"
namespace = kubernetes_namespace.gitea.metadata[0].name
app_name = "gitea"
@@ -97,7 +97,7 @@ module "gitea_database" {
# Setup rsync backup job to scaleway bucket
#
module "gitea_backup" {
source = "./modules/scw_backup_job"
source = "../modules/scw_backup_job"
namespace = kubernetes_namespace.gitea.metadata[0].name
app_name = "gitea"
+1 -1
View File
@@ -209,7 +209,7 @@ resource "kubernetes_manifest" "mail_certificate" {
module "mail_backup" {
source = "./modules/scw_backup_job"
source = "../modules/scw_backup_job"
namespace = kubernetes_namespace.mail.metadata[0].name
app_name = "mail"
@@ -1,244 +0,0 @@
resource "kubernetes_secret" "root_password" {
metadata {
name = "${var.app_name}-root-credentials"
namespace = var.namespace
}
data = {
username = "root"
password = var.root_password
}
type = "Opaque"
}
resource "kubernetes_service_account" "service_account" {
metadata {
name = "${var.app_name}-mariadb-sa"
namespace = var.namespace
}
automount_service_account_token = false
}
resource "kubernetes_secret" "app_password" {
metadata {
name = "${var.app_name}-app-credentials"
namespace = var.namespace
}
data = {
username = var.app_name
password = var.app_password
}
type = "Opaque"
}
resource "kubernetes_service" "service" {
metadata {
name = "mariadb"
namespace = var.namespace
}
spec {
port {
port = 3306
}
selector = {
app = "mariadb"
}
cluster_ip = "None"
}
}
resource "kubernetes_deployment" "deployment" {
metadata {
name = "mariadb"
namespace = var.namespace
}
spec {
selector {
match_labels = {
app = "mariadb"
}
}
strategy {
type = "Recreate"
}
template {
metadata {
labels = {
app = "mariadb"
}
}
spec {
service_account_name = kubernetes_service_account.service_account.metadata[0].name
container {
name = "mariadb"
image = "mariadb:${var.current_version}"
resources {
requests = {
memory = "100Mi"
}
}
liveness_probe {
exec {
command = ["sh", "-c", "mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"]
}
initial_delay_seconds = 30
period_seconds = 300
timeout_seconds = 1
failure_threshold = 15
}
readiness_probe {
exec {
command = ["sh", "-c", "mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"]
}
initial_delay_seconds = 120
period_seconds = 10
timeout_seconds = 1
failure_threshold = 15
}
env {
name = "MARIADB_ROOT_PASSWORD"
value_from {
secret_key_ref {
key = "password"
name = kubernetes_secret.root_password.metadata[0].name
}
}
}
env {
name = "MARIADB_ROOT_HOST"
value = "10.%"
}
env {
name = "MARIADB_USER"
value_from {
secret_key_ref {
key = "username"
name = kubernetes_secret.app_password.metadata[0].name
}
}
}
env {
name = "MARIADB_PASSWORD"
value_from {
secret_key_ref {
key = "password"
name = kubernetes_secret.app_password.metadata[0].name
}
}
}
env {
name = "MARIADB_DATABASE"
value = var.app_name
}
port {
container_port = 3306
name = "mariadb"
}
volume_mount {
mount_path = "/var/lib/mysql"
name = "persistent-storage"
}
volume_mount {
mount_path = "/docker-entrypoint-initdb.d"
name = "initdb-storage"
}
}
volume {
name = "persistent-storage"
host_path {
path = "${var.database_folder}/${var.app_name}.data"
}
}
volume {
name = "initdb-storage"
host_path {
path = "${var.database_folder}/${var.app_name}.initdb.d"
}
}
}
}
}
}
# TODO: This should move out of here
resource "kubernetes_cron_job_v1" "backup_data_cron" {
metadata {
name = "mariadb-backup-job"
namespace = var.namespace
}
spec {
concurrency_policy = "Forbid"
schedule = var.cron_schedule
failed_jobs_history_limit = 2
successful_jobs_history_limit = 2
job_template {
metadata {
labels = {
app = "mariadb"
}
}
spec {
backoff_limit = 1
parallelism = 1
completions = 1
active_deadline_seconds = 1800
template {
metadata {}
spec {
container {
name = "sql-cron-job"
image = "mariadb:${var.current_version}"
env {
name = "MARIADB_USER"
value_from {
secret_key_ref {
key = "username"
name = kubernetes_secret.app_password.metadata[0].name
}
}
}
env {
name = "MARIADB_PASSWORD"
value_from {
secret_key_ref {
key = "password"
name = kubernetes_secret.app_password.metadata[0].name
}
}
}
env {
name = "MARIADB_DATABASE"
value = var.app_name
}
security_context {
allow_privilege_escalation = false
}
command = ["/bin/sh", "-c"]
args = [join(";", [
"/usr/bin/mariadb-dump --single-transaction -h mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE | gzip > /backups/${var.app_name}-sql-`date +\"%Y%m%d\"`.sql.gz",
"find /backups -type f -name '${var.app_name}-sql-*.sql.gz' -mtime +${var.retention_days} | xargs --no-run-if-empty rm"
])]
volume_mount {
mount_path = "/backups"
name = "backups-storage"
}
}
volume {
name = "backups-storage"
host_path {
path = var.backup_folder
}
}
restart_policy = "Never"
termination_grace_period_seconds = 30
}
}
}
}
}
}
@@ -1,15 +0,0 @@
output "host" {
value = "mariadb"
}
output "port" {
value = 3306
}
output "root_crendentials_name" {
value = kubernetes_secret.root_password.metadata[0].name
}
output "app_crendentials_name" {
value = kubernetes_secret.app_password.metadata[0].name
}
@@ -1,11 +0,0 @@
terraform {
required_version = "~>1.8"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~>2.18"
}
}
}
@@ -1,51 +0,0 @@
variable "namespace" {
description = "namespace"
sensitive = false
type = string
}
variable "app_name" {
description = "database name"
sensitive = false
type = string
}
variable "root_password" {
description = "root password"
sensitive = true
type = string
}
variable "app_password" {
description = "user password"
sensitive = true
type = string
}
variable "database_folder" {
description = "host storage path"
type = string
}
variable "cron_schedule" {
description = "Cronjob schedule for data backups"
default = "* 2 * * *"
type = string
}
variable "retention_days" {
description = "Backup retention days"
default = 14
type = number
}
variable "current_version" {
description = "version"
default = "10.7.8"
type = string
}
variable "backup_folder" {
description = "Backup source location"
type = string
}
@@ -1,3 +0,0 @@
locals {
rclone_version = "1.61.1"
}
@@ -1,167 +0,0 @@
resource "kubernetes_service_account" "service_account" {
metadata {
name = "${var.app_name}-backup-sa"
namespace = var.namespace
}
automount_service_account_token = false
}
resource "kubernetes_secret" "scaleway_api_credentials" {
metadata {
name = "${var.app_name}-scaleway-api-credentials"
namespace = var.namespace
}
data = {
access_key = var.access_key
secret_key = var.secret_key
}
type = "Opaque"
}
resource "kubernetes_config_map" "rclone_config" {
metadata {
name = "rclone-config"
namespace = var.namespace
}
data = {
"rclone.conf" = file("${path.module}/resources/rclone.conf")
}
}
resource "kubernetes_cron_job_v1" "backup_job" {
metadata {
name = "${var.app_name}-backup"
namespace = var.namespace
}
spec {
concurrency_policy = "Forbid"
schedule = var.cron_schedule
failed_jobs_history_limit = 2
successful_jobs_history_limit = 2
job_template {
metadata {
labels = {
app = "${var.app_name}-backup"
}
}
spec {
backoff_limit = 1
parallelism = 1
completions = 1
active_deadline_seconds = 1800
template {
metadata {
labels = {
app = "${var.app_name}-backup"
}
}
spec {
service_account_name = kubernetes_service_account.service_account.metadata[0].name
security_context {
run_as_user = 0
}
container {
name = "${var.app_name}-backup"
image = "rclone/rclone:${local.rclone_version}"
image_pull_policy = "Always"
resources {
requests = {
memory = "100Mi"
}
}
env {
name = "SCW_ACCESS_KEY"
value_from {
secret_key_ref {
key = "access_key"
name = kubernetes_secret.scaleway_api_credentials.metadata[0].name
}
}
}
env {
name = "SCW_SECRET_KEY"
value_from {
secret_key_ref {
key = "secret_key"
name = kubernetes_secret.scaleway_api_credentials.metadata[0].name
}
}
}
env {
name = "SCW_DEFAULT_PROJECT_ID"
value = var.scaleway_project_id
}
env {
name = "SCW_DEFAULT_ORGANIZATION_ID"
value = var.scaleway_organization_id
}
env {
name = "SCW_DEFAULT_REGION"
value = var.scaleway_region
}
env {
name = "SCW_DEFAULT_ZONE"
value = var.scaleway_zone
}
env {
name = "BUCKET_NAME"
value = var.bucket_name
}
command = ["/bin/sh", "-c"]
args = [join(";", [
"apk update",
"apk add gettext",
"envsubst</etc/rclone.conf.tmpl>/etc/rclone.conf",
"tar -zcvpf /backup/${var.app_name}-files-`date +\"%Y%m%d\"`.tar.gz /files",
"find /backup -type f -name '${var.app_name}-files-*.tar.gz' -mtime +${var.retention_days} | xargs --no-run-if-empty rm",
"/usr/local/bin/rclone --config=/etc/rclone.conf sync --progress /backup scaleway:$BUCKET_NAME/${var.app_name}-backup"
])]
security_context {
allow_privilege_escalation = false
}
volume_mount {
name = "backup-storage"
mount_path = "/backup"
}
volume_mount {
name = "persistent-storage"
mount_path = "/files"
read_only = true
}
volume_mount {
name = "rclone-conf"
mount_path = "/etc/rclone.conf.tmpl"
sub_path = "rclone.conf"
read_only = true
}
}
volume {
name = "backup-storage"
host_path {
path = var.target_folder
}
}
volume {
name = "persistent-storage"
host_path {
path = var.source_folder
}
}
volume {
name = "rclone-conf"
config_map {
name = kubernetes_config_map.rclone_config.metadata[0].name
}
}
restart_policy = "Never"
termination_grace_period_seconds = 30
}
}
}
}
}
}
@@ -1,11 +0,0 @@
terraform {
required_version = "~>1.8"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~>2.18"
}
}
}
@@ -1,10 +0,0 @@
[scaleway]
type = s3
provider = Scaleway
env_auth = false
access_key_id = $SCW_ACCESS_KEY
secret_access_key = $SCW_SECRET_KEY
region = $SCW_DEFAULT_REGION
endpoint = s3.$SCW_DEFAULT_REGION.scw.cloud
acl = private
storage_class = STANDARD
@@ -1,70 +0,0 @@
variable "bucket_name" {
description = "Backup bucket name"
type = string
}
variable "namespace" {
description = "Namespace"
type = string
}
variable "scaleway_project_id" {
description = "Scaleway project id"
sensitive = true
type = string
}
variable "scaleway_organization_id" {
description = "Scaleway organization id"
sensitive = true
type = string
}
variable "scaleway_region" {
description = "Scaleway region"
default = "fr-par"
type = string
}
variable "scaleway_zone" {
default = "fr-par-2"
type = string
}
variable "cron_schedule" {
description = "Cronjob schedule for data backups"
default = "* 2 * * *"
type = string
}
variable "access_key" {
description = "Access key"
sensitive = true
type = string
}
variable "secret_key" {
description = "Secret key"
sensitive = true
type = string
}
variable "app_name" {
description = "Name of the app to backup"
type = string
}
variable "source_folder" {
description = "Name of host folder"
type = string
}
variable "target_folder" {
description = "Backup source location"
type = string
}
variable "retention_days" {
description = "backup retention days"
type = string
}
+2 -2
View File
@@ -154,7 +154,7 @@ resource "kubernetes_config_map" "nextcloud_apache_config" {
# Setup MariaDB database
#
module "nextcloud_database" {
source = "./modules/k8s_mariadb"
source = "../modules/k8s_mariadb"
namespace = kubernetes_namespace.nextcloud.metadata[0].name
app_name = "nextcloud"
@@ -174,7 +174,7 @@ module "nextcloud_database" {
#
module "nextcloud_backup" {
source = "./modules/scw_backup_job"
source = "../modules/scw_backup_job"
namespace = kubernetes_namespace.nextcloud.metadata[0].name
app_name = "nextcloud"
@@ -1,35 +0,0 @@
server {
listen 8080;
server_name localhost;
gzip on;
gzip_comp_level 9;
gzip_types
text/html
text/plain
text/css
text/js
text/xml
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
image/svg+xml;
root /tmp/site;
location / {
index index.html index.htm;
}
location ~* ^/([^/]+) {
index index.html index.htm;
error_page 404 = @error;
}
error_page 404 /404.html;
location @error {
try_files /$1/404.html /404.html =404;
}
}
@@ -1,34 +0,0 @@
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
proxy_temp_path /tmp/proxy_temp;
client_body_temp_path /tmp/client_temp;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}