🐷

GoでAWS Auroraを操作する

2024/03/31に公開

やろうとしたこと

オートスケーリングとは別でアプリケーションからAWS auroraインスタンスを増減するようにしたかった

使用したパッケージ

https://github.com/aws/aws-sdk-go-v2/service/rds
RDSも操作できる。
Auroraに対応していないメソッドや関数があるのでそれはドキュメントを見つつ確認する

参考にしたドキュメント

前提として

  1. AWS IAMにて操作を行うためのアカウント、ポリシーの許可、アクセスキーの発行を行う
  2. 後述するコードに使用する設定、認証ファイルの作成
    IAMで作成したユーザーの名前を「test-aurora」と仮定します。
    それぞれ/configディレクトリ以下に作成(ドキュメントに記載あり)
[profile test-aurora]
region = ap-northeast-1
output = json
[test-aurora]
aws_access_key_id = 発行されたアクセスキー
aws_secret_access_key = シークレットキー

全体のコード

クリーンアーキテクチャを想定しての記述となるため一部を抜粋しています。

  1. 環境変数のGoファイル
config/config.go
package config

type Config struct {
    AbsolutePath string
    Aurora struct {
        ClusterName string
        DBInstancePrefix string
    }
}

func NewConfig() *Config {
    c := new(Config)

    c.AbsolutePath = "/var/www/html"

    c.Aurora.ClusterName = "test-aurora-cluster
    c.Aurora.DBInstancePrefix = "test-aurora-instance-"

    return c
}
  1. ロジック内で値を扱うための構造体(ここは必要に応じて追加する)
domain/aws_aurora_db_instances.go
package domain

type AwsAuroraDBInstances struct {
	ClusterName string
	InstanceName string
	Engine string
	DBInstanceClass string
	DBParameterGroupName string
}
  1. Auroraを操作するGoファイル
    ここでは主にAWSクライアントへの接続、インスタンスの取得、作成、削除を行なっている(ロジック内で扱うように返り値は加工しています。)
infrastructure/aurora.go
package infrastructure

import (
	"context"
	"fmt"

	"github.com/sample-aurora/domain"
	"github.com/sample-aurora/config"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/rds"
)

type AwsAurora struct {
	AuroraClient *rds.Client
	ClusterName string
	DBInstancePrefix string
}

func NewAwsAurora(c *config.Config) *AwsAurora {
	return newAwsAurora(c)
}

func newAwsAurora(c *config.Config) *AwsAurora {
	sdkConfig, err := config.LoadDefaultConfig(context.TODO(),
		config.WithSharedCredentialsFiles(
			[]string{
				c.AbsolutePath + "/config/credentials", // staging
			},
		),
		config.WithSharedConfigFiles(
			[]string{
				c.AbsolutePath + "/config/config",
			},
		),
		config.WithSharedConfigProfile("sdk-aurora"))
	if err != nil {
		fmt.Println(err)
		panic("Couldn't load default configuration. Have you set up your AWS account?")
	}
	auroraClient := rds.NewFromConfig(sdkConfig)

	a := &AwsAurora{
		ClusterName: c.Aurora.ClusterName,
		AuroraClient: auroraClient,
		DBInstancePrefix: c.Aurora.DBInstancePrefix,
	}
	return a
}


// クラスター内のReader Instanceを取得する
func (a *AwsAurora) GetDbInstanceInCluster() (instances []domain.AwsAuroraDBInstances, err error) {
	output, err := a.AuroraClient.DescribeDBClusters(context.TODO(),
		&rds.DescribeDBClustersInput{
			DBClusterIdentifier: aws.String(a.ClusterName),
		})
	if err != nil {
		return []domain.AwsAuroraDBInstances{}, err
	}

	instances = []domain.AwsAuroraDBInstances{}
	for _, cluster := range output.DBClusters {
		for _, member := range cluster.DBClusterMembers {
			// リードレプリカのみ処理を行う
			if !*member.IsClusterWriter {
				identifer := *member.DBInstanceIdentifier
				output, err := a.AuroraClient.DescribeDBInstances(context.TODO(),
					&rds.DescribeDBInstancesInput{
						DBInstanceIdentifier: aws.String(identifer),
					})
				if err != nil {
					return []domain.AwsAuroraDBInstances{}, err
				}
				engine := *output.DBInstances[0].Engine
				instanceClass := *output.DBInstances[0].DBInstanceClass
				parameterGroupName := *output.DBInstances[0].DBParameterGroups[0].DBParameterGroupName

				instance := domain.AwsAuroraDBInstances{}
				instance.ClusterName = a.ClusterName
				instance.InstanceName = identifer
				instance.Engine = engine
				instance.DBInstanceClass = instanceClass
				instance.DBParameterGroupName = parameterGroupName

				instances = append(instances, instance)
			}
		}
	}

	return instances, err
}

func (a *AwsAurora) CreateInstanceInCluster(instance domain.AwsAuroraDBInstances) error {
	_, err := a.AuroraClient.CreateDBInstance(context.TODO(), &rds.CreateDBInstanceInput{
		DBInstanceIdentifier: aws.String(fmt.Sprintf("%s%s", a.DBInstancePrefix, instance.InstanceName)),
		DBClusterIdentifier:  aws.String(instance.ClusterName),
		Engine:               aws.String(instance.Engine),
		DBInstanceClass:      aws.String(instance.DBInstanceClass),
		DBParameterGroupName: aws.String(instance.DBParameterGroupName),
	})
	if err != nil {
		return err
	}
	return nil
}

func (a *AwsAurora) DeleteDBInstance(instanceName string) error {
	_, err := a.AuroraClient.DeleteDBInstance(context.TODO(), &rds.DeleteDBInstanceInput{
		DBInstanceIdentifier:   aws.String(a.DBInstancePrefix + instanceName),
		SkipFinalSnapshot:      aws.Bool(true),
		DeleteAutomatedBackups: aws.Bool(true),
	})
	if err != nil {
		return err
	}
	return nil
}

個人的に困った箇所

環境設定ごとに対応できるようにした

sdkConfig, err := config.LoadDefaultConfig(context.TODO(),
		config.WithSharedCredentialsFiles(
			[]string{
				c.AbsolutePath + "/config/credentials", // staging
			},
		),
		config.WithSharedConfigFiles(
			[]string{
				c.AbsolutePath + "/config/config",
			},
		),
		config.WithSharedConfigProfile("sdk-aurora"))

Discussion