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