OpenTelemetryCollectorOCBObservabilité

OCB : construire son propre OpenTelemetry Collector sur-mesure

Pourquoi et comment utiliser l'OpenTelemetry Collector Builder (OCB) pour assembler une distribution du Collector taillée pour vos besoins : plus légère, plus sûre et plus facile à maintenir.

L’OpenTelemetry Collector est la pièce maîtresse de toute architecture d’observabilité moderne : il reçoit, transforme et exporte vos traces, métriques et logs sans coupler votre code à un vendor particulier. Mais quand vient le moment de le déployer en production, une question revient toujours : quelle distribution utiliser ?

La réponse de beaucoup d’équipes est otelcol-contrib, la distribution « tout-en-un » qui embarque des centaines de composants. C’est pratique pour démarrer, mais c’est rarement le bon choix en production. C’est là qu’intervient OCB, l’OpenTelemetry Collector Builder.

Qu’est-ce qu’OCB ?

OCB (ocb, pour OpenTelemetry Collector Builder) est l’outil officiel qui permet de générer votre propre distribution du Collector à partir d’un simple fichier de manifeste. Vous déclarez la liste exacte des composants dont vous avez besoin — receivers, processors, exporters, connectors, extensions — et OCB se charge de générer le code Go, de résoudre les dépendances et de compiler un binaire unique.

En d’autres termes : au lieu de prendre une distribution figée et trop large, vous composez la vôtre.

flowchart LR
    A[builder-config.yaml] --> B[ocb]
    B --> C[Code Go généré]
    C --> D[go build]
    D --> E[Binaire otelcol sur-mesure]
    E --> F[Déploiement en production]

Pourquoi ne pas simplement utiliser otelcol-contrib ?

C’est la vraie question, et c’est elle qui justifie OCB. La distribution contrib est excellente pour expérimenter, mais en production elle pose plusieurs problèmes :

  • Surface d’attaque. Chaque composant embarqué est du code qui tourne dans votre infrastructure. Embarquer 200 composants alors que vous en utilisez 8, c’est exposer inutilement votre plateforme aux CVE des 192 autres.
  • Taille du binaire et de l’image. La distribution contrib pèse plusieurs centaines de Mo. Une distribution sur-mesure peut descendre à quelques dizaines de Mo, ce qui accélère les pulls d’image, les démarrages de pods et réduit la consommation mémoire.
  • Audit et conformité. Une SBOM (Software Bill of Materials) courte et maîtrisée est bien plus facile à auditer qu’une liste de dépendances pléthorique. Vos équipes sécurité vous remercieront.
  • Composants internes ou tiers. OCB permet d’intégrer vos propres composants ou des composants tiers non présents dans contrib, ce qui est impossible avec une distribution figée.

En résumé : otelcol-contrib pour prototyper, OCB pour produire.

Le manifeste : builder-config.yaml

Tout part d’un fichier de manifeste. Il décrit la distribution à produire et la liste des modules à inclure. Voici un exemple minimal mais réaliste — un Collector qui reçoit de l’OTLP, applique du batching et exporte vers un backend OTLP :

dist:
  name: otelcol-custom
  description: "Distribution OpenTelemetry Collector sur-mesure"
  output_path: ./_build
  version: 1.0.0

receivers:
  - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.116.0

processors:
  - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.116.0
  - gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.116.0

exporters:
  - gomod: go.opentelemetry.io/collector/exporter/otlpexporter v0.116.0
  - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.116.0

extensions:
  - gomod: go.opentelemetry.io/collector/extension/healthcheckextension v0.116.0

Chaque entrée référence un module Go (gomod) avec sa version. C’est aussi simple que ça : vous n’incluez que ce que vous déclarez.

Installer OCB : deux méthodes

Il existe deux façons d’obtenir le binaire ocb. Dans les deux cas, une chaîne d’outils Go reste nécessaire : OCB génère du code Go puis orchestre un go build pour produire le Collector final.

Méthode 1 — go install (compilation locale)

Si vous avez déjà Go sous la main, la commande la plus directe est :

go install go.opentelemetry.io/collector/cmd/builder@latest

Pratique en local, mais elle recompile OCB et installe « la dernière version » — deux choses peu souhaitables en CI, où l’on veut une version épinglée et des builds reproductibles.

Méthode 2 — télécharger le binaire pré-compilé (recommandé en CI)

Le projet opentelemetry-collector-releases publie OCB sous forme de binaires pré-compilés pour chaque OS/arch. On télécharge alors directement la version voulue, sans recompiler OCB :

OCB_VERSION=0.116.0
GOOS=$(go env GOOS)
GOARCH=$(go env GOARCH)

curl --proto '=https' --tlsv1.2 -fL -o ocb \
  "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/cmd%2Fbuilder%2Fv${OCB_VERSION}/ocb_${OCB_VERSION}_${GOOS}_${GOARCH}"
chmod +x ocb

Avantages : la version est explicitement épinglée (OCB_VERSION), il n’y a rien à compiler pour obtenir OCB, et le téléchargement est rapide et identique d’un run à l’autre — exactement ce qu’on attend d’un pipeline.

Construire la distribution

Quelle que soit la méthode d’installation, le build se fait avec la même commande :

# Générer le code Go + compiler la distribution
./ocb --config builder-config.yaml

# (option) générer le code SANS compiler — utile pour l'inspecter
# ou déléguer la compilation à go build (cross-compilation, cache, etc.)
./ocb --skip-compilation --config builder-config.yaml

# Lancer le Collector produit avec votre configuration d'exécution
./_build/otelcol-custom --config otel-config.yaml

OCB lit le manifeste, génère le code Go, télécharge les dépendances et compile le binaire dans output_path. Le drapeau --skip-compilation est précieux en CI : on génère les sources une fois, puis on lance autant de go build que d’architectures cibles.

Industrialiser : manifeste partagé + manifeste local

Sur un parc avec plusieurs équipes, on ne veut pas dupliquer le même manifeste partout. Un pattern qui passe très bien à l’échelle consiste à scinder la description en deux fichiers :

  • un manifeste partagé (shared-manifest.yaml), maintenu par l’équipe plateforme : le socle commun imposé à toutes les distributions (extensions de santé, processors de limitation mémoire, composants internes maison…) ;
  • un manifeste local (local-manifest.yaml), propre à chaque équipe : le nom de la distribution, sa version et les quelques composants spécifiques.

Le manifeste partagé peut être templatisé avec des variables d’environnement, ce qui permet d’aligner d’un coup la version du Collector sur tous ses composants — et d’intégrer vos propres modules Go hébergés dans votre organisation :

# shared-manifest.yaml — socle commun, templatisé
dist:
  name: ${COLLECTOR_NAME:-otelcol-custom}
  version: ${COLLECTOR_VERSION:-0.0.1}
  output_path: _build

processors:
  - gomod: go.opentelemetry.io/collector/processor/batchprocessor v${OTEL_VERSION}
  - gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v${OTEL_VERSION}
  # composant interne, hébergé dans votre propre organisation
  - gomod: github.com/votre-org/otel-signalsreporterprocessor v0.0.8
    name: signalsreporterprocessor

extensions:
  - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension v${OTEL_VERSION}
# local-manifest.yaml — spécifique à une équipe
dist:
  name: my-custom-collector
  description: My custom collector implementation
  version: 0.1.1

exporters:
  - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v${OTEL_VERSION}

Les deux fichiers sont ensuite fusionnés (par exemple avec yq, en dédoublonnant chaque liste de composants), puis les variables sont résolues (avec envsubst) pour produire le builder-config.yaml final passé à OCB :

export OTEL_VERSION=0.116.0
export COLLECTOR_NAME=$(yq '.dist.name' local-manifest.yaml)
export COLLECTOR_VERSION=$(yq '.dist.version' local-manifest.yaml)

# Fusion socle + local, puis résolution des variables -> builder-config.yaml
yq eval-all 'select(fileIndex==0) *+ select(fileIndex==1)' \
  shared-manifest.yaml local-manifest.yaml | envsubst > builder-config.yaml

./ocb --config builder-config.yaml

Ce découpage donne à l’équipe plateforme la maîtrise du socle (sécurité, conformité, composants internes) tout en laissant chaque équipe libre d’ajouter ce dont elle a besoin. Le tout reste versionné dans Git, donc auditable et reproductible.

Reproductibilité et CI/CD

L’intérêt d’OCB ne s’arrête pas au build local. Comme le manifeste est un simple fichier versionné dans Git, il devient la source de vérité de votre distribution. Quelques bonnes pratiques :

  • Épinglez les versions. Utilisez exactement la même version pour le Collector core et tous les composants afin d’éviter les incompatibilités.
  • Intégrez le build dans votre CI. Générez et publiez votre image dans un pipeline, exactement comme n’importe quel autre artefact applicatif.
  • Scannez l’image. Profitez de la SBOM réduite pour faire passer un scanner de vulnérabilités sur une surface maîtrisée.

Un exemple de job GitHub Actions

Voici les étapes clés d’un job qui valide et compile la distribution à chaque push. On y retrouve le téléchargement d’OCB en version épinglée, un cache Go pour accélérer les builds, puis la génération et la compilation :

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: '1.25.7'

      # Cache des dépendances et de la compilation Go
      - name: Setup Go build cache
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: go-build-${{ runner.os }}-${{ hashFiles('builder-config.yaml', '**/go.sum') }}
          restore-keys: go-build-${{ runner.os }}-

      # Télécharger le binaire OCB pré-compilé, version épinglée
      - name: Install OCB
        env:
          OCB_VERSION: 0.116.0
        run: |
          curl --proto '=https' --tlsv1.2 -fL -o ocb \
            "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/cmd%2Fbuilder%2Fv${OCB_VERSION}/ocb_${OCB_VERSION}_linux_amd64"
          chmod +x ocb

      # Générer le code Go puis compiler la distribution
      - name: Build distribution
        run: ./ocb --config builder-config.yaml

Pour produire des images multi-architecture, on dissocie génération et compilation : OCB génère les sources une seule fois (--skip-compilation), puis on lance un go build statique par architecture cible avant de construire l’image :

      - name: Generate collector source code
        run: ./ocb --skip-compilation --config builder-config.yaml

      - name: Build static binaries per arch
        run: |
          for arch in amd64 arm64; do
            mkdir -p "container_binaries/$arch"
            ( cd _build && \
              CGO_ENABLED=0 GOOS=linux GOARCH="$arch" \
                go build -trimpath -ldflags="-s -w" \
                -o "../container_binaries/$arch/otelcol" . )
          done

-trimpath et -ldflags="-s -w" retirent les chemins de build et les tables de symboles : le binaire est plus léger et plus reproductible. Avec CGO_ENABLED=0, il est entièrement statique — ce qui ouvre la porte à une image vraiment minimale (voir plus bas).

Voici à quoi ressemble la chaîne complète, du manifeste au runtime :

sequenceDiagram
    participant Dev as Développeur
    participant CI as Pipeline CI
    participant OCB as ocb
    participant Reg as Registry
    participant K8s as Kubernetes

    Dev->>CI: push builder-config.yaml
    CI->>OCB: ./ocb --config builder-config.yaml
    OCB-->>CI: binaire otelcol-custom
    CI->>Reg: docker push image sur-mesure
    Reg->>K8s: pull image
    K8s->>K8s: rollout du Collector

Une image minimale : FROM scratch

Comme le binaire produit est statique (CGO_ENABLED=0), on peut le glisser dans une image partant de scratch : aucun OS, aucun shell, aucun gestionnaire de paquets. Le seul ajout indispensable est le bundle de certificats CA (pour les exports en TLS) :

FROM alpine:3.21 AS certs
RUN apk --update add ca-certificates

FROM scratch
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --chmod=755 ./container_binaries/amd64/otelcol /otelcol

# Tourner en utilisateur non-root
USER 10001

# OTLP gRPC / HTTP
EXPOSE 4317 4318

ENTRYPOINT ["/otelcol"]
CMD ["--config", "/config/otel-config.yaml"]

Couplée à un manifeste réduit, cette approche donne une image de quelques dizaines de Mo, sans shell ni paquets superflus. La surface d’attaque se limite à votre seul binaire — exactement ce qu’attendent les équipes sécurité.

En pratique : par où commencer ?

  1. Listez vos besoins réels. Quels signaux collectez-vous (traces, métriques, logs) ? Vers quels backends exportez-vous ? Cette liste devient votre manifeste.
  2. Partez du strict nécessaire. Ajoutez les composants au fur et à mesure plutôt que de tout inclure « au cas où ».
  3. Versionnez le manifeste et automatisez le build dans votre CI.
  4. Mesurez. Comparez la taille d’image, l’empreinte mémoire et le temps de démarrage avant/après : les gains sont souvent spectaculaires.

Conclusion

OCB transforme l’OpenTelemetry Collector d’une boîte noire générique en un composant maîtrisé, léger et auditable, parfaitement adapté à votre contexte. C’est la différence entre subir une distribution et posséder la sienne — un réflexe d’ingénierie qui paie en sécurité, en coûts et en sérénité opérationnelle.

Si vous souhaitez auditer votre pipeline d’observabilité ou mettre en place une distribution sur-mesure du Collector, parlons-en.

Sylvain Germe

Expert OpenTelemetry freelance, basé à Lille. Il accompagne les équipes dans l'instrumentation, le tracing distribué et la réduction des coûts d'observabilité.

Retour au blog