- 投稿日:2020-06-25T20:53:19+09:00
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 の
target
とmodule
を以下に指定{ "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"]
- Tips:
npm ci
コマンドを利用することで依存パッケージのダウンロードとインストールの高速化が実現できる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
- 投稿日:2020-06-25T18:22:11+09:00
nodejs SSLサーバ構築 チートシート
説明はこちら
nodejs サーバ構築 チートシートnode SSLサーバ チートシート
構成
├ server.js
├ package.json # nodejs サーバ構築 チートシート と同じ
├ download.txt # ダウンロード用ファイル。中身はなんでもok
└ cert
├ server.cnf
├ server.crt
└ server.keyserver.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 = localhostserver.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起動
常時起動
参考
証明書の取り込みは以下のStep 6.を参照
create a trusted self-signed SSL cert for localhost
※取り込んだあと、ブラウザの再起動をお忘れなく σ(゚∀゚ )オレ
- 投稿日:2020-06-25T17:40:14+09:00
アリババクラウドが提供するサービスを利用した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 nodejsnode 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 アドレスを追加します。変更を保存します。このアクションは、アプリケーションがこの MongoDB インスタンスに接続するための許可を与えます。
DB_URI接続文字列の設定
Database Connection
タブから、データベースへの接続文字列を取得します。環境変数 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_name
とproxy_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
という名前を与えます。このコマンドは以下のような出力が出てきます。アプリケーションログのトラッキング
アプリケーションをデバッグして監視するには、以下のコマンドを使用して、アプリケーションからログに記録されたすべてのメッセージをリアルタイムで表示することができます。
pm2 logs blogこれは
blog
というプロセスのログをプリントアウトします。出力は以下のようになります。結論
このチュートリアルでは、Alibaba Cloud Elastic Compute Service (ECS) と ApsaraDB for MongoDB を使用して本番アプリケーションをデプロイする方法を学びました。また、PM2と呼ばれる高度なプロセスマネージャを使用してNode.jsアプリケーションを管理し、自動的にスケーリングしました。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-06-25T12:43:25+09:00
nodejs サーバ構築 チートシート
node サーバとは
nodejsだけで構築できるので、とにかく早い
他のPCからもアクセスできる
※ポート開放をお忘れなく(EC2の場合はセキュリティグループ) σ(゚∀゚ )オレ背景
他ベンダーの開発が遅れて、いつまでたっても接続テストが始まらないー
モック作るの面倒くさいー
、、と思ったらオー人事オー人事nodeサーバnode サーバ (http) チートシート
構成
├ server.js
├ package.json
└ download.txt # ダウンロード用ファイル。中身はなんでもokserver.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サーバ構築 チートシート