20191130のNode.jsに関する記事は7件です。

standard-version で便利にバージョニング&CHANGELOG.md 自動生成

個人プロジェクトで CHANGELOG.md を standard-version のガイドラインに沿って自動生成しています。やり方の雑なまとめです。

yarn release:patch:candidate  # パッチリリース候補 タグ打ち
yarn release:patch            # パッチリリース タグ打ち

yarn release:minor:candidate  # マイナーリリース候補 タグ打ち
yarn release:minor            # マイナーリリース タグ打ち

yarn release:major:candidate  # メジャーリリース候補 タグ打ち
yarn release:major            # メジャーリリース タグ打ち

↑これだけで、バージョニングと CHANGELOG 生成できます;
- git のコミットログから CHANGELOG.md が自動生成・追記されて、
- その CHANGELOG.md を git add しつつ、
- package.json の version をパッチレベルごとに semver し、こちらも git add して、
- git commit

スクリーンショット 2019-11-30 17.22.44.png

husky で git commit hooks に commitlint を設定する

CHANGELOG を生成するにあたって、一定フォーマットで揃えます。husky で git commit 時に commitlint を実行するようにして、フォーマットを強制しておきます。

yarn add --dev husky commitlint
package.json
:
  "devDependencies": {
    "@commitlint/cli": "^8.2.0",
    "@commitlint/config-conventional": "^8.2.0",
    "husky": "^3.1.0",
     :
  },
:
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "post-commit": "git update-index --again"
    }
  },
:

commitlint は Angular で使っているコミットメッセージのフォーマットをチェックできます。
以下は、先達諸兄のブログポストなど参考にして、私が設定しているフォーマットですが好み次第です。

こちらとても参考になりました。感謝です:
- https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
- http://falsandtru.hatenablog.com/entry/git-commit-message

              type   scope(opt) verb title 
                 \      |        |     |
                 feat(android): add template url parameter to events   (<- without period)
(empty line) ->
        body ->  追加の情報あれば   (<- within 72 chars per line)
                 本文ではどのようにではなく何をとなぜを説明する

<type>:
- feat (new feature for the user, not a new feature for build script)
- fix (bug fix for the user, not a fix to a build script)
- docs (changes to the documentation)
- style (formatting, missing semi colons, etc; no production code change)
- refactor (refactoring production code, eg. renaming a variable)
- test (adding missing tests, refactoring tests; no production code change)
- chore (updating grunt tasks etc; no production code change)

<scope>:
scope is just a option, and will be empty in most case. if you want to define scope, then you can write.

<verb>:(1行メッセージでも動詞をしっかりしておくと割と意味が通る、気がする)
- add A (to B)
- remove A (from B)
- move A (from B to C)
- replace A with B
- make A B
- change A to B
- update A to B
- ensure that A
- use A (instead of B for C)
- fix A

During a rebase you may want to skip all hooks, you can use HUSKY_SKIP_HOOKS environment variable.HUSKY_SKIP_HOOKS=1 git rebase ...

standard-version でバージョニングする

特に何も設定していないのですが、そのまま使えています。

yarn add --dev standard-version
package.json
:
  "scripts": {
    :
    "release:patch:candidate": "standard-version --release-as patch --prerelease",
    "release:patch": "standard-version --release-as patch",
    "release:minor:candidate": "standard-version --release-as minor --prerelease",
    "release:minor": "standard-version --release-as minor",
    "release:major:candidate": "standard-version --release-as major --prerelease",
    "release:major": "standard-version --release-as major"
  },
:
  "devDependencies": {
    :
    "standard-version": "^7.0.1",
    :
  },
:

END

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サーバーレス(cloud functions)のIPアドレスがどうなっているのか気になったので検証

背景

スクレイピングはEC2などのインスタンスからやるのが通常かと思っていましたが、それ以外にも、サーバーレス(cloud functions)からpuppeteer+headless chromeを使用してスクレイピングが可能なのことを最近知りました。
サーバーレスだと固定IPが付けられなく、IPも変動するのが通常のような感じがします。疑問に感じたので、サーバーレスのIPアドレス変動の実態について検証します。

cloud functionsから外部サイトへのアクセスは無料のsparkプラン以外からでないとできません。

IPを調べる方法

外部IPだけで十分ですが、せっかくなので外部IP内部IPの両方を確認してcloud functionsのことをもっと知りたい!
内部IPはosモジュール、外部IPはIPアドレスを表示してくれるWEBサイトを叩いて表示されたIPを取得します。

構成とソースをちょっと解説

構成とか

  1. firebase init --only functionsfunctionsを作成
  2. npm install --save puppeteer osで今回使用するモジュールをインストール
  3. puppeteerWEBサイトをスクレイピングして.outIpクラス=IPアドレスを取得する
    image.png

  4. osモジュールを利用して内部IPを取得

  5. console.logosモジュールの結果と.outIpの出力を入れる

  6. functions.region('foo')でマルチリージョンに展開

  7. firebase deploy!

ソース抜粋

const puppeteer = require('puppeteer');
const os = require('os');

// firebaseのexport
// puppeteerの初期化処理 pageオブジェクトが外部サイトの構造を持っている

let item = await page.$(".outIp");
let outerIp = await (await item.getProperty('textContent')).jsonValue();
let innerIps = os.networkInterfaces();

console.log("============================");
console.log("Outer Ip Adress is" + outerIp);
console.log("Inner Ip Adress is" + innerIps);

// マルチリージョンに展開する

これでcloud functionsのURLを叩けば、ログに外部IPと内部IPが出力される関数ができました。ローカルで動作確認をして、firebase deployを実行。

※ 256MBだと実行時にmemory errorになってしまうので、functions.runWith({memory: '512MB'}).を追加しました。

結果

外部IP

1-4回目: 約2分間隔
5回目: 約5分間
6回目: 約10分間隔
7回目: 約30分間隔
8回目: 約3時間間隔
9回目: 約1日間隔

us-central1 us-east1 us-east4 europe-wast1 europe-west2 asia-east2 asia-northeast1
1回目 107.178.239.204 107.178.239.245 107.178.229.223 107.178.238.124 107.178.231.62 35.203.253.255 107.178.234.88
2回目 107.178.232.166 107.178.200.177 107.178.229.254 107.178.238.124 107.178.231.62 35.203.253.255 107.178.234.28
3回目 107.178.239.204 107.178.200.177 107.178.229.254 107.178.238.124 107.178.231.95 35.203.253.255 107.178.234.28
4回目 107.178.232.166 107.178.200.177 107.178.229.254 107.178.238.124 107.178.231.95 35.203.253.255 107.178.234.28
5回目 107.178.232.166 107.178.200.177 107.178.229.254 107.178.238.124 107.178.231.62 35.203.253.255 107.178.234.28
6回目 107.178.232.166 107.178.239.245 107.178.229.223 107.178.238.105 107.178.231.19 35.203.253.255 107.178.234.150
7回目 107.178.200.231 107.178.239.245 107.178.229.223 107.178.238.105 107.178.231.19 35.203.254.16 107.178.234.150
8回目 35.203.245.117 107.178.239.245 107.178.229.220 107.178.238.105 107.178.231.19 35.203.253.240 107.178.234.146
9回目 107.178.231.237 107.178.200.191 107.178.229.220 107.178.238.105 107.178.231.50 35.203.253.240 107.178.234.149

内部IP

とれませんでした。。理由はよくわからないです。

まとめ

ある程度IPレンジはあるもののちょいちょい変動していることがわかりました。
法則性は今回の検証だけではちょっとわからなかったです。googleの中の人しかわからないか..?
IPのレンジや変動するタイミングなどご存じの方いれば情報共有お願いします。

※ 曜日間隔も試したいので継続して更新します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LambdaでSlackにアカウント毎のAWS利用料金を投稿

はじめに

下記、投稿を元にSlackに利用料金を投稿する仕組みを作成したが、AWSアカウントが増えてきてアカウント毎の料金がしりたくなり改修しました。基本的な流れは下記の投稿を参照してください。
わかりやすく書かれているので非常に参考になりました。

LambdaでSlackにAWSの利用料金を投稿する(Node.js)

注意

AWSの料金を取得するCost ExplorerのAPIはバージニア北部リージョンでしか利用できないので、Lambdaはバージニア北部リージョンで作成する必要がある。

Lambdaのコード

2ヵ所は個別に修正してください。
ENDPOINT :Incoming WebhooksのURL
PAYLOADのchannel:送信先のURL

/**
 * 指定範囲の料金の集計サービス
 *
 */

const AWS = require('aws-sdk');
const costexplorer = new AWS.CostExplorer();
const moment = require('moment');
const https = require ('https');
const url = require('url');
const ENDPOINT = "★★★★★SlackのエンドポイントのURL★★★★★";
const PAYLOAD = {
  "channel": "★★★★★送信するチャンネル名★★★★★",
  "username": "AWS使用料",
  "text": ""
  //"icon_url": ""
  };

/**
 * メイン処理
 *
 */
exports.handler = async (event) => {

  // アカウント名の取得
  let accountNameJson = await getAccountName();
  // 合計文字列
  const TOTAL = "total";
  const LINE = "---------------------------------------------------------\n";
  // 出力用の文字列取得
  var outPutStr = `昨日(${yesterday})のAWSの使用料は以下の通りです。\n\n`;
  await requestToAPI(outPutStr);
  // アカウントリスト
  var accountList = [];
  // アカウント毎の合計
  var accountCost = 0;

  for(let i=0; i<accountNameJson.DimensionValues.length; i++) { // アカウント毎
    var accounts = accountNameJson.DimensionValues[i];
    var accountNo = accounts.Value;
    var accountName = accounts.Attributes.description + '(' + accountNo + ')';
    outPutStr = "```\n"
    // アカウント名
    outPutStr = outPutStr + "" + accountName + "\n";
    // アカウント毎の全サービスの料金取得
    SERVICE_FEE_PARAMS.Filter.Dimensions.Values = [accountNo];
    var costServiceRaw  = await getCost(SERVICE_FEE_PARAMS);
    for(let i=0; i<costServiceRaw.ResultsByTime.length; i++) { // 日毎チェック
      var services = costServiceRaw.ResultsByTime[i].Groups;
      for(let j=0; j<services.length; j++) { // サービス毎チェック
        // サービス
        var service = services[j];
        // サービス名
        var serviceName = service.Keys[0];
        // ディスカウント適用前(EDP割引等)のコスト
        var cost = service.Metrics.UnblendedCost.Amount;
        // 0$の場合は出力しない
        if (cost != "0") {
          // サービス毎の料金
          outPutStr = outPutStr + getNot0Fee(serviceName, cost);
          // アカウント毎の合計に加算
          accountCost = accountCost + Number(cost);
        }
      }
    }
    // アカウント毎の合計
    outPutStr = outPutStr + LINE + getNot0Fee(TOTAL, accountCost) + "\n";
    // アカウント毎の情報を格納
    accoutInfo = {};
    accoutInfo.accountName = accountName;
    accoutInfo.accountCost = accountCost;
    accountList.push(accoutInfo);
    accountCost = 0;
    outPutStr = outPutStr + "```\n"
    await requestToAPI(outPutStr);
  }

  // 料金の降順にソート
  accountList.sort(function(a,b){
    if(a.accountCost > b.accountCost) return -1;
    if(a.accountCost < b.accountCost) return 1;
    return 0;
  });

  // アカウント毎の合計出力
  outPutStr = "```\n★総合計(料金の降順)\n";
  // 総合計
  var totalCost = 0;
  for(key in accountList){
    account = accountList[key]
    outPutStr = outPutStr + getNot0Fee(account.accountName, account.accountCost);
    totalCost = totalCost + account.accountCost;
  }
  outPutStr = outPutStr + LINE + getNot0Fee(TOTAL, totalCost) + "\n※ 金額は、ディスカウント適用前で、端数切捨てです。\n```\n";

  // 結果出力
  let result   = await requestToAPI(outPutStr);
  return result;
};

/* yyyy-mm-ddの書式で、集計の開始日と終了日をセット*/
const yesterday =  moment().subtract(1, 'days').format('YYYY-MM-DD');
const today = moment().subtract(0, 'days').format('YYYY-MM-DD');

/* サービス毎の料金取得パラメータ*/
const SERVICE_FEE_PARAMS = {
  "Granularity": 'DAILY',
  "Filter": {
    "Dimensions": {
      "Key": "LINKED_ACCOUNT",
      "Values": [""]
    }
  },
  "GroupBy": [
    {
      "Type": 'DIMENSION',
      "Key": 'SERVICE',
    }
  ],
  "Metrics": [ 'UnblendedCost' ],
  "TimePeriod": {
      "Start": yesterday,
      "End": today
  },
};

/* アカウント毎の名称取得パラメータ*/
const ACCOUNT_NAME_PARAMS = {
  "Dimension": 'LINKED_ACCOUNT',
  "Context": 'COST_AND_USAGE',
  "TimePeriod": {
      "Start": yesterday,
      "End": today
  },
};

/**
 * Cost Management APIsから料金情報の取得処理
 *
 * @return AWSから取得した料金情報
 */
function getCost(param) {
  console.log(`■□■□ Cost Management APIsから料金情報の取得処理 ■□■□`);
  return new Promise(
    (resolve) => {
      costexplorer.getCostAndUsage(param, (err, data) => {
        if (err) {
          console.error(err, err.stack);
          return;
        }
        resolve(data);
      });
    }
  );
}

/**
 * Cost Management APIsからアカウント情報の取得処理
 *
 * @return AWSから取得したアカウント情報
 */
function getAccountName() {
  console.log(`■□■□ Cost Management APIsからアカウント情報の取得処理 ■□■□`);
  return new Promise(
    (resolve) => {
      costexplorer.getDimensionValues(ACCOUNT_NAME_PARAMS, (err, data) => {
        if (err) {
          console.error(err, err.stack);
          return;
        }
        resolve(data);
      });
    }
  );
}

/**
 * Slackへ料金情報を投稿処理
 *
 * @param outPutStr Slack出力文字列
 * @return Slackへ投稿リクエストを投げた結果のレスポンス
 */
function requestToAPI(outPutStr) {

  console.log(`■□■□ Slackへ料金情報を投稿処理 ■□■□`);
  console.log(`■□■□ Slackへ料金情報を投稿処理 ■□■□`);
  return new Promise(
    (resolve, reject) => {
      PAYLOAD.text = outPutStr;

      console.log('■□■□■□ SlackへPOSTリクエスト:\n' + PAYLOAD.text);

      const parser = url.parse(ENDPOINT);
      let req, body = '';

      let options = {
        host : parser.host,
        port : 443,
        path : parser.path,
        method : 'POST',
        headers : {
          'Content-Type' : 'application/json'
        }
      };

      /* APIへのリクエスト */
      req = https.request(options, (res) => {
        res.setEncoding('utf8');
        /* レスポンスボディのデータ読み込み処理(dataイベント発生)*/
        res.on('data', (chunk) => {
          body += chunk;
        });
        /* 読み込むデータが無くなった時の処理(endイベント発生) */
        res.on('end', () => {
          if(res.statusCode == 200) {
            console.log('■□■□ レスポンス情報 ');
            console.log(body);
            resolve(body);
          } else {
            console.log('■□■□ レスポンス情報なし ');
          }
        });
      });

      req.write(JSON.stringify(PAYLOAD));
      req.on('error', (e) => {
        console.error(`problem with request: ${e.message}`);
        reject(e);
      });
      req.end();
  });
}

/**
 * 投稿用の料金名と料金を取得します。
 * 料金が0の場合は、空文字を返します。
 *
 * @param feeName 料金名
 * @param fee 料金
 * @return 投稿する料金
 */
function getNot0Fee(feeName, fee) {
  let roundFee = Math.round(fee  * 10) / 10;
  if (roundFee == 0) {
    return '';
  }
  return paddingright('' + feeName, ' ', 50)+ ' :' + roundFee + '$\n';
}

/**
  右埋めする処理
  指定桁数になるまで対象文字列の右側に
  指定された文字を埋めます。
  @param val 右埋め対象文字列
  @param char 埋める文字
  @param n 指定桁数
  @return 右埋めした文字列
**/
function paddingright(val, char, n){
  for(; val.length < n; val+=char);
  return val;
}

投稿

このように投稿されます。
fee.jpg

※アカウント・使用しているサービスが増えるとSlackの文字数の上限を超えて、ズタズタになってしまったので、アカウント毎にSlackに投稿しています。

その他

作成したLambdaをCloudWatch Eventsで毎日決まった時間にLambdaを起動するようにすれば、毎日Slackに料金が投稿されます。

参考

・LambdaでSlackにAWSの利用料金を投稿する(Node.js)
https://qiita.com/tamura_CD/items/33cceb2eac7f1f2fe221
・APIリファレンス
https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

自分用メモ

// 制限
// 毎秒1cm
// 1=<L<=10
// 1=<n<=10
// 0<=X<=L
// 求めるもの。
// 毎秒1cmで歩く全ての蟻が竿の端から落ちるまでの長さ。

// 竿の長さ
const LTotalCm = 10;
// 蟻の数
const nCounter = 2;
// 蟻の現在地から左端までの長さ。
const xMeter = [3, 5];

const Calculate = () => {
  // それぞれの蟻の左端までの最小の時間を計算
  let minArray = [];
  // for文を回す。
  for (let i = 0; i < nCounter; i++) {
  // 蟻のいる位置から端までの距離の中で最小値を取得する。
  minArray.push(LTotalCm - xMeter[i])
  }
  // それぞれの蟻の右端までの最大の時間を計算
  let maxArray = [];
  // for文を回す。
 for (let i = 0; i < nCounter; i++) {
  // 蟻のいる位置から端までの距離の中で最大値を取得する。(蟻の歩く時間かける毎秒1秒を出したいのはわかる。)
  maxArray.push(LTotalCm - xMeter[i]);
 }
 console.log('minT:', Math.min(...minArray), 'maxT:', Math.max(...maxArray));
}
Calculate();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コーディングスキルテストの回答(超基礎)

アリ本と呼ばれる、プログラミングスキルコーディング本の回答です。
自分用と、問題が知りたい方は、書籍を見て頂きたいので、
詳細は書きませんが、ざっくり蟻の移動時間の把握です。

// 制限
// 毎秒1cm
// 1=<L<=10
// 1=<n<=10
// 0<=X<=L
// 求めるもの。
// 毎秒1cmで歩く全ての蟻が竿の端から落ちるまでの長さ。

// 竿の長さ
const LTotalCm = 10;
// 蟻の数
const nCounter = 2;
// 蟻の現在地から左端までの長さ。
const xMeter = [3, 5];

const Calculate = () => {
  // それぞれの蟻の左端までの最小の時間を計算
  let minArray = [];
  // for文を回す。
  for (let i = 0; i < nCounter; i++) {
  // 蟻のいる位置から端までの距離の中で最小値を取得する。
  minArray.push(LTotalCm - xMeter[i])
  }
  // それぞれの蟻の右端までの最大の時間を計算
  let maxArray = [];
  // for文を回す。
 for (let i = 0; i < nCounter; i++) {
  // 蟻のいる位置から端までの距離の中で最大値を取得する。(蟻の歩く時間かける毎秒1秒を出したいのはわかる。)
  maxArray.push(LTotalCm - xMeter[i]);
 }
 console.log('minT:', Math.min(...minArray), 'maxT:', Math.max(...maxArray));
}
Calculate();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsのテスティングフレームワークmochaの使い方

Mochaとは

公式サイトによると、Mocha(モカ)とは以下のようなものらしいです。

Mochaは、豊富な機能を持つJavaScriptのテストフレームワークであり、Node.jsおよびブラウザーで実行されます。また、非同期処理のテストも簡単に行えます。Mochaによるテストは連続して実行されるため、対処できていない例外処理も抜け漏れなくテストすることができ、柔軟で正確なレポートを作成できます。
引用・参考:Mocha公式

私の勉強不足のため、よくわかりませんが、すごいテストフレームワークということに間違いないようです。

それでは公式サイトに沿って使っていきましょう。
Node.jsのインストールが完了していることが前提です。

Mochaでテストをしてみる

まずは、コンソールにて以下のように入力していきます。

console
$ mkdir test
$ cd test
$ npm init
$ npm install mocha
$ touch test.js

・1行目でtestと言う名のディレクトリを作成しています。
・2行目でtestディレクトリに移動しています。
・3行目でpackage.jsonを生成しています。
・4行目でmochaをインストールしています。
・5行目でtestディレクトリ内にtest.jsというファイルを作成しています。

続いて、test.js内にmochaの公式の書き方に沿って、テストを記述していきます。

test.js
const assert = require('assert');
describe('Array', () => {
  describe('#indexOf()', () => {
    it('指定された値が見つからない場合は -1 を返します。', () => {
      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});

・1行目でassertモジュールを読み込んでいます。
これはmocha自体にはアサーションという、プログラムの正当性をチェックする機能がないためです。

・2行目は、describe("テストの対象", () => { ... })を意味し、
第二引数に、テストの処理を無名関数として渡します。
つまり、「テストの対象」と「どういうテストを行うか」を宣言しています。
Arrayとは配列を意味し、配列をテストの対象とすることを意味しています。

・3行目も同じで、テストの対象とどういうテストを行うかを宣言しています。関数処理の場合は'#'とつけるのが良いようです。
indexOf()メソッドは引数に与えられた内容と同じ内容を持つ配列要素の内、最初のものの添字を返します。存在しない場合は -1 を返します。

今回は、ArrayとindexOf()をテストの対象としています。

説明を続けます。

・4行目は、it("テストの内容", () => { ... })
第一引数では、テストがどんなテストであるのかを記述し、第二引数は、それをテストするための関数を書いています。

・5行目は、assert.equal(テスト対象, 想定されるテスト結果)となります。
今回は、assert.equal([1, 2, 3].indexOf(4), -1)で
配列[1, 2, 3]の中に、4という数字が入っているはずがないので、-1という数字を返すことを想定しています。

これで、mochaのテストの記述が終わりました。
続いて、コンソールに戻り、以下を入力し、テストの実行を行います。

console
$ ./node_modules/mocha/bin/mocha
> Array
    #indexOf()
      ✓ 指定された値が見つからない場合は -1 を返します。


  1 passing (9ms)

./node_modules/mocha/bin/mochaはテストを実行するコマンドです。
テスト結果がうまくいったので、✓ 指定された値が見つからない場合は -1 を返します。という結果が出ました。

ただ、このままだとテストをするたびに長いコマンドを打たなくてはならず、面倒なので、
package.jsonでテストスクリプトを設定します。

package.json
"scripts": {
    "test": "node_modules/mocha/bin/mocha"
  }

これで簡単にtestが実行できるようになりました。
コンソールに戻り、以下のコマンドを実行します。

console
$ npm test

先ほどと同じ結果が表示されれば、完了です。

beforeとafterでテストの前後に記述する

テストを実行する前後に実行する before/after 関数を設定することができます。すべてのitの前後で実行するするには、 beforeEach/afterEach を使います。

describe("test", function() {

  before(() => alert("テストが実行される準備ができました"));
  after(() => alert("テストの実行が完了しました"));

  beforeEach(() => alert("テストが始まります"));
  afterEach(() => alert("テストが終わります"));

  it('test 1', () => alert(1));
  it('test 2', () => alert(2));

});

結果は以下のようになります。

テストが実行される準備ができました (before)
テストが始まります (beforeEach)
1
テストが終わります (afterEach)
テストが始まります (beforeEach)
2
テストが終わります (afterEach)
テストの実行が完了しました (after)

以上になります。

参考文献:現代の JavaScript チュートリアル

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud Foundry + Node.js で Hello World

概要

  • Cloud Foundry 上にシンプルな Node.js アプリケーションをデプロイして動作させる
  • Cloud Foundry 環境は Pivotal Web Services を使用する

ソースコード

ソースコード一覧

.
├── Procfile
├── index.js
├── manifest.yml
└── package.json

Procfile

Procfile にはプログラムの開始コマンドを記述する。
今回は node コマンドで index.js を実行するよう指定している。
コロン左側にはプロセスタイプを指定する。プロセスタイプの web は HTTP 通信を処理することを意味している。

web: node index.js

参考: Production Server Configuration | Cloud Foundry Docs

index.js

HTTP リクエストを受けて結果を返すプログラム。
環境変数 PORT のデフォルト値は 8080 になっている。
ローカル環境でも動作するよう、環境変数が存在しない場合はポート番号に 8080 を指定している。

const http = require('http')
http.createServer(function (req, res) {
  let body =
    '<html><body>' + "\n" +
    '<h1>Hello, world.</h1>' + "\n" +
    `<div>process.env.PORT: ${process.env.PORT}</div>\n` +
    '</body></html>' + "\n"
  res.writeHead(200, {'Content-Type': 'text/html'})
  res.end(body)
}).listen(process.env.PORT || 8080)

参考: Tips for Node.js Applications | Cloud Foundry Docs

manifest.yml

デプロイするアプリの環境設定ファイル。
random-route に true を指定することで衝突しないホストを自動生成している。

---
applications:
- name: my-nodejs-app
  memory: 512M
  instances: 1
  random-route: true
  buildpacks:
  - https://github.com/cloudfoundry/nodejs-buildpack

参考:

package.json

Node.js アプリケーションの設定ファイル。
Node.js のバージョンを指定しておく。

{
  "name": "my-nodejs-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "engines": {
    "node": "~>12"
  }
}

参考:

デプロイ

今回は、 Pivotal Web Services の Cloud Foundry 環境にデプロイしている。組織 (Org) とスペース (Space) は事前に作成している。

ログインする。

$ cf login -a https://api.run.pivotal.io
API エンドポイント: https://api.run.pivotal.io
(以下略)

組織とスペースを設定。

$ cf target -o myorg -s development
API エンドポイント   https://api.run.pivotal.io
API バージョン:      2.142.0
ユーザー:            you@example.com
組織:                myorg
スペース:            development

デプロイするソースコードを用意する。

$ tree
.
├── Procfile
├── index.js
├── manifest.yml
└── package.json

0 directories, 4 files

cf push コマンドでアプリケーションをデプロイする。

$ cf push
you@example.com としてマニフェストから組織 myorg / スペース development にプッシュしています...
(中略)
アプリが開始するのを待機しています...

名前:                   my-nodejs-app
要求された状態:         started
経路:                   my-nodejs-app-xxxx-xxxxx.cfapps.io
最終アップロード日時:   Sat 30 Nov 08:26:22 JST 2019
スタック:               cflinuxfs3
ビルドパック:           nodejs

タイプ:           web
インスタンス:     1/1
メモリー使用量:   512M
開始コマンド:     node index.js
     状態   開始日時               cpu    メモリー            ディスク          詳細
#0   実行   2019-11-29T23:26:33Z   0.0%   512M の中の 14.2M   1G の中の 82.4M   

デプロイしたアプリケーションに curl コマンドでアクセスして動作していることを確認。

$ curl https://my-nodejs-app-xxxx-xxxxx.cfapps.io/
<html><body>
<h1>Hello, world.</h1>
<div>process.env.PORT: 8080</div>
</body></html>

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む