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

webpackをかんたんに使う(Zero Configulation)

5年前の記事のメンテを行っていたんですが、流石にBrowserifyは実務で使う可能性は低いよなあとは思います。
Browserifyの単純さも初心者にはとっつきやすいのですが、今の主流はwebpackです。
webpack入門記事はたくさんあるはずですが、設定ファイルの各パラメータの羅列だったり、BabelやReact, TypeScriptの設定まで一気に済ませてしまうような記事は初心者には難しいかもしれないですね。

というわけで、Node.jsは3日前にはじめました、みたいな人向けに書いてみようと思います。

初心者なのでwebpackだけを使ってみたいんだけど

今どきだと初心者はReactならcreate-react-app、VueならVue CLIを使って入門するんじゃないかと思います。これらはwebpackの設定を代わりに済ませてくれる便利なツールで、難しいことを知らなくて済みます。

ただ、何も理解しないままにWebアプリが作れてしまうので、一つ一つ理解していきたいタイプの人には気持ちが悪いかもしれません。

というわけで、webpackのみを使ってWebアプリを作ってみましょう。

プロジェクトを作成

下記のコマンドを実行してpackage.jsonを作ります。

npm init -y

次にnode_modules下にwebpackと開発ツールをインストールします。

npm install -D webpack webpack-cli webpack-dev-server

その結果、下記のようなpackage.jsonが生成されます。

{
  "name": "your-folder",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

ソースコードを配置する

下記の2ファイルを作成してください。

  • dist/index.html
  • src/index.js
//index.js
document.write("Hello, webpack")
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    <script src="main.js"></script>
</body>
</html>

結果、下記のようなフォルダ構成になります。

image.png

webpackを動かすには色々設定が必要ですが、無設定の場合はsrc/index.jsが入り口になります。distはwebpackが処理した後のjsファイルが出力されます。上記のフォルダ構成にしておけば無設定でも動かせるわけです。

npm-scriptsを追加

開発はnpm run dev、ビルドはnpm run buildで行えるようにしましょう。package.json内にwebpackの実行時に渡すオプションを直書きしていきます。

package.jsonscriptsにbuild, devの2行を追加します。

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode=production",
    "dev": "webpack-dev-server --hot --open --content-base dist/"
  },

実行してみる

下記のコマンドを実行してみましょう。

npm run dev

ブラウザが開いてHello, webpackが表示されればOKです。

npmのライブラリを使ってみる

今回は日時処理ライブラリdate-fnsを使ってみましょう。
下記のコマンドを実行してください。

npm install -D date-fns

次に、index.jsの内容を下記のように書き換えてください。

//index.js
const dateFns = require("date-fns")
const ja = require("date-fns/locale/ja")

document.write(dateFns.format(new Date(), "'今日は' MMM do iiii", {locale: ja.default}))

その後、npm run devをもう一度実行してみてください。

image.png

こんな表示になりましたでしょうか。

webpackはjsを張り合わせる糊

webpackにはいろんな機能がありますが、一番基本的な機能はjsの結合処理です。今回の場合はindex.jsdate-fnsライブラリが張り合わせられて、一本のjsファイルにまとめられています。

npm run buildを実行後、dist/main.jsを開くと、結合されたjsファイルを見ることができます。
npm run dev実行時にはmain.jsは出力されません。これはwebpack-dev-serverというツールの機能で、ブラウザからはmain.jsがあるように見える状態になっています。

webpackが何をしているのか、詳細を知りたい方は下記記事がおすすめです。

https://mizchi.hatenablog.com/entry/2018/11/26/164523

終わりに

この状態ではかなりできることが限られていて、例えばReactのJSXファイルやVue SFC, TypeScriptファイル等を読ませてもエラーになります。
BabelやTypeScriptの設定に進みたい方は下記記事を参考にしてください。

https://qiita.com/clockmaker/items/8620cf6bd99d810dbf2a

あるいは、ここまでで概要を把握できたら、もうcreate-react-appやVue CLIを使っても良いでしょう。
個人的にはParcelをよく使っていて、今後はviteに移行する予定です。

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

Vue 3.0とgRPCを使ってTodoListを作ってみた

gRPCとは?

gRPCはオープンソース、RPCフレームワークをベースとして、最初はGoogleが開発されました。
インターフェース記述言語としてProtocol Buffersを使用し、protobufは構造化データをシリアル化するためのメカニズムです。
protoファイルでサービスとそのデータ構造を定義するだけで、gRPCがさまざまな言語でプラットフォームのクライアントとサーバーのStubsを自動的に生成します。
profobufを使用すると、JSONではなくバイナリを使用して資料を転送しています。
これにより、gRPCがはるかに高速で信頼性の高いものになります。
gRPCの他の主要な機能のいくつかは、双方向ストリーミングとフロー制御、BlockingまたはNonBlockingバインディング、および認証機能です。
gRPCはHTTP/2を使用して、シングルTCPコネクションの中で複数のストリームを開始することができます。
gRPCの詳細については、こちらをご覧ください:https://grpc.io/

gRPC V.S. REST

Feature gRPC REST
Portocol  HTTP/2 (早い) HTTP/1.1 (遅い)
Payload  Protobuf (バイナリ、小さい) JSON (テキスト、大きい)
API構造  厳格、必要 (.proto) ゆるい、選択
Code生成  内蔵 (protoc) 他のツール (Swagger)
安全性  TLS/SSL TLS/SSL
ストリーミング  双方向ストリーミング クライアント -> サーバーリクエストだけ
ブラウザのサポート 制限あり (grpc-webは必要) ほぼ全部

Protobuf と gRPC を使えば、REST API の GET、PUT やヘッダーなどを気にする必要はありません、そしてgRPCフレームワークによって生成されたStubsにはデータモデル用の記述が全部書いてるので、直接引用するだけで使えます。

開発環境とツール

  • Protoc v3.12.4 -- Protobuf コンパイラー Stubs を生成する為に使ます。
  • Node.js v14.2.0 -- バックエンドとVueのビルドに使います。
  • Docker v19.03.12 -- envoyを動かす為に使ます。
  • envoy v1.14 -- 普通WebからのHTTP/1.1をHTTP/2に変換する為のプロキシ。
  • Vue.js 3.0.0-rc.5 -- 今回は Vue 3 を使ってフロントエンドを作成します。
  • Docker Compose v1.26.2 -- 全部をDocker化する為に使います、なくでも動けます。

フォルダ構成

dya2g02.png

全体の流れ

  1. Protoファイルの作成
  2. Node.jsでバックエンド作成
  3. Envoy proxyの設定
  4. Client stubsの生成
  5. Clientの作成
  6. 動かしてみましょう
  7. Docker化

コードを書いてみましょう

1. Protoファイルの作成

ProtoファイルはgRPCの心臓と呼ばれる部分、ここでRequestとResponseとサービスを定義することによって、後でStubsファイルを自動的に生成することができます。
Protoファイルの構成は大体四つの部分に分けている。
1. Protoのバージョンを定義する
2. Packageの名前
3. サービス定義
4. メッセージ定義

todo.proto
syntax = "proto3";

package todo;

service todoService {
  rpc addTodo(addTodoParams) returns (todoObject) {}
  rpc deleteTodo(deleteTodoParams) returns (deleteResponse) {}
  rpc getTodos(getTodoParams) returns (todoResponse) {}
}
// Request
message getTodoParams{}

message addTodoParams {
  string task = 1;
}

message deleteTodoParams {
  string id = 1;
}
// Response
message todoObject {
  string id = 1;
  string task = 2;
}

message todoResponse {
  repeated todoObject todos = 1; //ここはArrayの中身にtodoObjectが複数ありますのこと。
}

message deleteResponse {
  string message = 1;
}

2. Node.jsでバックエンド作成

環境設定:

npm:

bash
npm init -y
npm i uuid grpc @grpc/proto-loader
npm i -D nodemon

yarn:

bash
yarn init -y
yarn add uuid grpc @grpc/proto-loader
yarn add -D nodemon

startのnodeをnodemonに書き換える:

package.json
{
  "name": "server",
  "version": "1.0.0",
  "description": "A Node.js gRPC API Server",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon server.js"
  },
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "nodemon": "^2.0.4"
  },
  "dependencies": {
    "@grpc/proto-loader": "^0.5.5",
    "grpc": "^1.24.3",
    "uuid": "^8.3.0"
  }
}

サーバーのコード内容:

server.js
// proto ファイルのパス
const todoProtoPath = './todo.proto';
// npm packageを導入
const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');
const { v4: uuidv4 } = require('uuid');
// grpcの初期化
const packageDefinition = protoLoader.loadSync(
  todoProtoPath,
  {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  },
);
// packageを指定
const todoProto = grpc.loadPackageDefinition(packageDefinition).todo;

// Todosの保存、リースダートしたら資料が消えます
let Todos = [];

const addTodo = (call, callback) => {
  const todoObject = {
    id: uuidv4(),
    task: call.request.task,
  };
  console.log(call.request);
  Todos.push(todoObject);
  console.log(`Todo: ${todoObject.id} added!`);
  callback(null, todoObject);
};

const getTodos = (call, callback) => {
  console.log('Get tasks');
  console.log(Todos);
  callback(null, { todos: Todos });
};

const deleteTodo = (call, callback) => {
  Todos = Todos.filter((todo) => todo.id !== call.request.id);
  console.log(`Todo: ${call.request.id} deleted`);
  callback(null, { message: 'Success' });
};

const getServer = () => {
  const server = new grpc.Server();
  // サービスを登録、名前はprotoファイルと同じなので省略できます
  server.addService(todoProto.todoService.service,
    { addTodo, getTodos, deleteTodo });
  return server;
};

if (require.main === module) {
  const server = getServer();
  server.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
  server.start();
  console.log('Server running at port: 9090');
}

3. Envoy proxyの設定

Envoy proxyはサーバーとクライアントの中央にいるサービスです、主にはHTTP/1.1のコネクションをHTTP/2に変換するの役割です。

Dockerのイメージ設定ファイル

Dockerfile
FROM envoyproxy/envoy:v1.14-latest
COPY ./envoy.yaml /etc/envoy/envoy.yaml
CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml

Envoyの設定ファイル

envoy.yaml
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route:
                  cluster: todo_service
                  max_grpc_timeout: 0s
              cors:
                allow_origin_string_match:
                  - prefix: "*"
                allow_methods: GET, PUT, DELETE, POST, OPTIONS
                allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                max_age: "1728000"
                expose_headers: custom-header-1,grpc-status,grpc-message
          http_filters:
          - name: envoy.filters.http.grpc_web
          - name: envoy.filters.http.cors
          - name: envoy.filters.http.router
  clusters:
  - name: todo_service
    connect_timeout: 0.25s
    type: logical_dns
    http2_protocol_options: {}
    lb_policy: round_robin
    load_assignment:
      cluster_name: cluster_0
      endpoints:
        - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    # docker-composeを使うときserverに書き換えます
                    address: host.docker.internal
                    port_value: 9090

gRPCサービスは9090 portで動かして、Envoyは8080 portでWebからのHTTP/1.1をHTTP/2に変換して9090に送るそいう仕組みです。

4. Client stubsの生成

protocをインストール : Protocol Buffer Compiler Installation

bash
# Linux
$ apt install -y protobuf-compiler
$ protoc --version
# MacOS using Homebrew
$ brew install protobuf
$ protoc --version

先にVueのProjectを作成します。
vue-cliを使います。

bash
vue create client

そしてStubsを作ります
このコメンドで ./client/src に二つのJSファイルを生成する

  • todo_pb.js // メッセージのType定義
  • todo_grpc_web_pb.js // gRPCクライアント
bash
protoc -I server todo.proto \
    --js_out=import_style=commonjs,binary:client/src \
    --grpc-web_out=import_style=commonjs,mode=grpcwebtext:client/src

5. Clientの作成

クライアントのTodoコンポーネントの中にtodo_pb.jsとtodo_grpc_web_pb.jsを導入して、todoServiceClient()を使ってlocalhost:8080のEnvoy proxyに接続します。

Todo.vue
import { ref } from 'vue'
// クライアントが使う部分だけを導入する
import { getTodoParams, addTodoParams, deleteTodoParams } from "../todo_pb.js";
import { todoServiceClient } from "../todo_grpc_web_pb.js";
import CloseIcon from './CloseIcon'
export default {
  components:{ CloseIcon },
  setup() {
    const todos = ref([])
    const inputField = ref('')
    // 新しクライアントのインスタンスを作成
    const client = new todoServiceClient("http://localhost:8080", null, null);

    const getTodos = () => {
      let getRequest = new getTodoParams();
      client.getTodos(getRequest, {}, (err, response) => {
        if (err) console.log(err);
        console.log(response.toObject());
        todos.value = response.toObject().todosList;
      });
    }

    getTodos()

    const addTodo = () => {
      let addRequest = new addTodoParams();
      addRequest.setTask(inputField.value);
      client.addTodo(addRequest, {}, (err) => {
        if (err) console.log(err);
        inputField.value = "";
        getTodos();
      });
    }
    const deleteTodo = (todo) => {
      let deleteRequest = new deleteTodoParams();
      deleteRequest.setId(todo.id);
      client.deleteTodo(deleteRequest, {}, (err, response) => {
        if (err) console.log(err);
        if (response.getMessage() === "Success") {
          getTodos();
        }
      });
    }

    return {
      todos,
      inputField,
      addTodo,
      deleteTodo
    }
  }
}

完成の参考 : Github

6. 動かしてみましょう

Back-Endを立ち上げて:

bash
$ cd ./server
$ npm start

enovy proxy:

bash
$ docker build -t envoy:v1 ./enovy
$ docker run --rm -it -p 8080:8080 envoy:v1

Front-End:

bash
$ cd ./client
$ yarn dev

成功すればこんな感じです:
giphy.gif

7. Docker化

Docker Compose一発で動かす為にDocker化します。
各フォルダにDockerfileと.dockerignore入れます。

./serverフォルダ

Dockerfile
FROM node:lts-alpine

# make the 'app' folder the current working directory
WORKDIR /app

# copy both 'package.json' and 'package-lock.json' (if available)
COPY package*.json ./

# install project dependencies
RUN npm install

# copy project files and folders to the current working directory (i.e. 'app' folder)
COPY . .

EXPOSE 9090
CMD [ "node", "server.js" ]
.dockerignore
.git
.gitignore

node_modules

README.md

./clientフォルダ

Dockerfile
FROM node:lts-alpine

# install simple http server for serving static content
RUN npm install -g http-server

# make the 'app' folder the current working directory
WORKDIR /app

# copy both 'package.json' and 'package-lock.json' (if available)
COPY package*.json ./
COPY yarn.lock ./

# install project dependencies
RUN yarn install

# copy project files and folders to the current working directory (i.e. 'app' folder)
COPY . .

# build app for production with minification
RUN yarn run build

EXPOSE 3000
CMD [ "http-server", "-p", "3000", "dist" ]
.dockerignore
.git
.gitignore

node_modules

README.md

./docker-composer.ymlを作成します。

docker-compose.yml
version: '3'

services: 
  web:
    build: ./client
    image: todo-grpc-vue-client:v1
    ports: 
      - 3000:3000
    restart: unless-stopped
    networks:
      - grpc-todolist
  proxy:
    build: ./envoy
    image: todo-grpc-envoy:v1
    ports: 
      - 8080:8080
    restart: unless-stopped
    networks: 
      - grpc-todolist
  server:
    build: ./server
    image: todo-grpc-server:v1
    restart: unless-stopped
    networks:
      - grpc-todolist
networks:
  grpc-todolist:
    driver: bridge

Envoyの設定ファイルのサーバーアドレス修正します

envoy.yaml
endpoints:
  - lb_endpoints:
      - endpoint:
          address:
          socket_address:
          # host.docker.internalをserverに書き換える
          address: server
          port_value: 9090

そしてビルドして立ち上げます。

bash
# イメージをビルド
$ docker-compose build
# 特定のイメージをリビルドします
$ docker-compose build --no-cache [service]
# 立ち上げる
$ docker-compose up
# 背景で立ち上げる
$ docker-compose up -d
# ログを確認
$ docker-compose logs

Front-Endに入ります:http://localhost:3000

最後

Qiitaで初めての投稿です、よろしくお願いします。
Githubでソースコードを公開しています、
なんが詰まったところがあれば参考してください:Github

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

Visual Studioのコードと同じDockerを使ってNode.jsアプリを開発する

このガイドでは、Ubuntu Linuxデスクトップ上でVisual Studio Codeを使ってNode.jsアプリを開発し、Alibaba Cloud上でDockerを使って同じアプリをデプロイしていきます。

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

よくある質問

Node.jsとは?

Node.jsはオープンソースでクロスプラットフォームなJavaScriptのランタイム環境で、サーバー上でJavaScriptを実行することができます。

Express とは?

Expressは、最小限で柔軟性の高いNode.jsのWebアプリケーションフレームワークで、Webとモバイルアプリケーションのための堅牢な機能セットを提供しています。

Dockerとは?

Dockerは非常に人気のあるコンテナプラットフォームで、アプリケーションやサービスを簡単にパッケージ化、デプロイ、消費することができます。

Docker Hubとは?

Docker Hubは、Dockerユーザーやパートナーがコンテナイメージを作成、テスト、保存、配布するクラウドベースのリポジトリです。

Visual Studio Codeとは?

Visual Studio Codeは、デスクトップ上で動作し、Windows、MacOS、Linuxに対応した軽量かつ強力なソースコードエディタです。JavaScript、TypeScript、Node.jsをビルトインでサポートしており、他の言語(C++、C#、Java、Python、PHP、Goなど)やランタイム(.NET、Unityなど)に対応した拡張機能の豊富なエコシステムを備えています。Visual Studio Codeでは、Dockerを使ったアプリケーションのデプロイを簡単に行うことができ、プロジェクトの種類に応じて適切なDockerファイルの生成と追加をサポートしています。

Alibaba Cloud Simple Application Serverとは?

Simple Application Serverは、アプリケーションの起動と管理、ドメイン名解決の設定、ウェブサイトの構築、監視、保守を数回クリックするだけで行えるオールインワンのソリューションを提供します。プライベートサーバーの構築が格段に簡単になり、初心者がアリババクラウドを始めるのに最適な方法です。

アプリケーションの開発例

Ubuntu Linuxデスクトップに前提条件をインストール

私はバージョン16.04のUbuntu Linuxデスクトップを持っています。記載されている手順は、すべてのバージョンでほとんど同じです。

Node.js

node.jsが既にインストールされているかどうかを確認します。

node –v

バージョン番号が表示されない場合は、以下の手順でインストールしてください。

sudo apt-get install curl python-software-properties
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash –
sudo apt-get install nodejs

ノードとnpmのバージョンを確認します。

node –v
npm –v

Docker

DockerにはEnterprise版とCommunity版があります。今回はコミュニティ版のDocker CEをインストールします。

以前のバージョンがインストールされているかどうかを確認し、インストールされている場合はアンインストールしてください。

sudo apt-get remove docker docker-engine docker.io containerd runc

aptパッケージを更新します。

sudo apt-get update

aptがHTTPSでリポジトリを使用できるようにします。

sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
           gnupg-agent \
    software-properties-common

GPGキーを追加

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add –

安定版リポジトリを設定してインストールします。

sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

dockerをインストールします。

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

hello-worldイメージを実行して、Dockerが正しくインストールされているかどうかを確認します。

sudo docker run hello-world

このコマンドはイメージをダウンロードしてコンテナ内で実行し、そのコンテナ内でHello Worldを印刷します。

しかし、上記のコマンドはsudoまたはrootユーザのアクセスで実行されています。

次にインストールするVisual Studio CodeとそのDockerエクステンションをIDEとして使用する予定なので、非rootユーザで実行できるようにする必要があります。VS Codeは非rootユーザで動作し、拡張機能を使うことでコンテナ化されたアプリケーションの構築、管理、デプロイが容易になります。

Dockerグループを作成します。

sudo groupadd docker

ユーザーをdockerグループに追加します。

sudo usermod -aG docker $USER

ログアウトして再度ログインするか、グループのメンバーシップが再評価されるように再起動してください。

sudo reboot

再起動後、sudoなしでdockerコマンドが実行できることを確認します。

docker run hello-world

Visual Studioコード

VS Code をインストールする最も簡単な方法は、Snap パッケージとしてインストールすることです。スナップはすべての主要な Linux ディストリビューションで使用することができ、ほとんどの Ubuntu デスクトップにプリインストールされています。もしあなたのデスクトップにない場合は、https://docs.snapcraft.io/installing-snapd からインストールすることができます。

VSコードをインストールします。

sudo snap install --classic code

アプリケーション/プログラミングから同じものを選択するか、linuxターミナルでコードを入力してVisual Studioのコードを開きます。

「表示」→「拡張機能」に移動します。

Docker」を検索して、拡張機能をインストールします。拡張機能の詳細は https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker を参照してください。

Node.jsプロジェクトの作成

アプリケーション/プログラミングから同じものを選択するか、linuxターミナルでコードを入力してVisual Studioのコードを開きます。

フォルダを開くを選択し、documentsディレクトリに移動します。ここでプロジェクトを作成します。

VS Codeにはシェルコマンドを実行するためのターミナルが統合されています。そこから直接Node.jsを実行することができます

Visual Studio Code のトップメニューから Terminal -> New Terminal を選択して Terminal を開きます。

Express用のテンプレエンジンをインストールするためにExpress Generatorをインストールします。

sudo npm install -g express-generator

新しいエクスプレスアプリケーションを足場にします。

express nodeexpress-alibaba-docker-tutorial --view=pug

これにより、nodeexpress-alibaba-docker-tutorialという名前の新しいフォルダが作成され、左のdocumentsの下にあるアプリケーションの内容を見ることができます。

コードの編集と変更

ここに独自の開発コードを追加します。ここでは、いくつかの簡単な変更を行います。

routes フォルダの下にある index.js を選択します。

index.jsの中のres.renderを含むコードの行に移動します。

以下のように変更してください。

res.render('index', { title: 'Docker on Simple Application Server', data:'Alibaba Cloud' });

フォルダビューの下にあるindex.pugを選択します。

Welcome toを含む行のデータタイトルを変更します。

アプリケーションの実行

VSCodeターミナルで、ディレクトリをアプリケーションフォルダに変更します。

cd nodeexpress-alibaba-docker-tutorial

package.jsonファイルに存在するアプリケーションの依存関係をインストールします。

npm install

image.png

Package.jsonファイルには、Node.jsアプリケーションを実行するためのスタートスクリプトも含まれています。

npm start

ブラウザを開き、localhost:3000にアクセスしてアプリケーションを表示します。ポート番号はスタートスクリプトから呼び出されるbinフォルダ内のファイルwwwの中に設定されています。

Visual Studio Codeのトップメニューから「ファイル」→「フォルダを閉じる」を選択し、VS Codeでアプリケーションを閉じます。

アプリケーションのDockerize例

パブリックリポジトリは、誰もが利用できるDockerイメージをホスティングするために使用することができます。Docker hubはパブリックリポジトリであり、利用するためのパブリックなDockerイメージのリストを見つけることができます。あとはそれらのイメージを引っ張ってきて、それを元にコンテナを起動し始めるだけです。Docker Hub上の公開リポジトリに公開することで、イメージを利用可能にしています。

Docker Hubアカウントとリポジトリの作成

https://hub.docker.com/ にサインアップし、アカウントを作成したらログインします。

トップメニューからリポジトリを選択します。

アカウントを作成したばかりなので、リポジトリはありません。リポジトリの作成ボタンを選択してリポジトリを作成します。これがあなたの画像が保存されるリポジトリです。

image.png

次の画面では、リポジトリの名前と説明を指定します。ここでは下の画像のようにnodeexpress-alibaba-docker-tutorialという名前を指定します。

リポジトリを公開し、dockerhub上で検索できるようにしています。また、リポジトリを非公開にすることもできます。1つのプライベートリポジトリは無料で、課金プランを利用すればそれ以上の数のリポジトリを作成することができます。プライベートリポジトリは公開リポジトリと同じように動作しますが、閲覧や検索ができません。

image.png

Click on the Create button and your repository is ready.

image.png

画面の右端にあるコマンドに注目してください。このコマンドを実行して画像をこのリポジトリにプッシュする必要があります。

画像は [registry or username]/[image name]:[tag] の形式である必要があります。

注:Alibaba Cloudは、https://www.alibabacloud.com/product/container-registry で利用できる安全なコンテナレジストリも提供しています。

アプリケーションをDockerizeする

フォルダを開くを選択し、documentsフォルダの下にあるnodeexpress-alibaba-docker-tutorialフォルダに移動し、アプリケーションを開きます。

Visual Studio Codeのトップメニューから「ターミナル」→「新規ターミナル」を選択してターミナルを開きます。

Dockerがインストールされて実行されていることを確認します。

docker –version

Visual Studio CodeのトップメニューからView->Command Paletteを選択してCommand Paletteを開きます。

ワークスペースにDockerファイルを追加と入力し、Dockerを選択して実行します。Dockerファイルをワークスペースに追加するコマンドを実行します。

image.png

コマンドパレットでアプリケーションプラットフォームの選択を求められますので、Node.jsを選択します。

image.png

また、アプリケーションがリスンするポートを指定します。

image.png

これにより、ソースファイルの場所やコンテナ内でアプリを起動するコマンドなど、アプリの環境を記述するDockerfileと共にプロジェクトに特定のファイルが追加されます。

コマンドパレットでDocker: Build Imageを実行してイメージをビルドします。先ほど作成したDockerfileを選択し、Docker hubリポジトリの作成時に指定したフォーマットでイメージに名前を付けます。名前は......... arnab74/nodeexpress-alibaba-docker-tutorial:firsttry

image.png

ターミナルパネルが開き、Dockerコマンドが実行されます。ビルドが完了すると、DockerエクスプローラーのImagesの下にイメージが表示されます。

コマンドパレットでdocker runと入力し、Docker:Images runを選択してコンテナをビルドします。

image.png

コマンドパレットで、イメージグループ arnab74/nodeexpress-alibaba-docker-tutorial を選択します。

image.png

コマンドパレットで、Image firsttryを選択します。

ターミナルに表示された生成されたコマンドを実行します。

コマンドは'docker run'で、2つのフラグがあります。

-p : これはコンテナ上のポートを公開し、ホスト上のポートにマッピングします。ここでマッピングされたホスト上のポートも同様に3000です。

-d : バックグラウンドでコンテナを実行します。

詳細は https://docs.docker.com/engine/reference/commandline/run/ を参照してください。

実行中のコンテナを検査します。

Docker ps

ブラウザを http://your_server_ip:3000 または http://localhost:3000 に移動してアプリケーションを表示します。

実行中のアプリケーションコンテナを停止するには、実行中のコンテナを右クリックして、DOCKER explorer の Containers の下にある stop を選択します。

image.png

または、ターミナルで以下のコマンドを実行することができます。

docker stop [CONTAINER ID]

CONTAINER ID]をDocker psコマンドで取得した自分のCONTAINER IDに置き換えてください。

お試し追加ステップ

VSCode の docker 拡張機能によって自動的に生成される dockerfile を改良しました。

https://hub.docker.com/_/node/ から現在の nodejs LTS バージョンに基づいてアプリケーションのベースイメージを更新します。

コンテナをrootで実行することは避けなければなりません。Docker Nodeイメージには、rootではないノードのユーザが含まれています。ユーザーと同じにして、そのホームディレクトリを作業ディレクトリに設定します。

新しいDockerfileは以下のようになります。

FROM node:10.16-alpine
ENV NODE_ENV production
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
USER node
RUN npm install --production --silent && mv node_modules ../
COPY --chown=node:node . .
EXPOSE 3000
CMD npm start

前述のプロセスに従って、新しいイメージをビルドし、arnab74/nodeexpress-alibaba-docker-tutorial:latestという名前で実行してテストします。

Docker Hubに画像をアップロードする

Docker Explorerで、レジストリセクションのConnect Registryを選択します。

image.png

利用可能なオプションで、レジストリプロバイダとしてDocker Hubを選択します。

image.png

ユーザー名とパスワードを入力してログインします。

コマンドパレットでdockerタグを書き、Docker Images:Tagを選択します。

次のコマンドパレット画面で画像グループを選択します。

arnab74/nodeexpress-alibaba-docker-tutorial

次の画面でタグ付けする画像を選択

latest

新しいタグを提供

arnab74/nodeexpress-alibaba-docker-tutorial:0.1

コマンドパレットでdocker pushを書き、Docker Images:Pushを選択します。

次のコマンドパレット画面で画像グループを選択します。

arnab74/nodeexpress-alibaba-docker-tutorial

次の画面でDocker hubにプッシュするImageを選択します。

0.1

ターミナル上にDocker Pushコマンドが生成されるので、それに従ってください。

以下のようにターミナル上で上記の手順を行うこともできます。

docker login --username=arnab74
docker images

docker tag [IMAGE ID] arnab74/nodeexpress-alibaba-docker-tutorial:0.2

IMAGE ID を最新のタグ付けされた画像の画像 ID に置き換えてください。

docker push arnab74/nodeexpress-alibaba-docker-tutorial:0.2

アップロードされた画像は、Docker hubのサイトのリポジトリやDocker Explorerのレジストリで確認できるはずです。

シンプルなアプリケーションサーバにアプリケーションをデプロイする

並行記事Developing Node.js App in Visual Studio and Deploying on Simple Application Serverをご覧いただき、Alibaba Cloud上にCentOS7でSimple Application Serverインスタンスを作成し、インスタンスへのパスワードを作成し、デスクトップ端末を使ってログインする方法をご紹介します(記事のようにputtyではなく)。

サーバーに前提条件をインストールする

yum

yumは、CentOSでソフトウェアパッケージを取得、インストール、削除、クエリ、管理するための主要なツールです。まず、ソフトウェアリポジトリを最新のバージョンにアップデートするために使います。

yum -y update

Docker

古いバージョンがある場合は、アンインストールしてください。

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

必要なパッケージをインストールします。

sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

インストールするリポジトリの設定

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

Dockerのインストール

sudo yum install docker-ce docker-ce-cli containerd.io

Dockerの起動

sudo systemctl start docker

Dockerを非rootユーザーで動作させます。

sudo groupadd docker
sudo usermod -aG docker $USER
sudo reboot
Start Docker again
sudo systemctl start docker

コマンドが sudo なしで実行されているかどうかを確認します。

docker run hello-world

アプリケーションのデプロイ

コンテナをビルドしてアプリケーションを実行します。マシンのローカルにイメージがない場合は、Dockerがリポジトリからイメージを引っ張ってきます。

docker run -d -p 80:3000 arnab74/nodeexpress-alibaba-docker-tutorial:0.1

ここでは、ホストに80番ポートを使用しました。そのため、簡易アプリケーションサーバの公開IP(サーバ管理画面で利用可能)を利用してサイトを閲覧する際には、ポート番号を使用する必要はありません。

http://public_ip にアクセスしてサイトをテストします。アプリケーションが起動しているのが確認できるはずです。

結論

これらは、Ubuntu Linux上でビジュアルコードを使用してNode.jsアプリケーションを開発し、アプリケーション用のDockerイメージを作成し、Dockerハブリポジトリを作成してDockerハブにイメージをプッシュし、最後にCentOSを使用してAlibaba CloudのSimple Application Server上にDockerハブイメージをデプロイするための基本的なステップです。

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

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

Azure Static Web AppsのAPI作成でLINE BOTを作る

前に書いた記事でAzure Static Web Appsを簡単に試してみましたが、後半の手順でAPIの作成も出来ました。

参考: たぶん10分で試せる。Azure Static Web AppsにWebサイトをデプロイして独自ドメイン設定とFunctionsでAPI公開まで

今回はこれを使ってLINE BOTを作ってみようと思います。

ちなみにStatic Web AppsのAPIの中身はAzure Functionsになる模様です。

参考: Azure Functions による Azure Static Web Apps プレビューでの API のサポート

環境

  • macOS Catalina
  • Node.js v12系

現状だとローカル実行やAzure FunctionsがNode.js v12までしか対応してなさそうでした。

APIエンドポイントを作ってみる(おさらい)

こんな手順です。

  • まずは参考記事をもとに、Azure Static Web Appで静的サイトを作成してみる
  • apiフォルダを作成し、さらにlinebotフォルダを作成
  • linebotフォルダ内にindex.jsfunction.jsonを作成
index.js
module.exports = async function (context, req) {
    context.res = {
       body: { 
         text: "Hello from the API" 
       }
    };
  };
function.json
{
    "bindings": [
      {
        "authLevel": "anonymous",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "methods": [
          "get"
        ],
        "route": "linebot"
      },
      {
        "type": "http",
        "direction": "out",
        "name": "res"
      }
    ]
  }

この状態でプッシュしてしばらくするとAPIが有効になります。

https://<APP ID>.azurestaticapps.net/api/linebotにアクセスするとAPIが見えます。

ローカル実行まで

まずはローカルで実行できる環境を整えます。

Azure Functionsの拡張機能のインストールと設定

VScodeにAzure Functionsの拡張機能をインストールします。

インストールが完了するとAzureマークがVSCodeのサイドバーに表示されます。選択するとサインインしましょう的なボタンがあるのでサインインします。

サインインすると少し読み込み待ち

Create New Project...のボタンを選択し、Browseを選択。

作成したapiフォルダを選択

言語選択でJavaScriptを選択

スクリーンショット 2020-08-17 1.27.33.png

テンプレート選択でHTTP triggerを選択

スクリーンショット 2020-08-17 1.27.28.png

関数名をwebhookとし、承認レベルをanonymousにします。

こんな感じでapiフォルダ内にwebhookフォルダやその他ファイルが作成されます。

Azure Functions拡張機能のタブに戻るとこのような表示になっています。

core toolsでローカル実行

Azure Functions Core Toolsというツールを使ってローカル実行をする模様です。

VScodeのコマンドパレットを開き(macだとcommand+shift+p)、core toolsと入力しインストールします。

スクリーンショット 2020-08-17 1.43.35.png

バージョン選択ですが、レコメンドされてるのでAzure Functions v3を選択します。

スクリーンショット 2020-08-17 1.43.44.png

しばらくするとインストールが完了します。

VSCodeのメニューのRun > Start Debuggingを選択するとローカルサーバーが起動します。

スクリーンショット 2020-08-17 1.55.28.png

ちなみに、このときにNode.js v14を利用してたらエラーが出ました。執筆時点ではv12までの対応らしいのでv12に切り替えたらうまく行きました。

ターミナルではこんな感じで作成したwebhooklinebotのエンドポイントが有効になったような表示になります。

ブラウザでアクセスしてみるとちゃんと表示されました。

LINE BOTプログラムの作成

現状だとルートディレクトリにapiフォルダindex.htmlがありますが、apiフォルダ内にpackage.jsonがあるのでそこでnpm installを実行していきます。

  • ルートからapiフォルダへ
$ ls
api        index.html
$ cd api
$ ls
host.json           local.settings.json package.json        webhook
linebot             package-lock.json   proxies.json
  • apiフォルダでモジュールのインストール
npm i @line/bot-sdk express azure-function-express

通常だとAzure Functionsのお作法で書く必要がありますが、azure-function-expressを利用することで、Azure Functionsっぽい書き方に依存せずに通常のexpressの利用っぽい雰囲気で書くことが出来ます。ひらりんさんの記事が参考になりました!

参考: LINE Bot を Azure Functions (Node.js) で作る際のオウム返しテンプレ

  • api/webhook/index.jsにコードを記述

'use strict';

const line = require('@line/bot-sdk');
const createHandler = require("azure-function-express").createHandler;
const express = require('express');

const config = {
    channelSecret: '作成したBOTのチャンネルシークレット',
    channelAccessToken: '作成したBOTのチャンネルアクセストークン'
};

const app = express();

app.get('/api/webhook', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/api/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
        res.send('Hello LINE BOT!(POST)');
        console.log('疎通確認用');
        return;
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text //実際に返信の言葉を入れる箇所
  });
}

module.exports = createHandler(app);

大元はこちらの1時間でLINE BOTを作るハンズオンの記事のコードになっています。

参考記事をもとにLINE BOTのチャンネルシークレットとアクセストークンもコードに記載しておきます。

参考: 1時間でLINE BOTを作るハンズオン

おうむ返し

ngrokでトンネリングして試してみましょう。

コードが出来たら先ほどと同様にRun > Start Debuggingでローカルサーバーを起動させます。

今回(デフォ?)7071ポートで起動したのでngrokで7071ポートにトンネリングさせます。

$ npx ngrok http 7071

これは別のターミナルで実行しておいた方が良いと思います。

こんな感じでhttps://35027c542caa.ngrok.ioというURLが発行されました。

このURLを使ってLINE DevelopersでWebhook URLの設定を行います。

今回はエンドポイントが/api/webhookになっているので、この場合はhttps://35027c542caa.ngrok.io/api/webhookとなります。

この状態(ローカルサーバー起動中 + ngrok起動中 + Webhook URL登録済)でLINE BOTに話しかけると無事におうむ返しをしてくれました。

ひとまずローカルで実行できて一安心。。

デプロイして永続化する

デプロイは簡単で、もともとGitHub連携ありきでAzure Static Web Appsを作っているのでローカルで作ったものをプッシュすればOKです。

裏側でGitHub Actionsでビルドされる模様なので少し待ちましょう。

しばらくするとポータルの画面にもwebhook関数が表示されます。

https://<APP ID>.azurestaticapps.net/api/webhookにブラウザでアクセスするとHello LINE BOT!の表示が見えるようになると思います。

最終的にこのアドレスをLINE BOTの管理画面からWebhook URLに登録します。

これでデプロイも完了し永続化まで出来ました。

念のため最後におうむ返しがちゃんと動くか確認しましょう。

おまけ 環境変数の利用

ソースコードのチャンネルシークレットチャンネルアクセストークンをコードに直書きしてましたが環境変数置き換えも出来ます。

//省略

const config = {
    channelSecret: process.env.CHANNEL_SECRET,
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
};

//省略

ポータルの構成の箇所から環境変数の追加ができます。

パスワードやトークンはここを活用すると良さそうです。

最後にチェックをして保存を押すことで保存されるのですが、これを忘れると値が保存されないので注意です。

まとめと所感

Static Web AppsでLINE BOTを作成することが出来ました。

当たり前ですがMSエコシステムということでVSCodeを使わないと厳しいかもしれないですね。
この辺は趣味趣向ありそうです。

内部はAzure Functionsを使ってるっぽいですが、Azure Functionsのリソースは一個も作っていないので金額とかは特に発生せずに使えてるのかな......

Static Web Appsの管理画面がシンプルすぎて稼働状況もいまいちわからないですが、とりあえずしばらく稼働させてみます。

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