diff --git a/README.md b/README.md index d0af5e054d0b36c35129b17999cee273d2b72c3c..31552c192b5f6a1c2032f8b606062d54c55a41f7 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,67 @@ hepto -iface eth0 -name myfull -info > cluster-info.yaml helm install hepto ./helm -f cluster-info.yaml ``` +## Deploying a cluster on many nodes using Ansible + +This repository provides an ansible role for deploying hepto on a node. Start with an +inventory file listing your nodes, and providing some variables, for instance: + +``` +--- +nodes: + hosts: + # Each host gets an entry + riri: + # Override the host IP if the node name does not resolve + ansible_host: "2a01:dead:beef::1" + # Provide an explicit node IP address (-ip) + node_ip: "2a01:dead:beef::101/64" + fifi: + ansible_host: "2a01:dead:beef::2" + loulou: + ansible_host: "2a01:dead:beef::3" + # Default role is node, explicitely set the master role here + node_role: master + vars: + # Copy the static node ip of any stable node (usually the master, but any node + # will do) + cluster_anchor: "2a01:dead:beef::101" + # This must be 64 hex randomly generated + cluster_key: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + # You may specify any option for all nodes, here a network gateway + node_gw: "fe80::1" +``` + +Then run Ansible: + +``` +ansible-playbook -i inventory.yaml ansible/deploy.yaml +``` + +Or if you wish to use cloud provisioning for deploying the nodes in the first place +(currently supporting Hetzner only): + +``` +--- +all: + vars: + nodes: + riri: + fifi: + loulou: + node_role: master + cluster_anchor: loulou + cluster_key: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + hcloud_token: YourHCloudToken + hcloud_ssh_key: yourkey@host +``` + +Then run Ansible: + +``` +ansible-playbook -i inventory.yaml ansible/cloud.yaml +``` + ## Development Hepto is being developped as part of an ongoing effort to provide decent diff --git a/ansible/cloud.yaml b/ansible/cloud.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f6f0d03cc81e5ac33f3eb96196b2f4675f3aceb9 --- /dev/null +++ b/ansible/cloud.yaml @@ -0,0 +1,7 @@ +--- +- hosts: localhost + roles: + - hetzner +- hosts: nodes + roles: + - hepto diff --git a/ansible/deploy.yaml b/ansible/deploy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..866b1d3a51ac2b3cda07c08c3064824ee3024617 --- /dev/null +++ b/ansible/deploy.yaml @@ -0,0 +1,4 @@ +--- +- hosts: all + roles: + - hepto diff --git a/ansible/roles/hepto/defaults/main.yaml b/ansible/roles/hepto/defaults/main.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0fe4639aa910fdb540a54f13ee946cd40e83278f --- /dev/null +++ b/ansible/roles/hepto/defaults/main.yaml @@ -0,0 +1,15 @@ +# Hepto install +version: 78680 +hepto_url: "https://forge.tedomum.net/acides/hepto/-/jobs/{{ version }}/artifacts/raw/hepto" +hepto_bin: /usr/local/bin/hepto.{{ version }} + +# Hepto general config +systemd_dir: /etc/systemd/system +storage_dir: /var/lib +cluster_name: hepto +cluster_anchor: "::1" +node_name: "{{ inventory_hostname }}" +node_iface: eth0 +node_role: node +config_file: "/etc/{{ cluster_name }}/{{ node_name }}" + diff --git a/ansible/roles/hepto/tasks/main.yaml b/ansible/roles/hepto/tasks/main.yaml new file mode 100644 index 0000000000000000000000000000000000000000..274fba62caa8eefe7b4a1dc0acd030a328abe914 --- /dev/null +++ b/ansible/roles/hepto/tasks/main.yaml @@ -0,0 +1,31 @@ +--- +- name: Create required directories + file: + path: "{{ item }}" + state: directory + with_items: + - "/etc/{{ cluster_name }}" + - "/usr/local/bin" +- name: Download hepto binary for amd64 + get_url: + url: "{{ hepto_url }}" + dest: "{{ hepto_bin }}" + owner: root + group: root + mode: 755 +- name: Install hepto service file + template: + src: service.j2 + dest: "{{ systemd_dir }}/hepto-{{ node_name }}.service" +- name: Install hepto config file + template: + src: config.j2 + dest: "{{ config_file }}" +- name: Enable hepto service + systemd: + name: "hepto-{{ node_name }}" + daemon_reload: yes + state: restarted + enabled: yes + + diff --git a/ansible/roles/hepto/templates/config.j2 b/ansible/roles/hepto/templates/config.j2 new file mode 100644 index 0000000000000000000000000000000000000000..4a44eefc940af613c32ab8abd052c6daa1945aef --- /dev/null +++ b/ansible/roles/hepto/templates/config.j2 @@ -0,0 +1,15 @@ +HEPTO_CLUSTER={{ cluster_name }} +HEPTO_KEY={{ cluster_key }} +HEPTO_IFACE={{ node_iface }} +HEPTO_ROLE={{ node_role }} +{% if cluster_anchor in hostvars %} +HEPTO_ANCHOR={{ hostvars[cluster_anchor]['node_ip'] | ansible.utils.ipaddr('address') }} +{% else %} +HEPTO_ANCHOR={{ cluster_anchor }} +{% endif %} +{% if node_ip is defined %} +HEPTO_IP={{ node_ip }} +{% endif %} +{% if node_gw is defined %} +HEPTO_GW={{ node_gw }} +{% endif %} diff --git a/ansible/roles/hepto/templates/service.j2 b/ansible/roles/hepto/templates/service.j2 new file mode 100644 index 0000000000000000000000000000000000000000..f11ab0eb59da958682e3291d3748abb72657847e --- /dev/null +++ b/ansible/roles/hepto/templates/service.j2 @@ -0,0 +1,17 @@ +[Unit] +Description=hepto node {{ node_name }} +Documentation=https://acides.org +Wants=network-online.target + +[Install] +WantedBy=multi-user.target + +[Service] +EnvironmentFile={{ config_file }} +Delegate=yes +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity +TasksMax=infinity +Type=exec +ExecStart={{ hepto_bin }} -name {{ node_name }} \ No newline at end of file diff --git a/ansible/roles/hetzner/defaults/main.yaml b/ansible/roles/hetzner/defaults/main.yaml new file mode 100644 index 0000000000000000000000000000000000000000..50c2ea72101811b0639ed2722cc85682ed8408b5 --- /dev/null +++ b/ansible/roles/hetzner/defaults/main.yaml @@ -0,0 +1,4 @@ +server_type: cx11 +server_image: debian-12 +server_location: eu-central + diff --git a/ansible/roles/hetzner/tasks/main.yaml b/ansible/roles/hetzner/tasks/main.yaml new file mode 100644 index 0000000000000000000000000000000000000000..959a3c98cca84f704269044d48ec7916ec74a48f --- /dev/null +++ b/ansible/roles/hetzner/tasks/main.yaml @@ -0,0 +1,39 @@ +- name: "Create nodes" + hcloud_server: + api_token: "{{ hcloud_token }}" + name: "node-{{ item.key }}" + server_type: "{{ server_type }}" + image: "{{ server_image }}" + location: "{{ server_location }}" + ssh_keys: + - "{{ hcloud_ssh_key }}" + enable_ipv6: true + enable_ipv4: false + state: started + register: servers + with_dict: "{{ nodes }}" + +- name: Wait for nodes to be ready + ansible.builtin.wait_for: + port: 22 + host: "{{ item.hcloud_server.ipv6 | split('/') | first }}1" + delay: 2 + with_items: "{{ servers.results }}" + +# This is required as an intermediary step to compute the node +# configuration to be added to inventory +- name: "Prepare node configs" + set_fact: + name: "{{ item.item.key }}" + groups: nodes + ansible_host: "{{ item.hcloud_server.ipv6 | ansible.utils.ipaddr('net') | ansible.utils.ipaddr('1') | ansible.utils.ipaddr('address') }}" + ansible_user: root + node_gw: "fe80::1" + node_ip: "{{ item.hcloud_server.ipv6 | ansible.utils.ipaddr('net') | ansible.utils.ipaddr('2') }}" + ansible_ssh_extra_args: "-o StrictHostKeyChecking=no" + register: configs + with_items: "{{ servers.results }}" + +- name: "Add nodes to inventory" + add_host: "{{ item.ansible_facts | combine(nodes[item.ansible_facts.name]) }}" + with_items: "{{ configs.results }}"