AWS Copilot で nginx を使ったサイドカーパターンを試す

前回からの続きで Express の前段に nginx を配置する構成を Copilot のサイドカーパターンを使って構築する方法を紹介します。

サイドカーパターンとは

コンテナのデザインパターンの 1 つで、メインとなるコンテナを拡張・補完する目的で別のコンテナを配置することです。 その名の通りバイクの横についてるサイドカーが由来になっているそうです。

参考1: Sidecars - AWS Copilot CLI

参考2: https://dl.acm.org/doi/10.5555/3027041.3027059

前提

前回の記事からの続きとして AWS Copilot で Express がデプロイされている状態から始めます。

blog.morugu.com

nginx の準備

前回のディレクトリ構造に nginx を追加して以下のようにしていきます。

copilot-express
 ┣ copilot
 ┃ ┣ api
 ┃ ┃ ┗ manifest.yml
 ┃ ┗ .workspace
 ┣ nginx ←これから作成
 ┃ ┣ Dockerfile
 ┃ ┗ nginx.conf
 ┣ Dockerfile
 ┣ index.ts
 ┣ package.json
 ┗ tsconfig.json

nginx の作業用ディレクトリを作成

$ mkdir nginx && cd nginx

nginx.conf の作成

$ touch nginx.conf
server {
  listen 80;

  location / {
    proxy_pass http://localhost:3000;
  }
}

ここで localhost:3000 を指定する理由は Fargate タスクはawsvpc networking modeと呼ばれる通信方式で動作しており、同一タスクに属するコンテナ間は localhost でのアクセスが可能なためです。

参考 1: Task Networking in AWS Fargate | AWS Compute Blog

参考 2: More detailed example for the nginx sidecar use case · Discussion #2674 · aws/copilot-cli · GitHub

Dockerfile の作成

$ touch Dockerfile
FROM nginx:alpine

RUN rm -f /etc/nginx/conf.d/*

ADD nginx.conf /etc/nginx/conf.d/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Docker イメージの作成

$ docker buildx build --platform=linux/amd64 -t copilot-express/nginx .

$ docker images | grep copilot-express/nginx

docker buildxを使う理由は、M1 Mac 上でdocker buildで作成したイメージだとデプロイ時にstandard_init_linux.go:228: exec user process caused: exec format errorが発生してタスクの起動に失敗するためです。

具体的には M1 Mac (arm64)と Fargate(Linux のデフォルトは x86_64) の CPU アーキテクチャが異なるため発生しており、対策として docker buildx を使用してビルドしています。

参考: node.js - 'exec user process caused: exec format error' in AWS Fargate Service - Stack Overflow

ECR リポジトリの作成 & プッシュ

前回copilot initで作成された ECR リポジトリ名がcopilot-express/apiだったので、 今回はcopilot-express/nginxという名前で ECR リポジトリを作成します。

$ aws ecr create-repository --repository-name copilot-express/nginx

タグをつけてプッシュします。

$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com

$ docker tag copilot-express/nginx:latest xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/copilot-express/nginx:latest

$ docker push xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/copilot-express/nginx:latest

これで nginx の準備が完了です。

Copilot Manifest の修正 & デプロイ

./copilot/api/manifest.yml の修正

前回copilot initで作成された ./copilot/api/manifest.yml に 以下のように追記します。

http:
  # 追加
  targetContainer: nginx

# 追加
sidecars:
  nginx:
    port: 80
    # ECR にプッシュした nginx イメージの URI を指定
    image: xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/copilot-express/nginx:latest

ECS デプロイ

./copilot/api/manifest.ymlへの追記が完了したらデプロイを実行します。

$ copilot svc deploy

~~~~~
✔ Deployed service api.
Recommended follow-up action:
  - You can access your service at http://xxxxx.ap-northeast-1.elb.amazonaws.com over the internet.

表示された URL にアクセスして前回同様 Express app works! が表示されたらサイドカーパターンでのデプロイ完了です。

構成の確認

マネジメントコンソールの ECS > クラスター > copilot-express-test-Cluster-xxxxx(copilot initで作成されたクラスター) > タスクから、apinginxのコンテナがRUNNINGになっていることが確認できます。

f:id:morugu:20220107191546p:plain


また EC2 > ロードバランサー > 対象のロードバランサー > リスナータブ > 転送先からポートが 3000 だったのが 80 に変わっていることが確認できます。

f:id:morugu:20220107192310p:plain

リソースの削除

各種構成や動作確認ができたので、Copilot で作成したリソースを削除します。

$ copilot app delete

ECR の copilot-express/nginx リポジトリは AWS CLI から作成したので個別で削除します。

$ aws ecr delete-repository --repository-name copilot-express/nginx --force

まとめ

we are planning to support using local images or Dockerfiles.

と、公式ドキュメントにあるのでローカルのコンテナ環境をそのまま Copilot で ECS にデプロイできる日も近そうです!

AWS Copilot で ECS(Fargate) に Express(Node.js) をデプロイする

AWS Copilot を使って ECS(Fargate) に Express(Node.js)をデプロイする方法を紹介します。

前提

AWS CLI, Copilot CLI がインストール・設定済みで
ローカルでの動作確認には Docker Desktop for Mac を使っています。

  • AWS CLI
$ aws --version
aws-cli/2.2.35 Python/3.8.0 Darwin/20.5.0 source/x86_64 prompt/off
  • Copilot CLI
$ copilot --version
copilot version: v1.13.0
  • Docker Engine
$ docker --version
Docker version 20.10.11, build dea9396
  • maOS 11.4

Express の準備

作業ディレクトリを作って TypeScript で Express が動くように設定します。

$ mkdir copilot-express && cd copilot-express

$ npm init -y

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

$ npm install express

$ npx tsc --init

index.ts の作成

$ touch index.ts

作成したindex.tsに Express の起動と / への GET リクエストハンドラーを定義します。

import express from 'express';
import { Request, Response } from 'express';

const app = express();
const port = 3000;

app.get('/', (req: Request, res: Response) => res.send('Express app works!'));

app.listen(port, () => console.info(`Express listening on port ${port}!`));

スクリプトの追加

package.jsonscripts"start": "ts-node index.ts"を追加します。

{
  ~~~
  "scripts": {
    "start": "ts-node index.ts"
  }
  ~~~
}

Dockerfile の作成

$ touch Dockerfile
FROM node:16

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD [ "npm", "start" ]

Docker イメージの作成 & 実行

$ docker build -t copilot-express .

$ docker images | grep copilot-express

$ docker run -p 3000:3000 -d copilot-express

http://localhost:3000 にアクセスして Express app works! が表示されたら準備完了です。

f:id:morugu:20220106175408p:plain

Copilot を構成する要素

デプロイする前に Copilot について紹介します。

Copilot には Service, Environment, Application という 3 つの中心となる要素があります。

f:id:morugu:20220106170948p:plain
Copilot の中心となる3つの要素

Service はコンテナで実行されるアプリケーションで
今回は api という名前で Service を作成します。

Environment は test, staging, production などの環境で
今回は test という名前で Environment を作成します。

Application は Service, Environment をまとめた概念で
今回は copilot-express という名前で Application を作成します。

今回デプロイするアプリケーションは Service, Environment, Application 全て 1 つずつの構成になります。

aws.github.io

Copilot で ECS(Fargate) にデプロイ

copilot initの実行

copilot initを実行して、Copilot の初期化 & デプロイを行います。 いくつか質問が表示されるので回答します。

Application 名を聞かれるのでcopilot-expressと入力します。
$ copilot init

Note: It's best to run this command in the root of your Git repository.
Welcome to the Copilot CLI! We're going to walk you through some questions
to help you get set up with a containerized application on AWS. An application is a collection of
containerized services that operate together.

What would you like to name your application? [? for help] copilot-express
ワークロードタイプを聞かれるのでLoad Balanced Web Serviceを選択します。
Which workload type best represents your architecture?  [Use arrows to move, type to filter, ? for more help]
  Request-Driven Web Service  (App Runner)
> Load Balanced Web Service   (Internet to ECS on Fargate)
  Backend Service             (ECS on Fargate)
  Worker Service              (Events to SQS to ECS on Fargate)
  Scheduled Job               (Scheduled event to State Machine to Fargate)
Service 名を聞かれるので api と入力します。
What do you want to name this service? [? for help] api
どの Dockerfile を使うか聞かれるので ./Dockerfile を選択します。
Which Dockerfile would you like to use for api?  [Use arrows to move, type to filter, ? for more help]
  > ./Dockerfile
    Enter custom path for your Dockerfile
    Use an existing image instead
今回は test 環境へのデプロイなので y を入力します。
All right, you're all set for local development.

Would you like to deploy a test environment? [? for help] (y/N) y

質問への回答が終わると IAM Role, Subnet, VPC などの環境構築が開始されるのでしばらく待ちます。

環境構築が終わったら ECR に Docker イメージがプッシュされ、ECS へのデプロイが始まります。

✔ Linked account AWS_ACCOUNT_ID and region ap-northeast-1 to application copilot-express..

✔ Proposing infrastructure changes for the copilot-express-test environment.

✔ Created environment test in region ap-northeast-1 under application copilot-express.

✔ Deployed service api.
Recommended follow-up action:
  - You can access your service at http://xxxxx.ap-northeast-1.elb.amazonaws.com over the internet.

表示された URL にアクセスしてローカルと同様に Express app works! が表示されたらデプロイ完了です!

Application の削除

動作確認や実際に ECS にデプロイされている状態が確認できたら
今回作成した Application を削除します。

$ copilot app delete

まとめ

Copilot の概念(Beanstalk と似ている)と手順を覚えてしまえば、コンテナを ECS にデプロイする良い手段だと感じました!

実際の運用では Copilot での CI/CD や Sidecar pattern なども入ってくるので、その辺りも試してみようと思います。

Monthly LINE API Update 2021-01

f:id:morugu:20210130203637p:plain

2021 年 1 月の LINE API 関連まとめです。

ナローキャストメッセージで利用できるオーディエンスタイプが追加

developers.line.biz

  • LINE 広告、LINE ポイント AD で作成されたオーディエンスが Messaging API で利用可能になりました
  • 事前に Messaging API 経由でオーディエンスを有効化しておく必要があります

iOS LIFF でのセーフエリアの厳格化

developers.line.biz

  • iPhone X など iOS の角丸ディスプレイで LINE v11.0.0 以降がインストールされている端末が対象です
  • iOS(と iPadOS)の既知の不具合が影響しているそう(具体的なバグに関する情報は見つけられず)です

グループのメンション情報を取得できるプロパティが追加

developers.line.biz

  • webhookObj.events.message.mention.mentionees プロパティが追加され、LINE 公式アカウントが参加しているグループでメンションされたユーザーの情報が取得できるようになりました
  • メンションされたユーザーがユーザー情報の取得に同意していない場合はuserIdは取得できません
  • LINE 公式アカウントへのメンションは今までと変わらずできません

ナローキャストメッセージの進行状況を取得する API に詳細情報が追加

developers.line.biz

  • acceptedTime(リクエストを受け付けた時間)、completedTime (メッセージの送信が完了した時間)がレスポンスに追加されました
  • completedTime から 7 日(168 時間)以上経過すると、進行状況は取得できなくなります

LIFF v2.7.0 & v2.7.1 がリリース

developers.line.biz

  • v2.7.0
    • RequireJS から npm パッケージ版 LIFF SDK を参照できるようになりました
    • liff.getDecodedIDToken()の不具合が修正されました

developers.line.biz

  • v2.7.1
    • v2.7.0 で LIFF アプリから外部ブラウザを開くと失敗する不具合が修正されました
    • LIFF のバージョン指定して利用している場合は v2.7.1 の利用が推奨されています

その他

ビーコンイベントの leave イベントが廃止

developers.line.biz

  • 2020 年末で廃止済、2021 年 1 月 6 日で Webhook の送信停止済です
  • 代替となるstayイベントの新規利用受付は停止しているので、ビーコンを利用してユーザーが電波の受信圏から出たことを検知するのは現状不可能です

画像コンテンツを扱う旧エンドポイントの廃止

developers.line.biz

  • コンテンツの取得、リッチメニューの画像をアップロード/ダウンロードのエンドポイントのドメインが変更になりました
  • 旧: api.line.me -> 新: api-data.line.me
  • 旧ドメインのエンドポイントは 2021 年 1 月 21 日では廃止済です

まとめ

ナローキャストメッセージが強化され LINE プラットフォームの他サービスと合わせてマーケティング活動での利用機会が増えていきそうです。

利用例:
LINE 広告で配信された広告 A を経由して LINE 公式アカウントと友だちになり(友だち追加経路オーディエンス)、 1 月に送ったメッセージを開封したユーザーのみに対して(インプレッションリターゲティング)、再度メッセージを配信する(ナローキャストメッセージ)ことで効果が高そうなユーザーにだけ効率よくメッセージを配信する。

メンション情報が取得できるプロパティの追加は、LINE 公式アカウントを絡めたグループでの使い方が広がります!

以上 2021 年 1 月の LINE API アップデートでした!

Google Cloud Speech-to-Text 向けに.wav の音声をステレオからモノラルに変換する

Google Cloud Speech-to-Text (以下、Speech-to-Text)で音声(.wav)から文字起こしをした時にMust use single channel (mono) audio, but WAV header indicates 2 channels.エラーが発生したので対策についてまとめました。

前提

  • 1 人が喋っている動画の文字起こしをしたい
  • ↑ の動画から音声のみを抜き出した.wav を使用する

Speech-to-Text の複数チャンネルの扱いについて

Speech-to-Text には複数のチャンネルを含む音声を扱えるオプションが存在しています。
電話やポッドキャストなどの複数人の会話を、発話者を認識して誰が話した音声か特定して文字起こしができます。

cloud.google.com

ですが、今回やりたいのは 1 人で話している音声の文字起こしです。
その場合の音声(.wav)は single channel(モノラル) を使用しなさい、というのが冒頭のエラー内容です。

.wav のチャンネルついて

モノラルは 1(0x0100)、ステレオは 2(0x0200)

www.youfit.co.jp

ステレオからモノラルへの変換

ffmpeg と Python の2種類の方法で紹介します。

状態確認

ffmpeg

-i: 入力ファイルのパス
-hide_banner: ビルドオプション、バージョン情報などを非表示にする

$ ffmpeg -i audio-stereo.wav -hide_banner
Guessed Channel Layout for Input Stream #0.0 : stereo
Input #0, wav, from 'audio-stereo.wav':

...省略...

Python

import wave

wave_read = wave.open('audio-stereo.wav', "rb")
print ("Channel:", wave_read.getnchannels())

# output: Channel: 2

変換

ffmpeg

-ac: チャンネル数

$ ffmpeg -i audio-stereo.wav -hide_banner -ac 1 audio-mono.wav

Python

Python での変換には pydub を使用します。 pydub は ffmpeg に依存しているので、インストールしておく必要があります。

github.com

$ pip install pydub
from pydub import AudioSegment

audio = AudioSegment.from_wav("audio-stereo.wav")
audio = audio.set_channels(1)
audio.export("audio-mono.wav", format="wav")

再度確認

ffmpeg

$ ffmpeg -i audio-mono.wav -hide_banner
Guessed Channel Layout for Input Stream #0.0 : mono
Input #0, wav, from 'audio-mono.wav':

...省略...

Python

from pydub import AudioSegment

audio = AudioSegment.from_wav("audio-mono.wav")
audio = audio.set_channels(1)
audio.export("audio-mono.wav", format="wav")

# output: Channel: 1

まとめ

.wav のチャンネル変換方法を Python と ffmpeg の 2 種類の方法で紹介しました。

Speech-to-Text の拡張モデルで日本が使えるようになって欲しい!

cloud.google.com

Monthly LINE API Update 2020-12

f:id:morugu:20210104120836p:plain

2020年 12 月の LINE API 関連のまとめです。

シェアターゲットピッカーの UI 改善

developers.line.biz

  • シェアターゲットピッカー = LIFF から起動して LINE の友だちにメッセージを送信する機能です
  • デフォルトでトークのセクションが開き最近やり取りした友だちが優先して表示されるようになったため、送信先を見つけやすくなりました

LIFF v2.6.0 リリース

developers.line.biz

  • 内部挙動の変更のみで追加機能などはなし

Webhook イベントオブジェクトに新しいプロパティが追加

developers.line.biz

  • スタンプが送信されたときに「どんな気持ちを表現しているスタンプか」を把握するためのプロパティ(webhookObj.events.message.keywords)が追加
  • スタンプにはキーワードが設定されており、最大 15 個のキーワード配列で返される
  • 16 個以上のキーワードを持つスタンプはランダムに 15 個のキーワード配列で返される

LIFF ブラウザの iOS サポートバージョンが変更

developers.line.biz

  • iOS 12.0 以降をサポート対象に変更(変更前は iOS 11 以降)

その他

iOS の LIFF で getUserMedia が使えるように

iOS での LIFF は WKWebView が使われており、getUserMedia が未対応だったため WebRTC を使った映像共有などができませんでした。

検証記事:

qiita.com

iOS 14.3 から WKWebView で getUserMedia が使えるようになった(Android では以前から使えていた)ので、 LIFF 上で WebRTC を使ったアプリの開発が可能になりました。

とはいえ、最新の iOS でないと動作しないため回避策が必要になります。

回避策について:

www.line-community.me

LINE API Use Case が公開

developers.line.biz

  • LINE API の活用事例が紹介されたサイト
  • 「LINE で予約」「自治体での防災、粗大ごみ収集」など様々な LINE を使った事例が掲載されていて、これからら LINE を使って何かやりたい方にオススメです
  • 開発者としても LINE API の事例として参考になる使い方が多く載っています

「福岡市 LINE 公式アカウント 防災ハンドブック」の配布開始

linefukuoka.co.jp

  • LINE 公式アカウントの使い方のを説明したハンドブック
  • 実際のハンドブックはここから読めます
  • LINE が慣れ親しんだアプリとはいえ、防災目的で使うのであれば本当に"誰でも"使えるようにするためこういったアプローチも必要になってきますね

ドキュメントの更新

  • LINE Developers サイトの「ガイド」の名称を「ドキュメント」へ変更

developers.line.biz

  • LINE Developers コンソールへログインするアカウントごとに利用できる機能の一覧を追加しました

developers.line.biz

  • LINE Pay の開発者向けドキュメントが LINE Developers サイトに公開されました

developers.line.biz

今まで独自のページで存在していた LINE Pay が LINE Developers に統合されました。

まとめ

LIFF から WebRTC を使ったアプリを開発が iOS でも可能になったことで
まずはLINE ボットに問い合わせ -> 必要に応じてビデオ通話でオンライン相談窓口対応のようなサービスが実現できそうです!

以上、2020 年 12 月のアップデートでした!