0
2

More than 1 year has passed since last update.

Lima の containerd へ接続してみる

Last updated at Posted at 2023-01-20

最近、macOS を使い始めて、課題となったのが Linux のコンテナ環境をどうするかという事でした。

あまり余計な事をせずに containerd が動作するようなものを探したところ Lima を見つけました。

Lima は Linux 仮想マシンで containerd がビルトインされています。
containerd を操作するための CLI ツール nerdctl (Docker との互換性あり)も導入してくれるので、これだけでコンテナの実行環境が整います。

そこで今回は、Lima の環境を構築するついでに、仮想マシン内の containerd へ Go 言語のコードで接続してみました。

はじめに

Lima をインストールし、仮想マシンを実行しておきます。

インストール例
% brew install lima

次のように引数無しで start を実行すると、default という名の仮想マシンのインスタンスが(デフォルト設定で)実行されます。

この場合、RootlessKit を用いた rootless モードで containerd が実行されるようです。

lima 起動例
% limactl start

lima コマンド1で仮想マシンへ入る事ができ、nerdctl コマンドでコンテナを実行できます。

例えば、nginx のコンテナを実行する場合は次のようになります。

nginx コンテナ実行例1
[macOS] % lima
[lima]  $ nerdctl run -d --name nginx1 -p 8080:80 nginx 

もしくは

nginx コンテナ実行例2
[macOS] % lima nerdctl run -d --name nginx1 -p 8080:80 nginx 

このように nerdctl は docker コマンドと同じように使えます。

containerd へ接続

containerd は gRPC の API 2で操作できるようになっているので、これを使ってコンテナの情報を取得してみます。

基本的に、containerd.sock というソケットファイルで接続する事になるので、このファイルが何処に配置されているかが重要です。

rootless モードで containerd を起動した場合は、/run/containerd/containerd.sock 3では無く、以下の場所にありました。

  • /proc/<PID of containerd>/root/run/containerd/containerd.sock

この PID の値は $XDG_RUNTIME_DIR/containerd-rootless/child_pid ファイルに記載されています。4

child_pid ファイルから PID を取得し containerd.sock へのパスを組み立て、containerd へ接続する処理は次のようになりました。(エラー処理は適当なのでご注意を)

sample.go
package main

import (
	"context"
	"fmt"
	"path/filepath"
	"log"
	"os"
	"strings"

	"github.com/containerd/containerd"
	"github.com/containerd/containerd/namespaces"
)

func main() {
	pid := containerdPid()
	address := filepath.Join("/proc", pid, "root/run/containerd/containerd.sock")

    // containerd へ接続
	client, err := containerd.New(address)

	if err != nil {
		log.Fatal(err)
	}

	defer client.Close()

	ctx := context.Background()
    // ネームスペースの取得
	ns, err := client.NamespaceService().List(ctx)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("namespaces: %v\n", ns)

	ctx = namespaces.WithNamespace(ctx, ns[0])
    // コンテナの取得
	cs, err := client.Containers(ctx)

	if err != nil {
		log.Fatal(err)
	}

	for _, c := range cs {
		img, _ := c.Image(ctx)
		fmt.Printf("container: id=%s, image=%s\n", c.ID(), img.Name())
	}
}
// PID の取得
func containerdPid() string {
	xrDir := os.Getenv("XDG_RUNTIME_DIR")

	file := filepath.Join(xrDir, "containerd-rootless/child_pid")

	pid, _ := os.ReadFile(file)

	return strings.TrimSpace(string(pid))
}

動作確認

今回は macOS 上でビルドしますが、Linux の仮想マシン内で実行する事になるので5GOOS 環境変数に linux を設定して、Linux 向けにビルドします。

ビルド例
[macOS] % GOOS=linux go build -o sample_linux sample.go

実行すると次のようになりました。

実行例1
[macOS] % lima ./sample_linux
namespaces: [default]
container: id=75b4181874ffde1aaa956ea3762a62cffb478086b3288d3f0ae171f1fe228519, image=docker.io/library/nginx:latest
実行例2
[macOS] % lima
[lima]  $ ./sample_linux
namespaces: [default]
container: id=75b4181874ffde1aaa956ea3762a62cffb478086b3288d3f0ae171f1fe228519, image=docker.io/library/nginx:latest
  1. lima <COMMAND>limactl shell $LIMA_INSTANCE <COMMAND> の短縮版のようです

  2. nerdctl もこの API を使って containerd を操作しています

  3. 一般的な配置場所らしく、rootful モードではここに配置されるようです。Docker の場合は /var/run/docker/containerd/containerd.sock だったり、以前 snap でインストールした Docker は /var/snap/docker/current/run/docker/containerd/containerd.sock でした

  4. XDG_RUNTIME_DIR 環境変数の値は /run/user/501 でした

  5. rootless モードの場合、containerd.sock の場所が固定されていないので macOS 側から接続させるのは厳しそうな気がします

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2