Kabanero を使ったクラウド・ネイティブなアプリ開発(VSCode + Codewind)を体験 - その後


2020年7月8日に実施された 初夏のIBM Dojo #9 Kabaneroを使ったクラウド・ネイティブなアプリ開発を体験 ワークショップに参加してきました。講師 @osonoi さんです。

以前に翻訳した Kabanero と、それと関連する Developer Experience にある「VS Code を使用して Kabanero を試す」を体験できるオンラインセッションでした。セッション資料 がわかりやすいので、ぜひ参照してください。

VSCode + codewind の環境、とっても手軽で便利。監視、ビルド、実行など全て Docker 環境で実行され、処理系のインストール無しでさくさく試せる。いろんな言語/環境を試してみたい。

と、Twitter で呟きましたが、すごく参考になったので、ちょっと自分でも中身を確認してみました。自分なりに迷いつつのメモなので、間違えていたり、意味不明だったらスミマセン。

お手軽に開発環境をセットアップ

セッション資料に従い、アプリケーション開発環境をセットアップしてみます。前提となるソフトウェアは以下の2つだけ。

  • Docker : 仮想マシン実行環境
  • VSCode : テキストエディタ

VSCode の 拡張機能 (Extension) で Codewind を探してインストールします。

新規プロジェクト作成で、Kabanero リポジトリにある Kabanero Node.js Express scaffold template を選択します。

後は開発環境などが自動でセットアップされます。ビルド環境も実行環境も Docker コンテナ化されているため、Node.js など開発に必要なツールがインストールされていなくても問題ありません。今回のセットアップも Docker 上にコンテナが追加されるだけで、ローカルにインストールされないため、気軽に試すことができます。

アプリを起動すると Node.js Express が動作し、シンプルな Webページが表示されます。

さあ、後はサンプルコードを修正して、いろいろ試すだけ。ソースコードに修正を保存すれば、ビルドが実施され、すぐにWebページに反映されます。

以上、ここまでの手順の詳細は IBM Dojo の セッション資料 をご参照ください。IBM Developer Dojo のどれかに参加すると、Dojo サポート用の Slack チャネルに招待されるので、そこで質問もできます。

生成された環境を眺めてみる

さてこのまま、環境はブラックボックスとして、Node.js + Express のアプリ開発を開始してもいいのですが… せっかくですから、自動生成された環境を少し眺めてみましょう。

VSCode のワークスペース

まず VSCode のワークスペースを見てみると、以下のような構成になっています。

ページを表示しているのは routes/index.js ですね。

表示に利用されている Pug 形式のテンプレート views/index.pug は以下のように非常にシンプルでした。

これらなのですが、探してみると GitHub の appsody アカウントにある stacks リポジトリ 配下にある /incubator/nodejs-express/templates/scaffold フォルダが元になっているようです。

Docker で動作するコンテナについて

さて、上記の Web ページを表示中、Docker は以下のように3つのコンテナを実行していました。

最初のコンテナ (イメージは kabanero/nodejs-express:0.4) がアプリを実行している環境のようですので、コンテナに sh アクセスして、動作しているプロセスの状態を見てみます。

自身の /bin/sh と ps コマンド以外のプロセスは以下のような感じ。

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
default      1  1.8  0.3 105308  7492 pts/0    Ssl+ 09:43   7:34 /.appsody/appsody-controller --mode=run
default     42  0.0  2.2 743756 45576 ?        Ssl  09:43   0:00 npm
default     58  0.2  4.2 1268880 86480 ?       Sl   09:43   1:08 node server.js

ついでに真ん中のコンテナ(イメージは eclipse/codewind-performance-amd64:0.13.0) のプロセスはこちらで、ちょっと何やっているか不明なのですが、実行しているイメージ名から Codewind 本体のような気がします。アプリのビルド前から居ましたし。

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000         1  0.0  1.5 741756 30936 ?        Ssl  07:20   0:00 npm
1000        16  0.0  2.5 680096 52572 ?        Sl   07:20   0:00 node server.js

三番目のコンテナ (イメージは eclipse/codewind-pfe-amd64:0.13.0) のプロセスはこちらで、こちらはソースコードの更新をチェックしたり、最初のコンテナを起動してたり、いろいろ働いているようです。

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  11892  2652 ?        Ss   07:20   0:00 sh -c  /file-watcher/scripts/root-watcher.sh ${HOST_WORKSPACE_DIRECTORY} ${CONTAINER_WORKSPACE_DIRECTORY}
root        33  0.0  2.1 676736 43460 ?        Sl   07:20   0:00 npm
root        61  0.0  0.1  11896  2916 ?        S    07:20   0:00 /bin/bash ./npm-start.sh
root        62  0.0  2.1 676368 44708 ?        Sl   07:20   0:00 npm
root        73  0.0  5.6 972028 115352 ?       Sl   07:20   0:16 node server.js
root       535  0.0  0.1  11896  2760 ?        S    09:43   0:00 /bin/bash /codewind-workspace/.extensions/codewind-appsody-extension/appsody run --name cw-dojoyamacha
root       536  0.0  0.0  23032  1408 ?        S    09:43   0:00 /usr/bin/coreutils --coreutils-prog-shebang=tee /usr/bin/tee -a /codewind-workspace/.logs/dojo-yamacha
root       542  0.0  0.5 116632 11840 ?        Sl   09:43   0:00 /codewind-workspace/.extensions/codewind-appsody-extension/bin/appsody run --name cw-dojoyamachan-df39
root       603  0.0  1.4  45688 29636 ?        Sl   09:43   0:00 docker run --rm -P --name cw-dojoyamachan-df39b4d0-c0eb-11ea-97e7-d775ccf52e96 --network codewind_netw
root       620  0.0  0.0  23032  1320 ?        S    16:11   0:00 /usr/bin/coreutils --coreutils-prog-shebang=tail /usr/bin/tail -q -F -c +0 /codewind-workspace/.logs/d

三番目のコンテナ内の環境変数に、幾つか興味深い値がありましたので、転記します。

CODEWIND_VERSION=0.13.0
CONTAINER_WORKSPACE_DIRECTORY=/codewind-workspace
ENABLE_CODE_COVERAGE=false
HELM_HOME=/root/.helm
HOSTNAME=72958642fbc6
HOST_HOME=C:\Users\z
HOST_MAVEN_OPTS=
HOST_OS=windows
HOST_WORKSPACE_DIRECTORY=C:\codewind-data
IMAGE_BUILD_TIME=20200612-133352
JAVA_HOME=/opt/java/jre
LOG_LEVEL=info
NODE_ENV=production
PATH=/opt/java/jre/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PERFORMANCE_CONTAINER=codewind-performance-amd64:0.13.0

そして気がついていませんでしたが、ローカルPCに C:\codewind-data なんてフォルダが生成されていました… 作業ディレクトリとほぼ同じ構成ですが、.vscode フォルダが無いかわりに空の node_modules フォルダが存在します。

試しに c:\ ドライブ全体を対象に appsody でファイル名検索した結果がこちら。c:\work\codewind\dojo-yamachan がプロジェクト作成時に自身で指定した作業フォルダです。

ふーむ、なかなか興味深いですね。

実際の起動プロセス

さて、作成したアプリがどう起動されるか追ってみましょう。実行用コンテナで node server.js とあるので、sh でコンテナの中に入り、server.js ファイルの中でそれっぽい部分を探してみます。

// Register the user's app.
const basePath = __dirname + '/user-app/';

function getEntryPoint() {
    let rawPackage = fs.readFileSync(basePath + 'package.json');
    let package = JSON.parse(rawPackage);
    if (!package.main) {
        console.error("Please define a primary entrypoint of your application by adding 'main: <entrypoint>' to package.json.")
        process.exit(1)
    }
    return package.main;
}

const userApp = require(basePath + getEntryPoint());
app.use('/', userApp({
  server: server,
  app: app,
  log: pino,
}));

まずわかるのが、作成した自身のアプリが /project/user-app/ ディレクトリに配置されているということです。

そしてこのディレクトリですが、Docker ランタイムにより、さきほど発見したローカルPCの C:\codewind-data 配下のフォルダがマウントされ、永続化されていることがわかります。

たぶんですが、作業フォルダの内容をビルドした結果 (今回は Node.js で webpack など前処理もないので単にファイルコピーのみ?) がこのローカルPC上のフォルダに配置され、それを実行環境の Docker コンテナの /project/user-app/ ディレクトリにマウントすることで、即時反映できている、という仕組みのようです。

で、さきほどの server.js ファイルのコードにある getEntryPoint() 関数の中を見ると、package.json のなかの main エントリがアプリの本体を指定しているようで、今回だと app.js が指定されています。

package.json
  "main": "app.js",

そしてこの app.js を見ると、これが Express アプリの本体で、これでようやく最初に出てきた routes/index.jsviews/index.pug ファイルに繋がります。

app.js
module.exports = (/*options*/) => {
  // Use options.server to access http.Server. Example of socket.io:
  //     const io = require('socket.io')(options.server)
  const app = require('express')()

  app.set('views', __dirname + "/views");
  app.set('view engine', 'pug');

  app.use('/', require('./routes'));

  return app;
};

起動順としては、以下のような感じですかね。

  1. Appsody が用意した実行用コンテナの /project/user-app にローカルPCの c\codewind-data 配下のプロジェクト用フォルダがマウントされる 【Appsody共通】
  2. Appsody が用意した実行用コンテナ内の /project/server.js/project/user-app/package.json ファイルの main エントリを参照する【Appsody Node.js 系共通】
  3. main エントリに指定された /project/user-app/app.js が実行される【Appsody Node.js Express 系共通?】
  4. app.js によって Pug テンプレートエンジンがセットされ、views/index.pug をテンプレートとして route/index.js が表示される。【今回の Stack 固有】

うん、これでやっとスッキリしました。

というわけで

Appsody (VSCode + Codewind) で作成した Node.js + Express サンプルアプリの起動の仕組みをざっくり調べてみました。なかなか良く出来た仕組みだなー、と感心してみたり。

ただ、時間の関係もあり、ソースコードの変更をウォッチしているところ、ビルドしているところ、などはまだ調べていません。また時間を作って、コンパイルする Java などの環境(変化がわかりやすいので)を対象に調べてみたいな、などと思っています。なにかわかったら、メモ公開するかもしれません。

それではまた!