commit f32371d5956c689cf992048f4e2b1f40b0f1bfbb Author: Timothy Stewart Date: Sun Mar 27 10:05:37 2022 -0500 initial commit diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..296747b --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,3 @@ +--- +skip_list: + - 'fqcn-builtins' diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..9535645 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: timothystewart6 +patreon: technotim diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..86f3c34 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +--- +name: Lint +'on': + pull_request: + push: + branches: + - master + +jobs: + + test: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Check out the codebase. + uses: actions/checkout@v2 + + - name: Set up Python 3.7. + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install test dependencies. + run: pip3 install yamllint ansible-lint ansible + + - name: Run yamllint. + run: yamllint . + + - name: Run ansible-lint. + run: ansible-lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..8f19687 --- /dev/null +++ b/.yamllint @@ -0,0 +1,9 @@ +--- +extends: default + +rules: + line-length: + max: 120 + level: warning + truthy: + allowed-values: ['true', 'false', 'yes', 'no'] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4947287 --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4610e27 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Build a Kubernetes HA-cluster using k3s & kube-vip & metal-lb via Ansible + +Based on + +Forked from + +Kube-vip Control Plane is described -> + +Video here + +More docs here + +## K3s Ansible Playbook + +Build a Kubernetes cluster using Ansible with k3s. The goal is easily install a Kubernetes cluster on machines running: + +- [X] Debian +- [X] Ubuntu +- [X] CentOS + +on processor architecture: + +- [X] x64 +- [X] arm64 +- [X] armhf + +## System requirements + +Deployment environment must have Ansible 2.4.0+ +Master and nodes must have passwordless SSH access + +## Usage + +First create a new directory based on the `sample` directory within the `inventory` directory: + +```bash +cp -R inventory/sample inventory/my-cluster +``` + +Second, edit `inventory/my-cluster/hosts.ini` to match the system information gathered above. For example: + +```ini +[master] +192.168.30.38 +192.168.30.39 +192.168.30.40 + +[node] +192.168.30.41 +192.168.30.42 + +[k3s_cluster:children] +master +node +``` + +If multiple hosts are in the master group, the playbook will automatically setup k3s in HA mode with etcd. + +This requires at least k3s version 1.19.1 + +If needed, you can also edit `inventory/my-cluster/group_vars/all.yml` to match your environment. + +Start provisioning of the cluster using the following command: + +```bash +ansible-playbook site.yml -i inventory/my-cluster/hosts.ini +``` + +After deployment control plane will be accessible via virtual ip-address which is defined in inventory/group_vars/all.yml as apiserver_endpoint + +Remove k3s cluster + +```bash +ansible-playbook reset.yml -i inventory/my-cluster/hosts.ini +``` + +**Note: add --ask-pass --ask-become-pass if you are using password logins over ssh** + +## Kubeconfig + +To get access to your **Kubernetes** cluster just + +```bash +scp debian@master_ip:~/.kube/config ~/.kube/config +``` + +## kube-vip + +See + +## MetalLB + +see diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..bafab4a --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,12 @@ +[defaults] +nocows = True +roles_path = ./roles +inventory = ./hosts.ini + +remote_tmp = $HOME/.ansible/tmp +local_tmp = $HOME/.ansible/tmp +pipelining = True +become = True +host_key_checking = False +deprecation_warnings = False +callback_whitelist = profile_tasks diff --git a/collections/requirements.yml b/collections/requirements.yml new file mode 100644 index 0000000..afc836d --- /dev/null +++ b/collections/requirements.yml @@ -0,0 +1,3 @@ +--- +collections: + - name: community.general diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..8b43b83 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +ansible-playbook site.yml -i inventory/my-cluster/hosts.ini --ask-pass --ask-become-pass \ No newline at end of file diff --git a/example/deployment.yml b/example/deployment.yml new file mode 100644 index 0000000..ad875ee --- /dev/null +++ b/example/deployment.yml @@ -0,0 +1,20 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + selector: + matchLabels: + app: nginx + replicas: 3 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 diff --git a/example/service.yml b/example/service.yml new file mode 100644 index 0000000..2ba6c7d --- /dev/null +++ b/example/service.yml @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx +spec: + selector: + app: nginx + ports: + - port: 80 + targetPort: 80 + type: LoadBalancer diff --git a/inventory/.gitignore b/inventory/.gitignore new file mode 100644 index 0000000..568d6c0 --- /dev/null +++ b/inventory/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!sample/ \ No newline at end of file diff --git a/inventory/sample/group_vars/all.yml b/inventory/sample/group_vars/all.yml new file mode 100644 index 0000000..db1738c --- /dev/null +++ b/inventory/sample/group_vars/all.yml @@ -0,0 +1,36 @@ +--- +k3s_version: v1.23.4+k3s1 +ansible_user: ansibleuser +systemd_dir: /etc/systemd/system + +# interface which will be used for flannel +flannel_iface: "eth0" + +# apiserver_endpoint is virtual ip-address which will be configured on each master +apiserver_endpoint: "192.168.30.222" + +# k3s_token is required masters can talk together securely +# this token should be alpha numeric only +k3s_token: "some-SUPER-DEDEUPER-secret-password" + +# change these to your liking, the only required one is--no-deploy servicelb +# yamllint disable-line rule:line-length +extra_server_args: "--no-deploy servicelb --no-deploy traefik --write-kubeconfig-mode 644 --kube-apiserver-arg default-not-ready-toleration-seconds=10 --kube-apiserver-arg default-unreachable-toleration-seconds=10 --kube-controller-arg node-monitor-period=10s --kube-controller-arg node-monitor-grace-period=10s --kubelet-arg node-status-update-frequency=5s" +extra_agent_args: "--kubelet-arg node-status-update-frequency=5s" + +# image tag for kube-vip +kube_vip_tag_version: "v0.4.2" + +# image tag for metal lb +metal_lb_speaker_tag_version: "v0.12.1" +metal_lb_controller_tag_version: "v0.12.1" + +# metallb ip range for load balancer +metal_lb_ip_range: "192.168.30.80-192.168.30.90" + +# If Vagrant is used +# ansible_user: vagrant +# flannel_iface: "eth1" +# yamllint disable-line rule:line-length +# extra_server_args: "--node-ip={{ ansible_eth1.ipv4.address }} --flannel-iface={{ flannel_iface }} --no-deploy servicelb --no-deploy traefik" +# extra_agent_args: "--flannel-iface={{ flannel_iface }}" diff --git a/inventory/sample/hosts.ini b/inventory/sample/hosts.ini new file mode 100644 index 0000000..b656847 --- /dev/null +++ b/inventory/sample/hosts.ini @@ -0,0 +1,12 @@ +[master] +192.168.30.38 +192.168.30.39 +192.168.30.40 + +[node] +192.168.30.41 +192.168.30.42 + +[k3s_cluster:children] +master +node diff --git a/kill-all.sh b/kill-all.sh new file mode 100755 index 0000000..493680b --- /dev/null +++ b/kill-all.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +ansible-playbook reset.yml -i inventory/my-cluster/hosts.ini --ask-pass --ask-become-pass diff --git a/reset.yml b/reset.yml new file mode 100644 index 0000000..77577fd --- /dev/null +++ b/reset.yml @@ -0,0 +1,7 @@ +--- + +- hosts: k3s_cluster + gather_facts: yes + become: yes + roles: + - role: reset diff --git a/roles/download/tasks/main.yml b/roles/download/tasks/main.yml new file mode 100644 index 0000000..1450fd8 --- /dev/null +++ b/roles/download/tasks/main.yml @@ -0,0 +1,36 @@ +--- + +- name: Download k3s binary x64 + get_url: + url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s + checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-amd64.txt + dest: /usr/local/bin/k3s + owner: root + group: root + mode: 0755 + when: ansible_facts.architecture == "x86_64" + +- name: Download k3s binary arm64 + get_url: + url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s-arm64 + checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-arm64.txt + dest: /usr/local/bin/k3s + owner: root + group: root + mode: 0755 + when: + - ( ansible_facts.architecture is search("arm") and + ansible_facts.userspace_bits == "64" ) or + ansible_facts.architecture is search("aarch64") + +- name: Download k3s binary armhf + get_url: + url: https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/k3s-armhf + checksum: sha256:https://github.com/k3s-io/k3s/releases/download/{{ k3s_version }}/sha256sum-arm.txt + dest: /usr/local/bin/k3s + owner: root + group: root + mode: 0755 + when: + - ansible_facts.architecture is search("arm") + - ansible_facts.userspace_bits == "32" diff --git a/roles/k3s/master/defaults/main.yml b/roles/k3s/master/defaults/main.yml new file mode 100644 index 0000000..596c9cb --- /dev/null +++ b/roles/k3s/master/defaults/main.yml @@ -0,0 +1,12 @@ +--- +ansible_user: root +server_init_args: >- + {% if groups['master'] | length > 1 %} + {% if ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) %} + --cluster-init + {% else %} + --server https://{{ hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) }}:6443 + {% endif %} + --token {{ k3s_token }} + {% endif %} + {{ extra_server_args | default('') }} diff --git a/roles/k3s/master/tasks/main.yml b/roles/k3s/master/tasks/main.yml new file mode 100644 index 0000000..8c02e3f --- /dev/null +++ b/roles/k3s/master/tasks/main.yml @@ -0,0 +1,172 @@ +--- +- name: Clean previous runs of k3s-init + systemd: + name: k3s-init + state: stopped + failed_when: false + +- name: Clean previous runs of k3s-init + command: systemctl reset-failed k3s-init + failed_when: false + changed_when: false + args: + warn: false # The ansible systemd module does not support reset-failed + +- name: Create manifests directory on first master + file: + path: /var/lib/rancher/k3s/server/manifests + state: directory + owner: root + group: root + mode: 0644 + when: ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) + +- name: Copy vip rbac manifest to first master + template: + src: "vip.rbac.yaml.j2" + dest: "/var/lib/rancher/k3s/server/manifests/vip.rbac.yaml" + owner: root + group: root + mode: 0644 + when: ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) + +- name: Copy vip manifest to first master + template: + src: "vip.yaml.j2" + dest: "/var/lib/rancher/k3s/server/manifests/vip.yaml" + owner: root + group: root + mode: 0644 + when: ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) + +- name: Copy metallb namespace manifest to first master + template: + src: "metallb.namespace.j2" + dest: "/var/lib/rancher/k3s/server/manifests/metallb.namespace.yaml" + owner: root + group: root + mode: 0644 + when: ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) + +- name: Copy metallb ConfigMap manifest to first master + template: + src: "metallb.configmap.j2" + dest: "/var/lib/rancher/k3s/server/manifests/metallb.configmap.yaml" + owner: root + group: root + mode: 0644 + when: ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) + +- name: Copy metallb main manifest to first master + template: + src: "metallb.yaml.j2" + dest: "/var/lib/rancher/k3s/server/manifests/metallb.yaml" + owner: root + group: root + mode: 0644 + when: ansible_host == hostvars[groups['master'][0]]['ansible_host'] | default(groups['master'][0]) + +- name: Init cluster inside the transient k3s-init service + command: + cmd: "systemd-run -p RestartSec=2 \ + -p Restart=on-failure \ + --unit=k3s-init \ + k3s server {{ server_init_args }}" + creates: "{{ systemd_dir }}/k3s.service" + args: + warn: false # The ansible systemd module does not support transient units + +- name: Verification + block: + - name: Verify that all nodes actually joined (check k3s-init.service if this fails) + command: + cmd: k3s kubectl get nodes -l "node-role.kubernetes.io/master=true" -o=jsonpath="{.items[*].metadata.name}" + register: nodes + until: nodes.rc == 0 and (nodes.stdout.split() | length) == (groups['master'] | length) + retries: 20 + delay: 10 + changed_when: false + always: + - name: Kill the temporary service used for initialization + systemd: + name: k3s-init + state: stopped + failed_when: false + +- name: Copy K3s service file + register: k3s_service + template: + src: "k3s.service.j2" + dest: "{{ systemd_dir }}/k3s.service" + owner: root + group: root + mode: 0644 + +- name: Enable and check K3s service + systemd: + name: k3s + daemon_reload: yes + state: restarted + enabled: yes + +- name: Wait for node-token + wait_for: + path: "{{ k3s_server_location }}/server/node-token" + +- name: Register node-token file access mode + stat: + path: "{{ k3s_server_location }}/server/node-token" + register: p + +- name: Change file access node-token + file: + path: "{{ k3s_server_location }}/server/node-token" + mode: "g+rx,o+rx" + +- name: Read node-token from master + slurp: + path: "{{ k3s_server_location }}/server/node-token" + register: node_token + +- name: Store Master node-token + set_fact: + token: "{{ node_token.content | b64decode | regex_replace('\n', '') }}" + +- name: Restore node-token file access + file: + path: "{{ k3s_server_location }}/server/node-token" + mode: "{{ p.stat.mode }}" + +- name: Create directory .kube + file: + path: ~{{ ansible_user }}/.kube + state: directory + owner: "{{ ansible_user }}" + mode: "u=rwx,g=rx,o=" + +- name: Copy config file to user home directory + copy: + src: /etc/rancher/k3s/k3s.yaml + dest: ~{{ ansible_user }}/.kube/config + remote_src: yes + owner: "{{ ansible_user }}" + mode: "u=rw,g=,o=" + +- name: Configure kubectl cluster to https://{{ apiserver_endpoint }}:6443 + command: >- + k3s kubectl config set-cluster default + --server=https://{{ apiserver_endpoint }}:6443 + --kubeconfig ~{{ ansible_user }}/.kube/config + changed_when: true + +- name: Create kubectl symlink + file: + src: /usr/local/bin/k3s + dest: /usr/local/bin/kubectl + state: link + +- name: Create crictl symlink + file: + src: /usr/local/bin/k3s + dest: /usr/local/bin/crictl + state: link diff --git a/roles/k3s/master/templates/k3s.service.j2 b/roles/k3s/master/templates/k3s.service.j2 new file mode 100644 index 0000000..a56ab10 --- /dev/null +++ b/roles/k3s/master/templates/k3s.service.j2 @@ -0,0 +1,24 @@ +[Unit] +Description=Lightweight Kubernetes +Documentation=https://k3s.io +After=network-online.target + +[Service] +Type=notify +ExecStartPre=-/sbin/modprobe br_netfilter +ExecStartPre=-/sbin/modprobe overlay +ExecStart=/usr/local/bin/k3s server --data-dir {{ k3s_server_location }} {{ extra_server_args | default("") }} +KillMode=process +Delegate=yes +# Having non-zero Limit*s causes performance problems due to accounting overhead +# in the kernel. We recommend using cgroups to do container-local accounting. +LimitNOFILE=1048576 +LimitNPROC=infinity +LimitCORE=infinity +TasksMax=infinity +TimeoutStartSec=0 +Restart=always +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/roles/k3s/master/templates/metallb.configmap.j2 b/roles/k3s/master/templates/metallb.configmap.j2 new file mode 100644 index 0000000..5721709 --- /dev/null +++ b/roles/k3s/master/templates/metallb.configmap.j2 @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: metallb-system + name: config +data: + config: | + address-pools: + - name: default + protocol: layer2 + addresses: + - {{ metal_lb_ip_range }} + \ No newline at end of file diff --git a/roles/k3s/master/templates/metallb.namespace.j2 b/roles/k3s/master/templates/metallb.namespace.j2 new file mode 100644 index 0000000..2c3979f --- /dev/null +++ b/roles/k3s/master/templates/metallb.namespace.j2 @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: metallb-system + labels: + app: metallb + \ No newline at end of file diff --git a/roles/k3s/master/templates/metallb.yaml.j2 b/roles/k3s/master/templates/metallb.yaml.j2 new file mode 100644 index 0000000..0de9cbf --- /dev/null +++ b/roles/k3s/master/templates/metallb.yaml.j2 @@ -0,0 +1,481 @@ +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + labels: + app: metallb + name: controller +spec: + allowPrivilegeEscalation: false + allowedCapabilities: [] + allowedHostPaths: [] + defaultAddCapabilities: [] + defaultAllowPrivilegeEscalation: false + fsGroup: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + hostIPC: false + hostNetwork: false + hostPID: false + privileged: false + readOnlyRootFilesystem: true + requiredDropCapabilities: + - ALL + runAsUser: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + seLinux: + rule: RunAsAny + supplementalGroups: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + volumes: + - configMap + - secret + - emptyDir +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + labels: + app: metallb + name: speaker +spec: + allowPrivilegeEscalation: false + allowedCapabilities: + - NET_RAW + allowedHostPaths: [] + defaultAddCapabilities: [] + defaultAllowPrivilegeEscalation: false + fsGroup: + rule: RunAsAny + hostIPC: false + hostNetwork: true + hostPID: false + hostPorts: + - max: 7472 + min: 7472 + - max: 7946 + min: 7946 + privileged: true + readOnlyRootFilesystem: true + requiredDropCapabilities: + - ALL + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - configMap + - secret + - emptyDir +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: metallb + name: controller + namespace: metallb-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: metallb + name: speaker + namespace: metallb-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app: metallb + name: metallb-system:controller +rules: +- apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - watch +- apiGroups: + - '' + resources: + - services/status + verbs: + - update +- apiGroups: + - '' + resources: + - events + verbs: + - create + - patch +- apiGroups: + - policy + resourceNames: + - controller + resources: + - podsecuritypolicies + verbs: + - use +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app: metallb + name: metallb-system:speaker +rules: +- apiGroups: + - '' + resources: + - services + - endpoints + - nodes + verbs: + - get + - list + - watch +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: + - get + - list + - watch +- apiGroups: + - '' + resources: + - events + verbs: + - create + - patch +- apiGroups: + - policy + resourceNames: + - speaker + resources: + - podsecuritypolicies + verbs: + - use +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app: metallb + name: config-watcher + namespace: metallb-system +rules: +- apiGroups: + - '' + resources: + - configmaps + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app: metallb + name: pod-lister + namespace: metallb-system +rules: +- apiGroups: + - '' + resources: + - pods + verbs: + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app: metallb + name: controller + namespace: metallb-system +rules: +- apiGroups: + - '' + resources: + - secrets + verbs: + - create +- apiGroups: + - '' + resources: + - secrets + resourceNames: + - memberlist + verbs: + - list +- apiGroups: + - apps + resources: + - deployments + resourceNames: + - controller + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app: metallb + name: metallb-system:controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metallb-system:controller +subjects: +- kind: ServiceAccount + name: controller + namespace: metallb-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app: metallb + name: metallb-system:speaker +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metallb-system:speaker +subjects: +- kind: ServiceAccount + name: speaker + namespace: metallb-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app: metallb + name: config-watcher + namespace: metallb-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: config-watcher +subjects: +- kind: ServiceAccount + name: controller +- kind: ServiceAccount + name: speaker +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app: metallb + name: pod-lister + namespace: metallb-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pod-lister +subjects: +- kind: ServiceAccount + name: speaker +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app: metallb + name: controller + namespace: metallb-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: controller +subjects: +- kind: ServiceAccount + name: controller +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + app: metallb + component: speaker + name: speaker + namespace: metallb-system +spec: + selector: + matchLabels: + app: metallb + component: speaker + template: + metadata: + annotations: + prometheus.io/port: '7472' + prometheus.io/scrape: 'true' + labels: + app: metallb + component: speaker + spec: + containers: + - args: + - --port=7472 + - --config=config + - --log-level=info + env: + - name: METALLB_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: METALLB_HOST + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: METALLB_ML_BIND_ADDR + valueFrom: + fieldRef: + fieldPath: status.podIP + # needed when another software is also using memberlist / port 7946 + # when changing this default you also need to update the container ports definition + # and the PodSecurityPolicy hostPorts definition + #- name: METALLB_ML_BIND_PORT + # value: "7946" + - name: METALLB_ML_LABELS + value: "app=metallb,component=speaker" + - name: METALLB_ML_SECRET_KEY + valueFrom: + secretKeyRef: + name: memberlist + key: secretkey + image: quay.io/metallb/speaker:{{ metal_lb_speaker_tag_version }} + name: speaker + ports: + - containerPort: 7472 + name: monitoring + - containerPort: 7946 + name: memberlist-tcp + - containerPort: 7946 + name: memberlist-udp + protocol: UDP + livenessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_RAW + drop: + - ALL + readOnlyRootFilesystem: true + hostNetwork: true + nodeSelector: + kubernetes.io/os: linux + serviceAccountName: speaker + terminationGracePeriodSeconds: 2 + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: metallb + component: controller + name: controller + namespace: metallb-system +spec: + revisionHistoryLimit: 3 + selector: + matchLabels: + app: metallb + component: controller + template: + metadata: + annotations: + prometheus.io/port: '7472' + prometheus.io/scrape: 'true' + labels: + app: metallb + component: controller + spec: + containers: + - args: + - --port=7472 + - --config=config + - --log-level=info + env: + - name: METALLB_ML_SECRET_NAME + value: memberlist + - name: METALLB_DEPLOYMENT + value: controller + image: quay.io/metallb/controller:{{ metal_lb_controller_tag_version }} + name: controller + ports: + - containerPort: 7472 + name: monitoring + livenessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /metrics + port: monitoring + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + nodeSelector: + kubernetes.io/os: linux + securityContext: + runAsNonRoot: true + runAsUser: 65534 + fsGroup: 65534 + serviceAccountName: controller + terminationGracePeriodSeconds: 0 + \ No newline at end of file diff --git a/roles/k3s/master/templates/vip.rbac.yaml.j2 b/roles/k3s/master/templates/vip.rbac.yaml.j2 new file mode 100644 index 0000000..e3795f7 --- /dev/null +++ b/roles/k3s/master/templates/vip.rbac.yaml.j2 @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kube-vip + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + rbac.authorization.kubernetes.io/autoupdate: "true" + name: system:kube-vip-role +rules: + - apiGroups: [""] + resources: ["services", "services/status", "nodes"] + verbs: ["list","get","watch", "update"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["list", "get", "watch", "update", "create"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: system:kube-vip-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:kube-vip-role +subjects: +- kind: ServiceAccount + name: kube-vip + namespace: kube-system + \ No newline at end of file diff --git a/roles/k3s/master/templates/vip.yaml.j2 b/roles/k3s/master/templates/vip.yaml.j2 new file mode 100644 index 0000000..118b403 --- /dev/null +++ b/roles/k3s/master/templates/vip.yaml.j2 @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + creationTimestamp: null + name: kube-vip-ds + namespace: kube-system +spec: + selector: + matchLabels: + name: kube-vip-ds + template: + metadata: + creationTimestamp: null + labels: + name: kube-vip-ds + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/master + operator: Exists + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists + containers: + - args: + - manager + env: + - name: vip_arp + value: "true" + - name: port + value: "6443" + - name: vip_interface + value: {{ flannel_iface }} + - name: vip_cidr + value: "32" + - name: cp_enable + value: "true" + - name: cp_namespace + value: kube-system + - name: vip_ddns + value: "false" + - name: svc_enable + value: "true" + - name: vip_leaderelection + value: "true" + - name: vip_leaseduration + value: "5" + - name: vip_renewdeadline + value: "3" + - name: vip_retryperiod + value: "1" + - name: address + value: {{ apiserver_endpoint }} + image: ghcr.io/kube-vip/kube-vip:{{ kube_vip_tag_version }} + imagePullPolicy: Always + name: kube-vip + resources: {} + securityContext: + capabilities: + add: + - NET_ADMIN + - NET_RAW + - SYS_TIME + hostNetwork: true + serviceAccountName: kube-vip + tolerations: + - effect: NoSchedule + operator: Exists + - effect: NoExecute + operator: Exists + updateStrategy: {} +status: + currentNumberScheduled: 0 + desiredNumberScheduled: 0 + numberMisscheduled: 0 + numberReady: 0 diff --git a/roles/k3s/node/tasks/main.yml b/roles/k3s/node/tasks/main.yml new file mode 100644 index 0000000..0ce8e08 --- /dev/null +++ b/roles/k3s/node/tasks/main.yml @@ -0,0 +1,16 @@ +--- + +- name: Copy K3s service file + template: + src: "k3s.service.j2" + dest: "{{ systemd_dir }}/k3s-node.service" + owner: root + group: root + mode: 0755 + +- name: Enable and check K3s service + systemd: + name: k3s-node + daemon_reload: yes + state: restarted + enabled: yes diff --git a/roles/k3s/node/templates/k3s.service.j2 b/roles/k3s/node/templates/k3s.service.j2 new file mode 100644 index 0000000..01baa64 --- /dev/null +++ b/roles/k3s/node/templates/k3s.service.j2 @@ -0,0 +1,24 @@ +[Unit] +Description=Lightweight Kubernetes +Documentation=https://k3s.io +After=network-online.target + +[Service] +Type=notify +ExecStartPre=-/sbin/modprobe br_netfilter +ExecStartPre=-/sbin/modprobe overlay +ExecStart=/usr/local/bin/k3s agent --server https://{{ apiserver_endpoint }}:6443 --token {{ hostvars[groups['master'][0]]['token'] | default(k3s_token) }} {{ extra_agent_args | default("") }} +KillMode=process +Delegate=yes +# Having non-zero Limit*s causes performance problems due to accounting overhead +# in the kernel. We recommend using cgroups to do container-local accounting. +LimitNOFILE=1048576 +LimitNPROC=infinity +LimitCORE=infinity +TasksMax=infinity +TimeoutStartSec=0 +Restart=always +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/roles/prereq/tasks/main.yml b/roles/prereq/tasks/main.yml new file mode 100644 index 0000000..e857729 --- /dev/null +++ b/roles/prereq/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: Set SELinux to disabled state + selinux: + state: disabled + when: ansible_distribution in ['CentOS', 'Red Hat Enterprise Linux'] + +- name: Enable IPv4 forwarding + sysctl: + name: net.ipv4.ip_forward + value: "1" + state: present + reload: yes + +- name: Enable IPv6 forwarding + sysctl: + name: net.ipv6.conf.all.forwarding + value: "1" + state: present + reload: yes + when: ansible_all_ipv6_addresses + +- name: Add br_netfilter to /etc/modules-load.d/ + copy: + content: "br_netfilter" + dest: /etc/modules-load.d/br_netfilter.conf + mode: "u=rw,g=,o=" + when: ansible_distribution in ['CentOS', 'Red Hat Enterprise Linux'] + +- name: Load br_netfilter + modprobe: + name: br_netfilter + state: present + when: ansible_distribution in ['CentOS', 'Red Hat Enterprise Linux'] + +- name: Set bridge-nf-call-iptables (just to be sure) + sysctl: + name: "{{ item }}" + value: "1" + state: present + reload: yes + when: ansible_distribution in ['CentOS', 'Red Hat Enterprise Linux'] + loop: + - net.bridge.bridge-nf-call-iptables + - net.bridge.bridge-nf-call-ip6tables + +- name: Add /usr/local/bin to sudo secure_path + lineinfile: + line: 'Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin' + regexp: "Defaults(\\s)*secure_path(\\s)*=" + state: present + insertafter: EOF + path: /etc/sudoers + validate: 'visudo -cf %s' + when: ansible_distribution in ['CentOS', 'Red Hat Enterprise Linux'] diff --git a/roles/raspberrypi/handlers/main.yml b/roles/raspberrypi/handlers/main.yml new file mode 100644 index 0000000..d25cf90 --- /dev/null +++ b/roles/raspberrypi/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: reboot + reboot: diff --git a/roles/raspberrypi/tasks/main.yml b/roles/raspberrypi/tasks/main.yml new file mode 100644 index 0000000..b80c91f --- /dev/null +++ b/roles/raspberrypi/tasks/main.yml @@ -0,0 +1,51 @@ +--- +- name: Test for raspberry pi /proc/cpuinfo + command: grep -E "Raspberry Pi|BCM2708|BCM2709|BCM2835|BCM2836" /proc/cpuinfo + register: grep_cpuinfo_raspberrypi + failed_when: false + changed_when: false + +- name: Test for raspberry pi /proc/device-tree/model + command: grep -E "Raspberry Pi" /proc/device-tree/model + register: grep_device_tree_model_raspberrypi + failed_when: false + changed_when: false + +- name: Set raspberry_pi fact to true + set_fact: + raspberry_pi: true + when: + grep_cpuinfo_raspberrypi.rc == 0 or grep_device_tree_model_raspberrypi.rc == 0 + +- name: Set detected_distribution to Raspbian + set_fact: + detected_distribution: Raspbian + when: > + raspberry_pi|default(false) and + ( ansible_facts.lsb.id|default("") == "Raspbian" or + ansible_facts.lsb.description|default("") is match("[Rr]aspbian.*") ) + +- name: Set detected_distribution to Raspbian (ARM64 on Debian Buster) + set_fact: + detected_distribution: Raspbian + when: + - ansible_facts.architecture is search("aarch64") + - raspberry_pi|default(false) + - ansible_facts.lsb.description|default("") is match("Debian.*buster") + +- name: Set detected_distribution_major_version + set_fact: + detected_distribution_major_version: "{{ ansible_facts.lsb.major_release }}" + when: + - detected_distribution | default("") == "Raspbian" + +- name: execute OS related tasks on the Raspberry Pi + include_tasks: "{{ item }}" + with_first_found: + - "prereq/{{ detected_distribution }}-{{ detected_distribution_major_version }}.yml" + - "prereq/{{ detected_distribution }}.yml" + - "prereq/{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml" + - "prereq/{{ ansible_distribution }}.yml" + - "prereq/default.yml" + when: + - raspberry_pi|default(false) diff --git a/roles/raspberrypi/tasks/prereq/CentOS.yml b/roles/raspberrypi/tasks/prereq/CentOS.yml new file mode 100644 index 0000000..af83564 --- /dev/null +++ b/roles/raspberrypi/tasks/prereq/CentOS.yml @@ -0,0 +1,8 @@ +--- +- name: Enable cgroup via boot commandline if not already enabled for Centos + lineinfile: + path: /boot/cmdline.txt + backrefs: yes + regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$' + line: '\1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory' + notify: reboot diff --git a/roles/raspberrypi/tasks/prereq/Raspbian.yml b/roles/raspberrypi/tasks/prereq/Raspbian.yml new file mode 100644 index 0000000..42bfe7d --- /dev/null +++ b/roles/raspberrypi/tasks/prereq/Raspbian.yml @@ -0,0 +1,25 @@ +--- +- name: Activating cgroup support + lineinfile: + path: /boot/cmdline.txt + regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$' + line: '\1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory' + backrefs: true + notify: reboot + +- name: Flush iptables before changing to iptables-legacy + iptables: + flush: true + changed_when: false # iptables flush always returns changed + +- name: Changing to iptables-legacy + alternatives: + path: /usr/sbin/iptables-legacy + name: iptables + register: ip4_legacy + +- name: Changing to ip6tables-legacy + alternatives: + path: /usr/sbin/ip6tables-legacy + name: ip6tables + register: ip6_legacy diff --git a/roles/raspberrypi/tasks/prereq/Ubuntu.yml b/roles/raspberrypi/tasks/prereq/Ubuntu.yml new file mode 100644 index 0000000..742fc21 --- /dev/null +++ b/roles/raspberrypi/tasks/prereq/Ubuntu.yml @@ -0,0 +1,8 @@ +--- +- name: Enable cgroup via boot commandline if not already enabled for Ubuntu on a Raspberry Pi + lineinfile: + path: /boot/firmware/cmdline.txt + backrefs: yes + regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory\b).*)$' + line: '\1 cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory' + notify: reboot diff --git a/roles/raspberrypi/tasks/prereq/default.yml b/roles/raspberrypi/tasks/prereq/default.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/raspberrypi/tasks/prereq/default.yml @@ -0,0 +1 @@ +--- diff --git a/roles/reset/tasks/main.yml b/roles/reset/tasks/main.yml new file mode 100644 index 0000000..1547c4d --- /dev/null +++ b/roles/reset/tasks/main.yml @@ -0,0 +1,43 @@ +--- +- name: Disable services + systemd: + name: "{{ item }}" + state: stopped + enabled: no + failed_when: false + with_items: + - k3s + - k3s-node + - k3s-init + +- name: pkill -9 -f "k3s/data/[^/]+/bin/containerd-shim-runc" + register: pkill_containerd_shim_runc + command: pkill -9 -f "k3s/data/[^/]+/bin/containerd-shim-runc" + changed_when: "pkill_containerd_shim_runc.rc == 0" + failed_when: false + +- name: Umount k3s filesystems + include_tasks: umount_with_children.yml + with_items: + - /run/k3s + - /var/lib/kubelet + - /run/netns + - /var/lib/rancher/k3s + loop_control: + loop_var: mounted_fs + +- name: Remove service files, binaries and data + file: + name: "{{ item }}" + state: absent + with_items: + - /usr/local/bin/k3s + - "{{ systemd_dir }}/k3s.service" + - "{{ systemd_dir }}/k3s-node.service" + - /etc/rancher/k3s + - /var/lib/kubelet + - /var/lib/rancher/k3s + +- name: daemon_reload + systemd: + daemon_reload: yes diff --git a/roles/reset/tasks/umount_with_children.yml b/roles/reset/tasks/umount_with_children.yml new file mode 100644 index 0000000..5883b70 --- /dev/null +++ b/roles/reset/tasks/umount_with_children.yml @@ -0,0 +1,16 @@ +--- +- name: Get the list of mounted filesystems + shell: set -o pipefail && cat /proc/mounts | awk '{ print $2}' | grep -E "^{{ mounted_fs }}" + register: get_mounted_filesystems + args: + executable: /bin/bash + failed_when: false + changed_when: get_mounted_filesystems.stdout | length > 0 + check_mode: false + +- name: Umount filesystem + mount: + path: "{{ item }}" + state: unmounted + with_items: + "{{ get_mounted_filesystems.stdout_lines | reverse | list }}" diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..31cc96e --- /dev/null +++ b/site.yml @@ -0,0 +1,19 @@ +--- + +- hosts: k3s_cluster + gather_facts: yes + become: yes + roles: + - role: prereq + - role: download + - role: raspberrypi + +- hosts: master + become: yes + roles: + - role: k3s/master + +- hosts: node + become: yes + roles: + - role: k3s/node