Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 8882 articles
Browse latest View live

Microsoft Bot FrameworkをMicrosoft Azureにデプロイするチュートリアルをかみ砕いてみた

$
0
0

はじめに

タイトルの通りです。Microsoft Bot Frameworkのチュートリアルで詰まりまくって具体的なパラメータをどう与えればいいのかわからなかったので、具体的な例を作って整理してみました。CMD上からのAzureログインからデプロイまで。

参考資料・前提条件

これの「ボットをデプロイする」の直前までが終わっていることが前提です。
基本的なボットの作成とデプロイに関するチュートリアル - Bot Service | Microsoft Docs

作業

1. Microsoft Azureにログインする。

Microsoft Azureにログインします。cmdでコマンドを叩くとそのままブラウザが立ち上がり、ブラウザ上でログインすることができます。

cmd
az login

2. アカウントリストを取得する

下記のコマンドを入力し、アカウントリストを取得します。

cmd
az account list

すると、このような感じでサブスクリプションのデータが出てきます。

cmd-こんなサブスクリプションデータだったとする
[
  {
    "cloudName": "AzureCloud",
    "id": "piyopiyo-piyo-nyan-nyan-hogehogehoge",
    "isDefault": true,
    "name": "無料試用版",
    "state": "Enabled",
    "tenantId": "hoge-hoge-fuga-fuga-piyopiyo",
    "user": {
      "name": "*****@hogehoge.com",
      "type": "user"
    }
  }
]

以降、これを使って説明していきます。

3. Azureのサブスクリプションにアクセスする

サブスクリプションの指定はcmd上で次のように入れます。チュートリアルの<azure-subscription>とは、上でいうところの"id"が対応します。

cmd
az account set --subscription "piyopiyo-piyo-nyan-nyan-hogehogehoge"

4. アプリケーションを登録する

アプリを作ります。

cmd
az ad app create --display-name "samplebot_hoge2" --password "password_hogefuga1" --available-to-other-tenants

--display-name:名前です。サンプルは適当(samplebot_hoge2)です。
--password:パスワードです。サンプルは適当(password_hogefuga1)です。

これを実行して問題なく作成できると、JSON形式でいろいろと出てきますが、appIdというGUIDをそのまま使用するので、控えておきましょう。

出てくるもの
{
  "acceptMappedClaims": null,
  "addIns": [],
  "allowGuestsSignIn": null,
  "allowPassthroughUsers": null,
  "appId": "fugafuga-piyo-piyo-piyo-hogehogehoge",
  "appLogoUrl": null,
...
控えておくべき情報
  "appId": "fugafuga-piyo-piyo-piyo-hogehogehoge",

5. ARM テンプレートを使用してデプロイする

ここからがめちゃくちゃ詰まりました。値のそれぞれの意味を整理しています。

そもそもARMテンプレートってなに

ARMはAzure Resource Managerの略であり、そのテンプレートをベースにすることでAzure内でうまくデプロイできるようです。すごい便利なんだなぁ、と思いました(小並感)
より詳しくは@t-tsurumiさんのAzureのARMテンプレートを理解する part1(基本編)が参考になるかと思います。

App Service Planがないとき

最初からやってみているときにはチュートリアルの「App Service Planがないとき」を実施しましょう。

cmd
az deployment create 
--name "samplebot-1gou" 
--template-file "./deploymentTemplates/template-with-new-rg.json" 
--location "centralus" 
--parameters 
appId="fugafuga-piyo-piyo-piyo-hogehogehoge" 
appSecret="password_hogefuga1" 
botId="samplebotkun03" 
botSku=F0 
newAppServicePlanName="new-sample-bot-plan-99" 
newWebAppName="newsamplebotkun-0X" 
groupName="atarashii-group" 
groupLocation="Central US" 
newAppServicePlanLocation="japaneast"

与える値の内容は下記

項目項目名任意備考
--name-任意わかりやすいボットの名称。適当につけてOK
--template-file-既存テンプレートファイルtemplate-with-new-rg.jsonが参照できるようにパスを入れてあげる。template-with-new-rg.jsonはボットの作成時に自動で生成される
--location-既存ロケーション。値はaz account list-locationsをcmd上で叩いて確認。jsonの形式で出力されるのでname項目を入れる
--parametersappId既存4で作成したappIdのGUID
--parametersappSecret既存4で作成した--passwordのパスワード
--parametersbotId任意自分が作ったボットの名称。任意
--parametersbotSku既定価格レベル。F0 (無料) または S1 (Standard) を指定できる
--parametersnewWebAppName任意Webアプリとして展開するための名称。任意
--parametersnewAppServicePlanName任意アプリサービスの名称。任意
--parametersnewWebAppName任意アプリサービスの名称。任意
--parametersnewAppServicePlanLocation既定リソースタイプに応じて決まったロケーションが定義されている。エラーの記載で詳しく教えてくれる。

6. デプロイ用のコードを準備する

ボットのルートディレクトリ上で、下記コマンドをそのまま実行します。
言語ごとに決まっていますが、下記の例はTypescriptです。成功するとweb.configができます。

cmd
az bot prepare-deploy --code-dir "." --lang Typescript

Kuduと呼ばれるAzure WebAppsで利用されているデプロイエンジンを用いてデプロイします。
そのために、まずは、フォルダの中身を下記のようにすべて選択してzip圧縮します。zipファイル名をcode.zipとします。

sample.png

その後、下記のようにコマンドを叩きます。

cmd
az webapp deployment source config-zip --resource-group "atarashii-group" --name "newsamplebotkun-0X" --src "code.zip" 
項目項目名任意備考
--resource-group-既存アプリケーションを作成したリソースグループ名
--name-既存5で定義したnewWebAppNameの値
--src-既存直前の手順で作成したzipファイル名

これが正常に終了して、結果のJSONが出力されればデプロイは完了です。

7.実際に動かしてみる

のこりはチュートリアルに従えばあまり詰まらずに実行することができます。
Web チャットでのテスト | 基本的なボットの作成とデプロイに関するチュートリアル - Bot Service | Microsoft Docs

エラー対応

Deployment failed. Correlation ID: ***

デプロイが失敗したことを示しています。Correlation ID(関連ID)に基づいて、ポータルなどでエラーの詳細を参照できます。

詳細はこちらを見ると結構わかりやすいと思います。
Azure Resource Manager でのデプロイ履歴の表示

The Microsoft App ID property was not recognized as a GUID and may be invalid.

原因

MicrosoftAppID が正しく選択されていないのが問題です。

解決策

アプリケーションを初回登録した際に発行されるJSONに含まれているappIdを参照してください。cmdに自分で打ち込んだアプリの名称ではないことに注意しましょう。

参考資料


JavaScript: 変数の初期化では`undefined` と `null` のどちらを使うのがよい?

$
0
0

概要

JavaScriptには undefinednullがあります。

先ほど以下のようなコードを見かけました。

letcallback=undefined;

私個人としては初期化の時は nullを入れておくことが多いので、変数の初期化ではundefinednullのどちらを使うのがよいのか考えました。

個人的な結論

undefinedはその名の通りundefinedを示すものです。
変数を初期化している以上、その変数はundefinedとは呼べないのではないかと。

そんなこんなで 「初期化の時は nullを入れておく」に落ち着きました。

補足

ググり力が足りないためか良い感じの初期化に関するドキュメントが見つけられませんでした。
何かあれば教えていただけると嬉しいです。

補足2

いろんなところに書かれている内容ではありますが、nudefinednullの比較について。

% node -v
v12.14.1
> undefined == undefined
true
> undefined == null
true
> null == null
true

> undefined === undefined
true
> undefined === null
false
> null === null
true

ポイント: undefined == nulltrue

参考

Node.js(axios)からDiscordに通知を送るメモ

$
0
0

忘れがちなのでコピペできる簡単なサンプルをメモしておきます。

スクリーンショット 2020-01-22 13.25.38.png

準備

$ mkdir myapp
$ cd myapp
$ npm init -y

インストール

$ npm i axios

コード

app.js
'use strict'constaxios=require('axios');constURL=`DiscordのWebhook URL`;//ヘッダーなどの設定constconfig={headers:{'Accept':'application/json','Content-type':'application/json',}}//送信するデータconstpostData={username:'n0bisuke BOT',content:'Node.jsからポストしてるよ :)'}constmain=async()=>{constres=awaitaxios.post(URL,postData,config);console.log(res);}main();

実行するとPOSTされます。

$ node app.js

ちなみにcurl版

curl -H"Accept: application/json"-H"Content-type: application/json"-X POST -d'{"username":"n0bisuke BOT","content":"Node.jsからポストしてるよ :)"}''DiscordのWebhook URL'

Cannot find module 'express' | dockerでNode.js

$
0
0

Node.jsで必要なファイルやディレクトリなどは割愛

早速、エラーが出たときの設定は以下の通り

Dockerfile

appディレクトリを作成、その中にDockerfileを保存します。

Dockerfile
# nodeの最新バージョンを指定FROM node:10#docker内でのディレクトリを作成RUN mkdir /code
WORKDIR /code# node.js公式サイトのdocker化ドキュメント通り# copy先にcodeディレクトリを指定COPY package*.json /code/RUN npm installRUN npm install express --saveRUN useradd -md /bin/bash localADD . /code/CMD [ "npm", "start" ]

docker-compose.yml

docker-compose.yml
version:'3'services:prog-app:build:context:./app/image:sample-app-nodecommand:bash -c "npm run start"volumes:-./app/:/codecontainer_name:sampappports:-"8001:5000"environment:TZ:'Asia/Tokyo'

package.json

package.json
{"name":"sample-app","version":"1.0.0","description":"sample app description","main":"app.js","scripts":{"test":"echo \"Error: no test specified\"&& exit 1","start":"node app.js"},"repository":{"type":"git","url":""},"license":"MIT","dependencies":{"express":"^4.17.1","ejs":"^3.0.1"},"engines":{"node":"10.x"}}

app.js

app.js
constexpress=require('express');constindex=require('./routes/index');constport=process.env.PORT||5000;constapp=express();app.use('/static',express.static('public'));app.set('view engine','ejs');app.use('/',index);app.listen(port,()=>console.log(`app listening on port ${port}!`));module.exports=app;

error

internal/modules/cjs/loader.js:638
    throw err;    ^

Error: Cannot find module 'express'

expressがなぜか使えない。
以下のサイトに解決策がありました。
https://castaneai.hatenablog.com/entry/2019/01/29/151257

docker-compose.yml
version:'3'services:prog-app:build:context:./app/image:sample-app-nodecommand:bash -c "npm run start"volumes:-./app/:/code-/code/node_modules# 追加container_name:sampappports:-"8001:5000"environment:TZ:'Asia/Tokyo'

dockerコンテナ・イメージ削除しdocker-compose upを再度実行すれば、以下のように成功しました。

>sample-app@1.0.0 start /code
>node app.js

app listening on port 5000!

DynamoDBでPromiseが使えた(Lambda Node.js)

$
0
0

はじめに

Lambda(Node.js)でDynamoDBに接続し、値を取得したいときに、
非同期であるために、自分でPromiseで返す関数を作成していた。

コード

/**
 * データの挿入を行う。
 * @param ddb
 * @param params
 * @returns {Promise<any>}
 */exports.addItem=function(ddb,params){returnnewPromise(function(resolve){ddb.putItem(params,function(err,data){if(err){console.log("PUT失敗",err,err.stack);}else{console.log("PUT成功");resolve(data);}});});};awaitthis.addItem(ddb,putParams);

改善後

なんと最後にpromise()を呼ぶことで、Promise型のものを返却してくれるようで、わざわざ自分でラップする必要がなかった。

awaitddb.getItem(findParams,function(err,data){if(err){console.log("GET失敗");}else{console.log("GET成功");}}).promise();

[JavaScript][Node.js]メモ:アロー関数の即時関数(関数を定義してその場で実行する)

$
0
0

アロー関数と即時関数を組み合わせる書き方。

consta=(()=>{// 何らかの処理return'Hello!'})()

余談だが、即時関数の正式名は、「IIFE (即時実行関数式)」っぽい。
「即時関数」でググっても公式のドキュメントにはなかなかたどり着けない。

参考リンク

pre-commitとtextlintを使ってHTMLに潜むゼロ幅スペース文字や記号の表記ゆれを倒す

$
0
0

概要

括弧や数字の全角半角が表記揺れしていたり、href内にゼロ幅スペースが紛れ込んでいてリンク切れが起こってしまったりすることをtextlintを使って防ごうという記事です。

サクッとページ単位で確認できればいい方はこちら

textlintとは?

文章の校正をしてくれるリンターツールです。漢字よりひらがなのほうがいい場合や、「てにをは」がおかしいような文章を指摘してくれます。

導入手順

1. プロジェクトの作成

※ この工程はpackage.jsonを生成するものです。すでにある方はスキップしてください。

npm init

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

npm install--save-dev textlint textlint-rule-prh # textlint関係
npm install--save-dev husky lint-staged # pre-commit関係

3. pre-commitが動作するように設定する

package.jsonに次の項目を追記してください。

{"husky":{"hooks":{"pre-commit":"lint-staged"}},"lint-staged":{"*.html":"textlint"}}

4. textlintの設定ファイルを作成

./node_modules/.bin/textlint --init

作成された.textlintrcファイルの中身を次のように上書きしてください。
これでtextlintがHTMLファイルを読むようになり、オリジナルの辞書を利用するようになります。

※今回は使いませんがHTMLを読むためのプラグイン「textlint-plugin-html」については後述します。

{"filters":{},"rules":{"prh":{"rulePaths":["./prh.yml"]}},"plugins":{"@textlint/text":{"extensions":[".html"]}}}

5. 辞書ファイルを作成

package.jsonと同じ階層にprh.ymlというファイルを作成します。
中身を次のように設定してみてください。

version:1rules:-expected:VS Codepatterns:VSCodeprh:正しい表記はVS Code-expected:""patterns:"/[\\u034f\\u200b\\u200c\\u200d\\u200e\\u200f\\u2028\\u2029\\u202a\\u202b\\u202c\\u202d\\u202e\\u2061\\u2062\\u2063\\ufeff]/"prh:ゼロ幅スペースを検出しました

項目を増やしたいときは次の塊を追加することで対応できます。patternsには正規表現も利用できますし、リストで複数項目設定することもできます。

-expected:正しい表記patterns:検出する表記prh:エラーメッセージ(説明文)

参考:prh/prh.yml at master · prh/prh

6. 試してみる

プロジェクト内に適当なHTMLファイルを作成します。たとえば、test.htmlなど。

<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Document</title></head><body><p>私はVSCodeが好きです</p><p>ここに→"​"←ゼロ幅スペース文字がいます</p></body></html>

このHTMLファイルがpackage.jsonと同じ階層にあるとき、次のようなコマンドでリンターに検査させられます。

./node_modules/.bin/textlint test.html

するとおそらく次のようなログがでるはずです。

D:\dummy\dummy\test.html
  10:6  ✓ error  VSCode => VS Code
正しい表記はVS Code           prh
  11:9  ✓ error  ​ =>
ゼロ幅スペースを検出しました  prh

✖ 2 problems (2 errors, 0 warnings)
✓ 2 fixable problems.
Try to run: $ textlint --fix [file]

出ない場合は、node_modulesフォルダを削除し、npm iコマンドを実行してみてください。

7. コミットしてみる

うまくpre-commitが動作していれば次のようなエラーが出てコミットが中断されるはずです。
こちらもうまく出ない場合は、node_modulesフォルダを削除し、npm iコマンドを実行してみてください。

Sourcetree

image.png

TortoiseGit

image.png

いろいろ試してみよう

検出には正規表現も使えるので、他にも様々な表記ゆれや誤実装を回収できそうです。

-expected:patterns:(prh:全角括弧を利用してください-expected:patterns:)prh:全角括弧を利用してください

などなど…。Webサイトの運用で表記ゆれやゼロ幅スペースにお困りの場合は試してみてください。

タグを無視するtextlint-plugin-html

HTMLのタグを無視させたい場合はtextlint-plugin-htmlを一緒にインストールしてください。

npm i -D textlint-plugin-html

.textlintrcの設定は次のようになります。

{"filters":{},"rules":{"prh":{"rulePaths":["./prh.yml"]}},"plugins":["html"]}

なお、どうやら&copy;などは「©」として解釈されるようなので、不要な実態参照を検出するルールはできない模様。たぶん。おそらく。

検出されない場合がある(追記)

コードの順番、単語の区切られ方で半角英字のみの文字列がうまく検出されない場合がある模様。調査中。

参考文献

Glitchが全く動作しない時または誤作動でコード変えても反応しないときの対処法

$
0
0

対処方法

  1. Consoleを開く コマンドライン
$killallnode

を実行する。
2. しばらくたってそれ以上動かなかったら、
package.jsonの一部の空白一文字消す。
3. しばらくたつと動く。


Slackに匿名で画像を投稿できるようにした

$
0
0

はじめに

Slackで匿名チャンネルを作りました。
直接聞きにくい相談やちょっとした雑談で使えるので便利です。

「画像も投稿できたらコミュニケーションの幅が広がるのでは?」と思ったのがきっかけで、
画像も匿名で投稿できるようにしました。

処理の流れとしては、
1. BotにDMで画像を送信する
2. Botが画像を受け取ったら、ローカルに保存する
3. 指定したチャンネルに保存した画像をBotが代わりに投稿する
4. ローカルに保存した画像を削除する
5. 終わり
となってます。

JavaScript初心者なので至らない点もあると思いますが、ご了承ください。

準備

この記事を参考に、

  • ワークスペース内で使うSlack Botの作成
  • API Tokenの取得
  • Botkitのインストール

を行ってください。

説明のために、今回作成したBotの名前は"anonymous_bot"とします。

BotとChannelのIDを取得

先ほど取得したAPI Tokenを使って、Botと匿名で投稿したいチャンネルのIDを取得してください。

  • BotのIDを取得
    https://slack.com/api/users.list?token=さっき取得したAPI_Token
  • ChannelのIDを取得
    https://slack.com/api/channels.list?token=さっき取得したAPI_Token

取得できるIDは、大文字のアルファベットと数字の組み合わせになっているはずです。
例:ABC0ED123

プログラムの作成

以下のプログラムをコピペしてください。

slack_bot.js
constBotkit=require('/path/to/Botkit.js');constos=require('os');constfs=require('fs');constdownload=require('download');consthttps=require('https');constdel=require('delete');constslackBot_id='BotのID';constchannel_id='投稿したいChannelのID';consttoken='取得したAPI Token';varcontroller=Botkit.slackbot({debug:false,});varbot=controller.spawn({token:token}).startRTM();controller.on('file_shared',function(bot,message){if(message.user_id!=slackBot_id){//Bot自身の投稿には反応しないconstmessageObj={token:token,file:message.file_id};bot.api.files.info(messageObj,function(err,res){if(err){console.log(err)}else{console.log('[file_shared] on');varnow=newDate();varfile_name=now.getFullYear()+':'+(now.getMonth()+1)+':'+now.getDate()+':'+now.getHours()+':'+now.getMinutes()+':'+now.getSeconds()+'.jpg';varfile_dir='/path/to/image_dir/';varfile_path=file_dir+file_name;//ローカルに保存する際のディレクトリとファイル名varfile_url=res.file.url_private_download;//送信された画像のURLvaroptions={'method':'GET','hostname':'files.slack.com','path':file_url,'rejectUnauthorized':'false','headers':{'Authorization':'Bearer '+token}};varfile=fs.createWriteStream(file_path);varresponseSent=false;//URL先の画像をローカルに保存https.get(options,response=>{response.pipe(file);file.on('finish',()=>{file.close(()=>{if(responseSent)return;responseSent=false;});//file.close});//file.on});//https.getconsole.log('file download');//時間差で画像の送信→画像の削除を行うsetTimeout(()=>{constmessageObj={file:fs.createReadStream(file_path),filename:file_name,title:file_name,channels:channel_id};bot.api.files.upload(messageObj,function(err,res){if(err){console.log(err);}else{console.log('file upload');}});//bot.api.filessetTimeout(()=>{del([file_path],function(err,res){if(err){console.log(err);}else{console.log('file delete');}});//del},1000);//setTimeout},1000);//setTimeout}//else});//bot.api.files}//ifconsole.log("finish");});//controller.on

実行方法

# Botの起動
$ forever start slack_bot.js 

# Botの停止
$ forever stop slack_bot.js

BotにDMで画像を送ると、
Screenshot from 2020-01-22 23-18-54.png

Botが指定したチャンネルに代わりに投稿してくれます。
Screenshot from 2020-01-22 23-17-00.png


WSL と VSCode と Windows Terminal でコマンドプロンプトにさようなら

$
0
0

macOS から Windows に戻ってきて、「コマンドプロンプトかー、せめて PowerShell だよな」ということで PowerShell Core を使っていたのですが、そういえば WSL(Windows Subsystem for Linux) があったな、と思い出して、開発環境は全面的に WSL を使っていくことにしましたので、その手順をメモしておきます。

なおここで言う開発環境とは、Webアプリのフロントエンドを Angular で、バックエンドを node.js で開発することを指します(また、実際のところ Linux にはあまり詳しくないので、WSL とディストリとシェルを混同して表記している自信があります)。

1. WSL の導入

を参考に、WSL を導入します。

2. Windows Terminal の導入

Windows Terminal は Windows 向けのターミナルクライアントで、現在はまだ Preview 版ですが、なかなか使いやすくカスタマイズ性も高いので愛用しています。

WSL をインストールすると、Windows Terminal にも「Ubuntu」という Profile が増えます。
Windows Terminal を起動したときに、Ubuntu の Terminal がデフォルトで開かれるようにするには、「ctrl + ,」を押して profiles.jsonを開き、defaultProfileを変更します。

他にも、

  • colorSchemeを設定して見やすい色に変更
  • startingDirectoryで初期のディレクトリを設定
  • keybindingsを追加して ctrl+cctrl+vでコピペできるように(既定では ctrl+shift+c/v)

を変更しています。Windows Terminal はこの profiles.jsonを編集することでいろいろカスタマイズできるので楽しいですね。参考までに私の設定を載せます。

profiles.json

{"$schema":"https://aka.ms/terminal-profiles-schema","defaultProfile":"{2c4de342-38b7-51cf-b940-2309a097f518}","profiles":[<中略>{"guid":"{2c4de342-38b7-51cf-b940-2309a097f518}","hidden":false,"name":"Ubuntu","colorScheme":"Campbell","cursorShape":"emptyBox","acrylicOpacity":0.85,"useAcrylic":true,"cursorColor":"#FFFFFF","fontFace":"Cascadia","fontSize":12,"startingDirectory":"C:\\dev","source":"Windows.Terminal.Wsl"}],"schemes":[{"name":"Campbell","foreground":"#F2F2F2","background":"#0C0C0C","colors":["#0C0C0C","#C50F1F","#13A10E","#C19C00","#0037DA","#881798","#3A96DD","#CCCCCC","#767676","#E74856","#16C60C","#F9F1A5","#3B78FF","#B4009E","#61D6D6","#F2F2F2"]}<中略>],//Addanykeybindingoverridestothisarray.//Tounbindadefaultkeybinding,setthecommandto"unbound""keybindings":[{"command":"copy","keys":["ctrl+c"]},{"command":"paste","keys":["ctrl+v"]}]}

3. VScode の Default Terminal を WSL に

WSL を導入すると Visual Studio Code の Terminal にも "wsl" が増えているので、ctrl + shift + pTerminal:Select default shellで、"wsl" に変更します。

なお、.code-workspaceに Terminal を指定することでワークスペース毎に Terminal を切り替えることもできるようですが、wsl が入っていない環境もあるので、私はあまりオススメしません。

4. VSCode のタスク実行を WSL で

VScode の Default Terminal を WSL に変更しても、VScode のタスクの実行は、Windows 側で行われてしまうようで、これも WSL 上で実行させたいです。残念ながらこれを解決するには「WSL に依存したタスク」の記述が必要になります。

./vscode/tasks.json

{"version":"2.0.0","tasks":[{"label":"ng-serve","type":"shell","isBackground":true,"command":"ng serve","problemMatcher":{"owner":"custom","pattern":{"regexp":"^$"},"background":{"activeOnStart":true,"beginsPattern":".*Angular Live Development Server.*","endsPattern":".*Compiled successfully.*"}}},{"label":"ng-serve-wsl","type":"shell","isBackground":true,"command":"\"ng serve\"","options":{"shell":{"executable":"C:\\Windows\\System32\\wsl.exe","args":["bash -ic"]}},"problemMatcher":{"owner":"custom","pattern":{"regexp":"^$"},"background":{"activeOnStart":true,"beginsPattern":".*Angular Live Development Server.*","endsPattern":".*Compiled successfully.*"}}}]}

例えば、上記の tasks.jsonの例では、Angular のデバッグを行うための ng-serveというタスクを定義していますが、これは Windows 上で動作してしまうので、それに加えて WSL 上で動作させる設定を加えた ng-serve-wslというタスクも用意しています。

WSL版の違いは、options: { }で設定した wsl.exeの情報で、これはコマンドプロンプトで、

wsl.exe bash -ic "ng serve"

を実行することに相当します。
重要なのは bash の実行引数 -iで、これを指定しないと .bashrcが読み込まれない(= PATH が設定されない)ため、ngが command not found エラーになってしまいます。

5. WSL 側に開発環境を構築する

git, node, npm, angular, python, aws-cli, firebase-cli などの開発に必要なツールは、WSL の方に入れていきます。
Ubuntu であれば大抵は apt-get でインストールします。

まとめとちょっと面倒(冗長)なところ

WSL2 まで待っていようかなと思っていたのですが、WSL1 でも使ってみたらとても便利でした。

開発時によく使う Terminal, VSCode については、ほとんど Windows である事を意識せずに過ごす事ができるようになりました。
スクリプトなどは macOS と同じものがほぼ動きますし、Linux で動作する CI のリハーサルも容易になりました。

Windows との相互運用性が思っていたよりも高く、コマンドに .exeをつければ WSL 上からでも Windows コマンドが実行できるので、かゆいところに手が届いてるなと感じました(WSL から msbuild.exeを叩けば Windows でしか動作しない .NET アプリもビルドできるはずだし、そもそも WSL 側に .NET Core を入れてクロスプラットフォームで動作する .NET プログラムの動作確認をする事ももちろん可能です)。

ちょっと冗長なところとしては、

git の認証情報を Win/WSL どちらにも記憶させる必要がある

bash で git コマンドを叩くこともあれば、Gitクライアントアプリ(GitKrakenや VSCode など)を使うこともあり、認証情報ストアを共通にする方法は知らないので、今のところ両方に記憶させています。

Windows パスから UNIX パスへの変換

「Windows のエクスプローラでパス名をコピーして、bash で cdする」ために、次のようなコマンドが必要です。

cd $(wslpath -u"C:\dev\hoge")

Windows のパス文字列を wslpath関数で UNIX パスに変換できるので、これを咬ませてディレクトリ移動しています。

チームの他のメンバは Windows の人が多い

ため、彼らの環境でも動作する事を前提にスクリプトを記述する必要がある場合、その動作確認のために PowerShell も併用し、結局 Windows 側にも node をインストールせざるを得なかったりします。
まあ、これはチームの事情です。
それでも、Windows Terminal は、タブで WSL と PowerShell どちらも管理できるので使いやすいです。

別解というか本命

VSCode 前提ですが、Remote - WSL Extensionを利用することで、プロジェクトの開発をすべて WSL上 で行えるようになります。上記で説明した Terminal も、タスクの実行もすべてです。

現在はまだ Preivew 版のため安定性が不安であること、リモート前提なため WSL でもなんか遅そう(主観です)な事、VSCodeの拡張機能がモノによってはリモート用をさらにインストールする必要があるなど、やや気になる点があるために少し試しただけで保留としました。

Node.js、Web Speech APIを使って音声認識を出力

$
0
0

概要

WebSpeechAPIを使用して聞き取った音声の文字おこしをブラウザ上に表示させます。

作成方法

1.WebSpeechAPIを含むhtmlフォルダの作成

新規フォルダを作成し、その中にindex.htmlを作成。

index.html
<!-- index_voice.html --><!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><title>音声認識サンプル</title></head><body><h2>音声認識サンプル</h2><buttonid="btn">start</button><divid="content"></div><script>//ここに音声認識の処理を書いていく</script><body><html>

音声認識をするための処理をscriptタグ内に書いていきます。

index.html
//ここに音声認識の処理を書いていく
const speech = new webkitSpeechRecognition();
speech.lang = 'ja-JP';
</script>

音声認識を実行する準備はこの2行で完了です。
webkitSpeechRecognition()を定義し、langをja-JPにすることで日本語に対応したWeb Speech APIが使えるようになります。
そしてこれをイベントで実行出来るようにしていきます。

index.html
//ここに音声認識の処理を書いていく
const speech = new webkitSpeechRecognition();
speech.lang = 'ja-JP';

//---------------追記---------------//
const btn = document.getElementById('btn');
const content = document.getElementById('content');

btn.addEventListener('click' , function() {
    // 音声認識をスタート
    speech.start();
});

speech.addEventListener('result' , function(e) {
   // 音声認識で取得した情報を、コンソール画面に表示
   console.log(e);
//---------------追記---------------//
    // 音声認識で取得した情報を、HTMLに表示
    const text = e.results[0][0].transcript;
    content.innerText = text;
    //--------------------------------//
});
//--------------------------------//

</script>

2.サーバー立ち上げ
node.js Expressを使ってローカルサーバーの立ち上げを行います。
Web speech Api-sampleというフォルダー作成します。

初期設定します。

npm init -y

必要なライブラリをインストールします。

npm i body-parser express

index.jsを作成し、こちらのコードをコピペします。

varexpress=require('express');varapp=express();// public というフォルダに入れられた静的ファイルはそのまま表示app.use(express.static(__dirname+'/public'));// bodyParservarbodyParser=require('body-parser');app.use(bodyParser.json());app.post('/post',function(req,res){for(keyinreq.body){console.log(key,'=',req.body[key]);}res.end();});app.listen(process.env.PORT||8080);console.log("server start! (heroku)");

こちらをindex.jsで保存します。
index.js と同じ階層に public フォルダを作りその中に 先ほど作成したindex.html を格納します。

node index.js

実行します。

http://localhost:8080/でアクセス。
スタートボタンをおして話し、終わりにまたボタンを押すと。
音声認識.PNG

これでひとまず完了です。次に使用してHerokuにデプロイしていきます。(続く)

考察

まずは音声認識APIをつかってアウトプットすることができました。音声の聞き取りの精度もパソコンを目の前にあるような状況、会議とかだったら問題なく拾える制度でした。
次は、Vue.jsでデザインを整えていきます。
また、いまのままではただの文字の羅列になるので、これをどう編集させていくか、プログラムで機能を追加できていければと思います。

Node.jsとDynamoDBで日時データの処理

$
0
0

DynamoDBに日時データを持たせる2つの方法

  1. データ型をStringにして2016-02-152015-12-21T17:42:34Zのように文字列で持たせる。
  2. データ型をNumberにして1579740176030のように数値で持たせる。

2.項のNumber型の実用例としては、エポック時間 (1970 年 1 月 1 日の 00:00:00 UTC 以降の秒数) を利用することができる。(UNIXTIMEの詳細)

Node.jsでエポック時間を扱う

現在の日時をDateオブジェクトで取得する

constdate=newDate();console.log(date);// 2020-01-23T01:09:41.444Zconsole.log(typeofdate);// 'object'

現在の日時をエポック時間で取得する

constdate=Date.now();console.log(date);// 1579740176030console.log(typeofdate);// 'number'

DynamoDBから取得したエポック時間をDateオブジェクトへ変換する

constunixtime=1579740176030;// DynamoDBから取得したエポック時間と想定constdate=newDate(unixtime);console.log(date);// 2020-01-23T01:09:41.444Zconsole.log(typeofdate);// 'object'

Typescript Node.js メソッドの結果をキャッシュする

$
0
0

背景

APIの結果を指定した時間キャッシュさせたかったので、シンプルにキャッシュできるクラスを自前で実装する。

使用ライブラリ

  • ioredis
  • zlib

実装

importioredisfrom'ioredis'importzlibfrom'zlib'classClient{privateredis:ioredis.Redis=newioredis('redis://exammple:6379')asyncfetch<T>(key:string,func:()=>Promise<T>,expire):Promise<T>{constcached=awaitthis.redis.get(key)// キャッシュがあれば、解凍して返却if(cached){constcacheString=awaitnewPromise<string>((resolve,reject)=>zlib.inflate(Buffer.from(cached,'base64'),(err,buffer)=>{if(err)returnreject(err)resolve(buffer.toString())}))returnJSON.parse(cacheString)}// キャッシュがなければ、メソッドの結果を圧縮してredisに保存constresult=awaitfunc()constcache=awaitnewPromise<string>((resolve,reject)=>zlib.deflate(JSON.stringify(result),(err,buffer)=>{if(err)returnreject(err)resolve(buffer.toString('base64'))}))awaitthis.redis.setex(key,expire,cache)returnresult}}exportconstcacheClient=newClient()

使用例

awaitcacheClient.fetch('cache-key',()=>{heavyTask()},3000)

[JavaScript][Node.js]メモ:アロー関数の文法

$
0
0

アロー関数(ES2015)の書き方。

f=()=>console.log('Hello!')f()// Hello!
評価した値を返す
// 式(Expressions)の評価結果が戻り値になるsum=(a,b)=>a+br=sum(1,2)console.log(r)// 3
returnで値を返す
// {}で文(Statements)を作り、returnで値を返すsum=(a,b)=>{returna+b}r=sum(1,2)console.log(r)// 3
引数のカッコを省略
// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可)say=word=>console.log(word+'!')say('Yeah')// Yeah!
即時関数
(()=>console.log('Hello!'))()
引数としてのアロー関数
arr=[1,2,3,4,5]// かっこよくないconsole.log(arr.map(function(e){returne+1}))// [ 2, 3, 4, 5, 6 ]// かっこいいconsole.log(arr.map(e=>e+1))// [ 2, 3, 4, 5, 6 ]

リンク(mozilla)

[Node.js]モジュール定義について整理(exports-require / export-import)

$
0
0

はじめに

あるファイルに定義した関数等を別のファイルで使いたいときにどうするか。

Node.jsでは二つのやり方がある。

  1. exportsで公開してrequireで読み込む(CommonJS)
  2. exportで公開してimportで読み込む(ES2015)

常に使えるのは1の方法。Babelを使うなら2の方法でも可能。
どちらを選んでもメリット・デメリットがあるわけではない(と思う)ので、お好きなほうで。

個人的には、常に使える1の方法がいいような気がしている。

使用例

exportsで公開してrequireで読み込む(CommonJS)

一つの関数だけをエクスポート

module.js
// 関数を定義constf=()=>{console.log('Hello!')}// 関数を公開module.exports=f
client.js
constf=require('./module')f()// Hello!

エクスポートするのは関数に限らず、文字列や数値でも良い。

複数の関数をエクスポート

module.js
// 関数を定義constf1=()=>console.log('Hello!')constf2=()=>console.log('World!')// 別名をつけて、二つの関数を公開module.exports={sayHello:f1,sayWorld:f2}
client.js
constmod=require('./module')mod.sayHello()// Hello!mod.sayWorld()// World!

クラスをエクスポート

module.js
// クラスを定義classPerson{constructor(name){this.name=name}greet(params){console.log(`Hello, ${this.name}!`)}}// 公開module.exports=Person
client.js
constPerson=require('./module')p=newPerson('aki')p.greet()// Hello, aki!

おまけ:選択してインポート

module.js
constf1=()=>console.log('Hello!')constf2=()=>console.log('World!')module.exports={sayHello:f1,sayWorld:f2}
client.js
// インポートする対象を選択するconst{sayHello}=require('./module')sayHello()// Hello!

exportで公開してimportで読み込む(ES2015)

下記の参考資料を参照。いつか書く。

参考資料

1. exports - require(CommonJS)

2. export - import((ES2015))


Node.jsからFBX SDK Pythonを呼べるDockerイメージ作った

$
0
0

Node.jsからFBX SDK Pythonを呼べるDockerイメージ作った

とある事情により、Node.jsからFBX SDK Pythonを呼ぶ必要があったので、Dockerイメージを作りました。

作ったDockerイメージは以下に公開しました。
https://hub.docker.com/r/seguropus/fbx-sdk-python-nodejs

サンプルコード

サンプルコードを以下に置きます。
https://github.com/segurvita/docker-fbx-sdk-python-nodejs

サンプルコードの使い方

# Dockerイメージをビルド
docker-compose build

# Dockerコンテナを起動
docker-compose up

これで、以下のような表示が出れば成功です。

fbx-sdk-python-nodejs    | # FBX SDK can read the following formats.
fbx-sdk-python-nodejs    | 00 FBX (*.fbx)
fbx-sdk-python-nodejs    | 01 AutoCAD DXF (*.dxf)
fbx-sdk-python-nodejs    | 02 Alias OBJ (*.obj)
fbx-sdk-python-nodejs    | 03 3D Studio 3DS (*.3ds)
fbx-sdk-python-nodejs    | 04 Collada DAE (*.dae)
fbx-sdk-python-nodejs    | 05 Alembic ABC (*.abc)
fbx-sdk-python-nodejs    | 06 Biovision BVH (*.bvh)
fbx-sdk-python-nodejs    | 07 Motion Analysis HTR (*.htr)      
fbx-sdk-python-nodejs    | 08 Motion Analysis TRC (*.trc)      
fbx-sdk-python-nodejs    | 09 Acclaim ASF (*.asf)
fbx-sdk-python-nodejs    | 10 Acclaim AMC (*.amc)
fbx-sdk-python-nodejs    | 11 Vicon C3D (*.c3d)
fbx-sdk-python-nodejs    | 12 Adaptive Optics AOA (*.aoa)      
fbx-sdk-python-nodejs    | 13 Superfluo MCD (*.mcd)
fbx-sdk-python-nodejs    | 14 (*.zip)
fbx-sdk-python-nodejs exited with code 0

表示されているのは、FBX SDKが読み込み可能なファイル形式の一覧です。

これが表示されたということは、無事にFBX SDK Pythonにアクセスできているということになります。

何が起きてるのか?

まず、 docker-compose upでDockerコンテナーが起動します。

Dockerコンテナーは、起動したらNode.jsのコード index.jsを実行します。

index.jsはPythonのコード main.pyを呼びます。

main.pyはFBX SDK Pythonから対応フォーマット一覧を取得して、表示します。

Dockerfile

PythonとNode.jsが同居したDockerイメージを公開している方がいたので、そちらをもとに Dockerfileを作成してみました。

Dockerfile
# Python 2.7とNode.js 12が入ったAlpineFROM nikolaik/python-nodejs:python2.7-nodejs12-alpine# apkでライブラリ更新RUN apk update &&\
    apk add \
    curl \
    libxml2 \
    libstdc++

# FBX SDKをダウンロードRUN curl -L\
    https://damassets.autodesk.net/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxpythonsdk_linux.tar.gz \
-o /tmp/fbx20195_fbxpythonsdk_linux.tar.gz

# インストール先フォルダ作成RUN mkdir-p /python-fbx/install

# FBX SDKを解凍RUN tar-zxvf\
    /tmp/fbx20195_fbxpythonsdk_linux.tar.gz \
-C /python-fbx &&\
printf"yes\nn" | \
    /python-fbx/fbx20195_fbxpythonsdk_linux \
    /python-fbx/install

# FBX SDKをインストールRUN cp /python-fbx/install/lib/Python27_ucs4_x64/*\
    /usr/local/lib/python2.7/site-packages/

# python-shellをインストールRUN npm install-g python-shell

# 一時ファイルを削除RUN rm-r /python-fbx
RUN rm /tmp/fbx20195_fbxpythonsdk_linux.tar.gz

# 環境変数NODE_PATHを設定ENV NODE_PATH /usr/local/lib/node_modules

python-shellというのは、Node.jsからPythonを呼ぶためのライブラリです。

グローバル領域にインストールしたので、index.jsが requireで取得できるように、環境変数 NODE_PATHを設定しています。

docker-compose.yml

docker-compose.ymlはこんな感じです。

docker-compose.yml
version:'3'services:fbx-sdk-python-nodejs:image:'seguropus/fbx-sdk-python-nodejs'container_name:'fbx-sdk-python-nodejs'build:context:./dockerfile:./Dockerfilevolumes:-.:/srcworking_dir:/srccommand:node index.js

Dockerコンテナー起動時に index.jsが実行されます。

index.js

index.jsはこんな感じです。

index.js
constpythonShell=require('python-shell');// python-shellのオプションconstpyOption={mode:'text',pythonPath:'/usr/local/bin/python',pythonOptions:['-u'],scriptPath:'/src',}// main.pyを実行constpyShell=newpythonShell.PythonShell('main.py',pyOption);// Pythonの標準出力を表示pyShell.on('message',(message)=>{console.log(message);});// 終了処理pyShell.end(function(err,code,signal){if(err){console.error(err);}console.log('The exit code was: '+code);});

python-shellmain.pyを呼んでいます。

main.py

main.pyはこんな感じです。

main.py
fromfbximport*deflist_reader_format(manager):print('# FBX SDK can read the following formats.')forformatIndexinrange(manager.GetIOPluginRegistry().GetReaderFormatCount()):description=manager.GetIOPluginRegistry().GetReaderFormatDescription(formatIndex)print(formatIndex,description)defmain():# Create
manager=FbxManager.Create()scene=FbxScene.Create(manager,"fbxScene")# List
list_reader_format(manager)# Destroy
scene.Destroy()manager.Destroy()if__name__=='__main__':main()

from fbx import *でFBX SDK Pythonを読み込んでいます。
list_reader_format()という関数で、FBX SDK Pythonが読み込み可能なファイル形式の一覧を標準出力に表示しています。

無事にNode.jsからFBX SDK Pythonを呼べるDockerイメージを作ることができました!

Docker Hubに公開してみる

せっかくなので、Docker Hubに公開してみます。

# ログインする
docker login

# Dockerイメージをプッシュする
docker push seguropus/fbx-sdk-python-nodejs

以下に公開されました。
https://hub.docker.com/r/seguropus/fbx-sdk-python-nodejs

さいごに

本記事作成にあたり、以下のページを参考にしました。ありがとうございました。

Azure DevOps を社内 npm registry として使う時の Tips

$
0
0

社内開発フレームワークの npm package を管理するために、Azure DevOps の Artifacts がメンバー限定 npm registry として使える事が判った。
その設定を行った際に判ったことをメモ。

Azure DevOps の設定

に書いてある通りなのだが、サラッと言うと。

  1. Azure DevOps にプロジェクトを作成し、Artifacts を選択
  2. Create Feed で Feed を作成。名前は適当に、Visibility は "Member of " で。"Upstream Source" もチェックを入れる。
  3. 右上のギアアイコンをクリック → Upstream sources タブを選択し、npmjs 以外の Source を削除する(npm registry としてのみ使うので)
  4. Artifacts の Top に戻り、"Connect to Feed" を押す。次の画面で npm グループ内の npm を選択。
  5. Linux(WSL) なので、Project setup 内の Other タブを選択。
  6. registry=https://xxxで始まる2行をコピーしておく。 ※A
  7. 次に、 ; begin auth tokenから ; end auth tokenまでをコピーしておく。 ※B
  8. Step 2 にある personal access token をクリックする。
  9. パッケージデプロイする用の Token を作成する。Token Name は npm-uploaderとし、期限は任意(最長で1年のようだ)、Scopes は PackagingRead & Writeを選択して保存する。表示される token をコピーしておく。 ※C
  10. パッケージを利用する人用の Token を作成する。Token Name は npm-readerとし、期限は任意(最長で1年のようだ)、Scopes は PackagingRead のみを選択して保存する。表示される token をコピーしておく。 ※D

※A ~ D の例

※A

registry=https://pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/registry/

always-auth=true

※B

;begin auth token
//pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/registry/:username=myorg
//pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
//pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/registry/:email=youraddress@mycompany.com
//pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/:username=myorg
//pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
//pkgs.dev.azure.com/myorg/myproj/_packaging/myfeed/npm/:email=youraddress@mycompany.com
;end auth token

↑なんだか後半の3行は特に必要ないみたい。※A の registry URL に対応してるのだからそうだよね。

※C、※D値は適当です

# エンコード前の token
wbeogamr5bhqnyxpiodntpdb57tffqbqholocat3e6iwvyx2rd3q

# Base64 エンコードecho-n"wbeogamr5bhqnyxpiodntpdb57tffqbqholocat3e6iwvyx2rd3q" | base64# Base64 エンコードされた tokend2Jlb2dhbXI1Ymhxbnl4cGlvZG50cGRiNTd0ZmZxYnFob2xvY2F0M2U2aXd2eXgycmQzcQ==

クライアント(パッケージデプロイ者)の設定

  1. 端末のホームディレクトリ($home) に .npmrcを作成し、※Aの2行を貼り付けて保存する。
  2. デプロイする npm プロジェクトの package.jsonと同じディレクトリに .npmrcを作成し、※B; begin auth tokenから ; end auth tokenを貼り付ける。2つある _password=に、※Cの token を BASE64 エンコードした文字列を貼り付けて保存する。
  3. 2箇所ある email=には、パッケージ開発者のメールアドレスを設定しておく(任意)。

これで npm publishを実行すると、Artifacts に npm パッケージがデプロイされる。
DevOps の Artifacts の Top ページを更新すると、デプロイされた npm パッケージが表示される。

クライアント(パッケージ利用者)の設定

  1. 端末のホームディレクトリ($home) に .npmrcを作成し、※Aの2行を貼り付けて保存する。
  2. 1. の .npmrcに、※B; begin auth tokenから ; end auth tokenを貼り付ける。2つある _password=に、※Dの token を BASE64 エンコードした文字列を貼り付けて保存する。

これで、npm install <デプロイしたnpmパッケージ名>とすると、そのパッケージがダウンロード、インストールされる。

ポイント

デプロイ用と読み取り専用

パッケージをデプロイできる token と、利用しかできない token を用意して、利用しかできない token をユーザーホームの(既定の) 認証情報として設定する。

パッケージのデプロイが必要な場合は、プロジェクトごとに .npmrcを作り設定することで、間違えてデプロイしてしまう危険性を排除している。

Upstrem source

セカンダリの npm registry として、既定の https://registry.npmjs.org/が使用されるため、https://www.npmjs.com/で公開されているすべての npm パッケージも利用できる。

ただし、

  • token が expire した場合は、https://www.npmjs.com/からのインストールも失敗する
  • DevOps を経由しているせいか、通常より遅い?(気のせい)

という留意点がある。

認証方式

Personal Access Token の代わりに SSH public keys が使えないだろうか?要検証。

Tektonを使ってAppsodyで作ったNodejsアプリケーションをK8sにデプロイする。(Tekton準備編)

$
0
0

先日、Appsodyを使ってNode.jsモジュールをK8sに簡単にデプロイする方法を記事にしました。

この記事ではさらに踏み込んで、作成したNode.jsモジュールをTektonを使ってK8sにデプロイするCI/CDパイプラインを作ってみましょう!
まず、この記事では以下の環境にTektonを準備するところから始めます。
※前回と同様に環境はmac。docker for macでKubernetesが有効であることが前提です。

What's Tekton?

ざっくりいうと、Kubernetes上で稼働するクラウドネイティブなCI/CDパイプラインとなります。
代表的な使い方は、Githubからソースコードを取得して、それを自動的にKubernetesへデプロイする、というところでしょうか。
詳細は本筋とは逸れるのであまり触れませんが、Githubのリポジトリを参照してください。
Tekton

ここからが本題

さて、実際Kubernetesで運用することを考えたときに、毎回、ソースコードをどこからかコピーしてきて

appsody deploy

を叩くのは、あまり現実的ではありませんよね?
そこで、上記で書いたTektonの出番です。

まずはTektonを導入する

はじめに
以下の記事は
https://www.skyarch.net/blog/?cat=518
→こちらを参考にさせて頂きました。ありがとうございます。

Tektonのインストール

$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/latest/release.yaml

下記のコマンドを打つことで、Tektonがインストールされたことを確認できます。

$ kubectl get pods --namespace tekton-pipelines
NAME                                           READY   STATUS              RESTARTS   AGE
tekton-pipelines-controller-5888756f5c-8jdhz   0/1     ContainerCreating   0          8s
tekton-pipelines-webhook-7494f6f84b-644nn      1/1     Running             0          8s

Tekton Dashboadを導入する

ただ、Tektonをインストールしただけでは、実際にKubernetesでのデプロイ結果を確認することが難しいです。ログなどを確認できるようにTekton Dashboadを導入しましょう。

はじめにTekton DashboardのソースをGit Hubからクローンします。

$ git clone https://github.com/tektoncd/dashboard.git

下記のコマンドでTekton Dashboardのインストールは完了です。

$ kubectl apply -f dashboard/config/release/gcr-tekton-dashboard.yaml
serviceaccount/tekton-dashboard created
customresourcedefinition.apiextensions.k8s.io/extensions.dashboard.tekton.dev created
clusterrole.rbac.authorization.k8s.io/tekton-dashboard-minimal created
clusterrolebinding.rbac.authorization.k8s.io/tekton-dashboard-minimal created
deployment.apps/tekton-dashboard created
service/tekton-dashboard created
pipeline.tekton.dev/pipeline0 created
task.tekton.dev/pipeline0-task created

下記のport-forwardコマンドを実行後、http://localhost:9097でダッシュボードにアクセスしてみましょう。

$ kubectl --namespace tekton-pipelines port-forward svc/tekton-dashboard 9097:9097

下記の画面が表示されたら準備は完了です。
スクリーンショット 2020-01-24 8.56.53.png

Tektonを使ってAppsodyで作ったNodejsアプリケーションをK8sにデプロイする。

$
0
0

さて、
Tektonを使ってAppsodyで作ったNodejsアプリケーションをK8sにデプロイする。(Tekton準備編)で、Tekton Dashboadの用意ができたら、いよいよAppsodyで作ったNode.jsのモジュールをデプロイしていきます。

今回は私のリポジトリをクローンします。

$ git clone https://github.com/motuo1201/appsody-test-build.git
$ cd appsody-test-build/

一度、ローカルで動かしてみましょう。

こちらの記事にも書きましたが、下記のコマンドでローカルの稼働が確認できます。

$ appsody run
......
[Container] App started on PORT 3000

http://localhost:3000でアクセスが可能になります。文言自体は、app.jsに書かれています。

スクリーンショット
スクリーンショット 2020-01-24 9.05.35.png

app.jsの中身

constapp=require('express')()app.get('/',(req,res)=>{res.send("Hey! Hello Appsody!");});module.exports.app=app;

では、これをいよいよTekton経由でデプロイしてみましょう。

Docker Registoryとアクセスする情報を予め用意する。

Tektonはデプロイをする時に、Docker Registoryを使います。そのため、事前にDocker Hubなどに自分が操作できるDocker Registoryを用意しましょう!

こんな感じ。

スクリーンショット 2020-01-24 9.15.10.png

次に、Kubernetesにdocker hubの認証情報を登録します。
secret.yamlという名前(何でも良いのですが、、、)のファイルを作って、認証情報を作成します。

apiVersion:v1kind:Secretmetadata:name:my-nodejs-dockannotations:tekton.dev/docker-0:https://index.docker.io/v1/type:kubernetes.io/basic-authstringData:username:********password:********

このファイルを作成したら、Kubernetesに登録しましょう。

$ kubectl apply -f secret.yaml 
secret/my-nodejs-dock created

これによって、Tektonからもmy-nodejs-dockという認証情報が登録されたことが確認できます。
スクリーンショット 2020-01-24 9.21.19.png

Tektonを動かすための各種定義を行う。

1.appsodyで作成したモジュールをTektonでデプロイするためのサンプルが既に用意されています。これをCloneしましょう。

$ git clone https://github.com/appsody/tekton-example
$ cd appsody-tekton/tekton-example/

2.次にサービスアカウント作成と適切な権限を付けていきます。(特にファイルの修正はありません)

$ kubectl apply -f appsody-service-account.yaml
serviceaccount/appsody-sa created
$ kubectl apply -f appsody-cluster-role-binding.yaml
clusterrolebinding.rbac.authorization.k8s.io/appsody-admin created

3.登録されたサービスアカウントに、先ほど作成した認証情報を紐づけていきます。

$ kubectl edit serviceaccount appsody-sa

これをするとファイルの修正画面が出るので、secretに先ほど作成した認証のnameを設定
(ここでは最終行に- name: my-nodejs-dockを追加しています。)

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"appsody-sa","namespace":"default"}}
  creationTimestamp: "2020-01-24T00:24:36Z"
  name: appsody-sa
  namespace: default
  resourceVersion: "3056"
  selfLink: /api/v1/namespaces/default/serviceaccounts/appsody-sa
  uid: 257efc19-e7d4-400f-a32c-b758956172e6
secrets:
- name: appsody-sa-token-6tw82
- name: my-nodejs-dock

4.Tektonのパイプラインおよび、タスクの登録をします。(特にファイルの修正はありません)

$ kubectl apply -f appsody-build-task.yaml
task.tekton.dev/appsody-build-task created
$ kubectl apply -f appsody-build-pipeline.yaml
pipeline.tekton.dev/appsody-build-pipeline created

5.Tektonで使用するDockerとgitのリソースを登録します。

appsody-pipeline-resources.yamlにあるDockerのimageとgitのリポジトリを修正します。

apiVersion:v1items:-apiVersion:tekton.dev/v1alpha1kind:PipelineResourcemetadata:name:docker-imagespec:params:-name:urlvalue:index.docker.io/yenomoto1201/appsody-deploy-nodejs#ここを修正type:image-apiVersion:tekton.dev/v1alpha1kind:PipelineResourcemetadata:name:git-sourcespec:params:-name:revisionvalue:master-name:urlvalue:https://github.com/motuo1201/appsody-test-build#ここを修正type:gitkind:List

修正したら、この情報をKubernetesに登録

$ kubectl apply -f appsody-pipeline-resources.yaml
pipelineresource.tekton.dev/docker-image created
pipelineresource.tekton.dev/git-source created

この情報もTekton Dashboad上で確認可能です。
スクリーンショット 2020-01-24 9.33.30.png

docker-image
スクリーンショット 2020-01-24 9.34.07.png

git-source

スクリーンショット 2020-01-24 9.35.13.png

6.いよいよ、動かしてみましょう!

下記のコマンドを実行して、Tekton Pipelineを起動します。

$ kubectl apply -f appsody-pipeline-run.yaml 
pipelinerun.tekton.dev/appsody-manual-pipeline-run created

ダッシュボードのPipelineRunsで処理が実行されていることを確認できます。

スクリーンショット 2020-01-24 9.37.13.png

約5分くらいで処理が完了します。このように各ステップのログを確認することができます。

スクリーンショット 2020-01-24 9.43.57.png

Serviceを確認すると、きちんとデプロイできていることがわかります。

$ kubectl get svc
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
appsody-test-build   NodePort    10.109.30.147   <none>        3000:32272/TCP   108s
kubernetes           ClusterIP   10.96.0.1       <none>        443/TCP          54m

ここで確認した、PORT:32272を元に、http://localhost:32272/にアクセスしてみます。
スクリーンショット 2020-01-24 9.46.48.png

良い感じですね。
これで、Appsodyを使ったCI/CD環境を構築することができました!

Lambda Node.js8.10から10.xへの バージョンアップに伴うImageMagickの対応

$
0
0

はじめに

今回、LambdaランタイムNode.js8.10のサポート終了に伴い、Node.js10.xへアップデートを行いました。
結構詰まった部分などが合ったので、その備忘録として残します。

LambdaランタイムNode.js8.10のサポート終了について

以下、AWS公式より
スクリーンショット 2020-01-24 14.17.37.png
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtime-support-policy.html

2020/2/3を持ってLambdaのランタイムNode.js8.10の更新が終了します。
これに伴い、Node.js8.10で実装しているLambdaのランタイムを10.xにバージョンアップをしました。

Lambdaのランタイム変更による変更内容

大きな変更は、コンテナ内で起動するEC2のOSがAmazon LinuxからAmazon Linux2へ変わることです。
これにより、もともとデフォルトでOSにインストールされていた、graphicsmagickimagemagickがAmazon Linux2ではインストールされていないという状態になります。

こちらが参考記事です
https://note.com/mohya/n/n48692d8e4a57#jgl1s
つまり、Node.js10以上で、imagemagickなどを使いたい場合は、インストールしなければけません。

本記事では、この点についての対応方法を紹介します。

手順

  1. 単純にランタイムを変更する
  2. Lambdaを実行させ、エラー内容を確認する
  3. graphicsmagickとimagemagickのレイヤーを追加する
  4. Lambdaを実行させ、成功を確認する

1. 単純にランタイムを変更する

まず、状況確認をするために単純にランタイムを変更します。
スクリーンショット 2020-01-24 15.36.06.png

2. Lambdaを実行させ、エラー内容を確認する

以下のようなエラーが出ました。

2020-01-24T02:40:09.509Z    c28c7e8b-b775-48ae-ac36-aec733c9b025    ERROR   Invoke Error    
{
    "errorType": "Error",
    "errorMessage": "Could not execute GraphicsMagick/ImageMagick: identify \"-ping\" \"-format\" \"%wx%h\" \"-\" this most likely means the gm/convert binaries can't be found",
    "stack": [
        "Error: Could not execute GraphicsMagick/ImageMagick: identify \"-ping\" \"-format\" \"%wx%h\" \"-\" this most likely means the gm/convert binaries can't be found",
        "    at ChildProcess.<anonymous> (/opt/nodejs/node_modules/gm/lib/command.js:232:12)",
        "    at ChildProcess.emit (events.js:198:13)",
        "    at ChildProcess.EventEmitter.emit (domain.js:448:20)",
        "    at Process.ChildProcess._handle.onexit (internal/child_process.js:246:12)",
        "    at onErrorNT (internal/child_process.js:415:16)",
        "    at process._tickCallback (internal/process/next_tick.js:63:19)"
    ]
}

ImageMagickが見つからず、実行できないと言われてしまっています。

3. graphicsmagickとimagemagickのレイヤーを追加する

ここがメインです。
Amazon Linux2ではImageMagickが存在しないので、インストールする必要があります。
こちらのissueが大変参考になりました。
https://github.com/ysugimoto/aws-lambda-image/issues/191

Imagemagickレイヤーの設置

サーバーレスレポジトリにて提供されているので、こちらから作成します。
https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:145266761615:applications~image-magick-lambda-layer

Gaphicsmagickレイヤーの設置

こちらは、誰か知らない人が作ってくれているので、使っちゃいます。
https://github.com/rpidanny/gm-lambda-layer
こちらは、Dockerになっていて、あれ???と思いましたが
LayerもそれぞれDockerを起動してくれ、起動時にDockerfileに記載してあるこの辺りでインストールしてくれます。

RUN curl https://versaweb.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${GM_VERSION}/GraphicsMagick-${GM_VERSION}.tar.xz | tar -xJ && \
cd GraphicsMagick-${GM_VERSION} && \
./configure --prefix=/opt --enable-shared=no --enable-static=yes --with-gs-font-dir=/opt/share/fonts/default/Type1 && \
make && \
make install

このレイヤーをLambdaに登録しておけば、いいらしいですね。便利です。

レイヤーの適応

今回はServerless Frameworkを使用しているため、ymlで記載します。

functions:storePhotoData:handler:storePhotoData.handlerlayers:-arn:aws:lambda:ap-northeast-1:579663348364:layer:image-magick:1-arn:aws:lambda:ap-northeast-1:175033217214:layer:graphicsmagick:2-{Ref:MyLayerLambdaLayer}tracing:Activedescription:create storePhotoDatamemorySize:2048timeout:10role:storePhotoDataLambdaFunctionRole

レイヤーには実行順序という概念もあるため、レイヤーの順番も気をつけなければなりません。
MyLayerLambdaLayerはカスタマイズして作ったレイヤーで、graphicsmagickとimagemagickがないと実行できないので、念の為後ろに持ってきました。
(あんまり意味ないかも...)

4. Lambdaを実行させ、成功を確認する

ログが出せないのが申し訳ないですが、これで成功しました!!

Node.js 12.xの対応

この時どうせならと思ってランタイムをNode.js12.xにしてみたのですが、一応動きました。w
少しだけ気になる点もありましたが、恐らく大丈夫かなと思います。
一気に12へあげたい方はやってみてください。

Viewing all 8882 articles
Browse latest View live