Developers.IO Cafe でのAWSアカウントとステージの構成

渡辺です。

Developers.IO Cafe のドキュメントシリーズ第4弾は、AWSの環境周り、つまりAWSアカウントの構成について解説します。

ステージ毎にAWSアカウントを分ける

カフェでは、プロダクション環境(PRD)、ステージング環境(STG)、インテグレーション環境(ITG)、開発環境(DEV)のステージ(環境)を持ちます。 原則としては、 このように プロダクトをステージに分ける場合、AWSアカウントを分割する ことがセオリーです。

カフェではさらにIoTアカウントを作成しています(後述)。

AWSアカウントを分割した場合のメリットは多くあります。

各環境でアクセスポリシーが明確になる

例えばIAMでログイン可能なユーザを明確にすることができます。 他の環境へのアクセスは許可しなければできません。 間違えてプロダクションを操作といった誤操作を防止できます。

リソースが混在しない

カフェは、SaaSとしてシステムを提供することを目標としています。 このため、リソースもかなり多く、各ステージ毎のリソースが同じAWSアカウントに混在するのは危険です。

コストを分けやすい

AWS環境が別れれば、コスト分析も簡単です。 同一AWS環境でも不可能ではありませんが、手間が違います。

デメリットも幾つかあります。

作るのが面倒くさい

アカウント発行は手間ですが、ウチの会社はAWSで食っているので・・・(笑)

共有リソースを使いにくい

仕方なく各ステージをひとつのAWS環境で使うケースのひとつがVPN接続です。 VPCやVPNが絡んでくると、AWSアカウントを分割することで、必要以上のコストがかかり、断念することがあります。 カフェはサーバレスアーキテクチャで、VPNなどとも無縁なため、発生しません。

CloudFormationによる環境構築

AWSアカウントやステージが増えた場合に問題となるのが、環境構築です。 IAM Role、DynamoDBテーブル、StepFunction、S3バケットといったリソースを手動で作ったら、確実にミスを犯します。 このため、原則としてすべてのリソースはCloudFormationで定義する方針となります。

例外となるリソースとしては、Route53等ドメイン周りのリソースです。 SSL証明書の発行やドメイン設定などはCloudFormationで行うのは辛く、一度設定すれば二度と変更も無いため、手動で行っています。

AWSアカウントを作成し、CloudFormationを流せば、新しい開発環境を簡単に構築できます。

IoT関連リソースはステージに紐付くべきか?

カフェのシステムでは、IoTデバイスと接続し、重量データや位置データをAWSに送信しています。

ある重量センサーがあった時、そのセンサーはどのステージにデータを投げるのでしょうか?

結論としては、センサー(物理)はステージを意識しない構成がベターです。

ステージ毎に証明書を切り替えるのが辛い

あるデバイスが、 PRD環境とSTG環境のどちらにもデータを送信できる のが理想です。 この時、PRD環境とSTG環境で、それぞれ証明書を作る必要があります。

それぞれの証明書をデバイスにインストールし、送信先AWSアカウント(トピック)の設定も行い、それを切り替える機能が必要です。

デバイス(物理)でやるのは辛いです。 Greengrassを利用するといった手段も考えられますが、Greengrassの環境がステージ毎に別れていては意味がありません。

モノはモノ、クラウドはクラウド

デバイスにインストールする証明書はひとつにし、送信先のAWSアカウントも変更したくない。 かつ、データ送信先のステージを切り替えたい。 でも、AWSアカウントはステージ毎に分割したい・・・。

それらを考慮すると、 IoTデバイスでデータを送信する専用のAWSアカウントを作成する ことがベターとなります。 結局、モノはモノとしてデータを投げる先は固定である方が自然なのです。

なお、VPN問題もこのパターンで解決する場合があります。 VPN専用AWSアカウントを作成し、各ステージを持つAWSアカウントのVPCとVPC Peeringする構成です。

IoT ActionsでクロスアカウントでLambdaを実行する

IoTデータはトピックに投げられた後、IoT ActionsでLambdaに渡されて処理されます。 ぱっと見、ドキュメントもありませんし、IoTの設定画面からはできませんが、設定自体はLambdaはARNで指定します。

クロスアカウントでいけそう。

確認した所、 実行されるLambdaに適切なポリシーを与えれば、クロスアカウントでIoT Actionを実行出来ました 。 これで、IoT Actionを介して、各ステージのAWS環境にデータを送信することが実現出来ます。 送信先ステージを変更する場合は、IoT Actionの設定を変更するだけなので、物理デバイスには一切の変更は不要です。

Lambdaアカウント(yyyyyyyyyyyy)側

  SensorLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: sensor_xxx
      Action: lambda:InvokeFunction
      Principal: iot.amazonaws.com
      SourceAccount: "XXXXXXXXXXXXXXXX"
      SourceArn: !Sub arn:aws:iot:${AWS::Region}:XXXXXXXXXXXXXXXX:rule/action_name

IoTアカウント(XXXXXXXXXXXXXXXX)側

  Rule:
    Type: AWS::IoT::TopicRule
    Properties:
      RuleName: sensor_xxx
      TopicRulePayload:
        Sql:SELECT * FROM 'sensor_xxx'
        Actions:
          - Lambda:
              FunctionArn: !Sub arn:aws:lambda:${AWS::Region}:yyyyyyyyyyyy:function:sensor_xxx

まとめ

AWSアカウントはステージ毎に作成しています。 各ステージのリソースは原則としてCloudFormationで管理しています。

ただし、IoTデバイスと接続する専用のAWSアカウントがあります。 センサーからのデータは、このAWSアカウントをGatewayとして、設定されたステージのAWSアカウントに送られます。