- name: Provision VM's hosts: localhost gather_facts: false vars_files: - hypervisor.vcenter.yml - cluster.k3s.yml tasks: - name: Download OVF-template ansible.builtin.get_url: url: "https://{{ repo_username }}:{{ repo_password }}@{{ image.ova_url }}" dest: /scratch/image.ova - name: Deploy VM's from OVF-template community.vmware.vmware_deploy_ovf: hostname: "{{ hv.hostname }}" username: "{{ hv.username }}" password: "{{ hv_password }}" validate_certs: no datacenter: "{{ hv.datacenter }}" folder: "{{ hv.folder }}" cluster: "{{ hv.cluster }}" name: "{{ cluster.name | upper }}-{{ (item.ip | checksum)[-5:] | upper }}" datastore: "{{ hv.datastore }}" disk_provisioning: thin networks: "LAN": "{{ hv.network }}" power_on: yes ovf: /scratch/image.ova deployment_option: "{{ image.deployment_option }}" properties: guestinfo.hostname: "{{ cluster.name | upper }}-{{ (item.ip | checksum)[-5:] | upper }}" guestinfo.rootpw: "{{ root_password }}" guestinfo.rootsshkey: "{{ public_key }}" guestinfo.ntpserver: "{{ network.ntpserver }}" guestinfo.ipaddress: "{{ item.ip | ansible.utils.ipaddr('address') }}" guestinfo.prefixlength: "{{ item.ip | ansible.utils.ipaddr('prefix') }}" guestinfo.dnsserver: "{{ network.dnsserver }}" guestinfo.gateway: "{{ network.gateway }}" register: job_init async: 300 poll: 0 delegate_to: localhost loop: "{{ servers }}" - name: Pause to allow initial calls to complete ansible.builtin.pause: seconds: 10 - name: Poll for completion ansible.builtin.async_status: jid: "{{ item.ansible_job_id }}" register: job_poll retries: 5 delay: 100 until: job_poll.finished loop: "{{ job_init.results }}" loop_control: label: "{{ { 'ip': item.item.ip } }}" - name: Parse results into dictionary ansible.builtin.set_fact: nodes: "{{ nodes | default([]) + [ {'name': item.instance.hw_name, 'ip': item.item.item.ip | ansible.utils.ipaddr('address')} ] }}" loop: "{{ job_poll | json_query('results[*]') }}" loop_control: label: "{{ { 'name': item.instance.hw_name, 'ip': item.item.item.ip } }}" - name: Register new VM's in inventory ansible.builtin.add_host: name: "{{ item.name }}" ansible_host: "{{ item.ip }}" groups: k3s_ha loop: "{{ nodes }}" - name: Wait for systems to become reachable over SSH ansible.builtin.wait_for: host: "{{ item.ip }}" port: 22 timeout: 300 loop: "{{ nodes }}" - name: Scan public keys ansible.builtin.shell: cmd: "ssh-keyscan -t rsa {{ item.ip }}" register: publickeys loop: "{{ nodes }}" loop_control: label: "{{ item.ip }}" - name: Store public keys ansible.builtin.known_hosts: name: "{{ item.item.name | lower }}" key: "{{ item.item.name | lower }},{{ item.stdout }}" state: present path: ~/.ssh/known_hosts loop: "{{ publickeys.results }}" loop_control: label: "{{ { 'name': item.item.name, 'ip': item.item.ip } }}" - name: Provision Kubernetes hosts: k3s_ha gather_facts: true vars_files: - cluster.k3s.yml tasks: - block: - name: Initial node -- Install K3s binary ansible.builtin.shell: cmd: "curl -sfL https://get.k3s.io | sh -s - server --cluster-init --disable local-storage --tls-san {{ cluster.virtualip | ansible.utils.ipaddr('address') }}" - name: Initial node -- Retrieve token ansible.builtin.slurp: src: /var/lib/rancher/k3s/server/token register: k3s_token - name: Initial node -- Retrieve kubeconfig ansible.builtin.shell: cmd: kubectl config view --raw register: k3s_kubeconfig - name: Initial node -- Store token ansible.builtin.set_fact: cluster: "{{ cluster | combine( { 'token': ( k3s_token.content | b64decode | trim ) }, { 'kubeconfig': ( k3s_kubeconfig.stdout ) } ) }}" - block: - name: Install 'kube-vip' -- Retrieve RBAC-manifest ansible.builtin.uri: url: https://kube-vip.io/manifests/rbac.yaml return_content: yes register: manifest_rbac - name: Install 'kube-vip' -- Pull image ansible.builtin.shell: cmd: ctr image pull ghcr.io/kube-vip/kube-vip:latest - name: Install 'kube-vip' -- Generate daemonSet-manifest ansible.builtin.shell: cmd: "ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:latest vip /kube-vip manifest daemonset --interface {{ ansible_default_ipv4.interface }} --address {{ cluster.virtualip | ansible.utils.ipaddr('address') }} --inCluster --taint --controlplane --services --arp --leaderElection" register: manifest_daemonset - name: Install 'kube-vip' -- Inject manifest ansible.builtin.template: src: kube-vip.j2 dest: /var/lib/rancher/k3s/server/manifests/kube-vip.yml delegate_to: "{{ ansible_play_hosts[0] }}" run_once: true - name: All nodes -- Ensure API availability ansible.utils.cli_parse: command: "curl -k https://{{ cluster.virtualip | ansible.utils.ipaddr('address') }}:6443/livez?verbose" parser: name: ansible.utils.json set_fact: api_readycheck ignore_errors: yes until: api_readycheck.apiVersion is defined retries: 3 delay: 30 - block: - name: Additional nodes -- Install K3s binary ansible.builtin.shell: cmd: "curl -sfL https://get.k3s.io | sh -s - server --disable local-storage" executable: /bin/bash environment: K3S_TOKEN: "{{ cluster.token }}" # (hostvars[ansible_play_hosts[0]]).cluster.token K3S_URL: "{{ 'https://' + ( cluster.virtualip | ansible.utils.ipaddr('address') ) + ':6443' }}" throttle: 1 when: inventory_hostname != ansible_play_hosts[0] - name: Deploy applications hosts: localhost gather_facts: false vars_files: - applications.k3s.yml - cluster.k3s.yml tasks: - block: - name: Initialize tempfile ansible.builtin.tempfile: state: file register: kubeconfig - name: Store kubeconfig in tempfile ansible.builtin.copy: dest: "{{ kubeconfig.path }}" content: "{{ ( lookup('dict', hostvars) )[0].value.cluster.kubeconfig }}" mode: 0600 - name: Replace API url ansible.builtin.replace: path: "{{ kubeconfig.path }}" regexp: 'server: https:\/\/127\.0\.0\.1:6443$' replace: "server: https://{{ cluster.virtualip | ansible.utils.ipaddr('address') }}:6443" - name: Add Helm chart repositories kubernetes.core.helm_repository: name: "{{ item.name }}" repo_url: "{{ item.url }}" no_log: true loop: "{{ helm.repositories }}" - block: - name: Rancher Fleet -- Determine latest version ansible.builtin.uri: url: https://api.github.com/repos/rancher/fleet/releases/latest return_content: yes register: latest_release - name: Rancher Fleet -- Install Helm chart w/ prereqs kubernetes.core.helm: name: "{{ item.name }}" chart_ref: "{{ item.ref }}" namespace: fleet-system create_namespace: yes wait: yes kubeconfig: "{{ kubeconfig.path }}" values: "{{ item.setvalues }}" loop: - name: cert-manager ref: jetstack/cert-manager setvalues: installCRDs: true - name: fleet-crd ref: "https://github.com/rancher/fleet/releases/download/{{ latest_release.json.tag_name }}/fleet-crd-{{ latest_release.json.tag_name[1:] }}.tgz" setvalues: ~ - name: fleet ref: "https://github.com/rancher/fleet/releases/download/{{ latest_release.json.tag_name }}/fleet-{{ latest_release.json.tag_name[1:] }}.tgz" setvalues: ~