mac環境にてmultipassとdevcontainerでcdk開発環境を作成する

記事タイトルとURLをコピーする

初めに

CS1の石井です。

突然自分語りから入ってしまうのですが、私は子供の頃からよく物を壊してしまいます。扱いが雑とか性格が大雑把すぎるので最早治しようがありません。

そのため仕事で使う自端末も物理面もソフト面もよく壊します。 特にソフト面はすぐ壊してしまうので、壊しても早く復旧できるように色々工夫しています。

例えば自端末で開発環境のセットアップはVMやコードで表現しておき、gitに上げておくこと、といったことをしています。

直近だとCDKを使った運用を行っており、CDKの開発環境が必要となるため下記のような環境整備を行っていました。

  1. vagrantでVMを作成
  2. ansibleのプラグインを用いてVMのOS内部のセットアップ(dockerの環境準備など)

ただ、最近multipassを使えばよりシンプルに開発環境を整備できることに気がついたため、本記事にてmultipassを使った準備を行なってみます。

※補足: 本記事は公式のチュートリアルをベースにdevcontainerでCDKが実行できるまでの内容を記載します。 https://multipass.run/docs/docker-tutorial

試したmacの環境:

catalina: 10.15.7

Ventura: 13.4

本記事の対象者

  • macユーザー
  • devcontainerを使ってみたい人

1. multipassをインストールする

特に難しい手順もなく、公式からpkgをダウンロードするかbrewでインストールしてください。 https://multipass.run/install

なお、multipassと言うツールはCanonical Ltd.によって開発されたオープンソースのソフトウェアツールです。このツールは、Windows, macOS, そして Linux 環境でUbuntuの仮想インスタンスを作成することができます。

2. multipassでdocker用のVMの作成と環境整備

これも難しく考えることなく、下記コマンドを実行するだけです。

multipass launch docker --name docker-vm

$ multipass launch docker --name docker-vm
You'll need to add this to your shell configuration (.bashrc, .zshrc or so) for
aliases to work without prefixing with `multipass`:

PATH="$PATH:/Users/ishiinobuaki/Library/Application Support/multipass/bin"
Mounted '/Users/ishiinobuaki/multipass/docker-vm' into 'docker-vm:docker-vm'
Launched: docker-vm

launchで指定する引数にimage_nameを指定するようですが、dockerと指定すれば諸々dockerの環境準備が整ったイメージで起動してくれるみたいです。

This command will create a virtual machine running the latest version of Ubuntu, with Docker and Portainer installed. We can now use Docker already! Try the command below to see for yourself!

引用元:https://multipass.run/docs/docker-tutorial

また、macでdockerコマンドを使えるようにaliasを指定します。 ※お好みのシェルのプロファイルに保存してください。

$ alias docker="multipass exec docker-vm -- docker"
$ docker ps
CONTAINER ID   IMAGE                    COMMAND        CREATED          STATUS          PORTS                                                           NAMES

次に自身のホームディレクトリをdocker-vmにマウントします。

multipass mount ~ docker-vm

最後にmultipass shellコマンドでvmに接続してマウントできているか確認します。

$ multipass shell docker-vm
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-83-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Mon Sep 18 10:24:53 JST 2023

  System load:  0.0146484375      Processes:                127
  Usage of /:   7.1% of 38.58GB   Users logged in:          0
  Memory usage: 11%               IPv4 address for docker0: 172.17.0.1
  Swap usage:   0%                IPv4 address for ens3:    192.168.64.3


Expanded Security Maintenance for Applications is not enabled.

12 updates can be applied immediately.
1 of these updates is a standard security update.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


Last login: Mon Sep 18 10:22:52 2023 from 192.168.64.1
ubuntu@docker-vm:~$ ls /Users/ishiinobuaki/
Applications  Documents  Library  Music     Public
Desktop       Downloads  Movies   Pictures  multipass
ubuntu@docker-vm:~$ 

/Users配下にちゃんとマウントできているようです。

3. vscodeでdocker-vmに接続する

vmへはssh接続するためには鍵の登録が必要となります。

もちろん自身が生成した鍵をvmのubuntu内のauthorized_keysに登録してもらっても問題ないのですが、せっかくなのでデフォルトで生成した鍵を使ってみようと思います。

multipassは自身の端末の/var/root/Library/Application Support/multipassd/ssh-keysと言うディレクトリに「id_rsa」と言う名前で秘密鍵を生成します。起動したvmのauthorized_keysには対応する公開鍵を自動的に登録します。

この鍵を使ってVMに対して接続を行います。

まずは鍵をユーザーの.sshにコピーします。

sudo cp /var/root/Library/Application\ Support/multipassd/ssh-keys/id_rsa ~/.ssh/vm_key
sudo chown $(whoami) ~/.ssh/vm_key

次に対象のvmのIPを調べます。調べ方もmultipass listを実行します。

% multipass list
Name                    State             IPv4             Image
docker-vm                  Running           192.168.64.XX    Ubuntu 22.04 LTS
                                          172.17.0.1

※上記の192.168.64.XXが接続先のIPとなります。

次にsshのconfigを下記のように編集します。

% cat ~/.ssh/config
Host docker-vm
  HostName 192.168.64.XX
  IdentityFile ~/.ssh/vm_key
  User ubuntu

編集が完了したらVScodeを起動してSSHの拡張機能の画面から対象のVMに接続してみます。

正常に接続できました。

4. VM内でdevcontainerを作成する

VMに接続したのでdevcontainerを使いnodejsとtypescriptを実行できるコンテナを用意します。

docker-vmにSSH接続した状態でVScodeのコマンドパレットを開き 「New Dev Container」を入力してください。

※コマンドパレットに上記の入力候補が出なければVSCodeの「Dev Containers」の拡張をインストールしてください。

コンテナのイメージ選択でtypescriptを選択

起動するコンテナに必要なソフトを事前に定義したいので、「その他のオプションを選択」

「20-bullseye(既定)」を選択

aws cli、git、docker in docker、cdkをそれぞれインストールするため、チェックを入れる。

再度に@devcontainers-contribを信頼する を選択しコンテナのビルドが実行

しばらくするとコンテナの起動が完了してVSCodeの画面が切り替わります。

これでdocker_vm内にCDKを実行できる環境が整いました。

5. 実際にCDKを作成してsynthしてみる

早速cdk initでプロジェクトを作成してみます。

cdk init --language typescript

作成されたプロジェクトlambdaを作成する下記のCDKサンプルを記載してみます。

lib/typescript-node-stack.ts

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";

export class TypescriptNodeStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const sampleLambda = new NodejsFunction(this, `SampleLambda`, {
      entry: "lib/sample-lambda.handler.ts",
    });

  }
}

lib/sample-lambda.handler.ts

import { APIGatewayProxyHandler } from 'aws-lambda';

export const handler: APIGatewayProxyHandler = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Hello, world!',
    }),
  };
};

記載が完了したらnpx cdk synthを実行してsynthできることを確認します。

node ➜ /workspaces/typescript-node (master) $ npx cdk synth
[+] Building 180.7s (14/14) FINISHED                                                                 docker:default
 => [internal] load .dockerignore                                                                              0.4s
 => => transferring context: 2B                                                                                0.0s
 => [internal] load build definition from Dockerfile                                                           0.4s
〜中略〜
Rules:
  CheckBootstrapVersion:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::Contains:
                - - "1"
                  - "2"
                  - "3"
                  - "4"
                  - "5"
                - Ref: BootstrapVersion
        AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.


node ➜ /workspaces/typescript-node (master) $ 

6. devcontainerにgitのconfigや鍵をマウントする

ここからは単なる補足ですが、docker_vmにホストマシンにある鍵やgit情報などを共有したかったり、devcontainerのfeaturesにないソフトをインストールしておきたい場合はdevcontainerの記載を少し編集する必要があります。

仮に以下の要件で使いたいとします。

  • pipが使用可能であること
  • GRCが使用可能であること
  • aws-lambdaがnpmでインストールされること

この要件を満たすようにdevcontainer.jsonを編集すると下記のようなjsonになります。 ※/Usersの次のディレクトリ名は適宜読み替え

{
    "name": "Node.js & TypeScript",
    // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
    "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
    "features": {
        "ghcr.io/devcontainers/features/aws-cli:1": {},
        "ghcr.io/devcontainers/features/docker-in-docker:2": {},
        "ghcr.io/devcontainers/features/git:1": {},
        "ghcr.io/devcontainers-contrib/features/aws-cdk:2": {},
        "ghcr.io/devcontainers/features/python:1": {
            "installTools": true,
            "version": "3.11"
        },
    },
    "mounts": [
        "source=/Users/ishiinobuaki/,target=/home/node/,type=bind,consistency=cached",
    ],
    "postStartCommand": "pip install git-remote-codecommit && npm i --save-dev @types/aws-lambda",
}

上記のファイルを保存した後、コマンドパレットにてrebuildと入力し、コンテナを再度ビルドし直します。

上記jsonではvmの/Users/ishiinobuaki/のディレクトリをマウントしていますが、実態としてはホストマシンからvmにマウントしたホームディレクトリをさらにコンテナの/home/nodeにマウントしています。 これでホストマシンの.awsやgitconfigの内容を使えるようになりました。

またpostStartCommandにてpipでgrcをインストールしているため、最初からgrcが使える環境が起動できます。

7.まとめ

devcontainerはCDK以外にも色々な場面で使えることと、実行環境や拡張機能をプロジェクトで統一できることが最大のメリットです。

これでAさんの環境では動いたけど、Bさんの環境では動かなかった、という事故は減らせるはずです。

※devcontainerは前々から採用したいと思っていたのですが、macだとコンテナは挙動が遅い + 権限系のエラーが頻発するため棚上げになっていました。

ただ、前にjaws-ug CDK支部の方と会話している中でdevcontainerを普通に活用できるところを見せてもらいました。

その日から再度devcontainer熱が出てきたので再度検証してみたら意外とあっさり行きました。何事もやってみる物だなぁと思いました。