LinuxKit meets AWS
本記事は、Docker Weekly #190 の Newsletter に掲載されていた下記ブログポストをベースに執筆しております。
はじめに
そもそも LinuxKit をご存知という方が現状は少ないかもしれませんので少し紹介します。 LinuxKit とは、DockerCon 2017 で発表され GitHub 上で OSS として公開されている新しい Linux ディストリビューションです。
下記の資料が日本語訳されており、読みやすかったので興味があればご一読ください。
なお、LinuxKit をさらっと紹介すると以下のとおりです。
- いくつかの企業(HPE、Intel、ARM、IBM、Microsoft)および Linux Foundation と Docker が提携し構築
- 安全でリーンでポータブルなOSの目標を達成するために、コンテナネイティブに設計されている
- コンテナネイティブで、全てのシステムサービスはコンテナ内で動作する。システム全体が immutable infrastructure
詳細については、以下を参照ください。
- Announcing LinuxKit: A Toolkit for building Secure, Lean and Portable Linux Subsystems - Docker Blog
- linuxkit/linuxkit: A toolkit for building secure, portable and lean operating systems for containers
ここまで読まれて少し興味を持って頂けたなら、AWS 上で動作する LinuxKit AMI イメージが作成出来ますので 最後までお付き合い頂けますと幸いです。
AWS EC2 上で動作する LinuxKit イメージの構築
構築手順は、以下のとおりです。
- macOS で LinuxKit をビルドする
- LinuxKit の RAW イメージを作成する
- LinuxKit RAW イメージを S3 バケットにアップロードする
- LinuxKit RAW イメージから EC2 スナップショットとしてインポートする
- EC2 スナップショットから AMI を作成する
- LinuxKit AMI の EC2 インスタンスを起動する
- SSH ログインし、動作確認
なお、筆者が利用している PC 環境は、以下のとおりです。
- MacBook Pro (Retina, 13-inch, Early 2015)
- macOS Sierra 10.12.5
順を追って、説明します。
本記事の手順を実施するにあたって、以下のツールを導入しておく必要があるとのこと。
- Docker For Mac
- git
- make
- awscli
1. macOS で LinuxKit をビルドする
まずは、linuxkit のリポジトリからソースを clone します。
$ mkdir ~/github $ cd ~/github $ git clone https://github.com/linuxkit/linuxkit $ cd linuxkit
次に LinuxKit をビルドします。
~/github/linuxkit $ make clean rm -rf bin *.log *-kernel *-cmdline *-state *.img *.iso *.gz *.qcow2 *.vhd *.vmx *.vmdk *.tar ~/github/linuxkit $ make && make install mkdir -p bin docker run --rm --log-driver=none -e GOOS=darwin linuxkit/go-compile:6579a00b44686d0e504d513fc4860094769fe7df --clone-path github.com/moby/tool --clone https://github.com/moby/tool.git --commit e0aac90f4476c7dadfec9ab4116cf1363e51ce77 --package github.com/moby/tool/cmd/moby --ldflags "-X main.GitCommit=dfb4f292d33c4a5daa725c481a415f8e7b5da9c0 -X main.Version="0.0" " -o bin/moby > tmp_moby_bin.tar Unable to find image 'linuxkit/go-compile:6579a00b44686d0e504d513fc4860094769fe7df' locally 6579a00b44686d0e504d513fc4860094769fe7df: Pulling from linuxkit/go-compile 7bee6021d73b: Pulling fs layer bc2351846d69: Pulling fs layer f445d9c10f23: Pulling fs layer f445d9c10f23: Verifying Checksum ...
ビルドが成功していることを確認します。
~/github/linuxkit $ ls -al ./bin/ total 151472 drwxr-xr-x 5 shimoda.yuji staff 170 6 16 09:25 . drwxr-xr-x 31 shimoda.yuji staff 1054 6 16 09:25 .. -rwxr-xr-x 1 shimoda.yuji staff 54679728 6 16 09:24 linuxkit -rwxr-xr-x 1 shimoda.yuji staff 12907248 6 16 09:24 moby -rwxr-xr-x 1 shimoda.yuji staff 9960064 6 16 09:25 rtf ~/github/linuxkit $ linuxkit version linuxkit version 0.0 commit: dfb4f292d33c4a5daa725c481a415f8e7b5da9c0 ~/github/linuxkit $ moby version moby version 0.0 commit: dfb4f292d33c4a5daa725c481a415f8e7b5da9c0
2. LinuxKit の RAW イメージを作成する
現状のバージョンではまず、QEMU で利用される qcow2 仮想ディスクを作成し qemu-img コマンドで RAW イメージへコンバートする必要があるようです。(いずれ、直接 RAW イメージを作成する機能が提供される予定とのこと) また、作成される LinuxKit の RAW イメージについては aws.yml ファイルに従って作成されるようです。
以下は、examples/aws.yml です。
kernel: image: "linuxkit/kernel:4.9.x" cmdline: "console=ttyS0 page_poison=1" init: - linuxkit/init:2599bcd5013ce5962aa155ee8929c26160de13bd - linuxkit/runc:3a4e6cbf15470f62501b019b55e1caac5ee7689f - linuxkit/containerd:b50181bc6e0084e5fcd6b6ad3cf433c4f66cae5a - linuxkit/ca-certificates:75cf419fb58770884c3464eb687ec8dfc704169d onboot: - name: sysctl image: "linuxkit/sysctl:3aa6bc663c2849ef239be7d941d3eaf3e6fcc018" - name: dhcpcd image: "linuxkit/dhcpcd:7d2b8aaaf20c24ad7d11a5ea2ea5b4a80dc966f1" command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] - name: metadata image: "linuxkit/metadata:31a0b0f5557c6123beaa9c33e3400ae3c03447e0" services: - name: rngd image: "linuxkit/rngd:1fa4de44c961bb5075647181891a3e7e7ba51c31" - name: sshd image: "linuxkit/sshd:abc1f5e096982ebc3fb61c506aed3ac9c2ae4d55" binds: - /var/config/ssh/authorized_keys:/root/.ssh/authorized_keys - name: nginx image: "nginx:alpine" capabilities: - CAP_NET_BIND_SERVICE - CAP_CHOWN - CAP_SETUID - CAP_SETGID - CAP_DAC_OVERRIDE trust: org: - linuxkit image: - nginx:alpine
起動するサービスとして、rngd や sshd および nginx が指定されていることが確認できます。
それでは、qcow2 イメージを作成します。
~/github/linuxkit $ moby build -output qcow2 examples/aws.yml Building LinuxKit image mkimage to generate output formats Extract kernel image: linuxkit/kernel:4.9.x Pull image: docker.io/linuxkit/kernel:4.9.x@sha256:1631c1ceea2007b308ae4efeee9179c1e7d578a5dedab54fbcc64746532f2828 Add init containers: Process init image: linuxkit/init:1b8a7e394d2ec2f1fdb4d67645829d1b5bdca037 Pull image: docker.io/linuxkit/init:1b8a7e394d2ec2f1fdb4d67645829d1b5bdca037@sha256:8e80db5f2ccd7214ccbee3ea1d2cf3b844d668676a2352c3739fa891942fe1df Process init image: linuxkit/runc:3a4e6cbf15470f62501b019b55e1caac5ee7689f ... ~/github/linuxkit $ ls -al aws.* -rw-r--r-- 1 shimoda.yuji staff 70189056 6 16 09:38 aws.qcow2
次に、qemu-img コマンドが利用出来るように Docker イメージを作成します。
~ $ mkdir -p ~/docker/qemu-img ~ $ cd ~/docker/qemu-img ~/docker/qemu-img $ cat > Dockerfile <<EOF > FROM alpine:3.6 > RUN apk add --no-cache qemu-img > ENTRYPOINT ["/usr/bin/qemu-img"] > EOF ~/docker/qemu-img $ docker build -t qemu-img . Sending build context to Docker daemon 2.048 kB Step 1/3 : FROM alpine:3.6 3.6: Pulling from library/alpine ... ~/docker/qemu-img $ alias qemu-img='docker run --rm -ti -v $(pwd):$(pwd) -w $(pwd) qemu-img'
最後に、qcow2 から raw へ変換します。
~/github/linuxkit $ qemu-img info aws.qcow2 image: aws.qcow2 file format: qcow2 virtual size: 1.0G (1073741824 bytes) disk size: 67M cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false ~/github/linuxkit $ qemu-img convert aws.qcow2 aws.raw ~/github/linuxkit $ qemu-img info aws.raw image: aws.raw file format: raw virtual size: 1.0G (1073741824 bytes) disk size: 1.0G ~/github/linuxkit $
RAW イメージが作成されました。
3. LinuxKit RAW イメージを S3 バケットにアップロードする
今回は、linuxkit バケットを作成し aws.raw イメージファイルをアップロードしました。
~/github/linuxkit $ aws s3 mb s3://linuxkit make_bucket: linuxkit ~/github/linuxkit $ aws s3 cp aws.raw s3://linuxkit upload: ./aws.raw to s3://linuxkit/aws.raw ~/github/linuxkit $ aws s3 ls s3://linuxkit/aws.raw 2017-06-16 09:51:15 1073741824 aws.raw ~/github/linuxkit $
4. LinuxKit RAW イメージから EC2 スナップショットとしてインポートする
ここでは、S3 にアップロードした RAW イメージファイルを元に、EC2 スナップショットとしてインポートします。
まずは、json ファイルを作成します。
~/github/linuxkit $ cat > container.json <<EOF > { > "Description": "LinuxKit AWS", > "Format": "raw", > "UserBucket": { > "S3Bucket": "linuxkit", > "S3Key": "aws.raw" > } > } > EOF ~/github/linuxkit $
次に、インポートします。
~/github/linuxkit $ aws ec2 import-snapshot --description "LinuxKit AWS" --disk-container file://container.json { "SnapshotTaskDetail": { "Status": "active", "Description": "LinuxKit AWS", "Format": "RAW", "DiskImageSize": 0.0, "Progress": "3", "UserBucket": { "S3Bucket": "linuxkit", "S3Key": "aws.raw" }, "StatusMessage": "pending" }, "Description": "LinuxKit AWS", "ImportTaskId": "import-snap-fgtkq76n" }
出力結果の ImportTaskId を元に、スナップショットタスクの進行状況を確認できます。
~/github/linuxkit $ aws ec2 describe-import-snapshot-tasks --import-task-ids import-snap-fgtkq76n { "ImportSnapshotTasks": [ { "SnapshotTaskDetail": { "Status": "completed", "Description": "LinuxKit AWS", "Format": "RAW", "DiskImageSize": 1073741824.0, "SnapshotId": "snap-061ecf2458d05eadf", "UserBucket": { "S3Bucket": "linuxkit", "S3Key": "aws.raw" } }, "Description": "LinuxKit AWS", "ImportTaskId": "import-snap-fgtkq76n" } ] } ~/github/linuxkit $
Status が completed になっているので、インポートは完了済みです。
もしも、import-snapshot コマンド実行時に以下のエラーが発生した場合は、以下のステップを実行して作業を進めてください。(インポートが完了している読者は、下記の内容をスキップしステップ5へ)
An error occurred (InvalidParameter) when calling the ImportSnapshot operation: The sevice role <vmimport> does not exist or does not have sufficient permissions for the service to continue
json ファイルを作成する。
~/github/linuxkit $ cat > trust-policy.json <<EOF > { > "Version": "2012-10-17", > "Statement": [ > { > "Effect": "Allow", > "Principal": { "Service": "vmie.amazonaws.com" }, > "Action": "sts:AssumeRole", > "Condition": { > "StringEquals":{ > "sts:Externalid": "vmimport" > } > } > } > ] > } > EOF
次に、IAM ロールを作成します。
~/github/linuxkit $ aws iam create-role --role-name vmimport --assume-role-policy-document file://trust-policy.json
最後に、linuxkit バケットへのアクセスを許可しておきます。
~/github/linuxkit $ cat > role-policy.json <<EOF > { > "Version": "2012-10-17", > "Statement": [ > { > "Effect": "Allow", > "Action": [ > "s3:ListBucket", > "s3:GetBucketLocation" > ], > "Resource": [ > "arn:aws:s3:::linuxkit" > ] > }, > { > "Effect": "Allow", > "Action": [ > "s3:GetObject" > ], > "Resource": [ > "arn:aws:s3:::linuxkit/*" > ] > }, > { > "Effect": "Allow", > "Action":[ > "ec2:ModifySnapshotAttribute", > "ec2:CopySnapshot", > "ec2:RegisterImage", > "ec2:Describe*" > ], > "Resource": "*" > } > ] > } > EOF ~/github/linuxkit $ aws iam put-role-policy --role-name vmimport --policy-name vmimport --policy-document file://role-policy.json
再度、インポートしてください。
~/github/linuxkit $ aws ec2 import-snapshot --description "LinuxKit AWS" --disk-container file://container.json
5. EC2 スナップショットから AMI を作成する
EC2 スナップショットから、AMI を作成します。
~/github/linuxkit $ aws ec2 register-image \ > --name 'LinuxKit AWS AMI' \ > --architecture x86_64 \ > --virtualization-type hvm \ > --root-device-name /dev/sda1 \ > --block-device-mappings \ > '[{"DeviceName": "/dev/sda1", "Ebs": {"SnapshotId": "snap-061ecf2458d05eadf", "VolumeType": "gp2"}}]' { "ImageId": "ami-1cd2da7b" } ~/github/linuxkit $
6. LinuxKit AMI の EC2 インスタンスを起動する
AMI が作成されたので、後は EC2 インスタンスを起動するだけですね。 AWS CLI およびマネジメントコンソールいずれか好みの手順でインスタンスを起動してください。
ここでは、ブログポストに従い AWS CLI でインスタンスを起動します。
まずは、TCP/22 および TCP/80 のインバウンドを許可するセキュリティグループを作成します。
~/github/linuxkit $ aws ec2 create-security-group --group-name linuxkit-sg --description "Security group for LinuxKit in EC2" { "GroupId": "sg-30d90956" } ~/github/linuxkit $ aws ec2 authorize-security-group-ingress --group-name linuxkit-sg --protocol tcp --port 22 --cidr 0.0.0.0/0 ~/github/linuxkit $ aws ec2 authorize-security-group-ingress --group-name linuxkit-sg --protocol tcp --port 80 --cidr 0.0.0.0/0
キーペアについては、既に既存のキーペアを利用するため割愛させていただきます。まだ、キーペアを作成されていない方については下記のドキュメントをご参照ください。
それでは、インスタンスを起動しましょう。
~/github/linuxkit $ aws ec2 run-instances --image-id ami-1cd2da7b --count 1 --instance-type t2.micro \ > --security-group-ids sg-30d90956 --key-name HL00213.local ...
7. SSH ログインし、動作確認
まずは、SSH でログインしてみます。ログインユーザーは、root
~/github/linuxkit $ ssh [email protected] The authenticity of host 'ec2-52-199-113-247.ap-northeast-1.compute.amazonaws.com (52.199.113.247)' can't be established. ECDSA key fingerprint is SHA256:UMf772t/jp87dwsjsHhM1cAn+Ek+aSfcNwjYFMb8v/A. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'ec2-52-199-113-247.ap-northeast-1.compute.amazonaws.com,52.199.113.247' (ECDSA) to the list of known hosts. Welcome to LinuxKit ip-172-31-29-175:~#
ログイン出来ました。 SSH ログイン後、プロセスの状況やディスクの使用状況を確認すると、以下のとおりでした。
ip-172-31-29-175:~# pstree init---010-containerd-+-containerd-+-containerd-shim---nginx---nginx | |-containerd-shim---tini---rngd | `-containerd-shim---tini---sshd---sshd---ash---pstree `-3*[ctr]v ip-172-31-29-175:~# df -m Filesystem 1M-blocks Used Available Use% Mounted on tmpfs 495 142 353 29% / tmpfs 64 0 64 0% /dev tmpfs 495 0 495 0% /sys/fs/cgroup tmpfs 495 142 353 29% /root/.ssh/authorized_keys
init プロセスから containerd プロセスが起動し、各コンテナが実行されているようです。 また、全てが tmpfs で構成されており、まさに immutable infrastructure という感じを覚えました。
コンテナの起動状況を確認するには、namespace 内に入る必要があるみたいです。
ip-172-31-29-175:~# nsenter -t 1 -m -u -n -i ash ip-172-31-29-175:/# runc list ID PID STATUS BUNDLE CREATED OWNER nginx 524 running /run/containerd/linux/nginx 2017-06-16T01:36:13.400674363Z root rngd 572 running /run/containerd/linux/rngd 2017-06-16T01:36:13.447738335Z root sshd 604 running /run/containerd/linux/sshd 2017-06-16T01:36:13.485589128Z root
すでに nginx が起動しており、ブラウザからも確認することが出来ました。
最後に
今回はじめて LinuxKit を触ることが出来ました。
LinuxKit を触り始めて1時間も経っておりませんが、 本当の意味で、コンテナを起動するためだけに作成されている Linux ディストリビューションという印象を受けました。
また、コンテナ特化型 Linux ディストリビューションとされる CoreOS(Container Linux) なども、コンテナを動作させる機能以外で 不要なものは極力入れないという印象でしたが、LinuxKit と比較すると それなりの自由度はある程度確保されているんだなという印象を、改めて感じました。
ここまで潔い Linux ディストリビューションは始めてであり、凄く興味が湧いたため もう少し LinuxKit と戯れたいと思います。
ではでは