WESEEK Tech Blog

WESEEK のエンジニアブログです

JavaScript/Node.jsエンジニア必見!MongoDB+Mongoose利用時のデータマイグレーション

こちらは 「JavaScript/node.jsエンジニア必見!MongoDB+Mongoose利用時のデータマイグレーション」からの転載です。


古くは「LAMP」に代表されるような技術スタック、JavaScript 界隈では「MEAN」スタックという用語もあるくらい、JavaScript(Node.js) との組み合わせでは MongoDB がよく使われるようです。

NoSQL ではスキーマレスのためデータマイグレーションを気にせず開発することも多いわけですが、例えば一度バージョン1をリリースして既にユーザーがついているシステムを更改するような場合、後方互換を保つためにもデータマイグレーション機構は重要です。

本記事では、Node.js 環境で MongoDB および Mongoose を利用しているようなシステムにデータマイグレーション機構を導入する Tips を紹介します。

データマイグレーション機構とは?

有名処で言えば、Ruby on RailsActiveRecord に搭載されている機能を想像するとよいでしょう。そこには以下のような特徴があります。

  • データベーススキーマを時系列に進化(またはロールバック)させることができる
    • Rails では rake タスクとして実行できる
  • migration ファイルを決まったスキームで実装できる
  • ActiveRecord のモデルを利用することができる

Node.js の世界でのデータマイグレーション事情

Ruby の世界では Rails, ActiveRecord というデファクトスタンダードが確立されていますが、Node.js の世界でのデファクトフレームワークは残念ながら Express であり、オールインワンのフレームワークでもフルスタックのフレームワークでもありません。

ORM/ODM にしても、ORM/ODM のデファクトは、RDBMS なら Sequelize、MongoDB なら Mongoose で固まりつつありますが、それぞれデータマイグレーション機構を備えているわけではなく、データマイグレーション機構が欲しい場合に「すぐ使える」状況ではないのが実情です。

そのため、ライブラリ選定から進める必要があります。

ライブラリ選定

npm でそれっぽいものを検索したところ、以下を見つけました。

npm パッケージ ライセンス npm weekly downloads
2018.10 時点
GitHub stars 直近1ヶ月でのコミット数
2018.10 時点
migrate-mongoose MIT 1405 86 0
mongoose-migrate (none) 506 63 0
migrate-mongo MIT 3515 97 25
db-migrate MIT 28399 1480 0
db-migrate-mongodb MIT 2604 17 2

weekly downloads と GitHub stars を見ると db-migrate がダントツなのですが、こちらは db-migrate-mongodb の親プロジェクトで、データベースの差異を吸収するような抽象化がなされています(子プロジェクトとして、MySQL 版や PostgreSQL 版が存在する)。

こと db-migrate-mongodb のみの数字で言えば、migrate-mongo が若干優勢といった感じです。今回は、直近コミット数で現在も開発が進んでいると見受けられる migrate-mongo を採用することにします。

Let's Try ~npm script の準備~

Rails の rake タスクのように使えるとなにかと便利なので、まず npm script を準備しましょう。

    "migrate": "npm run migrate:up",
    "migrate:create": "migrate-mongo create -f config/migrate.js -- ",
    "migrate:status": "migrate-mongo status -f config/migrate.js",
    "migrate:up": "migrate-mongo up -f config/migrate.js",
    "migrate:down": "migrate-mongo down -f config/migrate.js",

-f オプションで設定しているのはコンフィグファイルです。MongoDB への接続設定等が入ると思います。環境変数から接続先を取りたい場合は以下のようなファイルにするとよいでしょう。

/**
 * Configuration file for migrate-mongo
 * @see https://github.com/seppevs/migrate-mongo
 *
 * @author Yuki Takei <yuki@weseek.co.jp>
 */

function getMongoUri(env) {
  return env.MONGODB_URI ||
    env.MONGO_URI ||
    ((env.NODE_ENV === 'test') ? 'mongodb://localhost/myprj_test' : 'mongodb://localhost/myprj');
}

const mongoUri = getMongoUri(process.env);
const match = mongoUri.match(/^(.+)\/([^/]+)$/);
module.exports = {
  mongoUri,
  mongodb: {
    url: match[1],
    databaseName: match[2],
    options: {
      useNewUrlParser: true, // removes a deprecation warning when connecting
    },
  }
};

Let's Try ~初めての migration ファイル作成~

npm script を叩きましょう。

npm run migrate:create my-first-migration

Let's Try ~migration の実装1~

まずは Mongoose 対応の migration ファイルの書き方です。

my-first-migration.js

'use strict';

require('module-alias/register');
const logger = require('@alias/logger')('growi:migrate:my-first-migration');

const mongoose = require('mongoose');
const config = require('@root/config/migrate');

module.exports = {

  async up(db) {
    logger.info('Apply migration');
    mongoose.connect(config.mongoUri, config.mongodb.options);

    const User = require('@server/models/user');

    ...

    logger.info('Migration has successfully applied');
  },

  async down(db) {
    logger.info('Undo migration');
    mongoose.connect(config.mongoUri, config.mongodb.options);

    const User = require('@server/models/user');

    ...

    logger.info('Migration has successfully undoed');
  }

};

@alias@root@server 等は、module-alias パッケージで設定したエイリアスパスです。ご自身の環境に合わせて読み替えてください。

コード解説

up function が migration の適用のためのコード、 down function がロールバックのためのコードになります。それぞれ ES6 で記述可能な async function として定義しています。

User インスタンスは、 mongoose.model() で作成されるモデルインスタンスという想定です。

それぞれの function の2行目の mongoose.connect() を呼ばなければ Mongoose のモデルインスタンスのメソッドを呼んでも MongoDB にアクセスされませんので注意してください。

Let's Try ~migration の実装2~

次に、 MongoDB Driver を直に利用するような Example を紹介します。例えば既存の Mongoose モデルを廃棄するような場合です。

abolish-myrelations.js

'use strict';

require('module-alias/register');
const logger = require('@alias/logger')('growi:migrate:abolish-myrelations');

const mongoose = require('mongoose');
const config = require('@root/config/migrate');

/**
 * BEFORE
 *   - 'myrelations' collection exists (related to models/myrelations.js)
 *     - schema:
 *       {
 *         "_id" : ObjectId("5bc9de4d745e137e0424ed89"),
 *         "group" : ObjectId("5b028f13c1f7ba2e58d2fd21"),
 *         "user" : ObjectId("5b07e6e6929bad5d3cce9995"),
 *         "__v" : 0
 *       }
 * AFTER
 *   - 'myrelations' collection is dropped and models/myrelations.js is removed
 */
module.exports = {

  async up(db) {
    logger.info('Apply migration');
    mongoose.connect(config.mongoUri, config.mongodb.options);

    const User = require('@server/models/user')();
    const UserGroup = require('@server/models/user-group')();

    // retrieve all documents from 'myrelations'
    const relations = await db.collection('myrelations').find().toArray();

    for (let relation of relations) {
      const user = await User.findOne({ _id: relation.user });
      const group = await UserGroup.findOne({ _id: relation.group });

      ...

      await user.save();
      await group.save();
    }

    // drop collection
    await db.collection('myrelations').drop();

    logger.info('Migration has successfully applied');
  },

  async down(db) {
    logger.info('Undo migration');
    mongoose.connect(config.mongoUri, config.mongodb.options);

    const User = require('@server/models/user')();
    const UserGroup = require('@server/models/user-group')();

    const insertDocs = ...;
    ...

    await db.collection('myrelations').insertMany(insertDocs);

    logger.info('Migration has successfully undoed');
  }

};

コード解説

上記コードでは、 myrelations という関連テーブル風のコレクションで管理していた情報を破棄する、というシナリオを想定しました。myrelations コレクションは当初は Mongoose によって管理されていたモデルに対応する形で生成されたはずですが、モデルを廃止する場合は当然モデルクラス・モジュールも削除されますので、スキーマファイルを require してアクセスすることができません。そのため、Raw な MongoDB Driver API を用いてデータマイグレーションを行う必要があります。

まとめ

まだまだ発展途上の JavaScript、Node.js の世界では、MongoDB を利用しながらデータマイグレーション機構をプロジェクトに導入できるデファクトスタンダードがまだありません。しかしながらコミュニティは精力的に npm ライブラリを開発・追加し続けていますので、今回のように慎重に選定を行い、時にはいくつかの実装を実際に試しながら導入することで、他の枯れた言語・エコシステムに劣らないプロジェクト構成を実現できます。

今回の Tips が、JavaScript と MongoDB を好むプロジェクトたちの一助となれば幸いです。

Dev in Container on WSL2 でボリュームのパフォーマンスを比べてみる

f:id:yukitakei:20200613040453j:plain

こんにちは。武井です。

2020年5月末に待望の Windows 10 May 2020 Update が公開され、WESEEK, Inc. で開発中の GROWI でも WSL2 と Docker Desktop を利用して Dev in Container を実現した新しい開発スタートアップ を採用しました。

本日のエントリーでは、その開発時に利用するストレージ(Docker volume)の種類によってパフォーマンスがどれくらい変わるのかを実験した際の結果を紹介します。

続きを読む

Raspberry Piと温度・湿度・気圧センサーと天気予報APIでペット環境を見守る

こんにちは、 takayuki です。

最近、知り合いのインコ部屋にRaspberry Piと温度・湿度・気圧センサーを設置し、天気予報APIも使用して、お留守番中の環境を確認できるようにしました。

動機

インコはもともと暖かく湿気のある地域に生息しており、寒さに弱い生き物です。
日中に家を空けていると、インコ部屋の温度・湿度が保たれているか、心配になります。
以前、部屋の温度を数日低くしてしまったことで、インコが体調を崩してしまったことがありました。

現在は、家電メーカーから発売されているペットカメラを設置していますが、これは微妙にかゆいところに手が届かないと感じていました。
ペットカメラには、標準カメラ、赤外線カメラ、温度センサー、動作センサー、音センサーが搭載されています。

標準カメラ、赤外線カメラは、外出先でも様子が見られるため、非常に便利です。
温度センサーは、ペットカメラの画角の関係で距離を取る必要があり、インコ部屋の外に置いています ※。
そのため、計測される温度は人がいるところの室温で、インコが過ごしているケージ内の室温はわかりません。また、温度センサーしかないため、湿度はわかりません。

※インコ部屋はアクリルケージで囲い、ペット用のヒーターで暖めています。

そこで今回、温度・湿度・気圧センサーをインコ部屋の中に設置し、環境を計測できるようにしました。

構成

今回構築した構成は下記の通りです。

f:id:Aqutam:20200303020524p:plain

ハードウェア

# 品名 数量
1 Raspberry Pi 4 Mlodel B 1
2 OKdoブラック2ピース構造スライドケース 1
3 TOSHIBA microSDHC EXCERIA 16GB 100MB/s 1
4 BME280 温湿度・気圧センサ 2
5 タカチ SWプラケース SW-50B 2
6 6極4芯モジュラーケーブル 2m 3
7 モジュラーローゼット MJ-4S-MG 1
8 モジュラーソケット6極4芯 TM5RJ3-64 3

ソフトウェア

ネットワーク

  • インコの家は、 LTE を経由してインターネットに接続しています。
  • インコの家と自宅は相互に通信できるよう、 IPsec で拠点間接続しています。

センサーの製作

温度・湿度・気圧センサーは、秋月電子通商で販売されている BME280 を使用しています。このセンサーは、I2Cで制御できます。

Raspberry Pi と BME280 を下記のように配線していきます。

f:id:Aqutam:20200309234805p:plain

BME280は、ジャンパを設定することで、 0x760x77 の2つのI2Cアドレスを使用できます。
今回、2つセンサーを接続するため、それぞれのアドレスになるようにジャンパをはんだ付けします。

I2Cは、電源に2本、信号に2本 (SCL, SDA) の合計4本の配線が必要になります。
Raspberry PiとセンサーをつなぐI2Cバスは、容易に取り回しができるようにしたいと考え、4芯モジュラーケーブルを採用しました。
モジュラーケーブルは家電量販店で調達できるものなので、最適な長さのものに交換して、センサーの位置を自由に変更できます。

タカチ SWプラケースを削り、センサーと2つのモジュラージャックを収めました。
2つのモジュラージャックは導通しているため、他のセンサーをデイジーチェーンできるようにしています。
写真では穴が空いていませんが、温度・湿度・気圧を計測できるよう、ケースの前面には通気孔を空けています。

f:id:Aqutam:20200302232643j:plain f:id:Aqutam:20200302232624j:plain

Raspberry Pi からはI2Cで使用する4本の線を引き回し、モジュラーローゼットを配置しています。

f:id:Aqutam:20200302232435j:plain f:id:Aqutam:20200302232512j:plain

設置

下記の写真のように、インコ部屋に温度センサーを設置しています。
手作業で穴を空けたため、ガタガタになってしまっています。

f:id:Aqutam:20200302232558j:plain f:id:Aqutam:20200302232609j:plain

実装

センサーからのデータ取得

センサーからのデータの取得は、こちらのコードを参考にさせていただきました。 samplecodes/BME280 at master · SWITCHSCIENCE/samplecodes · GitHub

こちらのコードをベースに、下記CSVを出力するように実装します。

Date,GR Temp,GR Press,GR Hum,OR Temp,OR Press,OR Hum
2020-03-01 17:00:00,23.94,1004.81,53.46,21.53,1003.36,62.13
2020-03-01 17:30:00,25.75,1005.25,50.85,22.10,1003.73,62.95
2020-03-01 18:00:00,26.55,1005.34,48.78,22.22,1003.83,62.06
2020-03-01 18:30:00,26.58,1005.70,47.49,22.00,1004.25,62.20
2020-03-01 19:00:00,26.41,1005.88,42.79,21.68,1004.29,50.83
2020-03-01 19:30:00,26.18,1006.09,40.66,21.65,1004.61,51.54

天気予報APIからの実績と予報データの取得

天気予報データは、 Rakuten RapidAPI を使用し、このマーケットプレイスで公開されている、 Dark Sky API を使用しました。
Rakuten RapidAPI は、1つのAPIキーを使用して世界中の様々なAPIを利用できるようにした、プロキシサービスです。

様々なAPIをWebで簡単にテストでき、Pythonをはじめいろいろな言語に対応したAPIをコールするためのコードスニペットも用意されているため、結構便利です。

f:id:Aqutam:20200310003313p:plain

Pythonコードスニペットをダウンロードし、パースして下記CSVを出力するためのコードを追加実装しました。

Date,WT Temp,WT Press,WT Hum,WT Sum,WT PrecipIntensity,WT PrecipProbability,WT ApparentTemperature,WT WindSpeed,WT WindBearing,WT CloudCover
2020-03-01 17:00:00,14.79,1015.4,42.0,曇り,0,0,14.79,2.66,167,77
2020-03-01 17:30:00,14.2,1015.7,44.0,曇り,0,0,14.2,2.74,180,90
2020-03-01 18:00:00,13.65,1016,47.0,曇り,0,0,13.65,2.89,191,94
2020-03-01 18:30:00,13.14,1016.6,50.0,曇り,0,0,13.14,3.13,187,96.0
2020-03-01 19:00:00,12.73,1017.2,52.0,曇り,0,0,12.73,3.51,180,92.0
2020-03-01 19:30:00,12.46,1017.6,55,曇り,0,0,12.46,3.93,177,91.0

グラフの描画

グラフの描画は、まず Jupyter Notebook で行いました。
Jupyter Notebook はブラウザ上で Python のコードを記述でき、グラフなど視覚的な結果を確認しながらプログラミングできるツールです。

センサーからのデータ取得天気予報APIからの実績と予報データの取得 で出力していた2つのCSVを読み込ませ、 Pandas で加工していきます。
そのデータを Matplotlib でグラフを描画するようにしました。

f:id:Aqutam:20200310004559p:plain f:id:Aqutam:20200310004610p:plain

Slackへのアップロード

Jupyter Notebook でグラフが描画できたら、ここから Python のコードに落とし込みます。
Pythonコード上で、 Slack files.upload API を使用して、Slack の特定 channel にグラフをアップロードするようにします。
cron に登録し、定期的にグラフをアップロードするようにします。

動作画面

下記のように、定期的に1週間ごと、1日ごとの温度・湿度・気圧の推移をグラフに描画しています。
緑色、黄色の線はセンサーのデータで、グレーの線は DarkSky API から取得した天気データです。

f:id:Aqutam:20200314120352p:plain

DarkSky API は、1時間ごとの天気予報も取得することが可能です。
これを使用して、1日先の1時間ごとの温度・湿度・気圧・降水量・降水確率を取得して、下記のようにグラフをプロットしています。
毎晩1回Slackにアップロードし、翌日のインコ部屋の温度管理の計画を立てるための情報として使用しています。

f:id:Aqutam:20200314120359p:plain

使用してみての感想

今回は温度・湿度・気圧センサーと天気予報APIから取得したデータをグラフに描画する仕組みを構築しました。

使用を開始してからは1ヶ月弱が過ぎました。
今までは、インコ部屋に温湿度計を立てて、ペットカメラ越しに確認しに行っていたのですが、このシステムを構築してからは定期的にSlackに情報が通知されるため、だいぶ把握が楽になりました。
湿度・温度・気圧・降水確率・降水量のグラフは、それぞれ別の画像で書き出しSlackにアップロードしているのですが、短時間に連続してアップロードしているためか、たまにアップロードに失敗します。
そんなに頻度は多くないので、失敗したときは手動でグラフ描画を再実行しています。

製作時の感想は、ケースの加工がなかなか大変でした。バンドソーやボール盤など電動工具がなく手作業で削ったり穴を空けていたので結構疲れました。
ソフトウェアの実装は、参考にできるコードがすでにあったり、便利なAPIサービスがあったりと、すんなり実装できました。

インコ部屋の環境は、今後下記のように改良してみたいと考えています。

  • Prometheus・Grafana 環境の構築
  • 温度のしきい値に応じて Slack への通知 (Prometheus Alertmanager)
  • 温度に応じて赤外線でエアコンの電源ONにする
  • I2Cバスのノイズ対策

CircleCI 2.1 による kubernetes で動作するアプリケーションの CI/CD 事始め (Ruby on Railsアプリケーション)

はじめに

この記事は CircleCI Advent Calendar 2019 の 19 日目の記事です。

拙稿となりますが Ruby on Rails, Vue.js によるモダン WEB アプリケーション 実践編 (その2) にて GitHub Action を使って k8s 上で動作する Ruby on Rails アプリケーションを CI/CD する Workflow を作ったので、同じ機能を CircleCI で作ってみることにします。

尚、Rails アプリケーションを前提としてますが、k8s の deployment manifest で定義されたアプリケーションであれば概ね流用できると思います。

なぜ CircleCI?

アドベントカレンダーのネタとして何かないかと探したことがきっかけです。(笑)

Wercker, CircleCI, GitHub Actions, GitLab CI/CD ではどれも YAML を定義することで CI/CD 環境を作ることが出来ます。 (CI/CD をオンプレ環境で構築する場合は Jenkins を使う環境があると思います)

そこで、1つ処理の実例を題材として GitHub Actions, CircleCI のそれぞれで違いを探してみようと思い立ったため CircleCI を使うことにしました。

前提事項

CircleCI が VCS リポジトリと連携できるよう初期設定が既に完了していることを前提としています。

初めて CircleCI の Workflow を追加する場合は、Getting Started を参考にして Workflow を設定してみて下さい。

構築する CI/CD の動作説明

CI では VCS レポジトリの全ブランチを対象に、コミットされた時点のコードに対してテストを実行するものとします。

CD では VCS リポジトリのデプロイ用ブランチを対象に、アプリケーションの Docker container image をビルド及び、k8s へデプロイするものとします。

CI の動作説明

CIの動作図
CIの動作図

CI の動作は上図にあるように次のとおりです。

  1. 開発者VCSリポジトリ(ex.GitHub) に git push を実行
  2. VCSリポジトリ(ex.GitHub)CIツール(ex.CircleCI) に PUSHイベントを通知
  3. CIツール(ex.CircleCI) がテストを実行
  4. CIツール(ex.CircleCI)VCSリポジトリ(ex.GitHub) CI結果(テスト結果)を通知
  5. (opt) CIツール(ex.CircleCI)開発者 にCI結果(テスト結果)を通知

※ 今回「5. CIツール(ex.CircleCI)開発者 にCI結果(テスト結果)を通知」は設定しません。

CD の動作説明

CDの動作図
CDの動作図

CD の動作は上図にあるように次のとおりです。

  1. 開発者VCSリポジトリ(ex.GitHub) にてPR/MRをマージ
  2. VCSリポジトリ(ex.GitHub)CDツール(ex.CircleCI) にマージ(又はブランチ更新)イベントを通知
  3. CDツール(ex.CircleCI) にてDockerイメージをビルド
  4. CDツール(ex.CircleCI)Dockerイメージレジストリ(ex.Docker Hub) にDockerイメージをプッシュ
  5. CDツール(ex.CircleCI)Kubernetesk8sマニフェストを更新
  6. Kubernetes にてローリングアップデート
  7. CDツール(ex.CircleCI)Kubernetes にてローリングアップデートの成功を確認
  8. (opt) CIツール(ex.CircleCI)開発者 にCI結果(テスト結果)を通知

CircleCI の Workflow / Job を作成する

GitHub Actions から CircleCI に移行する際に参考になるドキュメントが migrating-from-github にあります。

GitHub Actions では 1 Workflow は 1 ファイルであり、Workflow の中では複数の Job が定義できます。Workflow 設定は .github/workflows 配下に保存します。 一方で CircleCI では複数の Workflow が 1 ファイルに保存され、複数の Job が定義できます。Workflow 設定は .circleci/config.yml に保存します。

CI 環境

まずは CI 環境を構築することにします。

GitHub Actions の CI Workflow

# .github/workflows/test.yml
name: Test

on: push

jobs:

  test:

    runs-on: ubuntu-latest
    container: ruby:2.5.3

    steps:
    - uses: actions/checkout@v1

    - name: Initialize
      env:
        RAILS_ENV: test
        DISABLE_SPRING: 1
      run: |
        # install tools
        curl -sL https://deb.nodesource.com/setup_10.x | bash -
        apt-get install -y nodejs
        npm install yarn@1.13.0
        gem install bundler -v 1.17.3
        # initialize DB
        bundle install
        bundle exec rails db:migrate

    - name: Test
      run: bundle exec rails test

CircleCI の CI Workflow

# .circleci/config.yml
version: 2.1
jobs:
  test:
    docker:
      - image: ruby:2.5.3

    steps:
      - checkout

      - run:
          name: Initialize
          environment:
            RAILS_ENV: test
            DISABLE_SPRING: 1
          command: |
            # install tools
            curl -sL https://deb.nodesource.com/setup_10.x | bash -
            apt-get install -y nodejs
            npm install yarn@1.13.0
            gem install bundler -v 1.17.3
            # initialize DB
            bundle install
            bundle exec rails db:migrate

      - run:
          name: Test
          command: bundle exec rails test

workflows:
  ci:
    jobs:
      - test

GitHub Actions と CircleCI の比較

GitHub Actions と CircleCI のそれぞれの Workflow で CI 環境を実現する YAML ファイルを見てみましたが、内容はほぼ同じに記載することが出来ました。

記載内容に違いが出るのは、並列実行、キャッシュ利用、実行ホストのOS/リソース指定などを行おうとした時だと思われます。 CI として紹介した Workflow は非常に単純なものであったため差異は出ませんでした。

CD 環境

次に CD 環境を構築することにします。

GitHub Actions の CD Workflow

# .github/workflows/docker_build_and_push.yml
name: Docker Image CI

on:
  push:
    branches:
    - stable

jobs:

  docker_build_and_push:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Build the Docker image
      env:
        # [TODO] ${{ github.repository }} から repository 名だけ抽出する
        IMAGE_NAME: ${{ secrets.DOCKER_HUB_USERNAME }}/vue_practice_app:${{ github.sha }}
      run: docker build . --file Dockerfile --tag $IMAGE_NAME

    - name: Login to Docker hub
      run: docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_PASSWORD }}

    - name: Push the Docker image to Docker hub
      env:
        # [TODO] ${{ github.repository }} から repository 名だけ抽出する
        IMAGE_NAME: ${{ secrets.DOCKER_HUB_USERNAME }}/vue_practice_app:${{ github.sha }}
      run: docker push $IMAGE_NAME
# .github/workflows/deploy.yml
name: Deploy docker container to kubernetes

on:
  release:
    types: [published]

jobs:

  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@master

    - name: migrate on cluster
      uses: steebchen/kubectl@master
      env:
        KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
      with:
        args: >
          run tmp-migrate -i --generator=run-pod/v1 --rm
          --image ${{ secrets.DOCKER_HUB_USERNAME }}/vue_practice_app:${{ github.sha }}
          --overrides='{
            \"spec\":{
              \"containers\":[{
                \"name\":\"app\",
                \"image\":\"${{ secrets.DOCKER_HUB_USERNAME }}/vue_practice_app:${{ github.sha }}\",
                \"command\":[\"bash\"],
                \"args\":[\"-c\",\"rails db:migrate SECRET_KEY_BASE=$(rails secret)\"],
                \"envFrom\":[{\"secretRef\":{\"name\":\"vue-practice\"}}]
              }]
            }
          }'

    - name: deploy to cluster
      uses: steebchen/kubectl@master
      env:
        KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
        # [TODO] ${{ github.repository }} から repository 名だけ抽出する
        IMAGE_NAME: ${{ secrets.DOCKER_HUB_USERNAME }}/vue_practice_app:${{ github.sha }}
        K8S_NAMESPACE: vue-practice
        K8S_DEPLOYMENT_NAME: vue-practice
        K8S_CONTAINER_NAME: app
      with:
        args: set image --namespace $K8S_NAMESPACE --record deployment/$K8S_DEPLOYMENT_NAME $K8S_CONTAINER_NAME=$IMAGE_NAME

    - name: verify deployment
      uses: steebchen/kubectl@master
      env:
        KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}
        K8S_NAMESPACE: vue-practice
        K8S_DEPLOYMENT_NAME: vue-practice
      with:
        args: rollout status --namespace $K8S_NAMESPACE deployment/$K8S_DEPLOYMENT_NAME

CircleCI の CD Workflow

Settings >> BUILD SETTINGS >> Environment Variables にて、環境変数を以下のとおり設定する必要があります。

名前 説明
DOCKER_HUB_USERNAME Docker Hub アカウントのユーザ名
DOCKER_HUB_PASSWORD Docker Hub アカウントのパスワード
KUBECONFIG_STR ※1 kubectl config の内容を base64 変換したもの ※2

※1 circleci/kubernetes の install-kubeconfig コマンドでは設定ファイルの内容を KUBECONFIG 環境変数で設定することが default なのですが、kubectl は KUBECONFIG 環境変数を設定ファイルへのパスとして使用するため KUBECONFIG 以外の名前にしましょう。(参考)

※2 echo -n "$KUBECONFIG" | base64 -d により復元できる値にすること

# .circleci/config.yml
version: 2.1

orbs:
  docker-orb: circleci/docker@0.5.19
  kube-orb: circleci/kubernetes@0.10.1

commands:
  migrate_vue_practice:
    parameters:
      image_name:
        type: string
    steps:
      - run: |
          kubectl run tmp-migrate -i --generator=run-pod/v1 --rm -n vue-practice --image << parameters.image_name >> \
            --overrides="'{ \
              \"spec\":{ \
                \"containers\":[{ \
                  \"name\":\"app\", \
                  \"image\":\"<< parameters.image_name >>\", \
                  \"command\":[\"bash\"], \
                  \"args\":[\"-c\",\"rails db:migrate SECRET_KEY_BASE=\$(rails secret)\"], \
                  \"envFrom\":[{ \
                    \"secretRef\":{ \
                      \"name\":\"vue-practice\" \
                    } \
                  }] \
                }] \
              } \
            }'"

jobs:
  test:
    docker:
      - image: ruby:2.5.3

    steps:
      - checkout

      - run:
          name: Initialize
          environment:
            RAILS_ENV: test
            DISABLE_SPRING: 1
          command: |
            # install tools
            curl -sL https://deb.nodesource.com/setup_10.x | bash -
            apt-get install -y nodejs
            npm install yarn@1.13.0
            gem install bundler -v 1.17.3
            # initialize DB
            bundle install
            bundle exec rails db:migrate

      - run:
          name: Test
          command: bundle exec rails test

  docker_build_and_push:
    machine:
      image: ubuntu-1604:201903-01

    steps:
      - checkout

      - run:
          name: Initialize
          command: |
            GIT_HASH=$(git rev-parse HEAD)
            echo "export GIT_HASH=${GIT_HASH}" >> $BASH_ENV
            echo "export IMAGE_NAME=${DOCKER_HUB_USERNAME}/vue_practice_app" >> $BASH_ENV
            source $BASH_ENV

      - run:
          name: docker login
          command: echo "${DOCKER_HUB_PASSWORD}" | docker login -u ${DOCKER_HUB_USERNAME} --password-stdin

      - docker-orb/build:
          image: $IMAGE_NAME
          tag: $GIT_HASH

      - docker-orb/push:
          image: $IMAGE_NAME
          tag: $GIT_HASH

  deploy:
    machine:
      image: ubuntu-1604:201903-01

    steps:
      - checkout

      - run:
          name: Initialize
          command: |
            GIT_HASH=$(git rev-parse HEAD)
            echo "export IMAGE_NAME=${DOCKER_HUB_USERNAME}/vue_practice_app:${GIT_HASH}" >> $BASH_ENV
            source $BASH_ENV

      - kube-orb/install-kubectl

      - kube-orb/install-kubeconfig:
          kubeconfig: KUBECONFIG_STR

      - migrate_vue_practice:
          image_name: $IMAGE_NAME

      - kube-orb/update-container-image:
          namespace: vue-practice
          resource-name: deployment/vue-practice
          container-image-updates: app=$IMAGE_NAME
          record: true

      - kube-orb/get-rollout-status:
          namespace: vue-practice
          resource-name: deployment/vue-practice

workflows:
  ci:
    jobs:
      - test

  cd:
    jobs:
      - test:
          filters:
            branches:
              only: stable
      - docker_build_and_push:
          requires:
            - test
      - deploy:
          requires:
            - docker_build_and_push

CircleCI Orbs を使う方法には .circleci/config.yml にて以下のようにします。

  • version: 2.1 以上にする
  • orbs の値として使用したい Orb を Hash 形式で指定する
    • Hash の key は job 内で使用する名前となる
    • Hash の value は公開された Orb のパスを指定する
      • @ をつけると Orb のバージョンを指定できる
    • 例) docker-orb: circleci/docker@0.5.19 を指定すると circleci/docker の version 0.5.19 が定義したコマンドが使えるようになり、Job の中で docker-orb/build のようにコマンドが指定できる

また、commands も version: 2.1 から使えるようになった設定です。 Job の中で run 等の代わりに使えるコマンドを定義できます。 今回 commands を使った理由は、kubectl run の override させる JSON パスに $IMAGE_NAME を展開しつつ生成できなかったため使うことにしました。

GitHub Actions と CircleCI の比較

GitHub Actions と CircleCI のそれぞれの Workflow で CD 環境を実現する YAML ファイルを見てみましたが、CI と同様に内容はほぼ同じに記載することが出来ました。

GitHub Actions も CircleCI も外部の Workflow/Job を流用できるため、同様の機能であれば、それらの使い方の違いはあるもののほぼ同じに記載することが出来ました。

作成した CI/CD の改善ポイント

  • npm, gem パッケージインストール後にキャッシュを有効にする
  • Docker イメージのビルドキャッシュを有効にする
  • CI/CD の実行結果をメールの代わりに Slack 通知する
  • CD にてデプロイする前に継続して実行するかどうかを人に尋ねる

キャッシュを有効にすることで Workflow を実行する時間が短縮できるようになることが期待できます。

最後に - GitHub Actions と CircleCI の違いについての所感

今回はせっかくなので version 2.1 から使えるようになった機能である CircleCI Orbs, Commands を使ってみました。

CircleCI の Marketplace と同様の仕組みとして CircleCI Orbs なるものがあると記事を執筆していて知りました。

CircleCI Orbs を知る前は GitHub Actions との大きな差異として、外部の Workflow が利用できる点だと思っていたので、version 2.1 の新機能を知ったことで GitHub Actions とはほぼ差異はなく移行できる印象を持ちました。

敢えて違いとなることを挙げると次のとおりかと感じてます。

GitHub Actions の Marketplace の手軽さ、GitHub Actions では JavaScript を使ってアクションを実装することが出来るため自由度やメンテナンス製が高いように感じます。

一方で CircleCI には CLI があるため、開発をする際にはわざわざ VCS リポジトリに commit/push せずに開発・デバッグ出来るメリットがあると思います。

イマドキの JavaScript 開発で使える、リモートデバッグとロガーの Tips (2018年版-後編)

こちらは 「イマドキの JavaScript 開発で使える、リモートデバッグとロガーの Tips (2018年版-後編)」からの転載です。

SEROKU の開発を例に、弊社で使っているリモートデバッグとロガーの Tips をご紹介します。 当記事は 2018 年、と過去の記事ですが、現在でも応用可能な Tips になっています。



案件としても OSS 成果物としても、JavaScript を利用するシチュエーションは増え続けています。まだまだ枯れた言語とは言い難い状況で、使われるバージョンも ES5 から ES7 まで進化を続け、新しい文字列リテラルや async/await のような「イマドキの JavaScript の書き方」を紹介する記事は多い中、デバッグはこうあるべきという情報は比較的少ないように思います。

本記事の前編、中編では、システム開発に於けるデバッガ、ロガーの大切さと、他の言語・フレームワークと比べた際の JavaScript 開発環境に於けるビハインドについて説明し、実際に理想的なロガーを利用する為の設定方法を紹介してきました。

本記事では JavaScript 開発時のデバッガー利用のための具体的な設定方法を紹介します。

理想の世界

まずは前編で掲げたゴールの再掲です。

対象システム

以下の3つのシナリオをカバーすることにします。

  • シナリオA
    • node.js (Express)
  • シナリオB
    • node.js (Express) + webpack/babel によるクライアントビルド
  • シナリオC
    • next.js on Express

やりたいこと

  • SourceMap の利用
    • ブラウザの開発者ツールでエラーを追う場合、トランスパイル前のソースの行数が分かる
  • リモートデバッグ

Let's Try! ― シナリオA

シナリオAは、node.js (Express) 環境向けのロガー設定です。
必要なことは以下 2 つです。

npm 経由のプロセスをデバッグ

node.js のプロセスは、 node コマンドに --inspect あるいは --inspect-brk を渡すことでリモートデバッグに必要な inspector の利用が可能になります。node コマンドをそのまま叩くことは少ないので、まずは npm 経由で立ち上げた node プロセスをデバッグできるようにしましょう。

VSCode公式ページによれば、以下のようなコードで、npm から起動したプロセスをデバッグできるようになります。

(..snip..)

"scripts": {
  "debug": "node --nolazy --inspect-brk=9229 app.js"
},

(..snip..)

9229 ポートは inspector 利用時のデフォルトポートなので、単に --inspect でも構いません。

次に VSCode の設定ファイルです。

launch.json

(..snip..)

{
    "name": "Launch via NPM",
    "type": "node",
    "request": "launch",
    "cwd": "${workspaceFolder}",
    "runtimeExecutable": "npm",
    "runtimeArgs": [
        "run-script", "debug"
    ],
    "port": 9229
}

(..snip..)

これだけで、VSCode のデバッガが利用可能になります。

個人的には以下のように若干カスタムしたものを使っています。

launch.json

(..snip..)

{
    "type": "node",
    "request": "launch",
    "name": "Debug: Server",
    "runtimeExecutable": "npm",
    "runtimeArgs": [
      "run", "debug"
    ],
    "port": 9229,
    "restart": true,
    "console": "integratedTerminal",
    "internalConsoleOptions": "neverOpen"
},

(..snip..)

node-dev を使おう

さて、debug script では node をそのまま起動していますが、開発中にソースコード変更を検知して Express サーバーが再起動してくれると更に楽です。巷では nodemon が有名ですが、筆者が推すのは断然 node-dev です。コンフィグレスで require されたものだけを watch してくれる上に、デスクトップ通知があって再起動したことがわかりやすいのが特徴です。

debug script を修正し、以下のようにしておきましょう。

(..snip..)

"scripts": {
  "debug": "node-dev --nolazy --inspect app.js"
},

(..snip..)

追加の watch 設定

開発環境で、js だけではなく json 等が変更されたときでも node-dev による再起動が働いて欲しい場合もあると思います。そんなときは、追加で require する機構を用意し、 development 環境でのみ働くようにしておきましょう。

dev.js

const fs = require('fs');
const path = require('path');

const localeDir = 'src/locales';

class Dev {

  init() {
    this.requireForAutoReloadServer();
  }

  /**
   * require files for node-dev auto reloading
   */
  requireForAutoReloadServer() {
    // load all json files for live reloading
    fs.readdirSync(localeDir)
      .filter(filename => {
        return fs.statSync(path.join(localeDir, filename)).isDirectory();
      })
      .map((dirname) => {
        require(path.join(localeDir, dirname, 'translation.json'));
      });
  }

}

module.exports = Dev;

app.js

(..snip..)

if (self.node_env === 'development') {
  const Dev = require('./dev');
  const dev = new Dev();
  dev.init();
}

(..snip..)

上記のサンプルコードは、 src/locales/* 下にある translation.json ファイルが変更された場合に再起動を行います。

Let's Try! ― シナリオB

シナリオBは、node.js (Express) + webpack/babel によるクライアントビルド両方で利用できる設定です。

ここまでに作ったファイルは完全に流用できます。
webpack の設定と、VSCode の設定を追加しましょう。

webpack.js

(..snip..)

devtool: 'cheap-module-eval-source-map',

(..snip..)

devtool にどのような値を設定可能かは webpack の公式ドキュメントを参照してください。ここでは速度を重視し、 cheap-module-eval-source-map を選びました。

次に VSCode の設定ファイルです。

launch.json

(..snip..)
{
    "type": "chrome",
    "request": "launch",
    "name": "Debug: Chrome",
    "sourceMaps": true,
    "webRoot": "${workspaceFolder}",
    "sourceMapPathOverrides": {
      "webpack:///*": "${workspaceFolder}/*"
    },
    "url": "http://localhost:3000",
}
(..snip..)

特に重要なのは webRootsourceMapPathOverrides です。

webRoot は、URL ベースで root がどこを指し示すか、
sourceMapPathOverrides はブラウザの開発者ツールで source map の参照先の webpack:/// から始まるパスが、VSCodeワークスペース上でどこを指し示すべきかのマッピングを行います。ブレークポイントがうまく設定できない場合は、この2つを見直しましょう。

Let's Try! ― シナリオC

シナリオCは、next.js 向けの設定です。ディレクトリ構成がほぼ決まっているので、シナリオBより簡単かもしれませんね。

launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Next: Node",
      "runtimeExecutable": "npm",
      "runtimeArgs": [
        "run",
        "debug"
      ],
      "port": 9229,
      "restart": true,
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "sourceMapPathOverrides": {
        "webpack:///*": "${workspaceFolder}/*"
      },
    },
    {
      "type": "chrome",
      "request": "launch",
      "name": "Next: Chrome",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}",
      "sourceMapPathOverrides": {
        "webpack:///*": "${webRoot}/*"
      },
    }
  ],
  "compounds": [
    {
      "name": "Next: Both",
      "configurations": ["Next: Node", "Next: Chrome"]
    },
  ],
}

まとめ

後編ではロギングに関して実際のコードを紹介し、デバッガ利用時の「理想の世界」を実現するコードを紹介しました。デバッガや node-dev のような自動で再起動するツールを使うことで、開発時の効率は数倍になります。

関連記事(前編・中編)と合わせて、是非自身とチームの開発をより効率的に、より楽しいものにしていただければと思います。

関連記事

tech.weseek.co.jp

tech.weseek.co.jp

ゲーム開発のために Cocos Creator 触ってみた! 〜その1〜

みなさんこんにちは。突然ですが Cocos Creator 知ってますか!? cocos といってもファミリーレストランではありません(笑)現在ゲームのアプリを作りたいとなった場合、いわゆるゲームエンジンを何を使うかというと、 Unity と Cocos2d-x の二つが人気の上位を占めると言える状態です。

Cocos Creator はその Cocos2d-x をベースとした、スマホ用(Web)ゲーム開発アプリ統合開発ツールです。統合開発ツールってなんのこっちゃ、って思う人は Unity と Cocos2d-x を足して2で割ったものだと思ってください(笑)

何ができるの?

上記の説明でピンとこない人に(当たり前や)、統合開発ツールって実際なんなんだってところなんですが、元々の Cocos2d-x はあくまでコードベースで開発するスタイルなので視覚的にわかりづらい、とっつきにくい部分が少なからずありました。 一方で、 Unity の場合は(こちらは統合開発ツール)、アプリケーションを開くと画面の UI 操作で制作するゲームのシーンを作成できたり、 UI を編集できたり、アニメーションを設定できたり、コーディング(別途 Editor の設定が必要)やビルドができたり、ざっくり言ってしまえばそれをインストールすればそれだけでアプリが作れちゃうんですね。

なので、 Cocos Creator は Cocos2d-x がなんか複雑すぎーって諦めちゃった人たちにはかなり有用なツールになりえるかと思います。 また、アプリケーションの UI は Unity をかなり参考にしているっぽいので、Unity を触っていた人たちはすんなり入れるかと思います。

コードを書く上で、言語ってとっても大事だと思うんですけど、 Cocos Creator では 2018/08/29 現在、JavaScript, TypeScript で書くことができます。

インストールしてみる

本家のサイトからまずダウンロードページに飛びましょう。(デフォが中国語なのでびっくりしないように)

最新バージョンは2系ですが、触ってみた感じちょっとバグ多めなので安定している 1.9 か 1.10 系をお勧めします(^_^;)

DL したセットアップファイルを開けば自動的にインストールが始まるので OK を押していけば基本的に大丈夫です。

トップページにあるダウンロードリンクだと最新バージョンを落とすことになるので、バージョン指定したい場合は上記のダウンロードページに直接飛んでください。

起動してみる

インストールが正常に完了すると、起動できる状態になります。 ?? みたいなアイコンなので早速起動してみましょう。

起動すると、まずはログイン画面が表示されます。Cocos Developer アカウントが必要なので、先に登録しておくか、もしくはログイン画面の Sign up をクリックして登録しましょう!

登録が完了した上で、ログインフォームに情報を入力して Login ボタンを押すと、正常に処理が進めばプロジェクト作成画面に切り替わるので、この状態でプロジェクトが作れるようになります!

プロジェクトを作成してみる

ログインが正常に済むと、Dashboard がプロジェクト作成の画面に切り替わります。

f:id:weseek:20191114182705p:plain

Cocos Creator では、「Recent Projects」タブから既存のプロジェクトを(一度開いたプロジェクトはここにリスティングされます)、「New Project」タブからは、いくつかのサンプルをベースに作るか、空の Project を、 Open Other… タブでは PC 上にある Cocos Creator のプロジェクトを選択形式でそれぞれプロジェクトを開くことができます。

せっかくなので Empty Project で新規に作成したいと思います。

画面の説明

プロジェクトが立ち上がると以下のような画面が開きます。

f:id:weseek:20191114182724p:plain

Unity を知ってる人は、その類似具合に驚くかと思います(笑)

ざっと解説していきます。

Node Tree

  • シーンに配置される Node が階層構造で表示される
  • 並列に Node が置かれた場合は、画面上で 下 に表示されているものが画面では全面に来る
  • Node は階層構造として表現できるので、Node の子、孫・・・のような作り方も可能(グルーピング)

Asset

  • プロジェクトを通して使われるアセットファイル、スクリプトファイルが表示される
  • プロジェクト作成時に作られる assets フォルダの中身がそのまま表示される

Scene

  • 名前の通り、シーン(画面の単位)を編集することができる

Properties

  • Node Tree 上で選択した Node のプロパティを確認(調整)することができる

Console

  • ログを確認することができる

Timeline

  • アニメーションを生成することができる

Node Library

いざ開発!

開発の流れとしては、基本的には、 Scene 上、または Node Tree 上にコンポーネントをペタペタ貼っていき UI を組み上げていきます。

音声や画像、フォント等のアセットを使用したい場合には Assets に適宜追加して、それを同じ要領で Scene または Node Tree 上に配置していく。という感じで画面を作っていきます。

ある程度画面が作れてきたらスクリプトを定義し、ボタンなどに対してイベントを仕掛け、処理を書いていく、というフローになるかと思われます。

また画面上部にある三角ボタンを押すと その状態でのデバッグができるので(デフォルトだとブラウザが開かれる)ちょこちょこ確認しながら作成することができます!

まとめ

今回は、 Cocos Creator という統合開発環境の簡単な説明と使い方を書いていきました。

前身となる Cocos Studio というものが Deprecated になってしまったり、 Cocos 陣営の戦略がなかなか不透明だったり、とにかくサンプルが少なくて「こういうときどうやればいいんだろう?」みたいなことが結構あったりするんですが、それでも手頃にゲームアプリが作れちゃうところはとっても魅力だと思います!

次回からは、なにか目的をもってゲームを作り上げていこうと思いますのでお楽しみに!

Go 言語での開発始めてみる〜開発環境を作る編〜

こちらは 「Go言語での開発始めてみる〜開発環境を作る編〜 」からの転載です。



前回の続き Go言語での開発を試してみる 〜調べる編〜 で調べたものを組み合わせて実際に Go 言語を使って Web システムを作ってみようと思います。今回はフレームワークの起動までです。

開発環境構築

OS は Windows 10 にて行っていきます。

Go 自体のバージョン

この前入れた通り下記で行こうと思います。こちらもそこまで意味は無いです。

> go version
go version go1.10.3 windows/amd64

開発エディタ

特に選定したわけではなく、普段使い慣れている Visual Studio Code を使って開発をしようと思います。 とりあえず拡張機能をインストール

f:id:weseek:20191107165922p:plain

Ctrl + Shift + p でコマンドパレットを開き、 Go: Install/Update tools を実行

f:id:weseek:20191107165939p:plain

ツールをインストールしていきます。

    Installing github.com/nsf/gocode SUCCEEDED
    Installing github.com/uudashr/gopkgs/cmd/gopkgs SUCCEEDED
    Installing github.com/ramya-rao-a/go-outline SUCCEEDED
    Installing github.com/acroca/go-symbols SUCCEEDED
    Installing golang.org/x/tools/cmd/guru SUCCEEDED
    Installing golang.org/x/tools/cmd/gorename SUCCEEDED
    Installing github.com/fatih/gomodifytags SUCCEEDED
    Installing github.com/haya14busa/goplay/cmd/goplay SUCCEEDED
    Installing github.com/josharian/impl SUCCEEDED
    Installing github.com/davidrjenni/reftools/cmd/fillstruct SUCCEEDED
    Installing github.com/rogpeppe/godef SUCCEEDED
    Installing golang.org/x/tools/cmd/godoc SUCCEEDED
    Installing github.com/sqs/goreturns SUCCEEDED
    Installing github.com/golang/lint/golint SUCCEEDED
    Installing github.com/cweill/gotests/... SUCCEEDED
    Installing github.com/derekparker/delve/cmd/dlv SUCCEEDED

    All tools successfully installed. You're ready to Go :).

これらがインストールされました。
すると。。。

f:id:weseek:20191107170012p:plain

わーすごい!色もついたしブレイクポイントを設定して F5 を押すことでデバッグもできるようになりました!

  • 若干詰まった点
    • setting.json を作業ディレクトリ配下に置き下記のように gopath を設定しなければいけないようです

        {
          "go.gopath": "/path/to/go/src/beegotest"
        }
      

こんな感じにフルパスで指定してあげるとインストール直下以外でも動きました。

実際にやってみた

それではここから本格的な(とはいっても大層なものを作るわけではないですが)開発に入って行こうと思います。
せっかくなので前回調べた中のフレームワークを使ってみようと思います。

今回は GitHub上の Star 数が一番多いという理由だけで beego を使ってみようと思います。

パッケージ管理

特にこだわりがないのでパッケージ管理は dep を使おうと思います。vgo (Versioned Go) も公式ではありますがまだ人類には早いという噂を聞いたので無難な選択をしますw
とりあえず go get で取得し、init して ensure で /vendor 配下にパケージをダウンロードするように設定します。

> go get -u github.com/golang/dep/cmd/dep
> dep init
> dep ensure

どのコマンドもうんともすんとも言わず不安ですが、指定したフォルダの配下に下記ができていれば大丈夫のようです。

> dir


    ディレクトリ: go\src\beegotest


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2018/08/20     19:32                vendor
-a----       2018/08/20     19:32            222 Gopkg.lock
-a----       2018/08/20     19:32            655 Gopkg.toml
-a----       2018/08/20     19:20             55 settings.json

beego インストール

dep の準備が整ったら実際に beego をインストールしていきましょう。

> dep ensure -add github.com/astaxie/beego
Fetching sources...

"github.com/astaxie/beego" is not imported by your project, and has been temporarily added to Gopkg.lock and vendor/.
If you run "dep ensure" again before actually importing it, it will disappear from Gopkg.lock and vendor/.

bee ツールもご一緒に

> dep ensure -add github.com/beego/bee

と思ったんですが、どうやら依存関係のせいで dep でインストールができませんでした。。。 しかたがないので go get で取得します。ソースコードとしては直接関係ないのでまぁ問題ないと判断します。

> go get github.com/beego/bee

無事インストールできました

> bee version
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0

├── Beego     : Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego
├── GoVersion : go1.10.3
├── GOOS      : windows
├── GOARCH    : amd64
├── NumCPU    : 4
├── GOPATH    : C:\Path\to\go\
├── GOROOT    : C:\Go\
├── Compiler  : gc
└── Date      : Monday, 20 Aug 2018

プロジェクト作成

適当に bee ツールにてプロジェクトを作成してみます。

> bee new haruch
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/08/20 20:13:05 INFO     0001 Creating application...
        create   C:\Path\to\go\src\beegotest\haruch\
        create   C:\Path\to\go\src\beegotest\haruch\conf\
        create   C:\Path\to\go\src\beegotest\haruch\controllers\
        create   C:\Path\to\go\src\beegotest\haruch\models\
        create   C:\Path\to\go\src\beegotest\haruch\routers\
        create   C:\Path\to\go\src\beegotest\haruch\tests\
        create   C:\Path\to\go\src\beegotest\haruch\static\
        create   C:\Path\to\go\src\beegotest\haruch\static\js\
        create   C:\Path\to\go\src\beegotest\haruch\static\css\
        create   C:\Path\to\go\src\beegotest\haruch\static\img\
        create   C:\Path\to\go\src\beegotest\haruch\views\
        create   C:\Path\to\go\src\beegotest\haruch\conf\app.conf
        create   C:\Path\to\go\src\beegotest\haruch\controllers\default.go
        create   C:\Path\to\go\src\beegotest\haruch\views\index.tpl
        create   C:\Path\to\go\src\beegotest\haruch\routers\router.go
        create   C:\Path\to\go\src\beegotest\haruch\tests\default_test.go
        create   C:\Path\to\go\src\beegotest\haruch\main.go
2018/08/20 20:13:05 SUCCESS  ? 0002 New application successfully created!

いろいろと自動作成されました。

起動

それでは早速起動します!

> cd haruch
> bee run
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.10.0
2018/08/20 20:16:35 INFO     ? 0001 Using 'haruch' as 'appname'
2018/08/20 20:16:35 INFO     ? 0002 Initializing watcher...
beegotest/vendor/github.com/astaxie/beego/config
beegotest/vendor/gopkg.in/yaml.v2
beegotest/vendor/github.com/astaxie/beego/utils
beegotest/vendor/github.com/astaxie/beego/logs
beegotest/vendor/github.com/astaxie/beego/grace
beegotest/vendor/github.com/astaxie/beego/session
beegotest/vendor/github.com/astaxie/beego/toolbox
beegotest/vendor/golang.org/x/crypto/acme
beegotest/vendor/github.com/astaxie/beego/context
beegotest/vendor/golang.org/x/crypto/acme/autocert
beegotest/vendor/github.com/astaxie/beego/context/param
beegotest/vendor/github.com/astaxie/beego
beegotest/haruch/controllers
beegotest/haruch/routers
beegotest/haruch
2018/08/20 20:16:40 SUCCESS  ? 0003 Built Successfully!
2018/08/20 20:16:40 INFO     ? 0004 Restarting 'haruch.exe'...
2018/08/20 20:16:40 SUCCESS  ? 0005 './haruch.exe' is running...
2018/08/20 20:16:40.951 [I] [asm_amd64.s:2361]  http server Running on http://:8080

起動した!!!

f:id:weseek:20191107170043p:plain

試しにいじってみる

普通の自動生成されたファイルを眺めてみると、普通の MVC フレームワークのようなので試しに View を編集してみましょうか。
/views/index.tpl というファイルを開いてみると想像通り html ファイルが入っていました。 なので下記のように編集してみます(抜粋)

<body>
  <header>
    <h1 class="logo">haruhikonyan 参上</h1>
    <div class="description">
      go 及び beego を試してみてるマン
    </div>
  </header>
  <footer>
    <div class="author">
      Official website:
      <a href="http://{{.Website}}">{{.Website}}</a> /
      Contact me:
      <a class="email" href="mailto:{{.Email}}">{{.Email}}</a>
    </div>
  </footer>
  <div class="backdrop"></div>

  <script src="/static/js/reload.min.js"></script>
</body>

f:id:weseek:20191107170109p:plain

無事編集できたのと、ライブリロードこそ対応してないものの、ソースを保存してブラウザを更新すればサーバの再起動無しにコードは反映されました。本日はこれくらいで。

まとめ

まだまだ Go言語たるものの真髄には全然触れられてはいないとは思いますが、 VSCode でのエディタの整備も特に苦も無く整備でき、デバッグ環境もすぐ整えられました。また、ちょこっと試してみた Go の Web フレームワークである beego も Rails のようなフルスタックフレームワークも体感的には Ruby で開発環境を整えるよりも楽に行えた気がしたのでこれはアリだなと思いました。

次回はこの beego を使ってもっと Web サービスっぽくするのと、他の機能も使ってみようと思います。

関連記事

tech.weseek.co.jp