Educational material go brr

This commit is contained in:
Greyscale 2024-06-14 04:18:48 +02:00
parent 757019b7d8
commit 55b3e74aa2
No known key found for this signature in database
GPG key ID: 74BAFF55434DA4B2
24 changed files with 380 additions and 0 deletions

3
.dockerignore Normal file
View file

@ -0,0 +1,3 @@
/*.tf*
/.git
/.terraform

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/.idea
/.terraform
/terraform.tfstate*
/.terraform.tfstate*

42
.terraform.lock.hcl Normal file
View file

@ -0,0 +1,42 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/random" {
version = "3.6.2"
constraints = "~> 3.3"
hashes = [
"h1:PXvoOj9gj+Or+9k0tQWCQJKxnsVO0GqnQwVahgwRrsU=",
"zh:1f27612f7099441526d8af59f5b4bdcc35f46915df5d243043d7337ea5a3e38a",
"zh:2a58e66502825db8b4b96116c04bd0323bca1cf1f5752bdd8f9c26feb84d3b1e",
"zh:4f0a4fa479e29de0c3c90146fd58799c097f7a55401cb00560dd4e9b1e6fad9d",
"zh:9c93c0fe6ef685513734527e0c8078636b2cc07591427502a7260f4744b1af1d",
"zh:a466ff5219beb77fb3b18a3d7e7fe30e7edd4d95c8e5c87f4f4e3fe3eeb8c2d7",
"zh:ab33e6176d0c757ddb31e40e01a941e6918ad10f7a786c8e8e4f35e5cff81c96",
"zh:b6eabf377a1c12cb3f9ddd97aacdd5b49c1646dc959074124f81d40fcd216d7e",
"zh:ccec5d03d0d1c0f354be299cdd6a417b2700f1a6781df36bcce77246b2f57e50",
"zh:d2a7945eeb691fdd2b1474da76ddc2d1655e2aedbb14b57f06d4f5123d47adf9",
"zh:ed62351f4ad9d1469c6798b77dee5f63b18b29c473620a0046ba3d4f111b621d",
]
}
provider "registry.opentofu.org/kreuzwerker/docker" {
version = "3.0.2"
constraints = "~> 3.0"
hashes = [
"h1:cT2ccWOtlfKYBUE60/v2/4Q6Stk1KYTNnhxSck+VPlU=",
"zh:15b0a2b2b563d8d40f62f83057d91acb02cd0096f207488d8b4298a59203d64f",
"zh:23d919de139f7cd5ebfd2ff1b94e6d9913f0977fcfc2ca02e1573be53e269f95",
"zh:38081b3fe317c7e9555b2aaad325ad3fa516a886d2dfa8605ae6a809c1072138",
"zh:4a9c5065b178082f79ad8160243369c185214d874ff5048556d48d3edd03c4da",
"zh:5438ef6afe057945f28bce43d76c4401254073de01a774760169ac1058830ac2",
"zh:60b7fadc287166e5c9873dfe53a7976d98244979e0ab66428ea0dea1ebf33e06",
"zh:61c5ec1cb94e4c4a4fb1e4a24576d5f39a955f09afb17dab982de62b70a9bdd1",
"zh:a38fe9016ace5f911ab00c88e64b156ebbbbfb72a51a44da3c13d442cd214710",
"zh:c2c4d2b1fd9ebb291c57f524b3bf9d0994ff3e815c0cd9c9bcb87166dc687005",
"zh:d567bb8ce483ab2cf0602e07eae57027a1a53994aba470fa76095912a505533d",
"zh:e83bf05ab6a19dd8c43547ce9a8a511f8c331a124d11ac64687c764ab9d5a792",
"zh:e90c934b5cd65516fbcc454c89a150bfa726e7cf1fe749790c7480bbeb19d387",
"zh:f05f167d2eaf913045d8e7b88c13757e3cf595dd5cd333057fdafc7c4b7fed62",
"zh:fcc9c1cea5ce85e8bcb593862e699a881bd36dffd29e2e367f82d15368659c3d",
]
}

View file

@ -0,0 +1,39 @@
Example Dockerised, Terraformed application deployment onto docker on bare metal
===============================================================================
We're gonna run Quassel on bare metal in the most low-effort and high-reward enterprisey way possible.
Why? Because we can.
## Pre-requisites
We're gonna need some tools:
* OpenTofu, the replacement for Terraform since Hashicorp changed the licence terms: https://opentofu.org/docs/intro/install/.
* As Tofu is a drop in replacement for Terraform, you can use Terraform if you prefer, and I'll refer to them interchangeably.
## What have we got going on in here?
The main players:
* database.tf - Configures our Quassel instances Postgres
* quassel.tf - Configures our Quassel instance itself
The bit-actors:
* docker.tf - Configures the Docker Provider for Tofu
* inputs.tf - Where inputs to this deployment are defined and may have defaults set
* outputs.tf - Where outputs from this deployment are defined, which will be output when we run `tofu apply` or `tofu outputs`
* terraform.tf - Telling Terraform/ToFu what packages we need to run this deployment
* .terraform.lock.hcl - Lock file for Terraform/ToFu, this is here to ensure that we're all using the same versions of the same packages
* terraform.tfvars - Where we may set the variables defined in inputs.tf
## What happens when we run this?
When we run `tofu apply`:
* Tofu will create a Docker network for our Quassel instance
* Tofu will create a Postgres instance for our Quassel instance
* Tofu will compile our Quassel instance image from the Dockerfile in the quassel directory
* Tofu will create a Quassel instance using that image
* Tofu will output the IP address of the Quassel instance and postgres instance
## How do we run this?
Did you give `tofu apply` a go yet?
## What we're gonna end up with:
`DIAGRAM GOES HERE`

62
database.tf Normal file
View file

@ -0,0 +1,62 @@
# Find our latest postgres 16 image
data "docker_registry_image" "postgres_quassel" {
name = "postgres:16"
}
# Generate a random password for our database
resource "random_password" "quassel_db_password" {
length = 32
special = false
}
# Create a volume for our database data to live in
resource "docker_volume" "quassel_db" {
name = "${var.docker_prefix}-quassel-db"
}
# Create our database service
resource "docker_service" "quassel_db" {
name = "${var.docker_prefix}-quassel-db"
task_spec {
container_spec {
# We've got our image from the registry...
image = "${data.docker_registry_image.postgres_quassel.name}@${data.docker_registry_image.postgres_quassel.sha256_digest}"
# And we're going to set some environment variables
env = {
POSTGRES_USER = local.pg_username
POSTGRES_DB = local.pg_database
POSTGRES_PASSWORD = local.pg_password
}
# We're going to define a nice healthcheck that will check that postgres is alive and well
healthcheck {
# Effectively this is running 'pg_isready -d postgres -U postgres' on the commandline inside the container and if it returns 0, the container is healthy, anything else is failure
test = ["CMD-SHELL", "pg_isready", "-d", local.pg_database, "-U", local.pg_username]
interval = "5s"
start_period = "15s"
}
# And we're going to mount our data volume to the container so that the data persists between restarts
mounts {
target = "/var/lib/postgresql/data"
type = "volume"
source = docker_volume.quassel_db.id
}
}
# And attach our network so that the quassel service can talk to the database
networks_advanced {
name = docker_network.quassel.id
}
}
# And we're going to wait for it to be up and running before we move on
converge_config {
delay = "5s" # Wait 5 seconds between checks
timeout = "2m" # Give up after 2 minutes
}
endpoint_spec {
ports {
target_port = local.pg_port_internal
published_port = local.pg_port_external
protocol = "tcp"
publish_mode = "ingress"
}
}
}

3
docker.tf Normal file
View file

@ -0,0 +1,3 @@
provider "docker" {
host = "ssh://california.ti"
}

7
inputs.tf Normal file
View file

@ -0,0 +1,7 @@
variable "docker_prefix" {
description = "Prefix for all docker resources.. We're not alone on this box, so we best avoid collisions"
}
variable "tz" {
description = "Timezone for the server"
default = "Europe/Amsterdam"
}

15
locals.tf Normal file
View file

@ -0,0 +1,15 @@
locals {
# Lets just take a moment to make some ease of life variables
# Quassel
quassel_port = random_integer.quassel_port.result # Lets just put the port in a variable so we can use it in multiple places
# database
pg_username = "postgres"
pg_database = "postgres"
pg_password = random_password.quassel_db_password.result
pg_hostname = docker_service.quassel_db.name
# We're going to use the quassel port + 1 for the postgres port because why not.. But this is only for external access, inside the internal network it will still be 5432!
pg_port_internal = 5432
pg_port_external = local.quassel_port + 1
}

15
outputs.tf Normal file
View file

@ -0,0 +1,15 @@
output "quassel" {
value = {
hostname = docker_service.quassel_db.name
port = local.quassel_port
}
}
output "postgres" {
value = {
hostname = docker_service.quassel_db.name
port = local.pg_port_external
username = local.pg_username
password = nonsensitive(local.pg_password)
database = local.pg_database
}
}

71
quassel.tf Normal file
View file

@ -0,0 +1,71 @@
# Pick a random port to use for our uplink port.
resource "random_integer" "quassel_port" {
max = 65535
min = 1024
}
# Build our latest quassel docker image.
resource "docker_image" "quassel" {
name = "${var.docker_prefix}-quassel"
build {
context = "${path.module}/quassel"
}
triggers = {
dir_sha1 = sha1(join("", [for f in fileset(path.module, "quassel/*") : filesha1(f)]))
}
}
# Create a network for our quassel service and postgres service to communicate upon
resource "docker_network" "quassel" {
name = "${var.docker_prefix}-quassel"
driver = "overlay" # We're using overlay networking because its fuckin' rad.
}
# Create our Quassel docker service.
resource "docker_service" "quassel" {
name = "${var.docker_prefix}-quassel"
# We need the database to be present for this container to work, so we can explicitly tell TF about it here
depends_on = [docker_service.quassel_db]
# We're going to define the task specification
task_spec {
# Which contains a container specification
container_spec {
# Which has a docker image set
#image = "${data.docker_registry_image.quassel.name}@${data.docker_registry_image.quassel.sha256_digest}"
image = docker_image.quassel.name
env = {
# And a bunch of environment variables as per the upstream documentation.
PUID = 1000
PGID = 1000
TZ = var.tz
RUN_OPTS = "--config-from-environment"
DB_BACKEND = "PostgreSQL"
DB_PGSQL_USERNAME = local.pg_username
DB_PGSQL_PASSWORD = local.pg_password
DB_PGSQL_HOSTNAME = local.pg_hostname
DB_PGSQL_PORT = local.pg_port_internal
AUTH_AUTHENTICATOR = "Database"
}
}
# Attach our task to the network we created earlier
networks_advanced {
name = docker_network.quassel.id
}
}
# Setting a converge config means that we will wait for the service to be up and running (and reporting it is healthy) before moving on.
converge_config {
delay = "5s" # Wait 5 seconds between checks
timeout = "2m" # Give up after 2 minutes
}
endpoint_spec {
# Configure our service to listen on a random port on the ingress network (which means any node in the swarm will redirect the traffic to (an instance of) this service)
ports {
target_port = 4242 # default quassel port on the container
published_port = local.quassel_port # Use the random port we generated earlier
protocol = "tcp"
publish_mode = "ingress" # Its that fwicked cool sweet awesome overlay network again, but this time ingress from the outside of the cluster
}
}
}

82
quassel/Dockerfile Normal file
View file

@ -0,0 +1,82 @@
FROM ghcr.io/linuxserver/baseimage-alpine:3.18 as build-stage
# build time arguements
ARG CXXFLAGS="\
-D_FORTIFY_SOURCE=2 \
-Wp,-D_GLIBCXX_ASSERTIONS \
-fstack-protector-strong \
-fPIE -pie -Wl,-z,noexecstack \
-Wl,-z,relro -Wl,-z,now"
ARG QUASSEL_RELEASE
# install build packages
RUN \
apk add --no-cache \
boost-dev \
build-base \
cmake \
dbus-dev \
icu-dev \
openssl-dev \
openldap-dev \
qt5-qtbase-dev \
qt5-qtscript-dev \
qt5-qtbase-postgresql \
qt5-qtbase-sqlite \
qca-dev \
zlib-dev
# fetch source
RUN \
mkdir -p \
/tmp/quassel-src/build && \
if [ -z ${QUASSEL_RELEASE+x} ]; then \
QUASSEL_RELEASE=$(curl -sX GET "https://api.github.com/repos/quassel/quassel/releases/latest" \
| jq -r .tag_name); \
fi && \
curl -o \
/tmp/quassel.tar.gz -L \
"https://github.com/quassel/quassel/archive/${QUASSEL_RELEASE}.tar.gz" && \
tar xf \
/tmp/quassel.tar.gz -C \
/tmp/quassel-src --strip-components=1
# build package
RUN \
cd /tmp/quassel-src/build && \
cmake \
-DCMAKE_BUILD_TYPE="Release" \
-DCMAKE_INSTALL_PREFIX=/usr \
-DWANT_CORE=ON \
-DWANT_MONO=OFF \
-DWANT_QTCLIENT=OFF \
-DWITH_KDE=OFF \
/tmp/quassel-src && \
make -j2 && \
make DESTDIR=/build/quassel install
FROM ghcr.io/linuxserver/baseimage-alpine:3.18
# set environment variables
ENV HOME /config
# install runtime packages
RUN \
apk add --no-cache \
icu-libs \
openssl \
qt5-qtbase \
qt5-qtbase-postgresql \
qt5-qtbase-sqlite \
qt5-qtscript \
libqca \
openldap
# copy artifacts build stage
COPY --from=build-stage /build/quassel/usr/ /usr/
# add local files
COPY root/ /
# ports and volumes
VOLUME /config
EXPOSE 4242

View file

@ -0,0 +1,13 @@
#!/usr/bin/with-contenv bash
# shellcheck shell=bash
# generate key
if [[ ! -f /config/quasselCert.pem ]]; then
openssl req -x509 -nodes -days 3650 \
-newkey rsa:4096 -keyout /config/quasselCert.pem -out /config/quasselCert.pem \
-subj "/CN=Quassel-core"
fi
# permissions
lsiown -R abc:abc \
/config

View file

@ -0,0 +1 @@
oneshot

View file

@ -0,0 +1 @@
/etc/s6-overlay/s6-rc.d/init-quassel-config/run

View file

@ -0,0 +1 @@
3

View file

@ -0,0 +1,7 @@
#!/usr/bin/with-contenv bash
# shellcheck shell=bash
exec \
s6-notifyoncheck -d -n 300 -w 1000 -c "nc -z localhost 4242" \
s6-setuidgid abc quasselcore \
--configdir /config ${RUN_OPTS}

View file

@ -0,0 +1 @@
longrun

12
terraform.tf Normal file
View file

@ -0,0 +1,12 @@
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~>3.0"
}
random = {
source = "hashicorp/random"
version = "~>3.3"
}
}
}

1
terraform.tfvars Normal file
View file

@ -0,0 +1 @@
docker_prefix = "example-deployable"