SFTPサーバーのMountpoint for Amazon S3によるマウントが切れる問題



これは、[Wano Group Advent Calendar 2024](https://qiita.com/advent-calendar/2024/wano-group)の14日目の記事です。
13日目は、私[@kotobuki5991](https://qiita.com/kotobuki5991)の、[URLのヒストリーを保持しつつ、1ページで複数の検索を走らせるために頑張った話](https://qiita.com/kotobuki5991/private/006a33909bc1264b9958)です。


# 概要

弊社では、カラオケやミュージックビデオの配信ができるサービス[Video Kicks](https://www.tunecore.co.jp/video-kicks-karaoke)を開発・運営しています。


配信のためには、配信ストアに対して動画ファイルや楽曲のメタ情報（タイトルやアーティスト名など）を納品する必要があります。

今回は、納品に使用しているSFTPサーバーで発生したエラーと、その解消のために実施したことを書いていきます。


# SFTPサーバーの構成

Lightsailインスタンス上のDockerコンテナ内でmount-s3を実行し、S3にアップロードされた納品ファイルをマウントしています。
配信ストアはS3に直接アクセスすることなく、mount-s3によりマウントされたファイルをDLします。

![スクリーンショット 2024-12-12 9.25.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475547/a183839e-6d1a-8791-0a24-edfb96d443e9.png)


SFTPサーバーを構成する他の方法としては、[AWS Transfer Family](https://aws.amazon.com/jp/aws-transfer-family/)などが挙げられます。
しかし、コスト面を考慮し、本構成が採用となりました。

https://docs.aws.amazon.com/ja_jp/transfer/latest/userguide/create-server-sftp.html


# マウントが切れる問題

このSFTPサーバーを運用する中で、ある日ファイルのマウントが切れてしまう問題が起こりました。
動作確認したところ、以下のようなエラーが発生しています。
エラーのタイミング的に、数十GB程度の大量DLリクエスト時に発生すると思われます。

```
sftp> ls
remote readdir("/"): Failure
```

また、調査中に以下のログを発見しました。
mount-s3プロセスがOOMによりkillされていることが分かります。
```
# dmesg | tail -n 50
[22051422.570373] Memory cgroup out of memory: Killed process 1234567 (mount-s3) total-vm:1900108kB, anon-rss:1022488kB, file-rss:1548kB, shmem-rss:0kB, UID:0 pgtables:2216kB oom_score_adj:0
```

# 対応策

以下の3つの対応を実施しました。

1. S3側で過去ファイルをマウント対象外の場所に移動
2. mount-s3のバージョン更新
3. swap領域の確保

## 1. S3側で過去ファイルをマウント対象外の場所に移動

マウント対象を減らすため、不要なS3オブジェクトをマウント対象外の場所に移動しました。

## 2. mount-s3のバージョン更新

以下のissueで話されていますが、mount-s3のバージョンが1.10以下の場合
メモリ使用量が多くなってしまうバグがあるようです。

https://github.com/awslabs/mountpoint-s3/issues/566

使用しているmount-s3のバージョンは、`1.5.0`でした。
こちらは、最新バージョンに更新しておきます。

## 3. swap領域の確保

swap領域とは、物理メモリが不足したときに使用されます。
ハードドライブ上に存在しているため、物理メモリに比べて処理速度が落ちます。一方、メモリ不足を一時的に補うことができるためOOM回避につながります。（積極的に利用するものではないですが）

詳細な設定項目については後述します。

# Dockerfile

ここでは、再度docker buildし直すことで、mount-s3最新版を取得します。

```Dockerfile

# DebianベースのDockerイメージを指定
FROM debian:latest AS builder

# 必要なパッケージをインストール
RUN apt-get update && \
    apt-get install -y --no-install-recommends wget gnupg && \
    rm -rf /var/lib/apt/lists/*
# MP_ARCHを取得
RUN wget -q --no-check-certificate "https://s3.amazonaws.com/mountpoint-s3-release/latest/x86_64/mount-s3.deb" && \
    wget -q --no-check-certificate "https://s3.amazonaws.com/mountpoint-s3-release/latest/x86_64/mount-s3.deb.asc" && \
    wget -q --no-check-certificate https://s3.amazonaws.com/mountpoint-s3-release/public_keys/KEYS
# Import the key and validate it has the fingerprint we expect
RUN gpg --import KEYS && \
    (gpg --fingerprint mountpoint-s3@amazon.com | grep "673F E406 1506 BB46 9A0E  F857 BE39 7A52 B086 DA5A")
# Verify the downloaded binary
RUN gpg --verify mount-s3.deb.asc
# 新しいコンテナに依存関係を最小限にしてRPMをインストール
FROM atmoz/sftp:debian
COPY --from=builder /mount-s3.deb /mount-s3.deb
# 必要なパッケージをインストール
RUN apt-get update && \
    apt-get install -y less vim curl unzip sudo && \
    apt-get install -y --no-install-recommends ./mount-s3.deb fuse && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* mount-s3.deb
# aws cli v2 のインストール
# https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-linux.html
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
    unzip awscliv2.zip && \
    sudo ./aws/install
RUN echo "user_allow_other" >> /etc/fuse.conf

```


# docker-compose.yml

このファイルでswap領域に関する設定を実施します。

メモリ不足の時、不足したメモリを回収するためには2パターンの方法があります。
1. anonymousページをswapに逃す
2. fileページ（ファイルシステムのキャッシュなど）を捨てる

docker-compose.ymlに定義されているmem_swapinessという項目（0〜100で設定）は、メモリ回収をanonymousページの回収寄りにするのかあるいはfileページ寄りにするのかの重み付けをすることができます。

ただし、その重み付けは、システム側で定義されているvm.swappiness（0〜200。デフォルトは60であることが多い）と併せて判断されるようです。
つまり、mem_swappinessを100にしても、100%swapのみが使用されるということにはなりません。

また、HDDはswap利用時のコストが高いため、swappinessを低くした方が速度を確保できます。
一方でLightsailのストレージにも利用されるSSDの場合は1と2のコストは同程度です。したがって、ある程度高い値を設定することでOOM回避に役立つと思います。

参考:

https://chrisdown.name/ja/2018/01/02/in-defence-of-swap.html

https://chienomi.org/articles/linux/202002-swap.html

```docker-compose.yml

services:
  sftp_mountpoint:
    image: sftp-mountpoint_sftp
    build:
      context: docker
      dockerfile: Dockerfile
    privileged: true
    stdin_open: true
    deploy:
      resources:
        limits:
          cpus: '0.5' # コンテナに割り当てるCPUの数を0.5に制限します。
          memory: 1.5g # コンテナの最大メモリ使用量を1.5GBに制限します。
        reservations:
          cpus: '0.25' # コンテナが保証される最低CPU使用量を0.25に設定します。
          memory: 20m # コンテナが保証される最低メモリ使用量を20MBに設定します。
    memswap_limit: 2g # メモリ+スワップの合計使用量を2GBに制限します。物理メモリとスワップの合計（memoryが1.5gなのでswapは0.5gとなる）
    mem_swappiness: 100 # スワップ使用の優先度を最大にします。(0-100の範囲; デフォルト値はシステムに依存しますが、一般的には60)
    cap_add:
      - mknod
      - SYS_ADMIN
    devices:
      - /dev/fuse
    volumes:
      # 省略
    tty: true
    # ${変数:?err} は、 変数 が設定されていないか、環境変数の値が空の場合、 err を含むエラーメッセージと共に終了する
    command: sftp-user:${SFTP_PASS}:${PORT}

```

# とりあえず解決！

## DLテストを実施
全体でメモリは500M前後使用しており、mem_swappiness: 100でもswapは使用されていないようでした。
おそらく、mount-s3のバージョン更新により[メモリを食い潰す問題](https://github.com/awslabs/mountpoint-s3/issues/566)が解決したためswapを使用する状況にならなかったと思われます。

### htopコマンド結果

![スクリーンショット 2024-12-12 22.03.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475547/14568866-6d0e-3ef9-3fba-d28c4584fd87.png)


# 人材募集

弊社グループでは一緒に働くメンバーを募集中です、ご応募お待ちしています！

https://herp.careers/v1/wano