Recording openstack metrics

add openstack operation metrics
Add support for emitting metrics from openstack cloudprovider about storage operations.
This commit is contained in:
NickrenREN 2017-05-17 17:37:43 +08:00
parent 7f183142de
commit 18852c58c1
4 changed files with 93 additions and 6 deletions

View File

@ -15,6 +15,7 @@ go_library(
"openstack.go", "openstack.go",
"openstack_instances.go", "openstack_instances.go",
"openstack_loadbalancer.go", "openstack_loadbalancer.go",
"openstack_metrics.go",
"openstack_routes.go", "openstack_routes.go",
"openstack_volumes.go", "openstack_volumes.go",
], ],
@ -53,6 +54,7 @@ go_library(
"//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports:go_default_library",
"//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library", "//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library",
"//vendor/github.com/mitchellh/mapstructure:go_default_library", "//vendor/github.com/mitchellh/mapstructure:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/gopkg.in/gcfg.v1:go_default_library", "//vendor/gopkg.in/gcfg.v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",

View File

@ -53,11 +53,6 @@ var ErrNotFound = errors.New("Failed to find object")
var ErrMultipleResults = errors.New("Multiple results where only one expected") var ErrMultipleResults = errors.New("Multiple results where only one expected")
var ErrNoAddressFound = errors.New("No address found for host") var ErrNoAddressFound = errors.New("No address found for host")
const (
MiB = 1024 * 1024
GB = 1000 * 1000 * 1000
)
// encoding.TextUnmarshaler interface for time.Duration // encoding.TextUnmarshaler interface for time.Duration
type MyDuration struct { type MyDuration struct {
time.Duration time.Duration
@ -131,6 +126,8 @@ type Config struct {
} }
func init() { func init() {
RegisterMetrics()
cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
cfg, err := readConfig(config) cfg, err := readConfig(config)
if err != nil { if err != nil {

View File

@ -0,0 +1,50 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package openstack
import "github.com/prometheus/client_golang/prometheus"
const (
OpenstackSubsystem = "openstack"
OpenstackOperationKey = "cloudprovider_openstack_api_request_duration_seconds"
OpenstackOperationErrorKey = "cloudprovider_openstack_api_request_errors"
)
var (
OpenstackOperationsLatency = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Subsystem: OpenstackSubsystem,
Name: OpenstackOperationKey,
Help: "Latency of openstack api call",
},
[]string{"request"},
)
OpenstackApiRequestErrors = prometheus.NewCounterVec(
prometheus.CounterOpts{
Subsystem: OpenstackSubsystem,
Name: OpenstackOperationErrorKey,
Help: "Cumulative number of openstack Api call errors",
},
[]string{"request"},
)
)
func RegisterMetrics() {
prometheus.MustRegister(OpenstackOperationsLatency)
prometheus.MustRegister(OpenstackApiRequestErrors)
}

View File

@ -22,6 +22,7 @@ import (
"io/ioutil" "io/ioutil"
"path" "path"
"strings" "strings"
"time"
k8s_volume "k8s.io/kubernetes/pkg/volume" k8s_volume "k8s.io/kubernetes/pkg/volume"
@ -31,6 +32,7 @@ import (
volumes_v2 "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" volumes_v2 "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
"github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/pagination"
"github.com/prometheus/client_golang/prometheus"
"github.com/golang/glog" "github.com/golang/glog"
) )
@ -82,6 +84,8 @@ const (
) )
func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, error) { func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, error) {
startTime := time.Now()
create_opts := volumes_v1.CreateOpts{ create_opts := volumes_v1.CreateOpts{
Name: opts.Name, Name: opts.Name,
Size: opts.Size, Size: opts.Size,
@ -91,6 +95,8 @@ func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, e
} }
vol, err := volumes_v1.Create(volumes.blockstorage, create_opts).Extract() vol, err := volumes_v1.Create(volumes.blockstorage, create_opts).Extract()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("create_v1_volume", timeTaken, err)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -98,6 +104,8 @@ func (volumes *VolumesV1) createVolume(opts VolumeCreateOpts) (string, string, e
} }
func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, error) { func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, error) {
startTime := time.Now()
create_opts := volumes_v2.CreateOpts{ create_opts := volumes_v2.CreateOpts{
Name: opts.Name, Name: opts.Name,
Size: opts.Size, Size: opts.Size,
@ -107,6 +115,8 @@ func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, e
} }
vol, err := volumes_v2.Create(volumes.blockstorage, create_opts).Extract() vol, err := volumes_v2.Create(volumes.blockstorage, create_opts).Extract()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("create_v2_volume", timeTaken, err)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -116,6 +126,7 @@ func (volumes *VolumesV2) createVolume(opts VolumeCreateOpts) (string, string, e
func (volumes *VolumesV1) getVolume(diskName string) (Volume, error) { func (volumes *VolumesV1) getVolume(diskName string) (Volume, error) {
var volume_v1 volumes_v1.Volume var volume_v1 volumes_v1.Volume
var volume Volume var volume Volume
startTime := time.Now()
err := volumes_v1.List(volumes.blockstorage, nil).EachPage(func(page pagination.Page) (bool, error) { err := volumes_v1.List(volumes.blockstorage, nil).EachPage(func(page pagination.Page) (bool, error) {
vols, err := volumes_v1.ExtractVolumes(page) vols, err := volumes_v1.ExtractVolumes(page)
if err != nil { if err != nil {
@ -134,6 +145,8 @@ func (volumes *VolumesV1) getVolume(diskName string) (Volume, error) {
errmsg := fmt.Sprintf("Unable to find disk: %s", diskName) errmsg := fmt.Sprintf("Unable to find disk: %s", diskName)
return false, errors.New(errmsg) return false, errors.New(errmsg)
}) })
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("get_v1_volume", timeTaken, err)
if err != nil { if err != nil {
glog.Errorf("Error occurred getting volume: %s", diskName) glog.Errorf("Error occurred getting volume: %s", diskName)
return volume, err return volume, err
@ -154,6 +167,7 @@ func (volumes *VolumesV1) getVolume(diskName string) (Volume, error) {
func (volumes *VolumesV2) getVolume(diskName string) (Volume, error) { func (volumes *VolumesV2) getVolume(diskName string) (Volume, error) {
var volume_v2 volumes_v2.Volume var volume_v2 volumes_v2.Volume
var volume Volume var volume Volume
startTime := time.Now()
err := volumes_v2.List(volumes.blockstorage, nil).EachPage(func(page pagination.Page) (bool, error) { err := volumes_v2.List(volumes.blockstorage, nil).EachPage(func(page pagination.Page) (bool, error) {
vols, err := volumes_v2.ExtractVolumes(page) vols, err := volumes_v2.ExtractVolumes(page)
if err != nil { if err != nil {
@ -172,6 +186,8 @@ func (volumes *VolumesV2) getVolume(diskName string) (Volume, error) {
errmsg := fmt.Sprintf("Unable to find disk: %s", diskName) errmsg := fmt.Sprintf("Unable to find disk: %s", diskName)
return false, errors.New(errmsg) return false, errors.New(errmsg)
}) })
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("get_v2_volume", timeTaken, err)
if err != nil { if err != nil {
glog.Errorf("Error occurred getting volume: %s", diskName) glog.Errorf("Error occurred getting volume: %s", diskName)
return volume, err return volume, err
@ -190,19 +206,26 @@ func (volumes *VolumesV2) getVolume(diskName string) (Volume, error) {
} }
func (volumes *VolumesV1) deleteVolume(volumeName string) error { func (volumes *VolumesV1) deleteVolume(volumeName string) error {
startTime := time.Now()
err := volumes_v1.Delete(volumes.blockstorage, volumeName).ExtractErr() err := volumes_v1.Delete(volumes.blockstorage, volumeName).ExtractErr()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("delete_v1_volume", timeTaken, err)
if err != nil { if err != nil {
glog.Errorf("Cannot delete volume %s: %v", volumeName, err) glog.Errorf("Cannot delete volume %s: %v", volumeName, err)
} }
return err return err
} }
func (volumes *VolumesV2) deleteVolume(volumeName string) error { func (volumes *VolumesV2) deleteVolume(volumeName string) error {
startTime := time.Now()
err := volumes_v2.Delete(volumes.blockstorage, volumeName).ExtractErr() err := volumes_v2.Delete(volumes.blockstorage, volumeName).ExtractErr()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("delete_v2_volume", timeTaken, err)
if err != nil { if err != nil {
glog.Errorf("Cannot delete volume %s: %v", volumeName, err) glog.Errorf("Cannot delete volume %s: %v", volumeName, err)
} }
return err return err
} }
@ -253,10 +276,13 @@ func (os *OpenStack) AttachDisk(instanceID string, diskName string) (string, err
} }
} }
startTime := time.Now()
// add read only flag here if possible spothanis // add read only flag here if possible spothanis
_, err = volumeattach.Create(cClient, instanceID, &volumeattach.CreateOpts{ _, err = volumeattach.Create(cClient, instanceID, &volumeattach.CreateOpts{
VolumeID: volume.ID, VolumeID: volume.ID,
}).Extract() }).Extract()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("attach_disk", timeTaken, err)
if err != nil { if err != nil {
glog.Errorf("Failed to attach %s volume to %s compute: %v", diskName, instanceID, err) glog.Errorf("Failed to attach %s volume to %s compute: %v", diskName, instanceID, err)
return "", err return "", err
@ -288,9 +314,12 @@ func (os *OpenStack) DetachDisk(instanceID string, partialDiskId string) error {
glog.Errorf(errMsg) glog.Errorf(errMsg)
return errors.New(errMsg) return errors.New(errMsg)
} else { } else {
startTime := time.Now()
// This is a blocking call and effects kubelet's performance directly. // This is a blocking call and effects kubelet's performance directly.
// We should consider kicking it out into a separate routine, if it is bad. // We should consider kicking it out into a separate routine, if it is bad.
err = volumeattach.Delete(cClient, instanceID, volume.ID).ExtractErr() err = volumeattach.Delete(cClient, instanceID, volume.ID).ExtractErr()
timeTaken := time.Since(startTime).Seconds()
recordOpenstackOperationMetric("detach_disk", timeTaken, err)
if err != nil { if err != nil {
glog.Errorf("Failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err) glog.Errorf("Failed to delete volume %s from compute %s attached %v", volume.ID, instanceID, err)
return err return err
@ -458,3 +487,12 @@ func (os *OpenStack) diskIsUsed(diskName string) (bool, error) {
func (os *OpenStack) ShouldTrustDevicePath() bool { func (os *OpenStack) ShouldTrustDevicePath() bool {
return os.bsOpts.TrustDevicePath return os.bsOpts.TrustDevicePath
} }
// recordOpenstackOperationMetric records openstack operation metrics
func recordOpenstackOperationMetric(operation string, timeTaken float64, err error) {
if err != nil {
OpenstackApiRequestErrors.With(prometheus.Labels{"request": operation}).Inc()
} else {
OpenstackOperationsLatency.With(prometheus.Labels{"request": operation}).Observe(timeTaken)
}
}