From 1632b59d77c0e915ac10fc26690d3dff1ebccaca Mon Sep 17 00:00:00 2001 From: Jin Hase Date: Tue, 1 Sep 2020 23:03:08 +0900 Subject: [PATCH 01/32] Propose log tracking KEP --- .../20200901-log-tracking.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 keps/sig-instrumentation/20200901-log-tracking.md diff --git a/keps/sig-instrumentation/20200901-log-tracking.md b/keps/sig-instrumentation/20200901-log-tracking.md new file mode 100644 index 00000000000..b43578a7170 --- /dev/null +++ b/keps/sig-instrumentation/20200901-log-tracking.md @@ -0,0 +1,199 @@ +--- +title: Log tracking for K8s component log + +authors: + - "@hase1128" + - "@KobayashiD27" + - "@fenggw-fnst" + - "@zhijianli88" + - "@Hellcatlk" +owning-sig: sig-instrumentation +participating-sigs: +reviewers: + - @dashpole + - @serathius +approvers: + - @dashpole +editor: TBD +creation-date: 2020-09-01 +last-updated: 2020-09-01 +status: provisional +--- + +# Log tracking for K8s component log + +## Table of Contents + + + - [Summary](#summary) + - [Motivation](#motivation) + - [Use Case 1](#use-case-1) + - [Use Case 2](#use-case-2) + - [Goals](#goals) + - [Non-Goals](#non-goals) + - [Proposal](#proposal) + - [Logging metadata](#logging-metadata) + - [Prerequisite](#prerequisite) + - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) + - [Design of Mutating webhook](#design-of-mutating-webhook) + - [Design of ID propagation (controller)](#design-of-id-propagation-controller) + - [Test Plan](#test-plan) + - [Migration / Graduation Criteria](#migration--graduation-criteria) + - [Alpha](#alpha) + - [Beta](#beta) + - [GA](#ga) + + + +## Summary + +This KEP proposes a method for adding new three unique logging meta-data into K8s component logs. +It makes us more easy to identify specific logs related to an user request (such as `kubectl apply`) and object (such as Pod, Deployment). +It is expected to reduce investigation cost greatly when trouble shoothing. + +### New three unique logging meta-data + +We use three meta-data. These meta-data have different features and are used for troubleshooting from different perspectives. + +| meta-data name | feature | +| ------ | ------ | +| trace-id | spans an user request. unique for user's request | +| span-id | spans a controller action. unique for controller action | +| initial-trace-id | spans the entire object lifecycle. unique for related objects | + +### Note + +This KEP is **how** a component could add meta-data to logs. To actually add meta-data to K8s component logs, the following procedure is necessary in addition. +- Open issues for each component, and discuss them with the SIGs that own that component. +- After get agreement, utilize this KEP's feature to change the source code that outputs log to add meta-data into these logs. +Please note that this KEP alone does not change the log format(does not add meta-data to logs). + +## Motivation + +Tracking logs among each Kubernetes component related to specific an user operation and objects is very tough work. +It is necessary to match logs by basically using timestamps and object's name as hints. +If multiple users throw many API requests at the same time, it is very difficult to track logs across each Kubernetes component log. + +### Use Case 1 + +Suspicious user operation(e.g. unknown pod operations) or cluster processing(e.g. unexpected pod migration to another node) is detected. +Users want to get their mind around the whole picture and root cause. +As part of the investigation, it may be necessary to scrutinize the relevant logs of each component in order to figure out the series of cluster processing. +It takes long time to scrutinize the relevant logs without this log tracking feature, because component logs are independent of each other, and it is difficult to find related logs and link them. + +This is similar to the [Auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/), except for the following points. + + - Audit only collects information about http request sending and receiving in kube-apiserver, so it can't track internal work of each component. + - Audit logs can't be associated to logs related to user operation (kubectl operation), because auditID is different for each http request. + +### Use Case 2 + +Failed to attach PV to pod +Prerequisite: It has been confirmed that the PV has been created successfully. +In this case, the volume generation on the storage side is OK, and there is a possibility that the mount process to the container in the pod is NG. +In order to identify the cause, it is necessary to look for the problem area while checking the component (kubelet) log as well as the system side syslog and mount related settings. + +This log tracking feature is useful to identify the logs related to specific user operation and cluster processing, and can reduce investigation cost in such cases. + +### Summary of Cases + + - Given a component log(such as error log), find the API request that caused this (error) log. + - Given an API Request(such as suspicious API request), find the resulting component logs. + +### Goals + + - Implement method which propagates new logging meta-data among each K8s component + - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) + - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. + +### Non-Goals + + - Add new logging metadata into actual K8s component logs + - This task will be done by opening issues after completing this KEP + - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). + +## Proposal + +### Logging metadata + +We use three logging meta-data, and propagate them each K8s component by using OpenTelemetry. +OpenTelemetry has SpanContext which is used for propagation of K8s component. + +| meta-data name | feature | +| ------ | ------ | +| trace-id | We use SpanContext.TraceID as trace-id
trace-id spans an user request.
trace-id is unique for user's request | +| span-id | We use SpanContext.SpanID as span-id
span-id spans a controller action.
span-id is unique for controller action | +| initial-trace-id | We implement new id(InitialTraceID) to SpanContext
We use SpanContext.InitialTraceID as initial-trace-id
initial-trace-id spans the entire object lifecycle.
initial-trace-id is unique for related objects | + +All of three id's inception is from object creation and it dies with object deletion + +### Prerequisite +We need to consider three cases: +- Case1: Requests from kubectl that creating an object +- Case2: Requests from kubectl other than creating (e.g. updating, deleting) an object +- Case3: Requests from controllers + +The design below is based on the above three cases + +### Design of ID propagation (incoming request to webhook) + +**1. Incoming request to apiserver from kubectl or controller** +- For request from kubectl, request's header does not have trace-id, span-id or initial-trace-id +- For request from controller, request's header has trace-id, span-id and initial-trace-id + +**2. Preprocessing handler (othttp handler)** +2.1 Do othttp's original Extract(), and get SpanContext +- For request from kubectl, result is null (no trace-id, span-id, initial-trace-id) +- For request from controller we can get trace-id, span-id and initial-trace-id +2.2 Create/Update SpanContext +- For request from kubectl + - Since we don't get any SpanContext, do StartSpan() to start new trace (new trace-id and span-id) + - the new SpanContext will be saved in the request's context "r.ctx" +- For request from controller + - Since we get SpanContext, do StartSpanWithRemoteParent() to update the SpanContext (new span-id) + - the updated SpanContext will be saved in the request's context "r.ctx" + +**3. Creation handler** +3.1 do our new Extract() to get initial-trace-id from request header to a golang ctx +- For request from kubectl we can't get initial-trace-id +- For request from controller we can get initial-trace-id +3.2 get SpanContext from r.ctx to golang ctx + +Notice that in this creation handler, the request will be consumed, so we need golang ctx to carry our information for propagation in apiserver. + +**4. Make new request for sending to webhook** +4.1 call othttp's original Inject() to inject the trace-id and span-id from golang ctx to header +4.2 call our new Inject() to inject the initial-trace-id from golang ctx to header +- For request from kubectl we don't have initial-trace-id, so do nothing +- For request from controller we can do this + +the order above(4.1 and 4.2) does not matter + +### Design of Mutating webhook +check the request's header +- if there is initial-trace-id, add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from controller.) +- if there is no initial-trace-id, check the request's operation + - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) + - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) + +### Design of ID propagation (controller) +When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: +``` + ctx = traceutil.WithObject(ctx, objB) + err = r.KubeClient.CoreV1().Create(ctx, objA...) +``` +We do propagation across objects without adding traces to that components. + +### Test Plan +TBD + +### Migration / Graduation Criteria + +#### Alpha +TBD + +#### Beta +TBD + +#### GA +TBD From a407e86923c66388d33862f876876b7caa789213 Mon Sep 17 00:00:00 2001 From: Jin Hase Date: Tue, 8 Sep 2020 19:20:24 +0900 Subject: [PATCH 02/32] Update 20200901-log-tracking.md --- .../20200901-log-tracking.md | 197 ++++++++++++------ 1 file changed, 139 insertions(+), 58 deletions(-) diff --git a/keps/sig-instrumentation/20200901-log-tracking.md b/keps/sig-instrumentation/20200901-log-tracking.md index b43578a7170..0083c734912 100644 --- a/keps/sig-instrumentation/20200901-log-tracking.md +++ b/keps/sig-instrumentation/20200901-log-tracking.md @@ -1,50 +1,55 @@ ---- -title: Log tracking for K8s component log - -authors: - - "@hase1128" - - "@KobayashiD27" - - "@fenggw-fnst" - - "@zhijianli88" - - "@Hellcatlk" -owning-sig: sig-instrumentation -participating-sigs: -reviewers: - - @dashpole - - @serathius -approvers: - - @dashpole -editor: TBD -creation-date: 2020-09-01 -last-updated: 2020-09-01 -status: provisional ---- - -# Log tracking for K8s component log - -## Table of Contents +# KEP-1961: Log tracking for K8s component log - - [Summary](#summary) - - [Motivation](#motivation) - - [Use Case 1](#use-case-1) - - [Use Case 2](#use-case-2) - - [Goals](#goals) - - [Non-Goals](#non-goals) - - [Proposal](#proposal) +- [Release Signoff Checklist](#release-signoff-checklist) +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) +- [Proposal](#proposal) + - [User Stories (Optional)](#user-stories-optional) + - [Story 1](#story-1) + - [Story 2](#story-2) + - [Notes/Constraints/Caveats (Optional)](#notesconstraintscaveats-optional) + - [Risks and Mitigations](#risks-and-mitigations) +- [Design Details](#design-details) - [Logging metadata](#logging-metadata) - [Prerequisite](#prerequisite) - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) - [Design of Mutating webhook](#design-of-mutating-webhook) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) - - [Test Plan](#test-plan) - - [Migration / Graduation Criteria](#migration--graduation-criteria) - - [Alpha](#alpha) - - [Beta](#beta) - - [GA](#ga) - + - [Test Plan](#test-plan) + - [Graduation Criteria](#graduation-criteria) + - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) + - [Version Skew Strategy](#version-skew-strategy) +- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) + - [Feature Enablement and Rollback](#feature-enablement-and-rollback) + - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) + - [Monitoring Requirements](#monitoring-requirements) + - [Dependencies](#dependencies) + - [Scalability](#scalability) + - [Troubleshooting](#troubleshooting) +- [Implementation History](#implementation-history) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) +- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) +## Release Signoff Checklist + +Items marked with (R) are required *prior to targeting to a milestone / release*. + +- [ ] (R) Enhancement issue in release milestone, which links to KEP dir in [kubernetes/enhancements] (not the initial KEP PR) +- [ ] (R) KEP approvers have approved the KEP status as `implementable` +- [ ] (R) Design details are appropriately documented +- [ ] (R) Test plan is in place, giving consideration to SIG Architecture and SIG Testing input +- [ ] (R) Graduation criteria is in place +- [ ] (R) Production readiness review completed +- [ ] Production readiness review approved +- [ ] "Implementation History" section is up-to-date for milestone +- [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io] +- [ ] Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes + ## Summary This KEP proposes a method for adding new three unique logging meta-data into K8s component logs. @@ -74,7 +79,34 @@ Tracking logs among each Kubernetes component related to specific an user operat It is necessary to match logs by basically using timestamps and object's name as hints. If multiple users throw many API requests at the same time, it is very difficult to track logs across each Kubernetes component log. -### Use Case 1 +### Goals + + - Implement method which propagates new logging meta-data among each K8s component + - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) + - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. + +### Non-Goals + + - Add new logging metadata into actual K8s component logs + - This task will be done by opening issues after completing this KEP + - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). + +## Proposal + + + +### User Stories (Optional) + + - Given a component log(such as error log), find the API request that caused this (error) log. + - Given an API Request(such as suspicious API request), find the resulting component logs. + +#### Story 1 Suspicious user operation(e.g. unknown pod operations) or cluster processing(e.g. unexpected pod migration to another node) is detected. Users want to get their mind around the whole picture and root cause. @@ -86,7 +118,7 @@ This is similar to the [Auditing](https://kubernetes.io/docs/tasks/debug-applica - Audit only collects information about http request sending and receiving in kube-apiserver, so it can't track internal work of each component. - Audit logs can't be associated to logs related to user operation (kubectl operation), because auditID is different for each http request. -### Use Case 2 +#### Story 2 Failed to attach PV to pod Prerequisite: It has been confirmed that the PV has been created successfully. @@ -95,24 +127,15 @@ In order to identify the cause, it is necessary to look for the problem area whi This log tracking feature is useful to identify the logs related to specific user operation and cluster processing, and can reduce investigation cost in such cases. -### Summary of Cases - - - Given a component log(such as error log), find the API request that caused this (error) log. - - Given an API Request(such as suspicious API request), find the resulting component logs. +### Notes/Constraints/Caveats (Optional) -### Goals +TBD - - Implement method which propagates new logging meta-data among each K8s component - - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) - - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. +### Risks and Mitigations -### Non-Goals - - - Add new logging metadata into actual K8s component logs - - This task will be done by opening issues after completing this KEP - - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). +TBD -## Proposal +## Design Details ### Logging metadata @@ -185,15 +208,73 @@ When controllers create/update/delete an object A based on another B, we propaga We do propagation across objects without adding traces to that components. ### Test Plan + TBD -### Migration / Graduation Criteria +### Graduation Criteria -#### Alpha TBD -#### Beta +#### Alpha -> Beta Graduation + TBD -#### GA +#### Beta -> GA Graduation + +TBD + +#### Removing a Deprecated Flag + +TBD + +### Upgrade / Downgrade Strategy + +TBD + +### Version Skew Strategy + +TBD + +## Production Readiness Review Questionnaire + +TBD + +### Feature Enablement and Rollback + +TBD + +### Rollout, Upgrade and Rollback Planning + +TBD + +### Monitoring Requirements + +TBD + +### Dependencies + +TBD + +### Scalability + +TBD + +### Troubleshooting + +TBD + +## Implementation History + +TBD + +## Drawbacks + +TBD + +## Alternatives + +TBD + +## Infrastructure Needed (Optional) + TBD From d2ec4924592361bc7c17d8ce5c98661196a85db0 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:09:29 +0900 Subject: [PATCH 03/32] Create README.md --- keps/1668-log-tracking/README.md | 706 +++++++++++++++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 keps/1668-log-tracking/README.md diff --git a/keps/1668-log-tracking/README.md b/keps/1668-log-tracking/README.md new file mode 100644 index 00000000000..79e58b6d617 --- /dev/null +++ b/keps/1668-log-tracking/README.md @@ -0,0 +1,706 @@ + +# Log tracking for K8s component log + + + + + + +- [Release Signoff Checklist](#release-signoff-checklist) +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) +- [Proposal](#proposal) + - [User Stories](#user-stories-optional) + - [Story 1](#story-1) + - [Story 2](#story-2) + - [Summary of cases](#summary-of-cases) + - [Logging metadata](#logging-metadata) + - [Risks and Mitigations](#risks-and-mitigations) +- [Design Details](#design-details) + - [Prerequisite](#prerequisite) + - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) + - [Design of Mutating webhook](#design-of-mutating-webhook) + - [Design of ID propagation (controller)](#design-of-id-propagation-controller) + - [Test Plan](#test-plan) + - [Graduation Criteria](#graduation-criteria) + - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) + - [Version Skew Strategy](#version-skew-strategy) +- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) + - [Feature Enablement and Rollback](#feature-enablement-and-rollback) + - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) + - [Monitoring Requirements](#monitoring-requirements) + - [Dependencies](#dependencies) + - [Scalability](#scalability) + - [Troubleshooting](#troubleshooting) +- [Implementation History](#implementation-history) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) +- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) + + +## Release Signoff Checklist + + + +Items marked with (R) are required *prior to targeting to a milestone / release*. + +- [ ] (R) Enhancement issue in release milestone, which links to KEP dir in [kubernetes/enhancements] (not the initial KEP PR) +- [ ] (R) KEP approvers have approved the KEP status as `implementable` +- [ ] (R) Design details are appropriately documented +- [ ] (R) Test plan is in place, giving consideration to SIG Architecture and SIG Testing input +- [ ] (R) Graduation criteria is in place +- [ ] (R) Production readiness review completed +- [ ] Production readiness review approved +- [ ] "Implementation History" section is up-to-date for milestone +- [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io] +- [ ] Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes + + + +[kubernetes.io]: https://kubernetes.io/ +[kubernetes/enhancements]: https://git.k8s.io/enhancements +[kubernetes/kubernetes]: https://git.k8s.io/kubernetes +[kubernetes/website]: https://git.k8s.io/website + +## Summary + + + +This KEP proposes a method for adding new three unique logging meta-data into K8s component logs. +It makes us more easy to identify specific logs related to an user request (such as `kubectl apply`) and object (such as Pod, Deployment). +It is expected to reduce investigation cost greatly when trouble shoothing. +### New three unique logging meta-data + +We use three meta-data. These meta-data have different features and are used for troubleshooting from different perspectives. + +| meta-data name | feature | +| ------ | ------ | +| trace-id | spans an user request. unique for user's request | +| span-id | spans a controller action. unique for controller action | +| initial-trace-id | spans the entire object lifecycle. unique for related objects | + +### Note + +This KEP is **how** a component could add meta-data to logs. To actually add meta-data to K8s component logs, the following procedure is necessary in addition. +- Open issues for each component, and discuss them with the SIGs that own that component. +- After get agreement, utilize this KEP's feature to change the source code that outputs log to add meta-data into these logs. +Please note that this KEP alone does not change the log format(does not add meta-data to logs). +## Motivation + + + +Tracking logs among each Kubernetes component related to specific an user operation and objects is very tough work. +It is necessary to match logs by basically using timestamps and object's name as hints. +If multiple users throw many API requests at the same time, it is very difficult to track logs across each Kubernetes component log. + + +### Goals + + + - Implement method which propagates new logging meta-data among each K8s component + - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) + - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. +### Non-Goals + + + - Add new logging metadata into actual K8s component logs + - This task will be done by opening issues after completing this KEP + - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). + +## Proposal + +### User Stories (Optional) + +#### Story 1 + +Suspicious user operation(e.g. unknown pod operations) or cluster processing(e.g. unexpected pod migration to another node) is detected. +Users want to get their mind around the whole picture and root cause. +As part of the investigation, it may be necessary to scrutinize the relevant logs of each component in order to figure out the series of cluster processing. +It takes long time to scrutinize the relevant logs without this log tracking feature, because component logs are independent of each other, and it is difficult to find related logs and link them. + +This is similar to the [Auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/), except for the following points. + + - Audit only collects information about http request sending and receiving in kube-apiserver, so it can't track internal work of each component. + - Audit logs can't be associated to logs related to user operation (kubectl operation), because auditID is different for each http request. + +#### Story 2 + +Failed to attach PV to pod +Prerequisite: It has been confirmed that the PV has been created successfully. +In this case, the volume generation on the storage side is OK, and there is a possibility that the mount process to the container in the pod is NG. +In order to identify the cause, it is necessary to look for the problem area while checking the component (kubelet) log as well as the system side syslog and mount related settings. + +This log tracking feature is useful to identify the logs related to specific user operation and cluster processing, and can reduce investigation cost in such cases. +### Summary of Cases + + - Given a component log(such as error log), find the API request that caused this (error) log. + - Given an API Request(such as suspicious API request), find the resulting component logs. + +### Logging metadata + +We use three logging meta-data, and propagate them each K8s component by using OpenTelemetry. +OpenTelemetry has SpanContext which is used for propagation of K8s component. + +| meta-data name | feature | +| ------ | ------ | +| trace-id | We use SpanContext.TraceID as trace-id
trace-id spans an user request.
trace-id is unique for user's request | +| span-id | We use SpanContext.SpanID as span-id
span-id spans a controller action.
span-id is unique for controller action | +| initial-trace-id | We implement new id(InitialTraceID) to SpanContext
We use SpanContext.InitialTraceID as initial-trace-id
initial-trace-id spans the entire object lifecycle.
initial-trace-id is unique for related objects | + +All of three id's inception is from object creation and it dies with object deletion + + + +## Design Details + + +### Prerequisite +We need to consider three cases: +- Case1: Requests from kubectl that creating an object +- Case2: Requests from kubectl other than creating (e.g. updating, deleting) an object +- Case3: Requests from controllers + +The design below is based on the above three cases + +### Design of ID propagation (incoming request to webhook) + +**1. Incoming request to apiserver from kubectl or controller** +- For request from kubectl, request's header does not have trace-id, span-id or initial-trace-id +- For request from controller, request's header has trace-id, span-id and initial-trace-id + +**2. Preprocessing handler (othttp handler)** +2.1 Do othttp's original Extract(), and get SpanContext +- For request from kubectl, result is null (no trace-id, span-id, initial-trace-id) +- For request from controller we can get trace-id, span-id and initial-trace-id +2.2 Create/Update SpanContext +- For request from kubectl + - Since we don't get any SpanContext, do StartSpan() to start new trace (new trace-id and span-id) + - the new SpanContext will be saved in the request's context "r.ctx" +- For request from controller + - Since we get SpanContext, do StartSpanWithRemoteParent() to update the SpanContext (new span-id) + - the updated SpanContext will be saved in the request's context "r.ctx" + +**3. Creation handler** +3.1 do our new Extract() to get initial-trace-id from request header to a golang ctx +- For request from kubectl we can't get initial-trace-id +- For request from controller we can get initial-trace-id +3.2 get SpanContext from r.ctx to golang ctx + +Notice that in this creation handler, the request will be consumed, so we need golang ctx to carry our information for propagation in apiserver. + +**4. Make new request for sending to webhook** +4.1 call othttp's original Inject() to inject the trace-id and span-id from golang ctx to header +4.2 call our new Inject() to inject the initial-trace-id from golang ctx to header +- For request from kubectl we don't have initial-trace-id, so do nothing +- For request from controller we can do this + +the order above(4.1 and 4.2) does not matter + +### Design of Mutating webhook +check the request's header +- if there is initial-trace-id, add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from controller.) +- if there is no initial-trace-id, check the request's operation + - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) + - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) + +### Design of ID propagation (controller) +When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: +``` + ctx = traceutil.WithObject(ctx, objB) + err = r.KubeClient.CoreV1().Create(ctx, objA...) +``` +We do propagation across objects without adding traces to that components. + +### Risks and Mitigations + + +TBD +### Test Plan + + +TBD + +### Graduation Criteria + + + +TBD + +### Upgrade / Downgrade Strategy + + +TBD + +### Version Skew Strategy + + +TBD +## Production Readiness Review Questionnaire + + +TBD +### Feature Enablement and Rollback +TBD + +_This section must be completed when targeting alpha to a release._ + +* **How can this feature be enabled / disabled in a live cluster?** + - [ ] Feature gate (also fill in values in `kep.yaml`) + - Feature gate name: + - Components depending on the feature gate: + - [ ] Other + - Describe the mechanism: + - Will enabling / disabling the feature require downtime of the control + plane? + - Will enabling / disabling the feature require downtime or reprovisioning + of a node? (Do not assume `Dynamic Kubelet Config` feature is enabled). + +* **Does enabling the feature change any default behavior?** + Any change of default behavior may be surprising to users or break existing + automations, so be extremely careful here. + +* **Can the feature be disabled once it has been enabled (i.e. can we roll back + the enablement)?** + Also set `disable-supported` to `true` or `false` in `kep.yaml`. + Describe the consequences on existing workloads (e.g., if this is a runtime + feature, can it break the existing applications?). + +* **What happens if we reenable the feature if it was previously rolled back?** + +* **Are there any tests for feature enablement/disablement?** + The e2e framework does not currently support enabling or disabling feature + gates. However, unit tests in each component dealing with managing data, created + with and without the feature, are necessary. At the very least, think about + conversion tests if API types are being modified. + +### Rollout, Upgrade and Rollback Planning +TBD + +_This section must be completed when targeting beta graduation to a release._ + +* **How can a rollout fail? Can it impact already running workloads?** + Try to be as paranoid as possible - e.g., what if some components will restart + mid-rollout? + +* **What specific metrics should inform a rollback?** + +* **Were upgrade and rollback tested? Was the upgrade->downgrade->upgrade path tested?** + Describe manual testing that was done and the outcomes. + Longer term, we may want to require automated upgrade/rollback tests, but we + are missing a bunch of machinery and tooling and can't do that now. + +* **Is the rollout accompanied by any deprecations and/or removals of features, APIs, +fields of API types, flags, etc.?** + Even if applying deprecation policies, they may still surprise some users. + +### Monitoring Requirements +TBD + +_This section must be completed when targeting beta graduation to a release._ + +* **How can an operator determine if the feature is in use by workloads?** + Ideally, this should be a metric. Operations against the Kubernetes API (e.g., + checking if there are objects with field X set) may be a last resort. Avoid + logs or events for this purpose. + +* **What are the SLIs (Service Level Indicators) an operator can use to determine +the health of the service?** + - [ ] Metrics + - Metric name: + - [Optional] Aggregation method: + - Components exposing the metric: + - [ ] Other (treat as last resort) + - Details: + +* **What are the reasonable SLOs (Service Level Objectives) for the above SLIs?** + At a high level, this usually will be in the form of "high percentile of SLI + per day <= X". It's impossible to provide comprehensive guidance, but at the very + high level (needs more precise definitions) those may be things like: + - per-day percentage of API calls finishing with 5XX errors <= 1% + - 99% percentile over day of absolute value from (job creation time minus expected + job creation time) for cron job <= 10% + - 99,9% of /health requests per day finish with 200 code + +* **Are there any missing metrics that would be useful to have to improve observability +of this feature?** + Describe the metrics themselves and the reasons why they weren't added (e.g., cost, + implementation difficulties, etc.). + +### Dependencies +TBD + +_This section must be completed when targeting beta graduation to a release._ + +* **Does this feature depend on any specific services running in the cluster?** + Think about both cluster-level services (e.g. metrics-server) as well + as node-level agents (e.g. specific version of CRI). Focus on external or + optional services that are needed. For example, if this feature depends on + a cloud provider API, or upon an external software-defined storage or network + control plane. + + For each of these, fill in the following—thinking about running existing user workloads + and creating new ones, as well as about cluster-level services (e.g. DNS): + - [Dependency name] + - Usage description: + - Impact of its outage on the feature: + - Impact of its degraded performance or high-error rates on the feature: + + +### Scalability +TBD + +_For alpha, this section is encouraged: reviewers should consider these questions +and attempt to answer them._ + +_For beta, this section is required: reviewers must answer these questions._ + +_For GA, this section is required: approvers should be able to confirm the +previous answers based on experience in the field._ + +* **Will enabling / using this feature result in any new API calls?** + Describe them, providing: + - API call type (e.g. PATCH pods) + - estimated throughput + - originating component(s) (e.g. Kubelet, Feature-X-controller) + focusing mostly on: + - components listing and/or watching resources they didn't before + - API calls that may be triggered by changes of some Kubernetes resources + (e.g. update of object X triggers new updates of object Y) + - periodic API calls to reconcile state (e.g. periodic fetching state, + heartbeats, leader election, etc.) + +* **Will enabling / using this feature result in introducing new API types?** + Describe them, providing: + - API type + - Supported number of objects per cluster + - Supported number of objects per namespace (for namespace-scoped objects) + +* **Will enabling / using this feature result in any new calls to the cloud +provider?** + +* **Will enabling / using this feature result in increasing size or count of +the existing API objects?** + Describe them, providing: + - API type(s): + - Estimated increase in size: (e.g., new annotation of size 32B) + - Estimated amount of new objects: (e.g., new Object X for every existing Pod) + +* **Will enabling / using this feature result in increasing time taken by any +operations covered by [existing SLIs/SLOs]?** + Think about adding additional work or introducing new steps in between + (e.g. need to do X to start a container), etc. Please describe the details. + +* **Will enabling / using this feature result in non-negligible increase of +resource usage (CPU, RAM, disk, IO, ...) in any components?** + Things to keep in mind include: additional in-memory state, additional + non-trivial computations, excessive access to disks (including increased log + volume), significant amount of data sent and/or received over network, etc. + This through this both in small and large cases, again with respect to the + [supported limits]. + +### Troubleshooting +TBD + +The Troubleshooting section currently serves the `Playbook` role. We may consider +splitting it into a dedicated `Playbook` document (potentially with some monitoring +details). For now, we leave it here. + +_This section must be completed when targeting beta graduation to a release._ + +* **How does this feature react if the API server and/or etcd is unavailable?** + +* **What are other known failure modes?** + For each of them, fill in the following information by copying the below template: + - [Failure mode brief description] + - Detection: How can it be detected via metrics? Stated another way: + how can an operator troubleshoot without logging into a master or worker node? + - Mitigations: What can be done to stop the bleeding, especially for already + running user workloads? + - Diagnostics: What are the useful log messages and their required logging + levels that could help debug the issue? + Not required until feature graduated to beta. + - Testing: Are there any tests for failure mode? If not, describe why. + +* **What steps should be taken if SLOs are not being met to determine the problem?** + +[supported limits]: https://git.k8s.io/community//sig-scalability/configs-and-limits/thresholds.md +[existing SLIs/SLOs]: https://git.k8s.io/community/sig-scalability/slos/slos.md#kubernetes-slisslos + +## Implementation History +TBD + + +## Drawbacks +TBD + + +## Alternatives +TBD + + +## Infrastructure Needed (Optional) +TBD + From 0c24512b5744c54fca1aa95f7b82132ada9ae638 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:09:57 +0900 Subject: [PATCH 04/32] Create kep.yaml --- keps/1668-log-tracking/kep.yaml | 43 +++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 keps/1668-log-tracking/kep.yaml diff --git a/keps/1668-log-tracking/kep.yaml b/keps/1668-log-tracking/kep.yaml new file mode 100644 index 00000000000..d07087d41b8 --- /dev/null +++ b/keps/1668-log-tracking/kep.yaml @@ -0,0 +1,43 @@ +title: Log tracking for K8s component log +kep-number: 1668 +authors: + - "@hase1128" + - "@KobayashiD27" + - "@fenggw-fnst" + - "@zhijianli88" + - "@Hellcatlk" +owning-sig: sig-instrumentation +participating-sigs: +status: provisional +creation-date: 2020-09-01 +reviewers: + - @dashpole + - @serathius +approvers: + - @dashpole +prr-approvers: + - TBD +see-also: +replaces: + +# The target maturity stage in the current dev cycle for this KEP. +stage: alpha + +# The most recent milestone for which work toward delivery of this KEP has been +# done. This can be the current (upcoming) milestone, if it is being actively +# worked on. +latest-milestone: "v1.20" + +# The milestone at which this feature was, or is targeted to be, at each stage. +milestone: + alpha: "v1.20" + beta: "v1.21" + stable: "v1.24" + +# The following PRR answers are required at alpha release +# List the feature gate name and the components for which it must be enabled +feature-gates: +disable-supported: true + +# The following PRR answers are required at beta release +metrics: From 5d32367d96ee88110755af4f523bd1f114585a08 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:11:28 +0900 Subject: [PATCH 05/32] Rename keps/1668-log-tracking/README.md to keps/sig-instrumentation/1668-log-tracking/README.md --- keps/{ => sig-instrumentation}/1668-log-tracking/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename keps/{ => sig-instrumentation}/1668-log-tracking/README.md (100%) diff --git a/keps/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md similarity index 100% rename from keps/1668-log-tracking/README.md rename to keps/sig-instrumentation/1668-log-tracking/README.md From 13bb68203cee8b86ef944e1e889d985fe06d878b Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:12:03 +0900 Subject: [PATCH 06/32] Rename keps/1668-log-tracking/kep.yaml to keps/sig-instrumentation/1668-log-tracking/kep.yaml --- keps/{ => sig-instrumentation}/1668-log-tracking/kep.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename keps/{ => sig-instrumentation}/1668-log-tracking/kep.yaml (100%) diff --git a/keps/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml similarity index 100% rename from keps/1668-log-tracking/kep.yaml rename to keps/sig-instrumentation/1668-log-tracking/kep.yaml From b95257e3afa76a42d11ea31439a7dd0eab9ad26d Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:15:21 +0900 Subject: [PATCH 07/32] Delete 20200901-log-tracking.md --- .../20200901-log-tracking.md | 280 ------------------ 1 file changed, 280 deletions(-) delete mode 100644 keps/sig-instrumentation/20200901-log-tracking.md diff --git a/keps/sig-instrumentation/20200901-log-tracking.md b/keps/sig-instrumentation/20200901-log-tracking.md deleted file mode 100644 index 0083c734912..00000000000 --- a/keps/sig-instrumentation/20200901-log-tracking.md +++ /dev/null @@ -1,280 +0,0 @@ -# KEP-1961: Log tracking for K8s component log - - -- [Release Signoff Checklist](#release-signoff-checklist) -- [Summary](#summary) -- [Motivation](#motivation) - - [Goals](#goals) - - [Non-Goals](#non-goals) -- [Proposal](#proposal) - - [User Stories (Optional)](#user-stories-optional) - - [Story 1](#story-1) - - [Story 2](#story-2) - - [Notes/Constraints/Caveats (Optional)](#notesconstraintscaveats-optional) - - [Risks and Mitigations](#risks-and-mitigations) -- [Design Details](#design-details) - - [Logging metadata](#logging-metadata) - - [Prerequisite](#prerequisite) - - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) - - [Design of Mutating webhook](#design-of-mutating-webhook) - - [Design of ID propagation (controller)](#design-of-id-propagation-controller) - - [Test Plan](#test-plan) - - [Graduation Criteria](#graduation-criteria) - - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) - - [Version Skew Strategy](#version-skew-strategy) -- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) - - [Feature Enablement and Rollback](#feature-enablement-and-rollback) - - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) - - [Monitoring Requirements](#monitoring-requirements) - - [Dependencies](#dependencies) - - [Scalability](#scalability) - - [Troubleshooting](#troubleshooting) -- [Implementation History](#implementation-history) -- [Drawbacks](#drawbacks) -- [Alternatives](#alternatives) -- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) - - -## Release Signoff Checklist - -Items marked with (R) are required *prior to targeting to a milestone / release*. - -- [ ] (R) Enhancement issue in release milestone, which links to KEP dir in [kubernetes/enhancements] (not the initial KEP PR) -- [ ] (R) KEP approvers have approved the KEP status as `implementable` -- [ ] (R) Design details are appropriately documented -- [ ] (R) Test plan is in place, giving consideration to SIG Architecture and SIG Testing input -- [ ] (R) Graduation criteria is in place -- [ ] (R) Production readiness review completed -- [ ] Production readiness review approved -- [ ] "Implementation History" section is up-to-date for milestone -- [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io] -- [ ] Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes - -## Summary - -This KEP proposes a method for adding new three unique logging meta-data into K8s component logs. -It makes us more easy to identify specific logs related to an user request (such as `kubectl apply`) and object (such as Pod, Deployment). -It is expected to reduce investigation cost greatly when trouble shoothing. - -### New three unique logging meta-data - -We use three meta-data. These meta-data have different features and are used for troubleshooting from different perspectives. - -| meta-data name | feature | -| ------ | ------ | -| trace-id | spans an user request. unique for user's request | -| span-id | spans a controller action. unique for controller action | -| initial-trace-id | spans the entire object lifecycle. unique for related objects | - -### Note - -This KEP is **how** a component could add meta-data to logs. To actually add meta-data to K8s component logs, the following procedure is necessary in addition. -- Open issues for each component, and discuss them with the SIGs that own that component. -- After get agreement, utilize this KEP's feature to change the source code that outputs log to add meta-data into these logs. -Please note that this KEP alone does not change the log format(does not add meta-data to logs). - -## Motivation - -Tracking logs among each Kubernetes component related to specific an user operation and objects is very tough work. -It is necessary to match logs by basically using timestamps and object's name as hints. -If multiple users throw many API requests at the same time, it is very difficult to track logs across each Kubernetes component log. - -### Goals - - - Implement method which propagates new logging meta-data among each K8s component - - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) - - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. - -### Non-Goals - - - Add new logging metadata into actual K8s component logs - - This task will be done by opening issues after completing this KEP - - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). - -## Proposal - - - -### User Stories (Optional) - - - Given a component log(such as error log), find the API request that caused this (error) log. - - Given an API Request(such as suspicious API request), find the resulting component logs. - -#### Story 1 - -Suspicious user operation(e.g. unknown pod operations) or cluster processing(e.g. unexpected pod migration to another node) is detected. -Users want to get their mind around the whole picture and root cause. -As part of the investigation, it may be necessary to scrutinize the relevant logs of each component in order to figure out the series of cluster processing. -It takes long time to scrutinize the relevant logs without this log tracking feature, because component logs are independent of each other, and it is difficult to find related logs and link them. - -This is similar to the [Auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/), except for the following points. - - - Audit only collects information about http request sending and receiving in kube-apiserver, so it can't track internal work of each component. - - Audit logs can't be associated to logs related to user operation (kubectl operation), because auditID is different for each http request. - -#### Story 2 - -Failed to attach PV to pod -Prerequisite: It has been confirmed that the PV has been created successfully. -In this case, the volume generation on the storage side is OK, and there is a possibility that the mount process to the container in the pod is NG. -In order to identify the cause, it is necessary to look for the problem area while checking the component (kubelet) log as well as the system side syslog and mount related settings. - -This log tracking feature is useful to identify the logs related to specific user operation and cluster processing, and can reduce investigation cost in such cases. - -### Notes/Constraints/Caveats (Optional) - -TBD - -### Risks and Mitigations - -TBD - -## Design Details - -### Logging metadata - -We use three logging meta-data, and propagate them each K8s component by using OpenTelemetry. -OpenTelemetry has SpanContext which is used for propagation of K8s component. - -| meta-data name | feature | -| ------ | ------ | -| trace-id | We use SpanContext.TraceID as trace-id
trace-id spans an user request.
trace-id is unique for user's request | -| span-id | We use SpanContext.SpanID as span-id
span-id spans a controller action.
span-id is unique for controller action | -| initial-trace-id | We implement new id(InitialTraceID) to SpanContext
We use SpanContext.InitialTraceID as initial-trace-id
initial-trace-id spans the entire object lifecycle.
initial-trace-id is unique for related objects | - -All of three id's inception is from object creation and it dies with object deletion - -### Prerequisite -We need to consider three cases: -- Case1: Requests from kubectl that creating an object -- Case2: Requests from kubectl other than creating (e.g. updating, deleting) an object -- Case3: Requests from controllers - -The design below is based on the above three cases - -### Design of ID propagation (incoming request to webhook) - -**1. Incoming request to apiserver from kubectl or controller** -- For request from kubectl, request's header does not have trace-id, span-id or initial-trace-id -- For request from controller, request's header has trace-id, span-id and initial-trace-id - -**2. Preprocessing handler (othttp handler)** -2.1 Do othttp's original Extract(), and get SpanContext -- For request from kubectl, result is null (no trace-id, span-id, initial-trace-id) -- For request from controller we can get trace-id, span-id and initial-trace-id -2.2 Create/Update SpanContext -- For request from kubectl - - Since we don't get any SpanContext, do StartSpan() to start new trace (new trace-id and span-id) - - the new SpanContext will be saved in the request's context "r.ctx" -- For request from controller - - Since we get SpanContext, do StartSpanWithRemoteParent() to update the SpanContext (new span-id) - - the updated SpanContext will be saved in the request's context "r.ctx" - -**3. Creation handler** -3.1 do our new Extract() to get initial-trace-id from request header to a golang ctx -- For request from kubectl we can't get initial-trace-id -- For request from controller we can get initial-trace-id -3.2 get SpanContext from r.ctx to golang ctx - -Notice that in this creation handler, the request will be consumed, so we need golang ctx to carry our information for propagation in apiserver. - -**4. Make new request for sending to webhook** -4.1 call othttp's original Inject() to inject the trace-id and span-id from golang ctx to header -4.2 call our new Inject() to inject the initial-trace-id from golang ctx to header -- For request from kubectl we don't have initial-trace-id, so do nothing -- For request from controller we can do this - -the order above(4.1 and 4.2) does not matter - -### Design of Mutating webhook -check the request's header -- if there is initial-trace-id, add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from controller.) -- if there is no initial-trace-id, check the request's operation - - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) - - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) - -### Design of ID propagation (controller) -When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: -``` - ctx = traceutil.WithObject(ctx, objB) - err = r.KubeClient.CoreV1().Create(ctx, objA...) -``` -We do propagation across objects without adding traces to that components. - -### Test Plan - -TBD - -### Graduation Criteria - -TBD - -#### Alpha -> Beta Graduation - -TBD - -#### Beta -> GA Graduation - -TBD - -#### Removing a Deprecated Flag - -TBD - -### Upgrade / Downgrade Strategy - -TBD - -### Version Skew Strategy - -TBD - -## Production Readiness Review Questionnaire - -TBD - -### Feature Enablement and Rollback - -TBD - -### Rollout, Upgrade and Rollback Planning - -TBD - -### Monitoring Requirements - -TBD - -### Dependencies - -TBD - -### Scalability - -TBD - -### Troubleshooting - -TBD - -## Implementation History - -TBD - -## Drawbacks - -TBD - -## Alternatives - -TBD - -## Infrastructure Needed (Optional) - -TBD From bcb70de063c6adf2b46beb67f0d667e2a6a92666 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:19:30 +0900 Subject: [PATCH 08/32] Update kep.yaml --- keps/sig-instrumentation/1668-log-tracking/kep.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index d07087d41b8..43bb6314240 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -11,10 +11,10 @@ participating-sigs: status: provisional creation-date: 2020-09-01 reviewers: - - @dashpole - - @serathius + - "@dashpole" + - "@serathius" approvers: - - @dashpole + - "@dashpole" prr-approvers: - TBD see-also: From 805b0bd3838d341dc3323ca1e8d50ba4cb3470fe Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 18 Sep 2020 17:22:30 +0900 Subject: [PATCH 09/32] Update kep.yaml --- keps/sig-instrumentation/1668-log-tracking/kep.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index 43bb6314240..c183dd47f61 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -16,7 +16,6 @@ reviewers: approvers: - "@dashpole" prr-approvers: - - TBD see-also: replaces: From a9af67785c3891d677731a7f62f59611d5660bad Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 25 Sep 2020 16:21:01 +0800 Subject: [PATCH 10/32] update toc Signed-off-by: Li Zhijian --- keps/sig-instrumentation/1668-log-tracking/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 79e58b6d617..d2ead9426b8 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -79,21 +79,23 @@ tags, and then generate with `hack/update-toc.sh`. - [Release Signoff Checklist](#release-signoff-checklist) - [Summary](#summary) + - [New three unique logging meta-data](#new-three-unique-logging-meta-data) + - [Note](#note) - [Motivation](#motivation) - [Goals](#goals) - [Non-Goals](#non-goals) - [Proposal](#proposal) - - [User Stories](#user-stories-optional) + - [User Stories (Optional)](#user-stories-optional) - [Story 1](#story-1) - [Story 2](#story-2) - - [Summary of cases](#summary-of-cases) + - [Summary of Cases](#summary-of-cases) - [Logging metadata](#logging-metadata) - - [Risks and Mitigations](#risks-and-mitigations) - [Design Details](#design-details) - [Prerequisite](#prerequisite) - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) - [Design of Mutating webhook](#design-of-mutating-webhook) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) + - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) From 94d9f414befa2103d21d4addf01d0ca92c2f4935 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 25 Sep 2020 17:25:57 +0900 Subject: [PATCH 11/32] Update README.md --- .../1668-log-tracking/README.md | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 79e58b6d617..29292affe3e 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -79,36 +79,26 @@ tags, and then generate with `hack/update-toc.sh`. - [Release Signoff Checklist](#release-signoff-checklist) - [Summary](#summary) + - [New three unique logging meta-data](#new-three-unique-logging-meta-data) + - [Note](#note) - [Motivation](#motivation) - [Goals](#goals) - [Non-Goals](#non-goals) - [Proposal](#proposal) - - [User Stories](#user-stories-optional) + - [User Stories (Optional)](#user-stories-optional) - [Story 1](#story-1) - [Story 2](#story-2) - - [Summary of cases](#summary-of-cases) + - [Summary of Cases](#summary-of-cases) - [Logging metadata](#logging-metadata) - - [Risks and Mitigations](#risks-and-mitigations) - [Design Details](#design-details) - [Prerequisite](#prerequisite) - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) - [Design of Mutating webhook](#design-of-mutating-webhook) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) + - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) - - [Version Skew Strategy](#version-skew-strategy) -- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) - - [Feature Enablement and Rollback](#feature-enablement-and-rollback) - - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) - - [Monitoring Requirements](#monitoring-requirements) - - [Dependencies](#dependencies) - - [Scalability](#scalability) - - [Troubleshooting](#troubleshooting) -- [Implementation History](#implementation-history) -- [Drawbacks](#drawbacks) -- [Alternatives](#alternatives) -- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) ## Release Signoff Checklist From a0380c3da9d8b2dbb1d31a9b6aa34e9f159dde83 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 25 Sep 2020 17:36:32 +0900 Subject: [PATCH 12/32] Update README.md --- keps/sig-instrumentation/1668-log-tracking/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 29292affe3e..d2ead9426b8 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -99,6 +99,18 @@ tags, and then generate with `hack/update-toc.sh`. - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) + - [Version Skew Strategy](#version-skew-strategy) +- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) + - [Feature Enablement and Rollback](#feature-enablement-and-rollback) + - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) + - [Monitoring Requirements](#monitoring-requirements) + - [Dependencies](#dependencies) + - [Scalability](#scalability) + - [Troubleshooting](#troubleshooting) +- [Implementation History](#implementation-history) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) +- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) ## Release Signoff Checklist From c1f80f3019b9c93fec42b654d69523b53143a72f Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Sun, 27 Sep 2020 15:10:08 +0800 Subject: [PATCH 13/32] update design details Signed-off-by: Li Zhijian --- .../1668-log-tracking/README.md | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index d2ead9426b8..ee31eedacea 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -77,6 +77,7 @@ tags, and then generate with `hack/update-toc.sh`. --> + - [Release Signoff Checklist](#release-signoff-checklist) - [Summary](#summary) - [New three unique logging meta-data](#new-three-unique-logging-meta-data) @@ -278,6 +279,7 @@ change are understandable. This may include API specs (though not always required) or even code snippets. If there's any ambiguity about HOW your proposal will be implemented, this is the place to discuss them. --> + ### Prerequisite We need to consider three cases: - Case1: Requests from kubectl that creating an object @@ -286,35 +288,42 @@ We need to consider three cases: The design below is based on the above three cases -### Design of ID propagation (incoming request to webhook) +### Design of ID propagation in apiserver (incoming request to webhook) **1. Incoming request to apiserver from kubectl or controller** + - For request from kubectl, request's header does not have trace-id, span-id or initial-trace-id - For request from controller, request's header has trace-id, span-id and initial-trace-id **2. Preprocessing handler (othttp handler)** -2.1 Do othttp's original Extract(), and get SpanContext -- For request from kubectl, result is null (no trace-id, span-id, initial-trace-id) -- For request from controller we can get trace-id, span-id and initial-trace-id +2.1 Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)(), and get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext) + +- For request from kubectl, SpanContext is null +- For request from controller we can get a valid SpanContext(including trace-id and span-id). + 2.2 Create/Update SpanContext + - For request from kubectl - - Since we don't get any SpanContext, do StartSpan() to start new trace (new trace-id and span-id) - - the new SpanContext will be saved in the request's context "r.ctx" + - Since we don't get any SpanContext, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new trace-id and span-id) + - Chain the new SpanContext to request's context "r.ctx" - For request from controller - - Since we get SpanContext, do StartSpanWithRemoteParent() to update the SpanContext (new span-id) - - the updated SpanContext will be saved in the request's context "r.ctx" + - Since we are able to get SpanContext, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new span-id) + - Chain the updated SpanContext to request's context "r.ctx" **3. Creation handler** 3.1 do our new Extract() to get initial-trace-id from request header to a golang ctx -- For request from kubectl we can't get initial-trace-id -- For request from controller we can get initial-trace-id -3.2 get SpanContext from r.ctx to golang ctx + +- For request from kubectl we can't get initial-trace-id, chain a empty initial-trace-id to a golang ctx +- For request from controller we can get initial-trace-id, chain the initial-trace-id to a golang ctx + +3.2 get SpanContext from r.ctx(updated in above 2.2) to above 3.1 golang ctx Notice that in this creation handler, the request will be consumed, so we need golang ctx to carry our information for propagation in apiserver. -**4. Make new request for sending to webhook** -4.1 call othttp's original Inject() to inject the trace-id and span-id from golang ctx to header +**4. Make a new request sent to webhook** +4.1 call othttp's original [Inject](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Inject)() to inject the trace-id and span-id from golang ctx to header 4.2 call our new Inject() to inject the initial-trace-id from golang ctx to header + - For request from kubectl we don't have initial-trace-id, so do nothing - For request from controller we can do this @@ -327,13 +336,14 @@ check the request's header - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) -### Design of ID propagation (controller) -When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: +### Design of ID propagation (controller)When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: ``` ctx = traceutil.WithObject(ctx, objB) err = r.KubeClient.CoreV1().Create(ctx, objA...) ``` -We do propagation across objects without adding traces to that components. +`traceutil.WithObject(ctx, objB)` chains the trace-id, span-id, and initial-trace-id(if existed) to ctx. + +We do propagation across objects without starting/adding traces in those components. ### Risks and Mitigations @@ -349,6 +359,7 @@ How will UX be reviewed, and by whom? Consider including folks who also work outside the SIG or subproject. --> TBD + ### Test Plan - - [Release Signoff Checklist](#release-signoff-checklist) - [Summary](#summary) - [New three unique logging meta-data](#new-three-unique-logging-meta-data) @@ -93,9 +92,9 @@ tags, and then generate with `hack/update-toc.sh`. - [Logging metadata](#logging-metadata) - [Design Details](#design-details) - [Prerequisite](#prerequisite) - - [Design of ID propagation (incoming request to webhook)](#design-of-id-propagation-incoming-request-to-webhook) - - [Design of Mutating webhook](#design-of-mutating-webhook) + - [Design of ID propagation in apiserver](#design-of-id-propagation-in-apiserver) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) + - [Design of Mutating webhook(Out of tree)](#design-of-mutating-webhookout-of-tree) - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) @@ -288,62 +287,63 @@ We need to consider three cases: The design below is based on the above three cases -### Design of ID propagation in apiserver (incoming request to webhook) +The following picture show our design![design](./overview.png) + + + +we don't have any modifications in kubectl in this design. + +### Design of ID propagation in apiserver -**1. Incoming request to apiserver from kubectl or controller** +**1. Preprocessing handler (othttp handler)** -- For request from kubectl, request's header does not have trace-id, span-id or initial-trace-id -- For request from controller, request's header has trace-id, span-id and initial-trace-id +1.1 Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)() to get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext) -**2. Preprocessing handler (othttp handler)** -2.1 Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)(), and get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext) +- For request from kubectl, SpanContext is null, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new trace-id and span-id) +- For request from controller we can get a valid SpanContext(including trace-id and span-id), do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new span-id) -- For request from kubectl, SpanContext is null -- For request from controller we can get a valid SpanContext(including trace-id and span-id). +1.2 Chain SpanContext and initial-trace-id to "r.ctx" -2.2 Create/Update SpanContext +- we use r.ctx to propagate those IDs to next handler -- For request from kubectl - - Since we don't get any SpanContext, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new trace-id and span-id) - - Chain the new SpanContext to request's context "r.ctx" -- For request from controller - - Since we are able to get SpanContext, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new span-id) - - Chain the updated SpanContext to request's context "r.ctx" +**2. inittrace handler** -**3. Creation handler** -3.1 do our new Extract() to get initial-trace-id from request header to a golang ctx +Implement a new initrace handler to propagate the initial-trace-id. + +2.1 do our new Extract() to get initial-trace-id from request header - For request from kubectl we can't get initial-trace-id, chain a empty initial-trace-id to a golang ctx - For request from controller we can get initial-trace-id, chain the initial-trace-id to a golang ctx -3.2 get SpanContext from r.ctx(updated in above 2.2) to above 3.1 golang ctx +2.2 chain above golang cxt to r.ctx(updated in above 1.2) +- we use r.ctx to propagate those IDs to next handler -Notice that in this creation handler, the request will be consumed, so we need golang ctx to carry our information for propagation in apiserver. +**3. Make a new request sent to webhook** +- call othttp's original [Inject](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Inject)() to inject the SpanContext from r.ctx to header +- call our new Inject() to inject the initial-trace-id from r.ctx to header -**4. Make a new request sent to webhook** -4.1 call othttp's original [Inject](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Inject)() to inject the trace-id and span-id from golang ctx to header -4.2 call our new Inject() to inject the initial-trace-id from golang ctx to header +### Design of ID propagation (controller) +When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: +``` + ctx = traceutil.WithObject(ctx, objB) + err = r.KubeClient.CoreV1().Create(ctx, objA...) +``` +`traceutil.WithObject(ctx, objB)` chains the SpanContext and initial-trace-id(if existed) to ctx. + +We can do propagation across objects without starting/adding a new trace in those components. -- For request from kubectl we don't have initial-trace-id, so do nothing -- For request from controller we can do this +### Design of Mutating webhook(Out of tree) +**Extract SpanContext and initial-trace-id from request's header** -the order above(4.1 and 4.2) does not matter +**Update SpanContext and initial-trace-id to object** -### Design of Mutating webhook -check the request's header - if there is initial-trace-id, add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from controller.) + - if there is no initial-trace-id, check the request's operation - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) -### Design of ID propagation (controller)When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: -``` - ctx = traceutil.WithObject(ctx, objB) - err = r.KubeClient.CoreV1().Create(ctx, objA...) -``` -`traceutil.WithObject(ctx, objB)` chains the trace-id, span-id, and initial-trace-id(if existed) to ctx. - -We do propagation across objects without starting/adding traces in those components. +**Persist object to etcd** ### Risks and Mitigations diff --git a/keps/sig-instrumentation/1668-log-tracking/overview.png b/keps/sig-instrumentation/1668-log-tracking/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea17a7447a6b503895110c7c411497b8609349b GIT binary patch literal 51762 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 literal 0 HcmV?d00001 From b57ffd3e22dcce1c5ae0c9276f4f645619c6d812 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 29 Sep 2020 16:49:16 +0800 Subject: [PATCH 15/32] minor updates Signed-off-by: Li Zhijian --- .../1668-log-tracking/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 8520275d524..f068a6ddad1 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -318,19 +318,20 @@ Implement a new initrace handler to propagate the initial-trace-id. 2.2 chain above golang cxt to r.ctx(updated in above 1.2) - we use r.ctx to propagate those IDs to next handler -**3. Make a new request sent to webhook** +**3. Inject SpanContext and initial-trace to request header sent to webhook** + - call othttp's original [Inject](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Inject)() to inject the SpanContext from r.ctx to header - call our new Inject() to inject the initial-trace-id from r.ctx to header ### Design of ID propagation (controller) -When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: -``` - ctx = traceutil.WithObject(ctx, objB) - err = r.KubeClient.CoreV1().Create(ctx, objA...) -``` -`traceutil.WithObject(ctx, objB)` chains the SpanContext and initial-trace-id(if existed) to ctx. +**Extract SpanContext and initial-trace-id from annotation of object to golang ctx** + +**Propagate golang ctx through objects** + +**Inject SpanContext and initial-trace to request header sent to apiserver** -We can do propagation across objects without starting/adding a new trace in those components. +- extract SpanContext and initial-trace-id from golang ctx, +- inject them the header ### Design of Mutating webhook(Out of tree) **Extract SpanContext and initial-trace-id from request's header** From 2da5b28dd03ad415595ab880fa15a594f8e0cd2c Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Tue, 29 Sep 2020 17:50:17 +0800 Subject: [PATCH 16/32] Update KEP Signed-off-by: Guangwen Feng --- .../1668-log-tracking/README.md | 296 +++--------------- .../1668-log-tracking/kep.yaml | 8 +- 2 files changed, 39 insertions(+), 265 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index d2ead9426b8..78bbb8f20a3 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -98,8 +98,6 @@ tags, and then generate with `hack/update-toc.sh`. - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) - - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) - - [Version Skew Strategy](#version-skew-strategy) - [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) - [Feature Enablement and Rollback](#feature-enablement-and-rollback) - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) @@ -108,9 +106,6 @@ tags, and then generate with `hack/update-toc.sh`. - [Scalability](#scalability) - [Troubleshooting](#troubleshooting) - [Implementation History](#implementation-history) -- [Drawbacks](#drawbacks) -- [Alternatives](#alternatives) -- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) ## Release Signoff Checklist @@ -337,163 +332,36 @@ We do propagation across objects without adding traces to that components. ### Risks and Mitigations - TBD -### Test Plan - - -TBD +All added code will be covered by unit tests. ### Graduation Criteria - - -TBD - -### Upgrade / Downgrade Strategy - - -TBD - -### Version Skew Strategy +- Feedback about this feature is collected and addressed +- Enabled in Beta for at least two releases without complaints - -TBD ## Production Readiness Review Questionnaire - -TBD ### Feature Enablement and Rollback -TBD _This section must be completed when targeting alpha to a release._ * **How can this feature be enabled / disabled in a live cluster?** - - [ ] Feature gate (also fill in values in `kep.yaml`) - - Feature gate name: - - Components depending on the feature gate: + - [x] Feature gate (also fill in values in `kep.yaml`) + - Feature gate name: ObjectTraceIDs + - Components depending on the feature gate: kube-apiserver, kube-controller-manager - [ ] Other - Describe the mechanism: - Will enabling / disabling the feature require downtime of the control @@ -507,93 +375,59 @@ _This section must be completed when targeting alpha to a release._ * **Can the feature be disabled once it has been enabled (i.e. can we roll back the enablement)?** - Also set `disable-supported` to `true` or `false` in `kep.yaml`. - Describe the consequences on existing workloads (e.g., if this is a runtime - feature, can it break the existing applications?). + Yes. * **What happens if we reenable the feature if it was previously rolled back?** + Objects created during the rollback will have no initial-trace-id until they + are recreated. * **Are there any tests for feature enablement/disablement?** - The e2e framework does not currently support enabling or disabling feature - gates. However, unit tests in each component dealing with managing data, created - with and without the feature, are necessary. At the very least, think about - conversion tests if API types are being modified. + Unit test can ensure that the feature enablement/disablement is valid. ### Rollout, Upgrade and Rollback Planning -TBD _This section must be completed when targeting beta graduation to a release._ * **How can a rollout fail? Can it impact already running workloads?** - Try to be as paranoid as possible - e.g., what if some components will restart - mid-rollout? + The proposed ids will not be added or updated. It will not have impact + on running workloads. * **What specific metrics should inform a rollback?** + N/A * **Were upgrade and rollback tested? Was the upgrade->downgrade->upgrade path tested?** - Describe manual testing that was done and the outcomes. - Longer term, we may want to require automated upgrade/rollback tests, but we - are missing a bunch of machinery and tooling and can't do that now. + N/A * **Is the rollout accompanied by any deprecations and/or removals of features, APIs, fields of API types, flags, etc.?** - Even if applying deprecation policies, they may still surprise some users. + N/A ### Monitoring Requirements -TBD _This section must be completed when targeting beta graduation to a release._ * **How can an operator determine if the feature is in use by workloads?** - Ideally, this should be a metric. Operations against the Kubernetes API (e.g., - checking if there are objects with field X set) may be a last resort. Avoid - logs or events for this purpose. + It can be determined by checking objects annotation with proposed ids. * **What are the SLIs (Service Level Indicators) an operator can use to determine the health of the service?** - - [ ] Metrics - - Metric name: - - [Optional] Aggregation method: - - Components exposing the metric: - - [ ] Other (treat as last resort) - - Details: + N/A * **What are the reasonable SLOs (Service Level Objectives) for the above SLIs?** - At a high level, this usually will be in the form of "high percentile of SLI - per day <= X". It's impossible to provide comprehensive guidance, but at the very - high level (needs more precise definitions) those may be things like: - - per-day percentage of API calls finishing with 5XX errors <= 1% - - 99% percentile over day of absolute value from (job creation time minus expected - job creation time) for cron job <= 10% - - 99,9% of /health requests per day finish with 200 code + N/A * **Are there any missing metrics that would be useful to have to improve observability of this feature?** - Describe the metrics themselves and the reasons why they weren't added (e.g., cost, - implementation difficulties, etc.). + N/A ### Dependencies -TBD _This section must be completed when targeting beta graduation to a release._ * **Does this feature depend on any specific services running in the cluster?** - Think about both cluster-level services (e.g. metrics-server) as well - as node-level agents (e.g. specific version of CRI). Focus on external or - optional services that are needed. For example, if this feature depends on - a cloud provider API, or upon an external software-defined storage or network - control plane. - - For each of these, fill in the following—thinking about running existing user workloads - and creating new ones, as well as about cluster-level services (e.g. DNS): - - [Dependency name] - - Usage description: - - Impact of its outage on the feature: - - Impact of its degraded performance or high-error rates on the feature: - + N/A ### Scalability -TBD _For alpha, this section is encouraged: reviewers should consider these questions and attempt to answer them._ @@ -604,48 +438,28 @@ _For GA, this section is required: approvers should be able to confirm the previous answers based on experience in the field._ * **Will enabling / using this feature result in any new API calls?** - Describe them, providing: - - API call type (e.g. PATCH pods) - - estimated throughput - - originating component(s) (e.g. Kubelet, Feature-X-controller) - focusing mostly on: - - components listing and/or watching resources they didn't before - - API calls that may be triggered by changes of some Kubernetes resources - (e.g. update of object X triggers new updates of object Y) - - periodic API calls to reconcile state (e.g. periodic fetching state, - heartbeats, leader election, etc.) + N/A * **Will enabling / using this feature result in introducing new API types?** - Describe them, providing: - - API type - - Supported number of objects per cluster - - Supported number of objects per namespace (for namespace-scoped objects) + N/A * **Will enabling / using this feature result in any new calls to the cloud provider?** + N/A * **Will enabling / using this feature result in increasing size or count of the existing API objects?** - Describe them, providing: - - API type(s): - - Estimated increase in size: (e.g., new annotation of size 32B) - - Estimated amount of new objects: (e.g., new Object X for every existing Pod) + N/A * **Will enabling / using this feature result in increasing time taken by any operations covered by [existing SLIs/SLOs]?** - Think about adding additional work or introducing new steps in between - (e.g. need to do X to start a container), etc. Please describe the details. + N/A * **Will enabling / using this feature result in non-negligible increase of resource usage (CPU, RAM, disk, IO, ...) in any components?** - Things to keep in mind include: additional in-memory state, additional - non-trivial computations, excessive access to disks (including increased log - volume), significant amount of data sent and/or received over network, etc. - This through this both in small and large cases, again with respect to the - [supported limits]. + TBD ### Troubleshooting -TBD The Troubleshooting section currently serves the `Playbook` role. We may consider splitting it into a dedicated `Playbook` document (potentially with some monitoring @@ -654,55 +468,15 @@ details). For now, we leave it here. _This section must be completed when targeting beta graduation to a release._ * **How does this feature react if the API server and/or etcd is unavailable?** + The feature will be unavailable. * **What are other known failure modes?** - For each of them, fill in the following information by copying the below template: - - [Failure mode brief description] - - Detection: How can it be detected via metrics? Stated another way: - how can an operator troubleshoot without logging into a master or worker node? - - Mitigations: What can be done to stop the bleeding, especially for already - running user workloads? - - Diagnostics: What are the useful log messages and their required logging - levels that could help debug the issue? - Not required until feature graduated to beta. - - Testing: Are there any tests for failure mode? If not, describe why. + TBD * **What steps should be taken if SLOs are not being met to determine the problem?** - -[supported limits]: https://git.k8s.io/community//sig-scalability/configs-and-limits/thresholds.md -[existing SLIs/SLOs]: https://git.k8s.io/community/sig-scalability/slos/slos.md#kubernetes-slisslos + N/A ## Implementation History -TBD - -## Drawbacks -TBD - - -## Alternatives -TBD - - -## Infrastructure Needed (Optional) -TBD - +* 2020-09-01: KEP proposed +* 2020-09-28: PRR questionnaire updated diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index c183dd47f61..856fcd11ccc 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -29,13 +29,13 @@ latest-milestone: "v1.20" # The milestone at which this feature was, or is targeted to be, at each stage. milestone: - alpha: "v1.20" - beta: "v1.21" - stable: "v1.24" + alpha: "v1.21" + beta: "v1.22" + stable: "v1.25" # The following PRR answers are required at alpha release # List the feature gate name and the components for which it must be enabled -feature-gates: +feature-gates: ObjectTraceIDs disable-supported: true # The following PRR answers are required at beta release From bb1a4960e4634f5bf4fcb15670bd148a23a95670 Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Tue, 29 Sep 2020 18:12:58 +0800 Subject: [PATCH 17/32] Update README.md Signed-off-by: Guangwen Feng --- .../1668-log-tracking/README.md | 149 +----------------- 1 file changed, 4 insertions(+), 145 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 7defef3bb64..948aa323b04 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -1,81 +1,5 @@ - # Log tracking for K8s component log - - - - - [Release Signoff Checklist](#release-signoff-checklist) - [Summary](#summary) @@ -110,20 +34,6 @@ tags, and then generate with `hack/update-toc.sh`. ## Release Signoff Checklist - - Items marked with (R) are required *prior to targeting to a milestone / release*. - [ ] (R) Enhancement issue in release milestone, which links to KEP dir in [kubernetes/enhancements] (not the initial KEP PR) @@ -137,39 +47,12 @@ Items marked with (R) are required *prior to targeting to a milestone / release* - [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io] - [ ] Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes - - -[kubernetes.io]: https://kubernetes.io/ -[kubernetes/enhancements]: https://git.k8s.io/enhancements -[kubernetes/kubernetes]: https://git.k8s.io/kubernetes -[kubernetes/website]: https://git.k8s.io/website - ## Summary - - This KEP proposes a method for adding new three unique logging meta-data into K8s component logs. It makes us more easy to identify specific logs related to an user request (such as `kubectl apply`) and object (such as Pod, Deployment). It is expected to reduce investigation cost greatly when trouble shoothing. + ### New three unique logging meta-data We use three meta-data. These meta-data have different features and are used for troubleshooting from different perspectives. @@ -186,37 +69,21 @@ This KEP is **how** a component could add meta-data to logs. To actually add met - Open issues for each component, and discuss them with the SIGs that own that component. - After get agreement, utilize this KEP's feature to change the source code that outputs log to add meta-data into these logs. Please note that this KEP alone does not change the log format(does not add meta-data to logs). -## Motivation - +## Motivation Tracking logs among each Kubernetes component related to specific an user operation and objects is very tough work. It is necessary to match logs by basically using timestamps and object's name as hints. If multiple users throw many API requests at the same time, it is very difficult to track logs across each Kubernetes component log. - ### Goals - - Implement method which propagates new logging meta-data among each K8s component - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. + ### Non-Goals - - Add new logging metadata into actual K8s component logs - This task will be done by opening issues after completing this KEP - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). @@ -263,18 +130,10 @@ OpenTelemetry has SpanContext which is used for propagation of K8s component. All of three id's inception is from object creation and it dies with object deletion - - ## Design Details - - ### Prerequisite + We need to consider three cases: - Case1: Requests from kubectl that creating an object - Case2: Requests from kubectl other than creating (e.g. updating, deleting) an object From c5b2f7fc498636965ba10bfd502a099f7d803025 Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Tue, 29 Sep 2020 18:23:50 +0800 Subject: [PATCH 18/32] Update kep.yaml Signed-off-by: Guangwen Feng --- keps/sig-instrumentation/1668-log-tracking/kep.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index 856fcd11ccc..62cd2d4b443 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -25,7 +25,7 @@ stage: alpha # The most recent milestone for which work toward delivery of this KEP has been # done. This can be the current (upcoming) milestone, if it is being actively # worked on. -latest-milestone: "v1.20" +latest-milestone: "v1.21" # The milestone at which this feature was, or is targeted to be, at each stage. milestone: From 7dc0657f01f7674a69dbb23b1081d7d47bbc9c7a Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Wed, 30 Sep 2020 16:09:28 +0800 Subject: [PATCH 19/32] Fix Graduation Criteria and trivial format issue Signed-off-by: Guangwen Feng --- .../1668-log-tracking/README.md | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 948aa323b04..fb837b6b129 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -162,7 +162,7 @@ we don't have any modifications in kubectl in this design. **2. inittrace handler** -Implement a new initrace handler to propagate the initial-trace-id. +Implement a new inittrace handler to propagate the initial-trace-id. 2.1 do our new Extract() to get initial-trace-id from request header @@ -178,19 +178,21 @@ Implement a new initrace handler to propagate the initial-trace-id. - call our new Inject() to inject the initial-trace-id from r.ctx to header ### Design of ID propagation (controller) -**Extract SpanContext and initial-trace-id from annotation of object to golang ctx** -**Propagate golang ctx through objects** +**1. Extract SpanContext and initial-trace-id from annotation of object to golang ctx** -**Inject SpanContext and initial-trace to request header sent to apiserver** +**2. Propagate golang ctx through objects** + +**3. Inject SpanContext and initial-trace to request header sent to apiserver** - extract SpanContext and initial-trace-id from golang ctx, - inject them the header ### Design of Mutating webhook(Out of tree) -**Extract SpanContext and initial-trace-id from request's header** -**Update SpanContext and initial-trace-id to object** +**1. Extract SpanContext and initial-trace-id from request's header** + +**2. Update SpanContext and initial-trace-id to object** - if there is initial-trace-id, add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from controller.) @@ -198,7 +200,7 @@ Implement a new initrace handler to propagate the initial-trace-id. - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) -**Persist object to etcd** +**3. Persist object to etcd** ### Risks and Mitigations @@ -210,14 +212,17 @@ All added code will be covered by unit tests. ### Graduation Criteria -Alpha should provide basic functionality covered with tests described in this KEP. +#### Alpha + +- Feature covers 3 important workload objects: Deployment, Statefulset, Daemonset +- Related unit tests described in this KEP are completed -#### Alpha -> Beta Graduation +#### Beta -- Feature covers all important workload objects +- Feature covers other objects which not limited to ownerRef relationship - All necessary tests are completed -#### Beta -> GA Graduation +#### GA - Feedback about this feature is collected and addressed - Enabled in Beta for at least two releases without complaints From 1600a21e5bd19e5e2d0824b540903a9aaec85985 Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Wed, 30 Sep 2020 18:18:15 +0800 Subject: [PATCH 20/32] Fix default behavior change Signed-off-by: Guangwen Feng --- keps/sig-instrumentation/1668-log-tracking/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index fb837b6b129..15c4c8d2c21 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -245,8 +245,14 @@ _This section must be completed when targeting alpha to a release._ of a node? (Do not assume `Dynamic Kubelet Config` feature is enabled). * **Does enabling the feature change any default behavior?** - Any change of default behavior may be surprising to users or break existing - automations, so be extremely careful here. + + In apiserver, new request handlers added by this feature will generate + or update the trace information (ids), then the ids will be added to the + object's annotation by the webhook provided by this feature. + + In controller-manager, when sending request to apiserver, it will get the + ids from the referenced object's annotation and inject the ids into the + outgoing request header with the W3C format. * **Can the feature be disabled once it has been enabled (i.e. can we roll back the enablement)?** From c012204a770802bb21e532e0e0789a480fea3917 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 30 Sep 2020 18:30:46 +0800 Subject: [PATCH 21/32] update toc Signed-off-by: Li Zhijian --- keps/sig-instrumentation/1668-log-tracking/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 15c4c8d2c21..6f98bfad9a5 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -22,6 +22,9 @@ - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) + - [Alpha](#alpha) + - [Beta](#beta) + - [GA](#ga) - [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) - [Feature Enablement and Rollback](#feature-enablement-and-rollback) - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) From 429df62c78d020e801593ad0d91be9397c966fc0 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 30 Sep 2020 18:36:04 +0800 Subject: [PATCH 22/32] fix feature gate validation Signed-off-by: Li Zhijian --- keps/sig-instrumentation/1668-log-tracking/kep.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index 62cd2d4b443..a1ba8fb18a9 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -35,7 +35,10 @@ milestone: # The following PRR answers are required at alpha release # List the feature gate name and the components for which it must be enabled -feature-gates: ObjectTraceIDs +feature-gates: + - name: ObjectTraceIDs + components: + - kube-apiserver disable-supported: true # The following PRR answers are required at beta release From 41cef3e2dd083567ecd9705d7ae3d14f016314da Mon Sep 17 00:00:00 2001 From: Guangwen Feng Date: Wed, 30 Sep 2020 18:57:14 +0800 Subject: [PATCH 23/32] Fix kep.yaml Signed-off-by: Guangwen Feng --- keps/sig-instrumentation/1668-log-tracking/kep.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index a1ba8fb18a9..507c1069def 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -39,6 +39,7 @@ feature-gates: - name: ObjectTraceIDs components: - kube-apiserver + - kube-controller-manager disable-supported: true # The following PRR answers are required at beta release From 9692e647ae65456ab405e9f0d368020055d1ad77 Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Fri, 2 Oct 2020 16:10:43 +0900 Subject: [PATCH 24/32] Log kep update (#7) * change names of IDs * update the Design of ID propagation. --- .../1668-log-tracking/README.md | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 6f98bfad9a5..2bf3937af12 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -62,9 +62,9 @@ We use three meta-data. These meta-data have different features and are used for | meta-data name | feature | | ------ | ------ | -| trace-id | spans an user request. unique for user's request | -| span-id | spans a controller action. unique for controller action | -| initial-trace-id | spans the entire object lifecycle. unique for related objects | +| traceid | spans an user request. unique for user's request | +| spanid | spans a controller action. unique for controller action | +| initialtraceid | spans the entire object lifecycle. unique for related objects | ### Note @@ -83,7 +83,7 @@ If multiple users throw many API requests at the same time, it is very difficult - Implement method which propagates new logging meta-data among each K8s component - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) - - e.g. implement of initial-trace-id, adding trace-id to object annotation executed in mutating webhook, etc. + - e.g. implement of initialtraceid, adding traceid to object annotation executed in mutating webhook, etc. ### Non-Goals @@ -127,9 +127,9 @@ OpenTelemetry has SpanContext which is used for propagation of K8s component. | meta-data name | feature | | ------ | ------ | -| trace-id | We use SpanContext.TraceID as trace-id
trace-id spans an user request.
trace-id is unique for user's request | -| span-id | We use SpanContext.SpanID as span-id
span-id spans a controller action.
span-id is unique for controller action | -| initial-trace-id | We implement new id(InitialTraceID) to SpanContext
We use SpanContext.InitialTraceID as initial-trace-id
initial-trace-id spans the entire object lifecycle.
initial-trace-id is unique for related objects | +| traceid | We use SpanContext.TraceID as traceid
traceid spans an user request.
traceid is unique for user's request | +| spanid | We use SpanContext.SpanID as spanid
spanid spans a controller action.
spanid is unique for controller action | +| initialtraceid | We implement new id(InitialTraceID) to SpanContext
We use SpanContext.InitialTraceID as initialtraceid
initialtraceid spans the entire object lifecycle.
initialtraceid is unique for related objects | All of three id's inception is from object creation and it dies with object deletion @@ -156,21 +156,21 @@ we don't have any modifications in kubectl in this design. 1.1 Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)() to get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext) -- For request from kubectl, SpanContext is null, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new trace-id and span-id) -- For request from controller we can get a valid SpanContext(including trace-id and span-id), do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new span-id) +- For request from kubectl, SpanContext is null, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new traceid and spanid) +- For request from controller we can get a valid SpanContext(including traceid and spanid), do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new spanid) -1.2 Chain SpanContext and initial-trace-id to "r.ctx" +1.2 Chain SpanContext and initialtraceid to "r.ctx" - we use r.ctx to propagate those IDs to next handler **2. inittrace handler** -Implement a new inittrace handler to propagate the initial-trace-id. +Implement a new inittrace handler to propagate the initialtraceid. -2.1 do our new Extract() to get initial-trace-id from request header +2.1 do our new Extract() to get initialtraceid from request header -- For request from kubectl we can't get initial-trace-id, chain a empty initial-trace-id to a golang ctx -- For request from controller we can get initial-trace-id, chain the initial-trace-id to a golang ctx +- For request from kubectl we can't get initialtraceid, chain a empty initialtraceid to a golang ctx +- For request from controller we can get initialtraceid, chain the initialtraceid to a golang ctx 2.2 chain above golang cxt to r.ctx(updated in above 1.2) - we use r.ctx to propagate those IDs to next handler @@ -178,32 +178,42 @@ Implement a new inittrace handler to propagate the initial-trace-id. **3. Inject SpanContext and initial-trace to request header sent to webhook** - call othttp's original [Inject](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Inject)() to inject the SpanContext from r.ctx to header -- call our new Inject() to inject the initial-trace-id from r.ctx to header +- call our new Inject() to inject the initialtraceid from r.ctx to header ### Design of ID propagation (controller) -**1. Extract SpanContext and initial-trace-id from annotation of object to golang ctx** +**1. Extract SpanContext and initialtraceid from annotation of object to golang ctx** **2. Propagate golang ctx through objects** +When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: +``` + ctx = traceutil.WithObject(ctx, objB) + err = r.KubeClient.CoreV1().Create(ctx, objA...) +``` +We do propagation across objects without adding traces to that components. + **3. Inject SpanContext and initial-trace to request header sent to apiserver** -- extract SpanContext and initial-trace-id from golang ctx, +- extract SpanContext and initialtraceid from golang ctx, - inject them the header ### Design of Mutating webhook(Out of tree) -**1. Extract SpanContext and initial-trace-id from request's header** +**1. Extract SpanContext and initialtraceid from request's header** + +**2. Update SpanContext and initialtraceid to object** + +- For traceid and spanid + +Always set the trace context annotation based on the incoming request. -**2. Update SpanContext and initial-trace-id to object** -- if there is initial-trace-id, add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from controller.) +- For initialtraceid -- if there is no initial-trace-id, check the request's operation - - if operation is create, copy the trace-id as initial-trace-id, and add trace-id, span-id and initial-trace-id to annotation (This is the case for requests from kubectl create.) - - if operation is not create, add trace-id, span-id to annotation (This is the case for requests from kubectl other than create.) +if initialtraceid is nil, set initialtraceid to traceid +else, leave initialtraceid as it is. -**3. Persist object to etcd** ### Risks and Mitigations @@ -262,7 +272,7 @@ _This section must be completed when targeting alpha to a release._ Yes. * **What happens if we reenable the feature if it was previously rolled back?** - Objects created during the rollback will have no initial-trace-id until they + Objects created during the rollback will have no initialtrace-id until they are recreated. * **Are there any tests for feature enablement/disablement?** From badbe6a15c35fb574ff198c9d8a03ad03d197f4c Mon Sep 17 00:00:00 2001 From: Kobayashi Daisuke Date: Mon, 5 Oct 2020 14:49:28 +0900 Subject: [PATCH 25/32] Log kep update (#9) * change trace-id to traceid * update the Design of ID propagation. * update the Design of ID propagation. * update a phrase. --- keps/sig-instrumentation/1668-log-tracking/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 2bf3937af12..6e45cb149b1 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -184,7 +184,14 @@ Implement a new inittrace handler to propagate the initialtraceid. **1. Extract SpanContext and initialtraceid from annotation of object to golang ctx** -**2. Propagate golang ctx through objects** +**2. Propagate golang ctx from objects to API Calls** + +When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: +``` + ctx = traceutil.WithObject(ctx, objB) + err = r.KubeClient.CoreV1().Create(ctx, objA...) +``` +We do propagation across objects without adding traces to that components. When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: ``` From 6fafbf0bd8665ae4f2f961d87719039d11af3114 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 14 Oct 2020 14:42:21 +0800 Subject: [PATCH 26/32] Zhijianli88/log tracking (#10) * a few updates - Use UID of root object as initialtraceid - add client-go changes - add Baggage to propagate initialtraceid - update overview Signed-off-by: Li Zhijian * add demo Signed-off-by: Li Zhijian --- .../1668-log-tracking/README.md | 65 ++++++++---------- .../1668-log-tracking/overview.png | Bin 51762 -> 42574 bytes 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 6e45cb149b1..dac2d42b51a 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -18,6 +18,7 @@ - [Prerequisite](#prerequisite) - [Design of ID propagation in apiserver](#design-of-id-propagation-in-apiserver) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) + - [Design of ID propagation in client-go](#design-of-id-propagation-in-client-go) - [Design of Mutating webhook(Out of tree)](#design-of-mutating-webhookout-of-tree) - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) @@ -123,13 +124,13 @@ This log tracking feature is useful to identify the logs related to specific use ### Logging metadata We use three logging meta-data, and propagate them each K8s component by using OpenTelemetry. -OpenTelemetry has SpanContext which is used for propagation of K8s component. +OpenTelemetry has SpanContext and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) which is used for propagation of K8s component. | meta-data name | feature | | ------ | ------ | | traceid | We use SpanContext.TraceID as traceid
traceid spans an user request.
traceid is unique for user's request | | spanid | We use SpanContext.SpanID as spanid
spanid spans a controller action.
spanid is unique for controller action | -| initialtraceid | We implement new id(InitialTraceID) to SpanContext
We use SpanContext.InitialTraceID as initialtraceid
initialtraceid spans the entire object lifecycle.
initialtraceid is unique for related objects | +| initialtraceid | We implement new id(InitialTraceID) to SpanContext
We use [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to propagate initialtraceid
We use UID of root object as initialtraceid
initialtraceid spans the entire object lifecycle.
initialtraceid is unique for related objects | All of three id's inception is from object creation and it dies with object deletion @@ -152,33 +153,16 @@ we don't have any modifications in kubectl in this design. ### Design of ID propagation in apiserver -**1. Preprocessing handler (othttp handler)** - -1.1 Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)() to get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext) +**1. Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)() to get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext)** - For request from kubectl, SpanContext is null, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new traceid and spanid) - For request from controller we can get a valid SpanContext(including traceid and spanid), do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new spanid) -1.2 Chain SpanContext and initialtraceid to "r.ctx" +**2. Chain SpanContext and initialtraceid to "r.ctx"** - we use r.ctx to propagate those IDs to next handler -**2. inittrace handler** - -Implement a new inittrace handler to propagate the initialtraceid. - -2.1 do our new Extract() to get initialtraceid from request header - -- For request from kubectl we can't get initialtraceid, chain a empty initialtraceid to a golang ctx -- For request from controller we can get initialtraceid, chain the initialtraceid to a golang ctx - -2.2 chain above golang cxt to r.ctx(updated in above 1.2) -- we use r.ctx to propagate those IDs to next handler - -**3. Inject SpanContext and initial-trace to request header sent to webhook** - -- call othttp's original [Inject](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Inject)() to inject the SpanContext from r.ctx to header -- call our new Inject() to inject the initialtraceid from r.ctx to header +**3. Make up a new outgoing [request](#design-of-id-progagation-in-client-go)** ### Design of ID propagation (controller) @@ -188,38 +172,41 @@ Implement a new inittrace handler to propagate the initialtraceid. When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: ``` - ctx = traceutil.WithObject(ctx, objB) + ctx = httptrace.SpanContextFromAnnotations(objB.GetAnnotations()) err = r.KubeClient.CoreV1().Create(ctx, objA...) ``` We do propagation across objects without adding traces to that components. -When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: -``` - ctx = traceutil.WithObject(ctx, objB) - err = r.KubeClient.CoreV1().Create(ctx, objA...) -``` -We do propagation across objects without adding traces to that components. +**3. Make up a new outgoing [request](#design-of-id-progagation-in-client-go)** -**3. Inject SpanContext and initial-trace to request header sent to apiserver** +### Design of ID propagation in [client-go](https://github.com/kubernetes/client-go) +client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io/otel/propagators#example-TraceContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to the outgoing http request header, something changes are like below: +```diff +@@ -868,6 +871,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp + } + req = req.WithContext(ctx) + req.Header = r.headers ++ propagator := otel.NewCompositeTextMapPropagator(propagators.TraceContext{}, propagators.Baggage{}) ++ propagator.Inject(ctx, req.Header) -- extract SpanContext and initialtraceid from golang ctx, -- inject them the header + r.backoff.Sleep(r.backoff.CalculateBackoff(r.URL())) + if retries > 0 { +``` +apiserver and controller use the API to make up a new outgoing request. ### Design of Mutating webhook(Out of tree) -**1. Extract SpanContext and initialtraceid from request's header** +**1.Extract SpanContext and initialtraceid from request's header ** -**2. Update SpanContext and initialtraceid to object** +**2.Update SpanContext and initialtraceid to object** -- For traceid and spanid +- For traceid and spanid and traceflags(sampled/not sampled) Always set the trace context annotation based on the incoming request. - - For initialtraceid -if initialtraceid is nil, set initialtraceid to traceid -else, leave initialtraceid as it is. +if initialtraceid is nil, Use the UID of object as initialtraceid, else, leave initialtraceid as it is. ### Risks and Mitigations @@ -381,3 +368,5 @@ _This section must be completed when targeting beta graduation to a release._ * 2020-09-01: KEP proposed * 2020-09-28: PRR questionnaire updated +* [Mutating admission webhook which injects trace context for demo](https://github.com/Hellcatlk/mutating-trace-admission-controller/tree/trace-ot) +* [Instrumentation of Kubernetes components for demo](https://github.com/Hellcatlk/kubernetes/tree/trace-ot) diff --git a/keps/sig-instrumentation/1668-log-tracking/overview.png b/keps/sig-instrumentation/1668-log-tracking/overview.png index 7ea17a7447a6b503895110c7c411497b8609349b..f3b3496912df510c8d10af3ab77c4901860cf373 100644 GIT binary patch literal 42574 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 literal 51762 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 From 5ccb15eeb062db240cd374ab5e25a4f602e44413 Mon Sep 17 00:00:00 2001 From: ZouYu Date: Wed, 21 Oct 2020 15:20:32 +0800 Subject: [PATCH 27/32] Update KEP (#11) - Design of ID propagation (controller) - Modified the parameters of the `SpanContextFromAnnotations` function - Design of ID propagation in client-go - Modify the function used by inject trace context and baggage - Implementation History - Update Instrumentation of Kubernetes components for demo's link Signed-off-by: zouyu --- keps/sig-instrumentation/1668-log-tracking/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index dac2d42b51a..bff838bf242 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -172,7 +172,7 @@ we don't have any modifications in kubectl in this design. When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: ``` - ctx = httptrace.SpanContextFromAnnotations(objB.GetAnnotations()) + ctx = httptrace.SpanContextFromAnnotations(context.Background(), objB.GetAnnotations()) err = r.KubeClient.CoreV1().Create(ctx, objA...) ``` We do propagation across objects without adding traces to that components. @@ -183,11 +183,11 @@ We do propagation across objects without adding traces to that components. client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io/otel/propagators#example-TraceContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to the outgoing http request header, something changes are like below: ```diff @@ -868,6 +871,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp - } req = req.WithContext(ctx) req.Header = r.headers -+ propagator := otel.NewCompositeTextMapPropagator(propagators.TraceContext{}, propagators.Baggage{}) -+ propagator.Inject(ctx, req.Header) + ++ props := otelhttptrace.WithPropagators(otel.NewCompositeTextMapPropagator(propagators.TraceContext{}, propagators.Baggage{})) ++ otelhttptrace.Inject(req.Context(), req, props) r.backoff.Sleep(r.backoff.CalculateBackoff(r.URL())) if retries > 0 { @@ -369,4 +369,4 @@ _This section must be completed when targeting beta graduation to a release._ * 2020-09-01: KEP proposed * 2020-09-28: PRR questionnaire updated * [Mutating admission webhook which injects trace context for demo](https://github.com/Hellcatlk/mutating-trace-admission-controller/tree/trace-ot) -* [Instrumentation of Kubernetes components for demo](https://github.com/Hellcatlk/kubernetes/tree/trace-ot) +* [Instrumentation of Kubernetes components for demo](https://github.com/Hellcatlk/kubernetes/pull/1) From 24ce1d1f84f12e59d832c24f4f593706749996a2 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 30 Oct 2020 08:37:38 +0800 Subject: [PATCH 28/32] reduce our work by refer to KEP647 (#12) * reduce our work by referring to KEP647 Signed-off-by: Li Zhijian * Update README.md Co-authored-by: Kobayashi Daisuke --- .../1668-log-tracking/README.md | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index bff838bf242..0f8832e2ee8 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -16,7 +16,7 @@ - [Logging metadata](#logging-metadata) - [Design Details](#design-details) - [Prerequisite](#prerequisite) - - [Design of ID propagation in apiserver](#design-of-id-propagation-in-apiserver) + - [API Server Tracing](#api-server-tracing) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) - [Design of ID propagation in client-go](#design-of-id-propagation-in-client-go) - [Design of Mutating webhook(Out of tree)](#design-of-mutating-webhookout-of-tree) @@ -151,24 +151,23 @@ The following picture show our design![design](./overview.png) we don't have any modifications in kubectl in this design. -### Design of ID propagation in apiserver +### API Server Tracing -**1. Do othttp's original [Extract](https://pkg.go.dev/go.opentelemetry.io/otel/api/propagators#TraceContext.Extract)() to get [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/api/trace#SpanContext)** +**1. refer to [KEP647](https://github.com/kubernetes/enhancements/issues/647)** -- For request from kubectl, SpanContext is null, do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to start new SpanContext (new traceid and spanid) -- For request from controller we can get a valid SpanContext(including traceid and spanid), do [Start](https://github.com/open-telemetry/opentelemetry-go/blob/3a9f5fe15f50a35ad8da5c5396a9ed3bbb82360c/sdk/trace/tracer.go#L38)() to update the SpanContext (new spanid) +KEP647 will help to -**2. Chain SpanContext and initialtraceid to "r.ctx"** - -- we use r.ctx to propagate those IDs to next handler +``` +Goals +The API Server generates and exports spans for incoming and outgoing requests. +The API Server propagates context from incoming requests to outgoing requests. +``` -**3. Make up a new outgoing [request](#design-of-id-progagation-in-client-go)** +**2. [Make up a new outgoing request](#design-of-id-progagation-in-client-go)** ### Design of ID propagation (controller) -**1. Extract SpanContext and initialtraceid from annotation of object to golang ctx** - -**2. Propagate golang ctx from objects to API Calls** +**1. Propagate golang ctx from objects to API Calls** When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: ``` @@ -177,10 +176,21 @@ When controllers create/update/delete an object A based on another B, we propaga ``` We do propagation across objects without adding traces to that components. -**3. Make up a new outgoing [request](#design-of-id-progagation-in-client-go)** +SpanContextFromAnnotations will do something like below pseudo code +``` +func SpanContextFromAnnotations(){ + // 1. decode annotation to SpanContext + // 2. generate Span from SpanContext + // 3. chain Span to ctx which is required by propagators.Inject +} +``` +**2. [Make up a new outgoing request](#design-of-id-progagation-in-client-go)** ### Design of ID propagation in [client-go](https://github.com/kubernetes/client-go) + + [KEP647](https://github.com/kubernetes/enhancements/issues/647) will do this work too, so we can refer to it directly in the future. client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io/otel/propagators#example-TraceContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to the outgoing http request header, something changes are like below: + ```diff @@ -868,6 +871,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp req = req.WithContext(ctx) @@ -192,11 +202,11 @@ client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io r.backoff.Sleep(r.backoff.CalculateBackoff(r.URL())) if retries > 0 { ``` -apiserver and controller use the API to make up a new outgoing request. +apiserver and controller use this API to make up a new outgoing request. ### Design of Mutating webhook(Out of tree) -**1.Extract SpanContext and initialtraceid from request's header ** +**1.Extract SpanContext and initialtraceid from request's header** **2.Update SpanContext and initialtraceid to object** From 693bee1a373a4b7c69966e9cd6fe8346252829d3 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 11 Nov 2020 15:17:21 +0800 Subject: [PATCH 29/32] update implementation history (#13) Signed-off-by: Li Zhijian --- .../1668-log-tracking/README.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 0f8832e2ee8..757188820bc 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -188,21 +188,10 @@ func SpanContextFromAnnotations(){ ### Design of ID propagation in [client-go](https://github.com/kubernetes/client-go) - [KEP647](https://github.com/kubernetes/enhancements/issues/647) will do this work too, so we can refer to it directly in the future. -client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io/otel/propagators#example-TraceContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to the outgoing http request header, something changes are like below: +[KEP647](https://github.com/kubernetes/enhancements/issues/647) will do this work too, so we can refer to it directly in the future. +client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io/otel/propagators#example-TraceContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to the outgoing http request header with API [RoundTrip](https://pkg.go.dev/go.opentelemetry.io/contrib/instrumentation/net/http#Transport.RoundTrip). -```diff -@@ -868,6 +871,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp - req = req.WithContext(ctx) - req.Header = r.headers - -+ props := otelhttptrace.WithPropagators(otel.NewCompositeTextMapPropagator(propagators.TraceContext{}, propagators.Baggage{})) -+ otelhttptrace.Inject(req.Context(), req, props) - - r.backoff.Sleep(r.backoff.CalculateBackoff(r.URL())) - if retries > 0 { -``` -apiserver and controller use this API to make up a new outgoing request. +apiserver and controllers use this API to make up a new outgoing request. ### Design of Mutating webhook(Out of tree) @@ -380,3 +369,4 @@ _This section must be completed when targeting beta graduation to a release._ * 2020-09-28: PRR questionnaire updated * [Mutating admission webhook which injects trace context for demo](https://github.com/Hellcatlk/mutating-trace-admission-controller/tree/trace-ot) * [Instrumentation of Kubernetes components for demo](https://github.com/Hellcatlk/kubernetes/pull/1) +* [Instrumentation of Kubernetes components for demo based on KEP647](https://github.com/Hellcatlk/kubernetes/pull/3) From cc282e79fa752571463564a050f9a2aaf31dc498 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Thu, 12 Nov 2020 09:27:36 +0800 Subject: [PATCH 30/32] remove feature gate in kube-apiserver in this kep, we don't touch kube-apiserver any more Signed-off-by: Li Zhijian --- keps/sig-instrumentation/1668-log-tracking/README.md | 2 +- keps/sig-instrumentation/1668-log-tracking/kep.yaml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index 757188820bc..bbe455bdf8d 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -242,7 +242,7 @@ _This section must be completed when targeting alpha to a release._ * **How can this feature be enabled / disabled in a live cluster?** - [x] Feature gate (also fill in values in `kep.yaml`) - Feature gate name: ObjectTraceIDs - - Components depending on the feature gate: kube-apiserver, kube-controller-manager + - Components depending on the feature gate: kube-controller-manager - [ ] Other - Describe the mechanism: - Will enabling / disabling the feature require downtime of the control diff --git a/keps/sig-instrumentation/1668-log-tracking/kep.yaml b/keps/sig-instrumentation/1668-log-tracking/kep.yaml index 507c1069def..80eff26d33a 100644 --- a/keps/sig-instrumentation/1668-log-tracking/kep.yaml +++ b/keps/sig-instrumentation/1668-log-tracking/kep.yaml @@ -38,7 +38,6 @@ milestone: feature-gates: - name: ObjectTraceIDs components: - - kube-apiserver - kube-controller-manager disable-supported: true From 739eb789e8978bba312ae8bb9342dd69c620ea2e Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 23 Dec 2020 15:22:25 +0800 Subject: [PATCH 31/32] Address comments (#14) - Use objsetid instead initialtraceid[1] - Update summary[2] - s/NOTE/Opt-in for Components - Remove unclear section[3] - Add Background section[4] - Update controller section[5] - Add new section: Behaviors with and without Mutating webhook - update overview.png - Move logging metadata to webhook section [1]: https://github.com/kubernetes/enhancements/pull/1961#discussion_r530735151 [2]: https://github.com/kubernetes/enhancements/pull/1961#discussion_r535489046 [3]: https://github.com/kubernetes/enhancements/pull/1961#discussion_r535493398 [4]: https://github.com/kubernetes/enhancements/pull/1961#discussion_r535493569 [5]: https://github.com/kubernetes/enhancements/pull/1961#discussion_r535519134 Signed-off-by: Li Zhijian --- .../1668-log-tracking/README.md | 203 ++++++++++++------ .../1668-log-tracking/overview.png | Bin 42574 -> 48473 bytes 2 files changed, 137 insertions(+), 66 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index bbe455bdf8d..a0553575c6a 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -3,23 +3,24 @@ - [Release Signoff Checklist](#release-signoff-checklist) - [Summary](#summary) - - [New three unique logging meta-data](#new-three-unique-logging-meta-data) - - [Note](#note) + - [New three unique logging metadata](#new-three-unique-logging-metadata) + - [Opt-in for Components](#opt-in-for-components) - [Motivation](#motivation) - [Goals](#goals) - [Non-Goals](#non-goals) +- [Background](#background) - [Proposal](#proposal) - [User Stories (Optional)](#user-stories-optional) - [Story 1](#story-1) - [Story 2](#story-2) - [Summary of Cases](#summary-of-cases) - - [Logging metadata](#logging-metadata) - [Design Details](#design-details) - [Prerequisite](#prerequisite) - - [API Server Tracing](#api-server-tracing) - [Design of ID propagation (controller)](#design-of-id-propagation-controller) - - [Design of ID propagation in client-go](#design-of-id-propagation-in-client-go) - [Design of Mutating webhook(Out of tree)](#design-of-mutating-webhookout-of-tree) + - [Behaviors with and without Mutating webhook](#behaviors-with-and-without-mutating-webhook) + - [with Mutating webhook](#with-mutating-webhook) + - [without Mutating webhook](#without-mutating-webhook) - [Risks and Mitigations](#risks-and-mitigations) - [Test Plan](#test-plan) - [Graduation Criteria](#graduation-criteria) @@ -53,26 +54,24 @@ Items marked with (R) are required *prior to targeting to a milestone / release* ## Summary -This KEP proposes a method for adding new three unique logging meta-data into K8s component logs. -It makes us more easy to identify specific logs related to an user request (such as `kubectl apply`) and object (such as Pod, Deployment). -It is expected to reduce investigation cost greatly when trouble shoothing. +This KEP proposes a method for adding new three unique logging metadata into annotation of objects and propagating them across components. Then the K8S components have ability to *track/filter* logs of all relevant object sets in need that makes us more easy to identify specific logs related to an user request (such as `kubectl apply`) and object (such as Pod, Deployment). +It is expected to reduce investigation cost greatly when troubleshooting. -### New three unique logging meta-data +### New three unique logging metadata -We use three meta-data. These meta-data have different features and are used for troubleshooting from different perspectives. +We use three metadata. These metadata have different features and are used for troubleshooting from different perspectives. -| meta-data name | feature | +| metadata name | feature | | ------ | ------ | | traceid | spans an user request. unique for user's request | -| spanid | spans a controller action. unique for controller action | -| initialtraceid | spans the entire object lifecycle. unique for related objects | +| spanid | spans a controller action. unique for controller actions | +| objsetid | spans the entire object lifecycle. unique for a related object set | -### Note +### Opt-in for Components -This KEP is **how** a component could add meta-data to logs. To actually add meta-data to K8s component logs, the following procedure is necessary in addition. +Updating/changing the logs is outside the scope of this KEP. To actually add metadata to K8s component logs, the following procedures are needed in addition. - Open issues for each component, and discuss them with the SIGs that own that component. -- After get agreement, utilize this KEP's feature to change the source code that outputs log to add meta-data into these logs. -Please note that this KEP alone does not change the log format(does not add meta-data to logs). +- After get agreement, utilize this KEP's feature to change the source code that outputs log to add metadata into these logs. ## Motivation @@ -82,15 +81,25 @@ If multiple users throw many API requests at the same time, it is very difficult ### Goals - - Implement method which propagates new logging meta-data among each K8s component - - Design and implement so as not to interfere with [Tracing KEP](https://github.com/kubernetes/enhancements/pull/1458) - - e.g. implement of initialtraceid, adding traceid to object annotation executed in mutating webhook, etc. + - Implement method which propagates new logging metadata among each K8s components + - Store and update logging metadata into object annotation through mutating admission controller(aka Webhook) ### Non-Goals - Add new logging metadata into actual K8s component logs - This task will be done by opening issues after completing this KEP - - To centrally manage the logs of each Kubernetes component with Request-ID (This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). + - To centrally manage the logs of each Kubernetes component with objsetid(This can be realized with existing OSS such as Kibana, so no need to implement into Kubernetes components). + - This proposal does not add additional telemetry to any components, just context-based metadata to existing logs. This KEP doesn't require running any additional OpenTelemetry components (such as the OpenTelemetry collector, which the [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) KEP uses) + +## Background +It's Known that all requests to K8s will reach API Server first, in order to propogate these metadata to mutating admission controller, we require API Server to support propogating these metadata as well. Fortunately there is already a KEP [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) to do this. + +``` +Goals +The API Server generates and exports spans for incoming and outgoing requests. +The API Server propagates context from incoming requests to outgoing requests. +``` +So this KEP relies on KEP [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) ## Proposal @@ -121,18 +130,6 @@ This log tracking feature is useful to identify the logs related to specific use - Given a component log(such as error log), find the API request that caused this (error) log. - Given an API Request(such as suspicious API request), find the resulting component logs. -### Logging metadata - -We use three logging meta-data, and propagate them each K8s component by using OpenTelemetry. -OpenTelemetry has SpanContext and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) which is used for propagation of K8s component. - -| meta-data name | feature | -| ------ | ------ | -| traceid | We use SpanContext.TraceID as traceid
traceid spans an user request.
traceid is unique for user's request | -| spanid | We use SpanContext.SpanID as spanid
spanid spans a controller action.
spanid is unique for controller action | -| initialtraceid | We implement new id(InitialTraceID) to SpanContext
We use [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to propagate initialtraceid
We use UID of root object as initialtraceid
initialtraceid spans the entire object lifecycle.
initialtraceid is unique for related objects | - -All of three id's inception is from object creation and it dies with object deletion ## Design Details @@ -147,66 +144,140 @@ The design below is based on the above three cases The following picture show our design![design](./overview.png) +we don't have any modifications in kubectl in this design. +We use three logging metadata, and propagate them across each K8s component by using [SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel@v0.15.0/trace#SpanContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) of OpenTelemetry. -we don't have any modifications in kubectl in this design. -### API Server Tracing +### Design of ID propagation (controller) +In controller side, we don't update the object annotation any more, instead, we just focus on how to propagate the new logging metadata across objects. And with the help of KEP [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) , the new logging metadata can be propagated across K8s components. -**1. refer to [KEP647](https://github.com/kubernetes/enhancements/issues/647)** +**1. New context propagation library function** -KEP647 will help to +A new API `SpanContextWithObject` is introduced to return a `context.Context` which includes a Span and a Baggage according to the passed object. ``` -Goals -The API Server generates and exports spans for incoming and outgoing requests. -The API Server propagates context from incoming requests to outgoing requests. +func SpanContextWithObject(ctx context.Context, meta metav1.Object) context.Context +``` +it does something like below pseudo code: +``` +// SpanContextWithObject returns a `context.Context` which includes a Span and a Baggage +func SpanContextWithObject(ctx context.Context, meta metav1.Object) context.Context{ + // 1. extract baggage from annotation and chain it to ctx + // 2. extract SpanContext from annotation + // 3. generate Span from SpanContext + // 4. chain Span to ctx which is required by propagators.Inject +} +``` +**2. Propagate Span and Baggage with context.Context across objects** + +When controllers create/update/delete an object A based on another B, we propagate context from B to A. Below is an example to show how deployment propagate Span and Baggage to replicaset. + +```diff + func (dc *DeploymentController) getNewReplicaSet(d *apps.Deployment, rsList, oldRSs []*apps.ReplicaSet, createIfNotExisted bool) (*apps.ReplicaSet, error) { ++ ctx := httptrace.SpanContextWithObject(context.Background(), d) + existingNewRS := deploymentutil.FindNewReplicaSet(d, rsList) +@@ -220,7 +227,8 @@ func (dc *DeploymentController) getNewReplicaSet(d *apps.Deployment, rsList, old + // hash collisions. If there is any other error, we need to report it in the status of + // the Deployment. + alreadyExists := false +- createdRS, err := dc.client.AppsV1().ReplicaSets(d.Namespace).Create(context.TODO(), &newRS, metav1.CreateOptions{}) ++ createdRS, err := dc.client.AppsV1().ReplicaSets(d.Namespace).Create(ctx, &newRS, metav1.CreateOptions{}) ``` +In order to propagate context across all objects we concerned , we also need to change some APIs by adding `ctx context.Context` to parameter list whose parameters doesn't contain context.Context yet. Below APIs will be impacted so far. -**2. [Make up a new outgoing request](#design-of-id-progagation-in-client-go)** +| APIs | file name | +| ----------------------------- | ------------------------------------------------------------ | +| createPods() | pkg/controller/controller_utils.go | +| CreatePodsWithControllerRef() | pkg/controller/controller_utils.go
pkg/controller/replication/conversion.go
pkg/controller/daemon/daemon_controller.go
pkg/controller/replication/conversion.go | -### Design of ID propagation (controller) +**3. How to log the metadata from a context.Context** -**1. Propagate golang ctx from objects to API Calls** +Please note that changing the log is *not* the scope of this KEP. In the feature, we can get the metadata and change the log like below accordingly. -When controllers create/update/delete an object A based on another B, we propagate context from B to A. E.g.: ``` - ctx = httptrace.SpanContextFromAnnotations(context.Background(), objB.GetAnnotations()) - err = r.KubeClient.CoreV1().Create(ctx, objA...) +spanctx := apitrace.SpanContextFromContext(ctx) +objSetID := otel.BaggageValue(ctx, "objectSetID").AsString() +klog.V(2).Infof("ObjectSetID: %s, TraceID: %s, SpanID: %s\n", objSetID, spanctx.TraceID, spactx.SpanID) ``` -We do propagation across objects without adding traces to that components. -SpanContextFromAnnotations will do something like below pseudo code -``` -func SpanContextFromAnnotations(){ - // 1. decode annotation to SpanContext - // 2. generate Span from SpanContext - // 3. chain Span to ctx which is required by propagators.Inject -} -``` -**2. [Make up a new outgoing request](#design-of-id-progagation-in-client-go)** +### Design of Mutating webhook(Out of tree) +We use mutating admission controller(aka webhook) to change/update the object annotation. It takes advantages of: + +- Ease of use. Using client-go with a context.Context is easier than adding an annotation. The webhook takes care of writing the annotation. +- Object to object context propagation. Without the mutating admission controller, we can only associate actions from a single object. With the mutating admission controller, the logging metadata would be added for objects modified by controllers of the initial object (e.g. metadata added to a deployment annotation would appear in pod logs). + +**Logging metadata** -### Design of ID propagation in [client-go](https://github.com/kubernetes/client-go) +below table show how these 3 logging metadata map to the SpanContext and Baggage of OpenTelemetry. +| metadata name | feature | +| ------ | ------ | +| traceid | We use SpanContext.TraceID as traceid
traceid spans an user request.
traceid is unique for user's request | +| spanid | We use SpanContext.SpanID as spanid
spanid spans a controller action.
spanid is unique for controller action | +| objsetid | We implement new id(objsetid) to SpanContext
We use [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to propagate objsetid
We use UID of root object as objsetid
objsetid spans the entire object lifecycle.
objsetid is unique for related object set. | -[KEP647](https://github.com/kubernetes/enhancements/issues/647) will do this work too, so we can refer to it directly in the future. -client-go helps to inject [TraceContext](https://pkg.go.dev/go.opentelemetry.io/otel/propagators#example-TraceContext) and [Baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/baggage/api.md) to the outgoing http request header with API [RoundTrip](https://pkg.go.dev/go.opentelemetry.io/contrib/instrumentation/net/http#Transport.RoundTrip). +All of three id's inception is from object creation and it dies with object deletion -apiserver and controllers use this API to make up a new outgoing request. -### Design of Mutating webhook(Out of tree) +These 3 logging metadata are stored with 2 pairs of key/value in annotation. + +| key | example value | description | +| ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | +| trace.kubernetes.io/span | 00-ca057eae1a26b66314fe3e361eedc5ca-3696483da6bfdcea-00 | it consists of `---`.
A corresponding http header field is like `traceparent: 00-ca057eae1a26b66314fe3e361eedc5ca-3696483da6bfdcea-00` which is a w3c specific [trace-context](https://w3c.github.io/trace-context/#traceparent-header ).
2 metadata(`traceid` and `spanid`) are stored here. | +| trace.kubernetes.io/objsetid | 04bc5995-0147-4db8-9f06-fdbcb2e1a087 | A corresponding http header field is like `baggage: objectSetID=04bc5995-0147-4db8-9f06-fdbcb2e1a087` which is a w3c specific [Baggage](https://w3c.github.io/baggage/#examples-of-http-headers).
It stores metadata `objsetid`. | -**1.Extract SpanContext and initialtraceid from request's header** +Roughly this webhook do something like below: -**2.Update SpanContext and initialtraceid to object** +**1.Extract SpanContext and objsetid from request's header** + +**2.Update SpanContext and objsetid to object annotation** - For traceid and spanid and traceflags(sampled/not sampled) -Always set the trace context annotation based on the incoming request. + Always update `trace.kubernetes.io/span` annotation based on the incoming request. + +- For objsetid + + if objsetid is nil, Use the UID of object as objsetid, else, leave objsetid as it is. Update it to `trace.kubernetes.io/objsetid` in need. + +### Behaviors with and without Mutating webhook +Since the mutating webhook is optional for users, we will explain the different behaviors between with and without mutating webhook. + +#### with Mutating webhook + +**kubectl request**: + +- APIServer uses otel to start a new Span +- APIServer uses otel to propagate SpanContext to the other end(webhook) +- Webhook generates an objsetid +- Webhook persists SpanContext and objsetid to object + +**controllers request:** + +- Controller uses otel to start a related Span, which connected to the SpanContext in object +- Controller uses otel to propagate Baggage(objsetid) stored in object and SpanContext to the other end(APIServer) +- APIServer uses otel to start a related Span, which connected to the SpancContext from the incoming request +- APIServer uses otel to propagate SpanContext and Baggage(objsetid) to the other end(webhook) +- Webhook persists SpanContext and objsetid to object + +#### without Mutating webhook + +**kubectl request:** + +- APIServer uses otel to start a new Span +- APIServer uses otel to propagate SpanContext to the other end +- ~~Webhook generates an objsetid~~ +- ~~Webhook persists SpanContext and objsetid to object~~ -- For initialtraceid +**controllers request:** -if initialtraceid is nil, Use the UID of object as initialtraceid, else, leave initialtraceid as it is. +- Controller start uses otel to start a new Span +- Controller uses otel to propagate SpanContext to the other end(APIServer) +- APIServer uses otel to start related Span, which connected to the SpancContext from the incoming request +- APIServer uses otel to propagate SpanContext to the other end +- ~~Webhook persists SpanContext and objsetid to object~~ +In short, the webhook decides whether to add logging metadata to the object. ### Risks and Mitigations diff --git a/keps/sig-instrumentation/1668-log-tracking/overview.png b/keps/sig-instrumentation/1668-log-tracking/overview.png index f3b3496912df510c8d10af3ab77c4901860cf373..9abf8acae7312edb5f3a0e6af6874cedc0754cc6 100644 GIT binary patch literal 48473 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%0D5?cncbMG zfokHOwn$$|7Q~1)bf^ z5%N=WDu)URpG`z4rOP+U7QV`ZgBf&-?;CJ(7$+!sp-WL-{->TW(uZhQNesXVhekT0 zJsiZ^;huvoR&ze|UPG`S7M3 z74|~GGcCGbChF7Z7dLKTMbyXDO#&yLo8TY0#fG~s6v>WX*5ouDe!uJW&!6ulTtgf6 zkIg$BAlnIV#W58O;8TwE4-CXko9puZSM7@p$c1b$jBR{)IN!2i`2!YxldU5tNG`1&=vP-Zp(jGdWI&tuKXKY!6-yE+L^pEU$X zK~i%sU1m7vzi~p$idBgr7~1GLM3xUTdpo})ngBCjZmA%_^c)^g0whd69o}>bTMGs< zL9_EHLq}yP>%pjVV`>t8rlr5H}x|r=?NPR)vY`$dhA&eKp@*Ken#op`@NHf@^uWx$$3+^;j8|PD2NTg*PEC4qRexSoLjm3YrPdz11bze`B)!PU{8_wPH{iqjNEefm1f}c6YN6%LlFJbE@fpGz zgjC5sij0&_ZXznpzHdlX77uyhv8Q`WhB1Y%DlUxWu?fz-fazO~0#k)G1=& zXF>4(Z<;cWG!W;yOb=#K(J<3tUJK-|{>7M;b4W&pK@MZy_b}6d_~*96{0Jhj9StLF z5Lk49jlWM!XusQqenVYdT?w;5iS0KNm5R{xveezHThrR1-EKm73X$1~ZUE6SK=Izk z5M~}N@b=9cX2h?e=EQ>x^^sDN<5n5^FoX?29)p=Ad-v|07-%t`N&NZV82t%Z$K!tq zib@1{C@y4|kvBX+G@JH3!gVrk|3{n0K>xCyx;mO-_VzCf2xb5)GoinUcBX-~4Qr}a zKHEO)(BjkoXc=iS&7++{6#l2R>YZxe|NZP@Tvu-8CYW(zNDkIvsj+&EzHd_SFk#O` z{2r@7{L-aM>z9E%TLcid-VASwvIIm%8x&=;mrF$(C`H#bACPd;6|`pPRK#a)Eb6Mc9P5ZKY76M(3Xy&t~w$v43~zzF#E9UUE5 zUAXt)J;k&%_%5|8bm7;+@cCz7-Q3(7Ja{d*F&z-F0%7mmSQnn3DqK0br%&pG|6k-q z$E)Jo$$!-miByH?vc?qu5O_H7Y*X64{f&of&X){r3cUt51TH&aMSF;w6wQYk>__{s zyZL#mbygFv?^Vk3asM~@(d5pBv!%^-B)RDf=kwn1b??D3LgabX%L|e_xxh&qNi3G+1J)7QAmHT*vP-G>iSu-L>mV69*i zcT^O}|7-uGWs5gtnCN2=*$v@h!rQi$Nxb<~=wpsqK3GT4(Ka!$-;svUdc^X;Y96j^ zF34Q}z`?x4Vz;~1mA>FjX}k9`%HzbvHW1|+?$9MSHy@f!+}!XfKw;(7_bj%Yx2S}i z()ExYKL>oJ2l{7d1R*CF%kB&oNJYoVg)=9Sj{6<@{(*d`qHQdB$v9M>(a|Bh3?JUR z2NluOTC;MFsgE%=&LA3uUx(zeZXVfOy(lrbzHWKJAtkkwS8b;}d1ihbDOeI@_+j38 zc|oDH+pg0}Le>}{$S)v3^p9hf^@@bx_x zk6TIjF{<%*OuR+y`Wd#j47a|$${$_RmiQxx|GP)W4UMaND7&8}-?h>nejxJ5*Wdra z_S*-qd}&H9YiqeV|1JEON7>z~>wdAZ_4Nu0o->_(G^{(hpG#`H-`R0xr9%esQ$L@7JVoMf2?uJ8B^GCiu^syImDbV6B6h3~pxZn{2I#IBxG4lCf{U9-oMsk66~SpRsV zy|v`shdes^Z36Ui!Oa$%p4>g5zB1U)erzlqDOs2N*PZj9KK*&e`u3V{E8kdum|P#Op>+1_r~T7C7xe$AI&tlL zYWcmnrl;#~sn-1ylGmOWB`L3byg%~o-4FIl9~l31o%6!^-Mlw7r9iJ7Xq*Dvnd5#u zLBDHJhsS@Zg!&%I7p#AOU3~HT;A-%MD&vBB2~oZOw5 zfuTX}Fp|?Zs_n`LP8*k(fJzAvxNsP_JQKo1@4E~};dFOspOV2EIO}myk%0kNoHcZG zWcgTe~DWM4fHNpzD literal 42574 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 From d1ea659a83e59553f92afc434436fe78ff5c5ce0 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 8 Jan 2021 18:45:43 +0800 Subject: [PATCH 32/32] update log example Signed-off-by: Li Zhijian --- .../1668-log-tracking/README.md | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/keps/sig-instrumentation/1668-log-tracking/README.md b/keps/sig-instrumentation/1668-log-tracking/README.md index a0553575c6a..6339e7de694 100644 --- a/keps/sig-instrumentation/1668-log-tracking/README.md +++ b/keps/sig-instrumentation/1668-log-tracking/README.md @@ -92,7 +92,7 @@ If multiple users throw many API requests at the same time, it is very difficult - This proposal does not add additional telemetry to any components, just context-based metadata to existing logs. This KEP doesn't require running any additional OpenTelemetry components (such as the OpenTelemetry collector, which the [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) KEP uses) ## Background -It's Known that all requests to K8s will reach API Server first, in order to propogate these metadata to mutating admission controller, we require API Server to support propogating these metadata as well. Fortunately there is already a KEP [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) to do this. +It's Known that all requests to K8s will reach API Server first, in order to propagate these metadata to mutating admission controller, we require API Server to support propogating these metadata as well. Fortunately there is already a KEP [API Server Tracing](https://github.com/kubernetes/enhancements/issues/647) to do this. ``` Goals @@ -193,13 +193,40 @@ In order to propagate context across all objects we concerned , we also need to **3. How to log the metadata from a context.Context** -Please note that changing the log is *not* the scope of this KEP. In the feature, we can get the metadata and change the log like below accordingly. +Please note that changing the log is *not* the scope of this KEP. In the future, we can get the metadata and change the log like below accordingly. +- Example +```go +// package httptrace +func WithObjectV2(ctx context.Context, meta metav1.Object) context.Context { + ctx = SpanContextWithObject(ctx, meta) + + log := kontext.FromContext(ctx) + objsetID, traceID, spanID := ObjSetIDsFrom(ctx) + log.WithValues("objectsetid", objsetID, "traceid", traceID, "spanid", spanID) + return kontext.IntoContext(ctx, log) +} +// package deployment +func cleanupDeployment(oldRSs []*apps.ReplicaSet, deployment *apps.Deployment) { + ctx := httptrace.WithObjectV2(context.TODO(), deployment) + [...] + logger := kontext.FromContext(ctx) + logger.Infof("Looking to cleanup old replica sets for deployment %q", deployment.Name) + cleanupFIXME(ctx, ...) + } + +func cleanupFIXME(ctx context.Context, ...){ + logger := kontext.FromContext(ctx) + logger.Info("Looking to cleanup FIXME") +} ``` -spanctx := apitrace.SpanContextFromContext(ctx) -objSetID := otel.BaggageValue(ctx, "objectSetID").AsString() -klog.V(2).Infof("ObjectSetID: %s, TraceID: %s, SpanID: %s\n", objSetID, spanctx.TraceID, spactx.SpanID) +In this example, it's expected that the log looks like: + +```shell +2019/12/01 14:49:12 [INFO] objectsetid="04bc5995-0147-4db8-9f06-fdbcb2e1a087" traceid="ca057eae1a26b66314fe3e361eedc5ca" spanid="3696483da6bfdcea" Looking to cleanup old replica sets for deployment hello-world +2019/12/01 14:49:12 [INFO] objectsetid="04bc5995-0147-4db8-9f06-fdbcb2e1a087" traceid="ca057eae1a26b66314fe3e361eedc5ca" spanid="3696483da6bfdcea" Looking to cleanup FIXME ``` +the logger and specific logging metadata will be propagated across the call stack. ### Design of Mutating webhook(Out of tree) We use mutating admission controller(aka webhook) to change/update the object annotation. It takes advantages of: @@ -336,7 +363,7 @@ _This section must be completed when targeting alpha to a release._ Yes. * **What happens if we reenable the feature if it was previously rolled back?** - Objects created during the rollback will have no initialtrace-id until they + Objects created during the rollback will have no objsetid until they are recreated. * **Are there any tests for feature enablement/disablement?**