LinuxKitで作成したイメージをPacketのベアメタルサーバー上で動かす

[DockerCon 2017](http://2017.dockercon.com/)で[LiuxKit](https://github.com/linuxkit/linuxkit)というツールが発表されました。

* https://github.com/linuxkit/linuxkit
* [Announcing LinuxKit: A Toolkit for building Secure, Lean and Portable Linux Subsystems - Docker Blog](https://blog.docker.com/2017/04/introducing-linuxkit-container-os-toolkit/)

LinuxKitを使用するとDockerのイメージを組み合わせてLinuxのイメージを作成することができます。イメージはISOやqcow2等の形式で出力して、各種ハイパーバイザ、クラウド、ベアメタルサーバー上で実行することが可能です。

今回はLinuxKitで作成したイメージをベアメタルサーバー上で実行する例として、[Packet](https://www.packet.net/)のベアメタルサーバー上で実行してみます。

# Packet

[Packet](https://www.packet.net/)はAWSやGCPと同じようなパブリッククラウドのプロバイダーですが、特徴として他の多くのクラウドと異なり仮想ではなくベアメタルでサーバーを提供しています。操作感的には他のクラウドとほぼ同様でAPIも提供されています。

# とりあえず動かしてみる

とりあえず、LinuxKitのGetting Startedの`linuxkit.yml`で作成したイメージをPacketのベアメタルサーバーで動かしてみます。

[linuxkit/README.md at master · linuxkit/linuxkit · GitHub](https://github.com/linuxkit/linuxkit/blob/master/README.md)

まず`moby buld`を実行してイメージを作成します。

```
$ moby build linuxkit.yml
$ ls linuxkit* | grep -v yml
linuxkit-bzImage
linuxkit-cmdline
linuxkit-efi.iso
linuxkit-initrd.img
linuxkit.iso
```

次に`linuxkit-bzImage`、`linuxkit-initrd.img`をどこかにアップロードします。例では[Google Cloud Storage](https://cloud.google.com)を使用していますが、Packetでサーバー作成する際にダウンロード可能であればどこでも良いです。

```
$ gsutil mb gs://<YourBucketName>
$ gsutil cp -a public-read linuxkit-bzImage  gs://<YourBucketName>
$ gsutil cp -a public-read linuxkit-initrd.img  gs://<YourBucketName>
```

PacketはLinuxKitでサポートされているため`linuxkit run packet`コマンドで簡単にサーバーを作成することができます。

```
$ linuxkit run packet --base-url http://storage.googleapis.com/<YourBucketName> --api-key <YourPacketAPIKey> --project-id <YourPacketProjectID> linuxkit
```

コマンドを実行したら、WebUI等でサーバーの作成状況とIPアドレスを確認します。Active状態になったらサーバーのIPアドレスをブラウザで開いてNignxの画面が表示されることを確認します[^1]。

[^1]: [linuxkit.ymlで作成されるイメージではNginxが起動しています](https://github.com/linuxkit/linuxkit/blob/master/linuxkit.yml)

今のところ`linuxkit run packet`コマンドではコンソールへの接続まではやってくれないですが以下のようにSSHするとコンソールに接続することも可能です。[^2]

[^2]: サーバー作成時に公開鍵を登録していないと接続に失敗しまうようなので、もし登録してなければ左メニューの「SSH Keys」から登録しておく

[How to use our S.O.S. Rescue Mode | Packet Support](https://help.packet.net/technical/networking/how-to-use-our-sos-rescue-mode)

動作を確認したら必要以上に課金されないよう忘れず削除しておきましょう。今のところ削除のコマンドはないのでPacketのAPIやUIから削除します。

# サーバーにSSHでログインする

Dockerイメージを組み合わせてOSのカスタマイズする例として[sshdのイメージ](https://github.com/linuxkit/linuxkit/blob/master/pkg/sshd/Dockerfile)を使ってSSHでログインできるようにしてみます。

次のように`linuxkit.yml`の`services`と`files`にSSHサーバーと鍵の情報を追記します。

```yaml
services:
  - name: rngd
    image: "mobylinux/rngd:3dad6dd43270fa632ac031e99d1947f20b22eec9"
    capabilities:
     - CAP_SYS_ADMIN
    oomScoreAdj: -800
    readonly: true
  - name: nginx
    image: "nginx:alpine"
    capabilities:
     - CAP_NET_BIND_SERVICE
     - CAP_CHOWN
     - CAP_SETUID
     - CAP_SETGID
     - CAP_DAC_OVERRIDE
    net: host
  - name: sshd
    image: "linuxkit/sshd:e108d208adf692c8a0954f602743e0eec445364e"
    capabilities:
     - all
    net: host
    pid: host
    binds:
     - /root/.ssh:/root/.ssh
     - /etc/resolv.conf:/etc/resolv.conf
files:
  - path: etc/docker/daemon.json
    contents: '{"debug": true}'
  - path: root/.ssh/authorized_keys
    contents: '#your ssh key here'
```

再度イメージをビルド、アップロードして`linuxkit run packet`コマンドでサーバーを作ります。

```
$ moby build linuxkit.yml
$ gsutil versioning set on gs://linuxkit
$ gsutil cp -a public-read linuxkit-bzImage　gs://<YourBucketName>
$ gsutil cp -a public-read linuxkit-initrd.img　gs://<YourBucketName>
$ linuxkit run packet --base-url http://storage.googleapis.com/<YourBucketName> --api-key <YourPacketAPIKey> --project-id <YourPacketProjectID> linuxkit
```

成功していればSSHで`sshd`コンテナの中に入ることができます。`binds`を変更することでホストのファイルをコンテナに見せることもできます。

```
ssh root@<サーバーのIP>
```

# linuxkit run packetは何をしているか？

PacketではインストールするOSをCentOSやUbuntu、ESXi等から選択できますが、独自のOSがインストールできるよう[iPXE](http://ipxe.org/)のスクリプトが使用できるようになっています。

[Custom iPXE | Packet Support](https://help.packet.net/technical/infrastructure/custom-ipxe)

`linuxkit run packet`はこの仕組みを利用し、以下のようなiPXEスクリプトを生成、サーバー作成時の引数に渡すことによってアップロードしたイメージでサーバーを起動しています。

```
#!ipxe

dhcp
set base-url http://http://storage.googleapis.com/<YourBucketName>
set kernel-params ip=dhcp nomodeset ro serial console=ttyS1,115200
kernel ${base-url}/linuxkit-bzImage ${kernel-params}
initrd ${base-url}/linuxkit-initrd.img
boot
```

[linuxkit/run_packet.go at 456f2623d57e547df11184e00fd2fae89b2da994 · linuxkit/linuxkit · GitHub](https://github.com/linuxkit/linuxkit/blob/456f2623d57e547df11184e00fd2fae89b2da994/src/cmd/moby/run_packet.go#L82-L85)

iPXEスクリプトが使われるのはサーバー作成時のみで、それ以降はローカルディスクから起動されます。今回のようにディスクにインストールしていない状態でサーバーを落とすと起動できなくなり作り直しになります。

# さいごに

簡単にですがLinuxKitで作成したイメージをPacketのベアメタルサーバーで動かす方法と仕組みを見てきました。基本的には同じような方法で、他の物理サーバーでもLinuxKitで作成したイメージを動かせると思います。

今回はデフォルトのType0のみで試しましたが構成の異なる他のタイプやARMのサーバーも試してみると面白そうです。

* [support drivers needed on packet.net · Issue #1245 · linuxkit/linuxkit · GitHub](https://github.com/linuxkit/linuxkit/issues/1245)
* [DockerCon and open source planning · Issue #1429 · linuxkit/linuxkit · GitHub](https://github.com/linuxkit/linuxkit/issues/1429)