Skip to content

Commit 2eea178

Browse files
authored
Merge pull request #1913 from khaines/feature/azure-storage
Adding Microsoft Azure Blob storage support for chunks
2 parents 1dad052 + 366eabf commit 2eea178

File tree

246 files changed

+30904
-9578
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

246 files changed

+30904
-9578
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
* [CHANGE] Ingesters now write only normalised tokens to the ring, although they can still read denormalised tokens used by other ingesters. `-ingester.normalise-tokens` is now deprecated, and ignored. If you want to switch back to using denormalised tokens, you need to downgrade to Cortex 0.4.0. Previous versions don't handle claiming tokens from normalised ingesters correctly. #1809
1515
* [FEATURE] The distributor can now drop labels from samples (similar to the removal of the replica label for HA ingestion) per user via the `distributor.drop-label` flag. #1726
1616
* [FEATURE] Added `global` ingestion rate limiter strategy. Deprecated `-distributor.limiter-reload-period` flag. #1766
17+
* [FEATURE] Added support for Microsoft Azure blob storage to be used for storing chunk data. #1913
1718
* [BUGFIX] Fixed unnecessary CAS operations done by the HA tracker when the jitter is enabled. #1861
18-
19+
1920
## 0.4.0 / 2019-12-02
2021

2122
* [CHANGE] The frontend component has been refactored to be easier to re-use. When upgrading the frontend, cache entries will be discarded and re-created with the new protobuf schema. #1734

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Cortex provides horizontally scalable, highly available, multi-tenant, long term
1414
- **Highly available:** When run in a cluster, Cortex can replicate data between machines. This allows you to survive machine failure without gaps in your graphs.
1515
- **Multi-tenant:** Cortex can isolate data and queries from multiple different independent
1616
Prometheus sources in a single cluster, allowing untrusted parties to share the same cluster.
17-
- **Long term storage:** Cortex supports Amazon DynamoDB, Google Bigtable, Cassandra, S3 and GCS for long term storage of metric data. This allows you to durably store data for longer than the lifetime of any single machine, and use this data for long term capacity planning.
17+
- **Long term storage:** Cortex supports Amazon DynamoDB, Google Bigtable, Cassandra, S3, GCS and Microsoft Azure for long term storage of metric data. This allows you to durably store data for longer than the lifetime of any single machine, and use this data for long term capacity planning.
1818

1919
Cortex is a [CNCF](https://cncf.io) sandbox project used in several production systems including [Weave Cloud](https://cloud.weave.works) and [Grafana Cloud](https://grafana.com/cloud).
2020
Cortex is primarily used as a [remote write](https://prometheus.io/docs/operating/configuration/#remote_write) destination for Prometheus, with a Prometheus-compatible query API.

go.mod

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ go 1.12
44

55
require (
66
cloud.google.com/go v0.44.1
7-
github.com/Azure/azure-sdk-for-go v26.3.0+incompatible // indirect
8-
github.com/Azure/go-autorest v11.5.1+incompatible // indirect
7+
github.com/Azure/azure-storage-blob-go v0.8.0
8+
github.com/Azure/go-autorest/autorest v0.9.2 // indirect
9+
github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
10+
github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
11+
github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect
912
github.com/Masterminds/squirrel v0.0.0-20161115235646-20f192218cf5
1013
github.com/NYTimes/gziphandler v1.1.1
1114
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4
@@ -73,6 +76,10 @@ require (
7376
gopkg.in/yaml.v2 v2.2.2
7477
)
7578

79+
replace github.com/Azure/azure-sdk-for-go => github.com/Azure/azure-sdk-for-go v36.2.0+incompatible
80+
81+
replace github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.0+incompatible
82+
7683
// Override since git.apache.org is down. The docs say to fetch from github.
7784
replace git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999
7885

go.sum

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,37 @@ contrib.go.opencensus.io/exporter/ocagent v0.6.0 h1:Z1n6UAyr0QwM284yUuh5Zd8JlvxU
88
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
99
github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo=
1010
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
11-
github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
12-
github.com/Azure/azure-sdk-for-go v26.3.0+incompatible h1:w/tfbWIy9a8SSNJFwcapWeOfknQXDYBVjh5UkuIr+NA=
13-
github.com/Azure/azure-sdk-for-go v26.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
11+
github.com/Azure/azure-sdk-for-go v36.2.0+incompatible h1:09cv2WoH0g6jl6m2iT+R9qcIPZKhXEL0sbmLhxP895s=
12+
github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
1413
github.com/Azure/azure-storage-blob-go v0.7.0 h1:MuueVOYkufCxJw5YZzF842DY2MBsp+hLuh2apKY0mck=
1514
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
15+
github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o=
16+
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
1617
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
1718
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
18-
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
19-
github.com/Azure/go-autorest v11.2.8+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
20-
github.com/Azure/go-autorest v11.5.1+incompatible h1:tdB6TZ8w2B7+F8wD6eTQSXXQo31zKKL55b6uqNDAGKw=
21-
github.com/Azure/go-autorest v11.5.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
19+
github.com/Azure/go-autorest v13.3.0+incompatible h1:8Ix0VdeOllBx9jEcZ2Wb1uqWUpE1awmJiaHztwaJCPk=
20+
github.com/Azure/go-autorest v13.3.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
21+
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
22+
github.com/Azure/go-autorest/autorest v0.9.2 h1:6AWuh3uWrsZJcNoCHrCF/+g4aKPCU39kaMO6/qrnK/4=
23+
github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
24+
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
25+
github.com/Azure/go-autorest/autorest/adal v0.8.0 h1:CxTzQrySOxDnKpLjFJeZAS5Qrv/qFPkgLjx5bOAi//I=
26+
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
27+
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
28+
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
29+
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
30+
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
31+
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
32+
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
33+
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
34+
github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8=
35+
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
36+
github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4=
37+
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
38+
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
39+
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
40+
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
41+
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
2242
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2343
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
2444
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package azure
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"flag"
7+
"fmt"
8+
"net/url"
9+
"strings"
10+
"time"
11+
12+
"github.com/Azure/azure-storage-blob-go/azblob"
13+
"github.com/cortexproject/cortex/pkg/chunk"
14+
"github.com/cortexproject/cortex/pkg/chunk/util"
15+
)
16+
17+
const blobURLFmt = "https://%s.blob.core.windows.net/%s/%s"
18+
19+
// BlobStorageConfig defines the configurable flags that can be defined when using azure blob storage.
20+
type BlobStorageConfig struct {
21+
ContainerName string `yaml:"container_name"`
22+
AccountName string `yaml:"account_name"`
23+
AccountKey string `yaml:"account_key"`
24+
DownloadBufferSize int `yaml:"download_buffer_size"`
25+
UploadBufferSize int `yaml:"upload_buffer_size"`
26+
UploadBufferCount int `yaml:"upload_buffer_count"`
27+
RequestTimeout time.Duration `yaml:"request_timeout"`
28+
MaxRetries int `yaml:"max_retries"`
29+
MinRetryDelay time.Duration `yaml:"min_retry_delay"`
30+
MaxRetryDelay time.Duration `yaml:"max_retry_delay"`
31+
}
32+
33+
// RegisterFlags adds the flags required to config this to the given FlagSet
34+
func (c *BlobStorageConfig) RegisterFlags(f *flag.FlagSet) {
35+
f.StringVar(&c.ContainerName, "azure.container-name", "cortex", "Name of the blob container used to store chunks. Defaults to `cortex`. This container must be created before running cortex.")
36+
f.StringVar(&c.AccountName, "azure.account-name", "", "The Microsoft Azure account name to be used")
37+
f.StringVar(&c.AccountKey, "azure.account-key", "", "The Microsoft Azure account key to use.")
38+
f.DurationVar(&c.RequestTimeout, "azure.request-timeout", 30*time.Second, "Timeout for requests made against azure blob storage. Defaults to 30 seconds.")
39+
f.IntVar(&c.DownloadBufferSize, "azure.download-buffer-size", 512000, "Preallocated buffer size for downloads (default is 512KB)")
40+
f.IntVar(&c.UploadBufferSize, "azure.upload-buffer-size", 256000, "Preallocated buffer size for up;oads (default is 256KB)")
41+
f.IntVar(&c.UploadBufferCount, "azure.download-buffer-count", 1, "Number of buffers used to used to upload a chunk. (defaults to 1)")
42+
f.IntVar(&c.MaxRetries, "azure.max-retries", 5, "Number of retries for a request which times out.")
43+
f.DurationVar(&c.MinRetryDelay, "azure.min-retry-delay", 10*time.Millisecond, "Minimum time to wait before retrying a request.")
44+
f.DurationVar(&c.MaxRetryDelay, "azure.max-retry-delay", 500*time.Millisecond, "Maximum time to wait before retrying a request.")
45+
}
46+
47+
// BlobStorage is used to interact with azure blob storage for setting or getting time series chunks.
48+
// Implements ObjectStorage
49+
type BlobStorage struct {
50+
//blobService storage.Serv
51+
cfg *BlobStorageConfig
52+
}
53+
54+
// NewBlobStorage creates a new instance of the BlobStorage struct.
55+
func NewBlobStorage(cfg *BlobStorageConfig) *BlobStorage {
56+
return &BlobStorage{cfg: cfg}
57+
}
58+
59+
// Stop is a no op, as there are no background workers with this driver currently
60+
func (b *BlobStorage) Stop() {}
61+
62+
// GetChunks retrieves the requested data chunks from blob storage.
63+
func (b *BlobStorage) GetChunks(ctx context.Context, chunks []chunk.Chunk) ([]chunk.Chunk, error) {
64+
return util.GetParallelChunks(ctx, chunks, b.getChunk)
65+
}
66+
67+
func (b *BlobStorage) getChunk(ctx context.Context, decodeContext *chunk.DecodeContext, input chunk.Chunk) (chunk.Chunk, error) {
68+
if b.cfg.RequestTimeout > 0 {
69+
// The context will be cancelled with the timeout or when the parent context is cancelled, whichever occurs first.
70+
var cancel context.CancelFunc
71+
ctx, cancel = context.WithTimeout(ctx, b.cfg.RequestTimeout)
72+
defer cancel()
73+
}
74+
75+
blockBlobURL, err := b.getBlobURL(input.ExternalKey())
76+
if err != nil {
77+
return chunk.Chunk{}, err
78+
}
79+
80+
buf := make([]byte, 0, b.cfg.DownloadBufferSize)
81+
82+
err = azblob.DownloadBlobToBuffer(ctx, blockBlobURL.BlobURL, 0, 0, buf, azblob.DownloadFromBlobOptions{})
83+
if err != nil {
84+
return chunk.Chunk{}, err
85+
}
86+
87+
if err := input.Decode(decodeContext, buf); err != nil {
88+
return chunk.Chunk{}, err
89+
}
90+
91+
return input, nil
92+
}
93+
94+
// PutChunks writes a set of chunks to azure blob storage using block blobs.
95+
func (b *BlobStorage) PutChunks(ctx context.Context, chunks []chunk.Chunk) error {
96+
97+
for _, chunk := range chunks {
98+
buf, err := chunk.Encoded()
99+
if err != nil {
100+
return err
101+
}
102+
103+
blockBlobURL, err := b.getBlobURL(chunk.ExternalKey())
104+
if err != nil {
105+
return err
106+
}
107+
108+
bufferSize := b.cfg.UploadBufferSize
109+
maxBuffers := b.cfg.UploadBufferCount
110+
_, err = azblob.UploadStreamToBlockBlob(ctx, bytes.NewReader(buf), blockBlobURL,
111+
azblob.UploadStreamToBlockBlobOptions{BufferSize: bufferSize, MaxBuffers: maxBuffers})
112+
113+
if err != nil {
114+
return err
115+
}
116+
}
117+
return nil
118+
}
119+
120+
func (b *BlobStorage) getBlobURL(blobID string) (azblob.BlockBlobURL, error) {
121+
122+
blobID = strings.Replace(blobID, ":", "-", -1)
123+
124+
//generate url for new chunk blob
125+
u, err := url.Parse(fmt.Sprintf(blobURLFmt, b.cfg.AccountName, b.cfg.ContainerName, blobID))
126+
if err != nil {
127+
return azblob.BlockBlobURL{}, err
128+
}
129+
credential, err := azblob.NewSharedKeyCredential(b.cfg.AccountName, b.cfg.AccountKey)
130+
if err != nil {
131+
return azblob.BlockBlobURL{}, err
132+
}
133+
134+
return azblob.NewBlockBlobURL(*u, azblob.NewPipeline(credential, azblob.PipelineOptions{
135+
Retry: azblob.RetryOptions{
136+
Policy: azblob.RetryPolicyExponential,
137+
MaxTries: (int32)(b.cfg.MaxRetries),
138+
TryTimeout: b.cfg.RequestTimeout,
139+
RetryDelay: b.cfg.MinRetryDelay,
140+
MaxRetryDelay: b.cfg.MaxRetryDelay,
141+
},
142+
})), nil
143+
}

pkg/chunk/storage/factory.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/cortexproject/cortex/pkg/chunk"
1111
"github.com/cortexproject/cortex/pkg/chunk/aws"
12+
"github.com/cortexproject/cortex/pkg/chunk/azure"
1213
"github.com/cortexproject/cortex/pkg/chunk/cache"
1314
"github.com/cortexproject/cortex/pkg/chunk/cassandra"
1415
"github.com/cortexproject/cortex/pkg/chunk/gcp"
@@ -33,13 +34,14 @@ type StoreLimits interface {
3334

3435
// Config chooses which storage client to use.
3536
type Config struct {
36-
Engine string `yaml:"engine"`
37-
AWSStorageConfig aws.StorageConfig `yaml:"aws"`
38-
GCPStorageConfig gcp.Config `yaml:"bigtable"`
39-
GCSConfig gcp.GCSConfig `yaml:"gcs"`
40-
CassandraStorageConfig cassandra.Config `yaml:"cassandra"`
41-
BoltDBConfig local.BoltDBConfig `yaml:"boltdb"`
42-
FSConfig local.FSConfig `yaml:"filesystem"`
37+
Engine string `yaml:"engine"`
38+
AWSStorageConfig aws.StorageConfig `yaml:"aws"`
39+
AzureStorageConfig azure.BlobStorageConfig `yaml:"azure"`
40+
GCPStorageConfig gcp.Config `yaml:"bigtable"`
41+
GCSConfig gcp.GCSConfig `yaml:"gcs"`
42+
CassandraStorageConfig cassandra.Config `yaml:"cassandra"`
43+
BoltDBConfig local.BoltDBConfig `yaml:"boltdb"`
44+
FSConfig local.FSConfig `yaml:"filesystem"`
4345

4446
IndexCacheValidity time.Duration
4547

@@ -49,6 +51,7 @@ type Config struct {
4951
// RegisterFlags adds the flags required to configure this flag set.
5052
func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
5153
cfg.AWSStorageConfig.RegisterFlags(f)
54+
cfg.AzureStorageConfig.RegisterFlags(f)
5255
cfg.GCPStorageConfig.RegisterFlags(f)
5356
cfg.GCSConfig.RegisterFlags(f)
5457
cfg.CassandraStorageConfig.RegisterFlags(f)
@@ -159,6 +162,8 @@ func NewObjectClient(name string, cfg Config, schemaCfg chunk.SchemaConfig) (chu
159162
level.Warn(util.Logger).Log("msg", "ignoring DynamoDB URL path", "path", path)
160163
}
161164
return aws.NewDynamoDBObjectClient(cfg.AWSStorageConfig.DynamoDBConfig, schemaCfg)
165+
case "azure":
166+
return azure.NewBlobStorage(&cfg.AzureStorageConfig), nil
162167
case "gcp":
163168
return gcp.NewBigtableObjectClient(context.Background(), cfg.GCPStorageConfig, schemaCfg)
164169
case "gcp-columnkey", "bigtable", "bigtable-hashed":
@@ -170,7 +175,7 @@ func NewObjectClient(name string, cfg Config, schemaCfg chunk.SchemaConfig) (chu
170175
case "filesystem":
171176
return local.NewFSObjectClient(cfg.FSConfig)
172177
default:
173-
return nil, fmt.Errorf("Unrecognized storage client %v, choose one of: aws, cassandra, inmemory, gcp, bigtable, bigtable-hashed", name)
178+
return nil, fmt.Errorf("Unrecognized storage client %v, choose one of: aws, azure, cassandra, inmemory, gcp, bigtable, bigtable-hashed", name)
174179
}
175180
}
176181

vendor/contrib.go.opencensus.io/exporter/ocagent/.gitignore

Lines changed: 0 additions & 17 deletions
This file was deleted.

vendor/contrib.go.opencensus.io/exporter/ocagent/.travis.yml

Lines changed: 0 additions & 18 deletions
This file was deleted.

vendor/contrib.go.opencensus.io/exporter/ocagent/CONTRIBUTING.md

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)