MacにVagrant1.8.5+VirtualBox5.1で作るCentOS7環境

# 前提条件
OSX 10.11 El Captain
Xcode 7.3 コマンドラインツールインストール済み (6.1以降は自動)

# ゴール
VagrantからゲストOSのCentOS7をVirutalBoxで起動し、正常に一連のvagrantコマンドが動作する。また、ホストOSからゲストOSへのhttpdアクセスができる。

想定として、開発者が特定のWeb環境を再現するためにゲストOSを切り替え使用。なのでゲストOSにプライベートIPの個々の割当もせず、ポートフォワードでネットワーク設定をする。
（複数のvagrantfile作成 = ゲストOSを構築しても、常に起動するゲストOSは一つでアクセスは普遍的にlocalhost:8080で行う運用）

# 必要ツールのインストール
**Vagrant1.8.5** - https://www.vagrantup.com
**VirtualBox 5.1** - https://www.virtualbox.org

なくても問題ないが複数立ち上げたりするならメニューバーからの管理で便利なもの
(記事中では非使用)
Vagrant Manager - http://vagrantmanager.com

上記3つはbrew運用ついでにbrew caskでもインストール可能だったが、caskにアップデート機構はないので運用として意味もなく、最新への入れ替え時にPCにゴミを残さないよう手間がかかるだけなので今回dmgでダウンロードしインストールした。

# Vagrantの基本的な使い方
## 3つの基本ポイント
1. Vagrantから**Box**と呼ばれるゲストOSのイメージをダウンロード（BOXは複数のVagrant環境から再利用が可能）
2. Vagrantで管理するゲストOS環境となるディレクトリに**Vagrantfile**を作成し、その環境がゲストOSのイメージと一対一の関係になる。VirtualBoxの場合は **~/VirtualBox VMs** にゲストOSのイメージが配置される
3. **~/.vagrant_d** はvagrant自体の設定、ゲストOS作成時の共通のデータとなるBOX(~/.vagrant_d/boxes)などが管理される。

## 記事中で使用する6つのコマンド

| コマンド | アクション |
|:-:|:-:|
|  vagrant init [BOX]     | Vagrant環境 (Vagrantfile) を指定BOXで作成する   |
|  vagrant up     | ゲストOSの起動   |
|  vagrant halt  |   ゲストOSの終了 |
|  vagrant reload  |  ゲストOSの再起動（Vagrantfileの設定再読込含む) |
| vagrant ssh   | ゲストOSへのsshログイン  |
| vagrant ssh-config | vagrant sshに関するssh設定情報  |

その他コマンドのヘルプ情報は `vagrant -h` で表示される。

# ゲストOSを構築する
## Vagrantfileを作成する

**Vagrantfile**の置かれるディレクトリはvagrantが作成するゲストOSの本体イメージ **~/VirtualBox VMs/[固有名]_suffix**と一対一に対応する。なので
**~/vagrant/[固有名]** で管理することにする。ここでは **~/vagrant/centos-7** とし、ディレクトリを作成する。
このディレクトリに移動後、下記コマンドを実行する。
BOXは [Discover Vagrant Boxes](https://atlas.hashicorp.com/boxes/search) に一覧があるので、ここでは centos/7 を選んだ。

```
% vagrant init centos/7
```

ディレクトリ内に、デフォルトのVagrantfileが作成され、このファイルにゲストOSに関する設定情報を適宜必要になったら加える事になる。ネットワークやゲストOSのメモリ量の設定はこのファイルを通してvagrantからのゲストOS起動時に**プロパイダー**（VirtualBOXのゲストOSに関する設定項目）に反映される。

## ゲストOSをダウンロードし起動する
続けて~/vagrant/centos-7のVagrantfileがあるディレクトリで、ゲストOSの起動コマンドを実行する。設定したBOXが初めてのゲストOS作成となる場合は、ダウンロードに非常に時間がかかるが、ダウンロードを終えると自動的に起動フェーズに移る。
(同一名称のBOXは~/.vagrant.d/boxesに保存され、新しいゲストOS構築時はダウンロード処理は省かれ再利用されるので高速。またBOXのダウンロード失敗時の一時データは~/.vagrant_d/tmpに保存されるようだ。ダウンロード失敗後のvagrant upの再試行で挙動がおかしくなる場合はこの中身を空にするといいかもしれない。）

```
% vagrant up
```
出力は下記のようになる。（ダウンロードプロセスの表示は省略）

```
==> default: Importing base box 'centos/7'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'centos/7' is up to date...
==> default: Setting the name of the VM: centos-7-2_default_1468999835010_55566
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Remote connection disconnect. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
    default: Warning: Authentication failure. Retrying...
    default: Warning: Authentication failure. Retrying...
    default: Warning: Authentication failure. Retrying...
    ...
    (繰り返しなので省略)
    ...
    default: Warning: Authentication failure. Retrying...
Timed out while waiting for the machine to boot. This means that
Vagrant was unable to communicate with the guest machine within
the configured ("config.vm.boot_timeout" value) time period.

If you look above, you should be able to see the error(s) that
Vagrant had when attempting to connect to the machine. These errors
are usually good hints as to what may be wrong.

If you're using a custom box, make sure that networking is properly
working and you're able to connect to the machine. It is a common
problem that networking isn't setup properly in these boxes.
Verify that authentication configurations are also setup properly,
as well.

If the box appears to be booting properly, you may want to increase
the timeout ("config.vm.boot_timeout") value.
```

default: SSH auth method: private key
default: Warning: Remote connection disconnect. Retrying...

上記部分のメッセージのようにssh鍵認証に問題がおこり、本来vagrantが提供しているゲストOSにパスワード無し鍵認証でのsshログインが正常に動作しない結果になる。
また、起動時の鍵認証の失敗で、本来存在するはずのゲストOSの/home/vagrant/**sync/**という、**ホストOSのVagrantfileがあるディレクトリと同期されたディレクトリ**も作成されない。

ただ、ゲストOS自体は問題なく立ち上がり、パスワードを伴うsshのログイン自体は可能になっている。
ゲストOSにはそれぞれのVagrantfileがあるディレクトリからvagrantコマンドのサブコマンドsshを利用してログインする事ができる。

```
 % vagrant ssh 
```
このコマンドでは自動的にユーザ名 vagrant としてログインする。
パスワードは vagrant が初期値となっている。またsudoも有効だ。
ssh鍵認証に問題がある状態ではパスワードを聞かれるので上記を入力してログインすることになる。

(この時点ではゲストOSはNATで、sshはホストOSの2222ポートからゲストOSの22ににポートフォワードされているので通常のsshコマンドからも `ssh localhost -p 2222` でログインを試みることができるが、認証に利用する鍵は異なることに注意)

vagrant ssh で使われる秘密鍵とssh設定に関する情報は下記コマンドで確認できる。

```
 % vagrant ssh-config
```
出力

```
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/[username]/vagrant/centos-7-2/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL
```

### ssh鍵認証の問題を解決する


`vagrant ssh` でゲストOSにログインし、vagrantホームディレクトリ内にある~/.ssh と　~/.ssh/authorized_keys のパーミッションの確認と修正をする。

現時点の~/.ssh/ディレクトリのパーミッション

```
# ゲストOS
[vagrant@localhost ~]$ ls -al
drwx------. 2 vagrant vagrant 4096 Jul 20 07:30 .ssh
...
```
700 となっており、こちらは問題ない。

現時点の~/.ssh/authorized_keys のパーミッション

```
# ゲストOS
[vagrant@localhost ~]$ ls -al .ssh
-rw-rw-r--. 1 vagrant vagrant  389 Jul 20 07:30 authorized_keys
...
```
**664 ここでユーザ以外のグループに公開鍵に書き込み権限があるのがエラー原因になっている。** ここでは最低限に600に変更する（644でも問題はない）。

```
# ゲストOS
[vagrant@localhost ~]$ chmod 600 .ssh/authorized_keys
-rw-------. 1 vagrant vagrant  389 Jul 20 07:30 authorized_keys
...

```

sshをexitし、再度 `vagrant sshで` ログインを試みてパスワードを聞かれずにログイン出来れば鍵認証は正常に機能するようになっている。
さらにホストOSをリロード（終了と起動の操作）し、さきほどの鍵認証に関するエラーメッセージが出ない事も確認する。

```
% vagrant reload
```

起動時の鍵認証が正常になると、起動時にrsyncも実行され、ゲストOSの/home/vagrant/にはsyncディレクトリが作成されている。

```
# ゲストOS
[vagrant@localhost ~]$ ls
sync
```

## スナップショット機能を使う

一通り正常に動くようになったので、ゲストOSのこの状態を正常な初期状態としてスナップショットに取っておく。

```
% vagrant snapshot save initial
```
スナップショット機能の使い方については [Vagrant 1.8の新機能 Linked CloneとSnapshotを試してみた](http://dev.classmethod.jp/tool/vagrant-linked-clone/) の説明がわかりやすい。

# ホストOSからゲストOSへhttpdアクセスを行う
## CentOS7 (ゲストOS) での作業

### httpdをインストールする。

```
# ゲストOS
[vagrant@localhost ~]$ sudo yum install httpd
```
### httpdのポートを8080に設定する

/etc/httpd/conf/httpd.conf に Listen 8080 を設定する。

### httpdの自動起動の登録
CentOS7ではserviceコマンドではなく、**systemctl** が使われるようだ。
(serviceコマンドはラッパーとなって残っている）

```
# ゲストOS
[vagrant@localhost ~]$ sudo systemctl enable httpd.service
```

systemctlで自動起動に登録 (enabled) されているかは次のコマンドの出力一覧から確認できる。

```
# ゲストOS
[vagrant@localhost ~]$ systemctl list-unit-files
```

出力

```
...
httpd.service                               enabled 
initrd-cleanup.service                      static  
initrd-parse-etc.service                    static   
...
```
以上で次回の起動から、httpdが8080ポートで自動起動するようになる。

## ホストOSでの作業
### Vagrantfileの設定

127.0.0.1(localhost)の8080ポートへの接続をゲストOSの8080ポートにポートフォワードする設定をする。
~/vagrant/centos-7/Vagrantfileの25行目にある設定例を参考に追記。

```
 25   # config.vm.network "forwarded_port", guest: 80, host: 8080
 26   config.vm.network "forwarded_port", guest: 8080, host: 8080
```

## httpdの起動とホストOSからのアクセスの確認

Vagrantfileを反映するためゲストOSをリロードする

```
% vagrant reload
```
出力

```
==> default: Attempting graceful shutdown of VM...
==> default: Checking if box 'centos/7' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 8080 (guest) => 8080 (host) (adapter 1)
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default: 
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.
==> default: Rsyncing folder: /Users/[username]/vagrant/centos-7/ => /home/vagrant/sync
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.
```

メッセージからポートフォーワードの設定が反映されている事が確認できる。

ホストOSのブラウザで下記URLが表示できれば設定完了
http://localhost:8080


# 付録
## もしhttpdの接続が上手くいかなかった時のCentOS7の基本的な調査ツール

CentOS7ではifconfigは**ipコマンド**のサブコマンドとして、netstat機能は**ssコマンド**として提供されるようになったようだ。インフラ作業で頻繁に使われるコマンドのためか、極端に短くキーを打ちやすいものになっている。

ipコマンドは　[ip コマンド チートシート](https://access.redhat.com/sites/default/files/attachments/rh_ip_command_cheatsheet_1214_jcs_print_ja2.pdf) が詳しい。
従来のifconfigと同等の出力は下記コマンドとなる

```
[vagrant@localhost ~]$ ip addr
```
出力

```
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 52:54:00:bd:97:1e brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
       valid_lft 82361sec preferred_lft 82361sec
    inet6 fe80::5054:ff:febd:971e/64 scope link 
       valid_lft forever preferred_lft forever

```

サービスのListenが意図したようにできているか確認するにはssコマンドを使い下記のように行う。-n なしであればポート名はservice名に解決され表示される。(8080 は webcacheと表示)

```
[vagrant@localhost ~]$ ss -natu
```

出力

```
Netid State      Recv-Q Send-Q         Local Address:Port                        Peer Address:Port              
udp   UNCONN     0      0                          *:38368                                  *:*                  
udp   UNCONN     0      0                  127.0.0.1:323                                    *:*                  
udp   UNCONN     0      0                          *:68                                     *:*                  
udp   UNCONN     0      0                         :::50431                                 :::*                  
udp   UNCONN     0      0                        ::1:323                                   :::*                  
tcp   LISTEN     0      128                        *:22                                     *:*                  
tcp   LISTEN     0      100                127.0.0.1:25                                     *:*                  
tcp   ESTAB      0      0                  10.0.2.15:22                              10.0.2.2:51972              
tcp   LISTEN     0      128                       :::8080                                  :::*                  
tcp   LISTEN     0      128                       :::22                                    :::*                  
tcp   LISTEN     0      100                      ::1:25                                    :::*    
```

この記事に関する作業では設定したhttpdの8080のLISTENが確認できれば問題ない。

httpdなどサービスをインストールした上で起動と登録の確認を行う程度であれば、CentOS7で新しくなった4点を抑えておけば基本的には事足りそうだ。
1. **systemctl** -> service, chkconfig の代わり
2. **ipコマンド群** -> ifconfigの代わり（その他ネットワークコマンド含む）
3. **ssコマンド** -> netstatの代わり
4. **firewalld** -> iptablesの代わり。初期状態では起動していない。

さらなるCentOS6までの構成との相違は　[RHEL6→RHEL7で変更された主要コマンドまとめ](https://oss.sios.com/yorozu-blog/rhel6-rhel7change) が詳しい。

## ゲストOSの削除

今回作成したゲストOSに関するデータは2つのディレクトリに存在する。
1. **~/VirtualBox VMs/centos-7_[suffix]**
2. **~/vagrant/centos-7** (Vagrantfile, .vagrant が含まれる)

ゲストOSの削除にはvagrantのコマンド経由と手動削除が必要になる。

1に関する削除。~/vagrant/centos-7 のディレクトリに移動し、下記コマンドを実行する。

```
% vagrant destory
```

2に関する削除。

```
% rm -Rf ~/vagrant/centos-7
```

以上でゲストOSに関するデータは全て削除される。