diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e09b2dc..ed29aff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: -f extensions/apm-server/apm-server-compose.yml \ -f extensions/metricbeat/metricbeat-compose.yml \ -f extensions/filebeat/filebeat-compose.yml \ + -f extensions/heartbeat/heartbeat-compose.yml \ build ######################################################## @@ -200,6 +201,23 @@ jobs: # next steps don't need Filebeat docker compose -f docker-compose.yml -f extensions/filebeat/filebeat-compose.yml stop filebeat + # + # Heartbeat + # + + - name: Execute Heartbeat test suite + run: | + docker compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml up -d heartbeat + .github/workflows/scripts/run-tests-heartbeat.sh + + - name: 'debug: Display state and logs (Heartbeat)' + if: always() + run: | + docker compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml ps + docker compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml logs heartbeat + # next steps don't need Heartbeat + docker compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml stop heartbeat + ############## # # # Tear down. # diff --git a/.github/workflows/scripts/run-tests-heartbeat.sh b/.github/workflows/scripts/run-tests-heartbeat.sh new file mode 100755 index 0000000..882a977 --- /dev/null +++ b/.github/workflows/scripts/run-tests-heartbeat.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + + +source "$(dirname ${BASH_SOURCE[0]})/lib/testing.sh" + + +cid_es="$(container_id elasticsearch)" +cid_mb="$(container_id heartbeat)" + +ip_es="$(service_ip elasticsearch)" +ip_mb="$(service_ip heartbeat)" + +log 'Waiting for readiness of Elasticsearch' +poll_ready "$cid_es" "http://${ip_es}:9200/" -u 'elastic:testpasswd' + +log 'Waiting for readiness of Heartbeat' +poll_ready "$cid_mb" "http://${ip_mb}:5066/?pretty" + +# We expect to find heartbeat entries for the 'elasticsearch' HTTP service +# using the following query: +# +# agent.type:"heartbeat" +# AND monitor.type:"http" +# AND url.domain:"elasticsearch" +# +log 'Searching a document generated by Heartbeat' + +declare response +declare -i count + +declare -i was_retried=0 + +# retry for max 60s (30*2s) +for _ in $(seq 1 30); do + response="$(curl "http://${ip_es}:9200/heartbeat-*/_search?q=agent.type:%22heartbeat%22%20AND%20monitor.type:%22http%22%20AND%20url.domain:%22elasticsearch%22&pretty" -s -u elastic:testpasswd)" + + set +u # prevent "unbound variable" if assigned value is not an integer + count="$(jq -rn --argjson data "${response}" '$data.hits.total.value')" + set -u + + if (( count > 0 )); then + break + fi + + was_retried=1 + echo -n 'x' >&2 + sleep 2 +done +if ((was_retried)); then + # flush stderr, important in non-interactive environments (CI) + echo >&2 +fi + +echo "$response" +if (( count == 0 )); then + echo 'Expected at least 1 document' + exit 1 +fi diff --git a/extensions/heartbeat/.dockerignore b/extensions/heartbeat/.dockerignore new file mode 100644 index 0000000..37eef9d --- /dev/null +++ b/extensions/heartbeat/.dockerignore @@ -0,0 +1,6 @@ +# Ignore Docker build files +Dockerfile +.dockerignore + +# Ignore OS artifacts +**/.DS_Store diff --git a/extensions/heartbeat/Dockerfile b/extensions/heartbeat/Dockerfile new file mode 100644 index 0000000..0d7de19 --- /dev/null +++ b/extensions/heartbeat/Dockerfile @@ -0,0 +1,3 @@ +ARG ELASTIC_VERSION + +FROM docker.elastic.co/beats/heartbeat:${ELASTIC_VERSION} diff --git a/extensions/heartbeat/README.md b/extensions/heartbeat/README.md new file mode 100644 index 0000000..da02f92 --- /dev/null +++ b/extensions/heartbeat/README.md @@ -0,0 +1,35 @@ +# Heartbeat + +Heartbeat is a lightweight daemon that periodically checks the status of your services and determines whether they are +available. + +## Usage + +To include Heartbeat in the stack, run Docker Compose from the root of the repository with an additional command line +argument referencing the `heartbeat-compose.yml` file: + +```console +$ docker-compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml up +``` + +## Configuring Heartbeat + +The Heartbeat configuration is stored in [`config/heartbeat.yml`](./config/heartbeat.yml). You can modify this file +with the help of the [Configuration reference][heartbeat-config]. + +Any change to the Heartbeat configuration requires a restart of the Heartbeat container: + +```console +$ docker-compose -f docker-compose.yml -f extensions/heartbeat/heartbeat-compose.yml restart heartbeat +``` + +Please refer to the following documentation page for more details about how to configure Heartbeat inside a +Docker container: [Run Heartbeat on Docker][heartbeat-docker]. + +## See also + +[Heartbeat documentation][heartbeat-doc] + +[heartbeat-config]: https://www.elastic.co/guide/en/beats/heartbeat/current/heartbeat-reference-yml.html +[heartbeat-docker]: https://www.elastic.co/guide/en/beats/heartbeat/current/running-on-docker.html +[heartbeat-doc]: https://www.elastic.co/guide/en/beats/heartbeat/current/index.html diff --git a/extensions/heartbeat/config/heartbeat.yml b/extensions/heartbeat/config/heartbeat.yml new file mode 100644 index 0000000..b5276c0 --- /dev/null +++ b/extensions/heartbeat/config/heartbeat.yml @@ -0,0 +1,31 @@ +## Heartbeat configuration +## https://github.com/elastic/beats/blob/main/deploy/docker/heartbeat.docker.yml +# + +heartbeat.monitors: +- type: http + schedule: '@every 5s' + urls: + - http://elasticsearch:9200 + username: elastic + password: ${ELASTIC_PASSWORD} + +- type: icmp + schedule: '@every 5s' + hosts: + - elasticsearch + +processors: +- add_cloud_metadata: ~ + +output.elasticsearch: + hosts: ['http://elasticsearch:9200'] + username: elastic + password: ${ELASTIC_PASSWORD} + +## HTTP endpoint for health checking +## https://www.elastic.co/guide/en/beats/heartbeat/current/http-endpoint.html +# + +http.enabled: true +http.host: 0.0.0.0 diff --git a/extensions/heartbeat/heartbeat-compose.yml b/extensions/heartbeat/heartbeat-compose.yml new file mode 100644 index 0000000..a6fe2ad --- /dev/null +++ b/extensions/heartbeat/heartbeat-compose.yml @@ -0,0 +1,23 @@ +version: '3.7' + +services: + heartbeat: + build: + context: extensions/heartbeat/ + args: + ELASTIC_VERSION: ${ELASTIC_VERSION} + command: + # Log to stderr. + - -e + # Disable config file permissions checks. Allows mounting + # 'config/heartbeat.yml' even if it's not owned by root. + # see: https://www.elastic.co/guide/en/beats/libbeat/current/config-file-permissions.html + - --strict.perms=false + volumes: + - ./extensions/heartbeat/config/heartbeat.yml:/usr/share/heartbeat/heartbeat.yml:ro,Z + environment: + ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-} + networks: + - elk + depends_on: + - elasticsearch