Simple Bosh release to deploy binary OpenTelemetry collector from upstream, allowing end users to define the full configuration file.
Usage. See the manifest directory for an example of how to deploy it. Check the Releases for the changelog and installation references.
Normally this release will be included as a Bosh job in the runtime-config to get it deployed as an agent in all VMs (like node-exporter).
Parameters (see jobs/otelcollector-bin/spec):
enabled:
description: "Enable OTel Collector"
default: true
ingress.grpc.address:
description: "Address to listen on to receive OTLP over gRPC, exposed as env var OTELCOL_GRPC_ADDR for configuration"
default: 0.0.0.0
ingress.grpc.port:
description: "Port the collector is listening on to receive OTLP over gRPC, exposed as env var OTELCOL_GRPC_PORT for configuration"
default: 4317
ingress.grpc.tls.ca_cert:
description: "CA root required for key/cert verification in gRPC ingress"
ingress.grpc.tls.cert:
description: "TLS server certificate for gRPC ingress"
ingress.grpc.tls.key:
description: "TLS server key for gRPC ingress"
ingress.loggr-protocol:
description: "Ingress protocol type for loggr-forwarder-agent job from loggregator-agent"
default: otelcol
otelcollector-bin.feature-gates:
description: "Enable or disable additional features"
example: ["gate1", "-gate2", "+gate3"]
default: []
otelcollector-bin.prom-jobs.enabled:
description: "Autodiscover and setup other local prometheus targets"
default: false
otelcollector-bin.prom-jobs.glob-pattern:
description: "Glob pattern to auto-discover local targets from other jobs"
default: "/var/vcap/jobs/*/config/prom_scraper_config.yml"
otelcollector-bin.prom-jobs.receiver-name:
description: "Name for the prometheus instance to get included in the configuration file with all local targets. This name will need to get included in a pipeline"
default: "boshjobs"
otelcollector-bin.healthcheck-endpoint:
description: "Health-check endpoint, exposed as env var OTELCOL_HEALTHCHECK for configuration"
default: "127.0.0.1:13133"
otelcollector-bin.setcap:
description: List of capabilities to pass ot setcap command. See setcap manual (https://www.man7.org/linux/man-pages/man8/setcap.8.html)
default:
- cap_net_bind_service=+ep
otelcollector-bin.env:
description: Key/value hash of environment variables.
default: {}
otelcollector-bin.config:
default: |
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:OTELCOL_GRPC_ADDR}:${env:OTELCOL_GRPC_PORT}
# Collect own metrics
prometheus:
config:
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 30s
static_configs:
- targets: ['127.0.0.1:8881']
hostmetrics:
collection_interval: 60s
scrapers:
cpu:
disk:
filesystem:
load:
memory:
network:
process:
processes:
paging:
processors:
batch:
exporters:
debug:
verbosity: basic
service:
extensions: []
logs:
level: INFO
metrics:
level: basic
address: 127.0.0.1:8881
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
metrics:
receivers: [otlp, hostmetrics, prometheus]
processors: [batch]
exporters: [debug]
Two jobs manage Mimir with mimirtool.
Neither runs a long-running process (empty monit). They work the same way:
- post-start: runs on every start or deploy of the instance.
- errand: runs the same logic on demand. If the job is colocated in an instance group, run
bosh -d <dep> run-errand mimir-rulesorbosh -d <dep> run-errand mimir-alertmanager. - bootstrap only: by default (
bootstrap_only: true) they only run on the bootstrap instance of the instance group. The other instances log "not bootstrap" and skip. - Entries (
rulesets/configs) are hashes keyed by id, so separate ops files can add or replace entries without indexes. They run one after another, sorted by id, with retries and backoff (5s, 10s, 20s, ...) per mimirtool API call. - If one entry fails, it is reported and the others continue. At the end, the job exits
non-zero only if
fail_on_error: trueand any entry failed. enabled: falseon an entry skips it. It does not delete anything in Mimir.- Configuration errors (missing namespace, duplicated namespace or tenant, invalid modes...) are validated at render time, so the deploy fails before touching any VM.
- Logs go to
/var/vcap/sys/log/<job>/<job>.log. API keys and receiver credentials are never printed.
Uploads rule sets (recording rules, alerting rules or both). Each rule set has its own namespace,
tenant, mode and groups, and is rendered to its own rules file. Before uploading, each rule set
runs mimirtool rules check and mimirtool rules lint --dry-run (report only, never rewrites).
| Property | Description | Default |
|---|---|---|
mimir-rules.enabled |
Enable rules sync | true |
mimir-rules.address |
Default Mimir base URL for all rule sets (mimirtool calls <address>/prometheus/config/v1/rules) |
|
mimir-rules.tenant_id |
Default tenant id (X-Scope-OrgID) for all rule sets |
|
mimir-rules.auth.user |
Default optional basic auth user (MIMIR_API_USER) |
|
mimir-rules.auth.key |
Default optional API key / password (MIMIR_API_KEY) |
|
mimir-rules.sync_mode |
Default mode for all rule sets (see below) | load |
mimir-rules.rulesets |
Hash of rule sets keyed by id (see below) | {} |
mimir-rules.fail_on_error |
If true, the job fails (and the deploy) when any rule set failed | true |
mimir-rules.bootstrap_only |
Only run on the bootstrap instance of the instance group | true |
mimir-rules.retries |
Number of attempts per API call before giving up | 5 |
Rule set fields:
| Field | Required | Default | Notes |
|---|---|---|---|
namespace |
yes | Non-empty. Unique per (address, tenant_id) |
|
groups |
yes | mimirtool/Prometheus groups, passed through as-is (interval, source_tenants, evaluation_delay, limit, record/alert rules, for, keep_firing_for, labels, annotations) |
|
tenant_id |
no | mimir-rules.tenant_id |
|
address |
no | mimir-rules.address |
|
sync_mode |
no | mimir-rules.sync_mode |
load, sync, delete-namespace or delete-groups |
enabled |
no | true |
false skips the rule set, nothing is deleted |
auth.user / auth.key |
no | mimir-rules.auth.* |
Per rule set credentials |
Modes:
load: creates or updates the groups of the rule set. Groups removed from the manifest stay in Mimir.sync: idempotent. Creates, updates and prunes groups removed from the manifest. It is always scoped with--namespaces=<namespace>, so it never touches namespaces it doesn't own.delete-namespace: deletes the rule set namespace.delete-groups: deletes only the groups listed in the rule set.
Recording rules and alerts in separate ops files, each adding a rule set:
# 60-recording-rules.yml
- type: replace
path: /instance_groups/name=otelcollector/jobs/name=mimir-rules/properties/mimir-rules?/rulesets?/recording?
value:
namespace: snpaas-key-metrics
groups:
- name: key-metrics
interval: 1m
rules:
- record: job:up:sum
expr: sum by (job) (up)
# 61-alerting-rules.yml
- type: replace
path: /instance_groups/name=otelcollector/jobs/name=mimir-rules/properties/mimir-rules?/rulesets?/alerts-bosh?
value:
namespace: snpaas-alerts-bosh
sync_mode: sync
groups:
- name: bosh
rules:
- alert: BoshJobDown
expr: up{job="bosh"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} is down ({{ $value | humanizePercentage }})"with the job defined once (see manifest/deployment-add-mimir-rules.yml):
- name: mimir-rules
release: otelcollector-binary
properties:
mimir-rules:
address: ((mimir_api))
tenant_id: ((management_tenant))
sync_mode: syncRemoving a rule set from the manifest only stops managing its namespace; the rules stay in Mimir. To delete it:
- Set
sync_mode: delete-namespaceon the rule set (ordelete-groupsto delete only its groups) and deploy once. - Remove the rule set from the manifest and deploy again. Otherwise every deploy deletes it again.
The mimir-rules.rules property has been removed. A manifest that only sets rules now fails at
render time (enabled but no 'rulesets' defined). Move its content to a rule set:
mimir-rules:
rulesets:
default: # any id
namespace: ... # the 'namespace' of the old rules
groups: [ ... ] # the 'groups' of the old rulesUploads the Alertmanager configuration (and its templates) for one or more tenants. Mimir holds one
Alertmanager configuration per tenant, so tenant_id must be unique per address across the enabled configs.
| Property | Description | Default |
|---|---|---|
mimir-alertmanager.enabled |
Enable Alertmanager configuration upload | true |
mimir-alertmanager.address |
Default Mimir base URL for all configs | |
mimir-alertmanager.auth.user |
Default optional basic auth user (MIMIR_API_USER) |
|
mimir-alertmanager.auth.key |
Default optional API key / password (MIMIR_API_KEY) |
|
mimir-alertmanager.configs |
Hash of configs keyed by id (see below) | {} |
mimir-alertmanager.fail_on_error |
If true, the job fails (and the deploy) when any config failed | true |
mimir-alertmanager.bootstrap_only |
Only run on the bootstrap instance of the instance group | true |
mimir-alertmanager.retries |
Number of attempts per API call before giving up | 5 |
Config fields:
| Field | Required | Default | Notes |
|---|---|---|---|
tenant_id |
yes | Unique per address | |
mode |
no | load |
load: mimirtool alertmanager verify and then load (replaces the full config, idempotent). delete: deletes the tenant config |
config |
yes (load) |
alertmanager.yml as a hash (ops files can patch it) or a YAML string |
|
templates |
no | {} |
Template file name => content, passed byte-for-byte |
address |
no | mimir-alertmanager.address |
|
enabled |
no | true |
false skips the config, nothing is deleted |
auth.user / auth.key |
no | mimir-alertmanager.auth.* |
Per config credentials |
Example with receiver credentials from CredHub (see manifest/deployment-add-mimir-alertmanager.yml):
- name: mimir-alertmanager
release: otelcollector-binary
properties:
mimir-alertmanager:
address: ((mimir_api))
configs:
management:
tenant_id: ((management_tenant))
config:
route:
receiver: slack
group_by: [alertname]
receivers:
- name: slack
slack_configs:
- api_url: ((slack_webhook_url)) # CredHub
title: '{{ template "sn.title" . }}'
templates:
sn.tmpl: |
{{ define "sn.title" }}[{{ .Status | toUpper }}] {{ .CommonLabels.alertname }}{{ end }}To delete a tenant configuration, set mode: delete on the config, deploy once, then remove it from the manifest.
The rendered configuration files are written to /var/vcap/data/mimir-alertmanager/ only while the job
runs, and are removed afterwards because they contain receiver credentials.
- Rules, Alertmanager configs and templates are rendered from parsed data (
YAML.dump) and passed base64-encoded to the scripts, so Go templates ({{ $labels.x }},{{ $value }}),%,$and YAML special characters reach Mimir unchanged (covered by the specs inspec/). - BOSH
((var))interpolation happens before rendering. Rules or templates that contain a literal((/))are not supported. - Empty groups (
rules: []) are allowed.
- Rules that use
source_tenantsneed-tenant-federation.enabled=trueand-ruler.tenant-federation.enabled=true. - The Alertmanager must be enabled for the target tenant (
-alertmanager.*, andalertmanager_receivers_firewall_*if it is used). - The API credentials need ruler and alertmanager write permissions.
bundle install
bundle exec rspec
To update the collector deployed by this release:
- In
packages/otelcollector-linux-amd64/specfile, update the version of the tar file underfilessection and also the commented URL. You can use files with 2 extensions:.binand.tgz. - Run
./update-blobs.sh. This will read the commented URL from the previous file, and create the file specified infilessection. This file will become a blob which will be uploaded/synced to theblobstore. - Upload the release to a Bosh director and test it (see below).
- Push all blob and package resources to the repository. Check if the pipeline works and check if the generated package (bosh release) works.
- Create a GH Release which includes a Bosh final Release artifact by triggering the pipeline with an annotated tag (see below).
Blobs are stored in this repository as LFS objects!. Commit all blobs to blobstore.
In order to test and create a "non final" (dev) release, run:
# Download, update or sync blobs
./update-blobs.sh
# Create a dev release
bosh create-release --force --tarball=/tmp/release.tgz
# Upload dev release to bosh director (for testing)
bosh -e <bosh-env> upload-release /tmp/release.tgz
Final versions are released by the GH Action workflow. Please commit all changes (and blobs) and then push an annotated tag to the repository.
# git add config
# git add packages
# git add blobs*
# git status .
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: blobs/otelcollector-linux-amd64/otelcol_contrib_0.121.0_linux_amd64.bin
new file: blobstore/a632163d-15d8-41c6-4871-e45ce2153166
modified: config/blobs.yml
modified: packages/otelcollector-linux-amd64/spec
# git commit -m "update otelcol"
Warning
Do not commit releases and .final_builds resources created when doing a final release manually. Those are commit by the pipeline
git tag -a v<version> -m "<comment used to commit the resources created by bosh>"
git push --tags
Trigger a workflow with annotated tags requires a GitHub token to access the GitHub API. You configure this token via the token configuration option.
Warning
If using GitHub Actions, you will need to specify a token for your workflows to run on
tags, releases and PRs.
By default, it uses the built-in GITHUB_TOKEN secret. However, all resources created
by workflows (release tag or release pull request) will not trigger future GitHub actions workflows, and workflows normally triggered by release.created events will also not run.
From GitHub's docs:
When you use the repository's
GITHUB_TOKENto perform tasks, events triggered by theGITHUB_TOKENwill not create a new workflow run. This prevents you from accidentally creating recursive workflow runs.
You will want to configure a GitHub Actions secret with a Personal Access Token if you want GitHub Actions CI checks to run on Release Please PRs.
MIT License
Copyright (c) 2024 Springer Nature Observabilty Engineering
Jose Riguera