Skip to content
云间札记
Go back

AWS S3 挂载 EC2 实战:s3fs-fuse 与实时同步监控

Updated:

背景

业务需要将 S3 对象存储作为 EC2 的扩展数据盘使用,要求:

成本对比

维度S3 StandardEBS gp3
存储费用$0.023/GB/月$0.08/GB/月
请求费用PUT $0.005/千次
性能高延迟(ms级)低延迟(亚毫秒)
适用场景冷数据/低频访问热数据/高频 I/O

结论:纯存储成本 S3 比 gp3 便宜约 70%,但请求费贵;生产环境推荐 gp3 热 + S3 冷分层架构。

IAM 权限设计

策略:最小权限原则

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "S3BucketAccess",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::qa-code-analysis-s3-xxx",
            "Condition": {
                "StringEquals": {
                    "aws:RequestedRegion": "ap-east-1",
                    "ec2:SourceInstanceARN": "arn:aws:ec2:ap-east-1:xxx:instance/i-xxx"
                }
            }
        },
        {
            "Sid": "S3ObjectOperations",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::qa-code-analysis-s3-xxx/*",
            "Condition": {
                "StringEquals": {
                    "ec2:SourceInstanceARN": "arn:aws:ec2:ap-east-1:xxx:instance/i-xxx"
                }
            }
        },
        {
            "Sid": "DenyAllOtherInstances",
            "Effect": "Deny",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::qa-code-analysis-s3-xxx",
                "arn:aws:s3:::qa-code-analysis-s3-xxx/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "ec2:SourceInstanceARN": "arn:aws:ec2:ap-east-1:xxx:instance/i-xxx"
                }
            }
        }
    ]
}

可信实体

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "aws:RequestedRegion": "ap-east-1"
                },
                "ArnEquals": {
                    "aws:SourceArn": "arn:aws:ec2:ap-east-1:xxx:instance/i-xxx"
                }
            }
        }
    ]
}

s3fs-fuse 安装

# Amazon Linux 2/CentOS/RHEL
sudo yum install -y gcc gcc-c++ make automake autoconf git fuse fuse-devel curl-devel libxml2-devel openssl-devel fuse3-devel

# 编译安装
git clone https://github.com/s3fs-fuse/s3fs-fuse.git
cd s3fs-fuse
./autogen.sh && ./configure && make && sudo make install

# 验证
s3fs --version
# Amazon Simple Storage Service File System V1.97

挂载配置

手动挂载

BUCKET_NAME="qa-code-analysis-s3-xxx"
MOUNT_POINT="/s3-data"
REGION="ap-east-1"
CACHE_DIR="/tmp/s3fs-cache"

mkdir -pv $MOUNT_POINT $CACHE_DIR
chmod 755 $CACHE_DIR

s3fs $BUCKET_NAME $MOUNT_POINT     -o iam_role=auto     -o url=https://s3.$REGION.amazonaws.com     -o endpoint=$REGION     -o use_cache=$CACHE_DIR     -o allow_other     -o uid=0     -o gid=0     -o mp_umask=022     -o multireq_max=5     -o parallel_count=10     -o multipart_size=52     -o connect_timeout=10     -o readwrite_timeout=30

# 验证
mountpoint /s3-data

fstab 开机自动挂载

# /etc/fstab
s3fs#qa-code-analysis-s3-xxx /s3-data fuse   _netdev,iam_role=auto,url=https://s3.ap-east-1.amazonaws.com,  endpoint=ap-east-1,use_cache=/tmp,allow_other,uid=1000,gid=1000,  mp_umask=002 0 0

实时同步监控脚本

cat > /root/s3fs-fuse/s3-realtime-sync.sh <<'EOF'
#!/bin/bash
SOURCE_DIR="/s3-data"
S3_BUCKET="s3://qa-code-analysis-s3-xxx"
LOG_FILE="/var/log/s3-realtime-sync.log"
BATCH_SIZE=10
BATCH_TIMEOUT=30

# 安装 inotify-tools
yum install -y inotify-tools

log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

declare -a file_queue=()
last_batch_time=$(date +%s)

process_batch() {
    if [[ ${#file_queue[@]} -eq 0 ]]; then return; fi
    log_message "处理批量文件: ${#file_queue[@]} 个"
    for file_path in "${file_queue[@]}"; do
        if [[ -f "$file_path" ]]; then
            local s3_path="${file_path#$SOURCE_DIR}"
            aws s3 cp "$file_path" "$S3_BUCKET/data$s3_path" --storage-class STANDARD_IA
        fi
    done
    file_queue=()
    last_batch_time=$(date +%s)
}

add_to_queue() {
    file_queue+=("$1")
    local current_time=$(date +%s)
    if [[ ${#file_queue[@]} -ge $BATCH_SIZE ]] ||        [[ $((current_time - last_batch_time)) -ge $BATCH_TIMEOUT ]]; then
        process_batch
    fi
}

# 定时刷新
periodic_flush() {
    while true; do
        sleep $BATCH_TIMEOUT
        if [[ ${#file_queue[@]} -gt 0 ]]; then
            local current_time=$(date +%s)
            if [[ $((current_time - last_batch_time)) -ge $BATCH_TIMEOUT ]]; then
                process_batch
            fi
        fi
    done
}

log_message "开始实时监控: $SOURCE_DIR"
periodic_flush &
flush_pid=$!

cleanup() {
    log_message "停止实时监控..."
    kill $flush_pid 2>/dev/null
    process_batch
    exit 0
}
trap cleanup EXIT INT TERM

# 监控文件变化
inotifywait -m -r "$SOURCE_DIR"     -e create -e modify -e moved_to     --exclude '\.(tmp|log|swp)$'     --format '%w%f %e' | while read file_path event; do
    if [[ -f "$file_path" ]]; then
        log_message "检测到文件变化: $file_path ($event)"
        add_to_queue "$file_path"
    fi
done
EOF

chmod +x /root/s3fs-fuse/s3-realtime-sync.sh

Systemd 服务化

cat > /etc/systemd/system/s3-realtime-sync.service <<'EOF'
[Unit]
Description=S3 Real-time Sync Service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/root/s3fs-fuse/s3-realtime-sync.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable s3-realtime-sync.service
sudo systemctl start s3-realtime-sync.service

定时任务

# crontab
# 每小时增量同步
0 * * * * /usr/local/bin/s3-sync-enhanced.sh

# 每天凌晨2点完整同步
0 2 * * * /usr/local/bin/s3-sync-enhanced.sh

# 每5分钟检查挂载状态
*/5 * * * * /usr/local/bin/check-s3-mount.sh

日志查看

# 实时同步日志
tail -f /var/log/s3-realtime-sync.log

# 系统服务日志
journalctl -u s3-realtime-sync.service -f

# S3 访问日志
aws s3api get-bucket-logging --bucket qa-code-analysis-s3-xxx

复盘

问题根因:初期使用 Access Key 挂载,密钥轮换导致服务中断

改进措施

本文首发于 wr.mrchi.cn,转载请注明出处。



Previous Post
Canal + Kafka CDC 数据同步实战:从 MySQL binlog 到实时数仓
Next Post
CDH 6.3.2 生产集群部署与磁盘扩容实战