Skip to content

Commit b485a2b

Browse files
authored
Merge pull request #4 from nishipy/otel
Add article on Red Hat build of OpenTelemetry
2 parents 739c954 + 3016767 commit b485a2b

File tree

3 files changed

+355
-0
lines changed

3 files changed

+355
-0
lines changed

Diff for: content/post/otel-openshift/index.md

+355
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
---
2+
title: "Red Hat build of OpenTelemetryで遊んでみた"
3+
date: 2024-05-12T00:00:00+09:00
4+
draft: false
5+
categories:
6+
- openshift
7+
tags:
8+
- openshift
9+
- kubernetes
10+
- opentelemetry
11+
image: telescope.jpg
12+
slug: otel-openshift
13+
---
14+
15+
Header Photo by [Matthew Ansley](https://unsplash.com/ja/@ansleycreative) on [Unsplash](https://unsplash.com/photos/black-telescope-during-day-time-8SjeH5pZbjw)
16+
17+
## OpenTelemetry
18+
19+
[OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/)(OTEL)は、CNCFのプロジェクトの1つです。Observabilityの重要性が語られ始めて久しい昨今ですが、OTELはクラウドネイティブなソフトウェアにおいてObsevabilityを実現するためのAPI、SDK、ツール群です。GitHubにたくさんの[関連リポジトリ](https://github.com/orgs/open-telemetry/repositories)があります。
20+
21+
メトリクス、ログ、トレースを合わせてテレメトリデータと呼んでいます。OpenTelemetryを使ソフトウェアを計装し、テレメトリの生成、収集、エクスポートが行えます。オープンソースかつベンダー(vendor neutral)に依存しない点も特長です。
22+
23+
### OpenTelemetry Collector
24+
25+
> ![](otel-collector.jpg)
26+
> https://opentelemetry.io/docs/collector/ より引用
27+
28+
OpenTelemetry Collectorは、さまざまなテレメトリデータを受信、処理、エクスポートするためのコンポーネントです。以下の図のように、Jeager、Prometheusなどからテレメトリデータを受信し、処理した後、バックエンドにデータを送信できます。これにより複数のエージェント/コレクターを管理・運用する必要がなくなります。
29+
30+
## Red Hat build of OpenTelemetry
31+
32+
UpstreamのOpenTelemetryプロジェクトに基づいた製品で、OpenShift上におけるOpenTelemetry Collector のデプロイと管理、さらにワークロードの計装の簡素化をサポートします。つまり、OTEL Collectorの導入や自動計装などを、OpenShiftクラスタおよびその上で動くワークロードに提供します。
33+
34+
OpenShiftでは、(OpenShiftらしく、)Operatorを使ってOpenTelemetry Collectorをデプロイします。具体的には、OperatorHubからRed Hat build of OpenTelemetry Operatorをインストールした後、`OpenTelemetryCollector` CRを作成します。`OpenTelemetryCollector` CRの `spec.onfig`には、主に以下の項目を設定します。
35+
36+
- **Receiver: データを取り込む。OTLP Receiver、Jaeger Receiver、Prometheus Receiver (Tech Preview)などのプロトコルがある**
37+
- **Processor:** データが受信されてからエクスポートされるまでの間のデータ処理。Batch Processor、Memory Limiter processorなどがある
38+
- **Exporter:** データを1つ以上のバックエンドや宛先に送信する。**OTLP exporter、OTLP HTTP exporter、Debug exporter、Prometheus exporterなどのプロトコルがサポートされる**
39+
40+
詳細は、[ドキュメント](https://docs.openshift.com/container-platform/4.14/observability/otel/otel-configuration-of-otel-collector.html#otel-collector-config-options_otel-configuration-of-otel-collector)を参照してください。
41+
42+
## Red Hat build of OpenTelemetryで遊ぶ
43+
44+
Red Hat build of OpenTelemetryおよびOTELがなにもわからなかったので、[Sidecar injectionを用いてOTEL Collectorをワークロードに挿入する方法](https://docs.openshift.com/container-platform/4.14/observability/otel/otel-sending-traces-and-metrics-to-otel-collector.html)を試してみました。サンプルアプリ(Go)とYAMLマニフェストたちは、[こちら](https://github.com/nishipy/go-otel-openshift)に置いておきます。今回使った環境は以下の通りです。
45+
46+
- OpenShift: v4.14.20
47+
- OpenTelemetry Operator: v0.93.0-3
48+
49+
### Red Hat build of OpenTelemetryのインストール
50+
51+
前述の通り、OperatorHubからRed Hat build of OpenTelemetry (OpenTelemetry Operator)をインストールします。[ドキュメント](https://docs.openshift.com/container-platform/4.14/observability/otel/otel-installing.html#installing-otel-by-using-the-cli_install-otel)に従って実行すればOKです。
52+
53+
```
54+
$ oc version
55+
Client Version: 4.12.10
56+
Kustomize Version: v4.5.7
57+
Server Version: 4.14.20
58+
Kubernetes Version: v1.27.11+ec42b99
59+
60+
$ oc get csv | grep opentelemetry-operator
61+
opentelemetry-operator.v0.93.0-3 Red Hat build of OpenTelemetry 0.93.0-3 opentelemetry-operator.v0.93.0-2 Succeeded
62+
```
63+
64+
### サンプルワークロード用コンテナイメージ作成
65+
66+
今回テレメトリデータをCollectorに送信するため、コンテナイメージを作成します。[こんな感じ](https://github.com/nishipy/go-otel-openshift/blob/main/cmd/sampleapp/main.go)でGoでOTELライブラリを使い計装してみました。
67+
68+
```go
69+
package main
70+
71+
import (
72+
"context"
73+
"log"
74+
"net/http"
75+
"time"
76+
77+
"go.opentelemetry.io/otel"
78+
"go.opentelemetry.io/otel/codes"
79+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
80+
"go.opentelemetry.io/otel/sdk/trace"
81+
)
82+
83+
func main() {
84+
ctx := context.Background()
85+
exp, err := otlptracegrpc.New(
86+
ctx,
87+
otlptracegrpc.WithInsecure(),
88+
)
89+
if err != nil {
90+
panic(err)
91+
}
92+
93+
tracerProvider := trace.NewTracerProvider(trace.WithBatcher(exp))
94+
defer func() {
95+
if err := tracerProvider.Shutdown(ctx); err != nil {
96+
panic(err)
97+
}
98+
}()
99+
otel.SetTracerProvider(tracerProvider)
100+
101+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
102+
tr := otel.Tracer("http-server")
103+
_, span := tr.Start(r.Context(), "handleRequest")
104+
defer span.End()
105+
106+
// Simulate some work
107+
time.Sleep(100 * time.Millisecond)
108+
span.SetStatus(codes.Ok, "Status is OK.")
109+
110+
w.Write([]byte("Hello, world!\n"))
111+
})
112+
113+
// Start the HTTP server
114+
port := ":8080"
115+
log.Printf("Starting server on port %s...", port)
116+
if err := http.ListenAndServe(port, nil); err != nil {
117+
log.Fatalf("failed to start server: %v", err)
118+
}
119+
}
120+
```
121+
122+
これを以下のようなDockerfileを使ってコンテナイメージをビルドします。ビルドしたものは、`ghcr.io/nishipy/go-otel-sample:latest` にも置いてあります。
123+
124+
```dockerfile
125+
FROM golang:1.21.3-alpine as builder
126+
WORKDIR /app
127+
COPY go.mod go.sum ./
128+
RUN go mod download go.opentelemetry.io/otel
129+
COPY ./cmd/sampleapp/*.go ./
130+
RUN go build -trimpath -ldflags="-w -s" -o "otel-sample"
131+
132+
FROM gcr.io/distroless/static-debian11
133+
COPY --from=builder /app/otel-sample /otel-sample
134+
CMD ["/otel-sample"]
135+
```
136+
137+
### サンプルワークロードのデプロイ
138+
139+
ビルドしたコンテナイメージを使って、ワークロードをデプロイし、`Route` を用いて公開してみましょう。まず、Deploymentを作成します。現時点では何の意味もないですが`sidecar.opentelemetry.io/inject: "true"` というアノテーションがポイントです。次節で作成する `OpenTelemetryCollector` CRによって追加されるAdmission Webhookがこのアノテーションを検知し、OTEL Collectorコンテナを挿入してくれます。
140+
141+
```yaml
142+
---
143+
apiVersion: v1
144+
kind: Namespace
145+
metadata:
146+
name: otel-sample
147+
---
148+
apiVersion: apps/v1
149+
kind: Deployment
150+
metadata:
151+
name: otel-sample-deployment
152+
namespace: otel-sample
153+
spec:
154+
serviceAccount: otel-collector-sidecar
155+
replicas: 1
156+
selector:
157+
matchLabels:
158+
app: otel-sample
159+
template:
160+
metadata:
161+
labels:
162+
app: otel-sample
163+
annotations:
164+
sidecar.opentelemetry.io/inject: "true"
165+
spec:
166+
containers:
167+
- name: otel-sample
168+
image: ghcr.io/nishipy/go-otel-sample:latest
169+
ports:
170+
- containerPort: 8080
171+
```
172+
173+
作成したPodはRouteで公開します。
174+
```yaml
175+
---
176+
apiVersion: v1
177+
kind: Service
178+
metadata:
179+
name: otel-sample-service
180+
namespace: otel-sample
181+
spec:
182+
selector:
183+
app: otel-sample
184+
ports:
185+
- protocol: TCP
186+
port: 8080
187+
targetPort: 8080
188+
type: ClusterIP
189+
---
190+
apiVersion: route.openshift.io/v1
191+
kind: Route
192+
metadata:
193+
name: otel-sample-route
194+
namespace: otel-sample
195+
spec:
196+
to:
197+
kind: Service
198+
name: otel-sample-service
199+
weight: 100
200+
port:
201+
targetPort: 8080
202+
```
203+
204+
これらのYAMLマニフェストを適用すると、以下のようにPodやRouteが作成されるはずです。
205+
206+
```
207+
$ oc get pod
208+
NAME READY STATUS RESTARTS AGE
209+
otel-sample-deployment-b8696df8d-7xkll 1/1 Running 0 39s
210+
$ oc get route
211+
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
212+
otel-sample-route otel-sample-route-otel-sample.apps.test.lab.local otel-sample-service 8080 None
213+
$ curl -I otel-sample-route-otel-sample.apps.test.lab.local
214+
HTTP/1.1 200 OK
215+
date: Mon, 06 May 2024 14:34:48 GMT
216+
content-length: 14
217+
content-type: text/plain; charset=utf-8
218+
set-cookie: 27c7a5203dbedf674a50081de44f4d21=f3ceec06cdf162006c6cd294f3279af3; path=/; HttpOnly
219+
```
220+
221+
## OpenTelemetry Collector の設定
222+
223+
[ドキュメント](https://docs.openshift.com/container-platform/4.14/observability/otel/otel-sending-traces-and-metrics-to-otel-collector.html#sending-traces-and-metrics-to-otel-collector-with-sidecar_otel-sending-traces-and-metrics-to-otel-collector)に従って、OpenTelemetry Collectorをサイドカーコンテナとして挿入しましょう。まず前段として、OTEL Collectorサイドカーコンテナのために必要な`ServiceAccount`および`ClusterRole`、`ClusterRoleBinding`を作成します。
224+
225+
```yaml
226+
---
227+
apiVersion: v1
228+
kind: ServiceAccount
229+
metadata:
230+
name: otel-collector-sidecar
231+
namespace: otel-sample
232+
---
233+
apiVersion: rbac.authorization.k8s.io/v1
234+
kind: ClusterRole
235+
metadata:
236+
name: otel-collector
237+
rules:
238+
- apiGroups: ["", "config.openshift.io"]
239+
resources: ["pods", "namespaces", "infrastructures", "infrastructures/status"]
240+
verbs: ["get", "watch", "list"]
241+
---
242+
apiVersion: rbac.authorization.k8s.io/v1
243+
kind: ClusterRoleBinding
244+
metadata:
245+
name: otel-collector
246+
subjects:
247+
- kind: ServiceAccount
248+
name: otel-collector-sidecar
249+
namespace: otel-sample
250+
roleRef:
251+
kind: ClusterRole
252+
name: otel-collector
253+
apiGroup: rbac.authorization.k8s.io
254+
```
255+
256+
次に、`OpenTelemetryCollector` CRを作成します。Export先にTempoのインスタンスを用意するのは骨が折れそうなので、今回は以下のようにします。(ちなみにドキュメントの設定をコピペしても、`service.pipelines` の設定が間違っていたり、serviceAccountを指定するインデントが間違っていたりして、エラーになると思うのでご注意ください)
257+
258+
```yaml
259+
apiVersion: opentelemetry.io/v1alpha1
260+
kind: OpenTelemetryCollector
261+
metadata:
262+
name: otel
263+
namespace: otel-sample
264+
spec:
265+
serviceAccount: otel-collector-sidecar
266+
mode: sidecar
267+
config: |
268+
receivers:
269+
otlp:
270+
protocols:
271+
grpc:
272+
http:
273+
processors:
274+
batch:
275+
memory_limiter:
276+
check_interval: 1s
277+
limit_percentage: 50
278+
spike_limit_percentage: 30
279+
resourcedetection:
280+
detectors: [openshift]
281+
timeout: 2s
282+
exporters:
283+
debug:
284+
verbosity: detailed
285+
service:
286+
pipelines:
287+
traces:
288+
receivers: [otlp]
289+
processors: [memory_limiter, resourcedetection, batch]
290+
exporters: [debug]
291+
metrics:
292+
receivers: [otlp]
293+
processors: [memory_limiter, resourcedetection, batch]
294+
exporters: [debug]
295+
```
296+
297+
`spec.mode: sidecar` を設定することにより、OTEL Collectorサイドカーコンテナを、`sidecar.opentelemetry.io/inject: "true"` アノテーションを持つPodに挿入します。
298+
299+
Receiverとしては、[OTLP Receiver](https://docs.openshift.com/container-platform/4.14/observability/otel/otel-configuration-of-otel-collector.html#otlp-receiver_otel-configuration-of-otel-collector)を使っています。これはGoで書いたサンプルコード内で、OTLPプロトコル(over gRPC)を使ってトレースを生成しているためです。また、Exporterには、[Debug Exporter](https://docs.openshift.com/container-platform/4.14/observability/otel/otel-troubleshooting.html#debug-exporter-to-stdout_otel-troubleshoot)を使います。その名の通り、デバッグ用にテレメトリデータを標準出力にエクスポートします。
300+
301+
YAMLマニフェストを適用すると、以下のように `mutating webhook` が見えるはずです。
302+
303+
```
304+
$ oc get mutatingwebhookconfigurations.admissionregistration.k8s.io -l olm.owner.namespace=openshift-opentelemetry-operator
305+
NAME WEBHOOKS AGE
306+
minstrumentation.kb.io-ddjq4 1 20d
307+
mopampbridge.kb.io-5hkkl 1 20d
308+
mopentelemetrycollector.kb.io-4bfwl 1 20d
309+
mpod.kb.io-7dmnz 1 20d
310+
```
311+
312+
## OpenTelemetry Collector sidecar の挿入
313+
314+
`mutating webhook` も用意できたので、実験用にデプロイしていたPodを再作成しましょう。Admission Webhookにより、OpenTelemetry Collectorがサイドカーコンテナとして追加されるはずです。Podを一旦削除すると再作成され、READY列が `2/2` になり、サイドカーコンテナが挿入されたことがわかります。準備完了です。
315+
316+
```
317+
$ oc get pod
318+
NAME READY STATUS RESTARTS AGE
319+
otel-sample-deployment-b8696df8d-7xkll 1/1 Running 0 8m31s
320+
$ oc delete pod otel-sample-deployment-b8696df8d-7xkll
321+
pod "otel-sample-deployment-b8696df8d-7xkll" deleted
322+
$ oc get pod
323+
NAME READY STATUS RESTARTS AGE
324+
otel-sample-deployment-b8696df8d-gc6hg 2/2 Running 0 37s
325+
```
326+
327+
試しにRoute経由でPodにcurlでアクセスした後、OpenTelemetry Collectorのサイドカーコンテナ(`otc-container`)を覗いてみると、テレメトリデータが表示されているのがわかります。
328+
329+
```
330+
$ curl -I otel-sample-route-otel-sample.apps.test.lab.local
331+
$ oc logs otel-sample-deployment-b8696df8d-gc6hg otc-container
332+
2024-05-06T14:45:02.667Z info TracesExporter {"kind": "exporter", "data_type": "traces", "name": "debug", "resource spans": 1, "spans": 1}
333+
2024-05-06T14:45:02.667Z info ResourceSpans #0
334+
Resource SchemaURL: https://opentelemetry.io/schemas/1.24.0
335+
Resource attributes:
336+
-> service.name: Str(unknown_service:otel-sample)
337+
-> telemetry.sdk.language: Str(go)
338+
-> telemetry.sdk.name: Str(opentelemetry)
339+
-> telemetry.sdk.version: Str(1.26.0)
340+
ScopeSpans #0
341+
ScopeSpans SchemaURL:
342+
InstrumentationScope http-server
343+
Span #0
344+
Trace ID : a1964374ea412edf92ee5d4f843071ab
345+
Parent ID :
346+
ID : 8fbada7e64abb4bf
347+
Name : handleRequest
348+
Kind : Internal
349+
Start time : 2024-05-06 14:45:01.285150513 +0000 UTC
350+
End time : 2024-05-06 14:45:01.385644776 +0000 UTC
351+
Status code : Ok
352+
Status message :
353+
{"kind": "exporter", "data_type": "traces", "name": "debug"}
354+
...
355+
```

Diff for: content/post/otel-openshift/otel-collector.jpg

92 KB
Loading

Diff for: content/post/otel-openshift/telescope.jpg

2.85 MB
Loading

0 commit comments

Comments
 (0)