自分用のメモをブログ用に再編しただけなので、特にアドベントカレンダーは関係ない。一応今年も一人アドベントカレンダー2021 Advent Calendar 2021 - Adventarに登録だけしようか思っていたけど、悩んでいたら枠が全部埋まったので出せる要素がない。
本題
AWS ElasticSearchことAWS OpenSearch Service(以下ES)のスナップショットは自動で取られる分も手動で取る分もコンパネではほぼ制御できない。前者は現在1時間おきに取る以外の選択肢がなく、後者はそういうインターフェイスを提供していない(おそらく、Kibanaを使えということだろうが...)。
じゃあ公式ドキュメントにはどう書いてあるかというと、スナップショットを取るようESに直接HTTPSリクエストを送る、という形になる。しかもリクエストはAWS4Authを通して認証する必要があって、これはcurlではできないのでそれができるライブラリを使え、ということになっている: [Python] Boto3以外でV4署名リクエストを行う | DevelopersIO
- Creating index snapshots in Amazon OpenSearch Service - Amazon OpenSearch Service (successor to Amazon Elasticsearch Service)
- 公式とやってることはほぼ同じやつ:
書くコード自体はたいしたことないけど、スナップショットリポジトリを作成するのに、「S3関連の操作を許可するRoleをPassRoleして付与できるRole」の必要がある。そのためにIAM Userを発行してローカルでコードを動かすならRoleを作成してAWS内で動く方がいいなー、ということで、雑にLambda SAMを作ってなんとかした。
できたもの : mi-24v/aws-es-snapshot-helper: Lambda簡易ESクライアントによるAWS ESの手動スナップショットヘルパ
IAM ロールの PassRole と AssumeRole をもう二度と忘れないために絵を描いてみた | DevelopersIO
Lambdaの作成
以降は先述したリポジトリの中身を想像しながら書いてるので、わけがわからない場合はソース読んで貰ったほうが早い。
AWS4Authの使い方を見ているとIAM UserのAccess KeyとSecret Access Keyが必要になる。これはLambdaだとLambdaのExecution Roleから付与される一時キーから取得できて、実行時に勝手に環境変数(AWS_ACCESS_KEY_ID
、AWS_SECRET_ACCESS_KEY
、AWS_SESSION_TOKEN
)に入る。
- AWS Lambda 環境変数の使用 - AWS Lambda
- ちなみに、リージョンやLambda関数名等も取得可能で、コードでは
AWS_REGION
を使ってる。
- ちなみに、リージョンやLambda関数名等も取得可能で、コードでは
この辺はBoto3(AWS SDK for Python)の利用する認証情報 - Qiitaのように、boto3が勝手にやっていることでもあるので、boto3.Session().get_credentials()
で出てくるっぽい(試していない)。
- Credentials — Boto3 Docs 1.20.15 documentation
- 公式の方。
- Amazon Elasticsearch Service とlambda(Python3) を一瞬で疎通するメモ - Qiita
- node.js - AWS Lambda credentials from the execution environment do not have the execution role's permissions - Stack Overflow
credentialsの取得ができたらAWS4Authの準備ができるので、公式ドキュメント(再掲)のコードを適当にコピーして整形する。
def lambda_handler(event, context): host = event["domain_url"] # include https:// and trailing / repository_name = event["repo_name"] access_key = os.environ["AWS_ACCESS_KEY_ID"] secret_key = os.environ["AWS_SECRET_ACCESS_KEY"] region = os.environ["AWS_REGION"] service = 'es' session_token = os.environ["AWS_SESSION_TOKEN"] awsauth = AWS4Auth(access_key, secret_key, region, service, session_token=session_token) # requestsでよしなにする(以下省略) # action = event["action"] # のようにして、処理を分岐
ESへ実際にリクエストを投げるのは、requestsを使わずに本家ElasticSearchの公式クライアントelastic/elasticsearch-pyを使う手もあるけど、スナップショット周りができるかどうかは知らない。
AWS Lambda (Python 3.8)から Amazon Elasticsearchを使う(LambdaはSAMで) – 或る阿呆の記
なお、今回はコンパネポチポチで操作するのが目標なので、ESドメインURL等はLambdaコンソールの「Test」欄から投げることを想定して、イベントに必要なパラメータは揃ってるものとして扱った。必要なイベントは次の感じのJSON。
{ "domain_url": "https://vpc-example-123456.ap-northeast-1.es.amazonaws.com/", # ESのドメインURL(末尾に/を入れる) "repo_name": "manual_snapshot_repo", # スナップショットレポジトリ名 "register_repository_role": "arn:aws:iam::123456:role/exampleRole-12345", # PassRole対象になるRoleのARN(レポジトリ登録時のみ) "index_name": ".kibana_1", # index単位で処理するときに使うindex名(delete等のときのみ) "action": "register_repository" # このLambdaが処理の分岐に使うキー。ESの操作とは直接関係しないのでよしなに実装。 }
時刻のformatstring
pythonのformat stringには日付フォーマットもある。ってだけのメモ。
日付フォーマット(datetime⇔文字列) | Python Snippets
SAM Templateの作成
前述したRoleを含むテンプレートを作っていく。
今回相手にするESインスタンスはVPCアクセスにしているので、LambdaをESと同じ(もしくは、ESインスタンスの属するSubnetに到達可能な)VPCにつなげておく必要がある。VPCとセキュリティグループのARNを控えて代入するか、テンプレートで定義してARNを渡せばOK。
- VPC 内のリソースにアクセスするように Lambda 関数を設定する - AWS Lambda
- VPCピアリングを作りながら学んでみた | DevelopersIO
- 別のVPCからアクセスする場合は、ピアリングでルートを通す。
- AWS LambdaをVPC内に配置する際の注意点 | そるでぶろぐ
また、LambdaをVPCアクセスにするとENIの操作が必要になるので、前述したRoleにENI操作のIAM権限をつける必要がある。
結局どのIAM権限がいるかを、作ったテンプレートのうち、Roleに関する部分を抜粋して示す。
SnapshotRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" - Effect: Allow Principal: Service: es.amazonaws.com Action: "sts:AssumeRole" ManagedPolicyArns: - "arn:aws:iam::aws:policy/AmazonESFullAccess" Policies: # 本当は自身のArnをPassRoleするPolicyも必要なのだが自己参照できないので手動で足す - PolicyName: "AllowTakeElasticSearchSnapshotPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "s3:ListBucket" Resource: !GetAtt SnapshotBucket.Arn - Effect: Allow Action: - "s3:GetObject" - "s3:PutObject" - "s3:DeleteObject" Resource: !Sub "${SnapshotBucket.Arn}/*" - PolicyName: "LambdaWithVPCPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: "arn:aws:logs:*:*:*" - Effect: Allow Action: - "ec2:CreateNetworkInterface" - "ec2:DescribeNetworkInterfaces" - "ec2:DetachNetworkInterface" - "ec2:DeleteNetworkInterface" Resource: "*"
注意として、公式ドキュメント(再掲)に乗ってるPassRole(以下の部分)に関しては、Roleの記述を分けるか、手動で足すかしないといけなくて、自身のRole ARNは参照できないっぽい(IDEAのSAM Syntaxチェックがそう怒ってきたから、そうに違いない)。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::123456789012:role/TheSnapshotRole" } ] }
CloudFormation組み込み関数の記述をなんとなくつかっていたので、その辺のメモも残す。
- 基本の使い方。
Ref
とかの使い方についてFn::Sub
は、変数を代入するだけでなく、Fn::Ref
もしくはFn::GetAtt
による解決も行ってくれるので、文字列中にテンプレートで定義した値を入れる、みたいなことができる。
動かしていく
ローカルでのテストはめんどくさかったのでしていない(ええ)。トリガーもないし、直接デプロイしてコンパネでポチポチ試していった。
で、実際にKibanaでインデックスを確認するには、プライベートVPCを超える何かが必要。一時的な用途なら踏み台で十分なので踏み台を建ててローカルポートフォワーディングで手元のPCでアクセスできるようにした。
- プライベートなEC2に一時的にSSHする3つの方法 | DevelopersIO
- PrivateSubnetのインスタンスにSSH接続したい - サーバーワークスエンジニアブログ
- SSHやSSM以外に、シリアルポートでのアクセスができるらしいけど、cloud-initか何か?で予めユーザパスワードを与えていないといけない。
- 普通のAmazon LinuxのAMIだとec2-userのパスワードはない。
- EC2 シリアルポートアクセスが可能になりました | DevelopersIO
今考えるとSSHポートフォワーディングでいけた気がするけど、踏み台からESへはリバースプロキシを建ててアクセスした。一時的な用途だと、NGINXを建てるのはだるくて....と思っていたら、cortesi/devdというコマンドラインリバースプロキシがあるのを見つけた。Go製なのでReleaseバイナリを解凍するだけですぐ使える。本来は開発サーバ用っぽいが、求めていたものと完全に合致していて良かった。
- パツイチでHTTPサーバになったりリバースプロキシになったりする devd - Qiita
- command line - How to unzip .tgz file using the terminal? - Ask Ubuntu
- Releaseバイナリの解凍メモだけど、Github Releaseには直リンクが貼れないっぽくて、踏み台内で直接curl(もしくはwget)できなかったので、結局ローカルに落として解凍してからscpで送りつけた。
アクセスできるようになったら、curlとかkibanaのコンソールでスナップショットのデータがリストアされているか試していく。
- [AWS]Amazon Elasticsearch Serviceでよく使うコマンド集(Amazon ES/Elasticsearch) - Qiita
- 【Elasticsearch】よく使うコマンド一覧 - Qiita
- Elasticsearchの確認系コマンド6つ | Simple is Beautiful.
これで確認できたら踏み台は閉じて、Lambdaでイベントをいじってコンパネからスナップショットを取ったり取らなかったりしておわり。
おわりに
AWS関連の操作やmiwkeyの管理はまだまだやることはいっぱいある。けど、時間とやる気と調査が足りねえ...