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-provishion.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などに対応する方法がありましたが、煩雑で、変更に追随するのが難しそうでした

Windows10+Vagrant+VirtualBoxでCentOS7の仮想マシンを構築する

Vagrantfileの生成

この記事では以下の環境を目指したいと思います。

ホストOS
Windows10
仮想マシン
VirtualBox
ゲストOS
CentOS7
ゲストOSに展開するツール
Docker
Docker Compose
前の記事までにVagrantVirtualBoxのインストールは完了していますので、さっそくVagrantfileを記述していきます。

この記事について
私が作業した際の手順の再現となっているため、エラーが発生する箇所があります。
上記の環境で作業する環境をいち早く作りたい方は最終形のVagrantfileを参照してください。

プロジェクトの初期化

起動テストの時と同じ手順で、任意のプロジェクトフォルダにVagrantfileを作成します。

#プロジェクトのフォルダ内で
> vagrant init centos/7

この状態で生成されたVagrantfileの、コメントアウトされずに有効になっている部分のみを抜き出すと、以下のようになります。

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
end

BoxとしてCentOS7の最新版を指定しているだけの状態です。
ここに、デフォルトの設定では気持ちの悪いの部分の修正・設定を追加していきます。

共有フォルダの設定

共有フォルダはデフォルトではホスト側のプロジェクトフォルダ全体と、ゲスト側の"/Vagrant"が指定されています。
この状態ではゲスト側には必要のないVagrantfile自体も共有されてしまいますので、この設定を無効にします。

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", disabled: true
end

プロジェクトフォルダ内に共有用のフォルダを作成して、そのフォルダを指定しなおします。
ここではプロジェクトフォルダ内に”vsync”フォルダを作成しました。共有が働いているかの確認のために、そのフォルダの中に"test.txt"ファイルを作成しておきます。

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.synced_folder "./vsync","/vsync"
end

これで一度、マシンを起動させてみます。

一度目のミス

Boxの取得、ポート転送、SSHの設定と進み、マシンが起動されて…エラーが表示されています。
これはVirtualBox仮想マシンとWindows10の間での共有フォルダーの作成に失敗したためです。
マシンの状況を確認してみます。

> vagrant status
Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

エラーは出ましたが動作はしているようです。
ここは一度、マシンを停止させ、廃棄してから再度挑戦してみます。

#マシンのあるプロジェクトフォルダ内で
#仮想マシンの停止
vagrant halt
#仮想マシンの破棄(強制)
vagrant destroy -f

回避策

調べてみると、VirtualBoxのゲストOSにVirtualBox Guest Additionsというツールをインストールしないと、共有フォルダが使用できないということでした。
これを回避する方法として一番手軽なのは、vagrant-vbguestプラグインVagrantへインストールすることです。
この手順を、Vagrantfileへ記述します。

Vagrant.configure("2") do |config|
  #なんとなく手順的にVagrantへの設定を先に書く
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.synced_folder "./vsync","/vsync"
end

このファイルでupすると、localにプラグインをインストールするかを尋ねられます。*1
yを入力すると、vagrant-vbguestをインストールしてから、いつもの流れに向かいます。
yumのアップデートの後、自動でGuestAdditionのイメージをダウンロードしてインストールしてくれます。これで、共有フォルダも機能するはずです。
upが完了してプロンプトが返ってきたら、vagrant ssh仮想マシンに接続します。

> vagrant ssh
[vagrant@localhost ~]$ cd /vsync
[vagrant@localhost vsync]$ ls
test.txt

共有フォルダの設定が成功したことが確認できました。

ゲストOSへの設定とインストール

マシンの起動はこれで問題ありませんので、さらに気になる点だけ修正します。

仮想マシンの名前

現状だと、仮想マシンVagrant上では"defaut”と認識されます。さらに、VirtualBoxGUI上では長い名前が自動生成されているはずですので、これを特定の名前に指定します。
これには2つの設定ブロックを使用します。
Vagrant上でマシン名を付けるには、”config.vm.define”を使ってマシンの設定を切り分けます。
defineには文字列を指定してブロックに名前を付けますが、この文字列がVagrantが認識するマシン名となります。
VirtualBox上のマシン名は、vm.providerへの設定として行います。
この設定を追加すると、以下のようになります。

Vagrant.configure("2") do |config|
  #なんとなく手順的にVagrantへの設定を先に書く
  config.vagrant.plugins = ["vagrant-vbguest"]
    
  #仮想マシンの設定
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.synced_folder "./vsync","/vsync", create: true

  #仮想マシン名の変更
  config.vm.define "local-test" do |local|
    local.vm.provider "virtualbox" do |vb|
      vb.name = "local-test"
    end
  end
end


config.vm.define
config.vm.defineは、本来は一つのVagrantfile内で複数のマシンを管理するときに使用するものです。
configの無名のブロックに共通させる設定を、defineで切り分けたブロックにはそれぞれのマシンへの設定を記述します。
今回で言えば、local-testマシンのproviderにVirtualBoxを指定して、VirtualBoxへマシン名として同じ名前を与えたことになります。
名前付きのブロックを複数作ってあげると、複数の仮想マシンを立ち上げることが出来ます。
複数マシンの設定についてはMulti-Machine - Vagrant by HashiCorpに記述があります。

OSのアップデートと設定テスト

OSのイメージは最新の状態とは限りません。
セキュリティや機能の修正が入っている可能性が高いので、アップデートを行う必要があります。
ここまでの設定でもvagrant sshでログインして手動でアップデートができますが、destroyしてupを繰り返すたびに同じ環境に手動で戻すのは骨が折れます。
そうした初回起動直後のアップデートや設定を、config.vm.provisionで設定できます。
provisionは様々な外部ツールも利用できますが、ここではシェルからコマンドでアップデートとサーバのタイムゾーンの設定を行ってみます。
シェルを使用する場合の記述方法は下記にまとめられています。
Shell Scripts - Provisioning - Vagrant by HashiCorp

ここでは、マシンの設定とマシン内での作業を切り離したいので、Vagrantfileの外部にシェルスクリプトを配置して、それを実行させます。
シェルスクリプト自体はシンプルで、以下のようになります。

#!/bin/bash
yum update -y
timedatectl set-timezone Asia/Tokyo

yumでアップデートを行って、タイムゾーンをAsia/Tokyoに変更しているだけです。
これをひとまず"os-config.sh"としてVagrantfileと同じ階層に保存して、Vagrantfileから指定します。

Vagrant.configure("2") do |config|
  #なんとなく手順的にVagrantへの設定を先に書く
  config.vagrant.plugins = ["vagrant-vbguest"]
    
  #仮想マシンの設定
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.synced_folder "./vsync","/vsync", create: true

  #仮想マシン名の変更+VirtualBox特有の設定
  config.vm.define "local-test" do |local|
    local.vm.provider "virtualbox" do |vb|
      vb.name = "local-test"
    end
  end
 
  #仮想マシン内のOSへの初期設定
  config.vm.provision "shell", path: "os-config.sh"

end

path:で外部ファイルを指定する場合は、Vagrantfileからの相対パスにするのが無難です。
最後に、Docker Composeを使用したいので、これも自動でインストールしてもらいましょう。
これには、vagrant-docker-composeプラグインが必要となります。
プラグインの指定にvagrant-docker-composeを追加して、GithubのUsageどおりのprovision設定を追加してみます。

Vagrant.configure("2") do |config|
  #なんとなく手順的にVagrantへの設定を先に書く
  config.vagrant.plugins = ["vagrant-vbguest","vagrant-docker-compose"]
    
  #仮想マシンの設定
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", disabled: true
  config.vm.synced_folder "./vsync","/vsync", create: true

  #仮想マシン名の変更+VirtualBox特有の設定
  config.vm.define "local-test" do |local|
    local.vm.provider "virtualbox" do |vb|
      vb.name = "local-test"
    end
  end
 
  #仮想マシン内のOSへの初期設定
  config.vm.provision "shell", path: "os-config.sh"
  
  #DockerComposeを使いたい
  config.vm.provision :docker
  config.vm.provision :docker_compose
end

上記のVagrantfileでvagrant upを行うと、これまでよりもはるかに長い時間がかかりますが、マシンが起動するはずです。
vagrant sshでログインして

$ docker-compose -v

でバージョン表示が返ってくれば、設定が成功していること確認できます。
これでWindows10上に仮想マシンのCentOS7を立ち上げてDocker Comoseが利用できる環境が作成できました。*2

*1:pluginはVagrant全体に対するglobalなインストールとプロジェクトごとのlocalなインストールがあります

*2:ネットワークの設定などがまだ棚上げ