ローカルの開発コンテナから、AWS Systems Manager (SSM) のポートフォワードで RDS などのプライベートリソースへ接続したいケースがありました。
当初はenvにアクセスキーとシークレットキーとスイッチロールのARNを含めて、ビルド時にそれらの値を取得してconfigとcredentialsを作成していました。

しかしメンバーにそれぞれ認証情報をenvに入れてもらうのも手間ですし、そもそもホスト側の.awsフォルダをマウントすれば良いと思い、認証情報をDockerfileに渡さずに、ホスト側の .aws(Profile/Role 設定)を 読み取り専用でマウントして利用する方法の紹介です。

前提

  • ホスト(macOS 等)に.aws/configと.aws/credentialsがある
  • 使いたいプロファイル名がホスト側の AWS 設定に存在している
  • Docker コンテナ内に以下が入っている
    • AWS CLI v2
    • session-manager-plugin

docker-composeファイル

.awsを:roでマウントすると安全ではありますが、AWS CLI はデフォルトで ~/.aws/cli/cacheにキャッシュを書き込みます。
そのままだとRead-only file systemで失敗するため、/root/.aws/cli/cacheだけは書き込み可能なボリュームを重ねます。

【docker-compose.yaml例】

services:
  api:
    volumes:
      - ./app:/app
      - ${HOME}/.aws:/root/.aws:ro
      - aws-cli-cache:/root/.aws/cli/cache
    environment:
      AWS_SDK_LOAD_CONFIG: "1"
      AWS_CACHE_DIR: /root/.aws/cli/cache

volumes:
  aws-cli-cache:

【ポイント】

  • ~/.awsは:roのまま
  • 書き込みが必要なcli/cacheだけDocker volume で上書き
  • AWS_SDK_LOAD_CONFIG=1を明示(SDKにconfigを見に行かせる)
  • AWS_CACHE_DIRにてAWS CLI のキャッシュ先を指定

プロファイル名は環境変数で渡す

AWS_PROFILEはenvでコンテナに渡します。

AWS_PROFILE=PROFILE名

コンテナをビルド&起動

docker compose up --build -d

コンテナ内で SSM ポートフォワードを開始

別ターミナルでコンテナに入り、SSM トンネルを起動します。

docker exec -it コンテナ名 /bin/bash

RDS(3306)へフォワードする場合(Fargateを踏み台としてます。)

aws ssm start-session \
  --profile "$AWS_PROFILE" \
  --target "ecs:<cluster>_<task>_<runtime-id>" \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters 'portNumber=3306,localPortNumber=3306,host=<rds-endpoint>'
  • localPortNumber=3306はコンテナ内のlocalhost:3306を開きます
  • コンテナにてDB_HOST=localhostDB_PORT=3306でDBに接続できるようになります

今回あったエラー

[Errno 30] Read-only file system: ‘/root/.aws/cli/cache/…json

【原因】

  • .awsを:roでマウントしているのに、AWS CLI が~/.aws/cli/cacheに書き込もうとして失敗

【対策】

  • aws-cli-cache:/root/.aws/cli/cacheのように、キャッシュディレクトリだけ書き込み可能ボリュームで上書きする
  • 併せてAWS_CACHE_DIR=/root/.aws/cli/cacheを指定してキャッシュ先を指定する