GAミント至上主義

Web Monomaniacal Developer.

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する(していた)組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が開発用Linuxサーバーのユーザー名に「yuyamazaki」が長いので勝手に作ってくれた。読み方わからないけどウヤマザク)

高校あたりからWEBサイトをやったり趣味の延長でWEB系を仕事にした感じの人間。

2020年1月から株式会社シニアジョブにジョイン。
2021/1現在、まだ開発者は3人、これからの会社なのでビジネス貢献しつつ、きれいな設計をしたい

アズールレーン@竹敷でモバイルのUI、UX研究中(初嫁ジャベリン)
フレンド&大艦隊メンバー募集してます ID:939524678 @竹敷



GitHub: https://github.com/uyamazak/

これまでの主なプロジェクト

続きを読む

AWS初心者がElastic Beanstalk環境をFargate化したメモ

優先度が高い仕事が落ち着いてきたため、ずっとやりたかった本番環境コンテナ化に着手しました。

たぶんお休みで全部忘れるので社内に説明用も兼ねてメモ。

下記の記事のように現在のシニアジョブに入社してすぐ、開発環境はDocker化していました。

シニアジョブに入社して1ヶ月でやったこととこれから - GAミント至上主義

現在、本番環境はElastic Beanstalkで動いていますが、Dockerfileで管理できるため、いつかは揃えてコンテナ化したいなぁと思っていました。

AWS Fargateとは

AWS ECSからEC2の管理を(でき)なくしたサーバーレス的なもの・・・という雑な認識。詳細は公式。
AWS Fargate(サーバーやクラスターの管理が不要なコンテナの使用)| AWS

専用ページはあるものの、コンソールの中だと、ECSやEKSの起動タイプとして存在してるだけで意外と地味な存在でした。

なぜFargateを使うか

コンテナ化に際しては、タイトルのFargateの他、下記の選択肢がありました。

下記がFargateにした理由です。

AWSのいろいろなサービスを使っていたから(GCPを使わない理由)

シニアジョブでは、現在のシステムが動いた当初(2016年ごろ?)からAWS上で求人サイトや管理システムも動かしていました。
そのためRDS(MySQL)をはじめ、Elasticsearch、DynamoDB、SQSなども使用しています。
そのため、個人的にはGCPのGKEは使い慣れていたので使いたい気持ちはあるものの、わざわざGCPに移すという選択肢はありませんでした。

そんなに必要なコンテナ数が多くない(Kubernetesを使わない理由)

これまで触ったことはないものの、ネット上の記事とかの印象だと、AWSでそこまで大規模じゃないシステムだったらFargateかなぁというイメージでした。
シニアジョブのシステムで必要になりそうなコンテナは下記3つだけで、ほかはRDS, RedisなどはAWSのマネージドで済みます。

  • アプリケーション (Laravel)
  • ワーカー (Laravel)
  • WEBサーバー(Nginx)

これだけだとKubernetesの機能はちょっとオーバーかなぁという印象。あとAWSKubernetesはGKEと比較すると高くつくというのもあります。
実際構築してみた感じ、ハードウェアリソースも柔軟で、Fargateで十分でした。

管理が楽そうだから(EC2タイプを使わない理由)

EC2タイプを使ったことはないので比較はできないのですが、EC2を意識せずに使えるというのは、エンジニア3人で何とかしている現状では、メンテナンスコストを削減できそうなので、大きな価値がありました。
サーバーレスとあるので、GCPでいうCloud Runみたいな感じかなぁと思ってたら、どちらかというとKubernetesで最近出たAutopilot的なものでした。

Fargate化するために必要な登場人物

GCPではいろいろ構築したことがあるもののAWSで複数サーバーのシステム構築は初めてでした。
そのためFargateを使用するにあたり、AWSの他のサービス、機能を知る必要がありました。

ECR (Elastic Container Registry)

まずはDockerで作ったコンテナをAWSに置いておく必要があります。その置き場所がECR。
Amazon ECR(Docker イメージの保存と取得)| AWS

最初1回だけ手作業でローカルからビルド&プッシュしましたが、面倒なのでCircle CIで自動化しました(後述)

ECS (Elastic Container Service)

これが今回使う本体になります。

Amazon ECS(Docker コンテナを実行および管理)| AWS
ECSの中に下記があります

ECS クラスタ

ECSの一番外側、EC2をひとまとめにしたもの。Kubernetesクラスターと同じ感じですね。
今回はFargateを使うので、ネットワーキングのみを使用します。
f:id:uyamazak:20210409171846p:plain

ECS サービス

後述するタスクを管理するもので、タスク定義や、起動数を指定し、その状態に変更、維持してくれます。
また後述するロードバランサーもサービスと紐づけることになります。
Amazon ECS サービス - Amazon ECS

作成後に変更できない設定が多々あり、5回ぐらいは作り直した記憶があります。

タスク定義

タスクの設計書のようなもので、ここに使用するコンテナ(複数可)や、コンテナごとの環境変数などの設定、必要なリソース(CPU、メモリ等)を指定します。
こちらも作成時に起動タイプにFargateを指定します(もう一つはEC2)。
更新の際は、上書きではなく、リビジョンを増やしていく形になります。
Amazon ECS タスク定義 - Amazon Elastic Container Service

コンテナ定義

具体的なコンテナ定義はタスク定義内にあります。開けるポートの指定もここ。
同じタスク定義内であれば、127.0.0.1で通信できるようなので、nginxからappへのプロキシ設定では127.0.0.1を使用しました。
KubernetesだとServiceを作る必要があったり面倒なので、今回はこれで十分だし、シンプルに感じます。
f:id:uyamazak:20210409184238p:plain

サービスでのロードバランサー設定は、ここで設定したnginxコンテナのポート80を指定しました。

タスク

上記のタスクをサービスが起動したもの。プログラミングだとタスク定義がクラス、タスクがインスタンス、みたいなイメージ?
変更の際はタスク定義のリビジョンを指定して更新します。

頻繁に消したり、作ったりするものなので、タスクIDは

32034c0a56d84d5eb95908090cdec44c

みたいなランダムな16進文字列になります(後述のシェルで入るときに使う)。

サービスを更新すると新しいのができたり古いのが消えたりします。

ECS以外

ECSと関係するのが以下のサービス&機能になります。

VPC (Virtual Private Cloud)

Amazon VPC(仮想ネットワーク内での AWS リソースの起動)| AWS

クラスター使用時に選択、または作成します。仮想のネットワークで、セキュリティ設定などに影響します。
新規のときは一緒に作ればいいですが、既存のシステムがあるときは同じものを使用しないと、いろいろ繋げるのが面倒になるので、注意が必要でした。

ロードバランサー(Application Load Balancer)

コンソール上ではEC2の中にある機能です。
Elastic Load Balancing(複数のターゲットにわたる着信トラフィックの分配)| AWS

上記サービスをインターネットに公開する際に使用します。
いくつか種類がありますが、ECSではApplication Load Balancerを使用します。
詳細は省略しますが、SSL証明書はここで設定できるため、ECS側でのHTTPS処理は不要です。
設置するVPCはECSクラスターと同じにしておいた方がよさそうです(不可能ではないかも)。

ロードバランサーのリスナー

ロードバランサーがリクエストを受けるポートです。
今回は一般的なWEBアプリなので80と443を作り、下記ECSサービスと連携したターゲットグループを転送先にします。
たしか80はサービス作成時に作られましたが、HTTPSの443の方は手作業で追加する必要があった気がします。

Route 53

Amazon Route 53(スケーラブルなドメインネームシステム (DNS))| AWS
言わずとしれたDNSサービス。
上記ロードバランサーでは

foobar-4580172169.ap-northeast-1.elb.amazonaws.com

のような長めのURLが振られるため、独自ドメインを使用する際はRoute53で紐付けます。
Aレコードから選択形式で簡単に指定できます。

ターゲットグループ

ロードバランサーから今回のECSのサービスに繋げる転送先(実体はタスクのローカルIP)に使用されます。
ロードバランサーの新規作成時や、ECSでサービスを作る際に自動でできてしまうので、サービスを作り直したりすると、余計なものが残っていて邪魔になり消すのが面倒でした。
ECSのサービスは、タスクが更新され、ヘルスチェックが通ると、このターゲットグループのIPを自動で変更することで、ユーザー側へも変更されます。

セキュリティグループ

ECSのサービスが持ちます。
IPは変わってしまうので、これを使って、VPC内の通信許可などを行います。
現環境では既存のセキュリティグループに、サービスからRDSやRedisへの許可を追加しました。

Secrets Manager

コンテナで使うパスワード等の機密情報を保持します。
とりあえずLaravelで.envファイルで使用していた項目を移しました。

AWS Secrets Manager(シークレットのローテーション、管理、取得)| AWS

タスク定義で、コンテナごとに設定し、環境変数として使うことができます。
JSON形式のオブジェクト形式で複数持つことができますが、自動で環境変数に展開することはできないようなので
valueFromで一つずつ

arn:aws:secretsmanager:ap-northeast-1:user-id:secret:secret-name-AAAAAA:YOURENV::

のように指定して読み込む必要がありました。

このチュートリアルのようにIAMロールの追加も必要です。
チュートリアル: Secrets Manager シークレットを使用した機密データの指定 - Amazon Elastic Container Service

その他

動かすためにいろんな設定を理解、修正する必要があり、独自ドメインからコンテナのWEBアプリケーションまで繋がるのに幾度となくはまり、3日ぐらいかかりましたがなんとか動かくことができました。

Circle CIでECRにプッシュ&アップデート

コンテナ変更のたびに手作業でビルド、プッシュするのは現実的ではないのでなるべく早い段階でやった方がよさそうです。
シニアジョブではCircle CIを使っていたので、そのまま利用しました。GitHub Actionsでもやってみたいなぁ。

Circle CIのいろんな処理をまとめたOrbsを使うとめっちゃ簡単でした。
ほぼ公式まんまですが、service-nameだけ公式ドキュメントになく、デフォルト値ではうまくいかなかったので、orbのソースを見て追加しました。
またfamilyがわかりにくいですが、タスク定義をJSONで確認するとfamilyの項目が見つかります。

まだステージング環境だけなのでtagの先頭にstageを固定でつけてます。

AWS ECR/ECS へのデプロイ - CircleCI

.circleci/config.yml

version: 2.1

orbs:
  aws-ecr: circleci/aws-ecr@6.15.3
  aws-ecs: circleci/aws-ecs@2.0.0

workflows:
  build_and_push_image_app:
    jobs:
      - aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          dockerfile: ./Dockerfiles/app/Dockerfile
          path: .
          region: AWS_REGION
          repo: senior-job/app
          create-repo: true
          tag: "stage-$CIRCLE_SHA1"
      - aws-ecs/deploy-service-update:
          cluster-name: 'example'
          service-name: 'example-service'
          container-image-name-updates: 'container=app-container,tag=stage-${CIRCLE_SHA1}'
          family: 'example'
          requires:
            - aws-ecr/build-and-push-image
      - aws-ecs/deploy-service-update:
          cluster-name: 'example'
          service-name: 'example-service'
          container-image-name-updates: 'container=queue-container,tag=stage-${CIRCLE_SHA1}'
          family: 'example'
          requires:
            - aws-ecr/build-and-push-image

コンテナにシェルで入る

なぜかうまく行かない時は、中で環境変数がどうなってるかとか、ファイルちゃんとあるかとか、やっぱりコンテナの中に入って確認したいもの。

Kubernetesだと`kubectl exec -it pod_name bash`みたいにサラっと入れますが、一筋縄では行きませんでした。

特にFargateはEC2を触れないことから、難しかったみたいですが最近(2021/3)公式な手段が準備されたようです。
このプロジェクトが2ヶ月早かったら詰んでたかも。5年以上前でもexecはできてたので、全体的な完成度としてはKubernetesがやっぱ進んでるなぁと思ったできごとでした。

New – Amazon ECS Exec による AWS Fargate, Amazon EC2 上のコンテナへのアクセス | Amazon Web Services ブログ

権限付与、サービスの設定変更、CLIのアップデートなどいろいろ面倒でしたが、こちらの記事がまとまっていてわかりやすかったです。
[アップデート] 実行中のコンテナに乗り込んでコマンドを実行できる「ECS Exec」が公開されました | DevelopersIO

これから

まだステージング環境が動いただけなので、いくつかまだたりないデプロイプロセスの追加や、死活監視関係、本番でのリソース調整やオートスケール設定など、まだまだたくさんありそうです。
あとAWSだけだと自動化つらいのでTerraformも使った方がいいのかなぁとか。

自作パッケージをnpmに公開&GitHub Actionsで自動化する

趣味で開発しているPuppeteerを使ったPDFサーバーhc-pdf-serverで一部分をnpmにパッケージとして公開しています。

これ↓
www.npmjs.com
FastifyからPuppeteerを通して、Headless ChromeのPageを使用できるプラグインです。
名前の通り速いFastifyの長所を殺すこと無く、起動しておくPageを増やすことで大量リクエストもさばけそうな気がします(実戦経験はない)。
PDFだけでなく、スクリーンショットとか、JavaScriptでできたHTMLを取得したりいろいろできるよ!


Puppeteerとの連携部分はもともとFastifyのプラグインとして作っていたこともあり、
また超絶頭のいいと思われる見知らぬプログラマからすばらしいプルリクを頂いて強いプラグインとなったという理由もあり、
次もPuppeteer + Fastifyで何か作ろうと思っているのもあり。


で、npmにパッケージ公開してそれを使ってみたら、いろいろ世界の見え方変わった気がしたのでかなりおすすめです。基本無料です。

細かい説明は他にあるので、ざっくり流れを忘れないようにメモ。

レポジトリを分ける

既存のアプリケーションからの分離なのでまずはこれでした。
必要最低限なファイルや設定で新しいレポジトリをつくり、package.jsonに必要な情報を書きます。

この記事を参考にしました。
qiita.com


nameには最初でグローバルを汚すのも申し訳ないのでscopedと呼ばれる@uyamazak/をつけました

package.json

"name": "@uyamazak/fastify-hc-pages",

qiita.com

TypeScriptのコードで、jsはレポジトリに含まれていないため、prepublishOnlyでビルドするようにしました。

package.json

  "scripts": {
    "build": "rm -rf ./dist && tsc",
    "prepublishOnly": "npm run build",

TypeScriptでnpmライブラリ開発ことはじめ - Qiita


あとmainのパスをちゃんとやったりぐらいかな?

ここらへんで普段意識していなかったpackage.jsonの項目を理解し、意識できるようになった気がします。

GitHubからインストールして動作確認

それっぽいものができたら、npmjsで公開する前に自分のGitHubレポジトリにプッシュしてそこからインストールして試します。

yarnでGitHubのレポジトリからインストールを初めて使いました。

$ yarn add https://github.com/uyamazak/fastify-hc-pages

package.jsonにこんな感じで入ります。
バージョンの数字の代わりにURLが入るんだなぁ。ブランチとかの指定もできそう

  "dependencies": {
    "@uyamazak/fastify-hc-pages": "https://github.com/uyamazak/fastify-hc-pages",
    "fastify": "^3.13.0",

型定義が吐き出されずに困ってましたが、原因は拡張子を.d.tsにしてたため、というしょうもないミスでハマってました。

ここらへんでnpmパッケージがどうインストールされるかとか、かなり理解できた気がします。

npmjsに登録したり、ローカルでログインしたり、READMEとか準備

まあ普通にやるので割愛。

GitHubに吸収されて1年経つので、アイコンはGitHubの使えるのかと思ったら・・・
Gravatarェ・・・・。

太古に作ったWordPressアカウントがあった気がするけど面倒なのでそのまま。

www.publickey1.jp

publish !

準備ができたらpublishします。公開パッケージなのでpublicが必要です。

npm publish --access public

ここでやらかしましたが、パッケージ名が他のFastifyプラグインと違うため、変更したくなりました(最後のpluginいらなかった・・・)

でも一度公開してしまうと基本的に名前の変更、削除はできないため、新しく作って、古いのはdeprecateして放置する必要があります。
以前消えて問題になったことあるししょうがない。

↓殘骸
@uyamazak/fastify-hc-pages-plugin - npm


改めてパッケージ名を変更し、このページができました。
www.npmjs.com

自動化

それからしばらくバージョンアップのたびに手動でやってましたが、ローカルで操作するの面倒ですね。
自動でできるようにします。

npmとは別のGithub Packagesの説明が入っていて邪魔ですが、こちらを参考にしました。
docs.github.com

npm側でトークンを作り、GitHubのSecretsに入れるNPM_TOKENの設定が必要ですが、yamlは最終的にはこんな感じ。

.github/workflows/npm.yml

name: Node.js Package
on:
  release:
    types: [created]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    # npmに公開する.npmrcファイルを設定する
    - uses: actions/setup-node@v1
      with:
        node-version: '14.x'
        registry-url: 'https://registry.npmjs.org'
    - run: npm install
    # npmに公開する
    - run: npm publish --access public
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

タイミングは、いつものマージ、プルリクなどではなく、GitHubのReleaseという機能を使います。

ここから、タグにバージョンを入れてリリースを作成すると、自動で公開されるようになりました。
f:id:uyamazak:20210318171326p:plain

リリースの作成もcommitメッセージを集めてきたり、ある程度自動化できそうですが、しばらくはこのままでいいかな。

ここらへんで、他のレポジトリがどうnpmのパッケージをリリースしているのかを理解できた気がします。

開発を続ける

で、しばらく開発をすすめると、プラグインを更新するたびにそれを利用している元本体側も変更が必要など手間が増え、作業時間は増えてしまいます。

が、そのおかげでユーザーとしての気持ちも持つことができるので、変更する際には少し慎重になり、いい設計、コードにしたいという気持ちが強くなった気がします。

でも趣味なので作業時間はプライスレス。

おまけ: npmのWeekly Downloadsについて

ここのダウンロード数、自分しか使ってないので結構行ってますが、このレポジトリと元のレポジトリのGitHub Actionsで回しているテスト時のダウンロードもカウントされてるようです。
そのため自動でdepandabotがつくるプルリク×3環境×2Node.jsバージョン分だけこの数が増えていくことに。

f:id:uyamazak:20210318212045p:plain

1人しかユーザーいなくてもCIなどのDLが含まれて200近く行ってしまうということで、他のパッケージのダウンロード数もそこまで参考にならないかなぁという気がします。


Google Apps ScriptとNuxtJSで簡易来客受付システムをつくったら実質サーバーコスト無料だった

シニアジョブのオフィスの引越にあたり、セキュリティの面からも来客の記録システムが必要になりました。

最初すでにいろんな会社が作ってると思ったので、それ使えばいいじゃんと思いましたが、仕様を聞くとたしかに超簡易的なものなのでまあいいかということで作りました。
とりあえず一通り動くまで2,3日、あとCIとかの環境まわり、デザイナーさんの工数とか入れたら1週間ぐらい?

できたもの

iPadでこんな感じに表示されていて
f:id:uyamazak:20210309171400p:plain

名前とかいれて呼び出しすると「ピンポーン」という音がして
\ピンポーン/
f:id:uyamazak:20210309171503p:plain

スプレッドシートに記録されて
f:id:uyamazak:20210309171555p:plain

Chatworkにも送信されます
f:id:uyamazak:20210309171633p:plain

システム的には

  1. Google App Engineで公開されたNuxtJSアプリ(静的ファイル配信のみ)
  2. GASウェブアプリ(REST API的なもの)で来客リクエスト受け付け + iPadでピンポーン鳴らす
  3. GASでスプレッドシートにログ書き込み & Chatworkにメッセージ送信
  4. 備え付けたiPadでこれを開く

だけのシンプルな構成です。

iPadの選定

まずこれから頼まれた。
生まれてからMac製品買ったこと無い人間なので、いろいろ調べてこんな条件で中古で選んで買ってもらいました。
3万ちょっとだった気がする。

  • 新しめ(2019)
  • 大きめ(10.2インチ)
  • WiFiで厚さとかストレージ気にしない

Raspbery Pi + タッチディスプレイも考えたけどめんどいよね。

フロント側選定

他でも使ってるしNuxtJSでいいよね。UIも大してないので定番のVuetify
いざとなったらサーバー側の処理もできるし(結局GASで済んだのでいらなかった)

インフラ選定

NetlifyとかVercelとか便利だけど、スプレッドシートへの書き込みなので、Google内が良さそう。
常時起動の必要はなく、必要なときだけ起動するGoogle App Engine(スタンダード環境)を選びました。
NuxtJSで普通に作ってローカルで動かしたあとapp.yaml作るだけでデプロイ出来ました。

休日開けとかの起動時間がちょっとネックだけど10秒はいかない感じなので今の所常時起動とかはまあいいかなと。

おそらく無料枠に収まるので実質無料では?

サーバー側選定

この記事で詳しく書いたけど結局サーバーサイド的な処理はGASのウェブアプリだけで完結していましました。
もともとGoogle Workplaceはつかってるので実質無料では?
uyamazak.hatenablog.com

CI、デプロイ

これもGitHubのmainブランチへのプッシュをフックにGCPのCloud Buildを設定しました。
権限周りでちょっとハマったけど、無料枠以上デプロイする気がしないので実質無料では?
uyamazak.hatenablog.com

小技集

ピンポーン鳴らす

もしネットが切れてるときにお客さんが操作するとただエラー画面が表示されるだけで、Chatworkにもスプレッドシートにも飛ばないので人をイラだたせる板になってしまいます。
そこでエラー時にも音出せば原始的なチャイムとして機能する?と思ってつけました。
音量最大にしてオフィスの中聞こえるかはまだ新オフィス行ったこと無いのでわかりません。

音はmp3などでネットで無料でライセンス的にも問題なさそうなのを拾ってきます。
ブラウザで音鳴らすの随分久しぶりな気がしましたが、この関数だけで動きました。
リモートなので実機テストはデザイナーの方に頼みましたが問題なかったそう。

ping-pong.ts

let audio: HTMLAudioElement

export const pingPong = () => {
  if (!audio) {
    audio = new Audio('pingpong.mp3')
  }
  audio.pause()
  audio.currentTime = 0
  audio.play()
  console.log('pinpong')
}

エラーページ出たらしばらくしたらトップにもどる

ネットワーク等なにかの問題でエラー画面がでてそのままだったらうざいので、自動でトップに戻るようにしました。

せっかくなので新しいcomposition-apiプラグイン的に作りました
手で戻った場合に備えてonUnmountedでキャンセルするのも忘れない。

plugins/setup-auto-back-top

import { onMounted, onUnmounted, useRouter } from '@nuxtjs/composition-api'

export const setupAutoBackToTop = (timeout: number) => {
  return () => {
    const router = useRouter()
    let timeoutId: number
    onMounted(() => {
      timeoutId = window.setTimeout(() => {
        router.push('/')
      }, timeout)
    })
    onUnmounted(() => {
      window.clearTimeout(timeoutId)
    })
  }
}

これをエラーページのテンプレートでsetupに時間(ミリ秒)を付けて入れるだけ。
15秒でトップに自動で戻ります。

error.vue

<script lang="ts">
import { defineComponent } from '@nuxtjs/composition-api'
import { setupAutoBackToTop } from '@/plugins/setup-auto-back-top'

export default defineComponent({
  setup: setupAutoBackToTop(15000),
})
</script>

PWAでiPadにインストール&アドレスバー無しを実現する

そのままだとアドレスバーとか出ててダサいのでアプリっぽく動くようにします。
といってもNuxtJSのモジュール入れて、アイコンとか設定するだけ。

pwa.nuxtjs.org

これでiPadにインストールすれば、アプリっぽくなります。
iPad手元にないので画像はない。

IP制限でオフィスからだけ見れるようにする

App Engineということでお気づきの方はいるかもしれませんが、グローバルに公開されちゃいます。
知らない国の知らない人に来客通知送られても困るので制限します。

メニューのファイアウォールで、デフォルト拒否にして、許可するIP追加するだけなので簡単。
ファイアウォールによるアクセスの制御  |  Python 2 の App Engine スタンダード環境  |  Google Cloud

ベーシック認証とかも考えたけど、認証切れたりした画面をお客さんに見せたくない&新オフィスは固定IPがあるのでIP制限にしました。

そういえばApp Engineの選定理由にこれもありました。

あともし同じIPを使われてURLを知っていて悪意がある人がいても、できるのはスプレッドシートへのログ書き込みとChatwork通知だけで、個人情報漏洩とかの心配はなかったものあり。
なにかあってもすぐGASのウェブアプリを止めればOK。


たぶん実運用が始まってからいろいろと問題が出ると思いますが、ご来社の際は触ってみてください(画面消毒用のアイテム用意しないと)