mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #64481 from immutableT/transormer-metrics-2
Automatic merge from submit-queue (batch tested with PRs 64481, 64569). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Instrument envelop transformer. **What this PR does / why we need it**: Add metrics for envelope transformer: transformation_operation_count transformation_failures_count envelope_transformation_cache_misses_count data_key_generation_latencies_microseconds data_key_generation_failures_count **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes # **Special notes for your reviewer**: **Release note**: ```release-note NONE ```
This commit is contained in:
commit
6466794b77
@ -24,6 +24,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
|
||||||
@ -33,6 +34,10 @@ import (
|
|||||||
// defaultCacheSize is the number of decrypted DEKs which would be cached by the transformer.
|
// defaultCacheSize is the number of decrypted DEKs which would be cached by the transformer.
|
||||||
const defaultCacheSize = 1000
|
const defaultCacheSize = 1000
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
value.RegisterMetrics()
|
||||||
|
}
|
||||||
|
|
||||||
// Service allows encrypting and decrypting data using an external Key Management Service.
|
// Service allows encrypting and decrypting data using an external Key Management Service.
|
||||||
type Service interface {
|
type Service interface {
|
||||||
// Decrypt a given bytearray to obtain the original data as bytes.
|
// Decrypt a given bytearray to obtain the original data as bytes.
|
||||||
@ -85,6 +90,7 @@ func (t *envelopeTransformer) TransformFromStorage(data []byte, context value.Co
|
|||||||
// Look up the decrypted DEK from cache or Envelope.
|
// Look up the decrypted DEK from cache or Envelope.
|
||||||
transformer := t.getTransformer(encKey)
|
transformer := t.getTransformer(encKey)
|
||||||
if transformer == nil {
|
if transformer == nil {
|
||||||
|
value.RecordCacheMiss()
|
||||||
key, err := t.envelopeService.Decrypt(encKey)
|
key, err := t.envelopeService.Decrypt(encKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("error while decrypting key: %q", err)
|
return nil, false, fmt.Errorf("error while decrypting key: %q", err)
|
||||||
@ -156,10 +162,12 @@ func (t *envelopeTransformer) getTransformer(encKey []byte) value.Transformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateKey generates a random key using system randomness.
|
// generateKey generates a random key using system randomness.
|
||||||
func generateKey(length int) ([]byte, error) {
|
func generateKey(length int) (key []byte, err error) {
|
||||||
key := make([]byte, length)
|
defer func(start time.Time) {
|
||||||
_, err := rand.Read(key)
|
value.RecordDataKeyGeneration(start, err)
|
||||||
if err != nil {
|
}(time.Now())
|
||||||
|
key = make([]byte, length)
|
||||||
|
if _, err = rand.Read(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,11 +23,16 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
namespace = "apiserver"
|
||||||
|
subsystem = "storage"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
transformerLatencies = prometheus.NewHistogramVec(
|
transformerLatencies = prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
Namespace: "apiserver",
|
Namespace: namespace,
|
||||||
Subsystem: "storage",
|
Subsystem: subsystem,
|
||||||
Name: "transformation_latencies_microseconds",
|
Name: "transformation_latencies_microseconds",
|
||||||
Help: "Latencies in microseconds of value transformation operations.",
|
Help: "Latencies in microseconds of value transformation operations.",
|
||||||
// In-process transformations (ex. AES CBC) complete on the order of 20 microseconds. However, when
|
// In-process transformations (ex. AES CBC) complete on the order of 20 microseconds. However, when
|
||||||
@ -36,6 +41,42 @@ var (
|
|||||||
},
|
},
|
||||||
[]string{"transformation_type"},
|
[]string{"transformation_type"},
|
||||||
)
|
)
|
||||||
|
transformerFailuresTotal = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: "transformation_failures_total",
|
||||||
|
Help: "Total number of failed transformation operations.",
|
||||||
|
},
|
||||||
|
[]string{"transformation_type"},
|
||||||
|
)
|
||||||
|
|
||||||
|
envelopeTransformationCacheMissTotal = prometheus.NewCounter(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: "envelope_transformation_cache_misses_total",
|
||||||
|
Help: "Total number of cache misses while accessing key decryption key(KEK).",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
dataKeyGenerationLatencies = prometheus.NewHistogram(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: "data_key_generation_latencies_microseconds",
|
||||||
|
Help: "Latencies in microseconds of data encryption key(DEK) generation operations.",
|
||||||
|
Buckets: prometheus.ExponentialBuckets(5, 2, 14),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
dataKeyGenerationFailuresTotal = prometheus.NewCounter(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Namespace: namespace,
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: "data_key_generation_failures_total",
|
||||||
|
Help: "Total number of failed data encryption key(DEK) generation operations.",
|
||||||
|
},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
var registerMetrics sync.Once
|
var registerMetrics sync.Once
|
||||||
@ -43,14 +84,40 @@ var registerMetrics sync.Once
|
|||||||
func RegisterMetrics() {
|
func RegisterMetrics() {
|
||||||
registerMetrics.Do(func() {
|
registerMetrics.Do(func() {
|
||||||
prometheus.MustRegister(transformerLatencies)
|
prometheus.MustRegister(transformerLatencies)
|
||||||
|
prometheus.MustRegister(transformerFailuresTotal)
|
||||||
|
prometheus.MustRegister(envelopeTransformationCacheMissTotal)
|
||||||
|
prometheus.MustRegister(dataKeyGenerationLatencies)
|
||||||
|
prometheus.MustRegister(dataKeyGenerationFailuresTotal)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecordTransformation(transformationType string, start time.Time) {
|
// RecordTransformation records latencies and count of TransformFromStorage and TransformToStorage operations.
|
||||||
|
func RecordTransformation(transformationType string, start time.Time, err error) {
|
||||||
|
if err != nil {
|
||||||
|
transformerFailuresTotal.WithLabelValues(transformationType).Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
since := sinceInMicroseconds(start)
|
since := sinceInMicroseconds(start)
|
||||||
transformerLatencies.WithLabelValues(transformationType).Observe(float64(since))
|
transformerLatencies.WithLabelValues(transformationType).Observe(float64(since))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordCacheMiss records a miss on Key Encryption Key(KEK) - call to KMS was required to decrypt KEK.
|
||||||
|
func RecordCacheMiss() {
|
||||||
|
envelopeTransformationCacheMissTotal.Inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordDataKeyGeneration records latencies and count of Data Encryption Key generation operations.
|
||||||
|
func RecordDataKeyGeneration(start time.Time, err error) {
|
||||||
|
if err != nil {
|
||||||
|
dataKeyGenerationFailuresTotal.Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
since := sinceInMicroseconds(start)
|
||||||
|
dataKeyGenerationLatencies.Observe(float64(since))
|
||||||
|
}
|
||||||
|
|
||||||
func sinceInMicroseconds(start time.Time) int64 {
|
func sinceInMicroseconds(start time.Time) int64 {
|
||||||
elapsedNanoseconds := time.Since(start).Nanoseconds()
|
elapsedNanoseconds := time.Since(start).Nanoseconds()
|
||||||
return elapsedNanoseconds / int64(time.Microsecond)
|
return elapsedNanoseconds / int64(time.Microsecond)
|
||||||
|
@ -85,14 +85,18 @@ func (t *MutableTransformer) Set(transformer Transformer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *MutableTransformer) TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error) {
|
func (t *MutableTransformer) TransformFromStorage(data []byte, context Context) (out []byte, stale bool, err error) {
|
||||||
defer RecordTransformation("from_storage", time.Now())
|
defer func(start time.Time) {
|
||||||
|
RecordTransformation("from_storage", start, err)
|
||||||
|
}(time.Now())
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
transformer := t.transformer
|
transformer := t.transformer
|
||||||
t.lock.RUnlock()
|
t.lock.RUnlock()
|
||||||
return transformer.TransformFromStorage(data, context)
|
return transformer.TransformFromStorage(data, context)
|
||||||
}
|
}
|
||||||
func (t *MutableTransformer) TransformToStorage(data []byte, context Context) (out []byte, err error) {
|
func (t *MutableTransformer) TransformToStorage(data []byte, context Context) (out []byte, err error) {
|
||||||
defer RecordTransformation("to_storage", time.Now())
|
defer func(start time.Time) {
|
||||||
|
RecordTransformation("to_storage", start, err)
|
||||||
|
}(time.Now())
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
transformer := t.transformer
|
transformer := t.transformer
|
||||||
t.lock.RUnlock()
|
t.lock.RUnlock()
|
||||||
|
@ -144,6 +144,7 @@ func TestKMSProvider(t *testing.T) {
|
|||||||
if secretVal != string(s.Data[secretKey]) {
|
if secretVal != string(s.Data[secretKey]) {
|
||||||
t.Fatalf("expected %s from KubeAPI, but got %s", secretVal, string(s.Data[secretKey]))
|
t.Fatalf("expected %s from KubeAPI, but got %s", secretVal, string(s.Data[secretKey]))
|
||||||
}
|
}
|
||||||
|
test.printMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDEKFromKMSPlugin(pluginMock *base64Plugin) ([]byte, error) {
|
func getDEKFromKMSPlugin(pluginMock *base64Plugin) ([]byte, error) {
|
||||||
|
@ -43,12 +43,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
secretKey = "api_key"
|
secretKey = "api_key"
|
||||||
secretVal = "086a7ffc-0225-11e8-ba89-0ed5f89f718b"
|
secretVal = "086a7ffc-0225-11e8-ba89-0ed5f89f718b"
|
||||||
encryptionConfigFileName = "encryption.conf"
|
encryptionConfigFileName = "encryption.conf"
|
||||||
testNamespace = "secret-encryption-test"
|
testNamespace = "secret-encryption-test"
|
||||||
testSecret = "test-secret"
|
testSecret = "test-secret"
|
||||||
latencySummaryMetricsFamily = "apiserver_storage_transformation_latencies_microseconds"
|
metricsPrefix = "apiserver_storage_"
|
||||||
)
|
)
|
||||||
|
|
||||||
type unSealSecret func(cipherText []byte, ctx value.Context, config encryptionconfig.ProviderConfig) ([]byte, error)
|
type unSealSecret func(cipherText []byte, ctx value.Context, config encryptionconfig.ProviderConfig) ([]byte, error)
|
||||||
@ -247,9 +247,9 @@ func (e *transformTest) printMetrics() error {
|
|||||||
return fmt.Errorf("failed to gather metrics: %s", err)
|
return fmt.Errorf("failed to gather metrics: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
metricsOfInterest := []string{latencySummaryMetricsFamily}
|
|
||||||
for _, mf := range metrics {
|
for _, mf := range metrics {
|
||||||
if contains(metricsOfInterest, *mf.Name) {
|
if strings.HasPrefix(*mf.Name, metricsPrefix) {
|
||||||
|
e.logger.Logf("%s", *mf.Name)
|
||||||
for _, metric := range mf.GetMetric() {
|
for _, metric := range mf.GetMetric() {
|
||||||
e.logger.Logf("%v", metric)
|
e.logger.Logf("%v", metric)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user