長生村本郷Engineers'Blog

千葉県長生村本郷育ちのエンジニアが書いているブログ

terraform workspace で環境毎に tfsate 管理

f:id:kenzo0107:20171205214728j:plain

概要

Terraform tfstate の管理をかつて
0.8 系では -backend-config でせっせと環境(stg,prod) 毎に bucket を変えて、
なんてコードを見てきました。

ですが、 workspace で 1つの bucket に 環境毎に保管できる様になりました。

厳密には環境毎でなくとも
リソースの集合毎、module 毎等で管理するイメージですが

今回はイメージを捉えやすく環境毎で分けました。

歴史

  • 0.5 で S3 で管理、
  • < 0.9 では、 remote config で管理場所を設定
  • = 0.9 では、terraform workspace で同一ディレクトリで複数のリソース群を管理

とより利用しやすくなりました。

前提

以下条件とします。

  • tfstate は backend.tf で s3 管理

移行手順

既存 terraform で tfstate 確認

  • 想定の実行計画通りか確認します。
  • 異なる場合は、そもそも現環境と差分が生じている、及び、tfstate が正しく取得できていない等、問題ありなのでそちらを修正します。
$ terraform plan

tfstate ファイル取得

local に terraform.tfstate を取得します。 中身を確認してリソースの設定がある程度問題ないか確認しておきます。

  • 0.8 系
$ terraform remote config \
-backend=s3 \
-backend-config="bucket=tfstate.bucket" \
-backend-config="key=terraform.tfstate" \
-backend-config="region=ap-northeast-1" \
-backend-config="profile=aws-hogehoge"
  • 0.9 系以降
macOS%$ terraform state pull > terraform.tfstate

terraform 0.11.x (2017年12月現在最新) へバージョンアップ

Homebrew ならば upgrade で!

macOS%$ brew upgrade terraform

state 管理を backent.tf で記述

既にこの様に設定されている方はスキップです。特に普遍的な書き方です。

terraform {
  backend "s3" {
      bucket  = "tfstate.bucket"
      key        = "terraform.tfstate"
      region   = "ap-northeast-1"
      encrypt = true
      profile   = "aws-hogehoge"
    }
}

Workspace 作成

  • Workspace stg 作成
$ terraform workspace new stg
  • workspace リスト一覧
$ terraform workspace list
  default
* stg

tfstate を push

$ terraform state push -force .terraform/terraform.tfstate

これで S3 tfstate.bucketenv:/stg/ ディレクトリ以下に terraform.tfstate が push されました。 実際に S3 を見て確認してみてください。

f:id:kenzo0107:20171205213108p:plain

env でなく env: なのが肝です。

実行計画確認

$ terraform plan

想定の実行計画通りか確認して問題なければ移行完了です。

おまけ

terraform を指定したバージョンで実行するには
one-off Container で実行できる様に Makefile でラップする、 が今の所自分の中のベストプラクティスです。

これによって local 環境に依存せず指定したバージョンの terraform 実行が可能となります。

one-off Container とは

one-off Container は Docker コンテナを run --rm で1度のコマンド実行の為だけに起動する手法です。

Makefile で Docker コマンドをラップしておくと
TERRAFORM_VERSION を変更するだけで
指定の terraform バージョンを利用できます。

以下は 0.11.1 の例です。

TERRAFORM_VERSION=0.11.1

DOCKER=docker run --rm -v ~/.ssh:/root/.ssh:ro -v ~/.aws:/root/.aws:ro -v ${PWD}:/work -w /work hashicorp/terraform:${TERRAFORM_VERSION}

$(eval ENV := $(shell ${DOCKER} workspace show))

ifeq (${ENV}, default)
$(error select workspace ! ex: make init ENV=<stg|prod>)
endif

default: init

init:
    # tfstate ファイル初期化
    ${DOCKER} init
    # workspace 作成. "; true" は既に作成済みエラーをスキップする為
    ${DOCKER} workspace new ${ENV}; true
    # 作成した workspace を選択
    ${DOCKER} workspace select ${ENV}
    # 作成した workspace の tfstate ファイルを同期
    ${DOCKER} init

plan:
    ${DOCKER} plan 

apply
    ${DOCKER} apply -auto-approve
  • make init ENV=stg 実行で以下まとめてました
    • tfstate 初期化
    • workspace stg 作成
    • 選択したworkspace の tfstate で初期化

きっとさらに素敵なベストプラクティスがあれば教えてください!

参考になれば幸いです。

DevOpsを支えるHashiCorpツール大全 ThinkIT Books

DevOpsを支えるHashiCorpツール大全 ThinkIT Books

Hubot で Git の Pull Request や Issue のコメントのメンション相手に Slack DM で通知

概要

Git での Pull Request や Issue コメントのメンションがメール通知で気づけず困った! という声を多く聞き、メンション相手に Slack DM を通知する様な仕組みを作りました。

システム概要

今回は AWS 上に構築しました。

  • Git は GHE on EC2
    • github.com の場合だと、IP 定まらない問題があるかと思うので、動的に IP を取得して解放させる様な仕組みを入れる必要がありそう。
  • hubot は t2.nano と最小
    • 当初、IBM Bluemix で構築してみましたが、サポートから IP 制限はまだできていない、とのことなので on AWS にしました。
  • GHE からの hubot の受け口は ELB で EIP のみ許可させてます。
    • 今後、受け口を色々作る目的で ELB 立てました。
    • 元々は JIRA のメンションを Slack DM に送るだけの目的だったので 同一 Private Subnet に置いてました。

f:id:kenzo0107:20171123213635p:plain

スクリプト

  • getSlackUsernameByGitUsername
    • 基本 git name と slack name は命名規則が統一されていたので正規表現で変換させる様に解決しています。
    • git name: kenzo-tanaka だったら slack name: kenzo.tanaka に変換
    • 命名規則に即していないユーザは以下の users リストに変換を任せます。
      • kimika.himura は DM 送られたくないと言う人を想定してます。
  • 依存ライブラリ
    • "hubot-slack": "^4.4.0"
    • "hubot-slack-attachement": "^1.0.1"
users = {
    "kenzo-tanaka": "kenzo0107",
    "kimika.himura": "no_send"
}

ソース全容は以下になります。

gist.github.com

Git 設定

設定したい Organization or Owner > Settings > Hooks で hubot への URL を設定します。*1

その他設定

  • Content type: application/json
  • Let me select individual events:
    • Issues
    • Issue comment
    • Pull request
    • Pull request review
    • Pull request review comment

f:id:kenzo0107:20171123225234p:plain

※ よりセキュアにする際には Secret 設定してください。

通知が来た!

早速 Pull Request でメンションしてみたら通知が来ました!
絵文字もしっかり!
URL も自動でリンクされている!

f:id:kenzo0107:20171123224451p:plain

以上、参考になれば幸いです♪

WEB+DB PRESS Vol.101

WEB+DB PRESS Vol.101

*1:Organization 跨いで一気に全部のリポジトリにHookかけるのは別途スクリプト組むなりしないと難しそう。GitHub社も Organization は 1つとすることを推奨とのことなので今回はこれで!

Prometheus2.0 remote storage 検証

いよいよ出ました Prometheus 2.0 !

prometheus.io

先日モニタリング勉強会でも Paul Taylor さんの LT を拝聴させて頂き
パフォーマンス向上とストレージフォーマット変更による圧縮・バックアップがしやすくなった等、 良い話がたくさん出ていました。

www.slideshare.net

中でも最も期待していた機能が Remote Long-Term Storage、
長期保存機能には歓喜しました♪

1系以下では、短期間用と長期間用の Prometheus を別途用意する等、対策が必要で 冗長な作りを余儀なくされたところがありましたが 2.0 リリースでついに!

早速試してみたく使用感をまとめました。

今回やりたかったことまとめ

  • Prometheus 2.0 リリースに際して期待の長期保存機能 (Remote long-term storage) を早速試す!
  • 実際にローカル環境で構築してみて1系からの変更箇所を確認
  • DB 側にどんなデータが入るのか確認

システム概要

あくまで使用感の検証をしたかったので docker-compose でお手軽に作れる環境にしました。

Imgur

前提条件

以下を Vagrant にインストール

  • Ubuntu 16.04.3 LTS \n \l
  • Docker version 17.09.0-ce, build afdb6d4
  • docker-compose version 1.12.0, build b31ff33

起動する Docker Container

  • Prometheus 2.0.0
  • Node Exporter 0.15.1
  • AlertManager 0.9.1
  • cAdvisor 0.28.0
  • Prometheu Adapter
  • PostgreSQL 9.6.3
  • Grafana 4.6.1
  • Nginx 1.13.6
  • Adminer

使い方

以下手順通りです。
https://github.com/kenzo0107/vagrant-docker/tree/vagrant-docker-ubuntu16.04/docker/prometheus-grafana-on-ubuntu

macOS%$ git clone https://github.com/kenzo0107/vagrant-docker
macOS%$ cd vagrant-docker
macOS%$ vagrant up

// Install docker, docker-compose
macOS%$ vagrant provision
macOS%$ vagrant ssh
vagrant%$ cd /vagrant/prometheus-grafana-on-ubuntu
vagrant%$ sudo docker-compose up -d

Name                             Command                            State                             Ports
-------------------------------------------------------------------------------------------------------------------------------------
adapter                           /prometheus-postgresql-ada ...    Up
adminer                           entrypoint.sh docker-php-e ...    Up                                8080/tcp
alertmanager                      /bin/alertmanager -config. ...    Up                                9093/tcp
cadvisor                          /usr/bin/cadvisor -logtost ...    Up                                8080/tcp
grafana                           /run.sh                           Up                                3000/tcp
nginx                             nginx -g daemon off;              Up                                0.0.0.0:18080->18080/tcp,
                                                                                         0.0.0.0:3000->3000/tcp, 80/tcp,
                                                                                         0.0.0.0:8080->8080/tcp,
                                                                                         0.0.0.0:9090->9090/tcp
node-exporter                     /bin/node_exporter                Up                                9100/tcp
pgsql                             docker-entrypoint.sh -csyn ...    Up                                5432/tcp
prometheus                        /bin/prometheus --config.f ...    Up                                9090/tcp

アクセスしてみる

Prometheus

Imgur

Grafana

GF_SECURITY_ADMIN_USER=admin-user
GF_SECURITY_ADMIN_PASSWORD=admin-pass

Imgur

  • Datasource 設定

Imgur

Datasource 設定フォームに以下情報を入力し Add ボタンをクリックします。

Item Value
Name Prometheus
Type Prometheus
URL http://prometheus:9090
Access proxy

Imgur

Imgur

グラフが表示されます。

Imgur

cAdvisor

Imgur

Adminer

Imgur

ログインフォームに以下情報を入力します。

Item Value
Server pgsql
Username prometheus
Password password
Database postgres
  • PostgreSQL に保存されているメトリクス情報が確認できます。

PostgreSQL >> pgsql >> postgres >> prometheus >> Select: metrics

Imgur

AlertManager でアラート通知してみる

例として node-exporter を停止

vagrant%$ sudo docker-compose stop node-exporter

./alertmanager/config.yml で設定した Slack Channel にちゃんと通知がきました。 f:id:kenzo0107:20171113021331p:plain

所感

  • 2.0 になって設定の仕方が諸々変わり、公式サイトじっくり見る必要あります。

  • 今回は Prometheus ×1 台構成ですが、2台以上で冗長化する構成も試してみたい。

余談

あとがき

Mackerel の様なマネージドな監視サービスで運用コストを削減する以上に
Prometheus をマネージドすれば、さらにトータルコストを抑えられる様になる、
と睨んでます。

ですが、Datadog は APM 付きプランも適度なコスト感で提供しておりマネージドサービスの魅力は尚大きいです。

モニタリングの棲み分けをできる様にするにも、
選択肢の一つにするにも Prometheus 挑戦しがいがあるのでは? と思っています。

Prometheus、今後さらに広まることを期待しています。

参考

iftop でネットワーク接続状況をリアルタイム監視

iftop 概要

CLI上で利用できるネットワークの接続状況をリアルモニタリングするツールです。
→ ネットワークのボトルネックを特定する為に利用します。

単にネットワークのモニタリングであれば、モニタリングツールで良いですが

具体的にどこ(ドメイン・IP・ポート)にどれくらい(データ転送量)がわかります。

インストール方法

$ sudo apt-get install -y iftop
$ sudo yum -y install epel-release
$ sudo yum -y install iftop

使い方

よく利用するのはこんな形です。
※eth0 がない場合は -i eth0 を除いてください。

$ sudo iftop -i eth0 -B -P -n -N
  • -i インターフェース指定
  • -B 表示単位を Byte にする
  • -P プロトコル or ポート表示
  • -n ドメインでなく ip で表示
  • -N プロトコルサービス名でなくポート番号で表示

表示項目

=> が送信、
<= が受信です

                           24.4kB                      48.8kB                      73.2kB                      97.7kB                 122kB
+--------------------------+---------------------------+---------------------------+---------------------------+---------------------------
ip-10-13-1-101.ap-northeast-1.compute.internal:http      => ip-10-13-100-41.ap-northeast-1.compute.internal:62635     559kB   121kB  67.1kB
                                                        <=                                                          3.60kB  1.90kB  1.05kB
ip-10-13-1-101.ap-northeast-1.compute.internal:35244     => ip-10-13-102-56.ap-northeast-1.compute.internal:mysql       0B   2.18kB  1.21kB
                                                        <=                                                             0B   23.1kB  12.8kB
ip-10-13-1-101.ap-northeast-1.compute.internal:35247     => ip-10-13-102-56.ap-northeast-1.compute.internal:mysql       0B   2.13kB  1.18kB
                                                        <=                                                             0B   23.0kB  12.8kB
ip-10-13-1-101.ap-northeast-1.compute.internal:http      => ip-10-13-0-231.ap-northeast-1.compute.internal:8239         0B   7.73kB  4.29kB
                                                        <=                                                             0B   1.16kB   658B
ip-10-13-1-101.ap-northeast-1.compute.internal:ssh       => ip-10-13-0-11.ap-northeast-1.compute.internal:56320       612B    576B    522B
                                                        <=                                                            26B     26B     32B
ip-10-13-1-101.ap-northeast-1.compute.internal:http      => ip-10-13-100-41.ap-northeast-1.compute.internal:62657       0B     49B     27B
                                                        <=                                                             0B     92B     51B
ip-10-13-1-101.ap-northeast-1.compute.internal:40069     => ip-10-13-103-247.ap-northeast-1.compute.internal:6379       0B     99B     55B
                                                        <=                                                             0B     34B     19B
ip-10-13-1-101.ap-northeast-1.compute.internal:40072     => ip-10-13-103-247.ap-northeast-1.compute.internal:6379       0B     99B     55B
                                                        <=                                                             0B     34B     19B
ip-10-13-1-101.ap-northeast-1.compute.internal:http      => ip-10-13-100-73.ap-northeast-1.compute.internal:27698       0B     44B     25B
                                                        <=                                                             0B     33B     18B
ip-10-13-1-101.ap-northeast-1.compute.internal:53696     => ip-10-13-0-2.ap-northeast-1.compute.internal:domain         0B     21B     12B
                                                        <=                                                             0B     31B     17B
ip-10-13-1-101.ap-northeast-1.compute.internal:41975     => ip-10-13-0-2.ap-northeast-1.compute.internal:domain         0B     21B     12B
                                                        <=                                                             0B     31B     17B
-------------------------------------------------------------------------------------------------------------------------------------------
TX:             cum:   1.31MB   peak:    560kB                                                             rates:    560kB   134kB  74.7kB
RX:                     505kB            117kB                                                                      3.69kB  49.8kB  28.1kB
TOTAL:                 1.81MB            564kB                                                                       564kB   184kB   103kB
Item Value
TX (Transmitter) 送信量
RX (Receiver) 受信量
TOTAL iftop 起動からの総量
cum 総量
peak 最大
右端3列 (各トラフィック, rates含む) 2秒、10秒、40秒の転送量平均値

※TX,RX の 「X」 は省略しますよという意味

閲覧し続けると気になる処理があった時には
Shift + P で一旦停止させます。

もう一度開始したい場合は Shift + P です。

実際のCLI

以下見ていただくと白い帯グラフが左から伸びているのが見えるかと思います。
この横棒が一番上のバーの目盛りに相応してぱっと見でどの程度かわかるのが便利です。

f:id:kenzo0107:20171109011640p:plain

DB への接続を確かめる

DB (MySQL) のデフォルトポート 3306 への送受信を調べたいとき

$ sudo iftop -B -P -n -N -f "port 3306"

当然ながら受信の方が大きいです。 f:id:kenzo0107:20171109120917p:plain

補足

実際に負荷が高い時等、特定のインシデントがあった際に追記していこうと思います♪

Reference

toda-tocochan-bus flask on IBM Bluemix へ引っ越し

GCP から IBM Bluemix へ引っ越しました!

toco ちゃんバス あと何分? f:id:kenzo0107:20171029095513p:plain

概要

さくらVPS から GCP
そして今度は GCP から IBM Bluemix に引越ししました。

以前 GCP 運用時の話はコチラ kenzo0107.hatenablog.com

GCP は GKE に LB かましたら価格がバコッと上がってしまい
無料枠を逸脱してしまいました (>_<)

なんとか低価格で運用したいという目論見です。

何故 Heroku でなく IBM Bluemix ?

IBM Bluemix の良い所は機能が充実している所です。
無料・デフォルトで kibana が見れます。

f:id:kenzo0107:20171029101148p:plain

f:id:kenzo0107:20171029101853p:plain

その他 Git との連携も可です。

以下 Mac で作業することを前提に手順まとめました。

事前準備

  • IBM Bluemix に Sign Up しときます

Signup IBM Bluemix

  • clone
macOS%$ git clone https://github.com/kenzo0107/toda-tocochan-bus-on-ibmbluemix
macOS%$ cd toda-tocochan-bus-on-ibmbluemix
  • cloudfoundry の CLI インストール
macOS%$ brew tap cloudfoundry/tap
macOS%$ brew install cf-cli

デプロイ

macOS%$ cf api https://api.ng.bluemix.net
macOS%$ cf login
macOS%$ cf push <application name>
  • API は Region によって変わります。
Region API URL
米国南部 https://api.ng.bluemix.net
英国 https://api.eu-gb.bluemix.net

総評

Cloudfoundry の CLI のお陰で引っ越しも簡単でした♪

セキュリティとして特定 IP やドメインからアクセスさせないとか出来たら
商用のメソッドとして利用出来そうかなと思いました。

その点質問してみましたが2週間ほど連絡がないので再度連絡してみます。
↑質問は英語限定でした!

サポートが強化されると有難いなと思いました。

以上 ご参考になれば幸いです。

CasperJS+PhantomJS で Github Organization 移行

f:id:kenzo0107:20171025214738p:plain

概要

Github Enterprise の Organization 移行を実施した際に CasperJS と PhantomJS でヘッドレスブラウザより操作し移行しました。
Github 上で API がない(はず?)為、この様な対応をしました。

どちらかというと CasperJS+PhantomJS でブラウザ上の試験作りを楽しんでいたこともあり
試してみてすんなりできたので採用した経緯になります。

ヘッドレスブラウザとは?

GUI なしのブラウザを CLI で利用するというもの。
ページ描画や画像ロード、ログインしたりとフロントの試験で期待される機能を持っています。

三行まとめ

  • CasperJS + PhantomJS でログイン認証はじめブラウザ操作で Transfer する工程を実行
  • 移行後、元の URL が移行先にリダイレクトされるか確認
  • 期待する要素が時間内に取得できないときはページをキャプチャ

やってみる

Get Started ご参照ください。

github.com

以下にも手順まとめてます。

Clone

macOS%$ git clone https://github.com/kenzo0107/ghe-org-transfer
macOS%$ cd ghe-org-transfer

scripts/ghe-org-transfer.js の 編集

var config = {
  // site root.
  siteRoot: 'https://<your github domain>',
  // Github Login path
  loginPath: '/login',
  // Github Login Account
  loginParams: {
    email: '<your email>',
    password:  '<your password>'
  },
  viewportSize: {
    width: 3000,
    height: 2500
  },
  paths: [
    "<your owner>/<your repository>"
  ],
  destOrganization: '<your destination of organization>',
  reason: 'transfer this repository to new oragnization',
};

移行イメージ

  • ex) hoge/mywonderfulrepo ---> moge/mywonderfulrepo
paths: [
  "hoge/mywonderfulrepo"
],
destOrganization: 'moge',

paths は複数指定しても問題ありません。

移行実施

macOS%$ make run
  • 実行結果

※ 移行後に元の URL でリダイレクトされるか試験しています。

[url] https://github.aiueo.com/hoge/mywonderfulrepo/settings
[step] fill '#transfer_confirm > form'
[step] input checkbox #team-59
[step] click the button labeled "Transfer"
(^-^) redirect ok:https://github.aiueo.co.jp/hoge/mywonderfulrepo to https://github.aiueo.co.jp/moge/mywonderfulrepo

総評

以前 Grafana のグラフのスナップショットを撮る Grafana API がうまく動作しなかったのも
CasperJS+PhantomJS で取得する様にできました。

いやはや便利♪

全然別件ですが、サイトの認証に利用される reCAPTCHA の突破など挑戦してましたがうまく行かず...
うまくいったぜ!という方、是非教えてください m( )m

WAF+CloudFront でリファラチェック (直リンク禁止)

概要

AWS WAF (Web Application Firewall) を利用し Cloudfront でのリファラ制御を実装しましたのでそのまとめです。

私は直リンク禁止対策として導入しました。

f:id:kenzo0107:20171005180531p:plain

以下手順になります。

Go to AWS WAF ボタンクリック

サービス > WAF & Shield と辿り Go to AWS WAF クリック

f:id:kenzo0107:20171005174955p:plain

Configure Web ACL ボタンクリック

ACL (Access Control List) を設定していきます。

f:id:kenzo0107:20171005180856p:plain

概要確認

特にチェックせず Next ボタンクリック

f:id:kenzo0107:20171005181044p:plain

web ACL 設定

f:id:kenzo0107:20171005181315p:plain

以下、設定項目を設定し、Next ボタンクリック

Item Value
Web ACL name (任意) 例ではCloudfront の CNAME を設定しています。
CloudWatch metric name Web ACL name を入力すると自動で入力される。変更したい場合のみ変更
Region Global(CloudFront) 選択
AWS resource to associate 対象となるCloudfrontを選択する箇所。運用中の Cloudfront を対象とすると場合は後々設定。

条件作成

今回は文字列一致を条件とする為、 String match conditions にて Create condition ボタンクリック

f:id:kenzo0107:20171005181800p:plain

string match condition 作成

f:id:kenzo0107:20171005181926p:plain

以下設定し Add filter ボタンクリック。
複数 filter がある場合、Add filter を繰り返します。

Item Value
Name (任意)
Part of the request to filter on Header
Header Referer
Match type Contains
Transformation Convert to lowercase
Value to match 対象となるドメイン設定


Add filter 後、 Create ボタンクリック。

f:id:kenzo0107:20171005182327p:plain

Next ボタンクリック

追加したもののすぐに反映されていない。 そのまま Next ボタンクリック

f:id:kenzo0107:20171005182446p:plain

ルール作成

Create rule ボタンクリック。

f:id:kenzo0107:20171005182608p:plain

ルールに条件を紐付け

Name, Cloudwatch metric name を設定し
Add conditions で条件を追加します。

その後、Create ボタンクリック。

f:id:kenzo0107:20171005182641p:plain

ルール以外のリクエストのアクセス禁止

やはり Rule は反映されていない。ですが、続けて
Block all requests that don't match any rules をチェックし Review and create ボタンクリック。

※対象のCloudfront に反映させたくない場合は、Cloudfront を選択したリソースを解除する必要があります。
※最後に関連付けができるのでここではするべきではないと思います。

f:id:kenzo0107:20171005182806p:plain

確認ページで入力内容確認後作成

Confirm and create ボタンクリック。

f:id:kenzo0107:20171005183423p:plain

対象の web ACL を編集

WEB ACLs より選択し Edit web ACL ボタンクリック

f:id:kenzo0107:20171005183628p:plain

web ACL 編集

  1. 作成したルールを選択
  2. Add rule to web ACL ボタンクリック
  3. Allow 選択
  4. Update ボタンクリック

f:id:kenzo0107:20171005183720p:plain

Cloudfront 関連付け

Add association ボタンクリック

f:id:kenzo0107:20171005184119p:plain

Web ACL に Cloudfront を関連付け

Resource で 対象の Cloudfront を選択し Add ボタンクリック

f:id:kenzo0107:20171005184153p:plain

以上で数分後 WAF+CloudFront によるリファラチェックが確認できる状態になります。

アクセス確認

自環境では
ローカルの /etc/hosts 修正し対象ドメインから Cloudfront CNAME へのリンクを貼って確認しました。

Cloudfront CNAME ドメインでのリソースを直接アクセスすると
以下の様な エラーページが表示されることが確認できました。

f:id:kenzo0107:20171005184505p:plain

もう少しユーザフレンドリーに

上記のエラーページは Cloudfront > Error Pages で Create Custom Error Response で S3 上のパスを指定することでカスタマイズが可能です。

是非サイトコンセプトに合ったエラーページをご用意されるとよりユーザフレンドリーな配信になるかと思います。

以上
ご参考になれば幸いです。