commit 95d2a1c75804c26306298287f44b64e3ea04cc47 Author: Urs Rudolph Date: Sun Mar 8 08:39:41 2026 +0100 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..d56e264 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Kubernetes on Raspberry Pi with Ansible - Usage Guide + +This guide details how to use the generated Ansible playbook to install Kubernetes on your Raspberry Pi cluster. + +## Prerequisites + +1. **Ansible Installed**: You need Ansible installed on your control machine (your laptop/desktop). + * **Debian/Ubuntu**: + ```bash + sudo apt update && sudo apt install ansible sshpass -y + ``` + * **Arch Linux**: + ```bash + sudo pacman -S ansible sshpass + ``` + * **Fedora**: + ```bash + sudo dnf install ansible sshpass + ``` +2. **SSH Access**: Ensure you have SSH access to all Raspberry Pi nodes. +3. **Hardware**: 4 Raspberry Pi nodes (1 Master, 3 Workers) with Raspberry Pi OS installed. + +## Configuration Steps + +### 1. Configure Inventory +Edit `inventory/hosts.ini` and replace the placeholder IP addresses with the actual IPs of your Raspberry Pis. + +```ini +[masters] +pi1 ansible_host=192.168.1.10 <-- Change to Master IP + +[workers] +pi2 ansible_host=192.168.1.11 <-- Change to Worker 1 IP +pi3 ansible_host=192.168.1.12 <-- Change to Worker 2 IP +pi4 ansible_host=192.168.1.13 <-- Change to Worker 3 IP +``` + +### 2. Configure Credentials +You need to set the SSH password for the `pi` user. We use Ansible Vault for security. + +1. Generate an encrypted password string: + ```bash + ansible-vault encrypt_string 'YOUR_ACTUAL_PASSWORD' --name 'vault_ssh_password' + ``` + *Replace `YOUR_ACTUAL_PASSWORD` with the real password.* + +2. Copy the output block and paste it into `group_vars/all.yml`, replacing the commented out section or just adding it. + + Example in `group_vars/all.yml`: + ```yaml + ansible_password: !vault | + $ANSIBLE_VAULT;1.1;AES256 + ... (encrypted string) ... + ``` + + Do the same for `vault_become_password` if your sudo password is different. If sudo password is same as ssh password, you can just set: + ```yaml + ansible_become_password: "{{ vault_ssh_password }}" + ``` + +### 3. Run the Playbook + +To start the installation, run: + +```bash +ansible-playbook site.yml --ask-vault-pass +``` + +*Note: Since you used `encrypt_string` without a password file, it might ask for a vault password if you set one. If you just used `encrypt_string`, you might need to provide the vault password you used to encrypt it.* + +**Alternative (Simpler for testing):** +If you don't want to use Vault yet, you can pass the password as an extra var (INSECURE - be careful with history): +```bash +ansible-playbook site.yml -e "ansible_password=yourpassword ansible_become_password=yourpassword" +``` + +## Verification + +After the playbook completes: + +1. **SSH into the Master Node**: + ```bash + ssh pi@ + ``` + +2. **Check Nodes**: + ```bash + kubectl get nodes + ``` + You should see 4 nodes with status `Ready`. + +3. **Check Pods**: + ```bash + kubectl get pods -A + ``` + Ensure `coredns` and `kube-flannel` are running. diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..71f17a9 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,7 @@ +[defaults] +inventory = ./inventory/hosts.ini +host_key_checking = False +retry_files_enabled = False +interpreter_python = auto_silent +stdout_callback = yaml +bin_ansible_callbacks = True diff --git a/group_vars/all.yml b/group_vars/all.yml new file mode 100644 index 0000000..6194d55 --- /dev/null +++ b/group_vars/all.yml @@ -0,0 +1,11 @@ +--- +ansible_user: pi +# Use ansible-vault to encrypt the password: +# ansible-vault encrypt_string 'your_password' --name 'vault_ssh_password' +# then put the result here. +# For now, we expect these variables to be defined, e.g. in a vault file or extra vars. +# ansible_password: "{{ vault_ssh_password }}" +# ansible_become_password: "{{ vault_become_password }}" + +# Kubernetes Version +k8s_version: "1.28.0-00" diff --git a/inventory/hosts.ini b/inventory/hosts.ini new file mode 100644 index 0000000..56ab1ad --- /dev/null +++ b/inventory/hosts.ini @@ -0,0 +1,10 @@ +[masters] +pi1 ansible_host=192.168.1.10 + +[workers] +pi2 ansible_host=192.168.1.11 +pi3 ansible_host=192.168.1.12 +pi4 ansible_host=192.168.1.13 + +[all:vars] +# These are placeholders, update with real IPs diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml new file mode 100644 index 0000000..b654004 --- /dev/null +++ b/roles/common/handlers/main.yml @@ -0,0 +1,10 @@ +--- +- name: restart containerd + service: + name: containerd + state: restarted + +- name: restart kubelet + service: + name: kubelet + state: restarted diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml new file mode 100644 index 0000000..d2edbfb --- /dev/null +++ b/roles/common/tasks/main.yml @@ -0,0 +1,129 @@ +--- +- name: Update apt cache + apt: + update_cache: yes + cache_valid_time: 3600 + +- name: Upgrade all packages + apt: + upgrade: dist + +- name: Install required system packages + apt: + name: + - apt-transport-https + - ca-certificates + - curl + - software-properties-common + - gnupg + state: present + +- name: Disable swap + command: swapoff -a + when: ansible_swaptotal_mb > 0 + +- name: Disable swap in dphys-swapfile + lineinfile: + path: /etc/dphys-swapfile + regexp: '^CONF_SWAPSIZE=' + line: 'CONF_SWAPSIZE=0' + notify: restart kubelet + +- name: Enable cgroup features in cmdline.txt + replace: + path: /boot/cmdline.txt + regexp: '^((?!.*\bcgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1\b).*)$' + replace: '\1 cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1' + register: cgroup_update + +- name: Reboot if cgroup features updated + reboot: + when: cgroup_update.changed + +- name: Load kernel modules for containerd + copy: + dest: /etc/modules-load.d/containerd.conf + content: | + overlay + br_netfilter + +- name: Load overlay module + modprobe: + name: overlay + state: present + +- name: Load br_netfilter module + modprobe: + name: br_netfilter + state: present + +- name: Configure sysctl params for Kubernetes + copy: + dest: /etc/sysctl.d/99-kubernetes-cri.conf + content: | + net.bridge.bridge-nf-call-iptables = 1 + net.ipv4.ip_forward = 1 + net.bridge.bridge-nf-call-ip6tables = 1 + register: sysctl_config + +- name: Apply sysctl params + command: sysctl --system + when: sysctl_config.changed + +- name: Install containerd + apt: + name: containerd + state: present + +- name: Create containerd config directory + file: + path: /etc/containerd + state: directory + +- name: Generate default containerd config + shell: containerd config default > /etc/containerd/config.toml + args: + creates: /etc/containerd/config.toml + +- name: Configure SystemdCgroup in containerd config + replace: + path: /etc/containerd/config.toml + regexp: 'SystemdCgroup = false' + replace: 'SystemdCgroup = true' + notify: restart containerd + +- name: Create keyrings directory + file: + path: /etc/apt/keyrings + state: directory + mode: '0755' + +- name: Download Kubernetes GPG key + get_url: + url: https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key + dest: /etc/apt/keyrings/kubernetes-apt-keyring.asc + mode: '0644' + +- name: Add Kubernetes apt repository + apt_repository: + repo: "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.asc] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /" + state: present + filename: kubernetes + +- name: Install Kubernetes binaries + apt: + name: + - kubelet + - kubeadm + - kubectl + state: present + update_cache: yes + +- name: Hold Kubernetes packages + dpkg_selections: + name: "{{ item }}" + selection: hold + loop: + - kubelet + - kubeadm + - kubectl diff --git a/roles/master/tasks/main.yml b/roles/master/tasks/main.yml new file mode 100644 index 0000000..bc75027 --- /dev/null +++ b/roles/master/tasks/main.yml @@ -0,0 +1,37 @@ +--- +- name: Initialize Kubernetes Control Plane + command: kubeadm init --pod-network-cidr=10.244.0.0/16 + args: + creates: /etc/kubernetes/admin.conf + register: kubeadm_init + +- name: Create .kube directory + file: + path: /home/{{ ansible_user }}/.kube + state: directory + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: 0755 + +- name: Copy admin.conf to user's kube config + copy: + src: /etc/kubernetes/admin.conf + dest: /home/{{ ansible_user }}/.kube/config + remote_src: yes + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + +- name: Install Flannel Pod Network + command: kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml + become: yes + become_user: "{{ ansible_user }}" + when: kubeadm_init.changed + +- name: Get join command + command: kubeadm token create --print-join-command + register: join_command_raw + +- name: Add dummy host with variable + add_host: + name: "K8S_TOKEN_HOLDER" + join_command: "{{ join_command_raw.stdout_lines[0] }}" diff --git a/roles/worker/tasks/main.yml b/roles/worker/tasks/main.yml new file mode 100644 index 0000000..733ea5b --- /dev/null +++ b/roles/worker/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- name: Join worker to cluster + command: "{{ hostvars['K8S_TOKEN_HOLDER']['join_command'] }}" + args: + creates: /etc/kubernetes/kubelet.conf diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..79a920b --- /dev/null +++ b/site.yml @@ -0,0 +1,18 @@ +--- +- name: Prepare all nodes + hosts: all + become: yes + roles: + - common + +- name: Setup Master Node + hosts: masters + become: yes + roles: + - master + +- name: Setup Worker Nodes + hosts: workers + become: yes + roles: + - worker