Windows10でCentOS7にDockerでMySQL8のコンテナを建てるVagrantfileを書く

## この記事について
この記事は、Vagrantの設定ファイルに当たるVagrantfileの概要と、ベースのCentOS7に、DockerでMySQL8のコンテナを建てる設定を探る、初心者の記事です。

## 前提条件
- ホストOSはWindows10
- VirtualBoxで仮想化
- ゲストOSはCentOS7
- Vagrantをインストール済み

上記の条件を満たす環境作りは別稿「[Windows10でVirtualBox+Vagrant+CentOS7の仮想マシンを作る](https://qiita.com/talesleaves/items/e98c288281cc940ed92d)」などを参照してください。

## Vagrantfileの構成
プロジェクトフォルダで`vagrant init`を行うと、Vagrantfileのテンプレートが生成されます。
生成されたファイルには、コメントで設定の書き方がごく簡単に説明されていたりしますが、そのコメントを取り除くと生成直後のVagrantfileは下記のようなごくシンプルなものになります。

```ruby:生成直後のVagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
end
```
この例だと仮想マシン（vm）のベースとなるBox（box）に"centos/7"を指定している状態です。
外側のブロックが設定のバージョンを指定していて、これは現在のVagrantのバージョンでは"2"で固定です。
もちろんこのほかにもたくさんの設定項目があり、ドキュメントのほどんどがVagrantfileの書き方に充てられるほどですので、概要だけとなりますがよく使う項目を確認しておきます。

## config.vagrant
`config.vagrant`はVagrant自体への設定です。ただし、ここで設定できる項目はプロジェクトごとに設定が変わるであろう、一部分のみです。ホストOS全体でのVagrantの挙動への設定は[環境変数](https://www.vagrantup.com/docs/other/environmental-variables.html)で行うようになっています。

### config.vagrant.plugins
Vagrantは仮想マシンの構築を肩代わりしてくれますが、その中でも定番としてよく使われる手順が出てきます。
それをVagrantユーザーの[コミュニティでプラグインとして利用可能](https://github.com/hashicorp/vagrant/wiki/Available-Vagrant-Plugins)にしてくれています。
プロジェクトで利用するプラグインを指定して、Vagrantに導入させる設定が`vagrant.plugins`です。これはあくまでもプロジェクトごとの設定ですので、ここでインストールされるプラグインは他のプロジェクトには影響しません。Vagrantのコマンドで言えば`vagrant plugin install --local`で導入された状態です[^1]。
[^1]: プラグイン関連のコマンドは[こちら](https://www.vagrantup.com/docs/cli/plugin.html)を参照してください。
基本的な書式は以下のようになります。

```ruby:config.vagrant.plugins
  config.vagrant.plugins = ["vagrant-vbguest"]
```
上記の例では配列として指定していますが、1つだけを指定するのであれば文字列としても問題ありません。この例で指定している"vagrant-vbguest"プラグインについても、少し触れておきます。

### vagrant-vbguestプラグイン
vagrant-vbguestは、VirtualBoxのホストOSとゲストOSの間の連携を強化する"Guest Additions"のインストールを肩代わりしてくれるプラグインです。
Guest Additionsは共有フォルダの拡張やクリップボードの共有等、作業の効率化に必須の機能が多く、VirtualBoxを利用する際はほぼ必須の機能ですが、ホストとゲストの双方にインストールする必要があるうえ、ゲスト側ではisoイメージをDLしてそれを利用してインストールを行う必要があるなど、手順が複雑で時間もかかります。このプラグインは一連の作業をすべて肩代わりしてくれますので、Vagrant+VirtualBoxで利用する場合はぜひ活用したいプラグインです[^2]。
[^2]: ただし癖がとても強いです。後述します。

### config.vagrant.sensitive
Vagrant実行時のログに表示されてはいけない変数を指定する項目です。
セキュリティ的にセンシティブな情報をログに表示しないように指定します。個人のテストではあまり気にすることはありませんが、Vagrantfileを共有する場合にはこういう機能もあることを知っていると良いと思います。

### config.vm
`config.vm`は仮想マシンへの設定のグループです。`vagrant init`で指定するBoxも、仮想マシンのベースを指定しますのでここに属しています。

#### config.vm.box
仮想マシンのベースになるBoxを指定する項目です。Boxは[公式のカタログ](https://app.vagrantup.com/boxes/search)から検索することができ、その詳細ページからVigrantfileでの指定方法も参照することができます。Boxの配布状況にもよりますが、ベンダー公式のOSなどはバージョン指定も可能な場合が多いです。

#### config.vm.provider
仮想環境を提供するソフト（provider）を指定する項目です。
VagrantのデフォルトではVirtualBoxが設定されており、それを利用する場合は指定を省略することができます。
また、この項目で[provider自体の設定を変更する](https://www.vagrantup.com/docs/providers/configuration.html)ことも可能ですが、ここではその内容は省きます。

#### config.vm.synced_folder
ホストOSとゲストOSの間で共有されるフォルダを設定する項目です。基本的な書式としてはホスト側のフォルダ、ゲスト側のフォルダで指定します。

```ruby:config.vm.synced_folder
  config.vm.synced_folder ".", "/vagrant"
```
上記の例はデフォルトで設定されている共有フォルダを明示した場合です。プロジェクトフォルダが丸ごと、ゲストOSの"/vagrant"フォルダに共有されています。この初期設定のままの時のみ、VirtualBoxでGuest Additionsをインストールしなくてもフォルダ共有ができるようになっているようです。

#### config.vm.provision
`config.vm.provision`は仮想マシンが起動した後に、仮想マシンのOSの設定を変更したり、OSにパッケージをインストール手順を指定する項目です。
この項目がVagrantfileの本体と言っても良いかと思います。それだけに公式ドキュメントのボリュームも多く、網羅できません。この記事では初歩的なごく一部に的を絞って触れていきます。

それでは、実際にVagrantfileを作成していきましょう。

## Vagrantfileを書く
実際にVagrantfileに設定を追加していきます。
Vagrantの良いところは失敗したとしても（起動中であれば`halt`してから）`vagrant destroy -f`して破棄、Vagrantfileを修正して`vagrant up`を繰り返す[^3]ことで必要な設定にたどり着くことができることです。臆せずに試していきましょう。
[^3]: vagrant upする前にvagrant vavidateでVagrantfileに文法的なエラーがないかチェックすることもできます。あくまでも文法チェックなのでupしても思い通りにならないことはよくあります。

### config.vagrantに追加する設定
Vagrant自体への設定はプラグインの設定をする場合がほとんどだと思います。さくっと進めていきましょう。

#### config.vagrant.pruginsの追加
前述しましたが、VirtualBoxでほぼ必須となるvagrant-vbguestプラグインの設定を追加します。

```ruby:Vagrantfile
Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"] #プラグインを導入します

  config.vm.box = "centos/7"
end
```
上記のようにすると、初回の`vagrant up`時にローカル（プロジェクト）にプラグインを導入するか尋ねられ、`y`を押すとプラグインがインストールされた後に、再度`vagrant up`を実行するように促されます。
プラグインはコマンドでインストールすることもできますが、環境や手順を統一するというVagrantの意義から考えると、Vagrantfileに指定するのがベターです。
ただし、vagrant-vbguestには癖があり、若干の問題があります。

#### vagrant-vbguestの問題点
vagrant-vbguestは、自動アップデート機能があり、常に自分を最新の状態にしようとする癖があります。このため、VirtualBoxとのバージョンが食い違ってしまったり、ゲストOSのカーネルをアップデートしてしまったりと、環境の統一という意味では不都合な場合もあります。
これに対応するためには、一度`vagrant up`してGuest Additionsがインストールされた後に、自動アップデート機能をオフにするのが最善です。しかし、そのためにはVagrantfileを修正する必要があります。個人の開発環境であれば問題ありませんが、熟練度に差があるチームでVagrantfileを配布している場合などはそれが難しいケースも考えられます。
これを解決するのは本題から外れてしまうため、ここではVirtualBoxもGuest Additionsも常に最新にするという意識の高い解決方法を取るものとして、このまま先に進みます[^4]。
[^4]: やや面倒ですがVagrantfileに細工をすると自動化できます。Vagrantfileの記述法というよりRubyとコマンドラインを組み合わせたコーディングになってしまうため、ここでは省きます。

### config.vmの編集
実際に稼働する仮想マシンへの設定を行っていきます。ここではVirtualBox特有の仮想マシンへの設定は省略して、OSやアプリの展開を中心に見ていきます。

#### 仮想マシンに名前を付ける
Vagrantのデフォルトでは、仮想マシンの名前は"default"になります。また、VirtualBoxが名付ける仮想マシンのイメージ名も自動生成のわかりづらいものになってしまいます。
一つのプロジェクトだけを扱っているならこれでも問題はありませんが、まずはわかりやすい名前を付けましょう。

仮想マシンに名前を付けるには、Vagrantが認識する仮想マシンと、VirtualBoxが管理する仮想マシンのイメージの2つを管理しなくてはなりません。
これを実現するには、[プロジェクトで複数のマシンを管理する](https://www.vagrantup.com/docs/multi-machine/#defining-multiple-machines)設定を応用します。

```diff_ruby:Vagrantfile
Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

+ config.vm.define "docker-mysql8" do |vm1|
+ #名前付き仮想マシンの設定を書く場所
  end
end
```

上記のように`config.vm.define`で仮想マシンの設定に名前を付けてブロックに切り分けると、Vagrantが仮想マシンを"docker-mysql8"という名前で認識するようになります。
この書式を使うと、名前を付けたブロックの外側がプロジェクトの仮想マシンすべてへの共通設定、ブロックの内側が名前を付けた仮想マシン固有の設定となります。上記の設定だと（ここでは仮想マシンは1つだけですが）、boxにCentOS7を使う指定はプロジェクト共通の設定となります。

続いてVirtualBoxの仮想マシンへの名づけを設定しますが、これはprovider（仮想化ソフト）への設定になるため、同様にブロックでの指定を使います。

```diff_ruby:Vagrantfile
Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

  config.vm.define "docker-mysql8" do |vm1|
+   #providerがVirtualBoxの時実行される設定
+   vm1.vm.provider :virtualbox do |vb|
+     vb.name = "docker-mysql8"
+   end

  end
end
```

これでVirtualBoxにもわかりやすい名前で仮想マシンを作らせることができます。が、同じ文字列を2度書くのはミスの元なのでマシン名は定数扱いとして、完成とします。

```diff_ruby:Vagrantfile
+VM1_NAME = "docker-mysql8"

Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

- config.vm.define "docker-mysql8" do |vm1|
+ config.vm.define VM1_NAME do |vm1|
    #providerがVirtualBoxの時実行される設定
    vm1.vm.provider :virtualbox do |vb|
-     vb.name = "docker-mysql8"
+     vb.name = VM1_NAME
    end

  end
end
```

#### 共有フォルダの設定
前述しましたが、Vagrantのデフォルト設定では、プロジェクトフォルダが丸ごと仮想マシンと共有されますので、仮想マシンの側では意味を持たないVagrantfileまで共有されてしまいます。
プロジェクト内に共有フォルダがあること自体は便利ですので、Vagrantfileと同じ階層に"vsync"と名付けたフォルダを配置して、これを仮想マシンと共有することにします。
設定は`config.vm.synced_folder`で行いますが、デフォルトで指定されている「プロジェクトフォルダ丸ごと共有」の設定を無効化しなくてはなりません。
そのうえで別途、共有フォルダを設定しなおします。この時、VirtualBox+Guest Additionsの環境であれば、それを明示しておきます。これによって同期が双方向になったり、反映が早くなるなどの利点があります。
設定を書くときは、マシン個別の設定を別名で切り分けていることに注意してください。

```diff_ruby:Vagrantfile
VM1_NAME = "docker-mysql8"

Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

  config.vm.define VM1_NAME do |vm1|
+   vm1.vm.synced_folder ".", "/vagrant", disabled: true #デフォルト設定をを明示的に無効にします
+   vm1.vm.synced_folder "./vsync", "/vsync", type: "virtualbox" #type指定でVirtualBox独自機能を使う
    #providerがVirtualBoxの時実行される設定
    vm1.vm.provider :virtualbox do |vb|
      vb.name = VM1_NAME
    end

  end
end
```
ホストOS側のフォルダの指定はVagrantfileからの相対パスで指定するのがおすすめです。配布を前提に考えた場合、絶対パスだと役に立たなくなってしまいます。

この時点で、仮想マシンを起動させることが可能です。もしも手探りで仮想マシン内のOSの設定手順を作成していくのであれば、この時点で仮想マシンを一度作っておくと良いかと思います。

### config.vm.provision
仮想マシンが起動した後、OSに対する設定やアプリのインストール手順を指定する項目が`config.vm.provision`です。provisionに指定した手順は、基本的に初回の`vagrant up`時にしか実行されません。
provisionにはprovisionerという実際の作業を行うプログラムを指定できます。provisionerはChef、Docker、Ansibleなど、様々なソフトを指定できますが、網羅できる量ではありませんし、それぞれに精通している必要がありますので、ここではOSのシェルのみを扱います。

#### ゲストOSのアップデートと設定
OSを使用できる状態にするためにはカーネルのアップデートが必須です。基礎となるBoxも随時更新されていますが、Box化された後にリリースされたセキュリティパッチ等が存在する可能性もあります。また、タイムゾーンも日本にしておきましょう。

```diff_ruby:Vagrantfile
VM1_NAME = "docker-mysql8"

Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

  config.vm.define VM1_NAME do |vm1|
    vm1.vm.synced_folder ".", "/vagrant", disabled: true #デフォルト設定をを明示的に無効にします
    vm1.vm.synced_folder "./vsync", "/vsync", type: "virtualbox" #type指定でVirtualBox独自機能を使う
    #providerがVirtualBoxの時実行される設定
    vm1.vm.provider :virtualbox do |vb|
      vb.name = VM1_NAME
    end

+   vm1.vm.provision "shell", inline: "yum update -y"
+   vm1.vm.provision "shell", inline: "timedatectl set-timezone Asia/Tokyo"
  end
end
```

シェルを扱う場合、上記のようにOSにログインして操作するのと同じように["inline:"で直接指定する方法](https://www.vagrantup.com/docs/provisioning/shell.html#inline-scripts)と、["path:"でスクリプトファイルを指定する方法](https://www.vagrantup.com/docs/provisioning/shell.html#external-script)があります。
実際のところ、手順が抜けたりコマンドのミスを随時修正しながら作業していくことになるかと思いますので、最初は"inline:"で指定して、上手くいく手順が確定したときにファイルに切り出すのが良いかと思います。
上記は愚直にカーネルをアップデートしてタイムゾーンを設定しています。
他に必要な設定があれば、同様に追加していきます。

#### Dockerのインストール
これも、ゲストOSにログインしているときと同様で問題ありませんので、[Docker公式のドキュメントの手順](https://docs.docker.com/install/linux/docker-ce/centos/)を追加します。

```diff_ruby:Vagrantfile
VM1_NAME = "docker-mysql8"

Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

  config.vm.define VM1_NAME do |vm1|
    vm1.vm.synced_folder ".", "/vagrant", disabled: true #デフォルト設定をを明示的に無効にします
    vm1.vm.synced_folder "./vsync", "/vsync", type: "virtualbox" #type指定でVirtualBox独自機能を有効にします
    #providerがVirtualBoxの時実行される設定
    vm1.vm.provider :virtualbox do |vb|
      vb.name = VM1_NAME
    end

    vm1.vm.provision "shell", inline: "yum update -y"
    vm1.vm.provision "shell", inline: "timedatectl set-timezone Asia/Tokyo"

+   #Docker-ceのインストール
+   vm1.vm.provision "shell", inline: "yum install -y yum-utils device-mapper-persistent-data lvm2"
+   vm1.vm.provision "shell", inline: "yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo"
+   vm1.vm.provision "shell", inline: "yum install docker-ce -y"
+   vm1.vm.provision "shell", inline: "yum clean all"
+   vm1.vm.provision "shell", inline: "systemctl enable docker" #Dockerデーモンを自動的に起動する設定

  end
end
```
インストール後に、少しでも仮想マシンの容量を小さくするためのyumのキャッシュクリアと、次回以降の起動時にdockerデーモンが起動するように設定しています。

#### MySQL8の設定ファイルの作成
MySQLの設定と言えば"my.cnf"ですが、Docker公式のMySQLコンテナに設定を追加する場合は"/etc/mysql/conf.d/mysql.cnf"に追加、または別途任意の名前の".cnf"ファイルをコピーするのが作法のようです。
ひとまず、先に設定した共有フォルダである"./vsync"内に、設定ファイルを作成します。MySQL8の設定は以前のバージョンとは異なる注意点がいくつかあるのでそれを記述していきましょう。

```text:my8.cnf
[mysqld]
default-authentication-plugin=mysql_native_password #最重要な認証形式の設定
default-time-zone=+9:00 #サーバーのタイムゾーン
collation_server=utf8mb4_ja_0900_as_cs #サーバーのデフォルト照合順序

```

ここでは"my8.cnf"としましたが、ファイル名は任意（拡張子は".cnf"）で問題ありません。
最も重要なのは、MySQL8以降、デフォルトの認証形式が変わったことです。現在も多くのクライアントがMySQL8デフォルトのcaching_sha2_passwordに対応していません。接続やログインができなくなるケースが多発するので、あらかじめ以前の認証形式に戻してしまいます。
その他の2つは、おまじないのようなもので、特定の環境下[^5]だと問題が発生する部分の設定です。このほか、実際の運用にはログの設定やチューニングに類する設定も必要になりますが、ここでは省略します。
[^5]: javaのSpringBootアプリとか

#### Dockerfileの作成
いよいよDocker上にMySQL8のコンテナを作ります。これについてもDocker自体が本を1冊書いて有り余る深淵ですので、シンプルにDocker Hubの公式イメージをベースに利用することにします。
Docker公式のMySQLコンテナはあらかじめ便利な設定がされているため、利用者側でDockerfileに書き込む情報を少なくできます[^7]。
[^7]: 省略された設定がどうなっているのか、公式のDockerfileを読むのは良い勉強になります。
ここでは、先ほど作成した設定ファイルを"/etc/mysql/conf.d/"に追加して、MySQLの起動時に読み込まれるように、パーミッションを変更しています。

```text:Dockerfile
FROM mysql

COPY ./my8.conf /etc/mysql/conf.d/my8.conf
RUN chmod 644 /etc/mysql/conf.d/my8.conf
```
Dockerfileの`COPY`コマンドは、転送元を指定する際にはDockerfileからの相対パスを使用する点だけ、注意が必要です。

#### Dockerfileからコンテナを作成させる
provisionが行われている時点では、まだDockerデーモンも起動していませんし、コンテナを起動させるためのイメージも作成されていません。準備したMySQL8のコンテナを、provisionでVagrantから生成させます。
これも、OSにログインして実行するコマンドをprovisionに指定していくだけです。

```diff_ruby:Vagrantfile
VM1_NAME = "docker-mysql8"
MYSQL_ROOT_PASSWORD = "defaultRoot"

Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

  config.vm.define VM1_NAME do |vm1|
    vm1.vm.synced_folder ".", "/vagrant", disabled: true #デフォルト設定をを明示的に無効にします
    vm1.vm.synced_folder "./vsync", "/vsync", type: "virtualbox" #type指定でVirtualBox独自機能を有効にします
    #providerがVirtualBoxの時実行される設定
    vm1.vm.provider :virtualbox do |vb|
      vb.name = VM1_NAME
    end

    vm1.vm.provision "shell", inline: "yum update -y"
    vm1.vm.provision "shell", inline: "timedatectl set-timezone Asia/Tokyo"

    #Docker-ceのインストール
    vm1.vm.provision "shell", inline: "yum install -y yum-utils device-mapper-persistent-data lvm2"
    vm1.vm.provision "shell", inline: "yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo"
    vm1.vm.provision "shell", inline: "yum install docker-ce -y"
    vm1.vm.provision "shell", inline: "yum clean all"
    vm1.vm.provision "shell", inline: "systemctl enable docker"

+   #コンテナのビルド
+   vm1.vm.provision "shell", inline: "systemctl start docker" #Dockerデーモンの起動
+   vm1.vm.provision "shell", inline: "docker build -t test/mysql8 /vsync"
+   vm1.vm.provision "shell", inline: "docker run -d --name mysql8 -v /var/lib/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=$1 -e TZ=Asia/Tokyo test/mysql8",
+   args: MYSQL_ROOT_PASSWORD

  end
end
```

`docker run`のオプションで、公式MySQLコンテナに設定されている変数を渡しています。ここではrootのパスワードのみを指定していますが、その他のオプションも`run`から指定することが可能です。
[`args`オプションを指定する](https://www.vagrantup.com/docs/provisioning/shell.html#script-arguments)ことで実行するシェルに変数を埋め込むことができます。柔軟な設定を行うには必須の書式です。
`-v`オプションはデータベースのデータをOS側に永続化させるために必要となります。これを指定しないとDockerコンテナの性質上、コンテナを起動するたびにMySQLのデータは初期状態に戻ります[^6]。この部分はDocker公式コンテナの書式に従っていますが、別の指定方法もあります[^8]。

[^6]: docker startの度に同じ状態を再現することがDockerの目的だからです。
[^8]: --mountを使うとかバインド（今回の方式）ではなくボリュームを使うとか。
ついでにですが、DockerのTZ変数でコンテナのタイムゾーンも指定しています。

#### 仮想マシン起動時の操作
ここまでの作業でDockerをインストールしてMySQL8のコンテナを作る部分は準備できました。
つぎに、2回目以降の`vagrant up`の時に自動的にDockerのコンテナを起動させる必要があります。
これもVagranfileの`config.vm.provision`で設定できます。

```diff_ruby:Vagrantfile

VM1_NAME = "docker-mysql8"
MYSQL_ROOT_PASSWORD = "defaultRoot"

Vagrant.configure("2") do |config|
  config.vagrant.plugins = ["vagrant-vbguest"]
  config.vm.box = "centos/7"

  config.vm.define VM1_NAME do |vm1|
    vm1.vm.synced_folder ".", "/vagrant", disabled: true #デフォルト設定をを明示的に無効にします
    vm1.vm.synced_folder "./vsync", "/vsync", type: "virtualbox" #type指定でVirtualBox独自機能を有効にします
    #providerがVirtualBoxの時実行される設定
    vm1.vm.provider :virtualbox do |vb|
      vb.name = VM1_NAME
    end

    vm1.vm.provision "shell", inline: "yum update -y"
    vm1.vm.provision "shell", inline: "timedatectl set-timezone Asia/Tokyo"

    #Docker-ceのインストール
    vm1.vm.provision "shell", inline: "yum install -y yum-utils device-mapper-persistent-data lvm2"
    vm1.vm.provision "shell", inline: "yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo"
    vm1.vm.provision "shell", inline: "yum install docker-ce -y"
    vm1.vm.provision "shell", inline: "yum clean all"
    vm1.vm.provision "shell", inline: "systemctl enable docker"
    
    #コンテナのビルド
    vm1.vm.provision "shell", inline: "systemctl start docker"
    vm1.vm.provision "shell", inline: "docker build -t test/mysql8 /vsync"
    vm1.vm.provision "shell",
     inline: "docker run -d --name mysql8 -v /var/lib/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=$1 -e TZ=Asia/Tokyo test/mysql8",　args: MYSQL_ROOT_PASSWORD
     
+   #vagrant upで毎回実行させる
+   vm1.vm.provision "shell", inline: "docker start mysql8", run: "always"
  end
```
他のprovisionと違うところは、最後に`run:`オプションに"always"を指定して、常にシェルの実行を指示しているところです。この指示がついているのはこの1文だけですので、2回目以降の`vagrant up`の際にはこの1文だけしか実行されません。

### 起動してみる
それでは、仮想マシンとその中のMySQLコンテナを起動させましょう。
Vagrantfileのあるフォルダへ移動して、`vagrant up`です。初回の起動は大変時間がかかりますので、他の作業をするかTwitterで時間をつぶしましょう。

#### vagrant sshしてみる
無事プロンプトが戻ってきたら、`vagrant ssh`でログインしてみます。
無事にコンテナが起動しているかは、以下のように確認できます。

```text:コマンド実行結果
[vagrant@localhost ~]$ sudo docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
eb40fa820ff1        test/mysql8         "docker-entrypoint.s…"   9 minutes ago       Up 9 minutes        3306/tcp, 33060/tcp   mysql8
```
起動できていれば上記のようにコンテナのリストが得られます。
お疲れ様でした！

## まとめ
- Vagrantfileで必須な構成は大雑把に３つ。`vagrant.plugins`、`vm.provider`、`vm.provision`
- 目的に合うプラグインがあれば、それを導入することで記述を減らせる。
- providerの設定は使用する仮想化ソフトのドキュメントをよく読んで。
- provisionは最初はshell、inlineで。`vagrant destroy -f`はお友達。

## 次のステップ
- 目的に合うVagrantのプラグインがあれば、provisionの定番の手順も削減できます。この記事では理解のために手動でインストールしていますが、Dockerなどのprovisionerをインストールして設定を行うプラグインもあります。
- 慣れている人ならば、provisionはファイルに切り出した方がやりやすいと思います。
- VagrantfileやDockerfileからパスワード等の秘匿情報を分離する方法ももちろん存在します。
- 目的に合うBoxを見つければ、さらに楽になります。あるいは、自分の定番の環境をBox化して時間と手間を減らすこともできます。

## 感想
あえてDocker単体で書きましたがDocker-composeの方が楽です…。プラグインのありがたみもよくわかりました。

~~おまけ:vagrant-vbguestをプロヴィジョニングの時だけインストールさせる~~←実験中です