Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
161 lines
4.5 KiB
Markdown
161 lines
4.5 KiB
Markdown
# 🚀 Provisioning Servers with railiance-infra
|
||
|
||
This guide explains how adopted hosts and provider-managed Hetzner hosts share
|
||
an inventory without sharing lifecycle behavior.
|
||
|
||
---
|
||
|
||
## 🚀 Fast Path: Using the Helper Script
|
||
|
||
Instead of manually editing `inventory/servers.yaml` and running `make tf-apply`, you can use the convenience script [`scripts/hcloud_new_server.sh`](../scripts/hcloud_new_server.sh).
|
||
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 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.
|
||
You can then connect directly:
|
||
|
||
```bash
|
||
ssh admin@<printed-ip>
|
||
```
|
||
|
||
👉 The script is optional. You can always manage servers by editing inventory/servers.yaml and running make tf-apply instead.
|
||
|
||
|
||
|
||
## 1) Where you define servers
|
||
|
||
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
|
||
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.
|
||
|
||
---
|
||
|
||
## 2) Two ways to add a server
|
||
|
||
### A) Edit YAML by hand (simple)
|
||
Open `inventory/servers.yaml`, add a new entry, save, commit.
|
||
|
||
### B) Use the helper script (safe & quick)
|
||
```bash
|
||
# requires scripts/new_host.py
|
||
make new-host NAME=web-01 TYPE=cpx21 REGION=nbg1 ROLE=web
|
||
# or directly:
|
||
python3 scripts/new_host.py --name web-01 --type cpx21 --region nbg1 --role web
|
||
```
|
||
|
||
You can also do **add + provision in one step**:
|
||
```bash
|
||
scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web
|
||
```
|
||
|
||
---
|
||
|
||
## 3) How Terraform uses your declaration
|
||
|
||
The module at `terraform/hetzner/`:
|
||
- 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
|
||
|
||
Outputs include a map of server names → IPv4 addresses.
|
||
|
||
---
|
||
|
||
## 4) Provision (create/update)
|
||
|
||
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
|
||
APPROVE_TF_APPLY=YES make tf-apply
|
||
```
|
||
or the end-to-end convenience:
|
||
```bash
|
||
APPROVE_TF_APPLY=YES make apply # terraform apply + Ansible bootstrap
|
||
```
|
||
|
||
If you used the one-shot script:
|
||
```bash
|
||
scripts/hcloud_new_server.sh web-01 --type cpx21 --region nbg1 --role web
|
||
```
|
||
|
||
Terraform will print the new servers’ IPv4 addresses at the end.
|
||
|
||
---
|
||
|
||
## 5) Connect & converge
|
||
|
||
Connect via SSH:
|
||
```bash
|
||
ssh admin@<server-ip>
|
||
```
|
||
|
||
Run Ansible base bootstrap (if not using `make apply`):
|
||
```bash
|
||
make ansible-bootstrap
|
||
```
|
||
|
||
---
|
||
|
||
## 6) Destroy (tear down)
|
||
|
||
To remove all servers managed by this repo:
|
||
```bash
|
||
APPROVE_TF_DESTROY=DESTROY-MANAGED-HETZNER make tf-destroy
|
||
```
|
||
|
||
To remove one **provider-managed Hetzner** server, delete its entry from
|
||
`inventory/servers.yaml`, review the plan, obtain the required approval, then:
|
||
```bash
|
||
APPROVE_TF_APPLY=YES make tf-apply
|
||
```
|
||
Terraform will destroy the missing managed Hetzner server and leave others
|
||
intact. Adopted Host Europe records are never Terraform resources.
|
||
|
||
---
|
||
|
||
## 7) Notes & conventions
|
||
|
||
- **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 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.
|