CoreOSでRocketを使ってnginxを動かす

# Roket

バージョンが、
CoreOS: 723.3.0
rkt: 0.5.5
の話。
今のAzureのstableです。

## Rocketって何？

ざっくり簡単に言ってDockerに変わるアプリケーションコンテナ技術、ですかね。
特に大きく違うところはdockerが仮想ネットワーク経由のところをRocketはホストのネットワークを使う、というところです。
なので、ネットワークについてあれこれ悩む必要が無いのが特徴です。

# 元データ作成

## ACI元データをつくる

元になるnginxはDockerHubから持ってきます。
[docker2aci](https://github.com/appc/docker2aci)というツールを使えば楽できるんですが、なんだかコンパイルできなかったので、手作業で作ります。

作り方は、
1. Dockerをpullしてrunする
2. runしてるDockerをexportしてroots下に保存
と、こんな感じ。
なので、あらかじめDockerを動かせるようにしておきます。

```yaml:/var/lib/coreos-install/user_data
coreos:
  units:
    - name: docker.service
      command: start
```

```bash
$ docker pull nginx
latest: Pulling from nginx

902b87aaaec9: Pull complete 
9a61b6b1315e: Pull complete 
aface2a79f55: Pull complete 
5dd2638d10a1: Pull complete 
97df1ddba09e: Pull complete 
56c99fa886e8: Pull complete 
cd27805ea89f: Pull complete 
5e95ac0e9a79: Pull complete 
9f36f81a1ccb: Pull complete 
4c1809b04591: Pull complete 
98c9ccd75644: Pull complete 
6886fb5a9b8d: Pull complete 
Digest: sha256:29ff4c68a1c9a014c06678f0882c8976bd83df501110aeb9e659bdfa2929e3c4
Status: Downloaded newer image for nginx:latest
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
nginx               latest              6886fb5a9b8d        4 weeks ago         132.8 MB
$ docker run -it nginx /bin/bash
```

```bash
$ mkdir -p ~/nginx/aci/rootfs
$ cd ~/nginx/aci/rootfs
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
e2ff0d86ca1b        nginx:latest        "/bin/bash"         About a minute ago   Up About a minute   80/tcp, 443/tcp     agitated_lumiere    
$ docker export e2ff0d86ca1b | tar xvp
```

これでACI用にコピーが取れます。

## manifestファイルを書く

コピーしてきたファイルの内容に合わせてmanifestファイルを書きます。
ここでは、動かすユーザーはroot、メンテナンスを楽にするためにconf.d用とsite用、log用のディレクトリをACIの外からマウントするように設定します。

```json:~/nginx/aci/manifest
{
    "acKind": "ImageManifest",
    "acVersion": "0.6.1",
    "name": "nginx",
    "labels": [
        {"name": "os", "value": "linux"},
        {"name": "arch", "value": "amd64"}
    ],
    "app": {
        "user": "0",
        "group": "0",
        "exec": [
            "/usr/sbin/nginx"
        ],
        "ports": [
            {
                "name": "http",
                "protocol": "tcp",
                "port": 80
            }, {
                "name": "https",
                "protocol": "tcp",
                "port": 443
            }
        ],
        "mountPoints": [
            {
                "name": "conf",
                "path": "/opt/nginx/conf",
                "readOnly": true
            }, {
                "name": "sites",
                "path": "/opt/nginx/sites",
                "readOnly": false
            }, {
                "name": "log",
                "path": "/opt/nginx/log",
                "readOnly": false
            }
        ]
    }
}
```

実は多いに嵌まってしまったのですが、execの中はコマンドライン引数を書けません。
`command not found` とか言われてしまいます。
なので、書くのは実行ファイルのみ（しかもフルパス）。
foregroundにするオプション、 `daemon off;` は設定ファイルに書かないといけません。
まあ、自分が知らないだけで書き方はあると思うんですけどねー。

## 設定ファイルを書き換える

logファイルやらincludeパスやらをmanifestで追加しているパスに合わせます。
あと、上記で書いた通り、 `daemon off;` を書いておきます。

```nginx:~/nginx/aci/root/etc/nginx/nginx.conf
user  nginx;
worker_processes  1;
daemon off;

error_log  /opt/nginx/log/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /opt/nginx/log/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /opt/nginx/conf/*.conf;
}
```

## ACIファイルを作る

これは普通に作ればおっけー。

```bash
$ cd ~/nginx
$ actool build aci nginx.aci
```

## nginx用の設定をしておく

ここでは、ホストの
/mnt/data/conf/nginx をnginxの設定ファイルを置く場所に、
/mnt/data/sites をサイトのデータを置く場所に、
/mnt/data/log をログを置く場所に、
それぞれ指定します。

そのため、

```nginx:/mnt/data/conf/nginx/default.conf
server {
    listen 80;
    server_name localhost;

    location / {
        root /opt/nginx/sites/root;
        index index.html index.htm;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /opt/nginx/sites/root;
    }
}
```

とか

```html:/mnt/data/sites/root/index.html
<html>
 <head>
  <title>Test</title>
 </head>

 <body>
  <h1>Hello, world</h1>
  <p>good morning !</p>
 </body>
</html>
```

などのようなファイルを仮に置いておきます。

## 試しに実行してみる

manifestで指定しているマウントディレクトリを引数で指定して実行します。

```bash
$ sudo rkt --insecure-skip-verify --debug run --volume=conf,kind=host,source=/mnt/data/conf/nginx --volume=sites,kind=host,source=/mnt/data/sites --volume=log,kind=host,source=/mnt/data/log ~/nginx/nginx.aci
```

`--volume=[manifestで指定しているname],kind=host,source=[実ディレクトリ]` という感じで引数に指定します。
名前同士で実ディレクトリとmanifestで指定したディレクトリがリンクし、同一のものとして扱われます。
また、署名していないので `--insecure-skip-verify` オプションが必要です。

これでとりあえずnginxが動きます。
試しにアクセスしてみましょう。

```bash
$ curl http://localhost/
<html>
 <head>
  <title>Test</title>
 </head>

 <body>
  <h1>Hello, world</h1>
  <p>good morning !</p>
 </body>
</html>
```

どうです？
表示されましたか？

やー。
localhostでアクセスできるの楽だわ。
やっぱ。

ログファイルを確認してみましょう。

```bash
$ tail /mnt/data/log/access.log
127.0.0.1 - - [20/Aug/2015:01:40:43 +0000] "GET / HTTP/1.1" 200 121 "-" "curl/7.42.1" "-"
```

ログも書かれています。
ホスト環境との受け渡しもできていますね。

# 自動起動

## 自動起動させるようにする

自動起動にさせるようにするには、いつものごとくcloud-configに書いておきます。

```yaml:/var/lib/coreos-install/user_data
coreos:
  units:
    - name: nginx.service
      command: start
      content: |
        [Unit]
        Description=Nginx next generation web server
        Requires=network-online.target
        After=network-online.target

        [Service]
        ExecStart=/usr/bin/rkt --insecure-skip-verify run --volume=conf,kind=host,source=/mnt/data/conf/nginx --volume=sites,kind=host,source=/mnt/data/sites --volume=log,kind=host,source=/mnt/data/log /home/xxx/nginx/nginx.aci
        KillMode=mixed
        Restart=always
```

書いたら一度試します。

```bash
$ sudo coreos-cloudinit -from-file=/var/lib/coreos-install/user_data
   :
2015/08/20 10:22:03 Calling unit command "start" on "nginx.service"'
2015/08/20 10:22:03 Result of "start" on "nginx.service": done
```

問題なければdoneになるはずです。
一度試すと、systemディレクトリに設定ファイルが書かれますので、あとはsystemctlで確認できます。

```bash
$ sudo systemctl status nginx.service
● nginx.service - Nginx next generation web server
   Loaded: loaded (/etc/systemd/system/nginx.service; static; vendor preset: disabled)
   Active: active (running) since Wed 2015-08-19 15:52:23 JST; 18h ago
 Main PID: 658 (ld-linux-x86-64)
   Memory: 24.5M
      CPU: 22.217s
   CGroup: /system.slice/nginx.service
           mq658 stage1/rootfs/usr/lib/ld-linux-x86-64.so.2 stage1/rootfs/usr...

Aug 19 15:52:23 logmanager systemd[1]: Started Nginx next generation web server.
Aug 19 15:52:23 logmanager systemd[1]: Starting Nginx next generation web s.....
Aug 20 10:20:24 logmanager systemd[1]: Started Nginx next generation web server.
Aug 20 10:22:03 logmanager systemd[1]: Started Nginx next generation web server.
Hint: Some lines were ellipsized, use -l to show in full.
```

無事、動かすことができました。
試験環境ならsudo restartとかしてみてください。

# メンテナンス

## nginxをreloadさせる

設定を書き換えたときにnginxをリスタートさせる方法です。
Rocketのリストを取得し、中に入り、nginxを操作します。

```bash
$ sudo rkt list
UUID            ACI     STATE   NETWORKS
31ffb1b9        nginx   running

$ sudo rkt enter 31ffb1b9
No command specified, assuming "/bin/bash"
root@rkt-31ffb1b9-fa8c-498c-9bbe-53e1842ae085:/# nginx -s reload
```