Init commit
This commit is contained in:
parent
8d7f97bf16
commit
542d3af8da
|
@ -0,0 +1,2 @@
|
||||||
|
venv
|
||||||
|
.ansible
|
|
@ -1,3 +1,4 @@
|
||||||
# ---> Ansible
|
# ---> Ansible
|
||||||
*.retry
|
*.retry
|
||||||
|
.ansible
|
||||||
|
venv
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: .*
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- age1lzythn62c4yug8w2wskckpgyjyja6rreyvgmwl9hj4mjvm0tvq6sl68d4z
|
|
@ -0,0 +1,27 @@
|
||||||
|
FROM python:3.12.2-slim-bullseye AS compile-image
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y --no-install-recommends build-essential gcc libffi-dev git vim ssh
|
||||||
|
RUN mkdir ~/.ssh
|
||||||
|
COPY ./resources/known_hosts ~/.ssh/known_hosts
|
||||||
|
RUN python -m venv /opt/venv
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
COPY requirements.yml .
|
||||||
|
RUN pip install hcloud ansible
|
||||||
|
RUN ansible-galaxy install -r ./requirements.yml --force
|
||||||
|
|
||||||
|
FROM ghcr.io/allanger/dumb-downloader as dudo
|
||||||
|
ARG SOPS_VERSION=v3.8.1
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
RUN mkdir -p /out
|
||||||
|
RUN dudo -l "https://github.com/getsops/sops/releases/download/{{ version }}/sops-{{ version }}.{{ os }}.{{ arch }}" -d /out/sops -p $SOPS_VERSION
|
||||||
|
RUN chmod +x /out/sops
|
||||||
|
|
||||||
|
FROM python:3.12.2-slim-bullseye AS build-image
|
||||||
|
COPY --from=compile-image /opt/venv /opt/venv
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install age -y
|
||||||
|
COPY . /src
|
||||||
|
COPY --from=dudo /out/sops /usr/bin/sops
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
CMD ['ansible']
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
CUSTOMER ?= softplayer
|
||||||
|
ENV ?= default
|
||||||
|
|
||||||
|
venv-create:
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate && \
|
||||||
|
export ANSIBLE_COLLECTIONS_PATH=./.ansible/collections && \
|
||||||
|
export ANSIBLE_ROLES_PATH=./.ansible/roles && \
|
||||||
|
python3 -m pip install -r ./requirements.txt && \
|
||||||
|
python3 -m ansible galaxy install -r ./requirements.yml --force
|
||||||
|
|
||||||
|
bootstrap:
|
||||||
|
source venv/bin/activate && \
|
||||||
|
export ANSIBLE_HOST_KEY_CHECKING=false && \
|
||||||
|
export ANSIBLE_COLLECTIONS_PATH=./.ansible/collections && \
|
||||||
|
export ANSIBLE_ROLES_PATH=./.ansible/roles && \
|
||||||
|
export SP_CUSTOMER=softplayer && \
|
||||||
|
export SP_ENV=dev && \
|
||||||
|
export SP_STATE=present && \
|
||||||
|
python3 -m ansible playbook ./playbooks/other/ssh-key-gen/playbook.yml && \
|
||||||
|
python3 -m ansible playbook ./playbooks/providers/hetzner/playbook.yml && \
|
||||||
|
export ANSIBLE_INVENTORY=/tmp/outputs/inventory.yaml && \
|
||||||
|
export ANSIBE_PRIVATE_KEY_FILE=/tmp/outputs/ssh_key && \
|
||||||
|
sleep 8 && \
|
||||||
|
python3 -m ansible playbook ./playbooks/systems/system-bootstrap/playbook.yml && \
|
||||||
|
python3 -m ansible playbook ./playbooks/systems/k3s-bootstrap/playbook.yml && \
|
||||||
|
python3 -m ansible playbook ./playbooks/other/k8s-create-user/playbook.yml
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
source venv/bin/activate && \
|
||||||
|
export ANSIBLE_HOST_KEY_CHECKING=false && \
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections && \
|
||||||
|
export ANSIBLE_ROLES_PATH=./.ansible/roles && \
|
||||||
|
export SP_CUSTOMER=softplayer && \
|
||||||
|
export SP_ENV=dev && \
|
||||||
|
export SP_STATE=absent && \
|
||||||
|
python3 -m ansible playbook ./playbooks/providers/hetzner/playbook.yml
|
15
README.md
15
README.md
|
@ -1,2 +1,17 @@
|
||||||
# softplayer-ansible
|
# softplayer-ansible
|
||||||
|
|
||||||
|
## Hetzner
|
||||||
|
|
||||||
|
There must be 3 kinds of playbooks:
|
||||||
|
- Bootstrap
|
||||||
|
- Maintenance
|
||||||
|
- Cleanup
|
||||||
|
|
||||||
|
Bootstrap playbooks should only be executed when a new environment is created, for example, it should
|
||||||
|
|
||||||
|
1. Prepare the cloud infrastructure
|
||||||
|
2. Install setup nodes
|
||||||
|
3. Bootstrap a k3s cluster
|
||||||
|
4. Create an admin k8s config
|
||||||
|
|
||||||
|
Cleanup should simply remove the whole cloud infrastructure
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
for i in $(yq '.kubernetes.hosts | keys' inventory.yaml -o tsv); do ssh $i; done
|
|
@ -0,0 +1,21 @@
|
||||||
|
api_token: ENC[AES256_GCM,data:espaqpkBv6yn/Jsz6Eiv/xp0NJIiv871AioqeXbZ4epBk9FUsBLSDEIbbak7hN8EL3wLkLDVNZmPF1G8b1UQBg==,iv:CWL6uX1lRz1hisYYWu5K/btWJyIn4OfNpBuylHaY5UI=,tag:GWcqBqNjrwLDL7RfChvM+g==,type:str]
|
||||||
|
sops:
|
||||||
|
kms: []
|
||||||
|
gcp_kms: []
|
||||||
|
azure_kv: []
|
||||||
|
hc_vault: []
|
||||||
|
age:
|
||||||
|
- recipient: age1lzythn62c4yug8w2wskckpgyjyja6rreyvgmwl9hj4mjvm0tvq6sl68d4z
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpcDNMeGtWeC9PdUFzMmlj
|
||||||
|
MUxFekZXVkVtREVSVFVnTFJoQzBJN05Bb2c0CnVaUmEwWlk0Z21KbHp5QmxBQnlB
|
||||||
|
aUxGUjJXNXVXRXMrVFArMDRXOVJFRmsKLS0tICt2SlFtT1o2QTFVYkZoeHpFU3d4
|
||||||
|
U0FzT1h2V1ZUSVoxcndVL2tlYnNpZDQKrLmIImEyY/2DNsX8xzthSNRL9/BGxB82
|
||||||
|
eksaJS+98LD7byPoxtBlbqqbxvnj5qr8D7G3wWe0eIfHzj/a1MtKLQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2024-03-12T13:34:49Z"
|
||||||
|
mac: ENC[AES256_GCM,data:t3OtDE8hH4hiqfE0CemCm0rQ4psLeMnMcqFtz1qki5gybmAxxFIiF+Y/9NuRZk2h8YPLyLAafWVfUBQ2vrcL4BkfexrSS3ouomibbfK94C5FZ7XIhl0K0grHKQa8oTvL3ErACPbnsKCa59WhGxmyJPi3MbZ9cWnMNRiv/BdYb6Y=,iv:o0cGK/WSwaA7EfkxofJEez3fwt0kkGhJrZR5Ls2sJAs=,tag:I/LFakV5N6SQqdXY7Cuauw==,type:str]
|
||||||
|
pgp: []
|
||||||
|
unencrypted_suffix: _unencrypted
|
||||||
|
version: 3.8.1
|
|
@ -0,0 +1,13 @@
|
||||||
|
common_packages:
|
||||||
|
- name: python3-cryptography
|
||||||
|
state: present
|
||||||
|
- name: iscsi-initiator-utils
|
||||||
|
state: present
|
||||||
|
- name: nfs-utils
|
||||||
|
state: present
|
||||||
|
- name: cifs-utils
|
||||||
|
state: present
|
||||||
|
common_services:
|
||||||
|
- name: iscsid
|
||||||
|
state: started
|
||||||
|
enabled: true
|
|
@ -0,0 +1,13 @@
|
||||||
|
common_sysctl:
|
||||||
|
- name: fs.inotify.max_user_watches
|
||||||
|
value: 2099999999
|
||||||
|
set: true
|
||||||
|
state: present
|
||||||
|
- name: fs.inotify.max_user_instances
|
||||||
|
value: 2099999999
|
||||||
|
set: true
|
||||||
|
state: present
|
||||||
|
- name: fs.inotify.max_queued_events
|
||||||
|
value: 2099999999
|
||||||
|
set: true
|
||||||
|
state: present
|
|
@ -0,0 +1,11 @@
|
||||||
|
user_groups:
|
||||||
|
- name: wheel
|
||||||
|
state: present
|
||||||
|
sudo: true
|
||||||
|
users:
|
||||||
|
- name: admin
|
||||||
|
groups:
|
||||||
|
- wheel
|
||||||
|
ssh_keys:
|
||||||
|
- key: "{{ lookup('file', '/tmp/outputs/ssh_key.pub') }}"
|
||||||
|
state: present
|
|
@ -0,0 +1,6 @@
|
||||||
|
# ---> Ansible
|
||||||
|
*.retry
|
||||||
|
venv
|
||||||
|
.ansible
|
||||||
|
|
||||||
|
config
|
|
@ -0,0 +1,34 @@
|
||||||
|
venv: venv-create venv-install-deps venv-install-reqs
|
||||||
|
run: venv venv-run
|
||||||
|
check: venv venv-check
|
||||||
|
|
||||||
|
venv-create:
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
venv-install-deps:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m pip install hcloud ansible ansible-lint
|
||||||
|
|
||||||
|
venv-install-reqs:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy role install -r ./requirements.yml -p ./.ansible/roles
|
||||||
|
|
||||||
|
venv-install-reqs-force:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy role install -r ./requirements.yml --force -p ./.ansible/roles
|
||||||
|
|
||||||
|
venv-check:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ROLES_PATH=./.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml --check -i ./inventory.yaml
|
||||||
|
|
||||||
|
venv-run:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ANSIBLE_ROLES_PATH=$(PWD)/.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i /tmp/outputs/inventory.yaml
|
||||||
|
|
||||||
|
venv-lint:
|
||||||
|
venv/bin/ansible-lint playbook.yml
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# k8s-ansible-playbooks
|
||||||
|
|
||||||
|
|
||||||
|
## Get a config
|
||||||
|
|
||||||
|
To get a k8s config, just run the script:
|
||||||
|
```bash
|
||||||
|
./bin/get_configs $USERNAME
|
||||||
|
```
|
||||||
|
|
||||||
|
It will create a config file in the repo root dir
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat config
|
||||||
|
```
|
|
@ -0,0 +1,2 @@
|
||||||
|
[defaults]
|
||||||
|
vars_plugins_enabled = host_group_vars,community.sops.sops
|
|
@ -0,0 +1,11 @@
|
||||||
|
- hosts: k8s
|
||||||
|
name: Create k8s users
|
||||||
|
remote_user: admin
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- ansible-create-k8s-user
|
||||||
|
vars:
|
||||||
|
kubectl:
|
||||||
|
version: v1.27.4
|
||||||
|
arch: amd64
|
||||||
|
working_dir: /home/admin/.k8s
|
|
@ -0,0 +1,5 @@
|
||||||
|
# ---> Ansible
|
||||||
|
*.retry
|
||||||
|
|
||||||
|
# ---> Python
|
||||||
|
venv
|
|
@ -0,0 +1,5 @@
|
||||||
|
creation_rules:
|
||||||
|
- path_regex: .*
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- age1lzythn62c4yug8w2wskckpgyjyja6rreyvgmwl9hj4mjvm0tvq6sl68d4z
|
|
@ -0,0 +1,17 @@
|
||||||
|
Permission is hereby granted, without written agreement and without
|
||||||
|
license or royalty fees, to use, copy, modify, and distribute this
|
||||||
|
software and its documentation for any purpose, provided that the
|
||||||
|
above copyright notice and the following two paragraphs appear in
|
||||||
|
all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||||
|
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||||
|
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
@ -0,0 +1,38 @@
|
||||||
|
venv: venv-create venv-install-deps
|
||||||
|
run: venv venv-run
|
||||||
|
check: venv venv-check
|
||||||
|
|
||||||
|
|
||||||
|
venv-create:
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
venv-install-deps:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m pip install ansible ansible-lint
|
||||||
|
|
||||||
|
venv-check:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ROLES_PATH=./.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml --check -i ./inventory.yaml
|
||||||
|
|
||||||
|
venv-run:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export SP_CUSTOMER=allanger && \
|
||||||
|
export SP_ENV=default && \
|
||||||
|
export SP_STATE=present && \
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ANSIBLE_ROLES_PATH=$(PWD)/.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i ./inventory.yaml
|
||||||
|
|
||||||
|
venv-run-cleanup:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export SP_CUSTOMER=allanger && \
|
||||||
|
export SP_ENV=default && \
|
||||||
|
export SP_STATE=absent && \
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ANSIBLE_ROLES_PATH=$(PWD)/.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i ./inventory.yaml
|
||||||
|
venv-lint:
|
||||||
|
venv/bin/ansible-lint playbook.yml
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# hetzner-ansilbe-playbook
|
||||||
|
|
||||||
|
Repo for managing the Hetzner infrastructure
|
||||||
|
|
||||||
|
## Removing stuff
|
||||||
|
|
||||||
|
Since the state of the config is the ansible code itself, you can't just remove something from the code and expect that it's going to be removed from Hetzner.
|
||||||
|
Each entity has a variable `state`, to remove anything, you need to set state to `absent` and run the playbook. And only after that you can remove it from the code.
|
||||||
|
|
||||||
|
Also, please, create a git commit, where on object with the `absent` state is tracked.
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
After running the role you'll see three variables being logged in the last step:
|
||||||
|
- Server public IP -> It should be used for `ssh` connection to the server
|
||||||
|
- Load balancer public IP -> It should be used by k8s as the load balancer IP
|
||||||
|
- Volume device name -> It's the name of device that should be mounted to Longhorn
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Resize the volume
|
||||||
|
Don't forget to resize the filesystem, it should be done manually
|
||||||
|
- https://docs.hetzner.com/cloud/volumes/faq/
|
|
@ -0,0 +1,20 @@
|
||||||
|
- name: Generate a SSH key
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
vars:
|
||||||
|
output_dir: /tmp/outputs
|
||||||
|
tasks:
|
||||||
|
- name: Create the output directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ output_dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
register: workdir
|
||||||
|
- name: Generate an OpenSSH keypair with the default values (4096 bits, rsa)
|
||||||
|
community.crypto.openssh_keypair:
|
||||||
|
path: "{{ output_dir}}/ssh_key"
|
||||||
|
type: ed25519
|
||||||
|
|
||||||
|
- name: Setting host facts using complex arguments
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
pub_key: "{{ lookup('file', '/tmp/outputs/ssh_key.pub') }}"
|
|
@ -0,0 +1,17 @@
|
||||||
|
Permission is hereby granted, without written agreement and without
|
||||||
|
license or royalty fees, to use, copy, modify, and distribute this
|
||||||
|
software and its documentation for any purpose, provided that the
|
||||||
|
above copyright notice and the following two paragraphs appear in
|
||||||
|
all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||||
|
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||||
|
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
@ -0,0 +1,41 @@
|
||||||
|
venv: venv-create venv-install-deps venv-install-reqs
|
||||||
|
run: venv venv-run
|
||||||
|
check: venv venv-check
|
||||||
|
|
||||||
|
|
||||||
|
venv-create:
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
venv-install-deps:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m pip install hcloud ansible ansible-lint
|
||||||
|
|
||||||
|
venv-install-reqs:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy role install -r ./requirements.yml -p ./.ansible/roles
|
||||||
|
|
||||||
|
venv-install-reqs-force:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy role install -r ./requirements.yml --force -p ./.ansible/roles
|
||||||
|
|
||||||
|
venv-check:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ROLES_PATH=./.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml --check -i ./inventory.yaml
|
||||||
|
|
||||||
|
venv-run:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ANSIBLE_ROLES_PATH=$(PWD)/.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i ./inventory.yaml
|
||||||
|
|
||||||
|
venv-run-cleanup:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export SP_STATE=absent && \
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ANSIBLE_ROLES_PATH=$(PWD)/.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i ./inventory.yaml
|
||||||
|
venv-lint:
|
||||||
|
venv/bin/ansible-lint playbook.yml
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# hetzner-ansilbe-playbook
|
||||||
|
|
||||||
|
Repo for managing the Hetzner infrastructure
|
||||||
|
|
||||||
|
## Removing stuff
|
||||||
|
|
||||||
|
Since the state of the config is the ansible code itself, you can't just remove something from the code and expect that it's going to be removed from Hetzner.
|
||||||
|
Each entity has a variable `state`, to remove anything, you need to set state to `absent` and run the playbook. And only after that you can remove it from the code.
|
||||||
|
|
||||||
|
Also, please, create a git commit, where on object with the `absent` state is tracked.
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
After running the role you'll see three variables being logged in the last step:
|
||||||
|
- Server public IP -> It should be used for `ssh` connection to the server
|
||||||
|
- Load balancer public IP -> It should be used by k8s as the load balancer IP
|
||||||
|
- Volume device name -> It's the name of device that should be mounted to Longhorn
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Resize the volume
|
||||||
|
Don't forget to resize the filesystem, it should be done manually
|
||||||
|
- https://docs.hetzner.com/cloud/volumes/faq/
|
|
@ -0,0 +1,88 @@
|
||||||
|
- name: Hetzner playbook
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
vars:
|
||||||
|
output_dir: /tmp/outputs
|
||||||
|
customer: "{{ lookup('ansible.builtin.env', 'SP_CUSTOMER') }}"
|
||||||
|
env: "{{ lookup('ansible.builtin.env', 'SP_ENV') }}"
|
||||||
|
state: "{{ lookup('ansible.builtin.env', 'SP_STATE') }}"
|
||||||
|
tags:
|
||||||
|
- cloud
|
||||||
|
tasks:
|
||||||
|
- name: Load encrypted credentials
|
||||||
|
community.sops.load_vars:
|
||||||
|
file: ../../../group_vars/hetzner.sops.yaml
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# -- Prepare SSH keys
|
||||||
|
# --------------------------------------------------------
|
||||||
|
- name: Add SSH keys to the hetzner subscription
|
||||||
|
hetzner.hcloud.hcloud_ssh_key:
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
name: "{{ customer }}-{{ env }}"
|
||||||
|
public_key: "{{ lookup('file', '/tmp/outputs/ssh_key.pub') }}"
|
||||||
|
state: "{{ state }}"
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# -- Prepare private networks
|
||||||
|
# --------------------------------------------------------
|
||||||
|
- name: Prepare private networks
|
||||||
|
hetzner.hcloud.hcloud_network:
|
||||||
|
name: "{{ customer }}-{{ env }}"
|
||||||
|
ip_range: 10.0.0.0/16
|
||||||
|
state: "{{ state }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# -- Prepare subnets
|
||||||
|
# --------------------------------------------------------
|
||||||
|
- name: Prepare subnets
|
||||||
|
when: state == "present"
|
||||||
|
hetzner.hcloud.hcloud_subnetwork:
|
||||||
|
network: "{{ customer }}-{{ env }}"
|
||||||
|
ip_range: 10.0.0.0/16
|
||||||
|
state: "{{ state }}"
|
||||||
|
network_zone: eu-central
|
||||||
|
type: cloud
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# -- Create servers
|
||||||
|
# --------------------------------------------------------
|
||||||
|
- name: Create server
|
||||||
|
hetzner.hcloud.hcloud_server:
|
||||||
|
name: "{{ customer }}-{{ env }}"
|
||||||
|
server_type: cx21
|
||||||
|
image: rocky-9
|
||||||
|
state: "{{ state }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
ssh_keys:
|
||||||
|
- "{{ customer }}-{{ env }}"
|
||||||
|
firewalls:
|
||||||
|
- admin
|
||||||
|
private_networks:
|
||||||
|
- "{{ customer }}-{{ env }}"
|
||||||
|
location: hel1
|
||||||
|
register: server_data
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# -- Create floating IPs
|
||||||
|
# --------------------------------------------------------
|
||||||
|
- name: Create a floating IP
|
||||||
|
hetzner.hcloud.hcloud_floating_ip:
|
||||||
|
name: "{{ customer }}-{{ env }}"
|
||||||
|
server: "{{ customer }}-{{ env }}"
|
||||||
|
type: ipv4
|
||||||
|
state: "{{ state }}"
|
||||||
|
api_token: "{{ api_token }}"
|
||||||
|
register: floating_ips_data
|
||||||
|
|
||||||
|
- name: Display important output data
|
||||||
|
block:
|
||||||
|
- name: Display info about servers public IPs
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "{{ server_data }}"
|
||||||
|
when: 'server_data'
|
||||||
|
- name: Generate new inventory
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/inventory.yaml.j2
|
||||||
|
dest: "{{ output_dir }}/inventory.yaml"
|
||||||
|
vars:
|
||||||
|
ipv4_address: "{{ server_data.hcloud_server.ipv4_address }}"
|
||||||
|
cluster_name: "{{ customer }}-{{ env }}"
|
|
@ -0,0 +1,46 @@
|
||||||
|
servers:
|
||||||
|
hosts:
|
||||||
|
{{ ipv4_address }}:
|
||||||
|
vars:
|
||||||
|
ansible_ssh_private_key_file: /tmp/outputs/ssh_key
|
||||||
|
k3s_cluster:
|
||||||
|
children:
|
||||||
|
server:
|
||||||
|
hosts:
|
||||||
|
{{ ipv4_address }}:
|
||||||
|
vars:
|
||||||
|
ansible_port: 22
|
||||||
|
k3s_version: v1.26.9+k3s1
|
||||||
|
token: "mytoken" # Use ansible vault if you want to keep it secret
|
||||||
|
api_endpoint: "{{ ipv4_address }}"
|
||||||
|
extra_server_args: |-
|
||||||
|
--write-kubeconfig-mode=644 \
|
||||||
|
--node-name="k3s-1" \
|
||||||
|
--tls-san="{{ ipv4_address }}" \
|
||||||
|
--disable-cloud-controller \
|
||||||
|
--disable-helm-controller \
|
||||||
|
--disable metrics-server \
|
||||||
|
--disable local-storage \
|
||||||
|
--disable traefik \
|
||||||
|
--cluster-cidr="10.244.0.0/16" \
|
||||||
|
--flannel-backend none \
|
||||||
|
--disable coredns
|
||||||
|
extra_agent_args: ""
|
||||||
|
ansible_ssh_private_key_file: /tmp/outputs/ssh_key
|
||||||
|
k8s:
|
||||||
|
hosts:
|
||||||
|
{{ ipv4_address }}:
|
||||||
|
name: {{ cluster_name }}
|
||||||
|
kubernetes_server: https://{{ ipv4_address }}:6443
|
||||||
|
k8s_config_path: /etc/rancher/k3s/k3s.yaml
|
||||||
|
k8s_cert_path: /var/lib/rancher/k3s/server/tls
|
||||||
|
k8s_cert_crt_file: client-ca.crt
|
||||||
|
k8s_cert_key_file: client-ca.key
|
||||||
|
users:
|
||||||
|
- cluster: default
|
||||||
|
host_user: admin
|
||||||
|
username: admin
|
||||||
|
download_config: true
|
||||||
|
vars:
|
||||||
|
ansible_ssh_private_key_file: /tmp/outputs/ssh_key
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
disk_device_name: {{ disk_device_name }}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# ---> Ansible
|
||||||
|
*.retry
|
||||||
|
|
||||||
|
# ---> Python
|
||||||
|
venv
|
|
@ -0,0 +1,17 @@
|
||||||
|
Permission is hereby granted, without written agreement and without
|
||||||
|
license or royalty fees, to use, copy, modify, and distribute this
|
||||||
|
software and its documentation for any purpose, provided that the
|
||||||
|
above copyright notice and the following two paragraphs appear in
|
||||||
|
all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||||
|
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||||
|
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
@ -0,0 +1,33 @@
|
||||||
|
venv: venv-create venv-install-deps venv-install-reqs
|
||||||
|
run: venv venv-run
|
||||||
|
check: venv venv-check
|
||||||
|
|
||||||
|
venv-create:
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
venv-install-deps:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m pip install hcloud ansible ansible-lint
|
||||||
|
|
||||||
|
venv-install-reqs:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy role install -r ./requirements.yml -p ./.ansible/roles
|
||||||
|
|
||||||
|
venv-install-reqs-force:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy role install -r ./requirements.yml --force -p ./.ansible/roles
|
||||||
|
|
||||||
|
venv-check:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ROLES_PATH=./.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml --check -i /tmp/outputs/inventory.yaml
|
||||||
|
|
||||||
|
venv-run:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
export COLLECTIONS_PATHS=./.ansible/collections &&\
|
||||||
|
export ANSIBLE_ROLES_PATH=$(PWD)/.ansible/roles &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i /tmp/outputs/inventory.yaml
|
||||||
|
|
||||||
|
venv-lint:
|
||||||
|
venv/bin/ansible-lint playbook.yml
|
|
@ -0,0 +1,22 @@
|
||||||
|
# systems-ansible-playbooks
|
||||||
|
|
||||||
|
Manage servers with ansible
|
||||||
|
|
||||||
|
## K3s
|
||||||
|
|
||||||
|
I'm not configuring k3s with ansble, it's configured with following flags
|
||||||
|
|
||||||
|
curl -sfL https://get.k3s.io | sh -s - server \
|
||||||
|
--write-kubeconfig-mode=644 \
|
||||||
|
--node-name="$(hostname -f)" \
|
||||||
|
--kubelet-arg "allowed-unsafe-sysctls=net.ipv4.ip_forward" \
|
||||||
|
--tls-san="$(hostname -I | awk '{print $1}')" \
|
||||||
|
--disable-cloud-controller \
|
||||||
|
--disable-helm-controller \
|
||||||
|
--disable metrics-server \
|
||||||
|
--disable local-storage \
|
||||||
|
--disable traefik \
|
||||||
|
--cluster-cidr="10.244.0.0/16" \
|
||||||
|
--flannel-backend none \
|
||||||
|
--disable coredns \
|
||||||
|
--cluster-init \
|
|
@ -0,0 +1,2 @@
|
||||||
|
[defaults]
|
||||||
|
vars_plugins_enabled = host_group_vars,community.sops.sops
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
- name: Cluster prep
|
||||||
|
hosts: k3s_cluster
|
||||||
|
remote_user: admin
|
||||||
|
gather_facts: true
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: ./.ansible/roles/k3s-ansible/roles/prereq
|
||||||
|
|
||||||
|
- name: Setup K3S server
|
||||||
|
hosts: server
|
||||||
|
remote_user: admin
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: ./.ansible/roles/k3s-ansible/roles/k3s_server
|
||||||
|
|
||||||
|
- name: Setup K3S agent
|
||||||
|
hosts: agent
|
||||||
|
remote_user: admin
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: ./.ansible/roles/k3s-ansible/roles/k3s_agent
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
- name: Netplan config
|
||||||
|
when: ansible_distribution == 'Ubuntu'
|
||||||
|
block:
|
||||||
|
- name: Update the netplan config
|
||||||
|
become: true
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/60-floating-ip.j2
|
||||||
|
dest: /etc/netplan/60-floating-ip.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0600'
|
||||||
|
vars:
|
||||||
|
publicIP: 195.201.250.50
|
||||||
|
|
||||||
|
- name: Apply a new config
|
||||||
|
become: true
|
||||||
|
ansible.builtin.shell: netplan apply
|
||||||
|
- name: Ifcfg onfig
|
||||||
|
when: ansible_distribution == 'Rocky'
|
||||||
|
block:
|
||||||
|
- name: Update the ifcfg config
|
||||||
|
notify: Restart the network service
|
||||||
|
become: true
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/ifcfg-eth0:1.j2
|
||||||
|
dest: /etc/sysconfig/network-scripts/ifcfg-eth0:1
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0600'
|
||||||
|
vars:
|
||||||
|
publicIP: 195.201.249.91
|
|
@ -0,0 +1,79 @@
|
||||||
|
- name: Update ubuntu repos
|
||||||
|
when: ansible_distribution == 'Ubuntu'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Update yum repos
|
||||||
|
when: ansible_distribution == 'Fedora' or ansible_distribution == 'Rocky'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.yum:
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Upgrade system if it's the first ansible run
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
- name: Check that the ansible_initialized file exists
|
||||||
|
become: true
|
||||||
|
stat:
|
||||||
|
path: /etc/ansible_initialized
|
||||||
|
register: stat_result
|
||||||
|
- name: Upgrade system
|
||||||
|
when: not stat_result.stat.exists
|
||||||
|
block:
|
||||||
|
- name: Upgrade ubuntu repos
|
||||||
|
when: ansible_distribution == 'Ubuntu'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.apt:
|
||||||
|
upgrade: full
|
||||||
|
|
||||||
|
- name: Upgrade all packages
|
||||||
|
when: ansible_distribution == 'Fedora' or ansible_distribution == 'Rocky'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.yum:
|
||||||
|
name: '*'
|
||||||
|
state: latest
|
||||||
|
|
||||||
|
- name: Reboot to apply upgrades
|
||||||
|
when: not stat_result.stat.exists
|
||||||
|
become: true
|
||||||
|
ansible.builtin.reboot:
|
||||||
|
|
||||||
|
- name: Create the ansible_initialized file
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: /etc/ansible_initialized
|
||||||
|
state: touch
|
||||||
|
when: not stat_result.stat.exists
|
||||||
|
|
||||||
|
- name: Install host-specific packages
|
||||||
|
when: packages is defined
|
||||||
|
become: true
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
loop: "{{ packages }}"
|
||||||
|
|
||||||
|
- name: Install common packages
|
||||||
|
become: true
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
loop: "{{ common_packages }}"
|
||||||
|
|
||||||
|
- name: Configure host-specific services
|
||||||
|
when: services is defined
|
||||||
|
become: true
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
enabled: "{{ item.enabled }}"
|
||||||
|
loop: "{{ services }}"
|
||||||
|
|
||||||
|
- name: Configure common services
|
||||||
|
become: true
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
enabled: "{{ item.enabled }}"
|
||||||
|
loop: "{{ common_services }}"
|
|
@ -0,0 +1,20 @@
|
||||||
|
- name: Install host-specific sysctl
|
||||||
|
when: sysctl is defined
|
||||||
|
become: true
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
value: "{{ item.value }}"
|
||||||
|
sysctl_set: "{{ item.set }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
reload: true
|
||||||
|
loop: "{{ sysctl }}"
|
||||||
|
|
||||||
|
- name: Install common sysctl
|
||||||
|
become: true
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
value: "{{ item.value }}"
|
||||||
|
sysctl_set: "{{ item.set }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
reload: true
|
||||||
|
loop: "{{ common_sysctl }}"
|
|
@ -0,0 +1,33 @@
|
||||||
|
- name: Prepare global users
|
||||||
|
block:
|
||||||
|
- name: Ensure required groups exist
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
loop: "{{ user_groups }}"
|
||||||
|
|
||||||
|
- name: Allow passwordless sudo for certain groups
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
dest: /etc/sudoers
|
||||||
|
state: present
|
||||||
|
regexp: '^%wheel'
|
||||||
|
line: '%wheel ALL=(ALL) NOPASSWD: ALL'
|
||||||
|
validate: 'visudo -cf %s'
|
||||||
|
when: 'item.sudo'
|
||||||
|
loop: "{{ user_groups }}"
|
||||||
|
|
||||||
|
- name: Create the users user
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
shell: /bin/bash
|
||||||
|
groups: "{% for grp in item.groups %}{{ grp }}{% if not loop.last %},{% endif %}{% endfor %}"
|
||||||
|
append: false
|
||||||
|
password:
|
||||||
|
loop: "{{ users }}"
|
||||||
|
|
||||||
|
- name: Set authorized keys for user
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: "{{ item.0.name }}"
|
||||||
|
state: "{{ item.1.state }}"
|
||||||
|
key: "{{ item.1.key }}"
|
||||||
|
loop: "{{ users | subelements('ssh_keys') }}"
|
|
@ -0,0 +1,33 @@
|
||||||
|
# -----------------------------------
|
||||||
|
# -- Configure volumes
|
||||||
|
# -----------------------------------
|
||||||
|
- name: Configure volumes
|
||||||
|
when: volumes is defined
|
||||||
|
block:
|
||||||
|
- name: Ensure volumes exist
|
||||||
|
become: true
|
||||||
|
community.general.parted:
|
||||||
|
device: "{{ item.device }}"
|
||||||
|
unit: KiB
|
||||||
|
loop: "{{ volumes }}"
|
||||||
|
- name: Create a directory if it does not exist
|
||||||
|
become: true
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
loop: "{{ volumes }}"
|
||||||
|
- name: Create a ext4 filesystem
|
||||||
|
become: true
|
||||||
|
community.general.filesystem:
|
||||||
|
fstype: "{{ item.fstype }}"
|
||||||
|
dev: "{{ item.device }}"
|
||||||
|
loop: "{{ volumes }}"
|
||||||
|
- name: Remount volume to another location
|
||||||
|
become: true
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: "{{ item.dir }}"
|
||||||
|
src: "{{ item.device }}"
|
||||||
|
state: mounted
|
||||||
|
fstype: "{{ item.fstype }}"
|
||||||
|
loop: "{{ volumes }}"
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
renderer: networkd
|
||||||
|
ethernets:
|
||||||
|
eth0:
|
||||||
|
addresses:
|
||||||
|
- {{ publicIP }}/32
|
|
@ -0,0 +1,7 @@
|
||||||
|
BOOTPROTO=static
|
||||||
|
DEVICE=eth0:1
|
||||||
|
IPADDR={{ publicIP }}
|
||||||
|
PREFIX=32
|
||||||
|
TYPE=Ethernet
|
||||||
|
USERCTL=no
|
||||||
|
ONBOOT=yes
|
|
@ -0,0 +1,5 @@
|
||||||
|
# ---> Ansible
|
||||||
|
*.retry
|
||||||
|
|
||||||
|
# ---> Python
|
||||||
|
venv
|
|
@ -0,0 +1,17 @@
|
||||||
|
Permission is hereby granted, without written agreement and without
|
||||||
|
license or royalty fees, to use, copy, modify, and distribute this
|
||||||
|
software and its documentation for any purpose, provided that the
|
||||||
|
above copyright notice and the following two paragraphs appear in
|
||||||
|
all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||||
|
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||||
|
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
@ -0,0 +1,23 @@
|
||||||
|
check: venv-install-deps venv-install-reqs venv-check
|
||||||
|
|
||||||
|
run: venv venv-install-deps venv-install-reqs venv-run
|
||||||
|
|
||||||
|
venv:
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
venv-install-deps:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m pip install hcloud ansible
|
||||||
|
|
||||||
|
venv-install-reqs:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible galaxy install -r ./requirements.yml
|
||||||
|
|
||||||
|
venv-check:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible playbook playbook.yml --check -i ./inventory.yaml
|
||||||
|
|
||||||
|
venv-run:
|
||||||
|
source venv/bin/activate &&\
|
||||||
|
python3 -m ansible playbook playbook.yml -i /tmp/outputs/inventory.yaml
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# systems-ansible-playbooks
|
||||||
|
|
||||||
|
Manage servers with ansible
|
||||||
|
|
||||||
|
## K3s
|
||||||
|
|
||||||
|
I'm not configuring k3s with ansble, it's configured with following flags
|
||||||
|
|
||||||
|
curl -sfL https://get.k3s.io | sh -s - server \
|
||||||
|
--write-kubeconfig-mode=644 \
|
||||||
|
--node-name="$(hostname -f)" \
|
||||||
|
--kubelet-arg "allowed-unsafe-sysctls=net.ipv4.ip_forward" \
|
||||||
|
--tls-san="$(hostname -I | awk '{print $1}')" \
|
||||||
|
--disable-cloud-controller \
|
||||||
|
--disable-helm-controller \
|
||||||
|
--disable metrics-server \
|
||||||
|
--disable local-storage \
|
||||||
|
--disable traefik \
|
||||||
|
--cluster-cidr="10.244.0.0/16" \
|
||||||
|
--flannel-backend none \
|
||||||
|
--disable coredns \
|
||||||
|
--cluster-init \
|
|
@ -0,0 +1,2 @@
|
||||||
|
[defaults]
|
||||||
|
vars_plugins_enabled = host_group_vars,community.sops.sops
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
- name: Prepare users
|
||||||
|
hosts: servers
|
||||||
|
remote_user: root
|
||||||
|
vars_files:
|
||||||
|
- ../../../group_vars/users.yaml
|
||||||
|
tags:
|
||||||
|
- system
|
||||||
|
tasks:
|
||||||
|
- name: Prepare Users
|
||||||
|
ansible.builtin.import_tasks: ./tasks/users.yaml
|
||||||
|
|
||||||
|
- name: Prepare systems
|
||||||
|
hosts: servers
|
||||||
|
remote_user: admin
|
||||||
|
vars_files:
|
||||||
|
- ../../../group_vars/packages.yaml
|
||||||
|
- ../../../group_vars/sysctl.yaml
|
||||||
|
tags:
|
||||||
|
- system
|
||||||
|
tasks:
|
||||||
|
- name: Install packages
|
||||||
|
ansible.builtin.import_tasks: ./tasks/packages.yaml
|
||||||
|
- name: Setup sysctl
|
||||||
|
ansible.builtin.import_tasks: ./tasks/sysctl.yaml
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
- name: Netplan config
|
||||||
|
when: ansible_distribution == 'Ubuntu'
|
||||||
|
block:
|
||||||
|
- name: Update the netplan config
|
||||||
|
become: true
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/60-floating-ip.j2
|
||||||
|
dest: /etc/netplan/60-floating-ip.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0600'
|
||||||
|
vars:
|
||||||
|
publicIP: 195.201.250.50
|
||||||
|
|
||||||
|
- name: Apply a new config
|
||||||
|
become: true
|
||||||
|
ansible.builtin.shell: netplan apply
|
||||||
|
- name: Ifcfg onfig
|
||||||
|
when: ansible_distribution == 'Rocky'
|
||||||
|
block:
|
||||||
|
- name: Update the ifcfg config
|
||||||
|
notify: Restart the network service
|
||||||
|
become: true
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: templates/ifcfg-eth0:1.j2
|
||||||
|
dest: /etc/sysconfig/network-scripts/ifcfg-eth0:1
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0600'
|
||||||
|
vars:
|
||||||
|
publicIP: 195.201.249.91
|
|
@ -0,0 +1,95 @@
|
||||||
|
- name: Update ubuntu repos
|
||||||
|
when: ansible_distribution == 'Ubuntu'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Add Kubernetes repo
|
||||||
|
when: ansible_distribution == 'Fedora' or ansible_distribution == 'Rocky'
|
||||||
|
become: true
|
||||||
|
template:
|
||||||
|
src: kubernetes.repo.j2
|
||||||
|
dest: /etc/yum.repos.d/kubernetes.repo
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Add Docker repo
|
||||||
|
when: ansible_distribution == 'Fedora' or ansible_distribution == 'Rocky'
|
||||||
|
become: true
|
||||||
|
template:
|
||||||
|
src: docker.repo.j2
|
||||||
|
dest: /etc/yum.repos.d/docker.repo
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Update yum repos
|
||||||
|
when: ansible_distribution == 'Fedora' or ansible_distribution == 'Rocky'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.yum:
|
||||||
|
update_cache: true
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Upgrade system if it's the first ansible run
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
- name: Check that the ansible_initialized file exists
|
||||||
|
become: true
|
||||||
|
stat:
|
||||||
|
path: /etc/ansible_initialized
|
||||||
|
register: stat_result
|
||||||
|
|
||||||
|
- name: Upgrade system
|
||||||
|
when: not stat_result.stat.exists
|
||||||
|
block:
|
||||||
|
- name: Upgrade ubuntu repos
|
||||||
|
when: ansible_distribution == 'Ubuntu'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.apt:
|
||||||
|
upgrade: full
|
||||||
|
|
||||||
|
- name: Upgrade all packages
|
||||||
|
when: ansible_distribution == 'Fedora' or ansible_distribution == 'Rocky'
|
||||||
|
become: true
|
||||||
|
ansible.builtin.yum:
|
||||||
|
name: '*'
|
||||||
|
state: latest
|
||||||
|
|
||||||
|
- name: Reboot to apply upgrades
|
||||||
|
when: not stat_result.stat.exists
|
||||||
|
become: true
|
||||||
|
ansible.builtin.reboot:
|
||||||
|
|
||||||
|
- name: Create the ansible_initialized file
|
||||||
|
become: true
|
||||||
|
file:
|
||||||
|
path: /etc/ansible_initialized
|
||||||
|
state: touch
|
||||||
|
when: not stat_result.stat.exists
|
||||||
|
|
||||||
|
- name: Install host-specific packages
|
||||||
|
when: packages is defined
|
||||||
|
become: true
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
loop: "{{ packages }}"
|
||||||
|
|
||||||
|
- name: Install common packages
|
||||||
|
become: true
|
||||||
|
ansible.builtin.package:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
loop: "{{ common_packages }}"
|
||||||
|
|
||||||
|
- name: Configure host-specific services
|
||||||
|
when: services is defined
|
||||||
|
become: true
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
enabled: "{{ item.enabled }}"
|
||||||
|
loop: "{{ services }}"
|
||||||
|
|
||||||
|
- name: Configure common services
|
||||||
|
become: true
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
enabled: "{{ item.enabled }}"
|
||||||
|
loop: "{{ common_services }}"
|
|
@ -0,0 +1,20 @@
|
||||||
|
- name: Install host-specific sysctl
|
||||||
|
when: sysctl is defined
|
||||||
|
become: true
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
value: "{{ item.value }}"
|
||||||
|
sysctl_set: "{{ item.set }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
reload: true
|
||||||
|
loop: "{{ sysctl }}"
|
||||||
|
|
||||||
|
- name: Install common sysctl
|
||||||
|
become: true
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
value: "{{ item.value }}"
|
||||||
|
sysctl_set: "{{ item.set }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
reload: true
|
||||||
|
loop: "{{ common_sysctl }}"
|
|
@ -0,0 +1,33 @@
|
||||||
|
- name: Prepare global users
|
||||||
|
block:
|
||||||
|
- name: Ensure required groups exist
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: "{{ item.state }}"
|
||||||
|
loop: "{{ user_groups }}"
|
||||||
|
|
||||||
|
- name: Allow passwordless sudo for certain groups
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
dest: /etc/sudoers
|
||||||
|
state: present
|
||||||
|
regexp: '^%wheel'
|
||||||
|
line: '%wheel ALL=(ALL) NOPASSWD: ALL'
|
||||||
|
validate: 'visudo -cf %s'
|
||||||
|
when: 'item.sudo'
|
||||||
|
loop: "{{ user_groups }}"
|
||||||
|
|
||||||
|
- name: Create the users user
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
shell: /bin/bash
|
||||||
|
groups: "{% for grp in item.groups %}{{ grp }}{% if not loop.last %},{% endif %}{% endfor %}"
|
||||||
|
append: false
|
||||||
|
password:
|
||||||
|
loop: "{{ users }}"
|
||||||
|
|
||||||
|
- name: Set authorized keys for user
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: "{{ item.0.name }}"
|
||||||
|
state: "{{ item.1.state }}"
|
||||||
|
key: "{{ item.1.key }}"
|
||||||
|
loop: "{{ users | subelements('ssh_keys') }}"
|
|
@ -0,0 +1,33 @@
|
||||||
|
# -----------------------------------
|
||||||
|
# -- Configure volumes
|
||||||
|
# -----------------------------------
|
||||||
|
- name: Configure volumes
|
||||||
|
when: volumes is defined
|
||||||
|
block:
|
||||||
|
- name: Ensure volumes exist
|
||||||
|
become: true
|
||||||
|
community.general.parted:
|
||||||
|
device: "{{ item.device }}"
|
||||||
|
unit: KiB
|
||||||
|
loop: "{{ volumes }}"
|
||||||
|
- name: Create a directory if it does not exist
|
||||||
|
become: true
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: '0755'
|
||||||
|
loop: "{{ volumes }}"
|
||||||
|
- name: Create a ext4 filesystem
|
||||||
|
become: true
|
||||||
|
community.general.filesystem:
|
||||||
|
fstype: "{{ item.fstype }}"
|
||||||
|
dev: "{{ item.device }}"
|
||||||
|
loop: "{{ volumes }}"
|
||||||
|
- name: Remount volume to another location
|
||||||
|
become: true
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: "{{ item.dir }}"
|
||||||
|
src: "{{ item.device }}"
|
||||||
|
state: mounted
|
||||||
|
fstype: "{{ item.fstype }}"
|
||||||
|
loop: "{{ volumes }}"
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
renderer: networkd
|
||||||
|
ethernets:
|
||||||
|
eth0:
|
||||||
|
addresses:
|
||||||
|
- {{ publicIP }}/32
|
|
@ -0,0 +1,6 @@
|
||||||
|
[docker-ce-stable]
|
||||||
|
name=Docker CE Stable - $basearch
|
||||||
|
baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=1
|
||||||
|
gpgkey=https://download.docker.com/linux/centos/gpg
|
|
@ -0,0 +1,7 @@
|
||||||
|
BOOTPROTO=static
|
||||||
|
DEVICE=eth0:1
|
||||||
|
IPADDR={{ publicIP }}
|
||||||
|
PREFIX=32
|
||||||
|
TYPE=Ethernet
|
||||||
|
USERCTL=no
|
||||||
|
ONBOOT=yes
|
|
@ -0,0 +1,6 @@
|
||||||
|
[kubernetes]
|
||||||
|
name=Kubernetes
|
||||||
|
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=1
|
||||||
|
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
|
|
@ -0,0 +1,2 @@
|
||||||
|
hcloud
|
||||||
|
ansible
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
collections:
|
||||||
|
- hetzner.hcloud
|
||||||
|
- community.sops
|
||||||
|
- name: ansible.utils
|
||||||
|
- name: community.general
|
||||||
|
version: 8.5.0
|
||||||
|
- name: ansible.posix
|
||||||
|
- name: kubernetes.core
|
||||||
|
roles:
|
||||||
|
- src: git+https://git.badhouseplants.net/allanger/k3s-ansible.git
|
||||||
|
version: master
|
||||||
|
- src: git+https://git.badhouseplants.net/allanger/ansible-create-k8s-user.git
|
||||||
|
version: download-config
|
||||||
|
- xanmanning.k3s
|
|
@ -0,0 +1,2 @@
|
||||||
|
git.badhouseplants.net ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDTsGvo2KESuvWLf4QEwg+1DdetksEgowGAW04OezLhpA6bqT/6WIx0O6NaQCSN+ZxBRV2a/+ICSGFtU+RFTtbcu30Lcsl/yD9qwbzBqNTUweaWPNze5LrHbIV0/J8nTSt4vzCw3eF0wdvnIQWLhdjQ4qjtV1OWlYJDwo1rHPDJ5JAuYKeLz50iEhs7R6oj2ccws2olGUssG4voi/EHWVxQa3khmSNWdHTNR6H6sdkehKWflGokS5oRf5dIUFj+BbFbq6JxJQXzoiVzOoLdfGz1S6m5i1O0UbDgnoL/my+0ZiswbIa5ApbP+oOjvW5NWF1lzehowNj7Kd1Ow9+FN+2eHSPN+g5rXapBNclQ5/JT5pcSP3vjVuvNoUE/GHhLLFLG3nsPd0GuSB9PDWqxBdnAybOjjldL2cTdDajcdkYbyUGT5HCvdjgpCeJe22JQElvTgKWnICDsnCaIXPwrp1ek5soAjY6ylsOv2KYUnoo+gy7P0+Bzf9JzngfgcN/4x5s8keRQ6YNC9I3NiN1XRdJA52/RGMQfuWjqcqG04yk6AHZZLD3OdBJRjLhXSRn4/DixQ6BxT25mZfZVuyS9pOuDXaI9ZmYtX+xSXAp1XTfIH6t/ud1A+jTKbrCeV5AS6CmlGVfge+9ewDw7RRGd3MW5i5N3N7RwJVMfpcTXVPY2sQ==
|
||||||
|
|
Loading…
Reference in New Issue