貧弱なメモリでも仮想環境を立てたい!!〜LXCを使う〜

本記事は [新人Advent Calendar 2016](http://qiita.com/advent-calendar/2016/freshman_engineer) の6日目の記事です。

# 背景
私、インフラエンジニアなのですよ。仮想マシン立てて検証とかしたいじゃないですか。Ansibleで複数のホストをプロビジョニングとかしたいじゃないですか。
ところがですね、職場で支給されるPCが Windows 7 32bit版、メモリ 4GB とかいう貧弱なPCなんですよ。
一応、検証環境はあるんですけど、IPアドレスの申請が必要で、ちょっと試したい目的には申請・承認フローがめんどくさいし、そもそも、IPが枯渇気味らしい。

# 2038年まで待ってられない
ということで、仕方ないので、軽量仮想コンテナで我慢することとしています。
コンテナといえばDockerみたいな時代ではありますが、インフラエンジニア的にはシステムコンテナの LXC かなと思うわけです。
それに、ガチモンの仮想マシンより、圧倒的に速いので、自宅で環境汚したくないなぁって思う作業をするときは、LXCを使っています。
~~Windowsサーバーの検証を手元でやるのはすでに諦めています。~~

# 環境準備
個人的にUbuntuが大好きなので、以下、Ubuntu 16.04 でお話を進めます。
VirtualBoxで仮想マシンを適当に立てて、Ubuntuの環境を構築してください。職場ではメモリ1GBで立てています。
続いて、LXCをインストールします:
  ``` sudo apt install lxc```

# ためしにUbuntuのコンテナを作ってみる
 実はこの段階でもうホストと同じUbuntuならLXCのコンテナが作れます。
 lxcコンテナを作成する lxc-create コマンドの記法はおおよそ次の通りです:
   ``` lxc-create -n (container_name) -t (template) -- (template_options)```
 では実際に作ってみます:
   ```sudo lxc-create -n ubuntu-test-container -t ubuntu -- -r xenial --mirror="http://ftp.jaist.ac.jp/ubuntu"```
   初回は各種パッケージのダウンロードがあるため、時間がかかりますが、2回目以降はすぐにコンテナを作成できます。```-r```オプションでリリースを指定していますが、省略すると、ホストと同じリリースのコンテナが作成されます。
 起動するには:
   ```sudo lxc-start -n ubuntu-test-container```
 コンソールにアタッチするには:
   ```sudo lxc-console -n ubuntu-test-container```
 抜けるときは:
   ```Ctrl-Q -> A```
 停止するには:
   ```sudo lxc-stop -n ubuntu-test-container```
 削除するときは:
   ```sudo lxc-destroy -n ubuntu-test-container```

 Ubuntuのテンプレートの認証情報はデフォルトではユーザー名・パスワードともに `ubuntu` です。SSHサーバーもデフォルトで入っているので、次のコマンドでIPアドレスを調べて、SSHでつなぐことができます:
   ```sudo lxc-info -n ubuntu-test-container```

 また、テンプレートオプションを使って認証情報を設定することができます。次の例はコンテナ作成にユーザー名とパスワードを指定し、SSHの公開鍵を差し込むものです:
   ```sudo lxc-create -n ubuntu-test-container -t ubuntu -- -r xenial --mirror="http://ftp.jaist.ac.jp/ubuntu" -S /home/popo/.ssh/id_rsa.pub -u popo --password password```

# CentOSのコンテナを作る
yumが要るので、インストールします:
   ```sudo apt install yum```
作り方自体は基本的に、Ubuntuのコンテナを作るときと同じです。

ただし、CentOSのテンプレートで作られるユーザーはrootのみで、パスワードはコンテナ作成時にランダムなものが生成され、初回ログイン時に設定することになります。
ですが、コンテナ作成時にパスワードを設定する方法もあります。こちらは既に[記事](http://qiita.com/_popopopoon_/items/91e2f39b172cb4074372)にしていますので、参照してください。
各種テンプレートは /usr/share/lxc/templates に収められていますが、実体はどれもbashのスクリプトです。CentOSのパスワードの件もそうなのですが、テンプレートオプションにはないものの、いろいろ設定できるみたいなので、気になるテンプレートは覗いてみればよいと思います。

# 社内環境の場合はプロキシに要注意
そのままです。aptやyumにプロキシの設定が必要です。詳細はほかに譲ります。
注意点としては、設定するのはホスト側のaptやyumである点です。コンテナ作成時にはホスト側のものが使用されるからです。

# 使用リソース
できたてほやほやのコンテナの使用リソースは次の通りです:

```bash:lxc-infoの出力
Name:           ubuntu-test-container
State:          RUNNING
PID:            16536
IP:             10.0.3.169
CPU use:        0.58 seconds
BlkIO use:      9.12 MiB
Memory use:     20.24 MiB
KMem use:       0 bytes
Link:           vethNLE20G
 TX bytes:      1.23 KiB
 RX bytes:      1.43 KiB
 Total bytes:   2.66 KiB
```

メモリ使用量が少なくて良いですね。これで、4GBメモリでも仮想マシンが複数立てられます!!!!

# AnsibleでLXCコンテナ作成・削除を自動化
(習作でロールにもなっていないのですが、いい感じにできたら、githubに上げます、そのうち。)
次のものをインストールします:

   - sshpass(apt経由)
   - ansible(pip経由)
   - lxc-python2(同上)

おしながき:
   - create-lxc-containers.yml
   - destroy-lxc-containers.yml
   - vars-containers.yml
   - hosts

vars-containers.yml という名前で、立てるコンテナの情報を記載します。IPアドレスも指定しています。
注意点としては、SSH公開鍵はフルパスで指定する必要がある点です。

```yaml:vars-containers.yml
- containers:
  - name: ubuntu01
    template: ubuntu
    ip_addr: 10.0.3.11
    netmask_bit: 24
    template_options: "-S /home/popo/.ssh/id_rsa.pub --packages python2.7"
    env: {}

  - name: ubuntu02
(中略)
  - name: ubuntu03
(中略)
  - name: centos01
    template: centos
    ip_addr: 10.0.3.21
    netmask_bit: 24
    template_options: "-R 7"
    env:
      root_password: "password"
      root_expire_password: "no"

  - name: centos02
(中略)
  - name: centos03
(後略)
```

create-lxc-containers.yml は、vars-containers.yml 設定したコンテナ情報でコンテナを作ります。ループさせているので、設定のほうを増やせば、いくつでも立てられます。
個人的にこだわったのは、SSHのknown_hostsを操作しているところです。
というのも、コンテナを作成・削除するたびに、ホスト側のSSH公開鍵が変わるため、前の鍵の情報が残っているとエラーが出るためです。

```yaml:create-lxc-containers.yml
- name: create LXC containers
  hosts: localhost
  vars_files:
    - vars-containers.yml

  tasks:
    - name: lxc-create
      become: true
      become_method: sudo
      lxc_container:
        name: "{{ item.name }}"
        template: "{{ item.template }}"
        container_config:
          - "lxc.network.ipv4={{ item.ip_addr }}/{{ item.netmask_bit }}"
        template_options: "{{ item.template_options }}"
      environment: "{{ item.env }}"
      with_items: "{{ containers }}"

    - name: delete ssh host keys from known_hosts
      shell: "ssh-keygen -f ~/.ssh/known_hosts -R {{ item.ip_addr }}"
      with_items: "{{ containers }}"

    - name: register ssh host keys to known_hosts
      shell: "ssh-keyscan -H {{ item.ip_addr }} >> ~/.ssh/known_hosts"
      with_items: "{{ containers }}"

  environment:
    http_proxy: "http://proxy:8080"
    https_proxy: "http://proxy:8080"
```

インベントリファイルには、コンテナを立てたあとにAnsibleで操作するための情報を含めています。(この情報はコンテナを立てるときには使いません。)

```ini:hosts
ubuntu01 ansible_ssh_host=10.0.3.11
ubuntu02 ansible_ssh_host=10.0.3.12
ubuntu03 ansible_ssh_host=10.0.3.13

centos01 ansible_ssh_host=10.0.3.21
centos02 ansible_ssh_host=10.0.3.22
centos03 ansible_ssh_host=10.0.3.23

[ubuntu]
ubuntu[01:03]

[centos]
centos[01:03]

[all:vars]
ansible_ssh_port=22

[ubuntu:vars]
ansible_user=ubuntu
ansible_python_interpreter=/usr/bin/python2.7
ansible_ssh_private_key=~/.ssh/id_rsa
ansible_sudo_pass=ubuntu

[centos:vars]
ansible_user=root
ansible_password=password
```

削除は destroy-lxc-containers.yml でおこないます。

```yaml:destroy-lxc-containers.yml
- name: destroy containers
  hosts: localhost
  vars_files:
    - vars-containers.yml

  tasks:
    - name: destroy containers
      lxc_container:
        name: "{{ item.name }}"
        state: absent
      with_items: "{{ containers }}"
      become: true

    - name: delete hosts from known_hosts
      shell: "ssh-keygen -f ~/.ssh/known_hosts -R {{ item.ip_addr }}"
      with_items: "{{ containers }}"
```

流し方は次の通りです:
```
ansible-playbook -i hosts (create|destroy)-lxc-containers.yml -K
```

# ちょっと気になったこと
bash on Ubuntu on Windows でも動くのかしら～というのが気になったので、調べてみようと思いました。空き&余裕があれば、別日に記事にします。
あと、コンテナ名のubuntu-test-containersは長すぎた。

## 参考URL
 1. http://gihyo.jp/admin/serial/01/ubuntu-recipe/0226 - UbuntuでのLXCの操作方法について
 1. http://d.hatena.ne.jp/mrgoofy33/20110125/1295966614 - yumのプロキシ設定について
 1. http://qiita.com/nojima/items/2980ea490476e6fe5e23 - UbuntuのLXCテンプレートオプションについて
 1. http://qiita.com/kentarosasaki/items/b44b5ffb57a69085027e - Dockerと比較してLXCの紹介
 1. http://qiita.com/Surgo/items/709a07d68c6eafbad267 - DockerとLXCの比較
 1. http://gihyo.jp/admin/serial/01/linux_containers?start=20 - LXCの技術的な解説
 	- http://gihyo.jp/admin/serial/01/linux_containers/0008 - このシリーズ内でとりあえず使ってみる回