この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、shoitoです。
先日、re:Invent 2018で発表された軽量な仮想環境Firecrackerについて、前回さわってみました。
今回はfirecracker-microvm/firecracker-go-sdkにGo言語用のSDKがあったので確認しました。その中でSDKを使って作られているfirectlコマンドが紹介されていたので試してみました。
検証環境
- i3.metal(EC2インスタンス)
- Ubuntu Server 18.04 LTS (HVM), SSD Volume Type - 64 ビット (x86)
必須要件
- Linux 4.14+
- KVM
セットアップ
Go言語のソースコードをビルドするので、Go言語環境を構築します。
モジュール機能を使ってサクッとビルドしたいので 1.11
以上にしておきます。
$ curl -L -O https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.11.2.linux-amd64.tar.gz
goコマンドをPATHに設定します。
$ vi ~/.profile
# 以下を追加する
PATH=$PATH:/usr/local/go/bin
$ source ~/.profile
$ go version
go version go1.11.2 linux/amd64
次にSDKのビルド時に必要になる make
と gcc
をインストールします。
sudo apt-get update
sudo apt install make gcc
ビルド環境は整ったので、GitHubからソースコードをcloneして、Makefileに従ってmake
でビルドします。
$ git clone https://github.com/firecracker-microvm/firecracker-go-sdk
$ cd firecracker-go-sdk
$ make
go build
for d in cmd/firectl; do \
cd $d && go build ; \
done
go: downloading github.com/jessevdk/go-flags v1.4.0
これで cmd/firectl
ディレクトリに firectl
コマンドのバイナリが作られます。
firectlでFirecrackerのゲストマシンを立ち上げてみる
前回のこちらの記事にまとめたようにFirecrackerをセットアップします。
準備ができたらFirecrackerを立ち上げます。
$ ./firecracker-v0.11.0 --api-sock /tmp/firecracker.sock
問題なく立ち上がったら、別のシェルから、firectlコマンドでゲストマシンを立ち上げてみます。
$ ./cmd/firectl/firectl \
--firecracker-binary=/home/ubuntu/firecracker-v0.11.0 \
--firecracker-console=stdio \
--kernel=/home/ubuntu/hello-vmlinux.bin \
--root-drive=/home/ubuntu/hello-rootfs.ext4 \
--tap-device=enp4s0/12:36:38:b9:38:8a \
--vmm-log-fifo=/tmp/fc-logs.fifo \
--metrics-fifo=/tmp/fc-metrics
コマンドの実行に成功すると、以下のように上記コマンド実行後、ブートログが出力され、ログインを求められるので root:root
でログインします。
INFO[0000] Called startVMM(), setting up a VMM on ./firecracker.sock
INFO[0000] Configured VMM logging to /tmp/fc-logs.fifo, metrics to /tmp/fc-metrics: [PUT /logger][204] putLoggerNoContent
INFO[0000] refreshMachineConfig: [GET /machine-config][200] getMachineConfigOK &{CPUTemplate:Uninitialized HtEnabled:true MemSizeMib:512 VcpuCount:1}
INFO[0000] PutGuestBootSource: [PUT /boot-source][204] putGuestBootSourceNoContent
INFO[0000] Attaching drive /home/ubuntu/hello-rootfs.ext4, mode rw, slot 1, root true.
INFO[0000] Attached drive /home/ubuntu/hello-rootfs.ext4: [PUT /drives/{drive_id}][204] putGuestDriveByIdNoContent
INFO[0000] startInstance successful: [PUT /actions][204] createSyncActionNoContent
[ 0.000000] Linux version 4.14.55-84.37.amzn2.x86_64 (mockbuild@ip-10-0-1-79) (gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)) #1 SMP Wed Jul 25 18:47:15 UTC 2018
[ 0.000000] Command line: ro console=ttyS0 noapic reboot=k panic=1 pci=off nomodules root=/dev/vda virtio_mmio.device=4K@0xd0000000:5
[ 0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
...(省略)
Welcome to Alpine Linux 3.8
Kernel 4.14.55-84.37.amzn2.x86_64 on an x86_64 (ttyS0)
localhost login: root
Password:
Welcome to Alpine!
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <http://wiki.alpinelinux.org>.
You can setup the system with the command: setup-alpine
You may change this message by editing /etc/motd.
login[857]: root login on 'ttyS0'
ここまででfirectlコマンドでゲストマシンを立ち上げて、ゲスト環境が使える状態になりました。
localhost:~# ls /
bin home media root srv usr
dev lib mnt run sys var
etc lost+found proc sbin tmp
SDKのコードを見てみる
さて、firectlコマンドが内部で使っている firecracker-microvm/firecracker-go-sdk
のコードを見てみます。
ちなみにFirecrackerは前回紹介したようにREST APIが提供されていて、HTTPリクエストを送ることでゲストマシンの操作ができます。
そのREST APIの仕様はSwaggerで定義されています。
https://github.com/firecracker-microvm/firecracker/blob/master/api_server/swagger/firecracker.yaml
一部抜粋してますが、paths
配下の/actions
エンドポイントはゲストマシンを立ち上げるときにInstanceStart
を送ったパスになります。
swagger: "2.0"
info:
title: Firecracker API
description: RESTful public-facing API.
The API is accessible through HTTP calls on specific URLs carrying JSON modeled data.
The transport medium is a Unix Domain Socket.
version: 0.11.0
termsOfService: ""
contact:
email: "compute-capsule@amazon.com"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
host: "localhost"
basePath: "/"
schemes:
- http
consumes:
- application/json
produces:
- application/json
paths:
/:
get:
summary: Returns general information about an instance.
operationId: describeInstance
responses:
200:
description: The instance information
schema:
$ref: "#/definitions/InstanceInfo"
default:
description: Internal Server Error
schema:
$ref: "#/definitions/Error"
/actions:
put:
summary: Creates a synchronous action.
operationId: createSyncAction
parameters:
- name: info
in: body
required: true
schema:
$ref: "#/definitions/InstanceActionInfo"
responses:
204:
description: The update was successful
400:
description: The action cannot be executed due to bad input
schema:
$ref: "#/definitions/Error"
default:
description: Internal Server Error
schema:
$ref: "#/definitions/Error"
...(省略)
ここからfirectl.go
のmainからREST APIを呼ぶところまで見ていきます。
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/cmd/firectl/main.go#L91
func main() {
var err error
var opts struct {
FcBinary string `long:"firecracker-binary" description:"Path to firecracker binary"`
FcConsole string `long:"firecracker-console"
firecracker.NewMachine()
で新しいゲストマシンを生成します。
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/cmd/firectl/main.go#L184
m, err := firecracker.NewMachine(fcCfg, firecracker.WithLogger(log.NewEntry(logger)))
if err != nil {
log.Fatalf("Failed creating machine: %s", err)
}
firecracker.NewMachine()
で何をしているかと追っていくと...
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/machine.go#L164
// NewMachine initializes a new Machine instance
func NewMachine(cfg Config, opts ...Opt) (*Machine, error) {
if err := cfg.Validate(); err != nil {
return nil, err
}
m := &Machine{}
ゲストマシンのインスタンスにclient
を生成してセットし...
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/machine.go#L186
if m.client == nil {
m.client = NewFirecrackerClient(cfg.SocketPath, m.logger, cfg.Debug)
}
m.StartInstance()
が呼ばれ...
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/cmd/firectl/main.go#L196
} else {
m.StartInstance(vmmCtx)
}
やっと内部でREST APIを呼ぶclient.CreateSyncAction()
が実行されます。
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/machine.go#L524
resp, err := m.client.CreateSyncAction(ctx, &info)
if err == nil {
m.logger.Printf("startInstance successful: %s", resp.Error())
} else {
m.logger.Printf("Error starting instance: %s", err)
}
インターフェース定義はここに。
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/machine.go#L60
type Firecracker interface {
...
CreateSyncAction(ctx context.Context, info *models.InstanceActionInfo) (*ops.CreateSyncActionNoContent, error)
あとはOperations.CreateSyncAction()
が呼ばれ...
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/firecracker.go#L132
return f.client.Operations.CreateSyncAction(params)
最終的に、REST APIで/actions
への PUT
リクエストが呼ばれます。
https://github.com/firecracker-microvm/firecracker-go-sdk/blob/master/client/operations/operations_client.go#L163
result, err := a.transport.Submit(&runtime.ClientOperation{
ID: "createSyncAction",
Method: "PUT",
PathPattern: "/actions",
ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{"application/json"},
Schemes: []string{"http"},
Params: params,
Reader: &CreateSyncActionReader{formats: a.formats},
Context: params.Context,
Client: params.HTTPClient,
})
さいごに
firectlコマンドを試してみて、実際にSDKをどんな感じで使っているのか1操作だけですがソースコードを追ってみましたがいかがでしょうか?
次はfirecracker-containerdを見てみようと思ってます。