はじめに

シェル芸オンラインジャッジというウェブサイトを開発しています。 今回はユーザが入力したコマンドを実行するDockerコンテナにメモリやCPUなどのリソースの制限を設定しました。

シェル芸オンラインジャッジ

設定内容

設定前

Pythonスクリプトからコンテナを実行しています。 元々はネットワークの制限だけ設定していました。

container = self.client.containers.run(
    self.image_id,
    detach=True,
    command="sleep 60",
    ipc_mode="none",
    network_mode="none",
)

設定後

メモリ、CPU、プロセス数、権限、データサイズの制限を設定しました。

container = self.client.containers.run(
    self.image_id,
    detach=True,
    command="sleep 60",
    ipc_mode="none",
    network_mode="none",
    mem_limit="512m",  # メモリ制限
    memswap_limit="512m",  # スワップ制限
    nano_cpus=500000000,  # CPU使用率制限 (0.5 CPU)
    pids_limit=50,  # 最大プロセス数制限
    cap_drop=["ALL"],  # 権限制限
    tmpfs={"/media": "size=100M"},
    ulimits=[
        # fsize (file size): 1プロセスが作成できる最大ファイルサイズ(バイト単位)
        docker.types.Ulimit(name="fsize", soft=50000000, hard=50000000)
    ],
)

設定の動作確認

設定が正しく行われているか確認してみます。

メモリ

200MB程度のメモリの使用を試します。

python3 -c "a = 'a' * (200 * 1024 * 1024)"

強制終了しました。

z.bash: 1 行: 19 強制終了 python3 -c "a = 'a' * (200 * 1024 * 1024)"

また、コンテナのステータスを見るとメモリが256MBに制限されていることを確認できました。

docker stats

CPU

下記コマンドで負荷をかけます。

timeout 5s bash -c "while true; do :; done"

コンテナのステータスを見るとCPUの使用率が50%程度が上限になっていることが確認できました。

docker stats

プロセス数

下記コマンドでプロセスを100個作成します。

for i in {1..100}; do sleep 1 & done

コンテナのステータスを見るとPIDSの上限が50になっていることが確認できました。

docker stats

データサイズ

下記コマンドで制限より大きいファイルを作成すると、制限を超過した判定が出ることを確認しました。

dd if=/dev/zero of=/media/test_60mb bs=1M count=60
z.bash: 1 行: 19 ファイルサイズ制限を超過しました (コアダンプ) dd if=/dev/zero of=/media/test_60mb bs=1M count=60

おわりに

コンテナのリソースの制限を設定しました。 本当はユーザも変更したかったのですが、 設定が面倒そうなのでまた時間がある時に対応します。 それでは、また。