20210419のAWSに関する記事は20件です。

AWSとオンプレ環境をインターネットVPNのSite to Site(IKEv2)で接続する手順

背景と雑談 クラウドVPNの第二弾として、自分のメモも兼ねてステップバイステップでAWSとのVPN接続手順を紹介します。 AWSではVPNを作る度に、pre-shared-keyやAWS側のグローバルIPが変更されます。コスト削減で接続と切断を繰り返す場合はAzureに比べて非常に煩雑なのでご注意ください。 余談ですがVPNの接続スピードの上限ですが、AWSは1.25Gbps、Azureは選択式、GCPは3Gbps(上り下りの合計)とのことです。(後述) Azure VPN接続手順は以下からご覧ください。 https://qiita.com/kan_itani/items/0cda4479b61d543c8dde GCP版も近日中に作成予定です。 目次 構成イメージ 事前確認・注意事項 AWSのVPC内のネットワーク作成 仮想プライベートゲートウェイ(VGW)の作成 VPCに仮想プライベートゲートウェイ(VGW)をアタッチ カスタマーゲートウェイの作成 VPNコネクションの作成 オンプレ側ルータ設定 VPCのルートテーブル設定変更 VPCのFirewall設定変更 VPNのトンネル状態確認とルーティングテーブル確認 関連情報 1. 構成イメージ 以下はAzure VPN編で使った構成図ですが、サブネットが違うだけであとは似たような感じなので(←適当すぎる)、そのまま絵を流用します。 192.168.70.0/24 → 今回は192.168.65.0/24 172.16.0.0/16 → 今回は172.31.0.0/16がAWS側を想定 実際にAWS側に存在するサブネットは以下の3つです。 172.31.0.0/20 172.31.16.0/20 172.31.32.0/20 サブネット一覧は以下の通りです。 2. 事前確認・注意事項 ネットワーク設計 (当たり前ですが)オンプレとAWSでそれぞれサブネットがかぶらないようにネットワークを割り当てます。 この手順ではVPC内のネットワーク作成手順は省略します。(デフォルトで3つ作られていたため。) VPN接続用の共通パスワードの準備 今回の手順ではpre-shared-keyは意識することなく設定できます。(自動生成&ダウンロードするルーターconfigに含まれているため) グローバルIPの確認 事前にオンプレミス側のVPN接続用ルータのグローバルIPを確認します。 AWS側は特に意識しなくてもこの手順実施後にダウンロードできるルータコンフィグにグローバルIPが含まれます。 接続する回線速度の検討 Azureの場合は設定画面でSKUを選ぶことができましたが、AWSは以下のFAQに記載があるように、1.25Gbpsが最大とのことです。 https://aws.amazon.com/jp/vpn/faqs/ Q: サイト間 VPN 接続の最大スループットはどれくらいですか? A: 各 AWS サイト間 VPN 接続には 2 つのトンネルがあり、各トンネルは最大 1.25 Gbps のスループットをサポートします。VPN 接続が仮想プライベートゲートウェイに接続されている場合、集約されたスループット制限が適用されます。 GCPの場合は3Gbps(上りと下りの合計)が性能上限とのことです。 Cloud VPN - 割り当てと上限 https://cloud.google.com/network-connectivity/docs/vpn/quotas?hl=ja#limits 3. AWSのVPC内のネットワーク作成 「Subnets」を見るとデフォルトで3つのセグメントが作られていたので、ここでは特に設定しません&手順は省略します。 4. 仮想プライベートゲートウェイ(VGW)の作成 (1) 「Virtual Private Gateways」を選び、「Create Virtual Private Gateway」を選択します。 (2) VGWのパラメーターを入力 「Name tag」に任意の文字列を入力します。 「ASN」はCustom ASNを選択し、値は既存の他のVPNやDirect Connectで使っていない値を入力します。 16ビットASNは64512~65534のレンジで指定。 32ビットASNは4200000000~4294967294のレンジで指定。 (3) 作成結果を確認 5. VPCに仮想プライベートゲートウェイ(VGW)をアタッチ (1) アタッチ前のデタッチ この環境は直前までAWSのDirect Connectを使ってVPCのサブネットとオンプレミスで通信を行っていました。以下の絵にあるように、すでに1つVGWがアタッチされているため、別のVGWはアタッチできません。使わないものを先にデタッチします。 (2) VPCにVGWをアタッチ 作成したVPN用のVGWを選択し、画面上部の「Actions」から「Attach to VPC」を選択。 VPCをプルダウンメニューから選択し。「Yes, Attach」を選択。 「attaching」というステータスが「attached」になるまで少しかかるので、次の作業に進みます。 6. カスタマーゲートウェイの作成 (1) 「Customer Gateways」→「Create Customer Gateway」を選択。 (2) カスタマーゲートウェイの名前を入力し、Routingを「Static」、「IP Address」にオンプレ環境のルータのグローバルIPを入力し、「Create Customer Gateway」を選択。 (3) 作成が成功することを確認 7. VPNコネクションの作成 (1) 事前チェック - VGWをVPCにアタッチした結果の確認 VPNコネクションを作成する前に、5.の手順でVGW(Virtual Private Gateway)をVPCにアタッチする作業を行いましたが、それが成功しているか確認します。 (2) 「Site-toSite VPN Connections」→ 「Create VPN Connection」を選択 (3) 各種設定を入力します。 設定項目 値 備考 Name tag 任意の文字列 Target Gateway Type Virtual Private Gateway Virtual Private Gateway 作成したものをプルダウンから選択。 Customer Gateway Existing Customer Gateway ID 作成したものをプルダウンから選択。 Routing Options Dynamic (require BGP) Tunnel Inside Ip Version IPv4 Local IPv4 Network Cidr 192.168.65.0/24 オンプレ側のルータのグローバルIP Remote IPv4 Network Cidr 172.31.0.0/16 AWS側のサブネット その他「Tunnel Options」はそのままで、「Create VPN Connection」を選択。 成功したことを確認。 8. オンプレ側ルータ設定 (1) 直前の手順で「Close」を押下すると以下の画面になるので、「Download Configuration」を選択。 ※以下の画面で「State」が「pending」になってますが、そのまま続行します。 (2) ポップアップ画面でルータ機種を選択し、Configをダウンロード (3) ルータ側にConfigを投入します。 ※ 詳細省略 9. VPCのルートテーブル設定変更 ここまでの手順がうまくいっていると、VPNコネクションの「State」も「available」になりますが、オンプレとAWSはまだ通信ができません。 (1)「Your VPCs」→「Main route table」のルートテーブルをクリックします。 (2) ルートテーブルが選択されているので、「Route Propagation」→ 「Edit route propagation」を選択。 (3) 「Propagate」をチェック → 「Save」 10. VPCのFirewall設定変更 (1) 「Security Group」 → 「Edit inbound rules」 (2) 「Add rule」でオンプレミス環境からの通信を許可する設定を入れて、「Save rules」で保存。 ここまで終わると、オンプレからpingが通るようになります。 11. VPNのトンネル状態確認とルーティングテーブル確認 (1) トンネル状態の確認 「Site-to-Site VPN Connection」 → VPNコネクションを選択 → 「Tunnel Details」タブを選択 → 「Status」がUPとなっていることを確認・ (2) ルーティングテーブル確認 「Route Tables」 → ルートテーブルを選択 → 「Routers」タブを選択 → 下段にオンプレ側のサブネットが表示され、仮想プライベートゲートウェイ(VGW)がTargetになっていることを確認。 12. 関連情報 (追記中)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デフォルト画像を設定する方法

はじめに 画像が登録されてない場合のデフォルト画像を設置する方法についてまとめていきます。 今回は、CarrierWave、asset pipelineを使用したやり方になります。 後半では、The asset "" is not present in the asset pipelineのエラーについて少し触れていきます。 CarrierWaveでデフォルト画像を定義 [app/uploaders/image_uploder.rb] このように前もってデフォルト画像を設置するためのメソッドがコメントアウトして用意されているので、必要な部分を解除する。 # def default_url(*args) # # For Rails 3.1+ asset pipeline compatibility: # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.jpeg"].compact.join('_')) # # "/images/fallback/" + [version_name, "default.png"].compact.join('_') # end 解除する場所。 def default_url(*args) ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.jpeg"].compact.join('_')) end "default.jpeg"の部分が、デフォルト画像のパスになります。 なので、デフォルトの画像のパスを統一してあげる作業が必要になります。 デフォルト画像の用意 実際に使用するデフォルトの画像をapp/assets/images/の配下に保存します。 先ほどもいったように保存するときは、"default.jpeg"の名前、形式を統一する必要があります。 ビューで呼び出し <%= image_tag '/assets/default.jpeg',:alt => 'ユーザーアイコン' %> 'default.jpeg'でもok 注意点としては、 ・/assets/images/...とimagesを指定しないこと。 imagesを使用すると以下のような表示になる。 また、logではエラーが吐かれている。 ActionController::RoutingError (No route matches [GET] "/assets/images/default.jpeg"): まとめ 簡単ではありますが、デフォルト画像の設置方法についてまとめてみました。 少しでも参考になれば嬉しいです。 補足(The asset "" is not present in the asset pipelineエラーについて) アセットパイプラインが存在しないと出てしまう原因。 結論 config/envitonments/production.rbファイルの config.assets.compile = true falseをtrueにしてあげる アセットアイプラインへのアクセスを許可するみたいなイメージだと思います。 参考資料 CarrierWaveでデフォルト画像の設定 アセットパイプライン、img_tagの使い方について記載されています。 Railsガイド The asset "" is not present in the asset pipeline GitHub/carrierwave アセット使用しない方法も記載されています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【試験合格記】AWS 認定ソリューションアーキテクト – プロフェッショナル(SAP-C01)

お疲れさまです。 表題の試験に合格したため記録として残したいと思います。 結果 合格(895点) 分野 名称 割合 1 組織の複雑さに対応する設計 12.5% 2 新しいソリューションの設計 31% 3 移行の計画 15% 4 コスト管理 12.5% 5 既存のソリューションの継続的な改善 29% 所有資格 資格名 取得年月日 AWS Certified Solutions Architect - Associate (SAA) 2018-06-14 AWS Certified SysOps Administrator - Associate (SOA) 2018-06-22 AWS Certified Developer - Associate (DVA) 2018-06-25 AWS Certified DevOps Engineer - Professional (DOP) 2020-02-19 AWS Certified Big Data - Speciality (BDS) 2020-03-23 AWS Certified Security - Speciality (SCS) 2020-04-01 AWS Certified Machine Learning - Speciality (MLS) 2020-04-13 AWS Certified Advanced Networking - Speciality (ANS) 2020-04-27 AWS Certified Cloud Practitioner (CLF) 2020-04-27 AWS Certified Database - Specialty (DBS) 2020-09-22 AWS Certified Alexa Skill Builder (AXS) 2020-12-06 AWS Certified Solutions Architect - Professional (SAP) 2021-04-18 所感 2018年に受験したときと比べて今時のAWSサービスがいくらか追加されてるなーと感じました。 全体的にはハイブリッドクラウドやAWSへの移行、Organizationレベルの適切な管理などがおおよそを占めており、幅広い出題範囲への対処が要求されるところは変わらないと思います。 受験者に向けたアドバイス AWS Organizationのマスターアカウントと配下アカウントの関係性を理解する SCPやアクセス権限の境界で出来ることを把握する 災害復旧(DR)を計画するにあたって起点となるリソースや設定の違いを理解する RDSやDynamoDBの特性を正しく理解する DirectConnect、VPN、VPC Peering、Transit Gatewayなどネットワークリソースの特徴およびユースケースを学ぶ セキュリティの最適化にNACL、Security Group、WAFなどをどのレイヤーに設置すべきか判断できるようにする EC2、Elastic Beanstalk、Lambda、ECSなどコンピュートリソースの特徴と応用的な使い方を理解する おわりに SAPはまだ大きな改定はされておらず、マイナーアップデートされ続けている試験という印象があります。 今の時点でもSAPの試験範囲に含まれる対象のAWSサービスは非常に多く存在しているため、初取得を目指す方は改定されないうちに頑張るとよいのではないでしょうか。 再学習して久しぶりに気付いたのですが、やはりAWS試験の最難関は間違いなくこのSAPだと思います。 学習範囲と予測の難しさは脳死で学習支援サイトの問題を解いても身につかないのは間違いないでしょう。 地道に日々AWSと戯れて各サービスのベストプラクティスを理解するように心がけていく必要があると再認識しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】【Azure】Azure側のVirtual Machinesに、Amazon CloudWatch Agentをインストールし、AWS側でメトリクスとログを監視してみました

はじめに 直近では、Azure側の Virtual Machines に、メトリクスとログを監視する必要があるという要望がありました。 いろんな案を考案した結果、最後はAzure側の Virtual Machines に、Amazon CloudWatch Agent をインストールし、AWS側でメトリクスとログを監視することになりました。(笑)Azureには申し訳ないですけど。。。 なお、Azure側で使っている Virtual Machines のOSタイプは、Red Hat Enterprise Linux 7です。 構成イメージ図は下記となります。 解決案の選別 最初は三つの案を考案していました。 1.Azure Monitor エージェント (もしくは Log Analytics エージェント)をインストールして監視 2.Zabbix Agentをインストールし、Zabbix Managerサーバーによる監視(※別途Zabbix Managerサーバーを作成する必要があります。) 3.Amazon CloudWatch Agentをインストールし、AWS側にて監視 ・案1: Azure環境での監視設定は、もちろん一番実施しやすいのは、Azure側に監視サービスを使用することに間違いがありません。Azure Monitorから、仮想マシンのCPU、メモリ等は直接監視することはできますが、ログ監視を実現するには、エージェントのインストールが必要とされます。 ただし、Azure Monitor エージェントとLog Analytics エージェントはなんと両方ともRed Hat Enterprise Linux Server 7をサポートしていません。 2021/4/20訂正: 出典URLの表を読み違えていました。「X」はバツ(サポートしていない)ではなく、チェック記号(サポートしている)のことです。 Azure Monitor エージェントとLog Analytics エージェントは両方ともRed Hat Enterprise Linux Server 7をサポートしています。 これで、案1はベストプラクティスになるので、今後試してみます! (@matarillo さん、@normalian さん、ご指摘ありがとうございました。) 出典URL: https://docs.microsoft.com/ja-jp/azure/azure-monitor/agents/agents-overview#linux ・案2: Zabbix Agentをインストールして、ログ監視やメトリクス監視は実現できそうです。 ただし、Zabbix Managerサーバーを作成しなくてはなりません。 費用面と設定手間から見れば、時間と料金がかかり、ベストプラクティスな案とは言えません。なので、当案は保留することとなりました。 ・案3: CloudWatch Agent はサーバーログを監視できるほか、メトリクスも監視できます。 メトリクスには、3種類のメトリクスレベル(Basic、Standard、Advanced)から選べられます。 詳細は下記のURLにご参照下さい。 https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/create-cloudwatch-agent-configuration-file-wizard.html CloudWatch Agent を Azure側の Virtual Machinesにインストールする発想は、多分なかなかそう考えている人がいないのではとは思いますが、実は技術的から言えば実現できます。方法はオンプレミス環境のサーバーに、CloudWatch Agent を実装することと同じです。 AWS側のCloudWatch Agentで実現できれば、Zabbix 監視手法より、ある意味の「サーバーレス監視手法」ではないでしょうか(笑) AWSとAzureを跨いてメトリクスとログデータ転送するので、もちろん転送中にセキュリティを懸念する方もいらっしゃると思います。実はメトリクスとログは転送中に暗号化されています。 CloudWatch Logs は、転送中のデータのエンドツーエンドの暗号化を使用します。CloudWatch Logs サービスは、サーバー側の暗号化キーを管理します。 出典URL:https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/data-protection.html 暗号化だけではご安心できない方は、別のセキュリティ方式をご検討下さい。 当案の設定はそこまで複雑ではなく、手軽に実施できます。 1.AWS側でIAMユーザーに、アクセスキーとシークレットキーを発行 AWSのIAMサービスで、ユーザーを作成し、下記のロールを付与しました。 ・CloudWatchAgentAdminPolicy ・AmazonSSMFullAccess ※今回は検証のため、SSMの権限を大きくしました。実際構築する際に、AWS最小権限原則をお忘れなく!なお、直近CloudTrail イベントに基づいてポリシーを生成できるようになりましたので、AWS最小権限に悩む方はぜひご利用下さい! IAMユーザーのアクセスキー、シークレットキーを発行します。 「アクセスキーの作成」ボタンを押していただきますと作成できますので、作成後にCSVファイルをダウンロードしてください。 2.Azure側の Virtual Machines に、Amazon CloudWatch Agentをインストールします。 Azure側に Virtual Machines は作成してあります。 (Virtual Machines の作成方法はこちらで割愛させていただきます) Puttyでログインし、EPELリポジトリをインストールします。OSに応じてインストールしておきましょう。 URL:https://aws.amazon.com/jp/premiumsupport/knowledge-center/ec2-enable-epel/ $ sudo yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 次にAWS CLIをインストールします。OSに応じてインストールしておきましょう。 URL:https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-linux.html#cliv2-linux-install $ sudo curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" $ unzip awscliv2.zip $ sudo ./aws/install collectdをインストールしておきます。 $ sudo yum install collectd IAMユーザーのアクセスキー、シークレットキーを実装します。 $ sudo aws configure --profile AmazonCloudWatchAgent AWS Access Key ID [None]: <アクセスキー> AWS Secret Access Key [None]: <シークレットキー> Default region name [None]: ap-northeast-1 Default output format [None]: json CloudWatch Agentをダウンロードします。OSに応じてインストールしておきましょう。 URL:https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/download-cloudwatch-agent-commandline.html $ wget https://s3.amazonaws.com/amazoncloudwatch-agent/redhat/amd64/latest/amazon-cloudwatch-agent.rpm $ sudo yum install ./amazon-cloudwatch-agent.rpm CloudWatch Agentをインストールします。 $ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard 私はCloudWatch Agentのメトリクスを、「Advanced」レベルを選択しました。 また、監視するログとしては「/var/log/messages」と、自ら作ったログ「/tmp/lalala.log」にしました。 ============================================================= = Welcome to the AWS CloudWatch Agent Configuration Manager = ============================================================= On which OS are you planning to use the agent? 1. linux 2. windows 3. darwin default choice: [1]: Trying to fetch the default region based on ec2 metadata... Are you using EC2 or On-Premises hosts? 1. EC2 2. On-Premises default choice: [2]: Please make sure the credentials and region set correctly on your hosts. Refer to http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html Which user are you planning to run the agent? 1. root 2. cwagent 3. others default choice: [1]: Do you want to turn on StatsD daemon? 1. yes 2. no default choice: [1]: Which port do you want StatsD daemon to listen to? default choice: [8125] What is the collect interval for StatsD daemon? 1. 10s 2. 30s 3. 60s default choice: [1]: What is the aggregation interval for metrics collected by StatsD daemon? 1. Do not aggregate 2. 10s 3. 30s 4. 60s default choice: [4]: Do you want to monitor metrics from CollectD? 1. yes 2. no default choice: [1]: Do you want to monitor any host metrics? e.g. CPU, memory, etc. 1. yes 2. no default choice: [1]: Do you want to monitor cpu metrics per core? Additional CloudWatch charges may apply. 1. yes 2. no default choice: [1]: Would you like to collect your metrics at high resolution (sub-minute resolution)? This enables sub-minute resolution for all metrics, but you can customize for specific metrics in the output json file. 1. 1s 2. 10s 3. 30s 4. 60s default choice: [4]: Which default metrics config do you want? 1. Basic 2. Standard 3. Advanced 4. None default choice: [1]: 3 Current config as follows: { "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, "metrics": { "metrics_collected": { "collectd": { "metrics_aggregation_interval": 60 }, "cpu": { "measurement": [ "cpu_usage_idle", "cpu_usage_iowait", "cpu_usage_steal", "cpu_usage_guest", "cpu_usage_user", "cpu_usage_system" ], "metrics_collection_interval": 60, "resources": [ "*" ], "totalcpu": true }, "disk": { "measurement": [ "used_percent" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "diskio": { "measurement": [ "io_time", "write_bytes", "read_bytes", "writes", "reads" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "mem": { "measurement": [ "mem_used_percent" ], "metrics_collection_interval": 60 }, "net": { "measurement": [ "bytes_sent", "bytes_recv", "packets_sent", "packets_recv" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "netstat": { "measurement": [ "tcp_established", "tcp_time_wait" ], "metrics_collection_interval": 60 }, "statsd": { "metrics_aggregation_interval": 60, "metrics_collection_interval": 10, "service_address": ":8125" }, "swap": { "measurement": [ "swap_used_percent" ], "metrics_collection_interval": 60 } } } } Are you satisfied with the above config? Note: it can be manually customized after the wizard completes to add additional items. 1. yes 2. no default choice: [1]: Do you have any existing CloudWatch Log Agent (http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html) configuration file to import for migration? 1. yes 2. no default choice: [2]: Do you want to monitor any log files? 1. yes 2. no default choice: [1]: Log file path: /var/log/messages Log group name: default choice: [messages] azure-linux-log Log stream name: default choice: [{hostname}] azure-log Do you want to specify any additional log files to monitor? 1. yes 2. no default choice: [1]: Log file path: /tmp/lalala.log Log group name: default choice: [lalala] azure-linux-lalala-log Log stream name: default choice: [{hostname}] azure-lalala Do you want to specify any additional log files to monitor? 1. yes 2. no default choice: [1]: 2 Saved config file to /opt/aws/amazon-cloudwatch-agent/bin/config.json successfully. Current config as follows: { "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, "logs": { "logs_collected": { "files": { "collect_list": [ { "file_path": "/var/log/messages", "log_group_name": "azure-linux-log", "log_stream_name": "azure-log" }, { "file_path": "/tmp/lalala.log", "log_group_name": "azure-linux-lalala-log", "log_stream_name": "azure-lalala" } ] } } }, "metrics": { "metrics_collected": { "collectd": { "metrics_aggregation_interval": 60 }, "cpu": { "measurement": [ "cpu_usage_idle", "cpu_usage_iowait", "cpu_usage_steal", "cpu_usage_guest", "cpu_usage_user", "cpu_usage_system" ], "metrics_collection_interval": 60, "resources": [ "*" ], "totalcpu": true }, "disk": { "measurement": [ "used_percent" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "diskio": { "measurement": [ "io_time", "write_bytes", "read_bytes", "writes", "reads" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "mem": { "measurement": [ "mem_used_percent" ], "metrics_collection_interval": 60 }, "net": { "measurement": [ "bytes_sent", "bytes_recv", "packets_sent", "packets_recv" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "netstat": { "measurement": [ "tcp_established", "tcp_time_wait" ], "metrics_collection_interval": 60 }, "statsd": { "metrics_aggregation_interval": 60, "metrics_collection_interval": 10, "service_address": ":8125" }, "swap": { "measurement": [ "swap_used_percent" ], "metrics_collection_interval": 60 } } } } Please check the above content of the config. The config file is also located at /opt/aws/amazon-cloudwatch-agent/bin/config.json. Edit it manually if needed. Do you want to store the config in the SSM parameter store? 1. yes 2. no default choice: [1]: 1 What parameter store name do you want to use to store your config? (Use 'AmazonCloudWatch-' prefix if you use our managed AWS policy) default choice: [AmazonCloudWatch-linux] Azure-Linux-Test Which region do you want to store the config in the parameter store? default choice: [ap-northeast-1] Which AWS credential should be used to send json config to parameter store? 1. <アクセスキー>(From Profile: AmazonCloudWatchAgent) 2. Other default choice: [1]: 1 Successfully put config to parameter store Azure-Linux-Test. Program exits now. 続いてAWS Systems Manager⇒パラメーターストアに、上記定義したパラメーターストアが反映できたかどうかを確認します。うまく反映できています! Systems ManagerのRun Commandを実行し、CloudWatch Agentを有効化します。 Azureのサーバー側に、下記のコマンドを実行します。 「ssm:<パラメーターストアの名前>」に、上記定義したパラメーターストアの名前を入れてください。 $ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m onPremise -c ssm:<パラメーターストアの名前> -s $ sudo /opt/aws/amazon-cloudwatch-agent/bin/config-downloader --output-dir /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d --download-source ssm:<パラメーターストアの名前> --mode onPrem --config /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml --multi-config default CloudWatch Agentのステータスを確認します。 ActiveであればOKです。 $ systemctl status amazon-cloudwatch-agent 3.AWS側でメトリクスとログを確認します。 まずはメトリクスを確認します。確かにメトリクスが反映できました!! ログを確認します。確かにロググループが反映できました!! 続いて試しに、Azure側Virtual Machinesで、先ほど作った「lalala.log」に、適当な文言を加えます。 $ echo sdfsdfsdfsdfsfsdfsaaaaaaa111111111111111 > /tmp/lalala.log $ echo dadada123123 > /tmp/lalala.log 上記加えた文言はすぐCloudWatch上で反映することが出来ました!! 最後に「/var/log/messages」ログも確認しておきます。確かに反映できました!! 最後に Azure側のVirtual Machinesに、AWSのCloudWatch Agentをインストールして、AWS側で監視する発想は、なかなかないのではないかと思います(笑)。 実は思ったより、AWSのCloudWatch Agentをオンプレミス環境にインストールすることと一緒です。 思考回転すれば、いろんな応用ケースは考えられるではないでしょうか? 今回はZabbix Managerサーバーをわざわざ立てる必要なく、ある意味では「サーバーレス」ですね(笑)。 本記事はだれかの役に立てればうれしいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】仮想化を理解する

プログラミング勉強日記 2021年4月19日 クラウドと仮想化  クラウドは、仮想化技術によって成り立ってるサービスのこと。仮想化は、サーバー、CPU、メモリといった物理的なインフラの構成を抽象化して、物理的な制限にとらわれることなくソフトウェア的に統合・分割できるようにする技術のこと。  図のようにサーバーの中に複数のサーバーを仮想化して作ることでインフラとしては物理的な1つのサーバーだが、それを分けることができる。 仮想化の仕組み  物理的なインフラに仮想化ソフトウェアを設定して、実質的な機能をユーザごとに切り分けて提供できる。  ①物理的なサーバーを仮想化したい。(容量が大きすぎて1つのシステムではもったいないときなど)  ②1つの物理的なサーバーを仮想化するためには、サーバーの中に仮想化ソフトウェアをインストールしてセットアップする必要がある。仮想化ソフトウェアでは、VMware型とハイパーバイザー型の2タイプがよく使われる。  ③個々のユーザはそれぞれが単体のサーバーのように利用できる。具体的には、別のOSを使用することができる。 仮想化の対象  サーバー・ストレージ・ネットワーク・デスクトップを仮想化することができる。  サーバーの仮想化は、1代の物理的なサーバー上に複数のOSを動作させる技術。ストレージの仮想化は複数のストレージを仮想的に統合して1つの大きなストレージプールを構成する技術。ネットワークの仮想化は、新しい仮想ネットワークの構築や制御をソフトウェアによって動的に行う技術。デスクトップの仮想化は、サーバー上においたPC環境のデスクトップ画面を遠隔地にある接続端末に転送する技術。  デスクトップの仮想化は主に、セキュリティを高めるために使われる。PCにすべての情報が入っているとPCを紛失したときにセキュリティのリスクが高い。そのため、PCにはセットアップされていなくてインターネットに接続できるようにする共有するだけの機能を使って、仮想化デスクトップとして遠隔地の端末にデスクトップをデータセンターから映し出すといった中央管理されたデスクトップの提供ができる。AWSではAWS WorkSpacesといったサービスで仮想化デスクトップを提供している。 仮想化のメリット  仮想化を利用することで、インフラ利用の効率性や柔軟性が向上する。 サーバースペースの削減 データセンターの費用の削減 セキュリティの向上 効率的にサーバーを利用できる 構成変更やメンテナンス対応の柔軟性
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Transit Gateway ConnectをFortiGate-VMとの組み合わせで構築してみた

はじめに AWS Transit GatewayとSD-WANを簡単に接続することができる「AWS Transit Gateway Connect」を触ってみました。アプライアンス(SD-WAN)側はFortiGate-VMを使用しました。 なお、AWS側の設定は、以下のドキュメントを参考にしています。 AWS Transit Gateway Connect 詳しくは上記リンク先通りですが、各社SD-WANとTransit Gatewayを簡単に接続できるようにしたものです。 SD-WAN側はVPCへ仮想アプライアンスを設置し、仮想アプライアンスとTransit Gatewayを今回の機能であるConnectアタッチメントを使用して接続します。 仮想アプライアンスとTransit Gateway間は、IPSecではなく、GREを使用します。ルーティングプロトコルはBGPのみサポートします。 FortiGate-VM Firewall製品で有名なFortinet社ForiGateのVM版となります。AWSではMarketPlaceを通じて利用することができます。課金は従量制とBYOLが用意されています。 今回使用するものは以下となります。 環境 構成 メインは黄色枠の「Transit Gateway Connect」ですが、FortiGate間のVPNを絡めた、一般的であろう構成にしてみました。 構築後、ServerA,B~ServerC間でPingによる疎通ができることが確認できればOKとします。 ※FortiGate-VMのPublic addressは構築中にElastic IPを取得するため、ここでは未確定としています。 バージョン等 FortiGate-VM: 7.0.0 Server A,B,C: Amazon Linux 2 構築 VPC,Transit Gateway,EC2の作成 ※Transit Gateway Connect周り, FortiGate-VM除く 本題では無いため、CloudFormationで一気に作成します。 ※Transit Gateway Connect関連とFortiGate-VMは後ほど手動で構築します。 ※構築後に稼働確認を行うため、各ServerへSession Managerを使用して接続できるようにCloudFormationへ必要な設定を組み込んでおきます。 ※AWS CLIを使用するため、事前にセットアップしておきます。 バージニア CloudFormationテンプレートは最後の補足を参照。 aws cloudformation validate-template --template-body file://010-UsVpcEc2.yml aws cloudformation deploy --stack-name handson-tgw-connect --template-file 010-UsVpcEc2.yml --region us-east-1 --capabilities CAPABILITY_NAMED_IAM 東京 CloudFormationテンプレートは最後の補足を参照。 aws cloudformation validate-template --template-body file://020-JpVpcEc2.yml aws cloudformation deploy --stack-name handson-tgw-connect --template-file 020-JpVpcEc2.yml --region ap-northeast-1 --capabilities CAPABILITY_NAMED_IAM (バージニア)Transit GatewayへCIDR(192.0.2.0/24)の割り当て Transit Gateway Connectは、割り当てたCIDRからGREで使用するアドレスを割り当てます。 [VPC]-[TRANSIT GATEWAY]-[Transit Gateway]から上記で作成済みのTGWを選択→[Modify]→[Transit Gateway CIDR blocks]の順で進み「192.0.2.0/24」を追加します。 (バージニア)各VPCへTransit Gateway向きのルートの追加 [VIRTUAL PRIVATE CLOUD]-[ルートテーブル]から、以下のように各VPCのルートテーブルへ上記で作成済みのTGWをターゲットとしたルートを追加します。 VPC A 上記で作成済みのTGWをターゲットとしたデフォルトルートを追加します。 VPC B 上記で作成済みのTGWをターゲットとしたデフォルトルートを追加します。 Connect VPC 上記で作成済みのTGWをターゲットとしたVPC A,B及び「192.0.2.0/24」宛てのルートを追加します。 (バージニア)Transit Gateway Connectの作成 [VPC]-[TRANSIT GATEWAY]-[Transit Gateway接続]から[Create Transit Gateway Attachment]をクリックし以下のように設定します。 項目 設定内容 Transit Gateway ID 上記で作成済みのTGW Attachment type Connect Attachment name tag 任意 Transport Attachment ID tgw_handson_us-connectvpc ※FortiGate-VMが存在するVPC [VPC]-[TRANSIT GATEWAY]-[Transit Gateway接続]から上記のConnect Attachmentを選択し、[Connect peers]から[Create Connect peer]をクリックします。 Connect Peerは以下のように設定します。 ※「Transit Gateway GRE address」は未入力の場合、自動でアサインされます。 ※BGPアドレスは「BGP inside CIDR blocks」から3つ自動でアサインされます。  挙動としては、Peer GRE address、Transit Gateway BGP 1 address、Transit Gateway BGP 2 addressの順にアサインされるようです。 (バージニア)FortiGate-VMの作成 [EC2]-[インスタンス]-[インスタンス]から[インスタンスを起動]をクリックします。 [AWS Marketplace]を選択し"fortigate"を検索後「Fortinet FortiGate Next-Generation Firewall」を選択します。 [Continue]をクリックします。 「ステップ 2: インスタンスタイプの選択」では、[t2.small]を選択し、次へ進みます。 「ステップ 3: インスタンスの詳細の設定」では、[ネットワーク]は「tgw_handson_us-connectvpc」を選択、[ネットワークインターフェース]-[eth0]-[プライマリIP]は「172.31.0.11」と入力し、次へ進みます。 ※上記以外はデフォルトのままで問題ありません。 「ステップ4:ストレージの追加」は何もせず、次へ進みます。 「ステップ 5: タグの追加」では、キーと値へ識別し易い名称を入力し、次へ進みます。 「ステップ 6: セキュリティグループの設定」では、以下のように設定し、[確認と作成]へ進みます。 操作 タイプ プロトコル ポート範囲 ソース 補足 変更 SSH TCP 22 管理者オフィスのIP 変更 HTTP TCP 80 管理者オフィスのIP 変更 HTTPS TCP 443 管理者オフィスのIP 削除 カスタムTCP TCP 541 0.0.0.0/0 今回未使用 削除 カスタムTCP TCP 3000 0.0.0.0/0 今回未使用 削除 カスタムTCP TCP 8080 0.0.0.0/0 今回未使用 新規 カスタムプロトコル 47 all 0.0.0.0/0 TGW ConnectとのGRE 新規 カスタムプロトコル 50 all 0.0.0.0/0 東京のFortiGate-VMとのIPSecVPN - ESP 新規 カスタムUDP UDP 500 0.0.0.0/0 東京のFortiGate-VMとのIPSecVPN - IKE 新規 カスタムUDP UDP 4500 0.0.0.0/0 東京のFortiGate-VMとのIPSecVPN - NAT-T 新規 すべてのICMP ICMP 0 - 65535 10.0.0.0/8 疎通確認用 [起動]をクリックします。キーペアは[キーペアなしで続行]とし、[インスタンスの作成]をクリックします。 [EC2]-[ネットワーク&セキュリティ]-[Elastic IP]から「Elastic IPアドレスの割り当て」を行い、上記のFortiGate-VMへ関連付けを行います。 ブラウザから「https://FortiGate-VMのパブリックIP」もしくは「https://パブリックDNS」へアクセスします。ブラウザによっては(自己署名のため)「潜在的なセキュリティリスク」と表示しますが、そのまま続行します。 [Accept]をクリックします。 [Username]は「admin」、「Password」は「インスタンスID」を入力し[Login]をクリックします。 [Old Password]は「インスタンスID」、「New Password」「Confirm Password」は「任意のパスワード」を入力し[OK]をクリックします。 再度、認証画面が表示するため、「admin」「任意のパスワード」を入力します。 [Begin]をクリックします。 [Hostname]へ「FortiGate-VM-US」を入力し、[OK]をクリックします。 そのまま[OK]をクリックします。 [Don't show again]へチェックを入れ、[OK]をクリックします。 GREの設定を行います。CLIからのみ設定が可能です。 右上の「>_」をクリックし、CLI Consoleを開きます。 GREトンネル及びTunnelインタフェースの設定を行います。 config system gre-tunnel edit "tunnel1" set interface port1 set local-gw 172.31.0.11 set remote-gw 192.0.2.1 next end config system interface edit "tunnel1" set ip 169.254.10.1 255.255.255.255 set allowaccess ping set type tunnel set remote-ip 169.254.10.2 255.255.255.255 set interface port1 next end 設定後、右上の[x]をクリックし、CLI Consoleを閉じます。 [Network]-[Interfaces]から「tunnel1」が作成されていることを確認します。 BGPの設定を行います。 東京のサブネットをTGWへアドバタイズするためにASパスプリペンドの設定を行います。 ASパスプリペンドの設定が無いと、TGW側でサブネット情報を受信してもTGWのルートテーブルへ反映しないようです。 [Network]-[Routing Objects]から[Create New]-[Prefix List]の順に選択します。 Prefix Listは以下のように設定します。 同様に[Create New]-[Route Map]の順に選択します。 Route Mapは以下のように設定します。 [Match IP address]は上記Prefix Listを選択、[Set AS path]は「99」を入力。 [Network]-[BGP]から以下のように設定します。 「Local AS」「RouterID」「Neighbors」「Networks」を設定します。 [Neighbors]は「IP:169.254.10.2」「Remote AS:64512」「Route map out:as-path-prepend-route-map」を設定します。 (補足) 上記設定後に再度何らかの理由でRouteMapを変更した場合は、設定を反映させるためにCLI ConsoleからBGPハードクリアを行います。 execute router clear bgp all BGPの状態を確認します。 Next Hopに「169.254.10.2」が表示していればTransit Gateway ConnectとBGPのコネクションが確立できています。 # get router info bgp network VRF 0 BGP table version is 2, local router ID is 169.254.10.1 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, S Stale Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight RouteTag Path *>i10.1.0.0/16 169.254.10.2 100 100 0 0 i <-/1> *>i10.2.0.0/16 169.254.10.2 100 100 0 0 i <-/1> *> 10.99.0.0/16 0.0.0.0 100 32768 0 i <-/1> *>i172.31.0.0 169.254.10.2 100 100 0 0 i <-/1> Total number of prefixes 4 (バージニア)確認1 [VPC]-[TRANSIT GATEWAY]-[Transit Gateway接続]から"tgw-connect01"を選択し、[Connect peers]から上記で作成したConnect peerから[Transit Gateway BGP 1 Status]が「UP」であることを確認します。 ※「Transit Gateway BGP 2 Status」はFortiGate-VM側で設定していないため「DOWN」のままで問題ありません。 [VPC]-[TRANSIT GATEWAY]-[Transit Gatewayルートテーブル]から今回作成したルートテーブルを選択し、[Routes]をクリックします。 「10.99.0.0/16」のルートがあることを確認します。FortiGate-VMからBGPでルートを正常に受信できていることが分かります。 (バージニア)VPCへFortiGate-VM宛てのルートの追加 [VIRTUAL PRIVATE CLOUD]-[ルートテーブル]から「tgw_handson_us-connectvpc-public」を選択し、ルートを追加します。 上記で作成したFortiGate-VMをターゲットとした東京宛てのルートを追加します。 (バージニア)FortiGate-VMの「送信元/送信先チェック」の無効化 [EC2]-[インスタンス]-[インスタンス]から「FortiGate-VM-US」を選択し、右クリック[ネットワーキング]-[ソース/宛先チェックを変更]をクリックします。「送信元/送信先チェック中」の「停止」へチェックを入れ[保存]をクリックします。 (東京)FortiGate-VMの作成 上述の「(バージニア)FortiGate-VMの作成」を参照し、ForiGate-VMを作成します。 以降は(バージニア)と異なる箇所のみ記載します。 「ステップ 3: インスタンスの詳細の設定」では、[ネットワーク]は「tgw_handson_jp」を選択、[ネットワークインターフェース]-[eth0]-[プライマリIP]は「10.99.1.11」と入力し、次へ進みます。 ※上記以外はデフォルトのままで問題ありません。 「ステップ 5: タグの追加」では、キーと値へ識別し易い名称を入力し、次へ進みます。 「ステップ 6: セキュリティグループの設定」では、以下のように設定し、[確認と作成]へ進みます。 操作 タイプ プロトコル ポート範囲 ソース 補足 変更 SSH TCP 22 管理者オフィスのIP 変更 HTTP TCP 80 管理者オフィスのIP 変更 HTTPS TCP 443 管理者オフィスのIP 削除 カスタムTCP TCP 541 0.0.0.0/0 今回未使用 削除 カスタムTCP TCP 3000 0.0.0.0/0 今回未使用 削除 カスタムTCP TCP 8080 0.0.0.0/0 今回未使用 新規 カスタムプロトコル 50 all 0.0.0.0/0 東京のFortiGate-VMとのIPSecVPN - ESP 新規 カスタムUDP UDP 500 0.0.0.0/0 東京のFortiGate-VMとのIPSecVPN - IKE 新規 カスタムUDP UDP 4500 0.0.0.0/0 東京のFortiGate-VMとのIPSecVPN - NAT-T 新規 すべてのICMP ICMP 0 - 65535 10.0.0.0/8 疎通確認用 [起動]をクリックします。キーペアは[キーペアなしで続行]とし、[インスタンスの作成]をクリックします。 [EC2]-[ネットワーク&セキュリティ]-[Elastic IP]から「Elastic IPアドレスの割り当て」を行い、上記のFortiGate-VMへ関連付けを行います。 ブラウザから「https://FortiGate-VMのパブリックIP」もしくは「https://パブリックDNS」へアクセスします。ブラウザによっては(自己署名のため)「潜在的なセキュリティリスク」と表示しますが、そのまま続行します。 上述の「(バージニア)FortiGate-VMの作成」を参照し、ログインします。 (バージニア,東京)FortiGate-VM間のIPSec VPNの作成 (東京)IPSec VPNを設定します。今回はウィザードで進めます。 [VPN]-[IPsec Wizard]から以下の順番で設定します。 [Remote IP address]は「FortiGate-VM-US」のEIPを入力します。 ここはFirewall Policyの設定情報となります。通信させたい送信元・宛先サブネットを指定します。 [Create]をクリックします。 [Show Tunnel List]をクリックします。 現時点ではVPNはInactiveとなります。 ※バージニア側のVPN設定が完了後、トラフィックが発生するとActiveとなります。 (バージニア)IPSec VPNを設定します。今回はウィザードで進めます。 [VPN]-[IPsec Wizard]から以下の順番で設定します。 [Remote IP address]は「FortiGate-VM-US」のEIPを入力します。 ここはFirewall Policyの設定情報となります。通信させたい送信元・宛先サブネットを指定します。 ※「10.1.0.0/24」「10.2.0.0/24」は「tunnel1(GRE)」インタフェースに関連するサブネットですが、本項目では「port1」しか選択できないため一旦仮で設定します。 [Create]をクリックします。 [Show Tunnel List]をクリックします。 現時点ではVPNはInactiveとなります。 ※トラフィックが発生するとActiveとなります。 [Policy & Objects]-[Firewall Policy]から「port1→vpn01」のポリシーを選択し[Edit]をクリックします。 [Incoming Interface]の「port1」を「tunnel1」へ変更します。 ※このとき[OK]をクリックすると「Comments」において「Invalid characters」となるため"("")"を削除します。 を へ変更。 同様に「vpn01→port01」のポリシーも[Edit]から「port1」を「tunnel1」へ変更します。 変更後のポリシー一覧は以下となります。 (東京)VPCへFortiGate-VM宛てのルートの追加 [VIRTUAL PRIVATE CLOUD]-[ルートテーブル]から「tgw_handson_jp-private」を選択し、ルートを追加します。 上記で作成したFortiGate-VMをターゲットとしたVPC A,B宛てのルートを追加します。 (東京)FortiGate-VMの「送信元/送信先チェック」の無効化 [EC2]-[インスタンス]-[インスタンス]から「FortiGate-VM-JP」を選択し、右クリック[ネットワーキング]-[ソース/宛先チェックを変更]をクリックします。「送信元/送信先チェック中」の「停止」へチェックを入れ[保存]をクリックします。 (バージニア,東京)確認2 バージニア、東京のそれぞれのServerから相互でPingの疎通ができることを確認します。 (東京)[EC2]-[インスタンス]-[インスタンス]から「tgw_handson_us-servera」を選択し「接続」をクリックします。「セッションマネージャー」で接続後、Pingを実行します。 # ServerA宛て ping 10.1.0.11 PING 10.1.0.11 (10.1.0.11) 56(84) bytes of data. 64 bytes from 10.1.0.11: icmp_seq=1 ttl=252 time=146 ms # ServerB宛て ping 10.2.0.11 PING 10.2.0.11 (10.2.0.11) 56(84) bytes of data. 64 bytes from 10.2.0.11: icmp_seq=1 ttl=252 time=147 ms (バージニア)[EC2]-[インスタンス]-[インスタンス]から「handson-tgw-connect」を選択し、同様にPingを実行します。 ping 10.99.0.11 PING 10.99.0.11 (10.99.0.11) 56(84) bytes of data. 64 bytes from 10.99.0.11: icmp_seq=1 ttl=252 time=146 ms (バージニア)[EC2]-[インスタンス]-[インスタンス]から「handson-tgw-connect」を選択し、同様にPingを実行します。 ping 10.99.0.11 PING 10.99.0.11 (10.99.0.11) 56(84) bytes of data. 64 bytes from 10.99.0.11: icmp_seq=1 ttl=252 time=149 ms まとめ AWS Transit Gateway ConnectとFortiGate-VMを接続しました。 従来はIPSecVPNで設定していましたが、Transit Gateway Connectを使用することでGREの設定になり若干ですが設定がシンプルとなりました。 なお、FortiGateのGREの設定は、CLIからとなりますので注意しましょう。 補足 バージニア分のCloudFormationテンプレート 020-JpVpcEc2.yml AWSTemplateFormatVersion: 2010-09-09 Description: TGW Connect handson in Virginia Parameters: ParamTag: Type: String Default: tgw_handson_us ParamVpcACidrBlock: Type: String Default: "10.1.0.0/16" ParamVpcBCidrBlock: Type: String Default: "10.2.0.0/16" ParamConnectVpcCidrBlock: Type: String Default: "172.31.0.0/16" ParamAMI: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Resources: ## VPC A VpcA: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref ParamVpcACidrBlock EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpca ] ] VpcAPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VpcA Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpca, private ] ] VpcAPrivateSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 0, !GetAZs ] VpcId: !Ref VpcA CidrBlock: !Select [ 0, !Cidr [ !Ref ParamVpcACidrBlock, 1, 8 ]] Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpca, private, a ] ] VpcAPrivateSubnetAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VpcAPrivateRouteTable SubnetId: !Ref VpcAPrivateSubnet ## VPC B VpcB: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref ParamVpcBCidrBlock EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcb ] ] VpcBPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VpcB Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcb, private ] ] VpcBPrivateSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 0, !GetAZs ] VpcId: !Ref VpcB CidrBlock: !Select [ 0, !Cidr [ !Ref ParamVpcBCidrBlock, 1, 8 ]] Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcb, private, a ] ] VpcBPrivateSubnetAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VpcBPrivateRouteTable SubnetId: !Ref VpcBPrivateSubnet ## Connect VPC ConnectVpc: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref ParamConnectVpcCidrBlock EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, connectvpc ] ] ConnectVpcIgw: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Ref ParamTag ConnectVpcAttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref ConnectVpc InternetGatewayId: !Ref ConnectVpcIgw ConnectVpcPublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref ConnectVpc Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, connectvpc, public ] ] ConnectVpcPublicRoute: DependsOn: ConnectVpcIgw Type: AWS::EC2::Route Properties: RouteTableId: !Ref ConnectVpcPublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref ConnectVpcIgw ConnectVpcPublicSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 0, !GetAZs ] VpcId: !Ref ConnectVpc CidrBlock: !Select [ 0, !Cidr [ !Ref ParamConnectVpcCidrBlock, 1, 8 ]] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, connectvpc, public, a ] ] ConnectVpcPublicSubnetAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref ConnectVpcPublicRouteTable SubnetId: !Ref ConnectVpcPublicSubnet # Transit Gateway Tgw: Type: AWS::EC2::TransitGateway Properties: AmazonSideAsn: 64512 Tags: - Key: Name Value: !Ref ParamTag TgwAttachmentToVpcA: Type: AWS::EC2::TransitGatewayAttachment Properties: SubnetIds: - !Ref VpcAPrivateSubnet TransitGatewayId: !Ref Tgw VpcId: !Ref VpcA Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpca ] ] TgwAttachmentToVpcB: Type: AWS::EC2::TransitGatewayAttachment Properties: SubnetIds: - !Ref VpcBPrivateSubnet TransitGatewayId: !Ref Tgw VpcId: !Ref VpcB Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcb ] ] TgwAttachmentToConnectVpc: Type: AWS::EC2::TransitGatewayAttachment Properties: SubnetIds: - !Ref ConnectVpcPublicSubnet TransitGatewayId: !Ref Tgw VpcId: !Ref ConnectVpc Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, connectvpc ] ] # VPC Endpoint for Session Manager EpSsmVpcA: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpointA ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm SubnetIds: - !Ref VpcAPrivateSubnet VpcEndpointType: Interface VpcId: !Ref VpcA EpSSMMsgVpcA: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpointA ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages SubnetIds: - !Ref VpcAPrivateSubnet VpcEndpointType: Interface VpcId: !Ref VpcA EpEC2MsgVpcA: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpointA ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages SubnetIds: - !Ref VpcAPrivateSubnet VpcEndpointType: Interface VpcId: !Ref VpcA EpS3VpcA: Type: AWS::EC2::VPCEndpoint Properties: RouteTableIds: - !Ref VpcAPrivateRouteTable ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 VpcEndpointType: Gateway VpcId: !Ref VpcA EpSsmVpcB: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpointB ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm SubnetIds: - !Ref VpcBPrivateSubnet VpcEndpointType: Interface VpcId: !Ref VpcB EpSSMMsgVpcB: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpointB ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages SubnetIds: - !Ref VpcBPrivateSubnet VpcEndpointType: Interface VpcId: !Ref VpcB EpEC2MsgVpcB: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpointB ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages SubnetIds: - !Ref VpcBPrivateSubnet VpcEndpointType: Interface VpcId: !Ref VpcB EpS3VpcB: Type: AWS::EC2::VPCEndpoint Properties: RouteTableIds: - !Ref VpcBPrivateRouteTable ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 VpcEndpointType: Gateway VpcId: !Ref VpcB # SecurityGroup SGEndpointA: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: EpA VpcId: !Ref VpcA SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 10.0.0.0/8 Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcendpointa ] ] SGEndpointB: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: EpB VpcId: !Ref VpcB SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 10.0.0.0/8 Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcendpointb ] ] SGServerA: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ServerA VpcId: !Ref VpcA SecurityGroupIngress: - IpProtocol: icmp FromPort: -1 ToPort: -1 CidrIp: 10.0.0.0/8 Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, servera ] ] SGServerB: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ServerB VpcId: !Ref VpcB SecurityGroupIngress: - IpProtocol: icmp FromPort: -1 ToPort: -1 CidrIp: 10.0.0.0/8 Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, serverb ] ] # IAM Role IamRoleForEc2: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore Tags: - Key: Name Value: !Ref ParamTag InstanceProfileForEc2: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Ref ParamTag Path: / Roles: - !Ref IamRoleForEc2 # EC2 ServerA: Type: AWS::EC2::Instance Properties: ImageId: !Ref ParamAMI IamInstanceProfile: !Ref InstanceProfileForEc2 InstanceType: t2.micro PrivateIpAddress: 10.1.0.11 SubnetId: !Ref VpcAPrivateSubnet SecurityGroupIds: - !Ref SGServerA Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, servera ] ] ServerB: Type: AWS::EC2::Instance Properties: ImageId: !Ref ParamAMI IamInstanceProfile: !Ref InstanceProfileForEc2 InstanceType: t2.micro PrivateIpAddress: 10.2.0.11 SubnetId: !Ref VpcBPrivateSubnet SecurityGroupIds: - !Ref SGServerB Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, serverb ] ] 東京分のCloudFormationテンプレート 020-JpVpcEc2.yml AWSTemplateFormatVersion: 2010-09-09 Description: TGW Connect handson in Tokyo Parameters: ParamTag: Type: String Default: tgw_handson_jp ParamVpcCidrBlock: Type: String Default: "10.99.0.0/16" ParamAMI: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Resources: ## VPC Vpc: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref ParamVpcCidrBlock EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag ] ] VpcIgw: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Ref ParamTag VpcAttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref VpcIgw VpcPublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, public ] ] VpcPublicRoute: DependsOn: VpcIgw Type: AWS::EC2::Route Properties: RouteTableId: !Ref VpcPublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref VpcIgw VpcPrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, private ] ] VpcPublicSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 1, !GetAZs ] VpcId: !Ref Vpc CidrBlock: !Select [ 1, !Cidr [ !Ref ParamVpcCidrBlock, 2, 8 ]] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, public, a ] ] VpcPrivateSubnet: Type: AWS::EC2::Subnet Properties: AvailabilityZone: !Select [ 1, !GetAZs ] VpcId: !Ref Vpc CidrBlock: !Select [ 0, !Cidr [ !Ref ParamVpcCidrBlock, 1, 8 ]] Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, private, a ] ] VpcPublicSubnetAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VpcPublicRouteTable SubnetId: !Ref VpcPublicSubnet VpcPrivateSubnetAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VpcPrivateRouteTable SubnetId: !Ref VpcPrivateSubnet # VPC Endpoint for Session Manager EpSsmVpc: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpoint ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm SubnetIds: - !Ref VpcPrivateSubnet VpcEndpointType: Interface VpcId: !Ref Vpc EpSSMMsgVpc: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpoint ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages SubnetIds: - !Ref VpcPrivateSubnet VpcEndpointType: Interface VpcId: !Ref Vpc EpEC2MsgVpc: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref SGEndpoint ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages SubnetIds: - !Ref VpcPrivateSubnet VpcEndpointType: Interface VpcId: !Ref Vpc EpS3Vpc: Type: AWS::EC2::VPCEndpoint Properties: RouteTableIds: - !Ref VpcPrivateRouteTable ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 VpcEndpointType: Gateway VpcId: !Ref Vpc # SecurityGroup SGEndpoint: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Ep VpcId: !Ref Vpc SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 10.0.0.0/8 Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag, vpcendpoint ] ] SGServerC: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: ServerC VpcId: !Ref Vpc SecurityGroupIngress: - IpProtocol: icmp FromPort: -1 ToPort: -1 CidrIp: 10.0.0.0/8 Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag ] ] # IAM Role IamRoleForEc2: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore Tags: - Key: Name Value: !Ref ParamTag InstanceProfileForEc2: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Ref ParamTag Path: / Roles: - !Ref IamRoleForEc2 # EC2 ServerC: Type: AWS::EC2::Instance Properties: ImageId: !Ref ParamAMI IamInstanceProfile: !Ref InstanceProfileForEc2 InstanceType: t2.micro PrivateIpAddress: 10.99.0.11 SubnetId: !Ref VpcPrivateSubnet SecurityGroupIds: - !Ref SGServerC Tags: - Key: Name Value: !Join [ "-", [ !Ref ParamTag ] ] 削除手順 以下の順で削除を行います。 (東京) FortiGate-VM (東京) ElasticIP (東京) FortiGate-VM Security Group (東京) CloudFormation (バージニア) FortiGate-VM (バージニア) ElasticIP (バージニア) FortiGate-VM Security Group (バージニア) Transit Gateway Connect peers (バージニア) Transit Gateway Connect (バージニア) CloudFormation 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CLI 多要素認証を設定しなければならなくなったとき

初めに 国のガイドラインなどで、多要素認証が義務付けられることは多いです。AWS CLIを使ってデータを扱う際にも多要素認証が必要になることがあります。そんなとき、AWSのドキュメントにちょうど解決方法が記載されていましたので紹介します。 手順 1. 仮想MFAデバイスのインストール こちらからインストールします。 2. MFAデバイスを割り当てる IAMからユーザーを選択し、赤枠で囲った「管理」をクリックします。 3. 一時認証情報の取得 デフォルトのまま続行をクリックします。 シークレットキーの表示をクリックし、シークレットキーをコピーします。 WinAuth.exeを起動し、Addをクリックします。Authenticatorを選択します。 シークレットキーを1. Enter the Secret Code ... のテキスト部分に貼り付けます(緑枠で囲った部分)。その後、3. Click the Verify button ... の箇所にある「Verify Authenticator」をクリックします。 次に、4. Verify the followinf code ... の場所に一定間隔で6桁の数字が表示されるので、2回連続でその数字を記録します。 それらの数字をMFAコード1, 2に入力します。その後、MFAの割り当てをクリックします。 4. profileに一時認証情報を追加する 以下の更新マークを押すと、- - - - - - の部分に6桁の数字が表示されるのでメモしておきます。 コマンドラインで以下を入力します。(もちろんcliを操作できるロールまたは認証情報が別途必要です) --serial-numberには、ARNを渡します。 --token-codeには、先ほどの6桁の数字を渡します。 $ aws sts get-session-token --serial-number arn:aws:iam::012345678912:mfa/user_name --token-code 012345 アクセスキーID、シークレットキーアクセスキー、セッショントークンが返ってきます。 { "Credentials": { "AccessKeyId": "ASIA...", "SecretAccessKey": "Edr...", "SessionToken": "IQo...", "Expiration": "2021-04-19T22:42:00+00:00" } } これらを.aws/credentialsに登録します。(もし永続的なIAM 認証情報である[default]があれば、[default]を消去して次のステップに進むと一時認証が機能していることがわかります) [mfa] aws_access_key_id = ASIA... aws_secret_access_key = Edr... aws_session_token = IQo... profile名を与えてcliを操作します。 $ aws s3 ls --profile mfa 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのEC2でバージョンが合わなくてpg_dumpできなかったのときの対処法

EC2でpg_dumpしたとき [ec2 ~]$ pg_dump -U postgres -h `host` -p 5432 `database` -f aws.dump パスワード: pg_dump: サーババージョン: 12.5、pg_dump バージョン: 9.6.19 pg_dump: サーババージョンの不整合のため処理を中断しています このようなエラーが起きたときは、サーバーバージョンのほうのpg_dumpをダウンロードする必要がある。 下記でリンクアドレスをコピー https://www.postgresql.org/ftp/source/v12.5/ $cd /usr/local/src $wget https://ftp.postgresql.org/pub/source/v12.5/postgresql-12.5.tar.gz 解凍 ディレクトリ作成 makeの実行。 $tar xzf postgresql-12.5.tar.gz $cd postgresql-12.5.tar.gz $mkdir /usr/local/postgresql-12.5/ $./configure --prefix=/usr/local/postgresql-12.5/ --with-pgport=5432 $make $make install 途中でこんな感じのエラーがでたら configure: error: no acceptable C compiler found in $PATH $yum install gcc するとinstallできるようになります。 エラーが出てもググれば解決できるのでググりましょう。 ダウンロードできたら 下記を実行し、ダウンロードしたpg_dumpのパスを確認。 $ find / -name pg_dump -type f 2>/dev/null /usr/bin/pg_dump /usr/local/src/postgresql-12.5/src/bin/pg_dump/pg_dump 確認したパスをつけてpg_dumpしてみる! [myuser@ip-172-31-2-37 ~]$ /usr/local/src/postgresql-12.5/src/bin/pg_dump/pg_dump -U postgres -h `host` -p 5432 `database` -f aws.dump これでダウンロードできました...!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エージェントレスでオートスケーリングなAWS監視(Centos8)

はじめに cloudwatchからprmetheusにmetricsを取得させてオートスケーリングな監視を実現する。 この仕組みはとある現場でたまたまできたものなのだが、とにかく手がかからない。 テスト環境や仮組みのときとかにちょろっと使えそうなので記載しておく。 ※本手順はprometheus構築済みの環境を想定しています。 設定 IAM準備 まず以下のようなポリシーでユーザを作成してアクセスキーとシークレットキーを用意しておく。 IAMポリシー "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "tag:GetResources", "cloudwatch:GetMetricData", "cloudwatch:GetMetricStatistics", "cloudwatch:ListMetrics" ], "Resource": "*" } ] } awscliインストール awscliインストール yum install python3-pip pip3 install awscli aws configure AWS Access Key ID [None]: [アクセスキー] AWS Secret Access Key [None]: [シークレットキー] Default region name [None]: ap-northeast-1 Default output format [None]: yaceバイナリ配備 cloudwatchからmetricsを取得するexpoterはいくつかあるが、今回はyaceを使用する yaceバイナリ配備 yum install wget wget https://github.com/ivx/yet-another-cloudwatch-exporter/releases/download/v0.26.3-alpha/yet-another-cloudwatch-exporter_0.26.3-alpha_Linux_x86_64.tar.gz tar -zxf yet-another-cloudwatch-exporter_0.26.3-alpha_Linux_x86_64.tar.gz mv yace /usr/local/ コンフィグ作成 /etc/prometheus/yace.conf discovery: exportedTagsOnMetrics: ec2: - Name jobs: - type: ec2 regions: - ap-northeast-1 period: 60 length: 300 delay: 60 nilToZero: true searchTags: - key: Name value: .* metrics: - name: CPUUtilization statistics: - Maximum - name: DiskReadBytes statistics: - Maximum - name: DiskWriteBytes statistics: - Maximum - name: NetworkIn statistics: - Sum - name: NetworkOut statistics: - Sum yaceサービス化 /usr/lib/systemd/system/yace.service [Unit] Description=yace After=network.target [Service] Type=simple User=root ExecStart=/usr/local/yace -config.file /etc/prometheus/yace.conf [Install] WantedBy=multi-user.target selinux停止 yaceをサービス化するときにselinuxが騒ぐのであらかじめ停止しておく selinux停止 sed -i "s/SELINUX=enforcing/SELINUX=disabled/" /etc/selinux/config reboot サービス設定 サービス設定 systemctl enable yace systemctl start yace ここまでで作業完了。 これ以降AWSコンソール側でインスタンスを作成すると自動で監視が開始されようになる。 停止した場合はmackerelのような退役作業は必要なく、自動で監視がとまる。 (私はmackerelさんが大好きですよ!) メモリ監視について cloudwatchはデフォルトではメモリを監視していない。 この為、メモリ監視を行う場合cloudwatachエージェントなどで出力させる必要がある 例えばCloudWatch モニタリングスクリプトでメモリ監視を出力させた場合はyace.confに以下のように設定を追加することでprometheus側で取得させることが可能。 (ただしこれはインスタンス追加の度に設定が必要になる) /etc/prometheus/yace.conf static: - namespace: System/Linux name: System/Linux regions: - ap-northeast-1 dimensions: - name: InstanceId value: [インスタンスID] metrics: - name: MemoryUtilization statistics: - Maximum period: 600 length: 600 なお、CloudWatch モニタリングスクリプトは現在非推奨なので特別な理由がなければ使用しないでください。 https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/monitoring-scripts-intro.html あとがき 今回スクリーンショットが用意できなかったので地味ですがいい感じにgrafana使っていると結構な便利感あります。 オートスケーリングといってもなんだかんだ手のかかるものの多い中でたまたまこんな仕組みができたので共有したいと思いました。 なお、今回紹介したyaceはEC2以外のサービスにも対応している為、ELBやRDSでもまったく同じことができます。 ひょっとしたらそちらで使用したほうが需要が多いかもしれないです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【未経験でも1万5千円+試験料でOK】AWS認定ソリューションアーキテクトアソシエイトを取得する方法

かけた時間と合格率の関係について 最初に声を大にして言いたいんですが、よく「○○時間で合格した話」みたいな合格までの記録をつづったエントリがあるじゃないですか。 あれ、無意味!!!! 読んでみたら普段AWS使ってるだとか、院生時代に使ってましたとか、前提がそもそも違うのにそっちのさじ加減で測った時間だけ書かれても何の参考になるんだ、という話です。(一つ一つの記事は全て面白く読ませていただきました。合格された皆さん素敵です) よしんば自分と同じスタートラインだとしても、あんまり短い時間で合格してると凄いなというか焦るというか正直僻みの感情が湧いてくるだけなので、タイトルの合計期間の部分は脳内一括削除して必要な情報だけ得るのが精神安定上よろしい、というのが個人的ベストプラクティスです。 人生のステージをいくつか昇ると育児、近親者の介護、病気治療、相続問題、離婚調停などで継続的にまとまった学習時間を確保すること自体が難しくなりますしね。 時間はないが小銭はある 言いたいことをいってスッキリしたところで、時間ではなく合計金額を提示するというスタイルで、合格するまでの個人的な道のりをまとめたいと思います。 各学習にかけた金額と感想 以下、各学習にかけた金額と感想です。 No 教材 金額 1 Udemyハンズオン動画 2,300円 2 インプレス黒本 2,618円 3 Udemy模試 3,600円 4 公式模試 2,200円 5 公式模試2回目 2,200円 6 公式模試3回目 2,200円 7 本番 16,500円 1. Udemyハンズオン動画(2,300円) 米シリコンバレーDevOps監修!超AWS完全入門+本番運用向け上級編ベストプラクティスとTerraform 評価の高さと他の講座より短かかったので購入。 これを受講して始めて、クラウドとは何かを知り、AWSがただのホスティングサーバではないと気づく。 2. インプレス黒本(2,618円) 改訂新版 徹底攻略 AWS認定 ソリューションアーキテクト − アソシエイト教科書[SAA-C02]対応 以前Javaの試験を受けた時にインプレスの黒本を買って大変参考になったので、その時の好印象も手伝い購入。模試がダウンロード可能。 3. udemy模試(3,600円) 【SAA-C02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問) 6回分とボリュームがありコスパが良かったので購入。難しいけど買って良かった。全ての模試で8割以上とれるようになれば、本番が優しく感じられるはず。 4. 公式模試(2,200円) udemyの模試を1周終わったところでピアソンの模試を腕試しに受験。実際の翻訳の微妙さを味わうことができるので一度受けておくことをおすすめ。この時点で65%。合格ラインは72%なので落ち込む。その後udemy模試で72%以上取れるまで復習。 5. 公式模試2回目(2,200円) 正解率が75%にあがり自信がつく。udemy模試をもう1周する。もう一度公式模試で合格点が取れれば本番受験すると決める。 6. 公式模試3回目(2,200円) ここでようやく公式模試は毎回同じ問題が出ていることに気づく←バカ 7. 本番(16,500円) 会社の会議室を使わせてもらいオンライン受験しました。 当初終業後を考えましたが、常に誰かが残業してて常に物音がしている楽しい職場のため、始業前の時間に受験することを決意。 試験官の言語を英語にすると時差の関係でAM3時台とかテストセンターではありえない時間に受験ができます。自分は5時台に受験。 英語と聞くだけで尻込みする人も多いですが、試験官とのやりとりが始まったタイミングで「Would you write it down in chatbox ?」と宣言する or チャットに書けば、テキストでのコミュニケーションをしてくれます。あとはリーディングさえできれば、机を見せればいいんだな、とか両腕を見せればいいんだな、というのがわかるのでなんとかなります。 後書き タイトルが怪しい求人広告みたいになってしまいました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【未経験でも1万5千円+試験料だけでOK】AWS認定ソリューションアーキテクトアソシエイトを取得する方法

かけた時間と合格率の関係について 最初に声を大にして言いたいんですが、よく「○○時間で合格した話」みたいな合格までの記録をつづったエントリがあるじゃないですか。 あれ、無意味!!!! 読んでみたら普段AWS使ってるだとか、院生時代に使ってましたとか、前提がそもそも違うのにそっちのさじ加減で測った時間だけ書かれても何の参考になるんだ、という話です。(一つ一つの記事は全て面白く読ませていただきました。合格された皆さん素敵です) よしんば自分と同じスタートラインだとしても、あんまり短い時間で合格してると凄いなというか焦るというか正直僻みの感情が湧いてくるだけなので、タイトルの合計期間の部分は脳内一括削除して必要な情報だけ得るのが精神安定上よろしい、というのが個人的ベストプラクティスです。 人生のステージをいくつか昇ると育児、近親者の介護、病気治療、相続問題、離婚調停などで継続的にまとまった学習時間を確保すること自体が難しくなりますしね。 時間はないが小銭はある 言いたいことをいってスッキリしたところで、時間ではなく合計金額を提示するというスタイルで、合格するまでの個人的な道のりをまとめたいと思います。 各学習にかけた金額と感想 以下、各学習にかけた金額と感想です。 No 教材 金額 1 Udemyハンズオン動画 2,300円 2 インプレス黒本 2,618円 3 Udemy模試 3,600円 4 公式模試 2,200円 5 公式模試2回目 2,200円 6 公式模試3回目 2,200円 7 本番 16,500円 1. Udemyハンズオン動画(2,300円) 米シリコンバレーDevOps監修!超AWS完全入門+本番運用向け上級編ベストプラクティスとTerraform 評価の高さと他の講座より短かかったので購入。 これを受講して始めて、クラウドとは何かを知り、AWSがただのホスティングサーバではないと気づく。 2. インプレス黒本(2,618円) 改訂新版 徹底攻略 AWS認定 ソリューションアーキテクト − アソシエイト教科書[SAA-C02]対応 以前Javaの試験を受けた時にインプレスの黒本を買って大変参考になったので、その時の好印象も手伝い購入。模試がダウンロード可能。 3. udemy模試(3,600円) 【SAA-C02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問) 6回分とボリュームがありコスパが良かったので購入。難しいけど買って良かった。全ての模試で8割以上とれるようになれば、本番が優しく感じられるはず。 4. 公式模試(2,200円) udemyの模試を1周終わったところでピアソンの模試を腕試しに受験。実際の翻訳の微妙さを味わうことができるので一度受けておくことをおすすめ。この時点で65%。合格ラインは72%なので落ち込む。その後udemy模試で72%以上取れるまで復習。 5. 公式模試2回目(2,200円) 正解率が75%にあがり自信がつく。udemy模試をもう1周する。もう一度公式模試で合格点が取れれば本番受験すると決める。 6. 公式模試3回目(2,200円) ここでようやく公式模試は毎回同じ問題が出ていることに気づく←バカ 7. 本番(16,500円) 会社の会議室を使わせてもらいオンライン受験しました。 当初終業後を考えましたが、常に誰かが残業してて常に物音がしている楽しい職場のため、始業前の時間に受験することを決意。 試験官の言語を英語にすると時差の関係でAM3時台とかテストセンターではありえない時間に受験ができます。自分は5時台に受験。 英語と聞くだけで尻込みする人も多いですが、試験官とのやりとりが始まったタイミングで「Would you write it down in chatbox ?」と宣言する or チャットに書けば、テキストでのコミュニケーションをしてくれます。あとはリーディングさえできれば、机を見せればいいんだな、とか両腕を見せればいいんだな、というのがわかるのでなんとかなります。 後書き タイトルが怪しい求人広告みたいになってしまいました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AmazonLinux2でApache(httpd)に環境変数を読ませる方法

環境変数の設定をする時、普通は/etc/environmentsに書いたりapacheの場合confファイルにSetEnvで書いたりしますが、私のAmazonLinux2上では何故かあらゆる方法を試しても環境変数に反映されなかったので、その対処方法を記載します。 まずは環境変数の確認(↓を参考にしています) まずはhttpdプロセスの番号を確認(pidofとか使えば一行で書けそうだが) # ps afx|grep httpd 7801 pts/0 S+ 0:00 \_ grep --color=auto httpd 7753 ? Ss 0:00 /usr/sbin/httpd -DFOREGROUND 7754 ? Sl 0:00 \_ /usr/sbin/httpd -DFOREGROUND 7756 ? Sl 0:00 \_ /usr/sbin/httpd -DFOREGROUND 適当なidから環境変数を取得(一番上はgrep自身なのでそれ以外) # sed -e 's/\x0/\n/g' /proc/7753/environ LANG=C PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin NOTIFY_SOCKET=/run/systemd/notify こんな感じで普段何もせず入っているHOSTNAMEやらLANGやらの環境変数すら入ってない。 解決方法 →systemctlの設定ファイルに環境変数設定ファイルのパスを追記してファイルの方に環境変数を書く statusを打つとhttpd.serviceの場所がわかる # systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled) Active: active (running) since Mon 2021-04-19 05:04:04 UTC; 30min ago Docs: man:httpd.service(8) Main PID: 7899 (httpd) Status: "Total requests: 376; Idle/Busy workers 100/0;Requests/sec: 0.208; Bytes served/sec: 840 B/sec" CGroup: /system.slice/httpd.service ├─7899 /usr/sbin/httpd -DFOREGROUND ├─7900 /usr/sbin/httpd -DFOREGROUND loadedにある/usr/lib/systemd/system/httpd.serviceという所 このファイルを適当なエディタで開いて追記する # vi /usr/lib/systemd/system/httpd.service [Unit] Description=The Apache HTTP Server Wants=httpd-init.service After=network.target remote-fs.target nss-lookup.target httpd-init.service Documentation=man:httpd.service(8) [Service] Type=notify # コメントアウト # Environment=LANG=C # ここを追記 EnvironmentFile=/etc/sysconfig/httpd ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND ExecReload=/usr/sbin/httpd $OPTIONS -k graceful # Send SIGWINCH for graceful stop KillSignal=SIGWINCH KillMode=mixed PrivateTmp=true [Install] WantedBy=multi-user.target /etc/sysconfig/httpdを新たに作成して環境変数を設定 # vi /etc/sysconfig/httpd ## ↓のように[変数名]=[値]の形式で入れたい変数を記入 LANG=C ENV=HOGE DATABASE=FUGA systemctlをリロード(.serviceを編集した場合はやらないとだめらしい)して、httpdプロセスを再起動 # systemctl daemon-reload # systemctl restart httpd またプロセスの環境変数を調べて入っていることを確認 # ps afx|grep httpd 7947 pts/0 S+ 0:00 \_ grep --color=auto httpd 7899 ? Ss 0:00 /usr/sbin/httpd -DFOREGROUND 7900 ? Sl 0:00 \_ /usr/sbin/httpd -DFOREGROUND 7902 ? Sl 0:00 \_ /usr/sbin/httpd -DFOREGROUND # sed -e 's/\x0/\n/g' /proc/7899/environ LANG=C PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin NOTIFY_SOCKET=/run/systemd/notify ENV=HOGE DATABASE=FUGA AWS的にはパラメータストアのパスを設定ファイルとかで設定して、環境変数系は全部そっちから読ませてローカルには入れないのが正解なのでしょうが…(それゆえこんな複雑なのかもしれないですが) とはいえデバッグ等で取り急ぎローカルで環境変数入れたい場合もあるかと思うので、参考になれば。 /etc/init.d時代とrestartとかの文法が逆になってるのがどうも慣れない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS lambdaでS3オブジェクトの自動ウイルススキャンをやろうとしてハマった話

やりたかったこと S3にファイルがアップされたら自動でウイルススキャンを行いたい 調べてみると以下がいい感じだったので実施したが思いの外ハマったのでメモ https://github.com/upsidetravel/bucket-antivirus-function 手順 権限周りは以下を参考にしてください https://dev.classmethod.jp/articles/s3-bucket-antivirus-lambda/ git clone https://github.com/upsidetravel/bucket-antivirus-function.git cd bucket-antivirus-function/ sudo make all ./build/lambda.zipが作成されるのでS3にアップする 以下のCfnテンプレートで3時間ごとに実行されるlambda関数作成 Resources: ScheduledRule: Type: AWS::Events::Rule Properties: ScheduleExpression: 'cron(00 0-23/3 * * ? *)' State: ENABLED Targets: - Arn: !GetAtt Lambda.Arn Id: bucket-antivirus-update PermissionForEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref Lambda Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt ScheduledRule.Arn Lambda: Type: AWS::Lambda::Function Properties: Code: S3Bucket: lambda-function-bucket S3Key: lambda.zip Environment: Variables: AV_DEFINITION_S3_BUCKET: antivirus-definition-bucket FunctionName: bucket-antivirus-update Handler: update.lambda_handler MemorySize: 1024 Role: antivirus-update-role-arn Runtime: python3.7 Timeout: 300 エラー発生 とりあえずここまでの工程でS3にウイルス定義ファイルをアップする部分はできたはずなので試しに実行してみると以下のエラー発生 START RequestId: xxxxxxxxxxxxxxxxxxxxxxxx Version: $LATEST Script starting at 2021/04/19 04:37:41 UTC Attempting to create directory /tmp/clamav_defs. Starting freshclam with defs in /tmp/clamav_defs. freshclam output: b'./bin/freshclam: error while loading shared libraries: libprelude.so.28: cannot open shared object file: No such file or directory\n' Unexpected exit code from freshclam: 127. File does not exist: main.cld File does not exist: main.cvd File does not exist: daily.cld File does not exist: daily.cvd File does not exist: bytecode.cld File does not exist: bytecode.cvd Script finished at 2021/04/19 04:37:42 UTC END RequestId: xxxxxxxxxxxxxxxxxxxxxxxx REPORT RequestId: xxxxxxxxxxxxxxxxxxxxxxxx Duration: 2604.36 ms Billed Duration: 2605 ms Memory Size: 128 MB Max Memory Used: 85 MB Init Duration: 334.40 ms あれこれ調べてみると https://github.com/upsidetravel/bucket-antivirus-function/issues/125 こちらのissueに遭遇。どうやらlambda側がライブラリを見つけられないことが原因のよう Dockerfileを以下のように書き換えてmakeし直したところエラーが解消しました。 FROM amazonlinux:2 # Set up working directories RUN mkdir -p /opt/app RUN mkdir -p /opt/app/build RUN mkdir -p /opt/app/bin/ # Copy in the lambda source WORKDIR /opt/app COPY ./*.py /opt/app/ COPY requirements.txt /opt/app/requirements.txt # Install packages RUN yum update -y RUN yum install -y cpio python3-pip yum-utils zip unzip less RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm # This had --no-cache-dir, tracing through multiple tickets led to a problem in wheel RUN pip3 install -r requirements.txt RUN rm -rf /root/.cache/pip # Download libraries we need to run in lambda WORKDIR /tmp RUN yumdownloader -x \*i686 --archlist=x86_64 clamav clamav-lib clamav-update json-c pcre2 libprelude gnutls libtasn1 lib64nettle nettle RUN rpm2cpio clamav-0*.rpm | cpio -idmv RUN rpm2cpio clamav-lib*.rpm | cpio -idmv RUN rpm2cpio clamav-update*.rpm | cpio -idmv RUN rpm2cpio json-c*.rpm | cpio -idmv RUN rpm2cpio pcre*.rpm | cpio -idmv RUN rpm2cpio gnutls* | cpio -idmv RUN rpm2cpio nettle* | cpio -idmv RUN rpm2cpio lib* | cpio -idmv RUN rpm2cpio *.rpm | cpio -idmv RUN rpm2cpio libtasn1* | cpio -idmv # Copy over the binaries and libraries RUN cp /tmp/usr/bin/clamscan /tmp/usr/bin/freshclam /tmp/usr/lib64/* /opt/app/bin/ # Fix the freshclam.conf settings RUN echo "DatabaseMirror database.clamav.net" > /opt/app/bin/freshclam.conf RUN echo "CompressLocalDatabase yes" >> /opt/app/bin/freshclam.conf # Create the zip file WORKDIR /opt/app RUN zip -r9 --exclude="*test*" /opt/app/build/lambda.zip *.py bin WORKDIR /usr/local/lib/python3.7/site-packages RUN zip -r9 /opt/app/build/lambda.zip * WORKDIR /opt/app
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudWatch Metric Streams使ってNew Relicにメトリクスを送ってみた

2021/03/31にリリースされたCloudWatch Metric Streamsを使ってNew Relicにメトリクスデータを送ってみた。 New RelicからAPIでポーリングしてメトリクスデータを取得するよりタイムラグが少なくなり、確実性があがるとのことなので、とりあえずやってみた。 CloudWatch Metric Streamsってなんや? CloudWatchからNew RelicなどのモニタリングSaaSに対してデータを送る機能です。 いままで New RelicなどのモニタリングSaaSでCloudWatchのメトリクスをモニタリングする場合、いままでは設定された間隔でSaaS側からCloudWatchにGetMetric APIを叩いてデータ収集していた。 CloudWatch Metric Streamsを使うと New RelicなどのモニタリングSaaSに対してCloudWatchからメトリックをストリームすることで、データ収集のタイムラグが大幅に改善される。 とりあえずやってみた New Relic側の設定 New RelicへログインしInfrastrucure > AWS > Add an AWS Accountをクリック Metric Streamを選択してクリック 以降は画面に表示されるとおりに設定 ※AWSにIAMロールを作ったりです。 ※既存のAWSインテグレーションがあるものは、IAMロールはそのまま使えます。 AWS側の設定 対象のAWSアカウントへログインしておく。 下記リンクのCFnテンプレートでスタックを作成して実行 ※New Relicのライセンスキーが必要、ライセンスキーはNRで右上アカウントメニュー > API Keysで確認できる。 ※Kinesisのエラー履歴を保存するS3バケット指定箇所があるが、既存バケットだとNGとなる。 https://console.aws.amazon.com/cloudformation/home?#/stacks/quickcreate?templateURL=https://nr-downloads-main.s3.amazonaws.com/cloud_integrations/aws/cloudformation/MetricStreams_CloudFormation.yml&stackName=NewRelic-Metric-Stream&param_NewRelicDatacenter=US 結果 16:29にNRQLでRDSのCPU Utilizationを確認したところ、16:26のメトリクスが記録されていた!はやい! タイムラグは3分くらいかな。 WHERE句に下記のようなcloudwatch-metrics-streamsで取得したものという指定をすれば、従来のAPIポーリングと共存してもstreamのメトリクスのみを対象にできます。 `collector.name` = 'cloudwatch-metric-streams' AWS利用料金 対象期間: 4/2 - 4/8の1週間 データ参照元: AWS Cost Explorer 使用コンポーネントなどにより変動しますので参考として。 CloudWatch Streams AWS利用料金: You pay $0.003 for every 1000 metric updates 料金参考: https://aws.amazon.com/jp/blogs/aws/cloudwatch-metric-streams-send-aws-metrics-to-partners-and-to-your-apps-in-real-time/ 23.81USD/1week 23.81USD * 4week = 95.24USD ちなみに従来通りのGetMetricData APIだと… 19.93USD/1week 19.93USD * 4week = 79.72USD Kinesis Data Firehose AWS利用料金: 0.036USD/GBあたり 料金参考: https://aws.amazon.com/jp/kinesis/data-firehose/pricing/ 0.09USD/1week 0.09USD * 4week = 0.36USD 参考 ■CloudWatch Metric Streams – Send AWS Metrics to Partners and to Your Apps in Real Time https://aws.amazon.com/jp/blogs/aws/cloudwatch-metric-streams-send-aws-metrics-to-partners-and-to-your-apps-in-real-time/ ■Move Faster with New Relic One and Amazon CloudWatch Metric Streams https://blog.newrelic.com/product-news/aws-cloudwatch-metric-streams/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

API GatewayでCloudFrontの真似事をしてSPAを動作させてみる

はじめに とある理由でSPAのアプリケーションの配信にS3+CloudFrontを使うことができなかったので、API Gateway+S3を使ってみることにしました。 S3を定義する API Gatewayのチュートリアル「API Gateway で REST API を Amazon S3 のプロキシとして作成する」にあるように、API GatewayとS3を直接連携して、バケットのCRUDができるAPIとして公開する機能がAPI Gatewayにあります。この機能を利用して今回はAPI GatewayからS3内にある静的サイト(index.html)にアクセスできるように実装してみます。 まずはs3を定義して静的サイトのホスティングができるようにします。 import * as cdk from '@aws-cdk/core' import * as s3 from '@aws-cdk/aws-s3' export class SpaHostingStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: WebStackProps) { super(scope, id) new s3.Bucket(this, 's3', { removalPolicy: cdk.RemovalPolicy.DESTROY, websiteIndexDocument: 'index.html', websiteErrorDocument: 'index.html', blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, }) } } IAMロールを作成する S3は公開設定をOFFにした状態です。API Gatewayにのみバケットの読み込み権限を与えるようにIAMロールを定義します。 import * as cdk from '@aws-cdk/core' import * as s3 from '@aws-cdk/aws-s3' import * as iam from '@aws-cdk/aws-iam' export class SpaHostingStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props) const bucket = new s3.Bucket(this, 's3', { removalPolicy: cdk.RemovalPolicy.DESTROY, websiteIndexDocument: 'index.html', websiteErrorDocument: 'index.html', blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, }) const credentialsRole = new iam.Role(this, 'role', { assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'), }) bucket.grantRead(credentialsRole) } } grantRead()メソッドを用いると特定のS3バケットに対しての読み込み権限をIAMロールのポリシーに上書きすることが簡単にできます。 API Gatewayを設定する SPAの配信を行うために、APIの/と{何かしらのパス}へのアクセスに対してはindex.html、ブラウザ自身が行う/bundle.jsと/favicon.icoへのアクセスに対しては静的なファイルを返すようにします。 import * as cdk from '@aws-cdk/core' import * as s3 from '@aws-cdk/aws-s3' import * as apigateway from '@aws-cdk/aws-apigateway' import * as iam from '@aws-cdk/aws-iam' export class SpaHostingStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props) const bucket = new s3.Bucket(this, 's3', { removalPolicy: cdk.RemovalPolicy.DESTROY, websiteIndexDocument: 'index.html', websiteErrorDocument: 'index.html', blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, }) const credentialsRole = new iam.Role(this, 'role', { assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'), }) bucket.grantRead(credentialsRole) const api = new apigateway.RestApi(this, 'api', { binaryMediaTypes: ['image/*'], }) const s3RootIntegration = new apigateway.AwsIntegration({ service: 's3', integrationHttpMethod: 'GET', path: `${bucket.bucketName}/index.html`, options: { credentialsRole, requestParameters: { 'integration.request.header.Accept': 'method.request.header.Accept', 'integration.request.header.Content-Type': 'method.request.header.Content-Type', }, integrationResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': 'integration.response.header.Content-Type', 'method.response.header.Access-Control-Allow-Headers': 'integration.response.header.Access-Control-Allow-Headers', 'method.response.header.Access-Control-Allow-Methods': 'integration.response.header.Access-Control-Allow-Methods', 'method.response.header.Access-Control-Allow-Credentials': 'integration.response.header.Access-Control-Allow-Credentials', 'method.response.header.Access-Control-Allow-Origin': 'integration.response.header.Access-Control-Allow-Origin', }, }, ], }, }) api.root.addMethod('GET', s3RootIntegration, { methodResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': false, 'method.response.header.Access-Control-Allow-Headers': false, 'method.response.header.Access-Control-Allow-Methods': false, 'method.response.header.Access-Control-Allow-Credentials': false, 'method.response.header.Access-Control-Allow-Origin': false, }, responseModels: { 'text/html': { modelId: 'Empty', }, }, }, ], requestParameters: { 'method.request.header.Content-Type': false, 'method.request.header.Accept': false, }, }) api.root.addResource('build.js').addMethod( 'GET', new apigateway.AwsIntegration({ service: 's3', integrationHttpMethod: 'GET', path: `${bucket.bucketName}/build.js`, options: { credentialsRole, requestParameters: { 'integration.request.header.Accept': 'method.request.header.Accept', 'integration.request.header.Content-Type': 'method.request.header.Content-Type', }, integrationResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': 'integration.response.header.Content-Type', 'method.response.header.Access-Control-Allow-Headers': 'integration.response.header.Access-Control-Allow-Headers', 'method.response.header.Access-Control-Allow-Methods': 'integration.response.header.Access-Control-Allow-Methods', 'method.response.header.Access-Control-Allow-Credentials': 'integration.response.header.Access-Control-Allow-Credentials', 'method.response.header.Access-Control-Allow-Origin': 'integration.response.header.Access-Control-Allow-Origin', }, }, ], }, }), { methodResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': false, 'method.response.header.Access-Control-Allow-Headers': false, 'method.response.header.Access-Control-Allow-Methods': false, 'method.response.header.Access-Control-Allow-Credentials': false, 'method.response.header.Access-Control-Allow-Origin': false, }, responseModels: { 'text/html': { modelId: 'Empty', }, }, }, ], requestParameters: { 'method.request.header.Content-Type': false, 'method.request.header.Accept': false, }, }, ) api.root.addResource('favicon.ico').addMethod( 'GET', new apigateway.AwsIntegration({ service: 's3', integrationHttpMethod: 'GET', path: `${bucket.bucketName}/favicon.ico`, options: { credentialsRole, requestParameters: { 'integration.request.header.Accept': 'method.request.header.Accept', 'integration.request.header.Content-Type': 'method.request.header.Content-Type', }, integrationResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': 'integration.response.header.Content-Type', 'method.response.header.Access-Control-Allow-Headers': 'integration.response.header.Access-Control-Allow-Headers', 'method.response.header.Access-Control-Allow-Methods': 'integration.response.header.Access-Control-Allow-Methods', 'method.response.header.Access-Control-Allow-Credentials': 'integration.response.header.Access-Control-Allow-Credentials', 'method.response.header.Access-Control-Allow-Origin': 'integration.response.header.Access-Control-Allow-Origin', }, }, ], }, }), { methodResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': false, 'method.response.header.Access-Control-Allow-Headers': false, 'method.response.header.Access-Control-Allow-Methods': false, 'method.response.header.Access-Control-Allow-Credentials': false, 'method.response.header.Access-Control-Allow-Origin': false, }, responseModels: { 'text/html': { modelId: 'Empty', }, }, }, ], requestParameters: { 'method.request.header.Content-Type': false, 'method.request.header.Accept': false, }, }, ) api.root.addResource('{proxy+}').addMethod('GET', s3RootIntegration, { methodResponses: [ { statusCode: '200', responseParameters: { 'method.response.header.Content-Type': false, 'method.response.header.Access-Control-Allow-Headers': false, 'method.response.header.Access-Control-Allow-Methods': false, 'method.response.header.Access-Control-Allow-Credentials': false, 'method.response.header.Access-Control-Allow-Origin': false, }, responseModels: { 'text/html': { modelId: 'Empty', }, }, }, ], requestParameters: { 'method.request.header.Content-Type': false, 'method.request.header.Accept': false, }, }) } } リソース名を{proxy+}と設定することで、ワイルドカードのような形でパス名を置くことができます。また、htmlのコンテンツを返すためにヘッダーに色々な設定を加えます。 おわりに とりあえずAPI GatewayでSPAの配信はできないことはないがとても面倒だということがわかりました。また、この方法だと例えばxxxx.svgなどの静的ファイルが加わった時にその都度リソースの設定で対応する必要があります。面倒な場合はwebpackでurl-loaderを設定して全部htmlファイルにまとめてしまうなど、ウェブアプリ側でも対応が必要になったりもします。基本的にはこういった用途にはCloudFrontを使うのが無難かなと思いました。 参考 API Gateway を用いて、S3 で静的ウェブサイトホスティングで公開したVue アプリをHTTPS化してみた。 How to integrate API Gateway with s3 in CDK
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS Amplify] amplify init して "InvalidSignatureException: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details." と言われたら確認するとこ

事象 こうすると % amplify init こう言われました。 InvalidSignatureException: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. DeepL様の翻訳 InvalidSignatureException: 私たちが計算したリクエスト署名は、あなたが提供した署名と一致しません。AWSシークレットアクセスキーと署名方法を確認してください。詳細は、サービスのドキュメントを参照してください。 解決策 ~/.aws/credentials の aws_access_key_id か aws_secret_access_key が間違えている可能性があるので確認しましょう。 % cat ~/.aws/credentials [default] aws_access_key_id=XXXXXXXXXXXXXXXXXXXX aws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS上にエッジ環境を構築してGreengrass(V1)の動きを確認する

はじめに GreengrassはIoT Coreとの疎通が切れても、エッジ環境内でメッセージのやり取りやLambdaの実行ができるようになっています。 また、GreengrassからIoT Coreへのメッセージであれば、疎通回復まである程度保持することもできます。 今回はAWS上に疑似的なエッジ環境を構築し、エッジ環境とIoT Core間の通信が切れた場合の挙動を確認します。 具体期には以下を検証しました。 IoT Coreと通信できない状況でもGreengrassデバイス間でMQTTのやり取りができること IoT Coreと通信できない状況でもGreengrassのローカルLambdaが実行できること IoT Coreと通信できない間のメッセージを疎通回復後に送信できること 1 AWS上にエッジ環境を構築 まず、AWS上に疑似的なエッジ環境を構築します。 Greengrassグループ内のネットワークを維持しつつIoT Coreとの疎通を切断するのが容易と考えての手段です。 作成後の構成は以下の通りです。 1-1 ネットワーク周りの作成 VPCをはじめ、ネットワーク周辺の設定を行います。 具体的には以下を作成します。 作成するもの 備考 VPC 10.0.0.0/16 サブネットA 10.0.0.0/24 サブネットB 10.0.1.0/24 インターネットゲートウェイ VPCにアタッチする ルートテーブルA to local と to Internet のルーティングを設定 ルートテーブルB to local のみルーティングを設定 VPCの作成 AWS上のプライベートなネットワーク環境を作成します。 1.VPCのマネジメントコンソールで VPC > 「VPCを作成」の順にクリック 2.以下の通り設定し、「VPCを作成」をクリック 名前タグ:※任意のVPC名 IPv4 CIDRブロック:10.0.0.0/16 ※その他はデフォルト設定のまま サブネットの作成 踏み台サーバを置くサブネットと仮想デバイスを置くサブネットをそれぞれ作成します。 1.VPCのマネジメントコンソールで サブネット > 「サブネットを作成」の順にクリック 2.以下の通り設定して「サブネットを作成」をクリック ※1つ目のサブネットの情報を入力したあと、「新しいサブネットを追加」をクリックで2つ目のサブネットの設定も入力できる VPC(共通):※先ほどの手順で作成したVPC サブネット名:※任意のサブネット名 アベイラビリティゾーン:ap-northeast-1a IPv4 CIDR ブロック(1つ目):10.0.0.0/24 IPv4 CIDR ブロック(2つ目):10.0.1.0/24 インターネットゲートウェイの作成とアタッチ VPCと外部が接続できるようにインターネットゲートウェイを作成し、アタッチします。 1.VPCのマネジメントコンソールで インターネットゲートウェイ > 「インターネットゲートウェイの作成」の順にクリック 2.任意の名前を入力して、「インターネットゲートウェイの作成」をクリック 3.アクション > VPCにアタッチ の順にクリック 4.先ほど作成したVPCを選択して、「インターネットゲートウェイのアタッチ」をクリック ルートテーブルの作成 今回は外部向けのルーティングを持つルートテーブルと持たないルートテーブル、それぞれを作成します。 以降の検証でエッジ環境とIoT Coreの接続を切る際に、後者のルートテーブルを利用します。 1.VPCのマネジメントコンソールで ルートテーブル をクリック 2.VPC作成時にあわせて作成されたルートテーブルを選択し、タグタブをクリック 3.Nameタグを追加する 4.ルートタブ > ルートの編集 の順にクリック 5.「ルートの追加」をクリックし、以下の通り設定して「ルートの保存」をクリック 送信先:0.0.0.0/0 ターゲット:※作成したインターネットゲートウェイ 6.「ルートテーブルの作成」をクリック 7.以下の通り設定して「作成」をクリック 名前タグ:※任意の名前 VPC:※作成したVPC 8.インターネットゲートウェイへのルートを追加ルートテーブルを選択して アクション > サブネットの関連付けを編集 の順にクリック 9.作成したサブネットをいずれも選択して、「保存」をクリック ※仮想デバイス用のサブネットもデプロイや疎通確認を行うため、検証時以外はパブリックサブネットとする 以上の手順で以下の構成まで完成しました。 1-2 EC2インスタンスの作成 仮想デバイスと踏み台サーバ用にEC2インスタンスを作成します。 また、セキュリティグループも合わせて作成します。 セキュリティグループの作成 以下の要件を満たすようにセキュリティグループを2つ作成します 踏み台サーバはSSHアクセスのみ許可 ※今回は接続元を問わない 仮想デバイスとするサーバは以下を許可 仮想デバイスサーバ間はすべての通信を許可 踏み台サーバからのSSH接続を許可 1.EC2のマネジメントコンソールで セキュリティグループ > 「セキュリティグループを作成」の順にクリック 2.以下の通り設定して「セキュリティグループを作成」をクリック セキュリティグループ名:※任意の名前 説明:※任意の説明 VPC:※作成したVPC インバウンドルール:タイプ SSH / ソース 任意の場所 アウトバウンドルール:タイプ すべてのトラフィック / 送信先 カスタム(0.0.0.0/0)※デフォルト タグ:キー Name / 値 ※任意の名前 3.「セキュリティグループを作成」をクリックし、以下の通り設定して再度「セキュリティグループを作成」をクリック セキュリティグループ名:※任意の名前 説明:※任意の説明 VPC:※作成したVPC インバウンドルール:タイプ SSH / ソース カスタム ※先ほど作成したセキュリティグループを指定 アウトバウンドルール:タイプ すべてのトラフィック / 送信先 カスタム(0.0.0.0/0)※デフォルト タグ:キー Name / 値 ※任意の名前 4.「インバウンドルールを編集」をクリック 5.以下を追加して「ルールを保存」をクリック タイプ:すべてのトラフィック ソース:カスタム ※編集中のセキュリティグループ自身を指定 EC2インスタンス(仮想デバイス用サーバ)の作成 1.EC2のマネジメントコンソールで インスタンス > インスタンスを起動 の順にクリック 2.「Amazon Linux 2 AMI」(64ビット(x86))の「選択」をクリック 3.「t2.micro」を選択し、「次のステップ:インスタンスの詳細の設定」をクリック 4.以下のみ設定を変更し、「次のステップ:ストレージの追加」をクリック インスタンス数:3 ネットワーク:※作成したVPC サブネット:※踏み台サーバ用に作成したVPC 自動割り当てパブリックIP:有効 5.ストレージ設定は変更せず「次のステップ:タグの追加」をクリック 6.Nameタグを追加して「次のステップ:セキュリティグループの設定」をクリック 7.以下の通り設定して「確認と作成」をクリック セキュリティグループの割り当て:既存のセキュリティグループを選択する セキュリティグループ:※仮想デバイスサーバ用に作成したセキュリティグループ 8.「起動」をクリック 9.「新しいキーペアの作成」を選択し、キーペア名を入力して「キーペアのダウンロード」をクリック 10.「インスタンスの作成」をクリック 11.作成されたインスタンスのName列をクリックし、各サーバの役割が分かるように名前をつけて「保存」をクリック ※Nameタグとして追加される EC2インスタンス(踏み台サーバ)の作成 1.EC2のマネジメントコンソールで インスタンス > インスタンスを起動 の順にクリック 2.「Amazon Linux 2 AMI」(64ビット(x86))の「選択」をクリック 3.「t2.micro」を選択し、「次のステップ:インスタンスの詳細の設定」をクリック 4.以下のみ設定を変更し、「次のステップ:ストレージの追加」をクリック ネットワーク:※作成したVPC サブネット:※踏み台サーバ用に作成したVPC 自動割り当てパブリックIP:有効 5.ストレージ設定は変更せず「次のステップ:タグの追加」をクリック 6.Nameタグはここでは追加せず「次のステップ:セキュリティグループの設定」をクリック 7.以下の通り設定して「確認と作成」をクリック セキュリティグループの割り当て:既存のセキュリティグループを選択する セキュリティグループ:※踏み台サーバ用に作成したセキュリティグループ 8.「起動」をクリック 9.「既存のキーペアの選択」を選択し、先ほど作成したキーペアを選択する 10.「選択した~」のチェックボックスを選択し、「インスタンスの作成」をクリック 1-3 接続確認 インスタンスの起動を確認できたら接続確認を行います。 1.EC2のマネジメントコンソールで インスタンス をクリック 2.踏み台サーバを選択し、ネットワーキングタブでパブリックIPv4アドレスをコピーする 3.以下の情報でSSH接続を行う ※今回はTereTermで接続 IPアドレス:※先ほどコピーしたIPアドレス ポート:22 ユーザ名:ec2-user 秘密鍵:インスタンス作成時に取得したキーペア 4.yum upgrade と yum update を実施 sudo yum upgrade -y sudo yum update -y 5.タイムゾーンを変更する 参考:【Linux】タイムゾーン(Timezone)の変更 #タイムゾーンファイルの変更 sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime #タイムゾーンが変更されていることを確認 date Wed Apr 14 18:29:31 JST 2021 #再起動後にUTCに戻ることを防ぐため /etc/sysconfig/clock を編集 sudo vim /etc/sysconfig/clock /etc/sysconfig/clock ZONE="Asia/Tokyo" UTC=false 6.踏み台サーバから仮想デバイス用のサーバにSSH接続するため、キーペアを踏み出サーバにアップロードする ※今回はFileZillaを利用 7.アップロードしたキーペアの権限を変更する chmod 400 [アップロードしたキーペア] 8.EC2のマネジメントコンソールで仮想デバイス用サーバのプライベートIPアドレスをコピーする 9.踏み台サーバからSSH接続する ssh ec2-user@[接続先のプライベートIPアドレス] -i [アップロードするしたキーペア] 10.仮想デバイス用のサーバでも yum upgrade と yum update を実施 11.仮想デバイス用のサーバでもタイムゾーンを変更する 12.検証で仮想デバイス用のサーバに何度も接続するため、接続用に以下のシェルスクリプトを作成した connection.sh #!/bin/bash #set variables USER="ec2-user" KEY="[キーペアのパス]" TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` #select server to connect echo "+----------------------------+" echo "接続先サーバを選択してください" echo "" echo "1 -> test-ggd-001" echo "2 -> test-ggd-002" echo "3 -> test-ggc-server" echo "+----------------------------+" read -p ">" INPUT #set connection information case "$INPUT" in "1") IP_ADDR="[対象サーバのプライベートIPアドレス]"; SERVER="[対象サーバ名]";; "2") IP_ADDR="[対象サーバのプライベートIPアドレス]"; SERVER="[対象サーバ名]";; "3") IP_ADDR="[対象サーバのプライベートIPアドレス]"; SERVER="[対象サーバ名]";; *) echo "[Error]1,2,3 のいずれかを入力してください"; exit 1 esac #connect to target server echo ">$TIMESTAMP $SERVER[$IP_ADDR]にssh接続します..." ssh $USER@$IP_ADDR -i $KEY 以上の手順で以下の構成まで完成しました。 2 Greengrassグループの設定 EC2インスタンスにGreengrassやMQTTクライアントをインストールし、仮想デバイスとして扱えるようにします。 2-1 エッジ環境にGreengrassをインストール まず、Greengrass Coreをインストールします。 インストールにあたっては、AWSのクレデンシャル情報が必要になります。 参考:AWS IoT Greengrass > 開発者ガイド、バージョン 1 > クイックスタート: Greengrass デバイスのセットアップ 1.踏み台サーバ経由で仮想デバイス用サーバにssh接続する 2.クレデンシャル情報を環境変数に設定する #環境変数の設定 export AWS_ACCESS_KEY_ID=[アクセスキー] export AWS_SECRET_ACCESS_KEY=[シークレットアクセスキー] #設定されていることを確認 echo $AWS_ACCESS_KEY_ID echo $AWS_SECRET_ACCESS_KEY 3.インストール用のスクリプトを実行する wget -q -O ./gg-device-setup-latest.sh https://d1onfpft10uf5o.cloudfront.net/greengrass-device-setup/downloads/gg-device-setup-latest.sh && chmod +x ./gg-device-setup-latest.sh && sudo -E ./gg-device-setup-latest.sh bootstrap-greengrass-interactive 4.コマンドプロンプト上でグループ名などをインタラクティブに設定する [GreengrassDeviceSetup] Forwarding command-line parameters: bootstrap-greengrass-interactive #先ほどの手順で環境変数に設定しているため、何も入力せずEnter Enter your AWS access key ID, or press 'Enter' to read it from your environment variables. #先ほどの手順で環境変数に設定しているため、何も入力せずEnter Enter your AWS secret access key, or press 'Enter' to read it from your environment variables. #一時認証情報を利用する場合は必要。そうでない場合は何も入力せずEnter Enter your AWS session token, which is required only when you are using temporary security credentials. Press 'Enter' to read it from your environment variables or if the session token is not required. #グループを作成するリージョンを指定。今回は東京リージョン(ap-northeast-1) Enter the AWS Region where you want to create a Greengrass group, or press 'Enter' to use 'us-west-2'. ap-northeast-1 #作成するグループ名を指定 Enter a name for the Greengrass group, or press 'Enter' to use 'GreengrassDeviceSetup_Group_aeb684fa-7dff-46ea-b68d-c57c0eca94ff'. GGTEST_GROUP #作成するGreengrass Coreの名前を指定 Enter a name for the Greengrass core, or press 'Enter' to use 'GreengrassDeviceSetup_Core_af687bff-efb2-4987-ad77-f37cdc5307b7'. GGTEST_CORE #Greengrass Coreのインストール先を指定。デフォルトでは '/' 配下にgreengrassディレクトリが作成される Enter the installation path for the Greengrass core software, or press 'Enter' to use '/'. #サンプルのLambdaをGreengrassグループに含めるか指定。今回は含めない Do you want to include a Hello World Lambda function and deploy the Greengrass group? Enter 'yes' or 'no'. no #デプロイのタイムアウト時間を指定 Enter a deployment timeout (in seconds), or press 'Enter' to use '180'. #インストール時のログファイルをどこに作成するか指定。デフォルトではカレントディレクトリに GreengrassDeviceSetup-YYYYMMDD-HHMMSS.log が作成される Enter the path for the Greengrass environment setup log file, or press 'Enter' to use './'. 5.以下の通り返ってきたらセットアップ完了 ※エラーが発生した場合は Enter the path for the Greengrass environment setup log file, or press 'Enter' to use './'. で設定したパスに作成されるログファイルを確認する You can now use AWS IoT Console to manage your Greengrass group. 2-2 Greengrassの自動起動設定 インスタンスを停止→起動した際にGreengrassが自動で起動するように設定します。 参考:Greengrass(V1)の自動起動設定&コアデバイスの接続情報をLambdaで取得 1.以下の通りユニットの設定ファイルを作成する #ユニットの設定ファイルを作成するディレクトリに移動 cd /etc/systemd/system #ユニットの設定ファイルの作成 sudo touch greengrass.service 2.作成したユニットの設定ファイルに以下の通り設定を書き込む sudo vim greengrass.service greengrass.service [Unit] Description=Greengrass Daemon [Service] Type=forking PIDFile=/var/run/greengrassd.pid Restart=on-failure ExecStart=/greengrass/ggc/core/greengrassd start ExecReload=/greengrass/ggc/core/greengrassd restart ExecStop=/greengrass/ggc/core/greengrassd stop [Install] WantedBy=multi-user.target 3.サービスを有効化する sudo systemctl enable greengrass.service 2-3 グループにデバイスを追加 Greengrassグループにデバイスを追加します。 参考:Greengrass(V1)のグループにデバイスを追加する 1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック 2.先ほど作成したグループが表示されているので名前をクリック 3.デバイス > デバイスの追加 の順にクリック 4.「新しいデバイスの作成」をクリック 5.任意の名前を入力して、「次へ」をクリック 6.今回は1-clickデプロイを利用することとし、「デフォルトを使用」をクリック 7.「これらのリソースはtar.gzとしてダウンロードしてください」をクリックし、「完了」をクリック ※AmazonのルートCAはのちの手順で取得する 8.手順3~7を繰り返して、デバイスをもう1つ追加する 9.アクション > デプロイ の順にクリック ※Greengrass Coreにグループ情報をデプロイする ※Greengrass Coreが起動していないとデプロイできないため注意 10.初回は検出方法を訊かれるので「自動検出」をクリック 11.「正常に完了しました」と表示されることを確認する 2-4 仮想デバイス用サーバにモノの情報を渡す デバイス情報をEC2インスタンスに転送し、仮想デバイスとして扱えるようにします。 また、Greengrass CoreのMQTTブローカーと通信するために必要なグループ証明書を取得します。 1.取得したtar.gzファイルを踏み台サーバにアップロードする 2.scpコマンドでそれぞれのサーバにtara.gzファイルを送信する #今回は/home/ec2-user/ 配下に送る scp -i [キーペア] xxxxxxxxx-setup.tar.gz ec2-user@[送信先サーバのプライベートIPアドレス]:~/ 3.送信先の仮想デバイス用サーバでtar.gzファイルを解凍する #tar.gzファイルを解凍 tar xvf xxxxxxxxx-setup.tar.gz #.certsディレクトリを作成し、配下に格納 mkdir .certs mv xxxxxxxxx* .certs 4.AmazonのルートCAを取得する #証明書を格納したディレクトリに移動 cd .certs #wgetでAmazonのルートCAを取得 wget "https://www.amazontrust.com/repository/AmazonRootCA1.pem" 5.グループ証明書取得用に以下のPythonファイルを作成する ※/home/ec2-user/.certs 配下に groupCA.crt という名前でグループ証明書を上書きで作成 ※実行しているAPIについては以下を参考  参考:検出レスポンスドキュメントの例 get_groupCA.py import requests import json import argparse #get args psr = argparse.ArgumentParser( description = "get groupCA" ) psr.add_argument("-t", "--things", required=True, help="thing name") psr.add_argument("-c", "--cert", required=True, help="cert file") psr.add_argument("-k", "--key", required=True, help="private key") args = psr.parse_args() things = args.things cert = args.cert key = args.key #set variables for requests url = "https://greengrass-ats.iot.ap-northeast-1.amazonaws.com:8443/greengrass/discover/thing/" + things #set path for CA file CAfile = "/home/ec2-user/.certs/groupCA.crt" #get present group CA response = requests.get(url, cert=(cert, key)) #make groupCA file jsonData = response.json() groupCA = jsonData["GGGroups"][0]['CAs'][0] f = open(CAfile, "w") f.write(groupCA) f.close() 6.作成したPythonファイルを実行して、グループ証明書を取得 python get_groupCA.py --things "モノの名前" --cert "デバイス証明書" --key "秘密鍵" 2-5 Mosquittoのインストール 今回はGreengrassデバイス間のMQTT通信にMosquittoを使います。 1.踏み台サーバ経由で仮想デバイス用サーバに接続する 2.以下のコマンドでMosquittoをインストールする 参考:EC2 の Amazon Linux 2 インスタンスに Mosquitto をインストールして SSL/TLS 認証を設定する sudo amazon-linux-extras install epel -y sudo yum install mosquitto -y 3.自動起動設定を行う sudo systemctl start mosquitto sudo systemctl enable mosquitto 以上の手順で以下の構成まで完成しました。 3 検証① Greengrassデバイス間でMQTT通信 Greengrass(V1)はMQTTブローカーとして、Greengrassデバイス間のMQTT通信を可能にします。 エッジ環境内で完結するはずなので、IoT Coreと接続していない状態でも動くことを確認してみます。 3-1 MQTT通信用シェルスクリプトの作成 Mosquittoを使ってPublish、Subscribeするためのシェルスクリプトを作成します。 今回は、IoT CoreへのPub/Sub、Greengrass CoreへのPub/Sub双方を試すので、シェルスクリプトで分岐処理できるようにしています。 なお、Greengrass Group内はQoS=0、Greengrass CoreからIoT Coreへの通信はQoS=1になる点に注意します。 ※Greengrass Group内のPub/Subを試した際、QoS=1を指定していたところ、エラーが発生した また、今回は検証用ということでGreengrass Coreのエンドポイント(ローカルのIPアドレス)をシェルスクリプト内に直接記載していますが、Greengrass Discovery APIで都度取得することが推奨されています。 参考:Greengrass コアを使用したデバイス認証の管理 1.IoT Coreのマネジメントコンソールで 設定 をクリックし、IoT Coreのエンドポイントを控える 2.仮想デバイス用(Publish側)のサーバで以下のシェルスクリプトを作成 mqtt_pub.sh #!/bin/bash #set common variables CERT_DIR=/home/ec2-user/.certs CERT=$CERT_DIR/[モノの証明書] KEY=$CERT_DIR/[秘密鍵] THING_NAME=[モノの名前] TLS_VERSION=tlsv1.2 PORT=8883 MESSAGE="{\"message\":\"Helloworld\"}" #select target to connect, AWS IoT Core or Greengrass Core echo ">接続先を選択してください" echo ">1 -> Greengrass Core" echo ">2 -> IoT Core" read -p ">" INPUT #input topic echo ">publishするtopicを入力してください" read -p ">" TOPIC #set variables about connection case "$INPUT" in "1") QOS=0; CA=$CERT_DIR/groupCA.crt; ENDPOINT=[Greengrass CoreのローカルIPアドレス];; "2") QOS=1; CA=$CERT_DIR/AmazonRootCA1.pem; ENDPOINT=[IoT Coreのエンドポイント];; *) echo "[Error]1,2 のいずれかを入力してください"; exit 1;; esac #send payload per 5 seconds while : do mosquitto_pub --cafile $CA --cert $CERT --key $KEY \ -i $THING_NAME --tls-version $TLS_VERSION \ -h $ENDPOINT -p $PORT -q $QOS -t $TOPIC -m $MESSAGE -d sleep 5 done 2.実行権限を付与する chmod 744 mqtt_pub.sh 3.仮想デバイス用(Subscribe側)のサーバで以下のシェルスクリプトを作成 mqtt_sub.sh #!/bin/bash #set common variables CERT_DIR=/home/ec2-user/.certs CERT=$CERT_DIR/[モノの証明書] KEY=$CERT_DIR/[秘密鍵] THING_NAME=[モノの名前] TLS_VERSION=tlsv1.2 PORT=8883 #select target to connect, AWS IoT Core or Greengrass Core echo ">接続先を選択してください" echo ">1 -> Greengrass Core" echo ">2 -> IoT Core" read -p ">" INPUT #input topic echo ">subscribeするtopicを入力してください" read -p ">" TOPIC #set variables about connection case "$INPUT" in "1") QOS=0; CA=$CERT_DIR/groupCA.crt; ENDPOINT=[Greengrass CoreのローカルIPアドレス];; "2") QOS=1; CA=$CERT_DIR/AmazonRootCA1.pem; ENDPOINT=[IoT Coreのエンドポイント];; *) echo "[Error]1,2 のいずれかを入力してください"; exit 1;; esac #send payload mosquitto_sub --cafile $CA --cert $CERT --key $KEY \ -i $THING_NAME --tls-version $TLS_VERSION \ -h $ENDPOINT -p $PORT -q $QOS -t $TOPIC -d 4.実行権限を付与する chmod 744 mqtt_sub.sh 3-2 サブスクリプションの設定 Greengrass Coreを介してメッセージをやりとりする場合は、グループのサブスクリプションを設定する必要があります。 1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック 2.対象のグループ名 > サブスクリプション > サブスクリプションの追加 の順にクリック 3.ソースとターゲットを以下の通り選択して、「次へ」をクリック ソース:mqtt_pub.sh を作成した仮想デバイス ターゲット:mqtt_sub.sh を作成した仮想デバイス 4.トピックのフィルターに任意のtopicを入力して「次へ」をクリック 5.内容を確認して「完了」をクリック 6.アクション > デプロイ の順にクリック 3-3 疎通確認(IoT Core経由) Greengrassのグループに登録したモノは、同様にIoT Coreのモノとしても登録されます。 そのため、モノの証明書と秘密鍵でIoT Coreと直接通信できます。 1.IoT Coreのマネジメントコンソールで テスト をクリック 2.トピックのフィルターに任意のtopicを入力して、「サブスクライブ」をクリック 3.mqtt_pub.sh を作成した仮想デバイス用サーバに踏み台サーバ経由で接続する 4.mqtt_pub.sh を実行し、IoT Coreでサブスクライブ中のtopicにメッセージをpublishする #mqtt_pub.shが格納されているディレクトリで実行した場合 ./mqtt_pub.sh >接続先を選択してください >1 -> Greengrass Core >2 -> IoT Core #今回はIoT Core宛にpublishするので2を選ぶ >2 >publishするtopicを入力してください #先程の手順で設定したtopicを入力 >test/pub #5秒おきに以下の通りメッセージを送付する Client test-ggd-001 sending CONNECT Client test-ggd-001 received CONNACK (0) Client test-ggd-001 sending PUBLISH (d0, q1, r0, m1, 'test/pub', ... (24 bytes)) Client test-ggd-001 received PUBACK (Mid: 1, RC:0) Client test-ggd-001 sending DISCONNECT 5.IoT Coreのマネジメントコンソールでサブスクライブされたことを確認する 6.今度はmqtt_sub.sh を作成した仮想デバイス用サーバに踏み台サーバ経由で接続する 7.mqtt_sub.sh を実行し、IoT Coreでサブスクライブ中のtopicにメッセージをsubscribeする #mqtt_pub.shが格納されているディレクトリで実行した場合 ./mqtt_sub.sh >接続先を選択してください >1 -> Greengrass Core >2 -> IoT Core #今回はIoT Core宛にsubscribeするので2を選ぶ >2 >subscribeするtopicを入力してください #任意のtopicを指定 >test/sub #以下の通りサブスクライブが開始される Client test-ggd-002 sending CONNECT Client test-ggd-002 received CONNACK (0) Client test-ggd-002 sending SUBSCRIBE (Mid: 1, Topic: test/sub, QoS: 1, Options: 0x00) Client test-ggd-002 received SUBACK Subscribed (mid: 1): 1 8.IoT Coreのマネジメントコンソールで テスト > トピックに公開する の順にクリック 9.トピック名に先程指定したtopicを入力し、「発行」をクリック 10.メッセージをサブスクライブできることを確認する Client test-ggd-002 received PUBLISH (d0, q0, r0, m0, 'test/sub', ... (57 bytes)) { "message": "AWS IoT コンソールからの挨拶" } ここでは以下の構成で確認を行いました。 3-4 疎通確認(Greengrass Core経由) GreengrassのMQTTブローカー経由でメッセージをやりとりできることを確認します。 この際、サブネットのルートテーブルをローカルのみ通信するものに変更し、エッジ環境が内に閉じている状況でもやりとりできることをあわせて確認します。 1.VPCのマネジメントコンソールで サブネット をクリック 2.仮想デバイス用サーバが起動しているサブネットを選択し ルートテーブルタブ > ルートテーブルの関連付を編集 の順にクリック 3.送信先がlocalのみのルートテーブルを選択して、「保存」をクリック 4.mqtt_pub.sh を作成した仮想サーバと mqtt_sub.sh を作成した仮想サーバそれぞれに踏み台サーバ経由で接続する 5.mqtt_pub.sh 、mqtt_sub.sh がいずれも実行できないことを確認する ※IoT Coreと接続できないため  シェルスクリプトはいずれもプロンプトが返ってこないまま止まるので、Ctrl + C で終了する 6.グループのサブスクリプションで設定したtopic宛にpub/subを実施し、Greengrass CoreのMQTTブローカー経由でやりとりできることを確認する subscribe側 #mqtt_sub.shが格納されているディレクトリで実行した場合 ./mqtt_sub.sh >接続先を選択してください >1 -> Greengrass Core >2 -> IoT Core #今回はGreengrass Core宛にsubscribeするので1を選ぶ >1 >subscribeするtopicを入力してください #グループのサブスクリプションで設定したtopicを指定 >test/pubsub #以下の通りサブスクライブが開始される Client test-ggd-002 sending CONNECT Client test-ggd-002 received CONNACK (0) Client test-ggd-002 sending SUBSCRIBE (Mid: 1, Topic: test/pubsub, QoS: 0, Options: 0x00) Client test-ggd-002 received SUBACK Subscribed (mid: 1): 0 publish側 #mqtt_pub.shが格納されているディレクトリで実行した場合 ./mqtt_pub.sh >接続先を選択してください >1 -> Greengrass Core >2 -> IoT Core #今回はGreengrass Core宛にpublishするので1を選ぶ >1 >publishするtopicを入力してください #グループのサブスクリプションで設定したtopicを指定 >test/pubsub #5秒おきに以下の通りメッセージを送付する Client test-ggd-001 sending CONNECT Client test-ggd-001 received CONNACK (0) Client test-ggd-001 sending PUBLISH (d0, q0, r0, m1, 'test/pubsub', ... (24 bytes)) Client test-ggd-001 received PUBACK (Mid: 1, RC:0) Client test-ggd-001 sending DISCONNECT subsribe側 Client test-ggd-002 received PUBLISH (d0, q0, r1, m0, 'test/pubsub', ... (24 bytes)) {"message":"Helloworld"} ここでは以下の構成で確認を行いました。 4 検証② ローカルLambdaの実行 Greengrassはエッジ環境でLambdaを実行することができます。 こちらも、IoT Coreと接続していない状態でも動くことを確認してみます。 4-1 Greengrass用のLambdaの作成 今回は特定topicへのメッセージをトリガーして起動するLambdaを作成します。 起動後、ローカルにログファイルの出力とIoT CoreへMQTTのメッセージを通知させます。 Lambda作成にあたっては「AWS IoT Greengrass Core SDK for Python」を利用しています。 プログラムの作成 Greengrass SDKの利用にあたっては、デプロイするLambdaの中にSDKのパッケージを含める必要があります。 Greengrass Lambda 関数で SDK を使用するには、AWS Lambda にアップロードする Lambda 関数デプロイパッケージに SDK を含めます。 出典:SDKs 関数のGreengrass Lambda 1.「AWS IoT Greengrass Core SDK for Python」のGitHubレポジトリにアクセスし、Code > Download ZIP の順にクリック Github:AWS IoT Greengrass Core SDK for Python 2.ダウンロードした「aws-greengrass-core-sdk-python-master.zip」を解凍する 3.今回は以下のプログラムを作成する gg_nwtest.py import greengrasssdk import logging import platform import sys from threading import Timer import json import subprocess import datetime logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) client = greengrasssdk.client("iot-data") logfile = "/tmp/gg-nw-test.log" def gg_nwtest(event, context): date = datetime.datetime.now() timestamp = date.strftime("%Y-%m-%d %H:%M:%S") client.publish( topic="test/gg-nwtest", payload=json.dumps( { "function": "gg-nwtest", "message": "This is Test", "version": "1.0", "timestamp": timestamp } ) ) f = open(logfile, mode="a") f.write(timestamp + " success!\n") f.close() 4.作成したプログラムと「greengrasssdk」フォルダをまとめて圧縮 Lambdaの作成 作成したプログラムをLambdaとして登録します。 1.Lambdaのマネジメントコンソールで「関数の作成」をクリック 2.「一から作成」を選択し、以下の通り設定して「関数の作成」をクリック 関数名:※任意の関数名 ランタイム:Python 3.7 3.関数が作成されたら、コードタブで アップロード元 > zipファイル の順にクリック 4.ファイル選択ウィンドウで先ほど作成したzipファイル(プログラムと「greengrasssdk」をまとめて圧縮したもの)を選択して「保存」をクリック ※これにより、以下の通り作成した関数とSDKをLambda関数に反映できる 5.コードタブでランタイム設定の「編集」をクリック 6.ハンドラを「[プログラムファイル名].[関数名]」に修正して「保存」をクリック 7.関数画面で アクション > 新しいバージョンを発行 をクリックし、「発行」をクリックする 7.アクション > エイリアスを作成 をクリック 8.以下の通りエイリアス設定を入力し、「保存」をクリックする 名前:※任意のエイリアス名 バージョン:1 LmabdaをGreengrassグループに登録 作成したLambda関数をGreengrassグループに登録します。 1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック 2.対象のグループ名 > Lambda > 「Lambdaの追加」の順にクリック 3.「既存のLambdaの使用」をクリック 4.Lambdaの選択で先ほど作成したLambdaを選択して、「次へ」をクリック 5.バージョンの選択で先ほど作成したエイリアスを選択して、「完了」をクリック 6.追加したLambdaの「…」 > 設定の編集 の順にクリック 7.Lambdaのライフサイクルで「オンデマンド関数」を選択して、「更新」をクリック ※イベント契機で実行するLambdaとして設定 サブスクリプションの追加 Lambdaからのメッセージ送信、Lambdaへのメッセージ送信をそれぞれ設定します。 サブスクリプションの設定手順の詳細は「3-2 サブスクリプションの設定」をご参照ください。 1.Lambda -> IoT CoreのMQTT通信を許可するため、以下のサブスクリプションをグループに追加する ソース:※グループに追加したLambda ターゲット:IoT Cloud トピック:test/gg-nwtest ※Lambdaからメッセージを送る宛先のtopic 2.Greengrassデバイス -> LambdaのMQTT通信を許可するため、以下のサブスクリプションをグループに追加する ソース:※mqtt_pub.sh を作成した仮想デバイス ターゲット:※グループに追加したLambda トピック:※任意のtopic ローカルリソースアクセスの追加 今回デプロイするLambdaは /tmp ファイルを作成し実行時刻を書き込みます。 Greengrass Coreを実行しているデバイス上のローカルリソースにアクセスするためには、ローカルリソースアクセスを追加します。 参考:Lambda 関数とコネクタを使ってローカルリソースにアクセスする 1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック 2.対象のグループ名 > リソース > 「ローカルリソースの追加」の順にクリック 3.以下の通り設定して、「保存」をクリック リソース名:※任意のリソース名 リソースタイプ:ボリューム ソースパス:/tmp ※ログを書き出す先のディレクトリ(ここにログが書き出される) 送信先パス:/tmp ※Lambdaのコード上で指定したログを書き出す先のディレクトリ グループ所有者のファイルアクセス許可:OSグループなし ※デフォルト Lambda関数の関連:※作成したLambda関数 / 読み取りと書き込みアクセス 4.以上でグループの設定が完了したので、アクション > デプロイ の順にクリック ※「3-4 疎通確認(Greengrass Core経由)」で仮想デバイス用のサブネットのルートテーブルをローカル宛のみのものにしたままの場合は、0.0.0.0/0 -> IGW を含むルートテーブルに切り替える 以上の手順で以下の構成まで完成しました。 4-2 動作確認(Greengrass Device -> Lambda) 仮想デバイス用のサーバがIoT Coreと接続していない状態でも動くことを確認します。 1.IoT Coreのマネジメントコンソールで テスト をクリック 2.LambdaでMQTTでpublishする宛先としたtopicをサブスクライブする 3.Greengrass Coreをインストールした仮想デバイス用サーバ、mqtt_pub.sh を作成した仮想デバイス用サーバにそれぞれ踏み台サーバ経由で接続する 4.mqtt_pub.sh を実行する #mqtt_pub.shが格納されているディレクトリで実行した場合 ./mqtt_pub.sh >接続先を選択してください >1 -> Greengrass Core >2 -> IoT Core #今回はGreengrass Core宛にpublishするので1を選ぶ >1 >publishするtopicを入力してください #グループのサブスクリプションで設定したtopicを指定 >cmd/gg-nwtest 5.以下をそれぞれ確認する IoT Coreでメッセージをサブスクライブできている Greengrass Coreをインストールした仮想デバイス用サーバに /tmp/nw-test.log が作成されている /tmp/nw-test.log に実行時刻が書き込まれている cat /tmp/nw-test.log 2021-04-16 20:06:18 success! 2021-04-16 20:06:23 success! 6.仮想デバイス用のサブネットのルートテーブルをローカル宛のみものに切り替える ※詳しい手順は「3-4 疎通確認(Greengrass Core経由)」を参照 7.以下を確認する IoT Coreにメッセージが届かなくなる /tmp/nw-test.log が継続して更新される #5秒おきにファイル末尾に実行時刻が追記されていくことを確認する tail -F /tmp/nw-test.log ここでは以下の構成で確認を行いました。 5 検証③ Greengrass Core -> IoT Coreのメッセージ保持 GreengrassからIoT Coreへ送付するMQTTメッセージは、未処理の場合キューに保存されます。 そのため、クラウドとの接続が切れてしまっても疎通再開後に送信することができます。 AWS クラウドターゲットを送信先とする MQTT メッセージは、処理待ちとしてキューされます。キュー状態のメッセージは先入れ先出し (FIFO) の順序で処理されます。メッセージが処理され、AWS IoT Core に発行された後、このメッセージはキューから削除されます。 デフォルトでは、Greengrass コアは AWS クラウドターゲット宛ての未処理のメッセージをメモリに保存します。代わりにコアを設定して、未処理のメッセージをメモリあるいはローカルストレージキャッシュに保存できます。インメモリストレージとは異なり、ローカルストレージキャッシュにはコアの再起動の後でも維持される機能があるため (たとえば、グループデプロイ後あるいはデバイスの再起動後など)、AWS IoT Greengrass はメッセージの処理を続けられます。また、ストレージサイズを設定することもできます。 参考:MQTT設定の設定 - クラウドターゲットのMQTTメッセージキュー ここでは上記の挙動を確認します。 5-1 Lambdaの設定変更 先ほど作成したLambdaの設定を変更して、Greengrass Coreをインストールした仮想デバイス用サーバから5秒おきにメッセージを送り続けるようにします。 コードの修正 1.Lambdaのマネジメントコンソールで 関数 > 作成した関数名 の順にクリック 2.コードを以下の通り変更し、「Deploy」をクリック gg_nwtest.py import greengrasssdk import logging import platform import sys from threading import Timer import json import subprocess import datetime logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) client = greengrasssdk.client("iot-data") logfile = "/tmp/gg-nw-test.log" def gg_nwtest(): date = datetime.datetime.now() timestamp = date.strftime("%Y-%m-%d %H:%M:%S") client.publish( topic="test/gg-nwtest", payload=json.dumps( { "function": "gg-nwtest", "message": "This is Test", "version": "1.0", "timestamp": timestamp } ) ) f = open(logfile, mode="a") f.write(timestamp + " success!\n") f.close() Timer(5, gg_nwtest).start() gg_nwtest() 3.アクション > 新しいバージョンを発行 > 「発行」の順にクリック 4.画面上部の関数名 > エイリアスタブ の順にクリック 5.作成済みのエイリアスを選択して、「編集」をクリック 6.先ほど発行したバージョンを選択して、「保存」をクリック ※これにより、GreengrassグループがデプロイするLambdaのバージョンがエイリアスに紐づけたバージョンとなる(GreengrassでLambdaのバージョン指定をエイリアスにしていることが前提) 設定の変更 1.IoT Coreのマネジメントコンソールで Greengrass > クラシック(V1) > グループ の順にクリック 2.対象のグループ名 > Lambda > 対象のLambda関数の「…」 > 設定の編集 の順にクリック 3.Lambdaのライフサイクルで「存続期間が長く無制限に稼働する関数にする」を選択して、「更新」をクリックする 4.アクション > デプロイ の順にクリック 以上の手順で以下の構成まで完成しました。 5-2 動作確認① 仮想デバイス用サーバとIoT Core間の接続を切断→回復し、その間のMQTTメッセージが届くことを確認します。 なお、Greengrassはデフォルトではメッセージをキャッシュに保存しているため再起動によってデータは失われてしまいます。 疎通再開前にGreengrassを再起動した場合の挙動もあわせて確認します。 確認準備 1.IoT Coreのマネジメントコンソールで テスト をクリック 2.トピックをサブスクライブするタブで追加設定をクリックし、以下の設定で「サブスクライブ」をクリック トピックのフィルター:test/gg-nwtest ※LambdaがMQTTメッセージをpublishする宛先topic 保持するメッセージ数:100 ※切断前後、溜めていた分も確認するため多めに設定する その他:※デフォルト設定 3.Greengrass Coreをインストールした仮想デバイス用サーバに踏み台サーバ経由で接続する ※2つのウィンドウで接続しておくと後の確認作業が楽 検証 今回は以下の流れで確認します。 STEP1:IoT Coreでサブスクライブ(スタート地点) STEP2:接続断 STEP3:Greemgrass Core再起動 STEP4:接続再開 STEP2~3の間に生成されたMQTTメッセージ(キャッシュに保持)は再起動によって失われます。 一方で、STEP3~4の間に生成されたMQTTメッセージは、再起動がないためキャッシュ上のメッセージは消えず、IoT Coreに送信されることを確認します。 1.IoT Coreのテスト画面でサブスクライブし続けていることを確認する 2.仮想デバイス用サーバで tail -F /tmp/gg-nwtest.log を実行し、ローカルでLambdaが実行されていることを確認する 3.仮想デバイス用のサブネットのルートテーブルをローカル宛のみものに切り替える ※詳しい手順は「3-4 疎通確認(Greengrass Core経由)」を参照 4.以下を確認する IoT Coreにメッセージが届かなくなる gg-nwtest.log に継続して書き込みが行われている(=Lambdaは継続して動いている) 5.仮想デバイス用サーバでGreengrass Coreを再起動する sudo systemctl restart greengrass 6.tail -F /tmp/gg-nwtest.log でログが再度書き出されることを確認する ※Greengrassが起動し、Lmabdaも実行されたことの確認 tail -F /tmp/gg-nwtest.log #(中略) 2021-04-17 17:31:36 success! 2021-04-17 17:31:41 success! 2021-04-17 17:31:46 success! 2021-04-17 17:31:51 success! 2021-04-17 17:31:56 success! 2021-04-17 17:32:01 success! #接続断から17:32:01までのメッセージは再起動で消失する想定 #17:32:02〜17:32:55の間はGreengrassは再起動中のためメッセージなし #以降のメッセージは接続再開で送信される想定 2021-04-17 17:32:56 success! 2021-04-17 17:33:01 success! 2021-04-17 17:33:06 success! 7.仮想デバイス用のサブネットのルートテーブルをIGW宛を含むものに切り替える 8.IoT Coreに届くメッセージで以下を確認する ネットワーク切断後〜再起動までのメッセージが届かない(=再起動で消失) 再起動後から接続再開後のメッセージは届く 以下は再開後のメッセージですが、2021-04-17 17:32:55までのデータが抜けています。 一方で再起動以降のデータは接続再開後にまとめて送付されています。 5-3 MQTTの設定変更 Greengrassを再起動してもメッセージが消えないように、メッセージキューの保存場所をローカルストレージに変更します。 設定はマネジメントコンソールではできないので、APIを使って行います。 参考:MQTT設定の設定 - ローカルストレージでメッセージをキャッシュするには 1.踏み台サーバに接続する ※今回は踏み台サーバ上でMQTT設定の変更を行う 2.レスポンスのjsonデータを整形するためにjqをインストールする 参考:jqコマンド(jsonデータの加工, 整形)の使い方 sudo yum install jq -y 3.以下のシェルスクリプトを作成する ※公式ドキュメントで実施している手順をシェルスクリプトとして整理したもの mqtt_setting.sh #!/bin/bash #set common variables LOG="/home/ec2-user/mqtt_setting.log" START_TIME=`date "+%Y-%m-%d %H:%M:%S"` #set mqtt settings to define echo "" echo "MQTT設定情報を入力してください" read -p ">設定対象のグループ名:" TARGET_GROUP read -p ">キューの場所(FileSystem or Memory ):" GG_CONFIG_STORAGE_TYPE read -p ">ストレージ容量(262144 バイト以上):" GG_CONFIG_MAX_SIZE_BYTES #check mqtt setting variables if [ $GG_CONFIG_STORAGE_TYPE != "FileSystem" ] && [ $GG_CONFIG_STORAGE_TYPE != "Memory" ] ; then echo "[ERROR]キューの場所はFileSystemかMemoryを選んでください" exit 1 ; fi if [ $GG_CONFIG_MAX_SIZE_BYTES -lt 262144 ] ; then echo "[ERROR]ストレージ容量は262144以上(単位はバイト)にしてください" exit 1 ; fi #get group information GROUP_INFO=`aws greengrass list-groups --query "Groups[?Name=='$TARGET_GROUP']" | jq -c .` LATEST_GROUP_VERSION=`echo $GROUP_INFO | jq -r .[].LatestVersion` GROUP_ID=`echo $GROUP_INFO | jq -r .[].Id` #get present group definition GROUP_DEFINITION=`aws greengrass get-group-version \ --group-id $GROUP_ID \ --group-version-id $LATEST_GROUP_VERSION | jq -c .` #set each definition arn CORE_DEFINITION_ARN=`echo $GROUP_DEFINITION | jq -r .Definition.CoreDefinitionVersionArn` RESOURCE_DEFINITION_ARN=`echo $GROUP_DEFINITION | jq -r .Definition.ResourceDefinitionVersionArn` FUNCTION_DEFINITION_ARN=`echo $GROUP_DEFINITION | jq -r .Definition.FunctionDefinitionVersionArn` DEVICE_DEFINITION_ARN=`echo $GROUP_DEFINITION | jq -r .Definition.DeviceDefinitionVersionArn` LOGGER_DEFINITION_ARN=`echo $GROUP_DEFINITION | jq -r .Definition.LoggerDefinitionVersionArn` SUBSCRIPTION_DEFINITION_ARN=`echo $GROUP_DEFINITION | jq -r .Definition.SubscriptionDefinitionVersionArn` FUNCTION_DEFINITION_ID=`echo $FUNCTION_DEFINITION_ARN | awk -F "/" '{print $5}'` FUNCTION_DEFINITION_VERSION_ID=`echo $FUNCTION_DEFINITION_ARN | awk -F "/" '{print $7}'` #get present lambda functions definition LAMBDA_DEFENITION=`aws greengrass get-function-definition-version \ --function-definition-id $FUNCTION_DEFINITION_ID \ --function-definition-version-id $FUNCTION_DEFINITION_VERSION_ID | jq -c .` LAMBDA_ARN=`echo $LAMBDA_DEFENITION | jq -r .Arn` LAMBDA_FUNCTIONS=`echo $LAMBDA_DEFENITION | jq -c .Definition.Functions` #add function definition NEW_FUNCTIONS=${LAMBDA_FUNCTIONS%]}',{"FunctionArn": "arn:aws:lambda:::function:GGCloudSpooler:1","FunctionConfiguration":{"Environment": {"Variables":{"GG_CONFIG_MAX_SIZE_BYTES":"'$GG_CONFIG_MAX_SIZE_BYTES'","GG_CONFIG_STORAGE_TYPE":"'$GG_CONFIG_STORAGE_TYPE'"}},"Executable": "spooler","MemorySize": 32768,"Pinned": true,"Timeout": 3},"Id": "spooler-function"}]' NEW_FUNCTION_DEFINITION=`aws greengrass create-function-definition-version \ --function-definition-id $FUNCTION_DEFINITION_ID \ --functions "$NEW_FUNCTIONS" | jq -c . ` NEW_FUNCTION_DEFINITION_ARN=`echo $NEW_FUNCTION_DEFINITION | jq -r .Arn` NEW_FUNCTION_DEFINITION_VERSION=`echo $NEW_FUNCTION_DEFINITION | jq -r .Version` #create new group version NEW_GROUP_VERSION=`aws greengrass create-group-version \ --group-id $GROUP_ID \ --core-definition-version-arn $CORE_DEFINITION_ARN \ --function-definition-version-arn $NEW_FUNCTION_DEFINITION_ARN \ --device-definition-version-arn $DEVICE_DEFINITION_ARN \ --logger-definition-version-arn $LOGGER_DEFINITION_ARN \ --resource-definition-version-arn $RESOURCE_DEFINITION_ARN \ --subscription-definition-version-arn $SUBSCRIPTION_DEFINITION_ARN | jq -c .` NEW_GROUP_VERSION_ARN=`echo $NEW_GROUP_VERSION | jq -r .Arn` NEW_GROUP_VERSION_ID=`echo $NEW_GROUP_VERSION | jq -r .Version` #deploy new group DEPROIMENT_INFO=`aws greengrass create-deployment \ --group-id $GROUP_ID \ --group-version-id $NEW_GROUP_VERSION_ID \ --deployment-type NewDeployment | jq -c .` echo ">設定が完了しました!" #logging if [ ! -f $LOG ] ; then touch $LOG fi FINISH_TIME=`date "+%Y-%m-%d %H:%M:%S"` { echo "------------------------------------------------------------------------" echo "####基本情報####" echo "リージョン:$AWS_DEFAULT_REGION" echo "開始時刻:"$START_TIME echo "終了時刻:"$FINISH_TIME echo "" echo "####各変数情報####" echo "設定情報:" echo " キューの場所:"$GG_CONFIG_STORAGE_TYPE echo " ストレージ容量:"$GG_CONFIG_MAX_SIZE_BYTES echo "" echo "変更前グループ情報:" echo " Latest Group Version:"$LATEST_GROUP_VERSION echo " Group ID:"$GROUP_ID echo "" echo "変更前グループ定義ARN:" echo " Core Definition Version Arn:"$CORE_DEFINITION_ARN echo " Resource Definition Version Arn:"$RESOURCE_DEFINITION_ARN echo " Function Definition Version Arn:"$FUNCTION_DEFINITION_ARN echo " Device Definition Version Arn:"$DeviceDefinitionVersionArn echo " Logger Definition Version Arn:"$LoggerDefinitionVersionArn echo " Subscription Definition Version Arn:"$SubscriptionDefinitionVersionArn echo "" echo "変更前Lambda関数定義:" echo " Function Definition ID:"$FUNCTION_DEFINITION_ID echo " Function Definition Version ID:"$FUNCTION_DEFINITION_VERSION_ID echo "" echo "変更後Lambda関数定義:" echo " Function Definition ARN:"$NEW_FUNCTION_DEFINITION_ARN echo " Function Definition Version ID:"$NEW_FUNCTION_DEFINITION_VERSION echo "" echo "変更後グループ情報:" echo " ARN:"$NEW_GROUP_VERSION_ARN echo " Group Version:"$NEW_GROUP_VERSION_ID echo "" echo "####実行コマンドとレスポンス####" echo "aws greengrass list-groups --query \"Groups[?Name==\'$TARGET_GROUP\']\"" echo $GROUP_INFO | jq . echo "" echo "aws greengrass get-group-version \\" echo " --group-id $GROUP_ID \\" echo " --group-version-id $LATEST_GROUP_VERSION" echo $GROUP_DEFINITION | jq . echo "" echo "aws greengrass get-function-definition-versio \\" echo " --function-definition-id $FUNCTION_DEFINITION_ID \\" echo " --function-definition-version-id $FUNCTION_DEFINITION_VERSION_ID" echo $LAMBDA_DEFENITION | jq . echo "" echo "aws greengrass create-function-definition-version \\" echo " --function-definition-id $FUNCTION_DEFINITION_ID \\" echo " --functions $NEW_FUNCTIONS" echo $NEW_FUNCTION_DEFINITION | jq . echo "" echo "aws greengrass create-group-version \\" echo " --group-id $GROUP_ID \\" echo " --core-definition-version-arn $CORE_DEFINITION_ARN \\" echo " --function-definition-version-arn $NEW_FUNCTION_DEFINITION_ARN \\" echo " --device-definition-version-arn $DEVICE_DEFINITION_ARN \\" echo " --logger-definition-version-arn $LOGGER_DEFINITION_ARN \\" echo " --resource-definition-version-arn $RESOURCE_DEFINITION_ARN \\" echo " --subscription-definition-version-arn $SUBSCRIPTION_DEFINITION_ARN" echo $NEW_GROUP_VERSION | jq . echo "" echo "aws greengrass create-deployment \\" echo " --group-id $GROUP_ID \\" echo " --group-version-id $NEW_GROUP_VERSION \\" echo " --deployment-type NewDeployment" echo $DEPROIMENT_INFO | jq . }>>$LOG 4.作成したシェルスクリプトに実行権限を与える chmod 744 mqtt_setting.sh 5.AWS CLIコマンド実行用に環境変数を設定 参考:AWS CLI を設定する環境変数 export AWS_ACCESS_KEY_ID=[AWSのアクセスキーID] export AWS_SECRET_ACCESS_KEY=[AWSのシークレットアクセスキー] export AWS_DEFAULT_REGION=[今回MQTT設定を変更するグループが所属するリージョン] 6.シェルスクリプトを実行する ※実行後 /home/ec2-user/mqtt_setting.log が作成される ./mqtt_setting.sh MQTT設定情報を入力してください #設定対象のグループ名を入力 >設定対象のグループ名: #今回はFileSystemを入力 >キューの場所(FileSystem or Memory ):FileSystem #今回は2621440を入力 >ストレージ容量(262144 バイト以上):2621440 >設定が完了しました! 5-4 動作確認② 「5-2 動作確認①」と同様にして動作確認を行います。 1.IoT Coreのマネジメントコンソールで テスト をクリック 2.トピックをサブスクライブするタブで追加設定をクリックし、以下の設定で「サブスクライブ」をクリック トピックのフィルター:test/gg-nwtest ※LambdaがMQTTメッセージをpublishする宛先topic 保持するメッセージ数:100 ※切断前後、溜めていた分も確認するため多めに設定する その他:※デフォルト設定 3.Greengrass Coreをインストールした仮想デバイス用サーバに踏み台サーバ経由で接続する 4.仮想デバイス用サーバで tail -F /tmp/gg-nwtest.log を実行し、ローカルでLambdaが実行されていることを確認する 5.仮想デバイス用のサブネットのルートテーブルをローカル宛のみものに切り替える ※詳しい手順は「3-4 疎通確認(Greengrass Core経由)」を参照 6.以下を確認する IoT Coreにメッセージが届かなくなる gg-nwtest.log に継続して書き込みが行われている(=Lambdaは継続して動いている) 7.仮想デバイス用サーバでGreengrass Coreを再起動する sudo systemctl restart greengrass 8.tail -F /tmp/gg-nwtest.log でログが再度書き出されることを確認する ※Greengrassが起動し、Lmabdaも実行されたことの確認 tail -F /tmp/gg-nwtest.log #(中略) 2021-04-19 10:10:52 success! 2021-04-19 10:10:57 success! 2021-04-19 10:11:02 success! 2021-04-19 10:11:07 success! 2021-04-19 10:11:12 success! 2021-04-19 10:11:17 success! #接続断から10:11:17までのメッセージも今回は残る想定 #10:11:18〜10:12:08の間はGreengrassは再起動中のためメッセージなし 2021-04-19 10:12:09 success! 2021-04-19 10:12:14 success! 2021-04-19 10:12:19 success! 7.仮想デバイス用のサブネットのルートテーブルをIGW宛を含むものに切り替える 8.IoT Coreにネットワーク切断後〜再起動までのメッセージも含めてメッセージがすべて届くことを確認する 以下の通り、接続再開後に切断前に届いた直後(5秒後)のメッセージが届いています。 再起動してもデータが失われていないことが分かります。 一方で、ログに吐かれていたうち「10:11:12」と「10:11:12」のメッセージは失われています。 sudo systemctl restart greengrass の実行後に書き出されていたので、キャッシュ自体は実行Lambdaより先に止まっていたなど考えられるかもしれません。 6 おわりに Greengrass(V1)とIoT Coreの接続が切れた場合の挙動を確認しました。 仮想エッジ環境の構築も含めて、最近触っていたことを総動員したようなかっこうです。 グループ証明書の取得、MQTT設定の変更など、一部検証にあたってPythonファイル、Lambda、シェルスクリプトの作成を行っています。 上記検証の中で動かしてはいますが、実行して何か問題等あればご連絡ください。 ※MQTT設定用のシェルスクリプトは2回目以降の設定変更用の処理がないので、いずれ修正したい… 7 参考文献(文中で登場していないもの) AWS周りについてではなく、Python、シェルスクリプトを書いた際に参照したものを備忘録として載せておきます。 Python Pythonでファイルの読み込み、書き込み(作成・追記) TypeError: list indices must be integers, not strがわからんかったけどどうにか出来た Advanced Usage - Session Objects PYTHONでコマンドライン引数が受け取れるコマンドを作成してみる シェルスクリプト シェルスクリプトで任意の文字入力待ち→入力→確認→完了 を作成 bashで変数から部分文字列を取得する 【sed / awk / grep】文字列の置換・抽出・検索と正規表現 | Linux Cheat Sheet コマンドプロンプトでAWS CLIを使うときのJSONの取り扱い
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2のRI(リザーブドインスタンス)の上限数について

はじめに 日々の業務で、お客様からさまざまなサービスの上限緩和申請の依頼を受けることがあります。 その中でEC2のコスト削減に利用されるRI(リザーブドインスタンス)の上限数の認識でちょっと引っかかったので備忘録として残します。 いきなりクイズ あるAWSアカウントの東京リージョンにおいて、以下の状況の時、RIの上限はいくつでしょうか?? 答えは20個っ! ではなく、80個です。 えっ? って一瞬なりますよね。私もなりましたw 公式ドキュメントには以下記載があります。 1 か月当たりに購入できる リザーブドインスタンス の数は制限されています。リージョンごとに 1 か月当たり 20 個のリージョン リザーブドインスタンス に追加して、アベイラビリティーゾーンごとに 1 か月当たり 20 個のゾーン リザーブドインスタンス を購入できます。 たとえば、リージョンに 3 つのアベイラビリティーゾーンがある場合、リザーブドインスタンス の制限は 1 か月当たり 80 です。つまり、そのリージョンのリージョン リザーブドインスタンス 20 と、3 つのアベイラビリティーゾーンそれぞれで 20 のゾーン リザーブドインスタンス (20x3=60) です。 これを図解すると、東京リージョンの場合は以下のようになります。 Tips Regional指定のRIがある Region全体で80個が上限となっていても、各AZの上限は20個のため、1つのAZで20個以上の購入は不可 東京リージョンで、RI制限数を20個から30個に変更した場合は、そのリージョンでは合計120個が上限値となる 緩和申請の受理まで数日を要しますので、注意が必要 RIの購入形態(Standard/Convertible) に関わらず全体の合計数が上限となる 参考 EC2のリザーブドインスタンス概要 https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-reserved-instances.html ReagionalとAZでのRIの違い https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/reserved-instances-scope.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon ConnectとAmazon Transcribeの素敵な関係

コールセンター構築サービスであるAmazon Connectと、音声認識システムであるAmazon Transcribeにはいくつかの基本的な組み合わせ方があります。 それぞれの詳細は別記事に譲るとして、3種類を簡単にまとめます。 バッチ音声認識 Amazon Connectの録音機能を用いて音声をS3に保存し、S3上の音声をバッチ処理でAmazon Transcribeに処理させます。 通話終了後に音声ファイルが生成されそこから音声認識が実行されるため、後処理のタイミングで利用するのは難しいですが、精度は一番高くなります。 「オペレータ側の通話」が録音単位であり、IVRでの応答などは録音対象となりません。外線転送も対象となりません。 リアルタイム音声認識 Amazon Connectのストリーム機能を用いて音声をKinesis Video Streamに流し、その音声ストリームをリアルタイムでAmazon Transcribeに処理させます。 通話と並行して音声認識が実行されるため、通話中のオペレータ支援や、通話完了後の後処理に利用することができます。 「ストリームした部分」が録音単位となるので、IVR含めて録音対象とすることができます。 Amazon Lex音声認識 Amazon Lexと組み合わせる場合、コールフロー上でLexを指定するだけで内部的にAmazon Transcribeによる音声認識が実行されます。 「Lexが音声認識した部分」が録音単位となるので、基本的にはLexの精度向上を行うための音声認識です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSONデータからAWS構成図を作図するスクリプト

はじめに AWS構成図の作図や図の更新作業をサクッとできないかと模索してます。模索第1弾としてJSONデータからAWS構成図を作図するスクリプトを作りました。 作ったスクリプトの動作はこんな感じ。通信経路の表示/非表示も簡単に切り替えできます。 参考情報 今回は、以前作成した記事のコードをベースにスクリプトを作成しました。 Webサーバ無しHTML(JavaScript)でJSONデータを扱う スクリプトソース 作成したスクリプトは以下のgithubに格納しました。ソースをダウンロードし、AwsDiagram.htmlを実行するとサンプルが動きます。WEBサーバは不要です。 Handling_JSON_with_no_web_server_HTML-JavaScript- 仕組み 以下の図のように、X軸とY軸で座標があって、各座標にはレイヤーがあります。この座標とレイヤーを指定して図形を描画します。 描画する図は、座標とレイヤーを指定することで描画する場所が指定されます。それらを設定するのが設定するのがlink.json.jsになります。link.json.jsを更新すれば作図内容を変更できます。 link.json.jsの作図例としてサンプル(1)~(5)を紹介します。 サンプル(1)EC2アイコンを4つ配置する 基本的なルールだけで描画したサンプル、EC2アイコンを4つ配置します。 link.json.jsです。 link.json_01.js let list = [ { "OBJECT": "NONE", "TYPE": "INIT", "MaxLayer": 6, "BaseWidth": 2, "CellWidth": 20, "EC2" : ["ec2.png","none","0.3","none","CENTER"], }, {"OBJECT": "EC2-11","TYPE": "FIG","SET": "EC2","LAYER": 1,"TopX": "1","TopY": "1","BtmX": "1","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-12","TYPE": "FIG","SET": "EC2","LAYER": 1,"TopX": "1","TopY": "2","BtmX": "1","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-21","TYPE": "FIG","SET": "EC2","LAYER": 1,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-22","TYPE": "FIG","SET": "EC2","LAYER": 1,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, ]; link.json.jsの基本は、以下の A, B, C の3つです。 Aについて "OBJECT": "NONE" があるデータ部は、全体の定義を設定します。ここに、レイヤーの最大数やEC2アイコンの描画規則などを設定します。 変数名 説明 OBJECT NONE を設定すると、全体の定義を設定 TYPE INIT を記載します MaxLayer レイヤーの最大数を設定 BaseWidth 描画する図形の基本サイズ CellWidth 描画する図形の基本サイズ EC2 EC2アイコンの描画規則 Bについて EC2アイコンを描画する規則を設定しています。 値 意味 ec2.png 描画する画像データ none 面の色 none は色なし 0.3 面の色の透明度 none 線の色 none は色なし CENTER 描画する画像データの配置場所。選択肢は、HEAD,CENTER の2つ Cについて 配置するアイコンの位置、レイヤー、描画規則を設定します。 変数名 説明 OBJECT データ名 TYPE データのタイプを指定。FIG は図形の意味 SET "OBJECT": "NONE"で定義した定義の名前。 LAYER 描画するレイヤーを指定 TopX 描画する図形の左上のX座標 TopY 描画する図形の左上のY座標 BtmX 描画する図形の右下のX座標 BtmY 描画する図形の右下のY座標 Width 固定値 0 を設定(スクリプトが、実行時に適切な値に更新) Height 固定値 0 を設定(スクリプトが、実行時に適切な値に更新) 指定するレイヤーを変更すると、描画するサイズも変わる。先ほどのjsonでLAYER値を変更した場合、このようになります。 link.json_01-2.js let list = [ { "OBJECT": "NONE", "TYPE": "INIT", "MaxLayer": 6, "BaseWidth": 2, "CellWidth": 20, "EC2" : ["ec2.png","none","0.3","none","CENTER"], }, {"OBJECT": "EC2-11","TYPE": "FIG","SET": "EC2","LAYER": 1,"TopX": "1","TopY": "1","BtmX": "1","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-12","TYPE": "FIG","SET": "EC2","LAYER": 2,"TopX": "1","TopY": "2","BtmX": "1","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-21","TYPE": "FIG","SET": "EC2","LAYER": 3,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-22","TYPE": "FIG","SET": "EC2","LAYER": 4,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, ]; サンプル(2)Web3層のネットワーク図 Web3層のAWSネットワーク図のサンプルです。 link.json_02.js let list = [ { "OBJECT": "NONE", "TYPE": "INIT", "MaxLayer": 6, "BaseWidth": 2, "CellWidth": 20, "VPC" : ["vpc.png", "none" ,"0.0","#000000","HEAD"], "AZ" : ["az.png", "none" ,"0.0","#5b9cd5","HEAD"], "PUB" : ["subnet-public.png", "#1f860f","0.2","#1f860f","HEAD"], "PRI" : ["subnet-private.png","#147eba","0.2","#147eba","HEAD"], }, {"OBJECT": "VPC-1", "TYPE": "FIG", "SET": "VPC","LAYER": 5,"TopX": "2","TopY": "1","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "AZ-1", "TYPE": "FIG", "SET": "AZ","LAYER": 4,"TopX": "2","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "AZ-2", "TYPE": "FIG", "SET": "AZ","LAYER": 4,"TopX": "2","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-01","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-02","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-11","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "1","BtmX": "3","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-12","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-21","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-22","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, ]; 長方形は、TopX,TopYとBtmX,BtmYの値によって設定します。長方形はVPCやAZなどの描画に使用できます。 サンプル(3) 線を描画する AWS構成図にデータの通信経路を描画する方法です。通信経路は描画のON/OFFを切り替えできます。 ライン(1)を描画した場合 ライン(2)を描画した場合 JSONデータはこちら link.json_03.js let list = [ { "OBJECT": "NONE", "TYPE": "INIT", "MaxLayer": 6, "BaseWidth": 2, "CellWidth": 20, "VPC" : ["vpc.png", "none" ,"0.0","#000000","HEAD"], "AZ" : ["az.png", "none" ,"0.0","#5b9cd5","HEAD"], "PUB" : ["subnet-public.png", "#1f860f","0.2","#1f860f","HEAD"], "PRI" : ["subnet-private.png","#147eba","0.2","#147eba","HEAD"], }, {"OBJECT": "Subnet-01","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-02","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-11","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "1","BtmX": "3","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-12","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-21","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-22","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"TYPE": "LINE","OBJECT": "LINE-01","LINE": ["Subnet-01","Subnet-11","Subnet-21"],}, {"TYPE": "LINE","OBJECT": "LINE-02","LINE": ["Subnet-01","Subnet-11","Subnet-21","Subnet-22","Subnet-12","Subnet-02","Subnet-01"],}, {"TYPE": "LINE","OBJECT": "LINE-03","LINE": ["Subnet-01","Subnet-22"],}, {"TYPE": "LINE","OBJECT": "LINE-04","LINE": ["Subnet-02","Subnet-21"],}, {"TYPE": "LINE-GRP","LAVEL": "ライン(1)","STROKE": "#000080","LINES": ["LINE-01"],}, {"TYPE": "LINE-GRP","LAVEL": "ライン(2)","STROKE": "#FF8FFF","LINES": ["LINE-02","LINE-03","LINE-04"],}, ]; 線の描画は、"TYPE": "LINE" と "TYPE": "LINE-GRP" の2つのデータを用意します。 "TYPE": "LINE"は、変数 OBJECT に名前を設定し、変数 LINE に線を引くOBJECT名を指定します。以下は、OBJECT "Subnet-01","Subnet-11","Subnet-21" の順に線を引く設定です。 {"TYPE": "LINE","OBJECT": "LINE-01","LINE": ["Subnet-01","Subnet-11","Subnet-21"],} "TYPE": "LINE-GRP"は以下のように、変数 LINES に LINE名 を指定します。STROKE は線の色を指定します。 {"TYPE": "LINE-GRP","LAVEL": "ライン(1)","STROKE": "#000080","LINES": ["LINE-01"],} サンプル(4)それっぽいAWS構成図 Web3層に、ログ収集、オンプレ環境の端末からのアクセス、エンドユーザーからの通信経路などを盛り込んだ構成図を描いてみました。 jsonデータはちょっと長いですが、やっていることはこれまで紹介したことと同じです。 link.json_04.js let list = [ { "OBJECT": "NONE", "TYPE": "INIT", "MaxLayer": 6, "BaseWidth": 2, "CellWidth": 20, "OFFICE" : ["office.png", "#000000","0.1","#000000","HEAD"], "VPC" : ["vpc.png", "none" ,"0.0","#000000","HEAD"], "AZ" : ["az.png", "none" ,"0.0","#5b9cd5","HEAD"], "PUB" : ["subnet-public.png", "#1f860f","0.2","#1f860f","HEAD"], "PRI" : ["subnet-private.png","#147eba","0.2","#147eba","HEAD"], "ALB" : ["alb.png", "#d86613","0.3","#d86613","CENTER"], "SG" : ["sg.png", "none" ,"0.0","#df3312","HEAD"], "EC2" : ["ec2.png", "none" ,"0.3","none","CENTER"], "CW" : ["cw.png", "none" ,"0.3","none","CENTER"], "SNS" : ["sns.png", "none" ,"0.3","none","CENTER"], "S3" : ["s3.png", "#000000" ,"0.5","none","CENTER"], "RDS" : ["rds.png", "none" ,"0.3","none","CENTER"], "USERS":["users.png", "none" ,"0.3","none","CENTER"], "PC":["pc.png", "#000000","0.5","none","CENTER"], "ALARM":["alarm.png", "none","0.5","none","CENTER"], }, {"OBJECT": "USERS","TYPE": "FIG", "SET": "USERS","LAYER": 1,"TopX": "1","TopY": "1","BtmX": "1","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "VPC-1", "TYPE": "FIG", "SET": "VPC","LAYER": 5,"TopX": "2","TopY": "1","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "AZ-1", "TYPE": "FIG", "SET": "AZ","LAYER": 4,"TopX": "2","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "AZ-2", "TYPE": "FIG", "SET": "AZ","LAYER": 4,"TopX": "2","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-01","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-02","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-11","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "1","BtmX": "3","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-12","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-21","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-22","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "ALB-01","TYPE": "FIG", "SET": "ALB","LAYER": 1,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "SG-01","TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "S3-01","TYPE": "FIG", "SET": "S3","LAYER": 1,"TopX": "2","TopY": "3","BtmX": "2","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-11","TYPE": "FIG", "SET": "EC2","LAYER": 1,"TopX": "3","TopY": "1","BtmX": "3","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-12","TYPE": "FIG", "SET": "EC2","LAYER": 1,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "SG-11","TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "3","TopY": "1","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "CW-11","TYPE": "FIG", "SET": "CW","LAYER": 1,"TopX": "3","TopY": "3","BtmX": "3","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "RDS-21","TYPE": "FIG", "SET": "RDS","LAYER": 1,"TopX": "4","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "RDS-22","TYPE": "FIG", "SET": "RDS","LAYER": 1,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "SG-21", "TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "4","TopY": "1","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "ALARM-01","TYPE": "FIG", "SET": "ALARM","LAYER": 1,"TopX": "4","TopY": "3","BtmX": "4","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "SNS-01","TYPE": "FIG", "SET": "SNS","LAYER": 1,"TopX": "5","TopY": "3","BtmX": "5","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "VPC-2", "TYPE": "FIG", "SET": "VPC","LAYER": 5,"TopX": "5","TopY": "1","BtmX": "5","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-31","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "5","TopY": "1","BtmX": "5","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-31","TYPE": "FIG", "SET": "EC2","LAYER": 1,"TopX": "5","TopY": "1","BtmX": "5","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "SG-31","TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "5","TopY": "1","BtmX": "5","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "OFFICE","TYPE": "FIG", "SET": "OFFICE","LAYER": 5,"TopX": "6","TopY": "1","BtmX": "6","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "PC-01","TYPE": "FIG", "SET": "PC","LAYER": 1,"TopX": "6","TopY": "1","BtmX": "6","BtmY": "1","Width" : 0,"Height": 0,}, {"TYPE": "LINE","OBJECT": "LINE-A1","LINE": ["ALB-01","EC2-11","RDS-21"],}, {"TYPE": "LINE","OBJECT": "LINE-A2","LINE": ["ALB-01","EC2-12","RDS-21"],}, {"TYPE": "LINE","OBJECT": "LINE-B1","LINE": ["RDS-21","RDS-22"],}, {"TYPE": "LINE","OBJECT": "LINE-A0","LINE": ["USERS","ALB-01"],}, {"TYPE": "LINE","OBJECT": "LINE-M0","LINE": ["PC-01","EC2-31","RDS-21"],}, {"TYPE": "LINE","OBJECT": "LINE-M1","LINE": ["EC2-31","RDS-22"],}, {"TYPE": "LINE","OBJECT": "LINE-M2","LINE": ["EC2-31","EC2-11"],}, {"TYPE": "LINE","OBJECT": "LINE-M3","LINE": ["EC2-31","EC2-12"],}, {"TYPE": "LINE","OBJECT": "LOG-01","LINE": ["ALB-01","S3-01"],}, {"TYPE": "LINE","OBJECT": "LOG-02","LINE": ["EC2-11","CW-11","S3-01"],}, {"TYPE": "LINE","OBJECT": "LOG-03","LINE": ["EC2-12","CW-11"],}, {"TYPE": "LINE","OBJECT": "LOG-04","LINE": ["RDS-21","CW-11"],}, {"TYPE": "LINE","OBJECT": "ALARM-01","LINE": ["CW-11","ALARM-01","SNS-01","PC-01"],}, {"TYPE": "LINE-GRP","LAVEL": "処理経路","STROKE": "#000080","LINES": ["LINE-A0","LINE-A1","LINE-A2"],}, {"TYPE": "LINE-GRP","LAVEL": "冗長化","STROKE": "#800000","LINES": ["LINE-B1"],}, {"TYPE": "LINE-GRP","LAVEL": "管理経路","STROKE": "#800000","LINES": ["LINE-M0","LINE-M1","LINE-M2","LINE-M3"],}, {"TYPE": "LINE-GRP","LAVEL": "ログ収集","STROKE": "#800000","LINES": ["LOG-01","LOG-02","LOG-03","LOG-04"],}, {"TYPE": "LINE-GRP","LAVEL": "障害通知","STROKE": "#800000","LINES": ["ALARM-01"],}, ]; サンプル(5)少し複雑なAWS構成図 少し複雑なAWS構成図です。以下の図は通信経路(破線)をすべて表示しています(通信経路(破線)の表示/非表示は切り替え可)。 jsonデータは少し長くなりましたが、これまでのサンプルと同じ規則なので"複雑さ"は変わってないです。 link.json_05.js let list = [ { "OBJECT": "NONE", "TYPE": "INIT", "MaxLayer": 7, "BaseWidth": 2, "CellWidth": 20, "OFFICE" : ["office.png", "#000000","0.1","#000000","HEAD"], "AWS" : ["aws.png", "none" ,"0.0","#000000","HEAD"], "VPC" : ["vpc.png", "none" ,"0.0","#000000","HEAD"], "AZ" : ["az.png", "none" ,"0.0","#5b9cd5","HEAD"], "PUB" : ["subnet-public.png", "#1f860f","0.2","#1f860f","HEAD"], "PRI" : ["subnet-private.png","#147eba","0.2","#147eba","HEAD"], "ALB" : ["alb.png", "#d86613","0.3","#d86613","CENTER"], "SG" : ["sg.png", "none" ,"0.0","#df3312","HEAD"], "NATGW": ["natgw.png", "none" ,"0.0","none","CENTER"], "EC2" : ["ec2.png", "none" ,"0.3","none","CENTER"], "CW" : ["cw.png", "none" ,"0.3","none","CENTER"], "SNS" : ["sns.png", "none" ,"0.3","none","CENTER"], "S3" : ["s3.png", "#000000" ,"0.5","none","CENTER"], "RDS" : ["rds.png", "none" ,"0.3","none","CENTER"], "USERS":["users.png", "none" ,"0.3","none","CENTER"], "PC1":["pc.png", "#CCCCFF","0.3","none","HEAD"], "PC2":["pc.png", "none","0.0","none","CENTER"], "ALARM":["alarm.png", "none","0.0","none","CENTER"], "MAIL":["mail.png", "none","0.0","none","CENTER"], "CPIPE":["cpipeline.png", "none","0.0","#000000","HEAD"], "CCOM":["ccommit.png", "none","0.0","none","CENTER"], "CBLD":["cbuild.png", "none","0.0","none","CENTER"], "CDEP":["cdeploy.png", "none","0.0","none","CENTER"], }, {"OBJECT": "USERS","TYPE": "FIG", "SET": "USERS","LAYER": 1,"TopX": "1","TopY": "2","BtmX": "1","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "AWS-1", "TYPE": "FIG", "SET": "AWS","LAYER": 6,"TopX": "2","TopY": "1","BtmX": "5","BtmY": "4","Width" : 0,"Height": 0,}, {"OBJECT": "VPC-1", "TYPE": "FIG", "SET": "VPC","LAYER": 5,"TopX": "2","TopY": "2","BtmX": "4","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "AZ-1", "TYPE": "FIG", "SET": "AZ","LAYER": 4,"TopX": "2","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "AZ-2", "TYPE": "FIG", "SET": "AZ","LAYER": 4,"TopX": "2","TopY": "3","BtmX": "4","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-01","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-02","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "2","TopY": "3","BtmX": "2","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-11","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-12","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "3","TopY": "3","BtmX": "3","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-21","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-22","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "4","TopY": "3","BtmX": "4","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "ALB-01","TYPE": "FIG", "SET": "ALB","LAYER": 1,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "SG-01","TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "2","TopY": "2","BtmX": "2","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "S3-01","TYPE": "FIG", "SET": "S3","LAYER": 1,"TopX": "2","TopY": "4","BtmX": "2","BtmY": "4","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-11","TYPE": "FIG", "SET": "EC2","LAYER": 1,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-12","TYPE": "FIG", "SET": "EC2","LAYER": 1,"TopX": "3","TopY": "3","BtmX": "3","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "SG-11","TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "3","TopY": "2","BtmX": "3","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "CW-11","TYPE": "FIG", "SET": "CW","LAYER": 1,"TopX": "3","TopY": "4","BtmX": "3","BtmY": "4","Width" : 0,"Height": 0,}, {"OBJECT": "RDS-21","TYPE": "FIG", "SET": "RDS","LAYER": 1,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "RDS-22","TYPE": "FIG", "SET": "RDS","LAYER": 1,"TopX": "4","TopY": "3","BtmX": "4","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "SG-21", "TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "4","TopY": "2","BtmX": "4","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "ALARM-01","TYPE": "FIG", "SET": "ALARM","LAYER": 1,"TopX": "4","TopY": "4","BtmX": "4","BtmY": "4","Width" : 0,"Height": 0,}, {"OBJECT": "SNS-01","TYPE": "FIG", "SET": "SNS","LAYER": 1,"TopX": "5","TopY": "4","BtmX": "5","BtmY": "4","Width" : 0,"Height": 0,}, {"OBJECT": "VPC-2", "TYPE": "FIG", "SET": "VPC","LAYER": 5,"TopX": "5","TopY": "2","BtmX": "5","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-31","TYPE": "FIG", "SET": "PUB","LAYER": 3,"TopX": "5","TopY": "2","BtmX": "5","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "Subnet-32","TYPE": "FIG", "SET": "PRI","LAYER": 3,"TopX": "5","TopY": "3","BtmX": "5","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "NATGW-01","TYPE": "FIG", "SET": "NATGW","LAYER": 1,"TopX": "5","TopY": "2","BtmX": "5","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "EC2-31","TYPE": "FIG", "SET": "EC2","LAYER": 1,"TopX": "5","TopY": "3","BtmX": "5","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "SG-31","TYPE": "FIG", "SET": "SG","LAYER": 2,"TopX": "5","TopY": "3","BtmX": "5","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "OFFICE","TYPE": "FIG", "SET": "OFFICE","LAYER": 6,"TopX": "6","TopY": "2","BtmX": "6","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "PC-01","TYPE": "FIG", "SET": "PC1","LAYER": 5,"TopX": "6","TopY": "2","BtmX": "6","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "PC-02","TYPE": "FIG", "SET": "PC2","LAYER": 2,"TopX": "6","TopY": "2","BtmX": "6","BtmY": "2","Width" : 0,"Height": 0,}, {"OBJECT": "ML-01","TYPE": "FIG", "SET": "MAIL","LAYER": 1,"TopX": "6","TopY": "3","BtmX": "6","BtmY": "3","Width" : 0,"Height": 0,}, {"OBJECT": "CP-01","TYPE": "FIG", "SET": "CPIPE","LAYER": 5,"TopX": "2","TopY": "1","BtmX": "5","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "CD-01","TYPE": "FIG", "SET": "CDEP","LAYER": 1,"TopX": "2","TopY": "1","BtmX": "2","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "S3-02","TYPE": "FIG", "SET": "S3","LAYER": 1,"TopX": "3","TopY": "1","BtmX": "3","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "CB-01","TYPE": "FIG", "SET": "CBLD","LAYER": 1,"TopX": "4","TopY": "1","BtmX": "4","BtmY": "1","Width" : 0,"Height": 0,}, {"OBJECT": "CM-01","TYPE": "FIG", "SET": "CCOM","LAYER": 1,"TopX": "5","TopY": "1","BtmX": "5","BtmY": "1","Width" : 0,"Height": 0,}, {"TYPE": "LINE","OBJECT": "LINE-A1","LINE": ["ALB-01","EC2-11","RDS-21"],}, {"TYPE": "LINE","OBJECT": "LINE-A2","LINE": ["ALB-01","EC2-12","RDS-21"],}, {"TYPE": "LINE","OBJECT": "LINE-B1","LINE": ["RDS-21","RDS-22"],}, {"TYPE": "LINE","OBJECT": "LINE-A0","LINE": ["USERS","ALB-01"],}, {"TYPE": "LINE","OBJECT": "LINE-M0","LINE": ["PC-02","EC2-31","RDS-21"],}, {"TYPE": "LINE","OBJECT": "LINE-M1","LINE": ["EC2-31","RDS-22"],}, {"TYPE": "LINE","OBJECT": "LINE-M2","LINE": ["EC2-31","EC2-11"],}, {"TYPE": "LINE","OBJECT": "LINE-M3","LINE": ["EC2-31","EC2-12"],}, {"TYPE": "LINE","OBJECT": "LOG-01","LINE": ["ALB-01","S3-01"],}, {"TYPE": "LINE","OBJECT": "LOG-02","LINE": ["EC2-11","CW-11","S3-01"],}, {"TYPE": "LINE","OBJECT": "LOG-03","LINE": ["EC2-12","CW-11"],}, {"TYPE": "LINE","OBJECT": "LOG-04","LINE": ["RDS-21","CW-11"],}, {"TYPE": "LINE","OBJECT": "ALARM-01","LINE": ["CW-11","ALARM-01","SNS-01","ML-01"],}, {"TYPE": "LINE","OBJECT": "DEP-01","LINE": ["PC-02","EC2-31","NATGW-01", "CM-01", "CB-01", "S3-02", "CD-01", "EC2-11"],}, {"TYPE": "LINE","OBJECT": "DEP-02","LINE": ["CD-01", "EC2-12"],}, {"TYPE": "LINE-GRP","LAVEL": "処理経路","STROKE": "#000080","LINES": ["LINE-A0","LINE-A1","LINE-A2"],}, {"TYPE": "LINE-GRP","LAVEL": "冗長化","STROKE": "#008000","LINES": ["LINE-B1"],}, {"TYPE": "LINE-GRP","LAVEL": "管理経路","STROKE": "#80FF00","LINES": ["LINE-M0","LINE-M1","LINE-M2","LINE-M3"],}, {"TYPE": "LINE-GRP","LAVEL": "ログ収集","STROKE": "#F0F0F9","LINES": ["LOG-01","LOG-02","LOG-03","LOG-04"],}, {"TYPE": "LINE-GRP","LAVEL": "障害通知","STROKE": "#981234","LINES": ["ALARM-01"],}, {"TYPE": "LINE-GRP","LAVEL": "デプロイ","STROKE": "#AABBCC","LINES": ["DEP-01","DEP-02"],}, ]; おわりに デザインの修正や、CIDRや許可した通信ポート設定などの情報も表示するようにしたりと改善したいことはいろいろあります。マウスで作図できるようにするのもいいかもしれません。更新版ができたらまた紹介しようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む