初心者でもKVMでインスタンスを建てたい！

# 動機

WebarenaやさくらのVPSがすごいいい．
自分でもVPSつくりたい．
Webarenaは，KVMでインスタンスが建てられているのか．．．

KVMにチャレンジするぞ．
ってことで毎回virt-managerで動かすのは面倒なので，完全CLIでインスタンスを建てます．
VPSのようにインスタンスを建てた段階で`ssh`もできるようにします．

一番下の参考文献のまとめの記事ですので，
そちらもみていただけますと幸いです． 

本記事で作成したスクリプトは，[Github](https://github.com/yu-sakana/kvm_installer)に載せています．

## 注意事項
何をやっても`virsh console vm名`で接続することができませんでした．
なので，初期設定でsshが通るところまでやっています．

動くまで母艦の初期化を2桁くらいしました．
ubuntuの初期設定だけは早いと思うので誰か雇ってください．(Ansibleでやれ)

# 環境
母艦（ホスト）
- Ubuntu Desktop 18.04.6 LTS
- CPU : intel
- RAM : 16GB
- Ethernet : 有線（esp2s0）

macから母艦にsshをして構築．
Ubuntu Serverでも大丈夫だと思います．

# 事前準備
VMを構築する下準備を行なっていきます．

## ライブラリインストール
kvmに必要なライブラリのインストールです．

```
$ sudo apt-get install -y qemu-kvm libvirt-bin virtinst bridge-utils cpu-checker libguestfs-tools
```

## そもそも仮想化，kvmができるPCなの？
仮想化の確認
出力が0以上であれば，okです．

```
$ egrep -c '(vmx|svm)' /proc/cpuinfo
8
```

kvmの確認
`kvm-ok`で確認します．

```
$ sudo kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
```

```
$ sudo nano /etc/group
```
で，kvm，libvirt-qemuに自分のユーザ名登録
（usermodでもいい．僕はトラウマがあるので直でいじってます．）
##libvirtdの確認
activeになっていればok

```
$ sudo systemctl status libvirtd
```

なっていない場合

```
$ sudo systemctl start libvirtd
$ sudo systemctl enable libvirtd
```

をした後に，`sudo systemctl status libvirtd`でactiveになっているか確認してください．

## ブリッジ接続編
僕はrootユーザでやりました．
別に`sudo `でやっても大丈夫です．
以下，全てsudoが必要です．

補足
言語を英語にしているため，消去するものが 'Wired Connection 1'となっています．
日本語では，新規接続1 となります．

```
# bridge-br0を作成
$ nmcli connection add type bridge ifname br0
Connection 'bridge-br0' (98bfb899-c06d-4421-b2c8-bec80647ac4d) successfully added.

# bridge-br0のスパニングツリーを無効にする
$ nmcli connection modify bridge-br0 bridge.stp no

# スパニングツリーが無効になっているか確認
$ nmcli connection show bridge-br0 | grep bridge.stp
bridge.stp:                             no

# bridge-br0の設定．IPアドレスは，自分のipを指定（僕の場合だと，enp2s0）
# ssh上で作業しているとここでsshが切れてしまうので，rebootをしてsshを繋ぎ直す
$ nmcli con modify bridge-br0 ipv4.method manual ipv4.address "your_ip_address/xx" ipv4.gateway "your_gateway" ipv4.dns "your_dns" &&reboot

#rebootしたので，sudoつけるかsudo suでrootユーザになってください．
$ nmcli connection add type bridge-slave ifname enp2s0 master bridge-br0

# 不要になった 'Wired connection 1' の削除
$ sudo nmcli connection delete 'Wired connection 1'
Connection 'Wired connection 1' (ced9c7da-9541-39c8-8ced-7b9696bbd0aa) successfully deleted.

# 確認
$ nmcli connection show 
NAME                 UUID                                  TYPE      DEVICE 
bridge-br0           98bfb899-c06d-4421-b2c8-bec80647ac4d  bridge    br0    
bridge-slave-enp2s0  75c0955b-21d5-486b-a37b-050a08fef62d  ethernet  enp2s0 
virbr0               31854afe-f628-4915-8622-0f1e47188f18  bridge    virbr0
```

## Storage Poolの設定

```
$ sudo virsh pool-list
 Name                 State      Autostart 
-------------------------------------------

$ sudo groupadd shared
$ sudo nano /etc/group
$ sudo mkdir -p /home/shared/images/
$ sudo chown -R root:shared /home/shared/
$ sudo chmod -R o-rwx /home/shared/
sudo virsh pool-define-as default dir --target /home/shared/images/

$ virsh pool-start default
Pool default started

$ virsh pool-autostart default
Pool default marked as autostarted

$ virsh pool-list
 Name                 State      Autostart 
-------------------------------------------
 default              active     yes   
```

# VM構築編
こちらでは，VMを実際に構築していきます．
`virt-builder編`,`virt-install編`の2種を別々に詳しく説明していきますが，
別々に実行していくのは，面倒なので`一発でVMを構築したい！`のパートで僕が今使っているスクリプトを載せています．
結局どれがいいねん！って方は，`一発でVMを構築したい！`のパートをみていただければ良いと思います．

## virt-builder編
root(suユーザ)でもsudoでもok

Host名とパスワードが一緒の理由は，
友人にもVMを振る際に毎回Host名を考えるのがめんどうくさい,
既存のVMと重複するとエラーで止まってしまう問題があり，
ランダムにして揃えることでsudoもすぐ使えるようにしています．
（セキュリティが甘々なので後で改善予定です．）


```virt_build.sh
#!/bin/sh                                                                                                                                                              

USERNAME=ubuntu
PASSWORD=$(more /dev/urandom  | tr -d -c '[:alnum:]' | fold -w 10 | head -1)

sudo virt-builder ubuntu-18.04 \
     --format qcow2 \
     --hostname $PASSWORD \
     --timezone Asia/Tokyo \
     --append-line '/etc/default/grub:GRUB_CMDLINE_LINUX="console=ttyS0,115200"' \
     --run-command 'update-grub' \
     --root-password password:password \
     --firstboot-command " \                                                                                                                                           
     bash -c 'echo -n > /etc/machine-id' \                                                                                                                             
     dpkg-reconfigure openssh-server; \                                                                                                                                
     useradd -m -G sudo,operator -s /bin/bash $USERNAME; \                                                                                                             
     echo '$USERNAME:$PASSWORD' | chpasswd; \                                                                                                                          
     chown -R $USERNAME /home/$USERNAME; \                                                                                                                             
     perl -pi -e 's/^%sudo.*$/%sudo     ALL=(ALL:ALL) NOPASSWD: ALL/' /etc/sudoers; \                                                                                  
     perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://jp.archive.ubuntu.com/ubuntu/%g' /etc/apt/sources.list; \                                                
     reboot \                                                                                                                                                          
     " \
     -o /home/shared/images/ubuntu_$PASSWORD.qcow2
```

公開鍵認証をするときは，以下の四行を追加してください．
これは，あからじめ母艦で作成した公開鍵をゲストOSにcopyし，sshする側で秘密鍵を持つことで，鍵認証ができます．


```
     --mkdir /home/$USERNAME/.ssh \
     --copy-in $HOME/.ssh/hawaii_vm.pub:/home/$USERNAME/.ssh \
     --move /home/$USERNAME/.ssh/Your_key.pub:/home/$USERNAME/.ssh/authorized_keys \
     --chmod 0700:/home/$USERNAME/.ssh \
```

あとは，権限をつけてあげて，実行しましょう．

```
$ sudo chmod 755 virt_bulid.sh
$ ./virt_build.sh
```

`virt_builder`の中身自体は，読めばわかると思うのですが，
`bash -c 'echo -n > /etc/machine-id'`と`reboot`って何やってんねんとなりますよね．
ここは，VMを作成するまでは良いが，何台立てても，全て同一のIPをもらってしまう問題がありました．
それの解決になります．
なにやら，IPは，/etc/machine-id のidによって決まっているらしく，
このidが同じだと，同一のIPが振られるそうです．
そのため，`echo -n`をすることで，/etc/machine-idを空にして，
再起動（reboot）によってidを新しく作成することで，違うIPをもらっています．
（僕の環境では，どうやっても`virsh console`ができなかったので，直接sshをできるように
ここまで整えています．）


僕の作成した順番でやる場合には，
virt_installの時に，ホスト名が必要であるため，
```sudo virsh list```
で作成されたイメージの名前を確認しておいてください．
（後半でbuildとinstallを一緒のスクリプトで作成しています．）

## virt-install編
出力をみたいときは，最後に`-d`をつけてみてください．

```virt_installer.sh
#!/bin/sh                                                                                                                                                              

echo 'Write Hostname'
read hostname

sudo virt-install \
     --name $hostname \
     --hvm \
     --virt-type kvm \
     --ram 2048 \
     --vcpus 2 \
     --arch x86_64 \
     --os-type linux \
     --os-variant ubuntu18.04 \
     --boot hd \
     --disk path=/home/shared/images/ubuntu_$hostname.qcow2 \
     --network bridge=br0 \
     --graphics none \
     --serial pty \
     --console pty
```

`virsh list`または，`./virt_build.sh`の実行時の出力でホスト名（パスワード）をコピーしてから実行しましょう．

```
$ sudo chmod 755 virt_installer.sh
$ ./virt_installer.sh
```

実行時にWrite Hostname と出力が出ると思うので，コピーしたホスト名をペーストしてエンターを押しましょう．
これで，VMのコンソールに飛びます．
（コンソールに飛ばないようにする設定もできますが，sshも通らない，
　IPもわからない状態ですので，注意をお願いします．）
ユーザ名：ubuntu
パスワード：ホスト名
でログインできれば大丈夫です．

ログイン後に`ip addr`でipを確認し，

```
$ sudo apt update && sudo apt upgrade -y

$ ssh ubuntu@your_hostname.local
or
$ ssh ubuntu@your_ip_address
```
を実行します．
sudoのパスワードはホスト名です．
sshのバージョンが古いのか，この段階だとsshが通らないので，
aptの更新をしてからsshで接続することできればokです．
お疲れ様でした．


## 一発でVMを構築したい！

上記の例だと，毎回ホスト名を確認する工程があります．
僕はめんどくさがり屋なので，buildとinstallをまとめることで楽をしました．
単純にシェルスクリプトでまとめただけです．

```make_vm.sh
#!/bin/sh

USERNAME=ubuntu
PASSWORD=$(more /dev/urandom  | tr -d -c '[:alnum:]' | fold -w 10 | head -1)

echo 'virt-builder'
sudo virt-builder ubuntu-18.04 \
     --format qcow2 \
     --hostname $PASSWORD \
     --timezone Asia/Tokyo \
     --append-line '/etc/default/grub:GRUB_CMDLINE_LINUX="console=ttyS0,115200"' \
     --run-command 'update-grub' \
     --root-password password:password \
     --firstboot-command " \
     bash -c 'echo -n > /etc/machine-id' \
     dpkg-reconfigure openssh-server; \
     useradd -m -G sudo,operator -s /bin/bash $USERNAME; \
     echo '$USERNAME:$PASSWORD' | chpasswd; \
     chown -R $USERNAME /home/$USERNAME; \
     perl -pi -e 's/^%sudo.*$/%sudo     ALL=(ALL:ALL) NOPASSWD: ALL/' /etc/sudoers; \
     perl -p -i.bak -e 's%https?://(?!security)[^ \t]+%http://jp.archive.ubuntu.com/ubuntu/%g' /etc/apt/sources.list; \                                               
     reboot \                                                                                                                                           
     " \
     -o /home/shared/images/ubuntu_$PASSWORD.qcow2

echo 'virt-install'

sudo virt-install \
     --name $PASSWORD \
     --hvm \
     --virt-type kvm \
     --ram 2048 \
     --vcpus 2 \
     --arch x86_64 \
     --os-type linux \
     --os-variant ubuntu18.04 \
     --boot hd \
     --disk path=/home/shared/images/ubuntu_$PASSWORD.qcow2 \
     --network bridge=br0 \
     --graphics none \
     --serial pty \
     --console pty
```

```
$ sudo chmod 755 make_vm.sh
$ ./make_vm.sh
```
でコンソールまで接続できます．

```
$ sudo apt update && sudo apt upgrade -y
```

ログイン確認後にaptの更新を行った後，
`ip addr`でIP確認を行い，sshが通っていることが確認できれば，完了です．

# VMを破壊するとき

```
$ sudo virsh destroy VM_name
$ sudo virsh undefine VM_name
```

# 問題点
僕がこれをやっている段階でどうしても解決できなかった2点を挙げます．
原因等ありましたら教えていただけますととても嬉しいです．

## 二回目のコンソール接続ができない

virt_installの時点でのコンソールの接続ができるのですが，一度コンソールからexitして，
`virsh console`で入り直そうとすると動きません．
なぜでしょうか．解決策を教えていただけると助かります．

参考
https://knqyf263.hatenablog.com/entry/2014/01/13/200509 

## ゲストOSのipを母艦から見れない問題

直接物理NICからIPをもらっているため`sudo virsh net-dhcp-leases`では，IPを確認できません．
なので，コンソール上で一回IPの確認,sshの疎通が確認できないと使用できません．
同じように構築した場合は気をつけてください．

# さいごに
ここまでご覧いただき，ありがとうございます．
以上で，KVMを用いてubuntu18のVMを立てることができました．

今後は，ubuntu18のサポートが終わってしまうので，
母艦をubuntu20，VMもubuntu20にしてやってみようと思います．
次回もよろしくお願いします．

# 参考文献
ほぼ以下のサイトのまとめです．
こちらもみていただけますと幸いです．

https://qiita.com/yoshiyasu1111/items/8d07a4fd55116fba07f7 

https://endy-tech.hatenablog.jp/entry/kvm_setup_and_libvirt_cli#%E5%8F%82%E8%80%83-%E3%81%8A%E3%81%99%E3%81%99%E3%82%81%E3%81%AE%E5%8B%95%E7%94%BB--%E8%A8%98%E4%BA%8B 

https://ngyuki.hatenablog.com/entry/2020/08/24/220548 

https://qiita.com/masru0714/items/05d262d1ced5c32023f2　

https://qiita.com/sandopan65/items/75ca7e6563e86a7dfd8c