Spring BootアプリケーションでAWS Systems Manager パラメータストアを利用する

Spring Bootのアプリケーション起動時に、AWS Systems Manager パラメータストアからパラメータを取得できるみたいなので試してみました。

AWS Systems Manager パラメータストアとは

設定データ管理と機密管理のための安全な階層型ストレージを提供してくれていて、パスワード、データベース文字列、AMI ID、ライセンスコードなどのデータをパラメータ値として保存できます。値はプレーンテキストまたは暗号化されたデータとして保存できます。詳しい説明は、公式ドキュメントを見てください。

サンプル

環境

Spring Boot等のバージョンは次の通りです。

Java 11
Gradle 6.6.1
Spring Boot 2.3.4.RELEASE
Spring Cloud Hoxton.SR8

パラメータの登録

登録するパラメータのkey

デフォルトだと次のような3階層で設定するようになっています。[prefix]/[name]_[アプリケーションのprofile(defaultの場合は省略可)]/[key]

設定値の抜粋です。(値はデフォルト値)

- パラメータストアから取得される全プロパティで共有されるプレフィクス。第1階層の値。
  - aws.paramstore.prefix=/config
- 全サービスで共有されるコンテキスト名。第2階層の値。
  - aws.paramstore.default-context=application
- コンテキスト名とプロファイルの区切り文字.
  - aws.paramstore.profile-separator=_
- パラメータストアを利用するかどうか
  - aws.paramstore.enabled=true

取得するパラメータを組み立てているロジックは AwsParamStorePropertySourceLocator#PropertySource<?> locate(Environment) にあります。

パラメータストアへの登録

パラメータストアにパラメータを登録します。今回は、DBへのアクセス情報を登録します。

/config/sample/spring.datasource.url=jdbc:mysql://localhost:33306/sample
/config/sample/spring.datasource.username=docker
/config/sample/spring.datasource.password=docker

依存関係

パラメータストアを利用するために追加する依存関係はこれだけです。

build.gradle

dependencies {
  ...
  
  implementation 'org.springframework.cloud:spring-cloud-starter-aws-parameter-store-config:2.2.4.RELEASE'
  ...
}

設定ファイル

appliaction.yml(application.properties)には何も書かなくてOKですが、bootstrap.ymlにいくつか設定を書く必要があります。

bootstrap.ymlの全体

spring:
  datasource:
    url:
    username:
    password:
cloud:
  aws:
    stack:
      auto: false
    region:
      auto: false
      static: ap-northeast-1
aws:
  paramstore:
    region: ${cloud.aws.region.static}
    default-context: sample

logging:
  level:
    com:
      amazonaws:
        util:
          EC2MetadataUtils: error

パラメータストアから取得するのでkeyのみ書きます。

spring:
  datasource:
    url:
    username:
    password:

ローカル環境でSpring Cloud AWSを使うためのおまじないです。アプリケーション起動時にメタデータからリージョン等を取得するのですが、ローカル環境だと取得に失敗してアプリケーションが起動しなくなってしまうために無効にしています。詳しくは、Configuring region と CloudFormation configuration in Spring Boot にあります。

cloud:
  aws:
    stack:
      auto: false
    region:
      auto: false
      static: ap-northeast-1

spring-cloud-starter-aws-parameter-store-configの設定です。

aws:
  paramstore:
    region: ${cloud.aws.region.static}
    default-context: sample

EC2MetadataUtilsがAWS上で動いているか取得しますが、AWS環境以外でのアプリケーション起動時にエラーログを出してしまって気持ち悪いので設定します。(なくても動作には何も問題ないです)

logging:
  level:
    com:
      amazonaws:
        util:
          EC2MetadataUtils: error

アプリケーションの起動

あとはアプリケーションを起動するだけです。

どのパラメータをパラメータストアから取得しているかを確認したい場合は、次の設定をbootstrap.ymlに書いてください。

logging:
  level:
    org:
      springframework:
        cloud:
          aws:
            paramstore:
              AwsParamStorePropertySource: debug

設定するとログにこんな感じで出力されます。

2020-10-11 18:35:27.847 DEBUG 55136 --- [           main] o.s.c.a.p.AwsParamStorePropertySource    : Populating property retrieved from AWS Parameter Store: spring.datasource.password
2020-10-11 18:35:27.849 DEBUG 55136 --- [           main] o.s.c.a.p.AwsParamStorePropertySource    : Populating property retrieved from AWS Parameter Store: spring.datasource.url
2020-10-11 18:35:27.849 DEBUG 55136 --- [           main] o.s.c.a.p.AwsParamStorePropertySource    : Populating property retrieved from AWS Parameter Store: spring.datasource.username

プロファイルで設定値を分ける

一般的に、defaultプロファイルしか存在しないことはないかと思います。testプロファイルでは、テスト環境用のDBアクセス情報を取得するみたいなことも可能です。

パラメータストアへの登録

パラメータストアへの登録の仕方については上で説明したので省略します。 これらを登録します。

/config/sample_test/spring.datasource.url=jdbc:mysql://localhost:33307/sample
/config/sample_test/spring.datasource.username=test
/config/sample_test/spring.datasource.password=test

アプリケーションの起動

アクティブプロファイルをtestにして、起動するだけで、 /config/sample_test/* のパラメータを取得してくれます。 デフォルトプロファイルの/config/sample/を読み込んでそのあとに/config/sample_testで上書きするようです。

まとめ

思っていたよりは簡単にできました!AWSのプロファイルはdefaultが使われるので変更したい場合は自前で設定書かないといけないみたいです。

ちなみに設定値の優先順位は パラメータストア > bootstram.yml > application.yml でした。bootstrap.ymlとかapplication.ymlに同じ設定書いてあってもパラメータストアに設定している場合は、パラメータストアの値が優先されます。