connpass更新のPuppeteerアプリをHerokuからAzure WebAppsに移行


こんにちは、n0bisukeです。IoTLTというライトニングトークイベントを毎月実施していますがスプレッドシートに書いてもらった登壇者情報をconnpassのイベントページに自動反映するというスクリプトをHerokuで動かしています。

> ![スクリーンショット 2022-12-05 20.22.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/d4fa89b1-4014-674d-c868-fc8606ec0006.png "スクリーンショット 2022-12-05 20.22.39.png")
> 昔の登壇スライド: https://speakerdeck.com/n0bisuke/iotltwozhi-erurpaji-shu-falsehua-false-bu-shao-jie-number-yururpa

Herokuの無料枠が2022年11月28日に終わってしまうという話題が有名でしたが、AzureのWebAppsに移行してみました。

> [PaaS「Heroku」が無料プラン廃止、11月から　非アクティブなアカウントとストレージも削除](https://www.itmedia.co.jp/news/articles/2208/26/news097.html)

## 動かしているconnpassの自動制御

**登壇者からのLT登壇情報をスプレッドシートに記載してもらい、Connpassページに自動反映する** といったことをやっています。

[connpass](https://connpass.com)にはPOSTのAPIがないのでPuppeteerを使って自動操作を行なって更新処理などを行なっています。

実際のページがこちらです。

> https://iotlt.connpass.com/event/263936/

スプレッドシートに記載してもらい、Puppeteerでスクリーンショットを撮影し、画像編集などを行います。

> ![スクリーンショット 2022-12-05 20.23.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/fbc98cba-9b65-ed9c-cd25-8868984895e3.png "スクリーンショット 2022-12-05 20.23.29.png")

Puppeteerでconnpassにログインや編集などをさせます。

> ![スクリーンショット 2022-12-05 20.24.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/8c5ef903-6ed8-6dce-d28d-4fa1871d470c.png "スクリーンショット 2022-12-05 20.24.10.png")

また、常に変更ではなくシートに変更があった際にだけ動かすようにしていて、HerokuでWebサーバーとして置いておき、スプレッドシートに変更にあったらHTTPリクエストをさせるような処理をしてました。

## 移行の流れとハマったところ

移行に際して

- Puppeteerがインストールできるか
- 日本語フォントを使えるか

ってところが障壁になりました。

この辺は別記事にメモしてあります。

https://qiita.com/n0bisuke/items/a172e95c01d06e54fa67


## 0. まずは手元で動くか確認

まずは放置してたので手元にコードがないのでHerokuから手元に持ってきます。

```
$ heroku git:clone -a connpassupdater2 
$ cd connpassupdater2
```

開いてみると`Procfile`というものがありますね。Herokuならでは感。

> ![スクリーンショット 2022-11-28 0.04.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/ceef61df-754f-c542-7cfd-b7f3b2df6568.png "スクリーンショット 2022-11-28 0.04.18.png")

```json
{
  "name": "iotltupdate_v2",
  "version": "1.2.2",
  "description": "Program last debug 2021/9/14 => Node.js v16.9.1",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "deploy": "git add . && git commit -m 'deloy' && git push heroku master",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "engines": {
    "node": "16.x"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.21.4",
    "express": "^4.17.1",
    "gyazo-api": "^0.3.1",
    "jimp": "^0.16.1",
    "moment": "^2.29.1",
    "moment-timezone": "^0.5.33",
    "puppeteer": "^10.2.0"
  }
}

```

このボットは`puppeteer`が色々な機能の中心となります。

最終更新をしていたタイミングだとv10系みたいですが、今調べるとv19らしいです。
そのままアップデートは結構怖いですね。

> https://github.com/puppeteer/puppeteer/releases

Node.jsもv16系で動かしていたみたいです。

ローカルで動かしてみます。

```bash
$ yarn install

yarn install v1.22.19
[1/5] 🔍  Validating package.json...
error iotltupdate_v2@1.2.2: The engine "node" is incompatible with this module. Expected version "16.x". Got "19.0.0"
error Found incompatible module.
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
```

早速こけましたね。

Node.js v19で動かそうとしたので怒られています。

取り急ぎpackage.jsonの記載を更新...というか削除します。

```json
  "engines": {
    "node": "16.x"
  },
```

package.jsonからここを削除すればOKです。うろ覚えですがHerokuだと指定してほしいといった形だったのだと思います。


```bash
$ npm i
```

無事にパッケージのインストールができました。

```bash
$ npm start
```

起動方法忘れちゃうので`package.json`にスクリプト書いておくの大事ですね。

取り急ぎ無事に起動しました。

> ![スクリーンショット 2022-11-28 0.20.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/0773267c-b9f7-011f-4381-d04d14149ce2.png "スクリーンショット 2022-11-28 0.20.35.png")

Puppeteerとは関係ないですが、現時点での移行先のWebAppsの最新ランタイムがv18系が使えるとのことで、axiosを使ってる箇所はFetch APIに切り替えてみました。

## 1. WebAppsでPuppeteerを動かす準備

[WebAppsのリソース作成](https://portal.azure.com/#create/Microsoft.WebSite)から進みます。

> ![スクリーンショット 2022-12-06 11.39.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/60f0f15f-323e-e397-9e28-700e27f6af4d.png "スクリーンショット 2022-12-06 11.39.13.png")

無料で動かしたいのでF1にしたいところですが、パワー不足なのかタイムアウトしてしまう事象が多かったので結果B1にして動かしてみてます。Puppetterを動かすだけならF1でもたぶん大丈夫です。
ただ現状F1にすると、`Japan East`は選べなく、`Central US`を選ぶ必要があります。

- 公開（デプロイ）: `コード`
- ランタイム: `Node 18 LTS`
- OS: `Linux`
- 地域: `Japan East`

> ![スクリーンショット 2022-12-06 11.43.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/13d2cc8c-ee8d-4252-a534-1e0abe6045fd.png "スクリーンショット 2022-12-06 11.43.46.png")

### GitHub Actions経由のデプロイ設定

Herokuで動いていたコードをGitHubにコードをあげておき、GitHub Actions経由でのデプロイ設定をします。

> ![スクリーンショット 2022-12-06 12.41.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/8198b660-c4a1-5158-f34d-1dd703bbdffe.png "スクリーンショット 2022-12-06 12.41.02.png")


ちなみにプランがF1だとGitHub Actions経由のデプロイは選択出来ないので注意です。直接ローカルGitを選択することになりそうです。

> ![スクリーンショット 2022-12-06 11.43.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/05c597c6-0ece-ea25-f87a-211535f57142.png "スクリーンショット 2022-12-06 11.43.06.png")
> F1にするとGitHub Actionsの設定ができなかった...

ここまでの設定でWebAppsのインスタンス作成します。

## 2. GitHub Actionsの設定

GitHubを見ると、`.github/workflows/main_アプリ名.yml`という形でymlファイルがGitHubリポジトリ上に作成されていると思います。

```yml
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - connpastest

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Set up Node.js version
        uses: actions/setup-node@v1
        with:
          node-version: '18.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present
      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: .

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: 'Production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v2
        with:
          name: node-app

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'connpastest'
          slot-name: 'Production'
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_123456789 }}
          package: .
```

下から2行目の`secrets.AZUREAPPSERVICE_PUBLISHPROFILE_123456789`の箇所は作成した環境によって異なるのでコピーしておきましょう。

@horihiroさんのブログでAzureでPuppeteerを動かす記事を書いてくれているのですが、

>  [ほりひログ > puppeteer](https://uncaughtexception.hatenablog.com/archive/category/puppeteer)

[@horihiroさんのGitHub Actionsのサンプル](https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/.github/workflows/main_app-puppeteer.yml)をまるっと差し替えた上で

- `secrets.AZUREAPPSERVICE_PUBLISHPROFILE_`の箇所で先ほどコピーした内容に差し替え
- [57行目](https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/.github/workflows/main_app-puppeteer.yml#L57)のappnameの指定で自信のアプリ名に差し替え

を行います。

> https://github.com/horihiro/20221129-puppeteer-on-webapp

- [26行目](https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/.github/workflows/main_app-puppeteer.yml#L26)のPUPPETEER_CACHE_DIRを設定している箇所
- [31-38行目](https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/.github/workflows/main_app-puppeteer.yml#L31-L38)や[60行目](https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/.github/workflows/main_app-puppeteer.yml#L60)のzip化している箇所
- [57行目](https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/.github/workflows/main_app-puppeteer.yml#L57)のappnameを指定する箇所

などが大枠の変更箇所な気がします。

まずは試したいのでnpm initした時のpackage.jsonのテスト部分は削除しておくとよさそう。
GitHub Actions側でここでコケてうまく動かないと思います。ymlからtest部分を削除しておくでもよさそうですね。

```json
  "scripts": {
  	"test": "echo \"Error: no test specified\" && exit 1",
  },
```

## 3. スタートアップスクリプトを記載する

プロジェクトのルートに`appservice`フォルダを作成し、`start.sh`というファイルを作成します。

```appservice/start.sh
#!/bin/sh

apt update  \
  && apt install -y \
    libasound2 \
    libatk1.0-0 \
    libatk-bridge2.0-0 \
    libcairo2 \
    libcups2 \
    libdrm2 \
    libgbm1 \
    libglib2.0-0 \
    libnss3 \
    libpango-1.0-0 \
    libxcomposite1 \
    libxdamage1 \
    libxfixes3 \
    libxkbcommon0 \
    libxrandr2 \
  && mkdir /noto && cd /noto \
  && wget https://noto-website.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip \
  && unzip NotoSansCJKjp-hinted.zip \
  && mkdir -p /usr/share/fonts/noto \
  && cp *.otf /usr/share/fonts/noto \
  && chmod 644 -R /usr/share/fonts/noto/ \
  && fc-cache -fv \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/* \
  && rm -rf /noto


cd /home/site/wwwroot
PUPPETEER_CACHE_DIR=$(pwd)/node_modules/puppeteer node app.js
```

参考のものでそのままやると日本語がうまく表示されなかったので後半（`mkdir /noto && cd /noto`あたり）でフォントのインストールをしています。

> 参考: https://github.com/horihiro/20221129-puppeteer-on-webapp/blob/main/appservice/start.sh

ちなみに、@horihiroさんリポジトリに`.devcontainer`フォルダがありますが、これはスルーして大丈夫です。

### スタートアップコマンドを設定する

Azureのポータルの`構成 > 全般設定 > スタートアップコマンド`に以下を指定します。

```
sh /home/site/wwwroot/appservice/start.sh
```

> ![スクリーンショット 2022-12-06 13.37.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/848c31a6-d12b-ee97-6c79-40be871999a9.png "スクリーンショット 2022-12-06 13.37.41.png")

これでデプロイして`アプリ名.azurewebsite.net`にアクセスすると無事に動いてくれてます。

> ![スクリーンショット 2022-12-06 13.56.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/04a32b87-59cd-e9f6-4ea0-765c269e2643.png "スクリーンショット 2022-12-06 13.56.19.png")


## 発生したトラブルその他

### GitHub Actionsからデプロイ時の認証エラー

`Error: Deployment Failed with Error: Error: Publish profile is invalid for app-name and slot-name provided. Provide correct publish profile credentials for app.`

> ![スクリーンショット 2022-12-06 13.44.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/db8b2291-c1dc-e63a-f3d0-f4a95da7d961.png "スクリーンショット 2022-12-06 13.44.21.png")

前述した、シークレットとappnameの指定をミスるとここでこけてしまします。

ここも地味にハマりました。

### WebAppsにデプロイするとcannot find module 'statuses'と怒られる問題

https://twitter.com/n0bisuke/status/1597890325879877632

他の人も上げてましたがイマイチ原因が分からず、npm installがうまくいってなさそうな雰囲気でした。 別のインスタンスを作り直したら消えてしまった事情です。なぞ。。

### GitHub Actionsの容量

GitHub Actions側で`Create Artifact Container failed: Artifact storage quota has been hit. Unable to upload any new artifacts.`というエラーが出てデプロイが止まっていたタイミングがありました。

どうやら使いすぎてしまって容量オーバーの模様です。一度Publicにして試したら回避されました。

https://twitter.com/n0bisuke/status/1598566119702671360


この辺は別途調べて容量がオーバーしないようにキャッシュクリアしながらやれば問題なさそう。


## Herokuとお別れする

移行がうまくできたのでHeroku側のアプリケーションを削除しておきます。

消さなくても勝手に消される模様です。学生や非営利団体向けのプログラムが出る方針らしいのでその辺の動きを待ってから消す消さない判断をしても良いかもしれないですね。

> ・学生と非営利団体のサポートプログラムを開始
Salesforceは、学生が自分の可能性を実現するために必要なリソースと経験を学生に提供することに、積極的に取り組む姿勢を示しています。学生プログラムの詳細については、2022年9月に開催されるSalesforceのDreamforceカンファレンスで発表される予定とのこと。また、Salesforceは非営利コミュニティとも密接に協力しており、学生及び非営利団体をサポートする今後のプログラムについて順次発表していくそうです。

> [開発者向けサービス「Heroku」が無料プランの段階的廃止を発表](https://gigazine.net/news/20220826-heroku-next/)

ログインすると色々とアラート出てますね。

> ![スクリーンショット 2022-12-06 11.50.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/057a77b9-2ce7-0e50-1ca1-3f03f0381d80.png "スクリーンショット 2022-12-06 11.50.47.png")

herokuコマンドもアンインストールしておきます。

```bash
$ brew uninstall heroku
```

## 先人に感謝

今回、色々試してたらめちゃ助けてもらいました。

ほりひろさんありがとうございました！！ 情報を色々と教えていただきました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">だいぶ変わってそう (<a href="https://twitter.com/n0bisuke?ref_src=twsrc%5Etfw">@n0bisuke</a> さんが奮闘中) なので、 2022 年版をまとめるか。。。 <a href="https://t.co/jNHd8xP9DT">https://t.co/jNHd8xP9DT</a></p>&mdash; ほりひろ loves ＜⚡＞ (@hori__hiro) <a href="https://twitter.com/hori__hiro/status/1597440854683422720?ref_src=twsrc%5Etfw">November 29, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Azureのデフォルトymlが少し古い

Azureが自動生成したGitHub Actionsの記述だと少し古いみたいで`Node.js 12 actions are deprecated.`と警告がでるのでこの辺りは少し修正したいですね。

> ![スクリーンショット 2022-12-06 13.58.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/c2b23f90-e3b5-14b6-ffad-e10c8d37dfc5.png "スクリーンショット 2022-12-06 13.58.20.png")

もうちょいやりたいところだけど一旦動いたのでここまでで一旦締めておきます。