- 投稿日:2020-12-18T23:51:19+09:00
privateのAmazonLinux2からyumとExtrasLibraryを利用する方法
AmazonLinux2からAWSが用意するAMIリポジトリにアクセスする方法です。
AMIリポジトリにVPC内からアクセス可能にする
インターネットアクセス可能な場合は特に考慮不要。
VPC内部からアクセスする場合は、VPCエンドポイントを利用しますが
リポジトリはS3に存在するのでS3へのアクセス許可が必要です。必要な作業は以下の2つです。
1.AmazonLinux2が存在するVPCにVPCエンドポイントを作成する
2.VPCエンドポイントにS3へのアクセス許可を付与する。以下参照。VPCエンドポイントポリシー例.json{ "Statement": [ { "Sid": "Amazon Linux AMI Repository Access", "Principal": "*", "Action": [ "s3:GetObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::packages.*.amazonaws.com/*", "arn:aws:s3:::repo.*.amazonaws.com/*" ] } ] }Extras Libraryの利用
AmazonLinux2ではさらに「Extras Library(amazon-linux-extras)」
と呼ばれる仕組みがあり、「トピック」という単位でパッケージが管理されています。
依存関係を考慮してバンドルされているのでトピック名を指定してインストールすることで
関連パッケージをまとめてインストルすることが可能です。準備されているトピックを確認する.amazon-linux-extrasトピックの情報を確認する.amazon-linux-extras info トピック名インストール.sudo amazon-linux-extras install トピック名EPELを有効にする
epelを有効にするには「amazon-linux-extras」を利用します。
利用するにはインターネットへのアクセスが必要です。sudo amazon-linux-extras install epel
- 投稿日:2020-12-18T23:47:23+09:00
AWS の EC2 Graviton2 インスタンスに MySQL をインストールしてみた(だけ)
これは MySQL Advent Calendar 2020 17 日目のエントリです(後から穴埋め)。
AWS では今年、EC2・RDS に 64-bit Arm ベースの Graviton2 プロセッサを採用したインスタンスを立てられるようになりましたので、EC2 の r6g.large インスタンス(2vCPU・メモリ 16GiB)に MySQL Server 8.0.22 をインストールしてみました。
※本当にインストールしただけです(続きは後日)。
EC2 インスタンス起動
まずは 64-bit Arm 向けの CentOS 8.3(あえて Stream ではない)を選んで、
r6g.large インスタンスを選択して…
ストレージは gp3 を選んでインスタンスを立ててみました。
MySQL Community Server 8.0.22 インストール
x86_64 系プロセッサを使う場合とほぼ同様に MySQL Community Server 8.0.22 をインストールしていきます。
※適宜、言語・ロケールなどの設定を行ってください(ここでは省略)。
まずはwgetを入れる$ sudo dnf install wget Last metadata expiration check: 0:01:08 ago on Fri 18 Dec 2020 12:56:37 PM UTC. Dependencies resolved. ================================================================================ Package Architecture Version Repository Size ================================================================================ Installing: wget aarch64 1.19.5-10.el8 appstream 716 k Transaction Summary ================================================================================ Install 1 Package Total download size: 716 k Installed size: 2.8 M Is this ok [y/N]: y Downloading Packages: wget-1.19.5-10.el8.aarch64.rpm 1.0 MB/s | 716 kB 00:00 -------------------------------------------------------------------------------- Total 608 kB/s | 716 kB 00:01 warning: /var/cache/dnf/appstream-ad81255f8cddaa99/packages/wget-1.19.5-10.el8.aarch64.rpm: Header V3 RSA/SHA256 Signature, key ID 8483c65d: NOKEY CentOS Linux 8 - AppStream 1.6 MB/s | 1.6 kB 00:00 Importing GPG key 0x8483C65D: Userid : "CentOS (CentOS Official Signing Key) <security@centos.org>" Fingerprint: 99DB 70FA E1D7 CE22 7FB6 4882 05B5 55B3 8483 C65D From : /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial Is this ok [y/N]: y Key imported successfully Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : wget-1.19.5-10.el8.aarch64 1/1 Running scriptlet: wget-1.19.5-10.el8.aarch64 1/1 Verifying : wget-1.19.5-10.el8.aarch64 1/1 Installed: wget-1.19.5-10.el8.aarch64 Complete!MySQLのリポジトリを追加$ wget https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm --2020-12-18 12:57:54-- https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm Resolving dev.mysql.com (dev.mysql.com)... 137.254.60.11 Connecting to dev.mysql.com (dev.mysql.com)|137.254.60.11|:443... connected. HTTP request sent, awaiting response... 302 Found Location: https://repo.mysql.com//mysql80-community-release-el8-1.noarch.rpm [following] --2020-12-18 12:57:55-- https://repo.mysql.com//mysql80-community-release-el8-1.noarch.rpm Resolving repo.mysql.com (repo.mysql.com)... 23.45.57.22 Connecting to repo.mysql.com (repo.mysql.com)|23.45.57.22|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 30388 (30K) [application/x-redhat-package-manager] Saving to: ‘mysql80-community-release-el8-1.noarch.rpm’ mysql80-community-r 100%[===================>] 29.68K --.-KB/s in 0.001s 2020-12-18 12:57:55 (32.6 MB/s) - ‘mysql80-community-release-el8-1.noarch.rpm’ saved [30388/30388] $ sudo dnf localinstall mysql80-community-release-el8-1.noarch.rpm Last metadata expiration check: 0:01:37 ago on Fri 18 Dec 2020 12:56:37 PM UTC. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: mysql80-community-release noarch el8-1 @commandline 30 k Transaction Summary ================================================================================ Install 1 Package Total size: 30 k Installed size: 29 k Is this ok [y/N]: y Downloading Packages: Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : mysql80-community-release-el8-1.noarch 1/1 Verifying : mysql80-community-release-el8-1.noarch 1/1 Installed: mysql80-community-release-el8-1.noarch Complete! $ sudo dnf module disable mysql MySQL 8.0 Community Server 3.0 MB/s | 1.1 MB 00:00 MySQL Connectors Community 127 kB/s | 43 kB 00:00 MySQL Tools Community 246 kB/s | 79 kB 00:00 Dependencies resolved. ================================================================================ Package Architecture Version Repository Size ================================================================================ Disabling modules: mysql Transaction Summary ================================================================================ Is this ok [y/N]: y Complete!MySQLサーバをインストール$ sudo dnf install mysql-community-server Last metadata expiration check: 0:00:25 ago on Fri 18 Dec 2020 12:58:39 PM UTC. Dependencies resolved. ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: mysql-community-server aarch64 8.0.22-1.el8 mysql80-community 60 M Installing dependencies: libaio aarch64 0.3.112-1.el8 baseos 33 k mysql-community-client aarch64 8.0.22-1.el8 mysql80-community 14 M mysql-community-client-plugins aarch64 8.0.22-1.el8 mysql80-community 106 k mysql-community-common aarch64 8.0.22-1.el8 mysql80-community 620 k mysql-community-libs aarch64 8.0.22-1.el8 mysql80-community 1.4 M perl-Carp noarch 1.42-396.el8 baseos 30 k perl-Data-Dumper aarch64 2.167-399.el8 baseos 57 k perl-Digest noarch 1.17-395.el8 appstream 27 k perl-Digest-MD5 aarch64 2.55-396.el8 appstream 37 k perl-Encode aarch64 4:2.97-3.el8 baseos 1.5 M perl-Errno aarch64 1.28-416.el8 baseos 76 k perl-Exporter noarch 5.72-396.el8 baseos 34 k perl-File-Path noarch 2.15-2.el8 baseos 38 k perl-File-Temp noarch 0.230.600-1.el8 baseos 63 k perl-Getopt-Long noarch 1:2.50-4.el8 baseos 63 k perl-HTTP-Tiny noarch 0.074-1.el8 baseos 58 k perl-IO aarch64 1.38-416.el8 baseos 141 k perl-MIME-Base64 aarch64 3.15-396.el8 baseos 31 k perl-Net-SSLeay aarch64 1.88-1.module_el8.3.0+410+ff426aa3 appstream 372 k perl-PathTools aarch64 3.74-1.el8 baseos 90 k perl-Pod-Escapes noarch 1:1.07-395.el8 baseos 20 k perl-Pod-Perldoc noarch 3.28-396.el8 baseos 86 k perl-Pod-Simple noarch 1:3.35-395.el8 baseos 213 k perl-Pod-Usage noarch 4:1.69-395.el8 baseos 34 k perl-Scalar-List-Utils aarch64 3:1.49-2.el8 baseos 67 k perl-Socket aarch64 4:2.027-3.el8 baseos 59 k perl-Storable aarch64 1:3.11-3.el8 baseos 95 k perl-Term-ANSIColor noarch 4.06-396.el8 baseos 46 k perl-Term-Cap noarch 1.17-395.el8 baseos 23 k perl-Text-ParseWords noarch 3.30-395.el8 baseos 18 k perl-Text-Tabs+Wrap noarch 2013.0523-395.el8 baseos 24 k perl-Time-Local noarch 1:1.280-1.el8 baseos 34 k perl-URI noarch 1.73-3.el8 appstream 116 k perl-Unicode-Normalize aarch64 1.25-396.el8 baseos 78 k perl-constant noarch 1.33-396.el8 baseos 25 k perl-interpreter aarch64 4:5.26.3-416.el8 baseos 6.3 M perl-libnet noarch 3.11-3.el8 appstream 121 k perl-libs aarch64 4:5.26.3-416.el8 baseos 1.5 M perl-macros aarch64 4:5.26.3-416.el8 baseos 72 k perl-parent noarch 1:0.237-1.el8 baseos 20 k perl-podlators noarch 4.11-1.el8 baseos 118 k perl-threads aarch64 1:2.21-2.el8 baseos 60 k perl-threads-shared aarch64 1.58-2.el8 baseos 47 k Installing weak dependencies: perl-IO-Socket-IP noarch 0.39-5.el8 appstream 47 k perl-IO-Socket-SSL noarch 2.066-4.module_el8.3.0+410+ff426aa3 appstream 298 k perl-Mozilla-CA noarch 20160104-7.module_el8.3.0+416+dee7bcef appstream 15 k Enabling module streams: perl 5.26 perl-IO-Socket-SSL 2.066 perl-libwww-perl 6.34 Transaction Summary ================================================================================ Install 47 Packages Total download size: 88 M Installed size: 407 M Is this ok [y/N]: y Downloading Packages: (1/47): perl-IO-Socket-IP-0.39-5.el8.noarch.rpm 229 kB/s | 47 kB 00:00 (2/47): perl-Digest-1.17-395.el8.noarch.rpm 78 kB/s | 27 kB 00:00 (3/47): perl-Digest-MD5-2.55-396.el8.aarch64.rp 104 kB/s | 37 kB 00:00 (4/47): perl-Mozilla-CA-20160104-7.module_el8.3 100 kB/s | 15 kB 00:00 (5/47): perl-IO-Socket-SSL-2.066-4.module_el8.3 527 kB/s | 298 kB 00:00 (6/47): perl-Net-SSLeay-1.88-1.module_el8.3.0+4 816 kB/s | 372 kB 00:00 (7/47): libaio-0.3.112-1.el8.aarch64.rpm 222 kB/s | 33 kB 00:00 (8/47): perl-URI-1.73-3.el8.noarch.rpm 243 kB/s | 116 kB 00:00 (9/47): perl-Data-Dumper-2.167-399.el8.aarch64. 400 kB/s | 57 kB 00:00 (10/47): perl-Carp-1.42-396.el8.noarch.rpm 131 kB/s | 30 kB 00:00 (11/47): perl-libnet-3.11-3.el8.noarch.rpm 206 kB/s | 121 kB 00:00 (12/47): perl-Exporter-5.72-396.el8.noarch.rpm 216 kB/s | 34 kB 00:00 (13/47): perl-Errno-1.28-416.el8.aarch64.rpm 158 kB/s | 76 kB 00:00 (14/47): perl-File-Path-2.15-2.el8.noarch.rpm 155 kB/s | 38 kB 00:00 (15/47): perl-File-Temp-0.230.600-1.el8.noarch. 177 kB/s | 63 kB 00:00 (16/47): perl-Encode-2.97-3.el8.aarch64.rpm 1.6 MB/s | 1.5 MB 00:00 (17/47): perl-Getopt-Long-2.50-4.el8.noarch.rpm 141 kB/s | 63 kB 00:00 (18/47): perl-HTTP-Tiny-0.074-1.el8.noarch.rpm 132 kB/s | 58 kB 00:00 (19/47): perl-IO-1.38-416.el8.aarch64.rpm 288 kB/s | 141 kB 00:00 (20/47): perl-MIME-Base64-3.15-396.el8.aarch64. 84 kB/s | 31 kB 00:00 (21/47): perl-Pod-Escapes-1.07-395.el8.noarch.r 80 kB/s | 20 kB 00:00 (22/47): perl-PathTools-3.74-1.el8.aarch64.rpm 189 kB/s | 90 kB 00:00 (23/47): perl-Pod-Usage-1.69-395.el8.noarch.rpm 246 kB/s | 34 kB 00:00 (24/47): perl-Pod-Perldoc-3.28-396.el8.noarch.r 166 kB/s | 86 kB 00:00 (25/47): perl-Pod-Simple-3.35-395.el8.noarch.rp 588 kB/s | 213 kB 00:00 (26/47): perl-Storable-3.11-3.el8.aarch64.rpm 245 kB/s | 95 kB 00:00 (27/47): perl-Scalar-List-Utils-1.49-2.el8.aarc 144 kB/s | 67 kB 00:00 (28/47): perl-Socket-2.027-3.el8.aarch64.rpm 130 kB/s | 59 kB 00:00 (29/47): perl-Term-ANSIColor-4.06-396.el8.noarc 137 kB/s | 46 kB 00:00 (30/47): perl-Text-ParseWords-3.30-395.el8.noar 52 kB/s | 18 kB 00:00 (31/47): perl-Term-Cap-1.17-395.el8.noarch.rpm 59 kB/s | 23 kB 00:00 (32/47): perl-Text-Tabs+Wrap-2013.0523-395.el8. 97 kB/s | 24 kB 00:00 (33/47): perl-Time-Local-1.280-1.el8.noarch.rpm 134 kB/s | 34 kB 00:00 (34/47): perl-Unicode-Normalize-1.25-396.el8.aa 293 kB/s | 78 kB 00:00 (35/47): perl-constant-1.33-396.el8.noarch.rpm 64 kB/s | 25 kB 00:00 (36/47): perl-macros-5.26.3-416.el8.aarch64.rpm 310 kB/s | 72 kB 00:00 (37/47): perl-parent-0.237-1.el8.noarch.rpm 77 kB/s | 20 kB 00:00 (38/47): perl-libs-5.26.3-416.el8.aarch64.rpm 1.6 MB/s | 1.5 MB 00:00 (39/47): perl-interpreter-5.26.3-416.el8.aarch6 5.6 MB/s | 6.3 MB 00:01 (40/47): perl-podlators-4.11-1.el8.noarch.rpm 303 kB/s | 118 kB 00:00 (41/47): perl-threads-2.21-2.el8.aarch64.rpm 132 kB/s | 60 kB 00:00 (42/47): perl-threads-shared-1.58-2.el8.aarch64 134 kB/s | 47 kB 00:00 (43/47): mysql-community-client-plugins-8.0.22- 88 kB/s | 106 kB 00:01 (44/47): mysql-community-common-8.0.22-1.el8.aa 451 kB/s | 620 kB 00:01 (45/47): mysql-community-client-8.0.22-1.el8.aa 5.2 MB/s | 14 MB 00:02 (46/47): mysql-community-libs-8.0.22-1.el8.aarc 914 kB/s | 1.4 MB 00:01 (47/47): mysql-community-server-8.0.22-1.el8.aa 10 MB/s | 60 MB 00:05 -------------------------------------------------------------------------------- Total 6.5 MB/s | 88 MB 00:13 warning: /var/cache/dnf/mysql80-community-4467c2595a927b0d/packages/mysql-community-client-8.0.22-1.el8.aarch64.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY MySQL 8.0 Community Server 27 MB/s | 27 kB 00:00 Importing GPG key 0x5072E1F5: Userid : "MySQL Release Engineering <mysql-build@oss.oracle.com>" Fingerprint: A4A9 4068 76FC BD3C 4567 70C8 8C71 8D3B 5072 E1F5 From : /etc/pki/rpm-gpg/RPM-GPG-KEY-mysql Is this ok [y/N]: y Key imported successfully Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : perl-Exporter-5.72-396.el8.noarch 1/47 Installing : perl-libs-4:5.26.3-416.el8.aarch64 2/47 Installing : perl-Carp-1.42-396.el8.noarch 3/47 Installing : perl-Scalar-List-Utils-3:1.49-2.el8.aarch64 4/47 Installing : perl-parent-1:0.237-1.el8.noarch 5/47 Installing : perl-Text-ParseWords-3.30-395.el8.noarch 6/47 Installing : mysql-community-common-8.0.22-1.el8.aarch64 7/47 Installing : mysql-community-client-plugins-8.0.22-1.el8.aarch6 8/47 Installing : mysql-community-libs-8.0.22-1.el8.aarch64 9/47 Running scriptlet: mysql-community-libs-8.0.22-1.el8.aarch64 9/47 Installing : mysql-community-client-8.0.22-1.el8.aarch64 10/47 Installing : perl-Term-ANSIColor-4.06-396.el8.noarch 11/47 Installing : perl-macros-4:5.26.3-416.el8.aarch64 12/47 Installing : perl-Errno-1.28-416.el8.aarch64 13/47 Installing : perl-Socket-4:2.027-3.el8.aarch64 14/47 Installing : perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch 15/47 Installing : perl-Unicode-Normalize-1.25-396.el8.aarch64 16/47 Installing : perl-File-Path-2.15-2.el8.noarch 17/47 Installing : perl-IO-1.38-416.el8.aarch64 18/47 Installing : perl-PathTools-3.74-1.el8.aarch64 19/47 Installing : perl-constant-1.33-396.el8.noarch 20/47 Installing : perl-threads-1:2.21-2.el8.aarch64 21/47 Installing : perl-threads-shared-1.58-2.el8.aarch64 22/47 Installing : perl-interpreter-4:5.26.3-416.el8.aarch64 23/47 Installing : perl-MIME-Base64-3.15-396.el8.aarch64 24/47 Installing : perl-IO-Socket-IP-0.39-5.el8.noarch 25/47 Installing : perl-Time-Local-1:1.280-1.el8.noarch 26/47 Installing : perl-Digest-1.17-395.el8.noarch 27/47 Installing : perl-Digest-MD5-2.55-396.el8.aarch64 28/47 Installing : perl-Net-SSLeay-1.88-1.module_el8.3.0+410+ff426aa3 29/47 Installing : perl-Data-Dumper-2.167-399.el8.aarch64 30/47 Installing : perl-File-Temp-0.230.600-1.el8.noarch 31/47 Installing : perl-Pod-Escapes-1:1.07-395.el8.noarch 32/47 Installing : perl-Storable-1:3.11-3.el8.aarch64 33/47 Installing : perl-Term-Cap-1.17-395.el8.noarch 34/47 Installing : perl-Mozilla-CA-20160104-7.module_el8.3.0+416+dee7 35/47 Installing : perl-Encode-4:2.97-3.el8.aarch64 36/47 Installing : perl-Pod-Simple-1:3.35-395.el8.noarch 37/47 Installing : perl-Getopt-Long-1:2.50-4.el8.noarch 38/47 Installing : perl-podlators-4.11-1.el8.noarch 39/47 Installing : perl-Pod-Usage-4:1.69-395.el8.noarch 40/47 Installing : perl-Pod-Perldoc-3.28-396.el8.noarch 41/47 Installing : perl-HTTP-Tiny-0.074-1.el8.noarch 42/47 Installing : perl-IO-Socket-SSL-2.066-4.module_el8.3.0+410+ff42 43/47 Installing : perl-libnet-3.11-3.el8.noarch 44/47 Installing : perl-URI-1.73-3.el8.noarch 45/47 Installing : libaio-0.3.112-1.el8.aarch64 46/47 Running scriptlet: mysql-community-server-8.0.22-1.el8.aarch64 47/47 Installing : mysql-community-server-8.0.22-1.el8.aarch64 47/47 Running scriptlet: mysql-community-server-8.0.22-1.el8.aarch64 47/47 Verifying : perl-Digest-1.17-395.el8.noarch 1/47 Verifying : perl-Digest-MD5-2.55-396.el8.aarch64 2/47 Verifying : perl-IO-Socket-IP-0.39-5.el8.noarch 3/47 Verifying : perl-IO-Socket-SSL-2.066-4.module_el8.3.0+410+ff42 4/47 Verifying : perl-Mozilla-CA-20160104-7.module_el8.3.0+416+dee7 5/47 Verifying : perl-Net-SSLeay-1.88-1.module_el8.3.0+410+ff426aa3 6/47 Verifying : perl-URI-1.73-3.el8.noarch 7/47 Verifying : perl-libnet-3.11-3.el8.noarch 8/47 Verifying : libaio-0.3.112-1.el8.aarch64 9/47 Verifying : perl-Carp-1.42-396.el8.noarch 10/47 Verifying : perl-Data-Dumper-2.167-399.el8.aarch64 11/47 Verifying : perl-Encode-4:2.97-3.el8.aarch64 12/47 Verifying : perl-Errno-1.28-416.el8.aarch64 13/47 Verifying : perl-Exporter-5.72-396.el8.noarch 14/47 Verifying : perl-File-Path-2.15-2.el8.noarch 15/47 Verifying : perl-File-Temp-0.230.600-1.el8.noarch 16/47 Verifying : perl-Getopt-Long-1:2.50-4.el8.noarch 17/47 Verifying : perl-HTTP-Tiny-0.074-1.el8.noarch 18/47 Verifying : perl-IO-1.38-416.el8.aarch64 19/47 Verifying : perl-MIME-Base64-3.15-396.el8.aarch64 20/47 Verifying : perl-PathTools-3.74-1.el8.aarch64 21/47 Verifying : perl-Pod-Escapes-1:1.07-395.el8.noarch 22/47 Verifying : perl-Pod-Perldoc-3.28-396.el8.noarch 23/47 Verifying : perl-Pod-Simple-1:3.35-395.el8.noarch 24/47 Verifying : perl-Pod-Usage-4:1.69-395.el8.noarch 25/47 Verifying : perl-Scalar-List-Utils-3:1.49-2.el8.aarch64 26/47 Verifying : perl-Socket-4:2.027-3.el8.aarch64 27/47 Verifying : perl-Storable-1:3.11-3.el8.aarch64 28/47 Verifying : perl-Term-ANSIColor-4.06-396.el8.noarch 29/47 Verifying : perl-Term-Cap-1.17-395.el8.noarch 30/47 Verifying : perl-Text-ParseWords-3.30-395.el8.noarch 31/47 Verifying : perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch 32/47 Verifying : perl-Time-Local-1:1.280-1.el8.noarch 33/47 Verifying : perl-Unicode-Normalize-1.25-396.el8.aarch64 34/47 Verifying : perl-constant-1.33-396.el8.noarch 35/47 Verifying : perl-interpreter-4:5.26.3-416.el8.aarch64 36/47 Verifying : perl-libs-4:5.26.3-416.el8.aarch64 37/47 Verifying : perl-macros-4:5.26.3-416.el8.aarch64 38/47 Verifying : perl-parent-1:0.237-1.el8.noarch 39/47 Verifying : perl-podlators-4.11-1.el8.noarch 40/47 Verifying : perl-threads-1:2.21-2.el8.aarch64 41/47 Verifying : perl-threads-shared-1.58-2.el8.aarch64 42/47 Verifying : mysql-community-client-8.0.22-1.el8.aarch64 43/47 Verifying : mysql-community-client-plugins-8.0.22-1.el8.aarch6 44/47 Verifying : mysql-community-common-8.0.22-1.el8.aarch64 45/47 Verifying : mysql-community-libs-8.0.22-1.el8.aarch64 46/47 Verifying : mysql-community-server-8.0.22-1.el8.aarch64 47/47 Installed: libaio-0.3.112-1.el8.aarch64 mysql-community-client-8.0.22-1.el8.aarch64 mysql-community-client-plugins-8.0.22-1.el8.aarch64 mysql-community-common-8.0.22-1.el8.aarch64 mysql-community-libs-8.0.22-1.el8.aarch64 mysql-community-server-8.0.22-1.el8.aarch64 perl-Carp-1.42-396.el8.noarch perl-Data-Dumper-2.167-399.el8.aarch64 perl-Digest-1.17-395.el8.noarch perl-Digest-MD5-2.55-396.el8.aarch64 perl-Encode-4:2.97-3.el8.aarch64 perl-Errno-1.28-416.el8.aarch64 perl-Exporter-5.72-396.el8.noarch perl-File-Path-2.15-2.el8.noarch perl-File-Temp-0.230.600-1.el8.noarch perl-Getopt-Long-1:2.50-4.el8.noarch perl-HTTP-Tiny-0.074-1.el8.noarch perl-IO-1.38-416.el8.aarch64 perl-IO-Socket-IP-0.39-5.el8.noarch perl-IO-Socket-SSL-2.066-4.module_el8.3.0+410+ff426aa3.noarch perl-MIME-Base64-3.15-396.el8.aarch64 perl-Mozilla-CA-20160104-7.module_el8.3.0+416+dee7bcef.noarch perl-Net-SSLeay-1.88-1.module_el8.3.0+410+ff426aa3.aarch64 perl-PathTools-3.74-1.el8.aarch64 perl-Pod-Escapes-1:1.07-395.el8.noarch perl-Pod-Perldoc-3.28-396.el8.noarch perl-Pod-Simple-1:3.35-395.el8.noarch perl-Pod-Usage-4:1.69-395.el8.noarch perl-Scalar-List-Utils-3:1.49-2.el8.aarch64 perl-Socket-4:2.027-3.el8.aarch64 perl-Storable-1:3.11-3.el8.aarch64 perl-Term-ANSIColor-4.06-396.el8.noarch perl-Term-Cap-1.17-395.el8.noarch perl-Text-ParseWords-3.30-395.el8.noarch perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch perl-Time-Local-1:1.280-1.el8.noarch perl-URI-1.73-3.el8.noarch perl-Unicode-Normalize-1.25-396.el8.aarch64 perl-constant-1.33-396.el8.noarch perl-interpreter-4:5.26.3-416.el8.aarch64 perl-libnet-3.11-3.el8.noarch perl-libs-4:5.26.3-416.el8.aarch64 perl-macros-4:5.26.3-416.el8.aarch64 perl-parent-1:0.237-1.el8.noarch perl-podlators-4.11-1.el8.noarch perl-threads-1:2.21-2.el8.aarch64 perl-threads-shared-1.58-2.el8.aarch64 Complete!起動・初期設定
MySQLサーバ起動・初期設定$ sudo systemctl enable mysqld.service $ sudo systemctl start mysqld.service $ sudo fgrep assword /var/log/mysqld.log 2020-12-18T13:08:42.554021Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: 【初期パスワード】 $ sudo mysql_secure_installation Securing the MySQL server deployment. Enter password for user root:(初期パスワードを入力) The existing password for the user account root has expired. Please set a new password. New password:(新しいパスワードを入力) Re-enter new password:(同じパスワードを入力) The 'validate_password' component is installed on the server. The subsequent steps will run with the existing configuration of the component. Using existing password for root. Estimated strength of the password: 100 Change the password for root ? ((Press y|Y for Yes, any other key for No) : n ... skipping. By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? (Press y|Y for Yes, any other key for No) : y Success. Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y Success. By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y - Dropping test database... Success. - Removing privileges on test database... Success. Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y Success. All done! $ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 8.0.22 MySQL Community Server - GPL Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> select * from performance_schema.global_variables where variable_name in ("innodb_buffer_pool_size", "innodb_log_file_size", "innodb_flush_method"); +-------------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +-------------------------+----------------+ | innodb_buffer_pool_size | 134217728 | | innodb_flush_method | fsync | | innodb_log_file_size | 50331648 | +-------------------------+----------------+ 3 rows in set (0.00 sec) mysql> quit Bye
innodb_dedicated_server=ON
で起動してみます(参考:MySQL 8.0 の innodb_dedicated_server について)。innodb_dedicated_server=ONで起動$ sudo systemctl stop mysqld.service $ vi /etc/my.cnf ※末尾に「innodb_dedicated_server=ON」を追記して保存 $ systemctl start mysqld.service $ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.0.22 MySQL Community Server - GPL Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> select * from performance_schema.global_variables where variable_name in ("innodb_buffer_pool_size", "innodb_log_file_size", "innodb_flush_method"); +-------------------------+-------------------+ | VARIABLE_NAME | VARIABLE_VALUE | +-------------------------+-------------------+ | innodb_buffer_pool_size | 12884901888 | | innodb_flush_method | O_DIRECT_NO_FSYNC | | innodb_log_file_size | 1073741824 | +-------------------------+-------------------+ 3 rows in set (0.01 sec)以上です。
次回(MySQL Advent Calendar 2020 21 日目)はこの環境を r5.large インスタンスと比較してみます。
- 投稿日:2020-12-18T23:42:12+09:00
Docker+Rails+AWS(EC2+RDS)を使ってデプロイした
概要
AWSを使ってアプリケーションを公開している人は多く、私も実際に体験してみたくタイトル通りDockerで環境構築し、Railsでアプリケーションを開発し、AWSで実際にデプロイすることにチャレンジしました。
AWSは無料枠内にてデプロイまで目指します。前提
- アプリケーションはGithubを使って管理する。
- 各サービスのアカウント登録は済ませている。(AWS、Github等)
- AWSはIAMユーザーを作成し、必要なセキュリティ、ポリシーを割り当てている。
- アプリケーションはDockerを使用して環境構築している。
AWSの設定
AWSのサービスの設定は極力IAMユーザーにて行います。まず、AWSにIAMユーザーでログインする。
- VPCの作成
まずはVPCの作成を行います。VPCサービスのページを開き、メニューからVPCを選択。VPCの作成を選択します。
VPC名、CIDRブロックを設定し、VPCを作成します。
- サブネットの作成
サブネットの作成を行います。
サブネットはEC2用のサブネットを1つ、RDS用のサブネットを2つ用意します。
メニューからサブネットを選択し、サブネットの作成を選択します。
先ほど作成したVPCを選択し、サブネット名、アベイラビリティゾーン、CIDRブロックを設定し、サブネットを作成を選択します。
※RDS用のサブネットはアベイラビリティゾーンを別々にします。
- インターネットゲートウェイの作成
インターネットに接続するためにインターネットゲートウェイを設定します。
メニューからインタネットゲートウェイを選択し、インターネットゲートウェイの作成を選択します。
名前を設定し、インターネットゲートウェイの作成を選択します。
アクションからVPCにアタッチを選択し、作成したVPCにアタッチします。
- EC2インスタンスの作成
EC2ダッシュボードに開き、インスタンスを起動を選択し、EC2インスタンスを作成します。
EC2インスタンスは無料枠で使えるマシンイメージとインスタンスタイプを選択します。
セキュリティグループは新しく作成します。
SSH接続を行いたいのでSSHの設定を行います。
タイプ プロトコル ポート範囲 ソース HTTP TCP 80 0.0.0.0/0 SSH TCP 22 マイIP 設定に問題がなければ起動を選択します。
インスタンスを作成するときにキーペアを作成します。
キーペアはEC2にSSH接続する際必要となります。新しいキーペアの作成を選択し、キーペア名を設定しローカルにダウンロードしておきます。インスタンスメニューの[ステータスチェック]が終了し、[インスタンスの状態]が[実行中]になれば、無事インスタンスが起動したこととなります。
続いてElastic IPを設定します。
メニューよりElastic IPを選択します。Elastic IP アドレスの割り当てを選択し、新しいアドレスの割り当てを選択し、割り当てを選択します。
作成したElastic IPを選択し、アクションからElastic IPアドレスの関連付けを選択します。
作成したEC2インスタンスを設定してElastic IPアドレスをEC2に関連付けます。
- RDSの作成
RDSを作成する前に、RDS用のサブネットグループの作成を行います。
RDSメニューよりサブネットグループを選択し、DBサブネットグループの作成を選択します。サブネットグループ名、VPCを選択し、RDS用に作成したサブネット2つを設定し、作成をします。
RDSサービスを開き、データベースの作成を選択します。
データベースはMySQLを選択します。基本的には既存の設定で大丈夫ですが、無料利用枠という箇所は忘れずに選択します。バージョン等は自身の使用するバージョンを設定します。データベース名、マスターユーザー名、マスターパスワードは適宜設定します。
後に、Railsの設定に必要となります。(作成後、確認することができます)
作成したVPC、サブネットグループを選択します。セキュリティグループは新規作成を選択し、新しいセキュリティグループを作成します。
データベースの作成を選択し、データベースを作成します。データベース用のセキュリティグループのインバウンドルールを変更します。
タイプ プロトコル ポート範囲 ソース MYSQL/Aurora TCP 3306 EC2のセキュリティグループのID Railsの設定
- RDSの設定
RDSの情報をcredentials.yml.encに書き込みます。
docker-compose run -e EDITOR="vim" app rails credentials:edit
credentials.yml.encrds: host: RDSのエンドポイント database: RDSのデータベース名 username: RDSのマスターユーザの名前 password: RDSのマスターパスワードconfig/database.ymlのproductionの箇所にRDSの情報を挿入します。
database.yml#省略 production: <<: *default host: <%= Rails.application.credentials.rds[:host] %> database: <%= Rails.application.credentials.rds[:database] %> username: <%= Rails.application.credentials.rds[:username] %> password: <%= Rails.application.credentials.rds[:password] %>
- Docker-compose.ymlの編集
データベースはRDSを使用するためdb:の箇所は全てコメントアウトします。volumes:のmysql-data:もコメントアウトします。
アプリケーションを本番環境で立ち上げるため、app:のcommand:に-e productionを追加します。docker-compose.ymlversion: '3' services: # db: # image: mysql:5.7 # command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci # env_file: # - ./.env # volumes: # - mysql-data:/var/lib/mysql # ports: # - "4306:3306" app: build: . env_file: - ./.env command: bundle exec puma -C config/puma.rb -e production init: true volumes: - .:/myproject - public-data:/myproject/public - tmp-data:/myproject/tmp - log-data:/myproject/log # depends_on: # - db web: build: context: containers/nginx init: true volumes: - public-data:/myproject/public - tmp-data:/myproject/tmp ports: - 80:80 # depends_on: # - app volumes: # mysql-data: public-data: tmp-data: log-data:
- nginx.comfの編集
server_nameのxx.xx.xx.xxの箇所にElastic IPアドレスを入力します。
nginx.confupstream myproject { server unix:///myproject/tmp/sockets/puma.sock; } server { listen 80; server_name xx.xx.xx.xx [or localhost]; #省略 } }
- その他の設定
その他Github等外部に知られたくないKEYや環境変数などは.envファイルを作成し、そちらに書き込みます。dotenv-railsを使うとGitを使用する際管理をし易くなります。
gemfilegem 'dotenv-rails'$ bundle install.gitignoreファイルに.envファイルを記述することで、githubにpushされなくなります。
.gitignore/.envconfig/environmentsのproduction.rbを編集します。
下記内容を編集すると、本番環境で立ち上げ時に便利です。production.rbconfig.assets.js_compressor = Uglifier.new(harmony: true) config.assets.compile = true以上、完了したらアプリケーションをgithubへpushします。
AWSでアプリケーションをデプロイ
- EC2のSSH接続する。
EC2にSSH接続するための設定を行います。
$ mkdir ~/.ssh $ mv ~/Downloads/myapp.pem ~/.ssh/ $ chmod 600 ~/.ssh/myapp.pem $ ssh -i ~/.ssh/myapp.pem ec2-user@xxx.xxx.xxx.xxxmyapp.pemはダウンロードしたキーペアの名前です。chmodで権限を変更後、キーペアを使用してEC2にログインします。ec2-userはAWS linuxのデフォルトユーザーです。xxx.xxx.xxx.xxxはEC2インスタンスのElastic IPを入力します。
まず初めにyumをアップデートします。
[ec2-user@ip-xxx-xxx-xxx-xxx ~]$ sudo yum update -y
- EC2にDocker環境を作る
EC2にDockerとdocker-composeをインストールします。
# Dockerをインストール [ec2-user@xxx.xxx.xxx.xxx ~]$ sudo yum install -y docker [ec2-user@xxx.xxx.xxx.xxx ~]$ sudo service docker start [ec2-user@xxx.xxx.xxx.xxx ~]$ sudo usermod -G docker ec2-user [ec2-user@xxx.xxx.xxx.xxx ~]$ exit $ ssh -i ~/.ssh/myapp.pem ec2-user@xxx.xxx.xxx.xxx [ec2-user@xxx.xxx.xxx.xxx ~]$ sudo chkconfig docker on # docker-composeをインストール [ec2-user@xxx.xxx.xxx.xxx ~]$ sudo curl -L "https://github.com/docker/compose/releases/download/インストールするバージョン/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose [ec2-user@xxx.xxx.xxx.xxx ~]$ sudo chmod +x /usr/local/bin/docker-compose
- gitをインストール
Githubからアプリケーションをクローンするためにgitをインストールします。
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo yum install -y git [ec2-user@xxx.xxx.xxx.xxx ~]$ git clone [Githubのパス]
- ローカルから必要ファイルをコピー
credentialsの解読するためのmaster.keyと.envファイルをローカルからコピーします。
[ec2-user@xxx.xxx.xxx.xxx ~]$ exit $ scp -i ~/.ssh/myapp.pem master.key ec2-user@xx.xxx.xxx.xx:myapp/config/ $ scp -i ~/.ssh/myapp.pem .env ec2-user@xx.xxx.xxx.xx:myapp/
- コンテナの起動
$ ssh -i ~/.ssh/myapp.pem ec2-user@xxx.xxx.xxx.xxx [ec2-user@xxx.xxx.xxx.xxx ~]$ cd myapp [ec2-user@xxx.xxx.xxx.xxx ~/myapp]$ docker-compose build [ec2-user@xxx.xxx.xxx.xxx ~/myapp]$ docker-compose up -d [ec2-user@xxx.xxx.xxx.xxx ~/myapp]$ docker-compose exec app bin/rails db:create db:migrate RAILS_ENV=productionElastic IPアドレスにアクセスして正しく表示されれば完了です。
まとめ
AWSを使ってデプロイをすることは、初学者にはかなりハードルが高い内容だと思っておりましたが、素晴らしい文献等が豊富にあったため乗り越えることができました。
所々修正したり、試行錯誤しながらデプロイまで行ったので、全く同じやり方でできるかはわかりませんが、現場私ができる最適な方法だと考えております。気になった点
参考記事によっては「この方法では開発環境で動いてしまうのでは?」「本番環境でないためRDSが使用されていないのではないか?」と思う部分もあり、いくつかの記事を参考にさせて頂きました。database.ymlのproduction:をRDSに指定したのであれば、本番環境でアプリケーションを立ち上げなければRDSは機能しないと考えております。(勘違いであれば申し訳ありません)
docker-composeの設定次第でなんとかできるのかもしれませんが、もう少しリサーチをしてみようと思いました。課題
せっかくDockerを使用したのでAWSのECSやFargateを駆使してデプロイすることを考えるべきだったと思いました。
次の機会があれば挑戦したいと思います。その他
私はS3も取り入れました。そちらは別記事にてまとめております。
AWS_S3+Ruby on Rails_Active Storageで画像データを保存参考記事
無料!かつ最短?で Ruby on Rails on Docker on AWS のアプリを公開するぞ。
EC2上でRailsアプリケーションにDockerを導入する(Rails、Nginx、RDS)
見ながらやろう! AWSを始めよう -VPC構築編-
Rails5.2から追加された credentials.yml.enc のキホン
- 投稿日:2020-12-18T23:18:03+09:00
Amazon ECRにプライベートリポジトリを作成してイメージのpush/pullを実行する
1. はじめに
AWS re:Invent 2020で発表されましたが、Amazon ECRでパブリックレジストリが利用できるようになりました。
しかし、今回はそれとは関係なく、プライベートレジストリを使用します。
これまでECRを触ってこなかったので、使ってみたという内容です。
2. 構成
2台のLinuxサーバを使用します。1台目からイメージをpushし、2台目はイメージをpullします。
ECRはリージョンサービスで、VPCエンドポイントも利用できます。(今回は使用しません)
3. ECRの設定
リポジトリの作成
ECRのリポジトリを以下の設定で作成します。
可視性設定:プライベート
リポジトリ名:test-app-repo
タグのイミュータビリティ:無効
プッシュ時にスキャン:有効
KMS暗号化:有効
4. EC2(1台目)からのpush
まず、ECRレジストリに対してDockerを認証します。「Login Succeeded」と表示されたことを確認します。
WARNINGが出ていますが、そのまま進めます。これについては本記事の「8. 警告&エラー集」で説明します。$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded今回は「test-app」というイメージをpushしたいと思います。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE test-app v1.0 72b199328340 10 days ago 461MBdocker tagコマンドでpushする対象にタグを設定します。
指定する形式は、「AWSアカウントID.dkr.ecr.リージョン.amazonaws.com/リポジトリ名:タグ」です。
タグを省略した場合は、自動でlatestのタグが付きます。$ docker tag test-app:v1.0 \ > 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo:v1.0タグ付けした結果を確認します。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo v1.0 72b199328340 10 days ago 461MB test-app v1.0 72b199328340 10 days ago 461MBでは、イメージをpushします。
$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo:v1.0コンソールから確認すると、イメージタグにv1.0が追加されたことが確認できます。
5. EC2(2台目)からのpull
5-1. IAMポリシーの作成とアタッチ
2台目のEC2はaws configureで認証情報(アクセスキー、シークレットアクセスキー)を渡していないので、
そのままではECRにアクセスできません。
以下のIAMポリシーを作成してEC2のIAMロールにアタッチし、ECRにアクセスできるようにします。
IAMポリシー名:AmazonECRFullAccessjson{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:*" ], "Resource": "*" } ] }5-2. EC2からのpull
まずは、1台目と同じようにECRレジストリに対してDockerを認証します。
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.comイメージをpullします。
$ docker pull 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo:v1.0結果を確認します。2台目のLinuxサーバがECRからイメージをpullすることができました。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo v1.0 72b199328340 10 days ago 461MB6. v2.0をpushする
次に、タグ「v2.0」を付けたイメージもpushしてみたいと思います。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo v1.0 72b199328340 10 days ago 461MB 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo v2.0 72b199328340 10 days ago 461MBイメージをpushします。
$ docker push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo:v2.07. イメージタグを削除する
v2.0が不要になったと仮定して、イメージタグを削除したいと思います。
(aws configureでregionを指定してある場合は、コマンドでは指定不要です)$ aws ecr batch-delete-image --repository-name test-app-repo --image-ids imageTag=v2.0 --region ap-northeast-1 { "failures": [], "imageIds": [ { "imageTag": "v2.0", "imageDigest": "sha256:58d3c26bee377e039c0ce5c2ef92ed2ce10b956bf3dc0cf5dba4b4d6f56aaf94" } ] }再度コンソールから確認すると、v2.0が削除されています。
イメージを削除する場合は、イメージのダイジェストを指定すれば削除できます。
参考:https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/delete_image.html8. 警告&エラー集
警告①
$ aws ecr get-login --region ap-northeast-1 --no-include-email $ docker login -u AWS -p {認証トークン} https://123456789012.dkr.ecr.ap-northeast-1.amazonaws.com WARNING! Using --password via the CLI is insecure. Use --password-stdin.→AWS CLI バージョン 1.17.10 より前のバージョンを使用している場合はget-loginコマンドで認証しますが、
セキュリティリスクがあり非推奨です。AWS CLIのバージョンを上げて、get-login-passwordを使用することが推奨されます。
参考:https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/Registries.html警告②
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store→Dockerのデフォルトの動作として、ログインパスワードを暗号化せずにconfig.jsonに保存します。
外部のクレデンシャルストアに保存する方がより安全という警告です。
参考:https://docs.docker.com/engine/reference/commandline/login/#credentials-storeエラー①
$ docker pull 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/test-app-repo:v1.0 Error response from daemon: Get https://123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/v2/test-app-repo/manifests/v1.0: no basic auth credentials→ECRレジストリに対してDockerを認証していないと出るエラーです。
エラー②
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:sts::123456789012:assumed-role/IAMロール名/インスタンスID is not authorized to perform: ecr:GetAuthorizationToken on resource: * Error: Cannot perform an interactive login from a non TTY device→aws configureで認証情報を指定していない、あるいはEC2のIAMロールにECRへのアクセスに必要な
IAMポリシー(本記事ではAmazonECRFullAccess)がアタッチされていない場合に出るエラーです。
- 投稿日:2020-12-18T22:29:25+09:00
Next.jsでWeb VitalsをAmazon CloudWatchに送信してみる
この記事は、ニフティグループ Advent Calendar 2020の19日目の記事です。
昨日は@shin27さんで「Amazon SageMaker Feature StoreにKaggleのTitanicデータを登録してみた」でした。はじめに
今年も早いものでもう2週間を切ってしまいましたね。今年11月に水樹奈々さんがおめでたを公表したり、近頃は寒い日が続いたりで皆さんいかがお過ごしでしょうか。
私は、今年11月にdアニメストアで配信が開始されたアニメ「頭文字D」に今更ながらハマったり、イベント「五等分の花嫁展 MAKEOVER」に参加したりして、コロナ禍でイベントが軒並み中止になる状況でもプライベートはそれなりに充実していたかなと思っています。
そして2021年1月から放映される予定のアニメ「五等分の花嫁∬」が待ち遠しいです。推しキャラは中野三玖です。
弊社内の話に移すと、ニフティではAWSやスクラム開発、フロントエンドフレームワークなどの勉強会が定期的に開催されていて、私も運営メンバー兼勉強メンバーとして精進しています。難しさもありますが、自分の中の知識をアップデートできる楽しさもあります!
ちなみに勉強会について少しでも気になった方や詳しい話を聞きたい方はぜひ説明会に来てみると良いかと!
ニフティ株式会社 採用情報さて前置きが長くなりすぎたのでそろそろ本題に入りますが、今回はNext.jsの勉強も兼ねつつ、Next.jsでWeb VitalsをAmazon CloudWatchに送信してみた話を書きます。
どうしてこのお題で執筆しようと思ったのかについて語っておくと、それはNext.jsでWeb VitalsをGoogle Analyticsに送信する記事をたまたま見つけたので、仮に送信先をAmazon CloudWatchに変更してみたらどうなるのだろうかと興味を持ったためです。
用語の説明
ここではこの記事のタイトルに登場する用語について簡単に説明します。
Next.jsとは
オープンソースのReactフロントエンド開発Webフレームワークで、主に以下の機能を持ち合わせています。
- サーバーサイドレンダリング
- ReactベースのWebアプリケーション用の静的Webサイトの生成
公式サイトはこちらで、執筆当時の最新はバージョン10.0.3です。
Web Vitalsとは
WebサイトやWebアプリケーションのパフォーマンスを計測するための仕組みで、Next.jsではバージョン9.4から導入されています。
Blog - Next.js 9.4 | Next.jsなおWeb Vitalsについて詳細を書くと記事が長くなってしまうのでここでは書きませんが、FE Study #2というイベントでも話に上がっていましたので、気になる方は以下の資料を見てください。
メタ・パフォーマンスチューニングAmazon CloudWatchとは
DevOpsエンジニア、開発者、SRE、および ITマネージャーのために構築されたモニタリング/オブザーバビリティサービスで、公式サイトはこちらです。
それでは早速チャレンジしてみましょう
今回はGetting Startedを参考にしながらアプリケーションを作成するところから始めます。
まずは適当な内容のページ
pages/index.js
を用意します。自己紹介を行うページでも何でも良いです。
次にWeb Vitalsを使うコードとSDKを組み込むためのコード例、そしてSDKドキュメントとにらめっこしながら、以下の内容をpages/_app.js
に書きます。pages/_app.js// import App from 'next/app' var AWS = require('aws-sdk'); AWS.config.update({ credentials: new AWS.Credentials( "AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ), region: 'us-west-2' }); // Create CloudWatch service object var cw = new AWS.CloudWatch({apiVersion: '2010-08-01'}); export function reportWebVitals(metric) { const name = "mypage" var params = { MetricData: [ { MetricName: 'PageAnalytics', Dimensions: [ { Name: 'NextJSAPP', Value: 'Initial' }, ], Unit: 'None', Value: 0.0 }, ], Namespace: 'Page/Analytics' }; console.log("=======CW Before======="); console.log(params); switch (metric.name) { case 'FCP': console.log("=======FCP======="); console.log(metric); params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name; params["MetricData"][0]["Dimensions"]["Value"] = name; params["MetricData"][0]["Value"] = metric["value"]; break case 'LCP': console.log("=======LCP======="); console.log(metric); params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name; params["MetricData"][0]["Dimensions"]["Value"] = name; params["MetricData"][0]["Value"] = metric["value"]; break case 'CLS': console.log("=======CLS======="); console.log(metric); params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name; params["MetricData"][0]["Dimensions"]["Value"] = name; params["MetricData"][0]["Value"] = metric["value"]; break case 'FID': console.log("=======FID======="); console.log(metric); params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name; params["MetricData"][0]["Dimensions"]["Value"] = name; params["MetricData"][0]["Value"] = metric["value"]; break case 'TTFB': console.log("=======TTFB======="); console.log(metric); params["MetricData"][0]["Dimensions"][0]["Value"] = metric.name; params["MetricData"][0]["Dimensions"]["Value"] = name; params["MetricData"][0]["Value"] = metric["value"]; break default: break } console.log("=======CW After======="); console.log(params); cw.putMetricData(params, function(err, data) { if (err) { console.log("Error", err); } else { console.log("Success", JSON.stringify(data)); } }); } function MyApp({ Component, pageProps }) { return <Component {...pageProps} /> } export default MyApp最終的なディレクトリは以下のようになります。
. ├── pages │ ├── _app.js │ └── index.js ├── .gitignore ├── package.json ├── README.md └── yarn.lock準備が整ったので実際に動かしてみましょう。
$ yarn add react react-dom next aws-sdk $ yarn build $ yarn start実際に動かしてみましたが、Webブラウザに搭載されている開発者ツールにあるコンソールにも
なお、今回は実装を簡単にするため、AWS認証情報をソースコード内に直接記述する方式にしていますが、GitHubのパブリックリポジトリに上げてしまうと攻撃されるのであまり望ましくはありません。あらかじめ.gitignoreファイルに記載した.envファイルを使用するなどしてよりセキュアにしましょう。
また、認証情報を設定する際には、AWS.config.loadFromPath('./credentials.json');
を記述する方法もありますが、こちらのマニュアルにも記載されている通り、対応していないケースがあるのでご注意ください。
さらに、こちらの記事にも記載されている通り、ソースコード内に認証情報を直接記述する方式には非推奨のものが存在しています。ご注意ください。おわりに
今回はNext.jsでWeb VitalsをAmazon CloudWatchに送信してみた話を書きました。
パフォーマンスメトリクスを追っていくことはサービスやシステムの運用を行う上で欠かせないことの一つなので、この記事が参考になれば幸いです。
また送信したメトリクスをもとにして、Slack通知など何らかのアクションを行うLambda関数を発火させるのもきっと面白いかもしれません。
そしてこの記事を執筆していたときに似たようなことをやっている方を見つけました。ただこちらはサーバレスでしたね。
https://github.com/serverless-nextjs/serverless-next.js/issues/762ここまで読んでいただきありがとうございました。明日は@D_Wさんの記事です。GitHub ActionsでStoryBookをデプロイする方法について執筆してくれるそうです。楽しみにお待ちください!
- 投稿日:2020-12-18T21:07:44+09:00
AWS LambdaとAPI GatewayでQiita検索botを作ってみた
完成物
■Qiita検索
検索したいタグを入れると、そのタグの新着記事が返ってくるLINE bot
今回使うもの
- LINE Messaging API
- Amazon API Gateway
- AWS Lambda(言語:node.js)
- Qiita API
LINE Messaging API, Amazon API Gateway, AWS Lambdaの準備は以下の記事に書きましたので、
こちらをご参照ください。
[過去記事]AWS(Lambda, API Gateway)でLINEの天気予報botを作ってみるこの記事では、実際のコードについてのみ書いています。
Lambda関数を書く
ローカルで、以下の通りにモジュールをインストールします。
$ npm install @line/bot-sdk $ npm install axiosindex.jsは以下の通り。
index.jsconst line = require('@line/bot-sdk'); const axios = require('axios'); axios.defaults.baseURL = 'https://qiita.com/api/v2'; const client = new line.Client({ channelAccessToken: process.env.ACCESSTOKEN }); let event; let callback; let userMessage = ''; let message = {}; exports.handler = (_event, _context, _callback) => { event = _event; callback = _callback; userMessage = JSON.parse(event.body).events[0].message.text; const url = '/tags/' + userMessage +'/items?page=1&per_page=5'; main(url); }; function main(url) { axios.get(url) .then(function (response) { if (response.data.length !== 0) { let columns = []; for (let i = 0; i < 5; i++) { let column = {}; let tags = ""; for (let j = 0; j < response.data[i].tags.length; j++) { tags += response.data[i].tags[j].name + ", "; } tags = tags.slice(0, -2); column = { "thumbnailImageUrl": response.data[i].user.profile_image_url, "title": response.data[i].title.length > 40 ? response.data[i].title.slice(0, 40) : response.data[i].title, //最大40文字 "text": tags.length > 55 ? "tag: " + tags.slice(0, 55) : "tag: " + tags, //最大60文字 "actions": [ { "type": "uri", "label": "記事を読む", "uri": response.data[i].url } ] } columns.push(column); } message = { "type": "template", "altText": "#" + userMessage + "の新着記事", "template": { "type": "carousel", "columns": columns } } } else { //検索結果が0件だった場合 message = { type: "text", text: "検索結果は0件でした。" }; } }) .catch(function (error) { //該当するタブが存在しない場合 console.log(error); message = { type: "text", text: "#" + userMessage + "は存在しません。" }; }) .finally(function () { //メッセージ返信 console.log(JSON.stringify(message)); client.replyMessage(JSON.parse(event.body).events[0].replyToken, message) .then(() => { callback(null, {}); }) .catch((err) => { callback(null, {}); }); }); };環境変数には、LINE botのチャネルアクセストークンを設定します。
これで完成!
まとめ
今回は、簡単なカルーセルテンプレートを使用したので記事は5つのみの表示です。
ここに色々なメッセージタイプが載っているので、是非色々試してみてください。
- 投稿日:2020-12-18T20:21:41+09:00
EC2の脆弱性診断をInspectorで自動化してみた
本記事はサムザップ #1 Advent Calendar 2020の 20 日目の記事です。
Amazon Inspectorとは
InspectorについてはAWSの公式ドキュメントでご確認ください。
https://aws.amazon.com/jp/inspector/実現したいこと
定期的に自動でEC2インスタンスを起動してInspectorで脆弱性をチェックしてSlackへ通知する。起動中のインスタンスを接直チェックはしたくない。
上記要件を踏まえた上で以下構成で定期的に脆弱性チェックを行うことにしました。
- Amazon EventBridgeでLambdaを実行
- Lambdaから脆弱性チェックしたいEC2インスタンスのAMIからインスタンスを起動
- 起動したインスタンスをAmazon Inspectorで脆弱性チェック
- Inspectorの診断が完了したらSNS経由で診断結果を取得してSlackに通知するLambdaを実行
AWSリソースの作成は全てTerraformでコード管理できるようにしました。
Inspectorの設定
inspector.tf
###################################### # 評価ターゲット作成 ###################################### resource "aws_inspector_resource_group" "resource" { tags = { Inspector = "true" } } resource "aws_inspector_assessment_target" "target" { name = "test-target" resource_group_arn = aws_inspector_resource_group.resource.arn } ###################################### # Rule Package 取得 ###################################### data "aws_inspector_rules_packages" "rules" {} ###################################### # 評価テンプレート作成 ###################################### resource "aws_inspector_assessment_template" "template" { name = "test-template" target_arn = aws_inspector_assessment_target.target.arn duration = 3600 rules_package_arns = data.aws_inspector_rules_packages.rules.arns }・評価ターゲットの作成
Tag:Inspector:trueが設定されているEC2インスタンスをターゲットにします。・評価Rule Packageの取得
以下全てのRule Packageで評価テンプレートを取得します。
Common Vulnerabilities and Exposures: 共通脆弱性識別子
Security Best Practices: Amazon Inspector のセキュリティのベストプラクティス
Center for Internet Security (CIS) Benchmarks: Center for Internet Security (CIS) ベンチマーク
Network Reachability: ネットワーク到達可能性・評価テンプレート作成
作成した評価ターゲットを設定
実行時間を1時間に設定
取得した評価Rule Packageを設定Lambdaの設定
Lambda関数は以下の2つの関数を作成します。
・AMIを起動してInspector評価の実行を行うLambda
・Inspector評価の結果をSlackに通知するLambdaAMIを起動してInspector評価の実行を行うLambda
function_inspector_run.tf###################################### # Lambda IAM Role作成 ###################################### resource "aws_iam_role" "lambda_role" { name = "lambda-inspector-role" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF } ###################################### # Lambda IAM Policy作成 ###################################### resource "aws_iam_policy" "lambda_policy" { name = "lamda-inspector-policy" description = "LambdaFunction function_inspector_run/function_inspector_report Use Policy" policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "inspector:GetAssessmentReport", "inspector:ListAssessmentTemplates", "inspector:ListAssessmentTargets", "inspector:ListAssessmentRuns", "inspector:StartAssessmentRun", "inspector:PreviewAgents" ], "Resource": "*" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" } ] } EOF } ###################################### # RoleにPolicyをアタッチ ###################################### resource "aws_iam_role_policy_attachment" "lambda_role_policy_attach" { role = aws_iam_role.lambda_role.name policy_arn = aws_iam_policy.lambda_policy.arn } ###################################### # Lambda関数の作成 ###################################### resource "aws_lambda_function" "inspector_run" { filename = "./function_inspector_run.zip" function_name = "function_inspector_run" role = aws_iam_role.lambda_role.arn handler = "lambda_function.lambda_handler" source_code_hash = filebase64sha256("./function_inspector_run.zip") runtime = "python3.8" timeout = 300 environment { variables = { TZ = "Asia/Tokyo" SECURITYGROUP_ID = "xxxxxx" SUBNET_ID = "xxxxxx" } } }function_inspector_run.py
import boto3 import os from operator import itemgetter import logging import datetime import time logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): create_ec2_instance() start_inspector_assessment_run() def create_ec2_instance(): """ Inspectorで分析するため最新のAMIを取得しAMIからEC2Instanceを起動します 起動設定でawsagentをinstallします """ # web AMI一覧取得 ec2 = boto3.client('ec2') # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.describe_images response = ec2.describe_images( Filters=[ { "Name": "tag:Name", "Values": [ "test-web*", ] } ], Owners=[ "self" ] ) # 最新のAMIを取得 image_details = sorted(response['Images'], key=itemgetter('CreationDate'), reverse=True) image_id = image_details[0]['ImageId'] user_data = """#!/bin/sh sudo su - wget https://inspector-agent.amazonaws.com/linux/latest/install bash install -u false /etc/init.d/awsagent restart """ # Instance作成/起動 # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.run_instances response = ec2.run_instances( ImageId=image_id, InstanceType="t2.micro", MinCount=1, MaxCount=1, KeyName="test_key_pair", SecurityGroupIds=[ os.environ['SECURITYGROUP_ID'] ], SubnetId=os.environ['SUBNET_ID'], UserData=user_data, TagSpecifications=[ { 'ResourceType': 'instance', 'Tags': [ { 'Key': 'Inspector', 'Value': 'true' }, { 'Key': 'Name', 'Value': 'inspector-target' } ] } ] ) # EC2Instanceがstatus:runningになるまで待ち instance_id = response.get('Instances')[0]['InstanceId'] while 1: time.sleep(30) instances = ec2.describe_instances( InstanceIds=[ instance_id ] ) logger.info(instances.get('Reservations')[0].get('Instances')[0].get('State').get('Name')) if instances.get('Reservations')[0].get('Instances')[0].get('State').get('Name') == 'running': break def start_inspector_assessment_run(): """ Inspector評価実行を行います """ # 評価テンプレート一覧取得 inspector = boto3.client('inspector') # 評価ターゲットのEC2Instance awsagentのステータスがHEALTHYになるまで待機 assessment_target = inspector.list_assessment_targets( filter={ 'assessmentTargetNamePattern': os.environ['ENV'] + '-target' } ) while 1: time.sleep(30) awsagents_status = inspector.preview_agents(previewAgentsArn=assessment_target.get('assessmentTargetArns')[0]) logger.info(awsagents_status.get('agentPreviews')[0]) if awsagents_status.get('agentPreviews')[0].get('agentHealth') == 'HEALTHY': break """ list_assessment_templates Response Syntax { 'assessmentTemplateArns': [ 'string', ], 'nextToken': 'string' } https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/inspector.html#Inspector.Client.list_assessment_templates """ assessment_templates = inspector.list_assessment_templates( filter={ 'namePattern': 'test-template' } ) # 評価テンプレートARN一覧取得 if not assessment_templates.get('assessmentTemplateArns'): return logger.info(assessment_templates.get('assessmentTemplateArns')) # 評価の実行 for template_arn in assessment_templates.get('assessmentTemplateArns'): inspector.start_assessment_run( assessmentTemplateArn=template_arn, assessmentRunName='RunAssessment_' + 'test-template' + datetime.date.today().strftime("%Y-%m-%d") )Inspector評価の結果をSlackに通知するLambda
function_inspector_report.tfresource "aws_lambda_function" "inspector_report" { filename = "./function_inspector_report.zip" function_name = "function_inspector_report" role = aws_iam_role.lambda_role.arn handler = "lambda_function.lambda_handler" source_code_hash = filebase64sha256("./function_inspector_report.zip") runtime = "python3.8" timeout = 300 environment { variables = { TZ = "Asia/Tokyo" SLACK_HOOK_URL = "https://xxxxxxxxxxxxx" } } } resource "aws_lambda_permission" "inspector_report_permission" { action = "lambda:InvokeFunction" function_name = aws_lambda_function.inspector_report.function_name principal = "sns.amazonaws.com" }function_inspector_report.py
import json import boto3 import os import logging import datetime from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError import time logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): assessment_run_arn = get_assessment_run_arn(event) if not assessment_run_arn: logger.info('AssessmentTarget Not Found') logger.info(event) return send_inspector_assessment_report(assessment_run_arn) terminate_ec2_instance() def get_assessment_run_arn(event): """ 評価実行のARN取得処理 SNS, Lambdaテスト実行の判定を行いInspector評価実行のARNを返却します Args: event : Lambda実行Parameter Return: assessment_run_arn: Inspector 評価実行ARN """ inspector = boto3.client('inspector') # SNSからのLambda実行 if event.get('Records'): logger.info('SNS execute') message = event['Records'][0]['Sns']['Message'] message = json.loads(message) logger.info(message) return message.get('run') # Lambda テスト実行パラメータ有 elif event.get('target_date'): logger.info('Lambda execute Parameter ON target_date =' + event.get('target_date')) """ list_assessment_runs Response Syntax { 'assessmentRunArns': [ 'string', ], 'nextToken': 'string' } https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/inspector.html#Inspector.Client.list_assessment_runs """ assessment_runs = inspector.list_assessment_runs( filter={ 'namePattern':'RunAssessment_' + 'test-template' + event.get('target_date') } ) return assessment_runs.get('assessmentRunArns')[0] # Lambda テスト実行パラメータ無 else: logger.info('Lambda execute Parameter OFF') assessment_runs = inspector.list_assessment_runs( filter={ 'namePattern':'RunAssessment_' + 'test-template' + datetime.date.today().strftime("%Y-%m-%d") } ) return assessment_runs.get('assessmentRunArns')[0] def send_inspector_assessment_report(assessment_run_arn): """ Inspector評価実行結果をSlackに送信します """ inspector = boto3.client('inspector') report = {} """ get_assessment_report Response Syntax { 'status': 'WORK_IN_PROGRESS'|'FAILED'|'COMPLETED', 'url': 'string' } https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/inspector.html#Inspector.Client.get_assessment_report """ # すぐ結果を取得しても取得できない時があるのでStatusがCOMPLETEDになるまで待機 while 1: time.sleep(30); # HTML形式でReport出力 assessment_report_html = inspector.get_assessment_report( assessmentRunArn=assessment_run_arn, reportFileFormat='HTML', reportType='FULL' ) if assessment_report_html.get('status') == 'COMPLETED': break # すぐ結果を取得しても取得できない時があるのでStatusがCOMPLETEDになるまで待機 while 1: time.sleep(30); # PDF形式でReport出力 assessment_report_pdf = inspector.get_assessment_report( assessmentRunArn=assessment_run_arn, reportFileFormat='PDF', reportType='FULL' ) logger.info(assessment_report_pdf.get('status')) if assessment_report_pdf.get('status') == 'COMPLETED': break report['arn'] = assessment_run_arn report['html'] = assessment_report_html.get('url') report['pdf'] = assessment_report_pdf.get('url') channel = '#' + 'inspector-report' message = "*Inspector 評価実行結果*\n*Arn*:```{}```\n\n*HTML結果*:```{}```\n\n*PDF結果*:```{}```\n\n※ページの有効期限が900sで切れます。有効期限が切れた時はLambda関数:function_inspector_reportでテスト実行またはawscliから実行を行なってください\n```テストイベントのパラメータ:{}\naws inspector get-assessment-report --assessment-run-arn {} --report-file-format PDF --report-type FULL```" slack_message = { 'channel': channel, 'text': message.format( report.get('arn'), report.get('html'), report.get('pdf'), '{"target_date": YYYYMMDD}', report.get('arn') ) } HOOK_URL = os.environ['SLACK_HOOK_URL'] req = Request(HOOK_URL, json.dumps(slack_message).encode('UTF-8')) try: response = urlopen(req) response.read() except HTTPError as e: logger.error("Request failed: %d %s", e.code, e.reason) except URLError as e: logger.error("Server connection failed: %s", e.reason) def terminate_ec2_instance(): """ Inspectorで分析したEC2InstanceをTerminateします """ ec2 = boto3.client('ec2') # tag:Inspector:trueのEC2Instance一覧取得 # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.describe_instances instances = ec2.describe_instances(Filters=[{ 'Name': 'tag:Inspector', 'Values': ['true'] }]) if not instances: return # InstanceIDの配列生成 instance_ids = [] for reservation in instances.get('Reservations'): for instance in reservation.get('Instances'): logger.info(instance.get('InstanceId')) instance_ids.append(instance.get('InstanceId')) if not instance_ids: return # 対象EC2InstanceのTerminate # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.terminate_instances ec2.terminate_instances( InstanceIds=instance_ids )CloudWatchEventsの設定
最後にAmazon EventBridgeでInspector実行Lambdaをスケジュール設定して完了です。
実行してみた結果
Slackに以下メッセージが表示されたことが確認できました!!
Inspector 評価実行結果 Arn: arn:aws:inspector:xxxx:xxxx:target/xxxx/template/xxxx/run/xxxx HTML結果: https://inspector-html-report PDF結果: https://inspector-pdf-report ※ページの有効期限が900sで切れます。有効期限が切れた時はLambda関数:function_inspector_reportでテスト実行またはawscliから実行を行なってください テストイベントのパラメータ:{"target_date": YYYYMMDD} aws inspector get-assessment-report --assessment-run-arn xxx --report-file-format PDF or HTML --report-type FULL
- 投稿日:2020-12-18T19:42:33+09:00
Amazon Transcribeで日本語会話を文字起こししてみた
インタビュー動画の記事作成をする機会があり、動画を再生して、少し戻って。。。というのが面倒だったので、音声をなんとか一発で文字起こしできないかを調べていたところ、いくつかツールが存在していることを知りました。
その中でも普段から馴染みのあるAWSの「Amazon Transcribe」を試しに使ってみたので、その感想をメモとして残します。
Amazon Transcribeとは
音声ファイルの文字起こしをしてくれるAWSのサービスです。
Amazon Transcribe は、自動音声認識 (ASR、automatic speech recognition) と呼ばれる深層学習プロセスを使って迅速かつ高精度に音声をテキストに変換します。
引用:https://aws.amazon.com/jp/transcribe/料金も15秒の音声の文字起こしで0.006 USDで、1時間でも1.44 USDととてもリーズナブルです。他の文字起こしサービスでは1時間で2000円を超えることも多いので、値段設定はさすがAWSですね。登録後12ヶ月は1ヶ月あたり60分の無料利用枠もあるので、気になる方は是非試してみてください。
使ってみた
使い方もとても簡単です。
s3にファイルをアップロードする
Amazon Transcribeでjobを作成する
以上です。
操作手順等は以下の記事を参考にさせていただきました。ありがとうございます。
https://qiita.com/shimizu-nowhere/items/2508f2e87daee8cb1083
https://qiita.com/kooohei/items/2580addd6c1bbc8f1c34以下が出力結果のサンプルです。内容は編集しています。
単語ごとに出力されて、それぞれの精度まで数値で出力されます。{ "jobName": "amazon_transcribe", "accountId": "hogehogehoge", "results": { "transcripts": [ { "transcript": "hoge fuga piyp" } ], "items": [ { "start_time": "0.04", "end_time": "0.41", "alternatives": [ { "confidence": "0.9699", "content": "hoge" } ], "type": "pronunciation" }, { "start_time": "0.42", "end_time": "0.79", "alternatives": [ { "confidence": "0.5512", "content": "fuga" } ], "type": "pronunciation" }, { "start_time": "0.79", "end_time": "1.45", "alternatives": [ { "confidence": "0.9987", "content": "piyo" } ], "type": "pronunciation" } ] }, "status": "COMPLETED" }感想
以下使ってみての感想です。
精度に関しては予想よりもよかった
英語ならまだしも日本語の文字起こしは難しいんじゃないかと思っていて、正直あまり期待していませんでした。ところが、実際に使ってみると単語単位では予想以上に高精度で文字起こしができており、文章単位でもところによっては精度が高かったです。
文字起こしまでの操作が簡単
AWSを少しでも触ったことのある人ならば、文字起こし完了までのハードルはとても低いと思います。
対話の音声データでは利用ハードルがグッと上がる
今回使用したデータはインタビューの音声データで、登場人物が二人いました。そのため、文章や単語の文字起こしができても、どちらが発言したのかはインタビューの流れから確認する必要があります。また、同時に発言しているシーンでは精度が極端に低下していました。利用シーンは考える必要がありそうです。
終わりに
実際にAmazon Transcribeを使ってみると、想像以上に精度は高かったです。ただ、所々でミスがあったりするので、あくまでサポート的な役割でこのサービスは使ってみるのがいいと思いました。AWSのアップデート速度は凄まじいので、日本語の識別精度もどんどん良くなっていくと思います。
参考
https://aws.amazon.com/jp/transcribe/
https://qiita.com/shimizu-nowhere/items/2508f2e87daee8cb1083
https://qiita.com/kooohei/items/2580addd6c1bbc8f1c34
- 投稿日:2020-12-18T19:04:28+09:00
AWS Protonのサンプルプロジェクトを試す
AWS Protonがパブリックプレビューで公開されているので、
サンプルプロジェクトを実施して具体的にどんなことが出来るのかを確かめてみました。
この記事ではサンプルプロジェクトで何ができたのか、またサンプルプロジェクトで実際に何が起きたのかを紹介したいと思います。AWS Proton とは
- 公式より
コンテナおよびサーバーレスアプリケーションのための初の完全マネージド型アプリケーションデプロイサービスです。 Proton を使用すれば、プラットフォームエンジニアリングチームは、インフラストラクチャのプロビジョニング、 コードデプロイ、モニタリング、更新の際に必要になるさまざまなツールをすべて接続し、協調させることできます。 数百、場合によっては数千のマイクロサービスを、絶えず変化するインフラストラクチャリソースと 継続的インテグレーション/継続的デリバリー (CI/CD) 構成で維持することは、 どんなに有能なプラットフォームチームにとってもほぼ不可能なタスクです。 AWS Proton を利用することで、この煩雑なタスクを管理して一貫した標準を適用するために必要となるツールをプラットフォームチームが 手にすることとなり、デベロッパーはコンテナとサーバーレステクノロジーを利用してコードを簡単にデプロイできるようになるため、前述の問題を解決できます。
- 私が触った感覚ではざっくりですが
- AWS上でのサーバレス環境とそのCICD環境をセットで管理できるサービス
- システムのリソースおよびCICD環境のリソース構築のためのテンプレートを作成し、バージョン管理できる
- 開発者はテンプレートからアプリケーションをデプロイ、運用できる
- アプリの更新、削除などもProtonのコマンドで実行可能
- テンプレートは現状はCloudFormationテンプレートを利用したものになります。またデプロイもCodePipelineを利用しています
- ロードマップによると今後terraformや他のサービスも利用できるようになるようです
環境テンプレートとサービステンプレート
Protonでは2種類のテンプレートを作成、管理することができます。
- 環境テンプレート
- 主にアプリケーションのインフラ周りのリソースを定義するテンプレートになります
- 例)VPC,DB,SG,Role,ECSClusterなど
- サービステンプレート
- アプリケーション本体のリソースを定義するテンプレート
- 例)APIGateway,Lambda、ECSService、ALBなど
- CICDパイプライン用のリソースもこちらで定義します
- 例)CodePipeline,CodeBuild,CodeDeployなど
ただし、テンプレートの記述自体は自由なので、環境テンプレート、サービステンプレートにどのリソースを定義するかは開発者で判断する必要があります。
チュートリアル実施
公式で用意されているサンプルテンプレートを試してみました。サンプルはAPI Gateway+LambdaでCRUD処理をするAPIをデプロイするサンプルとなっています。
サンプルのソースやサンプルを利用したチュートリアル手順はgithubで公開されていますので、ぜひ試してみてください。
1 事前準備
1.1 サンプルリポジトリのクローン
まずはワークスペース上にサンプルテンプレート用のリポジトリをクローンしてください。
その後、カレントディレクトリを「{ワークスペース}/aws-proton-sample-templates/lambda-crud-svc」まで移動してください。
基本的には今後の操作はそのディレクトリ上で実行していきます。git clone https://github.com/aws-samples/aws-proton-sample-templates.git cd ./aws-proton-sample-templates/lambda-crud-svc/
1.2 アカウントIDを環境変数に設定
チュートリアルのCLIコマンドでAWSアカウントIDが必要になるため予め環境変数に登録しておきます。
この手順は自分でコマンド内の${account_id}
をAWSアカウントIDに置き換えるのであれば実施不要です。account_id=`aws sts get-caller-identity|jq -r ".Account"`1.3 CLIでProtonのコマンドを実行できるようにする
Protonはまだプレビュー版なので、aws cliでコマンドを実行できるように設定が必要になります。
aws s3 cp s3://aws-proton-preview-public-files/model/proton-2020-07-20.normal.json . aws s3 cp s3://aws-proton-preview-public-files/model/waiters2.json . aws configure add-model --service-model file://proton-2020-07-20.normal.json --service-name proton-preview mv waiters2.json ~/.aws/models/proton-preview/2020-07-20/waiters-2.json rm proton-2020-07-20.normal.json1.4 Protonのテンプレート置き場用のS3バケット作成
Protonの環境、サービステンプレートはS3に保存されるため、テンプレート保存用のバケットを作成します。
aws s3api create-bucket \ --region us-west-2 \ --bucket "proton-cli-templates-${account_id}" \ --create-bucket-configuration LocationConstraint=us-west-21.5 IAMロール作成
Protonがサービス実行のために必要なIAMロールを作成します。
aws iam create-role \ --role-name ProtonServiceRole \ --assume-role-policy-document file://./policies/proton-service-assume-policy.json aws iam attach-role-policy \ --role-name ProtonServiceRole \ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess aws proton-preview update-account-roles \ --region us-west-2 \ --account-role-details "pipelineServiceRoleArn=arn:aws:iam::${account_id}:role/ProtonServiceRole"1.6 アプリのソースコード用リポジトリの設定
サンプルではGithubのリポジトリからソースコードをpullしてデプロイする構成になっているので、
ソースコードをGithub上のリポジトリに置き、さらにAWSからそのリポジトリを参照できるようにするための接続設定が必要になります。
- 以下のリポジトリを自分のGithubアカウントにフォークする
- CodeStarで自分のGithubアカウントの接続設定を作成する
- https://us-west-2.console.aws.amazon.com/codesuite/settings/connections?region=us-west-2
- 作成した接続設定のARNは後で利用するのでメモしておく
2 環境テンプレート作成
まずは環境テンプレートの作成から行っていきます。
2.1 テンプレート作成
まずはテンプレートを作成します。
aws proton-preview create-environment-template \ --region us-west-2 \ --template-name "crud-api" \ --display-name "CRUD Environment" \ --description "Environment with DDB Table"コンソール上では環境テンプレート「crud-api」が作成されていることが確認できました。
ただし、この時点ではまだ何もない状態となっています。2.2 メジャーバージョン作成
メジャーバージョンを新規登録します。
aws proton-preview create-environment-template-major-version \ --region us-west-2 \ --template-name "crud-api" \ --description "Version 1"コンソール上では新たにバージョン「1」が登録されたことが確認できます
2.3 マイナーバージョン作成
さらにマイナーバージョンの登録を行います。
環境テンプレート群を圧縮してS3にアップロードし、マイナーバージョン登録時のコマンドで
そのアップロードされた環境テンプレートのS3パスを指定しています。tar -zcvf env-template.tar.gz environment/ aws s3 cp env-template.tar.gz s3://proton-cli-templates-${account_id}/env-template.tar.gz --region us-west-2 rm env-template.tar.gz aws proton-preview create-environment-template-minor-version \ --region us-west-2 \ --template-name "crud-api" \ --description "Version 1" \ --major-version-id "1" \ --source-s3-bucket proton-cli-templates-${account_id} \ --source-s3-key env-template.tar.gzコンソールでもマイナーバージョン「1.0」が登録されていますね。
2.4 登録したバージョンの公開
コンソールの忠告にもあるようにまだバージョンの状態が「Draft」なので、この状態ではこのテンプレートはまだ利用できない状態になっています。
利用できるようにするためにステータスを「公開」状態に更新しましょう。aws proton-preview update-environment-template-minor-version \ --region us-west-2 \ --template-name "crud-api" \ --major-version-id "1" \ --minor-version-id "0" \ --status "PUBLISHED"ステータスが「Published」に更新されました。
ちなみに一度公開状態にしたバージョンはDraft状態には戻せないようです。
やり直す場合は対象のバージョンを削除して作り直す、または新しいバージョンを発行することになるようです。3 サービステンプレート作成
3.1 テンプレート作成
環境テンプレートと同様にまずはテンプレートを作成します。
aws proton-preview create-service-template \ --region us-west-2 \ --template-name "crud-api-service" \ --display-name "CRUD API Service" \ --description "CRUD API Service backed by AWS Lambda"3.2 メジャーバージョン作成
メジャーバージョンを新規登録します。
aws proton-preview create-service-template-major-version \ --region us-west-2 \ --template-name "crud-api-service" \ --description "Version 1" \ --compatible-environment-template-major-version-arns arn:aws:proton:us-west-2:${account_id}:environment-template/crud-api:1コンソール上では新たにバージョン「1」が登録されたことが確認できます
3.3 マイナーバージョン作成
さらにマイナーバージョンの登録を行います。
tar -zcvf svc-template.tar.gz service/ aws s3 cp svc-template.tar.gz s3://proton-cli-templates-${account_id}/svc-template.tar.gz --region us-west-2 rm svc-template.tar.gz aws proton-preview create-service-template-minor-version \ --region us-west-2 \ --template-name "crud-api-service" \ --description "Version 1" \ --major-version-id "1" \ --source-s3-bucket proton-cli-templates-${account_id} \ --source-s3-key svc-template.tar.gzコンソールでもマイナーバージョン「1.0」が登録されていますね。
2.4 登録したバージョンの公開
コンソールの忠告にもあるようにまだバージョンの状態が「Draft」なので、この状態ではこのテンプレートはまだ利用できない状態になっています。
利用できるようにするためにステータスを「公開」状態に更新しましょう。aws proton-preview update-service-template-minor-version \ --region us-west-2 \ --template-name "crud-api-service" \ --major-version-id "1" \ --minor-version-id "0" \ --status "PUBLISHED"ステータスが「Published」に更新されました。
4 環境デプロイ
まずは環境側のリソースをデプロイします。
aws proton-preview create-environment \ --region us-west-2 \ --environment-name "crud-api-beta" \ --environment-template-arn arn:aws:proton:us-west-2:${account_id}:environment-template/crud-api \ --template-major-version-id 1 \ --proton-service-role-arn arn:aws:iam::${account_id}:role/ProtonServiceRole \ --spec file://specs/env-spec.yamlここで指定している
specs/env-spec.yaml
は環境テンプレートに対する入力パラメータが定義されているyamlになります。このサンプルの環境テンプレートで作成されるテンプレートは以下になります。
- DynamoDBテーブル
コンソールで確認するとProtonコンソールでは環境が作成されていることが確認できます
CloudFormationコンソールでは環境リソース用のスタックが作成されています。
CloudFormationでDynamoDBテーブルが作成されたことが確認できますね。5 サービスデプロイ
デプロイコマンド実行
次にサービスのデプロイを行います。
デプロイコマンド時にアプリのソースコードがあるリポジトリおよびリポジトリ接続のための接続情報を入力する必要があります。ここでも環境デプロイ時と同様に入力パラメータは
file://specs/svc-spec.yaml
で定義されています。
サービスがどの環境リソースを利用するかは入力パラメータで指定されています。aws proton-preview create-service \ --region us-west-2 \ --service-name "tasks-front-end" \ --repository-connection-arn arn:aws:codestar-connections:us-west-2:${account_id}:connection/<your-codestar-connection-id> \ --repository-id "<your-source-repo-account>/<your-repository-name>" \ --branch "main" \ --template-major-version-id 1 \ --service-template-arn arn:aws:proton:us-west-2:${account_id}:service-template/crud-api-service \ --spec file://specs/svc-spec.yaml入力パラメータは以下の形式で定義されています。
すこしややこしい&私もまだ理解しきれていないですが、入力パラメータinstances
は配列のため、name以下のパラメータを複数定義できます。
配列が複数定義されている場合、その要素の数だけサービス用のリソースがデプロイされるような設計になっています。svc-spec.yamlproton: ServiceSpec # cicd用リソースで利用する入力パラメータ pipeline: unit_test_command: make test packaging_commands: make publish # サービスリソースで利用する入力パラメータ instances: - name: "front-end" # どの環境を利用するか environment: "crud-api-beta" spec: resource_name: task resource_handler: src/api lambda_runtime: ruby2.7デプロイコマンド実行直後
デプロイコマンド実行直後ではまずアプリケーションリソース用のテンプレート(
service/infrastructure/cloudformation.yaml
)のデプロイが実施されます。CICD用パイプラインリソースのデプロイ
アプリケーションリソース用のテンプレートデプロイ完了後、CICDパイプライン用のテンプレート(
service/pipeline/cloudformation.yaml
)がデプロイされます。
CodePipelineやCodBuildが作成されていることが確認できます。CodePipeline実行
CICDパイプライン用のテンプレートのデプロイ完了後、作成されたCodePipelineが実行し、
CodeBuild上でアプリケーションのデプロイ処理が実行されます。パイプライン実行後、アプリケーションリソース用のスタックを再度確認すると新たにリソースが作成されていることが確認できます。
サンプルのチュートリアル実施はこれで以上となります。
テンプレートが用意されていれば、
コマンド一つでCICD環境の構築からアプリケーションデプロイまで実施できますね。サービスデプロイ時の実施フロー
実際に触ってみてわかったのですが、サービステンプレートからサービスをデプロイする際、アプリケーションリソース用のスタックがデプロイコマンド実行時とPipeline実行後でスタックに含まれるリソースが異なっていました。
これ、サンプルソースを確認すると一つのテンプレートを使いまわしていて、入力パラメータによって、デプロイするリソースを切り替えていることがわかりました。ソースを見るだけでは少し分かりづらいので、実際にどのようなフローでリソースが作成されていったのか整理しました。
- デプロイコマンド実行
- アプリケーション用テンプレート(`service/infrastructure/cloudformation.yaml)からスタック作成
- この時点に作成されるリソース
- CrudHttpApi(AWS::Serverless::HttpApi) - CRUD処理用API Gateway
- CICDパイプライン用のテンプレート(
service/pipeline/cloudformation.yaml
)からスタック作成
- 作成されるリソース(IAMロール、ポリシーについては割愛しています)
- FunctionBucket(AWS::S3::Bucket) - Lambdaリソースアップロード用バケット
- BuildProject(AWS::CodeBuild::Project) - Buildステージ用CodeBuildプロジェクト
- PipelineArtifactsBucketEncryptionKey(AWS::KMS::Key) - アーティファクトアップロード時の暗号化キー
- PipelineArtifactsBucketEncryptionKeyAlias(AWS::KMS::Alias) - PipelineArtifactsBucketEncryptionKeyのエイリアス
- PipelineArtifactsBucket(AWS::S3::Bucket) - アーティファクト用S3バケット
- Pipeline(AWS::CodePipeline::Pipeline) - CICD用CodePipeline
- 入力パラメータの
service_instances
の要素数分デプロイ用のActionが追加されます- 以下のリソースは入力パラメータの
service_instances
の要素数分作成されます
- Deploy{{loop.index}}Project(AWS::CodeBuild::Project) - Deployステージ用CodeBuildプロジェクト
- CodePipeline実行
- CodePipelineのBuildステージでCodeBuildが実行
- CodeBuild内でProtonコマンド用の入力パラメータファイル(svc-spec.yamlのような)を作成
- 具体的にはsvc-spec.yamlに
instances[*].spec.code_uri
パラメータが追加された状態のファイルになります。- CodePipelineのDeployステージでCodeBuild実行
- CodeBuild上でprotonの
update-service-instance
コマンド実行
aws proton-preview --endpoint-url https://proton.$AWS_DEFAULT_REGION.amazonaws.com --region $AWS_DEFAULT_REGION update-service-instance --version-update-type UPDATE_SPEC --service-instance-name $service_instance_name --service-name $service_name --spec file://rendered_service.yaml
update-service-instance
で再度アプリケーション用テンプレート(`service/infrastructure/cloudformation.yaml)のスタック更新
- このとき入力パラメータに
instances[*].spec.code_uri
が含まれているため、追加で以下のリソースが作成される
- ListFunction(AWS::Serverless::Function) - データ一覧取得リクエスト用Lambda
- CreateFunction(AWS::Serverless::Function) - データ作成リクエスト用Lambda
- GetFunction(AWS::Serverless::Function) - データ取得リクエスト用Lambda
- DeleteFunction(AWS::Serverless::Function) - データ削除リクエスト用Lambda
- UpdateFunction(AWS::Serverless::Function) - データ更新リクエスト用Lambda
このように同じテンプレートファイルを利用しているのにも関わらず、入力パラメータなどによって動的にCfnテンプレートを作成し、実行しています。これはサンプルソースを見ればわかりますが、テンプレートファイルにテンプレートエンジン「jinja」を利用しているためです。
jinjaを利用して入力パラメータの参照はif文などでCfnテンプレートを動的に作成できるようになっています。以下のコードのように入力パラメータで条件分岐していたため、実行タイミングによって作成されるリソースが切り替わっていました。
service/infrastructure/cloudformation.yamlResources: CrudHttpApi: Type: AWS::Serverless::HttpApi Properties: FailOnWarnings: false {% if service_instance.code_uri|length %} # <-入力パラメータ「code_uri」の有無で条件分岐 ListFunction: Type: AWS::Serverless::Function Properties: CodeUri: "{{service_instance.code_uri}}" Handler: "{{service_instance.resource_handler}}.list_{{service_instance.resource_name}}" Runtime: "{{service_instance.lambda_runtime}}" Environment: Variables: TABLE_NAME: "{{environment.TableName}}" Policies: - DynamoDBCrudPolicy: TableName: "{{environment.TableName}}" ...まとめ
チュートリアル実施しただけになりますが、触った感想としては、
- アプリケーションリソースとCICDリソースをまとめて管理できるのは便利だと思いました
- プロトンでバージョンも管理できるのもいいですね
- 環境テンプレートとサービステンプレートそれぞれ用意できる
- 一つの環境に複数のサービステンプレートのデプロイ等もできるので色々応用できそうです
- ただしどちらのテンプレートにも任意のリソースを定義できてしまうので、どのリソースを環境、サービスどちらテンプレートに置くかは開発者で検討する必要があります
- 個人的には環境テンプレートにはDB等のデータストアや基本的に変更しないリソースを定義、サービステンプレートには全削除しても問題ないリソース群を定義するのが一つの切り分け方になるのかなと感じました
- まだプレビュー版なので仕方がない部分もあるのですが、現状気になる点としては
- テンプレートの記述がProtonの仕様に制限される
- service_instancesの取り扱いの都合上必ず、
{% if service_instance.code_uri|length %}
などの記載が必要になっている状態です- また当然といえば当然ですが学習コストが高いですね。CloudFormation、CodePipeline、CodeBuildに加えてProtonの仕様も理解する必要があります
- 現時点ではアプリケーションのソースコードリポジトリがGithubかBitbucketのみ対応
- CodeCommit対応してほしいですね。(ロードマップにはCodeCommit対応が載っています)
- 既存のシステムからProtonに移行するのは大変そう
プレビュー版なのでまだまだ機能は限定的ですが、ロードマップでCloudFormation以外のテンプレートなどにも対応していくようなので、より汎用的になっていくようです。GAされるが楽しみですね。
- 投稿日:2020-12-18T18:56:04+09:00
AssumeRoleをECS&Digdag環境で利用する
やりたいこと
AWS アカウント A
- S3 に日次でファイルが置かれる
AWS アカウント B
- アカウント A の S3 のファイルを取得したい
- 実行環境は ECS 上で動いている Digdag サーバー
AssumeRole は一時的なクレデンシャルを生成して、他の AWS アカウントからのアクセスを許可できるので、以下のようなセキュリティ的に避けたい設定を行わなくて済みます
- IAM ユーザーを作成し、AWS アクセスキー発行
- S3 の公開設定
AWS アカウント A - 参照先
ロールを作成
- AWS コンソールからロールを作成
- 信頼されたエンティティの種類を「別の AWS アカウント」にして、アカウント B のアカウント ID を設定
- この段階では、アカウント B の root に権限が付与される(ここは後で変えます)
- アカウント B 用のポリシーを作成して、ロールにアタッチ
以下は今回のやりたいことを実現する最低限のポリシーです{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "s3:GetObject", "Resource": ["arn:aws:s3:::[バケット名]/*"] } ] }AWS アカウント B - 参照元
AssumeRole のポリシーを作成
- AWS コンソールからポリシーを作成
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::[アカウントAのID]:role/[さっき作ったロール名]" } ] }
- ECS タスクのロールにこのポリシーをアタッチ
ECS タスクで使われているロールは、コンソールの ECS タスク定義から、「タスク実行ロール」で確認できますAWS アカウント A - 参照先
作成したロールの信頼関係タブから、以下のように編集
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::[アカウントBのID]:role/ECSタスクロール名" }, "Action": "sts:AssumeRole", "Condition": {} } ] }さきほどは、アカウント B の root に権限を与えていましたが、ECS タスクロールに権限を与えるように変更しました
動作確認
AWS CLI
Digdag のタスクが sh で、AWS CLI を使う場合
ECS タスクで使う Docker などで以下を用意する
~/.aws/config
を作成[profile assume] role_arn = arn:aws:iam::[アカウントAのID]:role/[ロール名] credential_source = EcsContainer または echo "[profile assume]" > ~/.aws/config echo "role_arn = arn:aws:iam::[アカウントAのID]:role/[ロール名]" >> ~/.aws/config echo "credential_source = EcsContainer" >> ~/.aws/configaws s3 cp [コピー元] [コピー先] --recursive --profile assumePython
Digdag のタスクが Python の場合
from sts import STS # role_arn: arn:aws:iam::[アカウントAのID]:role/[ロール名] # bucket: 参照先のバケット名 # key: 参照先のオブジェクトキー # save_file_path: 保存先(Digdagコンテナー内) class S3(object): def get_by_sts(self, role_arn, bucket, key, save_file_path): resource = STS().get_resource(role_arn, "s3") resource.Bucket(bucket).download_file( key, save_file_path, )import boto3 class STS(object): sts_client = boto3.client("sts") def get_resource(self, role_arn, resource_name): role_obj = self.sts_client.assume_role( RoleArn=role_arn, RoleSessionName="AssumeRoleSession", ) resource = boto3.resource( resource_name, aws_access_key_id=role_obj["Credentials"]["AccessKeyId"], aws_secret_access_key=role_obj["Credentials"]["SecretAccessKey"], aws_session_token=role_obj["Credentials"]["SessionToken"], ) return resource
boto3.client("sts").assume_role
に RoleArn と任意のセッション名を渡して、一時的なクレデンシャルを発行しています
- 投稿日:2020-12-18T18:18:26+09:00
機械学習を利用した不正検知サービス Amazon Fraud Detector を触ってみる
こんにちは。
ナレッジコミュニケーション 繁松です。この記事は株式会社ナレッジコミュニケーションが運営する Amazon AI by ナレコム Advent Calendar 2020 の 18日目にあたる記事になります。
本日は、機械学習を利用した不正検知サービスの[Amazon Fraud Detector]について書きたいと思います。
Amazon Fraud Detectorとは
AWSより引用
Amazon Fraud Detector は完全マネージドサービスであり、オンライン支払い詐欺や偽のアカウントの作成など、潜在的に不正なオンラインアクティビティを簡単に識別できます。 Amazon Fraud Detector は、機械学習 (ML) と AWS およびAmazon.com の 20 年にわたる不正検出の専門知識を使用して、不正行為の可能性を自動的に特定し、より多くの不正行為を迅速に発見できるようにします。 Amazon Fraud Detector は、数回クリックするだけで、ML の経験がまったくない不正検出モデルを作成できます。 これは、Amazon Fraud Detector がすべての ML の手間のかかる作業を処理するためです。機械学習の知識が無い人でも簡単に詐欺、偽のアカウント作成等の不正検知を取り入れることができるフルマネージドサービスです。
Amazon Fraud Detectorを使ってみた
実際に使ってみたいと思います。
手順は以下の手順になります。AWSブログより引用
ステップ 1: 不正行為について評価したいイベントを定義する。 ステップ 2: 履歴的なイベントデータセットを Amazon S3 にアップロードして、不正検出モデルタイプを選択する。 ステップ 3: Amazon Fraud Detector が履歴データを入力として使用し、カスタムモデルを構築する。 このサービスは、自動的にデータを調べてリッチ化し、特徴量エンジニアリングを実行して、アルゴリズムの選択、およびモデルのトレーニングと調整を行い、モデルをホストします。 ステップ 4: モデルの予測に基づいて受け入れる、レビューする、またはより多くの情報を収集するかどうかのルールを作成する。 ステップ 5: オンラインアプリケーションから Amazon Fraud Detector API を呼び出して、リアルタイムの不正行為予測を受け取り、設定された検出ルールに基づいて措置を講じる。 (例: e コマースアプリケーションは、E メールと IP アドレスを送信し、不正行為スコアとルールからの出力 (例: レビュー) を受け取ることができます)数クリックで簡単に?とは行かなそうですが、実際にやってみたいと思います。
今回は公式のチュートリアルの「パートA:Amazon Fraud Detectorモデルを構築」を参考に進めて行きます。1. サンプルのトレーニングデータの取得、アップロード
AWS公式の架空のトレーニングデータを以下のURLよりダウンロードします。
ダウンロードはこちら次にS3バケットにトレーニングデータをアップロードします。
※今回amazon-fraud-detector-testというバケットを作成しています。
※Zipファイルには、[registration_data_20K_full.csv]と[registration_data_20K_minimum.csv]が入っています。
S3とAmazon Fraud Detectorのリージョンは同じリージョンにする必要があります。
今回は[registration_data_20K_minimum.csv]をアップロードします。
2. イベント作成
実際にAmazon Fraud Detectorにイベントを作成していきます。
東京リージョンは2020年12月18日現在、未対応ですので、、バージニア北部リージョンに作成します。
[トレーニングデータセットから変数を選択する]を選択し、IAMロールの作成を行います。
IAMロール名は先程作成したS3バケット名と同じ名前にします。
先程アップロードしたcsvファイルのS3 URLを入力しアップロードを行い、
表示された変数の変数型を指定します。
ラベルを少なくとも2つ定義する必要があるので、
[fraud]と[legit]を定義し、イベントタイプの作成を行います。
3. モデル作成
モデルはデータのサイズに応じて30~40分から数時間かかるそうです。
生成後はパフォーマンスメトリクスの確認と、モデルのデプロイができるようになります。
Amazon Fraud Detectorを取り入れた運用を考え、不正アクセスなどを防ぎたいですね。
以上 Amazon Fraud Detectorについてでした。参考文献
https://aws.amazon.com/jp/fraud-detector/features/
https://aws.amazon.com/jp/blogs/news/amazon-fraud-detector-is-now-generally-available/
- 投稿日:2020-12-18T18:06:51+09:00
AWS Tools for PowerShellで不要なMFAデバイス登録したプロフィールでのロール切替えが失敗する
概要
AWS Tools for PowerShellでロール切替え用のプロフィールを作る際、MFA不要のロールに対してはMFAデバイスを登録してはいけない。不要に登録すると、このプロファイルでの操作が
Error calling AssumeRole for role (ロールのarn)
で失敗する。詳細
AWS Tools for PowerShellでロール切替えをしたいときは、ロール切替え用のプロファイルを作成するのが便利。作成するプロファイルが
MyRole
で使用する認証情報のプロファイルがMyCredProfile
だとして、こんな感じ。^
はコマンドが複数行にわたるときのWindows PowerShell環境でのシンボルなので、Linux環境では\
に置き換えてください。PS> Set-AWSCredential -StoreAs MyRoleProfile -SourceProfile MyCredProfile ^ -RoleArn arn:aws:iam::123456789012:role/MyRole登録したプロファイルが使用されるように
Set-AWSCredential
で指定しておく。PS> Set-AWSCredential -ProfileName MyRoleProfileここでもし使用するロールがMFA(多要素認証)必須の場合、次のようにしてMFAデバイスも登録しておく。
PS> Set-AWSCredential -StoreAs MyRoleProfile -SourceProfile MyCredProfile ^ -RoleArn arn:aws:iam::123456789012:role/MyRole -MfaSerial arn:aws:iam::123456789012:mfa/MyNameこうすると、コマンドレット実行時に適宜MFAコードを問合わせてくれる。
PS> Get-IAMUserList Enter MFA code:*******さて、もしここでロール
arn:aws:iam::123456789012:role/MyRole
がMFAを要求しないロールの場合どうなるか?もちろん、MFAコードを聞かれないとか、聞かれても無視されて、操作は成功すると思ってた。でも実際には、エラーになる。PS> Get-IAMUserList Enter MFA code:******* Get-IAMUserList: Error calling AssumeRole for role arn:aws:iam::123456789012:role/MyRole結論としてAWS Tools for PowerShellでロール切替え用のプロフィールを作る際、MFA不要のロールに対してはMFAデバイスを登録してはいけない。不要に登録すると、このプロファイルでの操作が
Error calling AssumeRole for role (ロールのarn)
で失敗する。このエラー内容でこれがいけないのだとは気づきにくいと思うのだけど、気づけないとMFAコード入力し間違えたかなとか認証用プロファイルおかしいだろうかとか無駄な調査をすることになる。参照
- 投稿日:2020-12-18T17:59:23+09:00
Amazon Braket を使って量子コンピュータの実機を動かしました
はじめに
今回は、Amazon Braketを使って代表的な量子アルゴリズムを幾つか実装してみることにしました。量子アルゴリズムを学んでも、それが正しいことを机上の計算で確かめるだけでは、それが実際の量子デバイス上でワークするかどうか実感がわきません。その点、Amazon Braketでは後述するようにRegetti、IonQ、D-waveなど複数の量子コンピュータへのアクセスが可能です。同じアルゴリズムを異なるNISQな量子デバイスで計算することによって、リアルに量子アルゴリズムの正しさを実感できるのは魅力的です。
この記事では、量子アルゴリズムとして有名なDeutsch-JozsaのアルゴリズムとShorのアルゴリズムを取り上げます。後者はRSA暗号を解読できるアルゴリズムとして有名です。この記事が、量子コンピューティングを体験するきっかけとなれば幸いです。
Amazon Braket について
Amazon Braket は AWS が提供している量子コンピューティングサービスです。量子回路の設計を行い、ローカルシミュレーターまたはクラウドベースのマネージドシミュレーター SV1 で回路を実行できます。さらにこれらの古典シミュレータで設計された回路を、量子マシンで実行するような操作を Python の SDK を通して行うことができます。今回はこの Amazon Braket を活用して、量子アルゴリズムを構築、実行してみたいと思います。本記事は Braket が提供している Jupyter notebook 形式の環境にて実行した内容をご紹介しています。この環境へは Braket SDK がすでにインストールされていますので、すぐに始めることができます。回路の構築については、Braket ではいくつかの量子ゲート操作を提供しており、組み合わせて回路を構築します。追加できるゲートを確認してみましょう。
import string from braket.circuits import Gate gate_set = [attr for attr in dir(Gate) if attr[0] in string.ascii_uppercase] print('Gate set supported by SDK:\n', gate_set)出力結果は下記のようになります。なんとなく、馴染みのあるゲート操作が出力されているのではないでしょうか。
Gate set supported by SDK: ['CCNot', 'CNot', 'CPhaseShift', 'CPhaseShift00', 'CPhaseShift01', 'CPhaseShift10', 'CSwap', 'CY', 'CZ', 'H', 'I', 'ISwap', 'PSwap', 'PhaseShift', 'Rx', 'Ry', 'Rz', 'S', 'Si', 'Swap', 'T', 'Ti', 'Unitary', 'V', 'Vi', 'X', 'XX', 'XY', 'Y', 'YY', 'Z', 'ZZ']回路の作成は、空の回路を作成し、ゲートを追加して構築していきます。試しにベル状態を構築してみました。
# ベル状態の回路を構築 bell = Circuit().h(0).cnot(0, 1) print(bell)T : |0|1| q0 : -H-C- | q1 : ---X- T : |0|1|Deutsch-Jozsa のアルゴリズム
Deutsh-Jozsa のアルゴリズムは、古典アルゴリズムよりも、実行時間が短くなるとされる量子アルゴリズムの中で、一番最初に発見されたものの一つです。与えられた関数のオラクルの判定に使います。解くべき問題のサイズ $n$ に対し、古典アルゴリズムでは指数関数的に処理時間が増えるような問題に対して、量子計算を活用すると、多項式時間で収まることを示しました。
Braket を使った実装
例えば、$n=2$ とした $f_1(x_1, x_2)=x_1$ のような関数は $x_2$ の値によらず、$x_1$ の入力がそのまま出力されます。$x_1$ が 0 になるか 1 になるかは半々なので、この関数は均等なオラクルと言えます。オラクルの量子回路のイメージは下図のようになります。
オラクルは関数 $f$ の引数となる $2$ 量子ビットと制御ビットに対するユニタリ変換 $U_f$ として定義されます。オラクルで判定を行う際には、入力される量子ビットにそれぞれアダマール変換を行って重ね合わせ状態にした上でオラクルに入力します。オラクルの出力を再びアダマール変換した結果、制御ビット以外の出力が $\left|00\right>$ ならば、関数 $f$ は一定であり、それ以外の $\left|10\right>$, $\left|01\right>$, $\left|11\right>$ の何れかであれば、関数 $f$ は均等と判定できます。
それでは、均等な関数 $f_1(x_1,x_2) = x_1$ について、実際に量子回路を設計し、測定してみましょう。まずは、回路の設計は下記のようになります。
my_circuit1 = Circuit()\ .x(target=2)\ .h(range(3))\ .cnot(control=0, target=2)\ .h(range(3)) print(my_circuit1)このように表記されました。
T : |0|1|2|3| q0 : -H---C-H- | q1 : -H-H-|--- | q2 : -X-H-X-H- T : |0|1|2|3|続いて、この回路を使った量子計算の実行です。今回はシミュレータである SV1 を活用しています。
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1") my_bucket = f"amazon-braket-Your-Bucket-Name" my_prefix = "simulation-output" # the name of the folder in the bucket s3_folder = (my_bucket, my_prefix) # タスクの定義と実行 task = device.run(my_circuit1, s3_folder, poll_timeout_seconds = 100, shots=1000) result = task.result() counts = result.measurement_counts plt.bar(counts.keys(), counts.values()); plt.xlabel('bitstrings'); plt.ylabel('counts');Deutsch-Jozsaのアルゴリズムでは、関数$f(x_1,x_2)$が一定な関数の場合、必ず$\left|00\right>$を返します。今回は$\left|10\right>$が返ってきていることが観測され、関数が均等なオラクルだと判定されます。
Shorのアルゴリズム
素因数分解を少ない計算量で解く、という量子計算では非常に有名なアルゴリズムです。
ここでは簡単な例についてBraketを使った実装を行います。
以下、簡単にアルゴリズムについて説明を行いますが、こちらについてはIBMQのShor's algorithmと次の教科書を参考にさせていただきました。周期発見問題
周期発見問題と呼ばれる問題が解ければ、因数分解が効率的に行えることは1970年代から数学者の間で知られていました。周期発見問題は次のように定義されています。
2つの整数 $N, a$ が与えられたとき、 $a^r -1$ が $N$ の倍数となるような最小の整数 $r>0$ を求める
$r$ は $N$ を法としたときの $a$ の周期と呼ばれます。合同式を使うと、周期 $r$ は $a^r \equiv 1 (\mbox{mod} N)$ を満たします。量子計算によって、この周期 $r$ を少ない計算量で求めることができる、というのが利点となります。
素因数分解問題
素因数分解問題はその名の通り自然数 $N$ に対し、 $N=pq$ を満たす素数の組 $(p,q)$ を求める問題です。$N$ が大きくなるにつれて、この問題を解くための計算量が爆発的に増える、というのは有名な話だと思います。
詳細は何も説明しないので恐縮ですが、 $N$ と互いに素な整数 $x$ について $x^r \equiv 1~ (\mbox{mod}~ N)$ を満たす最小の $r$ を求めることができれば、古典アルゴリズムでも素因数分解問題を効率よく解けることが知られています。これはまさに $x$ の $N$ を法とした周期を求める問題です。Shorのアルゴリズム
周期発見問題の部分を量子計算に行わせて、残りは古典計算で素因数をみつける、というのが Shorのアルゴリズムになります(以下の手順は量子情報科学入門3章からの引用です)。
- $x\in { 1,\ldots,N-1 }$ を一様ランダムに選ぶ
- 最大公約数 GCD($x,N$) を計算する。その値が $1$ でなければ GCD($x,N$) を出力して終了する
- $x^r \equiv 1~ \mbox{mod}~ N$ となる最小の $r$ を求める (量子計算部分)
- $r$ が偶数かつ $x^{r/2}\not\equiv -1 ~ \mbox{mod}~ N$ ならば、GCD($x^{r/2}+1,N$) と GCD($x^{r/2}-1,N$) が $N$ を割り切るか検査し、割り切るならそれを出力して終了する。そうでなければ最初の工程からやり直す
Braketでの実装
Shorのアルゴリズムと言っていますが、量子計算部分は周期発見問題なので、簡単な周期発見問題に対応する回路を組んでみようと思います。例として、IBMQのページと同様に $N=15, a=7$ を扱ってみます。このとき
7^2 \equiv 4,~ 7^3 \equiv 13,~ 7^4 \equiv 1 ~ (\mbox{mod} 15)なので、周期 $r=4$ となります。
この問題を量子回路で表現する際に、 modular multiplication function $f(x)$ と呼ばれる関数を考えます。今の例では、具体的には $f(x)=7x~ (\mbox{mod} 15)$ です。この関数をユニタリー演算子 $U_f$ で表現し、これを回路で実現する、というのが目標になります。ビット列で15を法とした整数を表現したベクトルを $\left|x\right>$ とすると、$U_f$ の作用は
U_f : \left|x\right> ~ \rightarrow ~ \left|7x~ (\mbox{mod} 15)\right>なので、具体的に $\left|1\right>$ に作用させてみると
\left|1\right> ~ \rightarrow ~ \left|7\right> ~ \rightarrow ~ \left|4\right> ~ \rightarrow ~ \left|13\right> ~ \rightarrow ~ \left|1\right>となり、$(U_f)^4$ で元の状態に戻っています。
$U_f$ の量子回路における実装は次の形になります[Markov, Saeedi]。アルゴリズムの関係上、これは $f^{-1}$ に対応するものになっていて、上で見せたものと逆向きに変換されます(なので本当は$U_{f^{-1}}$の回路です)。
また、各整数に対応する状態は次のようになります。Braketでは $\left|q_0q_1q_2q_3\right>$ という順で状態が表記されるようです。
$x$ 状態 $1$ $1000$ $7$ $1110$ $4$ $0010$ $13$ $1011$ 前準備
やっと本題ですが、$7\times 1 ~(\mbox{mod} 15)$の回路をBraketで実装したコードは次のようになります。ほとんどチュートリアルとして用意されているノート(
/Braket examples/getting_started/
以下にあります)のコピペで実装できます。# general imports import matplotlib.pyplot as plt # magic word for producing visualizations in notebook %matplotlib inline import string import time import numpy as np # AWS imports: Import Braket SDK modules from braket.circuits import Circuit, Gate, Instruction, circuit, Observable from braket.devices import LocalSimulator from braket.aws import AwsDevice, AwsQuantumTask量子コンピュータの実機や、SV1と呼ばれるシミュレーターを使うためには、S3のbucketを指定して上げる必要があります。
# Please enter the S3 bucket you created during onboarding in the code below my_bucket = "amazon-braket-Your-Bucket-Name" # the name of the bucket my_prefix = "Your-Folder-Name" # the name of the folder in the bucket s3_folder = (my_bucket, my_prefix) # set up device device = LocalSimulator() device_sv1 = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")7×1 (mod 15)
回路図としては、まず1に対応する状態$\left|1000\right>$を$X$演算子で作成し、それに$U_{f^{-1}}$の回路をかけるものになります。
# Multi 7 \times 1 Mod 15 circ = Circuit() # circ.x([0,1,2,3]) circ.x([0,1,2,3]) circ.x(0) circ.cnot(control=1, target=2) circ.cnot(control=2, target=1) circ.cnot(control=1, target=2) circ.cnot(control=0, target=1) circ.cnot(control=1, target=0) circ.cnot(control=0, target=1) circ.cnot(control=0, target=3) circ.cnot(control=3, target=0) circ.cnot(control=0, target=3) print(circ)
print(circ)
とすることで組んだ回路の模式図を出してくれます。T : |0|1|2|3|4|5|6|7|8|9| q0 : -X-X-----C-X-C-C-X-C- | | | | | | q1 : -X-C-X-C-X-C-X-|-|-|- | | | | | | q2 : -X-X-C-X-------|-|-|- | | | q3 : -X-------------X-C-X- T : |0|1|2|3|4|5|6|7|8|9|ローカルシミュレーター
ローカルシミュレーターを使って、100回測定を実行してみます。
# run circuit result = device.run(circ, shots=100).result() # get measurement shots counts = result.measurement_counts # print counts print(counts) # Counter({'1011': 100}) # plot using Counter plt.bar(counts.keys(), counts.values()); plt.xlabel('bitstrings'); plt.ylabel('counts');結果は100回とも$\left|1011\right> = \left|13\right>$となりました。$U_f\left|13\right>=\left|1\right>$なので正しいのですが、1つの状態だけが観測されるので少し面白みに欠けます。
SV1
では、SV1シミュレーターを使って同じことをやると次のようなコードになります。
run
の引数が少し違うだけでほぼ同じです。# run circuit on SV1 result_sv1 = device_sv1.run(circ, s3_folder, shots=100, poll_timeout_seconds=24*60*60).result() counts_sv1 = result_sv1.measurement_counts print(counts_sv1) # Counter({'1011': 100}) # plot using Counter plt.bar(counts_sv1.keys(), counts_sv1.values()); plt.xlabel('bitstrings'); plt.ylabel('counts');そして結果も全く同じとなりました。もう少し実機のような結果(他の状態も観測される)を期待したのですが、100回程度ではあんまりぶれないのでしょうか?ちなみに実機ほどではないですがSV1を使うとBraketで立てているSagemakerインスタンスとは別に料金が発生します。
IonQ
では、実機を回してみます。今回はシミュレーターとほぼ同じ書き方で実行が可能なIonQで試してみました。
# set up device ionq = AwsDevice("arn:aws:braket:::device/qpu/ionq/ionQdevice") # run circuit with a polling time of 2 days ionq_task = ionq.run(circ, s3_folder, shots=100, poll_timeout_seconds=2*24*60*60) # get id and status of submitted task ionq_task_id = ionq_task.id ionq_status = ionq_task.state() # print('ID of task:', ionq_task_id) print('Status of task:', ionq_status)IonQとRigettiは実機が利用できる時間が限られているため、上記を実行すると
Status of task: CREATED
とタスクが生成され、実機が動いてくれるまで待ちになります。自分が投げたタスク一覧は、BraketのTask
というタブから確認可能です。タスクが完了したら、タスク一覧で COMPLETED と表示され、タスク詳細にARNと呼ばれる実行結果に対応するラベルが発行されています。
ARNを使って、実行結果を確認することが可能です。
# recover task task_load = AwsQuantumTask(arn=ionq_task_id) # Task details からコピーしたARNを使う # print status status = task_load.state() print('Status of (reconstructed) task:', status) # terminal_states = ['COMPLETED', 'FAILED', 'CANCELLED'] # get results results = task_load.result() # get all metadata of submitted task metadata = task_load.metadata() # example for metadata shots = metadata['shots'] machine = metadata['deviceArn'] # print example metadata print("{} shots taken on machine {}.".format(shots, machine)) # get measurement counts counts = results.measurement_counts print('Measurement counts:', counts) # plot results: see effects of noise plt.bar(counts.keys(), counts.values()); plt.xlabel('bitstrings'); plt.ylabel('counts'); plt.tight_layout(); plt.savefig('bell_ionq.png', dpi=700);今回実行した結果は以下の通りです。
# print結果 Status of (reconstructed) task: COMPLETED 100 shots taken on machine arn:aws:braket:::device/qpu/ionq/ionQdevice. Measurement counts: Counter({'1011': 73, '1010': 10, '1000': 5, '0010': 3, '1110': 2, '1001': 2, '0011': 2, '1111': 2, '0110': 1})ピークは$\left|1011\right>$ですが、他の状態も観測されていることがわかります。
Shorのアルゴリズム、というセクションですが、周期発見問題に関係する回路1つを Amazon Braket 使ってやってみた、というところで終わりです。ここでは$\left|1\right>$に対応する入力を行いましたが、$X$演算子を使って2進数表示させた$0\sim 15$の整数値を作れば他の値でも実行可能です。
最後に
Amazon Braket が出てきたことで、AWSのアカウントがあればすぐに量子コンピュータの実機が動かせるようになったのは感動的でした。実機の値段がわからず恐る恐る回したのですが、IonQでは100ショット1USDでしたので、思ったよりも金額が掛からなかったのも良かったです。
ブレインパッドでは、量子コンピュータに興味がある人が集まって勉強会を行っています。BrainPad Advent Calendar 2020 1日目のohtamanさんの記事でも Amazon Braket で D-Wave を使っていたようです。実機が触れることでチャレンジできることの幅が広がったので、これからも色々試してみたいと思っています。
- 投稿日:2020-12-18T16:57:08+09:00
AWSでプッシュ通知の送る方法
AWSでプッシュ送りたいであれば、サービスは2件あります:
- SNS (Simple Notification Service)
- Pinpoint
SNS
SNSの中でも2パターンが存在してます、ユーザーを一人一人にプッシュを送信するかユーザーをTOPICに登録して、そのTOPICに送信する。
Snsの流れ
SNSでアプリをApplicationを作成して、そのApplicationにエンドポイント(ユーザーのDeviceToken)を登録して、そのエンドポイントを送信する。送信後はSNSにそのユーザーに送信出来たかどうかの確認する。一人一人のユーザーに送信したいならSNSのソフトリミットは1秒1500件を送ります。
でも1<>1の送信だけではなくてTopicにも送信出来ます。
TOPICとは?
Topicは送信先のエンドポイントです、120万エンドポイントまで登録出来るエンドポイントです。エンドポイントはSNSの中で「送信出来る場所」になります。
たとえば、このアプリは100ユーザーを使ってます、プッシュ通知を送りたいならユーザーをSNSのApplicationに登録して、一人一人に送信するですが、もっと早い方法欲しいであれば、Applicationに登録する時はその1エンドポイントをTopicにsubscribeすることです。100ユーザーをsubscribeするなら、Topicに送信する時は1リクエストで100ユーザーに送信出来ます。それは100ユーザーでも、100万ユーザーでも変わらないです!
Topicのいいところは送信速度早いとAWSのApiを1回のみです。
pinpoint
Pinpointはお話題になったサービスで、今年に日本で使えるようになりました!SNSと違って、一気に100ユーザーを登録して、一気にプッシュ出来ます!
pinpointのプッシュ流れはシンプル、セグメントを作成して、セグメント作成する時はユーザーのファイルをインポートする
ファイルのサンプル
import.csv :
ChannelType,Address,Location.Country,Demographic.Platform,Demographic.Make,User.UserId
APNS,482aba02e7da338707541bb4c4a570b0ec090b8b0001b28ae1634ee680f2cbc4,JP,iOS,Apple,255830951ユーザーのdeviceTokenとプラットフォームと他の情報で組み合わせのCSVやJsonです。
セグメント作成したあとはそのセグメントに対してキャンペーンを作成して、プッシュの設定をして(タイトルとメッセージ)と送信の設定(繰り返し送信などは出来ます)をして終わりです!
送信速度も早い、1秒2万件送信されてます。
だが、
送信率をもらえますが、誰に送信出来たか出来てないは出来ません。どこのユーザーをアンインストールしたどかも出来ません。Pinpointと言う名前なのにユーザーを特定出来ないは微妙ですね...その上、安いSNSに対しては大きなユーザー数あればPinpointは高いです。
https://aws.amazon.com/jp/pinpoint/pricing/通知 100 万件までは無料、その後は通知 100 万件あたり 1 USD。
MTA の 5,000 エンドポイントまでは無料、その後は 1,000 エンドポイントあたり 1.20 USD。なので1プッシュで200万ユーザーなら1ヶ月2400ドルになります。自分の使い方としてならPinpointは合わないです、地裁ユーザーベースでたくさんのプッシュ送りたいなら出来そうです。
SNSーPinpoint
判断する為に200万ユーザーを送りたい場合はどれぐらいかかる(送信時間と値段):
SNSとpinpointは両方いいところありますが今回の要望としては大きくな対象ユーザーがあれば、Topicは一番使いやすいです。
以上
- 投稿日:2020-12-18T16:36:43+09:00
AWS Route53 で DNSSEC を使う
こんにちは。re:Invent 2020 最終日の本日(JST基準)、Route53がDNSSECのサポートを始めるという発表がありました。AWSはDNSSECはやらない方針だという噂を聞いたことがあるので、アメリカで何か流れが変わってきたのかな?と推測しつつ、早速試してみました。Route53のDNSSEC設定方法と検証結果をみなさまと共有します。
目次
- 前提
- 設定
- 検証
- RRSIGについて解説
- 参考資料
前提
- ドメイン: uu1.jp (検証用に持っている本物のドメインです)
- ドメインレジストラー: JPRS
設定
1. AWS Route53でDNSSEC署名の有効化
Route53のホストゾーンをクリックすると「DNSSEC署名」という新しいタブメニューが出来ていました。それをクリックします。
信頼チェーンを確立情報 > 別のドメインレジストラでDSレコードを確認し、コピーしておきます。AWS側での作業はこれで終わりです。
2. ドメインレジストラーでDSを登録
ドメインレジストラーでドメインのNameserver設定メニューに、上記でコピーしたDSレコードを追加します。
ここは、利用しているレジストラー毎にメニューやUIで差があるので省略します。検証
1. digで引いてみる
# dig +dnssec uu1.jp ns ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> +dnssec uu1.jp ns ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3994 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;uu1.jp. IN NS ;; ANSWER SECTION: uu1.jp. 172707 IN NS ns-48.awsdns-06.com. uu1.jp. 172707 IN NS ns-852.awsdns-42.net. uu1.jp. 172707 IN NS ns-1510.awsdns-60.org. uu1.jp. 172707 IN NS ns-1857.awsdns-40.co.uk. uu1.jp. 172707 IN RRSIG NS 13 2 172800 20201220041808 20201218021808 11110 uu1.jp. CZrd12BqXetmHKZBNBwH7vqrQQ5VNGqTvm47mgeXTFr6CwqZOVRtI1Jw 9vcpUnmEf7lNMgoV8Tn/tz/9XUPE+Q==2. drillで引いてみる
# drill -r named.root -k root.key -S uu1.jp soa ;; Number of trusted keys: 1 ;; Chasing: uu1.jp. SOA DNSSEC Trust tree: uu1.jp. (SOA) |---uu1.jp. (DNSKEY keytag: 11110 alg: 13 flags: 256) |---uu1.jp. (DNSKEY keytag: 7052 alg: 13 flags: 257) |---uu1.jp. (DS keytag: 7052 digest type: 2) |---jp. (DNSKEY keytag: 18321 alg: 8 flags: 256) |---jp. (DNSKEY keytag: 46369 alg: 8 flags: 257) |---jp. (DS keytag: 46369 digest type: 2) |---. (DNSKEY keytag: 26116 alg: 8 flags: 256) |---. (DNSKEY keytag: 20326 alg: 8 flags: 257) ;; Chase successful3. dnsvizでAnalyzeしてみる
ちゃんとチェーンが出来ています。素晴らしい。
RRSIG署名について解説
有効期間
ふと、RRSIGの有効期間を見ると、かなり短いですね。一般的には1週間とか2週間で運用するのが多いのかと思っていましたが、2時間くらいになっています。
# dig @ns-48.awsdns-06.com. +dnssec uu1.jp soa ... ;; ANSWER SECTION: uu1.jp. 900 IN SOA ns-852.awsdns-42.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 864 uu1.jp. 900 IN RRSIG SOA 13 2 900 20201218062631 20201218041131 11110 uu1.jp. Qzkzm...(略)有効期間が2020年12月18日 04:11:31 ~ 2020年12月18日 06:26:31 です。期間が2時間15分です。
TTLが15分と短いのでいいですが、長いとどうなるのでしょうか。1日にしてやってみましょう。# dig @ns-48.awsdns-06.com. +dnssec ttl-86400.uu1.jp a ... ;; ANSWER SECTION: ttl-86400.uu1.jp. 86400 IN A 1.1.1.1 ttl-86400.uu1.jp. 86400 IN RRSIG A 13 3 86400 20201219061446 20201218041446 11110 uu1.jp. B9RoA...(略)2020年12月18日 04:14:46 ~ 2020年12月19日 06:14:46 になりました。期間が1日と2時間になりました。
なるほど。TTL + 2時間 がRRSIGの有効期間ということのようです。署名タイミング
署名の有効期間開始日時もちょっと気になります。現在時刻になっているような気がします。
試してみましょう。# dig @ns-48.awsdns-06.com. +norec +dnssec uu1.jp soa ... ;; ANSWER SECTION: uu1.jp. 900 IN SOA ns-852.awsdns-42.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400 uu1.jp. 900 IN RRSIG SOA 13 2 900 20201218063622 20201218042122 11110 uu1.jp. +3v2yqQSUmPANtvqEmLma5dtrauehT2zEP+5nR5H5Z2P0YUf5jodAE+I PSt8RRTN96BqE4gyxwPrJEAh0EAq8Q== ```console # dig @ns-48.awsdns-06.com. +norec +dnssec uu1.jp soa ... ;; ANSWER SECTION: uu1.jp. 900 IN SOA ns-852.awsdns-42.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400 uu1.jp. 900 IN RRSIG SOA 13 2 900 20201218063624 20201218042124 11110 uu1.jp. YH5Q+JGAK9KkXiQCt1Wio7AlvSPG2iKu9Yx5jB3sJtd3Qq4CSj25O+34 obVpzeuSaACuckcyDOWLntaln8iywA==やはり、現在時刻ですね。
あらかじめ署名したZoneを用意するのではなく、Queryが来るたびに署名しているようです。
たくさんあるDNSサーバーにZSKのPrivate Keyを使えるようにするのが大変そうですが、さすがAWSですね。DNSKEYの署名
レコードセットの中でも、DNSKEYだけは、ZSKではなくKSKで署名されます。
こちらはどうでしょうか。# dig @ns-48.awsdns-06.com. +norec +dnssec uu1.jp dnskey ... ;; ANSWER SECTION: uu1.jp. 3600 IN DNSKEY 256 3 13 4rIKNvbTPUfKYk1nJEQvtChbIheFCjpukQlt35iKoKwMag32hEhc124h h6qxQRbrXG7JC4HE0AJyo8+m5SIAYA== uu1.jp. 3600 IN DNSKEY 257 3 13 /FcqSpmG5z0DXzFOBnm5vGGweJUZ1BewWNZ7zLf5amARaEU1HTcKjGLT n6hmBND92WkjI4tkfSkFeRcVJuB4BQ== uu1.jp. 3600 IN RRSIG DNSKEY 13 2 3600 20201218090000 20201217230000 7052 uu1.jp. bUofVksYDT3Z5/H5OwS3vP7bb8X3KFq3vgEq5C/GwS8zlIVeGOvBDiy4 te+Ah1x2j3FbJ15M/kM3Zc5SN0gneA==こちらは毎回署名するわけではなさそうですね。
参考資料
- 投稿日:2020-12-18T16:36:43+09:00
AWS Route53でDNSSECを使う
こんにちは。re:Invent 2020 最終日の本日(JST基準)、Route53がDNSSECのサポートを始めるという発表がありました。AWSはDNSSECはやらない方針だという噂を聞いたことがあるので、アメリカで何か流れが変わってきたのかな?と推測しつつ、早速試してみました。Route53のDNSSEC設定方法と検証結果をみなさまと共有します。
目次
- 前提
- 設定
- 検証
- RRSIGについて解説
- 参考資料
前提
- ドメイン: uu1.jp (検証用に持っている本物のドメインです)
- ドメインレジストラー: JPRS
設定
1. AWS Route53でDNSSEC署名の有効化
Route53のホストゾーンをクリックすると「DNSSEC署名」という新しいタブメニューが出来ていました。それをクリックします。
信頼チェーンを確立情報 > 別のドメインレジストラでDSレコードを確認し、コピーしておきます。AWS側での作業はこれで終わりです。
2. ドメインレジストラーでDSを登録
ドメインレジストラーでドメインのNameserver設定メニューに、上記でコピーしたDSレコードを追加します。
ここは、利用しているレジストラー毎にメニューやUIで差があるので省略します。検証
1. digで引いてみる
# dig +dnssec uu1.jp ns ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> +dnssec uu1.jp ns ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3994 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;uu1.jp. IN NS ;; ANSWER SECTION: uu1.jp. 172707 IN NS ns-48.awsdns-06.com. uu1.jp. 172707 IN NS ns-852.awsdns-42.net. uu1.jp. 172707 IN NS ns-1510.awsdns-60.org. uu1.jp. 172707 IN NS ns-1857.awsdns-40.co.uk. uu1.jp. 172707 IN RRSIG NS 13 2 172800 20201220041808 20201218021808 11110 uu1.jp. CZrd12BqXetmHKZBNBwH7vqrQQ5VNGqTvm47mgeXTFr6CwqZOVRtI1Jw 9vcpUnmEf7lNMgoV8Tn/tz/9XUPE+Q==2. drillで引いてみる
# drill -r named.root -k root.key -S uu1.jp soa ;; Number of trusted keys: 1 ;; Chasing: uu1.jp. SOA DNSSEC Trust tree: uu1.jp. (SOA) |---uu1.jp. (DNSKEY keytag: 11110 alg: 13 flags: 256) |---uu1.jp. (DNSKEY keytag: 7052 alg: 13 flags: 257) |---uu1.jp. (DS keytag: 7052 digest type: 2) |---jp. (DNSKEY keytag: 18321 alg: 8 flags: 256) |---jp. (DNSKEY keytag: 46369 alg: 8 flags: 257) |---jp. (DS keytag: 46369 digest type: 2) |---. (DNSKEY keytag: 26116 alg: 8 flags: 256) |---. (DNSKEY keytag: 20326 alg: 8 flags: 257) ;; Chase successful3. dnsvizでAnalyzeしてみる
ちゃんとチェーンが出来ています。素晴らしい。
RRSIG署名について解説
有効期間
ふと、RRSIGの有効期間を見ると、かなり短いですね。一般的には1週間とか2週間で運用するのが多いのかと思っていましたが、2時間くらいになっています。
# dig @ns-48.awsdns-06.com. +dnssec uu1.jp soa ... ;; ANSWER SECTION: uu1.jp. 900 IN SOA ns-852.awsdns-42.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 864 uu1.jp. 900 IN RRSIG SOA 13 2 900 20201218062631 20201218041131 11110 uu1.jp. Qzkzm...(略)有効期間が2020年12月18日 04:11:31 ~ 2020年12月18日 06:26:31 です。期間が2時間15分です。
TTLが15分と短いのでいいですが、長いとどうなるのでしょうか。1日にしてやってみましょう。# dig @ns-48.awsdns-06.com. +dnssec ttl-86400.uu1.jp a ... ;; ANSWER SECTION: ttl-86400.uu1.jp. 86400 IN A 1.1.1.1 ttl-86400.uu1.jp. 86400 IN RRSIG A 13 3 86400 20201219061446 20201218041446 11110 uu1.jp. B9RoA...(略)2020年12月18日 04:14:46 ~ 2020年12月19日 06:14:46 になりました。期間が1日と2時間になりました。
なるほど。TTL + 2時間 がRRSIGの有効期間ということのようです。署名タイミング
署名の有効期間開始日時もちょっと気になります。現在時刻になっているような気がします。
試してみましょう。# dig @ns-48.awsdns-06.com. +norec +dnssec uu1.jp soa ... ;; ANSWER SECTION: uu1.jp. 900 IN SOA ns-852.awsdns-42.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400 uu1.jp. 900 IN RRSIG SOA 13 2 900 20201218063622 20201218042122 11110 uu1.jp. +3v2yqQSUmPANtvqEmLma5dtrauehT2zEP+5nR5H5Z2P0YUf5jodAE+I PSt8RRTN96BqE4gyxwPrJEAh0EAq8Q== ```console # dig @ns-48.awsdns-06.com. +norec +dnssec uu1.jp soa ... ;; ANSWER SECTION: uu1.jp. 900 IN SOA ns-852.awsdns-42.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400 uu1.jp. 900 IN RRSIG SOA 13 2 900 20201218063624 20201218042124 11110 uu1.jp. YH5Q+JGAK9KkXiQCt1Wio7AlvSPG2iKu9Yx5jB3sJtd3Qq4CSj25O+34 obVpzeuSaACuckcyDOWLntaln8iywA==やはり、現在時刻ですね。
あらかじめ署名したZoneを用意するのではなく、Queryが来るたびに署名しているようです。
たくさんあるDNSサーバーにZSKのPrivate Keyを使えるようにするのが大変そうですが、さすがAWSですね。DNSKEYの署名
レコードセットの中でも、DNSKEYだけは、ZSKではなくKSKで署名されます。
こちらはどうでしょうか。# dig @ns-48.awsdns-06.com. +norec +dnssec uu1.jp dnskey ... ;; ANSWER SECTION: uu1.jp. 3600 IN DNSKEY 256 3 13 4rIKNvbTPUfKYk1nJEQvtChbIheFCjpukQlt35iKoKwMag32hEhc124h h6qxQRbrXG7JC4HE0AJyo8+m5SIAYA== uu1.jp. 3600 IN DNSKEY 257 3 13 /FcqSpmG5z0DXzFOBnm5vGGweJUZ1BewWNZ7zLf5amARaEU1HTcKjGLT n6hmBND92WkjI4tkfSkFeRcVJuB4BQ== uu1.jp. 3600 IN RRSIG DNSKEY 13 2 3600 20201218090000 20201217230000 7052 uu1.jp. bUofVksYDT3Z5/H5OwS3vP7bb8X3KFq3vgEq5C/GwS8zlIVeGOvBDiy4 te+Ah1x2j3FbJ15M/kM3Zc5SN0gneA==こちらは毎回署名するわけではなさそうですね。
参考資料
- 投稿日:2020-12-18T16:31:58+09:00
【AWS】SystensManager AutomationでリソースID以外でターゲットを指定する方法
SystemsManager Automationのドキュメントでリソース指定にIDを使いたくない
AWSではSystemsManager Automation(以下、SSM Automation)という機能があります。その機能を利用したEC2バックアップ方法について記事を書きました。
実際はこのドキュメントをメンテナンスウィンドウを利用して定期的に実行しています。https://qiita.com/chittai/items/dbe9185ae552fd54fac5
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-maintenance.html
ここでの運用はバックアップ対象を直接ドキュメントに記述しています。ですが、これだと困ることがあります。例えば、EC2/Volumeのリストアを行い、リソースIDが変わるケースです。
リストアする毎にリソースIDを修正しなければなりません。それは耐えられないので、直接リソースIDを記入する運用を変えたいと思います。ここで、ポイントとして上げられるのは下記になります。
1. 操作対象のリソースにTagをつける
1. Tagベースでリソースグループを作成する
1. メンテナンスウィンドウのターゲットでリソースグループを指定する
1. メンテナンスウィンドウのタスクで疑似パラメータを使用する
1. 1タスクで操作する対象は1種類のリソースとする(EC2,EBSなどちゃんと分けて管理する)構成について
まず、構成について紐解きます。
Automationのタスクを定期的に実行する
には、メンテナンスウィンドウを利用
する必要があります。このメンテナンスウィンドウでは
タスクという単位で操作を登録
することができます。そして、操作対象をターゲットという単位で登録
することができます。このターゲットはリソースグループ単位でもEC2インスタンス単位でも登録できます
。そして、リソースグループはTagベースでの登録が可能です。なので、操作したい対象に同じタグをつけ、リソースグループでまとめることでリソースIDを直書きせずに
対象を指定することができます。対象リソースにTagをつける
今回は、
linux
,windows2019
というインスタンスにAutomation:Yes
というタグを追加しました。このタグベースでリソースグループを作成します。Tagベースでリソースグループを作成する
リソースグループ作成画面からTagベースでリソースグループを作成します。
メンテナンスウィンドウのターゲットでリソースグループを指定する
メンテナンスウィンドウのターゲットで先ほど作成したリソースグループを登録します。
メンテナンスウィンドウのタスクで疑似パラメータを使用する
ここからも大事なポイントとなります。次に、タスクを登録します。今回は
AWS-StopEC2Instance
を実行します。想定される動作は、先程作成したリソースグループに登録されているEC2インスタンスすべてが停止されることです。先程登録したリソースグループのターゲットを登録します。
リソースグループを操作するのに必要な権限をロールに付与します。
それと、EC2インスタンスを停止できる権限があるロールを選択します。(今回はAdministrator権限がついたロールを使用しています)} "resource-groups":"GetGroupQuery", "resource-groups":"GetGroup", "resource-groups":"GetTags" }ここで一番大事なのは、
InstanceId
のボックスに{{RESOURCE_ID}}
を記述することです。これは疑似パラメータといいます。この疑似パラメータを
記述することで、リソースグループに登録したリソースのID情報がAWS-StopEC2Instance
に自動で渡されて実行されます。動作として、各リソースに対して
順番に子タスクが作成されそれぞれ実行されます。以上で完了です。
感想
{{RESOURCE_ID}}
が登録されたリソースを順に置き換わるので、複数のリソースタイプが登録されていると、ドキュメントの引数と型があわずに基本失敗します。
なので、リソースごとにタスクとリソースグループを登録するのが良いかと思います。
- 投稿日:2020-12-18T14:21:45+09:00
[小ネタ] AWS SAM で Parameter Store 見るときはポリシーテンプレートがいいよ
ポリシーテンプレートとは
SAM にはポリシーテンプレートという、パラメータを渡すことで自動的に決められた IAM ポリシーを適用してくれる便利機能があります。
Lambda 関数は ECS タスクと違って、Parameter Store を環境変数に設定する機能が(まだ)ないので、基本的にコード内で ssm API を使ってシークレット値を取得する必要があります。
Parametrer Store を読めるマネージドポリシー(
arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
とか)をアタッチしてもいいけど、読める範囲が大きすぎるのでポリシーテンプレートを使ったほうがいいです。SampleFunction: Type: AWS::Serverless::Function Properties: FunctionName: sample-function CodeUri: . Handler: index.handler Runtime: nodejs12.x Environment: Variables: PARAMETER_PATH: /my/secret/parameter_path Policies: # ポリシーテンプレートを使う # ParameterName は先頭にスラッシュをつけてはいけないので注意 - SSMParameterReadPolicy: ParameterName: "my/secret/parameter_path"公式ドキュメント見ると、
ParameterName
ではなくparameterName
になってるんですけど、先頭大文字が正解です。(AWSにフィードバック済み)
- 投稿日:2020-12-18T12:06:56+09:00
AWS SNSからAndroidにプッシュ通知するためにやったこと、ハマったこと
今関わっているプロジェクトのバックエンドはAWSです
そんなAWSからユーザが操作するAndroidにプッシュ通知がしたいと思いました
しかしFirebase(FCM)が便利すぎて逆にハマってしまったのでメモしておきますプッシュ通知なんてどうせみんなオフするからいらん!という漢気も私は好きです
参考にした素晴らしい記事様はこちら
Android から Amazon SNS を使ってみる - Qiita
Android端末で、FCM経由でAWSSNSを受け取るまで - Qiita目指すもの
まずは最小限、プッシュ通知をすることだけを目標にします
細かい設定や他の人の方が詳しい!用意するもの
- 適当なAndroidプロジェクト
- Firebaseアカウント ※無料プランでOK
- AWSアカウント
FirebaseからAndroidに通知するまで
適当にAndroidプロジェクトを作成する
Firebaseにアカウントを作成、適当なプロジェクトを作る
「プロジェクトを追加」から適当な名前でプロジェクトを作成します
ちなみにFCM(Firebase Cloud Messaging)はだけなら無料プランでもよさそうです。素敵。Firebase Cloud MessagingにAndroidアプリを登録する
公式がとても丁寧に案内してくれるのでそれに従います
Androidパッケージを登録する
Androidパッケージ名に、先ほど作成したAndroidプロジェクトのパッケージ名を入力します
署名はよりセキュリティを高めるなら入れた方がいいんでしょうね。今回は省略!appディレクトリにJSONファイルを追加
firebaseからダウンロードしたgoogle-services.jsonをapp以下に追加しますAndroidプロジェクトに依存関係を追加する
/build.gradlebuildscript { ext.kotlin_version = "1.4.21" repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:4.1.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.google.gms:google-services:4.3.4" // <<<<< added ~/app/build.gradleplugins { id 'com.android.application' id 'kotlin-android' id 'com.google.gms.google-services' // <<< added } // ~略~ dependencies { // ~略~ implementation platform('com.google.firebase:firebase-bom:26.1.1') // <<< added implementation 'com.google.firebase:firebase-analytics-ktx' // <<< added implementation 'com.google.firebase:firebase-messaging-ktx' // <<< added }FCMにAndroidデバイスを登録する
通知用のデバイストークンを取得する
プッシュ通知するためには、Androidデバイスを一意に特定するためのトークンが必要になります
トークンの発行はAndroidプロジェクトに組み込まれたFirebaseがやってくれます
例えば下記のようにtokenをログ出力するようにしてみますMainActivity.ktclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // ↓added FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { if (!it.isSuccessful) { Log.e("Firebase", "Fetching FCM registration token failed", it.exception) return@OnCompleteListener } val token = it.result Log.d("Firebase Token", token.toString()) }) } }この状態でアプリを動かすと、ログにトークンが吐かれますので、これをメモしておきます
D/Firebase Token: c3ilo ~~~FCMからプッシュ通知を送信してみる
FCMのダッシュボードから「通知の作成」へ行き、早速メッセージを送ってみます
FCM登録トークンに、先ほどAndroidから生成されたトークンを登録して、テストメッセージを送信します
すると無事届きました!簡単ですねー
さあFirebase > Android間の連携ができたので、次はAWS SNSとの連携だ注意:アプリがバックグラウンドでないと通知が届かない
デフォルトの状態だと、アプリがフォアグラウンド中(前面表示)には届かないようです
「君もうアプリ開いてるから通知せんでええやろ?」ってことでしょうかAWS SNSからAndroidに通知するまで
AWS SNSとFirebaseを紐付ける
AWS公式が非常に親切に手順を説明してくれていますので、基本はこの通りに
Firebaseのサーバーキーを取得する
プロジェクトの設定→「Cloud Messaging」からサーバーキーを取得しますFirebaseサーバーキーをAWS SNSに登録する
- AWSコンソールからSimple Notification Serviceを開く
- 左バーの「プッシュ通知」からモバイルプッシュ通知画面を開き、「プラットフォームアプリケーションの作成」
- プッシュ通知プラットフォームは「FCM」
- APIキーに先ほど取得したFirebaseサーバーキーを入力する
- そのほかは空欄でOK
アプリケーションエンドポイントを作成する
- デバイストークンにAndroidから取得したトークンを入力する
- そのほかは空欄
いざSNSメッセージ送信!しかし全く反応なし
なにせFirebaseからAndroidへのプッシュ通知は成功しているので、AWS SNSとFirebase間の紐付けがうまくいってない?
と思ってサーバーキー周りをめちゃくちゃ確認しましたが、原因は別のところにありました
それでは、トラブルシュート編に続きますAWS SNSからAndroidに通知するために追加でやるべきこと
送信するメッセージにはフォーマットがある
AWS SNSをちょっと知っている人には当たり前だと思いますが...
Firebaseで受け取れるようなメッセージを送るにはフォーマットがあります
しかもAWS側がテンプレートを用意してくれています(カスタムペイロード)しかし、これでもAndroidにはプッシュ通知されませんでした
Android側でレシーバを実装する必要がある
Firebase公式にはしっかりとこんな説明があります
フォアグラウンド アプリで通知メッセージまたはデータ メッセージを受信する場合は、onMessageReceived コールバックを処理するコードを記述する必要があります。
FirebaseからAndroidへの通知は成功していたので、動くと思っていましたが甘かったようです
Androidマニフェストに通知受信用のサービスを追加
マニフェストファイルのタグ以下にサービスを追加します
AndroidManifest.xml<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>MyFirebaseMessagingService.ktclass MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) Log.d("onMessageReceived", "From: ${message.from}") if (message.data.isNotEmpty()) { Log.d("onMessageReceived", "payload: ${message.data}") } } }しかしこれでもプッシュ通知が飛んでこないなーと、何気なくログを見てたら
来てたァ!!カスタム通知を作成する
公式のFCMメッセージについてを参照する限り、AWS SNSからのメッセージはデータメッセージとして扱われていて、
その場合はクライアント側で処理をする必要があるとのこと
(SNSから送信するメッセージにdataって付けてますしね)MyFirebaseMessagingService.ktclass MyFirebaseMessagingService : FirebaseMessagingService() { val CHANNEL_ID = "MyNotification" override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) Log.d("onMessageReceived", "From: ${message.from}") if (message.data.isNotEmpty()) { Log.d("onMessageReceived", "payload: ${message.data}") } createNotificationChannel() val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("通知がきたで") .setContentText("内容は「${message.data}」やで") .setSmallIcon(R.drawable.ic_launcher_background) .setPriority(NotificationCompat.PRIORITY_DEFAULT) with(NotificationManagerCompat.from(this)) { notify(UUID.randomUUID().hashCode(), notificationBuilder.build()) } } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = "MyNotificationChannelName" val descriptionText = "MyNotificationChannelDescription" val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText } // Register the channel with the system val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } }正直ここの実装はかなりみようみまねで適当なので、ご使用の際は十分に注意してください
ちなみにこのとき、アイコンなど一部の設定を省略するとランタイムでクラッシュしたりします無事プッシュ通知ができました!
![]()
![]()
ということで無事通知が動きました!
まだ通知周りは詰められてないところもありますが、なんとかなりましたね
- 投稿日:2020-12-18T11:43:38+09:00
割れ窓を放置した人の末路
この記事は、本番環境でやらかしちゃった人 Advent Calendar 2020の18日目の記事になります。
自己紹介
バックエンドのエンジニアとしてそろそろ8年が経ちます、星光輝と申します。
守備範囲はサーバーサイドですが、アプリ・インフラ・テスト・開発環境改善あたりの経験もある、広く浅い系エンジニアです。
実務で経験した言語は、Java/C#/Ruby/PHP/Python/TypeScript/swift/kotlin...と様々です。昨年度もこちらで参加させて頂きました。
顧客のコンテンツデータを消失させた話案件概要
APIサーバー+スマートフォンアプリの構築案件で、私は APIサーバーの実装を担当しています。
このプロジェクトでは、サーバーレスアプリケーションで作られることが決まっており、
chalice という Python フレームワークを使って実装をしています。私の参画時点での経験値は、
- Python: それほど実装経験なし(バッチのスクリプトで記載したことがある程度)
- サーバーレス: 全くなし
- AWS APIGateway: 初めて使う
- DynamoDB: 見たことあるー
俗にいうド素人だったわけですが、当案件における私の役割はリードエンジニアでした。
チーム構成としては、インフラ、アプリ、サーバー、テスト、PMO のチームがあり、合計15名程度の中程度の案件です。経緯
あの日あの時あの場所でパスを間違わなかったら...
chalice は、規則に沿ってプログラムを書けば、APIGateway, Lambda などを自動で作ってくれるという、
インフラ側を詳しく知らない人にはとてもありがたいフレームワークです。プロジェクト初期の頃、検証のために chalice を使っていて、
Authorizer で認証しない場合、どんなメッセージが返ってくるんだろう...と思い、
下記のように、authorizer をつけた状態でリクエストしてみました。from chalice import Chalice, CognitoUserPoolAuthorizer app = Chalice(app_name='sample') authorizer = CognitoUserPoolAuthorizer( 'SamplePool', header='Authorization', provider_arns=['arn:aws:cognito-idp:ap-northeast-1....'] ) @app.route('/', authorizer=authorizer) def index(): return {'hello': 'world'}$ chalice deploy Creating deployment package. Reusing existing deployment package. Creating IAM role: sample-dev Creating lambda function: sample-dev Creating Rest API Resources deployed: - Lambda ARN: arn:aws:lambda:ap-northeast-1:XXXXXXXXXXX:function:sample-dev - Rest API URL: https://q13wuyd6hi.execute-api.ap-northeast-1.amazonaws.com/api/$ curl https://q13wuyd6hi.execute-api.ap-northeast-1.amazonaws.com/ -w '%{http_code}\n' -s {"message":"Forbidden"}403なるほど 403 なのか。(※注意: 正しくは https://q13wuyd6hi.execute-api.ap-northeast-1.amazonaws.com/api/)
そして、ログインユーザを認識するために、トークンからログインユーザを取る処理を書く。
Authorizer で認証情報が正しいのはわかるが、何かのエラーで取れないことはある。
そこで、デフォルト chalice の標準エラーが 403 だから...403にしよう。(※正しいパスを叩けば401)import boto3 from chalice import Chalice, CognitoUserPoolAuthorizer, ForbiddenError app = Chalice(app_name='sample') authorizer = CognitoUserPoolAuthorizer( 'SamplePool', header='Authorization', provider_arns=['arn:aws:cognito-idp:ap-northeast-1....'] ) @app.route('/', authorizer=authorizer) def index(): token = app.current_request.headers['Authorization'] try: user = boto3.client('cognito-idp').get_user(AccessToken=token) except Exception as e: raise ForbiddenError('Forbidden') return {'hello': 'world'}次第になくなるコミュニケーション
chalice はプログラムコードからインフラ環境を含めて作られるため、色んな学習コストを抑えることができます。
しかし、それは同時にプログラムコードが、インフラと密に連携したシステムと言えると思います。つまり、基本的に、chalice に従うということが暗黙の了解になりました。
その過程で、APIGateway, Lambda は chalice で作られるからAPIサーバー側で。
Cognito, DynamoDB と言ったデータストアは、業務要件が関わるためAPIサーバー側で開発の過程で増やして構わない。
...と言った感じになり、アプリ側とインフラ側が互いに関わることが少なくなりました。———— そして、先ほどのコードが生み出された後、5ヶ月後のお話。
そして時は動き出す...
デプロイを改良すべく、CloudFormation を使ったデプロイをすることになりました。
chalice では、chalice package
コマンドで CloudFormation のファイルに変換することができます。そして、一通り動くことを完了して AWS コンソールを確認していたときのこと。
....? なんだこれは
作っていない、ステージ(Stage)ができている!
どういうことだろうと思うと、既存のバグらしい。https://medium.com/veltra-engineering/avoid-aws-sam-stage-stage-45f7331b7b5d
解決策は、下記を OpenAPIバージョンを 3.0.2 にすること!簡単!
消えた!一応、処理確認。うん、何個か動かして動いているから問題なさそう!(しばらくして)
「なんかアプリ使っていて、しばらくした後にアプリが全く動かなくなったんだけど??」
!!!!
原因
- chalice が作る CloudFormation テンプレートファイルは Open API 2.0 相当の記法だった。
- OpenAPI 2.0/ 3.0 で Authorizer 定義キーが異なる(securityDefinitions/securitySchemes)
- Authorizer の定義が無視されてしまった
- アプリ側は、トークンの有効期限切れをステータス401で検知している
- Authorizer でトークン期限切れを検知できず、ユーザ取得部分でエラーが出るようになり 403 が常に返ってくるようになった
惨劇はなぜおこってしまったのか
割れ窓の放置
潜在的なバグを作っていたにも関わらず、検証せず放ったらかしにしたこと。
ここがバグっていなければ、このようなケースにはならなかったはず...。
言い訳をするならば、Authorizer を外さない限りは該当部分のテストはできず、Authorizer を外すことはなさそうだったので、正常系の確認のみで確認したつもりになって放置されたのだと思います。思い込み
動作検証時に気づけなかったこと、検証できていなかったこと。
それを生んでしまったことは、下記のような思い込みが原因だと思います。
- CloudFormation の定義が問題になるなら、エラーが出るはずと思っていた
- 仮に Authorizer がなくても、その後のユーザ取得で同じようになるから影響ないと思っていた
確認内容の把握不足
確認すべき項目をインフラ側とコミュニケーションを取り、確認すべき項目を詰めていなかったことも
今回の問題点だと思っています。これができていれば、検証段階で気づけたかもしれません。なお、自身では直接 API を実行して、挙動確認をしていましたが、
Authorizerの設定は下記を見れば確認ができました。これは後で知りました。。。二度と惨劇を起こさないためにどうしたのか
割れ窓を直す
ユーザの取得のエラーを正しい内容(401, Unauthorized) に変更。
念の為、Authorizer を外して動作確認して問題ないことを確認しました。テスト仕様書の作成
今回の思い込みの部分というのは、他人から指摘されない限り変えることはできないはずです。
...なので、そのあたりを明文化して他の人に確認してもらうしかなさそうです。今回の件を踏まえて、デプロイ実行に関する確認点をテスト仕様書として作成しました。
そして、動作確認時の確認項目に関してインフラ側にも確認して頂きました。まとめ
今回は昔のバグを放置し続けて、別項目のバグと複合してサービスを止めてしまった話を記載しました。
CloudFormationによるデプロイ化は、自身でも経験のないタスクという認識をしており、
なるべく気をつけて作業をしていましたが、やはり、一人の力ではその精度には限界があるように思います。
また、関連しそうな人を巻き込みコミュニケーションを取れば、自身の負担も多少は軽減されていたように思います。今回の件は、気づいた時点で即対応して、PMO側でも迅速に対応したため、顧客から厳重注意はありましたが、大ごとにはならずにすみました。
こんな風に助けられて成り立っているのだなぁということを改めて思い知らされました。
やってしまったことに関しては取り戻せませんが、それを上回るほどの貢献をして取り返そうと改めて思いました。
- 投稿日:2020-12-18T11:20:45+09:00
AWS AmplifyのWhat's Newに載らないアップデート情報の紹介
はじめに
本エントリは「AWS Amplify Advent Calendar 2020」の22日目です。
いやー今年もAmplifyはAdmin UIなど激アツでしたねw
カレンダー初日にも書きました通り、SSRが個人的には一番熱かったです!
でも本当にこんなものしかAmplifyって進化してないのでしょうか?
もちろん違います、では他にはどんな進化をしてきたのか、少し皆さんが知らないような機能まで紹介していこうと思います!
ちなみにこの記事が出た12/22時点でのAmplifyCLIの最新バージョンは4.40.0(2020/12/16リリース)ですアップデート①:AppSyncの@keyディレクティブで複数同時更新が可能になりました
これはamplify-cliの4.38.0(2020/12/8リリース)から利用可能になったオプションです。
今まではGSIなどの@keyディレクティブは一つずつamplify pushする必要があったのですが、一気に同時変更できるようになりました。ただしデフォルトではこの機能は有効になっていないので以下のような作業が必要です。
といっても難しく考える必要はなく、features.graphqltransformer.enableiterativegsiupdates
をtrue
にしてあげるだけなのでお手軽ですね!amplify/cli.jsonのデフォルト% cat amplify/cli.json { "features": { "graphqltransformer": { "addmissingownerfields": true, "validatetypenamereservedwords": true, "useexperimentalpipelinedtransformer": false, "enableiterativegsiupdates": false // ここをtrueにしてあげる }, "frontend-ios": { "enablexcodeintegration": true }, "auth": { "enablecaseinsensitivity": true }, "codegen": { "useappsyncmodelgenplugin": true } } }つまり(こんなGSIに意味があるのかは置いておいて)、このような複数の@keyが一発で作成・更新が可能になります。
amplify/backend/api/apiname/schema.graphql> cat amplify/backend/api/<apiname>/schema.graphql type Todo @model @key(name: "todosByName", fields: ["name"], queryField: "todosByName") @key(name: "todosByStatus", fields: ["status"], queryField: "todosByStatus") { id: ID! name: String! status: String! }今まで本番環境をAmplifyで構築するのに段階リリースをしなければならなかった手順を簡素化できるなどのメリットがあるかなと思います!
アップデート②:upgrade、uninstallコマンドが追加されました
こちらは4.35.0(2020/11/24リリース)からの機能になります。
amplify upgrade
、amplify uninstall
と2つのコマンドが実行できるようになりました。
npmでAplifyCLIを利用してる人には代わりのnpmコマンドを教えてくれるだけなのですが、MacやLinuxにシェルでインストールしている方やWindowsの方はこちらのコマンドで実行できるようになったようです。
私はnpmでインストールしてるので、あまり恩恵はないのですがアップデートコマンドを忘れてしまった場合などにぜひ利用してみてくださいw
npmでインストールしている人には実行すると以下のようなメッセージが出ます。AmplifyCLI% amplify uninstall Initializing new Amplify CLI version... Done initializing new version. Scanning for plugins... Plugin scan successful "uninstall" is not available in this installation of Amplify. Use npm uninstall -g @aws-amplify/cli instead. % amplify upgrade "upgrade" is not supported in this installation of Amplify. Use npm i -g @aws-amplify/cli instead.アップデート③:AppSyncの予約語制約をオフにする機能フラグが追加されました
こちらは4.32.1(2020/11/8リリース)からの機能になります。
アップデート①の対応と同様に機能フラグをオフにすることによって例えば「Subscription」などの予約語をモデル名に命名することが可能になりました。
AppSyncのスキーマ更新後のamplify push時に[:reservedWord] is a reserved type name and currently in use within the default schema element.
というエラーが出た際に思い出してください!
features.graphqltransformer.validatetypenamereservedwords
をfalse
にするだけです!amplify/cli.jsonのデフォルト% cat amplify/cli.json { "features": { "graphqltransformer": { "addmissingownerfields": true, "validatetypenamereservedwords": true, // ここをfalseにしてあげる "useexperimentalpipelinedtransformer": false, "enableiterativegsiupdates": false }, "frontend-ios": { "enablexcodeintegration": true }, "auth": { "enablecaseinsensitivity": true }, "codegen": { "useappsyncmodelgenplugin": true } } }一行で紹介するアップデート
- 4.32.0(2020/10/31リリース)でLambdaリゾルバの中からCognitoユーザープールのグループリストを参照できるようにIAMの変更が入りました
- 4.32.0(2020/10/31リリース)でAppSyncの@searchableディレクティブからクエリを投げる際に
from
パラメータのサポートが追加されました。これによりたくさんのデータを表示する際にページ遷移などを画面で表現する際にnextToken
を今まで通り使うことも出来ますし、from
パラメータでオフセットすることも選択出来るようになりました。まとめ
機能追加系の説明しやすいアップデートをチョイスしてお届けしていますが、他にもユーザーフレンドリーにするための細やかな文言変更や、amplify deleteコマンド実行時にいきなり削除せずに確認を挟むようになった、など細やかなアップデートもたくさんあった一年でした!
主に10月-12月のアップデートを中心に紹介していますが実際の今年のアップデート数はこんなものじゃないですので、興味がある方はGitHubのコミット履歴なども見て下さいね!ではまた!
- 投稿日:2020-12-18T10:53:27+09:00
Serverless FrameworkでLambda関数をデプロイ&VPC内に入れる方法
Serverless Frameworkとは
Serverlessはサーバーレスなアプリケーションを簡単に開発、デプロイするためのNode.js製のツールです。
AWS、Azure、GCP等のクラウドサービスによらず利用することができ、ランタイムの言語もクラウドサービス側で許されているものであれば利用することができます。下記サイトから引用。
https://www.wantedly.com/companies/forstartups/post_articles/279817インストールからデプロイまで
ServerlessFrameworkのインストール
$ npm install -g serverlessNode.jsがインストールしていない場合はインストールが必要。
インストールしたサーバーレスのバージョンを確認するには、次のコマンドを実行します。
$ serverless --versionAWSのセットアップ
IAMユーザーとアクセスキーを作成する
1. AWSアカウントにログインし、Identity&Access Management(IAM)ページに移動します。
[ユーザー]、[ユーザーの追加]の順にクリックします。最初のフィールドに名前を入力して、このユーザーがサーバーレスフレームワークに関連していることを思い出させますserverless-admin。チェックボックスをクリックして、プログラムによるアクセスを有効にします。[次へ]をクリックして、[権限]ページに移動します。[既存のポリシーを直接添付]をクリックします。AdministratorAccessを検索して選択し、[次へ:レビュー]をクリックします。すべてが良好に見えることを確認し、[ユーザーの作成]をクリックします。
API Key&Secretを表示して一時的な場所にコピーします。
AWSアクセスキーの使用
AWS API Key&Secretを使用するようにServerlessFrameworkを設定するには次の2つの方法があります。
クイックセットアップ
開始するための簡単なセットアップとして、それらを環境変数としてエクスポートして、サーバーレスとシェル内のAWSSDKにアクセスできるようにすることができます。export AWS_ACCESS_KEY_ID=<your-key-here> export AWS_SECRET_ACCESS_KEY=<your-secret-key-here> # AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are now available for serverless to use serverless deploy # 'export' command is valid only for unix shells. In Windows - use 'set' instead of 'export'注意: 自己署名証明書を使用している場合は、次のいずれかを実行する必要があります。
# String example: # if using the 'ca' variable, your certificate contents should replace the newline character with '\n' export ca="-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----" # or multiple, comma separated export ca="-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----,-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----" # File example: # if using the 'cafile' variable, your certificate contents should not contain '\n' export cafile="/path/to/cafile.pem" # or multiple, comma separated export cafile="/path/to/cafile1.pem,/path/to/cafile2.pem" # 'export' command is valid only for unix shells. In Windows - use 'set' instead of 'export'1.サービスを作成します
serverlessコマンドは
sls
と略すことができるため以降slsで行っています。
下記コマンドでサービスが作成できます。$ sls create --template aws-python --path myServerlessServicecreateコマンドを使用して、使用可能なテンプレートの1つを指定できます。この例では、--templateまたは短縮-tフラグを指定してaws-pythonを使用します。--pathまたは速記は、-pテンプレートのサービスファイルを作成する場所です。ディレクトリをこの新しいフォルダに変更します。
2.デプロイ
$ sls deployこれにより、の設定に基づいて関数がAWSLambdaにデプロイされます。
デプロイ先のstageはdevになっており、リージョンはデフォルトでes-east-1バージニア北部に指定されているので、regionキーの値をap-northeast-1にすることで東京に指定することができる。Lambda関数をVPCの中に入れたい場合は
serverless.yml# 使用するクラウドサービス(AWS)と言語(Pytho3.6)を指定 provider: name: aws runtime: python3.6 # ステージを指定。開発と本番環境を切り分けることができます。 stage: dev # デプロイするリージョンを指定 region: ap-northeast-1 # roleの設定 iamRoleStatements: - Effect: "Allow" Action: - "s3:*" Resource: - "*" #VPCの中に入れる場合 vpc: securityGroupIds: - sg-xxxxx subnetIds: - subnet-xxxxx3.デプロイされた関数を呼び出す
$ sls invoke -f hello呼び出しは、コマンドと機能を展開invokeし、--functionまたは速記-f。
ターミナルウィンドウに、AWSLambdaからの応答が表示されます。{ "statusCode": 200, "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}" }Hello World関数をデプロイして実行できました。
実際のLambdaコンソール
詰まったこと
チュートリアルのサイトに沿って進めて、lgwan側のAWSにセットアップしてデプロイしたはずですが関数を呼び出してレスポンスも正常に返ってきているのに実際にLambdaコンソールを見ても作成した関数がありませんでした。
リージョンも合っているはずなのに無し。神隠し?
結果的には他のIAMユーザーの環境にデプロイされてました。原因
元々~/.aws/credentialsにAWSプロファイルが設定されており、そこが他のIAMユーザーのアクセスキーが設定してあったためそちらを参照されていた。
~/.aws/credentials[default] aws_access_key_id=*************** aws_secret_access_key=***************解決法
~/.aws/credentialsに新たにAWSプロファイルを設定して下記コマンドでプロジェクトごと(/ API)を切り替えることができるため切り替えた後、再度デプロイしたことで指定のAWS Lambdaコンソールにデプロイされました!
~/.aws/credentials[default] aws_access_key_id=*************** aws_secret_access_key=*************** [profileName1] aws_access_key_id=*************** aws_secret_access_key=***************$ export AWS_PROFILE="profileName1"これで、ターミナルでsls deployなどを実行できるようにサーバーレスCLIオプションが設定されます。
- 投稿日:2020-12-18T08:36:57+09:00
【AWS】“Aws::S3::Errors::AccessDenied in 〇〇sController#create”エラー(AWSのS3に保存できない)
エラー内容
AWSのS3を使い、画像データを保存できるように実装していたが、
1. ローカル環境においてエラー
2. 本番環境においてエラー
3. AWSのS3上のバケットにはデータなし(保存されていない)
という事象が発生。エラーが起こったときの設定の状況
- macOS Catalina バージョン10.15.7
- Rails Rails 6.0.3.4
- Herokuを使ってデプロイ
- AWSのアカウント・IAMユーザー・S3のバケットの3つを新規作成したばかり
解決した方法(結論)
「AWSのアカウント削除→作成し直し」を行ったところ、エラー解決。
(保存できるようになった)解決方法の補足
- AWSアカウント作成時、Googleのシステム障害が発生していた。
- メンターさんにも見てもらいタイポや記述もれが無いのにも関わらず、保存ができなかった。
- アカウントの新規作成から1日以上、保存できない状況が続いた。(時間をおいても反映されない)
- AWSアカウントを削除して、新規登録し直す際、前回登録していたメールアドレスが90日間以内であれば再開できる対象になるからか、使えない状況になる。(前回のメールアドレス以外を選択しないといけない)
の方法を試すも、今回は上手くいかなかった。
AWSアカウント作成し直す前の確認作業
(1)AWS上のバケットポリシーは適切か?
結果:①②あたる部分は、ともに適切だった。
{ "Version": "2012-10-17", "Id": "Policy1544152951996", "Statement": [ { "Sid": "Stmt1544152948221", "Effect": "Allow", "Principal": { "AWS": "①今回のIAMユーザーのARNをここに記述" }, "Action": "s3:*", "Resource": "arn:aws:s3:::②今回のバケット名をここに記述" } ] }(2)ブロックパブリックアクセスのバケット設定は適切か?
結果:適切だった。
(3)VSコード上の、保存先は適切か?
結果:適切だった。
:amazon
になっているのでOK。(開発環境)config/environments/development.rb# Store uploaded files on the amazon file system (see config/storage.yml for options). config.active_storage.service = :amazon
:amazon
になっているのでOK。(本番環境)config/environments/production.rb# Store uploaded files on the amazon file system (see config/storage.yml for options). config.active_storage.service = :amazon”今回のバケット名”にあたる部分について、合っていたためOK。
config/storage.ymlamazon: service: S3 region: ap-northeast-1 bucket: 今回のバケット名 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>(4)環境変数は適切か?
結果:適切だった。
環境変数の確認方法(MacOSがCatalina以降の場合)
terminalvim ~/.zshrc
:wq
で閉じる。- もし誤っていた場合には
i
を押して編集し、esc
を押してから、:wq
で閉じる。コンソール確認
確認した内容は以上。
AWSアカウント(ルートユーザー)を作り直し
上記の通り、確認したが誤りはない様子。
思い当たる仮説としては、「Googleの調子が悪い頃、ちょうどアカウント登録していた」だったため、アカウントを作り直すことにした。AWSを作り直したあとは、
- 環境変数の設定し直し
- バケットポリシーの設定し直し
が必要となる。感想
- 今回のケースは、因果関係が定かではないが、「システム障害が起きている前後は、アカウントの作成をしないほうがよい」ことを学んだ。
- 今回はアカウントを新規作成したばかりで、作り直しても手間はかかるが他に消えると困る設定をしていなかった他の登録がなかったためラッキーだった。
- 2回作成したので、AWS作成の復習になった。
以上です。
同じように困った人の解決になれば幸いです。
(間違いあった時は、教えてください!)
- 投稿日:2020-12-18T08:36:57+09:00
【AWS】“Aws::S3::Errors::AccessDenied in 〇〇sController#create”エラー(AWSのS3に保存されない)
エラー内容
AWSのS3を使い、画像データを保存できるように実装していたが、
1. ローカル環境においてエラー
2. 本番環境においてエラー
3. AWSのS3上のバケットにはデータなし(保存されていない)
という事象が発生。エラーが起こったときの設定の状況
- macOS Catalina バージョン10.15.7
- Rails Rails 6.0.3.4
- Herokuを使ってデプロイ
- AWSのアカウント・IAMユーザー・S3のバケットの3つを新規作成したばかり
解決した方法(結論)
「AWSのアカウント削除→作成し直し」を行ったところ、エラー解決。
(保存できるようになった)解決方法の補足
- AWSアカウント作成時、Googleのシステム障害が発生していた。
- メンターさんにも見てもらいタイポや記述もれが無いのにも関わらず、保存ができなかった。
- アカウントの新規作成から1日以上、保存できない状況が続いた。(時間をおいても反映されない)
- AWSアカウントを削除して、新規登録し直す際、前回登録していたメールアドレスが90日間以内であれば再開できる対象になるからか、使えない状況になる。(前回のメールアドレス以外を選択しないといけない)
- こちらの方法を試すも、今回は上手くいかなかった。
AWSアカウント作成し直す前の確認作業
(1)AWS上のバケットポリシーは適切か?
結果:①②あたる部分は、ともに適切だった。
{ "Version": "2012-10-17", "Id": "Policy1544152951996", "Statement": [ { "Sid": "Stmt1544152948221", "Effect": "Allow", "Principal": { "AWS": "①今回のIAMユーザーのARNをここに記述" }, "Action": "s3:*", "Resource": "arn:aws:s3:::②今回のバケット名をここに記述" } ] }(2)ブロックパブリックアクセスのバケット設定は適切か?
結果:適切だった。
(3)VSコード上の、保存先は適切か?
結果:適切だった。
:amazon
になっているのでOK。(開発環境)config/environments/development.rb# Store uploaded files on the amazon file system (see config/storage.yml for options). config.active_storage.service = :amazon
:amazon
になっているのでOK。(本番環境)config/environments/production.rb# Store uploaded files on the amazon file system (see config/storage.yml for options). config.active_storage.service = :amazon”今回のバケット名”にあたる部分について、合っていたためOK。
config/storage.ymlamazon: service: S3 region: ap-northeast-1 bucket: 今回のバケット名 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>(4)環境変数は適切か?
結果:適切だった。
環境変数の確認方法(MacOSがCatalina以降の場合)
terminalvim ~/.zshrc
:wq
で閉じる。- もし誤っていた場合には
i
を押して編集し、esc
を押してから、:wq
で閉じる。コンソール確認
確認した内容は以上。
AWSアカウント(ルートユーザー)を作り直し
上記の通り、確認したが誤りはない様子。
思い当たる仮説としては、「Googleの調子が悪い頃、ちょうどアカウント登録していた」だったため、アカウントを作り直すことにした。
- 請求がゼロか確認
- バケットの削除
- IAMユーザーの削除(今回はルートユーザーに紐づけて作成したため、無し)
- AWSアカウント(ルートユーザー)の削除 (補足)リンク先は、1と同じ。
AWSを作り直したあとは、
- 環境変数の設定し直し
- バケットポリシーの設定し直し が必要となる。
感想
- 今回のケースは、因果関係が定かではないが、「システム障害が起きている前後は、アカウントの作成をしないほうがよい」ことを学んだ。
- 今回はアカウントを新規作成したばかりで、作り直しの手間はかかるが、他に消えると困る設定をしていなかったのでラッキーだった。
- 2回作成したので、AWS作成の復習になった。
以上です。
同じように困った人の解決になれば幸いです。
(間違いあった時は、教えてください!)
- 投稿日:2020-12-18T02:23:08+09:00
Amazon Managed Service for Prometheus 触ってみた
皆さん AWS re:Invent は楽しんでおりますか!
12 月は 12/18 まで開催中ですので、まだの人は見てみましょう!https://reinvent.awsevents.com/
自分は今年の夏に EKS へ移行するのに合わせて、Kubernetes 上に Airflow, Grafana, Prometheus を苦しみながら導入したのですが、全部 Managed Service としてあっけなく発表されてしまいました (Airflow は re:Invent の時期ではなく、予選落ち組)。
ただ、これらのサービスを AWS Managed のサービスとしてではなく構築・運用したことで、それぞれのサービスについての概念・知見を習得することができたので後悔はしておりません。さて EKS 上で管理している Prometheus についてですが、
- メモリを食いまくって落ちる
- インターネット経由して一定のセキュリティを担保した上でメトリクスを格納 & クエリしたい
といった困り・要望があり、ちょうどなんとかしないとなぁと思っていたところでした。
今回発表された Prometheus のマネージドサービスである Amazon Managed Service for Prometheus ですべて解決しそうだったのでどんなものかと触ってみたので共有します。話は脱線しますが、EKS 移行劇については下記 note にまとめてありますので、ご興味ある方はどうぞ!
https://note.com/tyrwzl/n/n14703cfca236
Amazon Managed Service for Prometheus について
Prometheus について詳しくは説明しません、他の記事をあたってください。
Amazon Managed Service for Prometheus は Prometheus 互換のメトリクスデータを格納したり、格納したデータをクエリしたりできるサービスみたいです。
Prometheus Exporter などを利用して、Amazon Managed Service for Prometheus にメトリクスを格納し、Grafana から Amazon Managed Service for Prometheus に対してメトリクスをクエリしてグラフとして確認する、といったように利用できます。
Prometheus のマネージドサービスといいましたが、「Prometheus で収集したメトリクスデータの格納庫」がイメージとしては近いのでしょうか。
そもそものスタンスとして Prometheus はメトリクスを永続保存しません。
永続保存したい場合、Thanos や Cortex といった別のサービスを使ってメトリクスデータを保管しておく必要がありました (弊社では Thanos を採用、Amazon Managed Service for Prometheus は Cortex を利用しているみたい)。
したがって、Amazon Managed Service for Prometheus を利用しても、コンテナからメトリクスをスクレイプしたりする Exporter などは自前でデプロイする必要がありますし、格納したデータをグラフなどで確認するには Grafana なども自前で用意する必要があります。
Grafana に関しては Amazon Managed Service for Grafana がありますね!(絶賛プレビュー申し込み中)。誰が使うべき?
さて、Amazon Managed Service for Grafana のターゲットユーザー、使って得する人は誰でしょう?
そこは流石 AWS、きちんと FAQ に記載されていました。Q: Why should I use Amazon Managed Service for Prometheus?
You should use AMP if you have adopted an open source-based monitoring strategy, have already deployed or plan to adopt Prometheus for container monitoring, and prefer a fully managed experience where AWS provides enhanced security, scalability, and availability.Q: How does Amazon Managed Service for Prometheus relate to Amazon CloudWatch? Which one should I use?
You should use Amazon CloudWatch if you are looking for a comprehensive observability service that brings together logs, metrics, tracing, dashboarding, and alerting in a unified experience that encompasses AWS services, EC2, containers, and serverless.
You should use AMP if you are running containers and want a service that is fully compatible with the Prometheus open source project. You should also choose AMP if you are already running Prometheus and are looking to eliminate that ongoing operational cost while also improving security.(https://aws.amazon.com/jp/grafana/faqs/ から抜粋)
既に Prometheus を利用してモニタリングシステムを構築していて、運用コストに嫌気が指している人をターゲットにしていそうです。
Amazon Managed Service for Prometheus を利用することで、収集したメトリクスデータの管理などについて気にかける必要がなくなります。主要な機能
下記ドキュメントから読み取れたことを記載します。
https://docs.aws.amazon.com/prometheus/index.html
セキュリティ
Amazon Managed Service for Prometheus ではメトリクスデータの格納と取得を実施するのに AWS IAM の認証情報が格納された AWS Signature Version 4 の署名が必要です。
したがって、AWS IAM によるセキュリティ機能の上に Prometheus を利用することができます。
更にインターネット経由でメトリクスデータのやり取りをしたくないユースケースのために、VPC Endpoint (Interface 型) を利用することができます。可用性
メトリクスデータについてはドキュメントを確認する限り、Multi-AZ での保管をしており、3 AZ への展開をしています
データはまず EBS に保管し、その後 S3 に保存しているとのことです。データ保管期間
現時点では 150 日の保管期間のみしか選択できません。
Prometheus の機能
現時点では下記機能は提供されていません。
- Prometheus alert manager
- Prometheus alerting rules and recording rules
リージョン
現時点では下記リージョンでしか利用できません
- Europe (Ireland)
- Europe (Frankfurt)
- US East (N. Virginia)
- US East (Ohio)
- US West (Oregon)
東京リージョンにも期待してます!
Amazon Managed Service for Prometheus 触ってみた with EKS
下記ブログを参照したので、ブログを読んだ人は下記を読まないでください。
ただ、リージョンをまたいで使えるか検証したかったので、そこが気になる場合や日本語で読みたい場合は読んでください。https://aws.amazon.com/jp/blogs/mt/getting-started-amazon-managed-service-for-prometheus/
下記のようにリージョンをまたいでいます。
リソース リージョン Amazon Managed Service for Prometheus us-east-1 EKS ap-northeast-1 なお、下記では AWS CLI や eksctl、kubectl, helm を利用するのでそれらが利用可能、かつ、それぞれの認証情報が揃っている shell 環境が必要です、
Amazon Managed Service for Prometheus の作成
AWS マネジメントコンソールにログインして、Amazon Managed Service for Prometheus のページ (https://console.aws.amazon.com/prometheus/home) に行きましょう。
Amazon Managed Service for Prometheus はプレビュー状態ですが、プレビュー申請は不要です。
上記画像のようにサクッと workspace を作成します。すると数秒後に Status が ACTIVE となってリソースが作成されます。
リソースが作成されると 2 種類の HTTP Endpoint (Endpoint - remote write URL, Endpoint - query URL) が発行され、それぞれメトリクスデータを格納するものと格納したメトリクスデータをクエリするものとなります。
なお、これらの URL はパブリックにアクセス可能な模様です。$ dig aps-workspaces.us-east-1.amazonaws.com +short 3.223.166.82 100.24.159.220 52.70.212.28 54.210.4.8 35.169.187.85 54.243.211.98IAM ロール、プロバイダーの作成
次に Amazon Managed Service for Prometheus へメトリクスデータを格納、格納したメトリクスデータを取得するのに利用する IAM リソースを作成します。
ブログにて用意されている下記 bash スクリプトを実行します。
書き換えなければならないのは 1 行目のYOUR_EKS_CLUSTER_NAME
と、今回 EKS が ap-northeast-1 にあるので最後の行の eksctl に--region
パラメータを渡しているところです。上記スクリプトを実行すると下記のようになります
エラー文が出力されているように見えますが、問題ありません✦ ❯ bash init.sh Creating a new trust policy An error occurred (NoSuchEntity) when calling the GetRole operation: The role with name EKS-AMP-ServiceAccount-Role cannot be found. Appending to the existing trust policy An error occurred (NoSuchEntity) when calling the GetPolicy operation: Policy arn:aws:iam::XXXXXXXXXX:policy/AWSManagedPrometheusWriteAccessPolicy was not found. Creating a new permission policy AWSManagedPrometheusWriteAccessPolicy { "Policy": { "PolicyName": "AWSManagedPrometheusWriteAccessPolicy", "PolicyId": "ANPARVSLQ63UWTO7GOCOY", "Arn": "arn:aws:iam::XXXXXXXXXX:policy/AWSManagedPrometheusWriteAccessPolicy", "Path": "/", "DefaultVersionId": "v1", "AttachmentCount": 0, "PermissionsBoundaryUsageCount": 0, "IsAttachable": true, "CreateDate": "2020-12-17T12:50:08+00:00", "UpdateDate": "2020-12-17T12:50:08+00:00" } } An error occurred (NoSuchEntity) when calling the GetRole operation: The role with name EKS-AMP-ServiceAccount-Role cannot be found. EKS-AMP-ServiceAccount-Role role does not exist. Creating a new role with a trust and permission policy arn:aws:iam::XXXXXXXXXX:role/EKS-AMP-ServiceAccount-Role [ℹ] eksctl version 0.30.0 [ℹ] using region ap-northeast-1 [ℹ] will create IAM Open ID Connect provider for cluster "eks-cluster" in "ap-northeast-1" [✔] created IAM Open ID Connect provider for cluster "eks-cluster" in "ap-northeast-1"IAM のページに行くと、下記のように IAM ロールが作成されていることが確認できます。
Prometheusのインストール
Amazon Managed Service for Prometheus を触るために、なんか適当なデータを投入しなければなりません。
ということで、Prometheus を EKS にインストールします。
Helm を使ってコマンドを実行するだけです。$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts "prometheus-community" has been added to your repositories $ kubectl create ns prometheus namespace/prometheus created $ helm install prometheus-for-amp prometheus-community/prometheus -n prometheus NAME: prometheus-for-amp LAST DEPLOYED: Thu Dec 17 12:56:30 2020 NAMESPACE: prometheus STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster: prometheus-for-amp-server.prometheus.svc.cluster.local Get the Prometheus server URL by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace prometheus port-forward $POD_NAME 9090 The Prometheus alertmanager can be accessed via port 80 on the following DNS name from within your cluster: prometheus-for-amp-alertmanager.prometheus.svc.cluster.local Get the Alertmanager URL by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=alertmanager" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace prometheus port-forward $POD_NAME 9093 ################################################################################# ###### WARNING: Pod Security Policy has been moved to a global property. ##### ###### use .Values.podSecurityPolicy.enabled with pod-based ##### ###### annotations ##### ###### (e.g. .Values.nodeExporter.podSecurityPolicy.annotations) ##### ################################################################################# The Prometheus PushGateway can be accessed via port 9091 on the following DNS name from within your cluster: prometheus-for-amp-pushgateway.prometheus.svc.cluster.local Get the PushGateway URL by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=pushgateway" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace prometheus port-forward $POD_NAME 9091 For more information on running Prometheus, visit: https://prometheus.io/下記のように
prometheus
ネームスペースに Prometheus 関連の POD が作成されていることがわかります。$ k get po -n prometheus NAME READY STATUS RESTARTS AGE prometheus-for-amp-alertmanager-5cb9f4478c-km4ht 2/2 Running 0 6m26s prometheus-for-amp-kube-state-metrics-bc9cb958f-l7p7k 1/1 Running 0 6m26s prometheus-for-amp-node-exporter-69qsw 1/1 Running 0 6m27s prometheus-for-amp-node-exporter-bg4ss 1/1 Running 0 6m26s prometheus-for-amp-node-exporter-fs74x 1/1 Running 0 6m26s prometheus-for-amp-pushgateway-56ff9d9d99-4z2sf 1/1 Running 0 6m26s prometheus-for-amp-server-7f6d6fcf59-kpl5m 2/2 Running 0 6m26sAWS signing proxy のデプロイ
先述したとおり、Amazon Managed Service for Prometheus にメトリクスデータを投入・取得するには AWS Signature Version 4 の署名が必要です。
先の Helm テンプレートは Amazon Managed Service for Prometheus のための Prometheus ではなく、AWS Signature Version 4 の署名を含める仕組みが用意されていません。
これを実現してくれるのが AWS signing proxy というもので、Prometheus のサイドカーコンテナとしてデプロイします。ブログのに記載のある YAML をコピー & 自分の環境に合わせて上書きしてもいいのですが、Amazon Managed Service for Prometheus のマネジメントコンソールにも同じものが用意されています。
マネジメントコンソールの YAML の場合は
annotations
のeks.amazonaws.com/role-arn
の値を「IAM ロール、プロバイダーの作成」で作成した IAM ロール (arn:aws:iam:::role/EKS-AMP-ServiceAccount-Role)の ARN に書き換えるだけで OK です。YAML が用意できたら Helm テンプレートの Value として渡して再度 Prometheus をデプロイします。
$ helm upgrade --install prometheus-for-amp prometheus-community/prometheus -n prometheus -f ./amp_ingest_override_values.yaml Release "prometheus-for-amp" has been upgraded. Happy Helming! NAME: prometheus-for-amp LAST DEPLOYED: Thu Dec 17 13:03:23 2020 NAMESPACE: prometheus STATUS: deployed REVISION: 2 TEST SUITE: None NOTES: The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster: prometheus-for-amp-server.prometheus.svc.cluster.local Get the Prometheus server URL by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace prometheus port-forward $POD_NAME 9090 The Prometheus alertmanager can be accessed via port 80 on the following DNS name from within your cluster: prometheus-for-amp-alertmanager.prometheus.svc.cluster.local Get the Alertmanager URL by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=alertmanager" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace prometheus port-forward $POD_NAME 9093 ################################################################################# ###### WARNING: Pod Security Policy has been moved to a global property. ##### ###### use .Values.podSecurityPolicy.enabled with pod-based ##### ###### annotations ##### ###### (e.g. .Values.nodeExporter.podSecurityPolicy.annotations) ##### ################################################################################# The Prometheus PushGateway can be accessed via port 9091 on the following DNS name from within your cluster: prometheus-for-amp-pushgateway.prometheus.svc.cluster.local Get the PushGateway URL by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=pushgateway" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace prometheus port-forward $POD_NAME 9091 For more information on running Prometheus, visit: https://prometheus.io/デプロイされたものを確認すると、Prometheus Server のみ再デプロイされています。
$ k get po -n prometheus NAME READY STATUS RESTARTS AGE prometheus-for-amp-alertmanager-5cb9f4478c-km4ht 2/2 Running 0 7m10s prometheus-for-amp-kube-state-metrics-bc9cb958f-l7p7k 1/1 Running 0 7m10s prometheus-for-amp-node-exporter-69qsw 1/1 Running 0 7m11s prometheus-for-amp-node-exporter-bg4ss 1/1 Running 0 7m10s prometheus-for-amp-node-exporter-fs74x 1/1 Running 0 7m10s prometheus-for-amp-pushgateway-56ff9d9d99-4z2sf 1/1 Running 0 7m10s prometheus-for-amp-server-0 0/3 ContainerCreating 0 17s詳細を確認してみるとサイドカーコンテナがついていることがわかります。
$ k describe po prometheus-for-amp-server-0 -n prometheus Name: prometheus-for-amp-server-0 ... Containers: prometheus-server-configmap-reload: Container ID: docker://51f6c62b778acb9b6c895b7dc6f14efe54f9747ed07936206be487fe4227a2ef Image: jimmidyson/configmap-reload:v0.4.0 ... prometheus-server: Container ID: docker://35840c35fcad0428b5568f469cd2e28209cda1eab52dc135bee1a1d8693546d7 Image: quay.io/prometheus/prometheus:v2.22.1 ... aws-sigv4-proxy-sidecar: Container ID: docker://6897c52c5114bee6886e254c8c526c80ce2f94fcd807ae202f4b5491bbbc6bdb Image: public.ecr.aws/aws-observability/aws-sigv4-proxy:1.0 ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 50s default-scheduler Successfully assigned prometheus/prometheus-for-amp-server-0 to ip-10-0-44-92.ap-northeast-1.compute.internal Normal SuccessfulAttachVolume 47s attachdetach-controller AttachVolume.Attach succeeded for volume "pvc-c1a0ad74-fe75-4646-86d8-61c51e320f87" Normal Pulling 40s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Pulling image "jimmidyson/configmap-reload:v0.4.0" Normal Pulled 35s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Successfully pulled image "jimmidyson/configmap-reload:v0.4.0" Normal Created 35s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Created container prometheus-server-configmap-reload Normal Started 35s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Started container prometheus-server-configmap-reload Normal Pulling 35s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Pulling image "quay.io/prometheus/prometheus:v2.22.1" Normal Pulled 24s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Successfully pulled image "quay.io/prometheus/prometheus:v2.22.1" Normal Created 23s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Created container prometheus-server Normal Started 23s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Started container prometheus-server Normal Pulling 23s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Pulling image "public.ecr.aws/aws-observability/aws-sigv4-proxy:1.0" Normal Pulled 19s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Successfully pulled image "public.ecr.aws/aws-observability/aws-sigv4-proxy:1.0" Normal Created 19s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Created container aws-sigv4-proxy-sidecar Normal Started 19s kubelet, ip-10-0-44-92.ap-northeast-1.compute.internal Started container aws-sigv4-proxy-sidecarGrafana のデプロイ
さて、Amazon Managed Service for Prometheus にメトリクスデータを投入できたか確認してみましょう。
Amazon Managed Service for Grafana のプレビュー申請が通ってないので、Grafana をデプロイします。$helm repo add grafana https://grafana.github.io/helm-charts "grafana" has been added to your repositories $kubectl create ns grafana namespace/grafana created $helm install grafana-for-amp grafana/grafana -n grafana NAME: grafana-for-amp LAST DEPLOYED: Thu Dec 17 13:08:08 2020 NAMESPACE: grafana STATUS: deployed REVISION: 1 NOTES: 1. Get your 'admin' user password by running: kubectl get secret --namespace grafana grafana-for-amp -o jsonpath="{.data.admin-password}" | base64 --decode ; echo 2. The Grafana server can be accessed via port 80 on the following DNS name from within your cluster: grafana-for-amp.grafana.svc.cluster.local Get the Grafana URL to visit by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace grafana -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana-for-amp" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace grafana port-forward $POD_NAME 3000 3. Login with the password from step 1 and the username: admin ################################################################################# ###### WARNING: Persistence is disabled!!! You will lose your data when ##### ###### the Grafana pod is terminated. ##### #################################################################################Prometheus の場合と同様に AWS signing proxy をデプロイします (必要な YAML は Amazon Managed Service for Prometheusのマネジメントコンソールにはないのでブログを参照してください)。
$ helm upgrade --install grafana-for-amp grafana/grafana -n grafana -f ./amp_query_override_values.yaml Release "grafana-for-amp" has been upgraded. Happy Helming! NAME: grafana-for-amp LAST DEPLOYED: Thu Dec 17 13:08:47 2020 NAMESPACE: grafana STATUS: deployed REVISION: 2 NOTES: 1. Get your 'admin' user password by running: kubectl get secret --namespace grafana grafana-for-amp -o jsonpath="{.data.admin-password}" | base64 --decode ; echo 2. The Grafana server can be accessed via port 80 on the following DNS name from within your cluster: grafana-for-amp.grafana.svc.cluster.local Get the Grafana URL to visit by running these commands in the same shell: export POD_NAME=$(kubectl get pods --namespace grafana -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana-for-amp" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace grafana port-forward $POD_NAME 3000 3. Login with the password from step 1 and the username: admin ################################################################################# ###### WARNING: Persistence is disabled!!! You will lose your data when ##### ###### the Grafana pod is terminated. ##### #################################################################################デプロイが完了したら、まず Grafana POD の名前を確認してから
kubectl port-forward
を利用してローカルのブラウザで開けるようにします。
(自分は Cloud9 上で実行したので 8080 番にフォワードしています。)$ k get po -n grafana NAME READY STATUS RESTARTS AGE grafana-for-amp-79d5454dbc-ghhp8 1/1 Running 0 3h42m $ kubectl port-forward -n grafana pods/grafana-for-amp-79d5454dbc-ghhp8 8080:3000うまくいくと上記のようにログイン画面が出ます。
パスワードは下記コマンドで確認します$ kubectl get secrets grafana-for-amp -n grafana -o jsonpath='{.data.admin-password}'|base64 --decodeログインできたら Grafana に Prometheus の設定をします。
Grafana のページ左側、歯車マークをクリックして "Data Sources" をクリックして、"Prometheus" を選択します。
すると上記のような画面が出るので、
- HTTP > URL: Amazon Managed Service for Prometheus のマネジメントコンソールの Endpoint - query URL
- Auth > SigV4 auth: ON
- SigV4 Auth Details > Authentication Provider: AWS SDK Default
- SigV4 Auth Details > Default Region: us-east-1
設定終わったら上記画像のように Grafana のページの下の方に "Save & Test" というボタンがあるのでクリックして、"Data source is working" と出れば設定完了です。
実際にメトリクスデータをクエリしてみます。
Grafana のページ左側、コンパスマークをクリックします。
"Metrics" の右脇にクエリしたいメトリクス名を入力して右上のボタンをクリックするとグラフが表示されます。今後試したいこと
今回はサラッと触っただけなので、メトリクスの数を増やして自前の Prometheus 環境とクエリ速度を比べたり、料金コストの比較をしてみたいと思います。
また、文頭の方で自前の Prometheus に対して「インターネット経由して一定のセキュリティを担保した上でメトリクスを格納 & クエリしたい」という要望を出していたのですが、これはカナリアリリースのために Spinnaker のカナリア分析ツール Kayenta で Prometheus のメトリクスを利用したいために出たものとなります。
なので、今後 kayenta で素直に Amazon Managed Service for Prometheus が利用できるか、Istio を利用しているので Istio Proxy と AWS signing proxy が共存できるのかなどを検証していきたいと思います。
- 投稿日:2020-12-18T00:29:08+09:00
Amazon DLMとは?
勉強前イメージ
EBSのスナップショットをスケジュールに組み込むことが出来る・・・?
EC2の中にあるのか、そもそも別サービスかは知らない調査
Amazon DLMとは?
Amazon Data Lifecycle Manager の略で
EBSのスナップショットのスケジューリングや世代管理をすることが出来る
HDD自体のバックアップを取るようなイメージそもそもEBSってなに?
Amazon Elastic Block Store の略で
EC2にアタッチするブロックレbwるのストレージサービス
仮想ディスクのようなもの。Amazon DLMを実際触ってみよう!
ボリュームにタグ付けを行う
対象となるEC2のボリュームにタグ付を行います。
- インスタンスから辿る
対象のEC2インスタンスにチェックを入れ、
ストレージ
タブに移ります。
ストレージタブの下にブロックデバイスとしてアタッチされているボリュームが表示されています。
今回はこちらのボリュームを対象としていきます。
ボリュームIDをクリックして、ボリュームに移ります。
- ボリュームににタグ付を行う
どのボリュームかわかりやすいようにタグ付を行います。
タグのタブへ移りますここで、
タグの追加/編集
を行います。今回は以下の文字列でタグとします。
キー : DLM_tag 値 : test_storage記入を行ったら、保存をします。
先程保存したキーが表示されていることを確認します
ライフサイクルポリシーを作成
- ライフサイクルポリシーの作成
EC2 > Elastic Block Store へ移動して、ライフサイクルポリシーを作成します
- ライフサイクルポリシーを設定
今回は以下の選択肢で作成
ポリシータイプ : EBSスナップショットポリシー リソースタイプ : ボリューム (インスタンスごとでも出来るらしい) 説明 : ライフサイクルポリシーの説明を記載 これらのタグを持つターゲット : 先程↑で設定したタグを記載IAMロールはデフォルトで作成します。
ポリシースケジュールに関しては、頻度や時間を設定します。
スケジュール名 : スケジュールの名前を設定します 頻度 : どれくらいの頻度で設定するかを決めます 毎 : ここは、↑の頻度によって変わるのですが、毎日に設定したので何時間ごとに取るか決めます 開始時間 : UTCの時間で何時から開始するかを決めます 保持タイプ : 保持タイプとしてカウント方式と期間方式があります 保持する : 今回は7回分保持します追加のタグに名前を入れておくことで、出来たスナップショットに名前(Nameの値)が追加されます。
概要が記載されていますので確認します。
作成してすぐに有効にするのか、無効のままにするのか選んで作成を行います。
記載していない部分はデフォルトで設定しております。
- 新しいポリシーが追加されました
作成されたスナップショットの確認
ちゃんと作られていました!
勉強後イメージ
結構思ったよりいろいろ選択項目があった。
でもバックアップとしてはめっちゃ楽だなー参考
- 投稿日:2020-12-18T00:12:26+09:00
AWS Lambda で作る無償の脅威インテリジェンスリレーモジュール
はじめに
この記事はシスコの同志による Cisco Systems Japan Advent Calendar 2018 の 18 日目として投稿しました。今年はカレンダーが2つあります!!
2020年版(1枚目): https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco22017年版: https://qiita.com/advent-calendar/2017/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2019年版: https://qiita.com/advent-calendar/2019/cisco
2020年版: https://qiita.com/advent-calendar/2020/ciscoこのエントリで取り上げるテーマは、「AWS Lambda で作る無償の脅威インテリジェンスリレーモジュール」です。Cisco SecureXという、こちらも無償のCiscoのプラットフォームを利用し、数多くの利用可能な脅威インテリジェンスプラットフォームを組み合わせて、AWS Lambda 上に情報中継用のアプリケーションをデプロイします。以下のステップでまとめます。
- 無償で使えるセキュリティプラットフォーム:Cisco SecureX
- リレーモジュールの導入ステップ
- AWS Lambda 環境セットアップ
- AWS Lambda にリレーモジュールデプロイ
- Cisco SecureX へのリレーモジュール組み込み
無償で使えるセキュリティプラットフォーム:Cisco SecureX
Ciscoのセキュリティ製品を利用している誰もが、すぐに、簡単に、サインナップして使える無償のプラットフォームとして、Cisco SecureX が今年の7月1日にリリースされました!Cisco SecureXは日々のセキュリティオペレーションについて、運用の簡素化、可視化の改善、運用の効率化を目指して開発された、「クラウドから提供されるセキュリティ製品のAPIアグリゲータ」です。
フリートライアルも含む、例えば Umbrella、AMP、Firepower、Stealthwatch など、どれかを使っていれば Cisco SecureX アカウントによりこれらの製品と連携でき、専用の統合化されたダッシュボードを構築することができます。さらに Cisco SecureX Orchestrator と、ビルドインも含む SecureX 上で新しく設計・設定できるオーケストレーションワークフローを使うことによって、インシデント発生時に行う作業の自動化、インシデントに関わる Observable (怪しいドメイン、IPアドレス、ファイルハッシュなどの観測情報)の調査を劇的に簡素化することが可能になります。Cisco SecureX はシスコ製品だけでなく、シスコ製品以外のメーカーのセキュリティ製品や、データセンターインフラ、ネットワーク・インフラ、クラウドセキュリティサービスに対してオーケストレーションワークフローを組み込むことが可能です。
Cisco SecureX (http://security.cisco.com)
のリンクから、ログイン/サインナップできます!無償の脅威インテリジェンスと SecureX リレーモジュール
Cisco SecureX で連携可能な脅威インテリジェンス提供サービス、連携可能な他ベンダー製品は以下より参照可能です。
Cisco SecureX threat response: Integrations and Partners
VirusTotal は非常に有名で良く使われる方も多いのではないでしょうか?他にも Shodan.io や AbuseIPDB 、 Google Safe Browsing 、 Microsoft Graph Security なども有名です。Cisco SecureX では、このような各インテリジェンス提供サービスと連携させるための「リレーモジュール」が用意されています。
これにより、各インテリジェンスソースをアプリケーションとして展開された AWS Lambda 関数および、 API Gateway により Cisco SecureX に対応した STIX 2.0 ベースの CTIM (Cisco Threat Intel Model) に変換し、SecureX Threat Response 機能に情報をエンリッチできます。
リレーモジュールの導入ステップ
今回はサンプルとして Shodan.io のリレーモジュールを AWS Lambda 上に展開します。基本的にはその他の AWS 上でリレーモジュールを利用するインテリジェンス連携も、作業ステップは同じです。
SecureX Shodan Relay
https://github.com/CiscoSecurity/tr-05-serverless-shodan#shodan-relay-api
- デプロイ用ローカル環境の整備 (python/pip/zappaのインストールと環境設定)(AWS Lambdaを展開する準備)
- AWS Lambda (IAM) 環境セットアップ
- AWS Lambda にリレーモジュールデプロイ
- Cisco SecureX へのリレーモジュール組み込み
以下、導入用の環境を使いました。
CentoOS 6.10
デプロイ用ローカル環境の整備 (python/pip/zappaのインストールと環境設定)(AWS Lambdaを展開する準備)
python 3.7 インストール
アプリケーション導入に必要な要求事項として、 python は Version 3.7 以上である必要があります。
https://github.com/CiscoSecurity/tr-05-serverless-shodan#shodan-relay-apiThe application has been implemented and tested using Python 3.7. You may try to use any higher versions if you wish as they should be backward-compatible.
python が旧バージョンだったため、 バージョン3.7 をインストールします。
pyhon3.7# yum install gcc openssl-devel bzip2-devel libffi-devel zlib-devel # cd /usr/src # wget https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tgz # tar xzf Python-3.7.9.tgz # cd Python-3.7.9 # ./configure --enable-optimizations # make altinstallシンボリックリンクの貼り直し等の調整を行いました
シンボリックリンク調整# ln -s /usr/local/bin/python3.7 /usr/bin/python3 # ln -s /usr/local/bin/pip3.7 /usr/bin/pip3Zappaイントール
AWS の PaaS である AWS Lambda + API Gateway として Cisco SecureX リレーモジュールをデプロイします。イベントドリブンであるこの Python ベースのアプリケーションは Zaapa を使って導入ができます。Cisco SecureX リレーモジュールは Python アプリのためのサーバレス Web アプリケーションとなります。もちろんすでに AWS Lambda を利用している方はこのステップは必要ありません。ここでは Zappa をインストールします。
Zappa# pip install --upgrade pip # pip install flask # pip install zappa # zappa init # zappa deployawscli インストール
Zappa で導入する AWS Lambda IAM ユーザ環境をセットアップするため、awscliをインストールします。
awscli# pip install awscli
デプロイ用ローカル環境のリレーモジュールの導入準備
リレーモジュールを導入用の環境へコピー
導入用環境 (CentOS) の任意のディレクトリに、リレーモジュールソースコードをコピーします。
git# git clone https://github.com/CiscoSecurity/tr-05-serverless-shodan
リレーモジュール導入用環境ファイルの編集
zappa_settings.json# cp zappa_settings.json zappa_settings.json.bak # vi zappa_settings.json"aws_region" は導入するべき正しい AWS リージョンに変更、"s3_bucket" は AWS Accoun ID 等任意の情報を追加するのが望ましいです。S3 バケットの情報は AWS アカウントに対してユニークである必要があります。同一 AWS アカウント内で展開する AWS Lambda 関数の名前が重複することはできません。
zappa_settings.json{ "dev": { "app_function": "app.app", "aws_region": "ap-northeast-1", "exclude": [".*", "*.json", "*.md", "*.txt"], "keep_warm": false, "log_level": "INFO", "manage_roles": false, "profile_name": "serverless", "project_name": "tr-shodan-relay", "role_name": "tr-serverless-relay-ZappaLambdaExecutionRole", "runtime": "python3.7", "s3_bucket": "zappa-tr-shodan-relay-xxxxxxxx" } }リレーモジュール導入用 IAM ユーザ Deployment ポリシーファイルの編集
以降のステップで適用する、AWS IAM ユーザの Deployment Policy のテンプレートファイルを編集します。以下のように の項目を "< >" を削除した、前のステップで設定をした AWS IAM ユーザを利用する AWS Account ID に書き換えます。
ZappaLambdaDeploymentPolicy.json# cd aws # vi ZappaLambdaDeploymentPolicy.jsonZappaLambdaDeploymentPolicy.json(編集前): "Resource": [ "arn:aws:iam::<ACCOUNT_ID>:role/*ZappaLambdaExecutionRole" ] :ZappaLambdaDeploymentPolicy.json(編集後): "Resource": [ "arn:aws:iam::9xxxxxxxxxx2:role/*ZappaLambdaExecutionRole" ] :AWS Lambda 環境セットアップ
AWS IAM ユーザ作成
以下に従って、AWS IAM ユーザの導入ポリシーとロールのセットアップを行います。2つ目以降のリレーモジュールを展開する場合も含め、この作業は一回のみで終了します。2つ目以降の新規モジュール導入では、このステップを省略できます。
https://github.com/CiscoSecurity/tr-05-serverless-shodan/blob/develop/aws/HOWTO.md
- AWS コンソールにログイン
- コンソールホームから ”IAM” を検索
- 左のPaneの ”アクセス管理” から ”ユーザー” タブを選択
- ”ユーザーを追加”(青ボタン)をクリック
- ユーザ名:serverless
- アクセスの種類
- プログラムによるアクセス:チェック
- ”次のステップ:アクセス権限” をクリック(次へ)
- ”ユーザーを追加” ”次のステップ:タグ” をクリック(何も選択せず次へ)
- ”ユーザーを追加” "タグの追加オプション" : ”次のステップ:確認” をクリック(何も選択せず次へ)
- ”ユーザーを追加”
- "このユーザにはアクセス権限がありません" と警告が出ますが、問題ありません。
- ”ユーザの作成” をクリック(次へ)
”ユーザーを追加”
保存した .csv ファイルを確認し、作成したserverlessユーザの以下情報を保存します
- アクセスキー (AWS Access Key ID)
- シークレットキー (AWS Secret Access Key)
Deployment ポリシー (ZappaLambdaDeploymentPolicy) の作成とIAMユーザへのアタッチ
- IAM コンソール
- アクセス管理 ポリシー 選択
- ポリシー作成 選択
- JSON タグ選択
- ポリシーの確認 選択
- ポリシー名を "ZappaLambdaDeploymentPolicy" に指定
- IAM コンソール アクセス管理 ユーザ 選択
- serverless 選択
- アクセス権限の追加 選択
- 既存のポリシーを直接アタッチ 選択
- "ZappaLambdaDeploymentPolicy" 選択
- アクセス権限の追加 選択
IAMロール : tr-serverless-relay-ZappaLambdaExecutionRole 作成
- IAM コンソール
- アクセス管理 ポリシー 選択
- ポリシーの作成 選択
- JSON タグ選択
- ローカルにクローンした aws/ZappaLambdaExecutionPolicy.json をコピー&ペースト
ZappaLambdaExecutionPolicy.json{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:*" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "*" ] } ] }
- ポリシーの選択 選択
- ポリシーの作成 選択
- IAM コンソール
- アクセス管理 ロール 選択
- ロールの作成 選択
- AWS サービス Lambda 選択
- 次のステップ:アクセス権限 選択
- ZappaLambdaExecutionPolicy を選択
- 検索ボックスに "Zappa" を入力、表示された
- ZappaLambdaExecutionPolicy を選択し、チェックボックスにチェック
- 次のステップ:タグ 次のステップ:確認
- ロール名を tr-serverless-relay-ZappaLambdaExecutionRole としてロール作成
- ロールから tr-serverless-relay-ZappaLambdaExecutionRole 選択
- 信頼関係 タブ選択
- 信頼関係の編集 選択
- JSONファイルの編集
- "apigateway.amazonaws.com", を追加
(編集前){ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }(編集後){ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "apigateway.amazonaws.com", "lambda.amazonaws.com" ] }, "Action": "sts:AssumeRole" } ] }
- 信頼ポリシーの更新
AWS Lambda にリレーモジュールデプロイ
リレーモジュール導入用ローカル環境のセットアップ (CentOS)
これでリレーモジュールを AWS Lambda に導入する準備が整いました。このステップにてリレーモジュール本体を導入できます。この作業のために Linux root 権限は必要としません。念の為、これまで行った各ステップを再度入念に確認しましょう。
Zappa コマンドにて AWS Lambda にアプリケーションを展開するための AWS IAM アカウント ID 環境を awscli を利用して設定します。
このステップは awscli を利用せず、以下のように指定ディレクトリに環境ファイルをマニュアルで作成することでも対応可能です。初期ステップにて作成した IAM ユーザのユーザ情報ファイル (.csv) から AWS アクセスキー、AWS シークレットアクセスキーを確認し aws コマンドで指定ます。
awscli% aws configure AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX AWS Secret Access Key [None]: XXXXXXXXXXAi5VxZyEwK+G+E/+qTjuXXXXXXXXXX Default region name [None]: ap-northeast-1 Default output format [None]: jsonこの結果、~/.aws ディレクトリ内に credentials ファイルと config ファイルが作成されます。以下はそのフォーマットです。
credential~/.aws/redentials [default] aws_access_key_id=XXXXXXXXXXXXXXXXXXXX aws_secret_access_key=XXXXXXXXXXAi5VxZyEwK+G+E/+qTjuXXXXXXXXXXconfig~/.aws/config [profile serverless] region=ap-northeast-1 output=jsonリレーモジュールを導入
再度 python 3.7 以上であることを確認します。
python% python3 --version
python 仮想環境をセットアップします。このコマンドを実行したディレクトリ配下に仮想環境用のテンポラリディレクトリが作成されます。このコマンドの実行は展開用リレーモジュールユーザのユーザディレクトリや、クローンしたモジュールのソースコード配下のディレクトリでも構いません。
python% python3 -m venv venv
仮想環境をアクティベートします
python% source venv/bin/activate
仮想環境にてpipをアップグレードします
pip% pip install --upgrade pip本体アプリケーションに必要なライブラリをインストールします。必要とするライブラリは導入するリレーモジュールによって異なります。このコマンドはソースコードをクローンしたディレクトリ内の requirements.txt を正しく指定する必要があります。
pip% pip install --upgrade --requirement requirements.txtいよいよリレーモジュールを導入します。これまでのステップを入念に再度確認します。以下のコマンドは、クローンしたソースコードのディレクトリ上で行う必要があります。
zappa% zappa deploy dev Calling deploy for stage dev.. Downloading and installing dependencies.. - markupsafe==1.1.1: Downloading 100%|██████████████████████████████████████| 27.5k/27.5k [00:00<00:00, 12.9MB/s] - cryptography==3.3.1: Downloading 100%|██████████████████████████████████████| 2.66M/2.66M [00:00<00:00, 11.5MB/s] - cffi==1.14.4: Using locally cached manylinux wheel Packaging project as zip. Uploading tr-shodan-relay-dev-1607574935.zip (10.6MiB).. 100%|██████████████████████████████████████| 11.1M/11.1M [00:01<00:00, 9.00MB/s] Uploading tr-shodan-relay-dev-template-1607574945.json (1.6KiB).. 100%|██████████████████████████████████████| 1.66k/1.66k [00:00<00:00, 19.2kB/s] Waiting for stack tr-shodan-relay-dev to create (this can take a bit).. 100%|████████████████████████████████████████████| 4/4 [00:15<00:00, 3.90s/res] Deploying API Gateway.. Deployment complete!: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/devエラー無くデプロイが完了すると、最後のzappaコマンドのステータスにAWA Lambda API GatewayのURLが出力されますので、これを保存します。
上記 Zappa でのアプリケーションデプロイは、以下リンクの動画のような流れで行われます。
Zappa - Serverless Pythonデプロイエラーが出る場合は、これまでの導入ステップを再度確認し、トラブルシューティングを行います。以下リンクの情報を是非確認ください。
- untitled (required permission for deploying AWS Lambda)
- Zappa throws AccessDenied exception while attempting to upload zip to S3 while AWS CLI can upload files with same profile
リレーモジュールのステータス確認
AWS Lambda に導入されたリレーモジュールのステータスを確認します。
- AWS コンソールにログイン
- コンソールホームから ”Lambda” を検索
デプロイに成功したリレーモジュール "tr-shodan-relay-dev" のLambda関数が確認できます。
Cisco SecureX へのリレーモジュール組み込み
Cisco SecureX へのリレーモジュール組み込み
最終フェーズです。Shodan.io のリレーモジュールは Cisco SecureX Integration ページから API Gateway のURL 指定のみで統合が完了します。
2020/12/12 に Cisco SecureX はVersion 1.6.2 にバージョンアップされ、連携できる製品が大幅に増えました。Shodan.io と同様の連携ステップでその他のリレーモジュールを展開してみます。
動作確認
Cisco SecureX Ribbon から Threat Response にて調査用のIOCを投入します。JP CERT、Cisco Talos Blog 等、ランサムウェアなどの任意のIOC情報をいくつか投入して確認します。
統合に成功したリレーモジュールからのエンリッチメントが確認できます。
おまけ
この機会に現時点で手軽に導入できるだけのリレーモジュールを展開してみました。
合計10以上のアプリケーションを AWS Lambda 上に組み込み、毎日リクエストして使いながら時間が経ちました。AWS Lambda の利用は、EC2で稼働させる定常的にCPUリソースを消費するインスタンスと異なり、実行時(リクエスト時)のみのリソース消費で課金がされます。100 万件リクエストに対して 0.20USD の課金となるとのこと、10個以上のモジュールをデプロイした私の今月の AWS への請求額は 4円程度となりました。非常に格安ですね!
参照
[0] Cisco SecureX
http://security.cisco.com
[1] SecureX Shodan Relay
https://github.com/CiscoSecurity/tr-05-serverless-shodan#shodan-relay-api
[2] Zappa
https://github.com/Miserlou/Zappa
[3] Cisco Security API
https://github.com/CiscoSecurity
[4] Cisco Threat Intel Model (CTIM)
https://github.com/threatgrid/ctim
[5] Cisco SecureX threat response: Integrations and Partners
https://www.cisco.com/c/en/us/products/security/threat-response/partners-integrations.html#~technology-partner-ecosystem
[6] AWS HOWTO
https://github.com/CiscoSecurity/tr-05-serverless-shodan/blob/develop/aws/HOWTO.md
[7] untitled (required permission for deploying AWS Lambda)
https://github.com/Miserlou/Zappa/blob/master/example/policy/deploy.json
[8] Zappa throws AccessDenied exception while attempting to upload zip to S3 while AWS CLI can upload files with same profile
https://github.com/Miserlou/Zappa/issues/1972
[9] JSON Web Token (JWT)
https://en.wikipedia.org/wiki/JSON_Web_Token
[10] AWS Lambda 料金
https://aws.amazon.com/jp/lambda/pricing/