Add support for API servers on IPv6 addresses (#48)
* Remove duplicate file for deletion * Add support for IPv6 clusters To correctly escape IPv6 addresses when ports are used, they must be wrapped in square brackets [1]. This patch adds support for that, using Ansible's ipwrap filter [2]. [1]: https://datatracker.ietf.org/doc/html/rfc4038#section-5.1 [2]: http://docs.ansible.com/ansible/latest/collections/ansible/utils/docsite/filters_ipaddr.html#wrapping-ipv6-addresses-in-brackets * Do not abort other molecule jobs on failure * Fix cache keys for Vagrant boxes * Molecule: Derive overrides.yml location from scenario dir # Conflicts: # molecule/default/molecule.yml # molecule/ipv6/molecule.yml
This commit is contained in:
parent
9ff3bb6b87
commit
a1ef590442
@ -9,8 +9,9 @@ exclude_paths:
|
|||||||
# The "converge" and "reset" playbooks use import_playbook in
|
# The "converge" and "reset" playbooks use import_playbook in
|
||||||
# conjunction with the "env" lookup plugin, which lets the
|
# conjunction with the "env" lookup plugin, which lets the
|
||||||
# syntax check of ansible-lint fail.
|
# syntax check of ansible-lint fail.
|
||||||
- '**/converge.yml'
|
- 'molecule/**/converge.yml'
|
||||||
- '**/reset.yml'
|
- 'molecule/**/prepare.yml'
|
||||||
|
- 'molecule/**/reset.yml'
|
||||||
|
|
||||||
skip_list:
|
skip_list:
|
||||||
- 'fqcn-builtins'
|
- 'fqcn-builtins'
|
||||||
|
|||||||
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
@ -15,6 +15,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
scenario:
|
scenario:
|
||||||
- default
|
- default
|
||||||
|
- ipv6
|
||||||
- single_node
|
- single_node
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
env:
|
env:
|
||||||
@ -25,16 +26,19 @@ jobs:
|
|||||||
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2
|
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2
|
||||||
|
|
||||||
- name: Configure VirtualBox
|
- name: Configure VirtualBox
|
||||||
run: >-
|
run: |-
|
||||||
sudo mkdir -p /etc/vbox &&
|
sudo mkdir -p /etc/vbox
|
||||||
echo "* 192.168.30.0/24" | sudo tee -a /etc/vbox/networks.conf > /dev/null
|
cat <<EOF | sudo tee -a /etc/vbox/networks.conf > /dev/null
|
||||||
|
* 192.168.30.0/24
|
||||||
|
* fdad:bad:ba55::/64
|
||||||
|
EOF
|
||||||
|
|
||||||
- name: Cache Vagrant boxes
|
- name: Cache Vagrant boxes
|
||||||
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # 3.0.8
|
uses: actions/cache@fd5de65bc895cf536527842281bea11763fefd77 # 3.0.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.vagrant.d/boxes
|
~/.vagrant.d/boxes
|
||||||
key: vagrant-boxes-${{ hashFiles('**/Vagrantfile') }}
|
key: vagrant-boxes-${{ hashFiles('**/molecule.yml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
vagrant-boxes
|
vagrant-boxes
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ on processor architecture:
|
|||||||
## ✅ System requirements
|
## ✅ System requirements
|
||||||
|
|
||||||
- Deployment environment must have Ansible 2.4.0+. If you need a quick primer on Ansible [you can check out my docs and setting up Ansible](https://docs.technotim.live/posts/ansible-automation/).
|
- Deployment environment must have Ansible 2.4.0+. If you need a quick primer on Ansible [you can check out my docs and setting up Ansible](https://docs.technotim.live/posts/ansible-automation/).
|
||||||
|
Furthermore, the [`netaddr` package](https://pypi.org/project/netaddr/) must be available to Ansible. If you have installed Ansible via apt, this is already taken care of. If you have installed Ansible via `pip`, make sure to install `netaddr` into the respective virtual environment.
|
||||||
- `server` and `agent` nodes should have passwordless SSH access, if not you can supply arguments to provide credentials `--ask-pass --ask-become-pass` to each command.
|
- `server` and `agent` nodes should have passwordless SSH access, if not you can supply arguments to provide credentials `--ask-pass --ask-become-pass` to each command.
|
||||||
|
|
||||||
## 🚀 Getting Started
|
## 🚀 Getting Started
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
collections:
|
collections:
|
||||||
|
- name: ansible.utils
|
||||||
- name: community.general
|
- name: community.general
|
||||||
- name: ansible.posix
|
- name: ansible.posix
|
||||||
- name: kubernetes.core
|
- name: kubernetes.core
|
||||||
|
|||||||
@ -4,6 +4,7 @@ kind: Service
|
|||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
spec:
|
spec:
|
||||||
|
ipFamilyPolicy: PreferDualStack
|
||||||
selector:
|
selector:
|
||||||
app: nginx
|
app: nginx
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@ -8,6 +8,9 @@ We have these scenarios:
|
|||||||
|
|
||||||
- **default**:
|
- **default**:
|
||||||
A 3 control + 2 worker node cluster based very closely on the [sample inventory](../inventory/sample/).
|
A 3 control + 2 worker node cluster based very closely on the [sample inventory](../inventory/sample/).
|
||||||
|
- **ipv6**:
|
||||||
|
A cluster that is externally accessible via IPv6 ([more information](ipv6/README.md))
|
||||||
|
To save a bit of test time, this cluster is _not_ highly available, it consists of only one control and one worker node.
|
||||||
- **single_node**:
|
- **single_node**:
|
||||||
Very similar to the default scenario, but uses only a single node for all cluster functionality.
|
Very similar to the default scenario, but uses only a single node for all cluster functionality.
|
||||||
|
|
||||||
@ -32,6 +35,7 @@ To set the subnet up for use with VirtualBox, please make sure that `/etc/vbox/n
|
|||||||
|
|
||||||
```
|
```
|
||||||
* 192.168.30.0/24
|
* 192.168.30.0/24
|
||||||
|
* fdad:bad:ba55::/64
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install Python dependencies
|
### Install Python dependencies
|
||||||
|
|||||||
35
molecule/ipv6/README.md
Normal file
35
molecule/ipv6/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Sample IPv6 configuration for `k3s-ansible`
|
||||||
|
|
||||||
|
This scenario contains a cluster configuration which is _IPv6 first_, but still supports dual-stack networking with IPv4 for most things.
|
||||||
|
This means:
|
||||||
|
|
||||||
|
- The API server VIP is an IPv6 address.
|
||||||
|
- The MetalLB pool consists of both IPv4 and IPv4 addresses.
|
||||||
|
- Nodes as well as cluster-internal resources (pods and services) are accessible via IPv4 as well as IPv6.
|
||||||
|
|
||||||
|
## Network design
|
||||||
|
|
||||||
|
All IPv6 addresses used in this scenario share a single `/48` prefix: `fdad:bad:ba55`.
|
||||||
|
The following subnets are used:
|
||||||
|
|
||||||
|
- `fdad:bad:ba55:`**`0`**`::/64` is the subnet which contains the cluster components meant for external access.
|
||||||
|
That includes:
|
||||||
|
|
||||||
|
- The VIP for the Kubernetes API server: `fdad:bad:ba55::333`
|
||||||
|
- Services load-balanced by MetalLB: `fdad:bad:ba55::1b:0/112`
|
||||||
|
- Cluster nodes: `fdad:bad:ba55::de:0/112`
|
||||||
|
- The host executing Vagrant: `fdad:bad:ba55::1`
|
||||||
|
|
||||||
|
In a home lab setup, this might be your LAN.
|
||||||
|
|
||||||
|
- `fdad:bad:ba55:`**`4200`**`::/56` is used internally by the cluster for pods.
|
||||||
|
|
||||||
|
- `fdad:bad:ba55:`**`4300`**`::/108` is used internally by the cluster for services.
|
||||||
|
|
||||||
|
IPv4 networking is also available:
|
||||||
|
|
||||||
|
- The nodes have addresses inside `192.168.123.0/24`.
|
||||||
|
MetalLB also has a bit of address space in this range: `192.168.123.80-192.168.123.90`
|
||||||
|
- For pods and services, the k3s defaults (`10.42.0.0/16` and `10.43.0.0/16)` are used.
|
||||||
|
|
||||||
|
Note that the host running Vagrant is not part any of these IPv4 networks.
|
||||||
3
molecule/ipv6/host_vars/control1.yml
Normal file
3
molecule/ipv6/host_vars/control1.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
node_ipv4: 192.168.123.11
|
||||||
|
node_ipv6: fdad:bad:ba55::de:11
|
||||||
3
molecule/ipv6/host_vars/node1.yml
Normal file
3
molecule/ipv6/host_vars/node1.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
node_ipv4: 192.168.123.21
|
||||||
|
node_ipv6: fdad:bad:ba55::de:21
|
||||||
57
molecule/ipv6/molecule.yml
Normal file
57
molecule/ipv6/molecule.yml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: galaxy
|
||||||
|
driver:
|
||||||
|
name: vagrant
|
||||||
|
platforms:
|
||||||
|
- &control
|
||||||
|
name: control1
|
||||||
|
box: generic/ubuntu2204
|
||||||
|
memory: 2048
|
||||||
|
cpus: 2
|
||||||
|
config_options:
|
||||||
|
# We currently can not use public-key based authentication on Ubuntu 22.04,
|
||||||
|
# see: https://github.com/chef/bento/issues/1405
|
||||||
|
ssh.username: "vagrant"
|
||||||
|
ssh.password: "vagrant"
|
||||||
|
groups:
|
||||||
|
- k3s_cluster
|
||||||
|
- master
|
||||||
|
interfaces:
|
||||||
|
- network_name: private_network
|
||||||
|
ip: fdad:bad:ba55::de:11
|
||||||
|
- <<: *control
|
||||||
|
name: node1
|
||||||
|
groups:
|
||||||
|
- k3s_cluster
|
||||||
|
- node
|
||||||
|
interfaces:
|
||||||
|
- network_name: private_network
|
||||||
|
ip: fdad:bad:ba55::de:21
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
playbooks:
|
||||||
|
converge: ../resources/converge.yml
|
||||||
|
side_effect: ../resources/reset.yml
|
||||||
|
verify: ../resources/verify.yml
|
||||||
|
inventory:
|
||||||
|
links:
|
||||||
|
group_vars: ../../inventory/sample/group_vars
|
||||||
|
scenario:
|
||||||
|
test_sequence:
|
||||||
|
- dependency
|
||||||
|
- lint
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
|
- syntax
|
||||||
|
- create
|
||||||
|
- prepare
|
||||||
|
- converge
|
||||||
|
# idempotence is not possible with the playbook in its current form.
|
||||||
|
- verify
|
||||||
|
# We are repurposing side_effect here to test the reset playbook.
|
||||||
|
# This is why we do not run it before verify (which tests the cluster),
|
||||||
|
# but after the verify step.
|
||||||
|
- side_effect
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
43
molecule/ipv6/overrides.yml
Normal file
43
molecule/ipv6/overrides.yml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
- name: Apply overrides
|
||||||
|
hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Override host variables (1/2)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
# See: https://github.com/flannel-io/flannel/blob/67d603aaf45ef80f5dd39f43714fc5e6f8a637eb/Documentation/troubleshooting.md#Vagrant # noqa yaml[line-length]
|
||||||
|
flannel_iface: eth1
|
||||||
|
|
||||||
|
# The test VMs might be a bit slow, so we give them more time to join the cluster:
|
||||||
|
retry_count: 45
|
||||||
|
|
||||||
|
# IPv6 configuration
|
||||||
|
# ######################################################################
|
||||||
|
|
||||||
|
# The API server will be reachable on IPv6 only
|
||||||
|
apiserver_endpoint: fdad:bad:ba55::333
|
||||||
|
|
||||||
|
# We give MetalLB address space for both IPv4 and IPv6
|
||||||
|
metal_lb_ip_range:
|
||||||
|
- fdad:bad:ba55::1b:0/112
|
||||||
|
- 192.168.123.80-192.168.123.90
|
||||||
|
|
||||||
|
# k3s_node_ip is by default set to the IPv4 address of flannel_iface.
|
||||||
|
# We want IPv6 addresses here of course, so we just specify them
|
||||||
|
# manually below.
|
||||||
|
k3s_node_ip: "{{ node_ipv4 }},{{ node_ipv6 }}"
|
||||||
|
|
||||||
|
- name: Override host variables (2/2)
|
||||||
|
# Since "extra_args" depends on "k3s_node_ip" and "flannel_iface" we have
|
||||||
|
# to set this AFTER overriding the both of them.
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
# A few extra server args are necessary:
|
||||||
|
# - the network policy needs to be disabled.
|
||||||
|
# - we need to manually specify the subnets for services and pods, as
|
||||||
|
# the default has IPv4 ranges only.
|
||||||
|
extra_server_args: >-
|
||||||
|
{{ extra_args }}
|
||||||
|
--disable servicelb
|
||||||
|
--disable traefik
|
||||||
|
--disable-network-policy
|
||||||
|
--cluster-cidr=10.42.0.0/16,fdad:bad:ba55:4200::/56
|
||||||
|
--service-cidr=10.43.0.0/16,fdad:bad:ba55:4300::/108
|
||||||
51
molecule/ipv6/prepare.yml
Normal file
51
molecule/ipv6/prepare.yml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
- name: Apply overrides
|
||||||
|
ansible.builtin.import_playbook: >-
|
||||||
|
{{ lookup("ansible.builtin.env", "MOLECULE_SCENARIO_DIRECTORY") }}/overrides.yml
|
||||||
|
|
||||||
|
- name: Configure dual-stack networking
|
||||||
|
hosts: all
|
||||||
|
become: true
|
||||||
|
|
||||||
|
# Unfortunately, as of 2022-09, Vagrant does not support the configuration
|
||||||
|
# of both IPv4 and IPv6 addresses for a single network adapter. So we have
|
||||||
|
# to configure that ourselves.
|
||||||
|
# Moreover, we have to explicitly enable IPv6 for the loopback interface.
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Enable IPv6 for network interfaces
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: net.ipv6.conf.{{ item }}.disable_ipv6
|
||||||
|
value: "0"
|
||||||
|
with_items:
|
||||||
|
- all
|
||||||
|
- default
|
||||||
|
- lo
|
||||||
|
|
||||||
|
- name: Disable duplicate address detection
|
||||||
|
# Duplicate address detection did repeatedly fail within the virtual
|
||||||
|
# network. But since this setup does not use SLAAC anyway, we can safely
|
||||||
|
# disable it.
|
||||||
|
ansible.posix.sysctl:
|
||||||
|
name: net.ipv6.conf.{{ item }}.accept_dad
|
||||||
|
value: "0"
|
||||||
|
with_items:
|
||||||
|
- "{{ flannel_iface }}"
|
||||||
|
|
||||||
|
- name: Write IPv4 configuration
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 55-flannel-ipv4.yaml.j2
|
||||||
|
dest: /etc/netplan/55-flannel-ipv4.yaml
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
register: netplan_template
|
||||||
|
|
||||||
|
- name: Apply netplan configuration
|
||||||
|
# Conceptually, this should be a handler rather than a task.
|
||||||
|
# However, we are currently not in a role context - creating
|
||||||
|
# one just for this seemed overkill.
|
||||||
|
when: netplan_template.changed
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: netplan apply
|
||||||
|
changed_when: true
|
||||||
8
molecule/ipv6/templates/55-flannel-ipv4.yaml.j2
Normal file
8
molecule/ipv6/templates/55-flannel-ipv4.yaml.j2
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
renderer: networkd
|
||||||
|
ethernets:
|
||||||
|
{{ flannel_iface }}:
|
||||||
|
addresses:
|
||||||
|
- {{ node_ipv4 }}/24
|
||||||
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
- name: Assert that the nginx welcome page is available
|
- name: Assert that the nginx welcome page is available
|
||||||
ansible.builtin.uri:
|
ansible.builtin.uri:
|
||||||
url: http://{{ ip }}:{{ port }}/
|
url: http://{{ ip | ansible.utils.ipwrap }}:{{ port }}/
|
||||||
return_content: yes
|
return_content: yes
|
||||||
register: result
|
register: result
|
||||||
failed_when: "'Welcome to nginx!' not in result.content"
|
failed_when: "'Welcome to nginx!' not in result.content"
|
||||||
|
|||||||
@ -4,4 +4,5 @@ jsonpatch
|
|||||||
kubernetes>=12.0.0
|
kubernetes>=12.0.0
|
||||||
molecule-vagrant>=1.0.0
|
molecule-vagrant>=1.0.0
|
||||||
molecule>=4.0.1
|
molecule>=4.0.1
|
||||||
|
netaddr>=0.8.0
|
||||||
pyyaml>=3.11
|
pyyaml>=3.11
|
||||||
|
|||||||
@ -152,10 +152,10 @@
|
|||||||
owner: "{{ ansible_user }}"
|
owner: "{{ ansible_user }}"
|
||||||
mode: "u=rw,g=,o="
|
mode: "u=rw,g=,o="
|
||||||
|
|
||||||
- name: Configure kubectl cluster to https://{{ apiserver_endpoint }}:6443
|
- name: Configure kubectl cluster to https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443
|
||||||
command: >-
|
command: >-
|
||||||
k3s kubectl config set-cluster default
|
k3s kubectl config set-cluster default
|
||||||
--server=https://{{ apiserver_endpoint }}:6443
|
--server=https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443
|
||||||
--kubeconfig ~{{ ansible_user }}/.kube/config
|
--kubeconfig ~{{ ansible_user }}/.kube/config
|
||||||
changed_when: true
|
changed_when: true
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ spec:
|
|||||||
- name: vip_interface
|
- name: vip_interface
|
||||||
value: {{ flannel_iface }}
|
value: {{ flannel_iface }}
|
||||||
- name: vip_cidr
|
- name: vip_cidr
|
||||||
value: "32"
|
value: "{{ apiserver_endpoint | ansible.utils.ipsubnet | ansible.utils.ipaddr('prefix') }}"
|
||||||
- name: cp_enable
|
- name: cp_enable
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: cp_namespace
|
- name: cp_namespace
|
||||||
|
|||||||
@ -7,7 +7,7 @@ After=network-online.target
|
|||||||
Type=notify
|
Type=notify
|
||||||
ExecStartPre=-/sbin/modprobe br_netfilter
|
ExecStartPre=-/sbin/modprobe br_netfilter
|
||||||
ExecStartPre=-/sbin/modprobe overlay
|
ExecStartPre=-/sbin/modprobe overlay
|
||||||
ExecStart=/usr/local/bin/k3s agent --server https://{{ apiserver_endpoint }}:6443 --token {{ hostvars[groups['master'][0]]['token'] | default(k3s_token) }} {{ extra_agent_args | default("") }}
|
ExecStart=/usr/local/bin/k3s agent --server https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443 --token {{ hostvars[groups['master'][0]]['token'] | default(k3s_token) }} {{ extra_agent_args | default("") }}
|
||||||
KillMode=process
|
KillMode=process
|
||||||
Delegate=yes
|
Delegate=yes
|
||||||
# Having non-zero Limit*s causes performance problems due to accounting overhead
|
# Having non-zero Limit*s causes performance problems due to accounting overhead
|
||||||
|
|||||||
@ -5,7 +5,14 @@ metadata:
|
|||||||
namespace: metallb-system
|
namespace: metallb-system
|
||||||
spec:
|
spec:
|
||||||
addresses:
|
addresses:
|
||||||
- {{ metal_lb_ip_range }}
|
{% if metal_lb_ip_range is string %}
|
||||||
|
{# metal_lb_ip_range was used in the legacy way: single string instead of a list #}
|
||||||
|
{# => transform to list with single element #}
|
||||||
|
{% set metal_lb_ip_range = [metal_lb_ip_range] %}
|
||||||
|
{% endif %}
|
||||||
|
{% for range in metal_lb_ip_range %}
|
||||||
|
- {{ range }}
|
||||||
|
{% endfor %}
|
||||||
---
|
---
|
||||||
apiVersion: metallb.io/v1beta1
|
apiVersion: metallb.io/v1beta1
|
||||||
kind: L2Advertisement
|
kind: L2Advertisement
|
||||||
|
|||||||
@ -23,6 +23,13 @@
|
|||||||
state: present
|
state: present
|
||||||
reload: yes
|
reload: yes
|
||||||
|
|
||||||
|
- name: Enable IPv6 router advertisements
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv6.conf.all.accept_ra
|
||||||
|
value: "2"
|
||||||
|
state: present
|
||||||
|
reload: yes
|
||||||
|
|
||||||
- name: Add br_netfilter to /etc/modules-load.d/
|
- name: Add br_netfilter to /etc/modules-load.d/
|
||||||
copy:
|
copy:
|
||||||
content: "br_netfilter"
|
content: "br_netfilter"
|
||||||
|
|||||||
@ -44,7 +44,6 @@
|
|||||||
- /var/lib/kubelet
|
- /var/lib/kubelet
|
||||||
- /var/lib/rancher/k3s
|
- /var/lib/rancher/k3s
|
||||||
- /var/lib/rancher/
|
- /var/lib/rancher/
|
||||||
- /usr/local/bin/k3s
|
|
||||||
- /var/lib/cni/
|
- /var/lib/cni/
|
||||||
|
|
||||||
- name: Reload daemon_reload
|
- name: Reload daemon_reload
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user