diff --git a/roles/hashistack_ingress/.docsible b/roles/hashistack_ingress/.docsible new file mode 100644 index 0000000..7713e0f --- /dev/null +++ b/roles/hashistack_ingress/.docsible @@ -0,0 +1,13 @@ +aap_hub: null +automation_kind: null +category: null +critical: null +description: null +dt_dev: null +dt_prod: null +dt_update: 26/08/2024 +requester: null +subCategory: null +time_saving: null +users: null +version: null diff --git a/roles/hashistack_ingress/README.md b/roles/hashistack_ingress/README.md new file mode 100644 index 0000000..53897f3 --- /dev/null +++ b/roles/hashistack_ingress/README.md @@ -0,0 +1,52 @@ + + +# 📃 Role overview + +## hashistack_ingress + + + +Description: Deploys an ingress reverse-proxy on a hashistack-ansible managed nomad cluster + + +| Field | Value | +|---------------|------------| +| Readme update | 26/08/2024 | + + + + + + + + + + + + +### Tasks + + + + + + + + +## Author Information +Bertrand Lanson + +#### License + +license (BSD, MIT) + +#### Minimum Ansible Version + +2.10 + +#### Platforms + +- **Ubuntu**: ['focal', 'jammy', 'noble'] +- **Debian**: ['bullseye', 'bookworm'] + + diff --git a/roles/hashistack_ingress/defaults/main.yml b/roles/hashistack_ingress/defaults/main.yml new file mode 100644 index 0000000..ee7df4f --- /dev/null +++ b/roles/hashistack_ingress/defaults/main.yml @@ -0,0 +1,45 @@ +--- +# defaults file for hashistack_ingress +hashistack_ingress_nomad_api_addr: "http://127.0.0.1:4646" +hashistack_ingress_nomad_api_token: + +hashistack_ingress_job_name: HashistackHAProxyIngress +hashistack_ingress_datacenters: [] +hashistack_ingress_namespace: default +hashistack_ingress_replicas: 1 +hashistack_ingress_enable_consul_service: true + +hashistack_ingress_virtual_ip_keepalived_version: latest +hashistack_ingress_virtual_ip_addr: "192.168.1.1" +hashistack_ingress_virtual_ip_interface: eth0 +hashistack_ingress_virtual_ip_vrrp_interface: "{{ hashistack_ingress_virtual_ip_interface }}" +hashistack_ingress_virtual_ip_vrrp_router_id: 50 +hashistack_ingress_virtual_ip_vrrp_priority: 100 +hashistack_ingress_virtual_ip_vrrp_advertise_interval: 1 +hashistack_ingress_virtual_ip_vrrp_password: password + +hashistack_ingress_enable_http: true +hashistack_ingress_enable_https: false +hashistack_ingress_enable_prometheus_metrics: false +hashistack_ingress_enable_admin_interface: false +hashistack_ingress_admin_interface_password: password + +hashistack_ingress_virtual_ip_haproxy_version: latest +hashistack_ingress_haproxy_global: + - log /dev/log local0 + - log /dev/log local1 notice + - stats socket {{ deploy_haproxy_socket }} level admin + - chroot {{ deploy_haproxy_chroot }} + - daemon + - description hashistack haproxy +hashistack_ingress_haproxy_defaults: + - log global + - mode http + - option httplog + - option dontlognull + - timeout connect 5000 + - timeout client 5000 + - timeout server 5000 +hashistack_ingress_haproxy_frontends: [] +hashistack_ingress_haproxy_backends: [] +hashistack_ingress_haproxy_listen: [] diff --git a/roles/hashistack_ingress/handlers/main.yml b/roles/hashistack_ingress/handlers/main.yml new file mode 100644 index 0000000..81ae4df --- /dev/null +++ b/roles/hashistack_ingress/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for hashistack_ingress diff --git a/roles/hashistack_ingress/meta/main.yml b/roles/hashistack_ingress/meta/main.yml new file mode 100644 index 0000000..01ba49c --- /dev/null +++ b/roles/hashistack_ingress/meta/main.yml @@ -0,0 +1,28 @@ +--- +# meta file for hashistack_ingress +galaxy_info: + namespace: "ednz_cloud" + role_name: "hashistack_ingress" + author: "Bertrand Lanson" + description: "Deploys an ingress reverse-proxy on a hashistack-ansible managed nomad cluster" + license: "license (BSD, MIT)" + min_ansible_version: "2.10" + platforms: + - name: Ubuntu + versions: + - focal + - jammy + - noble + - name: Debian + versions: + - bullseye + - bookworm + galaxy_tags: + - "ubuntu" + - "debian" + - "hashicorp" + - "nomad" + - "haproxy" + - "ingress" + +dependencies: [] diff --git a/roles/hashistack_ingress/tasks/main.yml b/roles/hashistack_ingress/tasks/main.yml new file mode 100644 index 0000000..da865fa --- /dev/null +++ b/roles/hashistack_ingress/tasks/main.yml @@ -0,0 +1,2 @@ +--- +# task/main file for hashistack_ingress diff --git a/roles/hashistack_ingress/templates/chk_haproxy.sh.j2 b/roles/hashistack_ingress/templates/chk_haproxy.sh.j2 new file mode 100644 index 0000000..50d4884 --- /dev/null +++ b/roles/hashistack_ingress/templates/chk_haproxy.sh.j2 @@ -0,0 +1 @@ +#! /bin/sh diff --git a/roles/hashistack_ingress/templates/haproxy.cfg.j2 b/roles/hashistack_ingress/templates/haproxy.cfg.j2 new file mode 100644 index 0000000..bf43a0d --- /dev/null +++ b/roles/hashistack_ingress/templates/haproxy.cfg.j2 @@ -0,0 +1,31 @@ +# {{ ansible_managed }} +global +{% for option in hashistack_ingress_haproxy_global %} + {{ option }} +{% endfor %} + +defaults +{% for option in hashistack_ingress_haproxy_defaults %} + {{ option }} +{% endfor %} + +{% for frontend in hashistack_ingress_haproxy_frontends + hashistack_ingress_mandatory_frontends %} +frontend {{ frontend.name }} +{% for option in frontend.options %} + {{ option }} +{% endfor %} +{% endfor %} + +{% for backend in hashistack_ingress_haproxy_backends %} +backend {{ backend.name }} +{% for option in backend.options%} + {{ option }} +{% endfor %} +{% endfor %} + +{% for listen in hashistack_ingress_haproxy_listen %} +listen {{ listen.name }} +{% for option in listen.options %} + {{ option }} +{% endfor %} +{% endfor %} diff --git a/roles/hashistack_ingress/templates/ingress.job.hcl.j2 b/roles/hashistack_ingress/templates/ingress.job.hcl.j2 new file mode 100644 index 0000000..188e915 --- /dev/null +++ b/roles/hashistack_ingress/templates/ingress.job.hcl.j2 @@ -0,0 +1,155 @@ +job "{{ hashistack_ingress_job_name }}" { + datacenters = {{ hashistack_ingress_datacenters }} + type = "service" + priority = 85 + namespace = {{ hashistack_ingress_namespace }} + + group "haproxy" { + network { + mode = "bridge" + port "http" { + to = 80 + static = 80 + } + port "https" { + to = 443 + static = 443 + } + port "stats" { + to = 9000 + } + } + +{% if hashistack_ingress_enable_http %} + service { + name = "haproxy-http" + provider = "{{ "consul" if hashistack_ingress_enable_consul_service else "nomad"}}" + port = "http" + task = "loadbalancer" + check { + type = "http" + port = "stats" + path = "/health" + interval = "10s" + timeout = "2s" + } + tags = [] + } +{% endif %} + +{% if hashistack_ingress_enable_https %} + service { + name = "haproxy-https" + provider = "{{ "consul" if hashistack_ingress_enable_consul_service else "nomad"}}" + port = "https" + task = "loadbalancer" + check { + type = "http" + port = "stats" + path = "/health" + interval = "10s" + timeout = "2s" + } + tags = [] + } +{% endif %} + + service { + name = "haproxy-stats" + provider = "{{ "consul" if hashistack_ingress_enable_consul_service else "nomad"}}" + port = "stats" + task = "loadbalancer" + check { + type = "http" + port = "stats" + path = "/health" + interval = "10s" + timeout = "2s" + } + tags = [] + } + + + +{% if hashistack_ingress_enable_prometheus_metrics %} + service { + name = "loadbalancer-exporter" + port = "prometheus-exporter" + task = "loadbalancer" + tags = [] + } +{% endif %} + + task "keepalived" { + driver = "docker" + lifecycle { + hook = "poststart" + sidecar = true + } + config { + image = "{{ hashistack_ingress_keepalived_image }}:{{ hashistack_ingress_virtual_ip_keepalived_version }}" + network_mode = "host" + cap_add = [ + "NET_ADMIN", + "NET_BROADCAST", + "NET_RAW" + ] + mount { + type = "bind" + source = "secrets/keepalived.conf" + target = "/etc/keepalived/keepalived.conf" + } + mount { + type = "bind" + source = "secrets/chk_haproxy.sh" + target = "/etc/keepalived/scripts.d/chk_haproxy.sh" + } + mount { + type = "bind" + target = "/var/run/docker.sock" + source = "/var/run/docker.sock" + readonly = true + } + } + template { + data = <<-EOT +{% include "keepalived.conf.j2" %} +EOT + destination = "secrets/keepalived.conf" + } + template { + data = <<-EOT +{% include "chk_haproxy.sh.j2" %} +EOT + destination = "secrets/chk_haproxy.sh" + perms = "755" + } + resources { + cpu = 50 + memory = 10 + } + } + + task "loadbalancer" { + driver = "docker" + config { + image = "{{ hashistack_ingress_haproxy_image }}:{{ hashistack_ingress_virtual_ip_haproxy_version }}" + mount { + type = "bind" + source = "secrets/haproxy.cfg" + target = "/usr/local/etc/haproxy/haproxy.cfg" + } + } + template { + data = <<-EOT +{% include "haproxy.cfg.j2" %} +EOT + destination = "secrets/haproxy.cfg" + } + resources { + cpu = 128 + memory = 256 + } + } + } +} diff --git a/roles/hashistack_ingress/templates/keepalived.conf.j2 b/roles/hashistack_ingress/templates/keepalived.conf.j2 new file mode 100644 index 0000000..7a6a2f3 --- /dev/null +++ b/roles/hashistack_ingress/templates/keepalived.conf.j2 @@ -0,0 +1,37 @@ +global_defs { + script_user root + enable_script_security +} + +vrrp_script chk_haproxy { + script "/etc/keepalived/scripts.d/chk_haproxy.sh" + user root + interval 3 + weight 0 + rise 6 + fall 1 +} + +vrrp_instance haproxy { + interface {{ hashistack_ingress_virtual_ip_vrrp_interface }} + + state {{ hashistack_ingress_keepalived_init_state }} + virtual_router_id {{ hashistack_ingress_virtual_ip_vrrp_router_id }} + priority {{ hashistack_ingress_virtual_ip_vrrp_priority }} + advert_int {{ hashistack_ingress_virtual_ip_vrrp_advertise_interval }} + + authentication { + auth_type PASS + auth_pass {{ hashistack_ingress_virtual_ip_vrrp_password }} + } + + virtual_ipaddress { + {{ hashistack_ingress_virtual_ip_addr }}/32 dev {{ hashistack_ingress_virtual_ip_interface }} + } + + track_script { + chk_haproxy + } + + notify /etc/keepalived/scripts.d/notify.sh +} diff --git a/roles/hashistack_ingress/vars/main.yml b/roles/hashistack_ingress/vars/main.yml new file mode 100644 index 0000000..6426983 --- /dev/null +++ b/roles/hashistack_ingress/vars/main.yml @@ -0,0 +1,28 @@ +--- +# vars file for hashistack_ingress +hashistack_ingress_keepalived_image: ednxzu/keepalived +hashistack_ingress_haproxy_image: haproxytech/haproxy-debian + +hashistack_ingress_keepalived_init_state: BACKUP + +hashistack_ingress_template_haproxy_cfg: "{{ lookup('ansible.builtin.template', 'haproxy.cfg.j2') }}" +hashistack_ingress_template_keepalived_conf: "{{ lookup('ansible.builtin.template', 'keepalived.conf.j2') }}" +hashistack_ingress_template_chk_haproxy_sh: "{{ lookup('ansible.builtin.template', 'chk_haproxy.sh.j2') }}" + +hashistack_ingress_mandatory_frontends: + - name: monitoring + options: + - bind :9000 + - mode http + - option httpchk + - "{{'stats enable' if hashistack_ingress_enable_admin_interface else omit }}" + - "{{'stats uri /stats' if hashistack_ingress_enable_admin_interface else omit }}" + - "{{'stats refresh 30s' if hashistack_ingress_enable_admin_interface else omit }}" + - "{{'stats show-desc' if hashistack_ingress_enable_admin_interface else omit }}" + - "{{'stats show-legends' if hashistack_ingress_enable_admin_interface else omit }}" + - "{{'stats auth admin:'~hashistack_ingress_admin_interface_password if hashistack_ingress_enable_admin_interface else omit }}" + - http-check send meth GET uri /health ver HTTP/1.1 hdr Host localhost + - http-check expect status 200 + - acl health_check_ok nbsrv() ge 1 + - monitor-uri /health + - "{{'http-request use-service prometheus-exporter if { path /metrics }' if hashistack_ingress_enable_prometheus_metrics else omit }}"