talesleaves-dev

諸々の開発のメモ。画像は面倒

VagrantでCentOS7にMySQL8最新バージョンをプロヴィジョニングする

前提条件と目標

  • ホストマシンはWindows10、仮想環境はVirtualBoxを使用、ゲストOSはCentOS7
  • MySQL8(執筆時点では 8.0.12)をyumでインストール
  • インストールからMySQLのパスワード変更までをSSHログイン無しで行う
  • 上記をVagrantfileに記述してvagrant sshで即MySQLのコマンドを実行できる状態まで持っていく

MySQL8のプロヴィジョニングの問題点

MySQL8ではセキュリティの強化が行われ、デフォルト設定が大幅に変更されました。
設定の変更についてはmy.cnfで書き換えが可能ですのでさほど大きな問題ではありません*1が、rootのパスワード変更を対話型のmysql_secure_installationを通じて行う必要があることが問題です。
mysql_secure_installationを通じて手動で変更する手順は他のサイトさんにお任せするとして、Vagrantの仮想環境でこの問題を解消する方法を探ります。
実際には何度もvagrant upとvagrant destroyを繰り返してようやく実現できました。

プロジェクトの作成

ここではmysql8-testというプロジェクト名で進めていきます。
任意の場所にmysql8-testを作成して、Vagrantfileを作成します。
前回までの理解を活かして、直接テキストエディタで編集していきます。

プラグインVMの設定

VirtualBoxを使用するときにはvagrant-vbguestプラグインを迷わず使用していきます。
VMの名前も指定して、仮想マシンの管理をしやすくしておきます。
この時点でのVagrantfileはこのようになります。

#設定ブロックの宣言
Vagrant.configure("2") do |config|

  #プラグインの指定
  config.vagrant.plugins = ["vagrant-vbguest"]
  
  #VMの設定
  config.vm.define "mysql8-test" do |mysql8|
    mysql8.vm.box = "centos/7"
    mysql8.vm.synced_folder ".", "/vagrant", disabled: true
    mysql8.vm.synced_folder "./vsync","/vsync", create: true
    
    #VirtualBoxへの設定
    mysql8.vm.provider "virtualbox" do |vb|
      vb.name = "mysql8-test"
    end
   
  end
end

まずはMySQL8をインストールするときの手順をの公式ドキュメントで確認

MySQLリポジトリを追加する

公式の手順によると、CentOS7でシェルから実行するなら以下のコマンドになります。
最後の”1”がバージョン番号とあるため、新規バージョンが出た時にはリポジトリを変更する必要がないか確認した方がいいかもしれません。*2

yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm

MySQLのインストール

これは特に工夫の必要もなく、以下となります。

yum install -y mysql-community-server

ここまでをVagrantfileに記述する

この手順をVagrantfileに追加しますが、私はこの時点では追加の手順がどの程度になるかわからなかったため、スクリプトとして切り出しました。
ここではスクリプトファイルをmysql8-provision.shとして、Vagrantfileと同じ階層に設置しています。

Vagrantfile
#設定ブロックの宣言
Vagrant.configure("2") do |config|

  #プラグインの指定
  config.vagrant.plugins = ["vagrant-vbguest"]
  
  #VMの設定
  config.vm.define "mysql8-test" do |mysql8|
    mysql8.vm.box = "centos/7"
    mysql8.vm.synced_folder ".", "/vagrant", disabled: true
    mysql8.vm.synced_folder "./vsync","/vsync", create: true
    
    #VirtualBoxへの設定
    mysql8.vm.provider "virtualbox" do |vb|
      vb.name = "mysql8-test"
    end
    
    #プロヴィジョン
    mysql8.vm.provision "shell", path: "./mysql8-provision.sh"  
  end
end
mysql8-provision.sh
#!/bin/bash

#OSアップデート
yum update -y

#MySQL8インストール
yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
yum install -y mysql-community-server

#yumキャッシュの削除
yum clean all
rm -rf /var/cache/yum

自動プロヴィジョニングのためのMySQL8の初期設定

公式のインストール手順としては、この後インストールの確認のためにサービスを起動して初期パスワードを変更しています。
最初はこれをそのまま自動化しようとしていましたが、それでは不都合な点が多く、ここからは独自の手順となります。

公式の手順の問題点

まず最初の問題点が、この状態でサービスを起動させてしまうと、すべてデフォルトの設定でMySQLサーバーの初期化が行われてしまうことでした。
SSHログインでシェルから操作する前提ならこの状態からも変更できますが、Vagrantを通じてプロヴィジョニングするとなると、課題が多すぎます。

  • 自動生成された初期パスワードを抽出する方法が難しい*3
  • mysql_secure_installationの対話型設定を実行するのが非常に困難*4

ただ、これはあくまでも「デフォルトでサービスを起動してしまった」ときの制限であって、以下のような手順であれば問題なく自動化できます。

mysqldでサーバーを初期化する

公式のドキュメントをよく読むと、mysqldに引数を渡すことで、条件を指定して初期化を実行できることがわかります。
これを利用して、少ない手順で初期化を行えるようにします。

rootパスワード無しでサーバーを初期化する

公式ドキュメントに、rootパスワード無しでサーバーを初期化する方法がありました。
CentOS7へのyumでのインストールだと、下記がそれにあたります。

/usr/sbin/mysqld --initialize-insecure --user=mysql

当然この状態で使い続けるのは危険ですので、rootのパスワードを変更する手順を確認します。

パスワード無しのrootにログインして、パスワードを変更する

対話型でのパスワードの使用が強制となっているため、通常の手順ではログインができません。オプションを指定して、パスワード無しのログインであることを明示します。

mysql -u root --skip-password

続けて、mysqlクライアントからSQLでパスワードの変更を行います。

ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password’;

上記のSQLは、mysqlコマンドへの引数として渡すことで、シェルから実行可能です。統合すると、下記のようなコマンドになります。

mysql -u root --skip-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';"

new_passwordの部分は当然ながら、書き換えの必要があります。

スクリプトに記述する

この手順を、先ほど作成したmysql8-provision.shに追加します。
先ほどはMySQL8を初期化することを念頭に置いていたため飛ばしていましたが、MySQL8をサービスに登録して起動させる手順も追加しておきます。

#!/bin/bash

#OSアップデート
yum update -y

#MySQL8インストール
yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
yum install -y mysql-community-server

#キャッシュの削除
yum clean all
rm -rf /var/cache/yum

#サーバー初期化
/usr/sbin/mysqld --initialize-insecure --user=mysql
systemctl enable mysqld
systemctl start mysqld

#パスワード変更
mysql -u root --skip-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';"

動作確認

プロジェクトフォルダにVagrantfileとmysql8-provision.shがあることを確認して、vagrant upを実行します。
VMが起動したらSSH接続を行い、mysqlへrootでログインできるか確認します。

mysql -u root -p
Enter password:(new_passwordを入力)
Welcome to the MySQL monitor.  Commands end with ; or \g.

これでVagrantでCentOS7にMySQL8最新バージョンをプロヴィジョニングすることに成功しました!
お疲れ様でした。

補足事項

MySQLサーバーの設定の変更

上記の手順ではMySQLサーバーの設定には一切変更を加えていません。
MySQL8の変更点の代表としてログイン時の認証形式の変更があり、デフォルト設定のままだと対応していない様々なクライアントが接続できない可能性があります。
必要に応じてmy.cnfに認証形式を従来のものに戻す設定をしておく方が良いかもしれません。従来の認証形式に戻す設定は下記になります。

default_authentication_plugin=mysql_native_password

また、MySQL8では、ユーザーアカウントごとに認証に使用するプラグインを指定できるようになっているようです。

mysql_secure_installationの処理

ネットで検索できるMySQLのインストール手順では、mysql_secure_installationを利用して不要なサンプルデータベースやデフォルト設定を削除する手順が含まれているものが多くなっています。
この記事の環境ではこれらのサンプルやデフォルトの設定がそもそも行われないため、必要がありませんでした。
他の環境ではmysql_secure_installationが行っている処理をプロヴィジョニングに含める必要があるかもしれません。
こちらの記事qiita.com
が参考になるかと思います。

Vagrantfileやスクリプトにパスワードを記述することについて

ローカル環境での一時的なテストであれば、さして問題ではありませんが、共同作業のためにこれらのファイルを共有する場合、セキュリティ的な問題になります。
Vagrantではファイルからの定数の追い出しには、環境変数を使う方法やRubyの変数を利用する方法があります。
環境変数はローカル環境全体に影響を持ちますので、Rubyの変数を利用するのがおすすめです。また、この場合でもプロジェクトのVagrantfileに記述してしまうと意味がありませんので、ローカル環境の.vagrant.dフォルダ内にVagrantfileを設置して、そこに変数を宣言するのが良いかと思います。
その場合、今回のプロジェクトのファイルには下記のような変更が加わります。

.vagrant.d/Vagrantfile
MYSQL8_PASS="new_password"
プロジェクトのVagrantfile(変更箇所のみ)
mysql8.vm.provision "shell", path: "./mysql8-provishion.sh" , args: MYSQL8_PASS

シェルを利用する場合、arg:に引数を指定することで、シェル側から$を使ってアクセスすることが出来ます。

mysql8-provision.sh(変更箇所のみ)
mysql -u root --skip-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$1';"

シェル側では$を使って引数をコマンドに埋め込むことが出来ます。
この方法でも、多数のプロジェクトをVagrantで管理していると名前の衝突の可能性があるため、その点には注意が必要です。

*1:新規インストールの場合。既存のサーバーをグレードアップするときは注意が必要。

*2:でも今の時点で1ならメジャーアップデート以外では変わらないのかも

*3:grepと文字列操作で可能です

*4:これもgitなどに対応する方法がありましたが、煩雑で、変更に追随するのが難しそうでした