概要
Oracle Cloud Infrastructure(OCI)のコンテナサービス Container Instance(CI)には、AWS ECS Exec のような標準の対話型コマンド実行機能がありません。そのため、通常は確認したい情報を標準出力や標準エラー出力へ出力して確認する必要があります。しかし、特に構築・検証フェーズでは、Shellを使ってコンテナ内の状態やファイルを確認したい場面が多くあります。
そこで本記事では、FSS 上の command.txt を定期的に監視する軽量なエージェントをコンテナ内で動作させ、指定したコマンドを実行して結果を出力する「Run command」の仕組みを検証した内容を紹介します
構成
File Storage Service (FSS)を作成して、Mount TargetをContainer Instance(CI)と操作VMそれぞれ配置します
FSS上にciagent ディレクトリを作成し、管理ファイルを配置します。
ファイルのそれぞれの役割は次のとおりです。
- runcmdagent.sh
- Run commandエージェント
command.txtに記載されたコマンドを60秒毎に読み込み、state.txtのハッシュ値と比較して前回と変更があった場合はコマンドを実行、結果をoutput.txtに保存
- Run commandエージェント
- command.txt
- 実行したいコマンドを記述するファイル
- output.txt
- Run commandエージェントの実行結果
- state.txt
- 最後に実行したcommand.txtのSHA-256ハッシュ値
- runcmdagent.log
- runcmdagent.sh 自身の動作ログ
事前準備
File Storage
File StorageおよびFile Storage Mount Targetを作成します
Run commandエージェント作成
コマンドを実行するエージェントを作成しFSSのディレクトリciagentに配置します
#!/bin/sh
# エラー発生時にスクリプトを即時終了
set -eu
# 各種ファイルパスとログの最大行数の定義
BASE="/mnt/fss/ciagent"
CMD_FILE="$BASE/command.txt"
OUT_FILE="$BASE/output.txt"
STATE_FILE="$BASE/state.txt"
LOG_FILE="$BASE/runcmdagent.log"
MAX_LINES=100
# エージェントの起動ログを記録
echo "===== $(date -Iseconds) =====" >> "$LOG_FILE"
echo "agent started pid=$$" >> "$LOG_FILE"
# 外部からのコマンド実行指示を監視する無限ループ
while true; do
# command.txtが存在する場合のみ処理を実行
if [ -f "$CMD_FILE" ]; then
# 現在のcommand.txtのハッシュ値(SHA-256)を取得
current_hash="$(sha256sum "$CMD_FILE" | awk '{print $1}')"
# 前回実行時のハッシュ値を読み込む(ファイルがない場合は空)
last_hash="$(cat "$STATE_FILE" 2>/dev/null || true)"
echo "poll $(date -Iseconds) current=$current_hash last=$last_hash" >> "$LOG_FILE"
# ハッシュ値が異なれば(=コマンドが新しく更新されていれば)実行
if [ "$current_hash" != "$last_hash" ]; then
# 実行するコマンドの中身を読み込む
cmd="$(cat "$CMD_FILE")"
# 出力ファイルに実行日時とコマンドを記録
{
echo "===== $(date -Iseconds) ====="
echo "\$ $cmd"
} >> "$OUT_FILE"
# コマンドの失敗でエージェント自体が落ちないよう一時的にset +eにする
set +e
sh -lc "$cmd" >> "$OUT_FILE" 2>&1
rc=$? # コマンドの終了コードを保持
set -e
# 終了コードを出力ファイルに追記
{
echo "[exit=$rc]"
echo
} >> "$OUT_FILE"
# 今回実行したハッシュ値を状態ファイルに保存して完了とする
printf '%s\n' "$current_hash" > "$STATE_FILE"
sync
fi
fi
# runcmdagent.log rotation
if [ -f "$LOG_FILE" ]; then
lines=$(wc -l < "$LOG_FILE")
if [ "$lines" -gt "$MAX_LINES" ]; then
KEEP_LINES=$((MAX_LINES / 2))
tail -n "$KEEP_LINES" "$LOG_FILE" > "$LOG_FILE.tmp"
mv "$LOG_FILE.tmp" "$LOG_FILE"
fi
fi
# Polling
sleep 60
done
実行コマンド作成
実行したいコマンドを記述するファイルを作成しFSSのディレクトリciagentに配置します
例えば以下のように設定します
#01
ps aux
Container Instancesの作成
Container Instancesの定義ファイルを作成します
起動コマンドにsh /mnt/fss/ciagent/runcmdagent.shを追加することによりエージェントを起動します
## Replace << >> with the appropriate value
{
"compartmentId": "<<ocid1.compartment.oc1..*******>>",
"displayName": "nginx-ciagent",
"availabilityDomain": "<<availabilityDomain>>",
"shape": "CI.Standard.E4.Flex",
"shapeConfig": {
"ocpus": 1.0,
"memoryInGBs": 1.0
},
"vnics": [
{
"subnetId": "<<ocid1.subnet.oc1.*******>>",
"privateIp": "<<Private IP>>",
"isPublicIpAssigned": false
}
],
"containers": [
{
"displayName": "nginx",
"imageUrl": "docker.io/library/nginx:stable-alpine",
"command": ["/bin/sh", "-c"],
"arguments": ["sh /mnt/fss/ciagent/runcmdagent.sh & exec nginx -g 'daemon off;'"],
"volumeMounts": [
{
"volumeName": "shared-fss",
"mountPath": "/mnt/fss",
"isReadOnly": false
}
]
}
],
"volumes": [
{
"name": "shared-fss",
"volumeType": "OCI_FSS_FILE_SYSTEM",
"mountTarget": {
"ociFssMountTargetType": "OCID",
"id": "<<ocid1.mounttarget.oc1.*******>>"
},
"export": {
"ociFssExportType": "OCID",
"id": "<<ocid1.export.oc1.*******>>"
},
"security": {
"auth": "SYS",
"isEncryptedInTransit": true
},
"mountCommand": {
"mountOptions": [
{ "option": "hard" },
{ "option": "noac" },
{ "option": "retrans", "value": "11" }
]
},
"subnetId": "<<ocid1.subnet.oc1.*******>>"
}
]
}
OCI CLIコマンドでCI作成実行します
$ oci container-instances container-instance create --from-json file://ci_nginx-ciagent.json --debug
動作確認
- FSS確認
コマンドが正常に実行されていればoutput.txt,state.txtおよびruncmdagent.logが作成されます
$ ls -lart /mnt/fss/ciagent
total 73
drwxrwxrwx 3 root root 12 Aug 5 18:36 ..
-rw-r--r-- 1 root root 1211 Aug 9 04:10 runcmdagent.sh
-rw-rw-r-- 1 root root 25 Aug 9 15:19 command.txt
-rw-r--r-- 1 root root 24832 Aug 9 15:20 output.txt
-rw-r--r-- 1 root root 65 Aug 9 15:20 state.txt
drwxr-xr-x 2 root root 5 Aug 9 23:23 .
-rw-r--r-- 1 root root 12528 Aug 9 23:45 runcmdagent.log
実行したいコマンドはcommand.txtに設定しています
#01
ps aux
コマンド実行結果がoutput.txtに出力されています
$ tail /mnt/fss/ciagent/output.txt
$ #01
ps aux
PID USER TIME COMMAND
1 root 0:00 nginx: master process nginx -g daemon off;
7 root 0:00 sh /mnt/fss/ciagent/runcmdagent.sh
8 nginx 0:00 nginx: worker process
9 nginx 0:00 nginx: worker process
19 root 0:00 ps aux
[exit=0]
コマンド内容を変更すれば実行してoutput.txtに追記出力します
まとめ
この方法により、構築・検証フェーズで必要な確認作業を、コンテナへログインせずに実施できます。実行結果、状態管理、エージェントの動作ログを分けて保存することで、確認しやすく運用しやすい構成にできました
OCIでは標準機能だけでECS Exec相当の機能は得られませんが、FSSとShellスクリプトを組み合わせることで、実用的な代替手段を作れることが分かりました
本構成は検証をスムーズに進めるための手軽な代替手段として非常に有効ですが、FSSへの書き込み権限がそのままコンテナ内の任意コード実行につながるため、FSSへのアクセス制限やファイルの権限設定等利用する環境や権限管理には十分にご注意ください