mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-08-09 07:23:19 +02:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5483cdabe | |||
| 88159b3875 | |||
| db85fa960c | |||
| 6aea6e71b6 | |||
| 890d43b339 |
@@ -1,85 +0,0 @@
|
|||||||
|
|
||||||
<!-- It's a good idea to check this post first for general troubleshooting https://github.com/timothystewart6/k3s-ansible/discussions/19 -->
|
|
||||||
|
|
||||||
<!--- Provide a general summary of the issue in the Title above -->
|
|
||||||
|
|
||||||
## Expected Behavior
|
|
||||||
|
|
||||||
<!--- Tell us what should happen -->
|
|
||||||
|
|
||||||
## Current Behavior
|
|
||||||
<!--- Tell us what happens instead of the expected behavior -->
|
|
||||||
|
|
||||||
## Steps to Reproduce
|
|
||||||
|
|
||||||
<!--- reproduce this bug. Include code to reproduce, if relevant -->
|
|
||||||
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
4.
|
|
||||||
|
|
||||||
## Context (variables)
|
|
||||||
<!--- please include which OS, along with the variables used when running the playbook -->
|
|
||||||
|
|
||||||
Operating system:
|
|
||||||
|
|
||||||
Hardware:
|
|
||||||
|
|
||||||
### Variables Used
|
|
||||||
|
|
||||||
`all.yml`
|
|
||||||
|
|
||||||
```yml
|
|
||||||
k3s_version: ""
|
|
||||||
ansible_user: NA
|
|
||||||
systemd_dir: ""
|
|
||||||
|
|
||||||
flannel_iface: ""
|
|
||||||
|
|
||||||
#calico_iface: ""
|
|
||||||
calico_ebpf: ""
|
|
||||||
calico_cidr: ""
|
|
||||||
calico_tag: ""
|
|
||||||
|
|
||||||
apiserver_endpoint: ""
|
|
||||||
|
|
||||||
k3s_token: "NA"
|
|
||||||
|
|
||||||
extra_server_args: ""
|
|
||||||
extra_agent_args: ""
|
|
||||||
|
|
||||||
kube_vip_tag_version: ""
|
|
||||||
|
|
||||||
kube_vip_cloud_provider_tag_version: ""
|
|
||||||
kube_vip_lb_ip_range: ""
|
|
||||||
|
|
||||||
metal_lb_speaker_tag_version: ""
|
|
||||||
metal_lb_controller_tag_version: ""
|
|
||||||
|
|
||||||
metal_lb_ip_range: ""
|
|
||||||
```
|
|
||||||
|
|
||||||
### Hosts
|
|
||||||
|
|
||||||
`host.ini`
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[master]
|
|
||||||
IP.ADDRESS.ONE
|
|
||||||
IP.ADDRESS.TWO
|
|
||||||
IP.ADDRESS.THREE
|
|
||||||
|
|
||||||
[node]
|
|
||||||
IP.ADDRESS.FOUR
|
|
||||||
IP.ADDRESS.FIVE
|
|
||||||
|
|
||||||
[k3s_cluster:children]
|
|
||||||
master
|
|
||||||
node
|
|
||||||
```
|
|
||||||
|
|
||||||
## Possible Solution
|
|
||||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
|
||||||
|
|
||||||
- [ ] I've checked the [General Troubleshooting Guide](https://github.com/timothystewart6/k3s-ansible/discussions/20)
|
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
description: Report a reproducible problem with the playbooks, roles, or generated resources.
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for reporting a problem. Search existing issues and review the troubleshooting link first.
|
||||||
|
Remove credentials, tokens, public IP addresses, and private hostnames from all fields and logs.
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: prerequisites
|
||||||
|
attributes:
|
||||||
|
label: Prerequisites
|
||||||
|
options:
|
||||||
|
- label: I searched existing issues and discussions for this problem.
|
||||||
|
required: true
|
||||||
|
- label: I reviewed the troubleshooting guidance linked from the issue chooser.
|
||||||
|
required: true
|
||||||
|
- label: I removed secrets and identifying infrastructure details from this report.
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: summary
|
||||||
|
attributes:
|
||||||
|
label: Problem summary
|
||||||
|
description: Describe what failed and its impact.
|
||||||
|
placeholder: A concise description of the problem and affected nodes or components.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
description: What should have happened?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduction
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Provide the smallest reliable sequence that reproduces the issue.
|
||||||
|
placeholder: |
|
||||||
|
1. Configure ...
|
||||||
|
2. Run ...
|
||||||
|
3. Observe ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: revision
|
||||||
|
attributes:
|
||||||
|
label: Repository revision
|
||||||
|
description: Release, tag, branch, or commit SHA used.
|
||||||
|
placeholder: v1.36.2+k3s1+tt1 or a commit SHA
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: ansible-version
|
||||||
|
attributes:
|
||||||
|
label: Ansible version
|
||||||
|
description: Output of `ansible --version`, shortened to version and Python details.
|
||||||
|
placeholder: ansible-core 2.18.0, Python 3.12
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: Include target OS and version, architecture, node counts, platform, and network provider.
|
||||||
|
placeholder: Debian 13, amd64, 3 control nodes and 2 agents, bare metal, Cilium
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: configuration
|
||||||
|
attributes:
|
||||||
|
label: Relevant sanitized configuration
|
||||||
|
description: Include only variables and inventory groups needed to reproduce the problem.
|
||||||
|
render: yaml
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant logs or task output
|
||||||
|
description: Include the failing task and surrounding output. Redact sensitive or identifying values.
|
||||||
|
render: shell
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add attempted fixes, suspected causes, regressions, or other useful context.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Troubleshooting and support
|
||||||
|
url: https://github.com/timothystewart6/k3s-ansible/discussions/20
|
||||||
|
about: Review common troubleshooting guidance and ask configuration or usage questions.
|
||||||
|
- name: General discussions
|
||||||
|
url: https://github.com/timothystewart6/k3s-ansible/discussions
|
||||||
|
about: Discuss ideas and questions that are not confirmed bugs or concrete feature requests.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
description: Propose a focused improvement to supported repository behavior.
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Describe the use case before proposing an implementation. Search existing issues and discussions first.
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: prerequisites
|
||||||
|
attributes:
|
||||||
|
label: Prerequisites
|
||||||
|
options:
|
||||||
|
- label: I searched existing issues and discussions for this request.
|
||||||
|
required: true
|
||||||
|
- label: This request is about reusable project behavior, not support for one private environment.
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Problem or use case
|
||||||
|
description: What limitation exists, who encounters it, and why does it matter?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: proposal
|
||||||
|
attributes:
|
||||||
|
label: Proposed behavior
|
||||||
|
description: Describe the desired user-visible result. Include example variables or commands when useful.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives considered
|
||||||
|
description: Describe workarounds or other designs and their tradeoffs.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: compatibility
|
||||||
|
attributes:
|
||||||
|
label: Compatibility and operational impact
|
||||||
|
description: Note affected operating systems, architectures, CNIs, existing clusters, or reset behavior.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add relevant upstream documentation, examples, or prior discussion.
|
||||||
@@ -1,15 +1,46 @@
|
|||||||
# Proposed Changes
|
## Summary
|
||||||
<!--- Provide a general summary of your changes -->
|
|
||||||
|
<!-- Explain the problem and the resulting behavior. Keep implementation details in the sections below. -->
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-
|
-
|
||||||
|
|
||||||
## Checklist
|
## Related issues
|
||||||
|
|
||||||
- [ ] Tested locally
|
<!-- Use "Fixes #123" when this pull request should close an issue. Write "None" when not applicable. -->
|
||||||
- [ ] Ran `site.yml` playbook
|
|
||||||
- [ ] Ran `reset.yml` playbook
|
## Testing
|
||||||
- [ ] Did not add any unnecessary changes
|
|
||||||
- [ ] Ran pre-commit install at least once before committing
|
<!-- List exact commands, scenarios, and relevant manual checks. Do not check a box for a test that was not run. -->
|
||||||
- [ ] 🚀
|
|
||||||
|
- [ ] `pre-commit run --all-files`
|
||||||
|
- [ ] Relevant Ansible syntax checks
|
||||||
|
- [ ] Relevant focused regression tests
|
||||||
|
- [ ] Relevant Molecule scenario
|
||||||
|
- [ ] Provisioning tested against a non-production cluster
|
||||||
|
- [ ] Reset behavior tested against a non-production cluster
|
||||||
|
|
||||||
|
Not run, with reason:
|
||||||
|
|
||||||
|
## Risk and compatibility
|
||||||
|
|
||||||
|
<!-- Cover existing clusters, upgrades, networking, supported platforms, security, and rollback. Write "None" when a
|
||||||
|
category is not affected. -->
|
||||||
|
|
||||||
|
- Existing cluster or upgrade impact:
|
||||||
|
- Networking or CNI impact:
|
||||||
|
- Security impact:
|
||||||
|
- Rollback plan:
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
<!-- Identify updated docs and sample configuration, or explain why no documentation change is needed. -->
|
||||||
|
|
||||||
|
## Final checklist
|
||||||
|
|
||||||
|
- [ ] The change is focused and contains no unrelated edits.
|
||||||
|
- [ ] Tests cover new behavior or a regression, where applicable.
|
||||||
|
- [ ] User-facing variables are documented in role defaults, sample inventory, and the README.
|
||||||
|
- [ ] Logs, examples, and configuration contain no secrets or identifying infrastructure details.
|
||||||
|
- [ ] Generated files and local environment files are not included.
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# GitHub Copilot instructions
|
||||||
|
|
||||||
|
Read and follow the repository's root-level `AGENTS.md` before proposing or making changes. It is the canonical guide
|
||||||
|
for architecture, safety, implementation, validation, and documentation expectations.
|
||||||
|
|
||||||
|
Do not duplicate repository guidance here. If instructions need to change, update `AGENTS.md`.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Assert the sample inventory resolves the flannel interface per host.
|
||||||
|
|
||||||
|
flannel_iface defaults to the host's default IPv4 interface rather than a
|
||||||
|
hardcoded eth0. This test extracts the flannel_iface expression from the
|
||||||
|
sample inventory and proves that a host whose primary interface is not named
|
||||||
|
eth0 (e.g. enp1s0, ens3) resolves the interface from ansible facts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from jinja2 import Environment, StrictUndefined
|
||||||
|
|
||||||
|
|
||||||
|
def repo_root():
|
||||||
|
return subprocess.check_output(
|
||||||
|
["git", "rev-parse", "--show-toplevel"], text=True
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def fail(message):
|
||||||
|
raise SystemExit("default-interface test failed: " + message)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeAnsibleFacts(object):
|
||||||
|
"""Stand-in for the per-host ``ansible_facts`` dict."""
|
||||||
|
|
||||||
|
def __init__(self, default_iface, iface_ip):
|
||||||
|
self._default = {"interface": default_iface, "address": iface_ip}
|
||||||
|
self._ifaces = {
|
||||||
|
default_iface: {"ipv4": {"address": iface_ip}},
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_ipv4(self):
|
||||||
|
return self._default
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._ifaces[key]
|
||||||
|
|
||||||
|
|
||||||
|
def read_all_yml(root):
|
||||||
|
path = os.path.join(root, "inventory", "sample", "group_vars", "all.yml")
|
||||||
|
with open(path, "r") as handle:
|
||||||
|
return handle.read()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_value(content, key):
|
||||||
|
# Match a quoted value assigned to the key, e.g. flannel_iface: "...".
|
||||||
|
match = re.search(r"^%s:\s*\"(.+)\"\s*$" % re.escape(key), content, re.M)
|
||||||
|
if not match:
|
||||||
|
fail("could not find %s in the sample inventory" % key)
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve(env, expression, facts):
|
||||||
|
template = env.from_string(expression)
|
||||||
|
return template.render(ansible_facts=facts)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
root = repo_root()
|
||||||
|
content = read_all_yml(root)
|
||||||
|
env = Environment(undefined=StrictUndefined)
|
||||||
|
|
||||||
|
flannel_expr = extract_value(content, "flannel_iface")
|
||||||
|
if "default_ipv4.interface" not in flannel_expr:
|
||||||
|
fail("flannel_iface no longer defaults from ansible facts")
|
||||||
|
|
||||||
|
# A host whose primary interface is enp1s0 (the core #621 scenario).
|
||||||
|
facts = FakeAnsibleFacts("enp1s0", "192.168.30.11")
|
||||||
|
resolved = resolve(env, flannel_expr, facts)
|
||||||
|
if resolved != "enp1s0":
|
||||||
|
fail("flannel_iface resolved to %r, expected enp1s0" % resolved)
|
||||||
|
|
||||||
|
# A different host with a different interface must resolve independently.
|
||||||
|
facts2 = FakeAnsibleFacts("ens3", "192.168.30.12")
|
||||||
|
resolved2 = resolve(env, flannel_expr, facts2)
|
||||||
|
if resolved2 != "ens3":
|
||||||
|
fail("flannel_iface resolved to %r, expected ens3" % resolved2)
|
||||||
|
|
||||||
|
print("default-interface regression test passed")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -14,6 +14,7 @@ on:
|
|||||||
- '**/FUNDING.yml'
|
- '**/FUNDING.yml'
|
||||||
- '**/host.ini'
|
- '**/host.ini'
|
||||||
- '**/*.md'
|
- '**/*.md'
|
||||||
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
- '**/.editorconfig'
|
- '**/.editorconfig'
|
||||||
- '**/ansible.example.cfg'
|
- '**/ansible.example.cfg'
|
||||||
- '**/deploy.sh'
|
- '**/deploy.sh'
|
||||||
|
|||||||
@@ -84,3 +84,11 @@ repos:
|
|||||||
language: system
|
language: system
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
files: ^roles/k3s_server/tasks/metallb\.yml$|^\.github/scripts/test-metallb-remote-read\.sh$
|
files: ^roles/k3s_server/tasks/metallb\.yml$|^\.github/scripts/test-metallb-remote-read\.sh$
|
||||||
|
- id: default-interface-test
|
||||||
|
name: default interface test
|
||||||
|
entry: python3 .github/scripts/test-default-interface.py
|
||||||
|
language: python
|
||||||
|
additional_dependencies:
|
||||||
|
- Jinja2>=3.1
|
||||||
|
pass_filenames: false
|
||||||
|
files: ^inventory/sample/group_vars/all\.yml$|^\.github/scripts/test-default-interface\.py$
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# Agent Guide
|
||||||
|
|
||||||
|
This file is the canonical repository guide for coding agents and automated contributors. Read it before making
|
||||||
|
changes. Human contributors should also review [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## Project overview
|
||||||
|
|
||||||
|
This repository is an Ansible collection that provisions and resets highly available k3s clusters. It supports
|
||||||
|
multiple networking choices, including Flannel, Calico, Cilium, kube-vip, and MetalLB.
|
||||||
|
|
||||||
|
The main entry points are:
|
||||||
|
|
||||||
|
- `site.yml`: provision or update a cluster.
|
||||||
|
- `reset.yml`: remove k3s from a cluster.
|
||||||
|
- `reboot.yml`: reboot cluster nodes.
|
||||||
|
- `inventory/sample/`: example inventory and variables.
|
||||||
|
- `roles/`: reusable Ansible roles used by the playbooks.
|
||||||
|
- `molecule/`: integration scenarios run by CI.
|
||||||
|
- `.github/scripts/`: CI support scripts and focused regression tests.
|
||||||
|
|
||||||
|
## Source of truth
|
||||||
|
|
||||||
|
- Role defaults belong in `roles/<role>/defaults/main.yml`.
|
||||||
|
- Tasks belong in `roles/<role>/tasks/` and handlers in `roles/<role>/handlers/`.
|
||||||
|
- Example user configuration belongs in `inventory/sample/`.
|
||||||
|
- User-facing setup and variable documentation belongs in `README.md`.
|
||||||
|
- Contributor workflows and review expectations belong in `CONTRIBUTING.md`.
|
||||||
|
- Agent-specific repository instructions belong in this file.
|
||||||
|
|
||||||
|
Keep `CLAUDE.md` and `.github/copilot-instructions.md` as small pointers to this file. Do not duplicate these
|
||||||
|
instructions in tool-specific files.
|
||||||
|
|
||||||
|
## Development setup
|
||||||
|
|
||||||
|
Use a Python virtual environment. Do not commit the environment, generated logs, inventories, kubeconfigs, or
|
||||||
|
credentials.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .env
|
||||||
|
source .env/bin/activate
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
ansible-galaxy collection install -r collections/requirements.yml
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
`ansible.cfg` is intentionally ignored. Copy `ansible.example.cfg` when local configuration is needed.
|
||||||
|
|
||||||
|
## Working rules
|
||||||
|
|
||||||
|
1. Inspect the current branch and worktree before editing. Preserve unrelated user changes.
|
||||||
|
2. Keep changes focused. Avoid drive-by formatting or dependency updates.
|
||||||
|
3. Never add real IP addresses, hostnames, tokens, private keys, kubeconfigs, or inventory secrets.
|
||||||
|
4. Use placeholders in examples and redact sensitive values from logs and issue reports.
|
||||||
|
5. Preserve idempotence. An already-converged host should not report changes without a real state transition.
|
||||||
|
6. Prefer Ansible modules over `ansible.builtin.command` or `ansible.builtin.shell`. When a command is required,
|
||||||
|
define accurate `changed_when` and `failed_when` behavior.
|
||||||
|
7. Use fully qualified collection names, such as `ansible.builtin.copy`.
|
||||||
|
8. Put configurable values in role defaults or inventory variables. Avoid embedding environment-specific values in
|
||||||
|
tasks and templates.
|
||||||
|
9. Maintain compatibility with the operating systems and architectures listed in `README.md`.
|
||||||
|
10. Do not weaken lint rules, tests, or CI checks to make a change pass.
|
||||||
|
|
||||||
|
## Change guidance
|
||||||
|
|
||||||
|
### Ansible tasks and roles
|
||||||
|
|
||||||
|
- Use descriptive task names in sentence case.
|
||||||
|
- Use YAML booleans (`true` and `false`) rather than aliases.
|
||||||
|
- Quote file modes, for example `mode: "0644"`.
|
||||||
|
- Notify handlers only when the managed resource changes.
|
||||||
|
- Use `become: true` only where privilege escalation is needed.
|
||||||
|
- Update role defaults, sample inventory, and the README together when adding or renaming user-facing variables.
|
||||||
|
- Check reset behavior when provisioning introduces persistent services, files, mounts, or network state.
|
||||||
|
|
||||||
|
### Templates and manifests
|
||||||
|
|
||||||
|
- Keep Jinja logic small and readable. Move complicated decisions into task variables where practical.
|
||||||
|
- Render valid YAML after Jinja evaluation.
|
||||||
|
- Preserve explicit handling for optional and undefined variables.
|
||||||
|
- Add or update a focused test under `.github/scripts/` when changing generated Kubernetes manifests or bootstrap
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
### Molecule scenarios
|
||||||
|
|
||||||
|
- Reuse `molecule/resources/` for shared behavior.
|
||||||
|
- Put scenario-specific inputs in `molecule/<scenario>/overrides.yml` and `verify-vars.yml`.
|
||||||
|
- Update `molecule/README.md` when adding, removing, or materially changing a scenario.
|
||||||
|
- Clean up resources created by tests, including failure paths.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Run the smallest relevant checks while iterating, then run the complete local validation before considering a change
|
||||||
|
ready:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pre-commit run --all-files
|
||||||
|
```
|
||||||
|
|
||||||
|
For playbook or role changes, also run syntax checks with a non-sensitive inventory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook site.yml --syntax-check -i inventory/sample/hosts.ini
|
||||||
|
ansible-playbook reset.yml --syntax-check -i inventory/sample/hosts.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
Run focused regression scripts when their related files change. The mapping is defined in
|
||||||
|
`.pre-commit-config.yaml`.
|
||||||
|
|
||||||
|
Molecule tests require Vagrant, VirtualBox, host-only networking, and substantial local resources. Run the most
|
||||||
|
relevant scenario when that environment is available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
molecule test --scenario-name <scenario>
|
||||||
|
```
|
||||||
|
|
||||||
|
If a required test can't be run locally, state exactly which check was skipped and why. Never claim a check passed
|
||||||
|
unless it was executed.
|
||||||
|
|
||||||
|
## Documentation and review
|
||||||
|
|
||||||
|
- Keep commands copyable and examples free of secrets.
|
||||||
|
- Update documentation in the same change as user-visible behavior.
|
||||||
|
- Explain behavior changes, compatibility concerns, operational risks, and rollback steps in the pull request.
|
||||||
|
- Use conventional commit messages with a scope, for example `fix(k3s-server): handle an existing token safely`.
|
||||||
|
- Do not commit, push, open a pull request, or modify remote resources unless the user explicitly requests it.
|
||||||
|
|
||||||
|
## Definition of done
|
||||||
|
|
||||||
|
A change is ready for review when it is focused, documented, linted, tested in proportion to its risk, and shown in a
|
||||||
|
clean diff with no secrets or generated artifacts.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@AGENTS.md
|
||||||
|
|
||||||
|
`AGENTS.md` is the canonical repository guide. Follow it for all work in this repository.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Contributing to k3s-ansible
|
||||||
|
|
||||||
|
Thank you for improving k3s-ansible. Contributions should be focused, safe to apply to existing clusters, and tested
|
||||||
|
in proportion to their operational impact.
|
||||||
|
|
||||||
|
## Before opening an issue
|
||||||
|
|
||||||
|
- Search existing issues and discussions for the same behavior.
|
||||||
|
- Review the [troubleshooting discussion](https://github.com/timothystewart6/k3s-ansible/discussions/20).
|
||||||
|
- Remove tokens, credentials, public IP addresses, private hostnames, and other sensitive values from logs and
|
||||||
|
configuration.
|
||||||
|
- For support requests, include the k3s-ansible revision, Ansible version, target operating system, architecture,
|
||||||
|
network provider, relevant sanitized variables, and a minimal reproduction.
|
||||||
|
|
||||||
|
Use the bug report template for reproducible defects and the feature request template for proposed behavior.
|
||||||
|
|
||||||
|
## Development environment
|
||||||
|
|
||||||
|
Fork and clone the repository, then create a branch from the latest `master`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git switch master
|
||||||
|
git pull --ff-only
|
||||||
|
git switch -c <type>/<short-description>
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a Python environment and install the pinned development dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .env
|
||||||
|
source .env/bin/activate
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
ansible-galaxy collection install -r collections/requirements.yml
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy `ansible.example.cfg` to the ignored `ansible.cfg` file if local Ansible configuration is needed. Start custom
|
||||||
|
inventories from `inventory/sample/`, keep them out of Git, and never use production credentials in tests.
|
||||||
|
|
||||||
|
## Making changes
|
||||||
|
|
||||||
|
- Keep each pull request focused on one problem or feature.
|
||||||
|
- Follow [AGENTS.md](AGENTS.md) for repository structure, implementation conventions, and safety requirements.
|
||||||
|
- Preserve idempotence and existing-cluster compatibility.
|
||||||
|
- Add or update tests for behavior changes and regressions.
|
||||||
|
- Update role defaults, sample inventory, and documentation when user-facing variables change.
|
||||||
|
- Consider both provisioning and reset behavior for persistent resources.
|
||||||
|
- Avoid unrelated reformatting and generated files.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Run all pre-commit checks before submitting a pull request:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pre-commit run --all-files
|
||||||
|
```
|
||||||
|
|
||||||
|
For playbook or role changes, run syntax checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook site.yml --syntax-check -i inventory/sample/hosts.ini
|
||||||
|
ansible-playbook reset.yml --syntax-check -i inventory/sample/hosts.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the most relevant Molecule scenario when Vagrant, VirtualBox, and the required host networking are available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
molecule test --scenario-name <scenario>
|
||||||
|
```
|
||||||
|
|
||||||
|
See [molecule/README.md](molecule/README.md) for scenario details and local requirements. Pull requests should list
|
||||||
|
every check that was run and clearly identify checks that could not be run locally.
|
||||||
|
|
||||||
|
## Commits and pull requests
|
||||||
|
|
||||||
|
Use a conventional commit subject with a scope:
|
||||||
|
|
||||||
|
```text
|
||||||
|
type(scope): short description
|
||||||
|
```
|
||||||
|
|
||||||
|
Common types are `feat`, `fix`, `refactor`, `test`, `docs`, and `chore`. Write subjects in the imperative mood and
|
||||||
|
keep commits logically focused.
|
||||||
|
|
||||||
|
Pull requests should:
|
||||||
|
|
||||||
|
- Describe the problem and the resulting behavior.
|
||||||
|
- Identify compatibility, security, networking, and upgrade risks.
|
||||||
|
- Include testing evidence without sensitive data.
|
||||||
|
- Call out documentation and sample configuration changes.
|
||||||
|
- Link related issues with `Fixes #<issue>` when applicable.
|
||||||
|
- Avoid checking boxes for tests that were not run.
|
||||||
|
|
||||||
|
Maintainers may ask for a change to be split when unrelated work makes it difficult to review or roll back.
|
||||||
@@ -2,51 +2,66 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
This playbook will build an HA Kubernetes cluster with `k3s`, `kube-vip` and MetalLB via `ansible`.
|
This Ansible collection builds a highly available Kubernetes cluster with k3s. It supports kube-vip for the control
|
||||||
|
plane virtual IP, multiple CNI options, and either MetalLB or kube-vip for service load balancing.
|
||||||
|
|
||||||
This is based on the work from [this fork](https://github.com/212850a/k3s-ansible) which is based on the work from [k3s-io/k3s-ansible](https://github.com/k3s-io/k3s-ansible). It uses [kube-vip](https://kube-vip.io/) to create a load balancer for control plane, and [metal-lb](https://metallb.universe.tf/installation/) for its service `LoadBalancer`.
|
This is based on the work from [this fork](https://github.com/212850a/k3s-ansible) which is based on the work from [k3s-io/k3s-ansible](https://github.com/k3s-io/k3s-ansible). It uses [kube-vip](https://kube-vip.io/) to create a load balancer for control plane, and [metal-lb](https://metallb.universe.tf/installation/) for its service `LoadBalancer`.
|
||||||
|
|
||||||
If you want more context on how this works, see:
|
For more context on how it works, see:
|
||||||
|
|
||||||
📄 [Documentation](https://technotim.com/posts/k3s-etcd-ansible/) (including example commands)
|
📄 [Documentation](https://technotim.com/posts/k3s-etcd-ansible/) (including example commands)
|
||||||
|
|
||||||
📺 [Watch the Video](https://www.youtube.com/watch?v=CbkEWcUZ7zM)
|
📺 [Watch the Video](https://www.youtube.com/watch?v=CbkEWcUZ7zM)
|
||||||
|
|
||||||
|
## Project guides
|
||||||
|
|
||||||
|
- [Getting started](#-getting-started)
|
||||||
|
- [Configuration variables](#variables)
|
||||||
|
- [Upgrading an existing cluster](#-upgrading-an-existing-cluster)
|
||||||
|
- [Local Molecule testing](molecule/README.md)
|
||||||
|
- [Contributing guidelines](CONTRIBUTING.md)
|
||||||
|
- [Repository guide for coding agents](AGENTS.md)
|
||||||
|
|
||||||
## 📖 k3s Ansible Playbook
|
## 📖 k3s Ansible Playbook
|
||||||
|
|
||||||
Build a Kubernetes cluster using Ansible with k3s. The goal is easily install a HA Kubernetes cluster on machines running:
|
Build a Kubernetes cluster using Ansible and k3s. The goal is to make a highly available cluster straightforward to
|
||||||
|
install on machines running:
|
||||||
|
|
||||||
- [x] Debian (tested on version 13)
|
- [x] Debian (tested on version 13)
|
||||||
- [x] Ubuntu (tested on version 26.04 LTS)
|
- [x] Ubuntu (tested on version 26.04 LTS)
|
||||||
- [x] Rocky (tested on version 10)
|
- [x] Rocky (tested on version 10)
|
||||||
|
|
||||||
on processor architecture:
|
Supported processor architectures are:
|
||||||
|
|
||||||
- [X] x64
|
- [x] x64
|
||||||
- [X] arm64
|
- [x] arm64
|
||||||
- [X] armhf
|
- [x] armhf
|
||||||
|
|
||||||
## ✅ System requirements
|
## ✅ System requirements
|
||||||
|
|
||||||
- Control Node (the machine you are running `ansible` commands) must have Ansible 2.11+ If you need a quick primer on Ansible [you can check out my docs and setting up Ansible](https://technotim.com/posts/ansible-automation/).
|
- The control node, which runs the Ansible commands, must have Ansible 2.11 or newer. For a quick primer, see
|
||||||
|
[setting up Ansible](https://technotim.com/posts/ansible-automation/).
|
||||||
|
|
||||||
- You will also need to install collections that this playbook uses by running `ansible-galaxy collection install -r ./collections/requirements.yml` (important❗)
|
- Install the required collections with
|
||||||
|
`ansible-galaxy collection install -r ./collections/requirements.yml`.
|
||||||
|
|
||||||
- [`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.
|
- [`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 support passwordless SSH access. Otherwise, pass `--ask-pass --ask-become-pass` to
|
||||||
|
each playbook command.
|
||||||
|
|
||||||
## 🚀 Getting Started
|
## 🚀 Getting Started
|
||||||
|
|
||||||
### 🍴 Preparation
|
### 🍴 Preparation
|
||||||
|
|
||||||
First create a new directory based on the `sample` directory within the `inventory` directory:
|
Create a cluster-specific inventory from the sample. The `inventory/` directory ignores custom inventory content so
|
||||||
|
credentials and environment details aren't committed accidentally.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp -R inventory/sample inventory/my-cluster
|
cp -R inventory/sample inventory/my-cluster
|
||||||
```
|
```
|
||||||
|
|
||||||
Second, edit `inventory/my-cluster/hosts.ini` to match the system information gathered above
|
Edit `inventory/my-cluster/hosts.ini` to match the target hosts.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@@ -67,9 +82,10 @@ node
|
|||||||
|
|
||||||
If multiple hosts are in the master group, the playbook will automatically set up k3s in [HA mode with etcd](https://rancher.com/docs/k3s/latest/en/installation/ha-embedded/).
|
If multiple hosts are in the master group, the playbook will automatically set up k3s in [HA mode with etcd](https://rancher.com/docs/k3s/latest/en/installation/ha-embedded/).
|
||||||
|
|
||||||
Finally, copy `ansible.example.cfg` to `ansible.cfg` and adapt the inventory path to match the files that you just created.
|
Copy `ansible.example.cfg` to `ansible.cfg`, then update its inventory path. The local `ansible.cfg` file is ignored by
|
||||||
|
Git.
|
||||||
|
|
||||||
This requires at least k3s version `1.19.1` however the version is configurable by using the `k3s_version` variable.
|
The minimum k3s version is `1.19.1`. Select the desired version with the `k3s_version` variable.
|
||||||
|
|
||||||
If needed, you can also edit `inventory/my-cluster/group_vars/all.yml` to match your environment.
|
If needed, you can also edit `inventory/my-cluster/group_vars/all.yml` to match your environment.
|
||||||
|
|
||||||
@@ -81,7 +97,8 @@ Start provisioning of the cluster using the following command:
|
|||||||
ansible-playbook site.yml -i inventory/my-cluster/hosts.ini
|
ansible-playbook site.yml -i inventory/my-cluster/hosts.ini
|
||||||
```
|
```
|
||||||
|
|
||||||
After deployment control plane will be accessible via virtual ip-address which is defined in inventory/group_vars/all.yml as `apiserver_endpoint`
|
After deployment, the control plane is accessible through the virtual IP defined by `apiserver_endpoint` in the
|
||||||
|
inventory variables.
|
||||||
|
|
||||||
### 🔥 Remove k3s cluster
|
### 🔥 Remove k3s cluster
|
||||||
|
|
||||||
@@ -89,7 +106,7 @@ After deployment control plane will be accessible via virtual ip-address which i
|
|||||||
ansible-playbook reset.yml -i inventory/my-cluster/hosts.ini
|
ansible-playbook reset.yml -i inventory/my-cluster/hosts.ini
|
||||||
```
|
```
|
||||||
|
|
||||||
>You should also reboot these nodes due to the VIP not being destroyed
|
> Reboot the nodes after reset because the virtual IP may remain configured.
|
||||||
|
|
||||||
## 🔁 Upgrading an existing cluster
|
## 🔁 Upgrading an existing cluster
|
||||||
|
|
||||||
@@ -124,13 +141,21 @@ To copy your `kube config` locally so that you can access your **Kubernetes** cl
|
|||||||
```bash
|
```bash
|
||||||
scp debian@master_ip:/etc/rancher/k3s/k3s.yaml ~/.kube/config
|
scp debian@master_ip:/etc/rancher/k3s/k3s.yaml ~/.kube/config
|
||||||
```
|
```
|
||||||
If you get file Permission denied, go into the node and temporarly run:
|
If the copy fails with a permission error, grant the SSH user temporary read access using the least permissive method
|
||||||
|
available for the target system. Restore the original ownership and permissions immediately after copying. Avoid
|
||||||
|
world-writable permissions on the kubeconfig because it contains cluster credentials.
|
||||||
|
|
||||||
|
For example, copy the file to a temporary user-readable path from the control node:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo chmod 777 /etc/rancher/k3s/k3s.yaml
|
ssh debian@master_ip 'sudo install -o "$(id -un)" -m 0600 /etc/rancher/k3s/k3s.yaml /tmp/k3s.yaml'
|
||||||
```
|
```
|
||||||
Then copy with the scp command and reset the permissions back to:
|
|
||||||
|
Copy `/tmp/k3s.yaml`, then remove the temporary remote copy:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo chmod 600 /etc/rancher/k3s/k3s.yaml
|
scp debian@master_ip:/tmp/k3s.yaml ~/.kube/config
|
||||||
|
ssh debian@master_ip rm -f /tmp/k3s.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll then want to modify the config to point to master IP by running:
|
You'll then want to modify the config to point to master IP by running:
|
||||||
@@ -150,7 +175,7 @@ See the commands [here](https://technotim.com/posts/k3s-etcd-ansible/#testing-yo
|
|||||||
| `download` | `k3s_version` | string | ❌ | Required | K3s binaries version |
|
| `download` | `k3s_version` | string | ❌ | Required | K3s binaries version |
|
||||||
| `k3s_agent`, `k3s_server`, `k3s_server_post` | `apiserver_endpoint` | string | ❌ | Required | Virtual ip-address configured on each master |
|
| `k3s_agent`, `k3s_server`, `k3s_server_post` | `apiserver_endpoint` | string | ❌ | Required | Virtual ip-address configured on each master |
|
||||||
| `k3s_agent` | `extra_agent_args` | string | `null` | Not required | Extra arguments for agents nodes |
|
| `k3s_agent` | `extra_agent_args` | string | `null` | Not required | Extra arguments for agents nodes |
|
||||||
| `k3s_agent`, `k3s_server` | `group_name_master` | string | `null` | Not required | Name othe master group |
|
| `k3s_agent`, `k3s_server` | `group_name_master` | string | `null` | Not required | Name of the master group |
|
||||||
| `k3s_agent` | `k3s_token` | string | `null` | Not required | Token used to communicate between masters |
|
| `k3s_agent` | `k3s_token` | string | `null` | Not required | Token used to communicate between masters |
|
||||||
| `k3s_agent`, `k3s_server` | `proxy_env` | dict | `null` | Not required | Internet proxy configurations |
|
| `k3s_agent`, `k3s_server` | `proxy_env` | dict | `null` | Not required | Internet proxy configurations |
|
||||||
| `k3s_agent`, `k3s_server` | `proxy_env.HTTP_PROXY` | string | ❌ | Required | HTTP internet proxy |
|
| `k3s_agent`, `k3s_server` | `proxy_env.HTTP_PROXY` | string | ❌ | Required | HTTP internet proxy |
|
||||||
@@ -227,9 +252,11 @@ It is run automatically in CI, but you can also run the tests locally.
|
|||||||
This might be helpful for quick feedback in a few cases.
|
This might be helpful for quick feedback in a few cases.
|
||||||
You can find more information about it [here](molecule/README.md).
|
You can find more information about it [here](molecule/README.md).
|
||||||
|
|
||||||
### Pre-commit Hooks
|
### Pre-commit hooks
|
||||||
|
|
||||||
This repo uses `pre-commit` and `pre-commit-hooks` to lint and fix common style and syntax errors. Be sure to install python packages and then run `pre-commit install`. For more information, see [pre-commit](https://pre-commit.com/)
|
This repository uses `pre-commit` to check style, syntax, Ansible content, and shell scripts. Install the Python
|
||||||
|
dependencies, run `pre-commit install` once, and run `pre-commit run --all-files` before submitting a change. See
|
||||||
|
[CONTRIBUTING.md](CONTRIBUTING.md) for the complete development workflow.
|
||||||
|
|
||||||
## 🌌 Ansible Galaxy
|
## 🌌 Ansible Galaxy
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,18 @@ systemd_dir: /etc/systemd/system
|
|||||||
system_timezone: Your/Timezone
|
system_timezone: Your/Timezone
|
||||||
|
|
||||||
# interface which will be used for flannel
|
# interface which will be used for flannel
|
||||||
flannel_iface: eth0
|
# Defaults to each host's default IPv4 interface (e.g. eth0, enp1s0, ens3)
|
||||||
|
# so KVM/cloud hosts without eth0 work out of the box. Override per-host if needed.
|
||||||
|
flannel_iface: "{{ ansible_facts.default_ipv4.interface }}"
|
||||||
|
|
||||||
# uncomment calico_iface to use tigera operator/calico cni instead of flannel https://docs.tigera.io/calico/latest/about
|
# uncomment calico_iface to use tigera operator/calico cni instead of flannel https://docs.tigera.io/calico/latest/about
|
||||||
# calico_iface: "eth0"
|
# calico_iface: "{{ ansible_facts.default_ipv4.interface }}"
|
||||||
calico_ebpf: false # use eBPF dataplane instead of iptables
|
calico_ebpf: false # use eBPF dataplane instead of iptables
|
||||||
calico_tag: v3.32.1 # calico version tag
|
calico_tag: v3.32.1 # calico version tag
|
||||||
|
|
||||||
# uncomment cilium_iface to use cilium cni instead of flannel or calico
|
# uncomment cilium_iface to use cilium cni instead of flannel or calico
|
||||||
# ensure v4.19.57, v5.1.16, v5.2.0 or more recent kernel
|
# ensure v4.19.57, v5.1.16, v5.2.0 or more recent kernel
|
||||||
# cilium_iface: "eth0"
|
# cilium_iface: "{{ ansible_facts.default_ipv4.interface }}"
|
||||||
cilium_mode: native # native when nodes are on the same subnet or use BGP, otherwise set tunnel
|
cilium_mode: native # native when nodes are on the same subnet or use BGP, otherwise set tunnel
|
||||||
cilium_tag: v1.20.0 # cilium version tag
|
cilium_tag: v1.20.0 # cilium version tag
|
||||||
cilium_cli_tag: v0.19.7 # cilium cli version tag
|
cilium_cli_tag: v0.19.7 # cilium cli version tag
|
||||||
@@ -47,7 +49,9 @@ kube_vip_bgp_as: "64513" # Defines the AS for the BGP server
|
|||||||
kube_vip_bgp_peeraddress: "192.168.30.1" # Defines the address for the BGP peer
|
kube_vip_bgp_peeraddress: "192.168.30.1" # Defines the address for the BGP peer
|
||||||
kube_vip_bgp_peeras: "64512" # Defines the AS for the BGP peer
|
kube_vip_bgp_peeras: "64512" # Defines the AS for the BGP peer
|
||||||
|
|
||||||
# apiserver_endpoint is virtual ip-address which will be configured on each master
|
# apiserver_endpoint is virtual ip-address which will be configured on each master.
|
||||||
|
# This must be a free, routable IP on your network (not already assigned to a host
|
||||||
|
# or service), and is used by kube-vip / MetalLB to expose the Kubernetes API.
|
||||||
apiserver_endpoint: 192.168.30.222
|
apiserver_endpoint: 192.168.30.222
|
||||||
|
|
||||||
# k3s_token is required masters can talk together securely
|
# k3s_token is required masters can talk together securely
|
||||||
|
|||||||
@@ -38,6 +38,30 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.metadata.name }} ready"
|
label: "{{ item.metadata.name }} ready"
|
||||||
|
|
||||||
|
- name: Assert every node registered a node IP from its interface
|
||||||
|
# Each k3s node is launched with --node-ip derived from flannel_iface.
|
||||||
|
# Confirm every node carries a real InternalIP (not a loopback), which
|
||||||
|
# proves k3s bound to the cluster interface rather than defaulting to 127.0.0.1.
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that: >-
|
||||||
|
(node_internal_ips | length) >= 1 and
|
||||||
|
(node_internal_ips | reject('eq', '127.0.0.1') | list | length) == node_internal_ips | length
|
||||||
|
success_msg: "{{ item.metadata.name }} is bound to {{ node_internal_ips | join(', ') }}"
|
||||||
|
fail_msg: >-
|
||||||
|
{{ item.metadata.name }} has no non-loopback InternalIP
|
||||||
|
(got: {{ node_internal_ips | join(', ') }})
|
||||||
|
vars:
|
||||||
|
node_internal_ips: >-
|
||||||
|
{{
|
||||||
|
(item.status.addresses | default([]))
|
||||||
|
| selectattr('type', 'equalto', 'InternalIP')
|
||||||
|
| map(attribute='address')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
loop: "{{ verify_nodes.resources }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.metadata.name }} InternalIP"
|
||||||
|
|
||||||
- name: Get any Calico namespaces with Flannel enabled
|
- name: Get any Calico namespaces with Flannel enabled
|
||||||
kubernetes.core.k8s_info:
|
kubernetes.core.k8s_info:
|
||||||
kind: Namespace
|
kind: Namespace
|
||||||
@@ -219,12 +243,26 @@
|
|||||||
namespace: metallb-system
|
namespace: metallb-system
|
||||||
kubeconfig: "{{ kubecfg_path }}"
|
kubeconfig: "{{ kubecfg_path }}"
|
||||||
register: metallb_info
|
register: metallb_info
|
||||||
|
until: metallb_info.resources | length > 0
|
||||||
|
retries: 15
|
||||||
|
delay: 10
|
||||||
loop:
|
loop:
|
||||||
- { kind: Deployment, name: controller }
|
- { kind: Deployment, name: controller }
|
||||||
- { kind: DaemonSet, name: speaker }
|
- { kind: DaemonSet, name: speaker }
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.kind }}/{{ item.name }}"
|
label: "{{ item.kind }}/{{ item.name }}"
|
||||||
|
|
||||||
|
- name: Fail with a clear message if MetalLB resources are missing
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
Did not find {{ item.kind | lower }} {{ item.name }} in
|
||||||
|
metallb-system. Expected MetalLB to be deployed in this
|
||||||
|
scenario (verify_lb: {{ verify_lb }}).
|
||||||
|
when: item.resources | length == 0
|
||||||
|
loop: "{{ metallb_info.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item.kind }}/{{ item.item.name }}"
|
||||||
|
|
||||||
- name: Assert MetalLB controller and speaker use the expected image tags
|
- name: Assert MetalLB controller and speaker use the expected image tags
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ ExecStartPre=-/sbin/modprobe overlay
|
|||||||
ExecStart=/usr/local/bin/k3s agent \
|
ExecStart=/usr/local/bin/k3s agent \
|
||||||
--server https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443 \
|
--server https://{{ apiserver_endpoint | ansible.utils.ipwrap }}:6443 \
|
||||||
{% if is_pxe_booted | default(false) %}--snapshotter native \
|
{% if is_pxe_booted | default(false) %}--snapshotter native \
|
||||||
{% endif %}--token {{ hostvars[groups[group_name_master | default('master')][0]]['token'] | default(k3s_token) }} \
|
{% endif %}--token {{ hostvars[groups[group_name_master | default('master')][0]]['token'] | default(k3s_token) | replace( '%', '%%' ) }} \
|
||||||
{{ extra_agent_args }}
|
{{ extra_agent_args }}
|
||||||
KillMode=process
|
KillMode=process
|
||||||
Delegate=yes
|
Delegate=yes
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ server_init_args: >-
|
|||||||
{% else %}
|
{% else %}
|
||||||
--server https://{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip | split(",") | first | ansible.utils.ipwrap }}:6443
|
--server https://{{ hostvars[groups[group_name_master | default('master')][0]].k3s_node_ip | split(",") | first | ansible.utils.ipwrap }}:6443
|
||||||
{% endif %}
|
{% endif %}
|
||||||
--token {{ k3s_token }}
|
--token {{ k3s_token | replace('%', '%%') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ extra_server_args }}
|
{{ extra_server_args }}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
- name: Deploy metallb manifest
|
- name: Deploy metallb manifest
|
||||||
ansible.builtin.include_tasks: metallb.yml
|
ansible.builtin.include_tasks: metallb.yml
|
||||||
tags: metallb
|
tags: metallb
|
||||||
when: kube_vip_lb_ip_range is not defined and (not cilium_bgp or cilium_iface is not defined)
|
when: kube_vip_lb_ip_range is not defined and (cilium_bgp is not defined or cilium_iface is not defined)
|
||||||
|
|
||||||
- name: Deploy kube-vip manifest
|
- name: Deploy kube-vip manifest
|
||||||
ansible.builtin.include_tasks: kube-vip.yml
|
ansible.builtin.include_tasks: kube-vip.yml
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
- name: Deploy metallb pool
|
- name: Deploy metallb pool
|
||||||
ansible.builtin.include_tasks: metallb.yml
|
ansible.builtin.include_tasks: metallb.yml
|
||||||
tags: metallb
|
tags: metallb
|
||||||
when: kube_vip_lb_ip_range is not defined and (not cilium_bgp or cilium_iface is not defined)
|
when: kube_vip_lb_ip_range is not defined and (cilium_bgp is not defined or cilium_iface is not defined)
|
||||||
|
|
||||||
- name: Remove tmp directory used for manifests
|
- name: Remove tmp directory used for manifests
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
|
|||||||
@@ -33,6 +33,27 @@
|
|||||||
reload: true
|
reload: true
|
||||||
tags: sysctl
|
tags: sysctl
|
||||||
|
|
||||||
|
- name: Check if br_netfilter module exists
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -o pipefail
|
||||||
|
find /lib/modules/$(uname -r) -name "br_netfilter.ko*" | wc -l
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
register: br_netfilter_exists
|
||||||
|
changed_when: false
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
tags: sysctl
|
||||||
|
|
||||||
|
- name: Install kernel-modules-extra if br_netfilter missing
|
||||||
|
ansible.builtin.yum:
|
||||||
|
name: kernel-modules-extra
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
register: kernel_modules_installed
|
||||||
|
when:
|
||||||
|
- ansible_os_family == "RedHat"
|
||||||
|
- br_netfilter_exists.stdout | int == 0
|
||||||
|
|
||||||
- name: Add br_netfilter to /etc/modules-load.d/
|
- name: Add br_netfilter to /etc/modules-load.d/
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
content: br_netfilter
|
content: br_netfilter
|
||||||
|
|||||||
Reference in New Issue
Block a user