Amazon S3 Filesが一般提供され、S3バケット上のデータをファイルシステムとして扱えるようになりました。AWSの発表では、S3 FilesはS3 上のデータに対してファイルシステムインターフェースを提供し、EC2、ECS、EKS、Lambda などのコンピューティングリソースから、既存のファイル操作に近い形でアクセスできる仕組みとして紹介されています。
S3はこれまで、AWS CLI、SDK、S3 API を利用してオブジェクト単位で操作するのが一般的でした。
一方で、運用現場では、
- S3に出力されたログを確認する
- S3上の生データを一時的に加工する
- 既存のファイル処理スクリプトを使ってログを集計する
、といった作業が多くあります。
このような作業では、S3上のデータを通常のファイルのように扱えると、実装や運用がかなり直感的になります。
そこで今回は、S3 Filesを利用することで、既存のS3 APIベースの処理と比較して、どの程度メリットがあるのかを検証しました。
検証対象として、テスト用S3バケットにCloudTrailログをコピーし、以下の2つの方式で読み取り処理を実施します。
- boto3を利用してS3 API経由で読み取る方式
- S3 Filesでマウントしたファイルシステム経由で読み取る方式
単純なlsやaws s3 lsの比較ではなく、実際の運用処理に近づけるため、.json.gzファイルの読み込み、gzip解凍、JSONパース、CloudTrailの Records件数カウントまで含めて比較しました。
実行環境
今回の検証は、EC2 インスタンス上で実施しました。
NAME="Red Hat Enterprise Linux"
VERSION="9.7 (Plow)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="9.7"
PLATFORM_ID="platform:el9"
PRETTY_NAME="Red Hat Enterprise Linux 9.7 (Plow)"
ANSI_COLOR="0;31"
LOGO="fedora-logo-icon"
CPE_NAME="cpe:/o:redhat:enterprise_linux:9::baseos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9"
BUG_REPORT_URL="https://issues.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 9"
REDHAT_BUGZILLA_PRODUCT_VERSION=9.7
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="9.7"
{
"Reservations": [
{
"ReservationId": "r-052f7fd3077807cae",
"OwnerId": "123456789123",
"Groups": [],
"Instances": [
{
"Architecture": "x86_64",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/sda1",
"Ebs": {
"AttachTime": "2026-04-25T04:28:55+00:00",
"DeleteOnTermination": true,
"Status": "attached",
"VolumeId": "vol-0548264a7a5ad5927",
"EbsCardIndex": 0
}
}
],
"ClientToken": "xxxxxxxx",
"EbsOptimized": true,
"EnaSupport": true,
"Hypervisor": "xen",
"IamInstanceProfile": {
"Arn": "arn:aws:iam::123456789123:instance-profile/saito-s3filestest-iamrole",
"Id": "AIPAXH6YR6RJRNZMKISAF"
},
"NetworkInterfaces": [
{
"Association": {
"IpOwnerId": "amazon",
"PublicDnsName": "ec2-xx-xx-xx-xx.ap-southeast-1.compute.amazonaws.com",
"PublicIp": "xx.xx.xx.xx"
},
"Attachment": {
"AttachTime": "2026-04-25T04:28:54+00:00",
"AttachmentId": "eni-attach-02adade095ee72c82",
"DeleteOnTermination": true,
"DeviceIndex": 0,
"Status": "attached",
"NetworkCardIndex": 0
},
"Description": "",
"Groups": [
{
"GroupId": "sg-0a4ffe5cf685352f0",
"GroupName": "saitou-test-sg"
}
],
"Ipv6Addresses": [],
"MacAddress": "06:51:92:de:06:a7",
"NetworkInterfaceId": "eni-08e273333c3818c7e",
"OwnerId": "123456789123",
"PrivateDnsName": "ip-10-0-12-6.ap-southeast-1.compute.internal",
"PrivateIpAddress": "10.0.12.6",
"PrivateIpAddresses": [
{
"Association": {
"IpOwnerId": "amazon",
"PublicDnsName": "ec2-xx-xx-xx-xx.ap-southeast-1.compute.amazonaws.com",
"PublicIp": "xx.xx.xx.xx"
},
"Primary": true,
"PrivateDnsName": "ip-10-0-12-6.ap-southeast-1.compute.internal",
"PrivateIpAddress": "10.0.12.6"
}
],
"SourceDestCheck": true,
"Status": "in-use",
"SubnetId": "subnet-050443a9af8436abd",
"VpcId": "vpc-0df5eeb6b81327dde",
"InterfaceType": "interface",
"Operator": {
"Managed": false
}
}
],
"RootDeviceName": "/dev/sda1",
"RootDeviceType": "ebs",
"SecurityGroups": [
{
"GroupId": "sg-0a4ffe5cf685352f0",
"GroupName": "saitou-test-sg"
}
],
"SourceDestCheck": true,
"Tags": [
{
"Key": "Name",
"Value": "saito-s3filestest-ec2"
}
],
"VirtualizationType": "hvm",
"CpuOptions": {
"CoreCount": 1,
"ThreadsPerCore": 2
},
"CapacityReservationSpecification": {
"CapacityReservationPreference": "open"
},
"HibernationOptions": {
"Configured": false
},
"MetadataOptions": {
"State": "applied",
"HttpTokens": "required",
"HttpPutResponseHopLimit": 2,
"HttpEndpoint": "enabled",
"HttpProtocolIpv6": "disabled",
"InstanceMetadataTags": "disabled"
},
"EnclaveOptions": {
"Enabled": false
},
"BootMode": "uefi-preferred",
"PlatformDetails": "Red Hat Enterprise Linux",
"UsageOperation": "RunInstances:0010",
"UsageOperationUpdateTime": "2026-04-25T04:28:54+00:00",
"PrivateDnsNameOptions": {
"HostnameType": "ip-name",
"EnableResourceNameDnsARecord": false,
"EnableResourceNameDnsAAAARecord": false
},
"MaintenanceOptions": {
"AutoRecovery": "default",
"RebootMigration": "default"
},
"CurrentInstanceBootMode": "uefi",
"NetworkPerformanceOptions": {
"BandwidthWeighting": "default"
},
"Operator": {
"Managed": false,
"HiddenByDefault": false
},
"InstanceId": "i-0f7cb45f45e56358e",
"ImageId": "ami-0fbbafe729d214f98",
"State": {
"Code": 16,
"Name": "running"
},
"PrivateDnsName": "ip-10-0-12-6.ap-southeast-1.compute.internal",
"PublicDnsName": "ec2-xx-xx-xx-xx.ap-southeast-1.compute.amazonaws.com",
"StateTransitionReason": "",
"KeyName": "xxxxxxxxxx",
"AmiLaunchIndex": 0,
"ProductCodes": [],
"InstanceType": "m5.large",
"LaunchTime": "2026-05-02T00:55:29+00:00",
"Placement": {
"AvailabilityZoneId": "apse1-az2",
"GroupName": "",
"Tenancy": "default",
"AvailabilityZone": "ap-southeast-1a"
},
"Monitoring": {
"State": "disabled"
},
"SubnetId": "subnet-050443a9af8436abd",
"VpcId": "vpc-0df5eeb6b81327dde",
"PrivateIpAddress": "10.0.12.6",
"PublicIpAddress": "xx.xx.xx.xx"
}
]
}
]
}
検証では、S3 FilesをEC2にマウントし、以下のようなパスからCloudTrailログを参照できる状態にしました。
/mnt/s3files/aws-cloudtrail-logs/AWSLogs/123456789123/CloudTrail/ap-southeast-1/2026/05/01/
S3 API 側では、同じオブジェクトを以下のプレフィックスで参照します。
s3://saito-test-s3bucket/aws-cloudtrail-logs/AWSLogs/123456789123/CloudTrail/ap-southeast-1/2026/05/01/
今回の対象は、2026年5月1日分のCloudTrailログです。
検証
それでは検証してきます。
検証1: 簡単な一括比較
まずは、S3 API経由とS3 Files経由で、同じCloudTrailログを読み込んだ場合の全体処理時間を比較しました。
処理内容は以下の通りです。
- 1.対象ディレクトリ、またはプレフィックス配下の
.json.gzファイルを取得する - 2.各ファイルを読み込む
- 3.gzipを解凍する
- 4.JSONとして読み込む
- 5.CloudTrailのRecords件数をカウントする
- 6.全体処理時間、ファイル数、Records件数、合計サイズを出力する
S3 API方式では、boto3.client("s3")を利用し、list_objects_v2でオブジェクト一覧を取得したあと、各ファイルに対してget_objectを実行しています。
一方、S3 Files方式では、マウント済みのディレクトリに対してPath.glob()でファイル一覧を取得し、通常のファイルと同じようにgzip.open()で読み込んでいます。
利用スクリプト
ここには、検証1で利用したシンプルな比較スクリプトを掲載します。
import boto3
import gzip
import json
import time
from pathlib import Path
from io import BytesIO
BUCKET = "<YOUR BUCKET NAME>"
PREFIX = "<YOUR BUCKET PREFIX>"
MOUNT_PATH = "<YOUR S3 BUCKET MOUNT PATH>"
def measure_s3_api():
s3 = boto3.client("s3")
start = time.perf_counter()
response = s3.list_objects_v2(
Bucket=BUCKET,
Prefix=PREFIX
)
objects = response.get("Contents", [])
file_count = 0
record_count = 0
total_bytes = 0
for obj in objects:
key = obj["Key"]
# ディレクトリ相当の 0 バイトオブジェクトは除外
if not key.endswith(".json.gz"):
continue
file_count += 1
total_bytes += obj["Size"]
body = s3.get_object(Bucket=BUCKET, Key=key)["Body"].read()
with gzip.GzipFile(fileobj=BytesIO(body)) as gz:
data = json.loads(gz.read().decode("utf-8"))
record_count += len(data.get("Records", []))
end = time.perf_counter()
return {
"method": "s3_api",
"elapsed_sec": end - start,
"file_count": file_count,
"record_count": record_count,
"total_bytes": total_bytes,
}
def measure_s3_files():
start = time.perf_counter()
base_path = Path(MOUNT_PATH)
file_count = 0
record_count = 0
total_bytes = 0
for file_path in sorted(base_path.glob("*.json.gz")):
file_count += 1
total_bytes += file_path.stat().st_size
with gzip.open(file_path, "rt", encoding="utf-8") as gz:
data = json.load(gz)
record_count += len(data.get("Records", []))
end = time.perf_counter()
return {
"method": "s3_files",
"elapsed_sec": end - start,
"file_count": file_count,
"record_count": record_count,
"total_bytes": total_bytes,
}
def main():
s3_api_result = measure_s3_api()
s3_files_result = measure_s3_files()
print("=== S3 API ===")
for k, v in s3_api_result.items():
print(f"{k}: {v}")
print("\n=== S3 Files ===")
for k, v in s3_files_result.items():
print(f"{k}: {v}")
if s3_files_result["elapsed_sec"] > 0:
ratio = s3_api_result["elapsed_sec"] / s3_files_result["elapsed_sec"]
print(f"\nS3 API / S3 Files elapsed ratio: {ratio:.2f}")
if __name__ == "__main__":
main()
結果
10回実行した結果は以下の通りです。
| 項目 | S3 API | S3 Files |
|---|---|---|
| 対象ファイル数 | 302 | 302 |
| Records件数 | 11,395 | 11,395 |
| 合計サイズ | 4,322,243 bytes | 4,322,243 bytes |
| 最速 | 約 8.45 秒 | 約 0.63 秒 |
| 最遅 | 約 13.70 秒 | 約 0.96 秒 |
| おおよその平均 | 約 9.55 秒 | 約 0.72 秒 |
| 平均倍率 | S3 API が約 13倍遅い | – |
結果として、今回の検証条件ではS3 Files経由の処理が大幅に高速でした。
S3 API方式では、302ファイルを処理するために、ファイル数分のget_object呼び出しが発生します。
今回のように1ファイルあたりのサイズが小さいログファイルを多数処理するケースでは、データ転送量そのものよりも、API呼び出し回数に伴うオーバーヘッドが大きく影響した可能性があります。
一方、S3 Files方式では、S3上のオブジェクトをマウント済みファイルシステム上のファイルとして扱えるため、Python側からは通常のファイル処理と同じように読み込めます。今回の結果では、この違いが全体処理時間に大きく表れました。
検証2: 詳細比較
検証1では、全体処理時間に大きな差があることが分かりました。
次に、どの処理工程で差が出ているのかを確認するため、処理を以下の項目に分けて計測しました。
| 測定項目 | S3 API | S3 Files |
|---|---|---|
| 一覧取得時間 | list_objects_v2 |
Path.glob() |
| ファイル読み込み時間 | get_object().read() |
open().read() |
| gzip 解凍時間 | gzip.GzipFile |
gzip.decompress() |
| JSON パース時間 | json.loads() |
json.loads() |
| 全体処理時間 | 合計 | 合計 |
検証1のコードでは、ファイル読み込み、gzip解凍、JSONパースが一連の処理としてまとまっていました。
そこで検証2では、S3 API方式、S3 Files方式の両方で、以下のように処理を分離しました。
- まずバイナリとして
.json.gzファイルを読み込む - 読み込んだバイナリを
gzip解凍する - 解凍後の文字列を
JSONとしてパースする
これにより、全体処理時間の差が、
- 「ファイル読み込み」なのか、
- 「gzip 解凍」なのか、
` 「JSON パース」なのか、
を確認できます。
利用スクリプト
検証2では、以下のように各処理の前後でtime.perf_counter()を取得し、処理時間を積み上げています。
import boto3
import gzip
import json
import time
from pathlib import Path
from io import BytesIO
BUCKET = "<YOUR BUCKET NAME>"
PREFIX = "<YOUR BUCKET PREFIX>"
MOUNT_PATH = "<YOUR S3 BUCKET MOUNT PATH>"
def measure_s3_api():
s3 = boto3.client("s3")
total_start = time.perf_counter()
# 一覧取得時間
list_start = time.perf_counter()
response = s3.list_objects_v2(
Bucket=BUCKET,
Prefix=PREFIX
)
objects = response.get("Contents", [])
list_end = time.perf_counter()
file_count = 0
record_count = 0
total_bytes = 0
read_time = 0.0
gzip_time = 0.0
json_time = 0.0
for obj in objects:
key = obj["Key"]
# ディレクトリ相当の 0 バイトオブジェクトは除外
if not key.endswith(".json.gz"):
continue
file_count += 1
total_bytes += obj["Size"]
# ファイル読み込み時間
read_start = time.perf_counter()
body = s3.get_object(Bucket=BUCKET, Key=key)["Body"].read()
read_end = time.perf_counter()
read_time += read_end - read_start
# gzip 解凍時間
gzip_start = time.perf_counter()
with gzip.GzipFile(fileobj=BytesIO(body)) as gz:
json_text = gz.read().decode("utf-8")
gzip_end = time.perf_counter()
gzip_time += gzip_end - gzip_start
# JSON パース時間
json_start = time.perf_counter()
data = json.loads(json_text)
json_end = time.perf_counter()
json_time += json_end - json_start
record_count += len(data.get("Records", []))
total_end = time.perf_counter()
return {
"method": "s3_api",
"total_elapsed_sec": total_end - total_start,
"list_time_sec": list_end - list_start,
"read_time_sec": read_time,
"gzip_time_sec": gzip_time,
"json_parse_time_sec": json_time,
"file_count": file_count,
"record_count": record_count,
"total_bytes": total_bytes,
}
def measure_s3_files():
total_start = time.perf_counter()
base_path = Path(MOUNT_PATH)
# 一覧取得時間
list_start = time.perf_counter()
file_paths = sorted(base_path.glob("*.json.gz"))
list_end = time.perf_counter()
file_count = 0
record_count = 0
total_bytes = 0
read_time = 0.0
gzip_time = 0.0
json_time = 0.0
for file_path in file_paths:
file_count += 1
# ファイルサイズ取得
total_bytes += file_path.stat().st_size
# ファイル読み込み時間
# gzip.open ではなく、まずバイナリとして読み込むことで、
# 「読み込み」「gzip解凍」「JSONパース」を分けて計測する
read_start = time.perf_counter()
with open(file_path, "rb") as f:
body = f.read()
read_end = time.perf_counter()
read_time += read_end - read_start
# gzip 解凍時間
gzip_start = time.perf_counter()
json_text = gzip.decompress(body).decode("utf-8")
gzip_end = time.perf_counter()
gzip_time += gzip_end - gzip_start
# JSON パース時間
json_start = time.perf_counter()
data = json.loads(json_text)
json_end = time.perf_counter()
json_time += json_end - json_start
record_count += len(data.get("Records", []))
total_end = time.perf_counter()
return {
"method": "s3_files",
"total_elapsed_sec": total_end - total_start,
"list_time_sec": list_end - list_start,
"read_time_sec": read_time,
"gzip_time_sec": gzip_time,
"json_parse_time_sec": json_time,
"file_count": file_count,
"record_count": record_count,
"total_bytes": total_bytes,
}
def print_result(result):
print(f"=== {result['method']} ===")
print(f"total_elapsed_sec : {result['total_elapsed_sec']:.6f}")
print(f"list_time_sec : {result['list_time_sec']:.6f}")
print(f"read_time_sec : {result['read_time_sec']:.6f}")
print(f"gzip_time_sec : {result['gzip_time_sec']:.6f}")
print(f"json_parse_time_sec: {result['json_parse_time_sec']:.6f}")
print(f"file_count : {result['file_count']}")
print(f"record_count : {result['record_count']}")
print(f"total_bytes : {result['total_bytes']}")
def main():
s3_api_result = measure_s3_api()
s3_files_result = measure_s3_files()
print_result(s3_api_result)
print()
print_result(s3_files_result)
print("\n=== comparison ===")
if s3_files_result["total_elapsed_sec"] > 0:
ratio = s3_api_result["total_elapsed_sec"] / s3_files_result["total_elapsed_sec"]
print(f"total elapsed ratio S3 API / S3 Files: {ratio:.2f}")
if s3_files_result["read_time_sec"] > 0:
read_ratio = s3_api_result["read_time_sec"] / s3_files_result["read_time_sec"]
print(f"read time ratio S3 API / S3 Files : {read_ratio:.2f}")
if __name__ == "__main__":
main()
結果
測定結果は以下の通りです。
| 測定項目 | S3 API | S3 Files | 差 |
|---|---|---|---|
| 全体処理時間 | 8.797 秒 | 0.792 秒 | 約 11.1 倍 |
| 一覧取得時間 | 0.152 秒 | 0.007 秒 | S3 Files が高速 |
| ファイル読み込み時間 | 8.373 秒 | 0.347 秒 | 約 24.2 倍 |
| gzip 解凍時間 | 0.106 秒 | 0.097 秒 | ほぼ同等 |
| JSON パース時間 | 0.164 秒 | 0.172 秒 | ほぼ同等 |
| ファイル数 | 302 | 302 | 同一 |
| Records件数 | 11,395 | 11,395 | 同一 |
| 合計サイズ | 4,322,243 bytes | 4,322,243 bytes | 同一 |
この結果を見ると、全体処理時間の差は、ほぼファイル読み込み時間の差で説明できます。
gzip解凍時間は、S3 APIが0.106秒、S3 Filesが0.097秒でした。
JSONパース時間も、S3 APIが0.164秒、S3 Filesが0.172秒であり、ほぼ同等です。
つまり、同じgzipファイルを解凍し、同じJSONを解析している処理自体には大きな差がありません。
一方で、ファイル読み込み時間は、S3 APIが8.373秒、S3 Filesが0.347秒となり、約24.2倍の差が出ました。
このことから、今回の検証条件では、S3 API経由とS3 Files経由の性能差は、主にファイル読み込み部分に起因していると考えられます。
考察
今回の検証では、CloudTrailログのような小容量ファイルを多数読み込むケースにおいて、S3 Files経由の処理が非常に高速に見えました。
特に重要なのは、gzip解凍やJSONパースの時間には大きな差がなく、差が出ていたのは主にファイル読み込み時間だった点です。
S3 API方式では、各ファイルに対してget_objectを実行します。今回の対象ファイルは302個だったため、302回のオブジェクト取得処理が発生しています。合計サイズは約4.3MBとそれほど大きくないため、純粋な転送量よりも、リクエスト回数やAPI呼び出しのオーバーヘッドが大きく影響したと考えられます。
一方、S3 Files方式では、S3上のデータをファイルシステムとして扱えるため、Pythonからは通常のファイル操作で読み込めます。これが肝ですよね。
AWSの発表でも、S3 Filesは既存のファイルベースのアプリケーションやツールから、S3上のデータを直接扱えることが特徴として説明されています。
運用目線では、この点はかなり大きなメリットです。
たとえば、以下のような作業ではS3 Filesの相性がよいと感じました。
- CloudTrailログを日付単位で読み込んで集計する
- CloudWatch Logsのエクスポート結果を一括処理する
- S3上のログファイルをgrepやPythonスクリプトで確認する
- 既存のローカルファイル処理スクリプトを大きく変更せずに使う
- 障害調査時に、S3上のファイルをサーバー上のディレクトリとして確認する
これまでS3上のファイルを処理する場合、AWS CLIで一度ローカルにコピーするか、boto3でS3 APIを呼び出す処理を作る必要がありました。
しかし S3 Filesを利用すると、S3上のデータをそのままファイルシステムとして扱えるため、運用担当者にとってはかなり直感的です。
なお、、注意点もあります。
今回のS3 API方式は、ファイルを1つずつ順番にget_objectする逐次処理です。そのため、S3 APIにとってはやや不利な比較でもあります。
そのため、今回の結果をそのまま「すべてのケースでS3 Filesが10倍以上速い」とは一概に言えないかもしれません。
あくまで、「CloudTrailログのような小容量ファイルを多数読み込む処理において、今回の検証条件では、S3 Files経由の方がS3 APIの逐次取得より大幅に高速だった。」ということになります。
S3 Filesは便利ですが、S3 APIを完全に置き換えるものではなく、用途に応じて使い分ける機能だと考えた方がよさそうです。
検証データ
検証1で紹介したスクリプトを10回実施したデータが以下の通りです。
(venv) [root@ip-10-0-12-6 ~]# for i in {1..10}; do
echo "===== run $i ====="
python3 compare_s3_read.pydone
===== run 1 =====
=== S3 API ===
method: s3_api
elapsed_sec: 13.700153353000132
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.956538516999899
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 14.32
===== run 2 =====
=== S3 API ===
method: s3_api
elapsed_sec: 10.714957922000394
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.685456905999672
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 15.63
===== run 3 =====
=== S3 API ===
method: s3_api
elapsed_sec: 8.449593481999727
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.6867093869996097
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 12.30
===== run 4 =====
=== S3 API ===
method: s3_api
elapsed_sec: 9.686885212999186
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.6284407640005156
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 15.41
===== run 5 =====
=== S3 API ===
method: s3_api
elapsed_sec: 8.508913101999497
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.6858619519998683
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 12.41
===== run 6 =====
=== S3 API ===
method: s3_api
elapsed_sec: 9.615807279000364
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.712119611000162
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 13.50
===== run 7 =====
=== S3 API ===
method: s3_api
elapsed_sec: 8.918573054999797
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.712975813000412
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 12.51
===== run 8 =====
=== S3 API ===
method: s3_api
elapsed_sec: 8.72625753200009
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.7023375799999485
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 12.42
===== run 9 =====
=== S3 API ===
method: s3_api
elapsed_sec: 8.588836015999732
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.7292637600003218
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 11.78
===== run 10 =====
=== S3 API ===
method: s3_api
elapsed_sec: 8.540032994000285
file_count: 302
record_count: 11395
total_bytes: 4322243
=== S3 Files ===
method: s3_files
elapsed_sec: 0.6577089439997508
file_count: 302
record_count: 11395
total_bytes: 4322243
S3 API / S3 Files elapsed ratio: 12.98
検証2で紹介したスクリプトを10回実施したデータが以下の通りです。
(venv) [root@ip-10-0-12-6 ~]# for i in {1..10}; do echo "===== run $i ====="; python3 compare_s3_read.py; done
===== run 1 =====
=== s3_api ===
total_elapsed_sec : 9.182397
list_time_sec : 0.136362
read_time_sec : 8.775479
gzip_time_sec : 0.103932
json_parse_time_sec: 0.164978
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.925259
list_time_sec : 0.010791
read_time_sec : 0.353174
gzip_time_sec : 0.094379
json_parse_time_sec: 0.163934
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 9.92
read time ratio S3 API / S3 Files : 24.85
===== run 2 =====
=== s3_api ===
total_elapsed_sec : 8.805745
list_time_sec : 0.147954
read_time_sec : 8.392774
gzip_time_sec : 0.103018
json_parse_time_sec: 0.160409
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.620261
list_time_sec : 0.002861
read_time_sec : 0.361579
gzip_time_sec : 0.092297
json_parse_time_sec: 0.156108
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.20
read time ratio S3 API / S3 Files : 23.21
===== run 3 =====
=== s3_api ===
total_elapsed_sec : 8.815728
list_time_sec : 0.147662
read_time_sec : 8.403637
gzip_time_sec : 0.103528
json_parse_time_sec: 0.159140
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.615479
list_time_sec : 0.002955
read_time_sec : 0.355518
gzip_time_sec : 0.091857
json_parse_time_sec: 0.157765
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.32
read time ratio S3 API / S3 Files : 23.64
===== run 4 =====
=== s3_api ===
total_elapsed_sec : 8.766945
list_time_sec : 0.136593
read_time_sec : 8.364551
gzip_time_sec : 0.103864
json_parse_time_sec: 0.160315
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.612358
list_time_sec : 0.002946
read_time_sec : 0.356212
gzip_time_sec : 0.092353
json_parse_time_sec: 0.153655
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.32
read time ratio S3 API / S3 Files : 23.48
===== run 5 =====
=== s3_api ===
total_elapsed_sec : 8.803680
list_time_sec : 0.142492
read_time_sec : 8.398505
gzip_time_sec : 0.102637
json_parse_time_sec: 0.158387
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.613867
list_time_sec : 0.002861
read_time_sec : 0.356200
gzip_time_sec : 0.092326
json_parse_time_sec: 0.155157
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.34
read time ratio S3 API / S3 Files : 23.58
===== run 6 =====
=== s3_api ===
total_elapsed_sec : 8.644280
list_time_sec : 0.138200
read_time_sec : 8.241534
gzip_time_sec : 0.103188
json_parse_time_sec: 0.159696
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.611697
list_time_sec : 0.002883
read_time_sec : 0.355424
gzip_time_sec : 0.092081
json_parse_time_sec: 0.153846
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.13
read time ratio S3 API / S3 Files : 23.19
===== run 7 =====
=== s3_api ===
total_elapsed_sec : 8.371290
list_time_sec : 0.130511
read_time_sec : 7.976224
gzip_time_sec : 0.103561
json_parse_time_sec: 0.159350
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.609421
list_time_sec : 0.002866
read_time_sec : 0.351773
gzip_time_sec : 0.093042
json_parse_time_sec: 0.154399
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 13.74
read time ratio S3 API / S3 Files : 22.67
===== run 8 =====
=== s3_api ===
total_elapsed_sec : 8.962538
list_time_sec : 0.195937
read_time_sec : 8.498111
gzip_time_sec : 0.104592
json_parse_time_sec: 0.162070
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.609839
list_time_sec : 0.006799
read_time_sec : 0.347849
gzip_time_sec : 0.092311
json_parse_time_sec: 0.155235
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.70
read time ratio S3 API / S3 Files : 24.43
===== run 9 =====
=== s3_api ===
total_elapsed_sec : 8.600058
list_time_sec : 0.135539
read_time_sec : 8.197581
gzip_time_sec : 0.104425
json_parse_time_sec: 0.160874
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.612863
list_time_sec : 0.002592
read_time_sec : 0.344288
gzip_time_sec : 0.096808
json_parse_time_sec: 0.161667
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.03
read time ratio S3 API / S3 Files : 23.81
===== run 10 =====
=== s3_api ===
total_elapsed_sec : 8.638668
list_time_sec : 0.158719
read_time_sec : 8.215220
gzip_time_sec : 0.103261
json_parse_time_sec: 0.159798
file_count : 302
record_count : 11395
total_bytes : 4322243
=== s3_files ===
total_elapsed_sec : 0.608960
list_time_sec : 0.002941
read_time_sec : 0.347605
gzip_time_sec : 0.093478
json_parse_time_sec: 0.156935
file_count : 302
record_count : 11395
total_bytes : 4322243
=== comparison ===
total elapsed ratio S3 API / S3 Files: 14.19
read time ratio S3 API / S3 Files : 23.63
まとめ
今回は、Amazon S3 Filesを利用して、S3上のCloudTrailログをファイルシステムとして読み込む検証を行いました。
結果として、今回の検証条件では、S3 Files経由の処理がS3 API経由より大幅に高速でした。
簡単な一括比較では、S3 APIの平均処理時間が約9.55秒、S3 Filesの平均処理時間が約0.72秒となり、S3 API の方が約13倍遅い結果となりました。
今回の検証から、S3 Filesは特に以下のような運用作業で有効だと感じました。
- S3上のログファイルを一括で確認・集計したい
- CloudTrailやCloudWatch Logsのエクスポート結果を処理したい
- 既存のファイル処理スクリプトを活用したい
- S3 APIを意識せず、ファイルパスベースでデータを扱いたい
- 障害調査やデータ確認をより直感的に行いたい
そのため、上記のような処理において、S3 Filesは運用面でも性能面でも十分に試す価値があると感じました。
参考リンク:AWS公式ドキュメント
↓ほかの協栄情報メンバーのAmazon S3についての記事を公開しています。ぜひ参考にしてみてください。
■AWS運用エンジニアがS3 Filesを触ってみた(齊藤弘樹)
■Amazon S3のアーカイブされたオブジェクトの復元で勘違いしていた話(齊藤弘樹)
■Amazon S3のオブジェクトのストレージクラスを知りたかっただけなのに、、、(齊藤弘樹)
■KMSキー暗号化バケットでのS3レプリケーション設定の解決方法(齊藤弘樹)


