Chainstack ansible
Based on the code snippets provided, here is the consolidated and structured Ansible Playbook (site.yml) and the necessary supporting files.
I have organized the fragments into a logical execution flow: 1. Setup: OS dependencies, Kernel tuning, Swap. 2. Kubernetes: Install K3s, Configure Traefik (TLS), Install Kyverno. 3. Application: Install Chainstack Control Panel via cpctl. 4. Networking: Configure Ingress and API routing. 5. Finalization: Save credentials.
1. Main Playbook: site.yml¶
- name: Deploy Chainstack Control Panel on Ubuntu/K3s
hosts: all
become: true
vars:
# --- Configuration Variables ---
# Update these values or pass them via -e
chainstack_version: "1.0.0"
chainstack_namespace: "control-panel"
chainstack_release: "cp"
chainstack_storage_class: "local-path" # Default for K3s
chainstack_install_dir: "/opt/chainstack"
# Domain Configuration
final_domain: "chainstack.example.com"
temp_domain: "" # Optional temporary domain
# TLS Configuration
certbot_email: "[email protected]"
# Kyverno Policy Path
kyverno_policy_file: "files/chainstack-node-policy.yaml"
# Derived Paths
playbook_dir: "{{ playbook_dir }}"
helm_install_script: "https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3"
tasks:
# 1. OS Preparation
- name: Install base dependencies
ansible.builtin.import_tasks: tasks/dependencies.yml
- name: Configure Kernel Parameters (Sysctl)
ansible.builtin.template:
src: templates/99-chainstack.conf.j2
dest: /etc/sysctl.d/99-chainstack.conf
mode: "0644"
notify: reload sysctl
- name: Create Swapfile
ansible.builtin.import_tasks: tasks/swapfile.yml
# 2. Kubernetes Cluster Setup
- name: Install K3s
ansible.builtin.import_tasks: tasks/k3s.yml
- name: Configure Traefik for Let's Encrypt
ansible.builtin.import_tasks: tasks/traefik.yml
# 3. Security & Policy (Kyverno)
- name: Install Kyverno
ansible.builtin.import_tasks: tasks/kyverno.yml
# 4. Chainstack Control Panel Installation
- name: Install Chainstack Control Panel
ansible.builtin.import_tasks: tasks/install_cp.yml
# 5. Networking & Ingress
- name: Create Ingress for Chainstack
ansible.builtin.import_tasks: tasks/ingress.yml
# 6. Post-Installation
- name: Save Chainstack credentials
ansible.builtin.import_tasks: tasks/password.yml
handlers:
- name: reload sysctl
ansible.builtin.command: sysctl --system
changed_when: true
2. Task Files¶
Ensure the directory structure is tasks/ and templates/.
tasks/dependencies.yml¶
- name: Wait for apt/dpkg lock
ansible.builtin.shell: |
while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \
fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \
fuser /var/cache/apt/archives/lock >/dev/null 2>&1; do
sleep 5
done
changed_when: false
- name: Install base packages
ansible.builtin.apt:
name:
- curl
- wget
- git
- jq
- ca-certificates
- openssl
state: present
update_cache: true
register: apt_result
retries: 10
delay: 15
until: apt_result is succeeded
- name: Install kubectl
ansible.builtin.shell: |
curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl
args:
creates: /usr/local/bin/kubectl
- name: Install Helm
ansible.builtin.shell: |
curl -fsSL {{ helm_install_script }} | bash
args:
creates: /usr/local/bin/helm
- name: Install yq
ansible.builtin.get_url:
url: https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
dest: /usr/local/bin/yq
mode: "0755"
force: false
tasks/k3s.yml¶
- name: Install k3s
shell: |
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik --flannel-backend=none" sh -
args:
creates: /usr/local/bin/k3s
# Note: We disable default traefik to manage it manually via manifests for better control
- name: Wait for k3s service
service_facts:
- name: Ensure k3s service started
service:
name: k3s
state: started
enabled: true
- name: Wait for Kubernetes API
shell: |
curl -k https://127.0.0.1:6443/healthz
register: api_ready
retries: 30
delay: 5
until: api_ready.rc == 0
- name: Configure kubeconfig for kubectl
file:
path: /root/.kube
state: directory
mode: "0700"
- name: Copy kubeconfig
copy:
src: /etc/rancher/k3s/k3s.yaml
dest: /root/.kube/config
remote_src: true
mode: "0600"
- name: Wait for node Ready
shell: |
kubectl get nodes --no-headers | grep -q " Ready"
environment:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
register: k3s_ready
retries: 20
delay: 10
until: k3s_ready.rc == 0
tasks/traefik.yml¶
- name: Configure Traefik Let's Encrypt
copy:
dest: /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
mode: "0644"
content: |
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
additionalArguments:
- "--certificatesresolvers.le.acme.email={{ certbot_email }}"
- "--certificatesresolvers.le.acme.storage=/data/acme.json"
- "--certificatesresolvers.le.acme.tlschallenge=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
tasks/kyverno.yml¶
- name: Add Kyverno Helm repo
kubernetes.core.helm_repository:
name: kyverno
repo_url: https://kyverno.github.io/kyverno/
- name: Install Kyverno
kubernetes.core.helm:
name: kyverno
chart_ref: kyverno/kyverno
release_namespace: kyverno
create_namespace: true
wait: true
wait_timeout: 600s
- name: Wait for Kyverno pods
kubernetes.core.k8s_info:
kind: Pod
namespace: kyverno
register: kyverno_pods
until: >
kyverno_pods.resources |
selectattr('status.phase','equalto','Running') |
list | length > 0
retries: 20
delay: 10
- name: Load policy
set_fact:
chainstack_policy: "{{ lookup('file', playbook_dir + '/' + kyverno_policy_file) | from_yaml }}"
- name: Apply Chainstack resource mutation policy
kubernetes.core.k8s:
state: present
apply: true
definition: "{{ chainstack_policy }}"
tasks/install_cp.yml¶
- name: Create install directory
ansible.builtin.file:
path: "{{ chainstack_install_dir }}"
state: directory
mode: "0755"
- name: Copy cpctl installer
ansible.builtin.copy:
src: cpctl.sh
dest: "{{ chainstack_install_dir }}/cpctl"
mode: "0755"
- name: Check if Chainstack Helm release exists
ansible.builtin.shell: >
helm status {{ chainstack_release }} -n {{ chainstack_namespace }}
register: cp_release
failed_when: false
changed_when: false
- name: Install Chainstack Control Plane
ansible.builtin.shell: |
{{ chainstack_install_dir }}/cpctl install \
-v {{ chainstack_version }} \
-s {{ chainstack_storage_class }} \
-y
args:
chdir: "{{ chainstack_install_dir }}"
register: cp_install
when: cp_release.rc != 0
no_log: true
- name: Wait for Control Plane deployments
ansible.builtin.shell: |
kubectl wait --for=condition=available deployment --all \
-n {{ chainstack_namespace }} \
--timeout=600s
register: cp_ready
changed_when: false
tasks/ingress.yml¶
- name: Install python3-pip
ansible.builtin.apt:
name: python3-pip
state: present
update_cache: true
lock_timeout: 600
- name: Install Kubernetes Python client
ansible.builtin.pip:
name:
- kubernetes
- openshift
- PyYAML
executable: pip3
state: present
- name: Create Chainstack API strip middleware
kubernetes.core.k8s:
state: present
apply: true
kubeconfig: /etc/rancher/k3s/k3s.yaml
definition:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: chainstack-api-strip
namespace: control-panel
spec:
stripPrefix:
prefixes:
- /api
- name: Build list of Chainstack ingress domains
ansible.builtin.set_fact:
chainstack_ingress_domains: >-
{{
[final_domain, temp_domain | default('')]
| map('trim')
| reject('equalto', '')
| unique
| list
}}
- name: Create Chainstack HTTPS ingress
kubernetes.core.k8s:
state: present
kubeconfig: /etc/rancher/k3s/k3s.yaml
api_version: networking.k8s.io/v1
kind: Ingress
definition:
metadata:
name: "chainstack-ui-{{ item | regex_replace('[^a-zA-Z0-9-]', '-') }}"
namespace: control-panel
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.certresolver: le
traefik.ingress.kubernetes.io/router.middlewares: control-panel-chainstack-api-strip@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: "{{ item }}"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: cp-cp-ui
port:
number: 80
tls:
- hosts:
- "{{ item }}"
loop: "{{ chainstack_ingress_domains }}"
loop_control:
label: "{{ item }}"
- name: Patch Chainstack UI ConfigMap apiBaseUrl
kubernetes.core.k8s:
state: patched
kubeconfig: /etc/rancher/k3s/k3s.yaml
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: cp-cp-ui-config
namespace: control-panel
data:
config.json: |
{"apiBaseUrl":"/api"}
register: ui_config_patch
- name: Restart Chainstack UI deployment
kubernetes.core.k8s:
state: patched
kubeconfig: /etc/rancher/k3s/k3s.yaml
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cp-cp-ui
namespace: control-panel
spec:
template:
metadata:
annotations:
kubectl.kubernetes.io/restartedAt: "{{ ansible_date_time.iso8601 }}"
when: ui_config_patch.changed
tasks/password.yml (Placeholder)¶
Note: The original snippet referenced password.yml but didn't provide the content. This is a standard implementation to extract secrets.
- name: Save Chainstack credentials to file
ansible.builtin.shell: |
kubectl get secret cp-postgresql-ha-postgresql -n {{ chainstack_namespace }} -o jsonpath='{.data.password}' | base64 -d > /tmp/chainstack_password.txt
echo "Password saved to /tmp/chainstack_password.txt"
args:
creates: /tmp/chainstack_password.txt
3. Template File¶
templates/99-chainstack.conf.j2¶
# Chainstack node kernel tuning
# Applied via sysctl --system
{% for key, value in chainstack_sysctl_params.items() %}
{{ key }}={{ value }}
{% endfor %}
4. Required Variables File (group_vars/all.yml or vars.yml)¶
You need to define the chainstack_sysctl_params variable used in the template.
chainstack_sysctl_params:
net.core.rmem_max: 16777216
net.core.wmem_max: 16777216
net.ipv4.tcp_rmem: "4096 87380 16777216"
net.ipv4.tcp_wmem: "4096 65536 16777216"
net.ipv4.tcp_congestion_control: bbr
net.ipv4.tcp_moderate_rcvbuf: 1
net.core.netdev_max_backlog: 5000
net.ipv4.tcp_max_syn_backlog: 8192
net.ipv4.tcp_max_tw_buckets: 1440000
net.ipv4.tcp_tw_reuse: 1
net.ipv4.tcp_fin_timeout: 15
net.ipv4.tcp_keepalive_time: 1200
net.ipv4.tcp_keepalive_probes: 5
net.ipv4.tcp_keepalive_intvl: 15
vm.swappiness: 10
vm.vfs_cache_pressure: 50
How to Run¶
- Prepare the environment: Ensure
cpctl.shandfiles/chainstack-node-policy.yamlexist in your playbook directory. - Edit variables: Modify
site.ymlor create agroup_vars/all.ymlto set your domain, email, and version. - Execute: (Where
inventory.inicontains your target Ubuntu server IP).