-
Notifications
You must be signed in to change notification settings - Fork 5
feat(ngxdig): add Helm chart for the ngxdig eBPF diagnosis agent #315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d363a1f
feat(ngxdig): add Helm chart for the ngxdig eBPF diagnosis agent
jarvis9443 8283b9f
feat(ngxdig): pull the moving latest tag with imagePullPolicy Always
jarvis9443 c0578b0
docs(ngxdig): document pinning the DaemonSet to specific nodes
jarvis9443 5c7419f
feat(ngxdig): optional NodePort exposure and CAP_SYS_ADMIN for uprobe…
jarvis9443 287acac
feat(ngxdig): always create the debug-UI Service (drop the enable tog…
jarvis9443 490adf9
feat(ngxdig): support NodePort/externalTrafficPolicy for a node-IP ro…
jarvis9443 e48dc10
docs(ngxdig): scope UI port-forward to the release and node, note Ser…
jarvis9443 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Patterns to ignore when building packages. | ||
| # This supports shell glob matching, relative path matching, and | ||
| # negation (prefixed with !). Only one pattern per line. | ||
| .DS_Store | ||
| # Common VCS dirs | ||
| .git/ | ||
| .gitignore | ||
| .bzr/ | ||
| .bzrignore | ||
| .hg/ | ||
| .hgignore | ||
| .svn/ | ||
| # Common backup files | ||
| *.swp | ||
| *.bak | ||
| *.tmp | ||
| *.orig | ||
| *~ | ||
| # Various IDEs | ||
| .project | ||
| .idea/ | ||
| *.tmproj | ||
| .vscode/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| apiVersion: v2 | ||
| name: ngxdig | ||
| description: eBPF on-CPU flame graphs and guided CPU/memory/latency diagnosis for OpenResty/Nginx, deployed as a per-node collector | ||
| type: application | ||
| version: 0.1.0 | ||
| appVersion: "0.1.0" | ||
| keywords: | ||
| - ebpf | ||
| - profiling | ||
| - flamegraph | ||
| - openresty | ||
| - nginx | ||
| - apisix | ||
| - diagnostics | ||
| home: https://github.com/api7/ngx-flame | ||
| sources: | ||
| - https://github.com/api7/ngx-flame | ||
| maintainers: | ||
| - name: API7 | ||
| email: support@api7.ai | ||
| url: https://api7.ai |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # ngxdig | ||
|
|
||
|    | ||
|
|
||
| eBPF on-CPU flame graphs and guided CPU/memory/latency diagnosis for OpenResty/Nginx, deployed as a per-node collector | ||
|
|
||
| **Homepage:** <https://github.com/api7/ngx-flame> | ||
|
|
||
| ## Maintainers | ||
|
|
||
| | Name | Email | Url | | ||
| | ---- | ------ | --- | | ||
| | API7 | <support@api7.ai> | <https://api7.ai> | | ||
|
|
||
| ## Source Code | ||
|
|
||
| * <https://github.com/api7/ngx-flame> | ||
|
|
||
| ## Install | ||
|
|
||
| ```sh | ||
| helm repo add api7 https://charts.api7.ai | ||
| helm repo update | ||
|
|
||
| helm install ngxdig api7/ngxdig --namespace ngxdig --create-namespace | ||
| ``` | ||
|
|
||
| ngxdig is deployed as a DaemonSet — one collector Pod per node — so it can | ||
| profile the OpenResty/Nginx (and APISIX) worker processes running on that node. | ||
|
|
||
| ## Accessing the debug UI | ||
|
|
||
| The chart always creates a Service for the UI. Most deployments run a single | ||
| collector — one node, or the DaemonSet pinned to one node (see below) — so the | ||
| `ClusterIP` points at that one Pod. | ||
|
|
||
| Quick local access — port-forward to the collector Pod. Scope the selector to | ||
| this release, and on a multi-node install pick the Pod on the node you want with | ||
| a `spec.nodeName` field selector: | ||
|
|
||
| ```sh | ||
| NODE=<node-name> # from `kubectl get nodes`; omit the field selector if single-node | ||
| POD=$(kubectl -n ngxdig get pod \ | ||
| -l app.kubernetes.io/name=ngxdig,app.kubernetes.io/instance=ngxdig \ | ||
| --field-selector spec.nodeName=$NODE \ | ||
| -o jsonpath='{.items[0].metadata.name}') | ||
| kubectl -n ngxdig port-forward $POD 8080:8080 | ||
| # then open http://127.0.0.1:8080/ | ||
| ``` | ||
|
|
||
| Node-IP access — set the Service to `NodePort`; with `externalTrafficPolicy: | ||
| Local`, `<nodeIP>:<nodePort>` reaches the collector on that same node: | ||
|
|
||
| ```yaml | ||
| # values.yaml | ||
| service: | ||
| type: NodePort | ||
| externalTrafficPolicy: Local | ||
| # nodePort: 30080 # optional; auto-assigned when omitted | ||
| ``` | ||
|
|
||
| The UI is not a hardened multi-user service — keep it on a trusted network and | ||
| prefer a port-forward over a public ingress. Note the `ClusterIP` Service is not | ||
| node-specific: on a single-node or node-pinned install it points at the one | ||
| collector, but when the DaemonSet spans several nodes it load-balances across | ||
| collectors and each collector only sees its own node's processes — so target a | ||
| specific node's Pod (the `spec.nodeName` selector above) rather than the Service. | ||
|
|
||
| ## Run on specific node(s) only | ||
|
|
||
| Usually you only want a collector on the node running the workload you are | ||
| debugging, not every node. Restrict the DaemonSet with a `nodeSelector`; the | ||
| controller then creates a Pod only on matching nodes. | ||
|
|
||
| Pin to a single node by its built-in `kubernetes.io/hostname` label (the value | ||
| is the node name shown by `kubectl get nodes`): | ||
|
|
||
| ```sh | ||
| helm install ngxdig api7/ngxdig --namespace ngxdig --create-namespace \ | ||
| --set nodeSelector."kubernetes\.io/hostname"=<node-name> | ||
| ``` | ||
|
|
||
| To target a group of nodes, label them first and select by that label: | ||
|
|
||
| ```sh | ||
| kubectl label node <node-a> <node-b> ngxdig=enabled | ||
| ``` | ||
|
|
||
| ```yaml | ||
| # values.yaml | ||
| nodeSelector: | ||
| ngxdig: enabled | ||
| ``` | ||
|
|
||
| For rules that `nodeSelector` cannot express, use `affinity` instead. Note that | ||
| for a DaemonSet you should not set the Pod's `.spec.nodeName` directly — node | ||
| placement is owned by the DaemonSet controller, and `nodeSelector`/`affinity` | ||
| is the supported way to constrain it. | ||
|
|
||
| ## Values | ||
|
|
||
| | Key | Type | Default | Description | | ||
| |-----|------|---------|-------------| | ||
| | affinity | object | `{}` | Affinity rules for the DaemonSet Pods | | ||
| | containerPort | int | `8080` | Port the ngxdig UI listens on inside the container | | ||
| | extraArgs | list | `[]` | Extra arguments appended to the `ngxdig ui` command | | ||
| | extraEnvVars | list | `[]` | Extra environment variables for the collector container | | ||
| | extraVolumeMounts | list | `[]` | Extra volume mounts for the collector container | | ||
| | extraVolumes | list | `[]` | Extra volumes for the collector Pod | | ||
| | fullnameOverride | string | `""` | Override the fully qualified release name | | ||
| | hostBtf.enabled | bool | `true` | Mount the node's kernel BTF read-only. Required by the eBPF collector to resolve kernel types without DWARF | | ||
| | hostBtf.path | string | `"/sys/kernel/btf"` | Host path to the kernel BTF directory | | ||
| | hostPID | bool | `true` | Share the host PID namespace so the collector can see Nginx/OpenResty worker processes elsewhere on the node. Required to profile targets outside the collector's own Pod | | ||
| | image.pullPolicy | string | `"Always"` | Image pull policy. Defaults to Always because the image tag is the moving `latest`, so nodes must re-pull to pick up newer builds | | ||
| | image.repository | string | `"api7/ngxdig"` | ngxdig image repository | | ||
| | image.tag | string | `"latest"` | Image tag. Defaults to the chart appVersion when left empty | | ||
| | imagePullSecrets | list | `[]` | Image pull secrets for a private registry | | ||
| | livenessProbe | object | `{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":10,"periodSeconds":15}` | Liveness probe for the collector container | | ||
| | nameOverride | string | `""` | Override the chart name | | ||
| | nodeSelector | object | `{}` | Node selector for the DaemonSet Pods | | ||
| | output.path | string | `"/out"` | Directory inside the container where run artifacts are written | | ||
| | output.sizeLimit | string | `""` | sizeLimit for the artifact emptyDir volume. No limit when empty | | ||
| | podAnnotations | object | `{}` | Additional annotations for the Pods | | ||
| | podLabels | object | `{}` | Additional labels for the Pods | | ||
| | podSecurityContext | object | `{}` | Pod-level security context | | ||
| | readinessProbe | object | `{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":5,"periodSeconds":10}` | Readiness probe for the collector container | | ||
| | resources | object | `{}` | Resource requests and limits for the collector container | | ||
| | securityContext | object | `{"appArmorProfile":{"type":"Unconfined"},"capabilities":{"add":["BPF","PERFMON","SYS_PTRACE","SYS_ADMIN"]},"runAsUser":0,"seccompProfile":{"type":"Unconfined"}}` | Container security context. The defaults grant exactly what the eBPF collector needs: run as root, and the CAP_BPF / CAP_PERFMON / CAP_SYS_PTRACE / CAP_SYS_ADMIN capabilities with unconfined seccomp/AppArmor so BPF, perf events, ptrace, and the perf_uprobe PMU attachments used by `cpu-on` and `latency` are all permitted. CAP_SYS_ADMIN is the smallest addition that satisfies the uprobe perf_event check — `--privileged` is not required | | ||
| | service.annotations | object | `{}` | Service annotations | | ||
| | service.externalTrafficPolicy | string | `"Local"` | externalTrafficPolicy for NodePort/LoadBalancer. Local keeps traffic on the receiving node, so <nodeIP>:<nodePort> reaches that node's own collector instead of being load-balanced to another node | | ||
| | service.nodePort | string | `""` | Static node port for type NodePort. Auto-assigned when empty | | ||
| | service.port | int | `8080` | Service port | | ||
| | service.type | string | `"ClusterIP"` | Service type (ClusterIP, NodePort, or LoadBalancer) | | ||
| | serviceAccount.annotations | object | `{}` | Annotations to add to the ServiceAccount | | ||
| | serviceAccount.create | bool | `true` | Create a ServiceAccount for the collector | | ||
| | serviceAccount.name | string | `""` | Name of the ServiceAccount to use. Generated from the fullname when empty | | ||
| | tolerations | list | `[]` | Tolerations for the DaemonSet Pods (e.g. to also run on control-plane nodes) | | ||
| | updateStrategy | object | `{}` | Update strategy for the DaemonSet | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| {{ template "chart.header" . }} | ||
|
|
||
| {{ template "chart.badgesSection" . }} | ||
|
|
||
| {{ template "chart.description" . }} | ||
|
|
||
| {{ template "chart.homepageLine" . }} | ||
|
|
||
| {{ template "chart.maintainersSection" . }} | ||
|
|
||
| {{ template "chart.sourcesSection" . }} | ||
|
|
||
| ## Install | ||
|
|
||
| ```sh | ||
| helm repo add api7 https://charts.api7.ai | ||
| helm repo update | ||
|
|
||
| helm install ngxdig api7/ngxdig --namespace ngxdig --create-namespace | ||
| ``` | ||
|
|
||
| ngxdig is deployed as a DaemonSet — one collector Pod per node — so it can | ||
| profile the OpenResty/Nginx (and APISIX) worker processes running on that node. | ||
|
|
||
| ## Accessing the debug UI | ||
|
|
||
| The chart always creates a Service for the UI. Most deployments run a single | ||
| collector — one node, or the DaemonSet pinned to one node (see below) — so the | ||
| `ClusterIP` points at that one Pod. | ||
|
|
||
| Quick local access — port-forward to the collector Pod. Scope the selector to | ||
| this release, and on a multi-node install pick the Pod on the node you want with | ||
| a `spec.nodeName` field selector: | ||
|
|
||
| ```sh | ||
| NODE=<node-name> # from `kubectl get nodes`; omit the field selector if single-node | ||
| POD=$(kubectl -n ngxdig get pod \ | ||
| -l app.kubernetes.io/name=ngxdig,app.kubernetes.io/instance=ngxdig \ | ||
| --field-selector spec.nodeName=$NODE \ | ||
| -o jsonpath='{.items[0].metadata.name}') | ||
| kubectl -n ngxdig port-forward $POD 8080:8080 | ||
| # then open http://127.0.0.1:8080/ | ||
| ``` | ||
|
|
||
| Node-IP access — set the Service to `NodePort`; with `externalTrafficPolicy: | ||
| Local`, `<nodeIP>:<nodePort>` reaches the collector on that same node: | ||
|
|
||
| ```yaml | ||
| # values.yaml | ||
| service: | ||
| type: NodePort | ||
| externalTrafficPolicy: Local | ||
| # nodePort: 30080 # optional; auto-assigned when omitted | ||
| ``` | ||
|
|
||
| The UI is not a hardened multi-user service — keep it on a trusted network and | ||
| prefer a port-forward over a public ingress. Note the `ClusterIP` Service is not | ||
| node-specific: on a single-node or node-pinned install it points at the one | ||
| collector, but when the DaemonSet spans several nodes it load-balances across | ||
| collectors and each collector only sees its own node's processes — so target a | ||
| specific node's Pod (the `spec.nodeName` selector above) rather than the Service. | ||
|
|
||
| ## Run on specific node(s) only | ||
|
|
||
| Usually you only want a collector on the node running the workload you are | ||
| debugging, not every node. Restrict the DaemonSet with a `nodeSelector`; the | ||
| controller then creates a Pod only on matching nodes. | ||
|
|
||
| Pin to a single node by its built-in `kubernetes.io/hostname` label (the value | ||
| is the node name shown by `kubectl get nodes`): | ||
|
|
||
| ```sh | ||
| helm install ngxdig api7/ngxdig --namespace ngxdig --create-namespace \ | ||
| --set nodeSelector."kubernetes\.io/hostname"=<node-name> | ||
| ``` | ||
|
|
||
| To target a group of nodes, label them first and select by that label: | ||
|
|
||
| ```sh | ||
| kubectl label node <node-a> <node-b> ngxdig=enabled | ||
| ``` | ||
|
|
||
| ```yaml | ||
| # values.yaml | ||
| nodeSelector: | ||
| ngxdig: enabled | ||
| ``` | ||
|
|
||
| For rules that `nodeSelector` cannot express, use `affinity` instead. Note that | ||
| for a DaemonSet you should not set the Pod's `.spec.nodeName` directly — node | ||
| placement is owned by the DaemonSet controller, and `nodeSelector`/`affinity` | ||
| is the supported way to constrain it. | ||
|
|
||
| {{ template "chart.valuesSection" . }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Values used by chart-testing (`ct install`) to validate the chart on a | ||
| # single-node kind cluster. Tolerate any node taint so the DaemonSet Pod is | ||
| # guaranteed to schedule on the CI node. | ||
| tolerations: | ||
| - operator: Exists |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| ngxdig is installed as a DaemonSet — one collector Pod per node. | ||
|
|
||
| Each Pod runs the ngxdig debug UI and can profile the OpenResty/Nginx (and | ||
| APISIX) worker processes running on its own node. | ||
|
|
||
| 1. List the collector Pods and the node each one runs on: | ||
|
|
||
| kubectl get pods --namespace {{ .Release.Namespace }} \ | ||
| -l "app.kubernetes.io/name={{ include "ngxdig.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" \ | ||
| -o wide | ||
|
|
||
| 2. Open the debug UI for the node whose workload you want to inspect. The UI is | ||
| not a hardened multi-user service, so the recommended access is a | ||
| port-forward to the collector Pod on that exact node — no Service, nothing | ||
| exposed on the network: | ||
|
|
||
| export NODE=<node-name> # a node from step 1 / `kubectl get nodes` | ||
| export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} \ | ||
| -l "app.kubernetes.io/name={{ include "ngxdig.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" \ | ||
| --field-selector spec.nodeName=$NODE \ | ||
| -o jsonpath="{.items[0].metadata.name}") | ||
| kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:{{ .Values.containerPort }} | ||
|
|
||
| Then browse to http://127.0.0.1:8080/ , pick a worker PID, and run a | ||
| diagnostic action (profile / cpu-on / memsnap / latency). | ||
|
|
||
| The UI is also reachable through the Service | ||
| {{ include "ngxdig.fullname" . }}:{{ .Values.service.port }} (type {{ .Values.service.type }}). | ||
| {{- if eq .Values.service.type "NodePort" }} | ||
| With type NodePort{{ if eq (toString .Values.service.externalTrafficPolicy) "Local" }} and externalTrafficPolicy Local{{ end }}, reach a | ||
| node's own collector at <that-node-IP>:<nodePort>: | ||
|
|
||
| kubectl get svc --namespace {{ .Release.Namespace }} {{ include "ngxdig.fullname" . }} -o jsonpath="{.spec.ports[0].nodePort}" | ||
| {{- end }} | ||
|
|
||
| 3. Check host readiness for eBPF collection on a node: | ||
|
|
||
| kubectl --namespace {{ .Release.Namespace }} exec $POD_NAME -- ngxdig doctor | ||
|
|
||
| Requirements: eBPF collection needs the node to expose kernel BTF | ||
| ({{ .Values.hostBtf.path }}){{ if not .Values.hostBtf.enabled }} — currently disabled via hostBtf.enabled=false{{ end }}, and the | ||
| collector must keep CAP_BPF, CAP_PERFMON, CAP_SYS_PTRACE, and CAP_SYS_ADMIN with | ||
| unconfined seccomp/AppArmor (the chart defaults grant these; CAP_SYS_ADMIN is | ||
| needed for the perf_uprobe attachments in `cpu-on`/`latency`). If a diagnostic | ||
| fails before collecting, `ngxdig doctor` reports the exact fix. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.