--- - name: Gather IPv4 address facts for all hosts hosts: all gather_facts: yes vars: dns_server: "192.168.1.151" tasks: - name: Configure systemd-resolved to use custom DNS become: true copy: dest: /etc/systemd/resolved.conf content: | [Resolve] DNS={{ dns_server }} FallbackDNS=192.168.1.1 Domains=mngoma.lab DNSStubListener=yes owner: root group: root mode: "0644" - name: Ensure systemd-resolved service is enabled and restarted become: true systemd: name: systemd-resolved state: restarted enabled: yes - name: Ensure host IPv4 address is available and stored as fact set_fact: node_ipv4: "{{ ansible_default_ipv4.address }}" when: ansible_default_ipv4 is defined and ansible_default_ipv4.address is defined - name: Fail if IPv4 address could not be determined fail: msg: "Could not determine IPv4 address for {{ inventory_hostname }}. Please check network configuration." when: node_ipv4 is not defined - name: Ensure /home/{{ ansible_user }}/k3s directory exists become: yes file: path: /home/{{ ansible_user }}/k3s state: directory owner: root group: root mode: '0777' - name: Initialise the K3s control plane (manager) hosts: manager become: yes vars: k3s_version: v1.29.4+k3s1 kubeconfig_dir: "/home/{{ ansible_user }}/.kube" kubeconfig_file: "{{ kubeconfig_dir }}/config" tasks: - name: Install required apt dependencies apt: name: - curl - python3-pip - python3-venv state: present update_cache: yes - name: Create a Python virtual environment for Ansible k8s modules command: python3 -m venv /opt/ansible-venv args: creates: /opt/ansible-venv - name: Install Kubernetes and OpenShift libraries in the venv command: /opt/ansible-venv/bin/pip install kubernetes openshift - name: Install k3s on manager (control plane) shell: | curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION={{ k3s_version }} sh - args: creates: /usr/local/bin/k3s - name: Gather architecture for kubectl set_fact: kubectl_arch: | {% if ansible_architecture == "x86_64" %} amd64 {% elif "armv7l" in ansible_architecture %} arm {% elif "aarch64" in ansible_architecture %} arm64 {% else %} {{ ansible_architecture }} {% endif %} - name: Download latest kubectl binary shell: | curl -Lo /usr/local/bin/kubectl "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/{{ kubectl_arch }}/kubectl" args: creates: /usr/local/bin/kubectl - name: Make kubectl executable file: path: /usr/local/bin/kubectl mode: '0755' owner: root group: root - name: Validate kubectl version shell: kubectl version --client --output=yaml register: kubectl_version changed_when: false - name: Show kubectl version debug: var: kubectl_version.stdout - name: Ensure .kube directory exists for ansible user become_user: "{{ ansible_user }}" file: path: "{{ kubeconfig_dir }}" state: directory owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0700' - name: Copy kubeconfig file to ansible user home become: true copy: src: /etc/rancher/k3s/k3s.yaml dest: "{{ kubeconfig_file }}" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0600' remote_src: yes - name: Replace server IP in kubeconfig with manager's IP address become_user: "{{ ansible_user }}" lineinfile: path: "{{ kubeconfig_file }}" regexp: 'server: https://127\\.0\\.0\\.1:6443' line: " server: https://{{ node_ipv4 }}:6443" - name: Get the cluster join token shell: cat /var/lib/rancher/k3s/server/node-token register: k3s_token changed_when: false - name: Set fact for join token and manager IP set_fact: k3s_node_token: "{{ k3s_token.stdout }}" manager_ip: "{{ node_ipv4 }}" - name: Add the join token and manager IP to hostvars for workers add_host: name: "cluster_primary" groups: join_info k3s_node_token: "{{ k3s_node_token }}" k3s_manager_ip: "{{ manager_ip }}" - name: Install and join worker nodes to the control plane hosts: workers become: yes vars: k3s_version: v1.29.4+k3s1 tasks: - name: Install required dependencies apt: name: [curl] state: present update_cache: yes - name: Ensure /home/{{ ansible_user }}/k3s directory exists file: path: /home/{{ ansible_user }}/k3s state: directory owner: root group: root mode: '0777' - name: Set manager join information set_fact: k3s_node_token: "{{ hostvars[groups['join_info'][0]]['k3s_node_token'] }}" k3s_manager_ip: "{{ hostvars[groups['join_info'][0]]['k3s_manager_ip'] }}" - name: Fail if manager's IP is not available fail: msg: "Could not determine manager's IP for joining cluster!" when: k3s_manager_ip is not defined - name: Install k3s agent (worker) shell: | curl -sfL https://get.k3s.io | K3S_URL=https://{{ k3s_manager_ip }}:6443 K3S_TOKEN={{ k3s_node_token }} INSTALL_K3S_VERSION={{ k3s_version }} sh - args: creates: /usr/local/bin/k3s-agent - name: Gather architecture for kubectl set_fact: kubectl_arch: | {% if ansible_architecture == "x86_64" %} amd64 {% elif "armv7l" in ansible_architecture %} arm {% elif "aarch64" in ansible_architecture %} arm64 {% else %} {{ ansible_architecture }} {% endif %} - name: Download latest kubectl binary (worker) shell: | curl -Lo /usr/local/bin/kubectl "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/{{ kubectl_arch }}/kubectl" args: creates: /usr/local/bin/kubectl - name: Make kubectl executable (worker) file: path: /usr/local/bin/kubectl mode: '0755' owner: root group: root - name: Verify cluster nodes from manager node and configure storage hosts: manager become: yes vars: kubeconfig_file: "/home/{{ ansible_user }}/.kube/config" tasks: - name: Wait for all worker nodes to join the cluster become_user: "{{ ansible_user }}" shell: | for i in {1..10}; do [ $(kubectl --kubeconfig={{ kubeconfig_file }} get nodes | grep -c worker) -ge 1 ] && exit 0 sleep 15 done exit 1 register: wait_worker failed_when: wait_worker.rc != 0 - name: List all k3s nodes become_user: "{{ ansible_user }}" shell: kubectl --kubeconfig={{ kubeconfig_file }} get nodes -o wide register: all_nodes - name: Show current k3s cluster nodes debug: var: all_nodes.stdout - name: Create StorageClass for /home/ansible/k3s INLINE vars: ansible_python_interpreter: /opt/ansible-venv/bin/python kubernetes.core.k8s: kubeconfig: "{{ kubeconfig_file }}" definition: apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-pvs provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer