Jeden projekt Packer, trzy szablony Proxmox

Jak jednym projektem Packera buduję szablony VM dla AlmaLinux, Ubuntu i Alpine na Proxmox.

Na początku zawsze wygląda to niewinnie.

Jedna maszyna testowa. Potem druga. Potem dochodzi inna dystrybucja. A na końcu masz:

  • kilka katalogów,
  • kilka „lekko” różnych konfiguracji,
  • provisioning kopiowany metodą copy–paste,
  • i zero pewności, czy te maszyny naprawdę są takie same.

Ten projekt powstał dokładnie z tego powodu — żeby przestać rzeźbić każdą maszynę osobno.


Co to w ogóle robi?

W skrócie:

projekt Packer → wiele szablonów VM na Proxmox

  • może budować AlmaLinux 10,
  • może budować Ubuntu,
  • może budować Alpine,

Jak to jest podzielone (bez bólu głowy)

Żeby nie robić jednego wielkiego potwora, projekt jest rozbity logicznie na repozytoria:

Każde repo wygląda bardzo podobnie — dzięki temu wiesz, gdzie czego szukać, niezależnie od dystrybucji.


Jak to działa – krok po kroku

1. Packer łączy się z Proxmox

Na starcie Packer:

  • łączy się z API Proxmox,
  • tworzy tymczasową maszynę,
  • podpina ISO instalacyjne.

Dane dostępowe nie są w kodzie — są pobierane z Vaulta.


2. Automatyczna instalacja systemu

Zamiast klikać instalator ręcznie:

  • AlmaLinux używa kickstarta,
  • Ubuntu będzie używać autoinstall,
  • Alpine używa setup-alpine.

Packer:

  • uruchamia mały serwer HTTP,
  • podaje instalatorowi adres pliku konfiguracyjnego,
  • instalacja leci sama, bez żadnych pytań.

Efekt? Zawsze ten sam system, zainstalowany w ten sam sposób.


3. Provisioning po instalacji

Gdy system już się uruchomi, Packer:

  • loguje się po SSH,
  • dodaje klucz publiczny,
  • ustawia sudo bez hasła,
  • uruchamia jeden wspólny skrypt provisioningowy.

Ten skrypt:

  • aktualizuje system,
  • instaluje qemu-guest-agent,
  • opcjonalnie instaluje rzeczy typu sssd / realmd,
  • włącza potrzebne usługi.

To jest jedno miejsce, w którym utrzymujesz „bazową konfigurację” VM.


4. Zapis jako template

Na końcu:

  • maszyna jest wyłączana,
  • zapisywana jako template w Proxmox,
  • gotowa do klonowania.

Bez ręcznego sprzątania. Bez „czy ja o czymś nie zapomniałem?”.


Gdzie są różnice między systemami?

I tu jest najważniejsza część.

Różnice nie są w logice, tylko w danych:

  • inny plik instalacyjny,
  • inny plik pkrvars,
  • czasem inne domyślne zasoby.

Cała reszta:

  • Vault,
  • sieć,
  • provisioning,
  • standardy bezpieczeństwa,

pozostaje taka sama.


Jak się tego używa?

Dla pojedynczego systemu:

packer init .
packer fmt -var-file=pkrvars/alma-10.hcl .
packer validate -var-file=pkrvars/alma-10.hcl .
packer build -var-file=pkrvars/alma-10.hcl .

Albo wszystko naraz:

bash scripts/packer_build.bash

Skrypt sam przechodzi po wszystkich profilach.


Co mi to realnie daje?

Najważniejsze rzeczy w praktyce:

  • koniec z ręcznym klikaniem instalatorów
  • koniec z różnymi „prawie takimi samymi” VM
  • jedno miejsce na zmiany
  • łatwa integracja z CI/CD
  • łatwe dodanie kolejnej dystrybucji

To nie jest projekt „na pokaz”. To jest narzędzie, które faktycznie upraszcza życie, gdy masz więcej niż jedną maszynę.

Automatyczne zarządzanie repozytoriami

IAC do zarządzania środowiskiem GitLab.

OpenTofu coraz częściej wykorzystywany jest nie tylko do budowy infrastruktury chmurowej, lecz także do zarządzania całym ekosystemem usług.
W repozytorium iac-gitlab przygotowałem modułowy zestaw definicji, który pozwala w deklaratywny sposób tworzyć i utrzymywać grupy oraz projekty w GitLabie.


Po co IaC dla GitLaba?

  • Automatyzacja: grupy i projekty tworzą się automatycznie na podstawie kodu.
  • Powtarzalność: każda zmiana jest opisana w repozytorium, a tofu plan pokazuje różnice.
  • Rozliczalność: pełna historia zmian wraz z informacją kto, gdzie i kiedy je zlecił.
  • Porządek: struktura katalogów odpowiada strukturze GitLaba, co ułatwia nawigację i odnalezienie definicji konkretnej grupy czy projektu.

Jak zbudowane jest repozytorium?

Struktura katalogów odzwierciedla hierarchię GitLaba:

iac-gitlab
├── data/                # listy do walidacji (typy projektów, avatary, etykiety)
├── pl.rachuna-net/      # główna grupa i jej podgrupy
│   ├── containers/      # definicje projektów kontenerowych
│   ├── cicd/            # ustawienia CI/CD
│   └── infrastructure/  # infrastruktura (np. opentofu)
└── _providers.tf        # konfiguracja providerów (m.in. GitLab)

Architektura rozwiązania

flowchart TB
 subgraph s1["opentofu module"]
        n2["gitlab-group"]
        n1("gitlab-project")
  end
    A["iac-gitlab"] --> n1
    A --> n2
    n2@{ shape: rounded}

Tworzenie grupy repozytoriów

Definicja grupy korzysta z modułu gitlab-group:

module "_containers" {
  source         = "git@gitlab.com:pl.rachuna-net/infrastructure/opentofu/modules/gitlab-group.git?ref=v1.0.0"
  name           = "containers"
  description    = "Repozytoria z obrazami kontenerowymi."
  parent_group   = local.parent_name
  visibility     = "public"
  default_branch = "main"
  avatar         = "containers"
}

module "containers" {
  source = "./containers/"
}

Pierwszy moduł tworzy grupę, a drugi ładuje definicje jej podprojektów. W pliku _locals.tf przechowuję wspólne parametry, aby uniknąć powielania konfiguracji.


Tworzenie projektu

Moduł gitlab-project umożliwia definiowanie parametrów projektu:

module "opentofu" {
  source        = "git@gitlab.com:pl.rachuna-net/infrastructure/opentofu/modules/gitlab-project.git?ref=v1.0.0"
  name          = "opentofu"
  description   = "Obraz Dockerowy z narzędziem opentofu."
  visibility    = "public"
  parent_group  = local.parent_name
  project_type  = "container"
  avatar        = "opentofu"
}

Jak pracować z iac-gitlab w praktyce?

  1. Dodaj definicję grupy lub projektu w odpowiednim katalogu.
  2. Wyślij Merge Request — pipeline wykona tylko plan
  3. Po mergu GitLab automatycznie tworzy lub aktualizuje grupy, projekty, etykiety, avatary oraz zmienne CI.

Co jeszcze znajduje się w repozytorium?

  • data/allowed_* — walidacja dozwolonych typów projektów i avatarów; spina IaC z politykami organizacji.
  • images/ — katalog z avatarami dla grup i projektów.

Szybki start

Automatyczne zarządzanie Vault za pomocą OpenTofu

IAC do zarządzania środowiskiem Vault.

Są takie elementy infrastruktury, które na początku wyglądają niewinnie. Ot, postawisz Vaulta, wrzucisz kilka sekretów, wystawisz parę certyfikatów i lecisz dalej. A potem przychodzi życie: kolejna usługa, kolejny zespół, kolejny pipeline, ktoś prosi o dostęp „tylko do tego jednego patha”, a Ty orientujesz się, że Vault zaczyna przypominać szufladę z kablami — daj się w to wciągnąć na chwilę i nagle nie wiesz, co do czego prowadzi.

Właśnie w tym miejscu wjeżdża iac-vault: repozytorium, które robi z Vaulta coś, co da się normalnie utrzymać. Wszystko jest opisane w IaC, wersjonowane w Git, odtwarzalne i gotowe pod automatyzację. Bez ręcznego klikania, bez „a kto to zmieniał?”, bez tajemnej wiedzy w głowie jednej osoby.

To jest wpis o tym, jak ogarniam środowisko Vaulta przy pomocy OpenTofu — od PKI i KV, po polityki i uwierzytelnianie — i dlaczego to podejście jest po prostu… wygodne.


Dlaczego w ogóle chciałem to mieć jako IaC?

Bo Vault jest świetny, ale ręczne utrzymanie Vaulta w rosnącym środowisku to proszenie się o problemy:

  • polityki „tymczasowe”, które zostają na zawsze,
  • sekrety tworzone ad-hoc w różnych mountach,
  • PKI, które żyje własnym życiem,
  • brak jasnej odpowiedzi na pytanie: „jak to odtworzyć od zera?”

Z iac-vault mam jasny układ:

  • Git jest źródłem prawdy,
  • zmiany przechodzą przez Code Review,
  • Rollout jest przewidywalny,
  • a Vault przestaje być czarną skrzynką.
  • jest rozliczalność i możliwości analizowania błędów na danych.

Co to właściwie jest iac-vault?

iac-vault to repozytorium, które pełni rolę orkiestratora konfiguracji Vault. Ono „składa” środowisko z czytelnych klocków:

  • pki – pełna warstwa certyfikatów (Root, Intermediate, leafy),
  • secrets – przestrzenie KV na sekrety (KV v2),
  • policies – polityki ACL (czytelne, modularne),
  • auth – metody logowania i integracje (userpass + AppRole),
  • plus providers.tf, variables.tf i reszta „kleju”.

W praktyce: repo mówi Vaultowi dokładnie, co ma istnieć — i możesz to odtworzyć na nowej instancji w identyczny sposób.

flowchart TB
 subgraph s1["opentofu module"]
        n2["vault-pki"]
  end
    A["iac-vault"] --> n3["auth"] & n4["pki"] & n5["secrets"] & n6["polices"]
    n4 --> n2

Struktura katalogów jest prosta i „czyta się sama”:

.
├── auth                     # mechanizmy autoryzacji (userpass, AppRole)
├── main.tf                  # główny plik wykonawczy OpenTofu
├── pki                      # konfiguracja PKI oparta o moduł vault-pki
├── policies                 # polityki ACL Vault
├── providers.tf             # definicje providerów
├── secrets                  # konfiguracja backendów KV
└── variables.tf             # zmienne globalne

PKI: to tutaj robi się naprawdę fajnie

Jeżeli masz więcej niż kilka usług, to PKI potrafi być największym źródłem bólu. Wiele osób kończy z:

  • jednym intermediate „do wszystkiego”,
  • certyfikatami porozrzucanymi po systemach,
  • rotacją robioną ręcznie,
  • i tym cudownym uczuciem stresu, kiedy cert się kończy „jutro” 😉

W moim podejściu PKI jest robione przez moduł 👉 vault-pki opentofu

I to jest turbo wygodne, bo moduł automatyzuje cały, zdroworozsądkowy model:

  • Root CA istnieje i jest używany wyłącznie do podpisywania intermediates,
  • intermediates są podzielone logicznie (np. infrastructure i apps),
  • leafy są generowane deklaratywnie z jednej mapy.

To na starcie wygląda jak „dodatkowa robota”, ale w praktyce daje ogromny spokój: PKI robi się przewidywalne i banalne do rozbudowy.

Jak wygląda hierarchia?

stateDiagram
  direction TB
  [*] --> s1
  s1 --> s2
  s1 --> s5
  s1:Root CA
  s2:Infrastructure Intermediate CA
  s5:Apps Intermediate CA

Ja trzymam prosty mental model:

  • infrastructure → Vault, Consul, GitLab, routery, load balancery, core infrastruktury,
  • apps → aplikacje, serwisy, fronty, API.

W OpenTofu to jest po prostu czytelna mapa:

intermediates = {
  infrastructure = {
    path        = "pki-infrastructure"
    common_name = "rachuna-net.pl Infrastructure Intermediate CA"
    role_name   = "infrastructure-default"
  }
  apps = {
    path        = "pki-apps"
    common_name = "rachuna-net.pl Apps Intermediate CA"
    role_name   = "apps-default"
  }
}

Leaf: certyfikaty jako lista życzeń

Najlepsze jest to, że wystawianie certyfikatów końcowych robi się trochę jak „zamawianie” usług: wpisujesz co chcesz mieć, a reszta dzieje się sama.

leaf_requests = {
  "infra-gitlab" = {
    backend_key = "infrastructure"
    common_name = "gitlab.rachuna-net.pl"
    alt_names   = ["registry.rachuna-net.pl"]
  }
}

I nagle certyfikaty przestają być „operacją”, a stają się zwykłym elementem konfiguracji. Dodajesz wpis → commit → apply → gotowe.

To podejście jest genialne szczególnie wtedy, kiedy usług jest dużo i ciągle dochodzą nowe.


Certyfikaty: skąd je biorę?

W zależności od use-case:

  • dla automatyzacji i narzędzi (Ansible/CI) — czytam z KV,
  • dla prostych skryptów albo debugowania — biorę bezpośrednio z PKI.

Przykład pobrania bezpośrednio:

RESP=$(curl \
  --header "X-Vault-Token: $VAULT_TOKEN" \
  --request POST \
  --data '{"common_name": "grafana.rachuna-net.pl"}' \
  https://vault.rachuna-net.pl/v1/pki-apps/issue/apps-default)

ACME: czyli „Vault, ale jak Let’s Encrypt”

To jest ten fragment, który sprawia, że całość zaczyna błyszczeć: moduł wspiera ACME, więc możesz podpiąć np. certbota1 i robić odnowienia bardziej „cloud-native” niż „przypominajka w kalendarzu”.

acme = {
  acme_enabled                = true
  acme_cluster_path           = "https://vault.rachuna-net.pl"
  allow_wildcard_certificates = true
  max_ttl                     = "168h"
}

Efekt? Certyfikaty przestają być tematem. Po prostu się robią i odnawiają.


KV: sekrety w końcu mają swoje miejsce

KV v2 to klasyk, ale różnica robi się wtedy, gdy backendy i ich przeznaczenie masz uporządkowane, nazwane i utrzymywane w IaC.

resource "vault_mount" "kv_gitlab" {
  path        = "kv-gitlab"
  type        = "kv-v2"
  description = "Storage for GitLab related secrets"
}

Dzięki temu:

  • łatwiej delegować dostępy,
  • łatwiej utrzymać porządek,
  • łatwiej przenieść środowisko.

Polityki: bezpieczeństwo bez bólu

Polityki to miejsce, gdzie zwykle kończy się „porządek” — bo łatwo dorzucić wyjątek, trudniej go potem wyczyścić.

W iac-vault polityki są modularne: dla KV osobno, dla PKI osobno, dla admina osobno. Do tego klasyczny podział na read/write/admin.

To brzmi prosto, ale działa fenomenalnie przy rosnącej liczbie zespołów i integracji.


Auth: ludzie logują się prosto, automaty logują się bezpiecznie

Dwie metody, które pokrywają 95% potrzeb:

  • userpass — dla ludzi (admin/dev),
  • AppRole — dla CI/CD i automatyzacji.

AppRole jest idealny do GitLaba, bo daje kontrolę nad TTL, rolami, politykami i dostępami, bez zostawiania wszędzie tokenów „na wieczność”.


Podsumowanie: dlaczego to jest fajne?

Bo Vault przestaje być „ustawiony kiedyś” i staje się „zarządzany”.

Z iac-vault dostajesz:

  • ✅ odtwarzalność środowiska,
  • ✅ porządek w PKI (root -> intermediates -> leafy),
  • ✅ certyfikaty jako deklaracja, nie jako operacja,
  • ✅ opcję ACME i automatycznego odnawiania,
  • ✅ przewidywalne, modularne polityki,
  • ✅ przejrzysty model auth dla ludzi i automatyzacji,
  • ✅ repozytorium, które jest dokumentacją samą w sobie.

I najważniejsze: zyskujesz spokój. A to w infrastrukturze jest walutą premium.

Automatyczne zarządzanie LXC w Proxmox za pomocą OpenTofu

IAC do zarządzania środowiskiem Proxmox.

Nie ma nic bardziej satysfakcjonującego niż kliknięcie tofu apply i zobaczenie, jak Proxmox sam pobiera template LXC, tworzy kontenery i konfiguruje dostęp. Poniżej opisuję, jak wygląda ten proces w moim repozytorium iac-proxmox i na co warto zwrócić uwagę, jeśli chcesz to odwzorować u siebie.

Dlaczego LXC do IaC w Proxmox

  • Kontenery startują w sekundy i zużywają minimalne zasoby w porównaniu z pełnymi VM.
  • Wspólny kernel i brak emulacji sprzętu sprawiają, że świetnie nadają się do mikroserwisów, proxy, workerów CI czy środowisk testowych.
  • Dzięki OpenTofu cały cykl życia kontenera (tworzenie, aktualizacja, usuwanie) jest wersjonowany w Git i odtwarzalny na innym hoście.

Jak zorganizowałem repozytorium iac-proxmox

Wydzielam dwa kluczowe katalogi:

  • containers_templates/ – zasoby odpowiedzialne za pobieranie obrazów LXC do magazynu Proxmoxa.
  • machines/ – pojedyncze pliki ctXXXXX.tf, każdy opisuje wygląd kontenera.

Krok 1. Pobieranie template: jeden zasób, zero ręcznych klików

Szablon Ubuntu 24.04 LTS pobieram wprost z repozytorium Proxmoxa:

resource "proxmox_virtual_environment_download_file" "ubuntu24-10" {
  content_type       = "vztmpl"
  datastore_id       = local.storage_name
  node_name          = local.default_node
  file_name          = "ubuntu-24.04.tar.zst"
  url                = "http://download.proxmox.com/images/system/ubuntu-24.04-standard_24.04-2_amd64.tar.zst"
  checksum           = "4030982618eeae70854e8f9711adbd09"
  checksum_algorithm = "md5"
}

Kluczowe jest ustawienie checksum i checksum_algorithm – mam pewność, że obraz nie został uszkodzony, a plan w razie różnic przerwie się na weryfikacji.

Krok 2. Tworzenie kontenera – minimalny moduł

Każdy kontener to osobny moduł z jednoznacznym ct_id:

module "ct01011" {
  source = "git@gitlab.rachuna-net.pl:pl.rachuna-net/infrastructure/opentofu/modules/proxmox-container.git?ref=v1.0.0"

  hostname      = "ct01011.rachuna-net.pl"
  description   = "example-service"
  node_name     = "pve-s1"
  ct_id         = 1011
  pool_id       = "web-proxy"
  start_on_boot = true
  tags          = ["example", "ubuntu"]

  cpu_cores = 2
  memory = { dedicated = 2048, swap = 1024 }
  disk   = { storage_name = "local-lvm", disk_size = 32 }

  operating_system = {
    template_file = "local:vztmpl/ubuntu-24.04.tar.zst"
    type          = "ubuntu"
  }

  user_account = {
    username       = "techuser"
    password       = "change-me"
    public_ssh_key = "ssh-ed25519 AAAA..."
  }
}

Najważniejsze parametry, które zawsze ustawiam:

Parametr Co robi
hostname Pełna nazwa DNS kontenera
node_name Węzeł Proxmoxa, na którym zostanie utworzony
ct_id Stałe ID kontenera – ułatwia śledzenie i logging
pool_id Przynależność do puli logicznej (np. web, db, proxy)
start_on_boot Autostart po restarcie noda
cpu_cores/memory/disk Twarda kontrola zasobów hosta
operating_system.template_file Ścieżka do wcześniej pobranego template
user_account Automatycznie zakładane konto i klucz SSH

Krok 3. Bezpieczeństwo: sekrety z Vault zamiast w pliku

Hasła i klucze trzymam poza repozytorium. Wartości w user_account mogą pochodzić z Vault:

user_account = {
  username       = data.vault_kv_secret_v2.ct.data["username"]
  password       = data.vault_kv_secret_v2.ct.data["password"]
  public_ssh_key = data.vault_kv_secret_v2.ct.data["ssh_key"]
}

Zyskuję rotację haseł, brak sekretów w Git i spójny audyt dostępu.

Automatyczne zarządzanie RouterOS za pomocą OpenTofu

IAC do zarządzania konfiguracją Mikrotika

W tym wpisie dzielę się praktycznym podejściem do Infrastructure as Code dla routerów MikroTik (RouterOS). Projekt iac-mikrotik to zestaw modułów OpenTofu (API RouterOS), który pozwala w pełni deklaratywnie zarządzać routerem bez klikania w GUI. Całość jest wersjonowana w Git, dzięki czemu łatwo zrobić review, audyt i rollback.

Co automatyzuje iac-mikrotik

  • Interfaces: Ethernet, Bonding (LACP), Bridge, VLAN – każdy typ w osobnym module i katalogu.
  • Serwery DHCP: sieci, pule, instancje serwerów, statyczne leases, a całość spójnie spięta z DNS.
  • Serwer DNS: centralny resolver z cache, serwerami upstream i kontrolą bezpieczeństwa.
  • Rekordy DNS: statyczne wpisy A/AAAA/CNAME/TXT/MX/NS w mapie dns_records – dla krytycznych usług.

Struktura repozytorium

router.rachuna-net.pl/
├── interfaces/         # ethernet, bonding, bridge, vlan (*.tf)
├── dhcp-servers/       # każdy serwer DHCP w osobnym pliku
├── dns-server/         # main.tf z konfiguracją resolvera
└── dns-records/        # mapa statycznych rekordów DNS

Moduły OpenTofu w użyciu

  • routeros-ethernet, routeros-bonding, routeros-bridge, routeros-vlan – warstwa L2/L3 interfejsów.
  • routeros-dhcp-server – sieci, pule, serwery DHCP oraz statyczne leases.
  • routeros-dns – resolver, cache, upstream oraz statyczne dns_records.

Kolejność wdrażania (dlaczego ma znaczenie)

  1. Interfaces: najpierw Ethernet, potem Bonding, Bridge, VLAN – zapewnia zależności L2/L3.
  2. Serwery DHCP: działają na gotowych interfejsach L3, z jasno zdefiniowanymi pulami.
  3. Serwer DNS: centralny resolver + cache, wskazywany przez DHCP.
  4. Rekordy DNS: statyczne wpisy dla usług krytycznych (routery, klastry, proxy).

Przykładowy moduł VLAN

module "vlan_vms_internal" {
  source     = "git@gitlab.rachuna-net.pl:pl.rachuna-net/infrastructure/opentofu/modules/routeros-vlan.git?ref=v1.0.0"
  name       = "vlan-vms-int"
  interface  = "bond-storage"
  vlan_id    = 20
  arp        = "enabled"
  mtu        = 1500
  comment    = "VLAN VMs internal"
}

Dlaczego tak? Każdy element (np. VLAN) żyje w osobnym pliku – można go przetestować, zreviewować i wycofać bez dotykania reszty konfiguracji.

Integracja DHCP ↔ DNS

  • Lease z hostname automatycznie tworzy rekordy A i PTR w resolverze RouterOS.
  • Statyczne rekordy w dns_records zabezpieczają krytyczne hosty (routery, proxmoxy, load balancery).
  • DHCP zawsze wskazuje lokalny DNS, co eliminuje problemy z nazwami, TLS i monitoringiem.

Zasady projektowe, które się sprawdziły

  • Zero konfiguracji w GUI – tylko IaC.
  • Jeden moduł = jedna funkcja; bez „kombajnów” w jednym pliku.
  • Krytyczne hosty zawsze mają statyczne rekordy DNS; DHCP dla urządzeń dynamicznych.
  • Wszystko przez Git: review, audyt, rollback.

Efekt

Projekt iac-mikrotik upraszcza życie przy RouterOS: wgrywam moduły OpenTofu, a router buduje się sam – z kompletnymi interfejsami, DHCP, DNS i statycznymi rekordami. Jeśli coś pójdzie nie tak, rollback to kwestia jednego commita.

Kontener deweloperski z z pluginem vscode devcontainers

Przygotowanie własnego obrazu

W pracy nad wieloma projektami infrastrukturalnymi (OpenTofu, Ansible, Vault, GitLab CI) szybko pojawia się ten sam problem: jak zapewnić powtarzalne, gotowe do pracy środowisko developerskie, bez ręcznego konfigurowania systemu na każdym laptopie lub VM.

Rozwiązaniem, które sprawdza się w praktyce, jest Dev Container w Visual Studio Code, uzupełniony o zewnętrzny katalog /userfiles z konfiguracją użytkownika oraz automatyczny bootstrap wykonywany przy starcie kontenera.

W tym wpisie pokażę, jak zbudować takie rozwiązanie krok po kroku.


Założenia architektoniczne

Celem jest środowisko, które:

  • działa identycznie na każdym hoście,

  • nie wymaga kopiowania kluczy SSH ani dotfiles do repozytorium,

  • automatycznie:

    • instaluje zaufane CA (Vault PKI),
    • konfiguruje .zshrc, Git, SSH,
    • przygotowuje użytkownika do pracy od razu po otwarciu repozytorium.

Kluczowe elementy:

  • Dev Container oparty o własny obraz Dockera,
  • bind-mount katalogu /userfiles,
  • postCreateCommand jako punkt wejścia do bootstrapu.

Struktura /userfiles

Na hoście (lub w VM) utrzymywany jest katalog:

/userfiles
├── bootstrap.bash
├── configure_user.bash
├── .zshrc
├── .gitconfig
├── .gitignore_global
└── .ssh/
    ├── id_ed25519
    ├── id_ed25519.pub
    └── known_hosts

To jest źródło prawdy dla konfiguracji użytkownika – niezależne od repozytoriów i kontenerów.


Konfiguracja devcontainer.json

Minimalna i stabilna konfiguracja wygląda następująco:

{
  "name": "Devcontainer",
  "image": "registry.rachuna-net.pl/pl.rachuna-net/containers/devcontainer:1.0.0",
  "remoteUser": "ubuntu",
  "postCreateCommand": "bash -lc '/userfiles/bootstrap.bash'",
  "mounts": [
    "source=/userfiles,target=/userfiles,type=bind"
  ]
}

Najważniejsze decyzje:

  • /userfilesln -s .devcontainer/userfiles w repozytorium kodu,
  • postCreateCommand uruchamiany jawnie przez bash -lc,
  • brak logiki konfiguracyjnej w samym repozytorium.

Bootstrap – jeden punkt wejścia

Plik bootstrap.bash odpowiada za konfigurację systemową.

#!/usr/bin/env bash
set -euo pipefail

echo "===> Run bootstrap script"

echo "===> Install Vault CA certificates"
sudo curl -ks https://vault.localhost/v1/pki-root/ca/pem -o /usr/local/share/ca-certificates/ca.crt

sudo update-ca-certificates

bash -lc '/userfiles/configure_user.bash'

Dlaczego tak?

  • brak interaktywnych shelli (zsh, bash bez -c),

  • jednoznaczny podział:

    • bootstrap → system,
    • configure_user → użytkownik.

Konfiguracja użytkownika

configure_user.bash zajmuje się wyłącznie $HOME.

#!/usr/bin/env bash
set -euo pipefail

echo "===> Configure user profile"

cp /userfiles/.zshrc "$HOME/.zshrc" || true
cp /userfiles/.gitconfig "$HOME/.gitconfig" || true
cp /userfiles/.gitignore_global "$HOME/.gitignore_global" || true

mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
cp -a /userfiles/.ssh/. "$HOME/.ssh/" || true
chmod 600 "$HOME/.ssh"/* 2>/dev/null || true

Efekt:

  • klucze SSH dostępne w kontenerze,
  • Git skonfigurowany globalnie,
  • brak duplikowania sekretów w repozytorium.

Efekt końcowy

Po otwarciu repozytorium w VS Code:

  • kontener buduje się automatycznie,
  • CA z Vaulta są zaufane,
  • Git i SSH działają od razu,
  • developer może od razu pracować, bez setupu lokalnego.

To podejście sprawdza się szczególnie dobrze w zespołach DevOps / Platform Engineering, gdzie:

  • środowiska muszą być powtarzalne,
  • bezpieczeństwo (PKI, klucze) ma znaczenie,
  • repozytoria nie powinny zawierać konfiguracji użytkownika.

Przygotowanie środowiska deweloperskiego z Vagrant

Przygotowanie własnego obrazu Ubuntu 24.10 dla Vagranta

W tym wpisie pokazuję, jak przygotować własny obraz Ubuntu 24.10 dla Vagranta, opublikować go w rejestrze Vagrant Cloud (lub przez alternatywny hosting) oraz jak uruchomić środowisko deweloperskie przy pomocy vagrant up.

Cały proces ilustruje poniższy diagram:

---
config:
  theme: neo
  layout: dagre
  look: neo
---
flowchart LR
 subgraph s1["Przygotowanie obrazu"]
        n5["Przygotowanie maszyny<br>wirtualnej na virtualbox"]
        n6@{ label: "<span style=\"padding-left:\">Utworzenie box<br></span><span style=\"padding-left:\">(vagrant package)</span>" }
        n7@{ label: "Wysłanie do<br style=\"--tw-scale-x:\">Vagrant box Registry" }
  end
 subgraph s2["Użycie obrazu (vagrant up)"]
        n8["vagrant up"]
        n9["run bootstrap.bash"]
        n11["bash scripts"]
  end
    n5 --> n6
    n6 --> n7
    s1 --> s2
    n8 --> n9
    s2 --> n10("Ready to use")
    n9 --> n11
    n5@{ shape: rect}
    n6@{ shape: rect}
    n7@{ shape: rect}
    n9@{ shape: lean-r}
    n11@{ shape: procs}
    click n5 "#przygotowanie-maszyny-wirtualnej-na-virtualboxie"
    click n6 "#utworzenie-box"
    click n7 "#wys%c5%82anie-do-vagrant-box-registry"
    click n8 "#użycie-obrazu-vagrant-up"
    click n9 "#ręczne-uruchomienie-bootstrapbash"

Przygotowanie maszyny wirtualnej na virtualboxie

Przygotowałem maszynę wirtualną na virtualbox ubuntu 24-10

z użytkownikiem vagrant hasłem vagrant

echo "vagrant ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/vagrant
sudo apt-get update
sudo apt-get dist-upgrade -y
sudo apt install -y openssh-server curl wget sudo
sudo apt install -y build-essential dkms linux-headers-$(uname -r)
mkdir -p /home/vagrant/.ssh
curl -fsSL https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant.pub -o /home/vagrant/.ssh/authorized_keys
chown -R vagrant:vagrant /home/vagrant/.ssh
chmod 700 /home/vagrant/.ssh
chmod 600 /home/vagrant/.ssh/authorized_keys
sudo apt-get autoremove -y
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
sudo timedatectl set-timezone UTC
sudo locale-gen en_US.UTF-8
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab

Utworzenie box

vagrant package --base ubuntu-24-10 --output ubuntu-24.10.box
m@nbo-001-mr  ~  vagrant package --base ubuntu-24-10 --output ubuntu-24.10.box
==> ubuntu-24-10: Exporting VM...
==> ubuntu-24-10: Compressing package to: /home/maciej-rachuna/ubuntu-24.10.box
m@nbo-001-mr ~ ls -l |grep ubuntu-24
-rw-rw-r--  1 m m 1871476745 lip 23 08:49 ubuntu-24.10.box

Wysłanie do Vagrant box Registry

U mnie z jakiegoś powodu opcja nie działa, więc wrzuciłem obraz przez UI

vagrant cloud auth login

Użycie obrazu (vagrant up)

Pobranie repozytorium z vagrantem

git clone https://gitlab.com/pl.rachuna-net/tools/vagrant.git

Uruchomienie maszyny developerskiej

chmod +x start_vm.bash
./start_vm.bash
m@nbo-001-mr /repo/pl.rachuna-net/tools/vagrant (main) vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'rachuna-net.pl/ubuntu-24-10' version '1.0.0' is up to date...
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.
==> default: Running provisioner: shell...
    default: Running: /tmp/vagrant-shell20250723-96391-b06anr.bash
    default: ===> 🚀 Run bootstrap script
    default: ===> 📁 Copy userfiles to /root
    default: ===> 🚀 Running script: /vagrant/provisioning/upgrade-ubuntu.bash
    default: ===> Upgrade system packages and distribution
    default: ===> 🚀 Running script: /vagrant/provisioning/install-packages.bash
    default: ===> Install packages
    default: ===> 🚀 Running script: /vagrant/provisioning/bootstrap-certs.bash
    default: ===> Pobieranie CA certyfikatu dla rachuna-net.pl
    default: ===> 🚀 Running script: /vagrant/provisioning/install-hashicorp.bash
    default: ===> 🚀 Running script: /vagrant/provisioning/install-zsh.bash
    default: ===> 🚀 Running script: /vagrant/provisioning/install-hugo.bash

Dostęp do maszyny wirtualnej

  • SSH jest dostępne na porcie 2222 na hoście, przekierowanym do portu 22 w VM.
  • Włączone jest przekazywanie agenta SSH oraz X11.
  • Połączenie można nawiązać poleceniem:
    ssh -p 2222 vagrant@localhost
    

Edytuj plik ~/.ssh/config, dodając wpis:

Host dev-station
   HostName 127.0.0.1
   Port 2222
   User vagrant
   IdentityFile /repo/pl.rachuna-net/tools/vagrant/.vagrant/machines/default/virtualbox/private_key
   ForwardX11 yes

Dzięki temu możesz łatwo łączyć się z maszyną wirtualną za pomocą polecenia:

ssh dev-station

Ręczne uruchomienie bootstrap.bash

vagrant@dev-station:/vagrant$ cd /vagrant/
vagrant@dev-station:/vagrant$ chmod +x bootstrap.bash 
vagrant@dev-station:/vagrant$ ./bootstrap.bash 
===> 🚀 Run bootstrap script
===> 📁 Copy userfiles to /home/vagrant
===> 🚀 Running script: /vagrant/provisioning/upgrade-ubuntu.bash
===> Upgrade system packages and distribution
===> 🚀 Running script: /vagrant/provisioning/install-packages.bash
===> Install packages
===> 🚀 Running script: /vagrant/provisioning/bootstrap-certs.bash
===> Pobieranie CA certyfikatu dla rachuna-net.pl
===> 🚀 Running script: /vagrant/provisioning/install-hashicorp.bash
===> 🚀 Running script: /vagrant/provisioning/install-zsh.bash
===> 🚀 Running script: /vagrant/provisioning/install-hugo.bash

Przygotowanie środowiska LAB

Przygotowanie środowiska LAB

W ostatnim czasie postanowiłem zrealizować długo odkładaną wizję – stworzyć własne laboratorium IT w domu, które pozwoli mi ćwiczyć i testować technologie DevOps w praktyce. Pomysł powstał z potrzeby eksperymentowania w środowisku zbliżonym do produkcyjnego, ale przy minimalnych kosztach i maksymalnej elastyczności.

Wybór sprzętu: Mini PC jako fundament laboratorium

Do budowy mojego LAB-a wybrałem trzy kompaktowe Mini PC. Powody są proste: niski pobór energii, cicha praca i wystarczająca moc obliczeniowa do uruchamiania wielu maszyn wirtualnych. Każda z maszyn będzie pełnić rolę węzła wirtualizacyjnego, na którym zainstaluję Proxmox VE – sprawdzone narzędzie do zarządzania wirtualizacją i kontenerami LXC/VM. Dzięki temu będę miał pełną kontrolę nad maszynami wirtualnymi, snapshotami i klastrami HA, co jest kluczowe przy testach infrastruktury.

Storage na Synology

Kluczowym elementem mojego LAB-a będzie centralny storage. Postawiłem na NAS Synology, który zapewnia nie tylko przestrzeń dyskową, ale również funkcje zaawansowanego zarządzania danymi, backupu i dostępności przez sieć. Wszystkie Mini PC będą korzystać z Synology jako magazynu dla maszyn wirtualnych i obrazów systemów, co pozwoli mi w prosty sposób tworzyć i przenosić VM-y pomiędzy węzłami. Dodatkowo, Synology umożliwia implementację iSCSI czy NFS, co idealnie integruje się z Proxmoxem.

Sieć zarządzana przez Mikrotika

Żeby móc testować złożone scenariusze sieciowe i DevOpsowe, potrzebowałem elastycznego rozwiązania do zarządzania siecią. W tym celu wybrałem router MikroTik. Dzięki niemu mogę tworzyć VLAN-y, testować routing, firewall, VPN-y i QoS. To pozwoli mi symulować środowiska produkcyjne, gdzie różne usługi i zespoły korzystają z odseparowanych segmentów sieci. MikroTik zapewnia też świetny interfejs do monitorowania ruchu i diagnostyki problemów w sieci.

Plany rozwoju: Kubernetes i DevOps

Budowa laboratorium to dopiero początek. Moim celem jest stopniowe wdrażanie technologii kontenerowych i DevOpsowych. W najbliższym czasie planuję postawić Kubernetes na trzech węzłach Mini PC, co pozwoli mi testować skalowanie aplikacji, zarządzanie konfiguracją, automatyczne deploymenty i monitoring usług.

Dalej chcę zintegrować narzędzia typowe dla DevOps, takie jak:

  • CI/CD – Jenkins, GitLab CI/CD lub ArgoCD,
  • Monitoring i logowanie – Prometheus, Grafana, Loki,
  • Kontenery i obrazowanie – Docker i Harbor,
  • Zarządzanie konfiguracją – Ansible, Terraform, Helm.

Dzięki temu będę mógł ćwiczyć całe cykle DevOpsowe – od budowy aplikacji, przez automatyzację wdrożeń, aż po monitoring i optymalizację wydajności.

Zalety własnego LAB-a

Posiadanie własnego środowiska testowego daje nieocenione możliwości:

  1. Eksperymentowanie bez ryzyka – mogę testować konfiguracje produkcyjne bez obawy o wpływ na rzeczywiste systemy.
  2. Praktyczna nauka – od wirtualizacji, przez sieci, po Kubernetes i CI/CD.
  3. Elastyczność – możliwość szybkiego tworzenia i usuwania maszyn wirtualnych, wdrażania usług i testowania scenariuszy awaryjnych.
  4. Nauka DevOps od A do Z – zrozumienie, jak wszystkie warstwy infrastruktury współpracują ze sobą.

Podsumowanie

Budowa domowego laboratorium DevOps na Mini PC, z Proxmoxem, Synology jako centralnym storage’em i siecią zarządzaną przez MikroTika, to nie tylko świetna zabawa, ale też praktyczna inwestycja w rozwój umiejętności IT. W przyszłości planuję rozbudować LAB o Kubernetes, narzędzia CI/CD i monitoring, co pozwoli mi w pełni odtworzyć środowisko zbliżone do rzeczywistego centrum danych. To idealne miejsce do nauki, testowania i eksperymentowania z nowoczesnymi technologiami DevOps.