Scaffold Ansible deployment structure + Homarr playbook
This commit is contained in:
parent
3ac4720dd1
commit
4c337f6d08
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.retry
|
||||
*.log
|
||||
.vault_pass
|
||||
secrets.yml
|
||||
35
README.md
35
README.md
@ -1,2 +1,35 @@
|
||||
# semaphore
|
||||
# semaphore — SnS deployment playbooks
|
||||
|
||||
Ansible playbooks run by [Semaphore](http://192.168.122.99:3000) for deploying
|
||||
services across SnS hosts. Source of truth is this Gitea repo (`sam/semaphore`);
|
||||
Semaphore pulls from here.
|
||||
|
||||
## Layout
|
||||
```
|
||||
ansible.cfg # ansible defaults (inventory path, ssh opts)
|
||||
inventory/hosts.yml # target hosts (racknerd1/2/3), mesh IPs
|
||||
group_vars/all.yml # shared non-secret vars
|
||||
playbooks/
|
||||
ping.yml # connectivity check (run this first)
|
||||
deploy-homarr.yml # Homarr dashboard on racknerd1
|
||||
requirements.yml # ansible collections (community.docker)
|
||||
```
|
||||
|
||||
## Semaphore setup (one-time)
|
||||
1. **Key Store**: add the SSH private key Semaphore uses to reach target hosts.
|
||||
Its public half must be in each target's `~/.ssh/authorized_keys`.
|
||||
2. **Repository**: this repo — `git@gitea.snsnetlabs.com:sam/semaphore.git`
|
||||
(SSH deploy key) or HTTPS + Gitea token.
|
||||
3. **Inventory**: point at `inventory/hosts.yml`.
|
||||
4. **Environment/Variable Group**: set `homarr_secret_encryption_key`
|
||||
= `openssl rand -hex 32` (secret; do NOT commit).
|
||||
5. **Task Templates**:
|
||||
- `ping` -> playbook `playbooks/ping.yml`
|
||||
- `homarr` -> playbook `playbooks/deploy-homarr.yml`, limit `racknerd1`
|
||||
|
||||
## Run order
|
||||
Run `ping` first to confirm Semaphore can reach the target, then `homarr`.
|
||||
|
||||
## Notes
|
||||
- Targets are reached over the NetBird mesh (Semaphore LXC is on the mesh).
|
||||
- Secrets live in Semaphore's variable store, never in this repo.
|
||||
|
||||
10
ansible.cfg
Normal file
10
ansible.cfg
Normal file
@ -0,0 +1,10 @@
|
||||
[defaults]
|
||||
inventory = inventory/hosts.yml
|
||||
host_key_checking = False
|
||||
retry_files_enabled = False
|
||||
stdout_callback = yaml
|
||||
interpreter_python = auto_silent
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ServerAliveInterval=15
|
||||
2
group_vars/all.yml
Normal file
2
group_vars/all.yml
Normal file
@ -0,0 +1,2 @@
|
||||
# Shared vars. Secrets should come from Semaphore's variable/secret store, NOT committed here.
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
18
inventory/hosts.yml
Normal file
18
inventory/hosts.yml
Normal file
@ -0,0 +1,18 @@
|
||||
# Semaphore deployment inventory
|
||||
# Hosts reached over the NetBird mesh (Semaphore LXC .99 is on the mesh).
|
||||
all:
|
||||
children:
|
||||
vps:
|
||||
hosts:
|
||||
racknerd1:
|
||||
ansible_host: 100.96.198.46 # NetBird mesh IP (public: 172.245.72.108)
|
||||
ansible_user: root
|
||||
racknerd2:
|
||||
ansible_host: 100.96.217.250
|
||||
ansible_user: root
|
||||
racknerd3:
|
||||
ansible_host: 67.215.255.203
|
||||
ansible_user: root
|
||||
dashboards:
|
||||
hosts:
|
||||
racknerd1:
|
||||
76
playbooks/deploy-homarr.yml
Normal file
76
playbooks/deploy-homarr.yml
Normal file
@ -0,0 +1,76 @@
|
||||
---
|
||||
# Deploy Homarr dashboard via Docker Compose.
|
||||
# Usage (Semaphore Task Template): playbook = playbooks/deploy-homarr.yml, inventory = inventory/hosts.yml, limit = racknerd1
|
||||
#
|
||||
# REQUIRED extra var (set in Semaphore -> Variable Groups / Survey, NOT committed):
|
||||
# homarr_secret_encryption_key : 64-char hex (openssl rand -hex 32). Homarr v1 refuses to start without it.
|
||||
- name: Deploy Homarr
|
||||
hosts: racknerd1
|
||||
become: true
|
||||
vars:
|
||||
homarr_dir: /opt/homarr
|
||||
homarr_port: 7575
|
||||
homarr_image: ghcr.io/homarr-labs/homarr:latest
|
||||
# homarr_secret_encryption_key: "<injected by Semaphore>"
|
||||
tasks:
|
||||
- name: Ensure Docker is present
|
||||
ansible.builtin.command: docker version
|
||||
register: docker_check
|
||||
changed_when: false
|
||||
failed_when: docker_check.rc != 0
|
||||
|
||||
- name: Fail early if encryption key not provided
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- homarr_secret_encryption_key is defined
|
||||
- homarr_secret_encryption_key | length == 64
|
||||
fail_msg: >-
|
||||
homarr_secret_encryption_key must be a 64-char hex string.
|
||||
Generate with `openssl rand -hex 32` and set it as a Semaphore variable.
|
||||
|
||||
- name: Create Homarr directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ homarr_dir }}"
|
||||
- "{{ homarr_dir }}/appdata"
|
||||
|
||||
- name: Write docker-compose.yml
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ homarr_dir }}/docker-compose.yml"
|
||||
mode: "0644"
|
||||
content: |
|
||||
services:
|
||||
homarr:
|
||||
container_name: homarr
|
||||
image: {{ homarr_image }}
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- {{ homarr_dir }}/appdata:/appdata
|
||||
environment:
|
||||
- SECRET_ENCRYPTION_KEY={{ homarr_secret_encryption_key }}
|
||||
ports:
|
||||
- "{{ homarr_port }}:7575"
|
||||
|
||||
- name: Pull and start Homarr
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ homarr_dir }}"
|
||||
pull: always
|
||||
state: present
|
||||
register: compose_result
|
||||
|
||||
- name: Wait for Homarr to answer
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ homarr_port }}/"
|
||||
status_code: [200, 302, 307]
|
||||
register: health
|
||||
retries: 12
|
||||
delay: 5
|
||||
until: health.status is defined
|
||||
|
||||
- name: Report
|
||||
ansible.builtin.debug:
|
||||
msg: "Homarr is up on {{ inventory_hostname }}:{{ homarr_port }} (status {{ health.status | default('n/a') }})"
|
||||
11
playbooks/ping.yml
Normal file
11
playbooks/ping.yml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
# Quick connectivity + facts check for all/limited hosts.
|
||||
- name: Connectivity check
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: Ping over SSH
|
||||
ansible.builtin.ping:
|
||||
- name: Show basics
|
||||
ansible.builtin.raw: "echo $(hostname) reachable, docker=$(command -v docker >/dev/null && echo yes || echo no)"
|
||||
changed_when: false
|
||||
2
requirements.yml
Normal file
2
requirements.yml
Normal file
@ -0,0 +1,2 @@
|
||||
collections:
|
||||
- name: community.docker
|
||||
Loading…
Reference in New Issue
Block a user