move roles
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/common/defaults/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
common_timezone: America/Chicago
|
||||
|
||||
common_ntp_server: 10.1.71.33 # sundial
|
||||
|
||||
common_packages:
|
||||
- curl
|
||||
- wget
|
||||
- vim
|
||||
- htop
|
||||
- git
|
||||
- jq
|
||||
- unzip
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- net-tools
|
||||
- dnsutils
|
||||
@@ -1,8 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/common/handlers/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: restart timesyncd
|
||||
systemd:
|
||||
name: systemd-timesyncd
|
||||
state: restarted
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/common/defaults/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
common_timezone: America/Chicago
|
||||
|
||||
common_ntp_server: 10.1.71.33 # sundial
|
||||
|
||||
common_packages:
|
||||
- curl
|
||||
- wget
|
||||
- vim
|
||||
- htop
|
||||
- git
|
||||
- jq
|
||||
- unzip
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- net-tools
|
||||
- dnsutils
|
||||
@@ -1,45 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/common/tasks/main.yml
|
||||
# DESCRIPTION: Baseline configuration applied to all managed hosts.
|
||||
# Handles hostname, timezone, core packages, NTP, and
|
||||
# ansible user setup.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Set hostname
|
||||
hostname:
|
||||
name: "{{ inventory_hostname | replace('_', '-') }}"
|
||||
|
||||
- name: Set timezone
|
||||
timezone:
|
||||
name: "{{ common_timezone }}"
|
||||
|
||||
- name: Update apt cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Install base utility packages
|
||||
apt:
|
||||
name: "{{ common_packages }}"
|
||||
state: present
|
||||
|
||||
- name: Configure NTP to use sundial
|
||||
template:
|
||||
src: timesyncd.conf.j2
|
||||
dest: /etc/systemd/timesyncd.conf
|
||||
mode: '0644'
|
||||
notify: restart timesyncd
|
||||
|
||||
- name: Ensure systemd-timesyncd is enabled and running
|
||||
systemd:
|
||||
name: systemd-timesyncd
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: Ensure ansible user has sudo without password
|
||||
lineinfile:
|
||||
path: /etc/sudoers.d/{{ ansible_user }}
|
||||
line: "{{ ansible_user }} ALL=(ALL) NOPASSWD:ALL"
|
||||
create: yes
|
||||
mode: '0440'
|
||||
validate: 'visudo -cf %s'
|
||||
@@ -1,4 +0,0 @@
|
||||
# Managed by Ansible — do not edit manually
|
||||
[Time]
|
||||
NTP={{ common_ntp_server }}
|
||||
FallbackNTP=ntp.ubuntu.com
|
||||
@@ -1,59 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/control-plane/tasks/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
- name: Check if cluster is already initialized
|
||||
stat:
|
||||
path: /etc/kubernetes/admin.conf
|
||||
register: kube_init_stat
|
||||
|
||||
- name: Initialize the Kubernetes cluster
|
||||
when: not kube_init_stat.stat.exists
|
||||
command: >
|
||||
kubeadm init
|
||||
--pod-network-cidr=10.244.0.0/16
|
||||
--control-plane-endpoint={{ inventory_hostname }}
|
||||
register: kubeadm_init
|
||||
|
||||
- name: Create .kube directory for the user
|
||||
become: no
|
||||
file:
|
||||
path: "{{ ansible_env.HOME }}/.kube"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
delegate_to: localhost
|
||||
run_once: true
|
||||
|
||||
- name: Copy admin.conf to user's .kube directory on controller
|
||||
when: not kube_init_stat.stat.exists
|
||||
fetch:
|
||||
src: /etc/kubernetes/admin.conf
|
||||
dest: "{{ ansible_env.HOME }}/.kube/config"
|
||||
flat: yes
|
||||
|
||||
- name: Install Calico CNI
|
||||
become: no
|
||||
command: kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
|
||||
delegate_to: localhost
|
||||
run_once: true
|
||||
when: not kube_init_stat.stat.exists
|
||||
|
||||
- name: Generate join command for control plane nodes
|
||||
when: not kube_init_stat.stat.exists
|
||||
command: kubeadm token create --print-join-command
|
||||
register: control_plane_join_command_raw
|
||||
|
||||
- name: Generate certificate key for control plane join
|
||||
when: not kube_init_stat.stat.exists
|
||||
command: kubeadm init phase upload-certs --upload-certs
|
||||
register: control_plane_cert_key
|
||||
|
||||
- name: Store control plane join command
|
||||
set_fact:
|
||||
control_plane_join_command: "{{ control_plane_join_command_raw.stdout }} --control-plane --certificate-key {{ control_plane_cert_key.stdout_lines[-1] }}"
|
||||
when: not kube_init_stat.stat.exists
|
||||
|
||||
- name: Join other control plane nodes to the cluster
|
||||
when:
|
||||
- inventory_hostname != groups['control_plane'][0]
|
||||
- not kube_init_stat.stat.exists
|
||||
command: "{{ hostvars[groups['control_plane'][0]].control_plane_join_command }}"
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
# Default variables for dns-manager role
|
||||
|
||||
# DNS Configuration
|
||||
dns_management_enabled: true
|
||||
use_hosts_file_fallback: false
|
||||
dns_ttl: 360
|
||||
dns_propagation_wait: 2
|
||||
create_ptr_record: true
|
||||
|
||||
# Cluster Configuration (to be provided by calling role)
|
||||
# cluster_endpoint: "fastpass.local.mk-labs.cloud"
|
||||
# cluster_vip: "10.1.71.53"
|
||||
|
||||
# DNS Server Configuration (from group_vars)
|
||||
# dns_server: "monorail"
|
||||
# base_domain: "local.mk-labs.cloud"
|
||||
# vault_technitium_api_key: "{{ vault_technitium_api_key }}"
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
# role: dns-manager
|
||||
# description: Reusable role for managing DNS entries across clusters using Technitium DNS
|
||||
# author: mk-labs
|
||||
# version: 1.0.0
|
||||
|
||||
- name: Create DNS entry using Technitium DNS task
|
||||
ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/add_technitium_dns_entry.yml"
|
||||
|
||||
- name: Wait for DNS propagation
|
||||
ansible.builtin.pause:
|
||||
seconds: "{{ dns_propagation_wait | default(10) }}"
|
||||
when: dns_management_enabled | default(true)
|
||||
|
||||
- name: Look up A (IPv4) records for example.org
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ query('community.dns.lookup', 'fastpass') }}"
|
||||
@@ -1,8 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/docker-host/handlers/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: restart docker
|
||||
systemd:
|
||||
name: docker
|
||||
state: restarted
|
||||
@@ -1,88 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/docker-host/tasks/main.yml
|
||||
# DESCRIPTION: Prepares an Ubuntu VM to run Docker Compose workloads.
|
||||
# Installs Docker CE, configures the daemon, and ensures
|
||||
# the ansible user (wed) can manage containers.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Install prerequisite packages
|
||||
apt:
|
||||
name:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg
|
||||
- lsb-release
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Create keyrings directory
|
||||
file:
|
||||
path: /etc/apt/keyrings
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Add Docker GPG key
|
||||
get_url:
|
||||
url: https://download.docker.com/linux/ubuntu/gpg
|
||||
dest: /etc/apt/keyrings/docker.asc
|
||||
mode: '0644'
|
||||
|
||||
- name: Add Docker repository
|
||||
apt_repository:
|
||||
repo: >-
|
||||
deb [arch={{ ansible_architecture | replace('x86_64', 'amd64') }}
|
||||
signed-by=/etc/apt/keyrings/docker.asc]
|
||||
https://download.docker.com/linux/ubuntu
|
||||
{{ ansible_distribution_release }} stable
|
||||
state: present
|
||||
filename: docker
|
||||
|
||||
- name: Install Docker CE and Compose plugin
|
||||
apt:
|
||||
name:
|
||||
- docker-ce
|
||||
- docker-ce-cli
|
||||
- containerd.io
|
||||
- docker-compose-plugin
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Ensure Docker service is started and enabled
|
||||
systemd:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: Add ansible user to docker group
|
||||
user:
|
||||
name: "{{ ansible_user }}"
|
||||
groups: docker
|
||||
append: yes
|
||||
|
||||
- name: Configure Docker daemon
|
||||
copy:
|
||||
dest: /etc/docker/daemon.json
|
||||
content: |
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "3"
|
||||
},
|
||||
"default-address-pools": [
|
||||
{
|
||||
"base": "172.17.0.0/16",
|
||||
"size": 24
|
||||
}
|
||||
]
|
||||
}
|
||||
mode: '0644'
|
||||
notify: restart docker
|
||||
|
||||
- name: Create Docker Compose project directory
|
||||
file:
|
||||
path: /opt/docker
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: '0775'
|
||||
@@ -1,72 +0,0 @@
|
||||
---
|
||||
# Container runtime setup for Kubernetes
|
||||
|
||||
- name: Add containerd repository
|
||||
become: true
|
||||
ansible.builtin.yum_repository:
|
||||
name: docker-ce
|
||||
description: "Docker CE Repository"
|
||||
baseurl: "https://download.docker.com/linux/fedora/{{ ansible_distribution_major_version }}/x86_64/stable"
|
||||
gpgkey: "https://download.docker.com/linux/fedora/gpg"
|
||||
gpgcheck: true
|
||||
enabled: true
|
||||
state: present
|
||||
|
||||
- name: Install containerd
|
||||
become: true
|
||||
ansible.builtin.dnf:
|
||||
name: containerd
|
||||
state: present
|
||||
disable_excludes: docker-ce-stable
|
||||
|
||||
- name: Create containerd config directory
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: /etc/containerd
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Generate default containerd config
|
||||
become: true
|
||||
ansible.builtin.shell: |
|
||||
containerd config default > /etc/containerd/config.toml
|
||||
args:
|
||||
creates: /etc/containerd/config.toml
|
||||
|
||||
- name: Configure containerd for Kubernetes
|
||||
become: true
|
||||
ansible.builtin.replace:
|
||||
path: /etc/containerd/config.toml
|
||||
regexp: 'SystemdCgroup = false'
|
||||
replace: 'SystemdCgroup = true'
|
||||
|
||||
- name: Create containerd service override directory
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: /etc/systemd/system/containerd.service.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Configure containerd service override
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/containerd.service.d/override.conf
|
||||
content: |
|
||||
[Service]
|
||||
ExecStartPre=-/sbin/modprobe overlay
|
||||
ExecStartPre=-/sbin/modprobe br_netfilter
|
||||
mode: '0644'
|
||||
|
||||
- name: Restart and enable containerd service
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: containerd
|
||||
state: restarted
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
|
||||
- name: Verify containerd is running
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: containerd
|
||||
state: started
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
# FastPass Kubernetes Cluster Preparation for Fedora
|
||||
# This role prepares all nodes for Kubernetes installation
|
||||
|
||||
- name: Include preflight checks
|
||||
ansible.builtin.include_tasks: preflight.yml
|
||||
when: preflight_checks | default(true)
|
||||
|
||||
- name: Include system preparation
|
||||
ansible.builtin.include_tasks: system-prep.yml
|
||||
|
||||
- name: Include container runtime setup
|
||||
ansible.builtin.include_tasks: container-runtime.yml
|
||||
|
||||
- name: Include Kubernetes installation
|
||||
ansible.builtin.include_tasks: kubernetes-install.yml
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
# Preflight checks for Kubernetes installation
|
||||
|
||||
- name: Check if running on supported OS
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_os_family == "RedHat"
|
||||
- ansible_distribution == "Fedora"
|
||||
fail_msg: "This role only supports Fedora"
|
||||
success_msg: "OS check passed"
|
||||
|
||||
- name: Check minimum memory requirement (2GB)
|
||||
ansible.builtin.assert:
|
||||
that: ansible_memtotal_mb >= 2048
|
||||
fail_msg: "Minimum 2GB RAM required, found {{ ansible_memtotal_mb }}MB"
|
||||
success_msg: "Memory check passed: {{ ansible_memtotal_mb }}MB"
|
||||
|
||||
- name: Check minimum CPU cores (2)
|
||||
ansible.builtin.assert:
|
||||
that: ansible_processor_cores >= 2
|
||||
fail_msg: "Minimum 2 CPU cores required, found {{ ansible_processor_cores }}"
|
||||
success_msg: "CPU check passed: {{ ansible_processor_cores }} cores"
|
||||
|
||||
- name: Check available disk space (10GB)
|
||||
ansible.builtin.assert:
|
||||
that: ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first >= 10737418240
|
||||
fail_msg: "Minimum 10GB free space required on root filesystem"
|
||||
success_msg: "Disk space check passed"
|
||||
|
||||
- name: Check if system is 64-bit
|
||||
ansible.builtin.assert:
|
||||
that: ansible_architecture in ['x86_64', 'amd64']
|
||||
fail_msg: "Only 64-bit architectures are supported"
|
||||
success_msg: "Architecture check passed: {{ ansible_architecture }}"
|
||||
|
||||
- name: Check if running as root or with sudo
|
||||
ansible.builtin.assert:
|
||||
that: ansible_become | default(false)
|
||||
fail_msg: "This role requires root privileges (become: true)"
|
||||
success_msg: "Privilege check passed"
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
# System preparation for Kubernetes
|
||||
|
||||
- name: Disable swap
|
||||
ansible.builtin.command: swapoff -a
|
||||
become: true
|
||||
changed_when: false
|
||||
when: disable_swap | default(true)
|
||||
|
||||
- name: Persist swap off by commenting out swap in fstab
|
||||
become: true
|
||||
ansible.builtin.replace:
|
||||
path: /etc/fstab
|
||||
regexp: '^(\s*)([^#\n]+\s+swap\s+.*)$'
|
||||
replace: '#\2'
|
||||
backup: true
|
||||
when: disable_swap | default(true)
|
||||
|
||||
- name: Load required kernel modules
|
||||
become: true
|
||||
community.general.modprobe:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- overlay
|
||||
- br_netfilter
|
||||
|
||||
- name: Persist kernel modules on boot
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/modules-load.d/k8s.conf
|
||||
content: |
|
||||
overlay
|
||||
br_netfilter
|
||||
mode: '0644'
|
||||
|
||||
- name: Configure required sysctl params for Kubernetes
|
||||
become: true
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/sysctl.d/k8s.conf
|
||||
content: |
|
||||
net.bridge.bridge-nf-call-iptables = 1
|
||||
net.bridge.bridge-nf-call-ip6tables = 1
|
||||
net.ipv4.ip_forward = 1
|
||||
mode: '0644'
|
||||
|
||||
- name: Apply sysctl params without reboot
|
||||
become: true
|
||||
ansible.builtin.command: sysctl --system
|
||||
changed_when: false
|
||||
|
||||
- name: Install required packages
|
||||
become: true
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- python3-libselinux
|
||||
- dnf-plugins-core
|
||||
- curl
|
||||
- wget
|
||||
- ca-certificates
|
||||
state: present
|
||||
|
||||
- name: Set SELinux to permissive mode
|
||||
become: true
|
||||
ansible.posix.selinux:
|
||||
policy: targeted
|
||||
state: "{{ selinux_mode | default('permissive') }}"
|
||||
|
||||
- name: Stop and disable firewalld
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: firewalld
|
||||
state: stopped
|
||||
enabled: false
|
||||
when: not firewall_enabled | default(false)
|
||||
|
||||
- name: Update system packages
|
||||
become: true
|
||||
ansible.builtin.dnf:
|
||||
name: "*"
|
||||
state: latest
|
||||
async: 300
|
||||
poll: 10
|
||||
@@ -1,147 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/defaults/main.yml
|
||||
|
||||
# HAProxy version and installation
|
||||
haproxy_version: "3.2"
|
||||
haproxy_install_method: "package" # package or source
|
||||
|
||||
# System configuration
|
||||
haproxy_user: haproxy
|
||||
haproxy_group: haproxy
|
||||
haproxy_chroot_dir: /var/lib/haproxy
|
||||
haproxy_config_dir: /etc/haproxy
|
||||
haproxy_log_dir: /var/log/haproxy
|
||||
|
||||
# Global settings
|
||||
haproxy_global_maxconn: 4096
|
||||
haproxy_global_log_facility: local0
|
||||
haproxy_global_log_level: info
|
||||
haproxy_global_nbthread: "{{ ansible_processor_vcpus }}"
|
||||
|
||||
# Default timeouts (milliseconds)
|
||||
haproxy_timeout_connect: 5000
|
||||
haproxy_timeout_client: 50000
|
||||
haproxy_timeout_server: 50000
|
||||
|
||||
# Stats interface
|
||||
haproxy_stats_enable: true
|
||||
haproxy_stats_port: 8404
|
||||
haproxy_stats_uri: /stats
|
||||
haproxy_stats_refresh: 30s
|
||||
haproxy_stats_username: admin
|
||||
haproxy_stats_password: "{{ vault_haproxy_stats_password | default('changeme') }}"
|
||||
|
||||
# SSL/TLS configuration
|
||||
haproxy_ssl_cert_dir: "{{ haproxy_config_dir }}/certs"
|
||||
haproxy_ssl_default_crt: "{{ haproxy_ssl_cert_dir }}/default.pem"
|
||||
haproxy_ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
|
||||
haproxy_ssl_options: "ssl-min-ver TLSv1.2 no-tls-tickets"
|
||||
|
||||
# Health check defaults
|
||||
haproxy_health_check_interval: 2000
|
||||
haproxy_health_check_timeout: 1000
|
||||
haproxy_health_check_rise: 2
|
||||
haproxy_health_check_fall: 3
|
||||
|
||||
# Service discovery integration
|
||||
haproxy_netbox_integration: false # true
|
||||
haproxy_netbox_url: "http://fire-station.local.mk-labs.cloud:8000"
|
||||
haproxy_netbox_token: "{{ vault_netbox_token | default('') }}"
|
||||
|
||||
# Frontends configuration
|
||||
haproxy_frontends: []
|
||||
# - name: http_front
|
||||
# bind: "*:80"
|
||||
# mode: http
|
||||
# redirect: "scheme https code 301"
|
||||
#
|
||||
# - name: https_front
|
||||
# bind: "*:443 ssl crt {{ haproxy_ssl_cert_dir }}"
|
||||
# mode: http
|
||||
# options:
|
||||
# - "http-server-close"
|
||||
# - "forwardfor"
|
||||
# acls:
|
||||
# - "netbox hdr(host) -i fire-station.local.mk-labs.cloud"
|
||||
# - "wordpress hdr(host) -i be-our-guest.local.mk-labs.cloud"
|
||||
# use_backends:
|
||||
# - "netbox_back if netbox"
|
||||
# - "wordpress_back if wordpress"
|
||||
|
||||
# Backends configuration
|
||||
haproxy_backends: []
|
||||
# - name: netbox_back
|
||||
# mode: http
|
||||
# balance: roundrobin
|
||||
# options:
|
||||
# - "httpchk GET /"
|
||||
# servers:
|
||||
# - name: fire-station
|
||||
# address: 10.1.71.102:8000
|
||||
# check: true
|
||||
|
||||
# TCP services configuration
|
||||
haproxy_tcp_services: []
|
||||
# - name: postgres
|
||||
# frontend_port: 5432
|
||||
# backend_port: 5432
|
||||
# balance: leastconn
|
||||
# servers:
|
||||
# - name: netbox-db
|
||||
# address: 10.1.71.102
|
||||
|
||||
# Firewall configuration
|
||||
haproxy_configure_firewall: true
|
||||
haproxy_firewall_allowed_ports:
|
||||
- 80/tcp
|
||||
- 443/tcp
|
||||
- 8404/tcp
|
||||
|
||||
# Monitoring and observability
|
||||
haproxy_prometheus_exporter: false
|
||||
haproxy_prometheus_port: 9101
|
||||
|
||||
# Logging
|
||||
haproxy_rsyslog_config: true
|
||||
haproxy_log_rotation: true
|
||||
|
||||
# Certificate management - Certbot with Cloudflare DNS
|
||||
haproxy_certbot_enable: true
|
||||
haproxy_certbot_challenge_method: "dns-cloudflare" # or "webroot" for HTTP-01
|
||||
haproxy_certbot_email: "ryan.blundon@protonmail.com"
|
||||
haproxy_certbot_staging: false # Set to true for testing with Let's Encrypt staging
|
||||
|
||||
# Cloudflare DNS configuration
|
||||
haproxy_certbot_cloudflare_api_token: "{{ vault_cloudflare_api_token }}"
|
||||
haproxy_certbot_cloudflare_credentials_file: "/etc/letsencrypt/cloudflare.ini"
|
||||
|
||||
# Certificate domains - DNS-01 supports wildcards
|
||||
haproxy_certbot_domains:
|
||||
- domain: "*.mk-labs.cloud"
|
||||
include_base: true
|
||||
- domain: "*.local.mk-labs.cloud"
|
||||
include_base: true # Also include local.mk-labs.cloud
|
||||
- domain: "lightning-lane.local.mk-labs.cloud"
|
||||
include_base: false
|
||||
|
||||
# Alternative: specific domains without wildcard
|
||||
# haproxy_certbot_domains:
|
||||
# - domain: "lightning-lane.local.mk-labs.cloud"
|
||||
# - domain: "fire-station.local.mk-labs.cloud"
|
||||
# - domain: "be-our-guest.local.mk-labs.cloud"
|
||||
|
||||
# Certbot renewal settings
|
||||
haproxy_certbot_renewal_cron_minute: "0"
|
||||
haproxy_certbot_renewal_cron_hour: "3" # 3 AM daily
|
||||
haproxy_certbot_renewal_cron_day: "*"
|
||||
haproxy_certbot_post_hook: "{{ haproxy_config_dir }}/certbot-post-hook.sh"
|
||||
|
||||
# Certificate storage
|
||||
haproxy_certbot_cert_dir: "/etc/letsencrypt/live"
|
||||
haproxy_certbot_archive_dir: "/etc/letsencrypt/archive"
|
||||
|
||||
# DNS propagation wait time (seconds)
|
||||
haproxy_certbot_dns_propagation_seconds: 60
|
||||
|
||||
# Fallback to self-signed if Certbot fails
|
||||
haproxy_certbot_fallback_selfsigned: true
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/handlers/main.yml
|
||||
|
||||
- name: Reload haproxy
|
||||
ansible.builtin.systemd:
|
||||
name: haproxy
|
||||
state: reloaded
|
||||
listen: reload haproxy
|
||||
|
||||
- name: Restart haproxy
|
||||
ansible.builtin.systemd:
|
||||
name: haproxy
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
listen: restart haproxy
|
||||
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
listen: reload systemd
|
||||
|
||||
- name: Restart rsyslog
|
||||
ansible.builtin.systemd:
|
||||
name: rsyslog
|
||||
state: restarted
|
||||
listen: restart rsyslog
|
||||
@@ -1,202 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/tasks/certbot-cloudflare.yml
|
||||
|
||||
- name: Install Certbot and Cloudflare DNS plugin
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- certbot
|
||||
- python3-certbot-dns-cloudflare
|
||||
state: present
|
||||
|
||||
- name: Create Let's Encrypt configuration directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/letsencrypt
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: Create Cloudflare credentials file
|
||||
ansible.builtin.template:
|
||||
src: cloudflare-credentials.ini.j2
|
||||
dest: "{{ haproxy_certbot_cloudflare_credentials_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0600'
|
||||
no_log: true
|
||||
|
||||
- name: Create Certbot post-renewal hook script
|
||||
ansible.builtin.template:
|
||||
src: certbot-post-hook.sh.j2
|
||||
dest: "{{ haproxy_certbot_post_hook }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: Create Certbot renewal hooks directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/letsencrypt/renewal-hooks/deploy
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Link post-hook to renewal hooks directory
|
||||
ansible.builtin.file:
|
||||
src: "{{ haproxy_certbot_post_hook }}"
|
||||
dest: /etc/letsencrypt/renewal-hooks/deploy/haproxy-reload.sh
|
||||
state: link
|
||||
force: true
|
||||
|
||||
- name: Check for existing certificates
|
||||
ansible.builtin.stat:
|
||||
path: "{{ haproxy_certbot_cert_dir }}/{{ item.domain | regex_replace('\\*\\.', '') }}"
|
||||
register: existing_certs
|
||||
loop: "{{ haproxy_certbot_domains }}"
|
||||
loop_control:
|
||||
label: "{{ item.domain }}"
|
||||
|
||||
- name: Build certificate request commands
|
||||
ansible.builtin.set_fact:
|
||||
certbot_commands: "{{ certbot_commands | default([]) + [command_item] }}"
|
||||
vars:
|
||||
cert_name: "{{ item.item.domain | regex_replace('\\*\\.', '') }}"
|
||||
domain_list: >-
|
||||
{{ [item.item.domain] +
|
||||
(item.item.include_base | default(false) | ternary(
|
||||
[item.item.domain | regex_replace('\\*\\.', '')],
|
||||
[]
|
||||
))
|
||||
}}
|
||||
command_item:
|
||||
domain: "{{ item.item.domain }}"
|
||||
cert_name: "{{ cert_name }}"
|
||||
domains: "{{ domain_list }}"
|
||||
exists: "{{ item.stat.exists }}"
|
||||
loop: "{{ existing_certs.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.domain }}"
|
||||
|
||||
- name: Display certificate request plan
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Will request certificates for:
|
||||
{% for cmd in certbot_commands %}
|
||||
- {{ cmd.cert_name }} ({{ cmd.domains | join(', ') }}) - {{ 'EXISTS' if cmd.exists else 'NEW' }}
|
||||
{% endfor %}
|
||||
|
||||
- name: Request certificates from Let's Encrypt via Cloudflare DNS
|
||||
ansible.builtin.command: >
|
||||
certbot certonly
|
||||
--non-interactive
|
||||
--agree-tos
|
||||
--email {{ haproxy_certbot_email }}
|
||||
--dns-cloudflare
|
||||
--dns-cloudflare-credentials {{ haproxy_certbot_cloudflare_credentials_file }}
|
||||
--dns-cloudflare-propagation-seconds {{ haproxy_certbot_dns_propagation_seconds }}
|
||||
--cert-name {{ item.cert_name }}
|
||||
{% for domain in item.domains %}
|
||||
--domain {{ domain }}
|
||||
{% endfor %}
|
||||
{% if haproxy_certbot_staging %}
|
||||
--staging
|
||||
{% endif %}
|
||||
--deploy-hook {{ haproxy_certbot_post_hook }}
|
||||
loop: "{{ certbot_commands }}"
|
||||
loop_control:
|
||||
label: "{{ item.cert_name }}"
|
||||
when: not item.exists
|
||||
register: certbot_result
|
||||
failed_when:
|
||||
- certbot_result.rc != 0
|
||||
- "'Certificate not yet due for renewal' not in certbot_result.stdout"
|
||||
- "'too many certificates already issued' not in certbot_result.stderr"
|
||||
|
||||
- name: Display certificate request results
|
||||
ansible.builtin.debug:
|
||||
msg: "Certificate for {{ item.item.cert_name }}: {{ 'SUCCESS' if item.rc == 0 else 'SKIPPED/FAILED' }}"
|
||||
loop: "{{ certbot_result.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.cert_name }}"
|
||||
when: certbot_result.results is defined
|
||||
|
||||
- name: Run post-hook to combine certificates for HAProxy
|
||||
ansible.builtin.command: "{{ haproxy_certbot_post_hook }}"
|
||||
changed_when: true
|
||||
when: certbot_result.changed
|
||||
|
||||
- name: Setup automatic certificate renewal via cron
|
||||
ansible.builtin.cron:
|
||||
name: "Certbot certificate renewal"
|
||||
minute: "{{ haproxy_certbot_renewal_cron_minute }}"
|
||||
hour: "{{ haproxy_certbot_renewal_cron_hour }}"
|
||||
day: "{{ haproxy_certbot_renewal_cron_day }}"
|
||||
job: "/usr/bin/certbot renew --quiet --deploy-hook {{ haproxy_certbot_post_hook }} 2>&1 | logger -t certbot"
|
||||
user: root
|
||||
state: present
|
||||
|
||||
- name: Create systemd timer for certificate renewal (alternative to cron)
|
||||
when: ansible_service_mgr == "systemd"
|
||||
block:
|
||||
- name: Create certbot renewal service
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Certbot Renewal
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook {{ haproxy_certbot_post_hook }}
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
dest: /etc/systemd/system/certbot-renewal.service
|
||||
mode: '0644'
|
||||
|
||||
- name: Create certbot renewal timer
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Certbot Renewal Timer
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
RandomizedDelaySec=1h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
dest: /etc/systemd/system/certbot-renewal.timer
|
||||
mode: '0644'
|
||||
|
||||
- name: Enable and start certbot renewal timer
|
||||
ansible.builtin.systemd:
|
||||
name: certbot-renewal.timer
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
|
||||
- name: Test certificate renewal (dry-run)
|
||||
ansible.builtin.command: certbot renew --dry-run --dns-cloudflare --dns-cloudflare-credentials {{ haproxy_certbot_cloudflare_credentials_file }}
|
||||
register: renewal_test
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Display renewal test results
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Certificate renewal dry-run: {{ 'SUCCESS ✓' if renewal_test.rc == 0 else 'FAILED ✗' }}
|
||||
|
||||
{% if renewal_test.rc == 0 %}
|
||||
Your certificates will renew automatically.
|
||||
{% else %}
|
||||
Please check the output above for errors.
|
||||
{% endif %}
|
||||
|
||||
- name: List all certificates
|
||||
ansible.builtin.command: certbot certificates
|
||||
register: cert_list
|
||||
changed_when: false
|
||||
|
||||
- name: Display certificate information
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ cert_list.stdout_lines }}"
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/tasks/certificates.yml
|
||||
|
||||
- name: Check if default certificate exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ haproxy_ssl_default_crt }}"
|
||||
register: default_cert
|
||||
|
||||
- name: Generate self-signed certificate for development
|
||||
when: not default_cert.stat.exists
|
||||
block:
|
||||
- name: Generate private key
|
||||
openssl_privatekey:
|
||||
path: "{{ haproxy_ssl_cert_dir }}/default.key"
|
||||
size: 2048
|
||||
mode: '0600'
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
|
||||
- name: Generate certificate signing request
|
||||
openssl_csr:
|
||||
path: "{{ haproxy_ssl_cert_dir }}/default.csr"
|
||||
privatekey_path: "{{ haproxy_ssl_cert_dir }}/default.key"
|
||||
common_name: "lightning-lane.local.mk-labs.cloud"
|
||||
subject_alt_name:
|
||||
- "DNS:lightning-lane.local.mk-labs.cloud"
|
||||
- "DNS:*.local.mk-labs.cloud"
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
|
||||
- name: Generate self-signed certificate
|
||||
openssl_certificate:
|
||||
path: "{{ haproxy_ssl_cert_dir }}/default.crt"
|
||||
privatekey_path: "{{ haproxy_ssl_cert_dir }}/default.key"
|
||||
csr_path: "{{ haproxy_ssl_cert_dir }}/default.csr"
|
||||
provider: selfsigned
|
||||
selfsigned_not_after: "+3650d"
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
|
||||
- name: Combine certificate and key for HAProxy
|
||||
ansible.builtin.shell: |
|
||||
cat {{ haproxy_ssl_cert_dir }}/default.crt \
|
||||
{{ haproxy_ssl_cert_dir }}/default.key \
|
||||
> {{ haproxy_ssl_default_crt }}
|
||||
args:
|
||||
creates: "{{ haproxy_ssl_default_crt }}"
|
||||
|
||||
- name: Set permissions on combined certificate
|
||||
ansible.builtin.file:
|
||||
path: "{{ haproxy_ssl_default_crt }}"
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
mode: '0600'
|
||||
|
||||
- name: Create certificate directory structure
|
||||
ansible.builtin.file:
|
||||
path: "{{ haproxy_ssl_cert_dir }}/{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
mode: '0755'
|
||||
loop:
|
||||
- live
|
||||
- archive
|
||||
|
||||
- name: Deploy additional certificates
|
||||
when: haproxy_ssl_certificates is defined
|
||||
block:
|
||||
- name: Copy certificate files
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ haproxy_ssl_cert_dir }}/live/{{ item.name }}.pem"
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
mode: '0600'
|
||||
loop: "{{ haproxy_ssl_certificates }}"
|
||||
notify: Reload haproxy
|
||||
@@ -1,94 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/tasks/configure.yml
|
||||
|
||||
- name: Fetch services from NetBox
|
||||
when: haproxy_netbox_integration
|
||||
block:
|
||||
- name: Query NetBox for service configurations
|
||||
ansible.builtin.uri:
|
||||
url: "{{ haproxy_netbox_url }}/api/ipam/services/"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Token {{ haproxy_netbox_token }}"
|
||||
Accept: "application/json"
|
||||
return_content: true
|
||||
register: netbox_services
|
||||
changed_when: false
|
||||
|
||||
- name: Parse NetBox services
|
||||
ansible.builtin.set_fact:
|
||||
haproxy_frontends: "{{ haproxy_frontends | default([]) + netbox_parsed_frontends }}"
|
||||
haproxy_backends: "{{ haproxy_backends | default([]) + netbox_parsed_backends }}"
|
||||
vars:
|
||||
netbox_parsed_frontends: "{{ netbox_services.json.results | map(attribute='custom_fields') | list }}"
|
||||
netbox_parsed_backends: "{{ netbox_services.json.results | map(attribute='custom_fields') | list }}"
|
||||
when: netbox_services.json.results is defined
|
||||
|
||||
- name: Generate HAProxy main configuration
|
||||
ansible.builtin.template:
|
||||
src: haproxy.cfg.j2
|
||||
dest: "{{ haproxy_config_dir }}/haproxy.cfg"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
backup: true
|
||||
validate: 'haproxy -c -f %s'
|
||||
notify: reload haproxy
|
||||
|
||||
- name: Configure rsyslog for HAProxy
|
||||
when: haproxy_rsyslog_config
|
||||
block:
|
||||
- name: Create rsyslog configuration for HAProxy
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
# HAProxy logging configuration
|
||||
$ModLoad imudp
|
||||
$UDPServerRun 514
|
||||
|
||||
# Create separate log files for HAProxy
|
||||
local0.* {{ haproxy_log_dir }}/haproxy.log
|
||||
local0.info {{ haproxy_log_dir }}/haproxy-info.log
|
||||
local0.notice {{ haproxy_log_dir }}/haproxy-notice.log
|
||||
local0.err {{ haproxy_log_dir }}/haproxy-error.log
|
||||
|
||||
# Stop processing HAProxy logs
|
||||
& stop
|
||||
dest: /etc/rsyslog.d/49-haproxy.conf
|
||||
mode: '0644'
|
||||
notify: restart rsyslog
|
||||
|
||||
- name: Configure logrotate for HAProxy
|
||||
when: haproxy_log_rotation
|
||||
ansible.builtin.template:
|
||||
src: logrotate.j2
|
||||
dest: /etc/logrotate.d/haproxy
|
||||
mode: '0644'
|
||||
|
||||
- name: Create HAProxy systemd override directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/systemd/system/haproxy.service.d
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Configure HAProxy systemd service overrides
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
[Service]
|
||||
# Increase file descriptor limits
|
||||
LimitNOFILE=65536
|
||||
|
||||
# Runtime directory
|
||||
RuntimeDirectory=haproxy
|
||||
RuntimeDirectoryMode=0755
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths={{ haproxy_log_dir }}
|
||||
dest: /etc/systemd/system/haproxy.service.d/override.conf
|
||||
mode: '0644'
|
||||
notify:
|
||||
- Reload systemd
|
||||
- Restart haproxy
|
||||
@@ -1,128 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/tasks/install.yml
|
||||
|
||||
- name: Install HAProxy repository (Ubuntu)
|
||||
when: ansible_os_family == "Debian"
|
||||
block:
|
||||
- name: Add HAProxy PPA
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "ppa:vbernat/haproxy-{{ haproxy_version }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install HAProxy
|
||||
ansible.builtin.apt:
|
||||
name: haproxy
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Install HAProxy (Fedora/RHEL)
|
||||
when: ansible_os_family == "RedHat"
|
||||
block:
|
||||
- name: Install HAProxy
|
||||
ansible.builtin.dnf:
|
||||
name: haproxy
|
||||
state: present
|
||||
|
||||
- name: Install additional packages
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- rsyslog
|
||||
- logrotate
|
||||
- socat # For HAProxy socket communication
|
||||
state: present
|
||||
|
||||
- name: Create HAProxy system user
|
||||
ansible.builtin.user:
|
||||
name: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
system: true
|
||||
shell: /usr/sbin/nologin
|
||||
home: "{{ haproxy_chroot_dir }}"
|
||||
create_home: true
|
||||
|
||||
- name: Create required directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
mode: '0755'
|
||||
loop:
|
||||
- "{{ haproxy_config_dir }}"
|
||||
- "{{ haproxy_ssl_cert_dir }}"
|
||||
- "{{ haproxy_log_dir }}"
|
||||
- "{{ haproxy_chroot_dir }}"
|
||||
- /var/lib/haproxy/dev
|
||||
|
||||
- name: Create HAProxy socket directory
|
||||
ansible.builtin.file:
|
||||
path: /run/haproxy
|
||||
state: directory
|
||||
owner: "{{ haproxy_user }}"
|
||||
group: "{{ haproxy_group }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Configure firewall rules
|
||||
when: haproxy_configure_firewall
|
||||
block:
|
||||
- name: Open HAProxy ports (UFW)
|
||||
when: ansible_os_family == "Debian"
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item.split('/')[0] }}"
|
||||
proto: "{{ item.split('/')[1] }}"
|
||||
loop: "{{ haproxy_firewall_allowed_ports }}"
|
||||
|
||||
- name: Open HAProxy ports (firewalld)
|
||||
when: ansible_os_family == "RedHat"
|
||||
ansible.posix.firewalld:
|
||||
port: "{{ item }}"
|
||||
permanent: true
|
||||
state: enabled
|
||||
immediate: true
|
||||
loop: "{{ haproxy_firewall_allowed_ports }}"
|
||||
|
||||
- name: Install Prometheus HAProxy exporter
|
||||
when: haproxy_prometheus_exporter
|
||||
block:
|
||||
- name: Download HAProxy exporter
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/prometheus/haproxy_exporter/releases/download/v0.15.0/haproxy_exporter-0.15.0.linux-amd64.tar.gz"
|
||||
dest: /tmp/haproxy_exporter.tar.gz
|
||||
mode: '0644'
|
||||
|
||||
- name: Extract HAProxy exporter
|
||||
ansible.builtin.unarchive:
|
||||
src: /tmp/haproxy_exporter.tar.gz
|
||||
dest: /usr/local/bin/
|
||||
remote_src: true
|
||||
extra_opts:
|
||||
- --strip-components=1
|
||||
- --wildcards
|
||||
- '*/haproxy_exporter'
|
||||
|
||||
- name: Create HAProxy exporter systemd service
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
[Unit]
|
||||
Description=HAProxy Exporter
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ haproxy_user }}
|
||||
ExecStart=/usr/local/bin/haproxy_exporter --haproxy.scrape-uri=unix:/run/haproxy/admin.sock
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
dest: /etc/systemd/system/haproxy-exporter.service
|
||||
mode: '0644'
|
||||
|
||||
- name: Enable and start HAProxy exporter
|
||||
ansible.builtin.systemd:
|
||||
name: haproxy-exporter
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/tasks/main.yml
|
||||
|
||||
# - name: Include OS-specific variables
|
||||
# include_vars: "{{ ansible_os_family }}.yml"
|
||||
# tags: [haproxy, always]
|
||||
|
||||
- name: Include installation tasks
|
||||
ansible.builtin.include_tasks: install.yml
|
||||
tags: [haproxy, install]
|
||||
|
||||
- name: Include Certbot with Cloudflare DNS tasks
|
||||
ansible.builtin.include_tasks: certbot-cloudflare.yml
|
||||
when:
|
||||
- haproxy_certbot_enable
|
||||
- haproxy_certbot_challenge_method == "dns-cloudflare"
|
||||
tags: [haproxy, certificates, certbot, cloudflare]
|
||||
|
||||
- name: Include fallback self-signed certificate tasks
|
||||
ansible.builtin.include_tasks: certificates.yml
|
||||
when:
|
||||
- not haproxy_certbot_enable
|
||||
- haproxy_certbot_fallback_selfsigned | default(false)
|
||||
tags: [haproxy, certificates, selfsigned]
|
||||
|
||||
- name: Include configuration tasks
|
||||
ansible.builtin.include_tasks: configure.yml
|
||||
tags: [haproxy, configure]
|
||||
|
||||
- name: Include validation tasks
|
||||
ansible.builtin.include_tasks: validate.yml
|
||||
tags: [haproxy, validate]
|
||||
|
||||
- name: Ensure HAProxy is started and enabled
|
||||
ansible.builtin.systemd:
|
||||
name: haproxy
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
tags: [haproxy, service]
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
# ansible/roles/haproxy/tasks/validate.yml
|
||||
|
||||
- name: Validate HAProxy configuration syntax
|
||||
command: haproxy -c -f {{ haproxy_config_dir }}/haproxy.cfg
|
||||
register: haproxy_validation
|
||||
changed_when: false
|
||||
failed_when: haproxy_validation.rc != 0
|
||||
|
||||
- name: Display validation results
|
||||
debug:
|
||||
msg: "{{ haproxy_validation.stdout_lines }}"
|
||||
when: haproxy_validation.stdout_lines is defined
|
||||
|
||||
- name: Check HAProxy service status
|
||||
systemd:
|
||||
name: haproxy
|
||||
state: started
|
||||
register: haproxy_status
|
||||
changed_when: false
|
||||
|
||||
- name: Verify HAProxy is listening on configured ports
|
||||
wait_for:
|
||||
host: "{{ ansible_default_ipv4.address }}"
|
||||
port: "{{ item }}"
|
||||
timeout: 10
|
||||
loop:
|
||||
- 80
|
||||
- 443
|
||||
- "{{ haproxy_stats_port }}"
|
||||
when: haproxy_status.status.ActiveState == "active"
|
||||
|
||||
- name: Test stats interface accessibility
|
||||
uri:
|
||||
url: "http://localhost:{{ haproxy_stats_port }}{{ haproxy_stats_uri }}"
|
||||
user: "{{ haproxy_stats_username }}"
|
||||
password: "{{ haproxy_stats_password }}"
|
||||
force_basic_auth: true
|
||||
status_code: 200
|
||||
register: stats_test
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Display stats interface status
|
||||
debug:
|
||||
msg: "Stats interface is {{ 'accessible' if stats_test.status == 200 else 'not accessible' }}"
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/bin/bash
|
||||
# {{ ansible_managed }}
|
||||
# Certbot post-renewal hook for HAProxy
|
||||
# Combines certificate and private key for HAProxy consumption
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
LETSENCRYPT_LIVE="/etc/letsencrypt/live"
|
||||
HAPROXY_CERT_DIR="{{ haproxy_ssl_cert_dir }}/live"
|
||||
HAPROXY_USER="{{ haproxy_user }}"
|
||||
HAPROXY_GROUP="{{ haproxy_group }}"
|
||||
LOG_FILE="{{ haproxy_log_dir }}/certbot-hook.log"
|
||||
|
||||
# Ensure HAProxy cert directory exists
|
||||
mkdir -p "$HAPROXY_CERT_DIR"
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log "Starting certificate deployment for HAProxy"
|
||||
|
||||
# Track if any certificates were updated
|
||||
CERTS_UPDATED=false
|
||||
|
||||
# Process each certificate in Let's Encrypt live directory
|
||||
for cert_dir in "$LETSENCRYPT_LIVE"/*; do
|
||||
if [ ! -d "$cert_dir" ] || [ "$(basename "$cert_dir")" = "README" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
CERT_NAME=$(basename "$cert_dir")
|
||||
|
||||
# Check if certificate files exist
|
||||
if [ ! -f "$cert_dir/fullchain.pem" ] || [ ! -f "$cert_dir/privkey.pem" ]; then
|
||||
log "WARNING: Missing certificate files for $CERT_NAME, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
log "Processing certificate: $CERT_NAME"
|
||||
|
||||
# Create combined PEM file (fullchain + privkey)
|
||||
# HAProxy requires the certificate chain and private key in a single file
|
||||
COMBINED_PEM="$HAPROXY_CERT_DIR/${CERT_NAME}.pem"
|
||||
|
||||
# Combine certificates
|
||||
cat "$cert_dir/fullchain.pem" "$cert_dir/privkey.pem" > "$COMBINED_PEM.tmp"
|
||||
|
||||
# Check if certificate actually changed
|
||||
if [ -f "$COMBINED_PEM" ] && cmp -s "$COMBINED_PEM" "$COMBINED_PEM.tmp"; then
|
||||
log "Certificate $CERT_NAME unchanged, skipping"
|
||||
rm "$COMBINED_PEM.tmp"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Move new certificate into place
|
||||
mv "$COMBINED_PEM.tmp" "$COMBINED_PEM"
|
||||
|
||||
# Set proper permissions
|
||||
chown "$HAPROXY_USER:$HAPROXY_GROUP" "$COMBINED_PEM"
|
||||
chmod 600 "$COMBINED_PEM"
|
||||
|
||||
log "Created/Updated: $COMBINED_PEM"
|
||||
CERTS_UPDATED=true
|
||||
done
|
||||
|
||||
# Create/update default certificate symlink
|
||||
# Use the first wildcard cert or the first available cert as default
|
||||
DEFAULT_CERT="{{ haproxy_ssl_default_crt }}"
|
||||
WILDCARD_CERT="$HAPROXY_CERT_DIR/local.mk-labs.cloud.pem"
|
||||
|
||||
if [ -f "$WILDCARD_CERT" ]; then
|
||||
ln -sf "$WILDCARD_CERT" "$DEFAULT_CERT"
|
||||
log "Updated default certificate symlink to wildcard cert"
|
||||
elif [ -n "$(ls -A "$HAPROXY_CERT_DIR" 2>/dev/null)" ]; then
|
||||
FIRST_CERT=$(ls "$HAPROXY_CERT_DIR"/*.pem 2>/dev/null | head -n1)
|
||||
ln -sf "$FIRST_CERT" "$DEFAULT_CERT"
|
||||
log "Updated default certificate symlink to $FIRST_CERT"
|
||||
fi
|
||||
|
||||
# Only reload HAProxy if certificates were actually updated
|
||||
if [ "$CERTS_UPDATED" = true ]; then
|
||||
log "Certificates updated, validating HAProxy configuration"
|
||||
|
||||
# Validate HAProxy configuration
|
||||
if haproxy -c -f {{ haproxy_config_dir }}/haproxy.cfg >/dev/null 2>&1; then
|
||||
log "HAProxy configuration valid, reloading service"
|
||||
|
||||
if systemctl reload haproxy; then
|
||||
log "HAProxy reloaded successfully"
|
||||
else
|
||||
log "ERROR: Failed to reload HAProxy"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log "ERROR: HAProxy configuration validation failed"
|
||||
haproxy -c -f {{ haproxy_config_dir }}/haproxy.cfg 2>&1 | tee -a "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log "No certificates were updated, skipping HAProxy reload"
|
||||
fi
|
||||
|
||||
log "Certificate deployment complete"
|
||||
exit 0
|
||||
@@ -1,12 +0,0 @@
|
||||
# {{ ansible_managed }}
|
||||
# Cloudflare API credentials for Certbot DNS-01 challenge
|
||||
# This file contains sensitive information and should be protected
|
||||
|
||||
# Cloudflare API Token (RECOMMENDED)
|
||||
# Create at: https://dash.cloudflare.com/profile/api-tokens
|
||||
# Required permissions: Zone:DNS:Edit for specific zone
|
||||
dns_cloudflare_api_token = {{ haproxy_certbot_cloudflare_api_token }}
|
||||
|
||||
# Alternative: Global API Key (NOT RECOMMENDED - too permissive)
|
||||
# dns_cloudflare_email = your-email@example.com
|
||||
# dns_cloudflare_api_key = your-global-api-key
|
||||
@@ -1,188 +0,0 @@
|
||||
{# ansible/roles/haproxy/templates/haproxy.cfg.j2 #}
|
||||
#---------------------------------------------------------------------
|
||||
# HAProxy Configuration for {{ inventory_hostname }}
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Magic Kingdom Labs - lightning-lane load balancer
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Global settings
|
||||
#---------------------------------------------------------------------
|
||||
global
|
||||
log 127.0.0.1 {{ haproxy_global_log_facility }} {{ haproxy_global_log_level }}
|
||||
chroot {{ haproxy_chroot_dir }}
|
||||
pidfile /var/run/haproxy.pid
|
||||
maxconn {{ haproxy_global_maxconn }}
|
||||
user {{ haproxy_user }}
|
||||
group {{ haproxy_group }}
|
||||
daemon
|
||||
nbthread {{ haproxy_global_nbthread }}
|
||||
|
||||
# Runtime socket for management
|
||||
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
|
||||
stats timeout 30s
|
||||
|
||||
# SSL/TLS configuration
|
||||
ssl-default-bind-ciphers {{ haproxy_ssl_ciphers }}
|
||||
ssl-default-bind-options {{ haproxy_ssl_options }}
|
||||
ssl-default-server-ciphers {{ haproxy_ssl_ciphers }}
|
||||
ssl-default-server-options {{ haproxy_ssl_options }}
|
||||
|
||||
# Performance tuning
|
||||
tune.ssl.default-dh-param 2048
|
||||
tune.ssl.cachesize 100000
|
||||
tune.ssl.lifetime 600
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Common defaults
|
||||
#---------------------------------------------------------------------
|
||||
defaults
|
||||
mode http
|
||||
log global
|
||||
option httplog
|
||||
option dontlognull
|
||||
option http-server-close
|
||||
option redispatch
|
||||
retries 3
|
||||
timeout connect {{ haproxy_timeout_connect }}
|
||||
timeout client {{ haproxy_timeout_client }}
|
||||
timeout server {{ haproxy_timeout_server }}
|
||||
timeout http-request 10s
|
||||
timeout http-keep-alive 10s
|
||||
timeout check 10s
|
||||
maxconn 3000
|
||||
|
||||
# Error pages
|
||||
errorfile 400 /etc/haproxy/errors/400.http
|
||||
errorfile 403 /etc/haproxy/errors/403.http
|
||||
errorfile 408 /etc/haproxy/errors/408.http
|
||||
errorfile 500 /etc/haproxy/errors/500.http
|
||||
errorfile 502 /etc/haproxy/errors/502.http
|
||||
errorfile 503 /etc/haproxy/errors/503.http
|
||||
errorfile 504 /etc/haproxy/errors/504.http
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Stats interface
|
||||
#---------------------------------------------------------------------
|
||||
{% if haproxy_stats_enable %}
|
||||
listen stats
|
||||
bind *:{{ haproxy_stats_port }}
|
||||
mode http
|
||||
stats enable
|
||||
stats uri {{ haproxy_stats_uri }}
|
||||
stats refresh {{ haproxy_stats_refresh }}
|
||||
stats realm Magic\ Kingdom\ Labs\ -\ HAProxy\ Statistics
|
||||
stats auth {{ haproxy_stats_username }}:{{ haproxy_stats_password }}
|
||||
stats admin if TRUE
|
||||
{% endif %}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# HTTP Redirect Frontend (HTTP -> HTTPS)
|
||||
#---------------------------------------------------------------------
|
||||
frontend http_front
|
||||
bind *:80
|
||||
mode http
|
||||
|
||||
# Redirect all HTTP traffic to HTTPS
|
||||
redirect scheme https code 301
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# HTTPS Frontend with SNI
|
||||
#---------------------------------------------------------------------
|
||||
frontend https_front
|
||||
bind *:443 ssl crt {{ haproxy_ssl_cert_dir }}/live crt {{ haproxy_ssl_default_crt }} alpn h2,http/1.1
|
||||
mode http
|
||||
|
||||
# Security headers
|
||||
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
http-response set-header X-Frame-Options "SAMEORIGIN"
|
||||
http-response set-header X-Content-Type-Options "nosniff"
|
||||
http-response set-header X-XSS-Protection "1; mode=block"
|
||||
|
||||
# Logging
|
||||
option httplog
|
||||
option forwardfor
|
||||
|
||||
{% if haproxy_frontends is defined and haproxy_frontends | length > 0 %}
|
||||
{% for frontend in haproxy_frontends %}
|
||||
{% if frontend.acls is defined %}
|
||||
# ACLs for {{ frontend.name }}
|
||||
{% for acl in frontend.acls %}
|
||||
acl {{ acl }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for frontend in haproxy_frontends %}
|
||||
{% if frontend.use_backends is defined %}
|
||||
# Backend routing for {{ frontend.name }}
|
||||
{% for use_backend in frontend.use_backends %}
|
||||
use_backend {{ use_backend }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
# Default backend
|
||||
default_backend no_match_back
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Backends
|
||||
#---------------------------------------------------------------------
|
||||
{% for backend in haproxy_backends %}
|
||||
backend {{ backend.name }}
|
||||
mode {{ backend.mode | default('http') }}
|
||||
balance {{ backend.balance | default('roundrobin') }}
|
||||
{% if backend.options is defined %}
|
||||
{% for option in backend.options %}
|
||||
option {{ option }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if backend.http_check is defined %}
|
||||
option httpchk {{ backend.http_check }}
|
||||
{% endif %}
|
||||
{% if backend.servers is defined %}
|
||||
{% for server in backend.servers %}
|
||||
server {{ server.name }} {{ server.address }}{% if server.check | default(false) %} check{% endif %}{% if server.backup | default(false) %} backup{% endif %}{% if server.weight is defined %} weight {{ server.weight }}{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
# Default backend for unmatched requests
|
||||
backend no_match_back
|
||||
mode http
|
||||
http-request deny deny_status 404
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# TCP Services
|
||||
#---------------------------------------------------------------------
|
||||
{% for tcp_service in haproxy_tcp_services %}
|
||||
# {{ tcp_service.name | upper }} Service (TCP)
|
||||
frontend {{ tcp_service.name }}_front
|
||||
bind *:{{ tcp_service.frontend_port }}
|
||||
mode tcp
|
||||
option tcplog
|
||||
{% if tcp_service.timeout_client is defined %}
|
||||
timeout client {{ tcp_service.timeout_client }}
|
||||
{% endif %}
|
||||
default_backend {{ tcp_service.name }}_back
|
||||
|
||||
backend {{ tcp_service.name }}_back
|
||||
mode tcp
|
||||
balance {{ tcp_service.balance | default('roundrobin') }}
|
||||
{% if tcp_service.timeout_server is defined %}
|
||||
timeout server {{ tcp_service.timeout_server }}
|
||||
{% endif %}
|
||||
{% if tcp_service.timeout_connect is defined %}
|
||||
timeout connect {{ tcp_service.timeout_connect }}
|
||||
{% endif %}
|
||||
{% if tcp_service.servers is defined %}
|
||||
{% for server in tcp_service.servers %}
|
||||
server {{ server.name }} {{ server.address }}:{{ tcp_service.backend_port }}{% if server.check | default(true) %} check{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
@@ -1,14 +0,0 @@
|
||||
{# ansible/roles/haproxy/templates/logrotate.j2 #}
|
||||
{{ haproxy_log_dir }}/*.log {
|
||||
daily
|
||||
rotate 14
|
||||
missingok
|
||||
notifempty
|
||||
compress
|
||||
delaycompress
|
||||
sharedscripts
|
||||
postrotate
|
||||
/bin/kill -HUP `cat /var/run/rsyslog.pid 2> /dev/null` 2> /dev/null || true
|
||||
/bin/kill -HUP `cat /var/run/haproxy.pid 2> /dev/null` 2> /dev/null || true
|
||||
endscript
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
# Role to install and configure a n8n server
|
||||
|
||||
- name: 1. Pre-install system updates
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
name: '*'
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: 2. Install required packages
|
||||
become: true
|
||||
ansible.builtin.package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
loop:
|
||||
- curl
|
||||
- wget
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- ca-certificates
|
||||
- software-properties-common
|
||||
|
||||
- name: 3. Enable UFW
|
||||
become: true
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
|
||||
- name: 4. Open firewall ports for n8n
|
||||
become: true
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- 22
|
||||
- 5678
|
||||
|
||||
- name: 5. Download node installer
|
||||
# become: true
|
||||
ansible.builtin.get_url:
|
||||
url: 'https://deb.nodesource.com/setup_lts.x'
|
||||
dest: '/tmp/setup_lts.x'
|
||||
mode: '0755'
|
||||
|
||||
- name: 6. Run node installer
|
||||
become: true
|
||||
ansible.builtin.shell:
|
||||
cmd: '/tmp/setup_lts.x'
|
||||
|
||||
- name: 7. Install Node.js and npm
|
||||
become: true
|
||||
ansible.builtin.package:
|
||||
name: nodejs
|
||||
state: present
|
||||
|
||||
- name: 8. Install npm@latest
|
||||
become: true
|
||||
ansible.builtin.shell:
|
||||
cmd: 'sudo npm install -g npm@latest'
|
||||
|
||||
- name: 9. Install "n8n" node.js package globally.
|
||||
become: true
|
||||
community.general.npm:
|
||||
name: n8n
|
||||
global: true
|
||||
|
||||
- name: 10. Copy n8n.service to /etc/systemd/system
|
||||
become: true
|
||||
ansible.builtin.template:
|
||||
dest: /etc/systemd/system/n8n.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
src: n8n.service.j2
|
||||
|
||||
- name: 11. Create n8n user
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: n8n
|
||||
state: present
|
||||
shell: /bin/bash
|
||||
home: /home/n8n
|
||||
createhome: true
|
||||
|
||||
- name: 12. Reload systemd to re-read configs
|
||||
ansible.builtin.systemd_service:
|
||||
daemon_reload: true
|
||||
|
||||
- name: 13. Start n8n service
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: n8n.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
@@ -1,22 +0,0 @@
|
||||
[Unit]
|
||||
Description=n8n - Workflow Automation Tool
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=n8n
|
||||
Group=n8n
|
||||
ExecStart=/usr/local/bin/n8n
|
||||
WorkingDirectory=/home/n8n
|
||||
Environment=NODE_ENV=production
|
||||
Environment=N8N_BASIC_AUTH_ACTIVE=true
|
||||
Environment=N8N_BASIC_AUTH_USER=admin
|
||||
#Environment=N8N_BASIC_AUTH_PASSWORD=your_secure_password
|
||||
Environment=N8N_HOST=0.0.0.0
|
||||
Environment=N8N_PORT=5678
|
||||
Environment=N8N_PROTOCOL=http
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
# Role to disable swap
|
||||
|
||||
- name: 1. Check if swap is enabled
|
||||
ansible.builtin.shell: swapon --show=NAME --noheadings
|
||||
register: swap_status
|
||||
changed_when: false
|
||||
|
||||
- name: 2. Disable swap
|
||||
become: true
|
||||
when: swap_status.stdout != ''
|
||||
block:
|
||||
- name: 2.1 Disable swap
|
||||
ansible.builtin.shell: swapoff -a
|
||||
|
||||
- name: 2.2 Persist swap off by commenting out swap in fstab
|
||||
ansible.builtin.replace:
|
||||
path: /etc/fstab
|
||||
regexp: '^([^#].*?\sswap\s+sw\s+.*)$'
|
||||
replace: '# \1'
|
||||
backup: true
|
||||
|
||||
- name: 2.3 Remove swap file
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: "{{ swap_status.stdout }}"
|
||||
state: absent
|
||||
|
||||
- name: 2.4 Remove zram-generator-defaults
|
||||
become: true
|
||||
when: ansible_os_family == "RedHat"
|
||||
ansible.builtin.file:
|
||||
path: /etc/systemd/zram-generator.conf
|
||||
state: absent
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
# Role to install and configure an Chrony NTP server
|
||||
|
||||
- name: 1. Install Chrony NTP for time synchronization
|
||||
become: true
|
||||
ansible.builtin.dnf:
|
||||
name: chrony
|
||||
state: present
|
||||
|
||||
- name: 2.1 Remove DHCP NTP servers from config
|
||||
become: true
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/chrony.conf
|
||||
line: "server unifi.int.mk-labs.cloud iburst"
|
||||
state: absent
|
||||
|
||||
- name: 2.2 Ensure specific NTP servers are present
|
||||
become: true
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/chrony.conf
|
||||
line: "server {{ item }} iburst"
|
||||
state: present
|
||||
loop: "{{ ntp_servers }}"
|
||||
|
||||
- name: 3. Ensure network allowed are present
|
||||
become: true
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/chrony.conf
|
||||
line: "allow {{ item }}"
|
||||
state: present
|
||||
loop: "{{ allowed_networks }}"
|
||||
|
||||
- name: 4.Restart ntpd service
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: chronyd
|
||||
state: restarted
|
||||
enabled: true
|
||||
|
||||
- name: 5. Allow NTP traffic
|
||||
become: true
|
||||
ansible.posix.firewalld:
|
||||
service: ntp
|
||||
permanent: true
|
||||
immediate: true
|
||||
state: enabled
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
prometheus_version: v2.40.1
|
||||
grafana_version: "9.2.5"
|
||||
alertmanager_version: v0.24.0
|
||||
#alertmanager_smtp_password: !vault |
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
64306663363562356132323065396635636630373031303739323666373262663961393132316333
|
||||
6135653763363566303331313639633030623530646239310a353236343035643132646230333466
|
||||
36336439376131333630346563323833313164353265313264643232373465633561663331396133
|
||||
3163303166373166390a396131303239356139653063616437363933333130393563646338663933
|
||||
3966
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: Pre-loaded local dashboards
|
||||
type: file
|
||||
options:
|
||||
foldersFromFilesStructure: true
|
||||
path: /var/lib/grafana/dashboards
|
||||
@@ -1,814 +0,0 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_PROMETHEUS",
|
||||
"label": "prometheus",
|
||||
"description": "Prometheus as the datasource is obligatory",
|
||||
"type": "datasource",
|
||||
"pluginId": "prometheus",
|
||||
"pluginName": "Prometheus"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "7.4.5"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "prometheus",
|
||||
"name": "Prometheus",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "table",
|
||||
"name": "Table",
|
||||
"version": ""
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": 14282,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"iteration": 1617715580880,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 8,
|
||||
"panels": [],
|
||||
"title": "CPU",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 15,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null as zero",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.4.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": true,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100",
|
||||
"hide": false,
|
||||
"interval": "",
|
||||
"legendFormat": "{{name}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "CPU Usage",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:606",
|
||||
"format": "percent",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:607",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 11,
|
||||
"panels": [],
|
||||
"title": "Memory",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 9,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null as zero",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.4.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": true,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)",
|
||||
"hide": false,
|
||||
"interval": "",
|
||||
"legendFormat": "{{name}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Memory Usage",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:606",
|
||||
"format": "bytes",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:607",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 9
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 14,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null as zero",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.4.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": true,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)",
|
||||
"hide": false,
|
||||
"interval": "",
|
||||
"legendFormat": "{{name}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Memory Cached",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:606",
|
||||
"format": "bytes",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:607",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
},
|
||||
"id": 2,
|
||||
"panels": [],
|
||||
"title": "Network",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 4,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"hideEmpty": false,
|
||||
"hideZero": false,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"sideWidth": null,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.4.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(container_network_receive_bytes_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name)",
|
||||
"hide": false,
|
||||
"interval": "",
|
||||
"legendFormat": "{{name}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Received Network Traffic",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:674",
|
||||
"format": "Bps",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:675",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 18
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 6,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.4.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(container_network_transmit_bytes_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name)",
|
||||
"interval": "",
|
||||
"legendFormat": "{{name}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Sent Network Traffic",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:832",
|
||||
"format": "Bps",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:833",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 26
|
||||
},
|
||||
"id": 19,
|
||||
"panels": [],
|
||||
"title": "Misc",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"align": null,
|
||||
"filterable": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "id"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.width",
|
||||
"value": 260
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Running"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "d"
|
||||
},
|
||||
{
|
||||
"id": "decimals",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-text"
|
||||
},
|
||||
{
|
||||
"id": "color",
|
||||
"value": {
|
||||
"fixedColor": "dark-green",
|
||||
"mode": "fixed"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 27
|
||||
},
|
||||
"id": 17,
|
||||
"options": {
|
||||
"showHeader": true,
|
||||
"sortBy": []
|
||||
},
|
||||
"pluginVersion": "7.4.5",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400",
|
||||
"format": "table",
|
||||
"instant": true,
|
||||
"interval": "",
|
||||
"legendFormat": "{{name}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Containers Info",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "filterFieldsByName",
|
||||
"options": {
|
||||
"include": {
|
||||
"names": [
|
||||
"container_label_com_docker_compose_project",
|
||||
"container_label_com_docker_compose_project_working_dir",
|
||||
"image",
|
||||
"instance",
|
||||
"name",
|
||||
"Value",
|
||||
"container_label_com_docker_compose_service"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {},
|
||||
"indexByName": {},
|
||||
"renameByName": {
|
||||
"Value": "Running",
|
||||
"container_label_com_docker_compose_project": "Label",
|
||||
"container_label_com_docker_compose_project_working_dir": "Working dir",
|
||||
"container_label_com_docker_compose_service": "Service",
|
||||
"image": "Registry Image",
|
||||
"instance": "Instance",
|
||||
"name": "Name"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 27,
|
||||
"style": "dark",
|
||||
"tags": ["cadvisor", "docker"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {},
|
||||
"datasource": "Prometheus",
|
||||
"definition": "label_values({__name__=~\"container.*\"},instance)",
|
||||
"description": null,
|
||||
"error": null,
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Host",
|
||||
"multi": false,
|
||||
"name": "host",
|
||||
"options": [],
|
||||
"query": {
|
||||
"query": "label_values({__name__=~\"container.*\"},instance)",
|
||||
"refId": "Prometheus-host-Variable-Query"
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {},
|
||||
"datasource": "Prometheus",
|
||||
"definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)",
|
||||
"description": null,
|
||||
"error": null,
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Container",
|
||||
"multi": false,
|
||||
"name": "container",
|
||||
"options": [],
|
||||
"query": {
|
||||
"query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)",
|
||||
"refId": "Prometheus-container-Variable-Query"
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 0,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "Cadvisor exporter",
|
||||
"uid": "pMEd7m0Mz",
|
||||
"version": 1,
|
||||
"description": "Simple exporter for cadvisor only"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://192.168.0.1:9090
|
||||
@@ -1,11 +0,0 @@
|
||||
groups:
|
||||
- name: AllInstances
|
||||
rules:
|
||||
- alert: InstanceDown
|
||||
expr: up == 0
|
||||
for: 1m
|
||||
annotations:
|
||||
title: 'Instance {{ $labels.instance }} down'
|
||||
description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minute.'
|
||||
labels:
|
||||
severity: 'critical'
|
||||
@@ -1,28 +0,0 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: prometheus
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
|
||||
- job_name: node-exporter
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets:
|
||||
["192.168.0.1:9100", "192.168.0.10:9100", "192.168.0.11:9100"]
|
||||
|
||||
- job_name: cadvisor
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: ["192.168.0.1:9101", "192.168.0.11:9101"]
|
||||
|
||||
rule_files:
|
||||
- prometheus_alerts_rules.yml
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- 192.168.0.1:9093
|
||||
@@ -1,85 +0,0 @@
|
||||
- name: Create Folder /srv/prometheus if not exist
|
||||
file:
|
||||
path: /srv/prometheus
|
||||
mode: 0755
|
||||
state: directory
|
||||
|
||||
- name: Create Folder /srv/grafana if not exist
|
||||
file:
|
||||
path: /srv/grafana
|
||||
mode: 0755
|
||||
state: directory
|
||||
|
||||
- name: Create Folder /srv/alertmanager if not exist
|
||||
file:
|
||||
path: /srv/alertmanager
|
||||
mode: 0755
|
||||
state: directory
|
||||
|
||||
- name: Create prometheus configuration file
|
||||
copy:
|
||||
dest: /srv/prometheus/prometheus.yml
|
||||
src: prometheus_main.yml
|
||||
mode: 0644
|
||||
|
||||
- name: Create prometheus alert configuration file
|
||||
copy:
|
||||
dest: /srv/prometheus/prometheus_alerts_rules.yml
|
||||
src: prometheus_alerts_rules.yml
|
||||
mode: 0644
|
||||
|
||||
- name: Create grafana configuration files
|
||||
copy:
|
||||
dest: /srv/
|
||||
src: grafana
|
||||
mode: 0644
|
||||
|
||||
- name: Create alertmanager configuration file
|
||||
template:
|
||||
dest: /srv/alertmanager/alertmanager.yml
|
||||
src: alertmanager/alertmanager.j2
|
||||
mode: 0644
|
||||
|
||||
- name: Create Prometheus container
|
||||
docker_container:
|
||||
name: prometheus
|
||||
restart_policy: always
|
||||
image: prom/prometheus:
|
||||
volumes:
|
||||
- /srv/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- /srv/prometheus/prometheus_alerts_rules.yml:/etc/prometheus/prometheus_alerts_rules.yml
|
||||
- prometheus_main_data:/prometheus
|
||||
command: >
|
||||
--config.file=/etc/prometheus/prometheus.yml
|
||||
--storage.tsdb.path=/prometheus
|
||||
--web.console.libraries=/etc/prometheus/console_libraries
|
||||
--web.console.templates=/etc/prometheus/consoles
|
||||
--web.enable-lifecycle
|
||||
published_ports: "9090:9090"
|
||||
|
||||
- name: Create Grafana container
|
||||
docker_container:
|
||||
name: grafana
|
||||
restart_policy: always
|
||||
image: grafana/grafana:
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
- /srv/grafana/provisioning:/etc/grafana/provisioning
|
||||
- /srv/grafana/dashboards:/var/lib/grafana/dashboards
|
||||
env:
|
||||
GF_AUTH_ANONYMOUS_ENABLED: "true"
|
||||
GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin"
|
||||
published_ports: "3000:3000"
|
||||
|
||||
- name: Create Alertmanager container
|
||||
docker_container:
|
||||
name: alertmanager
|
||||
restart_policy: always
|
||||
image: prom/alertmanager:
|
||||
volumes:
|
||||
- alertmanager-data:/data
|
||||
- /srv/alertmanager:/config
|
||||
command: >
|
||||
--config.file=/config/alertmanager.yml
|
||||
--log.level=debug
|
||||
published_ports: "9093:9093"
|
||||
@@ -1,13 +0,0 @@
|
||||
route:
|
||||
receiver: "mail"
|
||||
repeat_interval: 4h
|
||||
group_by: [ alertname ]
|
||||
|
||||
receivers:
|
||||
- name: "mail"
|
||||
email_configs:
|
||||
- smarthost: "outlook.office365.com:587"
|
||||
auth_username: "test@padok.fr"
|
||||
auth_password: "{{ alertmanager_smtp_password }}"
|
||||
from: "test@padok.fr"
|
||||
to: "test@padok.fr"
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
node_exporter_version: v1.4.0
|
||||
cadvisor_version: v0.46.0
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
- name: Create NodeExporter
|
||||
docker_container:
|
||||
name: node-exporter
|
||||
restart_policy: always
|
||||
image: prom/node-exporter:{{ node_exporter_version }}
|
||||
volumes:
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /:/rootfs:ro
|
||||
command: >
|
||||
--path.procfs=/host/proc
|
||||
--path.rootfs=/rootfs
|
||||
--path.sysfs=/host/sys
|
||||
--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
|
||||
published_ports: "9100:9100"
|
||||
|
||||
- name: Create cAdvisor
|
||||
docker_container:
|
||||
name: cadvisor
|
||||
restart_policy: always
|
||||
image: gcr.io/cadvisor/cadvisor:{{ cadvisor_version }}
|
||||
volumes:
|
||||
- /:/rootfs:ro
|
||||
- /var/run:/var/run:ro
|
||||
- /sys:/sys:ro
|
||||
- /var/lib/docker/:/var/lib/docker:ro
|
||||
- /dev/disk/:/dev/disk:ro
|
||||
published_ports: "9101:8080"
|
||||
@@ -1,38 +0,0 @@
|
||||
Role Name
|
||||
=========
|
||||
|
||||
A brief description of the role goes here.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
||||
@@ -1,3 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# defaults file for time-sync
|
||||
@@ -1,3 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# handlers file for time-sync
|
||||
@@ -1,35 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
galaxy_info:
|
||||
author: your name
|
||||
description: your role description
|
||||
company: your company (optional)
|
||||
|
||||
# If the issue tracker for your role is not on github, uncomment the
|
||||
# next line and provide a value
|
||||
# issue_tracker_url: http://example.com/issue/tracker
|
||||
|
||||
# Choose a valid license ID from https://spdx.org - some suggested licenses:
|
||||
# - BSD-3-Clause (default)
|
||||
# - MIT
|
||||
# - GPL-2.0-or-later
|
||||
# - GPL-3.0-only
|
||||
# - Apache-2.0
|
||||
# - CC-BY-4.0
|
||||
license: license (GPL-2.0-or-later, MIT, etc)
|
||||
|
||||
min_ansible_version: 2.1
|
||||
|
||||
# If this a Container Enabled role, provide the minimum Ansible Container version.
|
||||
# min_ansible_container_version:
|
||||
|
||||
galaxy_tags: []
|
||||
# List tags for your role here, one per line. A tag is a keyword that describes
|
||||
# and categorizes the role. Users find roles by searching for tags. Be sure to
|
||||
# remove the '[]' above, if you add tags to this list.
|
||||
#
|
||||
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
|
||||
# Maximum 20 tags per role.
|
||||
|
||||
dependencies: []
|
||||
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
|
||||
# if you add dependencies to this list.
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
# tasks file for time-sync
|
||||
- name: Manage Time Synchronization
|
||||
become: true
|
||||
block:
|
||||
- name: Remove the ntp package
|
||||
ansible.builtin.package:
|
||||
name: ntp
|
||||
state: absent
|
||||
|
||||
- name: Install the chrony package
|
||||
ansible.builtin.package:
|
||||
name: chrony
|
||||
state: present
|
||||
|
||||
- name: Remove pool entries from Chrony config file
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/chrony/chrony.conf
|
||||
regexp: '^pool\s*'
|
||||
state: absent
|
||||
|
||||
- name: Add local NTP servers to Chrony config file
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/chrony/chrony.conf
|
||||
line: "server 10.1.71.21 iburst"
|
||||
state: present
|
||||
|
||||
- name: Start and enable chronyd
|
||||
ansible.builtin.systemd:
|
||||
name: chronyd
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Force time synchronization
|
||||
become: true
|
||||
ansible.builtin.command: chronyc makestep
|
||||
register: chronyc_status
|
||||
|
||||
- name: Display chronyc status
|
||||
ansible.builtin.debug:
|
||||
var: chronyc_status.stdout
|
||||
@@ -1,3 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
localhost
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
- hosts: localhost
|
||||
remote_user: root
|
||||
roles:
|
||||
- time-sync
|
||||
@@ -1,3 +0,0 @@
|
||||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# vars file for time-sync
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/traefik/defaults/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Directory on the host where Traefik files are deployed
|
||||
traefik_base_dir: /opt/docker/traefik
|
||||
|
||||
# Cloudflare API token — MUST be set in vault
|
||||
traefik_cloudflare_api_token: ""
|
||||
@@ -1,8 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/traefik/handlers/main.yml
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: restart traefik
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ traefik_base_dir }}"
|
||||
state: restarted
|
||||
@@ -1,55 +0,0 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: roles/traefik/tasks/main.yml
|
||||
# DESCRIPTION: Deploys Traefik from boilerplate-generated files in the repo.
|
||||
# The compose.yaml and dynamic config live in boilerplates/traefik/
|
||||
# and are copied to the host. Only the .env file is templated
|
||||
# to inject secrets from Ansible vault.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: Create Traefik directory structure
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: '0775'
|
||||
loop:
|
||||
- "{{ traefik_base_dir }}"
|
||||
- "{{ traefik_base_dir }}/dynamic"
|
||||
|
||||
- name: Copy compose.yaml from boilerplate
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/../../boilerplates/traefik/compose.yaml"
|
||||
dest: "{{ traefik_base_dir }}/compose.yaml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: '0644'
|
||||
notify: restart traefik
|
||||
|
||||
- name: Copy dynamic configuration from boilerplate
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/../../boilerplates/traefik/dynamic/"
|
||||
dest: "{{ traefik_base_dir }}/dynamic/"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: '0644'
|
||||
notify: restart traefik
|
||||
|
||||
- name: Deploy .env with secrets from vault
|
||||
template:
|
||||
src: env.j2
|
||||
dest: "{{ traefik_base_dir }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: docker
|
||||
mode: '0600'
|
||||
notify: restart traefik
|
||||
|
||||
- name: Create Traefik Docker network
|
||||
docker_network:
|
||||
name: proxy
|
||||
state: present
|
||||
|
||||
- name: Start Traefik
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ traefik_base_dir }}"
|
||||
state: present
|
||||
@@ -1,2 +0,0 @@
|
||||
# Managed by Ansible — do not edit manually
|
||||
CF_DNS_API_TOKEN={{ traefik_cloudflare_api_token }}
|
||||
Reference in New Issue
Block a user