※人生で初めて書いたQiitaですので、いたらぬ点があれば(やさしく)ご指摘ください..
長めの対話型コマンドをdockerfileに書きたい。
dockerfileで環境構築を自動化する際、自分はつまずきました。
dockerfile、expectファイル、正規表現3つを絡めて解説した記事があると便利かな、と思い、執筆した次第です。
全部 YES でOKなコマンドなら..
yes | (y 入力を必要とするコマンド)
と記述すればOK。
(引用:コマンドの途中で聞いてくる yes を自動入力したい?それyesで出来るよ)
パスワード設定とか、YES以外の文字列を打つコマンドの場合は?
**「expectファイル」(.exp)**を使います! expect:「待ち受ける、予期する」 の意。
**「こう聞かれたらこう答えよ!」**という命令を書き込んでおくファイルです。
今回は、MYSQLをUbuntu(20.04)にインストール時に実行する、mysql_secure_installationコマンド を例に説明します。
こちらの記事の「ステップ2 — MySQLの設定」作業を、dockerfileで自動化することを想定します。
それぞれの設定項目の説明は割愛しますが、6つの質問に対し、
1. No
2. password (※自由に決めてください)
3. y
という、3種類の返答をしたい場合を考えます。(返答の順番はNo→password→y→y→y→y)
まず、dockerfileと、expectファイルはそれぞれこんな感じです。
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y sudo wget vim curl gawk make gcc
# 〜中略〜
# ↓mysqlのインストールあれこれ
RUN yes | sudo apt install mysql-server
RUN sudo usermod -d /var/lib/mysql/ mysql
RUN sudo service mysql start
# ↓本題のexp構文です!
COPY mysql_secure_installation.exp /
# dockerfileと同じディレクトリにあるmysql_secure_installation.expファイルの中身をコピー
RUN expect mysql_secure_installation.exp
# mysql_secure_installation.expの内容を実行
CMD [...]
COPY句で、Dockerfileと同じディレクトリに置いてあるmysql_secure_installation.expファイルを呼び出します。
次に、RUN expect mysql_secure_installation.expで、expファイルをそのまま実行。
気になるmysql_secure_installation.expの中身がこちら↓。
# !/usr/bin/expect
#↑これはシバン(shebang)と呼ばれる決まり文句みたいなもの。「インタプリタにexpectを指定」してるそうです(^^;
#参考:https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14146257229
set ans1 No #"返答1"は"No"をセット
set ans2 password #"返答2"は"password"をセット
set ans3 y #"返答3"は"y"をセット
spawn sudo mysql_secure_installation #実行するコマンドをspawnで指定
expect {
-regexp ".*VALIDATE PASSWORD.*" { #正規表現でキーワードをキャッチ、質問を特定
send "${ans1}\n" #そのキーワード("VALIDATE PASSWORD")を含む質問が来たら、ans1(No)を送る(send)
exp_continue
}
-regexp ".*set the password for root.*" {#同じく正規表現でキーワードをキャッチ、質問を特定
send "${ans2}\n" #同じく、キーワード("set the password for root")を含む質問が来たら、ans2(password)を送る
exp_continue
}
"Re-enter new password:" { #この質問は短かったので、正規表現は使わずに、一字一句全部指定して質問を特定しています。
send "${ans2}\n"
exp_continue
}
-regexp ".*anonymous users.*" {
send "${ans3}\n"
exp_continue
}
-regexp ".*root login remotely.*" {
send "${ans3}\n"
exp_continue
}
-regexp ".*test database and access to.*" {
send "${ans3}\n"
exp_continue
}
-regexp ".*privilege tables.*" {
send "${ans3}\n"
exp_continue
}
}
exit 0
簡単に解説すると、
まず冒頭の3行、SET〜で3種類の返答を規定します。
次に、spawn句で、実行するコマンド(=expectで返答を待ち受けたいコマンド=sudo mysql_secure_installation)を指定します。
そしてexpect句で、待ち受ける質問を特定する方法と、その質問に対する返答(ans1〜3のいずれか)を指定します。
-regexpは、正規表現を使いますよ、というオプションです。
(regexp:regular expression,直訳で正規表現)
dockerfileを書く時は、まず適当なコンテナ等でコマンドを一通り試してから書くはずで、本来は**対話型の質問は一字一句わかってるわけだし、正規表現なんてなんで使うの?**とそもそもの疑問を持たれた方もいるかもしれません。
自分はそうはいっても、そこそこ長い質問文を完全一致でexpect句に書くのは怖かったので(一文字足りないとか、スペースの有無とか)、regexpオプションを使いました。
キーワードだけ書くようにすれば、expectファイルもグッと見やすくなりますし、、ですが、その質問固有のキーワードを抜き出すことに注意してください!他の質問にも現れるキーワードは当然避ける!
exp_continueについて。本来ならばexpectは、対話がマッチした時点で指定したアクションを実行し、アクション実行後はexpectコマンドは終了、次のコマンドへ移るのですが、exp_continueを書くことで、マッチ&アクション後もexpectから抜けずにexpectの処理を続けることができます。(この点参考にさせていただいた投稿)
mysql_secure_installationのコマンドが一通り終わったら、exit 0で正常終了。
以上、長くなりましたが、dockerfile,expect,regexを絡めて使う参考例でした。
その他参考にしたサイト1 2