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

AWS Cloud9 でダークフラットテーマを使用する方法

サンプルコード

Cloud9 のフラットテーマはlight しかない?

  • Cloud9 のsetting GUI 上からはフラットテーマの選択肢が一つしか表示されていません thems.PNG
  • 旧UI ではダークフラットテーマが選択できたため,そちらに慣れているユーザや,
    VS Code を代表としたモダンなエディタに目が慣れているユーザにとってはちょっと気分が悪いです

user.settings で直接指定するとdark flat テーマが指定できる

  • USER SETTINGS タブから,設定ファイルを直接編集するモードに移ります settings.PNG
  • 以下のようにgeneral.@skin フィールドに"flat-dark" を指定すると隠れたダークフラットテーマが指定できます
  • 隠しコマンドみたいでちょっとわくわくしますね
user.settings
{
    "general": {
        "@skin": "flat-dark"
    }
}

console もダークテーマに

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Equinix Network Edge で Oracle Cloud とAWSを接続してみてみた

Equinix Network Edgeは、相互接続できる仮想ネットワークサービスです。
ハードウェアを追加購入することなくメトロを選び、エッジデバイスの選択と展開、Equinix Cloud Exchange Fabric™(ECX Fabric™)への接続を数分で行うことができます。
ネットワークエッジでは、Cisco、Juniper Networks、Fortinet、Palo Alto Networksなどのプロバイダーのネットワークおよびセキュリティデバイスを選択、設定し他の市場のネットワークプロバイダーやクラウドに接続できます。
ということで、Oracle Cloud Infrastructure(OCI) と AWSを接続してみてみます。
そしてついでに、OCIの Public Service である、Object StorageなどはTransit Routing設定すれば、Private PeeringでAWSとFastConnect経由で疎通できるのでやってみてみます。

■ 構成イメージ

Equinix Network Edge のCisco CSR1000V を使用してOCI とAWS を接続します
OCIとAWSの設定は事前に行っておき、ここでは、Equinix EdgeとOCI FastConnect、AWS Direct Connectの設定を行います。
構成図AWS.png
OCIのTransit Routing設定は次を参考として事前にVCN,DRG,ルート・テーブル,セキュリティリストなど作成しておきます
・参考: Transit Routing + IPSec VPN / FastConnectで Object Storage, Autonomous Databaseへ接続してみてみた

■ Equinix Cloud ExchangeコンソールLOGIN

Equinix Cloud ExchangeポータルへLoginします

① Equinixホームページ
https://www.equinix.co.jp/services/edge-services/network-edge/ ページから、右上にある[ログイン]から[Equinix Cloud Exchangeポータル]をクリック

② Equinix Cloud Exchangeポータル 選択
01.png

③ Login
03_Create_Device_Cisco03.png

④ Login完了
02_Create_Connection.png

■ Equinix Network Edge Virtual Device作成

Equinix Network EdgeのVirtual Deviceを作成します。
ここでのVirtual Deviceは、Cisco CSR100Vを選択します。

(1) Create Virtual Device
[Network Edge] > [Create Virtual Device]をクリック

03_Create_Device_Cisco04.png

(2) Select Vendor Packages
ここで、CISCO, Juniper vSRX , Fortinat Fotigateを選択できます
ここでは、'Cisco CSR 1000V'を選択し,[See Description]をクリック

03_Create_Device_Cisco05.png

(3) Cisco CSR 1000V
[Select and Continue]をクリック
03_Create_Device_Cisco06.png

(4) Select Edge Location
Edge Deviceの Metoro(ロケーション)を選択
ここでは、'Ashburn'を選択
03_Create_Device_Cisco07.png

(5) Device Deetails
以下内容を入力し、[Next Additional Services]をクリック

・Edge Device
    - Device Name: Webコンソールに出力する名前
    - Host Name Prifix: Deviceホスト名
・Device Status Notifications
    - email address(es): Notification Mailを受信するアドレス
・Software Pakage A version
    - Software Pakage: Pakageタイプ
    - Version: Version
・Select License Throughput: 帯域を選択

03_Create_Device_Cisco08.png

(6) Additional Services
Virtual Deviceの SSHユーザー、IPアドレスを必要に応じて追加設定し、[Next Review]をクリック
03_Create_Device_Cisco09.png

(7) Create a Cisco CSR 1000V(3)
Terms & COndition項目にある [Order Trems]をクリック
03_Create_Device_Cisco10.png

(8) Review and Submit
内容を確認し、Terms & Conditionsにある [order Terms]をクリック
03_Create_Device_Cisco11.png

(9) Terms & Conditions
記載されている契約事項を確認し、問題なければ、[I Have read and understand these terms]チェックし、[Accept]をクリック
03_Create_Device_Cisco12.png

(10) Terms & Conditions Accepted
[Create Edge Device]をクリック
03_Create_Device_Cisco13.png

(11) Your Edge Devices(s) were created and are being provisoned
View your device in your dashboardにある[Go to the Edge Device]をクリック

■ Equinix Network Edge と OCI FastConnect接続

● OCI FastConnect 作成

OCIコンソールへログインし、Equinix Cloud Exchangeへ接続するFastConnectを作成します

(1) FastConnect画面
OCIへログインし、リージョンを選択、ここでは[US East(Ashburn)]を選択
[ネットワーキング] > [FastConnect]画面へ遷移し、
[FastConnectの作成]をクリック

01.png

(2) 接続の作成:接続タイプ
以下設定を行い、[次]をクリック

・接続タイプ:[Oracleプロバイダの使用]を選択
・プロバイダ: [Equinix:CloudExchange] を選択

02.png

(3) 接続の作成:構成
以下設定をおこない[作成]をクリック

・名前: 抵当な名前を設定
・コンパートメント: 作成するコンパートメントを選択
・仮想回線タイプ: [プライベート仮想回線]を選択
・動的ルーティング・ゲートウェイ: DRGを選択
・プロビジョニングされた帯域幅: 帯域を選択
・顧客GBP IPアドレス: Equinix側 BGPアドレスを設定
・ORACLE BGP IPアドレス: OCI側 BGPアドレスを設定
・顧客GBP ASN: Equinix側 BGPアドレスを設定

05.png

(4) FastoConnect作成完了
OCIDを[コピー]しておき、Equinix Edge画面で接続設定するときに使用して接続します
04.png

● Equinix Edge Create Connection to OCI

作成したOCI FastConnectと Equinix Edgeの接続設定をします

(1) Virtual Device画面
作成したEdge Device画面にある[Connections]をクリック
04_Create_Connection_OCI_Ashburn01.png

(2) I want to connect to:
Frequent Connectionsより、[Oracle Cloud]をSelectし、[Oracle CLoud Infrastructure-OCI-FastConnect]の[Create COnnection]をクリック
04_Create_Connection_OCI_Ashburn04.png

(3) Select Locations
以下設定を行い、[Next]をクリック

・Select Location: [Ashburn]を選択
・Virtual Device in Ashburn: [CSR1000v]を選択
・Destination: Ashburn]を選択

04_Create_Connection_OCI_Ashburn05.png

(4) Connection Details
以下入力し、[Next]をクリック

・Connection information: コンソール表示する名前を設定
・VIrtual Circuit OCID: 作成したFastConnectのOCIDを設定
・Connection Speed: 帯域幅を設定

04_Create_Connection_OCI_Ashburn06.png

(5) Review
設定内容を確認し、[Submit your Order]を設定
04_Create_Connection_OCI_Ashburn07.png

(6) Your order was submitted
[Go to My Inventory]をクリック
04_Create_Connection_OCI_Ashburn08.png

(7) Connect-OCI-Ashburn
以下、Primary BGP Information項目を入力し、[Accept]をクリック

・Local ASN: Equinix EdgeのASNを設定
・Local IP Address: Equinix EdgeのBGP IPを設定
・Remote ASN: OCIのASN 31898 を設定
・Remote IP address OCIのBGP IPを設定

04_Create_Connection_OCI_Ashburn11.png

04_Create_Connection_OCI_Ashburn12.png

Equinix Edge と OCI FastConnect接続確認

Equinix EdgeとOCI FastConnect が接続できたことを確認します

OCI FastConnect画面にある以下項目がグリーン色であることを確認

・ライフサイクル状態: プロビジョニング済
・BGP状態: 稼働中

01.png

■ Equinix Network Edge と AWS Direct Connect接続

● 1. AWSアカウントID確認

ECX Fabric portal画面で、AWSアカウントIDを用いてAWS接続用Connectionを作成すると、自動で AWS側に Direct Connectが作成されます。
ということでまず、AWSアカウントIDを確認します。

① AWSログイン
AWSコンソール上部にある[ユーザー名]から、[マイアカウント]をクリック
01.png

② アカウント設定画面
 アカウントID を確認し、コピーしておきます
02.png

● 2. Equinix Cloud Exchange Connection作成

(1) ECX Fabric portalログイン
ECX Fabric portalログインし、「Network Edge]画面から、[Connections]をクリック
01.png

(2) Frequent Connections画面
[AWS]を選択し、"AWS Direct Connect" の[Create Connection]をクリック
03.png

(3) Steps: Connecting to AWS画面
3つの接続手順を確認して、[Create a Connection to Amazon Web Services]をクリック
04.png

(4) Select Locations画面
以下設定をして、[Next]をクリック

・Origin欄
    - Select Location : Equinix Edgeルーターのlocationを選択
    - Cirtual Devices in Ashburn: AWSと接続するEquinix Edgeルーターを選択

・Destination欄
    - Suggested: Equinix Edgeルーターと最も近い AWSのLocaitonを選択

05.png

(5) Connection Details画面
以下設定をして、[Next]をクリック

- Connection Information: 任意の名前を設定
- AWS ACCOUNT ID: 事前に調べておいた AWS ACCOUNT IDを設定
- Connection Speed: 作成するAWS Direcconnectの帯域を設定

06.png

(6) Review画面
設定内容を確認して、[Submit your Order]をクリック
07.png

(7) Your order was submitted画面
[Go to My Inbentory] をクリック
もしくは、[Accept hosted Connection on AWS]をクリックしてもOK
08.png

(8)Important Information画面
[Accept hosted Connection]をクリック
すると、設定したAWS User IDに 設定した接続名で Direct Connect が作成されます。
09.png

● 3. AWS 仮想プライベートゲートウェイ作成

AWS VPCのゲートウェイとなる、仮想プライベートゲートウェイを作成
そして、これをDirect Connect接続するための Direct Connectゲートウェイへ関連付けます

(1) 仮想プライベートゲートウェイ画面
[AWS Direct Connect] > [仮想プライベートゲートウェイ]画面から、[仮想プライベートゲートウェイ]をクリック
01.png

(2) 仮想プライベートゲートウェイを作成する画面
以下内容を設定し、[仮想プライベートゲートウェイを作成する]をクリック

- 名前タグ:必要に応じて任意の名前を設定
- ASN: [AmazonのデフォルトASN]を選択

02.png

(3) 仮想プライベートゲートウェイを作成画面
[閉じる]をクリック
03.png

(4) 仮想プライベートゲートウェイをVPCにアタッチ
仮想プライベートゲートウェイ画面から、作成した仮想プライベートゲートウェイを選択し、[アクション]リストから、[VPCにアタッチ]をクリック05.png

(5) VPCにアタッチ画面
以下内容を設定し、[はい、アタッチします]クリック

 - VPC: 仮想プライベートゲートウェイをアタッチするVPCを選択

06.png

(6) 仮想プライベートゲートウェイ画面
 状態が ’atattching’ になればOK
07.png

● 4. AWS Direct Connect ゲートウェイ作成

(1) Direct Connect ゲートウェイ画面
[AWS Direct Connect] > [Direct Connect ゲートウェイ]画面から、[Direct Connect ゲートウェイを作成する]をクリック
01.png

(2) Direct Connect ゲートウェイを作成する画面
以下内容を設定し、[Direct Connect ゲートウェイを作成する]をクリック

- 名前:任意の名前を設定
- Amazon側のASN: デフォルトの'64512'を設定

02.png

(3) ゲートウェイの関連付け
[Direct Connect ゲートウェイ]画面から,[ゲートウェイの関連付け]タブを選択し、[ゲートウェイを関連づける]をクリック
12.png

(4) ゲートウェイを関連付ける画面
以下内容を設定し、[ゲートウェイを関連付ける]をクリック

- ゲートウェイ: 事前に作成した仮想プライベートゲートウェイを選択

13.png

● 5. AWS Direct Connect接続設定

(1) AWS Direct Connect接続画面
AWS Webコンソールにログインし、[AWS Direct Connect] > [接続]をクリック
Equinix Network Edgeの手順で[Accept hosted Connection]クリックでDirect Connectが作成されていて「状態」は'ordering'であるとを確認
作成された Direct Connect IDをクリック
01.png

(2) AWS Direct Connect画面
[承認する]をクリックして、Direct Connect接続を承諾
02.png

(3) ホスト接続を承諾する画面
[確認する]をクリックし、承諾
03.png

(4) 仮想インターフェイス画面
[仮想インターフェイスを作成する]をクリック
04.png

(5) 仮想インターフェイスを作成する画面
以下設定を行い、[仮想インターフェイスを作成する]をクリック

- 仮想インターフェイスのタイプ: Private Peerinしたいので[プライベート]を選択
- 仮想インターフェイス名: 任意の名前を設定
- 接続: Direct Connectの接続名を選択
- 仮想インターフェイスの所有者: 自分のAWSアカウントを設定
- ゲートウェイタイプ: ここでは、[Direct Connect ゲートウェイ]を選択
- Direct Connect ゲートウェイ: 事前に作成した「Direct Connect ゲートウェイ」を選択
- VLAN: 自動で裁判されたデフォルト値を使用
- BGP ASN: Equinix Network Edgeに設定した65201を使用

05.png

(6) 仮想インターフェイス画面
仮想インターフェイスが作成され、以下ステータスを確認

[状態]:  'Avalable'
[BGP ステータス] : 'down'

07.png

そして、以下自動生成されたピアリング情報をメモし、Equinix Connections設定で使用します

- BGP 認証キー
- ルーターのピアIP
- AmazonルーターのピアIP

● 6. Equinix Connections AWS接続設定

(1) Connections画面
Equinix Webコンソールにログインし、作成したAWSの Connection をクリック
02.png

(2) ConnectionーAWS画面
右下の欄にある Primary 'BGP Information' 欄にある[Edit]をクリック

(3) BGP Information欄
以下内容を設定し、[Accept]をクリック

- Local ASN: 任意のPrivate ASN(64512~65534)を設定
- Local IP Address: AWS仮想インターフェース作成で自動採番された、'ルーターのピアIP'を設定
- Remote ASN: '64512'を設定
- Remote IP address: AWS仮想インターフェース作成で自動採番された、'AmazonルーターのピアIP'を設定
- BGP Authentication key: AWS仮想インターフェース作成で自動作成された'BGP 認証キー'を設定

04.png

(4) BGP Information欄
Provisionig Statusが’PROVISIONING’から、'Active' になれば、BGP通信確立完了
06.png

● 7. AWS Direct Connect通信確立確認

(1) 仮想インターフェイス BGPステータス確認
AWSv Webコンソールにログインし、[AWS Direct Connect] > [仮想インターフェイス] > [作成した仮想インターフェイス名]をクリック
BGPステータスが 'UP' であることを確認

08マーク.png

■ AWS VPC設定

● VPC ルートテーブル設定

(1) VPC ルート伝搬設定
 VPC の[ルート伝搬]タブをクリックし、以下内容を設定

- 仮想プライベートゲートウェイ: Direct Connect利用している[仮想プライベートゲートウェイ]を選択
- 伝搬: [はい]を選択

03.png

(2) VPC ルート伝搬確認
 VPC の[ルート]タブをクリックし、以下のように対抗のルートが伝搬されていることを確認
 ここでは、OCI Ashburnリージョンと接続しているので、以下2種類のルートがOCIからAWSへ伝搬されていることを確認できる
 OCI VCNルート: 10.10.10.0/24 OCI OSNルート: Public IP Address Rangesを参照

04.png

● ネットワークACL設定

[VRTUAL PRIVATE CLOUD] > [ネットワークACL]を選択し、インバウンドルールを以下のように設定
ここでは、テスト用途でOCIのVCN 10.10.0.0/16 のプロトコルをすべて通すように設定しています
03マーク.png

● セキュリティグループ設定

[VRTUAL PRIVATE CLOUD] > [セキュリティーグループ]を選択し、インバウンドルールを以下のように設定
ここでは、テスト用途でOCIのVCN 10.10.0.0/16 のプロトコルをすべて通すように設定しています
03マーク.png

■ 疎通確認

● OCI --> AWS 疎通確認

OCI Compute Instanceから、AWS EC2インスタンスへ通信できることを確認

・ssh接続確認

[opc@ashburn-inst01 ~]$ ssh -i AWS_EC2.pem ec2-user@172.31.0.10 hostname
    ip-172-31-0−10.ec2.internal

・ping疎通確認

[opc@ashburn-inst01 ~]$ ping 172.31.0.10 -c 3
    PING 172.31.0.10 (172.31.0.10) 56(84) bytes of data.
    64 bytes from 172.31.0.10: icmp_seq=1 ttl=60 time=1.88 ms
    64 bytes from 172.31.0.10: icmp_seq=2 ttl=60 time=1.90 ms
    64 bytes from 172.31.0.10: icmp_seq=3 ttl=60 time=1.85 ms

    --- 172.31.0.10 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2004ms
    rtt min/avg/max/mdev = 1.855/1.882/1.903/0.040 ms

・tracerouteルート確認
OCI->EquinixのBGPピアIP 192.168.254.~ を通ること確認

[opc@ashburn-inst01 ~]$ traceroute 172.31.0.10
    traceroute to 172.31.0.10 (172.31.0.10), 30 hops max, 60 byte packets
    1  140.91.196.18 (140.91.196.18)  0.090 ms 140.91.196.82 (140.91.196.82)  0.073 ms 140.91.196.119 (140.91.196.119)  0.087 ms
    2  192.168.254.22 (192.168.254.22)  0.784 ms  0.777 ms  0.748 ms
    3  169.254.255.25 (169.254.255.25)  1.061 ms  1.365 ms  1.020 ms
    4  172.31.0.10 (172.31.0.10)  3.469 ms  3.375 ms  3.366 ms

・iperf3帯域確認
AWSのDirect Connect 50Mで設定してるので、50M程度出力されることを確認

[root@ashburn-inst01 opc]# iperf3 -c 172.31.0.10
    Connecting to host 172.31.0.10, port 5201
    [  4] local 10.10.0.2 port 58216 connected to 172.31.0.10 port 5201
    [ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
    [  4]   0.00-1.00   sec  7.86 MBytes  65.9 Mbits/sec  206   11.3 KBytes
    [  4]   1.00-2.00   sec  5.59 MBytes  46.9 Mbits/sec   87   15.6 KBytes
    [  4]   2.00-3.00   sec  5.90 MBytes  49.5 Mbits/sec  106   4.24 KBytes
    [  4]   3.00-4.00   sec  5.59 MBytes  46.9 Mbits/sec  117   9.90 KBytes
    [  4]   4.00-5.00   sec  5.90 MBytes  49.5 Mbits/sec  108   25.5 KBytes
    [  4]   5.00-6.00   sec  5.59 MBytes  46.9 Mbits/sec  106   15.6 KBytes
    [  4]   6.00-7.00   sec  5.90 MBytes  49.5 Mbits/sec   70   7.07 KBytes
    [  4]   7.00-8.00   sec  5.59 MBytes  46.9 Mbits/sec   76   8.48 KBytes
    [  4]   8.00-9.00   sec  5.90 MBytes  49.5 Mbits/sec   95   11.3 KBytes
    [  4]   9.00-10.00  sec  5.59 MBytes  46.9 Mbits/sec   99   14.1 KBytes
    - - - - - - - - - - - - - - - - - - - - - - - - -
    [ ID] Interval           Transfer     Bandwidth       Retr
    [  4]   0.00-10.00  sec  59.4 MBytes  49.9 Mbits/sec  1070             sender
    [  4]   0.00-10.00  sec  58.9 MBytes  49.4 Mbits/sec                  receiver

    iperf Done.

● AWS --> OCI 疎通確認

AWS EC2インスタンスから、OCI Compute Instanceへ通信できることを確認

・ssh接続確認

[ec2-user@ip-172-31-0-2 ~]$ ssh -i ~/id_rsa opc@10.10.0.2
 ashburn-inst01

・ping疎通確認

[ec2-user@ip-172-31-0-2 ~]$ ping 10.10.0.2 -c 3
    PING 10.10.0.2 (10.10.0.2) 56(84) bytes of data.
    64 bytes from 10.10.0.2: icmp_seq=1 ttl=61 time=1.90 ms
    64 bytes from 10.10.0.2: icmp_seq=2 ttl=61 time=2.05 ms
    64 bytes from 10.10.0.2: icmp_seq=3 ttl=61 time=2.07 ms

    --- 10.10.0.2 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 5ms
    rtt min/avg/max/mdev = 1.895/2.005/2.066/0.077 ms

・tracerouteルート確認
AWSのBGPピアIP 169.254.~ を通ること確認

[ec2-user@ip-172-31-0-2 ~]$ traceroute 10.10.0.2
    traceroute to 10.10.0.2 (10.10.0.2), 30 hops max, 60 byte packets
    1  169.254.255.1 (169.254.255.1)  1.096 ms  1.088 ms 169.254.255.5 (169.254.255.5)  0.374 ms
    2  169.254.255.25 (169.254.255.25)  1.157 ms  1.082 ms  1.080 ms
    3  169.254.255.26 (169.254.255.26)  1.487 ms  1.734 ms  1.715 ms
    4  140.91.196.50 (140.91.196.50)  1.672 ms 140.91.196.55 (140.91.196.55)  1.649 ms 140.91.196.123 (140.91.196.123)  1.624 ms
    5  ip-10-10-0-2.ec2.internal (10.10.0.2)  1.892 ms !X  1.835 ms !X  1.927 ms !X

・iperf3帯域確認
AWSのDirect Connect 50Mで設定してるので、50M程度出力されることを確認

[root@ip-172-31-0-2 ec2-user]# iperf3 -c 10.10.0.2
    Connecting to host 10.10.0.2, port 5201
    [  5] local 172.31.0.10 port 58610 connected to 10.10.0.2 port 5201
    [ ID] Interval           Transfer     Bitrate         Retr  Cwnd
    [  5]   0.00-1.00   sec  7.42 MBytes  62.2 Mbits/sec  129   15.6 KBytes
    [  5]   1.00-2.00   sec  5.84 MBytes  49.0 Mbits/sec   89   19.8 KBytes
    [  5]   2.00-3.00   sec  5.59 MBytes  46.9 Mbits/sec   69   12.7 KBytes
    [  5]   3.00-4.00   sec  5.72 MBytes  48.0 Mbits/sec   63   19.8 KBytes
    [  5]   4.00-5.00   sec  5.47 MBytes  45.9 Mbits/sec   88   25.5 KBytes
    [  5]   5.00-6.00   sec  6.15 MBytes  51.6 Mbits/sec   70   11.3 KBytes
    [  5]   6.00-7.00   sec  5.90 MBytes  49.5 Mbits/sec  103   9.90 KBytes
    [  5]   7.00-8.00   sec  5.41 MBytes  45.4 Mbits/sec   60   18.4 KBytes
    [  5]   8.00-9.00   sec  6.09 MBytes  51.1 Mbits/sec  112   11.3 KBytes
    [  5]   9.00-10.00  sec  5.47 MBytes  45.9 Mbits/sec   53   15.6 KBytes
    - - - - - - - - - - - - - - - - - - - - - - - - -
    [ ID] Interval           Transfer     Bitrate         Retr
    [  5]   0.00-10.00  sec  59.1 MBytes  49.5 Mbits/sec  836             sender
    [  5]   0.00-10.00  sec  58.7 MBytes  49.2 Mbits/sec                  receiver

    iperf Done.

● AWS --> OCI Object Storage疏通確認

AWS EC2インスタンスから、OCI OSNにあるObject Storageへ疏通できることを確認

・ping疏通確認

[ec2-user@ip-172-31-0-2 ~]$ ping  objectstorage.us-ashburn-1.oraclecloud.com -c 3
    PING objectstorage.us-ashburn-1.oraclecloud.com (134.70.31.247) 56(84) bytes of data.
    64 bytes from 134.70.31.247 (134.70.31.247): icmp_seq=1 ttl=61 time=2.48 ms
    64 bytes from 134.70.31.247 (134.70.31.247): icmp_seq=2 ttl=61 time=2.46 ms
    64 bytes from 134.70.31.247 (134.70.31.247): icmp_seq=3 ttl=61 time=2.41 ms

    --- objectstorage.us-ashburn-1.oraclecloud.com ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 6ms
    rtt min/avg/max/mdev = 2.414/2.450/2.487/0.071 ms

・tracerouteルート確認
AWSのBGPピアIP 169.254.~ を通ること確認

[root@ip-172-31-0-2 ec2-user]# traceroute -I objectstorage.us-ashburn-1.oraclecloud.com
    traceroute to objectstorage.us-ashburn-1.oraclecloud.com (134.70.35.189), 30 hops max, 60 byte packets
    1  169.254.255.1 (169.254.255.1)  0.672 ms  0.665 ms  0.663 ms
    2  169.254.255.25 (169.254.255.25)  1.326 ms  1.345 ms  1.369 ms
    3  169.254.255.26 (169.254.255.26)  1.836 ms  1.837 ms  1.836 ms
    4  140.91.196.123 (140.91.196.123)  1.830 ms  1.992 ms  2.004 ms
    5  134.70.35.189 (134.70.35.189)  28.091 ms  27.916 ms  27.931 ms

■ 参考

●Network Edge
 • Network Edge
 • Platform Equinix上でNetwork Edgeを活用したCisco vManage

●Network Edge 技術リソース
 • Interconnection - Network Edge
 • User Guide
 • Deploy & Config

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SSMで特定のInstanceへのRDPだけを許可する

SSMネタの第3弾です

ことの発端

社長:「〇〇のAWSアカウントの請求額、高いねんけど」

社長:「ClientVPNが一番しめてるわ」

社長:「なんとかならん?」

このClientVPNはプライベートにあるWindowsサーバへ接続するためで、利用するのはWindows上で動いてるソフトのメンテ用になってます

何かあるとき(トラブル時)しか使わないので常時ClientVPNを置いておくのはもったいないってことで、ClientVPNをやめてSSMに置き換えてみます(インフラ民はSSMでつないでるのでClientVPNは使ってません)

やりたいこと

  • RDPだけに制限したい
  • 接続先を特定のWindowsサーバだけにしたい

RDPだけに制限する

RDPのポートだけに接続するSSMドキュメントを作成する

AWS-StartPortForwardingSession はどのポートでも接続することができるため、このドキュメントをコピーして新たにRDPのポートだけのドキュメントを作成します

以下はドキュメントのコンテンツ内容です

rdp_forward_document.json
{
  "schemaVersion": "1.0",
  "description": "Document to start port forwarding session over Session Manager",
  "sessionType": "Port",
  "parameters": {
    "localPortNumber": {
      "type": "String",
      "description": "(Optional) Port number on local machine to forward traffic to. An open port is chosen at run-time if not provided",
      "allowedPattern": "^([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$",
      "default": "0"
    }
  },
  "properties": {
    "portNumber": "3389",
    "type": "LocalPortForwarding",
    "localPortNumber": "{{ localPortNumber }}"
  }
}

変更箇所は parameters から portNumber を削除して、propertiesportNumber3389 の固定にするだけです

上記のファイルで新たにドキュメントを作成

aws ssm create-document --content file://rdp_forward_document.json --name RdpForwardingSession --document-type Session --document-format JSON

IAMユーザを作成

IAMユーザはSSMだけ使えるようにしたいので「プログラムによるアクセス」で作成します

IAMユーザに作成したドキュメントを許可するポリシーを設定します

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:xxxxxxxxxxxx:document/RdpForwardingSession"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:ap-northeast-1:xxxxxxxxxxxx:session/${aws:username}-*"
        }
    ]
}

ssm:TerminateSession では自分自身のsessionだけを対象とするため、${aws:username}-* とします

試しにテストしてみます

$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX --document-name RdpForwardingSession

Starting session with SessionId: hoge-xxxxxxxxxxxxxxxxx
Port 13389 opened for sessionId hoge-xxxxxxxxxxxxxxxxx.
$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX --document-name AWS-StartPortForwardingSession --parameters 'portNumber=3389'

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/hoge is not authorized to perform: ssm:StartSession on resource: arn:aws:ssm:ap-northeast-1::document/AWS-StartPortForwardingSession

AWS-StartPortForwardingSession は AccessDenied となったので意図通りのようです

Document指定なしでも試してみます

$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX

Starting session with SessionId: hoge-xxxxxxxxxxxxxxxxx
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.

PS C:\Windows\system32>

あれ・・・? 入れる・・・

AWSのドキュメントを確認してみます
AWS CLI でセッションドキュメントのアクセス許可チェックを適用する

デフォルトでは、IAM ユーザーポリシーでセッションを開始するアクセス許可がアカウントのユーザーに付与されている場合、そのユーザーはこの SSM-SessionManagerRunShell SSM ドキュメントにアクセスできます。つまり、AWS CLI を使用して start-session コマンドを実行し、--document-name オプションでドキュメントを指定しない場合、システムは SSM-SessionManagerRunShell を使用してセッションを開始します。セッションは、ユーザーの IAM ポリシーで SSM-SessionManagerRunShell ドキュメントへのアクセスが明示的に許可されていない場合でも開始されます。

なるほど・・・

デフォルトまたは任意のセッションドキュメントへのアクセスを制限するには、ユーザーにセッションドキュメントへのアクセスが明示的に許可されているかどうかを検証する条件要素を、ユーザーの IAM ポリシーに追加できます。この条件が適用されると、ユーザーは start-session AWS CLI コマンドの --document-name オプションの値を指定する必要があります。

ってことなのでポリシーに明示的に許可されているかどうの条件を追加します

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:document/RdpForwardingSession"
            ],
            "Condition": {
                "BoolIfExists": {
                    "ssm:SessionDocumentAccessCheck": "true"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:session/${aws:username}-*"
        }
    ]
}

もう再度テスト

$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/hoge is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-XXXXXXXXXXXXXXXXX

AccessDenied となりました

これで1つ目の課題はOKになりました

接続先を特定のInstanceだけにする

2つ目の課題、接続先も制限したいのでResourceにInstanceのARNを追加します

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-XXXXXXXXXXXXXXXXX",
                "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:document/RdpForwardingSession"
            ],
            "Condition": {
                "BoolIfExists": {
                    "ssm:SessionDocumentAccessCheck": "true"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:session/${aws:username}-*"
        }
    ]
}

試しに別のInstanceへ接続

$ aws ssm start-session --target i-YYYYYYYYYYYYYYYYY

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/hoge is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-YYYYYYYYYYYYYYYYY

あとは、AccessKey/SecretAccessKey と aws cliコマンドをべた書きしたバッチファイルをメンテする人に渡せばOK

これでなんとかClientVPNを撤廃できそう

SSMネタが続いたので次回は違うものにしよう・・・

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Greengrassでエッジ推論 - ビデオからの物体検知〜MQTT通知

AWS Greengrass でエッジ推論をやってみたかったので、TensorflowのObject Detection APIで、カメラに人が写ったらMQTTトピックで通知し、映像はKinesis Video Streamsに配信するという習作と実験を行いました。

作業はほぼ環境構築に終始してしまいましたが、Greengrassを使ってクラウド側からの設定によって、エッジ側にMLモデルと処理を同期して実行する一連の流れを理解することができました。
また、Greengrassの設定の要所も確認することができました。

全体のイメージ

Untitled Diagram (3).png

  • AWS側は、Greengrassグループを作成し、エッジでTensorflow Object Detectionを行うLambda関数と、使用するモデルデータを関連付けします。また、ビデオをリモートで確認できるようにKinesis Video Streamsを準備します。
  • エッジ側は、Greengrassコアを動作させますが、今回は練習なので、GreengrassCoreはDockerで作成します。Dockerfileがここからダウンロード出来ます。
  • Lambdaの実行環境はPython3.7で、Gstreamerを使ってカメラの映像から、Tensorflowでの物体検知、Kinesis Video Streamsへの配信をパイプライン処理します。Gstreamerを使うのはビデオの処理をプラガブルに変更できるからで、ここで詳しい使い方は説明しませんが、情報は検索すればたくさん出てきます。
  • カメラは、Gstreamerで処理するので何でも良いです。私は Tapo C200というAmazonで投げ売りされていたのを使っています。このカメラだとRTSPで覗けます。ラズパイやPCのカメラならV4L2を使うと良いです。
  • 物体検知は、SSD Mobilenet ver.2のcocoデータセットのモデルをダウンロードして使用しました。検知するのは "person" だけとします。 検知できたらMQTTトピックで通知します。
  • 図にはトピックをサブスクライブしてアラームする側のデバイスを描いていますが、これは次回にします。Echoに喋らせるか、AWS Chatbotでslackに通知したりする方が良いかもしれません。

作成したものは https://github.com/sabmeua/gg_gst このリポジトリにあります。以降の設定などは、このリポジトリを使って説明します。

ちなみに、私は普段Pythonを書きません。またTensorflowも今回初めて使用しました。その点ご容赦いただき、変な用法や実装がありましたらご指摘いただけるとありがたいです。

設定手順

Greengrassコアの準備

今回はDockerなのでイメージをビルドするだけです。Gstreamerと必要なプラグイン、Tensorflowなどをインストールしています。
docker-compose でビルドします。環境によってはけっこう時間がかかるかもしれません。Dockerボリュームとして証明書や設定ファイルをマウントするようになっていますが、これはGreengrassグループ作成時に準備します。

$ docker-compose build

物体検知処理

Gstreamerのプラグインとして、イメージ中の/myplugins/python/ に実装しています。

  • /myplugins/python/object_detection.py : Tensorflowで映像からpersonを検知して、Gstreamerメタデータに入れるプラグイン
  • /myplugins/python/awsiot_notify.py : Gstreamerメタデータから検知した情報をMQTTトピック object/detection にパブリッシュするプラグイン

Greengrass環境で特別なことと言うと、モデルのファイルは別段ダウンロードの処理などを書かなくても、設定したファイルをGreengrassコアがデプロイ時に配置してくれるので、 AWS_GG_RESOURCE_PREFIX という環境変数のパスでアクセスできることです。

他には特別なことはしていません。PythonでのTensorflowの扱い方などは、こちらに詳しく解説されているので参考にしました。

Lambda関数の作成

LambdaでPython3.7の関数を作成します。アクセス権限など、その他の設定はGreengrass側の設定が採用されるので、ここでは名前以外は気にする必要はありません。

gg_15.png

作成したら、以下のようにmakeでソースをfunction.zipにまとめてアップロードして関数を更新します。この src/lambda_function.pyが関数の本体です。

$ make lambda-archive 
cd src;\
zip -r ../function.zip greengrasssdk lambda_function.py
  adding: greengrasssdk/ (stored 0%)
  adding: greengrasssdk/__init__.py (deflated 32%)
  adding: greengrasssdk/utils/ (stored 0%)
  adding: greengrasssdk/utils/__init__.py (stored 0%)
  adding: greengrasssdk/utils/testing.py (deflated 62%)
  adding: greengrasssdk/client.py (deflated 53%)
  adding: greengrasssdk/Lambda.py (deflated 65%)
  adding: greengrasssdk/SecretsManager.py (deflated 67%)
  adding: greengrasssdk/IoTDataPlane.py (deflated 74%)
  adding: greengrasssdk/stream_manager/ (stored 0%)
  adding: greengrasssdk/stream_manager/__init__.py (deflated 40%)
  adding: greengrasssdk/stream_manager/util.py (deflated 81%)
  adding: greengrasssdk/stream_manager/data/ (stored 0%)
  adding: greengrasssdk/stream_manager/data/__init__.py (deflated 90%)
  adding: greengrasssdk/stream_manager/streammanagerclient.py (deflated 79%)
  adding: greengrasssdk/stream_manager/exceptions.py (deflated 72%)
  adding: lambda_function.py (deflated 56%)

実装について簡単に説明します。
通常、Lambdaをデマンドで実行する場合は、イベント発生時に、設定したハンドラが呼び出されるわけですが、今回はGreengrassコアが起動したタイミングで実行し、制限時間でタイムアウトすることなく実行し続ける 長い存続期間の Lambda 関数というライフサイクルの設定で実行します。この場合、ハンドラとして設定されている lambda_handler(event, context) に処理を記述せず、ソースがロードされた時点で処理が開始されるように無限ループなり、スレッドを起動するように実装します。
今回は main() に処理を記述しており、ここでGstreamerのイベントループを開始しています。
lambda_function.py で行っているのはGstreamerパイプラインの起動のみです。物体検知やKVSへの送信といった処理のパイプラインは環境変数で設定できるようにしていて、Greengrass側のLambdaの実行設定の際に設定します。

src/lambda_function.py
#!/usr/bin/env python3

import os
import sys
import traceback

import gi
gi.require_version('Gst', '1.0')
gi.require_version('GLib', '2.0')
gi.require_version('GObject', '2.0')
from gi.repository import GLib, GObject, Gst

import logging
logger = logging.getLogger(__name__)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

STREAM_NAME = os.environ.get('STREAM_NAME')
IOT_CREDENTIAL_ENDPOINT = os.environ.get('IOT_CREDENTIAL_ENDPOINT')
CERT_ID = os.environ.get('CERT_ID')

KVS_CMD = f'kvssink stream-name={STREAM_NAME} framerate=15'\
          ' aws-region=ap-northeast-1'\
          ' log-config=/kvs_log_configuration'\
          ' iot-certificate="iot-certificate,'\
          f'endpoint={IOT_CREDENTIAL_ENDPOINT},'\
          f'cert-path=/greengrass/certs/{CERT_ID}.cert.pem,'\
          f'key-path=/greengrass/certs/{CERT_ID}.private.key,'\
          'ca-path=/greengrass/certs/root.ca.pem,'\
          'role-aliases=KvsCameraIoTRoleAlias"'

DEFAULT_PIPELINE = 'videotestsrc ! clockoverlay auto-resize=false'\
                   ' ! videorate ! video/x-raw,format=I420,framerate=15/1'\
                   ' ! x264enc tune=zerolatency ! h264parse'

def on_message(bus: Gst.Bus, message: Gst.Message, loop: GLib.MainLoop):
    mtype = message.type
    if mtype == Gst.MessageType.EOS:
        logger.info("End of stream")
        loop.quit()

    elif mtype == Gst.MessageType.ERROR:
        err, debug = message.parse_error()
        logger.error(err, debug)
        loop.quit()

    elif mtype == Gst.MessageType.WARNING:
        err, debug = message.parse_warning()
        logger.warning(err, debug)

    return True

def main():
    logger.info('Start gstream pipeline')

    Gst.init(sys.argv)

    cmd = os.environ.get('PIPELINE', DEFAULT_PIPELINE)
    if os.environ.get('USE_KVS', True):
        cmd += f' ! {KVS_CMD}'
    logger.info('Pipeline : %s', cmd)

    pipeline = Gst.parse_launch(cmd)
    bus = pipeline.get_bus()
    bus.add_signal_watch()
    pipeline.set_state(Gst.State.PLAYING)
    loop = GLib.MainLoop()
    bus.connect("message", on_message, loop)

    try:
        loop.run()
    except Exception:
        traceback.print_exc()
        loop.quit()

    pipeline.set_state(Gst.State.NULL)

main()

# This is a dummy handler and will not be invoked
# Instead the code above will be executed in an infinite loop for our example
def lambda_handler(event, context):
    return

GreengrassにLambda関数を設定する際、バージョンまたはエイリアスで指定する必要があるため、作成した関数のバージョンを発行して、これにエイリアス dev を設定します(キャプチャでは ver.2 になっています)。

gg_16.png

Greengrassグループの設定

Greengrassグループの作成はAWSコンソールでAWS IoTのウィザードに沿って設定するだけですが、設定にはいくつかポイントがあります。

Greengrassグループ作成で デフォルト作成を使用 を選択します。

gg_10.png

グループ名は test としました。Greengrassコアの名前は test_Core となりますが、これもそのままOKしました。もう少し気の利いた名前にした方が良かったです。

gg_11.png

証明書の配置

作成を実行すると、コア用の証明書、プライベートキー、パブリックキー、設定ファイルのtarballがダウンロードできます。これをdocker-composeのディレクトリの certsconfig に展開します。またCA 証明書も必要なので、ダウンロードして certs/root.ca.pem に保存します。
プライベートキーはLambdaを実行する ggc_user/ggc_group から参照されるので 644 にしておきます。

$ ls -l certs/ config/
certs/:
合計 16
-rw-r--r-- 1 sabmeua sabmeua 1220  7月 13 11:35 0f0c0953bd.cert.pem
-rw-r--r-- 1 sabmeua sabmeua 1679  7月 13 11:35 0f0c0953bd.private.key
-rw-r--r-- 1 sabmeua sabmeua  451  7月 13 11:35 0f0c0953bd.public.key
-rw-r--r-- 1 sabmeua sabmeua 1188  6月 22 14:03 root.ca.pem

config/:
合計 4
-rw-r--r-- 1 sabmeua sabmeua 875  7月 13 11:35 config.json

エッジ環境のローカルファイルシステムへのロギングの設定

コンソールで作成したGreengrassグループの 設定 画面から ローカルログ設定 をinfoレベルで行うようにします。これによりLambdaの出力をファイルにロギングできます。

gg_12.png

Lambda実行環境の設定

今回Lambda環境をDockerで実行するので、同じく 設定 画面にて、デフォルトの Lambda 関数コンテナ化の設定を コンテナなし に変更します。実行ユーザはデフォルトのままggc_user/ggc_groupにしておきます。

gg_13.png

Lambdaの設定

Greengrassグループの Lambda の画面で、 Lambdaの追加 -> 既存のLambdaの使用 を選択して、先ほど作成した関数 gg-gst-test を、バージョンにエイリアス dev を選択して追加します。

gg_18.png

gg_19.png

追加した関数の設定を編集します。
ここで、設定するのは2箇所です。まず、Lambdaのライフサイクルを 存続期間が長く無制限に稼働する関数にする に変更します。もう一つは、タイムアウトを 25 に変更します。無制限と言いながらタイムアウトを設定するのは変な気がしますが、ここにかかれている通り、Lambda関数の呼び出し前に20秒のスリープがあるので、その間に終了しないようにするためです。

gg_20.png

また、このLambdaの設定画面で、実行時の環境変数を設定します。

gg_25.png

キー 説明
GST_PLUGIN_PATH Gstreamerのプラグインの所在を指定する /usr/local/lib/gstreamer-1.0:/myplugins
STREAM_NAME KVSのストリーム名 test_Core
IOT_CREDENTIAL_ENDPOINT kVSにiot-credentialsでアクセスするための認証エンドポイント ※自身の環境を参照
CERT_ID 証明書、キーのファイルのbasename ※自身の環境を参照
PIPELINE Gstreamerの処理パイプライン ※後述

IOT_CREDENTIAL_ENDPOINT はaws cliでこのように確認できます。

$ aws iot describe-endpoint --endpoint-type iot:CredentialProvider
{
    "endpointAddress": "XXXXXXXXXXXXXX.credentials.iot.ap-northeast-1.amazonaws.com"
}

PIPELINE にはビデオソースにあわせたパイプラインを記述します。RTSPのカメラからの場合、私の環境では前処理など含めて以下のようになります。KVSに配信する kvssink のパイプラインは、Lambda関数内に記述しています。

rtspsrc location=rtsp://192.168.10.115:554/stream2 user-id=hogehoge user-pw=fugafuga ! rtph264_depay ! videorate ! video/x-h264,framerate=15/1 ! videoconvert ! object_detection model=models/ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb ! awsiot_notify !  videoconvert ! x264enc tune=zerolatency bframes=0 key-int-max=45 bitrate=512 ! video/x-h264,profile=baseline,stream-format=avc,alignment=au,framerate=15/1

object_detection が物体検知のプラグインで、オプションでモデルファイルを指定します。ここで指定したモデルファイルは、デプロイ時に展開されるよう後ほど設定します。
続く awsiot_notify が検知した情報を MQTT パブリッシュするプラグインです、それ以降はKVSに配信する kvssink の入力に合わせるため、h.264エンコードしています。

MLモデルリソースの設定

TensorflowのObject Detectionで使用するモデルをGreengrassに設定して、Lambda関数と関連付けます。
SageMakerのモデルを使用できるようですが、今回はこちらからダウンロードして使用します。

http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz

ダウンロードしたファイルをS3にアップロードして、Greengrassグループの リソース の画面から選択します。

gg_22.png

Lambdaの実行をコンテナなしで行うため、パーミッションを以下のように設定する必要があります。OSグループIDの 997 は ggc_group の番号です。Lambda関数gg-gst-testと関連付けて、実行時に参照できるようにします。

gg_26.png

以上の設定で、Greengrassコアのデプロイ時にエッジ側にリソースが同期されます。

MQTTトピックの設定

サブスクリプション の画面で、物体検知を通知するためのMQTTトピックの設定を行います。
ソースをLambda関数 gg-gst-test に、ターゲットを Iot Cloud にします。トピックフィルタは指定しないと、ワイルドカード指定されすべてキャプチャされるようになります。

gg_29.png

この設定により、ローカルMQTTブローカーではなく、AWS IoT側に通知できます。

Kinesis Video Streamsの設定

ストリームの作成

最後に、Kinesis Video Streams(以下 KVS)のストリームを作成します。ストリーム名は、Greengrassコアの名前と同じにする必要があります。その他はデフォルトのままで問題ありません。

gg_14.png

ストリーミング用ポリシー、ロールの作成

KVSへのストリーミングはGstreamerのkvssinkで行いますが、kvssinkはオプションで、AWSIoTの証明書を利用してKVSにアクセスすることができます。
そのために必要となるロールとポリシーの設定をこの説明に従って、以下のように実行します。

$ cat <<JSON  > iam-policy-document.json
{
    "Version":"2012-10-17",
    "Statement":[
        {
            "Effect":"Allow",
            "Principal":{
                "Service":"credentials.iot.amazonaws.com"
            },
            "Action":"sts:AssumeRole"
        }
    ]
}
JSON
$ aws iam create-role --role-name KVSCameraCertificateBasedIAMRole --assume-role-policy-document 'file://iam-policy-document.json' | tee iam-role.json
{
    "Role": {
        "Path": "/",
        "RoleName": "KVSCameraCertificateBasedIAMRole",
        "RoleId": "AROAXGR6GGYYWJPW32Z5L",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/KVSCameraCertificateBasedIAMRole",
        "CreateDate": "2020-07-13T04:50:07Z",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "credentials.iot.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }
}
$ cat <<'JSON' > iam-permission-document.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "kinesisvideo:DescribeStream",
                "kinesisvideo:PutMedia",
                "kinesisvideo:TagStream",
                "kinesisvideo:GetDataEndpoint"
            ],
            "Resource": "arn:aws:kinesisvideo:*:*:stream/${credentials-iot:ThingName}/*"
        }
    ]
}
JSON
$ aws iam put-role-policy --role-name KVSCameraCertificateBasedIAMRole --policy-name KVSCameraIAMPolicy --policy-document 'file://iam-permission-document.json'
$ aws iot create-role-alias --role-alias KvsCameraIoTRoleAlias --role-arn $(jq --raw-output '.Role.Arn' iam-role.json) --credential-duration-seconds 3600 | tee iot-role-alias.json
{
    "roleAlias": "KvsCameraIoTRoleAlias",
    "roleAliasArn": "arn:aws:iot:ap-northeast-1:XXXXXXXXXXXX:rolealias/KvsCameraIoTRoleAlias"
}
$ cat <<JSON > iot-policy-document.json
{
    "Version":"2012-10-17",
    "Statement":[
        {
            "Effect":"Allow",
            "Action":[
                "iot:Connect"
            ],
            "Resource":"$(jq --raw-output '.roleAliasArn' iot-role-alias.json)"
        },
        {
            "Effect":"Allow",
            "Action":[
                "iot:AssumeRoleWithCertificate"
            ],
            "Resource":"$(jq --raw-output '.roleAliasArn' iot-role-alias.json)"
        }
    ]
}
JSON
$ aws iot create-policy --policy-name KvsCameraIoTPolicy --policy-document 'file://iot-policy-document.json'
$ aws iot attach-policy --policy-name KvsCameraIoTPolicy --target 'arn:aws:iot:ap-northeast-1:XXXXXXXXXXXX:cert/0f0c0953bd4a248aa210a2411328e9659c9dc0bc90e64c9625c08e807c0819c0'

正しく作成出来ているかどうかは、以下のように確認できます。describe-streamが成功すれば問題ないです。

$ export IOT_GET_CREDENTIAL_ENDPOINT=$(aws iot describe-endpoint --endpoint-type iot:CredentialProvider --output text)
$ curl -v -H "x-amzn-iot-thingname:test_Core" --cert certs/0f0c0953bd.cert.pem --key certs/0f0c0953bd.private.key https://${IOT_GET_CREDENTIAL_ENDPOINT}/role-aliases/KvsCameraIoTRoleAlias/credentials --cacert certs/0f0c0953bd.cert.pem > token.json
$ AWS_ACCESS_KEY_ID=$(jq --raw-output '.credentials.accessKeyId' token.json) AWS_SECRET_ACCESS_KEY=$(jq --raw-output '.credentials.secretAccessKey' token.json) AWS_SESSION_TOKEN=$(jq --raw-output '.credentials.sessionToken' token.json) aws kinesisvideo describe-stream --stream-name test_Core

以上で準備が整いました。

動作確認

結果がわかりやすいように、カメラ画像ではなく、MP4のファイルからの物体検知で試します。
ビデオはこれを使いました。
https://www2.nhk.or.jp/archives/creative/material/view.cgi?m=D0002161315_00000
CPUだけだとけっこう処理が厳しいので、予め640x360、15fpsに落としています。

Lambdaの環境変数をこのように変更しました。ビデオをdemux, decodeして、確認ようの時刻を入れています。検出、通知処理を行い、再度h.264エンコードしています。

filesrc location=/data/D0002161315_00000_V_000_640x360_15fps.mp4 ! decodebin ! clockoverlay ! videoconvert ! object_detection model=models/ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb ! awsiot_notify ! videoconvert ! x264enc tune=zerolatency bframes=0 key-int-max=45 bitrate=512 ! video/x-h264,profile=baseline,stream-format=avc,alignment=au,framerate=15/1

まず、Greengrassコアのコンテナを docker-compose up して起動します。

$ docker-compose up
Starting aws-iot-greengrass ... done
Attaching to aws-iot-greengrass
aws-iot-greengrass | Process with pid 13 does not exist already
aws-iot-greengrass | Setting up greengrass daemon
aws-iot-greengrass | Validating hardlink/softlink protection
aws-iot-greengrass | Waiting for up to 1m10s for Daemon to start

aws-iot-greengrass | 
aws-iot-greengrass | Greengrass successfully started with PID: 13

このようにコアのプロセスが起動したら、AWS IoTのコンソールのデプロイの画面から デプロイ を選択します。上手くいけば 正常に完了しました となり、Lambda関数や、リソースがAWS側と同期され、関数が起動します。

gg_27.png

関数の出力は log/user/ap-northeast-1/XXXXXXXXXXXX/gg-gst-test.log というログファイルに出力されるので、これを tail すると、このような感じでログが流れました。

[2020-07-21T07:18:57.327Z][INFO]-lambda_function.py:53,Start gstream pipeline
[2020-07-21T07:18:58.455Z][INFO]-lambda_function.py:60,Pipeline : filesrc location=/data/D0002161315_00000_V_000_640x360_15fps.mp4 ! decodebin ! clockoverlay ! videoconvert ! object_detection model=models/ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb ! awsiot_notify !  videoconvert ! x264enc tune=zerolatency bframes=0 key-int-max=45 bitrate=512 ! video/x-h264,profile=baseline,stream-format=avc,alignment=au,framerate=15/1 ! kvssink stream-name=test_Core framerate=15 aws-region=ap-northeast-1 log-config=/kvs_log_configuration iot-certificate="iot-certificate,endpoint=c1m4lov0fgg33l.credentials.iot.ap-northeast-1.amazonaws.com,cert-path=/greengrass/certs/0f0c0953bd.cert.pem,key-path=/greengrass/certs/0f0c0953bd.private.key,ca-path=/greengrass/certs/root.ca.pem,role-aliases=KvsCameraIoTRoleAlias"

(略)

[2020-07-21T07:19:45.151Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.150 | Dummy-1 | Detect score=0.9208017587661743
[2020-07-21T07:19:45.155Z][ERROR]-__init__.py:1028,DEBUG  | greengrasssdk.IoTDataPlane | 21.07 07:19:45.151 | Dummy-1 | Publishing message on topic "object/detection" with Payload "{"bounding_box": [0, 0, 0, 0], "confidence": 0.9208017587661743, "class_name": "", "track_id": 0, "pts": "0:00:53.866666666"}"
[2020-07-21T07:19:45.208Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.207 | Dummy-1 | Detect score=0.9566311836242676
[2020-07-21T07:19:45.257Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.256 | Dummy-1 | Detect score=0.9690361022949219
[2020-07-21T07:19:45.305Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.305 | Dummy-1 | Detect score=0.952594518661499
[2020-07-21T07:19:45.353Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.353 | Dummy-1 | Detect score=0.9480921030044556
[2020-07-21T07:19:45.402Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.401 | Dummy-1 | Detect score=0.9473453164100647
[2020-07-21T07:19:45.452Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.451 | Dummy-1 | Detect score=0.9678215384483337
[2020-07-21T07:19:45.504Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:45.503 | Dummy-1 | Detect score=0.9282958507537842
[2020-07-21T07:19:46.195Z][ERROR]-__init__.py:1028,DEBUG  | root                 | 21.07 07:19:46.195 | Dummy-1 | Detect score=0.9128057956695557
[2020-07-21T07:19:46.196Z][ERROR]-__init__.py:1028,DEBUG  | greengrasssdk.IoTDataPlane | 21.07 07:19:46.195 | Dummy-1 | Publishing message on topic "object/detection" with Payload "{"bounding_box": [0, 0, 0, 0], "confidence": 0.9128057956695557, "class_name": "", "track_id": 0, "pts": "0:00:55.266666666"}"

(略)

07:19:45 に人を検知して、トピック object/detection にパブリッシュされています。MQTTテストクライアントでサブスクライブすると、このように確認できました。

gg_30.png

また、Kinesis Video Streamsで該当時刻の映像をみると、 07:19:45 に人が映っていました。

gg_31.png

実験は成功です。

まとめ

  • Greengrassでエッジ推論での物体検知の実装方法を知ることができました。
  • Lambdaの実装や設定はかなり自由度が高いことがわかりました。
  • CPUだけでは処理が厳しいことがわかったので、ラズパイでH/Wでenc/decするとか、JetsonNanoやEdgeTPUなどを導入したほうが良いと思いました。
  • Greengrassのドキュメントはけっこう充実していることがわかりました。ドキュメントを読んでいて、今回は使用しなかった コネクタ を使うと、色々できそうなことがわかりました。
  • Dockerで動かす(Lambdaをコンテナなしで実行する)ために必要な設定が多いです。コンテナで実行したほうが良いと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kubernetes CLIオンリーのデプロイ環境構築

わざわざJenkinsを立てるのはかったるい、でもデプロイユーザーは欲しい。
そんなわがままな構成です。

image.png
Github, aws ECR, k8s(not EKS)という構成。deployサーバはk8sとは別だがEC2。
②~⑥までをSkaffoldが行います。

tl;dr

deployユーザーを作成し、skaffold runできるところまでの要点をまとめています。

Deployサーバのユーザー調整

Dockerはインストールしてdeployサーバ上で動かしておいてください。
k8sは、deployサーバから管理者ユーザーからkubectlが一通り動くようにしておいてください。(deployユーザーは別途作ります)

POSIXユーザー作成

Deployサーバに普通にadduserしてください。sudo権限はいりません。今回はdeployユーザーとします。

Github DeployKey登録

レポジトリのSettingsからDeploy Keysってのを登録できます。
sshkeygen -t ed25519 とかで作ってください。

ssh 秘密鍵作成

Githubへのアクセス権限とサーバへのアクセス権限は変わってくるので別で作っておくことをお勧めします。
きょうびed25519でよいでしょう。
~/.ssh/authorized_keysに追加するのをお忘れなく。

sudoなしでdockerコマンドを実行できるように。

こちらを参考に。
https://qiita.com/DQNEO/items/da5df074c48b012152ee
deployユーザにsudo権限はつけないほうがいいので別ユーザーでdeployユーザーをdocker groupに入れてあげておくれまし。
dockerdの再起動とdockerユーザーの再ログインが必要です。

k8s ServiceAccount作成

deployユーザーでk8sのクラスタにアクセスできるようにします。
awsのセキュリティグループで、deployサーバからk8sのapiにアクセスできるようにするのを忘れないようにしましょう。デフォルトだと6443番とか?

https://qiita.com/murata-tomohide/items/a8a6d66534f55491bd01 を参考にdeployユーザーのサービスアカウントを作成します。clusterrolebindingするところでnamespaceを指定してアクセスを限定します。

aws ECR

Elastic Container Registryの略、Dockerのレジストリですね。
deployサーバからはpushが行える必要があり、k8sのクラスタからはpullができる必要があります。
EC2ロールを使うか、IAMでユーザー作るかはあなた次第。

  • ECRリポジトリ作成
  • ポリシー調整
    • 今回はRoleを付けました。ポリシーはecr:なんちゃら、既存のならばAmazonEC2ContainerRegistryなんちゃらです。

VPCエンドポイントを作成するのを忘れないようにしてください。https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/vpc-endpoints.html#ecr-vpc-endpoint-considerations
ようするに、VPCエンドポイントでECRへのアクセスは内側を通るように設定しましょう。ということです。今回はs3に加えてecrを設定しました。

k8s ECR設定

今回はEC2内のノードなので、AmazonEC2ContainerRegistryReadOnlyポリシーEC2のIAMロールに当てました。
ロール使っているのでsecretなどの設定は不要です。

github からソースを持ってくる。

githubのDeploykeysを使ってソースを落とします。
環境変数GIT_SSH_COMMANDを入れれば指定した鍵を使ってくれます。(必ずしも~/.gitconfigに書く必要はありません)

$ GIT_SSH_COMMAND='ssh -i ~/.ssh/hogehoge' git clone

Skaffoldでpushする

最初にECRのログインパスワードを取得してログインしておくのを忘れないようにしてください。ECRのpushコマンドの1個目です。(セッション切れ次第再度実行する必要があり〼。)
aws ecr get-login-password --region [region] | docker login --username AWS --password-stdin [repoURL]

マニフェストは
build.artifacts[].imageにECRのリポジトリのURLを入れます。tagはなくてもOKです。同じようにk8sのyamlも変えます。
build.local.pushをtrueにします。

https://qiita.com/murata-tomohide/items/5f40a8e6528ecf2e3794

できた

これでdeployサーバからdockerユーザーで一連のデプロイが行えるようになっているはずです。
shell等にしておけば一発ですね。(じゃあJenkinsでええやr、k8sならJenkinsXつかえy)
今回は要点だけにして、コマンド等は端折りました。(手抜き

みなさんデプロイオペレーションは自分のPCで行わないようにしましょう。お兄さんとの約束だ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Elastic File Systemのアップデートについて

EFSの自動バックアップがデフォルト設定に

Announcing automatic backups for Amazon Elastic File System

今までEFSをバックアップする場合は、AWS BackupでVaultを手動で作成する必要がありましたが、上記アップデートによりバックアップ設定を自動で作成してくれるとのこと。

EFSの作成が簡単にできる新しいコンソール

New Amazon Elastic File System console simplifies file system creation and management

今まで諸々と設定を入れなければならなかったEFSが2クリックで作成できるようになっております。
勿論、前述のバックアップもデフォルトで設定。逆に細かい設定をする場合は、カスタマイズという項目を選べばOK。
バックアップの可否も決めることが可能。

簡単作成について

まずは新しいコンソール画面。
image.png
[ファイルシステムの作成から]…
2.JPG
[作成]。
image.png
もうできた!!

自動バックアップについて

ちなみに自動バックアップの設定は以下。
image.png
増分バックアップですが、毎日取得のライフサイクル5週間なので、
費用を抑えるなら1週間とかに変えた方が良さそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSハンズオン実践メモ 〜AWS 環境のコード管理 AWS CloudFormationで Web システムを構築する〜

はじめに

AWS公式のハンズオンシリーズの中から、CloudFormationで Web システムを構築するハンズオンを実施しました。

本記事は自身のハンズオン学習メモとして投稿します。

目次

ハンズオンの目的

  • AWS環境をコードを使用して管理する方法を学習する

クラウドにおける構成管理の考え方をご理解頂くとともに、AWS CloudFormationを使用したWebシステムの環境構築について、実際に手を動かして理解を深めることができます。

20200717_ar.png

(https://aws.amazon.com/jp/aws-jp-introduction/aws-jp-webinar-hands-on/ より引用)

本編

開発環境の構築

  • 開発環境としてAWS Cloud9の環境を構築
    • マネジメントコンソールからインスタンスを作成
    • Cloud9の環境確認と設定
    • バージョン情報の確認
    • AWS CLIの補完設定
    • エディタ設定

テンプレートの実行方法+VPCの作成

  • マネジメントコンソールからVPC用スタックを作る
01_vpc.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for VPC

Resources:
  CFnVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: handson-cfn

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 1, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet2

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.2.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      Tags:
        - Key: Name
          Value: PrivateSubnet1

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.3.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [ 1, !GetAZs ]
      Tags:
        - Key: Name
          Value: PrivateSubnet2

  CFnVPCIGW:
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags:
        - Key: Name
          Value: handson-cfn

  CFnVPCIGWAttach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref CFnVPCIGW
      VpcId: !Ref CFnVPC

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref CFnVPC
      Tags:
        - Key: Name
          Value: Public Route

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: CFnVPCIGW
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref CFnVPCIGW

  PublicSubnet1Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  PublicSubnet2Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

Outputs:
  VPCID:
    Description: VPC ID
    Value: !Ref CFnVPC
    Export:
      Name: !Sub ${AWS::StackName}-VPCID

  PublicSubnet1:
    Description: PublicSubnet1
    Value: !Ref PublicSubnet1
    Export:
      Name: !Sub ${AWS::StackName}-PublicSubnet1

  PublicSubnet2:
    Description: PublicSubnet2
    Value: !Ref PublicSubnet2
    Export:
      Name: !Sub ${AWS::StackName}-PublicSubnet2

  PrivateSubnet1:
    Description: PrivateSubnet1
    Value: !Ref PrivateSubnet1
    Export:
      Name: !Sub ${AWS::StackName}-PrivateSubnet1

  PrivateSubnet2:
    Description: PrivateSubnet2
    Value: !Ref PrivateSubnet2
    Export:
      Name: !Sub ${AWS::StackName}-PrivateSubnet2

  • Cloud9でのテンプレートの編集

パラメータについては、適宜テンプレートリファレンスを参照して作成していく。

  • AWS CLIを利用してスタックの更新

validateして

aws cloudformation validate-template --template-body file://01_vpc.yml

updateする

aws cloudformation update-stack --stack-name handson-cfn --template-body file://01_vpc.yml

EC2の作成

  • 配布したテンプレートでEC2を起動

createする

aws cloudformation create-stack --stack-name handson-cfn-ec2 --template-body file://02_ec2.yml
  • 不足設定を書き加えて、テンプレートを完成させる
02_ec2.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for EC2

Parameters:
  VPCStack:
    Type: String
    Default: handson-cfn
  EC2AMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

Resources:
  EC2WebServer01:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2AMI
      InstanceType: t2.micro
      SubnetId: subnet-04c1d329aec13802b
      UserData: !Base64 |
        #! /bin/bash
        yum update -y
        amazon-linux-extras install php7.2 -y
        yum -y install mysql httpd php-mbstring php-xml

        wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
        tar zxvf /tmp/latest-ja.tar.gz -C /tmp
        cp -r /tmp/wordpress/* /var/www/html/
        touch /var/www/html/.check_alive
        chown apache:apache -R /var/www/html

        systemctl enable httpd.service
        systemctl start httpd.service
      SecurityGroupIds:
        - !Ref EC2SG
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: sg for web server
      VpcId: vpc-0dca3e5092f72557c
      SecurityGroupIngress: 
        - IpProtocol: tcp
          CidrIp: 10.0.0.0/16
          FromPort: 80
          ToPort: 80

Outputs:
  EC2WebServer01:
    Value: !Ref EC2WebServer01
    Export:
      Name: !Sub ${AWS::StackName}-EC2WebServer01

validateして

aws cloudformation validate-template --template-body file://02_ec2.yml

updateする

aws cloudformation update-stack --stack-name handson-cfn-ec2 --template-body file://02_ec2.yml

RDS, ELBの作成、スタックのライフサイクル・分割

  • RDS・ELB用スタックの作成

以下のテンプレートを作成し、上記と同様にCreate&validate&update。

03_rds.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for RDS

Parameters:
  VPCStack:
    Type: String
    Default: handson-cfn
  DBUser:
    Type: String
    Default: dbmaster
  DBPassword:
    Type: String
    Default: H&ppyHands0n
    NoEcho: true


Resources:
  DBInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      DBInstanceClass: db.t2.micro
      AllocatedStorage: "10"
      StorageType: gp2
      Engine: MySQL
      MasterUsername: !Ref DBUser
      MasterUserPassword: !Ref DBPassword
      DBName: wordpress
      BackupRetentionPeriod: 0
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref DBSecurityGroup

  DBSubnetGroup: 
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: DB Subnet Group for Private Subnet
      SubnetIds: 
        - Fn::ImportValue: !Sub ${VPCStack}-PrivateSubnet1
        - Fn::ImportValue: !Sub ${VPCStack}-PrivateSubnet2

  DBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: !Sub ${AWS::StackName}-MySQL
      VpcId:
        Fn::ImportValue: !Sub ${VPCStack}-VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: 10.0.0.0/16


Outputs:
  DBEndpoint:
    Value: !GetAtt DBInstance.Endpoint.Address
    Export:
      Name: !Sub ${AWS::StackName}-DBEndpoint

04_elb.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Hands-on template for ALB

Parameters:
  VPCStack:
    Type: String
    Default: handson-cfn
  EC2Stack:
    Type: String
    Default: handson-cfn-ec2

Resources:
  FrontLB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Ref AWS::StackName
      Subnets:
        - Fn::ImportValue: !Sub ${VPCStack}-PublicSubnet1
        - Fn::ImportValue: !Sub ${VPCStack}-PublicSubnet2
      SecurityGroups: 
        - !Ref SecurityGroupLB

  FrontLBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref FrontLB
      Port: 80
      Protocol: HTTP 
      DefaultActions: 
        - Type: forward
          TargetGroupArn: !Ref FrontLBTargetGroup

  FrontLBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-tg
      VpcId:
        Fn::ImportValue: !Sub ${VPCStack}-VPCID
      Port: 80
      Protocol: HTTP
      HealthCheckPath: /.check_alive
      Targets:
        - Id:
            Fn::ImportValue: !Sub ${EC2Stack}-EC2WebServer01

  SecurityGroupLB:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: !Ref AWS::StackName
      VpcId:
        Fn::ImportValue: !Sub ${VPCStack}-VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

Outputs:
  FrontLBEndpoint:
    Value: !GetAtt FrontLB.DNSName
    Export:
      Name: !Sub ${AWS::StackName}-Endpoint

  • 完成した構成の動作を確認

20200717_wp.png

無事にWordPressに接続成功。

スタックのライフサイクル・分割について

  • 個々のスタックは、ライフサイクルと所有者を基準に分割
  • VPC用スタックをベースとして、他スタックからクロススタック参照によって連携

作成した AWS リソースの削除

スタックを削除すると紐づくリソースが全て削除される。

おわりに

異なるスタックのリソースを参照できるクロススタック参照と、ネストの方法について調べてみた。

クロススタック参照

  • 参照される側のテンプレート
    "Outputs"の項目でExportプロパティを記述し、値をエクスポート
  • 参照する側のテンプレート
    組み込み関数Fn::ImportValueを使って、値をインポート

詳しくは以下を参照。
CloudFormationのスタック間でリソースを参照する

ネスト

以下により、上位テンプレートで下位テンプレートからの出力を受ける事が出来る。また、上位テンプレートで受けた出力は別の下位テンプレートへの入力として指定することもできる。

  • ネストするテンプレートをAmazonS3上にアップロード
  • 下位のテンプレートで渡したいパラメータをOutputsに記述
  • 上位のテンプレートでは"組み込み関数Fn::GetAttを利用する

詳しくは以下を参照。
CloudFormationでスタックをネストする

所感

スタックの作成時、テンプレートから依存関係を自動で判別し構築してくれるのは便利だと感じた。
比較対象としては他プラットフォームも含めて管理できるTerraformが挙がってくると思うので、そちらも触ってみたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[C#]AWSの使用料金をAPIで取得する

AWSの使用料金がえらいことに・・・

思いのほかAWSの使用料金が上がっていて、青ざめた経験が誰しもあるはず。
定期的に請求ページを確認すればいいのですが、休日は見てなかったりして
気づいたときは多額の請求がきていた…なんてことも。

そこで、CostExplorer APIを使っていつどのくらい使用したのかわかるようにしてみました。
※CostExplorer APIはリクエストごとに費用が発生するのでやりすぎに注意しましょう!
(記載時点で1回のリクエストにつき0.01ドル)

必要なもの

AWSのAccessKeyとSecretKey
AWSSDK.CostExplorerをインストール

コード(Console)

            string awsAccessKey = "xxxxxxxxxxx";
            string awsSecretKey = "xxxxxxxxxxxxxxxxxxxxx";

            AmazonCostExplorerClient client = new AmazonCostExplorerClient(awsAccessKey, awsSecretKey, RegionEndpoint.USEast1);
            GetCostAndUsageRequest req = new GetCostAndUsageRequest();
            req.TimePeriod = new DateInterval();
            req.TimePeriod.Start = "2020-07-01";
            req.TimePeriod.End = "2020-07-20";
            req.Granularity = Granularity.DAILY;
            req.Metrics = new List<string>() { "AMORTIZED_COST" };

            GetCostAndUsageResponse cost = client.GetCostAndUsage(req);
            for (int i = 0; i < cost.ResultsByTime.Count; i++) {
                string start = cost.ResultsByTime[i].TimePeriod.Start;
                string end = cost.ResultsByTime[i].TimePeriod.End;
                foreach (string key in cost.ResultsByTime[i].Total.Keys) {
                    decimal amount = decimal.Parse(cost.ResultsByTime[i].Total[key].Amount);
                    string unit = cost.ResultsByTime[i].Total[key].Unit;
                    Console.WriteLine(start + "~" + end + " Amount=" + amount + " Unit=" + unit);
                }
            }


結果

2020-07-01~2020-07-02 Amount=47.6436917916 Unit=USD
2020-07-02~2020-07-03 Amount=47.2799385496 Unit=USD
2020-07-03~2020-07-04 Amount=0.0012009547 Unit=USD
2020-07-04~2020-07-05 Amount=0.001352335 Unit=USD
2020-07-05~2020-07-06 Amount=0.0013171085 Unit=USD
2020-07-06~2020-07-07 Amount=0.0013171085 Unit=USD
2020-07-07~2020-07-08 Amount=0.0013171085 Unit=USD
2020-07-08~2020-07-09 Amount=0.0013171085 Unit=USD
2020-07-09~2020-07-10 Amount=0.2201781931 Unit=USD
2020-07-10~2020-07-11 Amount=48.1395790234 Unit=USD
2020-07-11~2020-07-12 Amount=48.1393261349 Unit=USD
2020-07-12~2020-07-13 Amount=48.1394533349 Unit=USD
2020-07-13~2020-07-14 Amount=6.6676519934 Unit=USD
2020-07-14~2020-07-15 Amount=0.0009903077 Unit=USD
2020-07-15~2020-07-16 Amount=0.0009903077 Unit=USD
2020-07-16~2020-07-17 Amount=0.0009903077 Unit=USD
2020-07-17~2020-07-18 Amount=0.0009903077 Unit=USD
2020-07-18~2020-07-19 Amount=0.0009903077 Unit=USD
2020-07-19~2020-07-20 Amount=0.0009903077 Unit=USD

10日~13日にやらかしてますね…
この時、DocumentDBの検証をやっていてインスタンス落とすの忘れてて
土日放置してたようです。

あとは、このレポートを定期的/閾値を超えたらメールで送ったり
メッセンジャーに送ったりすれば被害を低減できるでしょうか。

そもそも自分でちゃんと管理しろと言われそうですがね。

自分用の雑なコンソールアプリです。
閾値を超えたらメール送信するアプリ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】AWS Hands-on for Beginnersをやってみた感想

はじめに

AWS(Amazon Web Services)には、初めてAWSを触る人向けに、ハンズオン形式学ぶことができる「AWS Hands-on for Beginners」というコンテンツが提供されています。

オンプレの現場経験のない自分が、こちらを一通り学んでみた所感を書き連ねようと思います。

「AWSを触ってみたいけど、よくわからないからハンズオンで学んでみようかな...」
と考えている方の参考になれば幸いです。

行ったコンテンツ

  1. ハンズオンはじめの一歩: AWS アカウントの作り方 & IAM 基本のキ

  2. Security #1 アカウント作成後すぐやるセキュリティ対策

  3. Network 編#1 AWS 上にセキュアなプライベートネットワーク空間を作成する

  4. スケーラブルウェブサイト構築編

  5. AWS 上で静的な Web サイトを公開しよう!

注意事項

基本的には受講する前提条件はありません。

アカウント作成もコンテンツ内で丁寧に解説されています。

しかし、すでに所持しているAWSアカウントを使用する方は注意が必要です。

ハンズオン内で様々なリソースを作成するため、今あるリソースに影響が及ぶ可能性がありますので注意して進めてください。

また、一部料金が発生するコンテンツもあります。
詳しくはコンテンツ内で解説されているのでご参照下さい。

自分が進めていく中ではそんなにかかっていないので($1にも満たない)あまり気にしすぎないでもいいと思われます。

事前知識(あればいいかな程度)

  • AWSの基礎知識(簡単な単語を見てわかる程度)

    • 「リージョン」,「アベイラビリティーゾーン」,「EC2」といった基本的な用語がすごく出てきます。
      途中で少し解説があり、知らなくても問題はないですが、知っていたほうがよりわかりやすいです。
  • ネットワークやミドルウェアの知識(概要)

    • AWS上でネットワークを構築してちょっとしたシステムを動かす、みたいなことも行うので、サーバの役割やデータベースがどういうものか、IPアドレスはなんなのか等はわかっておいたほうがいいのかなと思います。
      「冗長性」や「ロードバランサ」などの単語に聞き覚えがあれば問題ないかと思います。

それぞれのコンテンツについて

ハンズオンはじめの一歩: AWS アカウントの作り方 & IAM 基本のキ

まず、AWSアカウントの作り方を学びます。

その後、AWSアカウントを何も見ずに作ると真っ先につまずくであろう「ルートユーザ」と「IAMユーザ」の違いを、実際にユーザを作成しながら理解します。

IAMユーザにはポリシーと呼ばれるアクセス権限の設定を振りあてるのですが、デフォルトで用意されているポリシーだけではなく、自分でポリシーを作ってみるということも行います。

そしてグループとロールの概念を理解し、終了となります。

こちらのコンテンツでは、AWSで作業を行うにあたって基本であるアカウント周りについてわかりやすくカバーされていたかなと感じます。

IAMという概念を理解していなければ、「リソースにアクセスできない」などでつまずいてしまうこともあるのかな?と思ったので、普段IAMユーザの方も知識として持っておいて損はないと思います。

Security #1 アカウント作成後すぐやるセキュリティ対策

こちらのコンテンツでは、上記で作成したアカウントのIAMページにある「セキュリティステータス」をオールグリーンにすることを目標に進めます。

その後、請求金額の確認方法やレポートの取り方などAWSを利用していく上での基本的な管理方法、セキュリティ上やるべきことを学びます。そして「ベストプラクティス」と呼ばれるシステムの最も良い状態を目指すためにやるべきことを確認します。

ハンズオンなので実際に手を動かしてレポートをためてみたり、予算を決定してみたりするのは非常にわかりやすく、よりセキュアに今後AWSを利用できるようになると思います。

しかし、レポートをためる演習の際は、一定期間リソースを動かしておかないとレポートがたまらないので、講師が行っているのをみて納得するしかないのかなと思います。

Network 編#1 AWS 上にセキュアなプライベートネットワーク空間を作成する

このコンテンツでは実際にVPC(Virtual Private Cloud)を作成し、AWS内にネットワーク空間を作成します。

VPC内の「PrivateSubnet」と「PublicSubnet」について理解をし、実際にルーティングを定義します。そしてVPCにインターネットゲートウェイを置き、通信を行います。

また、PrivateSubnetから「NAT ゲートウェイ」を用いたインターネットへの接続方法とその用法も学びます。

構成図を見ただけでは理解しにくい「VPC」、「サブネット」などを実際にアドレスを定義してルートテーブルを明示的に関連付けすることで、AWSのネットワークについて理解できる非常にいい内容だったなと思います。
汎用的な構成図をもとに作成していたので、自分で応用もできるような内容でした。

スケーラブルウェブサイト構築編

structure4.png

本コンテンツでは、最終目標をスケーラブルなWebサイトが構築できるまでとし、進めていきます。
なのでかなり具体的にAWSの使い方がとらえられるコンテンツかなと思います。

まず、VPCを作成し、EC2(Elastic Compute Cloud:仮想サーバを構築できるサービス)でWebサーバを作成し、WordPressをインストールします。

次に、そのWordPress用のDBをRDS(Relational Database Service:分散リレーショナルデータベースサービス)で作成します。

そして、ロードバランサを作成し、AMI(Amazon マシンイメージ)により2つ目のEC2インスタンスを起動し、先ほど作成したRDS DBインスタンスのマルチAZ化を行います。

その後、システム全体の可用性の確認と称して片方のEC2インスタンスを停止しても問題がないこと、RDSのフェイルオーバーを行っても問題がないことを確認し、終了となります。

全体を通してAWSでよく言われているマルチAZ配置がよくわかりました。片方のEC2を実際に落としてみて可用性を確認するのは楽しかったです。
どういう構成にするのかというのは大事なことなので、私のような初心者にはお勧めの内容となっていると思います。

AWS 上で静的な Web サイトを公開しよう!

structure5.png

本コンテンツではHTMLとCSSで構成された静的なWebサイトをS3(Simple Storage Service:オンラインストレージのWebサービス)のホスティング機能を用いて公開します。

Cloud9(オンラインのIDE)を用いて静的コンテンツを開発し、AWS CLI(Command Line Interface:コマンドラインからサービスを制御できるサービス)でコンテンツの最初に作るS3のバケットにファイルをアップします。

そしてS3のホスティング機能を用い、外からWebページがしっかりと表示されていることを確認します。

また、CloudFrontを用いてキャッシュさせてCDNを体験します。

その後はオプションとなるのですが、Route 53を用いて独自のドメインを取得し、S3にHTTPアクセスをしてみたり、ACMで証明書を発行し、HTTPS通信を実現します。

実務では静的なWebサイトを用いた構成はそんなにないとは思うのですが、こういった学習で抽象的になってしまうCDNを体験できるのはいいかなと思います。

ですが、CloudFrontが一つの変更を行うのに数分から数十分かかってしまい、さくさく進められないハンズオンだなという印象を受けました。

まとめ

今回、AWS Hands-on for Beginners をやってみて、非常にわかりやすいハンズオンであると感じました。
一つ一つの画面に対して丁寧に解説を行っていたり、それぞれのリソースがどういった動きをするのかを構成図からわかりやすく解説をされていて、非常に為になると思いました。

自分のようにオンプレの経験がなくともAWSやクラウドに興味があるという方にはいい内容かなと思うのでぜひご参考ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オンプレからAWSに移行した話

もう一年以上前の話ですが、エキサイトのWisteriaをAWSに移行した話を書こうと思います。

Wisteriaとは

機械学習を使ったコンテンツレコメンドのシステムです、紹介ページ

システム構成

移行前のシステム構成はこんな感じ、MLの部分が重いのでキューの仕組みを入れて処理をしていました。
wisteriaシステム構成 (1).png

移行前の課題

  • openstackが暴走する
  • cassandraが安定しない
  • zookeeperのファイルがよく壊れる
  • stormがうまく立ち上がらない
  • 色んなミドルウエアのバージョンが古い
  • 障害時にサーバーが多すぎて、原因の特定が難しい(ログが色んな場所に点在)、zabbixも入れているがそれだけでは追いきれない

会社ではDevOpsが推奨されていたので、サーバー周りもアプリケションエンジニアが見ないといけない、3ヶ月に一度は障害が起きていたのでしんどい

移行の計画

オンプレでは運用上の色んな問題があり、せっかく移行するので問題を解決しつつ、クラウドに最適化した形で運用のコストを下げたいと言う思いがありました、そこで考えたプランは3つ

  • プランA (色々最適化)

    • Front / Api / Tool / Batch はコンテナ(ECS)
    • MySQLはAurora
    • CassandraはDynamoDB
    • Kafka、StormはKinesis、Lambda
    • AutoScale、自動Deployなど
  • プランB (問題になっている部分をなるべくマネージド)

    • Front / Api / Tool / Batch はEC2
    • MySQLはAurora
    • CassandraはDynamoDB
    • Kafka、StormはMSK、EC2
    • AutoScale、自動Deployなど
  • プランC (ほぼ何もせずそのまま移行)

    • Front / Api / Tool / Batch / Cassandra / Kafka、Storm はEC2
    • MySQLはAurora
    • AutoScale、自動Deployなど

移行期間が2ヶ月と言うこともありプランAは現実的に無理、頑張ればプランBで行けるかもしれないということでプランBで行くことにしました。

メンバー構成

初期メンバーは3名+途中から1名追加になり計4名で行いました、分担は以下の通り

  • EC2を構築してアプリケーションを移行するメンバー
  • CassandraをDynamoDBに移行するメンバー
  • Kafka、Stormを移行するメンバー
  • その他は手が開いた人がやる

みんなクラウド移行はほぼ初めてだったので、実際に移行を始めるといろんな問題が発生することになった

移行

EC2編

オンプレ時代からansibleで構成管理していたので、スムーズにくと思っていたが…

  • OS古い問題
    • Ubuntu16を使っていたがサポートが2021年で切れるため、やもなくUbuntu18へアップデート

 → これにより諸々のパッケージのバージョンが上がる、フロント、APIをphpで書いていたので、アプリケーションのテスト工数が膨らむ、やもなくphpだけダウングレード

Kafka/Storm編

  • バージョンがあまりにも古い問題
    • Stormのバージョンがv0.9.xだったので、ec2でもこのバージョンをインストール、Amazon MSKと連携してみたが全くうまくいかない

 → チーム内で協議
 ①stomのバージョンをあげてMLのソースを書き直す
 ②MSKを使わず、kafkaをec2で構築する
 ③kafka/stormをやめる
 → ③を選択し、kinesis/Lambdaにすることにしました。理由は、運用で悩まされていたkafka/stormをやめたかった、将来的にStormをやめるのにソースを書き直すモチベーションがわかないなど

Cassandra編

  • Cassandraのデータ移行に悪戦苦闘
    • マイグレーションツールを使って移行をする予定で進めていたが、Macだとツールが不安定で落ちたり、ツールの使い方も分からなかった部分もあり悪戦苦闘、ようやく進んだと思ったらcassandraとツールのバージョンが合わず、そもそも使えなかったりで1ヶ月溶かす

 → 結局マイグレーションツールを使わず、cassandraのデータをダンプして、データパイプラインで入れることにしました。

移行後の構成

そんなこんなで移行後の構成はこうなりました。
wisteria移行後 (1).png
当初想定していた構成とは若干異なりますが、ec2以外はマネージドにすることが出来ました。

感想

大変な部分もありましたが比較的順調に移行が行えました。
クラウドに移行したことにより、運用で問題になっていた部分もほぼ解消し、安定した運用が行えるようになりました。
費用も半分以下に抑えることができてよかったです。

サーバー周りのインフラ的なことから解放され、アプリケーションに専念できることが一番よかったとつくづく思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS SQS】SQSを構築し、EC2からメッセージをポーリングする

目標

AWS SQSを構築しメッセージをキューに投入(AWSコンソール上から投入する)、その後投入したメッセージをAWS SDKを利用してEC2から取得(ポーリング)する。

SQSとは

アプリケーション間でやりとりされるメッセージを送受信するためのAWSが提供するキューサービスのこと。
受信側がメッセージをSQSに問い合わせてメッセージを取得するPull型のサービスです。

SQSに関する基本情報は以下記事がわかりやすくまとめられていると思いました。
Amazon SQS

前提

・AWS SDK(AWS SDK for Rubyを本記事では利用)を使用可能なEC2インスタンスが構築済みであること(※)。

※AWS SDK for Rubyを構築した際の参考手順
【AWS SDK】EC2自動構築用スクリプト(1.AWS SDK for Rubyのセットアップ)

作業の流れ

項番 タイトル
1 SQSの構築
2 SQSにメッセージを送信
3 EC2からメッセージをポーリングする

手順

1.SQSの構築

SQSコンソールを開く

キューを作成をクリック

③作成するキューの設定
今回はスタンダードキューを利用します(※1)
キューの名前は任意のものを記載します。

※1 SQSには以下2つのタイプのキューが存在します。特徴を簡単にまとめます。
スタンダードキュー
 ⇒最低1回のメッセージ配信を保証するが、リクエストのタイミングによっては同一メッセージが複数回配信される可能性がある。
 ⇒メッセージ配信の順序はベストエフォート型(メッセージ送信と配信の順序が異なる可能性がある。)

FIFOキュー
 ⇒同一メッセージが複数回配信されることはない。
 ⇒メッセージ配信の順序は先入れ先出し型(メッセージ送信と配信の順序が同一となる。)
 ⇒高性能なため、スタンダードキューよりも少し料金が高い。

tempsnip.png

キューの詳細設定(※2)をします。
今回はメッセージ受信待機時間のみ20秒に設定し、それ以外はデフォルトのままとしています。

※2 以下簡単にキューの詳細設定の内容を記載致します。
可視性タイムアウト
メッセージを受信した後に一定時間、該当のメッセージを他システムから見えなくさせる機能のこと。
可視性タイムアウトとして指定した時間内にメッセージ処理を完了させ、メッセージ削除することで、
同一メッセージに対する複数回処理を防ぐことが可能となります。

配信遅延(遅延キュー)
キューにメッセージを送信した後、指定した時間が経過した後にメッセージを表示させるようにする機能

メッセージ受信待機時間
ロングポーリングorショートポーリングの設定を行うことが出来ます。
デフォルトはショートポーリング(メッセージ受信待機時間が0秒)となっており、キューが空の場合でもすぐ再リクエストを行います。
SQSはメッセージのリクエスト回数によって料金が判断されるため、空メッセージのリクエストを繰り返すと料金がかさんでしまいます。
その対策として有効なのが、ロングポーリング(メッセージ受信待機時間が0秒より上)となります。
ロングポーリングを設定すると空メッセージを受け取った場合、指定した秒数分待機する(キュー接続を持続する)ため、
SQSのリクエスト回数を減らし料金を節約することが可能となります。
要件にもよりますが、基本的にはロングポーリングを使用するのがAWSの推奨となっているようです。

tempsnip.png

キューへのアクセス設定はデフォルトのままとします。

image.png

最後に暗号化、デッドレターキュー(※3)、タグの設定もありますが今回は設定なしとします。
キューの作成をクリック

※3 デッドレターキュー
処理結果がエラーとなったメッセージを何回かリトライ後、自動的に別のキューに移動する機能
問題のあるメッセージが別キューに集中するため識別しやすくなり、当該メッセージが正規のキューに残存することを防ぎます。

tempsnip.png

2.SQSにメッセージを送信

作成したSQSキューにAWSコンソール上からメッセージを送信してみます。

①作成したSQSキューの詳細画面からメッセージの送受信をクリック
tempsnip.png

メッセージ本文を記載後メッセージ送信をクリック
この手順を何回か繰り返します。
tempsnip.png

③送信したメッセージの内容確認
メッセージ送信欄の下段にあるメッセージ受信欄からメッセージをポーリングをクリック
tempsnip.png

メッセージが表示されればOKです

tempsnip.png

3.EC2からメッセージをポーリングする

①AWS SDK for Rubyが利用可能なEC2インスタンスにポーリング実行用スクリプトを配備

# **********************************************************************************
# <機能概要>
# 指定したSQSキューのメッセージをポーリングする
#
# <機能詳細>
# 常駐プロセスとして稼働し、指定したSQSキューのメッセージをポーリングし取得時刻及びメッセージ本文を標準出力する。
# 取得したメッセージは出力後削除する。
# メッセージが空の場合は指定した時間(wait_time_seconds)分待機した後、キューが空であることを伝えるメッセージを出力する。
#
# <スクリプト用法>
# ruby <スクリプトパス>
# **********************************************************************************

require 'aws-sdk'

# キュー名
queue_name = "MyTestQueue"

# SQS操作用インスタンス作成
sqs = Aws::SQS::Client.new

# キューURLの取得
begin
  queue_url = sqs.get_queue_url(queue_name: queue_name).queue_url
rescue Aws::SQS::Errors::NonExistentQueue
  puts "A queue named '#{queue_name}' does not exist."
  exit(false)
end

loop do
  # キューからメッセージを取得
  receive_message_result = sqs.receive_message({
    queue_url: queue_url, 
    message_attribute_names: ["All"], # 全属性のメッセージを取得
    max_number_of_messages: 5,        # 最大でも5メッセージの取得
    wait_time_seconds: 20             # メッセージが空の場合は20秒待機
  })

  # メッセージを取得した時刻を取得
  timestamp = Time.new

  # メッセージが空のときはemptyメッセージ出力
  if receive_message_result.messages.nil?
    puts "#{timestamp.strftime("%Y-%m-%d %H:%M:%S")}: Message is empty."
  end


  receive_message_result.messages.each do |message|
    # 取得したメッセージ本文を表示
    puts "#{timestamp.strftime("%Y-%m-%d %H:%M:%S")}: #{message.body}" 

    # メッセージをキューから削除
    sqs.delete_message({
      queue_url: queue_url,
      receipt_handle: message.receipt_handle    
    })
  end
end

②スクリプト実行し、メッセージをポーリングする。
AWSコンソールから事前投入した3つメッセージは即座に処理され、メッセージが空の場合は指定した時間分待機することを確認できました。
またメッセージ待機中に新規メッセージをキューにプットした場合、そのメッセージは即座に処理されることも確認できました。

[ec2-user@basehost ~]$ ruby queue.rb
2020-07-21 04:13:32: test2          # AWSコンソールから事前投入したメッセージその1
2020-07-21 04:13:33: test3          # AWSコンソールから事前投入したメッセージその2
2020-07-21 04:13:33: test1          # AWSコンソールから事前投入したメッセージその3
2020-07-21 04:13:53: Message is empty.    # wait_time_secondsで指定した時間分(20秒)待機後、emptyメッセージ出力
2020-07-21 04:14:01: This message is the message sent during the waiting time.   # メッセージ待機時間中にキューにプットしたメッセージ、プット後即座に処理された。
2020-07-21 04:14:21: Message is empty.
2020-07-21 04:14:41: Message is empty.
2020-07-21 04:15:01: Message is empty.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonではじめるAWS CDK

Pythonで始めるAWS CDK

最近までいろいろと触る機会があったのと、AWS CDK+Pythonの記事が少なかったので書いてみた。
内容はAWS CDKのインストール〜デプロイまで。ついでにLambda関係でよく使いそうなところをいくつか。

実行環境

  • Python 3.7.4
  • OSX Catalina 10.15.5

Python、Pipが実行可能な環境であることが前提で書いています。

はじめに

AWS CDKってなに?

AWS クラウド開発キットより

AWS クラウド開発キット (AWS CDK) は、使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースをモデル化およびプロビジョニングするためのオープンソースのソフトウェア開発フレームワークです。

かなり大雑把にまとめると、使い慣れたプログラミング言語でAWSのリソースを定義、作成できるIaC(Infrastructure as Code)ツールですよっと。

なんでAWS CDKなの?

環境設定をコードで管理したい。けどterraformとかに手を出すほどでもないし、template.ymlは書きたくない…。何よりAWSの公式!

なんでPythonなの?

(TypeScriptやったことないから…)
LambdaをPythonで書いていたからその延長線上でPythonでやってみた

使ってみた感想

  • 後述するが間接的にCloudFormationを使用しているので、実務で使用する場合はAWS CloudFormation の制限は一読しておくことをお勧めする。
  • リリースは頻繁にされているので日々改善されているようだが、使い込んでいくと正直、現状では痒いところに手が届かない感が否めない。少なくともクロススタック参照はなんとかして欲しい。
  • 調べて出てくるのはTypeScriptがほとんどなので、わざわざPythonを使うメリットはそこまでない

AWS CDKのインストール

Node.jsのインストール

前提として、Node.jsが必要なのでインストールされていない場合はまずこちらをインストールする
なぜNode.jsが必要かというと、AWS CDK自体はTypeScriptで書かれており、Pythonなど他の言語はjsiiというライブラリで自動的にTypeScriptに変換してくれているらしい。すごい。

AWS クラウド開発キットのよくある質問

Q: AWS CDK を使用するために JavaScript ランタイムをインストールする必要があるのはなぜですか?
AWS は、AWS Construct ライブラリパッケージのビジネスロジックを TypeScript で構築し、サポートされている各プログラミング言語へのマッピングを提供します。これにより、AWS CDK コンストラクトの動作が異なる言語間で一貫していることを確認でき、すべての言語で利用できる包括的なコンストラクトパッケージのセットを提供できます。AWS CDK プロジェクトで作成したコードはすべてお客様ご希望のプログラミング言語でネイティブになっています。JavaScript ランタイムはお客様のプログラミング経験の実施詳細です。jsii プロジェクトは https://github.com/aws/jsii で参照できます。

Node.jsのインストール自体は簡単で、Node.jsの公式からインストーラをダウンロード。あとはインストーラに従ってポチポチしていけば勝手にインストールしてくれる。

インストールが完了したらバージョンを確認

$ node -v
v12.18.2
$ npm -v
6.14.5

AWS認証情報の設定

AWS CLIがインストールされている場合はaws configure コマンドを実行すればプロンプトで設定可能だが、わざわざAWS CLIインストールしたくない場合は手動で設定する。

以下はmac or linuxの設定手順。Windowsの場合は%USERPROFILE%\.aws\config%USERPROFILE%\.aws\credentialsを作成し、認証情報、リージョンを設定する

$ mkdir ~/.aws
$ touch ~/.aws/config
$ touch ~/.aws/credentials

エディタで~/.aws/configを開き、以下の通りリージョンを設定し、保存する

[default]
region=ap-northeast-1

エディタで~/.aws/credentialsを開き、以下の通り認証情報を設定し、保存する。

[default]
aws_access_key_id={アクセスキーID}
aws_secret_access_key={シークレットアクセスキー}

AWS CDKのインストール

npmを使用してインストールする

$ sudo npm install -g aws-cdk

バージョンを表示し、インストールされていることを確認。

$ cdk --version
1.52.0 (build 5263664)

デプロイ用S3バケットの作成

cdk bootstrapコマンドでCloudFormationで使用するS3バケットを作成する
これはリージョンごとに実施する必要があるので注意。

$ cdk bootstrap
 ⏳  Bootstrapping environment aws://323617333195/ap-northeast-1...
CDKToolkit: creating CloudFormation changeset...

 ✅  Environment aws://323617333195/ap-northeast-1 bootstrapped.

CDK appを作る

プロジェクトディレクトリを作成

mkdir hello-cdk
cd hello-cdk

cdk initコマンドを実行

cdk init app --language python

実行が終わったらvirtualenvを起動する
(pip3を使用している場合はpip3に読み替えて実行すること)

source .env/bin/activate
pip install -r requirements.txt

pipenvを使用したい場合は、virtualenvを起動せずに.envディレクトリを削除して代わりにpipenvを起動する

S3バケットを作成してみる

pip install aws-cdk.aws-s3

hello_cdk_stack.pyにs3バケットを作成するコードを追加する
(bucket_nameは既存のバケット名と重複するとデプロイ時にエラーになるため、重複しないものへ書き換えること)

hello_cdk_stack.py
from aws_cdk import core
from aws_cdk import aws_s3 as _s3


class HelloCdkStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # The code that defines your stack goes here
        bucket = _s3.Bucket(self, "MyFirstBucket", bucket_name="kimi-first-cdk-bucket")

デプロイコマンドを実行

$ cdk deploy
hello-cdk: deploying...
hello-cdk: creating CloudFormation changeset...

 ✅  hello-cdk

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:323617333195:stack/hello-cdk/3497b790-ca3f-11ea-9236-0eb7b90bbf8e

デプロイ完了後にAWSマネジメントコンソールでS3バケットが作成されていることを確認できた

2020-07-20-13-31-33.png

AWSマネジメントコンソールでCloudFormationを開くとhello-cdkというスタックができている。

2020-07-20-14-43-44.png

これはAWS CDKがリソースを作成するのにCloudFormationを使用しているため。
cdk synthコマンドを実行すればCloudFormationテンプレートを出力することもできる。

$ cdk synth

また、作成したリソースを全て削除したい場合はcdk destroyコマンドでスタック単位でリソースを削除することが可能。
ただし、S3バケットなど削除されないリソースが一部あるので注意が必要。

$ cdk destroy

Lambdaをデプロイしてみる

  • 参考までに、今回作成したコードはここに置いてあります。

シンプルなLambda関数をデプロイする

新しいプロジェクトを作成。

$ mkdir cdk-lambda
$ cd cdk-lambda/
$ cdk init app --language python

せっかくなのでPipenvを使ってみる。

$ rm -rf .env
$ rm -rf source.bat
$ rm -rf requirements.txt
$ pipenv shell

必要なパッケージをインストール

$ pipenv install aws_cdk.core
$ pipenv install aws_cdk.aws_lambda

Lambda関数を置くディレクトリを作成し、Lambda関数のPythonファイルを作成

$ mkdir function
$ touch function/index.py

今回はあくまでもサンプルなので、Lambdaのコードは適当

index.py
import json

print('Loading function')


def lambda_handler(event, context):
    print("value1 = " + event['key1'])
    print("value2 = " + event['key2'])
    print("value3 = " + event['key3'])
    return event['key1']  # Echo back the first key value

cdk_lambda_stack.pyにLambda関数を作成するコードを追記

cdk_lambda_stack.py
from aws_cdk import core
from aws_cdk import aws_lambda as _lambda


class CdkLambdaStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # The code that defines your stack goes here
        lambdaFn = _lambda.Function(self, "SampleLambdaFunction", 
            code=_lambda.Code.from_asset('function/'),
            runtime=_lambda.Runtime.PYTHON_3_7,
            handler="index.lambda_handler",
            function_name="sample_lambda_function"
        )

いざ、デプロイ!

$ cdk deploy

AWSマネジメントコンソールでデプロイされていることを確認。

2020-07-20-17-50-07.png

Lambda関数に環境変数を設定する

Functionを作成する際にenvironmentパラメータを指定するか、add_environmentメソッドで設定する

cdk_lambda_stack.py
        # 環境変数を追加
        lambdaFn.add_environment(key="STAGE", value="DEV")

2020-07-20-18-15-19.png

s3トリガーを設定する

必要なライブラリをインストール

$ pipenv install aws_cdk.aws_s3
$ pipenv install aws_cdk.aws_s3_notifications

ライブラリをインポート

cdk_lambda_stack.py
from aws_cdk import (
    core,
    aws_lambda as _lambda,
    aws_s3 as _s3,
    aws_s3_notifications,
)

s3バケットを作成し、通知イベントを設定。
今のところ、この方法では既存のS3バケットには設定できない。既存のS3バケットにイベントを設定したい場合はカスタムリソースを使用する。

cdk_lambda_stack.py
        bucket = _s3.Bucket(self, "SampleBucket", bucket_name="kimi-first-cdk-bucket")
        notification = aws_s3_notifications.LambdaDestination(lambdaFn)
        bucket.add_event_notification(_s3.EventType.OBJECT_CREATED, notification, _s3.NotificationKeyFilter(prefix="hoge", suffix=".csv"))

2020-07-20-19-07-57.png

Cloudwatch Eventsから定期的に起動する

必要なライブラリをインストール

$ pipenv install aws_cdk.aws_events
$ pipenv install aws_cdk.aws_events_targets

インストールしたライブラリをインポート

cdk_lambda_stack.py
from aws_cdk import (
    core,
    aws_lambda as _lambda,
    aws_s3 as _s3,
    aws_s3_notifications,
    aws_events as _events,
    aws_events_targets as _targets
)

イベントルールを作成するコードを追加。
今回スケジュール文字列を使用して設定しているが、Schedule.cronSchedule.rateなど別の方法でも指定可能。

cdk_lambda_stack.py
        rule = _events.Rule(self, "SampleEventRule",
            rule_name="schedule_trigger_event",
            schedule=_events.Schedule.expression("cron(10 * * * ? *)")
        )
        rule.add_target(_targets.LambdaFunction(lambdaFn))

2020-07-21-11-18-19.png

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ネットワークインターフェースのでタッチで】You do not have permission to access the specified resource.が出る

原因

You do not have permissionと書かれてますが特に権限がないわけではなく、関連付けられているリソースが存在するため削除できない状態にあります。

対処法

descriptoonを参照して、関連付けられてるリソースを先に削除するとエラーが解決します。

スクリーンショット (98).png

NatゲートウェイとRDSが原因になっている場合が多いようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ネットワークインターフェースのデタッチで】You do not have permission to access the specified resource.が出る

原因

You do not have permissionと書かれてますが特に権限がないわけではなく、関連付けられているリソースが存在するため削除できない状態にあります。

対処法

descriptionを参照して、関連付けられてるリソースを先に削除するとエラーが解決します。

スクリーンショット (98).png

NatゲートウェイとRDSが原因になっている場合が多いようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Elastic BeanstalkのドメインをRoute53の独自ドメインに紐付ける方法

はじめに

Elastic Beanstalkで構築したWebアプリケーションを独自ドメインに紐付ける方法をまとめました

方法

Route 53にて、独自ドメイン(Aレコード)を選択して、エイリアスに「はい」を選択する
その後、エイリアス先にElastic Beanstalkのドメインを記入する

img1.png

参考

Route 53 でエイリアスレコードを作成する方法を教えてください。

エイリアスレコードと非エイリアスレコードの選択

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IAMユーザーを作成する

AWSでのユーザー

AWSではユーザーは2種類あります。
・ルートユーザー
・IAMユーザー

一番最初に作ったAWSアカウントは「ルートユーザー」になります。
普段の作業はIAM(アイアム)ユーザーでログインしてから作業します。
これはセキュリティを強固にするためです。
詳しくは「IAM ベストプラクティス」で検索してください。

IAMユーザーの作成手順

今回は一番最初のIAMユーザーの作成をメモしていますので、ルートユーザーでログインしています。

山田さん用のIAMを作る場合は、
・システムA用の山田さん
・システムA用の鈴木さん
・システムB用の山田さん
というイメージで作成するといいようです。

IAMのダッシュボードへ移動

1D1CB2FD-B1B5-43B5-987C-D283286241D3.png

ユーザー>ユーザーを追加

20DDCFAF-9E46-443C-9AF2-834E38CDFBCC.png

詳細設定を入力

87706353-1113-45F4-9A26-8B38E7C15AE3.png

プログラムによるアクセス
プログラムからアクセスするのに使ったりする場合にチェック入れる
AWS マネジメントコンソールへのアクセス
AWS マネジメントコンソール へのログイン、つまり人間が操作する場合にチェックを入れる
カスタムパスワード
好きなパスワードを設定できる
パスワードのリセットが必要
チェックを入れると最初にサインインした人がパスワードを再設定するようにできる。
管理者とユーザーが違う場合に使うと便利。

アクセス許可の設定

4365AB05-22F5-4567-AC08-F12B3BF60F55.png

ここでは簡単に設定するため
AWSが用意している管理者権限(基本機能が全て使える)ポリシーを選択しました。

タグの追加

23A68FBA-FC43-481C-8F35-26769C30E3E6.png

タグをつけることで絞り込みができたりします。

システム名や状態(status : terminated)、権限とかロールとか?良い方法がわかったら追記します…
一応以下の記事が参考になるかも…

確認画面

9F1619D3-34B1-4634-9987-2DC7A2A11597.png
CDBC7DC3-7AC7-4A10-AFA5-5238C8513D4C.png

閉じると、

E1B8151F-745D-4271-BB00-EF3F5764DE09.png

IAMユーザーが追加されたのを確認できます。

IAMユーザーでサインインしなおす

ルートからサインアウトして、

A0696AC4-C3F2-4306-A093-E465F9B65A18.png

IAMユーザーとしてサインインしなおします。

08BB9FF4-A14E-45F7-A7E2-178270617D5B.png

これでIAMユーザーでサインインしました。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2上のnginxとPHP7.4を連携

EC2にインストールされたnginx上で、PHP7.4を動かす方法のメモです。
Appacheと違って、少し設定が必要だったので、手順をまとめておこうと思います。
(Amazon Linux 2での設定方法です。)

  • php7.4のインストールは、こちらを参考にしてください。

nginxの設定

/etc/nginx/nginx.confにphpの設定を行います。

$sudo vi /etc/nginx/nginx.conf

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }

        location ~ \.php$ {
           fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
           fastcgi_index  index.php;
           fastcgi_param  SCRIPT_FILENAME
           $document_root$fastcgi_script_name;
           include        fastcgi_params;
      }
    }

php-fpmの設定

/etc/opt/remi/php74/php-fpm.d/www.confにnginxと連携するための設定を行います(5箇所、設定を行います)。

$sudo vi /etc/opt/remi/php74/php-fpm.d/www.conf
user = nginx
group = nginx

listen = /var/run/php-fpm/php-fpm.sock

listen.owner = nginx
listen.group = nginx

sockファイル用の設定

php-fpmでは、上記の/var/run/php-fpm/php-fpm.sockファイルを起動時に作成します。
Amazon Linux 2では、再起動すると/var/runディレクトリがリセットされてしますので、起動時に/var/run/php-fpmを自動生成するスクリプトを設定します。

$sudo vi /etc/tmpfiles.d/php-fpm-run.conf
d /var/run/php-fpm 0755 root root

php-fpmの起動

以下のコマンドで、php-fpmを起動します。

$sudo systemctl start php74-php-fpm.service

php-fpmの確認

以下のコマンドで、php-fpmの状態を確認します。
active(running)となっていれば、正常に起動しています。

$sudo systemctl status php74-php-fpm.service
 php74-php-fpm.service - The PHP FastCGI Process Manager
   Loaded: loaded (/usr/lib/systemd/system/php74-php-fpm.service; disabled; vendor preset: disabled)
   Active: active (running) since Mo 2020-07-20 21:13:23 UTC; 36min ago
 Main PID: 24651 (php-fpm)
   Status: "Processes active: 0, idle: 5, Requests: 1, slow: 0, Traffic: 0req/sec"
   CGroup: /system.slice/php74-php-fpm.service
           ├─24651 php-fpm: master process (/etc/opt/remi/php74/php-fpm.conf)
           ├─24652 php-fpm: pool www
           ├─24653 php-fpm: pool www
           ├─24654 php-fpm: pool www
           ├─24655 php-fpm: pool www
           └─24656 php-fpm: pool www

phpinfo()で、phpが動いているかを確認

phpinfoを呼び出すファイルを作成し、nginxのホームディレクトリ(/usr/share/nginx/html)に配置します。

$sudo vi info.php
<? php phpinfo(); ?>

ブラウザからアクセスし、以下の画面が表示されれば、nginx上で、phpが正常に稼働しています。

以上で、nginxとPHP7.4の連携設定は完了です。




  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2にPHP7.4をセットアップする

EC2にPHP7.4をインストールする方法のメモです。
EC2のインスタンスを立ち上げてからの手順をまとめておこうと思います。
(Amazon Linux 2 へのインストール方法です。)

インスタンスのアップデート

yum updateを実行し、インスタンスをアップデートします。

$ sudo yum update

リポジトリのアップデート

Amazon Linux 2は、デフォルトでPHP5.4がインストールされてしまうため、
PHP7.4がインストールされるように、リポジトリをアップデートします。

$sudo amazon-linux-extras install epel
$sudo yum install epel-release
$sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

PHP7.4のインストール

yum コマンドでPHP7.4をインストールします。

$ sudo yum install -y php74 php74-php php74-php-fpm

PHP7.4のリンク設定

PHP7.4が動作するように、シンボリックリンクを設定します。

$ sudo ln -s /usr/bin/php74 /usr/bin/php

インストールの確認

以下のコマンドで、インストールされたバージョンを確認します。

$ php -v
 PHP 7.4.8 (cli) (built: Jul  9 2020 08:57:23) ( NTS )
 Copyright (c) The PHP Group
 Zend Engine v3.4.0, Copyright (c) Zend Technologies

以上で、PHP7.4のセットアップは完了です。




  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2にMySQL8.Xをセットアップする

EC2にMySQL 8.X をインストールする方法のメモです。
EC2のインスタンスを立ち上げてからの手順をまとめておこうと思います。
(Amazon Linux 2 へのインストール方法です。)

インスタンスのアップデート

yum updateを実行し、インスタンスをアップデートします。

$ sudo yum update

インスタンスからMariaDB用パッケージを削除

yum コマンドで、MariaDBのパッケージを削除します。

$ sudo yum remove mariadb-libs

MySQLのリポジトリを追加し、有効化

yum コマンドで、MySQLのリポジトリを追加し、有効化します。

$ sudo yum -y install https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
$ sudo yum-config-manager enable mysql80-community

yumリポジトリの確認

MySQL8.Xのリポジトリが、”enabled=1”になっているかを確認します。

sudo cat /etc/yum.repos.d/mysql-community.repo

[mysql55-community]
name=MySQL 5.5 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.5-community/el/7/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

[mysql56-community]
name=MySQL 5.6 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.6-community/el/7/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

[mysql57-community]
name=MySQL 5.7 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

[mysql80-community]
name=MySQL 8.0 Community Server
baseurl=http://repo.mysql.com/yum/mysql-8.0-community/el/7/$basearch/
enabled=1
gpgcheck=1

MySQLのインストール

yum コマンドで、MySQLをインストールします。

$ sudo yum install mysql-community-server

インストールされたMySQLのバージョン確認

MySQLバージョンを確認します。

$ mysqld --version
 /usr/sbin/mysqld  Ver 8.0.21 for Linux on x86_64 (MySQL Community Server - GPL)

MySQLの起動と自動起動の設定

MySQLを起動し、インスタンス起動時に自動で起動するように設定します。

$ sudo systemctl start mysqld.service
$ sudo systemctl enable mysqld.service

MySQLの状態確認

MySQLの状態を確認する。

$ sudo systemctl start mysqld.service

 mysqld.service - MySQL Server
   Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
   Active: active (running) since Mo 2020-07-20 13:32:14 UTC; 3h 47min ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
 Main PID: 21998 (mysqld)
   Status: "Server is operational"
   CGroup: /system.slice/mysqld.service
           └─21998 /usr/sbin/mysqld

以上で、MySQL8.Xのセットアップは完了です。




  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】Amazon SESを利用してメールを送信する

はじめに

AWSのSES(Simple Email Service)を使用する機会があったため、復習も兼ねて、Javaプログラムでメール送信する方法をまとめたいと思います。
SMTPを使用する方法とAWS SDKを使用する方法がありますが、ひとまず、SMTPの方法を記載します。
後日、AWS SDKについても追記します。

Amazon SESについて

  • Amazon SES(Simple Email Service)はAmazonが提供するメール配信サービス
  • 導入コストが低く、小規模で安価な運用が可能
  • 毎月最初の 62,000 通のメールについては無料で送信できる(SES料金)
  • AWSの他のサービスを使用することでメールログの保存や分析が可能

事前準備(SESコンソール)

プログラムを作成する前に下記をする必要があります。

送信元(From)アドレスの認証、送信先(To)アドレスの認証

アカウントはサンドボックスと呼ばれるテスト環境に新規ユーザーとして作成されるため、確認した E メールアドレスでの E メール送受信のみができます。
未確認の E メールアドレスに E メールを送信する、1 日あたりに送信できる E メールの数を増やす、高速で E メールを送信するためには、アカウントをサンドボックスの外に移動する必要があります。

手順

  1. AWSにログインし、SESコンソールを開く
  2. 「Email Addresses」をクリック
  3. 「Verify a New Email Address」ボタンをクリック
  4. 認証するメールアドレスを入力して、「Verify This Email Address」ボタンをクリック
  5. 受信したメール内のリンクをクリック
  6. 手順2.の画面を開くと指定したメールアドレスのVerification Statusがverified(認証済み)になっている

これでメールアドレスの認証は完了です。
「Send a Test Email」ボタンをクリックでメールが送受信できるか確認できます。

SMTPユーザーを作成して認証情報(ユーザー名とパスワード)を取得する

手順

  1. AWSにログインし、SESコンソールを開く
  2. 「SMTP Settings」をクリック
  3. 「Create My SMTP Credentials」ボタンをクリック
  4. SMTPユーザーの名前を入力して「作成」ボタンをクリック(名前はデフォルト値でも可)
  5. 「ユーザーの SMTP セキュリティ認証情報を表示」をクリック
  6. 表示された認証情報をコピーして安全な場所に保存しておく or 「認証情報のダウンロード」でcsvをダウンロード

IAMコンソール > アクセス管理 > ユーザー で作成したSMTPユーザーを確認できます。

動作環境

  • eclipse Version: 2020-06 (4.16.0)
  • javaバージョン
$ java -version
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

Javaプログラム

  1. eclipseでMavenプロジェクトを作成
  2. pom.xmlにJavaMailの依存関係を追加(MvnRepositoryでJavaMailを検索して最新バージョンのjarを追加)
  3. メール送信プログラム作成
pom.xml
  <!-- 依存関係 -->
  <dependencies>
    <dependency>
      <groupId>com.sun.mail</groupId>
      <artifactId>javax.mail</artifactId>
      <version>1.6.2</version>
    </dependency>
  </dependencies>
SesSmtpSample.java
public class SesSmtpSample {

    // 送信者アドレスと送信者名(SESコンソールで認証したメールアドレス)
    static final String FROM = "sender@example.com";
    static final String FROMNAME = "Sender Name";

    // 宛先アドレス(SESコンソールで認証したメールアドレス)
    static final String TO = "recipient@example.com";

    // SESコンソールで作成したSMTPユーザー名とパスワード
    static final String SMTP_USERNAME = "smtp_username";
    static final String SMTP_PASSWORD = "smtp_password";

    // SESコンソールで作成したConfig Setを指定。メールログの保存をするときなどに使用する。今回は不要なのでコメントアウト
    // static final String CONFIGSET = "ConfigSet";

    // Amazon SES SMTPのエンドポイント(リージョンがオレゴンの場合はus-west-2)
    static final String HOST = "email-smtp.us-west-2.amazonaws.com";

    // Amazon SES SMTPのエンドポイントのポート番号.
    static final int PORT = 587;

    // メール件名
    static final String SUBJECT = "Amazon SES test (SMTP interface accessed using Java)";
    // 本文
    static final String BODY = String.join(
            System.getProperty("line.separator"),
            "<h1>Amazon SES SMTP Email Test</h1>",
            "<p>This email was sent with Amazon SES using the ",
            "<a href='https://github.com/javaee/javamail'>Javamail Package</a>",
            " for <a href='https://www.java.com'>Java</a>."
        );

    public static void main(String[] args) throws Exception {

        // SMTPサーバーを定義
        Properties props = System.getProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.port", PORT);
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.auth", "true");

        // メールセッションの確立
        Session session = Session.getDefaultInstance(props);

        // メール作成
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(FROM,FROMNAME));
        msg.setRecipient(Message.RecipientType.TO, new InternetAddress(TO));
        msg.setSubject(SUBJECT);
        msg.setContent(BODY,"text/html");

        // Configuration Setを設定。今回は使用しないのでコメントアウト
        //msg.setHeader("X-SES-CONFIGURATION-SET", CONFIGSET);

        Transport transport = session.getTransport();

        // メール送信
        try {
            System.out.println("Sending...");

            // SMTPサーバに接続
            transport.connect(HOST, SMTP_USERNAME, SMTP_PASSWORD);

            // メール送信
            transport.sendMessage(msg, msg.getAllRecipients());
            System.out.println("Email sent!");
        } catch (Exception ex) {
            System.out.println("The email was not sent.");
            System.out.println("Error message: " + ex.getMessage());
        } finally {
            // 接続終了
            transport.close();
        }
    }
}

SMTPのエンドポイントはリージョンによって変わります。
AWSのリファレンスで確認できます。

おわりに

Amazon SESの日本語ドキュメントがあるため、基本的にはドキュメント通りにやっていけば問題なくメール送信できると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む