ポートフォワードするまでを自動化するシェルファイルを作ってみた

EC2とRDSのcli操作をシェルファイルにまとめてポートフォワードするまでを自動化してみました
2020.08.11

こんにちは、cli操作とシェルファイルの作成にはまっている下地です。

以前、EC2を踏み台にAmazonRDS(RDS)にポートフォワードをする記事(ポートフォワードを行いローカル環境からプライベートサブネットのAmazonRDSを操作する)を書きました。本日はEC2とRDSの立ち上げからポートフォワードするまでを自動化するシェルスクリプトを作成したのでまとめたいと思います。

前提

AWS CLIがインストール済み(AWS CLI バージョン 2 のインストール)かつ、EC2にsshできEC2からRDSに接続できる環境があることを前提で進めます。

全体像

以下図のように、EC2とRDSの起動状態を確認し両方停止なら起動処理・ポートフォワード接続を行い、両方起動なら停止処理をします。また起動中もしくは停止中の場合は何もしないように設定します。

ポートフォワードをする際は最終的に以下のコマンドを実行するため、EC2のIPv4パブリックIP、RDSエンドポイント、RDSポート番号を取得する必要があります。

$ ssh -i ~/.ssh/*******.pem ec2-user@パブリックIP -L (頭に1をつける)RDSポート番号:[RDSエンドポイント]:RDSポート番号

上記を実行するために今回は以下の点について実装確認を行います。

  • シェルファイルの作成
  • wait
  • query

シェルファイルの作成

シェルファイルでcliコマンドの実行確認をします。接続情報を変数に格納しEC2の起動確認を行います。

sample.sh

#!/usr/bin/bash
pem_path="~/**/***.pem"          #pemキーパス
profile="****"                   #profile
ec2_instance="i-***************" #ec2インスタンスのid

aws ec2 start-instances --instance-ids ${ec2_instance} --profile ${profile} #EC2起動

実行します。

$ bash sample.sh 
{
    "StartingInstances": [
        {
            "CurrentState": {
                "Code": 16,
                "Name": "running"
            },
            "InstanceId": "i-***************",
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        }
    ]
}

起動処理が実行できました。しかし戻り値の表示は不要です。こちらのサイト(コマンドの実行結果を変数に代入)に以下のように記載がありました。

バッククォート(`)で文字列中のコマンドを実行することができます。 これを使って変数にコマンドの実行結果を代入します。

上記を参考にEC2起動時の戻り値をec2_startに実行結果を代入するように修正します。

sample.sh

ec2_start=`aws ec2 start-instances --instance-ids ${ec2_instance} --profile ${profile}`

EC2とRDSの条件によって処理する内容を変更する必要があるため条件分岐(if文)について確認します。

sample.sh

#!/usr/bin/bash
ec2="running"
rds="available"
if [ `echo ${ec2} | grep 'stopped'` ] && [ `echo ${rds} | grep 'stopped'` ] ; then
  echo "EC2とRDSを起動し、ポートフォワードします"
elif [ `echo ${ec2} | grep 'running'` ] && [ `echo ${rds} | grep 'available'` ] ; then
  echo "EC2とRDSを停止します"
else
  echo "EC2・RDSが起動中もしくは停止中です。確認してください。"
fi

EC2とRDSが起動している条件を入力し分岐の確認をします。

$ bash sample.sh
EC2とRDSを停止します

起動時は、停止処理を行う分岐に入る事を確認できました。

シェルスクリプトでcliコマンドを実行すること、条件分岐処理を確認することができました。

waitコマンド

起動処理時にはEC2とRDSの起動を確認してポートフォワードを実行する必要があります。そこで特定のステータスになるまでポーリングするwaitコマンドを使用します。こちらの記事(AWS CLIのWaitersによる待ち受け処理を実装する)を参考に設定します。

aws ec2 start-instancesにてEC2を立ち上げた後、aws ec2 wait instance-runningを使用することで、EC2が立ち上がるまで処理をポーリングします。起動確認後、EC2の詳細を表示します。

sample.sh

#!/usr/bin/bash
pem_path="~/**/***.pem"          #pemキーパス
profile="****"                   #profile
ec2_instance="i-***************" #ec2インスタンスのid

ec2_start=`aws ec2 start-instances --instance-ids ${ec2_instance} --profile ${profile}`
aws ec2 wait instance-running --instance-ids ${ec2_instance} --profile ${profile}
echo "起動処理完了"
aws ec2 describe-instances --instance-ids ${ec2_instance} --profile ${profile}

以下のようにEC2起動後に詳細を確認することができました(詳細データは長くなるので省略します)。

$ bash sample.sh
起動処理完了
{
    "Reservations": [
        {
            "Groups": [],
            "Instances": [
            ・・・

query

ec2 describe-instancesを使用してEC2の詳細を確認できました。しかし、ポートフォワードするためには、EC2のIPv4パブリックIPを取得する必要があります。そこで、queryパラメータを追加してIPv4パブリックIPのみを取得します(AWS CLI からのコマンド出力の制御)。

--query 'Reservations[].Instances[].PublicIpAddress|[0]を追加し、ec2 describe-instancesからIPアドレスのみを取得します。

address=`aws ec2 describe-instances --instance-ids ${ec2_instance} --query 'Reservations[].Instances[].PublicIpAddress|[0]' --profile ${profile}`

EC2のIPv4パブリックIPを取得することができました。

$ "**.***.***.***"

シェルファイル全体像

必要なコマンドの実行について確認できました。今回使用するシェルスクリプトは最終的に以下のようになりました。

sample.sh

#!/usr/bin/bash

pem_path="~/**/***.pem"          #pemキーパス
profile="****"                   #profile
ec2_instance="i-***************" #ec2インスタンスのid
rds_cluster="*********"     #RDSインスタンス名

#条件確認
ec2=`aws ec2 describe-instances --instance-ids ${ec2_instance} --query 'Reservations[].Instances[].State.Name|[0]' --profile ${profile}`
rds=`aws rds describe-db-instances --db-instance-identifier ${rds_cluster} --query 'DBInstances[].DBInstanceStatus|[0]' --profile ${profile}`

echo "ec2: ${ec2} || rds: ${rds}"

if [ `echo ${ec2} | grep 'stopped'` ] && [ `echo ${rds} | grep 'stopped'` ] ; then
  echo "EC2とRDSを起動し、ポートフォワードします"
  #ec2起動とipアドレス取得
  echo "EC2を起動します"
  ec2_start=`aws ec2 start-instances --instance-ids ${ec2_instance} --profile ${profile}`
  aws ec2 wait instance-running --instance-ids ${ec2_instance} --profile ${profile}
  address=`aws ec2 describe-instances --instance-ids ${ec2_instance} --query 'Reservations[].Instances[].PublicIpAddress|[0]' --profile ${profile}`
  echo "EC2を起動しました。IPv4は${address}です"

  #rds起動とエンドポイント・ポート番号取得
  echo "RDSを起動します"
  rds_start=`aws rds start-db-instance --db-instance-identifier ${rds_cluster} --profile ${profile}`
  aws rds wait db-instance-available --db-instance-identifier ${rds_cluster} --profile ${profile}
  endpoint=`aws rds describe-db-instances --db-instance-identifier ${rds_cluster} --query 'DBInstances[].Endpoint.Address|[0]' --profile ${profile}`
  port=`aws rds describe-db-instances --db-instance-identifier ${rds_cluster} --query 'DBInstances[].Endpoint.Port|[0]' --profile ${profile}`
  echo "RDSを起動しました"
  echo "エンドポイント: ${endpoint}"
  echo "ポート番号: ${port}"

  #ssh接続
  connect="ssh -i ${pem_path} ec2-user@${address} -L 1${port}:${endpoint}:${port}"
  echo "実行コマンド:  ${connect}"
  ${connect}

elif [ `echo ${ec2} | grep 'running'` ] && [ `echo ${rds} | grep 'available'` ] ; then
  #起動時は停止処理を実施
  echo "EC2とRDSを停止します"
  ec2_end=`aws ec2 stop-instances --instance-ids ${ec2_instance} --profile ${profile}`
  rds_end=`aws rds stop-db-instance --db-instance-identifier ${rds_cluster} --profile ${profile}`

else
  echo "EC2・RDSが起動中もしくは停止中です。確認してください。"
fi

自動化確認

始める前にEC2とRDSが停止中であることを確認し、ポートフォワードまで自動化できるか確認を行います。

$ bash sample.sh
EC2とRDSを起動し、ポートフォワードします
EC2を起動します
EC2を起動しました。IPv4は"**.***.**.***"です
RDSを起動します
RDSを起動しました
エンドポイント: "***.cxboorgzneuf.**.rds.amazonaws.com"
ポート番号: 5432
実行コマンド:  ssh -i ~/**/***.pem ec2-user@"**.***.**.***" -L 15432:"***.cxboorgzneuf.**.rds.amazonaws.com":5432
The authenticity of host '"**.***.**.**" (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is *********.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '"**.***.**.**"' (ECDSA) to the list of known hosts.
Last login: Fri Aug  7 09:16:20 2020 from elasticgate1.core.classmethod.jp

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
17 package(s) needed for security, out of 24 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-**.***.** ~]$

RDSの起動に時間はかかりますが自動でポートフォワード接続出来ました!

停止確認

では、起動確認ができたので停止の確認をします。先ほどと同じファイルを実行します。

$ bash ec2_rds_start_stop.sh
ec2: "running" || rds: "available"
EC2とRDSを停止します

停止処理ができました。停止処理後にもう一度実行してみるとRDSが停止の処理中ですので処理はされません。

$ bash ec2_rds_start_stop.sh
ec2: "stopped" || rds: "stopping"
EC2・RDSが起動中もしくは停止中です。確認してください。

まとめ

踏み台経由の環境を使用する際のcliコマンドを自動化できるかなと思いシェルの勉強もかねて作成しました。今回はEC2とRDSでしたが他のサービスでも使えそうだなと思いましたので今後も実装しながら使っていきたいと思います。この記事がどなたかの助けになれば幸いです。

参考リンク