使用aws-sdk-go-v2连接Ceph的对象存储服务

1,951 阅读2分钟

Ceph兼容S3 API,这意味着可以直接使用S3 Rest API进行连接,不过也有已经封装的SDK可供选择。

简单来说,如果需要使用aws-sdk连接ceph,主要是需要获取以下三个值:

  1. AK
  2. SK
  3. Ceph rgw暴露的端口

以下使用Rook部署的方法来讲解如何获取这四个值。

获取Ceph用户配置

要想获取AK和SK很简单,首先需要保证已经创建了一个用户

然后根据已创建的服务来获取秘钥,比如对于官方教程

kubectl -n rook-ceph get secret rook-ceph-object-user-my-store-my-user -o jsonpath='{.data.AccessKey}' | base64 --decode
kubectl -n rook-ceph get secret rook-ceph-object-user-my-store-my-user -o jsonpath='{.data.SecretKey}' | base64 --decode

rook-ceph 定义在cluster.yaml这样的配置文件中,用户名定义在metadataname一栏。

RGW可以通过端口转发的方式来进行访问,虽然Rook官网有提到需要部署成NodePort类型来提供外部访问接口,但是直接转发似乎也可以达到类似的效果

kubectl -n rook-ceph port-forward --address 0.0.0.0 svc/rook-ceph-rgw-my-store 7080:80

这样就可以通过访问虚拟机的IP来访问RGW了。

配置AWS-SDK环境变量

现在假设已经获取到了这些值:

{
  AK: "926R3V2NRO9G8JKTL11X",
  SK: "IrQyBhf8EMU6e4uFBarrOTerWza4lhBCibvxW7Rx",
  EndpointURL: "http://192.168.130.135:7080"
}

将AK和SK设置至$HOME/.aws/credentials(Linux、MACOS)或%USERPROFILE%\.aws\credentials(Windows)

# ~/.aws/credentials
[default]
aws_access_key_id=926R3V2NRO9G8JKTL11X
aws_secret_access_key=IrQyBhf8EMU6e4uFBarrOTerWza4lhBCibvxW7Rx

使用aws-sdk-go-v2

由于使用的主要是对象存储,因此需要安装以下三个依赖

go get -u "github.com/aws/aws-sdk-go-v2/aws"
go get -u "github.com/aws/aws-sdk-go-v2/config"
go get -u "github.com/aws/aws-sdk-go-v2/service/s3"

Endpoint的配置需要单独写一个Resolver

endpointURL := "http://192.168.130.135:7080"

customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
   return aws.Endpoint{
      URL: endpointURL,
   }, nil
})

然后使用这个Resolver创建一个S3客户端

cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
   panic(err)
}

svc := s3.NewFromConfig(cfg, func(options *s3.Options) {
   options.UsePathStyle = true
})

需要注意的是Ceph的S3 Rest API和AWS的并不完全一致,比如下面两个创建桶的操作

# Ceph
PUT /{bucket} HTTP/1.1
Host: cname.domain.com
# AWS S3 REST API
PUT / HTTP/1.1
Host: `Bucket`.s3.amazonaws.com

可以看到AWS的桶名放在了域名中,所以需要设置options.UsePathStyle = true

桶操作

列出所有桶

func listBuckets(svc *s3.Client) {
   out, err := svc.ListBuckets(context.TODO(), nil)
   if err != nil 
      panic(err)
   }

   fmt.Print("[")
   for i := range out.Buckets {
      if i > 0 {
         fmt.Print(", ")
      }
      bkt := out.Buckets[i]
      fmt.Print(*bkt.Name)
   }
   fmt.Print("]")
}

创建桶

func createBucket(svc *s3.Client, bktName string) {
   bktInput := s3.CreateBucketInput{
      Bucket: &bktName,
   }

   _, err := svc.CreateBucket(context.TODO(), &bktInput)
   if err != nil {
      panic(err)
   }
}

删除桶

func deleteBucket(svc *s3.Client, bktName string) {
   bktInput := s3.DeleteBucketInput{
      Bucket: &bktName,
   }

   _, err := svc.DeleteBucket(context.TODO(), &bktInput)
   if err != nil {
      panic(err)
   }
}

对象操作

列出桶中的对象

// 假设已经存在 Bucket: "test-bucket"

bkt := "test-bucket"
bktInput := s3.ListObjectsV2Input{
   Bucket: &bkt,
}
out, err := svc.ListObjectsV2(context.TODO(), &bktInput)
if err != nil {
   panic(err)
}
for i := range out.Contents {
   obj := out.Contents[i]
   fmt.Printf("out: %v\n", *obj.Key)
}