20191115のAWSに関する記事は27件です。

【AWS/RDS】今夜勝ちたいスロークエリログ出力

AWSにapi作ったんだけど(非公開)、相当数の同時接続時に速さが足りないと言われたんで、
とりま原因究明のためにRDS(MariaDB)からスロークエリログを吐き出してCloudWatchで眺めてみた。
その軌跡です。

深奥まで知る必要はないけどとりあえず今夜勝ちたい人向けです。

環境

本記事はAWS RDS(MariaDB10.2)で試しています。
以降はこの前提で進めますがバージョン違いやMysqlでも基本同じな模様。
Auroraは若干微妙に違うので他の記事をご参照下さい。

作業工程

やることは大きく2つ。

  • パラメータグループの設定(DBエンジンのパラメータ設定)
  • ログエクスポートの設定(CloudWatchへの出力設定)

tips

RDSでは新しくデータベースを作成する際に、エンジン毎にデフォルトで用意されているパラメータ&オプショングループを選択できます。

できますが下記理由からオリジナルを作ることをお勧めします。(特にパラメータ)

  • デフォルトグループは設定値が変更不可
  • グループの付け替えは再起動が必要

パラメータグループの設定

「RDS > パラメータグループ > パラメータグループの作成」より

  1. パラメータグループを作成
  2. 作成したパラメータグループを選択
  3. パラメータの編集で以下の値を設定
    • slow_query_log = 1 (スロークエリ出力の有無)
    • long_query_time = 1 (スロークエリと判定する秒数。お好みで)
    • log_output = FILE  (ログの出力方法)

グループ作成時のパラメータグループファミリーには使用するDBエンジンを指定。
他は適当でおk。(嘘、あとでわかりやすい名前を設定して)

デフォルトではslow_query_log=0(false)なのでスロークエリは出力されません。
また、log_output = TABLEの場合、DB接続して下記でログは参照可能です。

select * from mysql.slow_log

ちなみにここで設定した値は適用タイプがdynamicなので即時適用されます。
ここがstaticのパラメータはDBの再起動が必要です。

ログエクスポートの設定

新規DB作成:「RDS > データベース > データベースの作成」より
既存DB変更:「RDS > データベース > {当該DB} > 変更」より

どちらの場合も下記を設定します。

  • DBパラメータグループ = 作成したパラメータグループ
  • ログのエクスポート > スロークエリログ = ON

新規作成の場合、追加設定というところに折り畳まれてるんで見逃してそのまま作成しないように気をつけてください。
俺はやった。

結果

これで「CloudWatch > ログ」/aws/rds/instance/{DB名}/slowquery
というログが出力されるようになるはずです。

あとはスロークエリから遅延の原因を探すだけです!やったね!
僕はクエリが原因ではなさそうと判定されたので来週も戦い続けます。

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

【AWS EC2】bundler -v -bash: bundler: コマンドが見つかりません

エラー文はこんな感じですか?

[ec2-user@ip-XXX-XX-XX-XX chat-space2]$ bundler -v
-bash: bundler: コマンドが見つかりません

bundlerが入ってない場合

まずはbundlerをいれましょう。

$ gem install bundler -v 2.0.1
$ bundle install

バージョンはご自身のものに合わせて。

rbenvが入ってない場合

$ rbenv install 2.5.1
$ rbenv global 2.5.1
$ rbenv rehash  #rehashを行う
$ ruby -v # バージョンを確認

こちらも、バージョンはご自身のものに合わせて。

なぜこんな初歩的なミスの記事を書いたか

単純なことなのですが、初めてのことで
「EC2は失敗すると1から作り直すんだぞ!変なことすると重課金になるんだぞ!」
みたいな脅しも色々聞いてきたので、焦って難しく考えすぎてエラーの解決ができませんでした。

スクールに通う身で、この解決に取り組んでいただいたメンターさんにも「ちゃんとカリキュラム読もうね!(げんなり)」というようなありがたいお言葉(涙)

こんな簡単なことでも、解決策が検索で引っかかれば、
凡ミスはもっと早く解決できると思うんです。
なので私は恥をさらす。絶対にだ!

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

Amazon Linux2 にdockerをインストールしようとしたらはまった話

Amazon Linuxはちょっと違うので備忘録。
2019/11/15日時点での対応

Amazon Linux2にDockerのインストールを試みる

ちょっと簡単に開発環境を作りたかったので、EC2上にDockerをインストールしようとしました。
DockerはいつもCentOS方式でインストールしてましたんで、ここを参照しながらインストール。

$ sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
$ sudo yum-config-manager \
 --add-repo \
 https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker

すると、依存性の関係でエラーが発生

$ sudo yum install docker-ce docker-ce-cli containerd.io
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
docker-ce-stable                                                                                                                | 3.5 kB  00:00:00     
(1/2): docker-ce-stable/x86_64/updateinfo                                                                                       |   55 B  00:00:00     
(2/2): docker-ce-stable/x86_64/primary_db                                                                                       |  37 kB  00:00:00     
依存性の解決をしています
--> トランザクションの確認を実行しています。
〜 略 〜
エラー: パッケージ: containerd.io-1.2.10-3.2.el7.x86_64 (docker-ce-stable)
             要求: container-selinux >= 2:2.74
エラー: パッケージ: 3:docker-ce-19.03.5-3.el7.x86_64 (docker-ce-stable)
             要求: container-selinux >= 2:2.74
 問題を回避するために --skip-broken を用いることができます。
 これらを試行できます: rpm -Va --nofiles --nodigest

oh。マジか。どうやらcontainer-selinuxのバージョンが合わないよ、とのこと。

助けてGoogle先生

ということで色々漁ってみた結果、CentOSのリポジトリから対象のバージョンをダウンロードするも、今度selinux-policyの依存性でエラーが。

$ sudo yum install ftp://bo.mirror.garr.it/1/slc/centos/7.1.1503/extras/x86_64/Packages/container-selinux-2.74-1.el7.noarch.rpm
〜 略 〜
エラー: パッケージ: 2:container-selinux-2.74-1.el7.noarch (/container-selinux-2.74-1.el7.noarch)
             要求: selinux-policy-base >= 3.13.1-216.el7
            インストール: selinux-policy-targeted-3.13.1-192.amzn2.6.noarch (installed)
                selinux-policy-base = 3.13.1-192.amzn2.6
            利用可能: selinux-policy-minimum-3.13.1-166.amzn2.5.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-166.amzn2.5
            利用可能: selinux-policy-minimum-3.13.1-166.amzn2.9.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-166.amzn2.9
            利用可能: selinux-policy-minimum-3.13.1-192.amzn2.6.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-192.amzn2.6
            利用可能: selinux-policy-mls-3.13.1-166.amzn2.5.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-166.amzn2.5
            利用可能: selinux-policy-mls-3.13.1-166.amzn2.9.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-166.amzn2.9
            利用可能: selinux-policy-mls-3.13.1-192.amzn2.6.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-192.amzn2.6
            利用可能: selinux-policy-targeted-3.13.1-166.amzn2.5.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-166.amzn2.5
            利用可能: selinux-policy-targeted-3.13.1-166.amzn2.9.noarch (amzn2-core)
                selinux-policy-base = 3.13.1-166.amzn2.9
エラー: パッケージ: 2:container-selinux-2.74-1.el7.noarch (/container-selinux-2.74-1.el7.noarch)
             要求: selinux-policy-targeted >= 3.13.1-216.el7
            インストール: selinux-policy-targeted-3.13.1-192.amzn2.6.noarch (installed)
                selinux-policy-targeted = 3.13.1-192.amzn2.6
            利用可能: selinux-policy-targeted-3.13.1-166.amzn2.5.noarch (amzn2-core)
                selinux-policy-targeted = 3.13.1-166.amzn2.5
            利用可能: selinux-policy-targeted-3.13.1-166.amzn2.9.noarch (amzn2-core)
                selinux-policy-targeted = 3.13.1-166.amzn2.9
エラー: パッケージ: 2:container-selinux-2.74-1.el7.noarch (/container-selinux-2.74-1.el7.noarch)
             要求: selinux-policy >= 3.13.1-216.el7
            インストール: selinux-policy-3.13.1-192.amzn2.6.noarch (installed)
                selinux-policy = 3.13.1-192.amzn2.6
            利用可能: selinux-policy-3.13.1-166.amzn2.5.noarch (amzn2-core)
                selinux-policy = 3.13.1-166.amzn2.5
            利用可能: selinux-policy-3.13.1-166.amzn2.9.noarch (amzn2-core)
                selinux-policy = 3.13.1-166.amzn2.9
 問題を回避するために --skip-broken を用いることができます。
 これらを試行できます: rpm -Va --nofiles --nodigest

めんどくせえ・・・orz

回り回って公式にたどり着いた

んで、どうしたもんかなと。
依存性の解決をいちいちやりたくないなー・・・、かといってECS使う程ガチな環境じゃないんだよなー・・・とか思ってECSのページを見ると、Amazon LinuxへのDockerインストールマニュアルが偶然見つかりました。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/docker-basics.html

$ sudo yum update -y

パッケージをアップデートして・・・

$ sudo amazon-linux-extras install docker

Extraリポジトリからdockerをインストールして・・・

$ sudo service docker start

サービスを起動

$ sudo service docker start
Redirecting to /bin/systemctl start docker.service
$ ps -ef |grep docker
root     12642     1  1 10:25 ?        00:00:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --default-ulimit nofile=1024:4096
ec2-user 12818  3438  0 10:25 pts/0    00:00:00 grep --color=auto docker

こんな簡単やったんか・・・orz

教訓

困ったら公式を読め

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

Deeplearning on AWS をやってみたの巻き

目的

Deep Learning を学ぶ

目標

よくある犬の学習をやってみる
学習させてみる
Deep Learning 周辺ツールを使ってみる

結果

sage maker を使うと学習効率がいいね!

機械学習を学びたい人にとって、環境設定やAPI 連携などの"機械学習を使うための環境構築" は、障壁になりそう。
最初は、AWS トレーニング等を通じて、1回教えてもらうと学びたいところへジャンプしやすそう。

周辺ツールとして、
コンテナ やLambda などのオンプレミスサーバ構築と比べ、高速化を実感しながら、機械学習を体験できた。
今までより、OS起動、OS設定でつまずかない分、気楽でした!

できたこと

よくある画像認識のスコアリング。今回は、ビーグル犬を判定!
キャプチャ.PNG

やったこと

サンプル画像とサンプルコードで、コンテナ作ったり、機械学習のトレーニングと推論を実際にやってみた。
CPU と GPU の応答速度の差を体験できたのも良かったかも。GPU だと3分でもCPUだと20分ぐらいかかっちゃう。
model.png

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

CloudFormation Resource Importで既存のスタックをリファクタリング!

はじめに

CloudFormation で全世界待望?のリソースインポート機能がリリースされました。

AWS CloudFormation Launches Resource Import
https://aws.amazon.com/jp/about-aws/whats-new/2019/11/aws-cloudformation-launches-resource-import/

CloudFormationで管理されていないリソースをスタックに取り込むことができるようになったわけですが、
このリソースインポート機能を活用することで既存のスタック間でリソースを移動したり
複雑化したスタックのリファクタリングを行ったりすることもできます。

スタック分割を例に考える

既存のスタックを分割しようとする場合、概ね以下のような流れとなります。

  1. ソーステンプレート内の分割対象のリソースに対し DeletionPolicy: Retain を指定し、適用する
  2. 分割対象のリソースをソーステンプレートから削除し、ターゲットテンプレートに追加する
  3. 既存スタックを更新して分割対象のリソースを削除(管理対象外)にする
  4. ターゲットテンプレートを使用して、管理対象外となった分割対象リソースをインポートする

ソーステンプレート: 既存スタックのテンプレート
ターゲットテンプレート: 分割する新しいスタックのテンプレート

やってみる

1つのテンプレートでVPCとEC2を一緒に管理していて、それぞれを別スタックに分割する例を考えてみます。
検証目的であるため、ここでは以下のようなシンプルなテンプレートで試してみます。

source.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description:
  split stack test

Parameters:
  pLatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
  pInstanceType:
    Type: String
    Default: 't3.micro'

Resources:
# Create VPC
  rMyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
      - Key: Name
        Value: test-vpc

# Create Public RouteTable
  rPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref rMyVPC
      Tags:
      - Key: Name
        Value: test-pubric-route

# Create Public Subnet 
  rPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref rMyVPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select [ 0, !GetAZs "" ]
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: test-public-subnet
  rRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref rPublicSubnet
      RouteTableId: !Ref rPublicRouteTable

# Create InternetGateway
  rInternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
      - Key: Name
        Value: test-igw
  rAttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref rMyVPC
      InternetGatewayId: !Ref rInternetGateway

# Route for InternetGateway
  rRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref rPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref rInternetGateway

# Create EC2 Secuirty Group
  rSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref rMyVPC
      GroupDescription: Security Group for EC2 Instance
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          FromPort: 443
          IpProtocol: tcp
          ToPort: 443
      Tags:
        - Key: Name
          Value: test-sg

# Create EC2 Instance
  rMyEC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref pLatestAmiId
      InstanceType: !Ref pInstanceType
      SecurityGroupIds:
        - !Ref rSecurityGroup
      SubnetId: !Ref rPublicSubnet
      Tags:
        - Key: Name
          Value: test-ec2

以降、前述の手順1~4に沿って順番にやっていきます。

手順1

EC2およびセキュリティグループを別スタックに分割するため、ソーステンプレートで
DeletionPolicy: Retain を追加します。
既存のスタックで既に設定されている場合はスキップ可能な作業です。

  rSecurityGroup:
    Type: AWS::EC2::SecurityGroup
+   DeletionPolicy: Retain
    Properties:

  rMyEC2:
    Type: AWS::EC2::Instance
+   DeletionPolicy: Retain
    Properties:

編集後のソーステンプレート
source.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description:
  split stack test add Deletion Policy

Parameters:
  pLatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
  pInstanceType:
    Type: String
    Default: 't3.micro'

Resources:
# Create VPC
  rMyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
      - Key: Name
        Value: test-vpc

# Create Public RouteTable
  rPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref rMyVPC
      Tags:
      - Key: Name
        Value: test-pubric-route

# Create Public Subnet 
  rPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref rMyVPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select [ 0, !GetAZs "" ]
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: test-public-subnet
  rRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref rPublicSubnet
      RouteTableId: !Ref rPublicRouteTable

# Create InternetGateway
  rInternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
      - Key: Name
        Value: test-igw
  rAttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref rMyVPC
      InternetGatewayId: !Ref rInternetGateway

# Route for InternetGateway
  rRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref rPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref rInternetGateway

# Create EC2 Secuirty Group
  rSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    DeletionPolicy: Retain
    Properties:
      VpcId: !Ref rMyVPC
      GroupDescription: Security Group for EC2 Instance
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          FromPort: 443
          IpProtocol: tcp
          ToPort: 443
      Tags:
        - Key: Name
          Value: test-sg

# Create EC2 Instance
  rMyEC2:
    Type: AWS::EC2::Instance
    DeletionPolicy: Retain
    Properties:
      ImageId: !Ref pLatestAmiId
      InstanceType: !Ref pInstanceType
      SecurityGroupIds:
        - !Ref rSecurityGroup
      SubnetId: !Ref rPublicSubnet
      Tags:
        - Key: Name
          Value: test-ec2

※クリックで展開

編集後のソーステンプレートを既存スタックに反映させます。
Change Set上は、変更なしでアップデートが完了します。
image.png

手順2

セキュリティグループおよびEC2インスタンスに関連する記述をソーステンプレートから削除します。
また分割先のスタックからVPCやSubnetの情報を参照できるように、リソースIDをExportしておきます。

- Parameters:
-  pLatestAmiId:
-    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
-    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
-  pInstanceType:
-    Type: String
-    Default: 't3.micro'
-
-  rSecurityGroup:
-    Type: AWS::EC2::SecurityGroup
-    DeletionPolicy: Retain
-    Properties:
-      VpcId: !Ref rMyVPC
-      GroupDescription: Security Group for EC2 Instance
-      SecurityGroupIngress:
-        - CidrIp: 0.0.0.0/0
-          FromPort: 443
-          IpProtocol: tcp
-          ToPort: 443
-      Tags:
-        - Key: Name
-          Value: test-sg
-
-  rMyEC2:
-    Type: AWS::EC2::Instance
-    DeletionPolicy: Retain
-    Properties:
-      ImageId: !Ref pLatestAmiId
-      InstanceType: !Ref pInstanceType
-      SecurityGroupIds:
-        - !Ref rSecurityGroup
-      SubnetId: !Ref rPublicSubnet
-      Tags:
-        - Key: Name
-          Value: test-ec2
+
+Outputs:
+  rMyVPC:
+    Value: !Ref rMyVPC
+    Export
+     Name: rMyVPC
+  rPublicSubnet:
+    Value: !Ref rPublicSubnet
+   Export:
+     Name: rPublicSubnet

編集後のソーステンプレート
source.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description:
  split stack test add vpc only

Resources:
# Create VPC
  rMyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
      - Key: Name
        Value: test-vpc

# Create Public RouteTable
  rPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref rMyVPC
      Tags:
      - Key: Name
        Value: test-pubric-route

# Create Public Subnet 
  rPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref rMyVPC
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: !Select [ 0, !GetAZs "" ]
      MapPublicIpOnLaunch: true
      Tags:
      - Key: Name
        Value: test-public-subnet
  rRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref rPublicSubnet
      RouteTableId: !Ref rPublicRouteTable

# Create InternetGateway
  rInternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
      - Key: Name
        Value: test-igw
  rAttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref rMyVPC
      InternetGatewayId: !Ref rInternetGateway

# Route for InternetGateway
  rRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref rPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref rInternetGateway

Outputs:
  rMyVPC:
    Value: !Ref rMyVPC
    Export:
      Name: rMyVPC
  rPublicSubnet:
    Value: !Ref rPublicSubnet
    Export:
      Name: rPublicSubnet

※クリックで展開

分割するセキュリティグループとEC2に関する記載はターゲットテンプレートとして新規作成します。
VPCIDおよびSubnetIDはクロススタック参照させています。
インポートするリソースにもテンプレートで DeletionPolicy 属性を指定する必要があります。

target.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description:
  split stack test ec2 only

Parameters:
  pLatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
  pInstanceType:
    Type: String
    Default: 't3.micro'

Resources:
# Create EC2 Secuirty Group
  rSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    DeletionPolicy: Retain
    Properties:
      VpcId: !ImportValue rMyVPC
      GroupDescription: Security Group for EC2 Instance
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          FromPort: 443
          IpProtocol: tcp
          ToPort: 443
      Tags:
        - Key: Name
          Value: test-sg

# Create EC2 Instance
  rMyEC2:
    Type: AWS::EC2::Instance
    DeletionPolicy: Retain
    Properties:
      ImageId: !Ref pLatestAmiId
      InstanceType: !Ref pInstanceType
      SecurityGroupIds:
        - !Ref rSecurityGroup
      SubnetId: !ImportValue rPublicSubnet
      Tags:
        - Key: Name
          Value: test-ec2

手順3

更新したソーステンプレートを使用して、既存のスタックを Update します。
image.png

DeletionPolicyにより、リソース自体の削除はスキップされますが、
この時点でセキュリティグループおよび、EC2は既存スタックの管理対象外となります。
image.png

手順4

ターゲットテンプレートを使用して、管理対象外となったEC2とセキュリティグループをインポートします。
スタックの作成で 既存のリソースを使用 を選択します。
image.png

テンプレートの指定で、ターゲットテンプレートをアップロードして次に進みます。
image.png

リソースを識別で、インポート対象のEC2とセキュリティグループのリソースIDを指定します。
image.png

スタックの詳細を指定で、新たに作成するスタックの名前を設定します。
image.png

対象のリソースを確認して、インポートを実行します。
image.png

インポートが完了し、既存のEC2とセキュリティグループが新しいスタックの
管理対象となっていることを確認します。
image.png

無事、VPCとEC2のスタックに分割することができました!
image.png

まとめ

移動するリソースに削除ポリシーを追加し、ソーススタックから削除&ターゲットスタックに
インポートすることでスタック間でのリソースの移動を簡単に行うことができます。
だだし、現地時点でインポートに対応しているリソースは限られているのでご注意ください。

Resources that Support Import Operations
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html
Moving Resources Between Stacks
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/refactor-stacks.html

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

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

AWSソリューションアーキテクトアソシエイトに不合格だったので勉強法をまとめます

本記事の結論

・始めて試験を受けるのであればどんな問題がでるか確認のためAWS模擬試験を受けてみる。
・模擬試験を参考にどの教材があっているか決める

教材

・Udemy-これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座(初心者向け21時間完全コース)
・AWS模擬試験

学習方法

・通勤時間を使って毎日Udemyの模擬試験を10問解く
Udemyではハンズオンもありましたがハンズオンを行わずに模擬試験のみを2か月した。
Udemyの模擬試験が90%程とれるようになったのでAWSの模擬試験を受ける

・AWS模擬試験を受けたがスコアが思うようにならなかった。
Udemyの模擬試験とAWSの模擬試験にレベルの違いがあった。
↓AWS模擬試験の結果

総合スコア: 52%
トピックレベルスコア:
1.0  Design Resilient Architectures: 44%
2.0  Define Performant Architectures: 71%
3.0  Specify Secure Applications and Architectures: 33%
4.0  Design Cost-Optimized Architectures: 50%
5.0  Define Operationally-Excellent Architectures: 100%

本試験

AWS模擬試験が52%で不安だったがとりあえず本試験を受けてみた。
結果は不合格

受験者スコア: 612 結果: 不合格

合格に必要な最低スコアは 720 なので半分ぐらいはとれたイメージ

やっておけばよかったと思ったこと

勉強をどのようにすればよいかわからないのでとりあえずUdemyをやったが問題を覚えて解けるようになったという感じです。
問題を解くだけでは試験は厳しいと感じました。
始めて試験を受けるのであればどんな問題がでるか確認のためAWS模擬試験を受けてみてテストのイメージを掴んでいたほうが良いと思います。
AWS模擬試験に似たような問題が教材にあればそれを中心に勉強をすれば良いですね。

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

AWS Elastic Beanstalkで定期処理実行(cron)

Heroku Schedulerでできたことを、Elastic Beanstalkでも行いたい

運用しているRailsアプリの本番環境を、HerokuからElastic Beanstalkに移行させました。
その際タスクの定期処理を行う方法で少し苦労したので、メモ的にシェアしておきます。

以前はHeroku Schedulerでrakeコマンドを呼び出せましたが、EBでそれを行うにはEC2内でcronを使うか、ワーカー環境でPOSTメソッドを呼び出す方法があるみたいです。今回は安く済ませたいので、前者を選びました。
後者の方法はこちらが参考になります。
https://qiita.com/tomoeine/items/38a9b2123e3afa1d5cd0

方法

このファイルを.ebextensionsに置くだけでOKです。あとはeb deployすれば、自動でcronのファイルを作成し自動実行してくれます。
eb sshで/etc/cron.dを確認してみてください。

.ebextensions/cron-sample.config
files: # filesは、EC2上にファイルを作成する。パスはアプリのルートではなくLinuxのルート。
    "/etc/cron.d/hello_world":
        mode: "000644"
        owner: root
        group: root
        content: |
            0 4 * * * root /usr/local/bin/hello_world.sh
       # 指定した頻度で、↓で作成するシェルスクリプトを実行。これは毎日午前4時

    "/usr/local/bin/hello_world.sh":
        mode: "000755"
        owner: root
        group: root
        content: |
            #!/bin/bash

            . /opt/elasticbeanstalk/support/envvars # アプリの環境変数読み込み
            cd /var/app/current

            /opt/rubies/ruby-2.5.7/bin/bundle exec /opt/rubies/ruby-2.5.7/bin/rake hello_world >> /var/log/hello_world.log 2>&1
            # rakeコマンド打つ。例ではhello_worldというrakeメソッドをすでに定義しているとする。
            # ログも出力。

            exit 0

commands:
    remove_old_cron:
        command: "rm -f /etc/cron.d/hello_world.bak" # バックアップを削除

参考

Linux サーバーでのソフトウェアのカスタマイズ
https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/customize-containers-ec2.html#linux-files

Ruby on Railsの環境構築をElastic Beanstalkで行う
https://qiita.com/yuyasat/items/4d93b4ad4f86a6e13d50#cron%E3%81%A7rails-runner%E3%82%84rake%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99

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

各種 PaaS FaaS 実行環境 (プログラミング言語とバージョン) まとめ (2019年11月15日現在)

概要

  • 各種 PaaS FaaS で使える実行環境 (プログラミング言語とそのバージョン) をまとめる
  • 公式ドキュメントも資料によってバージョン番号が明示されていなかったり異なっていることがあるので細かいバージョンについての正確さは期待できない

各種 PaaS で使える実行環境 (プログラミング言語とそのバージョン)

AWS Elastic Beanstalk Azure App Service Google App Engine
.NET Core .NET Core 2.2.7, 3.0.0 .NET Core 1.0, 1.1, 2.0, 2.1, 2.2 .NET Core
Go Go 1.13 なし Go 1.9, 1.10, 1.11, 1.12, 1.13
Java Java 7, 8 Java 8 Java 8, 11
Node.js Node.js 4, 5, 6, 8 Node.js 4, 6, 8, 9, 10 Node.js 10, 12
PHP PHP 7.2.19 PHP 5.6, 7.0, 7.2, 7.3 PHP 5.5, 5.6, 7.0, 7.1, 7.2
Ruby Ruby 2.4, 2.5, 2.6 Ruby 2.3, 2.4, 2.5, 2.6 Ruby 2.5
Python Python 3.6.8 Python 2.7, 3.6, 3.7 Python 2.7, 3.6, 3.7

参考資料:

各種 FaaS で使える実行環境 (プログラミング言語とそのバージョン)

AWS Lambda Azure Functions Google Cloud Functions Firebase Functions
.NET Core .NET Core 2.1 .NET Core 2.2, 3.x なし なし
Go Go 1.x なし Go 1.11 なし
Java Java 8 Java 8 なし なし
Node.js Node.js 8, 10 Node.js 6, 8, 10 Node.js 8, 10 Node.js 8, 10
PHP なし なし なし なし
Ruby Ruby 2.5 なし なし なし
Python Python 2.7, 3.6, 3.7 Python 3.7 Python 3.7 なし

参考資料:

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

AWS CDKのDynamoデプロイ時に、Cannot update GSI's properties other than Provisioned Throughput. You can create a new GSI with a different name のエラーが出たときのメモ

事象

DynamoDBをCloudFormationでデプロイしているが、GSIの設定をアップデートするときに、以下のエラーが発生した。

Cannot update GSI's properties other than Provisioned Throughput. You can create a new GSI with a different name

解決策

「更新」はできないので、「削除」してから「作成」する。

    new dynamoDb.CfnTable(scope, '[tableName]', {
        tableName: tableName,
        attributeDefinitions: [...],
        keySchema: [],
        billingMode: ...,
        tags: ...,
    });

GSI設定を除いたものをデプロイする。

    new dynamoDb.CfnTable(scope, '[tableName]', {
        tableName: tableName,
        attributeDefinitions: [...],
        keySchema: [],
        globalSecondaryIndexes: [],
        billingMode: ...,
        tags: ...,
    });

GSI設定を含めたものをデプロイする。

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

Laravel 6.x(またはLumen 6.x)でDynamoDBに接続する

SDKのインストール

AWS SDK for PHPをLaravelに取り込みましょう。
プロジェクトルートで下記コマンドを実行します。

$ composer require aws/aws-sdk-php

これでvendor配下にSDKがインストールされます。

DynamoDBに接続する

AWS SDK for PHPでつなぐにはこのようにします。

下記の例ではクレデンシャルを含めていますが、この方法は推奨されていないので注意してください
ハードコードされた認証情報の使用

use Aws\Sdk;

$awsSdk = new Sdk([
    'credentials' => [
        'key' => アクセスキー,
        'secret' => シークレットキー,
    ],
    'endpoint' => 'エンドポイント',
    'region'   => 'リージョン',
    'version'  => 'バージョン'
]);
$dynamo = $awsSdk->createDynamoDb();

以上。

Laravelらしくしよう

と、まあこれじゃあちょっとLaravelらしくないよね、ってことでDIしてみます。new禁止。

envに登録

dotEnv便利ですよね。

.env
AWS_DYNAMO_KEY=アクセスキー
AWS_DYNAMO_SECRET=シークレットキー
AWS_DYNAMO_ENDPOINT=エンドポイント
AWS_DYNAMO_REGION=リージョン
AWS_DYNAMO_VERION=バージョン

ServiceProviderを記述

LaravelでDIといえばServiceProviderですよね。

app/Providers/DynamoDbServiceProvider.php
<?php
declare(strict_types=1);

namespace App\Providers;

use Aws\DynamoDb\DynamoDbClient;
use Aws\Sdk;
use Illuminate\Support\ServiceProvider;

class DynamoServiceProvider extends ServiceProvider
{
    /**
     * Dynamo AWS-SDK接続
     */
    public function register()
    {
        $this->app->singleton(DynamoDbClient::class, function () {
            $awsSdk = new Sdk([
                'credentials' => [
                    'key' => env('AWS_DYNAMO_KEY'),
                    'secret' => env('AWS_DYNAMO_SECRET'),
                ],
                'endpoint' => env('AWS_DYNAMO_ENDPOINT'),
                'region' => env('AWS_DYNAMO_REGION'),
                'version' => env('AWS_DYNAMO_VERSION')
            ]);
            return $awsSdk->createDynamoDb();
        });
    }
}

Laravelなら config/app.phpのproviders に登録。

config/app.php
    'providers' => [
                App\Providers\DynamoServiceProvider::class,

Lumenならbootstrap/app.phpに登録します。

bootstrap/app.php
    $app->register(App\Providers\DynamoServiceProvider::class);

コンストラクタインジェクション

ここまでできたら、お好きな場所でインジェクションできます。Facadeにしてもいいかもですね。

    /**
     * @var DynamoDbClient
     */
    private $client;

    /**
     * constructor.
     * @param DynamoDbClient $client
     */
    public function __construct(DynamoDbClient $client)
    {
        $this->client = $client;
    }

Eloquentっぽくしよう

Dynamoにアクセスするのに毎回

$this->client->putItem($paramertes);

とかって書くのめんどいですよね。
普段使い慣れているEloquentやQueryBuilderっぽく、find()とかsave()とかdestory()とか使いたいです。

じゃあ、EloqentModelに変わる基底クラスを作っちゃいましょう。

DynamoModelを作る

たとえばこんな感じでしょうか。

<?php
declare(strict_types=1);

namespace App\Models\Dynamo;

use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Exception\DynamoDbException;
use Aws\DynamoDb\Marshaler;
use Aws\Result;
use Illuminate\Support\Str;

/**
 * Class DynamoModel
 * @package App\Models\Dynamo
 */
class DynamoModel
{
    /**
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * @var string|null
     */
    protected $table = null;

    /**
     * @var DynamoDbClient
     */
    private $client;

    /**
     * @var Marshaler
     */
    private $marshaler;

    /**
     * DynamoModel constructor.
     * @param DynamoDbClient $client
     * @param Marshaler $marshaler
     */
    public function __construct(DynamoDbClient $client, Marshaler $marshaler)
    {
        $this->client = $client;
        $this->marshaler = $marshaler;
    }

    /**
     * @return string
     */
    public function getTable(): string
    {
        $tableName = $this->table ?? Str::snake(Str::pluralStudly(class_basename($this)));
        return env('APP_ENV', 'local') . '_' . $tableName;
    }

    /**
     * DynamoにINSERT
     * @param array $attributes
     * @return Result
     */
    public function save(array $attributes): Result
    {
        $marshalItem = $this->marshaler->marshalItem($attributes);
        $dynamoParameters = [
            'TableName' => $this->getTable(),
            'Item' => $marshalItem
        ];

        try {
            return $this->client->putItem($dynamoParameters);
        } catch (DynamoDbException $exception) {
            throw $exception;
        }
    }

    /**
     * Dynamoからレコード削除
     * @param array $ids
     */
    public function destroy(array $ids): void
    {
        foreach ($ids as $id) {
            $deleteParameter = [$this->primaryKey => $id];
            $marshalItem = $this->marshaler->marshalItem($deleteParameter);
            $dynamoParameters = [
                'TableName' => $this->getTable(),
                'Key' => $marshalItem
            ];
            try {
                $this->client->deleteItem($dynamoParameters);
            } catch (DynamoDbException $exception) {
                throw $exception;
            }
        }
    }

    /**
     * Dynamoから1件レコードを取得
     * @param int $id
     * @return array|null
     */
    public function find(int $id): ?array
    {
        $idParameter = [$this->primaryKey => $id];
        $marshalItem = $this->marshaler->marshalItem($idParameter);
        $dynamoParameters = [
            'TableName' => $this->getTable(),
            'Key' => $marshalItem
        ];
        try {
            $awsResult = $this->client->getItem($dynamoParameters)->get('Item');
            if (is_null($awsResult)) {
                return null;
            }
            return $this->marshaler->unmarshalItem($awsResult);
        } catch (DynamoDbException $exception) {
            throw $exception;
        }
    }
}

あとはこれを継承したModelをつかえば、EloquentみたいにDynamoにアクセスできるようになります。
必要に応じで、findOrFail()でもupdateOrCreate()でもはやしていけばいいのかなーと思います。

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

ElasticBeanstalkにアップロードするファイルを.gitignoreとは別に.ebignoreで指定する

ElasticBeanstalkでは

eb deploy [環境名]

とすると実行したディレクトリの.gitignoreを元にプロダクトコードがzipされS3にアップロードされてebの環境に上がります。

CircleCIなどのCIツールを使っていると.gitignoreで除外したい部分とアップロードしたい部分がずれてきて別々になってきます。

そういったときは.ebignoreというファイルを使って指定することができます。

記法は.gitignoreと同じです。

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

Session Manager で SSH/SCP をトンネリングして EC2 接続

今更ながらこれやってみる

セッションマネージャーが SSH と SCP のトンネリングサポートを開始

これができることで

セキュリティグループで SSH 22番ポートを開ける必要がなくなり、踏み台も不要、 EIP も不要になるので、コスト削減につながりますよね。

前提

今回の手順は Client: Mac, Server: Amazon Linux 2 で実施する場合。

Client

Server

  • ssm agent 2.3.672.0以上
  • AmazonEC2RoleforSSM の IAM Role がインスタンスにアタッチされていること

Client の設定

awscli

適宜、前提バージョンの awscli をインストール/アップデートする。

Session Manager Plugin

バージョン確認

$ session-manager-plugin --version

インストール/アップデート

curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
unzip sessionmanager-bundle.zip
sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin 

(オプション) AWS CLI 用の Session Manager Plugin をインストールする

SSH ファイルの設定

適当に名前つけて設定入れる。

~/.ssh/config
Host matsu-test
      Hostname i-XXXXXXXXXXXX
      User ec2-user
      IdentityFile ~/.ssh/matsu-test.pem
      ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

Server の設定

SSM エージェントをインストールする

SSM エージェント プリインストールされた AMI

SSM エージェント は、次の Amazon マシンイメージ (AMI) にデフォルトで事前インストールされています。とのこと。

  • 2016 年 11 月以降に公開された Windows Server 2003-2012 R2 AMI
  • Windows Server 2016 および 2019
  • Amazon Linux
  • Amazon Linux 2
  • Ubuntu Server 16.04
  • Ubuntu Server 18.04

SSM エージェント の使用

手動でインストールするなら

ssm-agent がインストール可能な下記の Linux OS

  • Amazon Linux および Amazon Linux 2
  • Ubuntu Server
  • Red Hat Enterprise Linux (RHEL)
  • CentOS
  • SUSE Linux Enterprise Server (SLES) 12
  • Raspbian

Amazon EC2 Linux インスタンスに SSM エージェント を手動でインストールする

SSM エージェントをアップデートする

バージョン確認

$ rpm -qi amazon-ssm-agent

起動時にインターネットにアクセスできるインスタンスなら

userdata に入れちゃう。

userdata
#!/bin/bash
yum update -y
yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm

手動でアップデートするならこれだけ。

$ yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm

セキュリティグループ

SSH 22番ポートを開けなくて良い。

SSH してみる

$ ssh matsu-test
The authenticity of host 'i-06a06ac4cebcc5301 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:3ZAr+OF/DACbhOWrOUxzjmwM/OFOhTQHUwxAAwVbDaI.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'i-06a06ac4cebcc5301' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-4-2-202 ~]$

SCP でファイル転送

普通にできる。

$ scp test.txt matsu-test:~/
test.txt                                                                                                                    100%    0     0.0KB/s   00:00

$ ssh matsu-test
Last login: Fri Nov 15 02:40:28 2019 from localhost

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/

$ ll
合計 0
-rw-r--r-- 1 ec2-user ec2-user 0 11月 15 02:46 test.txt

PostgreSQL にポートフォワード

RDS PostgreSQL に対してポートフォワードし、ローカル環境から PostgreSQL に接続してみる。

RDS エンドポイントの 5432 ポートを ローカルの 9999 ポートに向ける。

$ ssh matsu-test -L 9999:matsuki-postgre.XXXXXXXXX.ap-northeast-1.rds.amazonaws.com:5432

ローカル環境から PostgreSQL に接続してみる。

$ psql -h 127.0.0.1 -p 9999 -U postgres
Password for user postgres:
psql (11.5)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=>

OK

Private なインスタンスの場合

Private Subnet に配置されたインスタンスはインターネット接続できないため、 Session Manager を利用できないが、 NAT GW を作成することで、インターネット向けの通信を通すことができるのでこれでいける。

NAT GWがなくても VPC エンドポイントがあればいける

VPC の設定確認

VPC の DNS 解決 および DNS ホスト名 を有効化する。

VPC エンドポイントを作成

以下を作成。

  • com.amazonaws.ap-northeast-1.ssm
  • com.amazonaws.ap-northeast-1.ssmmessages
  • com.amazonaws.ap-northeast-1.ec2messages

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

インスタンスから VPC エンドポイントの SG への https (443) アクセスを許可する。
※VPC エンドポイントの SG をインスタンスと同じ SG で作成していれば、該当 SG 自身を全て許可するように設定すれば良い。

ステップ 6: (オプション) プライベートクラウドエンドポントの作成

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

ELB切り替え時の動作確認 (hostsファイルを編集する)

これはなんですか

  • DNSのレコード登録が済んでないALBに対してリスナールールの動作確認をした時のメモです
  • hostファイルを編集して名前解決する方法です。

先にまとめ

  • hostsファイルはローカルで名前解決するためのもの
    • macOSのhostsファイルは /private/etc/hosts にある
    • Windowsの場合は C:\Windows\System32\drivers\etc\hosts にある
  • DNSレコード登録前にLBの動作確認がしたければ、hostsファイルにLBのIPと対象のHostを書いて名前解決の動作をエミュレートすればよい
    • HTTP接続するだけなら curl -H 'Host:example.com' 'http://xxx................' とした方が楽だが、HTTPS接続がしたければhostsを使う必要がある

hosts ファイルとは

ローカルで名前を解決してくれるやつです。以下ざっくり説明です。
https://wa3.i-3-i.info/word16969.html

mac環境のhostsファイルの置き場所は

/private/etc/hosts にあります。

Windows環境のhostsファイルの置き場所は

C:\Windows\System32\drivers\etc\hosts にあります。
http://onocom.net/blog/windows-hosts-file/#hostsWindows

やってみた

ALB の準備

  • (主にACMで) 証明書発行
  • ALB作成
    • ALBデフォルトのDNS名(Aレコード)を控えておく
  • ターゲットグループ作成&配下のターゲットへの紐づけ
  • リスナールール 設定
    • 証明書をリスナーに紐づけておく

デフォルトのDNS Nameにクエリする

ALBのIPを確認する

$ dig example-xxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com +short
54.250.192.xx
52.196.88.yyy

hosts ファイルを編集する

$ cat /private/etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost

### (略)

### ALBのIPを登録
52.196.88.yyy example.com

curl する

IPがALBに向いていることを確認する

$ curl -I https://example.com/healthcheck.html -v
*   Trying 52.196.88.yyy...
* TCP_NODELAY set
* Connected to emd-dev.watashi-move.com (52.196.88.yyy) port 443 (#0)
...
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Mon, 07 Oct 2019 06:23:58 GMT
Date: Mon, 07 Oct 2019 06:23:58 GMT
...

ALBログを確認する

ログが残っていればok。

余談: digはシステムのDNSリゾルバを使わない

hostsを編集した後digしてもhostsに書いた内容が反映されなかったので調べたらこんな記事が。
https://qiita.com/kakipo/items/71f091ecd1fa27fde2ed

dig, nslookup, そして host コマンドは /etc/hosts を参照しない。

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

aws cloud9 ruby on rails migration 後にしたこと

まずは bundle install

mysql が動かない。

username: <%=ENV['C9_USER']%>
#  password: <%=ENV['C9_USER']%> 

もともとこんな感じになってたので、設定変更。
これで、 rails db:create, db:migrate ... ok

rails server を動かす IP とか若干違った

before
rails server -b $IP -p $PORT
after
rails s -b 0.0.0.0

app が error 出す

ruby の version が 変わってた。gem とか 大幅アップデートとか嫌だったので、昔のsetupでいく方針 (live app だったので)
lvm listで状況確認して
rvm install 2.3した (live 環境 2.3.x だったので)
rvm default 2.3.X
この状態で も一度 rails s -b 0.0.0.0 ...ok

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

AWS DynamoDBをjsでいじるときに非同期処理で詰まった話

AWS DynamoDBをjsでいじるときに非同期処理で詰まった話

初めに

このお話は、単に私がJavaScriptの非同期処理を理解していなかっただけなので、プロの皆さんでしたら余裕で回避できる問題だと思います。

私みたいな初学者で、同じようなことに詰まっている方がこの記事を見て解決していただけたら幸いです。

私がやりたかったこと

DynamoDBのCRUD操作を関数内で行い、実行結果を戻り値として受け取る といったことをしたかったのですが、戻り値に値が入っていない状態で返されてしまいました。

// 問題のあるコード
function getHoge(){
    let hoge;

    let params = {
        TableName: 'table',
        Key:{
            "id": "01"
        }
    };

    documentClient.put(params, function(err, data){
        if(err){
            console.log(err);
        }else {
            console.log(data); // {}
            hoge = data;
        }
    });

    return hoge;
}

exports.handler = function (event) {
    let hoge = getHoge();
    console.log(hoge); // undifind
}

PHPのようなノリで書いてしまったため、undifindの原因がわからずに悩みまくってましたが、ようやっと非同期処理のせいだ!って気づいたため、以下のように書き直しました

書き直した

// ちゃんと動くやつ
async function getUser() {
    let hoge;

    let params = {
        TableName: 'table',
        Key:{
            "id": "01"
        }
    };

    try {
        // hogeにデータが入るまで待つ
        hoge = await documentClient.get(params).promise();
    } catch (e) {
        console.log(e);
    }

    return hoge;
}

exports.handler = async (event) => {
    let hoge = await getUser();
    console.log(hoge); // {jsonデータが見れる!}
};

はい、解決。

公式ドキュメントを見てみると、、、

DynamoDB DocumentClient get-propaty

var params = {
  TableName : 'Table',
  Key: {
    HashKey: 'hashkey'
  }
};

var documentClient = new AWS.DynamoDB.DocumentClient();

documentClient.get(params, function(err, data) {
  if (err) console.log(err);
  else console.log(data);
});

コールバックで書かれてるので、JS初心者には自力での解決キツイっす。。。

最後に言いたいこと

非同期むづかしい

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

別のAWSアカウントのSNSに通知を飛ばす方法

背景・やりたいこと

何かある度に(例えば、ヘルスチェックのエラーだったり、負荷が高くなったり、オートスケールが走ったり)
管理用のメールアドレスに通知を飛ばしたかったりする。
もちろんそういう場合はCloudWatchなどで監視して、SNSに通知を飛ばし、そこからメールを飛ばす構成が一般である。

SNSにアクセスして、新しくトピックを作成し、更にそこに新しいサブスクリプションを追加し、
送信したいメールアドレスを追加して、認証メールを受け取って承認すれば、
このトピックあての通知は設定したメールに通知されるようになる。
後はCloudWatchのアラームが上がったときなどにこのトピックあてに通してあげればよいわけだ。
構成1.png

しかし、アカウントがひとつの場合は、ここまでの設定でやりたいことが解決するのだが
AWSアカウントを様々な事情で複数持っていると、特に2桁を超えてくると厄介である。
例えばメール通知したいアドレスが変わったり、増えたりしたら、すべてのアカウントに入って
その都度設定しなおさなければならない。
しかも受け取り側に、認証メールが何十通も届くことになる。
構成2.png

そこで、1つのAWSアカウントを代表アカウントとして、
その中で構築したSNSに対して、他のアカウントの通知を送り付けたくなる。
構成3.png

ということで、そんなことができるよう設定してみる。

設定方法

トピックをAWSマネジメントコンソールにて新規作成する場合、
トピックお作成 > アクセスポリシー - オプション
より

設定項目 選択する値
メソッドの選択 基本
トピックにメッセージを発行できるユーザーを定義します 全員
このトピックにサブスクライブできるユーザーを定義します トピック所有者のみ

とすることで、他のAWSアカウントから通知を受け取れるようになる。

既存のトピックを共有したい場合は、アクセスポリシーを直接いじることになる。
初期のなにもいじっていないポリシーの場合

"Statement":[ {"Sid": "__default_statement_ID", "Effect": "Allow", ... }, ]

という値が入っているはずなので、このリストに以下の内容を追加する。

{
    "Sid": "__console_pub_0",
    "Effect": "Allow",
    "Principal": { "AWS": "*" },
    "Action": "SNS:Publish",
    "Resource": "arn:aws:sns:<<使用リージョン名>>:<<AWSアカウントID>>:<<SNS名>>"
}

※<<使用リージョン名>> <<AWSアカウントID>> <<SNS名>>は適宜値を入れ替えてください


セキュリティ的にすべてのAWSアカウントより通知を受け取るのがまずいという場合は

{ "AWS": "*" }

{ "AWS": [ "111111111111", "22222222222222", ... , "777777777777" ] }

の様に書き換えると、リストに指定したAWSアカウント以外からの通知をはじくようになる。

後は他のAWSアカウントで、上記で設定したSNSを通知する先に設定してあげればよい。
設定する場合はARNを用いることになるので、確認をしておこう。

まとめ

想像以上に簡単に設定できました。
boto3でCloudwatchのアラームを自動生成する(後日執筆予定)時に指定するSNSとしても最適ですね。

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

AWS ACM で com.amazon.coral.service.InternalFailure エラーが出る

ACM で証明書のリクエストを行ったら、com.amazon.coral.service.InternalFailure エラーが表示された。

実行していた IAM ユーザは、AdministratorAccess ポリシーと下記のように NotIpAddress による IP 制限ポリシーをアタッチしていた。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_aws_deny-ip.html

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "xxx.xxx.xxx.xxx/32"
                    ]
                }
            }
        }
    ]
}

一旦、この IP 制限をかけていたポリシーを外すと正常に動作した。
ACM 設定が終わってから、IP 制限を戻す予定。

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

AWS IoT エラー AMQJS0008I Socket closed

AMQJS0008I Socket closed

PCからAWS IoTに向けて、メッセージを送信したが、
タイトルのエラーが表示された。

結論

モノに対してポリシーをアタッチする必要がある。
(チュートリアルの中で、アタッチしたつもりだったが、漏れていたみたい?)

対応

管理→モノ→対象のモノを選択
 2019-11-15 10.14.47.jpg

セキュリティを選択し、対象のセキュリティを選択
 2019-11-15 10.15.32.jpg

右上からポリシーのアタッチを選択する
 2019-11-15 10.16.08.jpg

作成したポリシーをアタッチする。
 2019-11-15 10.17.54.jpg

おわり

めちゃ簡単に使えますね:joy:

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

Amazon RDS for MySQL でスナップショットから復元したDBにアクセスする

某案件で Amazon RDS for MySQL を利用しています。
追加開発で既存テーブルにカラム追加をしてAPIを新設してほしいという作業依頼がきました。

追加対象のテーブルの現在のレコード数を聞くと、7桁レベルのレコードがあるようです。
そこへ ALTER TABLE 〜 を流すと、どのくらい時間がかかるのだろうか? リリース時、本番環境で流せるレベルだろうか?

ふと気になったので先輩に相談し、スナップショットからデータベースを復元し、検証用のデータベースを作成して試してみることにしました。
スナップショットは毎晩深夜に自動取得されているものを使います。

Amazon RDS > スナップショット で表示される一覧から、復元したいスナップショットを選択して 「スナップショットから復元」を選択します。

データベース名の入力が必須、インスタンスタイプがデフォルトで本番環境と異なったものが選択されていたので、選択し直しました。
これは本番環境のインスタンスタイプが "旧世代のインスタンスタイプ" という扱いになってしまったからかもしれません。

それと念の為、本番環境とは別のVPCを選択して起動することにしました。

最後に 「DBインスタンスの復元」 ボタンを押して数分待つと復元されたデータベースを使えるようになります。

さて、どうやって接続してSQLを流そうか?

データベースを復元した先と同じVPCにEC2インスタンスを1つ立てました。これをSSHの踏み台サーバーとして使います。
RDSについているセキュリティーグループでEC2インスタンスのIPから MySQL/Auroraのアクセスを許可する設定を追加しました。

さてこれで接続できるはず・・・!

MySQLクライアントとして、TablePlus というアプリを使います
https://tableplus.com/

ここで気がつきました、DB復元時にユーザー名やパスワードの設定が何もしていません。
「RDS 復元 パスワード」 いろいろググった結果、これだという情報にたどり着けませんでした。

もしや元のデータベースと同じ情報ではないか・・・と試してみると無事接続できました!

大事なことなのでもう一度、

スナップショットから復元したデータベースは、元のデータベースと同じユーザ名・パスワード を使ってアクセスできます

結果

TablePlusから目的の SQL文を流して、おおよその実行時間が判明しました。
結果、数十秒 という単位で終わることがわかり、先輩に報告です。

このくらいであれば、無理な負荷ではない、ただしリリース時はメンテナンス状態にしてアクセスをなくした状態で行うのが良さそう
といった相談をしました。

負荷が気になったときは、実際に同等の環境を作って動かして検証するのが確実ですね。
そんな話を、別な先輩が書いた Amazon Web Services負荷試験入門 っていう本で読んだことを思い出しました。

最後に

検証用に立てたデータベースインスタンスはそれなりのサイズなので、料金もそれなりにかかってきます。
検証が終わったあとは忘れずに削除しましょう。EC2インスタンスも忘れずに。

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

CloudFormation Resource Importで、実体と違うテンプレートを使うとどうなるのか

概要

CloudFormationに待望のリソースインポート機能がリリース1:tada:
使い方は、例によってクラメソさんが記事化しているので2、気になったところを追加で確認する。

気になっていること

対象のリソースが存在していなければならない、などいくつかの必須条件はドキュメントにも記載済み3
では、インポートするリソースと、違うパラメータ設定のテンプレートを使うとどうなるのか。置換されたり、上書きされたりする?

実験

対象はS3 Bucket:hogehoge-fugafuga-poyopoyo
全てデフォルト設定。

テストその1:ユニークキーとなるBucketNameが違うテンプレート

取り込むバケット名とBucketNameが異なる。

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    DeletionPolicy: 'Retain'
    Properties:
      BucketName: 'piyopiyo-foo-bar'

image.png

失敗。

テストその2:デフォルト値と異なるテンプレート

バージョニングはデフォルトで無効。

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  S3Bucket:
    Type: 'AWS::S3::Bucket'
    DeletionPolicy: 'Retain'
    Properties:
      BucketName: 'hogehoge-fugafuga-poyopoyo'
      VersioningConfiguration: 
        Status: 'Enabled'

image.png

インポートは成功。バージョニングは無効化のまま
どうやら、反映はされないよう。
その後、テンプレート側をDisabled / Enabledと変更しつつ反映したところちゃんと更新された。

まとめ

リソースインポートによって既存リソースが取り込めるようになってとても便利になった。
取り込みと設定更新は同時に行われないようなので、
1. 必須項目だけを記述したテンプレートで取り込み
2. 任意項目を追加

の方が安全そう。

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

クラウド開発環境構築(メモ)

はじめに

本記事は、「Webサービス開発のため」の仮想マシン構築(AWS)~クラウド開発環境構築(Cloud9)までの手順を自分用にまとめたものです。

前提条件

  • クライアントはWindows環境
  • クラウドはAWS利用(AWSアカウント作成済み)
  • AWSの基礎知識有

 基礎知識が足りない場合@Futo23さんの記事が大変参考になりましたのでそちらを参照してください。

引用:AWS素人がインフラ設計/構築を理解するために、まず最初におさえるべきキーワード15選

手順

仮想マシン構築

管理コンソールサイトへアクセス

マネージメントコンソール

管理コンソールへログイン

テンプレートから仮想マシンを作成

  1. 「ソリューション構築」>「仮想マシンを起動する」を選択
  2. 「Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type」を選択
  3. 「確認と作成」を選択
  4. 「新しいキーペア名」を入力して「キーペアのダウンロード」を選択
  5. 「インスタンスの作成」を選択

インターネット利用のためのIPアドレス割当

  1. 「サービス」>「EC2」を選択
  2. 「ネットワーク&セキュリティ」>「Elstatic IP」を選択
  3. 「新しいアドレスの割当」を選択
  4. 「IPv4 アドレスプール」を「Amazonプール」に設定(デフォルト)
  5. 「割り当て」を選択
  6. 「閉じる」を選択
  7. 「アクション」>「アドレスの関連付け」を選択
  8. 対象の「インスタンス」と「プライベートIPアドレス」を設定して「関連付け」を選択
  9. 「閉じる」を選択

アクセス制限

  1. 「ネットワーク&セキュリティ」>「セキュリティグループ」を選択
  2. 仮想マシンと関連付けられている「セキュリティグループ」を選択して「アクション」>「インバウンドのルールの編集」を選択
  3. 「ルール追加」を選択
  4. 「カスタムTCP」を「HTTP」へ変更して「保存」を選択
  5. 「ルール追加」を選択
  6. 「カスタムTCP」の「ポート」に「20022」を入力して「保存」を選択

SSH接続確認

  1. ターミナルソフト起動(TeraTerm、Putty等)
  2. SSH接続:「Elastatic IP」、「新しいキーペア」を指定
  3. 結果確認

aws-ssh-open.png

IAM設定

  1. 「サービス」>「IAM」を選択
  2. 「ダッシュボード」>「ユーザー」を選択
  3. 「ユーザーを追加」を選択
  4. 「ユーザー名」の入力と「プログラムによるアクセス」にチェックを入れて「次のステップ:アクセス権限」を選択
  5. 「既存のポリシーを直接アタッチ」を選択
  6. 「AdministratorAccess」にチェックを入れて「次のステップ:タグ」を選択
  7. 「キー名」を入力して「次のステップ:確認」を選択
  8. 「ユーザーの作成」を選択
  9. 「アクセスキーID」「シークレットアクセスキー」をメモに控える

仮想マシン起動後の初期設定

ソフトウェアの最新化

$ sudo yum update -y

タイムゾーンの変更(日本時間)

$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

タイムゾーンの結果を確認(UTC→JST)。

$ date
Tue Nov 12 13:23:56 JST 2019

再起動後も日本時間にする。

/etc/sysconfig/clockファイルの修正
$ vim /etc/sysconfig/clock
【変更前】
ZONE="UTC"
UTC=true
【変更後】
ZONE="Asia/Tokyo"
UTC=false

【注意事項:読取り専用のため強制的に保存する】
:w !sudo tee %
:q!

日本語環境へ変更

/etc/sysconfig/i18nファイルの修正
$ sudo sh -c "echo 'LANG=ja_JP.UTF-8' > /etc/sysconfig/i18n"
$ sudo yum install -y man-pages-ja

【日本語表示反映の為、一旦再起動】
$ sudo shutdown -r now

rootのパスワード設定(セキュリティ強化)

$ sudo su -
$ passwd

ユーザー root のパスワードを変更。
新しいパスワード:
新しいパスワードを再入力してください:
passwd: すべての認証トークンが正しく更新できました。

ec2-user(デフォルト)の代替ユーザー作成(セキュリティ強化)

ユーザー作成
# useradd -g wheel ユーザー名
sudo権限付与
# echo 'ユーザー名 ALL = NOPASSWD: ALL' >> /etc/sudoers.d/cloud-init
【注意事項】
「NOPASSWD」設定により、sudo実行時にパスワード入力が不要となります。
セキュリティ上のリスクがあるのでお気を付けください。
authorized_keysのコピーとパーミッション設定
# rsync -a ~ec2-user/.ssh/authorized_keys ~ユーザー名/.ssh/
# chown -R ユーザー名:wheel ~ユーザー名/.ssh
# chmod -R go-rwx ~ユーザー名/.ssh

新しいユーザーでsshログインsudo権限が利用できる事を確認したら削除します。

sudo動作確認
$ sudo -s
ec2-user削除
$ sudo userdel ec2-user

不要コンソール起動を無効化(サーバー資源節約)

/etc/sysconfig/initファイルの修正
# vim /etc/sysconfig/init

【変更前】
ACTIVE_CONSOLES=/dev/tty[1-6]
【変更後】
ACTIVE_CONSOLES=/dev/tty1

SSHのデフォルトポート変更(セキュリティ強化)

/etc/ssh/sshd_configファイルの修正
$ sudo vim /etc/ssh/sshd_config

【追加】
Port 22
Port 22022
設定ファイル再読み込み
$ sudo service sshd reload

sshd を再読み込み中:                                       [  OK  ]
20022ポートの確認
$ sudo netstat -anp | grep sshd

tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      15543/sshd
tcp        0      0 0.0.0.0:20022               0.0.0.0:*                   LISTEN      15543/sshd

AWSコマンド利用準備

インストール確認
$ aws --version

aws-cli/1.16.102 Python/2.7.16 Linux/4.14.146-93.123.amzn1.x86_64 botocore/1.12.92
アクセスキー等の設定
$ aws configure

AWS Access Key ID [None]: XXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json

AWS Access Key ID [None]:アクセスキーID
AWS Secret Access Key [None]: シークレットアクセスキー
Default region name [None]: Tokyoリージョン(ap-northeast-1)
Default output format [None]: "text","json","table"のどれかを指定

アクセスキー等の設定

$ aws ec2 describe-instance-status
{
    "InstanceStatuses": [
        {
            "InstanceId": "i-0c7f2cff2082514c9",
            "InstanceState": {
                "Code": 16,
                "Name": "running"
            },
            "AvailabilityZone": "ap-northeast-1a",
            "SystemStatus": {
                "Status": "ok",
                "Details": [
                    {
                        "Status": "passed",
                        "Name": "reachability"
                    }
                ]
            },
            "InstanceStatus": {
                "Status": "ok",
                "Details": [
                    {
                        "Status": "passed",
                        "Name": "reachability"
                    }
                ]
            }
        }
    ]
}

Cloud9用の環境インストール(Python、Node.js)

引用:AWS Cloud9 SSH 開発環境 ホスト要件

pythonインストール状況確認
$ python --version

Python 2.7.16
node.jsインストール状況確認
$ node -v

-bash: node: command not found
node.jsインストール下準備
$ sudo yum -y install gcc-c++ git

【メッセージ省略】
Installed:
  gcc-c++.noarch 0:4.8.5-1.22.amzn1        git.x86_64 0:2.14.5-1.60.amzn1

Dependency Installed:
  cpp48.x86_64 0:4.8.5-28.142.amzn1
  gcc.noarch 0:4.8.5-1.22.amzn1
  gcc48.x86_64 0:4.8.5-28.142.amzn1
  gcc48-c++.x86_64 0:4.8.5-28.142.amzn1
  glibc-devel.x86_64 0:2.17-260.175.amzn1
  glibc-headers.x86_64 0:2.17-260.175.amzn1
  kernel-headers.x86_64 0:4.14.146-93.123.amzn1
  libgcc48.x86_64 0:4.8.5-28.142.amzn1
  libgomp.x86_64 0:6.4.1-1.45.amzn1
  libmpc.x86_64 0:1.0.1-3.3.amzn1
  libstdc++48.x86_64 0:4.8.5-28.142.amzn1
  mpfr.x86_64 0:3.1.1-4.14.amzn1
  perl-Error.noarch 1:0.17020-2.9.amzn1
  perl-Git.noarch 0:2.14.5-1.60.amzn1
  perl-TermReadKey.x86_64 0:2.30-20.9.amzn1

Complete!
nvmダウンロード
$ git clone https://github.com/creationix/nvm.git ~/.nvm

remote: Enumerating objects: 24, done.
remote: Counting objects: 100% (24/24), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 7573 (delta 7), reused 16 (delta 4), pack-reused 7549
Receiving objects: 100% (7573/7573), 2.51 MiB | 2.56 MiB/s, done.
Resolving deltas: 100% (4779/4779), done.
nvmへのパス設定
$ source ~/.nvm/nvm.sh
nvmコマンドへのパス設定
$ vim ~/.bash_profile

【追記】
# nvm
if [[ -s ~/.nvm/nvm.sh ]] ; then
        source ~/.nvm/nvm.sh ;
fi
Node.jsインストール
$ nvm install node

Downloading and installing node v13.1.0...
Downloading https://nodejs.org/dist/v13.1.0/node-v13.1.0-linux-x64.tar.xz...
######################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v13.1.0 (npm v6.12.1)
Creating default alias: default -> node (-> v13.1.0)
Node.jsのバージョン確認
$ node -v

v13.1.0
Node.jsの使用バージョン指定
$ nvm use v13.1.0

Now using node v13.1.0 (npm v6.12.1)
Node.jsのバイナリパス控え
$ which node
~/.nvm/versions/node/v13.1.0/bin/node
cloud9用のルートディレクトリ作成
$ mkdir ~/cloud9

クラウド開発環境構築(Cloud9)

AWS Cloud9

  1. 「サービス」>「Cloud9」を選択
  2. 「Create environment」を選択
  3. 「Name」を入力して「Next Step」を選択
  4. 「Environment type」を「Connect and run in remote server (SSH)」に変更して下記を入力して「Next Step」を選択

    入力項目名 説明
    User SSH接続ユーザー名
    Host EC2のグローバルIPアドレス
    Port 20022
    Environment path - Optional ~/cloud9
    Node.js binary path - Optional ~/.nvm/versions/node/v13.1.0/bin/node
  5. AWSの仮想マシンにログイン

  6. 「Copy key to clipboard」を選択(クリップボードへコピー)

  7. 公開鍵の登録

    キーファイルへの追記
    $ vim ~.ssh/authorized_keys
    
    【追記内容例:クリップボードの内容】
    ssh-rsa AAAAB3NzaC1yc2EAAAADAQA(省略)XXXXXXXXXXXX== XXXXXXXX@cloud9.amazon.com
    
    
  8. 「Next step」を選択

  9. 「Create Environment」を選択

  10. 「Next」を選択

  11. 「Next」を選択

  12. 「y」を入力して「Enter」実行

  13. 「Next」を選択

  14. 「Finish」を選択
    image.png

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

gsutil、aws s3での基本的なファイル操作(Google Cloud Storage、S3)

aws s3コマンドはS3しか操作できませんが、Google Cloud Storage(GCS)のCLIであるgsutilコマンドはGCSだけでなくS3に対しても同じように操作できます。

ファイルのアップロード(ローカルからクラウド)

$ gsutil cp dir/foo.txt gs://xxx-bucket/dir/
$ gsutil cp dir/foo.txt s3://yyy-bucket/dir/
$ aws s3 cp dir/foo.txt s3://yyy-bucket/dir/

g3://xxx-bucket/dir/foo.txtまたはs3://yyy-bucket/dir/foo.txtにファイルができます

ローカルでのパイプからのアップロード

$ cat dir/foo.txt | gsutil cp - gs://xxx-bucket/dir/foo.txt
$ cat dir/foo.txt | gsutil cp - s3://yyy-bucket/dir/foo.txt
$ cat dir/foo.txt | aws s3 cp - s3://yyy-bucket/dir/foo.txt

※Google Cloud Storage上でContent-Typeがapplication/octed-streamになってしまう
※コピー先はファイル名まで指定が必要

gsutil cpaws s3 cp-は標準入力の意味です。

いずれの方法でもアップロード先に指定のディレクトリがない場合は自動で作られるかのように振る舞います。

正確にはGoogle Cloud StorageもS3もオブジェクトストレージで、ファイルシステムとは異なり、ディレクトリもしくはフォルダの概念は本来はありません。以下の記事がわかりやすいです。

Amazon S3における「フォルダ」という幻想をぶち壊し、その実体を明らかにする | Developers.IO

Google Cloud StorageのファイルのContent-Typeを変更するには以下のようにします。

$ gsutil setmeta -h "Content-Type:text/plain" gs://xxx-bucket/dir/foo.txt

-hオプションはgsのファイルパスよりも先に指定する必要あり

コピー時にContent-Typeを指定するには以下のようにします。

$ cat dir/foo.txt | gsutil -h "Content-Type:text/plain" cp - gs://xxx-bucket/dir/foo.txt

-hオプションはcpよりも前に指定する必要あり

ファイルの一覧表示(ローカルおよびクラウド)

$ ls -l dir/
$ gsutil ls -l gs://xxx-bucket/dir/
$ gsutil ls -l s3://yyy-bucket/dir/
$ aws s3 ls s3://yyy-bucket/dir/

aws s3コマンドには-lオプションがありません

参考
gsutil lsはS3にも対応している件
Google Cloud StorageとAWS S3との間でファイルをコピーするには

ファイルの表示(ローカルおよびクラウド)

プレーンテキストのファイルをターミナル上に表示する方法です。ローカルのファイルに対するcatコマンドに相当するものです。

$ cat dir/foo.txt
$ gsutil cp gs://xxx-bucket/dir/foo.txt -
$ gsutil cp s3://yyy-bucket/dir/foo.txt -
$ aws s3 cp s3://yyy-bucket/dir/foo.txt -

gsutil cpaws s3 cp-は標準出力の意味です。

ファイルのダウンロード(クラウドからローカル)

$ gsutil cp gs://xxx-bucket/dir/foo.txt dir/
$ gsutil cp s3://yyy-bucket/dir/foo.txt dir/
$ aws s3 cp s3://yyy-bucket/dir/foo.txt dir/

ファイル名の変更(ローカルおよびクラウド)

$ mv dir/foo.txt dir/bar.txt
$ gsutil mv dir/foo.txt dir/bar.txt
$ gsutil mv gs://xxx-bucket/dir/foo.txt gs://xxx-bucket/dir/bar.txt
$ gsutil mv s3://yyy-bucket/dir/foo.txt s3://yyy-bucket/dir/bar.txt
$ aws s3 mv s3://yyy-bucket/dir/foo.txt s3://yyy-bucket/dir/bar.txt

gsutil mvはGoogle Cloud Storage(GCS)だけでなくS3やローカルのファイルの名前も変更できます。

また、gsutil mvはローカルからGCS、GCSからローカル、GCSの異なるバケット間、ローカルからS3、S3からローカル、S3の異なるバケット間、GCSからS3、S3からGCSの移動もできます。

aws s3 mvはローカルからS3、S3からローカル、S3の異なるバケット間の移動もできます。

ファイルの削除(ローカルおよびクラウド)

$ rm dir/foo.txt
$ gsutil rm gs://xxx-bucket/dir/foo.txt
$ gsutil rm s3://yyy-bucket/dir/foo.txt
$ aws s3 rm s3://yyy-bucket/dir/foo.txt

Google Cloud StorageもS3もファイルシステムではなくオブジェクトストレージなので、dir/foo.txtを削除した結果、dirの中に1つもオブジェクトがなくなればdir自体も自動で削除されるかのように振る舞います。

ディレクトリの容量確認(ローカルおよびクラウド)

$ du -sh dir/
$ gsutil du -sh gs://xxx-bucket/dir/
$ gsutil du -sh s3://yyy-bucket/dir/
$ aws s3 ls --human --recursive --sum s3://yyy-bucket/dir/

参考
ディレクトリの合計容量を確認するには(local, GCS, S3) - Qiita

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

Amazon CloudWatch LogsのログをAWS CLIでいい感じに取得する

Amazon CloudWatch LogsにあるログをAWS CLIでいい感じに取得する方法を毎回忘れている気がするのでメモ。

コマンド

とある環境でUnixBenchを走らせた際のログがあったのでそれを取得してみました。
ポイントは--query "events[].[message]" でログだけを抽出して、--output text でテキスト形式にします。

> aws logs get-log-events \
  --log-group-name <ロググループ名> \
  --log-stream-name <ストリーム名> \
  --query "events[].[message]" \
  --output text

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #
   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark
   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com
1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10
1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10
1 x Execl Throughput  1 2 3
1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3
1 x File Copy 256 bufsize 500 maxblocks  1 2 3
1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3
1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10
1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10
1 x Process Creation  1 2 3
1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10
1 x Shell Scripts (1 concurrent)  1 2 3
1 x Shell Scripts (8 concurrent)  1 2 3
2 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10
2 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10
2 x Execl Throughput  1 2 3
2 x File Copy 1024 bufsize 2000 maxblocks  1 2 3
2 x File Copy 256 bufsize 500 maxblocks  1 2 3
2 x File Copy 4096 bufsize 8000 maxblocks  1 2 3
2 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10
2 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10
2 x Process Creation  1 2 3
2 x System Call Overhead  1 2 3 4 5 6 7 8 9 10
2 x Shell Scripts (1 concurrent)  1 2 3
2 x Shell Scripts (8 concurrent)  1 2 3

いい感じじゃない例

オプション指定なし

JSON形式で、ログメッセージ以外の項目があります。またnextForwardTokennextBackwardToken でページングしつつ取得することになります。
ローカルの`jq`コマンドを使って加工するならこの状態でも良いかもです。

> aws logs get-log-events \
  --log-group-name <ロググループ名> \
  --log-stream-name <ストリーム名>

{
    "nextForwardToken": "f/35067999245249879332392031329876277123727621361937743943",
    "events": [
        {
            "ingestionTime": 1572499609423,
            "timestamp": 1572499604566,
            "message": "gcc -o ./pgms/arithoh -DTIME -Wall -pedantic -ansi -O2 -fomit-frame-pointer -fforce-addr -ffast-math -Wall -Darithoh ./src/arith.c"
        },
        {
            "ingestionTime": 1572499609423,
            "timestamp": 1572499604652,
            "message": "gcc -o ./pgms/register -DTIME -Wall -pedantic -ansi -O2 -fomit-frame-pointer -fforce-addr -ffast-math -Wall -Ddatum='register int' ./src/arith.c"
        },
        {
            "ingestionTime": 1572499609423,
            "timestamp": 1572499604691,
            "message": "gcc -o ./pgms/short -DTIME -Wall -pedantic -ansi -O2 -fomit-frame-pointer -fforce-addr -ffast-math -Wall -Ddatum=short ./src/arith.c"
        },
(略)

改行がない

--query 指定をミスってる例。惜しいんですがちょっと違う。

> aws logs get-log-events \
  --log-group-name <ロググループ名> \
  --log-stream-name <ストリーム名> \
  --query "events[].message" \
  --output text

#    #  #    #  #  #    #          #####   ######  #    #   ####   #    #       #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #     #    #  # #  #  #    ##            #####   #####   # #  #  #       ######       #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #     #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #     ####   #    #  #  #    #          #####   ######  #    #   ####   #    #        Version 5.1.3                      Based on the Byte Magazine Unix Benchmark          Multi-CPU version                  Version 5 revisions by Ian Smith,                                       Sunnyvale, CA, USA         January 13, 2011                   johantheghost at yahoo period com 1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10        1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10    1 x Execl Throughput  1 2 3     1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3      1 x File Copy 256 bufsize 500 maxblocks  1 2 3  1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3      1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10       1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10  1 x Process Creation  1 2 3   1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10  1 x Shell Scripts (1 concurrent)  1 2 3 1 x Shell Scripts (8 concurrent)  1 2 3       2 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10  2 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10 2 x Execl Throughput  1 2 3      2 x File Copy 1024 bufsize 2000 maxblocks  1 2 3        2 x File Copy 256 bufsize 500 maxblocks  1 2 32 x File Copy 4096 bufsize 8000 maxblocks  1 2 3        2 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10       2 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10        2 x Process Creation  1 2 3     2 x System Call Overhead  1 2 3 4 5 6 7 8 9 10  2 x Shell Scripts (1 concurrent)  1 2 3       2 x Shell Scripts (8 concurrent)  1 2 3 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS無料枠内だと思ってたら料金発生してた話

EC2のt2.microは月750時間までは無料とういことで早速使ってみたんですが、いきなり料金発生してる!なんで?ってなったのでメモ

▼AWS ドキュメント » Amazon EC2 » Linux インスタンス用ユーザーガイド » ネットワークとセキュリティ » Elastic IP アドレス
image.png

なるほど、

  • Elastic IP アドレスが実行中のインスタンスに関連付けられていない場合
  • Elastic IP アドレスが停止しているインスタンスやアタッチされていないネットワークインターフェイスに関連付けられている場合

にも料金が発生するようです
今回はElastic IPに関連付けられているインスタンスを停止させていたせいでした
使ってないときは停止しといた方がいいかなーってな感じで停止させていたのですが、
24時間起動していても1インスタンス月750時間は超えないので、常時起動させとくのがいいですね

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

各種クラウド PaaS (Platform as a Service) 実行環境 (プログラミング言語とバージョン) まとめ (2019年11月15日現在)

概要

  • AWS Elastic Beanstalk, Azure App Service, Google App Engine について実行環境 (プログラミング言語とバージョン) の情報をまとめる

AWS Elastic Beanstalk (Amazon Web Services: AWS)

Azure App Service (Microsoft Azure)

Google App Engine (Google Cloud Platform: GCP)

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

【Amazon SES】C#でメール一括送付するツールを作成してみた

概要

以前にGASでQRCodeを一括でメール送付する仕組みを作成しました。これはこれで便利なのですが1日に送信できるメールが限られているのが弱点でした。そこで今回は社内でAWSをお試しで使っているので、SESを使ってメールを一括送付する仕組みを作成しました。

■GoogleSpread(GAS)でQRCodeを一括で送付する

構成

主にC#のフォームで作成しました。C#でのSESの利用についてはSDKを使う方法とSMTPを使用した方法がサンプルとしてあるので、今回はSMTPを使用する方法を採用しました。C#のフォームアプリでSMTPインターフェイスからEメールを送信する仕組みを作成します。フォームアプリについてはSMTPの設定値や送信アドレスなどを設定できるようにし、SESの設定ができていればアプリから一括でメール送信が可能です。

■AWS SDK for .NETを使用してEメールを送信する

■C#でSMTPを使用してEメールを送信する

まずは Amazon SES の設定

送信するだけなら結構シンプルに設定できます。今回は送付用アドレスにGmailのメールを使用しました。ちなみに外部へのメール送信についてはサンドボックスの外への移動する必要があります。

いきなりイミワカメで困りますが、何もしていないと不正使用や悪用を防止し、送信者としての評判を保つため、新しい Amazon SES アカウントには一定の制限が適用されている状態になっています。それらの制限の解除をサポートセンターにお願いして解除してもらいます。SESの設定については以下に記載します。

AWS-SESの設定方法

このツールを使用するにあたり、AWS側の設定が必要になります。要はAWS-SESで送信用のメールアドレスやSMTPの設定などを行います。以降の手順の参考としてスクリーンショットを取得しました。ただし画像の一部にモザイクなどの加工する部分がありますのでご了承ください。

【AWS-SES】マネジメントコンソールを開く

まずはAWSマネジメントコンソールを開きSESと入力してSimple Email Serviceの画面を開きます。

2019-11-11 08_38_41-スタート.png

【AWS-SES】送信用のアドレスを設定する

まずは送信用のメールアドレスを設定します。こちらに設定するメールアドレスは基本的には何でも大丈夫なはずです。メールアドレスを設定すると設定したメールアドレスにAWSから認証のメールが届きます。AWSからメールを承認(Verify)することで送信用のメールアドレスとして設定できます。

sending_mail_setting.png

■ 設定したメールアドレスはC#フォームアプリの「送信アドレス」に設定します

【AWS-SES】サンドボックスの外への移動

SESの画面からSending Statisticsの画面を開きます。

SESはデフォルトだとサンドボックスと呼ばれる試用環境?の状態で制限が掛かっています。以下のスクリーンショットのように「Request a Sending Limit increase」と表示されている場合は、まだサンドボックスの状態で外部へのメール送信ができません。表示されている「Request a Sending Limit increase」をクリックしてサポートセンターへのページに遷移しサンドボックスの制限解除を依頼します。

ここで個人的に詰まったのがオレゴン以外のリージョンだといくら待っても制限が解除されませんでした。オレゴンのリージョンに選択しなおし制限解除の依頼をするとすぐに対応してくれました。まだ、アジアのリージョンもそこまで用意されていないのでオレゴンを選択した方がイイかもしれません。

なお、サンドボックスの制限解除はリージョンごとに設定する必要があるため、他のリージョンに切り替えているとRequest a Sending Limit increase」のボタンが表示されます。

sending_limit.png
※リージョンをムンバイに切り替えてスクリーンショットを取得

【AWS-SES】SMTPの設定

最後にSMTPの設定を行います。SMTPの「Server Name」などは同じ設定だと思うので、サンプルのコードと同じであることを確認してください。確認後「Create My SMTP Credentials」をクリックするとIAMの画面に切り替わります。

smtp_settings.png

IAMの画面に切り替わりIAMのユーザーを作成します。
SMTPでメールを送付する場合はIAM Userにメール送付用の専用のユーザーを作成します。

smtp_setting_iam.png

作成されると以下のような「認証情報のダウンロード」ボタンからSMTP USERとSMTP PASSWORDが記載されたCSVをダウンロードできます。

もしこの画面を閉じてしまってもIAMにはユーザーが登録されており、そのユーザーの認証情報から発行し直すことができます。

最悪、もう一度「Create My SMTP Credentials」で作り直しても大丈夫です。作り直した場合はIAMで必要のないIAMのユーザーは整理しましょう。

2019-11-11 22_14_45-IAM Management Console.png

■ ダウンロードしたCSVの情報を元にC#フォームアプリの「SMTP_USERNAME」と「SMTP_PASSWORD」を設定します

IAMの画面では先ほど作成したSMTPのユーザーが表示されているはずです。実は認証情報のアクセスキーがSMTPのユーザーに該当します。ここではパスワードを確認することができません。ただしアクセスキーを発行し直しSMTPのユーザーとパスワードを取得しなおすことはできそうです。

2019-11-11 22_08_31-IAM Management Console.png

C#によるフォームアプリの作成

上記のAWS-SESが完了したらC#フォームで送る準備をします。ツールを使う場合は上記のSMTPユーザーなどを設定すれば一括でメールを送付することができます。
なおAWSの設定情報のどの項目を設定すべきかはAWS手順の画像の下に■の記号で補足している部分を参考にしていただけると助かります。
それでは実装した機能を記載していこうと思います。

【C#フォーム】プロパティによる設定値の保存

入力した設定値についてはプロパティに保存され次回起動したときにも設定値を維持するようにします。
保存する値がセキュアなモノである場合は、暗号化して保存するようにしています。

これは以前にプロパティではなくリソースを使っていたときにEXEのバイナリから読み取れてしまうことがあったので、念のために暗号化の処理を挟むことにしました。

【C#フォーム】Excelから送付リストの取り込み

DataGridViewに表示する内容についてはExcelファイルから取り込めるようにしました。外部のデータベースに接続して利用することも可能ですが、データベース自体が決まっていないので、とりあえずClosedXMLを使用してExcelファイルを読み込むようにしました。

ClosedXMLを使用すればExcelがインストールされてなくても処理できるので便利ですね。Office Interopでも読み込めますが後処理とかちゃんとしないとイケないんで少し面倒です。
フォーム 送信画面.png

取り込んだ後のDataGridViewでは列項目で絞り込めるようにしています。
フォーム 送信画面 絞り込み.png

【C#フォーム】テキストの置換

3つの項目だけですが自分で設定したテキストの内容と取り込んだExcelファイルの列項目に対応して置換するようにしました。文章にするとわかりづらいため設定画面も日本語でわかりやすくしてみたつもりです。
フォーム取り込みデータの設定.png

【C#フォーム】AWSの設定

AWSで設定したSMTPなどを設定できます。AWS側の設定については後段で説明します。
フォーム AWS-SESの設定.png

【C#フォーム】メールの設定

メールの件名や本文の設定ができます。上記のテキストの置換に合わせて本文を置換することができます。
フォーム メールの設定.png

【C#フォーム】ログ出力

送信後に本当に送信されたかどうか不安だと思うのでログを出力するようにしました。

送信結果

捨てメアド【メルアドぽいぽい】というサービスでメールアドレスを作成して試しに送ってみました。

2019-11-11 07_13_10-捨てメアド【メルアドぽいぽい】.png

テキストの置換も上手く機能しています。

04.png

リンク先を確認するとちゃんと社員番号のQRコードが生成されています。
2019-11-11 22_51_08-QRCode ツクール.png

総評

AWS-SESとC#のフォームアプリで一括で送付する仕組みができました。別にメールで送付するならGASでもイイじゃんとなりそうですが、GASはGASで送付エラーのときが若干怖かったりします。
作成したC#のフォームアプリでは送付後にログも出力するので正しく送付されたかどうか確認できます。もし送付できていなかった場合でもログから拾うことができ、もう一度再送してあげることもできます。
今回、作成したC#フォームアプリは以下の場所に格納しましたのでAWS-SESの設定をした上でご試用いただければ幸いです。

■AWS-SendMail.zip(BOXの共有URLです)

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

【AWS-SES】C#でメール一括送付するツールを作成してみた

概要

以前にGASでQRCodeを一括でメール送付する仕組みを作成しました。これはこれで便利なのですが1日に送信できるメールが限られているのが弱点でした。そこで今回は社内でAWSをお試しで使っているので、SESを使ってメールを一括送付する仕組みを作成しました。

■GoogleSpread(GAS)でQRCodeを一括で送付する

構成

主にC#のフォームで作成しました。C#でのSESの利用についてはSDKを使う方法とSMTPを使用した方法がサンプルとしてあるので、今回はSMTPを使用する方法を採用しました。C#のフォームアプリでSMTPインターフェイスからEメールを送信する仕組みを作成します。フォームアプリについてはSMTPの設定値や送信アドレスなどを設定できるようにし、SESの設定ができていればアプリから一括でメール送信が可能です。

■AWS SDK for .NETを使用してEメールを送信する

■C#でSMTPを使用してEメールを送信する

まずは Amazon SES の設定

送信するだけなら結構シンプルに設定できます。今回は送付用アドレスにGmailのメールを使用しました。ちなみに外部へのメール送信についてはサンドボックスの外への移動する必要があります。

いきなりイミワカメで困りますが、何もしていないと不正使用や悪用を防止し、送信者としての評判を保つため、新しい Amazon SES アカウントには一定の制限が適用されている状態になっています。それらの制限の解除をサポートセンターにお願いして解除してもらいます。SESの設定については以下に記載します。

AWS-SESの設定方法

このツールを使用するにあたり、AWS側の設定が必要になります。要はAWS-SESで送信用のメールアドレスやSMTPの設定などを行います。以降の手順の参考としてスクリーンショットを取得しました。ただし画像の一部にモザイクなどの加工する部分がありますのでご了承ください。

【AWS-SES】マネジメントコンソールを開く

まずはAWSマネジメントコンソールを開きSESと入力してSimple Email Serviceの画面を開きます。

2019-11-11 08_38_41-スタート.png

【AWS-SES】送信用のアドレスを設定する

まずは送信用のメールアドレスを設定します。こちらに設定するメールアドレスは基本的には何でも大丈夫なはずです。メールアドレスを設定すると設定したメールアドレスにAWSから認証のメールが届きます。AWSからメールを承認(Verify)することで送信用のメールアドレスとして設定できます。

sending_mail_setting.png

■ 設定したメールアドレスはC#フォームアプリの「送信アドレス」に設定します

【AWS-SES】サンドボックスの外への移動

SESの画面からSending Statisticsの画面を開きます。

SESはデフォルトだとサンドボックスと呼ばれる試用環境?の状態で制限が掛かっています。以下のスクリーンショットのように「Request a Sending Limit increase」と表示されている場合は、まだサンドボックスの状態で外部へのメール送信ができません。表示されている「Request a Sending Limit increase」をクリックしてサポートセンターへのページに遷移しサンドボックスの制限解除を依頼します。

ここで個人的に詰まったのがオレゴン以外のリージョンだといくら待っても制限が解除されませんでした。オレゴンのリージョンに選択しなおし制限解除の依頼をするとすぐに対応してくれました。まだ、アジアのリージョンもそこまで用意されていないのでオレゴンを選択した方がイイかもしれません。

なお、サンドボックスの制限解除はリージョンごとに設定する必要があるため、他のリージョンに切り替えているとRequest a Sending Limit increase」のボタンが表示されます。

sending_limit.png
※リージョンをムンバイに切り替えてスクリーンショットを取得

【AWS-SES】SMTPの設定

最後にSMTPの設定を行います。SMTPの「Server Name」などは同じ設定だと思うので、サンプルのコードと同じであることを確認してください。確認後「Create My SMTP Credentials」をクリックするとIAMの画面に切り替わります。

smtp_settings.png

IAMの画面に切り替わりIAMのユーザーを作成します。
SMTPでメールを送付する場合はIAM Userにメール送付用の専用のユーザーを作成します。

smtp_setting_iam.png

作成されると以下のような「認証情報のダウンロード」ボタンからSMTP USERとSMTP PASSWORDが記載されたCSVをダウンロードできます。

もしこの画面を閉じてしまってもIAMにはユーザーが登録されており、そのユーザーの認証情報から発行し直すことができます。

最悪、もう一度「Create My SMTP Credentials」で作り直しても大丈夫です。作り直した場合はIAMで必要のないIAMのユーザーは整理しましょう。

2019-11-11 22_14_45-IAM Management Console.png

■ ダウンロードしたCSVの情報を元にC#フォームアプリの「SMTP_USERNAME」と「SMTP_PASSWORD」を設定します

IAMの画面では先ほど作成したSMTPのユーザーが表示されているはずです。実は認証情報のアクセスキーがSMTPのユーザーに該当します。ここではパスワードを確認することができません。ただしアクセスキーを発行し直しSMTPのユーザーとパスワードを取得しなおすことはできそうです。

2019-11-11 22_08_31-IAM Management Console.png

C#によるフォームアプリの作成

上記のAWS-SESが完了したらC#フォームで送る準備をします。ツールを使う場合は上記のSMTPユーザーなどを設定すれば一括でメールを送付することができます。
なおAWSの設定情報のどの項目を設定すべきかはAWS手順の画像の下に■の記号で補足している部分を参考にしていただけると助かります。
それでは実装した機能を記載していこうと思います。

【C#フォーム】プロパティによる設定値の保存

入力した設定値についてはプロパティに保存され次回起動したときにも設定値を維持するようにします。
保存する値がセキュアなモノである場合は、暗号化して保存するようにしています。

これは以前にプロパティではなくリソースを使っていたときにEXEのバイナリから読み取れてしまうことがあったので、念のために暗号化の処理を挟むことにしました。

【C#フォーム】Excelから送付リストの取り込み

DataGridViewに表示する内容についてはExcelファイルから取り込めるようにしました。外部のデータベースに接続して利用することも可能ですが、データベース自体が決まっていないので、とりあえずClosedXMLを使用してExcelファイルを読み込むようにしました。

ClosedXMLを使用すればExcelがインストールされてなくても処理できるので便利ですね。Office Interopでも読み込めますが後処理とかちゃんとしないとイケないんで少し面倒です。
フォーム 送信画面.png

取り込んだ後のDataGridViewでは列項目で絞り込めるようにしています。
フォーム 送信画面 絞り込み.png

【C#フォーム】テキストの置換

3つの項目だけですが自分で設定したテキストの内容と取り込んだExcelファイルの列項目に対応して置換するようにしました。文章にするとわかりづらいため設定画面も日本語でわかりやすくしてみたつもりです。
フォーム取り込みデータの設定.png

【C#フォーム】AWSの設定

AWSで設定したSMTPなどを設定できます。AWS側の設定については後段で説明します。
フォーム AWS-SESの設定.png

【C#フォーム】メールの設定

メールの件名や本文の設定ができます。上記のテキストの置換に合わせて本文を置換することができます。
フォーム メールの設定.png

【C#フォーム】ログ出力

送信後に本当に送信されたかどうか不安だと思うのでログを出力するようにしました。

送信結果

捨てメアド【メルアドぽいぽい】というサービスでメールアドレスを作成して試しに送ってみました。

2019-11-11 07_13_10-捨てメアド【メルアドぽいぽい】.png

テキストの置換も上手く機能しています。

04.png

リンク先を確認するとちゃんと社員番号のQRコードが生成されています。
2019-11-11 22_51_08-QRCode ツクール.png

総評

AWS-SESとC#のフォームアプリで一括で送付する仕組みができました。別にメールで送付するならGASでもイイじゃんとなりそうですが、GASはGASで送付エラーのときが若干怖かったりします。
作成したC#のフォームアプリでは送付後にログも出力するので正しく送付されたかどうか確認できます。もし送付できていなかった場合でもログから拾うことができ、もう一度再送してあげることもできます。
今回、作成したC#フォームアプリは以下の場所に格納しましたのでAWS-SESの設定をした上でご試用いただければ幸いです。

■AWS-SendMail.zip(BOXの共有URLです)

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