├── .ansible-lint ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── .yamllint ├── LICENSE ├── README.md ├── defaults └── main.yml ├── handlers └── main.yml ├── meta └── main.yml ├── molecule └── default │ ├── calico.yml │ ├── converge.yml │ ├── molecule.yml │ └── requirements.yml ├── tasks ├── control-plane-setup.yml ├── kubelet-setup.yml ├── main.yml ├── node-setup.yml ├── setup-Debian.yml ├── setup-RedHat.yml └── sysctl-setup.yml ├── templates ├── apt-preferences-kubernetes.j2 └── kubeadm-kubelet-config.j2 └── vars ├── Debian.yml └── RedHat.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | skip_list: 2 | - 'yaml' 3 | - 'risky-shell-pipe' 4 | - 'role-name' 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | --- 3 | github: geerlingguy 4 | patreon: geerlingguy 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 'on': 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: "0 4 * * 3" 10 | 11 | defaults: 12 | run: 13 | working-directory: 'geerlingguy.kubernetes' 14 | 15 | jobs: 16 | 17 | lint: 18 | name: Lint 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check out the codebase. 22 | uses: actions/checkout@v4 23 | with: 24 | path: 'geerlingguy.kubernetes' 25 | 26 | - name: Set up Python 3. 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.x' 30 | 31 | - name: Install test dependencies. 32 | run: pip3 install yamllint 33 | 34 | - name: Lint code. 35 | run: | 36 | yamllint . 37 | 38 | molecule: 39 | name: Molecule 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | include: 44 | - distro: rockylinux9 45 | playbook: converge.yml 46 | - distro: ubuntu2004 47 | playbook: converge.yml 48 | - distro: debian11 49 | playbook: converge.yml 50 | 51 | - distro: debian11 52 | playbook: calico.yml 53 | 54 | steps: 55 | - name: Check out the codebase. 56 | uses: actions/checkout@v4 57 | with: 58 | path: 'geerlingguy.kubernetes' 59 | 60 | - name: Set up Python 3. 61 | uses: actions/setup-python@v5 62 | with: 63 | python-version: '3.x' 64 | 65 | - name: Install test dependencies. 66 | run: pip3 install ansible molecule molecule-plugins[docker] docker 67 | 68 | - name: Run Molecule tests. 69 | run: molecule test 70 | env: 71 | PY_COLORS: '1' 72 | ANSIBLE_FORCE_COLOR: '1' 73 | MOLECULE_DISTRO: ${{ matrix.distro }} 74 | MOLECULE_PLAYBOOK: ${{ matrix.playbook }} 75 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow requires a GALAXY_API_KEY secret present in the GitHub 3 | # repository or organization. 4 | # 5 | # See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy 6 | # See: https://github.com/ansible/galaxy/issues/46 7 | 8 | name: Release 9 | 'on': 10 | push: 11 | tags: 12 | - '*' 13 | 14 | defaults: 15 | run: 16 | working-directory: 'geerlingguy.kubernetes' 17 | 18 | jobs: 19 | 20 | release: 21 | name: Release 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Check out the codebase. 25 | uses: actions/checkout@v4 26 | with: 27 | path: 'geerlingguy.kubernetes' 28 | 29 | - name: Set up Python 3. 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: '3.x' 33 | 34 | - name: Install Ansible. 35 | run: pip3 install ansible-core 36 | 37 | - name: Trigger a new import on Galaxy. 38 | run: >- 39 | ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} 40 | $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) 41 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close inactive issues 3 | 'on': 4 | schedule: 5 | - cron: "55 3 * * 0" # semi-random time 6 | 7 | jobs: 8 | close-issues: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/stale@v8 15 | with: 16 | days-before-stale: 120 17 | days-before-close: 60 18 | exempt-issue-labels: bug,pinned,security,planned 19 | exempt-pr-labels: bug,pinned,security,planned 20 | stale-issue-label: "stale" 21 | stale-pr-label: "stale" 22 | stale-issue-message: | 23 | This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 24 | 25 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 26 | close-issue-message: | 27 | This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 28 | stale-pr-message: | 29 | This pr has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 30 | 31 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 32 | close-pr-message: | 33 | This pr has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 34 | repo-token: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | */__pycache__ 3 | *.pyc 4 | .cache 5 | 6 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: 6 | max: 150 7 | level: warning 8 | 9 | ignore: | 10 | .github/workflows/stale.yml 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Jeff Geerling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Role: Kubernetes 2 | 3 | [![CI](https://github.com/geerlingguy/ansible-role-kubernetes/actions/workflows/ci.yml/badge.svg)](https://github.com/geerlingguy/ansible-role-kubernetes/actions/workflows/ci.yml) 4 | 5 | An Ansible Role that installs [Kubernetes](https://kubernetes.io) on Linux. 6 | 7 | ## Requirements 8 | 9 | Requires a compatible [Container Runtime](https://kubernetes.io/docs/setup/production-environment/container-runtimes); recommended role for CRI installation: `geerlingguy.containerd`. 10 | 11 | ## Role Variables 12 | 13 | Available variables are listed below, along with default values (see `defaults/main.yml`): 14 | 15 | ```yaml 16 | kubernetes_packages: 17 | - name: kubelet 18 | state: present 19 | - name: kubectl 20 | state: present 21 | - name: kubeadm 22 | state: present 23 | - name: kubernetes-cni 24 | state: present 25 | ``` 26 | 27 | Kubernetes packages to be installed on the server. You can either provide a list of package names, or set `name` and `state` to have more control over whether the package is `present`, `absent`, `latest`, etc. 28 | 29 | ```yaml 30 | kubernetes_version: '1.32' 31 | kubernetes_version_rhel_package: '1.32' 32 | ``` 33 | 34 | The minor version of Kubernetes to install. The plain `kubernetes_version` is used to pin an apt package version on Debian, and as the Kubernetes version passed into the `kubeadm init` command (see `kubernetes_version_kubeadm`). The `kubernetes_version_rhel_package` variable must be a specific Kubernetes release, and is used to pin the version on Red Hat / CentOS servers. 35 | 36 | ```yaml 37 | kubernetes_role: control_plane 38 | ``` 39 | 40 | Whether the particular server will serve as a Kubernetes `control_plane` (default) or `node`. The control plane will have `kubeadm init` run on it to intialize the entire K8s control plane, while `node`s will have `kubeadm join` run on them to join them to the `control_plane`. 41 | 42 | ### Variables to configure kubeadm and kubelet with `kubeadm init` through a config file (recommended) 43 | 44 | With this role, `kubeadm init` will be run with `--config `. 45 | 46 | ```yaml 47 | kubernetes_kubeadm_kubelet_config_file_path: '/etc/kubernetes/kubeadm-kubelet-config.yaml' 48 | ``` 49 | 50 | Path for ``. If the directory does not exist, this role will create it. 51 | 52 | The following variables are parsed as options to . To understand its syntax, see [kubelet-integration](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/kubelet-integration) and [kubeadm-config-file](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/#config-file) . The skeleton (`apiVersion`, `kind`) of the config file will be created by this role, so do not define them within the variables. (See `templates/kubeadm-kubelet-config.j2`). 53 | 54 | ```yaml 55 | kubernetes_config_init_configuration: 56 | localAPIEndpoint: 57 | advertiseAddress: "{{ kubernetes_apiserver_advertise_address | default(ansible_default_ipv4.address, true) }}" 58 | ``` 59 | 60 | Defines the options under `kind: InitConfiguration`. Including `kubernetes_apiserver_advertise_address` here is for backward-compatibilty to older versions of this role, where `kubernetes_apiserver_advertise_address` was used with a command-line-option. 61 | 62 | ```yaml 63 | kubernetes_config_cluster_configuration: 64 | networking: 65 | podSubnet: "{{ kubernetes_pod_network.cidr }}" 66 | kubernetesVersion: "{{ kubernetes_version_kubeadm }}" 67 | ``` 68 | 69 | Options under `kind: ClusterConfiguration`. Including `kubernetes_pod_network.cidr` and `kubernetes_version_kubeadm` here are for backward-compatibilty to older versions of this role, where they were used with command-line-options. 70 | 71 | ```yaml 72 | kubernetes_config_kubelet_configuration: 73 | cgroupDriver: systemd 74 | ``` 75 | 76 | Options to configure kubelet on any nodes in your cluster through the `kubeadm init` process. For syntax options read the [kubelet config file](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file) and [kubelet integration](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/kubelet-integration) documentation. 77 | 78 | NOTE: This is the recommended way to do the kubelet-configuration. Most command-line-options are deprecated. 79 | 80 | NOTE: The recommended cgroupDriver depends on your [Container Runtime](https://kubernetes.io/docs/setup/production-environment/container-runtimes). When using this role with Docker instead of containerd, this value should be changed to `cgroupfs`. 81 | 82 | ```yaml 83 | kubernetes_config_kube_proxy_configuration: {} 84 | ``` 85 | 86 | Options to configure kubelet's proxy configuration in the `KubeProxyConfiguration` section of the kubelet configuration. 87 | 88 | ### Variables to configure kubeadm and kubelet through command-line-options 89 | 90 | ```yaml 91 | kubernetes_kubelet_extra_args: "" 92 | kubernetes_kubelet_extra_args_config_file: /etc/default/kubelet 93 | ``` 94 | 95 | Extra args to pass to `kubelet` during startup. E.g. to allow `kubelet` to start up even if there is swap is enabled on your server, set this to: `"--fail-swap-on=false"`. Or to specify the node-ip advertised by `kubelet`, set this to `"--node-ip={{ ansible_host }}"`. **This option is deprecated. Please use `kubernetes_config_kubelet_configuration` instead.** 96 | 97 | ```yaml 98 | kubernetes_kubeadm_init_extra_opts: "" 99 | ``` 100 | 101 | Extra args to pass to `kubeadm init` during K8s control plane initialization. E.g. to specify extra Subject Alternative Names for API server certificate, set this to: `"--apiserver-cert-extra-sans my-custom.host"` 102 | 103 | ```yaml 104 | kubernetes_join_command_extra_opts: "" 105 | ``` 106 | 107 | Extra args to pass to the generated `kubeadm join` command during K8s node initialization. E.g. to ignore certain preflight errors like swap being enabled, set this to: `--ignore-preflight-errors=Swap` 108 | 109 | ### Additional variables 110 | 111 | ```yaml 112 | kubernetes_allow_pods_on_control_plane: true 113 | ``` 114 | 115 | Whether to remove the taint that denies pods from being deployed to the Kubernetes control plane. If you have a single-node cluster, this should definitely be `True`. Otherwise, set to `False` if you want a dedicated Kubernetes control plane which doesn't run any other pods. 116 | 117 | ```yaml 118 | kubernetes_pod_network: 119 | # Flannel CNI. 120 | cni: 'flannel' 121 | cidr: '10.244.0.0/16' 122 | # 123 | # Calico CNI. 124 | # cni: 'calico' 125 | # cidr: '192.168.0.0/16' 126 | # 127 | # Weave CNI. 128 | # cni: 'weave' 129 | # cidr: '192.168.0.0/16' 130 | ``` 131 | 132 | This role currently supports `flannel` (default), `calico` or `weave` for cluster pod networking. Choose only one for your cluster; converting between them is not done automatically and could result in broken networking; if you need to switch from one to another, it should be done outside of this role. 133 | 134 | ```yaml 135 | kubernetes_apiserver_advertise_address: ''` 136 | kubernetes_version_kubeadm: 'stable-{{ kubernetes_version }}'` 137 | kubernetes_ignore_preflight_errors: 'all' 138 | ``` 139 | 140 | Options passed to `kubeadm init` when initializing the Kubernetes control plane. The `kubernetes_apiserver_advertise_address` defaults to `ansible_default_ipv4.address` if it's left empty. 141 | 142 | ```yaml 143 | kubernetes_apt_release_channel: "stable" 144 | kubernetes_apt_repository: "https://pkgs.k8s.io/core:/{{ kubernetes_apt_release_channel }}:/v{{ kubernetes_version }}/deb/" 145 | ``` 146 | 147 | Apt repository options for Kubernetes installation. 148 | 149 | ```yaml 150 | kubernetes_yum_base_url: "https://pkgs.k8s.io/core:/stable:/v{{ kubernetes_version }}/rpm/" 151 | kubernetes_yum_gpg_key: "https://pkgs.k8s.io/core:/stable:/v{{ kubernetes_version }}/rpm/repodata/repomd.xml.key" 152 | kubernetes_yum_gpg_check: true 153 | kubernetes_yum_repo_gpg_check: true 154 | ``` 155 | 156 | Yum repository options for Kubernetes installation. You can change `kubernete_yum_gpg_key` to a different url if you are behind a firewall or provide a trustworthy mirror. Usually in combination with changing `kubernetes_yum_base_url` as well. 157 | 158 | ```yaml 159 | kubernetes_flannel_manifest_file: https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 160 | ``` 161 | 162 | Flannel manifest file to apply to the Kubernetes cluster to enable networking. You can copy your own files to your server and apply them instead, if you need to customize the Flannel networking configuration. 163 | 164 | ```yaml 165 | kubernetes_calico_manifest_file: https://projectcalico.docs.tigera.io/manifests/calico.yaml 166 | ``` 167 | 168 | Calico manifest file to apply to the Kubernetes cluster (if using Calico instead of Flannel). 169 | 170 | ## Dependencies 171 | 172 | None. 173 | 174 | ## Example Playbooks 175 | 176 | ### Single node (control-plane-only) cluster 177 | 178 | ```yaml 179 | - hosts: all 180 | 181 | vars: 182 | kubernetes_allow_pods_on_control_plane: true 183 | 184 | roles: 185 | - geerlingguy.docker 186 | - geerlingguy.kubernetes 187 | ``` 188 | 189 | ### Two or more nodes (single control-plane) cluster 190 | 191 | Control plane inventory vars: 192 | 193 | ```yaml 194 | kubernetes_role: "control_plane" 195 | ``` 196 | 197 | Node(s) inventory vars: 198 | 199 | ```yaml 200 | kubernetes_role: "node" 201 | ``` 202 | 203 | Playbook: 204 | 205 | ```yaml 206 | - hosts: all 207 | 208 | vars: 209 | kubernetes_allow_pods_on_control_plane: true 210 | 211 | roles: 212 | - geerlingguy.docker 213 | - geerlingguy.kubernetes 214 | ``` 215 | 216 | Then, log into the Kubernetes control plane, and run `kubectl get nodes` as root, and you should see a list of all the servers. 217 | 218 | ## License 219 | 220 | MIT / BSD 221 | 222 | ## Author Information 223 | 224 | This role was created in 2018 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). 225 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kubernetes_packages: 3 | - name: kubelet 4 | state: present 5 | - name: kubectl 6 | state: present 7 | - name: kubeadm 8 | state: present 9 | - name: kubernetes-cni 10 | state: present 11 | 12 | kubernetes_version: '1.32' 13 | kubernetes_version_rhel_package: '1.32' 14 | 15 | kubernetes_role: control_plane 16 | 17 | # This is deprecated. Please use kubernetes_config_kubelet_configuration instead. 18 | kubernetes_kubelet_extra_args: "" 19 | 20 | kubernetes_kubeadm_init_extra_opts: "" 21 | kubernetes_join_command_extra_opts: "" 22 | kubernetes_allow_pods_on_control_plane: true 23 | kubernetes_pod_network: 24 | # Flannel CNI. 25 | cni: 'flannel' 26 | cidr: '10.244.0.0/16' 27 | # Calico CNI. 28 | # cni: 'calico' 29 | # cidr: '192.168.0.0/16' 30 | 31 | kubernetes_kubeadm_kubelet_config_file_path: '/etc/kubernetes/kubeadm-kubelet-config.yaml' 32 | kubernetes_config_kubelet_configuration: 33 | cgroupDriver: "systemd" 34 | 35 | kubernetes_config_init_configuration: 36 | localAPIEndpoint: 37 | advertiseAddress: "{{ kubernetes_apiserver_advertise_address | default(ansible_default_ipv4.address, true) }}" 38 | # if you use the next lines, remove the command line argument below 39 | # nodeRegistration: 40 | # ignorePreflightErrors: 41 | # - all 42 | 43 | kubernetes_config_cluster_configuration: 44 | networking: 45 | podSubnet: "{{ kubernetes_pod_network.cidr }}" 46 | kubernetesVersion: "{{ kubernetes_version_kubeadm }}" 47 | 48 | kubernetes_config_kube_proxy_configuration: {} 49 | 50 | kubernetes_apiserver_advertise_address: '' 51 | kubernetes_version_kubeadm: 'stable-{{ kubernetes_version }}' 52 | kubernetes_ignore_preflight_errors: 'all' 53 | 54 | kubernetes_apt_release_channel: "stable" 55 | kubernetes_apt_repository: "https://pkgs.k8s.io/core:/{{ kubernetes_apt_release_channel }}:/v{{ kubernetes_version }}/deb/" 56 | 57 | kubernetes_yum_base_url: "https://pkgs.k8s.io/core:/stable:/v{{ kubernetes_version }}/rpm/" 58 | kubernetes_yum_gpg_key: "https://pkgs.k8s.io/core:/stable:/v{{ kubernetes_version }}/rpm/repodata/repomd.xml.key" 59 | kubernetes_yum_gpg_check: true 60 | kubernetes_yum_repo_gpg_check: true 61 | 62 | # Flannel config file. 63 | kubernetes_flannel_manifest_file: https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml 64 | 65 | # Calico config file. 66 | kubernetes_calico_manifest_file: https://projectcalico.docs.tigera.io/manifests/calico.yaml 67 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart kubelet 3 | service: name=kubelet state=restarted 4 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: [] 3 | 4 | galaxy_info: 5 | role_name: kubernetes 6 | author: geerlingguy 7 | description: Kubernetes for Linux. 8 | company: "Midwestern Mac, LLC" 9 | license: "license (BSD, MIT)" 10 | min_ansible_version: 2.10 11 | platforms: 12 | - name: Debian 13 | versions: 14 | - stretch 15 | - buster 16 | - bullseye 17 | - name: Ubuntu 18 | versions: 19 | - xenial 20 | - bionic 21 | - focal 22 | - jammy 23 | galaxy_tags: 24 | - system 25 | - containers 26 | - docker 27 | - rkt 28 | - orchestration 29 | - kubernetes 30 | - k8s 31 | - paas 32 | - saas 33 | - hosting 34 | -------------------------------------------------------------------------------- /molecule/default/calico.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | #become: true 5 | 6 | vars: 7 | kubernetes_pod_network: 8 | cni: 'calico' 9 | cidr: '192.168.0.0/16' 10 | 11 | # Allow swap in test environments (hard to control in some envs). 12 | kubernetes_config_kubelet_configuration: 13 | cgroupDriver: "systemd" 14 | failSwapOn: false 15 | cgroupsPerQOS: true 16 | enforceNodeAllocatable: ['pods'] 17 | containerd_config_cgroup_driver_systemd: true 18 | 19 | pre_tasks: 20 | - name: Update apt cache. 21 | apt: update_cache=true cache_valid_time=600 22 | when: ansible_os_family == 'Debian' 23 | 24 | - name: Ensure test dependencies are installed (RedHat). 25 | package: name=iproute state=present 26 | when: ansible_os_family == 'RedHat' 27 | 28 | - name: Ensure test dependencies are installed (Debian). 29 | package: name=iproute2 state=present 30 | when: ansible_os_family == 'Debian' 31 | 32 | - name: Gather facts. 33 | action: setup 34 | 35 | roles: 36 | - role: geerlingguy.containerd 37 | - role: geerlingguy.kubernetes 38 | 39 | post_tasks: 40 | - name: Get cluster info. 41 | command: kubectl cluster-info 42 | changed_when: false 43 | register: kubernetes_info 44 | 45 | - name: Print cluster info. 46 | debug: var=kubernetes_info.stdout 47 | 48 | - name: Get all running pods. 49 | command: kubectl get pods --all-namespaces 50 | changed_when: false 51 | register: kubernetes_pods 52 | 53 | - name: Print list of running pods. 54 | debug: var=kubernetes_pods.stdout 55 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | #become: true 5 | 6 | vars: 7 | # Allow swap in test environments (hard to control in some envs). 8 | kubernetes_config_kubelet_configuration: 9 | cgroupDriver: "systemd" 10 | failSwapOn: false 11 | cgroupsPerQOS: true 12 | enforceNodeAllocatable: ['pods'] 13 | containerd_config_cgroup_driver_systemd: true 14 | 15 | pre_tasks: 16 | - name: Update apt cache. 17 | apt: update_cache=true cache_valid_time=600 18 | when: ansible_os_family == 'Debian' 19 | 20 | - name: Ensure test dependencies are installed (RedHat). 21 | package: name=iproute state=present 22 | when: ansible_os_family == 'RedHat' 23 | 24 | - name: Ensure test dependencies are installed (Debian). 25 | package: name=iproute2 state=present 26 | when: ansible_os_family == 'Debian' 27 | 28 | - name: Gather facts. 29 | action: setup 30 | 31 | roles: 32 | - role: geerlingguy.containerd 33 | - role: geerlingguy.kubernetes 34 | 35 | post_tasks: 36 | - name: Get cluster info. 37 | command: kubectl cluster-info 38 | changed_when: false 39 | register: kubernetes_info 40 | 41 | - name: Print cluster info. 42 | debug: var=kubernetes_info.stdout 43 | 44 | - name: Get all running pods. 45 | command: kubectl get pods --all-namespaces 46 | changed_when: false 47 | register: kubernetes_pods 48 | 49 | - name: Print list of running pods. 50 | debug: var=kubernetes_pods.stdout 51 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | role_name_check: 1 3 | dependency: 4 | name: galaxy 5 | options: 6 | ignore-errors: true 7 | driver: 8 | name: docker 9 | platforms: 10 | - name: instance 11 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-rockylinux9}-ansible:latest" 12 | command: ${MOLECULE_DOCKER_COMMAND:-""} 13 | volumes: 14 | - /sys/fs/cgroup:/sys/fs/cgroup:rw 15 | - /var/lib/containerd 16 | cgroupns_mode: host 17 | privileged: true 18 | pre_build_image: true 19 | provisioner: 20 | name: ansible 21 | playbooks: 22 | converge: ${MOLECULE_PLAYBOOK:-converge.yml} 23 | -------------------------------------------------------------------------------- /molecule/default/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - src: geerlingguy.containerd 3 | -------------------------------------------------------------------------------- /tasks/control-plane-setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create the directory for the kubernetes_config_file 3 | file: 4 | path: "{{ kubernetes_kubeadm_kubelet_config_file_path | dirname }}" 5 | state: directory 6 | 7 | - name: Deploy the config-file for kubeadm and kubelet 8 | template: 9 | src: "kubeadm-kubelet-config.j2" 10 | dest: "{{ kubernetes_kubeadm_kubelet_config_file_path }}" 11 | 12 | - name: Initialize Kubernetes control plane with kubeadm init 13 | command: > 14 | kubeadm init 15 | --config {{ kubernetes_kubeadm_kubelet_config_file_path }} 16 | {{ kubernetes_kubeadm_init_extra_opts }} 17 | register: kubeadmin_init 18 | when: (not kubernetes_init_stat.stat.exists) and (kubernetes_ignore_preflight_errors is not defined) 19 | 20 | - name: Initialize Kubernetes control plane with kubeadm init and ignore_preflight_errors 21 | command: > 22 | kubeadm init 23 | --config {{ kubernetes_kubeadm_kubelet_config_file_path }} 24 | --ignore-preflight-errors={{ kubernetes_ignore_preflight_errors }} 25 | {{ kubernetes_kubeadm_init_extra_opts }} 26 | register: kubeadmin_init 27 | when: (not kubernetes_init_stat.stat.exists) and (kubernetes_ignore_preflight_errors is defined) 28 | 29 | - name: Print the init output to screen. 30 | debug: 31 | var: kubeadmin_init.stdout 32 | verbosity: 2 33 | when: not kubernetes_init_stat.stat.exists 34 | 35 | - name: Ensure .kube directory exists. 36 | file: 37 | path: ~/.kube 38 | state: directory 39 | mode: 0755 40 | 41 | - name: Symlink the kubectl admin.conf to ~/.kube/conf. 42 | file: 43 | src: /etc/kubernetes/admin.conf 44 | dest: ~/.kube/config 45 | state: link 46 | mode: 0644 47 | 48 | - name: Configure Flannel networking. 49 | command: "kubectl apply -f {{ kubernetes_flannel_manifest_file }}" 50 | register: flannel_result 51 | changed_when: "'created' in flannel_result.stdout" 52 | when: kubernetes_pod_network.cni == 'flannel' 53 | until: flannel_result is not failed 54 | retries: 12 55 | delay: 5 56 | 57 | - name: Configure Calico networking. 58 | command: "kubectl apply -f {{ kubernetes_calico_manifest_file }}" 59 | register: calico_result 60 | changed_when: "'created' in calico_result.stdout" 61 | when: kubernetes_pod_network.cni == 'calico' 62 | until: calico_result is not failed 63 | retries: 12 64 | delay: 5 65 | 66 | - name: Get Kubernetes version for Weave installation. 67 | shell: kubectl version | base64 | tr -d '\n' 68 | changed_when: false 69 | register: kubectl_version 70 | when: kubernetes_pod_network.cni == 'weave' 71 | until: kubectl_version is not failed 72 | retries: 12 73 | delay: 5 74 | 75 | - name: Configure Weave networking. 76 | command: "{{ item }}" 77 | with_items: 78 | - "kubectl apply -f https://cloud.weave.works/k8s/net?k8s-version={{ kubectl_version.stdout_lines[0] }}" 79 | register: weave_result 80 | changed_when: "'created' in weave_result.stdout" 81 | when: kubernetes_pod_network.cni == 'weave' 82 | 83 | # TODO: Check if taint exists with something like `kubectl describe nodes` 84 | # instead of using kubernetes_init_stat.stat.exists check. 85 | - name: Allow pods on control plane (if configured). 86 | command: "kubectl taint nodes --all node-role.kubernetes.io/control-plane-" 87 | when: 88 | - kubernetes_allow_pods_on_control_plane | bool 89 | - not kubernetes_init_stat.stat.exists 90 | -------------------------------------------------------------------------------- /tasks/kubelet-setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ---- DEPRECATED ---------------- 4 | # 5 | # Most of the kubernetes_kubelet_extra_args are deprecated. See https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet for details. 6 | # Use the kubernetes_kubelet_config variable instead, which will be used to create the kubelet config file. 7 | 8 | - name: Check for existence of kubelet environment file. (deprecated) 9 | stat: 10 | path: '{{ kubelet_environment_file_path }}' 11 | register: kubelet_environment_file 12 | 13 | - name: Set facts for KUBELET_EXTRA_ARGS task if environment file exists. (deprecated) 14 | set_fact: 15 | kubelet_args_path: '{{ kubelet_environment_file_path }}' 16 | kubelet_args_line: "{{ 'KUBELET_EXTRA_ARGS=' + kubernetes_kubelet_extra_args }}" 17 | kubelet_args_regexp: '^KUBELET_EXTRA_ARGS=' 18 | when: kubelet_environment_file.stat.exists 19 | 20 | - name: Set facts for KUBELET_EXTRA_ARGS task if environment file doesn't exist. (deprecated) 21 | set_fact: 22 | kubelet_args_path: '/etc/systemd/system/kubelet.service.d/10-kubeadm.conf' 23 | kubelet_args_line: "{{ 'Environment=\"KUBELET_EXTRA_ARGS=' + kubernetes_kubelet_extra_args + '\"' }}" 24 | kubelet_args_regexp: '^Environment="KUBELET_EXTRA_ARGS=' 25 | when: not kubelet_environment_file.stat.exists 26 | 27 | - name: Configure KUBELET_EXTRA_ARGS. (deprecated) 28 | lineinfile: 29 | path: '{{ kubelet_args_path }}' 30 | line: '{{ kubelet_args_line }}' 31 | regexp: '{{ kubelet_args_regexp }}' 32 | state: present 33 | mode: 0644 34 | register: kubelet_extra_args 35 | when: kubernetes_kubelet_extra_args|length > 0 36 | 37 | - name: Reload systemd unit if args were changed. (deprecated) 38 | systemd: 39 | state: restarted 40 | daemon_reload: true 41 | name: kubelet 42 | when: kubelet_extra_args is changed 43 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Include OS-specific variables. 3 | include_vars: "{{ ansible_os_family }}.yml" 4 | 5 | - include_tasks: setup-RedHat.yml 6 | when: ansible_os_family == 'RedHat' 7 | 8 | - include_tasks: setup-Debian.yml 9 | when: ansible_os_family == 'Debian' 10 | 11 | - name: Ensure dependencies are installed. 12 | package: name=curl state=present 13 | 14 | - name: Install Kubernetes packages. 15 | package: 16 | name: "{{ item.name | default(item) }}" 17 | state: "{{ item.state | default('present') }}" 18 | notify: restart kubelet 19 | with_items: "{{ kubernetes_packages }}" 20 | 21 | - include_tasks: sysctl-setup.yml 22 | 23 | - include_tasks: kubelet-setup.yml # deprecated 24 | when: kubernetes_kubelet_extra_args|length > 0 25 | 26 | - name: Ensure kubelet is started and enabled at boot. 27 | service: 28 | name: kubelet 29 | state: started 30 | enabled: true 31 | 32 | - name: Check if Kubernetes has already been initialized. 33 | stat: 34 | path: /etc/kubernetes/admin.conf 35 | register: kubernetes_init_stat 36 | 37 | # Set up control plane. 38 | - include_tasks: control-plane-setup.yml 39 | when: kubernetes_role == 'control_plane' 40 | 41 | # Set up nodes. 42 | - name: Get the kubeadm join command from the Kubernetes control plane. 43 | command: kubeadm token create --print-join-command 44 | changed_when: false 45 | when: kubernetes_role == 'control_plane' 46 | register: kubernetes_join_command_result 47 | 48 | - name: Set the kubeadm join command globally. 49 | set_fact: 50 | kubernetes_join_command: > 51 | {{ kubernetes_join_command_result.stdout }} 52 | {{ kubernetes_join_command_extra_opts }} 53 | when: kubernetes_join_command_result.stdout is defined 54 | delegate_to: "{{ item }}" 55 | delegate_facts: true 56 | with_items: "{{ groups['all'] }}" 57 | 58 | - include_tasks: node-setup.yml 59 | when: kubernetes_role == 'node' 60 | -------------------------------------------------------------------------------- /tasks/node-setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Join node to Kubernetes control plane. 3 | shell: > 4 | {{ kubernetes_join_command }} 5 | creates=/etc/kubernetes/kubelet.conf 6 | tags: ['skip_ansible_lint'] 7 | -------------------------------------------------------------------------------- /tasks/setup-Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure dependencies are installed. 3 | apt: 4 | name: 5 | - apt-transport-https 6 | - ca-certificates 7 | - python3-debian 8 | state: present 9 | 10 | - name: Add Kubernetes repository. 11 | deb822_repository: 12 | name: kubernetes 13 | types: deb 14 | uris: "{{ kubernetes_apt_repository }}" 15 | suites: / 16 | signed_by: "{{ kubernetes_apt_repository }}/Release.key" 17 | register: kubernetes_repository 18 | 19 | - name: Update Apt cache. 20 | apt: 21 | update_cache: true 22 | when: kubernetes_repository.changed 23 | 24 | - name: Add Kubernetes apt preferences file to pin a version. 25 | template: 26 | src: apt-preferences-kubernetes.j2 27 | dest: /etc/apt/preferences.d/kubernetes 28 | mode: 0644 29 | -------------------------------------------------------------------------------- /tasks/setup-RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure Kubernetes repository exists. 3 | yum_repository: 4 | name: kubernetes 5 | description: Kubernetes 6 | enabled: true 7 | gpgcheck: "{{ kubernetes_yum_gpg_check }}" 8 | repo_gpgcheck: "{{ kubernetes_yum_repo_gpg_check }}" 9 | baseurl: "{{ kubernetes_yum_base_url }}" 10 | gpgkey: "{{ kubernetes_yum_gpg_key }}" 11 | 12 | - name: Add Kubernetes GPG keys. 13 | rpm_key: 14 | key: "{{ kubernetes_yum_gpg_key }}" 15 | state: present 16 | register: kubernetes_rpm_key 17 | 18 | - name: Make cache if Kubernetes GPG key changed. 19 | command: "yum -q makecache -y --disablerepo='*' --enablerepo='kubernetes'" 20 | when: kubernetes_rpm_key is changed 21 | -------------------------------------------------------------------------------- /tasks/sysctl-setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure procps is installed. 3 | package: 4 | name: "{{ procps_package }}" 5 | state: present 6 | when: > 7 | ansible_distribution != 'Debian' 8 | or ansible_distribution_major_version | int < 10 9 | 10 | # See: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#letting-iptables-see-bridged-traffic 11 | - name: Let iptables see bridged traffic. 12 | sysctl: 13 | name: "{{ item }}" 14 | value: '1' 15 | state: present 16 | loop: 17 | - net.bridge.bridge-nf-call-iptables 18 | - net.bridge.bridge-nf-call-ip6tables 19 | when: > 20 | ansible_distribution != 'Debian' 21 | or ansible_distribution_major_version | int < 10 22 | -------------------------------------------------------------------------------- /templates/apt-preferences-kubernetes.j2: -------------------------------------------------------------------------------- 1 | Package: kubectl 2 | Pin: version {{ kubernetes_version }}.* 3 | Pin-Priority: 1000 4 | 5 | Package: kubeadm 6 | Pin: version {{ kubernetes_version }}.* 7 | Pin-Priority: 1000 8 | 9 | Package: kubelet 10 | Pin: version {{ kubernetes_version }}.* 11 | Pin-Priority: 1000 12 | -------------------------------------------------------------------------------- /templates/kubeadm-kubelet-config.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kubeadm.k8s.io/v1beta3 3 | kind: InitConfiguration 4 | {{ kubernetes_config_init_configuration | to_nice_yaml }} 5 | --- 6 | apiVersion: kubeadm.k8s.io/v1beta3 7 | kind: ClusterConfiguration 8 | {{ kubernetes_config_cluster_configuration | to_nice_yaml }} 9 | {% if kubernetes_config_kubelet_configuration|length > 0 %} 10 | --- 11 | apiVersion: kubelet.config.k8s.io/v1beta1 12 | kind: KubeletConfiguration 13 | {{ kubernetes_config_kubelet_configuration | to_nice_yaml }} 14 | {% endif %} 15 | {% if kubernetes_config_kube_proxy_configuration|length > 0 %} 16 | --- 17 | apiVersion: kubeproxy.config.k8s.io/v1alpha1 18 | kind: KubeProxyConfiguration 19 | {{ kubernetes_config_kube_proxy_configuration | to_nice_yaml }} 20 | {% endif %} 21 | -------------------------------------------------------------------------------- /vars/Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | procps_package: procps 3 | kubelet_environment_file_path: /etc/default/kubelet 4 | -------------------------------------------------------------------------------- /vars/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | procps_package: procps-ng 3 | kubelet_environment_file_path: /etc/sysconfig/kubelet 4 | --------------------------------------------------------------------------------