tag:crieit.net,2005:https://crieit.net/tags/BusyBox/feed 「BusyBox」の記事 - Crieit Crieitでタグ「BusyBox」に投稿された最近の記事 2022-12-29T16:47:37+09:00 https://crieit.net/tags/BusyBox/feed tag:crieit.net,2005:PublicArticle/18357 2022-12-29T16:47:37+09:00 2022-12-29T16:47:37+09:00 https://crieit.net/posts/Docker-run-bash-ash Docker run の起動時に任意コードを実行後 bash や ash を終了しない <p>この記事は、 <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2022/docker">Docker Advent Calendar 2022</a> の 23日目の記事だ。<br /> 空いていたので埋めちゃうよ。</p> <p>この記事では、 bash や ash で任意のコードの実行後、ターミナルを終了せずに入力待ちにする方法について紹介する。<br /> 特に、 <code>docker run</code> の実行後に、その環境を維持したまま入力待ちにすることを考える。</p> <p>例えば、 Windows コマンドプロンプトや PowerShell であれば、 <code>CMD /K ***</code> オプションや、 <code>-NoExit -Command ***</code> オプションで実現できるような内容だ。</p> <p>本来なら、 docker build にてその任意コードの実行後の内容をイメージにするべきだが、 わざわざ build するまでもないとか、 build できない事情などもあるかもしれない。</p> <p>ということで、 bash の場合と、 alpine などで使われる BusyBox ash それぞれについて、 <code>docker run</code> 実行時に任意コード実行後、ターミナルの入力待ちにする方法を紹介する。</p> <h2 id="bash の場合"><a href="#bash+%E3%81%AE%E5%A0%B4%E5%90%88">bash の場合</a></h2> <p>bash の場合、 <code>--rcfile</code> オプションにて、起動時に実行するコマンドを指定できる。</p> <p>ただし、 <code>--rcfile</code> はファイルを指定する必要があるため、 替わりに<strong>プロセス置換</strong>で実行コードを与えてやる方法をとる。</p> <pre><code class="console">user@hostmachine:~$ docker run --rm -it debian:bullseye bash -c "bash --rcfile <(echo 'ls && export '\''FOO=B A R'\'' && MY_TIME=\$(date)')" bin dev home lib64 mnt proc run srv tmp var boot etc lib media opt root sbin sys usr root@container:/# echo $FOO : $MY_TIME B A R : Thu Dec 22 15:00:00 UTC 2022 root@container:/# </code></pre> <p>プロセス置換はコンテナ内で実行される必要があるため、一旦 <code>bash -c</code> にてコンテナ内で bash 実行させ、その中で改めて <code>--rcfile</code> オプションを指定した bash を起動する流れとなる。</p> <p>実際に実行したいコマンドは、 echo で文字列として書き出す。<br /> 上記例では、 <code>ls && export 'FOO=B A R' && MY_TIME=$(date)</code> と言う文字列を echo させている。</p> <p>引用符が二重三重になっていて、エスケープが非常に難しくなっているので注意。</p> <h2 id="BusyBox ash"><a href="#BusyBox+ash">BusyBox ash</a></h2> <p>alpine 3.15 以降に含まれる BusyBox の ash であれば、意外にもプロセス置換が使える。</p> <p>しかし、 <code>--rcfile</code> に相当するオプションは残念ながら無い。<br /> 替わりに、 ash には <code>ENV</code> という環境変数に記載されたファイルを ash 起動時に実行する機能がある。</p> <p>これを使おう。</p> <pre><code class="console">user@hostmachine:~$ docker run --rm -it alpine:3.15 ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'ls && export '\''FOO=B A R'\'' && MY_TIME=\$(date)')" bin etc lib mnt proc run srv tmp var dev home media opt root sbin sys usr / # echo $FOO : $MY_TIME B A R : Thu Dec 22 15:00:00 UTC 2022 / # </code></pre> <p><code>ash</code> の <code>-c</code> のコマンドに対して引数を与える <code>-s</code> オプションを使ってプロセス置換のファイルを与え、 それを <code>$1</code> 経由で <code>ENV</code> 環境変数にセット。 その状態で再度 ash を起動させれば、 bash と同様のことが行える。</p> <p>ENV 環境変数に直接プロセス置換のファイルを指定せず、わざわざ一旦引数を経由させているのは、 入力を受け付ける ash プロセスが動いている間、 プロセス置換のファイルにアクセス可能にする必要があるためだ。<br /> 例えば、 <code>export ENV=<(echo 'command');ash</code> と実行しても、 ash 実行の段階ではプロセス置換のファイルが閉じられているので、コマンドは実行されない。</p> <h2 id="もうちょっと複雑な例"><a href="#%E3%82%82%E3%81%86%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E8%A4%87%E9%9B%91%E3%81%AA%E4%BE%8B">もうちょっと複雑な例</a></h2> <p>起動時に apt パッケージマネージャーのリポジトリを書き換える方法(Ubuntu):</p> <pre><code>docker run --rm -it ubuntu:22.04 bash -c "bash --rcfile <(echo 'sed -i -E '\''s%^(deb(-src|)\s+)https?://(archive|security)\.ubuntu\.com/ubuntu/%\1http://srv2.ftp.ne.jp/Linux/packages/ubuntu/archive/%'\'' /etc/apt/sources.list && apt update && FooBar=`date -uIs`')" </code></pre> <p>起動時に apk パッケージマネージャーのリポジトリを書き換える方法(Alpine):</p> <pre><code>docker run --rm -it alpine:3.15 ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'sed -i -E '\''s%^https?://dl-cdn\.alpinelinux\.org/alpine/%https://ftp.udx.icscoe.jp/Linux/alpine/%'\'' /etc/apk/repositories && apk update && FooBar=`date -uIs`')" </code></pre> <p>参考: <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/74094552/how-not-to-terminate-after-carried-out-commands-in-bash">https://stackoverflow.com/questions/74094552/how-not-to-terminate-after-carried-out-commands-in-bash</a></p> advanceboy