Docker: DockerfileでExpectスクリプトを使うと複雑になる例

DockerfileのRUNでExpectスクリプトを実行して、keyboard-configurationパッケージのインストールを自動化する例を記載します。ただし、結論を言えば、Expectスクリプトは別ファイルで用意すべきです。

1 keyboard-configurationパッケージ

keyboard-configurationパッケージをインストールするとキーボードレイアウトをインタラクティブに設定できます。

keyboard-configurationパッケージの応答は/etc/default/keyboardを用意すればスキップできますが、console-setupパッケージのようにできないケースもあるので、ここではExpectスクリプトを使って応答を自動化します。

2 Expectスクリプトの場合

keyboard-configurationのインストールを自動化するには以下のExpectスクリプトが必要なります。

  • "[More]"に対して"\n"を送信します。Expectスクリプトは[と]に対して\\\でエスケープする必要があります。
  • "Country of origin for the keyboard: "に対して"50\n"を送信します。
  • "Keyboard layout: "に対して"1\n"を送信します。
#!/usr/bin/env expect

set timeout -1
spawn apt-get install -y keyboard-configuration
expect {
    "\\\[More\\\]" { send "\n"; exp_continue; }
    "Country of origin for the keyboard: " { send "50\n"; }
}
expect {
    "\\\[More\\\]" { send "\n"; exp_continue; }
    "Keyboard layout: " { send "1\n" }
}
expect eof

[More]はキーボード一覧表示がターミナルソフトの高さに到達した場合に表示されます。

Configuring keyboard-configuration
----------------------------------

The layout of keyboards varies per country, with some countries having multiple
common layouts. Please select the country of origin for the keyboard of this
computer.
[More]

3 シェルスクリプトの場合

シェルスクリプトにすると以下のようになります。

  • ダブルコーテーション内の\\\が\に変換されます(シングルコーテーションの場合は\\が\に変換されますが、シェル変数を代入できません)。
  • printfの場合はシェルでの改行が改行文字として扱われます。
#!/bin/sh

apt-get update -y
apt-get install -y expect

printf "
set timeout -1
spawn apt-get install -y keyboard-configuration
expect {
    \"\\\\\\\\\[More\\\\\\\\\]\" { send \"\\\n\"; exp_continue; }
    \"Country of origin for the keyboard: \" { send \"50\\\n\"; }
}
expect {
    \"\\\\\\\\\[More\\\\\\\\\]\" { send \"\\\n\"; exp_continue; }
    \"Keyboard layout: \" { send \"1\\\n\" }
}
expect eof
" | expect

4 Dockerfileの場合

Dockerfileにすると以下のようになります。

  • Dockerfileは行末に\をつけることで複数行に処理を記載できます。
  • 複数行に処理を記載してもDockefileでの改行は処理に渡されません。printfの文字列の場合は改行文字の\nを明記する必要があります。複数のコマンドを記載する場合はセミコロンや&&で繋ぐ必要があります。
  • [More]の処理は不要になるようです(-tオプションなしでTERMを空にしている?)。
FROM ubuntu:16.04

RUN apt-get update -y && \
apt-get install -y expect && \
printf "\
set timeout -1\n\
spawn apt-get install -y keyboard-configuration\n\
expect {\n\
    \"\\\\\\\\\[More\\\\\\\\\]\" { send \"\\\n\"; exp_continue; }\n\
    \"Country of origin for the keyboard: \" { send \"50\\\n\"; }\n\
}\n\
expect {\n\
    \"\\\\\\\\\[More\\\\\\\\\]\" { send \"\\\n\"; exp_continue; }\n\
    \"Keyboard layout: \" { send \"1\\\n\" }\n\
}\n\
expect eof\n\
" | expect

5 まとめ

ExpectスクリプトをDockerfileのRUNで直接使おうとすると余計なコストが掛かります。Expectスクリプトを別に用意し、DockerfileのCOPYやADDでコンテナにコピーしてから実行するようにしましょう。

FROM ubuntu:16.04

RUN apt-get update -y && apt-get instal -y expect
COPY keyboard-configuration.exp /
RUN expect keyboard-configuration.exp