Implement reproducible S1 handoff contracts
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
This commit is contained in:
parent
c8cb1c8edf
commit
b93af8cc78
44 changed files with 2035 additions and 342 deletions
|
|
@ -1,6 +1,7 @@
|
|||
# 🚀 Provisioning Servers with railiance-infra
|
||||
|
||||
This guide explains **where you declare servers**, **how Terraform uses that declaration**, and **how to provision** (and later destroy) machines on Hetzner.
|
||||
This guide explains how adopted hosts and provider-managed Hetzner hosts share
|
||||
an inventory without sharing lifecycle behavior.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -11,13 +12,16 @@ This script will:
|
|||
|
||||
1. Add the new host entry to `inventory/servers.yaml`
|
||||
2. Decrypt your Hetzner API token with SOPS
|
||||
3. Run Terraform (`init/plan/apply`) to provision the server
|
||||
4. Print the IPv4 address and a ready-to-use SSH command
|
||||
3. Run Terraform init and plan
|
||||
4. Apply only when the operator supplies the explicit `--apply` flag after
|
||||
reviewing the plan
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core
|
||||
# after review and the required approval:
|
||||
scripts/hcloud_new_server.sh core-01 --type cpx11 --region nbg1 --role core --apply
|
||||
```
|
||||
|
||||
This will create a small cpx11 instance in the Nuremberg (nbg1) region, tagged with the role core.
|
||||
|
|
@ -33,17 +37,24 @@ ssh admin@<printed-ip>
|
|||
|
||||
## 1) Where you define servers
|
||||
|
||||
All desired hosts live in **`inventory/servers.yaml`**. Each entry is a simple YAML object with the required attributes:
|
||||
All host identities live in **`inventory/servers.yaml`**. Run
|
||||
`make validate-inventory` before Terraform or Ansible. Adopted hosts have a
|
||||
provider address but no `provisioning` block. Provider-managed Hetzner hosts
|
||||
have no committed IP and use this shape:
|
||||
|
||||
```yaml
|
||||
servers:
|
||||
- name: core-01
|
||||
labels: [core, wireguard, git]
|
||||
role: "core"
|
||||
region: "nbg1" # Hetzner location (e.g., nbg1, fsn1, hel1)
|
||||
type: "cpx21" # Hetzner server type/flavor
|
||||
image: "ubuntu-24.04" # OS image slug
|
||||
ssh_user: "admin" # bootstrap user (cloud-init creates this)
|
||||
provider: hetzner
|
||||
lifecycle_mode: provider-managed
|
||||
ssh_user: admin
|
||||
baseline_profile: ufw-managed
|
||||
provisioning:
|
||||
server_type: cpx21
|
||||
region: nbg1
|
||||
image: ubuntu-24.04
|
||||
role: core
|
||||
labels: [core, wireguard, git]
|
||||
```
|
||||
|
||||
> Tip: Keep **names stable**. Renaming a server in this file makes Terraform think the old one was destroyed and a new one should be created.
|
||||
|
|
@ -73,7 +84,8 @@ scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web
|
|||
## 3) How Terraform uses your declaration
|
||||
|
||||
The module at `terraform/hetzner/`:
|
||||
- Reads `inventory/servers.yaml` (`for_each` over `servers`)
|
||||
- Selects only records with `provider: hetzner` and
|
||||
`lifecycle_mode: provider-managed`
|
||||
- Registers your SSH key from `keys/admin_ssh.pub`
|
||||
- Injects **cloud-init** that sets up the `admin` user and basic hardening
|
||||
- Creates/updates/destroys servers to match the YAML
|
||||
|
|
@ -84,17 +96,19 @@ Outputs include a map of server names → IPv4 addresses.
|
|||
|
||||
## 4) Provision (create/update)
|
||||
|
||||
Make sure your Hetzner API token is present and **SOPS-decryptable** in `inventory/group_vars/secrets.sops.yaml` under `ops.hcloud_token`.
|
||||
The Hetzner API token is **SOPS-decryptable** at
|
||||
`secrets/hetzner-token.yaml`, field `hetzner.token`. It is decrypted only into
|
||||
the invoking process environment.
|
||||
|
||||
Then run either:
|
||||
```bash
|
||||
# plan and apply in separate steps
|
||||
make tf-plan
|
||||
make tf-apply
|
||||
APPROVE_TF_APPLY=YES make tf-apply
|
||||
```
|
||||
or the end-to-end convenience:
|
||||
```bash
|
||||
make apply # terraform apply + ansible bootstrap
|
||||
APPROVE_TF_APPLY=YES make apply # terraform apply + Ansible bootstrap
|
||||
```
|
||||
|
||||
If you used the one-shot script:
|
||||
|
|
@ -124,14 +138,16 @@ make ansible-bootstrap
|
|||
|
||||
To remove all servers managed by this repo:
|
||||
```bash
|
||||
make tf-destroy
|
||||
APPROVE_TF_DESTROY=DESTROY-MANAGED-HETZNER make tf-destroy
|
||||
```
|
||||
|
||||
To remove just one server, delete its entry from `inventory/servers.yaml`, commit, then:
|
||||
To remove one **provider-managed Hetzner** server, delete its entry from
|
||||
`inventory/servers.yaml`, review the plan, obtain the required approval, then:
|
||||
```bash
|
||||
make tf-apply
|
||||
APPROVE_TF_APPLY=YES make tf-apply
|
||||
```
|
||||
Terraform will destroy the missing server and leave others intact.
|
||||
Terraform will destroy the missing managed Hetzner server and leave others
|
||||
intact. Adopted Host Europe records are never Terraform resources.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -139,6 +155,7 @@ Terraform will destroy the missing server and leave others intact.
|
|||
|
||||
- **Idempotent:** You can run `make apply` repeatedly; Terraform converges infra, Ansible converges config.
|
||||
- **SSH keys:** Ensure `keys/admin_ssh.pub` exists before provisioning.
|
||||
- **Secret token:** The Hetzner API token must be in `inventory/group_vars/secrets.sops.yaml` (encrypted with SOPS).
|
||||
- **Secret token:** The Hetzner API token is `secrets/hetzner-token.yaml`, field
|
||||
`hetzner.token`, and must remain SOPS-encrypted.
|
||||
- **Cloud-init delay:** Allow ~30–60s after creation for first-boot tasks before first SSH.
|
||||
- **Labels & role:** `labels` are freeform tags; `role` can drive Ansible plays as you grow.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue