はじめに
関係者で保有するデータを見える化するため、Nuxt.jsを利用したプロトタイプ開発を実施していました。
コンテンツ開放前は、Basic認証をかけて関係者のみに展開できるようにするため、
Nuxt.jsに簡単にBasic認証をかけられる「nuxt-basic-auth-module」を利用したのですが、
諸所の条件が重なり、データ表示の際にBad Request(400)が発生するようになってしまいました。
解決することができたので、それらの過程をまとめてみました。
実現しようとしていたこと
当初は下図のイメージで、プロトタイプを構築しようとしていました。
データはDBは利用せず、S3に保存し、API Gateway経由で取得するようにしています。
S3は公開領域に保存せず、API Gatewayのキー情報をヘッダーに入れて取得するようにしています。
nuxt-basic-auth-moduleの導入
Nuxt.jsアプリケーションやAPI Gateway経由でS3のファイルでアクセスする処理は、下記のような感じで出来上がったため、早速 nuxt-basic-auth-moduleを導入することとしました。
const response = await $axios.$get($config.API_GATEWAY_URL) {
headers: {"x-api-key": $config.X_API_KEY}
}
return {
data: response.data,
}
nuxt-basic-auth-moduleは下記の記事を参考に導入しました。
あっさりと導入できたので、お客様に展開するため、アプリケーションのデプロイを実施しました。
ヘルスチェックのエラー対応
いつまでたっても反映されないので、ECSのサービスのログを見てみると下記のようにコンテナ起動・削除が繰り返されていました。
service xxxxxx-service has stopped 1 running tasks: task e68b4001fa904aa187ac793bdd48810a.
080ad5cf-d721-4ab3-b8c7-d3b048f9504f
2021-12-29 17:57:29 +0900
service xxxxxx-service deregistered 1 targets in target-group TG-ecs-xxxxxx
e7892e21-5e35-45c0-8130-7bd0c6ea89d5
2021-12-29 17:57:29 +0900
service xxxxxx-service (port 3000) is unhealthy in target-group TG-ecs-xxxxxx due to (reason Health checks failed with these codes: [401]).
00a9e933-e8d0-4f41-834c-c0894b2eb0a5
2021-12-29 17:52:25 +0900
service xxxxxx-service registered 1 targets in target-group TG-ecs-xxxxxx
c1367a52-62b8-419e-bc13-b76b51464058
2021-12-29 17:51:19 +0900
service xxxxxx-service has started 1 tasks: task e68b4001fa904aa187ac793bdd48810a.
e7ee4c31-86b3-47c4-89a8-f1b8e38fd5c7
2021-12-29 17:51:08 +0900
(省略)
service xxxxxx-service has stopped 1 running tasks: task cd0081685db84991ac02e88dd85269db.
f6f36466-6821-471b-acfa-a2e4c2e13d53
2021-12-29 17:45:07 +0900
service xxxxxx-service deregistered 1 targets in target-group TG-ecs-xxxxxx
53e05ed0-fe70-4618-80e6-8581dc0327ac
2021-12-29 17:45:07 +0900
service xxxxxx-service (port 3000) is unhealthy in target-group TG-ecs-xxxxxx due to (reason Health checks failed with these codes: [401]).
9ffacb22-29ad-4440-84c7-8b8256017fc9
2021-12-29 17:40:10 +0900
service xxxxxx-service registered 1 targets in target-group TG-ecs-xxxxxx
e572bc65-a3ee-42a6-8e3d-417848e3a668
2021-12-29 17:39:13 +0900
service xxxxxx-service has started 1 tasks: task cd0081685db84991ac02e88dd85269db.
原因はヘルスチェックで指定していたパス「/」に対して、Basic認証が動くようになってしまったため、ヘルスチェックが失敗してしまい、コンテナ起動・削除が繰り返されていたのが原因でした。
そこで「healthcheck.vue」というファイルを「pages」ディレクトリに保存し、「/healthcheck」でヘルスチェックを行うように設定することにしました。
特定のパスを Basic認証から除外したい
ただ該当のパスをBasic認証から除外する必要があり、「nuxt-basic-auth-module」でどのようにしたらよいか?を調べることとなりました。
サンプルには記載されていなかったのですが、「match」という設定項目があり、
下記の通り記載されています。
match
type: String(regex literal) | Function
required: false
The target path. This allows you to set up basic authentication that is limited to routes that match regular expression literals or where the function returns true.
なるほど正規表現で指定すればいいんですね。
ということで「/healthcheck」を除外(否定)する正規表現を記載して設定してみたのですが、
なかなかうまく動きません。(おそらく正規表現の記載方法が間違っているのかなとは思います。)
もう1つ「function」で指定できるとのことでしたので、以下の functionを記載して、設定してみました。
// Basic Authentication
basic: {
name: 'xxxxxxxx',
pass: 'xxxxxxxx',
enabled: 'true',
match: req => {
return req.url !== '/healthcheck'
&& req.url.indexOf('_nuxt') == -1
&& req.url.indexOf('_loading') == -1
}
},
※_nuxtや_loadingも結果として必要で、入れないとBasic認証に引っかかって、アプリケーションが動作しませんでした。
これでヘルスチェックも成功するようになり、無事デプロイ完了しました。
後は動作確認をしたら、お客様に展開するだけです。
ファイルサイズが大きすぎた(S3 Signed URLの導入)
アプリケーションを表示しようとしたところ、データ取得のAPIで「500エラー(Internal server error)」
調べていたら、Lambdaではレスポンスのサイズ制限があるとのことでした。
下記の記事を参考に、S3のPre-Signed URLを導入し、解決を図ることにしました。
■S3のPre-signed URLを利用したアクセス
// Pre-signed URLの取得
const url = await $axios.$get($config.API_GATEWAY_URL) ~
headers: {"x-api-key": $config.X_API_KEY}
})
// S3ファイルの取得
const response = await $axios.$get(url)
return {
data: response.data,
}
Bad Request (400)の発生と解消
500エラーは解消したのですが、今度は下記のようにBad Request(400)エラーが発生するようになってしまいました。
原因探るのには結構時間がかかってしまったのですが、Basic認証が成功した後のリクエストでは、「Authentication」ヘッダーが自動で設定されていて、S3の署名付きURLでアクセスした場合、エラーとなってしまっているのが原因でした。
そのためS3の署名付きURLの呼び出し前で「Authentication」ヘッダーを削除して、アクセスするようにしたところ、無事アクセスができるようになりました。
// Pre-signed URLの取得
const url = await $axios.$get($config.API_GATEWAY_URL) ~
headers: {"x-api-key": $config.X_API_KEY}
})
// Authenticationヘッダーの削除
delete $axios.defaults.headers.common.authorization
// S3ファイルの取得
const response = await $axios.$get(url)
return {
data: response.data,
}
ようやくお客様にプロトタイプを展開できそうです。
でも今後フィードバックが返ってくるので、これからが本番です。