20200625のNode.jsに関する記事は4件です。

Node (Express + TypeScript) を Docker Image で起動するときの Tips

対応実施前

  • express + typescript の hello world アプリを用意し、official の node image を利用した場合の image サイズ
REPOSITORY                 TAG                  IMAGE ID            CREATED             SIZE
test-node-app-before       latest               620fd8e74f42        3 seconds ago       1.32GB
  • package.json
  "scripts": {
    "start": "cross-env NODE_ENV=production ts-node --transpile-only src/server.ts",
  • Dockerfile
    • ts-node にて起動
    • official の node image を利用
FROM node:12.18.1

WORKDIR /app
ENV JWT_SECRET="dummy" PORT="80"
COPY package.json /app
COPY src /app/src
RUN cd /app && npm install

CMD ["npm","start"]

対応内容

tsc コマンドで trance compile

  • package.json の build に tsc を追加
"scripts": {
    "build": "tsc",

tsconfig.json の target / module を書き換え

JavaScriptにはいくつかのモジュールパターン(CommonJSやAMD、ECMAScriptなど)がある。TypeScriptをJavaScriptに変換する際、どのモジュールパターンにするかをmoduleに指定する必要がある。

moduleには'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'esnext'などを指定できる。デフォルトはtarget === "es3" or "es5" ? "commonjs" : "es6"、つまりtargetがes3かes5ならcommonjsとなる。
  • tsconfig.json の targetmodule を以下に指定
{
    "compilerOptions": {
      "sourceMap": true,
      "target": "es2017",
      "module": "commonjs",

マルチステージビルドを実行 / apline linux を利用

  • Dockerfile を以下のように編集
FROM node:12.18.1-alpine3.12 AS build

WORKDIR /app
COPY . /app
RUN npm ci && npm run build


FROM node:12.18.1-alpine3.12

WORKDIR /app
ENV JWT_SECRET="dummy" PORT="80"
COPY --from=build /app/dist /app/dist
COPY package.json /app
COPY package-lock.json /app
RUN npm ci --production

CMD ["npm","start"]

devDependencies に移動できるパッケージがないか確認する

  • 以下が dependencies に記載されていたので修正
"jest": "^26.0.1",
"ts-jest": "^26.1.0",
"ts-node": "^8.10.2",
"tslint-config-airbnb": "^5.11.2",
"typescript": "^3.9.5"

結果

  • 101MB まで image の容量を削減することができた
docker images
REPOSITORY     TAG     IMAGEID       CREATED      SIZE
test-node-app  latest  9cdb52f0f5cf  2 hours ago  101MB
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodejs SSLサーバ構築 チートシート

説明はこちら
nodejs サーバ構築 チートシート

node SSLサーバ チートシート

構成

├ server.js
├ package.json # nodejs サーバ構築 チートシート と同じ
├ download.txt # ダウンロード用ファイル。中身はなんでもok
└ cert
 ├ server.cnf
 ├ server.crt
 └ server.key

server.js
'use strict';
var log4js = require('log4js');
var logger = log4js.getLogger('test');
log4js.configure({ appenders: { default: { type: "console", layout: { type: "basic" } } }, categories: { default: { appenders: ['default'], level: 'debug' }}, replaceConsole: true, level: 'info' })
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
var https = require('https');
var fs = require('fs');
var options = {
  key:  fs.readFileSync(__dirname +'/cert/server.key'),
  cert: fs.readFileSync(__dirname +'/cert/server.crt')
};
var util = require('util');
var host = 'localhost'; 
var port = 8443;

app.use( function(req,res,next) {
    logger.info('%s %s', req.method, req.url);
    logger.info('header='+JSON.stringify(req.headers));
    logger.info('query='+JSON.stringify(req.query));
    logger.info('body='+JSON.stringify(req.body));
    next();
});

var server = https.createServer(options,app).listen(port, function() {});
console.log('*** server start https://%s:%s ***',host,port);
logger.info('*** server start https://%s:%s ***',host,port);
server.timeout = 30000;
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0; // オレオレ証明書サーバへリクエストする場合

// top page
app.all('/', function(req, res) { // methodを限定しない
    res.header({'Content-type': 'text/html'});
    res.end(util.format('<h1>http://%s:%s for honya</h1>',host,port)); // サーバの用途が分かるようなタイトルを表示する
});

// json を返す場合 REST形式
app.all('/rest/v1/user/:id', function(req,res) { 
    logger.info('id='+req.params.id); // url パラメータの参照

    let json = {
        id: req.params.id,
        name: "honya",
        dob: (new Date('2020-01-01 12:34:56')).getTime(),
    }
    res.status(201).json(json);
});

// html を返す場合 ASP.NET形式
app.all('/asp/honya.asmx', function(req,res) {
    let html = `
<html>
<body>
    <h1>request url: ${req.method} ${req.url}</h1>
    <div>response</div>
</body>
</html>
`;
    res.end(html); 
});

// 存在するファイルをダウンロードする場合 cgi形式
app.all('/cgi-bin/honya.cgi', function(req,res) { 
    res.download(__dirname + '/download.txt','server.log'); 
});

// csvデータをダウンロードする場合 php形式
app.all('/php/honya.php', function(req,res) { 
    let header = {
        'Content-disposition': 'attachment; filename=server.csv',
        'Content-type': 'text/plain; charset=utf-8',
    }
    res.header(header);

    let csv = "id,name\n1,honya\n";
    res.send(csv); 
});
server.cnf
[req]
distinguished_name = req_distinguished_name
x509_extensions = SAN
prompt = no

[req_distinguished_name]
C = Country initials like US, RO, GE
ST = State
L = Location
O = Organization Name
OU = Organizational Unit 
CN = localhost

[SAN]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

server.crt と server.key はコマンドで作成

cd ~/cert # 証明書フォルダへ移動
openssl req \
  -newkey     rsa:4096 \
  -keyout     server.key \
  -x509 \
  -nodes \
  -out        server.crt \
  -subj       "//CN=localhost" \
  -days       3650 \
  -reqexts    SAN \
  -extensions SAN \
  -config     server.cnf

起動

nodejs サーバ構築 チートシート と同じ

常時起動

nodejs サーバ構築 チートシート と同じ

参考

証明書の取り込みは以下のStep 6.を参照
create a trusted self-signed SSL cert for localhost
※取り込んだあと、ブラウザの再起動をお忘れなく σ(゚∀゚ )オレ

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

アリババクラウドが提供するサービスを利用したNode.jsアプリケーションの展開

このチュートリアルでは、Alibaba Cloudが提供するサービスを利用して、本番環境に対応したNode.jsアプリケーションのセットアップを取り上げます。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

序章

Node.jsは、高性能で高速なサーバーサイドアプリケーションを構築するために使用されるJavascriptのランタイム環境です。Node.js サーバーサイドアプリケーションは、間違った方法でデプロイされた場合、本当に遅くなり、トラフィックの増加をサポートできなくなる可能性があります。

このチュートリアルでは、アリババクラウドサービスが提供する素晴らしいサービスを使用して、本番環境に対応したNode.jsアプリケーションを設定する方法を説明します。Node.js Blogというアプリケーションをデプロイしますが、そのために以下のAlibaba Cloudサービスを使用します。

  • Elastic Compute Service:CPU、メモリ、OS、帯域、ディスクなどの基本的なコンピューティングコンポーネントを含む仮想コンピューティング環境。このサービスを使用してLinuxインスタンスを起動し、そこにNode.jsアプリケーションをデプロイします。
  • ApsaraDB for MongoDB: 安全で信頼性が高く、弾力的にスケーラブルなクラウドデータベースサービスです。このサービスを使ってNode.jsアプリケーション用のMongoDBデータベースを起動します。

前提条件

このチュートリアルを始める前に、以下のものが必要です。

  • デプロイする Node.js アプリケーション。以下の Github リポジトリにあります。
  • Ubuntu 16.04 64ビットECSインスタンス。このチュートリアルを使って作成することができます。
  • ApsaraDB for MongoDB のインスタンス。このチュートリアルを使って作成することができます。
  • ECS インスタンスへ接続します。SSH を使って接続するには、このチュートリアルに従ってください。

  • : ECS インスタンスと ApsaraDB for MongoDB インスタンスがまったく同じゾーンに作成されていることを確認してください。そうすることでアプリケーションのパフォーマンスが向上し、接続の問題や制限を回避することができます。

ステップ 1 - すべてのサーバーの依存関係をインストールする

このステップでは、node js アプリケーションを実行するために必要なすべての依存関係をインストールします。手順に従うには、ECS インスタンスにログインしていることを確認してください。

Git のインストール

GitHubからアプリケーションのコードを取得できるようにするには、gitを使ってリポジトリをクローンする必要があります。以下のコマンドでインスタンスにgitをインストールしてみましょう。

sudo apt-get install git

インストールを確認するには、以下のコマンドを実行してインストールされているgitのバージョンを確認します: git --version.私の場合、以下のような出力が出てきました: git version 2.7.4.

Node.jsのインストール

node.jsアプリケーションを実行するためには、このアプリケーションを実行するために必要な特定のnode.jsのバージョンをインストールする必要があります。私たちのnode.jsブログの場合は、Nodeのバージョン8以上が必要です。インストールするには、以下のコマンドを実行します。

curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh

このコマンドは、nodeource_setup.shというファイルをダウンロードします。これはnode jsをインスタンスにインストールするためのシェルスクリプトです。このシェルスクリプトを以下のコマンドで実行します。

sudo bash nodesource_setup.sh

そして最後に、インストールを完了させるために、以下を実行します。

sudo apt-get install -y nodejs

node jsのインストールを確認するには、node -vを実行して、インストールされているnodeのバージョンを確認します。私の場合は、最新のバージョン8のサブバージョン(v8.13.0)がインストールされています。

Nginxのインストール

Nginx は世界で最も人気のあるウェブサーバーの一つであり、いくつかの大規模なサイトで使用されています。アプリケーションをデプロイするために、プロキシサーバとしてnginxを使用してアプリケーションを提供します。これについては後ほど詳しく説明します。nginxをインストールするには、以下を実行してください。

sudo apt-get install nginx

インストールをテストするには、nginx -vを実行して、インストールされているバージョンを確認します。私の場合、以下のような出力が出てきました:nginx version: nginx/1.10.3 (Ubuntu)

PM2プロセスマネージャのインストール

Node.jsアプリケーションは非常に脆いため、運用環境では強力なプロセスマネージャを使用してnode.jsアプリケーションを監視し、プロセスが常に実行されていることを確認する必要があります。このケースで使用するプロセスマネージャは pm2 と呼ばれ、node.js パッケージとして提供されています。PM2はnode jsアプリケーションのクラッシュを監視するだけでなく、node jsアプリケーションを自動的にスケーリングし、インスタンス上のすべてのCPUを最適化します。PM2をインストールするには、次のように実行します。

npm i -g pm2

インストールを確認するには、pm2 -v を実行してください。私の場合、以下のような出力が表示されました。3.2.2.

ステップ 2 - アプリケーションコードの取得

このステップでは、インスタンス上で実行できるようにアプリケーションリポジトリをクローンします。

アプリケーションコードのクローン

アプリケーションのコードをクローンするには、以下のコマンドを実行します。

cd /home
git clone https://github.com/bahdcasts/node-js-blog.git

プロジェクトの依存関係のインストール

プロジェクトの依存関係をインストールするには、以下のコマンドを実行します。

cd /home/node-js-blog
npm install

環境変数の設定

ほとんどのnode jsアプリケーションでは、サーバの環境変数を設定する必要があります。このプロジェクトではdot-envというパッケージを使用しており、このパッケージを使用することで、すべての環境変数を.envというファイルに配置することができます。このファイルを作成し、環境変数を追加します。以下のコマンドを実行します。

cd /home/node-js-blog
nano .env

ターミナルコードエディタnanoを使って.envという新しいファイルを開きます。ファイルの中に以下の内容を入れます。

PORT=3000
DB_URI=
CLOUDINARY_API_KEY=xxxx
CLOUDINARY_API_SECRET=xxxx
CLOUDINARY_NAME=xxx
EXPRESS_SESSION_KEY=d29z4944SxETKPNWqKAkCNUjrZ4A

これらはこのプロジェクトが動作するために必要な環境変数です。PORTはNode.jsアプリケーションが動作する特定のポートです。EXPRESS_SESSION_KEYは、ユーザーセッションを暗号化するために使用されるランダムな文字列です。

また、このブログの画像はcloudinaryにアップロードされており、そのためにcloudinaryの環境変数が必要になります。このチュートリアルの焦点ではないので省略しても構いません。これは、独自のNode jsプロジェクトをデプロイする際に、独自のカスタム環境変数を設定する方法を紹介するだけです。

最後の環境変数は DB_URI で、これが ApsaraDB for MongoDB インスタンスに接続するための環境変数になります。これは後のステップで追加します。

: nano editorを終了するには、CTRL + Xを押してからYを押してください。

ステップ3 - MongoDBインスタンスへの接続

このステップでは、先ほど作成した ApsaraDB for MongoDB インスタンスにアプリケーションを接続します。

ApsaraDB for MongoDB のサーバー IP をホワイトリスト化する

デフォルトでは、MongoDB インスタンスはどのアドレスからの接続も許可しません。コンソールを使って、ECS インスタンスの IP アドレスをホワイトリストに登録して、アプリケーションが正常に接続できるようにします。

まず、コンソールにアクセスして MongoDB インスタンスを開き、「データセキュリティ」タブにアクセスします。ここで、Manually modify ボタンをクリックして新しい IP アドレスを追加します。

image.png

変更を保存します。このアクションは、アプリケーションがこの MongoDB インスタンスに接続するための許可を与えます。

DB_URI接続文字列の設定

Database Connection タブから、データベースへの接続文字列を取得します。

image.png

環境変数 DB_URI の設定

接続文字列をコピーし、環境変数 DB_URI の値を更新します。nano .envを使うと、.envファイルは以下のようになります。

PORT=3000
DB_URI="mongodb://root:passWORD1234@dds-gs50fb23291731d41.mongodb.singapore.rds.aliyuncs.com:3717,dds-gs50fb23291731d42.mongodb.singapore.rds.aliyuncs.com:3717/admin?replicaSet=mgset-300215248"
CLOUDINARY_API_KEY=132255634713478
CLOUDINARY_API_SECRET=aeBNzdZFcju9ZEAiL7KsEMbBlFI
CLOUDINARY_NAME=bahdcoder
EXPRESS_SESSION_KEY=d29z4944SxETKPNWqKAkCNUjrZ4P

: 文字列中の***は、インスタンスの実際のルートパスワードに置き換えることを忘れないでください。

ステップ4 - Nginxの設定をする

このステップでは、nginx ウェブサーバが node js アプリケーションを利用できるように、nginx の設定を行います。

これを行うために、デフォルトのnginx設定ファイルを編集します。以下のコマンドを実行します。

# Empty the current configuration file
echo > /etc/nginx/sites-available/default

# Open the file using nano
nano /etc/nginx/sites-available/default

このファイルの内容を以下のように置き換えてください。

server {
  listen 80;

  server_name 47.88.230.255;

  location / {
      proxy_pass http://localhost:3000;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_cache_bypass $http_upgrade;
  }
}

注意すべき2つの重要な設定は、server_nameproxy_passです。server_name は理想的にはアプリケーションのドメイン名であるべきで、ユーザーが特定のサイトにアクセスしようとした場合に基礎となる設定を使用するよう nginx に指示します。この場合、このアプリケーションのためにドメイン名を設定しないので、単に ECS のインスタンスの IP アドレスを使用します。

第二に、proxy_pass の設定は nginx に、http://localhost:3000 上で実行されているプロセスに、server_name のために入ってくるすべてのトラフィックをリダイレクトするように指示します。つまり、ユーザーが47.88.230.255にアクセスしようとすると、nginxはユーザーのリクエストをポート3000で動作しているnode jsアプリケーションにリダイレクトします。ここでは3000番ポートを使用していますが、これはnode jsアプリケーションを起動するポートであるためです。

: 47.88.230.255をECSインスタンスの実際のIPアドレスに置き換えることを忘れないでください。

最後に、設定変更を保存するためにnginxを再起動する必要があります。

以下のコマンドを実行してください。

sudo systemctl restart nginx

ステップ5 - アプリケーションの起動

最後に、プロセスマネージャPM2を使用してnode jsアプリケーションを起動する必要があります。 これを行うには、以下のコマンドを実行します。

cd /home/node-js-blog

pm2 start index.js --name "blog"

このコマンドは、index.jsファイルをルートとして使用してアプリケーションを起動し、このプロセスにblogという名前を与えます。このコマンドは以下のような出力が出てきます。

image.png

アプリケーションログのトラッキング

アプリケーションをデバッグして監視するには、以下のコマンドを使用して、アプリケーションからログに記録されたすべてのメッセージをリアルタイムで表示することができます。

pm2 logs blog

これはblogというプロセスのログをプリントアウトします。出力は以下のようになります。

image.png

結論

このチュートリアルでは、Alibaba Cloud Elastic Compute Service (ECS) と ApsaraDB for MongoDB を使用して本番アプリケーションをデプロイする方法を学びました。また、PM2と呼ばれる高度なプロセスマネージャを使用してNode.jsアプリケーションを管理し、自動的にスケーリングしました。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

nodejs サーバ構築 チートシート

node サーバとは

nodejsだけで構築できるので、とにかく早い
他のPCからもアクセスできる
※ポート開放をお忘れなく(EC2の場合はセキュリティグループ) σ(゚∀゚ )オレ

背景

他ベンダーの開発が遅れて、いつまでたっても接続テストが始まらないー
モック作るの面倒くさいー
、、と思ったら オー人事オー人事 nodeサーバ

node サーバ (http) チートシート

構成

├ server.js
├ package.json
└ download.txt # ダウンロード用ファイル。中身はなんでもok

server.js
'use strict';
var log4js = require('log4js');
var logger = log4js.getLogger('test');
log4js.configure({ appenders: { default: { type: "console", layout: { type: "basic" } } }, categories: { default: { appenders: ['default'], level: 'debug' }}, replaceConsole: true, level: 'info' })
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
var http = require('http');
var util = require('util');
var host = 'localhost'; 
var port = 8000;

app.use( function(req,res,next) {
    logger.info('%s %s', req.method, req.url);
    logger.info('header='+JSON.stringify(req.headers));
    logger.info('query='+JSON.stringify(req.query));
    logger.info('body='+JSON.stringify(req.body));
    next();
});

var server = http.createServer(app).listen(port, function() {});
console.log('*** server start http://%s:%s ***',host,port);
logger.info('*** server start http://%s:%s ***',host,port);
server.timeout = 30000;
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0; // オレオレ証明書サーバへリクエストする場合

// top page
app.all('/', function(req, res) { // methodを限定しない
    res.header({'Content-type': 'text/html'});
    res.end(util.format('<h1>http://%s:%s for honya</h1>',host,port)); // サーバの用途が分かるようなタイトルを表示する
});

// json を返す場合 REST形式
app.all('/rest/v1/user/:id', function(req,res) { 
    logger.info('id='+req.params.id); // url パラメータの参照

    let json = {
        id: req.params.id,
        name: "honya",
        dob: (new Date('2020-01-01 12:34:56')).getTime(),
    }
    res.status(201).json(json);
});

// html を返す場合 ASP.NET形式
app.all('/asp/honya.asmx', function(req,res) {
    let html = `
<html>
<body>
    <h1>request url: ${req.method} ${req.url}</h1>
    <div>response</div>
</body>
</html>
`;
    res.end(html); 
});

// 存在するファイルをダウンロードする場合 cgi形式
app.all('/cgi-bin/honya.cgi', function(req,res) { 
    res.download(__dirname + '/download.txt','server.log'); 
});

// csvデータをダウンロードする場合 php形式
app.all('/php/honya.php', function(req,res) { 
    let header = {
        'Content-disposition': 'attachment; filename=server.csv',
        'Content-type': 'text/plain; charset=utf-8',
    }
    res.header(header);

    let csv = "id,name\n1,honya\n";
    res.send(csv); 
});
package.json
{
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "log4js": "^6.3.0"
  }
}

起動

cd ~/ # ルートフォルダへ移動
npm install # 初回のみ
node server.js
# 終了するときは Ctrl + C

常時起動

cd ~/ # ルートフォルダへ移動
npm install # 初回のみ
npm install forever -g # 初回のみ
forever start -l server.log -a server.js
# 終了するときは forever stopall # 全部止まるけど何個も立ち上げる方が悪い(笑)

あとがき

形式について

REST、ASP.NET、cgi、php のurlで4パターンのレスポンスを用意しました。
これが1ファイルで実現できるのは nodejs しかないでしょう。

接続テストができず、100近いurlを用意したことがありますが、このときはjsonを用意して、そこから値を返すようにしました。これは nodejs でなくてもできますが。

環境依存について

環境ごとに設定ファイルがあると思いますが、nodeサーバならhost以外は同じにできます。
例)
honyaUrl: http://devhost:8000/v1/customer/get.asmx(開発環境)
honyaUrl: http://prodhost/v1/customer/get.asmx(本番環境)

凡ミス多いが多い私には、こういう小っさいところが大きいです

モックだよ!

用途がモックなので無駄なことは省きました。

・複数台建てる(portを変えれば いくつでもok)
・portを消す(apache または nginx を使用する)
・チェックを入れる(methodチェック、ヘッダーチェック、入力チェックなど)
・モックから別のサービスへアクセスする(nodejs request のチートシート

これらを要求されたら無駄のラッシュで切り抜けてください
無駄

nodejs SSLサーバ構築 チートシート

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