Docker run の起動時に任意コードを実行後 bash や ash を終了しない

この記事は、 Docker Advent Calendar 2022 の 23日目の記事だ。
空いていたので埋めちゃうよ。

この記事では、 bash や ash で任意のコードの実行後、ターミナルを終了せずに入力待ちにする方法について紹介する。
特に、 docker run の実行後に、その環境を維持したまま入力待ちにすることを考える。

例えば、 Windows コマンドプロンプトや PowerShell であれば、 CMD /K *** オプションや、 -NoExit -Command *** オプションで実現できるような内容だ。

本来なら、 docker build にてその任意コードの実行後の内容をイメージにするべきだが、 わざわざ build するまでもないとか、 build できない事情などもあるかもしれない。

ということで、 bash の場合と、 alpine などで使われる BusyBox ash それぞれについて、 docker run 実行時に任意コード実行後、ターミナルの入力待ちにする方法を紹介する。

bash の場合

bash の場合、 --rcfile オプションにて、起動時に実行するコマンドを指定できる。

ただし、 --rcfile はファイルを指定する必要があるため、 替わりにプロセス置換で実行コードを与えてやる方法をとる。

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:/# 

プロセス置換はコンテナ内で実行される必要があるため、一旦 bash -c にてコンテナ内で bash 実行させ、その中で改めて --rcfile オプションを指定した bash を起動する流れとなる。

実際に実行したいコマンドは、 echo で文字列として書き出す。
上記例では、 ls && export 'FOO=B A R' && MY_TIME=$(date) と言う文字列を echo させている。

引用符が二重三重になっていて、エスケープが非常に難しくなっているので注意。

BusyBox ash

alpine 3.15 以降に含まれる BusyBox の ash であれば、意外にもプロセス置換が使える。

しかし、 --rcfile に相当するオプションは残念ながら無い。
替わりに、 ash には ENV という環境変数に記載されたファイルを ash 起動時に実行する機能がある。

これを使おう。

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
/ # 

ash-c のコマンドに対して引数を与える -s オプションを使ってプロセス置換のファイルを与え、 それを $1 経由で ENV 環境変数にセット。 その状態で再度 ash を起動させれば、 bash と同様のことが行える。

ENV 環境変数に直接プロセス置換のファイルを指定せず、わざわざ一旦引数を経由させているのは、 入力を受け付ける ash プロセスが動いている間、 プロセス置換のファイルにアクセス可能にする必要があるためだ。
例えば、 export ENV=<(echo 'command');ash と実行しても、 ash 実行の段階ではプロセス置換のファイルが閉じられているので、コマンドは実行されない。

もうちょっと複雑な例

起動時に apt パッケージマネージャーのリポジトリを書き換える方法(Ubuntu):

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`')"

起動時に apk パッケージマネージャーのリポジトリを書き換える方法(Alpine):

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`')"

参考: https://stackoverflow.com/questions/74094552/how-not-to-terminate-after-carried-out-commands-in-bash

GNS3 に VyOS 仮想ルーターを追加する長い道のり③ 実践編

本記事は 【アットホームな現場です】🎄★☆ネットワーク系エンジニア★☆アレコレアウトプット★☆🎄 Advent Calendar 2022 17日目の記事だ。

GNS3 という OSS のネットワークエミュレータを、 Cisco の IOS などの取得なしに、無料のライセンス内で利用できるようにしようという話。

環境導入編、イメージ準備編に続く実践編。
以前の投稿はこちら。


実際に、 GNS3 上で VyOS を使ってみよう。

GNS3 プロジェクトを作成

GNS3 を開き、適当なプロジェクトを新規作成(ないし開く)する。

平行して、 GNN3 VM が起動するまで待とう。

VyOS の起動

GNS3 GUI 上で、テンプレート一覧から VyOS のデバイスをドラッグ&ドロップして、プロジェクトに追加する。

続きを読む

プロキシ環境下で Docker Desktop から Rancher Desktop への移行する

この記事は、 Docker Advent Calendar 2022 の 22日目の記事だ。

Windows 上や mac OS 上で、手軽に Docker や Kubernetes の環境を構築するための、 Docker Desktop というツールが存在する。

この Docker Desktop は、 2021年8月31日以降、営利企業での利用は基本的に有料サブスクリプションが必須 となった。
更に、 2022年10月27日以降、 Docker Team プランにて 100人のユーザー数上限が設けられ 、それ以上の場合は Docker Business プランの契約が必要となっている。

まぁ、営利企業ならそれぐらい払えよって話なんだが、のっぴきならん理由でなかなかそうもいかない諸兄も少なくないだろう。

正直、 Business プランで Docker Desktop 以外の機能を全く使わない(ないし使えない)場合、 $21/user/month はだいぶ高い。

ということで、代替?になるかわからんが、主に Windows での実行をスコープに、 同一コンセプトの Rancher Desktop への移行を紹介してみよう。


Rancher Desktop について

Rancher Desktop は、 Docker Desktop と同様 PC の仮想環境上にコンテナデーモンを起動させ、 デスクトップから手軽に Docker CLI (with Moby/dockerd) や nerdctl (with containerd) を利用できるようにするツールだ。

かつての SUSE Linux の開発元、 現在の openSUSE プロジェクトの主要スポンサーとなっている SUSE 社 が開発している、 Rancher ソリューションの一環として、 オープンソースで開発されているプロジェクトである。

WSL2 上に dockerd や containerd デーモンを立ち上げ、 他の WSL2 ディストリビューションや ホストOS から Docker CLI や nerdctl が使えるようになっている。

Windows の Docker CLI からも、 WSL 内の Docker CLI からも、 Docker outside of Docker (DooD) が問題なくできるなど、基本的な機能はしっかりしている。

Docker Desktop と比較すると、以下のような利点がある。

  • 無料で利用できる
  • nerdctl にも対応している
  • Kubernetes のバージョンが選べる

一方で、使っているといくつかの問題が目につく。 (1.7.0 現在)

  • プロキシ環境下で イメージの pull させる公式手順がない
    • 回避手順は後述する
  • WSL 内からの CLI の実行にいくつか難がある
    • docker CLI の実行が、 2~3 テンポ遅い
    • docker run--interactive ( -i ) オプションを付けないと、標準出力がホスト側に表示されない

また、問題とまでは言わないまでも、いくつか注意点もある。

  • 付属の docker-compose が、 docker compose コマンドと同等の Compose V2 である
  • Hyper-V の仮想化に依存するため、 VPN 環境下と相性が悪い
    • 後述

Docker Desktop から Rancher Desktop への移行

Windows 版に於ける、 Docker Desktop から Rancher Desktop への移行手順をまとめておく。

1. Docker Desktop を削除

公式の FAQ では、「Docker Desktop を Rancher Desktop と一緒にインストールできる(同時に実行できない)」と書かれてはいる。
しかし、 クライアントの Docker CLI コマンドやその設定フォルダ (%USERPROFILE%\.docker\) の設定内容が衝突して問題が発生するので、 Docker Desktop を削除しておいたほうが良い。

Q: Can I have Docker Desktop installed alongside Rancher Desktop?

A: Yes, but they cannot be run at the same time as both Rancher Desktop and Docker Desktop use the same Docker socket (/var/run/docker.sock). Before starting one, be sure to stop the other first.

その手順としては、以下の通り。

  1. Docker Desktop のアンインストール
  2. %USERPROFILE%\.docker フォルダの削除(ないし、リネーム)
  3. PC の再起動

3. Rancher Desktop のインストール

Rancher Desktop 本体のインストール。

Docker Desktop を動かしていたなら問題ないと思うが、 WSL2 を動かせるだけの仮想化支援機能を持った CPU が必要だ。

  1. https://rancherdesktop.io/ から、 Windows 向けインストーラーをダウンロード
  2. インストーラーを起動し、ライセンス (Apache License, Version 2.0) に同意してインストール
    • システムにインストールするか、ユーザーごとにインストールするか選べるが、 基本的にはシステムにインストールさせる。
    • 予め WSL2 が有効にされた状態でないと、 WSL2 の有効化のために何度か OS の再起動が必要になる。
  3. 初回起動時に、 Kubernetes の要否と コンテナランタイムを選択する。
    dockerd に変更しておこう。
  4. Rancher Desktop の設定で、 WSL2 ディストリビューション内からの docker コマンドの実行を有効にしておく
  5. プロキシ環境下の場合、以下の設定を行う

    • Docker daemon 側でイメージを pull する際のプロキシーを有効にするため、 PowerShell で以下のコマンドを実行し、 Rancher Desktop を再起動させる。

      'echo ''# proxy configuration'' >> /etc/rc.conf; echo ''rc_env_allow="http_proxy http_proxy no_proxy"'' >> /etc/rc.conf; echo ''export http_proxy="http://proxy.example.com:8080/"'' >> /etc/rc.conf; echo ''export https_proxy="http://proxy.example.com:8080/"'' >> /etc/rc.conf;' | wsl --distribution rancher-desktop --exec /bin/ash --stdin
      • : not found というエラーが出るが、気にしない。
      • Rancher Desktop のバージョンアップで、 docker daemon イメージの更新が入った場合、この設定をやり直す必要がある。
    • Docker CLI で イメージのビルドやコンテナ実行時のプロキシーを有効にするため、 Docker クライアントの設定 の例に従い、 %USERPROFILE%\.docker/config.json と、 各 WSL2 ディストリビューション毎の ~/.docker/config.json に proxy の設定を追記する。
      例:

      {
        "proxies":
        {
          "default":
          {
            "httpProxy": "http://proxy.example.com:8080",
            "httpsProxy": "http://proxy.example.com:8080",
            "noProxy": "*.test.example.com,.example2.com,127.0.0.0/8"
          }
        }
      }

Rancher Desktop を起動してしばらく待てば、 Windows ホストや、 WSL2 内から docker コマンドが利用できるようになる。

トラブルシューティング

プロキシ環境下の pull 絡みの操作で connection reset by peer のエラー

pull が絡む操作で以下のようなエラーが出る場合

username@pcname:~$ docker pull alpine:3.16
Error response from daemon: Get "https://registry-1.docker.io/v2/": read tcp 172.25.205.147:57296->34.205.13.154:443: read: connection reset by peer

Rancher Desktop のバージョンアップで、 docker daemon 側のプロキシの設定がリセットされているかもしれない。

上述の 『Docker daemon 側でイメージを pull する際のプロキシーを有効』 の設定をやり直す。

pull 絡みの操作で credentials のエラー

pull が絡む操作で以下のようなエラーが出る場合

error getting credentials - err: exit status 127, out: ``

Windows 側の場合、 %USERPROFILE%.docker\config.json に "credsStore": "wincred" を付け加える

@@ -1,4 +1,5 @@
 {
+  "credsStore": "wincred",
   "proxies": {
     "default": {

WSL 側の場合、 ~/.docker/config.json に "credsStore": "pass" を付け加えてみる

@@ -1,4 +1,5 @@
 {
+  "credsStore": "pass",
   "proxies": {
     "default": {

WSL 上で docker.sock の権限エラー

/var/run/docker.sock がパーミッションエラーになってしまう場合

dial unix /var/run/docker.sock: connect: permission denied

グループの設定とパーミッションを振ってから、 WSL のシェルにログインし直す。
ref: Q: How do I fix permission denied errors when trying to use Docker on WSL?

$ sudo groupadd docker
$ sudo adduser $USER docker
$ sudo chown root:docker /var/run/docker.sock
$ sudo chmod u=rw,g=rw,o= /var/run/docker.sock

WSL 上で実行するとコンテナ内の標準出力が表示されない

WSL ではなぜか、 docker run に --interactive ( -i ) オプションを付けないと、標準出力がホスト側に表示されない。

$ docker run --rm alpine ls -la
$ docker run --rm -i alpine ls -la
total 64
drwxr-xr-x    1 root     root          4096 Dec 22 17:31 .
drwxr-xr-x    1 root     root          4096 Dec 22 17:31 ..
-rwxr-xr-x    1 root     root             0 Dec 22 17:31 .dockerenv
drwxr-xr-x    2 root     root          4096 Nov 22 13:06 bin
drwxr-xr-x    5 root     root           340 Dec 22 17:31 dev
drwxr-xr-x    1 root     root          4096 Dec 22 17:31 etc
drwxr-xr-x    2 root     root          4096 Nov 22 13:06 home
[...]

Windows 側からだと問題なく表示される。

VPN 下で通信できない

Hyper-V の仮想化に依存するため、一部の VPN 環境下と相性が悪い。
回避方法は以下の記事を参照。

docker コマンド実行時に docker daemon is not running エラー

Rancher Desktop を起動して居るのにもかかわらず、以下のようなエラーが出る場合。

error during connect: This error may indicate that the docker daemon is not running.:

とりあえずしばらく待って再試行してみよう。
Rancher Desktop UI 上で起動が完了したように見えてから、実際に docker コマンドが使えるようになるまで 十数秒~数十秒 かかることが多い。

bash で標準出力と標準エラーを 2つのコマンドに出し分ける

本記事は シェルスクリプトのカレンダー | Advent Calendar 2021 - Qiita 17日目の記事だ。

殆どカレンダーが埋まってなかったので、思いついたネタで埋めちゃえ埋めちゃえ。

今回は、 bash 系列 (bash, zsh 等) の プロセス置換 (process substitution) 機能の話だ。

このプロセス置換は POSIX 互換の機能では無いため、以降の例は ash 系列 (busybox hush (ash), dash 等) では利用できない。

bash のプロセス置換

続きを読む

置換ができない/複数ある場合に sed の終了コード0以外にする

本記事は、 シェルスクリプト Advent Calendar 2021 の 4日目 の記事だ。
そして、 且つ docker Advent Calendar 2021 4日目 の記事でもある。

どちらのカレンダーもまだまだスッカスカなので、禁じ手で埋めにかかってしまった。


Docker 公式イメージ などをベースにして、カスタムしてイメージをビルドして使おうとした際、 なるべくなら /etc/apt/apt.conf.d/ 等のように、設定用のファイルを追加して、ツール側がいい感じにマージして利用してくれるのが望ましい。
しかし、 場合によってはやむを得ず、既存のファイルを sed コマンドなどで編集せざるを得ないこともあるだろう。

カスタムイメージの Dockerfile をビルドする際に、当初は意図通り書き換えられていても、イメージが更新された結果、イメージのリビルド時にファイルの書き換えが意図しない結果となってしまう場合がある。 1

通常、 sed コマンドは、置換が発生してもしなくても、 終了コード 0 で終了する。
このため、書き換えの成否にかかわらず、 docker build 時にエラーにならないため、コンテナ実行時に初めて置換が意図しない結果だったことに気づくことがある。

そこで、sed コマンドの書き換えで適切なパターンが見つからなかった場合に 0以外の終了コードを返し、ビルド時にエラーとする方法を考える。

続きを読む

sed の ブロック {} 内で i, r, e コマンドを使うと “unmatched `{‘” とエラーになる

本記事は、 シェルスクリプト Advent Calendar 2021 の 3日目 の記事だ。
3日目が終わりそうになっても誰も書きそうにないので、最近 sed コマンドで ブロック {} を使っていたら、 "unmatched `{" というエラーにハマったので、そのメモ。


target.txt:

foo
bar
foo
bar
foo

insert.txt:

***

上記のような、2つのファイルがあったとする。

target.txt ファイルに対して、 正規表現アドレスbar から始まる行を選択し、 その後ろに r コマンド insert.txt のファイルの中身を挿入する。

するとこんな結果になる。

$ sed -e '/^bar/rinsert.txt' target.txt
foo
bar
***
foo
bar
***
foo

では、アドレス指定の後ろにブロック {} を追加し、以下のように bar が2回以上ヒットしたらエラーコード出して終了するようにしてみる。

$ sed -e '/^bar/{rinsert.txt;x;/./Q129;g}' target.txt
sed: -e expression #1, char 0: unmatched `{'
$ echo $?
1

はい、別のエラーで失敗した。
ちゃんと {} の数はマッチしているのに……

続きを読む

次の○曜日 や 来週の○曜日 を取得する JavaScript

JavaScript の標準組込みオブジェクトは、日時関連の処理が非常に貧弱だ。

次の水曜日は何月何日? とか、 来週の月曜日は何月何日? といった値を取得しようと思ったら、タイムゾーン周りの処理と合わさって、思いのほか面倒だった。

とりあえず、動くコードができたので、記事にして紹介する。

続きを読む

puppeteer でファイルをダウンロードするときに、任意のパスと名前を指定して保存する

Puppeteer を使ってファイルをダウンロードする際に、任意のパスと名前で保存したい。

残念ながら、 現時点ではシンプルな方法は提供されていないようだ。
以下の Issue で何年にもわたって議論されているものの、 「コレ!」 という解決方法は無さそう。
Question: How do I get puppeteer to download a file? · Issue #299 · puppeteer/puppeteer

しかし、 この Issue の #issuecomment-668087154 のコメントで、 なかなか泥臭い方法で実現するヒントが書かれていた。
これを参考にして、任意のパスと名前でダウンロードファイルを保存してみよう。

実行方法

あらかじめ、 puppeteer の npm パッケージをローカルにインストールしておく。

npm install puppeteer --save

その状態で、後述の .js ファイルを nodejs で実行すれば OK だ。

node puppeteer-download-with-specify-name.js

コードと解説

続きを読む

Vagrant で Temporary failure resolving となる問題の解決 – イントラネット DNS 編

Vagrant で Ubuntu の VM を立ち上げたとき、 apt 等を行おうとすると、以下のようなエラーに遭遇した。

client: Err:1 http://security.ubuntu.com/ubuntu focal-updates/main amd64 libjpeg-turbo8 amd64 2.0.3-0ubuntu1.20.04.1
client:   Temporary failure resolving 'proxy.local.example'

上記のエラーの内容はプロキシに接続できないというものだが、 問題のポイントはプロキシかどうかはあまり関係が無く、 名前解決に失敗しているという部分だ。
こういうのはだいたい systemd-resolved のスタブリゾルバ周りの問題だと相場が決まっている。

…ということで、順番に確認しながら問題を解決していこう。

なお、 使った box は generic/ubuntu2004 で、 VirtualBox で VM をホストしている。

スタブリゾルバの確認

まず、 resolv.conf を確認してみる。

続きを読む

超簡単に大量の nanaco ギフトを自動登録するツール GUI 版

突然だが、自動車税や固定資産税などの税金や、公共料金などの払込票などを、どのように支払っているだろうか?
支払う額も大きくなりがちなので、なるべくお得に支払いたいのではないだろうか。

そこでオススメなのが nanaco ギフト だ。

(以下略)

この記事は、以前紹介した以下のツールの GUI 版を紹介するものだ。
nanaco ギフトのなんたるかは、以下のページをご参照をば。

端的に言うと、以下の通り:
税金や公共料金などに使える nanaco の残高を、 「ベネフィットワン」や「ちょコムショップ」などでお得に購入できる nanaco ギフトだが、 1,000 円単位で提供されることが多い。
大きな額の支払いだと、例えば 10万円 なら 100回 面倒なチャージ作業を繰り返す必要があり、その手間がお得分に見合わなくなる。
このため、 nanaco ギフトの登録を自動化してしまおうという話だ。

前回は、 PowerShell 版を作成して個人的にはこれで完全に事足りていたのだけれど、 ブログの記事にして公開したら、どうやら敷居が高かったらしく 「素人には難しい」とか「Excel で公開して」とかメールやらコメントやらを、いくつもいただいた。
確かに、昨年私が PowerShell 版の記事を出した頃、同じような時期に同じ目的の記事がいろんなブログで書かれるようになった気がするが、どれもこれも、いわゆる「プログラマ」なら簡単に使えるものの、それ以外の人には厳しそうなものばかりだった。

そこで、 専門的な知識がなくても使える、 みんなに易しい GUI (グラフィカルユーザーインターフェイス) で動くツールとして作り直してみたのが、今回のお話。

当初は、 PowerShell版 を流用して簡単に作れるかな~ とか目論んでいたのだけれど、色々うまくいかず、 結局イチから作り直している。

booth で販売中

続きを読む