シェルスクリプトを利用して、ログをS3バケットへ自動アップロードしてみました

目的

ログファイルは、システムの状態をチェックし、監視する為には重要なデータです。
しかし、サーバ上での運用が長時間化するとログが肥大化し、下記影響が生じる可能性があります。

  • ディスク容量が急速に消費され、他の重要なアプリケーションの正常性に悪影響を及ぼす。
  • ログの処理によるシステムの高負荷により、パフォーマンスの低下。
  • ログが不正にアクセスされると悪用されるといった、セキュリティリスク。

 
そこで今回は、これらの影響を最小限に抑えるために、シェルスクリプトを使用した自動ログローテート(S3バケットへのアップロード + サーバ上のログ削除) を行いました。
 

今回、構築する環境

■構成図

利用するメイン部分となるAWSサービス 利用目的
Amazon EC2 apacheをインストールし、アクセスログを取得/シェルスクリプト実行の為
AWS CLI AWSリソース(S3バケット)を管理・操作する為に、EC2にインストールする
Amazon S3 Apacheアクセスログのエクスポート先として使う為
Application Load Balancer 今回の利用目的としては、EC2とのヘルスチェックを行い、アクセスログを出力させる為
その他のAWSサービス 利用目的
CloudWatch Alarm ALBの死活監視に利用し、閾値を超えればメールで通知する
Amazon Simple Notification Service アラーム発生時のメール送信先トピックを作成する為

前提条件

事前に、各セキュリティグループ・IAMロール・VPCを準備しておくこと.

  • Amazon EC2 ・・・
    -SGインバウンドルール(SSH、HTTP)※1
    -IAMロール(AmazonS3FullAccess、CloudWatchAgentServerPolicy)※2

  • Application Load Balancer ・・・
    -SGインバウンドルール(HTTP)※3

  • VPC

 

■S3バケット作成

まず初めに、Apacheアクセスログのエクスポート先となるS3バケットを作成します。
1.サービスからS3、サイドメニューからバケットを選択し、「バケットを作成」を押下。
2.以下の設定値を入力。

設定項目 入力値 備考
バケット名 任意 大文字が使えず、バケット名は世界で一意である必要がある
リージョン eu-west-1 遠隔に保管する必要がないのでVPCと同一のリージョンとしました
オブジェクト所有者 ACL無効 検証用で自身が利用する為
パブリックアクセス制限 すべてブロック パブリックアクセスを行わない為
バージョニング 無効 要件がないのでコストを考慮し無効
暗号化キータイプ SSE-S3 要件がないので、デフォルト値とします


 

■EC2作成

Apacheアクセスログ元となるEC2を作成していきます。
1.サービスからEC2、サイドメニューからインスタンスを選択し、「インスタンスを起動」を押下。
2.以下の設定値を入力。

設定項目 入力値
名前 任意
OS Red Hat
AMI Red Hat Enterprise Linux 9 (HVM), SSD Volume Type(無料枠)
インスタンスタイプ t2.micro(無料枠)
キーペア 使用しているものを選択
VPC 事前に作成したもの
サブネット 事前に作成したもの
パブリックIPの自動割り当て 有効化
SG 事前に作成したもの※1

3.高度な設定を開き、IAMインスタンスプロフィールに、事前に作成したIAMロール※2を付与する。

4.ユーザーデータに下記スクリプトを記入する。
ユーザーデータを設定することで、インスタンスを起動した段階で自動的に入力しておいたコマンドが実行されます。
※本来はインスタンス起動後に、コンソールからコマンド入力するので、その手間が省けます。

#!/bin/bash
sudo yum install python -y
sudo dnf install -y wget
sudo wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py

sudo curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo yum install zip unzip -y
unzip awscliv2.zip
sudo ./aws/install
source ~/.bash_profile

sudo yum install httpd -y
sudo systemctl enable httpd.service
sudo systemctl start httpd.service

echo "hello!" | sudo tee /var/www/html/index.html

sudo chmod 2755 /var/log/httpd/
  • 1行目 #!/bin/bash

    ユーザーデータのシェルスクリプトは、#! の記号と、スクリプトを読み取るインタープリタのパス (通常は /bin/bash) から始める必要があります。

起動時に Linux インスタンスでコマンドを実行するより引用

  • 2行目 sudo yum install python -y
    Pythonのインストールコマンドです。
    PythonはAWS CLIの実行に必要なランタイム環境です。
    ランタイム環境とは特定のプログラムやアプリケーションを実行する為に必要な実行環境のことです。
    プログラムやスクリプトを正常に実行する為には必須となる。

  • 3行目 sudo dnf install -y wget
    wgetのインストールコマンドです。
    Web上からファイルを取得する機能を持つフリーソフトウェアです。
    以降で、Web上からファイルを取得する際に必要となる。

  • 4~5行目
    sudo wget https://bootstrap.pypa.io/get-pip.py
    python get-pip.py
    pipのインストールコマンドです。
    pipはPythonのパッケージ管理システムであり、AWS CLIをインストールする為に利用します。

  • 6行目 sudo curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    AWS CLI(Ver2)のインストールファイルをダウンロードするコマンドです。

  • 7行目 sudo yum install zip unzip -y
    unzipのインストールコマンドです。
    6行目でインストールしたzipファイルを解凍する為に利用します。

  • 8行目 unzip awscliv2.zip
    6行目でインストールしたawscliv2.zipファイルを解凍するコマンドです。

  • 9行目 sudo ./aws/install
    インストールプログラムを実行するコマンドです。

  • 10行目 source ~/.bash_profile
    .bash_profileの変更内容を即座に反映させるコマンドです。
    変更内容が反映されないと、AWS CLIが利用できません。

  • 11行目 sudo yum install httpd -y
    apacheのインストールコマンドです。
    ApacheはWebサーバ(HTTPサーバ)ソフトウェアの一つで、一番普及している物です。

  • 12行目 sudo systemctl enable httpd.service
    起動時にapacheの自動起動を有効にするコマンドです。

  • 13行目 sudo systemctl start httpd.service
    apacheの起動コマンドです。

  • 14行目 echo "hello!" | sudo tee /var/www/html/index.html
    hello! と書かれたindex.html という名のファイルを /var/www/html 配下に作成するコマンドです。
    次の項目で作成するALBのヘルスチェックパスで「 / 」(ドキュメントルート)を指定する為、事前に index.html を作成しておく。

  • 15行目 sudo chmod 2755 /var/log/httpd/
    パーミッション(権限)変更をするコマンドです。
    /var/log/httpd/ 配下の access_log を参照する為に変更する。

上記作成手順で2台目も作成する。
 

■Application Load Balancer作成

1.ALBは、事前に作成した2つのサブネットを含んだものを作成して下さい。
事前に作成したSG※3をALBにアタッチすること。

2.ターゲットグループは、先程作成したEC2インスタンスをターゲットに登録すること。

 

■Amazon Simple Notification Service と CloudWatch Alarmの作成

本記事では、シェルスクリプトの作成がメインとなる為、SNSとCWAlarmの作成を割愛させていただきます。
詳細は、下に記載の記事を参考にして作成して下さい。

  • Amazon Simple Notification Service
    こちらの記事の「SNSトピック作成」を参考に作成してください。
  • CloudWatch Alarm
    こちらの記事の「CWAlarm(ALB死活監視)作成」を参考にしてください。
    ※設定値は、どちらとも上の記事を参考に作成しました。
     

ALBのターゲットで指定したEC2インスタンスが、一台でもヘルスステータスが[unhealthy]となると、メールで通知が来ます。
運用上あった方が、障害(異常)にすぐに気が付くことができるので便利ですね。
 

■シェルスクリプトの作成

そもそもシェルスクリプトとは

 
図のように、シェルはユーザーからのコマンドを受付け、動作した結果を出力するプログラムです。
LinuxやUnixでは、原則コマンド入力によるOS操作を行ないます。
その際に入力するコマンドは、シェルによって解釈・実行され、ユーザーに結果が表示されます。
シェルスクリプトは、シェルによる操作(解釈・実行)がまとめられた台本(スクリプト)となります。
 

■logrotate.shの作成(EC2からS3バケットへファイルをアップロードするシェルスクリプト)

1.作成したEC2インスタンスに、事前に作成したIAMロール※2をアタッチする。
2.SSHで、EC2へログインする。
3.cd /var/log/httpd/ で /var/log/httpd/ へ移動する。
4.sudo vi logrotate.sh でlogrotate.shという名のファイルを作成する。
5.i を押下し、INSERT(編集)モードに変更し、シェルスクリプト(台本)を記入する。
6.スクリプトの記入が完了したら、ESCを押下し :wq と記載しENTERで保存。

#!/bin/bash

#aws_cli-path
export PATH=/usr/local/aws-cli/v2/current/bin:$PATH

# S3バケット名
bucket_name="shiraiwa-s3-shellscript"

# ログエクスポートフォルダパス
export_folder="shellscript_logexport"

# アクセスログファイルパス
access_log="/var/log/httpd/access_log"

# 10分ごとに繰り返し実行
while true; do
     # 現在の日時を取得
     current_date=$(date +"%Y%m%d%H%M")

     # ファイル名に日時を付けてアップロード
     aws s3 cp $access_log s3://$bucket_name/$export_folder/access_log-$current_date.gz

     # アクセスログを空にする
     echo -n > $access_log

     # 10分待機
     sleep 600
done
  • 1行目 #!/bin/bash
    ユーザーデータでも説明した通りシェルスクリプトは、#! の記号と、スクリプトを読み取るインタープリタのパス (通常は /bin/bash) から始める必要があります。

  • 2,4,6,8,10,12,14,16,18行目 #から始まる文字列(コメント)
    # (シャープ)から始まる文字列は、スクリプト内の行やブロックについての説明やメモを記述する為のものであり、実行時には無視される為、スクリプトの動作には影響しないです。

  • 3行目 export PATH=/usr/local/aws-cli/v2/current/bin:$PATH
    シェルスクリプトで環境変数 PATH を変更しています。
    これにより、AWS CLIのVer2がインストールされている場所を PATH に含めることができ、AWS CLIのコマンドをシェルスクリプトから直接実行できるようになります。

  • 5行目 bucket_name="shiraiwa-s3-shellscript"
    bucket_name(変数)へ、アップロード先であるS3バケットを指定します。
    プログラミングにおける「変数」とは、数値や文字などの値を入れられる領域のことで、わかりやすくするため「値を入れる箱」と例えられることがあります。

  • 7行目 export_folder="shellscript_logexport"
    export_folder(変数)へ、S3バケットのフォルダを指定しています。

  • 9行目 access_log="/var/log/httpd/access_log"
    access_log(変数)へ、アップロード元であるアクセスログファイルを指定します。

  • 11行目 while true; do
    while とは、シェルスクリプト内で無限ループする構文です。
    do の後に続くループ内の処理が実行され、条件式が常に真(true)となる限り、ループ内の処理が繰り返されます。
    ※条件式が常に真(true)となる限り = ループ内の処理が続く限り。

下の図のような動きになります。

  • 13行目 current_date=$(date +"%Y%m%d%H%M")
    date コマンド取得した現在の時刻をcurrent_date(変数)変数へ入れる。

  • 15行目 aws s3 cp $access_log s3://$bucket_name/$export_folder/access_log-$current_date.gz
    awsコマンドで、access_log(変数)でアップロード元であるアクセスログファイルを、bucket_name(変数) の export_folder(変数)へアップロード(コピー)する。

  • 17行目 echo -n > $access_log
    /var/log/httpd/access_log の中(アクセスログ)を削除します。

  • 19行目 sleep 600
    sleepコマンドで、指定した時間だけ次のアクションを待機します。
    ※今回は検証の為、600秒(10分)待機し、処理がループするかを確認します。
    本来であれば、24時間に一度などの処理で良い。

  • 21行目 done
    done は対応する制御構造のブロックの終了を示し、ループの条件式の評価に戻ることを意味します。
    while構文は、指定した条件が真である限り、繰り返し処理が実行されます。

 
7.sudo chmod 755 logrotate.sh でシェルスクリプトを実行できるよう権限を変更し、ls -l で確認し下記画像と同じであることを確認する。


 

■アップロード元となる/var/log/httpd 配下の access_logファイルの内容を確認する

アップロード元となるaccess_logファイルの中身を事前に確認することで、S3へのアップロード後に整合性の確認ができます。
1.tailコマンドを使用して、access_logの中を見てみます。
sudo tail -f /var/log/httpd/access_log

2.ALBのヘルスチェックから、200のステータスコードのレスポンスと日時が確認できました。
出力された値をスクショ又はメモ帳にコピーし、残しておきましょう。
 

■シェルスクリプト実行

アップロード元となるaccess_logファイルの内容が確認できましたので、シェルスクリプトを実行していきます。
1.logrotate.sh の権限が確認できたところで、sudo ./logrotate.sh とコンソールに記入しENTERを押下。
アップロードが成功すると下の画像のようなコメントが表示されます。
※シェルスクリプトを実行する際は、作成したディレクトリにいること。
今回は /var/log/httpd/ です。

2.マネジメントコンソールから、実際にS3バケットのshellscript_logexportフォルダへ、access_log-日時.gz ファイルがアップロードされていることを確認できました。

3.実際にS3へアップロードされたファイルと、先程tailコマンドで出力した内容を比較していきます。
S3へアップロードしたファイルを選択し、ダウンロードを押下します。
ダウンロードできたら、解凍しメモ帳などで開きます。

整合性の確認が取れましたので、S3へのアップロードが成功となります。
このままシェルスクリプトの実行を止めなければ、10分毎にS3へ自動アップロードされることが確認できます。
確認後、Ctrl + c で実行キャンセルすることを忘れずに。
 

■logrotate.sh(改良版)

先程、作成したシェルスクリプトは10分毎に実行されるようプログラムしましたが、実際には1日に1回の頻度でS3バケットへアップロード出来れば十分です。
10分毎にアップロードをすると、S3バケットのフォルダ内に大量のバックアップが出来てしまいます、、、
なので、改良版ではシェルスクリプト内のループ処理を削除し、 cron コマンドで処理を定期実行させます。

1.cd /var/log/httpd/ でシェルスクリプトを作成したディレクトリへ移動します。
2.sudo vi logrotate.sh でファイルを開きます。
※同じ名前のファイルが存在する場合は、新規でファイルが作成されず、既存のファイルを開きます。
3.i を押下し、INSERT(編集)モードに変更し、シェルスクリプト内のwhile構文を削除します。

#!/bin/bash

#aws_cli-path
export PATH=/usr/local/aws-cli/v2/current/bin:$PATH

# S3バケット名
bucket_name="shiraiwa-s3-shellscript"

# ログエクスポートフォルダパス
export_folder="shellscript_logexport"

# アクセスログファイルパス
access_log="/var/log/httpd/access_log"

# 現在の日時を取得
current_date=$(date +"%Y%m%d%H%M")

# ファイル名に日時を付けてアップロード
aws s3 cp $access_log s3://$bucket_name/$export_folder/access_log-$current_date.gz

# アクセスログを空にする
echo -n > $access_log

4.crontab -e で cron を開く。
5.i を押下し、INSERT(編集)モードに変更。
6.cronの設定を行う(今回は月~金の0:00にスクリプトを実行する)
0 0 * * 1-5 /var/log/httpd/logrotate.sh
分 時 日 月 曜日 コマンド(シェルスクリプト名)
7.ESCを押下し :wq と記載しENTERで保存。
※vi コマンドと同様です。

8.保存されると、指定した日時でスクリプトが実行されます。
実際に実行されているか crontab -l で確認します。

月~金の0:00にアクセスログのファイルが、S3へ保存されているのが確認できたら成功です。
※今回アイルランドの環境での検証となるため、同環境で実行する場合は時差も考慮してください。
 

まとめ

今回はEC2インスタンス上にシェルスクリプトを作成し、スクリプト内にAWS CLIのPATHを記入する方法で、自動化しました。
.bash_profileにAWS CLIのPATHを記入する方法もありますので、ご参考までに。

ちなみに、シェルスクリプトを学習する上で、LPICやLinuCといった資格学習はお勧めです!
 

Last modified: 2023-06-20

Author