AWS-Systems-Manager

【AWS SSM Run Command】S3イベントで自動的にファイルを同期させる[前編]

はじめに

お疲れ様です、寺井です。

今回はみんな大好き 「自動化」です。

特定のイベントを検知して、後続処理を実行する
イベントドリブンなアーキテクチャを作ってみました。

背景

<要望>
アプリケーションファイルの変更はローカルで行う
変更を行う度にサーバー(EC2)へファイル更新しにいくのが面倒

<解決策>
S3へのファイルアップロードをトリガーに、自動でEC2へS3のファイルを同期させる

完成イメージ(構成図)

実装

前提

  • 全て東京リージョンを利用
  • VPC、サブネット、ルートテーブル、セキュリティグループは設定済

今回はEC2からS3バケットへのファイル同期に焦点を当ててお話しますので、VPC周りやEC2の起動に関する説明は省略します。

EC2の準備

まずはWebアプリケーションサーバーとしてEC2インスタンスの準備をします。

IAMポリシー

EC2には以下のAWS管理ポリシーを付与したIAMロールをアタッチします。

  • AmazonS3FullAccess
    • [S3] アクセス用途
  • AmazonSSMManagedInstanceCore
    • [SSM Run Command] 実行用途

OS

今回は [RHEL8.7]を使用しています

$ cat /etc/redhat-release
Red Hat Enterprise Linux release 8.7 (Ootpa)

ミドルウェア

PHPファイルをブラウザで確認するため、PHPとApacheをインストールしておきます。

$ php -v
PHP 7.4.30 (cli) (built: Jun  7 2022 08:38:19) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

$ httpd -v
Server version: Apache/2.4.37 (Red Hat Enterprise Linux)
Server built:   Jul 28 2022 23:43:33

「AWS CLI」のインストール

こちらのページの内容でインストールを実施

参考:AWS CLI バージョン 2 用ユーザーガイド

$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
︙

$ ls
awscliv2.zip

$ unzip awscliv2.zip
︙

$ sudo ./aws/install

$ aws --version
aws-cli/2.10.1 Python/3.9.11 Linux/4.18.0-425.10.1.el8_7.x86_64 exe/x86_64.rhel.8 prompt/off

「SSM Agent」のインストール

参考:Linux 用 EC2 インスタンスに手動で SSM Agent をインストールする

今回は [RHEL 8系]を使用するので、以下のページの内容でインストールを実施

RHEL 8.x に SSM Agent をインストールする

$ dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
︙

$ systemctl enable amazon-ssm-agent
$ systemctl start amazon-ssm-agent

$ systemctl status amazon-ssm-agent
● amazon-ssm-agent.service - amazon-ssm-agent
   Loaded: loaded (/etc/systemd/system/amazon-ssm-agent.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2023-02-20 16:08:30 JST; 15min ago
 Main PID: 1368 (amazon-ssm-agen)
    Tasks: 17 (limit: 4645)
   Memory: 104.6M
   CGroup: /system.slice/amazon-ssm-agent.service
           tq1368 /usr/bin/amazon-ssm-agent
           mq1454 /usr/bin/ssm-agent-worker

S3バケットの作成

作成したS3バケットの直下に「app/」というフォルダを作成し、その中に「index.php」を設置します。

「s3 sync」コマンドの確認

参考:AWS CLI での高レベル (S3) コマンドの使用

AWS CLIで実行する 「s3 sync」コマンドについて挙動の確認をしておきます。

S3バケットの状態を確認

# S3バケットの確認
$ aws s3 ls
2023-02-13 14:23:41 s3sync-test-bucket-20230213

$ aws s3 ls s3://s3sync-test-bucket-20230213
PRE app/

$ aws s3 ls s3://s3sync-test-bucket-20230213/app/
2023-02-13 14:27:01         23 index.php

S3のファイルをEC2側に同期させる

# 構文
$ aws s3 sync <source> <target> [--options]
# 現在のディレクトリは空の状態
$ ls -l
合計 0

# 「s3sync-test-bucket-20230213/app/」内のファイルを現在のディレクトリ「.」に同期
$ aws s3 sync s3://s3sync-test-bucket-20230213/app/ .
download: s3://s3sync-test-bucket-20230213/app/index.php to ./index.php

$ ls -l
合計 4
-rw-r--r-- 1 root root 23  2月 13 14:27 index.php

S3のファイルがEC2側にダウンロードされた

EC2側のファイルをS3側に同期させる

先程とは逆パターンの挙動を確認

# 現在のディレクトリは空の状態
$ ls -l
合計 0

# S3側にはファイルがある状態
$ aws s3 ls s3://s3sync-test-bucket-20230213/app/
2023-02-13 14:27:01         23 index.php

# 現在のディレクトリ「.」を「s3sync-test-bucket-20230213/app/」に同期
$ aws s3 sync . s3://s3sync-test-bucket-20230213/app/

# 実行しても反応なし

実行されません。一旦別のファイル[index1.php]を作成してみます。

# 既にS3に置いてあるファイル名とは別のファイルを作成
$ vi index1.php
$ ls -l
合計 4
-rw-r--r-- 1 root root 8  2月 13 14:49 index1.php

# 「s3 sync」の実行
$ aws s3 sync . s3://s3sync-test-bucket-20230213/app/
upload: ./index1.php to s3://s3sync-test-bucket-20230213/app/index1.php

アップロードだけ実行された

削除を伴うファイル同期

先程の「AWS CLI での高レベル (S3) コマンドの使用」ページを良く読んでみると、削除を伴う場合は「--delete」オプションが必要とのこと

通常、s3 sync は欠落しているか古くなったファイルやオブジェクトを、ソースとターゲットの間でコピーします。ただし、--delete オプションを指定して、ソースに存在しないファイルまたはオブジェクトをターゲットから削除することもできます。

勢いでコマンド走らせてしまって、意図せずファイルが削除されてしまうこともありそうなので、これは有り難い。

# S3の状態確認(「index.php」と「index1.php」の2つのファイルを設置)
$ aws s3 ls s3://s3sync-test-bucket-20230213/app/
2023-02-13 14:27:01         23 index.php
2023-02-13 14:51:14          8 index1.php

# EC2側の状態確認
$ ls -l
合計 4
-rw-r--r-- 1 root root 23  2月 13 14:27 index.php

# 「s3 sync」の実行
$ aws s3 sync --delete . s3://s3sync-test-bucket-20230213/app/
delete: s3://s3sync-test-bucket-20230213/app/index1.php

# S3の状態確認
$ aws s3 ls s3://s3sync-test-bucket-20230213/app/
2023-02-13 14:27:01         23 index.php

EC2側にはなかった「index1.php」ファイルがS3から削除(=同期)された

S3のファイルをサーバー側と同期させることも、その逆も可能です。

補足:同じファイルを同期する場合

「おんなじファイルはどうなるんやろう…」と気になって夜も眠れなくなる夢を見たので更に検証してみました。

EC2→S3へのsync

EC2側とS3側のファイルが全く同じものである状態で挙動を確認します。

# EC2側のファイルの状態
$ ls --full-time
合計 4
-rw-r--r-- 1 root root 29 2023-02-17 12:19:01.874374467 +0900 index.php

$ aws s3 ls s3://s3sync-test-bucket-20230213/app/
2023-02-17 12:19:01         29 index.php

# 「s3 sync」実行
$ aws s3 sync . s3://s3sync-test-bucket-20230213/app/
# 差分が無いと何も起こらない

続いて、EC2側のファイルを変更せずに保存しなおして、挙動を確認します。

# EC2側のファイルを編集し
$ vi index.php
# 変更せずに保存

$ ls --full-time
合計 4
-rw-r--r-- 1 root root 29 2023-02-17 12:21:05.860578665 +0900 index.php

# 「s3 sync」実行
$ aws s3 sync . s3://s3sync-test-bucket-20230213/app/
upload: ./index.php to s3://s3sync-test-bucket-20230213/app/index.php

$ aws s3 ls s3://s3sync-test-bucket-20230213/app/
2023-02-17 12:21:39         29 index.php

中身を変更せずに保存しなおしても「s3 sync」が実行されたので、ファイルの中身は関係なく、タイムスタンプで判断されるようです。

S3→EC2へのsync

逆パターンでファイルを変更せずにS3にファイルをアップロードし直して、コマンドを実行しても何も返ってこない…。

# S3のファイルをEC2側にsyncさせる
$ aws s3 sync s3://s3sync-test-bucket-20230213/app/ .

よくよく調べたところ以下のドキュメントに遭遇

参考:AWS CLI 1.27.73 Command Reference|s3 sync

デフォルトの動作では、同じサイズのアイテムは無視されます。

なんでや?!
ファイルサイズが変わらず内容が少し変わるパターンってありそうなんですが…。

ということで、ファイルサイズではなくタイムスタンプでsyncさせたいときは、「--exact-timestampsオプションをつけて実行する必要がありますのでご注意を。

# S3のファイルをEC2側にsyncさせる(タイムスタンプで判断)
$ aws s3 sync --exact-timestamps s3://s3sync-test-bucket-20230213/app/ .
download: s3://s3sync-test-bucket-20230213/app/index.php to ./index.php

ここまでの振り返り

必要な情報が増えてきたので一旦まとめます。

WEBサーバーに設置しているアプリケーションのパス

アプリケーションファイル(index.php)

<?php
// 心ばかりのPHP要素
$version = "1.0";
?>

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test-app|トップページ</title>
</head>

<body>
    <h1 style="text-align: center;">アプリケーション</h1>
    <p style="text-align: center;">Version:<?= $version ?></p>
</body>

</html>

アプリの画面イメージ

S3 URI

s3://s3sync-test-bucket-20230213/app/

S3からEC2にファイルを同期させるコマンド

タイムスタンプで実行するか否かを判断して、S3の対象ディレクトリを EC2の対象ディレクトリに同期するコマンド

$ aws s3 sync --exact-timestamps s3://s3sync-test-bucket-20230213/app/ /var/www/html/s3sync_dir/app/

※実際に実行する際のWEBサーバーのパスや、S3 URIに関しては、適宜ご自身の環境に読み換えてください。

まとめ

いらぬ検証をしたせいで、かなり長くなったので、今回は一旦ここまでとします。
次回は[EventBridge]の作成と[SSM Run Command]の設定を行って、一連の動作を確認していきたいと思います。

ここまでお読みいただきありがとうございました!
次回も宜しくお願いします。

続きはこちら:
【AWS SSM Run Command】S3イベントで自動的にファイルを同期させる[前編]

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA