EatSmartシステム部ブログ

ウェブサイトの開発や運営に関する情報です。

Amplify プレビューで割り当てられたURLを参照する

認証に Auth0 を利用する場合、戻り先のURLを指定する必要があります。 本番環境なら固定されたURLを指定することが出来ますが、Pull Request ごとに Amplify プレビューを利用する場合はURLを固定することが出来ません。 このため、ビルド処理で割り当てられたURLを環境変数へセットするようにしてみました。

プルリクエストのウェブプレビュー - AWS Amplifyホブ

検証した環境は、Next.js で Auth0 を利用したものです。 この際、Next.js も Auth0 も最新のバージョンにする必要がありました。 どちらかのバージョンが古いと、ログイン・ログアウトの際に503エラーが発生してしまいました。

amplify.yml の設定では、以下のように Auth0 で利用する環境変数をセットする処理を記述していました。

version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm install
    build:
      commands:
        - npm run build
        - echo "AUTH0_CUSTOM_DOMAIN=$AUTH0_CUSTOM_DOMAIN" >> .env
        - echo "AUTH0_SECRET=$AUTH0_SECRET" >> .env
        - echo "AUTH0_BASE_URL=$AUTH0_BASE_URL" >> .env
        - echo "AUTH0_ISSUER_BASE_URL=$AUTH0_ISSUER_BASE_URL" >> .env
        - echo "AUTH0_CLIENT_ID=$AUTH0_CLIENT_ID" >> .env
        - echo "AUTH0_CLIENT_SECRET=$AUTH0_CLIENT_SECRET" >> .env

Amplify の環境変数で指定した AUTH0_BASE_URL が戻り先URLに利用されます。 これでは Amplify プレビューで認証後に元のアプリケーションに戻ることが出来ません。 そこで、Amplify に提供される環境変数を利用して、Amplify プレビューのURLを環境変数にセットするように変更しました。

-        - echo "AUTH0_BASE_URL=$AUTH0_BASE_URL" >> .env
+       - echo "AUTH0_BASE_URL=https://pr-${AWS_PULL_REQUEST_ID}.${AWS_APP_ID}.amplifyapp.com" >> .env

この変更で、Next.js + Auth0 が参照する環境変数 AUTH0_BASE_URL が Amplify プレビューごとのURLになり認証後に元のアプリケーションへ戻ることが出来るようになります。

ファイルサーバーからGoogle共有ドライブへの移行について(後編)

前回紹介した内容の後編として「移行準備」「移行作業」「移行後の運用状況」を紹介して行きます。

eatsmart.hatenablog.com

移行準備

移行準備として以下の作業を行いました。

アクセス権の整理

ユーザ(社員)毎にどの共有ドライブをアクセス可能とするかを設定する上で、GoogleWorkspaceのグループ機能(以降「グループ」)を活用しました。
グループを活用した経緯としては、もともとメールサーバーにGMailを導入しおり、部署宛等のエイリアス配信を行う目的で利用していた為、グループに設定しているユーザーを若干見直すことで活用することができました。
また、ファイルサーバーの時はフォルダー毎に関連する社員のアクセス権を付与しておりましたが、ドライブ毎のアクセス権にグループを付与することで煩雑さを解消することが出来ました。

support.google.com

部署毎の共有ドライブ作成

GASを利用して下記の様な構成の共有ドライブを一括で作成しました。

root共有ドライブ
 ┗部署1共有ドライブ
 ┗部署2共有ドライブ
 ┗部署3共有ドライブ
   ・
   ・
   ・

部署毎の共有ドライブへのアクセス権の付与

こちらも、GASを利用してアクセス権を付与しました。

※GAS作成は、下記サイトが非常に参考になりました。

note.com

移行作業

移行作業自体は全社員に協力頂き手動で行いました。
手順としては上記「移行準備」「部署毎の共有ドライブ作成」で作成した共有ドライブ毎に担当者を割り当てて行いました。

また、今回を機に全社員にパソコン版ドライブを導入しました。
パソコン版ドライブは、Windowsの場合Gドライブに共有ドライブが割り当てられエクスプローラーでファイル操作が可能で且つドライブとの同期も自動で行われるので、ファイルサーバーを利用していた時と同様の操作が可能です。

www.google.com 移行期間としては、全容量:1TB、全アイテム(ファイル+フォルダ)数:75万弱を1週間程度で移行する事ができました。

移行後の運用状況

7月に共有ドライブに移行して3ヵ月程度運用しておりますが、今の所大きなトラブル等もなく稼働しています。
共有ドライブの場合オブジェクト400,000個ネスト階層20という制限があるので、その制限を回避する為のフォルダ階層の設計がポイントになってくると思います。

後、共有ドライブを運用してみた上での所感ですが

  • リモートで業務を行う際に、ファイルサーバーの場合はVPNを経由してアクセスする必要があったが、共有ドライブの場合ネット環境さえあればいつでもアクセス出来るので(VPNが利用できなき場合等)利便性が向上した。

  • 副次的効果として、社員の部署移動や退職が発生した時に、今まではメールとファイルサーバーそれぞれ対処が必要だったが、グループに統一する事で1か所で対処できる様になった。

最後に、共有ドライブのベストプラクティスになりますので、導入を検討されている方は参考にしてみて下さい。

support.google.com

React + Highcharts で Client side export を行う

Highcharts で作成したグラフをPNGへ出力する際に、フォントの指定が反映されない現象に悩まされました。

出力する際のフォントの指定は以下のように行います。これが反映されません。

exporting: {
  chartOptions: {
    chart: {
      style: {
        fontFamily: 'フォント名
      }
    }
  }
}

ドキュメントを読んでいたところ、Client side export という記述を見つけました。 そもそも、実装していた内容だとPNGへ出力する際に外部のサーバーを利用して出力をしていました。 このため、フォントの反映に問題があったようです。

Client side export | Highcharts

早速 Client side export を利用するようにコードを改修しました。

import Highcharts from 'highcharts';
import HighchartsExporting from 'highcharts/modules/exporting';
import HighchartsOfflineExporting from 'highcharts/modules/offline-exporting';  // 追加

....

if (typeof Highcharts === 'object') {
  HighchartsExporting(Highcharts);
  HighchartsOfflineExporting(Highcharts);  // 追加
}

const exportChartImage = () => {
  //chartRef.current?.chart.exportChart({
  chartRef.current?.chart.exportChartLocal({  // 変更
    type: 'image/png',
    filename: 'chart.png'
  }, {});
};

この変更で、出力したPNGにフォントが反映されるようになりました。 また、外部サーバーを利用しないことから、画像がダウンロードされるまでの応答速度も改善されました。

nginxのconfで環境変数を利用する

Dockerコンテナで動かすnginxの設定ファイルでホストのIPアドレスを参照する必要があったので、方法を調べました。

まずnginxの設定ファイルを用意します。 参照したい環境変数を"${HOST_IP_ADDRESS}"のように記述します。

proxy_pass    http://${HOST_IP_ADDRESS}:8080;

つぎに、Dockerfileで起動時にenvsubstでnginxの設定ファイルに環境変数を埋め込むようにします。

CMD ["/bin/sh", "-c", "envsubst '$$HOST_IP_ADDRESS' < /etc/nginx/conf.d/config.template > /etc/nginx/conf.d/config.conf && nginx -g 'daemon off;'"]

最後に、Dockerコンテナを起動する際にenvオプションで環境変数を指定します。

HOST_IP_ADDRESS=`hostname -I | egrep -o '(192.168|172.17).[0-9]+.[0-9]+' | head -n 1`
docker run -d --env="HOST_IP_ADDRESS=${HOST_IP_ADDRESS}" nginx

ちなみに、Apacheだと以下のように記述することで、環境変数参照することが可能です。

ProxyPass / http://${HOST_IP_ADDRESS}:8080/

LinuxからGoogleドライブをストレージとして使うために

Linuxサーバー上にある、数が多く容量を必要とするけどすぐには使わないファイルたちを、Googleドライブをストレージとして格納しておけないか検討をしています。

ファイル自体は日々追加更新されるので、現在はストレージ用サーバーにrsyncしているのですが、そのサーバーが使えなくなるので、大容量でコストを抑えて使えるストレージとしてGoogleドライブの共有フォルダを候補にしました。

google-drive-ocamlfuse

色々調べてみて、はじめにgoogle-drive-ocamlfuseを試しました。 google-drive-ocamlfuseを使うと、Googleドライブをnfsのようにmountできるので、今のrsyncの同期先をmountしたフォルダに変更するだけで対応できると考えました。

参考) Google Driveをマウントする - やってみようよ!

途中でGoogleドライブへのアクセスでOAuth認証が必要となるのですが、サーバー上ではブラウザ認証ができないので、headlessで別PCで認証をします。

参考) 【2021年最新版】SSH接続しかできないAWS内の仮想マシン(Debian)からGUI無しでGoogle Driveにアクセスする

実際にGoogleドライブをmountしてrsyncしてみると、転送速度がかなり遅く、実用的にはなかなか厳しいかなという印象でした。

rclone

google-drive-ocamlfuseの速度改善方法が無いかを調べていたら、rcloneというものを見つけました。

rclone.org

rclone.org

rcloneを使うと、rsyncと同様にファイルの同期を行うことができるので、このコマンドの転送速度が実用的に耐えるかどうかを検証してみようと思います。

結果は次回!

ファイルサーバーからGoogle共有ドライブへの移行について

今回はオンプレで運用してるファイルサーバーからGoogle共有ドライブへ移行を行った際のナレッジを紹介します。

移行先選定

まず、ファイルサーバーの移行先をどこにするか選定作業を行いました。
候補としては、クラウドストレージ(Amazon S3/DropBox/One Drive)やさくらインターネットのオブジェクトストレージ等検討しましたが、以下の様な観点からGoogle共有ドライブを選定しました。

  • もともとGoogleWorkspaceBusinessStarterを利用していた為、上位プランを差額分で利用可能だった

  • 個人用Googleドライブ(マイドライブ)の容量が枯渇してきていた

  • クラウドストレージ容量に対するコストパフォーマンスがまあまあ良い

  • 移行ノウハウが豊富に存在してる
    ※特に下記サイトは非常に参考になりました。

note.com

移行計画

移行先をGoogle共有ドライブに確定した後に、以下の様な移行計画を立ておおよそ1ヶ月程度かけて実施しました。

Google共有ドライブの仕様調査
     ↓
移行時の問題点の洗い出し
     ↓
移行準備
     ↓
移行作業
     ↓
移行後の運用状況

Google共有ドライブの仕様制限調査

次に、Google共有ドライブを利用する上で仕様制限の調査を行いました。調査した中で、移行に直結する仕様をいくつか紹介します。

・1ユーザーあたりのストレージ容量
2TB(Business_Standardの場合)
・1日のアップロードの上限
750GB迄
・1ファイルの最大サイズ
5TB迄
・フォルダー階層数制限
最大20階層
・共有ドライブの最大数
1000個迄
・アイテム数制限
40万個迄

(参考)
GoogleWorkspaceエディション比較 workspace.google.com

Google共有ドライブの制限 support.google.com

移行時の問題点洗い出し

上記仕様制限を把握した所で、問題点の洗い出しを行いました。
まずは、自分たちが利用しているファイルサーバの規模を調査した結果下記の通りとなりました。

全容量:1TB程度
全アイテム(ファイル+フォルダ)数:75万弱
1ファイルの最大サイズ:4GB程度
フォルダーの最大階層数:20階層

上記の結果、全容量/フォルダー階層数/1ファイルの最大サイズについては制限に該当しない事が分かりました。
また、アイテム数制限(40万個)に対しては、ファイルサーバー上はrootに2フォルダ存在する構成でした。
このままだと、運用していくうちにで制限に該当する可能性があった為下記の様に見直しを行いました。

(変更前)ファイルサーバ

rootフォルダ
 ┗sub1フォルダ
  ┗sub3_1
  ┗sub3_2
  ┗sub3_3
   ・
   ・
 ┗sub2フォルダ
  ┗sub3_1
  ┗sub3_2
  ┗sub3_3
   ・
   ・

(変更後)

root共有ドライブ
 ┗sub1_sub3_1共有ドライブ
 ┗sub1_sub3_2共有ドライブ
 ┗sub1_sub3_3共有ドライブ
   ・
   ・
   ・

今回はここまでとさせて頂きます。次回「移行準備」「移行作業」「移行後の運用状況」等共有できたらと思います。

SeleniumでE2Eテストを自動化する

SeleniumでE2Eテストを自動化してみました。 きっかけは、Google Analytics 4を導入したタイミングで既存のJavaScriptの処理に不具合が発生したことで、これを検出することが目的です。

環境構築

必要なライブラリをインストールします。 テストには以前node.jsのプロジェクトで利用したmochaを利用します。 chromeはローカルのメジャーバージョンが100だったので、それを指定しています。

npm install -D selenium-webdriver mocha chromedriver@100

テストの実装

クリックするリンクをCSSセレクタで指定し、クリックイベントを実行します。 手元で実行し、パスすることを確認します。

const { Builder, By } = require('selenium-webdriver');
const assert = require('assert');

// chromeのパスを通す
const chrome = require('selenium-webdriver/chrome');
const chromedriver = require('chromedriver');
chrome.setDefaultService(new chrome.ServiceBuilder(chromedriver.path).build());

let driver;

describe('Selenium_E2E_Test', () => {
  before(() => {
    const options = new chrome.Options()
            .headless()    // Dockerコンテナで実行するのでheadless
            .addArguments(['--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage'])    // メモリの使用量を減らす
            .windowSize({ width: 375, height: 667 });    // スマートフォンの画面サイズ
    driver = new Builder().forBrowser('chrome').setChromeOptions(options).build();
  });

  after(() => {
    return driver.quit();
  });

  it('リンクをクリックしてページの遷移を確認', async () => {
    // ページ遷移
    await driver.get('https://example.com/abc');
    assert.strictEqual(currentUrl, 'https://example.com/abc');

    // リンクをクリック
    await driver.findElement(By.css('.button a')).click();
    currentUrl = await driver.getCurrentUrl();

    // ページの遷移を確認
    assert.strictEqual(currentUrl, 'https://example.com/123');
  });

});

テストの実行

Dockerコンテナで実行するため、Dockerfileを用意します。

Seleniumを利用するため、以下のイメージを利用しました。 github.com

FROM selenium/standalone-chrome:4.1.4-20220427

RUN mkdir /tmp/selenium/
WORKDIR /tmp/selenium/

# node.jsとnpmをインストール
RUN sudo apt update
RUN sudo curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
RUN sudo sh nodesource_setup.sh
RUN sudo apt install -y nodejs
RUN node -v
RUN npm -v

// package.jsonをコピー
COPY package.json /tmp/selenium/
RUN npm install

// テストをコピー
COPY test.js /tmp/selenium/test/

// テストを実行
CMD ["npm", "run", "test"]

コンテナをビルドして実行すると、テストを実行することができます。

> docker build -t selenium-e2e . && docker run --rm selenium-e2e
> mocha test/test.js --timeout 0

  SeleniumChromeTest
    ✔ リンクをクリックしてページの遷移を確認 (2582ms)

  1 passing (3s)

簡単なテスト内容ですが、Seleniumを利用することでE2Eテストを自動化することができました。