Goole Cloud Build上でシェルを実行した結果、エラーが発生しても正常終了してしまう問題に対処

2023-03-27Bash,GCP

はじめに

開発効率化、品質向上のために現在のシステム開発に CI/CD パイプラインの構築は必須だと思います。

Github Action / CircleCI / Cloud Build など、CI/CD を構築するためのサービスがありますが、Cloud Build を多用させていただいています。

その "Cloud Build" でシェルコマンドを実行したところ、エラーが発生したにもかかわらずなぜか正常終了してしまい原因特定までに多少時間がかかってしまいました。

わかってしまえば当然なのですが、ついつい「シェルを実行している」ことを忘れてしまったことが原因でした。

検証環境

Google Cloud Platform ( GCP )Cloud Build を利用しました。

再現方法

Cloud Build でビルドステップを構築する方法として、ちょっと複雑なものを構築しようとした場合は Git リポジトリに cloudbuild.yaml という YAML ファイルを配置してやります。

ここで、以下のような cloudbuild.yaml というファイルを用意としましょう。

steps:
  - name: "bash"
    entrypoint: "bash"
    args:
      - "-c"
      - |
        echo "begin"
        ls -la /tmp
        ls -la /nothing
        echo "end"

処理概要は以下のとおりです。

  • name: "bash" : DockerHub で公開されている bash イメージを利用する
  • entrypoint: "bash" : bash コマンドを実行する
  • args: : bash の引数を指定する

以下の docker コマンドを実行したのと同義になるわけです。

docker run --rm bash -c '
  echo "begin"
  ls -la /tmp
  ls -la /nothing
  echo "end"
'

設定ページは以下のようになっています。

この cloudbuild.yaml の設定を元にビルドを実行します。

このビルドは エラー終了 となる想定でした。
なぜなら、実行しているコマンドのうち ls -la /nothing がエラーになるからです。
( 試しに読者の PC で実行していただければわかります。 /nothing というディレクトリが存在しないため、エラーとなりますね。 )

しかし、結果は正常終了してしまいます。

実行ログを見ると、たしかに ls -la /nothing というコマンドではエラーが発生しているのですが、 ビルドステップ全体は 正常終了 となっています。

原因

シェルコマンドの特性であるのにすっかり忘れていました。

話をさかのぼりますが、 Cloud Build で実行しているコマンドは以下のコマンドと同義です。

docker run --rm bash -c '
  echo "begin"
  ls -la /tmp
  ls -la /nothing
  echo "end"
'

こちらのコマンドをローカル PC などで実行してみましょう。

begin
total 8
drwxrwxrwt    1 root     root          4096 Mar 26 02:14 .
drwxr-xr-x    1 root     root          4096 May 12 02:47 ..
ls: /nothing: No such file or directory
end

2 つめの ls コマンドでエラーが発生していますが、最後の echo "end" まで実行が行われています。

上記コマンド実行後にステータスを確認してみます。

$ echo $?
0

正常終了扱いですね。

bash -c "..." コマンドは、 -c で渡した複数コマンドのうち、最後のコマンドの合否で実行ステータスが決まります。

最後の echo を消してみましょう。

$ docker run --rm bash -c '
  echo "begin"
  ls -la /tmp
  ls -la /nothing
'

begin
total 8
drwxrwxrwt    1 root     root          4096 Mar 26 02:14 .
drwxr-xr-x    1 root     root          4096 May 12 02:47 ..
ls: /nothing: No such file or directory

$ echo $?
1

では、最後の echo を削除せずに、問題を解決するにはどうしたら良いでしょう?

対処法 1

実行時のコマンドラインオプションを追加してやります。 -e オプションを追加します。

$ docker run --rm bash -e -c '
  echo "begin"
  ls -la /tmp
  ls -la /nothing
  echo "end"
'

begin
total 8
drwxrwxrwt    1 root     root          4096 Mar 26 02:14 .
drwxr-xr-x    1 root     root          4096 May 12 02:48 ..
ls: /nothing: No such file or directory

$ echo $?
1

Cloud Build の YAML ファイルを修正します。

steps:
  - name: "bash"
    entrypoint: "bash"
    args:
      - "-e"
      - "-c"
      - |
        echo "begin"
        ls -la /tmp
        ls -la /nothing
        echo "end"

対処法 2

実行される複数コマンドの戦闘で、 set -o errexit を実行し、エラー発生時に強制的にシェル実行をエラー停止させるようにします。

$ docker run --rm bash -e -c '
  set -o errexit
  echo "begin"
  ls -la /tmp
  ls -la /nothing
  echo "end"
'
begin
total 8
drwxrwxrwt    1 root     root          4096 Mar 26 02:14 .
drwxr-xr-x    1 root     root          4096 May 12 02:51 ..
ls: /nothing: No such file or directory

$ echo $?
1

Cloud Build の YAML ファイルを修正します。

steps:
  - name: "bash"
    entrypoint: "bash"
    args:
      - "-c"
      - |
        set -o errexit
        echo "begin"
        ls -la /tmp
        ls -la /nothing
        echo "end"

ひとこと

どちらの方法でも問題は解決できます。

結局問題は "Cloud Build" の設定などではなく、Bash実行時のオプションや設定でした。

いつものシェルに関するブログ記事になりましたね。

2023-03-27Bash,GCP