はじめに

ソフトウェアでいうコンテナは、元は運輸業界でいうコンテナに由来しているわけですが、どちらのコンテナも類似するメリットを備えています。

コンテナ(運輸) コンテナ(ソフトウェア)
船や列車に乗せられる Windows/Mac/Linux等で動かせる
荷物をまとめて積載できる 必要なコード、ライブラリ、データをまとめて配布できる
輸送効率がアップする 開発・ビルド・テスト・デプロイ効率がアップする

これらのメリットをソフトウェア開発において享受するためのステップとして、

  1. 開発環境を共通化するためのCircleCIによるDockerイメージの作成
  2. Dockerコンテナ上での開発(本ブログ)
  3. 実行環境を共通化するためのCircleCIによるDockerイメージの作成

をご紹介していきます。

なお、本ブログは2020年9月に開催した「CircleCIを学ぼうシリーズ 第1弾 カスタムDockerイメージの作成」での内容を基にしています。

Dockerコンテナ上での開発: 1. Webアプリ開発用リポジトリの作成とコンテナの起動

はじめに、Webアプリ開発用のリポジトリをGitHub上に作成します。筆者はCCI-App-Nodeというリポジトリを作成しました。

次に、作成した空のWebアプリ開発用リポジトリをローカル環境にcloneして、clone先のローカルのディレクトリにDockerfileファイルを作成します。Dockerfileファイルには、次の1行だけを記述します。

FROM mfunaki/cci-img-node

ここでのポイントは、FROMの後に前回構築したDockerイメージ cci-img-node をWebアプリ開発用環境として指定していることです。

Dockerfileが用意できたら、この cci-img-node イメージからコンテナを起動します。通常、コンテナイメージのダウンロード(docker pull)やコンテナの作成(docker run)は docker コマンドを使って行いますが、ここでは、Visual Studio Code とプラグインを使い、コンテナ内で開発していることをあまり意識しない(ローカル環境での開発と同様の)やり方で開発を進めていきます。

Visual Studio Code をインストールしたら、Remote-Containers 拡張機能をインストールします。

Remote containers

拡張機能をインストールしたら、左下のアイコン(><)をクリックし、表示されるメニューから Reopen in Container をクリックします。

Reopen in container

次に From 'Dockerfile' を選択することで、先ほど作成した Dockerfile を元にコンテナイメージのダウンロード、コンテナ作成、およびコンテナ内の環境表示まで行われます(その際、環境設定ファイル .devcontainer/devcontainer.json が自動生成されています)。一見すると何も変わっていないように見える Visual Studio Code の画面ですが、下図の中でオレンジ色の四角で囲っているように、表示されているファイルやコンソールはすべてコンテナ環境です。

コンテナ環境

Dockerコンテナ上での開発: 2. コンテナ内での開発

それでは、実際にコードを書いていきましょう。

Node.jsのアプリケーションを開発するのに必要なベースは、cci-img-nodeイメージ作成時に/node-appフォルダに格納しておいたので、右下のターミナルウィンドウ(Webアプリ開発用環境です)で次のコマンドを実行して、ルートディレクトリにコピーします。

cp -r /node-app .

次に、app.jsファイルを作成し、Hello Worldと出力するだけのシンプルなコードを入力します。

'use strict';

const express = require('express');

// Constants
const PORT = 8080;
const HOST = '0.0.0.0';

// App
const app = express();
app.get('/', (req, res) => {
  res.send('Hello World\n');
});

// For testing from outside
module.exports = app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

次に、testディレクトリを作成し、Webアプリからの通知が正しいかどうか(ここでは、GETメソッドでルート/ディレクトリにアクセスしたら、ステータスコードが200(成功)で、正しく 'Hello World¥n' と出力されているかどうかを確認するためのテストコード app.test.js を記述します。

const request = require('supertest');

const app = require('../app');

describe('cci-app-node', () => {

    describe('#GET /', () => {
        it('Should return Hello World response', (done) => {
            request(app)
                .get('/')
                .expect(200)    // Status
                .expect('Hello World\n')
                .end(done);
        })
    })
})

では、開発したアプリケーションをテストするための定義、および起動するための定義を package.json ファイル中に付け加えます。

 "scripts": {
    "start": "node app.js",
    "test": "jest"
  },

ここまで準備できたら、右下のターミナル画面で npm run test と入力することで、jestを使ったテストを実行することができます。

npm run test

テスト結果に問題がなければ、npm run startと入力することで、Node.jsによるWebアプリが起動します。

npm run start

画面出力にもあるとおり、ブラウザを使ってhttp://0.0.0.0:8080にアクセスすることで、サーバからの Hello World¥n メッセージを受け取ることができます。ここでは、テスト対象のWebアプリも、Webアプリにアクセスしているブラウザ(curl)もコンテナ上で動作しています。

curlでのアクセス

Dockerコンテナ上での開発: 3. GitHub上のリポジトリを更新

ここまで問題なく完了したら、GitHub上のWebアプリ開発用リポジトリを更新(add → commit → push)しておきましょう。

と、さらっと書いてしまいましたが、よく考えると、リポジトリからpullしたときはローカル環境だったのに、コンテナ環境でリポジトリの更新をかけているというところに違和感を感じられなかったでしょうか? Remote-Containers拡張機能が、ローカル環境なのかコンテナ環境なのかを意識することなくGitHubリポジトリにアクセスする際にプロキシとして動作してくれています。

GitHub上でご自身のリポジトリに先ほどまでの開発成果が収められているか、確認しておきましょう。

GitHub

さいごに

Webアプリケーションの開発、およびテストまでが完了しました。最終回となるその3では、デプロイ用のコンテナイメージを CircleCI を使って自動的に作成し、Docker Hubに登録する方法を紹介していきます。