diff --git a/.github/workflows/loki-molecule.yml b/.github/workflows/loki-molecule.yml new file mode 100644 index 00000000..8d8da57f --- /dev/null +++ b/.github/workflows/loki-molecule.yml @@ -0,0 +1,44 @@ +--- +name: Loki Molecule + +on: + push: + branches: + - main + pull_request: + branches: + - main + +defaults: + run: + working-directory: roles/loki + +jobs: + molecule: + name: Molecule + runs-on: ubuntu-latest + strategy: + matrix: + distro: + - rockylinux9 + - ubuntu2204 + - debian12 + + steps: + - name: Check out the codebase. + uses: actions/checkout@v4 + + - name: Set up Python 3. + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install test dependencies. + run: pip3 install ansible molecule molecule-plugins[docker] docker + + - name: Run Molecule tests. + run: molecule test + env: + PY_COLORS: '1' + ANSIBLE_FORCE_COLOR: '1' + MOLECULE_DISTRO: ${{ matrix.distro }} diff --git a/CODEOWNERS b/CODEOWNERS index c20c5746..093419f1 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,7 @@ -./ @ishanjainn -/roles/grafana @gardar @ishanjainn -/roles/grafana_agent @ishanjainn @v-zhuravlev @gardar -/roles/mimir @GVengelen @gardar @ishanjainn +./ @ishanjainn +/roles/grafana @gardar @ishanjainn +/roles/grafana_agent @ishanjainn @v-zhuravlev @gardar +/roles/alloy @ishanjainn @v-zhuravlev @gardar +/roles/opentelemetry_collector @ishanjainn +/roles/loki @voidquark @ishanjainn +/roles/mimir @GVengelen @gardar @ishanjainn \ No newline at end of file diff --git a/README.md b/README.md index abb879f7..8b0df350 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![GitHub Last Commit](https://img.shields.io/github/last-commit/grafana/grafana-ansible-collection)](https://github.com/grafana/grafana-ansible-collection/tags) [![GitHub Contributors](https://img.shields.io/github/contributors/grafana/grafana-ansible-collection)](https://github.com/grafana/grafana-ansible-collection/tags) -This collection (`grafana.grafana`) contains modules and roles to assist in automating the management of resources in **Grafana**, **Grafana Agent**, and **OpenTelemetry Collector** with Ansible. +This collection (`grafana.grafana`) contains modules and roles to assist in automating the management of resources in **Grafana**, **Grafana Agent**, **OpenTelemetry Collector**, and **Loki** with Ansible. - [Ansible collection Documentation](https://docs.ansible.com/ansible/latest/collections/grafana/grafana/) - [Grafana](https://grafana.com) @@ -43,11 +43,12 @@ collections: ## Roles included in the collection -This collection includes the following roles to help set up and manage Grafana, Grafana Agent, and OpenTelemetry Collector: +This collection includes the following roles to help set up and manage Grafana, Grafana Agent, OpenTelemetry Collector, and Loki: - **Grafana**: Installs and configures Grafana on your target hosts. - **Grafana Agent**: Deploys and configures Grafana Agent, allowing for efficient metrics, logs, and trace data shipping to Grafana Cloud or other endpoints. - **OpenTelemetry Collector**: Sets up and configures the OpenTelemetry Collector, enabling advanced observability features through data collection and transmission. +- **Loki**: Deploy and manage Loki, the log aggregation system. ## Using this collection diff --git a/examples/loki-basic-no-options.yml b/examples/loki-basic-no-options.yml new file mode 100644 index 00000000..cda128da --- /dev/null +++ b/examples/loki-basic-no-options.yml @@ -0,0 +1,6 @@ +--- +- name: Deploy Loki using the default configuration + hosts: all + become: true + roles: + - role: grafana.grafana.loki diff --git a/examples/loki-local-filesystem-with-retention-and-alert.yml b/examples/loki-local-filesystem-with-retention-and-alert.yml new file mode 100644 index 00000000..f3771881 --- /dev/null +++ b/examples/loki-local-filesystem-with-retention-and-alert.yml @@ -0,0 +1,70 @@ +--- +- name: Deploy Loki using the local filesystem + hosts: all + become: true + roles: + - role: grafana.grafana.loki + vars: + loki_querier: + max_concurrent: 16 + engine: + max_look_back_period: 8760h + loki_storage_config: + tsdb_shipper: + active_index_directory: "{{ loki_working_path }}/tsdb-index" + cache_location: "{{ loki_working_path }}/tsdb-cache" + filesystem: + directory: "{{ loki_working_path }}/chunks" + loki_ingester: + wal: + enabled: true + dir: "{{ loki_working_path }}/wal" + lifecycler: + address: 127.0.0.1 + ring: + kvstore: + store: inmemory + replication_factor: 1 + final_sleep: 0s + chunk_idle_period: 1h + max_chunk_age: 2h + chunk_target_size: 1048576 + query_store_max_look_back_period: 8760h + loki_limits_config: + split_queries_by_interval: 0 + reject_old_samples: true + reject_old_samples_max_age: 168h + max_query_length: 0 + max_query_series: 50000 + retention_period: 8760h + allow_structured_metadata: false + max_query_lookback: 8760h + loki_compactor: + working_directory: "{{ loki_working_path }}/compactor" + compaction_interval: 10m + retention_enabled: true + retention_delete_delay: 2h + retention_delete_worker_count: 150 + delete_request_store: filesystem + loki_common: + path_prefix: "{{ loki_working_path }}" + storage: + filesystem: + rules_directory: "{{ loki_working_path }}/rules" + replication_factor: 1 + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + loki_ruler_alerts: + - name: Logs.sshd + rules: + - alert: SshLoginFailed + expr: | + count_over_time({job=~"secure"} |="sshd[" |~": Failed|: Invalid|: Connection closed by authenticating user" | __error__="" [15m]) > 6 + for: 0m + labels: + severity: critical + annotations: + summary: "{% raw %}SSH authentication failure (instance {{ $labels.instance }}).{% endraw %}" + description: "{% raw %}Increase of SSH authentication failures in last 15 minutes\\n VALUE = {{ $value }}{% endraw %}" diff --git a/requirements.yml b/requirements.yml index c1917b40..8d80a4d2 100644 --- a/requirements.yml +++ b/requirements.yml @@ -4,3 +4,5 @@ collections: type: git - name: https://github.com/ansible-collections/community.grafana.git type: git + - name: https://github.com/ansible-collections/ansible.posix.git + type: git diff --git a/roles/loki/README.md b/roles/loki/README.md new file mode 100644 index 00000000..73ee9955 --- /dev/null +++ b/roles/loki/README.md @@ -0,0 +1,238 @@ +# Ansible role - Loki + +[![License](https://img.shields.io/github/license/grafana/grafana-ansible-collection)](LICENSE) + +The Ansible Loki Role allows you to effortlessly deploy and manage [Loki](https://grafana.com/oss/loki/), the log aggregation system. This role is tailored for operating systems such as **RedHat**, **Rocky Linux**, **AlmaLinux**, **Ubuntu**, and **Debian**. + +**๐Ÿ”‘ Key Features** +- **๐Ÿ“ฆ Out-of-the-box Deployment**: Get Loki up and running quickly with default configurations. +- **๐Ÿงน Effortless Uninstall**: Easily remove Loki from your system using the "loki_uninstall" tag. +- **๐Ÿ”” Example Alerting Rules**: Benefit from the included sample Ruler configuration. Utilize the provided example alerting rules as a reference guide for structuring your own rules effectively. + +## Table of Content + +- [Requirements](#requirements) +- [Role Variables](#role-variables) +- - [Default Variables - `defaults/main.yml`](#default-variables---defaultsmainyml) +- - [Alerting Rules Variables](#alerting-rules-variables) +- - [Additional Config Variables for `/etc/loki/config.yml`](#additional-config-variables-for-etclokiconfigyml) +- [Playbook](#playbook) + +## Requirements + +- Ansible 2.10+ + +## Role Variables + +- ๐Ÿ“š Official Loki configuration [documentation](https://grafana.com/docs/loki/latest/configuration/) +- ๐Ÿ—๏ธ Upgrading Loki [documentation](https://grafana.com/docs/loki/latest/upgrading/) + +### **Default Variables - `defaults/main.yml`** + +```yaml +loki_version: "latest" +``` +The version of Loki to download and deploy. Supported standard version "3.0.0" format or "latest". + +```yaml +loki_http_listen_port: 3100 +``` +The TCP port on which Loki listens. By default, it listens on port `3100`. + +```yaml +loki_http_listen_address: "0.0.0.0" +``` +The address on which Loki listens for HTTP requests. By default, it listens on all interfaces. + +```yaml +loki_expose_port: false +``` +By default, this is set to `false`. It supports only simple `firewalld` configurations. If set to `true`, a firewalld rule is added to expose the TCP `loki_http_listen_port`. If set to `false`, the system ensures that the rule is not present. If the `firewalld.service` is not active, all firewalld tasks are skipped. + +```yaml +loki_download_url_rpm: "https://github.com/grafana/loki/releases/download/v{{ loki_version }}/loki-{{ loki_version }}.{{ __loki_arch }}.rpm" +``` +The default download URL for the Loki rpm package from GitHub. + +```yaml +loki_download_url_deb: "https://github.com/grafana/loki/releases/download/v{{ loki_version }}/loki_{{ loki_version }}_{{ __loki_arch }}.deb" +``` +The default download URL for the Loki deb package from GitHub. + +```yaml +loki_working_path: "/var/lib/loki" +``` +โš ๏ธ Avoid using `/tmp/loki` as the working path. This role removes the /tmp/loki directory and replaces it with the specified working path to ensure a permanent configuration. + +```yaml +loki_ruler_alert_path: "{{ loki_working_path }}/rules/fake" +``` +The variable defines the location where the `ruler` configuration `alerts` are stored. +โš ๏ธ Please note that the role currently does not support multi-tenancy for alerting, so there is no need to modify this variable for different tenants. + +```yaml +loki_auth_enabled: false +``` +Enables authentication through the X-Scope-OrgID header, which must be present if `true`. If `false`, the OrgID will always be set to `fake`. + +```yaml +loki_target: "all" +``` +A comma-separated list of components to run. The default value 'all' runs Loki in single binary mode. +Supported values: `all`, `compactor`, `distributor`, `ingester`, `querier`, `query-scheduler`, `ingester-querier`, `query-frontend`, `index-gateway`, `ruler`, `table-manager`, `read`, `write`. + +```yaml +loki_ballast_bytes: 0 +``` +The amount of virtual memory in bytes to reserve as ballast in order to optimize garbage collection. + +```yaml +loki_server: + http_listen_address: "{{ loki_http_listen_address }}" + http_listen_port: "{{ loki_http_listen_port }}" + grpc_listen_port: 9096 +``` +Configures the `server` of the launched module(s). [All possible values for `server`](https://grafana.com/docs/loki/latest/configuration/#server) + +```yaml +loki_common: + instance_addr: 127.0.0.1 + path_prefix: "{{ loki_working_path }}" + storage: + filesystem: + chunks_directory: "{{ loki_working_path }}/chunks" + rules_directory: "{{ loki_working_path }}/rules" + replication_factor: 1 + ring: + kvstore: + store: inmemory +``` +Common configuration to be shared between multiple modules. If a more specific configuration is given in other sections, the related configuration within this section will be ignored. [All possible values for `common`](https://grafana.com/docs/loki/latest/configuration/#common) + +```yaml +loki_query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 +``` +The `query_range` block configures the query splitting and caching in the Loki query-frontend. [All possible values for `query_range`](https://grafana.com/docs/loki/latest/configuration/#query_range) + +```yaml +loki_schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h +``` +Configures the chunk index schema and where it is stored. [All possible values for `schema_config`](https://grafana.com/docs/loki/latest/configuration/#schema_config) + +```yaml +loki_ruler: + storage: + type: local + local: + directory: "{{ loki_working_path }}/rules" + rule_path: "{{ loki_working_path }}/rules_tmp" + ring: + kvstore: + store: inmemory + enable_api: true + enable_alertmanager_v2: true + alertmanager_url: http://localhost:9093 +``` +The `ruler` block configures the Loki ruler. [All possible values for `ruler`](https://grafana.com/docs/loki/latest/configuration/#ruler) + +```yaml +loki_analytics: + reporting_enabled: false +``` +Enable anonymous usage reporting. Disabled by default. + + +### **Alerting Rules Variables** +(not set by default) + +```yaml +--- +loki_ruler_alerts: + - name: Logs.Nextcloud + rules: + - alert: NextcloudLoginFailed + expr: | + count by (filename,env,job) (count_over_time({job=~"nextcloud"} | json | message=~"Login failed.*" [10m])) > 4 + for: 0m + labels: + severity: critical + annotations: + summary: "{% raw %}On {{ $labels.job }} in log {{ $labels.filename }} failed login detected.{% endraw %}" + - name: Logs.sshd + rules: + - alert: SshLoginFailed + expr: | + count_over_time({job=~"secure"} |="sshd[" |~": Failed|: Invalid|: Connection closed by authenticating user" | __error__="" [15m]) > 15 + for: 0m + labels: + severity: critical + annotations: + summary: "{% raw %}SSH authentication failure (instance {{ $labels.instance }}).{% endraw %}" +``` +Example alerting rule configuration. You can add multiple alerting rules to suit your requirements. Please note that the alerting rules are not templated by default + +### **Additional Config Variables for `/etc/loki/config.yml`** +(not set by default) + +Below variables allow you to extend Loki configuration to fit your needs. Always refer to official [Loki configuration](https://grafana.com/docs/loki/latest/configuration/) to obtain possible configuration parameters. + +| Variable Name | Description +| ----------- | ----------- | +| `loki_distributor` | Configures the `distributor`. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#distributor) +| `loki_querier` | Configures the `querier`. Only appropriate when running all modules or just the querier. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#querier) +| `loki_query_scheduler` | The `query_scheduler` block configures the Loki query scheduler. When configured it separates the tenant query queues from the query-frontend. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#query_scheduler) +| `loki_frontend` | The `frontend` block configures the Loki query-frontend. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#frontend) +| `loki_ingester_client` | The `ingester_client` block configures how the distributor will connect to ingesters. Only appropriate when running all components, the distributor, or the querier. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#ingester_client) +| `loki_ingester` | The `ingester` block configures the ingester and how the ingester will register itself to a key value store. ๐Ÿ“š configuration [documentation](https://grafana.com/docs/loki/latest/configuration/#ingester) +| `loki_index_gateway` | The `index_gateway` block configures the Loki index gateway server, responsible for serving index queries without the need to constantly interact with the object store. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#index_gateway) +| `loki_storage_config` | The `storage_config` block configures one of many possible stores for both the index and chunks. Which configuration to be picked should be defined in schema_config block. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#storage_config) +| `loki_chunk_store_config` | The `chunk_store_config` block configures how chunks will be cached and how long to wait before saving them to the backing store. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#chunk_store_config) +| `loki_compactor` | The `compactor` block configures the compactor component, which compacts index shards for performance. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configure/#compactor) +| `loki_limits_config` | The `limits_config` block configures global and per-tenant limits in Loki. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#limits_config) +| `loki_frontend_worker` | The `frontend_worker` configures the worker - running within the Loki querier - picking up and executing queries enqueued by the query-frontend. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#frontend_worker) +| `loki_table_manager` | The `table_manager` block configures the table manager for retention. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#table_manager) +| `loki_memberlist` | Configuration for memberlist client. Only applies if the selected kvstore is memberlist. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#memberlist) +| `loki_runtime_config` | Configuration for `runtime config` module, responsible for reloading runtime configuration file. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#runtime_config) +| `loki_operational_config` | These are values which allow you to control aspects of Loki's operation, most commonly used for controlling types of higher verbosity logging, the values here can be overridden in the configs section of the `runtime_config` file. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configure/#operational_config) +| `loki_tracing` | Configuration for tracing. ๐Ÿ“š [documentation](https://grafana.com/docs/loki/latest/configuration/#tracing) + +## Playbook + +- playbook +```yaml +- name: Manage loki service + hosts: all + become: true + roles: + - role: grafana.grafana.loki +``` + +- Playbook execution example +```shell +# Deployment +ansible-playbook -i inventory/hosts playbook/function_loki_play.yml + +# Uninstall +ansible-playbook -i inventory/hosts playbook/function_loki_play.yml -t loki_uninstall +``` + +## License + +See [LICENSE](https://github.com/grafana/grafana-ansible-collection/blob/main/LICENSE) + +## Author Information + +- [VoidQuark](https://github.com/voidquark) diff --git a/roles/loki/defaults/main.yml b/roles/loki/defaults/main.yml new file mode 100644 index 00000000..ee712bb2 --- /dev/null +++ b/roles/loki/defaults/main.yml @@ -0,0 +1,107 @@ +--- +# defaults file for loki +loki_version: "latest" +loki_http_listen_port: 3100 +loki_http_listen_address: "0.0.0.0" +loki_expose_port: false +loki_download_url_rpm: "https://github.com/grafana/loki/releases/download/v{{ loki_version }}/loki-{{ loki_version }}.{{ __loki_arch }}.rpm" +loki_download_url_deb: "https://github.com/grafana/loki/releases/download/v{{ loki_version }}/loki_{{ loki_version }}_{{ __loki_arch }}.deb" +loki_working_path: "/var/lib/loki" +loki_ruler_alert_path: "{{ loki_working_path }}/rules/fake" + +# Default Variables for /etc/loki/config.yml +loki_auth_enabled: false +loki_target: "all" +loki_ballast_bytes: 0 + +loki_server: + http_listen_address: "{{ loki_http_listen_address }}" + http_listen_port: "{{ loki_http_listen_port }}" + grpc_listen_port: 9096 + +loki_common: + instance_addr: 127.0.0.1 + path_prefix: "{{ loki_working_path }}" + storage: + filesystem: + chunks_directory: "{{ loki_working_path }}/chunks" + rules_directory: "{{ loki_working_path }}/rules" + replication_factor: 1 + ring: + kvstore: + store: inmemory + +loki_query_range: + results_cache: + cache: + embedded_cache: + enabled: true + max_size_mb: 100 + +loki_schema_config: + configs: + - from: 2020-10-24 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +loki_ruler: + storage: + type: local + local: + directory: "{{ loki_working_path }}/rules" + rule_path: "{{ loki_working_path }}/rules_tmp" + ring: + kvstore: + store: inmemory + enable_api: true + enable_alertmanager_v2: true + alertmanager_url: http://localhost:9093 + +loki_analytics: + reporting_enabled: false + +# Alerting Rules Variables +# loki_ruler_alerts: +# - name: Logs.Nextcloud +# rules: +# - alert: NextcloudLoginFailed +# expr: | +# count by (filename,env,job) (count_over_time({job=~"nextcloud"} | json | message=~"Login failed.*" [10m])) > 4 +# for: 0m +# labels: +# severity: critical +# annotations: +# summary: "{% raw %}On {{ $labels.job }} in log {{ $labels.filename }} failed login detected.{% endraw %}" +# - name: Logs.sshd +# rules: +# - alert: SshLoginFailed +# expr: | +# count_over_time({job=~"secure"} |="sshd[" |~": Failed|: Invalid|: Connection closed by authenticating user" | __error__="" [15m]) > 15 +# for: 0m +# labels: +# severity: critical +# annotations: +# summary: "{% raw %}SSH authentication failure (instance {{ $labels.instance }}).{% endraw %}" + +# Additional Config Variables for /etc/loki/config.yml +# loki_distributor: +# loki_querier: +# loki_query_scheduler: +# loki_frontend: +# loki_ingester_client: +# loki_ingester: +# loki_index_gateway: +# loki_storage_config: +# loki_chunk_store_config: +# loki_compactor: +# loki_limits_config: +# loki_frontend_worker: +# loki_table_manager: +# loki_memberlist: +# loki_runtime_config: +# loki_operational_config: +# loki_tracing: diff --git a/roles/loki/handlers/main.yml b/roles/loki/handlers/main.yml new file mode 100644 index 00000000..b2326ec6 --- /dev/null +++ b/roles/loki/handlers/main.yml @@ -0,0 +1,9 @@ +--- +# handlers file for loki +- name: Restart loki + listen: "restart loki" + ansible.builtin.systemd: + daemon_reload: true + name: loki.service + state: restarted + enabled: true diff --git a/roles/loki/meta/main.yml b/roles/loki/meta/main.yml new file mode 100644 index 00000000..b66e783d --- /dev/null +++ b/roles/loki/meta/main.yml @@ -0,0 +1,28 @@ +--- +galaxy_info: + role_name: loki + author: voidquark + description: Manage Grafana Loki Application + license: "GPL-3.0-or-later" + min_ansible_version: "2.10" + platforms: + - name: EL + versions: + - "8" + - "9" + - name: Fedora + versions: + - all + - name: Debian + versions: + - all + - name: Ubuntu + versions: + - all + galaxy_tags: + - loki + - grafana + - logging + - monitoring + +dependencies: [] diff --git a/roles/loki/molecule/default/converge.yml b/roles/loki/molecule/default/converge.yml new file mode 100644 index 00000000..1f2ce7f5 --- /dev/null +++ b/roles/loki/molecule/default/converge.yml @@ -0,0 +1,5 @@ +--- +- name: Converge + hosts: all + roles: + - role: grafana.grafana.loki diff --git a/roles/loki/molecule/default/molecule.yml b/roles/loki/molecule/default/molecule.yml new file mode 100644 index 00000000..339965a5 --- /dev/null +++ b/roles/loki/molecule/default/molecule.yml @@ -0,0 +1,20 @@ +--- +dependency: + name: galaxy + options: + ignore-errors: true +driver: + name: docker +platforms: + - name: instance + image: "geerlingguy/docker-${MOLECULE_DISTRO:-rockylinux8}-ansible:latest" + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + pre_build_image: true +provisioner: + name: ansible + playbooks: + converge: converge.yml diff --git a/roles/loki/tasks/deploy.yml b/roles/loki/tasks/deploy.yml new file mode 100644 index 00000000..65ecaf6f --- /dev/null +++ b/roles/loki/tasks/deploy.yml @@ -0,0 +1,146 @@ +--- +# tasks file for loki deployment + +- name: Obtain the latest version from the Loki GitHub repo + when: loki_version == "latest" + block: + - name: Scrape Github API endpoint to obtain latest Loki version + ansible.builtin.uri: + url: "https://api.github.com/repos/grafana/loki/releases/latest" + method: GET + body_format: json + become: false + delegate_to: localhost + run_once: true + register: __github_latest_version + + - name: Latest available Loki version + ansible.builtin.set_fact: + loki_version: "{{ __github_latest_version.json.tag_name | regex_replace('^v?(\\d+\\.\\d+\\.\\d+)$', '\\1') }}" + +- name: Verify current deployed version + block: + - name: Check if Loki binary is present + ansible.builtin.stat: + path: "/usr/bin/loki" + register: __already_deployed + + - name: Obtain current deployed Loki version + ansible.builtin.command: + cmd: "/usr/bin/loki --version" + changed_when: false + register: __current_deployed_version + when: __already_deployed.stat.exists | bool + +- name: Include RedHat/Rocky setup + ansible.builtin.include_tasks: + file: setup-RedHat.yml + when: ansible_os_family in ['RedHat', 'Rocky'] + +- name: Include Debian/Ubuntu setup + ansible.builtin.include_tasks: + file: setup-Debian.yml + when: ansible_os_family == 'Debian' + +- name: Check if Loki default dir is present + ansible.builtin.stat: + path: "/tmp/loki/boltdb-shipper-active" + register: __default_structure + +- name: Default structure cleanup + when: __default_structure.stat.exists | bool + block: + - name: Ensure that Loki is stopped before default cleanup + ansible.builtin.systemd: + name: loki.service + state: stopped + + - name: Remove default configuration from "/tmp/loki" directory + ansible.builtin.file: + path: "/tmp/loki" + state: absent + +- name: Ensure that Loki working path exists + ansible.builtin.file: + path: "{{ loki_working_path }}" + state: directory + owner: "loki" + group: "root" + mode: "0755" + +- name: Template Loki config - /etc/loki/config.yml + ansible.builtin.template: + src: "config.yml.j2" + dest: "/etc/loki/config.yml" + owner: "root" + group: "root" + mode: "0644" + validate: "/usr/bin/loki --verify-config -config.file %s" + notify: restart loki + +- name: Ensure that Loki rule path exists + ansible.builtin.file: + path: "{{ loki_ruler_alert_path }}" + state: directory + owner: "loki" + group: "root" + mode: "0750" + when: + - loki_ruler_alert_path is defined + - loki_ruler is defined + +- name: Template Loki Rule File + ansible.builtin.template: + src: "rules.yml.j2" + dest: "{{ loki_ruler_alert_path }}/rules.yml" + owner: "loki" + group: "root" + mode: "0644" + notify: restart loki + when: + - loki_ruler_alerts is defined + - loki_ruler_alert_path is defined + - loki_ruler is defined + +- name: Get firewalld state + ansible.builtin.systemd: + name: "firewalld" + register: __firewalld_service_state + +- name: Enable firewalld rule to expose Loki tcp port {{ loki_http_listen_port }} + ansible.posix.firewalld: + immediate: true + permanent: true + port: "{{ loki_http_listen_port }}/tcp" + state: enabled + when: + - __firewalld_service_state.status.ActiveState == "active" + - loki_expose_port | bool + +- name: Ensure that Loki firewalld rule is not present - tcp port {{ loki_http_listen_port }} + ansible.posix.firewalld: + immediate: true + permanent: true + port: "{{ loki_http_listen_port }}/tcp" + state: disabled + when: + - __firewalld_service_state.status.ActiveState == "active" + - not loki_expose_port | bool + +- name: Flush handlers after deployment + ansible.builtin.meta: flush_handlers + +- name: Ensure that Loki is started + ansible.builtin.systemd: + name: loki.service + state: started + +- name: Verify that Loki URL is responding + ansible.builtin.uri: + url: "http://{{ loki_http_listen_address }}:{{ loki_http_listen_port }}/ready" + method: GET + register: loki_verify_url_status_code + retries: 5 + delay: 8 + until: loki_verify_url_status_code.status == 200 + when: loki_expose_port | bool diff --git a/roles/loki/tasks/main.yml b/roles/loki/tasks/main.yml new file mode 100644 index 00000000..4e0bb741 --- /dev/null +++ b/roles/loki/tasks/main.yml @@ -0,0 +1,23 @@ +--- +# tasks file for loki +- name: Include OS specific variables + ansible.builtin.include_vars: + file: "{{ ansible_os_family }}.yml" + +- name: Deploy Loki service + ansible.builtin.include_tasks: + file: "deploy.yml" + apply: + tags: loki_deploy + tags: loki_deploy + +- name: Uninstall Loki service + ansible.builtin.include_tasks: + file: "uninstall.yml" + apply: + tags: + - loki_uninstall + - never + tags: + - loki_uninstall + - never diff --git a/roles/loki/tasks/setup-Debian.yml b/roles/loki/tasks/setup-Debian.yml new file mode 100644 index 00000000..c71af7f4 --- /dev/null +++ b/roles/loki/tasks/setup-Debian.yml @@ -0,0 +1,7 @@ +--- +- name: APT - Install Loki + ansible.builtin.apt: + deb: "{{ loki_download_url_deb }}" + state: present + notify: restart loki + when: __current_deployed_version.stdout is not defined or loki_version not in __current_deployed_version.stdout diff --git a/roles/loki/tasks/setup-RedHat.yml b/roles/loki/tasks/setup-RedHat.yml new file mode 100644 index 00000000..91e60c50 --- /dev/null +++ b/roles/loki/tasks/setup-RedHat.yml @@ -0,0 +1,8 @@ +--- +- name: DNF - Install Loki from remote URL + ansible.builtin.dnf: + name: "{{ loki_download_url_rpm }}" + state: present + disable_gpg_check: true + notify: restart loki + when: __current_deployed_version.stdout is not defined or loki_version not in __current_deployed_version.stdout diff --git a/roles/loki/tasks/uninstall.yml b/roles/loki/tasks/uninstall.yml new file mode 100644 index 00000000..c8ad2595 --- /dev/null +++ b/roles/loki/tasks/uninstall.yml @@ -0,0 +1,50 @@ +--- +# tasks file for loki uninstall + +- name: Stop Loki service + ansible.builtin.systemd: # noqa ignore-errors + name: loki + state: stopped + ignore_errors: true + +- name: Uninstall Loki rpm package + ansible.builtin.dnf: + name: "loki" + state: absent + autoremove: true + when: ansible_os_family in ['RedHat', 'Rocky'] + +- name: Uninstall Loki deb package + ansible.builtin.apt: + name: "loki" + state: absent + when: ansible_os_family == 'Debian' + +- name: Ensure that Loki firewalld rule is not present - tcp port {{ loki_http_listen_port }} + ansible.posix.firewalld: # noqa ignore-errors + immediate: true + permanent: true + port: "{{ loki_http_listen_port }}/tcp" + state: disabled + ignore_errors: true + +- name: Remove Loki directories" + ansible.builtin.file: + path: "{{ remove_me }}" + state: absent + loop: + - "/etc/loki" + - "{{ loki_working_path }}" + loop_control: + loop_var: remove_me + +- name: Remove the Loki system user + ansible.builtin.user: + name: "loki" + force: true + state: absent + +- name: Remove Loki system group + ansible.builtin.group: + name: "loki" + state: absent diff --git a/roles/loki/templates/config.yml.j2 b/roles/loki/templates/config.yml.j2 new file mode 100644 index 00000000..72147184 --- /dev/null +++ b/roles/loki/templates/config.yml.j2 @@ -0,0 +1,99 @@ +{% if loki_target is defined %} +target: {{ loki_target }} +{% endif %} +{% if loki_auth_enabled is defined %} +auth_enabled: {{ loki_auth_enabled }} +{% endif %} +{% if loki_ballast_bytes is defined %} +ballast_bytes: {{ loki_ballast_bytes }} +{% endif %} +server: + {{ loki_server | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% if loki_distributor is defined %} +distributor: + {{ loki_distributor | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_querier is defined %} +querier: + {{ loki_querier | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_query_scheduler is defined %} +query_scheduler: + {{ loki_query_scheduler | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_frontend is defined %} +frontend: + {{ loki_frontend | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_query_range is defined %} +query_range: + {{ loki_query_range | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_ruler is defined %} +ruler: + {{ loki_ruler | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_ingester_client is defined %} +ingester_client: + {{ loki_ingester_client | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_ingester is defined %} +ingester: + {{ loki_ingester | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_index_gateway is defined %} +index_gateway: + {{ loki_index_gateway | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_storage_config is defined %} +storage_config: + {{ loki_storage_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_chunk_store_config is defined %} +chunk_store_config: + {{ loki_chunk_store_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_schema_config is defined %} +schema_config: + {{ loki_schema_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_compactor is defined %} +compactor: + {{ loki_compactor | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_limits_config is defined %} +limits_config: + {{ loki_limits_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_frontend_worker is defined %} +frontend_worker: + {{ loki_frontend_worker | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_table_manager is defined %} +table_manager: + {{ loki_table_manager | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_memberlist is defined %} +memberlist: + {{ loki_memberlist | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_runtime_config is defined %} +runtime_config: + {{ loki_runtime_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if operational_config is defined %} +operational_config: + {{ loki_operational_config | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_tracing is defined %} +tracing: + {{ loki_tracing | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_analytics is defined %} +analytics: + {{ loki_analytics | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} +{% if loki_common is defined %} +common: + {{ loki_common | to_nice_yaml(indent=2,sort_keys=False) | indent(2, False) }} +{% endif %} diff --git a/roles/loki/templates/rules.yml.j2 b/roles/loki/templates/rules.yml.j2 new file mode 100644 index 00000000..a3dc3be2 --- /dev/null +++ b/roles/loki/templates/rules.yml.j2 @@ -0,0 +1,3 @@ +--- +groups: + {{ loki_ruler_alerts | to_nice_yaml(indent=2,sort_keys=False) | indent(2,False) }} diff --git a/roles/loki/vars/Debian.yml b/roles/loki/vars/Debian.yml new file mode 100644 index 00000000..b211affe --- /dev/null +++ b/roles/loki/vars/Debian.yml @@ -0,0 +1,5 @@ +--- +__loki_arch_map: + x86_64: 'amd64' + +__loki_arch: "{{ __loki_arch_map[ansible_architecture] | default(ansible_architecture) }}" diff --git a/roles/loki/vars/RedHat.yml b/roles/loki/vars/RedHat.yml new file mode 100644 index 00000000..77fe6b4d --- /dev/null +++ b/roles/loki/vars/RedHat.yml @@ -0,0 +1,2 @@ +--- +__loki_arch: "{{ ansible_architecture }}"