戻る

pm2+expressでデプロイ環境の構築

PM2のデプロイのイメージ

pm2はforeverと類似のプロセスマネージャですが、foreverと比べると以下のようなメリットがあります。

  • マシンのコア数に応じて複数のプロセスを起動し、バランシングを行うことができる
  • CPU使用率、メモリ使用量、再起動回数などの項目を監視することができる
  • Gitリポジトリからのデプロイ機能を備えている

というわけで今回、Gitリポジトリからのデプロイ機能をexpress(typescript)で構築したサーバで試してみました。デプロイのイメージは以下のような感じです。

f:id:takezoe:20140722143334p:image:w500

基本として、pm2でデプロイを行う場合の注意点としてNodeのモジュールはすべてアプリケーションにローカルインストールし、node_modulesディレクトリをGitリポジトリにコミットしておく必要がありますが、以下の手順では、毎回npm installを実行してオンラインから必要なNodeのモジュールをインストールしています。

環境

開発クライアント

  • Windows 10 build 19042
  • Visual Studio Code 1.57.1
  • Node.js v14.16.1
  • pm2 2.4.5
  • git 2系

デプロイするサーバ

テスト用プロジェクトを作成

プロジェクトフォルダの作成

mkdir work
cd work

必要なモジュールのインストール(Typescript使うため)

tsc, ts-nodeをインストールして、tsconfig.jsonも生成しておきます。

npm init -y

npm install -D typescript
npm install -D @types/node
npm install -D ts-node

npx tsc --init

expressのインストール

expressとtypesをインストールします。

npm install express
npm install -D @types/express

テストコードの実装

色々実装です。とりあえず実装用のファイルを作成。

touch index.ts

index.tsに以下の記述をします。とりあえずGETだけ実装。
ここで利用したAPIを再利用。

index.ts

import express from 'express'
const app: express.Express = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

//CROS対応(というか完全無防備:本番環境ではだめ絶対)
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Methods", "*")
    res.header("Access-Control-Allow-Headers", "*");
    next();
})

app.listen(3000, () => {
    console.log("Start on port 3000.")
})

type User = {
    id: number
    name: string
    email: string
};

const users: User[] = [
    { id: 1, name: "User1", email: "user1@test.local" },
    { id: 2, name: "User2", email: "user2@test.local" },
    { id: 3, name: "User3", email: "user3@test.local" }
]

//一覧取得
app.get('/users', (req: express.Request, res: express.Response) => {
    res.send(JSON.stringify(users))
})

ビルド後の出力先の変更

tsconfig.jsonの編集します。

{
  "compilerOptions": {
      :
    "outDir": "./dist",                              /* Redirect output structure to the directory. */
      :
  }
}

package.jsonの編集します。

{
  :
  "main": "dist/index.js",
    :
}

実行と動作確認

では動作確認。

実行

expressの実行は下記の通り。

npm run build
npm start

Start on port 3000.

とりあえずcurlで動作確認。

curl -s -X GET http://localhost:3000/users | python -mjson.tool

[
    {
        "email": "user1@test.local",
        "id": 1,
        "name": "User1"
    },
    {
        "email": "user2@test.local",
        "id": 2,
        "name": "User2"
    },
    {
        "email": "user3@test.local",
        "id": 3,
        "name": "User3"
    }
]

ローカルポジトリの作成(Git)

gitでローカルポジトリ作成し、上で作成したテスト用プロジェクトを登録します。

git init --initial-branch main
git add .
git commit -m "initial commit"

注意)Gitのバージョン 2.28  からconfigでgit initで作成するリポジトリのデフォルト・ブランチ名を設定できるようになっています。たとえば、以下のコマンドでデフォルト・ブランチ名をmainに設定できます。

git config --global init.defaultBranch main

これでgit initコマンドでリポジトリを作成する際に作られるブランチ名がmainになります。

リモートポジトリの作成(Github)

ローカルリポジトリからリモートリポジトリに移動させる時に、受け皿つくります。

githubにログインし、左上の赤枠で囲まれたプラスをクリックします。
github 追加.PNG
プルダウンでメニューが出てくるので、New repositoryをクリックします。
github 追加2.PNG

リポジトリの作成の画面になります。

github 追加8.PNG

Privateを選択し、Initialize this repository with a READMEはチェックしないで、「Create repository」をクリックします、github上での受け皿が完成します。

リモートポジトリへ登録(Github)

コマンドプロンプトに移動して以下のようなコマンドを打ちます。

git remote add origin [リモートリポジトリ情報]

上の[リモートリポジトリ情報]は以下の赤枠から取得します。

github 追加7.PNG

コマンドにて、githubへプッシュします。

git push origin main

githubのページをリロードすると、しっかり反映が確認できます。

PM2+デプロイ環境の作成

パッケージのインストール

npm install -g pm2

その他パッケージのインストール

npm install dotenv

envファイルを作成(.gitignoreでgithubへは登録されません)

touch .env

envファイルを以下のように編集します。GITHUB_PASSWORDはgithubのkeyをします。

GITHUB_USER=tokuda
GITHUB_PASSWORD=xxxxxxxxxxxxx
HOST=192.168.xxx.xxx
SSH_PRIVATE_KEY_PATH=~/.ssh/id_rsa

GITHUB_PASSWORDはgithubより以下の手順にします。

  1. githubへログイン
  2. プロフィール画像をクリックし、Settingsをクリック
  3. 左サイドバーで Developer settings をクリック
  4. 左のサイドバーでPersonal access tokensをクリック
  5. Generate new token をクリック
  6. トークンを使用目的に則して設定する, その後 Generate token をクリック
  7. トークンをGITHUB_PASSWORDにコピーする。

 注意)トークンはページを離れると参照不可になる。

pm2用のデプロイファイルを作成

pm2 ecosystem

ecosystem.config.jsを編集

上で作成されたecosystem.config.jsをVSCodeで開き、以下のように編集します。

require('dotenv').config();

var user = encodeURIComponent(process.env.GITHUB_USER);
var password = encodeURIComponent(process.env.GITHUB_PASSWORD);
var keyPath = process.env.SSH_PRIVATE_KEY_PATH || "~/.ssh/id_rsa";

module.exports = {
  /**
   * Application configuration section
   * http://pm2.keymetrics.io/docs/usage/application-declaration/
   */
  apps : [

    // First application
    {
      name      : "hello-world",
      script    : "npm",
      args      : "start",
      env: {
        PORT: 3000
      },
      env_production : {
        NODE_ENV: 'production',
      }
    }
  ],

  /**
   * Deployment section
   * http://pm2.keymetrics.io/docs/usage/deployment/
   */
  deploy : {
    production : {
      "key"  : keyPath,
      "user" : user,
      "host" : [process.env.HOST],
      "ref"  : "origin/main",
      "repo" : "https://"+user+":"+password+"@github.com/YujiTokuda/hello-world.git",
      "ssh_options" : "StrictHostKeyChecking=no",
      "path" : "/home/"+user+"/hello-world",
      "post-setup" : "npm install",
      "post-deploy" : "pm2 restart ecosystem.config.js --env production"
    }
  }
};

pm2ではデプロイ先を複数指定できます。

"host" : ["XXX.XXX.XXX.XXX", "YYY.YYY.YYY.YYY", "ZZZ.ZZZ.ZZZ.ZZZ"],

package.jsonを編集

スクリプトに下記コマンドを登録します。

{
  :
  "scripts": {
    "start": "npm run build && node ./dist/index.js",
    "dev:debug": "rm -rf dist && set DEBUG=* && tsc-watch --noClear --onSuccess \"node -r pino-debug ./dist/index.js\" | pino-pretty -t \"SYS:yyyy-mm-dd HH:MM:ss.l o\"",
    "build": "tsc",
    "lint": "eslint -c .eslintrc.js \"src/**/*.{js,ts,tsx}\" --quiet",
    "lint:fix": "eslint -c .eslintrc.js \"src/**/*.{js,ts,tsx}\" --quiet --fix",
    "deploy:setup": "pm2 deploy ecosystem.config.js production setup",
    "deploy:update": "pm2 deploy ecosystem.config.js production update"
  },
  :
}

デプロイの実行

npm run deploy:setup
npm run deploy:update

ローカルマシンからデプロイ先(Linuxを想定)サーバにデプロイは完了です。

デプロイ先の動作確認

再度curlで動作確認。

curl -s -X GET http://192.168.xxx.xxx:3000/users | python -mjson.tool

[
    {
        "email": "user1@test.local",
        "id": 1,
        "name": "User1"
    },
    {
        "email": "user2@test.local",
        "id": 2,
        "name": "User2"
    },
    {
        "email": "user3@test.local",
        "id": 3,
        "name": "User3"
    }
]

デプロイ先サーバにtelnetでログインし、以下のコマンドを実行します。

pm2 dashboard

最後に

デプロイ先サーバは以下を設定済みとして進めています。

1.SSHのログイン環境

2.node.js

3.PM2