Go Firecracker SDKを使ってFirecrackerをコマンドライン操作するfirectlコマンドを試し、さらにコードも追ってみた #reinvent
こんにちは、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を見てみようと思ってます。