Containerアクション
Containerアクションは、後続のステップのためにコンテナを起動し、実行が終わると削除します。テスト対象のAPIが必要とするデータベースやメールサーバーを、ワークフローの中で用意できます。
外部アクションとしてmozership/probe-container で公開しています。ワークフローが初めて使うときにProbeがダウンロードし、固定したコミットのaction.ymlが示すSHA-256と一致する実行ファイルだけを実行します。実行の終了後にアクションを呼び出すProbe v1.27.0以降と、dockerコマンドが必要です。同じ引数を受け付ける別のランタイムも使えます。
基本的な構文
ワークフローでは40文字のコミットSHAでアクションを固定します。各リリース のノートの先頭に、コピーして使うアクションとコミットがあります。このページの例では、actionsで一度だけ名前を付けています。
name: Orders
actions:
container: github.com/mozership/probe-container@cd5c071a029cc1c6d4afe31750bf33ec6d9aa5b8 # v0.1.0
jobs:
- name: orders
steps:
- name: Start Postgres
id: pg
uses: container
with:
image: postgres:17
env:
POSTGRES_PASSWORD: "{{vars.pg_password}}"
ports: [5432]
wait:
port: 5432
outputs:
addr: res.addr
- name: The schema is empty to begin with
uses: db
with:
dsn: "postgres://postgres:{{vars.pg_password}}@{{outputs.pg.addr}}/postgres?sslmode=disable"
query: SELECT count(*) AS n FROM pg_tables WHERE schemaname = 'public'
test: res.rows[0].n == 0ステップは、コンテナを起動した時点で終わります。waitがあれば、準備ができるまで待ってから終わります。コンテナはそのジョブと他のジョブの後続のステップのために動き続け、実行が終わると匿名ボリュームとともに削除されます。実行が成功しても、失敗しても、Ctrl+Cで止めても同じです。そのために書くものはありません。
パラメータ
| パラメータ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
image | String | はい | - | 起動するイメージ |
command | List | いいえ | イメージのコマンド | コンテナで実行するコマンド。[redis-server, --appendonly, "yes"]のように、1語を1要素として書きます |
env | Map | いいえ | - | コンテナの環境変数。名前と値の組で書きます |
ports | List | いいえ | - | マシンから届くようにするコンテナのポート。ポートを参照してください |
volumes | List | いいえ | - | マウントするもの。/abs/path/init.sql:/docker-entrypoint-initdb.d/init.sql:roのように、ランタイムの--volumeが受け付ける形で書きます |
network | String | いいえ | ランタイムのデフォルト | コンテナをつなぐネットワーク。既にあるものを指定します。アクションはネットワークを作りません |
name | String | いいえ | ランタイムが付ける名前 | コンテナの名前。同時に動く2つの実行が同じ名前のコンテナを持つことはできないので、必要がなければ指定しません |
pull | String | いいえ | missing | イメージを取得する条件。missing、always、neverのいずれか |
wait | Map | いいえ | - | ステップが終わる前にコンテナが満たす条件。待機を参照してください |
keep | Boolean | いいえ | false | 実行が終わってもコンテナを残します。コンテナを残すを参照してください |
これら以外のキーをwithに書くと、何も起動しないうちにステップが失敗します。action.ymlがこれらをparamsとして宣言しているので、probe checkはそのようなキーを行番号とともに報告します。
アクションはenvの値を結果にもログにも出さず、変数の名前だけを出します。値はランタイムに引数として渡すので、コンテナの起動中はマシンのプロセス一覧から見え、起動後はランタイムが表示するコンテナの情報からも見えます。
ポート
portsの各要素は、コンテナのポートです。
| 要素 | 公開先 |
|---|---|
5432 | 127.0.0.1の空いているポート。結果がその番号を返します |
53/udp | 同じく空いているポート。TCP以外のプロトコル(udpまたはsctp)で公開します |
"8080:80"、"0.0.0.0:8080:80" | 要素に書いた場所。ランタイムの--publishが受け付ける形で書きます |
ポートだけを書く形を使ってください。ランタイムが空いているポートを選ぶので、同じマシンで動く2つの実行や、1つの実行の2つのジョブがぶつかりません。ポートがネットワークに開くこともありません。公開先は結果から読みます。
待機
waitがないと、ステップはコンテナを起動した時点で終わります。その時点では、中で動くものの準備はまだできていません。waitは、準備ができるまでステップを終わらせません。
| キー | 型 | 説明 |
|---|---|---|
port | Number | portsにあるTCPのポート。接続を受け付け、保ち続けることを待ちます。ランタイムは、コンテナの中で何も待ち受けていなくても公開したポートで接続を受け付け、すぐに閉じます。そのため、受け付けられただけの接続は数えません |
log | String | 正規表現 。コンテナがそれまでに標準出力と標準エラーに書いた内容の全体と照合します。全体と照合するので、(?s)ready.*readyと書けば、2回書かれる行を待てます |
healthcheck | Boolean | trueにすると、イメージのヘルスチェックが通るまで待ちます。ヘルスチェックのないイメージではステップが失敗します |
timeout | Duration | 待つ時間。90sのような形か、秒数で書きます。デフォルトは60sです |
複数を指定すると、すべてが満たされるまで待ちます。イメージの取得にかかる時間は、タイムアウトに含まれません。
待っている間にコンテナが止まるか、時間内に準備ができないと、ステップは失敗します。エラーの末尾には、コンテナのログの最後の数行が付きます。コンテナは、実行が終わるときに他のコンテナとともに削除されます。
データベースのように、応答できるようになる前から接続を受け付けるサーバーは、ログかヘルスチェックで待つほうが確実です。
steps:
- name: Start Postgres, and wait until it is up for good
uses: container
with:
image: postgres:17
env:
POSTGRES_PASSWORD: "{{vars.pg_password}}"
ports: [5432]
wait:
# The image starts the server once to set it up, and then again.
log: "(?s)ready to accept connections.*ready to accept connections"
timeout: 90sレスポンスオブジェクト
| プロパティ | 型 | 説明 |
|---|---|---|
res.id | String | コンテナのID |
res.name | String | コンテナの名前。同じnetworkにある別のコンテナは、この名前で接続します |
res.ports | Object | portsの各要素に対する、マシン側のポート。コンテナのポートをキーにして、res.ports["5432"]のように読みます。TCP以外のプロトコルではres.ports["53/udp"]です |
res.host | String | portsの最初の要素に届くアドレス。要素が別のアドレスを指定していなければ127.0.0.1です |
res.port | Integer | portsの最初の要素に対する、マシン側のポート |
res.addr | String | res.hostとres.portを合わせたもの。127.0.0.1:51110の形です |
req | Object | コンテナの起動に使った内容。envは変数の名前だけです |
rt | Duration | ステップにかかった時間 |
status | Integer | 0 |
res.host、res.port、res.addrは、ステップにportsがあるときだけ返ります。
起動できないコンテナや準備ができないコンテナは、結果ではなくエラーになります。そのため、testの内容にかかわらずステップは失敗します。
コンテナを残す
テストが失敗したときなど、実行のあとでコンテナの中を調べたい場合は、コンテナを残せます。
PROBE_CONTAINER_KEEP=1 probe workflow.ymlは、ワークフローを書き換えずに、その実行のコンテナをすべて残します。- ステップに
keep: trueを書くと、そのステップのコンテナだけを残します。
実行が終わると、アクションは残したコンテナと、それを削除するコマンドを表示します。
[WARN] actions.probe-container: kept containers of the run: run_id=88041310b4926f7b count=1 remove="docker rm --force --volumes $(docker ps --all --quiet --filter label=probe.run_id=88041310b4926f7b)"
[WARN] actions.probe-container: kept container: name=sleepy_dubinsky image=postgres:17 ports=127.0.0.1:51110->5432/tcpアクションが起動するコンテナには、実行のIDを値とするラベルprobe.run_idが付きます。残すコンテナにはprobe.keep=trueも付きます。docker ps --all --filter label=probe.run_idで、すべての実行が残したコンテナを一覧できます。
残るもの
Probeは実行が終わるとアクションを呼び出し、アクションはその実行のラベルが付いたコンテナをすべて削除します。コンテナが残るのは、次の場合だけです。
- ステップまたは環境変数が、コンテナを残すよう指定している。
kill -9などでProbeが強制終了され、後始末まで進めなかった。- ランタイムが25秒以内にコンテナを削除できなかった。この場合、Probeが警告を表示します。
名前付きのボリュームとネットワークは、アクションが作ったものではないので残ります。
ランタイム
アクションはdockerを実行します。PROBE_CONTAINER_RUNTIMEには、podmanやnerdctlのように同じ引数を受け付ける別のコマンドか、そのパスを指定できます。
PROBE_CONTAINER_RUNTIME=podman probe workflow.ymlランタイムはステップではなく環境変数で指定します。ランタイムはワークフローではなくマシンに属するものであり、実行の後始末は、ステップが使ったのと同じランタイムで行う必要があるためです。
アクションはイメージをビルドせず、既にあるコンテナの中で何かを実行することもありません。どちらも、shellのステップからランタイムを直接呼べば行えます。
ガードの下での動作
アクションはどのガードも守りません。コンテナの中で動くものや、それが接続するホストは、アクションが制限できるものではないためです。--read-onlyまたは--allow-hostの下では、--allow-actionでアクションを指定しない限り、ステップは拒否されます。
ガードを参照してください。