diff --git a/.taskfiles/AnsibleTasks.yaml b/.taskfiles/AnsibleTasks.yaml index 66f63c85..1a5d25d9 100644 --- a/.taskfiles/AnsibleTasks.yaml +++ b/.taskfiles/AnsibleTasks.yaml @@ -1,54 +1,63 @@ --- +# yaml-language-server: $schema=https://taskfile.dev/schema.json version: "3" -vars: - ANSIBLE_PLAYBOOK_DIR: "{{.ANSIBLE_DIR}}/playbooks" - ANSIBLE_INVENTORY_DIR: "{{.ANSIBLE_DIR}}/inventory" +env: + PATH: "{{.ROOT_DIR}}/.venv/bin:$PATH" + VIRTUAL_ENV: "{{.ROOT_DIR}}/.venv" + ANSIBLE_COLLECTIONS_PATH: "{{.ROOT_DIR}}/.venv/galaxy" + ANSIBLE_ROLES_PATH: "{{.ROOT_DIR}}/.venv/galaxy/ansible_roles" + ANSIBLE_VARS_ENABLED: "host_group_vars,community.sops.sops" tasks: - prepare: - desc: Prepare all the k8s nodes for running k3s - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible-playbook -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml {{.ANSIBLE_PLAYBOOK_DIR}}/cluster-prepare.yaml + deps: + desc: Set up Ansible dependencies for the environment + cmds: + - task: .venv - install: - desc: Install Kubernetes on the nodes - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible-playbook -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml {{.ANSIBLE_PLAYBOOK_DIR}}/cluster-installation.yaml - - rollout-update: - desc: Preform operating system updates and rollout restart the cluster - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible-playbook -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml {{.ANSIBLE_PLAYBOOK_DIR}}/cluster-rollout-update.yaml - - nuke: - desc: Uninstall Kubernetes on the nodes - dir: "{{.ANSIBLE_DIR}}" - interactive: true - cmd: ansible-playbook -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml {{.ANSIBLE_PLAYBOOK_DIR}}/cluster-nuke.yaml - - reboot: - desc: Reboot all the k8s nodes - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible-playbook -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml {{.ANSIBLE_PLAYBOOK_DIR}}/cluster-reboot.yaml + run: + desc: Run an Ansible playbook for configuring a cluster + summary: | + Args: + playbook: Playbook to run (required) + prompt: Run Ansible playbook '{{.playbook}}'... continue? + deps: ["deps"] + cmd: | + .venv/bin/ansible-playbook \ + --inventory {{.ANSIBLE_DIR}}/inventory/hosts.yaml \ + {{.ANSIBLE_DIR}}/playbooks/{{.playbook}}.yaml {{.CLI_ARGS}} + preconditions: + - { msg: "Argument (playbook) is required", sh: "test -n {{.playbook}}" } + - { msg: "Venv not found", sh: "test -d {{.ROOT_DIR}}/.venv" } + - { msg: "Inventory not found", sh: "test -f {{.ANSIBLE_DIR}}/inventory/hosts.yaml" } + - { msg: "Playbook not found", sh: "test -f {{.ANSIBLE_DIR}}/playbooks/{{.playbook}}.yaml" } poweroff: desc: Shutdown all the k8s nodes - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible kubernetes -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml -a '/usr/bin/systemctl poweroff' --become + cmd: ansible kubernetes -i {{.ANSIBLE_DIR}}/inventory/hosts.yaml -a '/usr/bin/systemctl poweroff' --become list: desc: List all the hosts - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible all -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml --list-hosts + cmd: ansible kubernetes -i {{.ANSIBLE_DIR}}/inventory/hosts.yaml --list-hosts ping: desc: Ping all the hosts - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible all -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml --one-line -m 'ping' + cmd: ansible kubernetes -i {{.ANSIBLE_DIR}}/inventory/hosts.yaml --one-line -m 'ping' uptime: desc: Uptime of all the hosts - dir: "{{.ANSIBLE_DIR}}" - cmd: ansible all -i {{.ANSIBLE_INVENTORY_DIR}}/hosts.yaml --one-line -a 'uptime' + cmd: ansible kubernetes -i {{.ANSIBLE_DIR}}/inventory/hosts.yaml --one-line -a 'uptime' + + .venv: + internal: true + cmds: + - true && {{.PYTHON_BIN}} -m venv {{.ROOT_DIR}}/.venv + - .venv/bin/python3 -m pip install --upgrade pip setuptools wheel + - .venv/bin/python3 -m pip install --upgrade --requirement "{{.ROOT_DIR}}/requirements.txt" + - .venv/bin/ansible-galaxy install --role-file "{{.ROOT_DIR}}/requirements.yaml" --force + sources: + - "{{.ANSIBLE_DIR}}/requirements.txt" + - "{{.ANSIBLE_DIR}}/requirements.yaml" + generates: + - "{{.ROOT_DIR}}/.venv/pyvenv.cfg" diff --git a/.taskfiles/BrewTasks.yaml b/.taskfiles/BrewTasks.yaml index a1bbb276..03b8fb4b 100644 --- a/.taskfiles/BrewTasks.yaml +++ b/.taskfiles/BrewTasks.yaml @@ -1,4 +1,5 @@ --- +# yaml-language-server: $schema=https://taskfile.dev/schema.json version: "3" tasks: diff --git a/.taskfiles/ClusterTasks.yaml b/.taskfiles/ClusterTasks.yaml deleted file mode 100644 index 597bf856..00000000 --- a/.taskfiles/ClusterTasks.yaml +++ /dev/null @@ -1,80 +0,0 @@ ---- -version: "3" - -vars: - # renovate: datasource=github-releases depName=prometheus-operator/prometheus-operator - PROMETHEUS_OPERATOR_VERSION: "v0.70.0" - -tasks: - - install: - desc: Install Flux into your cluster - cmds: - # Install essential Prometheus Operator CRDs - - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{ .PROMETHEUS_OPERATOR_VERSION }}/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml - - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{ .PROMETHEUS_OPERATOR_VERSION }}/example/prometheus-operator-crd/monitoring.coreos.com_prometheusrules.yaml - - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{ .PROMETHEUS_OPERATOR_VERSION }}/example/prometheus-operator-crd/monitoring.coreos.com_scrapeconfigs.yaml - - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{ .PROMETHEUS_OPERATOR_VERSION }}/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml - # Install Flux - - kubectl apply --server-side --kustomize {{.KUBERNETES_DIR}}/bootstrap - - cat {{.SOPS_AGE_KEY_FILE}} | kubectl -n flux-system create secret generic sops-age --from-file=age.agekey=/dev/stdin - - sops --decrypt {{.KUBERNETES_DIR}}/flux/vars/cluster-secrets.sops.yaml | kubectl apply --server-side --filename - - - sops --decrypt {{.KUBERNETES_DIR}}/flux/vars/cluster-secrets-user.sops.yaml | kubectl apply --server-side --filename - - - kubectl apply --server-side --filename {{.KUBERNETES_DIR}}/flux/vars/cluster-settings.yaml - - kubectl apply --server-side --filename {{.KUBERNETES_DIR}}/flux/vars/cluster-settings-user.yaml - - kubectl apply --server-side --kustomize {{.KUBERNETES_DIR}}/flux/config - preconditions: - - sh: test -f {{.SOPS_AGE_KEY_FILE}} - msg: Age key file is not found. Did you forget to create it? - vars: - SOPS_AGE_KEY_FILE: "{{.ROOT_DIR}}/age.key" - - reconcile: - desc: Force update Flux to pull in changes from your Git repository - cmd: flux reconcile -n flux-system kustomization cluster --with-source - - nodes: - desc: List all the nodes in your cluster - cmd: kubectl get nodes {{.CLI_ARGS | default "-o wide"}} - - pods: - desc: List all the pods in your cluster - cmd: kubectl get pods {{.CLI_ARGS | default "-A"}} - - kustomizations: - desc: List all the kustomizations in your cluster - cmd: kubectl get kustomizations {{.CLI_ARGS | default "-A"}} - - helmreleases: - desc: List all the helmreleases in your cluster - cmd: kubectl get helmreleases {{.CLI_ARGS | default "-A"}} - - helmrepositories: - desc: List all the helmrepositories in your cluster - cmd: kubectl get helmrepositories {{.CLI_ARGS | default "-A"}} - - gitrepositories: - desc: List all the gitrepositories in your cluster - cmd: kubectl get gitrepositories {{.CLI_ARGS | default "-A"}} - - certificates: - desc: List all the certificates in your cluster - cmds: - - kubectl get certificates {{.CLI_ARGS | default "-A"}} - - kubectl get certificaterequests {{.CLI_ARGS | default "-A"}} - - ingresses: - desc: List all the ingresses in your cluster - cmd: kubectl get ingress {{.CLI_ARGS | default "-A"}} - - resources: - desc: Gather common resources in your cluster, useful when asking for support - cmds: - - task: nodes - - task: kustomizations - - task: helmreleases - - task: helmrepositories - - task: gitrepositories - - task: certificates - - task: ingresses - - task: pods diff --git a/.taskfiles/FluxTasks.yaml b/.taskfiles/FluxTasks.yaml new file mode 100644 index 00000000..f01e722f --- /dev/null +++ b/.taskfiles/FluxTasks.yaml @@ -0,0 +1,57 @@ +--- +# yaml-language-server: $schema=https://taskfile.dev/schema.json +version: "3" + +vars: + # renovate: datasource=github-releases depName=prometheus-operator/prometheus-operator + PROMETHEUS_OPERATOR_VERSION: "v0.70.0" + +tasks: + + bootstrap: + desc: Bootstrap Flux into a Kubernetes cluster + cmds: + # Install essential Prometheus Operator CRDs + - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{.PROMETHEUS_OPERATOR_VERSION}}/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml + - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{.PROMETHEUS_OPERATOR_VERSION}}/example/prometheus-operator-crd/monitoring.coreos.com_prometheusrules.yaml + - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{.PROMETHEUS_OPERATOR_VERSION}}/example/prometheus-operator-crd/monitoring.coreos.com_scrapeconfigs.yaml + - kubectl apply --server-side --filename https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/{{.PROMETHEUS_OPERATOR_VERSION}}/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml + # Install Flux + - kubectl apply --server-side --kustomize {{.KUBERNETES_DIR}}/bootstrap + # Install Secrets and Kustomizations + - cat {{.ROOT_DIR}}/age.key | kubectl -n flux-system create secret generic sops-age --from-file=age.agekey=/dev/stdin + - sops --decrypt {{.KUBERNETES_DIR}}/flux/vars/cluster-secrets.sops.yaml | kubectl apply --server-side --filename - + - sops --decrypt {{.KUBERNETES_DIR}}/flux/vars/cluster-secrets-user.sops.yaml | kubectl apply --server-side --filename - + - kubectl apply --server-side --filename {{.KUBERNETES_DIR}}/flux/vars/cluster-settings.yaml + - kubectl apply --server-side --filename {{.KUBERNETES_DIR}}/flux/vars/cluster-settings-user.yaml + - kubectl apply --server-side --kustomize {{.KUBERNETES_DIR}}/flux/config + preconditions: + - { msg: "Flux already appears installed", sh: "exit $(( ! $(kubectl get namespace flux-system) ))" } + - { msg: "Age private key not found", sh: "test -f {{.ROOT_DIR}}/age.key" } + + apply: + desc: Apply a Flux Kustomization resource for a cluster + summary: | + Args: + path: Path containing the Flux Kustomization resource (ks.yaml) (required) + ns: Namespace the Flux Kustomization exists in (default: flux-system) + cmd: | + flux build ks $(basename {{.path}}) \ + --namespace {{.ns}} \ + --kustomization-file {{.KUBERNETES_DIR}}/apps/{{.path}}/ks.yaml \ + --path {{.KUBERNETES_DIR}}/apps/{{.path}} \ + {{- if contains "not found" .ks }}--dry-run \{{ end }} + | \ + kubectl apply --server-side \ + --field-manager=kustomize-controller -f - + vars: + path: '{{ or .path (fail "Argument (path) is required") }}' + ns: '{{.ns | default "flux-system"}}' + ks: + sh: flux --namespace {{.ns}} get kustomizations $(basename {{.path}}) 2>&1 + preconditions: + - { msg: "Kustomization file not found", sh: "test -f {{.KUBERNETES_DIR}}/apps/{{.path}}/ks.yaml" } + + reconcile: + desc: Force update Flux to pull in changes from your Git repository + cmd: flux reconcile --namespace flux-system kustomization cluster --with-source diff --git a/.taskfiles/KubernetesTasks.yaml b/.taskfiles/KubernetesTasks.yaml new file mode 100644 index 00000000..043c5952 --- /dev/null +++ b/.taskfiles/KubernetesTasks.yaml @@ -0,0 +1,67 @@ +--- +# yaml-language-server: $schema=https://taskfile.dev/schema.json +version: "3" + +tasks: + + mount: + desc: Mount a PersistentVolumeClaim to a temporary pod for a cluster + summary: | + Args: + ns: Namespace the PVC is in (default: default) + claim: PVC to mount (required) + interactive: true + cmd: | + kubectl run -n {{.ns}} debug-{{.claim}} -i --tty --rm --image=null --privileged --overrides=' + { + "apiVersion": "v1", + "spec": { + "containers": [ + { + "name": "debug", + "image": "ghcr.io/onedr0p/alpine:rolling", + "command": ["/bin/bash"], + "stdin": true, + "stdinOnce": true, + "tty": true, + "volumeMounts": [ + { + "name": "config", + "mountPath": "/config" + } + ] + } + ], + "volumes": [ + { + "name": "config", + "persistentVolumeClaim": { + "claimName": "{{.claim}}" + } + } + ], + "restartPolicy": "Never" + } + }' + vars: + ns: '{{.ns | default "default"}}' + claim: '{{ or .claim (fail "Argument (claim) is required") }}' + preconditions: + - { msg: "PVC not found", sh: "kubectl -n {{.ns}} get persistentvolumeclaim {{.claim}}" } + + resources: + desc: Gather common resources in your cluster, useful when asking for support + cmds: + - for: { var: RESOURCES } + cmd: kubectl get {{.ITEM}} {{.CLI_ARGS | default "-A"}} + vars: + RESOURCES: >- + nodes + gitrepositories + kustomizations + helmrepositories + helmreleases + certificates + certificaterequests + ingresses + pods diff --git a/README.md b/README.md index a1d8dbc8..7789ea0b 100644 --- a/README.md +++ b/README.md @@ -142,19 +142,17 @@ Once you have installed Debian on your nodes, there are six stages to getting a ### 🌱 Stage 2: Setup your local workstation environment -1. Install the most recent version of [task](https://taskfile.dev/), see the task [installation docs](https://taskfile.dev/installation/) for other supported platforms. +1. Install the most recent version of [task](https://taskfile.dev/), see the [installation docs](https://taskfile.dev/installation/) for other supported platforms. ```sh - # Brew brew install go-task ``` -2. Install the most recent version of [direnv](https://direnv.net/), see the direnv [installation docs](https://direnv.net/docs/installation.html) for other supported platforms. +2. Install the most recent version of [direnv](https://direnv.net/), see the [installation docs](https://direnv.net/docs/installation.html) for other supported platforms. 📍 _After installing `direnv` be sure to **[hook it into your shell](https://direnv.net/docs/hook.html)** and after that is done run `direnv allow` while in your repos' directory._ ```sh - # Brew brew install direnv ``` @@ -163,8 +161,7 @@ Once you have installed Debian on your nodes, there are six stages to getting a 📍 _This commands requires Python 3.10+ to be installed_ ```sh - # Platform agnostic - task deps + task ansible:deps ``` 4. Install the required tools: [age](https://github.com/FiloSottile/age), [flux](https://toolkit.fluxcd.io/), [cloudflared](https://github.com/cloudflare/cloudflared), [kubectl](https://kubernetes.io/docs/tasks/tools/), [sops](https://github.com/getsops/sops) @@ -172,7 +169,6 @@ Once you have installed Debian on your nodes, there are six stages to getting a 📍 _Not using brew? Make sure to look up how to install the latest version of each of these CLI tools yourself._ ```sh - # Brew task brew:deps ``` @@ -283,12 +279,12 @@ Once you have installed Debian on your nodes, there are six stages to getting a 4. Run the Ansible prepare playbook (nodes wil reboot when done) ```sh - task ansible:prepare + task ansible:run playbook=cluster-prepare ``` ### ⛵ Stage 5: Use Ansible to install k3s -📍 _Here we will be running a Ansible Playbook to install [k3s](https://k3s.io/) with [this](https://galaxy.ansible.com/xanmanning/k3s) Ansible galaxy role. If you run into problems, you can run `task ansible:nuke` to destroy the k3s cluster and start over from this point._ +📍 _Here we will be running a Ansible Playbook to install [k3s](https://k3s.io/) with [this](https://galaxy.ansible.com/xanmanning/k3s) Ansible galaxy role. If you run into problems, you can run `task ansible:run playbook=cluster-nuke` to destroy the k3s cluster and start over from this point._ 1. Verify Ansible can view your config @@ -305,7 +301,7 @@ Once you have installed Debian on your nodes, there are six stages to getting a 3. Install k3s with Ansible ```sh - task ansible:install + task ansible:run playbook=cluster-installation ``` 4. Verify the nodes are online @@ -348,7 +344,7 @@ Once you have installed Debian on your nodes, there are six stages to getting a 3. Install Flux and sync the cluster to the Git repository ```sh - task cluster:install + task flux:bootstrap # namespace/flux-system configured # customresourcedefinition.apiextensions.k8s.io/alerts.notification.toolkit.fluxcd.io created # ... @@ -371,11 +367,10 @@ _Mic check, 1, 2_ - In a few moments applications should be lighting up like Chr 1. Output all the common resources in your cluster. - 📍 _Feel free to use the provided [cluster tasks](.taskfiles/ClusterTasks.yaml) for validation of cluster resources or continue to get familiar with the `kubectl` and `flux` CLI tools._ - + 📍 _Feel free to use the provided [kubernetes tasks](.taskfiles/KubernetesTasks.yaml) for validation of cluster resources or continue to get familiar with the `kubectl` and `flux` CLI tools._ ```sh - task cluster:resources + task kubernetes:resources ``` 2. ⚠️ It might take `cert-manager` awhile to generate certificates, this is normal so be patient. diff --git a/Taskfile.yaml b/Taskfile.yaml index 60047f1e..8a31c904 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,43 +1,29 @@ --- +# yaml-language-server: $schema=https://taskfile.dev/schema.json version: "3" vars: - PYTHON_BIN: python3 BOOTSTRAP_DIR: "{{.ROOT_DIR}}/bootstrap" ANSIBLE_DIR: "{{.ROOT_DIR}}/ansible" KUBERNETES_DIR: "{{.ROOT_DIR}}/kubernetes" env: KUBECONFIG: "{{.ROOT_DIR}}/kubeconfig" - SOPS_AGE_KEY_FILE: "{{.ROOT_DIR}}/age.key" - PATH: "{{.ROOT_DIR}}/.venv/bin:$PATH" - VIRTUAL_ENV: "{{.ROOT_DIR}}/.venv" - ANSIBLE_COLLECTIONS_PATH: "{{.ROOT_DIR}}/.venv/galaxy" - ANSIBLE_ROLES_PATH: "{{.ROOT_DIR}}/.venv/galaxy/ansible_roles" - ANSIBLE_VARS_ENABLED: "host_group_vars,community.sops.sops" - K8S_AUTH_KUBECONFIG: "{{.ROOT_DIR}}/kubeconfig" includes: ansible: .taskfiles/AnsibleTasks.yaml brew: .taskfiles/BrewTasks.yaml - cluster: .taskfiles/ClusterTasks.yaml + kubernetes: + aliases: ["k8s"] + taskfile: .taskfiles/KubernetesTasks.yaml + flux: .taskfiles/FluxTasks.yaml tasks: default: task -l - deps: - desc: Create a Python virtual env and install required packages - summary: task {{.TASK}} - cmds: - - "{{.PYTHON_BIN}} -m venv {{.ROOT_DIR}}/.venv" - - .venv/bin/python3 -m pip install --upgrade pip setuptools wheel - - .venv/bin/python3 -m pip install --upgrade --requirement "{{.ROOT_DIR}}/requirements.txt" - - .venv/bin/ansible-galaxy install --role-file "{{.ROOT_DIR}}/requirements.yaml" --force - init: desc: Initialize configuration files - summary: task {{.TASK}} dir: "{{.BOOTSTRAP_DIR}}" cmds: - cp -n vars/addons.sample.yaml vars/addons.yaml @@ -58,25 +44,8 @@ tasks: # https://github.com/go-task/task/issues/1330 configure: desc: Configure repository from Ansible vars - summary: task {{.TASK}} prompt: Any conflicting config in the root kubernetes and ansible directories will be overwritten... continue? dir: "{{.BOOTSTRAP_DIR}}" cmd: ansible-playbook configure.yaml env: ANSIBLE_DISPLAY_SKIPPED_HOSTS: "false" - - update-template: - desc: Update from the upstream flux-cluster-template repository - summary: task {{.TASK}} - cmds: - - mkdir -p $(dirname {{.shafile}}) - - touch {{.shafile}} - - git remote get-url template >/dev/null 2>&1 || git remote add template git@github.com:onedr0p/flux-cluster-template - - git fetch --all - - git cherry-pick --no-commit --allow-empty $(cat {{.shafile}})..template/main - - git ls-remote template HEAD | awk '{ print $1}' > {{.shafile}} - vars: - shafile: "{{.ROOT_DIR}}/.tasks/.latest-template.sha" - preconditions: - - { msg: "Git repository not up-to-date", sh: "git diff --exit-code" } - - { msg: "Git repository not up-to-date", sh: "git diff --cached --exit-code" }