6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS Copilotを使用したサーバーレスな踏み台サーバーの構築

Last updated at Posted at 2023-05-23

概要

AWSを使用しているとVPC内のリソースにアクセスするため踏み台サーバーを構築する機会があるかと思います。
EC2を踏み台サーバーとする選択肢もあると思いますが、今回はFargateを使用して踏み台サーバーを構築し、ローカルからポートフォワードしてプライベートサブネット内に配置されたDBへ接続するまでを行います。
FargateのデプロイにはAWS Copilotを使用します。

目的

  • 踏み台サーバーにFargateを使用してサーバーの管理コストを下げる
  • AWS Copilotを使用してコンテナのデプロイを簡略化する

前提

  • AWS CopilotでVPCを作成済み
    • copilot app init example
    • copilot env init --app example --name dev --default-config
    • copilot env deploy --app example --name dev
  • 作成したVPCのプライベートサブネットにRDBを作成済み
  • RDBに接続可能なセキュリティグループを作成済み
セキュリティグループの作成

addonsでAWSリソースを作成する場合の例
copilot/envilonments/dev/addons/rds.yml

Parameters:
  App:
    Type: String
    Description: Your application's name.
  Env:
    Type: String
    Description: The environment name your service, job, or workflow is being deployed to.

Resources:
# ~~ RDSの作成部分省略 ~~

  AllowDBAccessSecurityGroup:  # DBに接続するため踏み台サーバーにアタッチするセキュリティグループ
    Metadata:
      'aws:copilot:description': 'A security group for your workload to access the db'
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: !Sub 'The Security Group for ServiceCluster to access ${App}-${Env}-rds.'
      GroupName: !Sub ${App}-${Env}-allow-db-access-security-group
      VpcId:
        Fn::ImportValue:
          !Sub '${App}-${Env}-VpcId'

  DBClusterSecurityGroup:
    Metadata:
      'aws:copilot:description': 'A security group for your db'
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub The Security Group for the ${App}-${Env}-rds.
      GroupName: !Sub ${App}-${Env}-DBSecurityGroup
      SecurityGroupIngress:
        - ToPort: 3306
          FromPort: 3306
          IpProtocol: tcp
          Description: !Sub 'From the Aurora Security Group of the workload.'
          SourceSecurityGroupId: !Ref AllowDBAccessSecurityGroup
      VpcId:
        Fn::ImportValue:
          !Sub '${App}-${Env}-VpcId'

Outputs:
  AllowDBAccessSecurityGroupId:
    Description: Id of Service Cluster Security Group from environments addons.
    Value: !Ref AllowDBAccessSecurityGroup
    # DBと接続が必要なコンテナのCloudFormationでimportするためexportしておく
    Export:
      Name: !Sub ${App}-${Env}-AllowDBAccessSecurityGroup

踏み台サーバーの構築

ディレクトリ構成

copilot
├── bastion
│   └── Dockerfile
└── environments
    └── dev
        └── manifest.yml

copilot/bastion/Dockerfile

FROM gcr.io/distroless/static-debian11:debug

Dockerfileを作成したらcopilot svc initコマンドでサービスを初期化します。
サービスのタイプはBackend Serviceを選択します。

copilot svc init \
--name bastion \
--dockerfile './copilot/bastion/Dockerfile' \
--svc-type 'Backend Service'

すると、copilot/bastion/manifest.ymlというファイルができるので、中身を編集します。

#  https://aws.github.io/copilot-cli/docs/manifest/backend-service/
name: bastion
type: Backend Service

image:
  build: 
    dockerfile: ./copilot/bastion/Dockerfile

cpu: 256
memory: 512
platform: linux/x86_64
count: 1
exec: true

network:
  vpc:
    security_groups:
      groups: # ここにRDSと接続可能なセキュリティグループを設定する
        - 'sg-xxxxxx'
        # CloudFormationのexportから取得する場合
        # - from_cfn: ${COPILOT_APPLICATION_NAME}-${COPILOT_ENVIRONMENT_NAME}-AllowDBAccessSecurityGroup

# タスク定義の上書き
taskdef_overrides:
  - path: ContainerDefinitions[0].PseudoTerminal
    value: true

ポイントは以下です.

  • exec: true
    • コンテナ内部に入れるように設定
  • network.vpc.security_groups.groups
    • プライベートサブネット内に配置したRDSに接続できるセキュリティグループを追加
  • taskdef_overridesの設定
    • 今回のDockerfileの書き方の場合、デプロイ後にすぐサービスが終了してしまうので永続化の設定が必要
      ローカルで実行する場合のdocker run --ttyに相当する設定を追加することで起動したままにします

デプロイ

copilot svc deploy --app example --env dev --name bastion

これでデプロイできたので次はコンテナに接続していきます。

Session Manager

踏み台サーバーへの接続にはSSHではなくSession Managerを使用します。
SSH接続のために鍵やネットワークの管理をする必要がなくなるのでより運用コストを下げることができます。
また、ポートを許可する必要もないのでセキュリティ面でのメリットもあります。

ローカルからの接続にはSession Manager Pluginが必要なので入っていなければインストールします。
AWS CLI 用の Session Manager プラグインをインストールする

# Homebrewからインストールする場合
brew install --cask session-manager-plugin

踏み台サーバーに接続するだけであれば、copilotのコマンド経由でコンテナに入れます。

copilot svc exec --app example --env dev --name bastion

なのでコンテナに入ることだけが目的であればここまでで実現できます。

今回はポートフォワーディングが目的なのでもう少し進めます。

ポートフォワード

copilot svc execでポートフォワーディングするオプションはなさそうなのでAWS CLISession Manager Pluginを使用して接続します。

ひとまずSession Manager Plugin経由でコンテナに入れるかを試します。

コンテナとの接続を開始するにはaws ssm start-sessionコマンドを使用します。
--targetオブションの引数にはecs:<cluster_id>_<task>_<runtime_id>で接続先のコンテナを指定します。

aws ssm start-session \
--target ecs:<cluster_name>_<task_id>_<runtime_id>
cluster_idの取得

copilot env deploy --name devでenvをデプロイしていると、cluster_idがCloudFormationでexportされているので下記で取得できます。

aws cloudformation list-exports | jq -r '.Exports[] | select(.Name == "example-dev-ClusterId").Value'
task_idの取得
copilot svc status -a example -e dev -n bastion --json | jq -r '.tasks[].id'
runtime_idの取得
aws ecs describe-tasks \
--cluster <cluster_id> \
--tasks <task_id> \
| jq -r '.tasks[].containers[].runtimeId'

上記でコンテナに入れることを確認できたと思いますので続けてポートフォワードを試します。
ポートフォワーディングする際のオプションはこちらを参考に、--document-name--parametersを指定します。

aws ssm start-session \
--target ecs:<cluster_name>_<task_id>_<runtime_id> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host": ["<RDBのエンドポイント>"], "portNumber": ["<RDBのポート番号>"], "localPortNumber": ["<ローカルのポート番号>"]}'

これで接続が開始されたと思うので、あとはローカルからAWS上のDBへ接続を試します。

mysql -h 127.0.0.1 -u user -P <ローカルのポート番号> -p

mysql>

最後に

以上、サーバーレスな踏み台サーバーを構築する方法を紹介しました。
どなたかの参考になれば幸いです。
あと、bastionの発音は「バスチャン」らしいです。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?