diff --git a/roles/scale_ocp_workers/README.md b/roles/scale_ocp_workers/README.md index 8907d31..cb5b27f 100644 --- a/roles/scale_ocp_workers/README.md +++ b/roles/scale_ocp_workers/README.md @@ -26,6 +26,22 @@ Scale to 5 workers with a different instance type: worker_machineset_suffix: compute ``` +Scale to 3 workers and enable worker-only mode (compact/SNO clusters): + +```yaml +- name: Scale OCP workers with worker-only mode + ansible.builtin.include_role: + name: agnosticd.cloud_provider_aws.scale_ocp_workers + vars: + worker_instance_count: 3 + worker_only: true +``` + +When `worker_only: true`, the role will: +1. Remove the `node-role.kubernetes.io/worker` label from control-plane nodes +2. Set `mastersSchedulable: false` on the Scheduler object (adds `NoSchedule` taint automatically) +3. Drain control-plane nodes to move existing workloads to the new workers (set `worker_drain_control_plane: false` to skip) + ## Variables See [defaults/main.yml](defaults/main.yml) for all variables and their defaults. diff --git a/roles/scale_ocp_workers/defaults/main.yml b/roles/scale_ocp_workers/defaults/main.yml index f7e873f..aa53926 100644 --- a/roles/scale_ocp_workers/defaults/main.yml +++ b/roles/scale_ocp_workers/defaults/main.yml @@ -30,3 +30,11 @@ worker_scale_approve_csrs: true worker_scale_csr_wait_seconds: 180 worker_scale_csr_retries: 30 worker_scale_csr_delay: 10 + +# Worker-only mode: removes worker label from control-plane nodes +# and sets mastersSchedulable: false on the Scheduler object +worker_only: false + +# Drain control-plane nodes to move existing workloads to dedicated workers +# Only applies when worker_only is true +worker_drain_control_plane: true diff --git a/roles/scale_ocp_workers/tasks/configure_worker_only.yml b/roles/scale_ocp_workers/tasks/configure_worker_only.yml new file mode 100644 index 0000000..5904b76 --- /dev/null +++ b/roles/scale_ocp_workers/tasks/configure_worker_only.yml @@ -0,0 +1,92 @@ +--- +# configure_worker_only.yml +# Removes worker role from control-plane nodes, disables master scheduling, +# and optionally drains control-plane nodes to move workloads to dedicated workers. + +- name: Get control-plane nodes + kubernetes.core.k8s_info: + api_version: v1 + kind: Node + label_selectors: + - "node-role.kubernetes.io/control-plane" + register: r_control_plane_nodes + +- name: Get master-labeled nodes + kubernetes.core.k8s_info: + api_version: v1 + kind: Node + label_selectors: + - "node-role.kubernetes.io/master" + register: r_master_nodes + +- name: Build list of control-plane nodes with worker label + ansible.builtin.set_fact: + control_plane_worker_nodes: >- + {%- set nodes = [] -%} + {%- set seen = [] -%} + {%- for node in (r_control_plane_nodes.resources + r_master_nodes.resources) -%} + {%- if node.metadata.name not in seen + and node.metadata.labels.get('node-role.kubernetes.io/worker') is defined -%} + {%- set _ = nodes.append(node.metadata.name) -%} + {%- set _ = seen.append(node.metadata.name) -%} + {%- endif -%} + {%- endfor -%} + {{ nodes }} + +- name: Display control-plane nodes with worker label + ansible.builtin.debug: + msg: "Control-plane nodes with worker label: {{ control_plane_worker_nodes }}" + +- name: Remove worker label from control-plane nodes + kubernetes.core.k8s: + api_version: v1 + kind: Node + name: "{{ item }}" + state: present + definition: + metadata: + labels: + node-role.kubernetes.io/worker: null + loop: "{{ control_plane_worker_nodes }}" + when: control_plane_worker_nodes | length > 0 + +- name: Set mastersSchedulable to false + kubernetes.core.k8s: + api_version: config.openshift.io/v1 + kind: Scheduler + name: cluster + state: present + definition: + spec: + mastersSchedulable: false + +- name: Drain and uncordon control-plane nodes + when: + - worker_drain_control_plane | default(true) | bool + - control_plane_worker_nodes | length > 0 + block: + - name: Drain control-plane node + kubernetes.core.k8s_drain: + name: "{{ item }}" + state: drain + delete_options: + ignore_daemonsets: true + delete_emptydir_data: true + force: true + loop: "{{ control_plane_worker_nodes }}" + # Ignore errors (e.g. apiserver PDB violation) + ignore_errors: true + + - name: Uncordon control-plane node + kubernetes.core.k8s_drain: + name: "{{ item }}" + state: uncordon + loop: "{{ control_plane_worker_nodes }}" + +- name: Display worker-only mode configuration results + ansible.builtin.debug: + msg: + - "Worker-only mode configured successfully" + - "Control-plane nodes with worker label removed: {{ control_plane_worker_nodes }}" + - "mastersSchedulable: false" + - "Control-plane nodes drained: {{ worker_drain_control_plane | default(true) | bool }}" diff --git a/roles/scale_ocp_workers/tasks/main.yml b/roles/scale_ocp_workers/tasks/main.yml index ebcd955..30ce3ec 100644 --- a/roles/scale_ocp_workers/tasks/main.yml +++ b/roles/scale_ocp_workers/tasks/main.yml @@ -78,3 +78,7 @@ - name: Wait for worker nodes to be Ready when: worker_instance_count | int > 0 ansible.builtin.include_tasks: wait_for_nodes.yml + + - name: Configure worker-only mode + when: worker_only | default(false) | bool + ansible.builtin.include_tasks: configure_worker_only.yml