20200114のAWSに関する記事は24件です。

DynamoDBのテーブルデータをNodejsでS3にBackupする

はじめに

DynamoDBに大量にあるテーブルのデータを一括でS3にバックアップしたい。。みたいな状況があったので、メモとして書いておきます。

ちなみにテーブルが少量であれば、Data Pipelineを使って簡単に出来ます!!
aws-sdkではData Pipelineの細かいオプションは設定出来なかったようなので、nodejsでData Pipelineを操作することはしていません。

環境

  • Nodejs v11.1.0
  • DynamoDB

事前準備

バックアップを行うためにS3に専用のバケットを作成しておいてください。
この記事ではbackupsというバケットを作成したという例で進めます。

プロジェクトを作成

適当ですが、dynamodb-backuperみたいなディレクトリを作成して進めていきます。

$ mkdir dynamodb-backuper

$ cd dynamodb-backuper

$ npm init

$ npm install aws-sdk dynamodb-backup-restore --save

$ touch export.js restore.js

export.js

export.jsはその名通りDynamoDBから全テーブルのbackupを行うファイルです。

'use strict'

const AWS = require('aws-sdk')
const DynamoDB = new AWS.DynamoDB({region: 'ap-northeast-1'})

async function main(){
    let tables = await getAllTableLists()
    tables.forEach((v) => {
        exportToS3(v)
    })
}

// 実行
main()

// Get all table names from DynamoDB
async function getAllTableLists() {
    let params = {}
    let tables = []

    while(true) {
        let response = await DynamoDB.listTables(params).promise()
        tables = tables.concat(response.TableNames)

        if (!response.LastEvaluatedTableName) {
            break
        } else {
            params.ExclusiveStartTableName = response.LastEvaluatedTableName
        }
    }
    return tables
}

// Function to perform backup
function exportToS3(tableName){
    const Backup = require('dynamodb-backup-restore').Backup
    let config = {
        S3Bucket:     'backups', // 必須 - バケット名 
        S3Prefix:     tableName, // 任意 - 保存するS3のサブフォルダ名
        S3Encryption: 'AES256', // 任意 - 暗号化
        S3Region:     'ap-northeast-1', // 必須 - リージョン
        DbTable:      tableName // 必須 - エクスポートするテーブル名
    }

    let backup = new Backup(config)
    backup.full()
}

以下でエクスポートを実行出来ます。

$ node export.js

restore.js

restore.jsもそのままですが、export.jsでS3にエクスポートしたデータを今度はDynamoDBの指定のテーブルにインポートします。

リストアを行う前に事前にインポートするテーブルを作成しておく必要があります。
今回はnewTest1というテーブルを作ったとして進めます。
この時に注意が必要なのが、データ量によっては無料枠ではキャパシティーレベルが超えてしまうため、データサイズに応じてオンデマンドに変更してください。

以下のコードでは、execRestore関数の第一引数にS3にバックアップをとったtest1というサブフォルダを指定し、インポート用に作成したnewTest1というテーブル名を第二引数に設定します。

'use strict'

function execRestore(bucketName, tableName){
    const Restore = require('dynamodb-backup-restore').Restore
    let config = {
        S3Bucket:   'backups', // 必須
        S3Prefix:   bucketName, // 任意
        S3Region:   'ap-northeast-1', // 必須
        DbTable:    tableName, // 必須
        DbRegion:   'ap-northeast-1', // 必須
    }

    Restore(config)
}

// 実行
execRestore('test1', 'newTest1')

以下でリストアを実行出来ます。

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

Amazon EKS のチュートリアルで Kubernetes を理解する #03 メトリクス&ダッシュボード

はじめに

本記事は、以下の内容の続きになります。

前回、k8sクラスタにアプリケーションをデプロしてみたので、
今回は、クラスタやアプリケーションのモニタリングを行ってみます。

前提条件

  • k8sのクラスタが作成されており、ワーカーノードが稼働していること。

Kubernetes Metrics Server の構築

Kubernetesには、メトリクスサーバー(metrics-server)というモノがあり、
これを利用することで、クラスタに登録された各種リソースの情報を簡単に取得できるようになります。

Kubernetes Metrics Server のデプロイ

ここでは、最新版をダウンロードして、クラスタに登録を行います。

$ cd /Users/$USERNAME/MyWork/amazon-eks

$ DOWNLOAD_URL=$(curl -Ls "https://api.github.com/repos/kubernetes-sigs/metrics-server/releases/latest" | jq -r .tarball_url)

$ DOWNLOAD_VERSION=$(grep -o '[^/v]*$' <<< $DOWNLOAD_URL)

$ curl -Ls $DOWNLOAD_URL -o metrics-server-$DOWNLOAD_VERSION.tar.gz

$ mkdir metrics-server-$DOWNLOAD_VERSION

$ tar -xzf metrics-server-$DOWNLOAD_VERSION.tar.gz --directory metrics-server-$DOWNLOAD_VERSION --strip-components 1

$ kubectl apply -f metrics-server-$DOWNLOAD_VERSION/deploy/1.8+/
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
serviceaccount/metrics-server created
deployment.apps/metrics-server created
service/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created

動作確認

以下で、metrics-serverのDeploymentで、必要な数のポッドが実行されていることが確認できます。

$ kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   1/1     1            1           2m55s

Kubernetes Dashboard の構築

metrics-server単体では、メトリクスの可視化はされません。
メトリクスの可視化を行うために、Kubernetes ダッシュボード を構築します。

Kubernetes Dashboard のデプロイ

まずは、Kubernetes のGitHubリポジトリで公開されているダッシュボードの定義を取得し、クラスタにデプロイします。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml
namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

サービスアカウント/クラスターロールバインディングの作成

デフォルトでは、Kubernetes ダッシュボードへのアクセスは制限されています。
そのため、管理者レベルのアクセス権限を使用してダッシュボードに安全に接続するために使用できるようにします。

「eks-admin-service-account.yaml」というファイルを作成します。

$ touch eks-admin-service-account.yaml

そのファイルに、以下の内容を定義します。
ここでは、サービスアカウントと、eks-admin と呼ばれるクラスターロールバインディングを定義しています。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: eks-admin
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: eks-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: eks-admin
  namespace: kube-system

上記内容を、クラスタにデプロイします。

$ kubectl apply -f eks-admin-service-account.yaml
serviceaccount/eks-admin created
clusterrolebinding.rbac.authorization.k8s.io/eks-admin created

ダッシュボードへの接続

先の手順で、クラスタの状態を表示可能な管理者サービスアカウントを作成したため、このサービスアカウントを使用してダッシュボードに接続します。

まず、サービスアカウントの認証トークンを取得します。

$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}')

以下のような内容が出力されるので、そこから <authentication_token> の値をコピーしておきます。
このトークンを使用してダッシュボードに接続します。

Name:         eks-admin-token-jnn96
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: eks-admin
              kubernetes.io/service-account.uid: e9201e83-3454-11ea-bc0a-0e7ed5a45fb2

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      <authentication_token>

ローカル環境から、ダッシュボードへアクセスできるよう、 kubectl proxy を開始します。
以下のコマンドを実行したら、そのまま実行中の状態になるため、ダッシュボードへの接続確認ができるまで、
コンソールはそのままにしておきます。

kubectl proxy

ブラウザで以下のリンクを開いて、ダッシュボードのエンドポイントにアクセスします。

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#!/login

amazon-eks-dev04.jpg

さらに、[トークン] を選択して、先の手順でコピーしておいた <authentication_token> の内容を [トークン] フィールドに貼り付け、[サインイン] を押下します。
接続に成功すると、以下のようなダッシュボード画面が表示されます。

amazon-eks-dev05.jpg

このダッシュボード画面から、ノードの状態や、PodやReplicaSetなどのワークロードの情報など、
各種のクラスタの情報を確認することができます。

まとめ

Kubernetes Metrics Server/Kubernetes Dashboard を利用することで、
クラスタの構成や各種リソースの状況などを簡単に確認できるようになるので、
まずはクラスタ構成や動作を理解するには、このダッシュボードの画面を確認しながら操作してみると、
効率的に内容を把握できるかと思います。

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

AWS APIGateway/LambdaとJavascriptで簡易問い合わせサイトをつくる

概要

問い合わせフォーム(javascript) ⇒ APIGateway ⇒ Lambda(Node.js) ⇒ Lambda(Node.js) の流れで簡単な問い合わせサイトを作ります。
contact_form.png
一応レスポンシブにします。
contact_form2.png
バリデーションもあります。
contact_form3.png
contact_form4.png

index.htmlのコーディング

index.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <link rel="stylesheet" href="style.css" />
  <title>問い合わせ</title>
</head>

<body>
  <div class="header">
    <a href="#" class="logo">Hoge Hoge Company</a>
    <a class="active" href="#">Home</a>
  </div>

  <main class="main-container">
    <h2>問い合わせフォーム</h2>
    <div class="container">
      <form id="form">
        <div class="row">
          <div class="col-20">
            <label for="name">名前</label>
          </div>
          <div class="col-80">
            <input type="text" id="name" name="name" placeholder="Your name.." />
          </div>
        </div>
        <div id="name-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <div class="col-20">
            <label for="email">E-Mail</label>
          </div>
          <div class="col-80">
            <input type="email" id="email" name="email" placeholder="Your E-Mail.." />
          </div>
        </div>
        <div id="email-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <div class="col-20">
            <label for="dept">所属</label>
          </div>
          <div class="col-80">
            <input type="text" id="dept" name="dept" placeholder="Ex: Example Co., Ltd." />
          </div>
        </div>
        <div id="dept-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <div class="col-20">
            <label for="body">内容</label>
          </div>
          <div class="col-80">
            <textarea id="body" name="body" placeholder="お気軽にお問い合わせください。" style="height:200px"></textarea>
          </div>
        </div>
        <div id="body-alert" class="alert-message" hidden>入力値が不正です。</div>
        <div class="row">
          <button id="submitBtn">送信</button>
        </div>
      </form>
    </div>

    <!-- The Modal -->
    <div id="myModal" class="modal">
      <!-- Modal content -->
      <div id="modal-content">
        <span class="close">&times;</span>
        <p id="result"></p>
        <p id="detail" style="font-size: small;"></p>
      </div>
    </div>
  </main>
  <script src="main.js" defer></script>
</body>
</html>

CSSのコーディング

style.css
* {
  margin: 0;
  padding: 0;
  font-family: "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", "メイリオ",
    Meiryo, "MS Pゴシック", sans-serif;
}

/* Style the header with a grey background and some padding */
.header {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  justify-content: space-between;
  background-color: #000000;
  padding: 20px 10px;
}

/* Style the header links */
.header a {
  color: #f2f2f2;
  text-align: center;
  padding: 12px;
  text-decoration: none;
  font-size: 18px;
  line-height: 25px;
  border-radius: 4px;
}

/* Style the logo link (notice that we set the same value of line-height and font-size to prevent the header to increase when the font gets bigger */
.header a.logo {
  font-size: 25px;
  font-weight: bold;
}

/* Change the background color on mouse-over */
.header a:hover {
  background-color: #ddd;
  color: black;
}

/* Style the active/current link*/
.header a.active {
  background-color: dodgerblue;
  color: white;
}

/* Add media queries for responsiveness - when the screen is 500px wide or less, stack the links on top of each other */
@media screen and (max-width: 500px) {
  .header {
    justify-content: center;
  }
  .header a {
    padding: 12px;
  }
}

.main-container {
  width: 90%;
  margin: auto;
}
.main-container h2 {
  margin-top: 12px;
}

/* Style inputs, select elements and textareas */
input,
textarea {
  width: 100%;
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  resize: vertical;
}

.alert-red {
  border: #ff4500 2px solid;
}

.alert-green {
  border: #00ff7f 2px solid;
}

.alert-message {
  color: #ff4500;
  font-size: small;
  text-align: end;
  margin-bottom: 10px;
}

/* Style the label to display next to the inputs */
label {
  padding: 12px 12px 12px 0;
  display: inline-block;
}

/* Style the submit button */
#submitBtn {
  background-color: #4caf50;
  color: white;
  padding: 10px 18px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 15px;
  float: right;
}

/* Style the container */
.container {
  border-radius: 5px;
  background-color: #f2f2f2;
  padding: 20px;
}

/* Floating column for labels: 25% width */
.col-20 {
  float: left;
  width: 20%;
  margin-top: 6px;
}

/* Floating column for inputs: 75% width */
.col-80 {
  float: left;
  width: 80%;
  margin-top: 6px;
}

/* Clear floats after the columns */
.row:after {
  content: "";
  display: table;
  clear: both;
}

/* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 600px) {
  .col-20,
  .col-80,
  #submitBtn {
    width: 100%;
    margin-top: 0;
  }
}

/* The Modal (background) */
.modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0, 0, 0); /* Fallback color */
  background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
}

/* Modal Content/Box */
#modal-content {
  background-color: #fefefe;
  margin: 15% auto; /* 15% from the top and centered */
  padding: 20px;
  width: 80%; /* Could be more or less, depending on screen size */
}

.request-loading {
  border: 1px solid #888;
}

.request-success {
  border: 1px solid #00ff7f;
}

.request-fail {
  border: 1px solid #ff4500;
}

/* The Close Button */
.close {
  color: #aaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}

javascriptのコーディング

変数URLはAPIGatewayで発行してから実装します。

main.js
const URL = 'https://'; // APIGatewayで作成したURL

const LOADING = '処理中...';
const RESULT_OK = 'リクエストを受け付けました。';
const RESULT_OK_DETAIL =
  "正常に処理が完了すると 'support@hogehoge.com' からメールが配信されます。";
const RESULT_NG = 'リクエストの受付に失敗しました';
const RESULT_NG_DETAIL =
  '大変申し訳ありません。担当者へ直接お問い合わせください。';
const DOMAIN = '@sample.com';
document.getElementById('email').value = DOMAIN;

(() => {
  const modal = document.getElementById('myModal');
  const modalContent = document.getElementById('modal-content');
  const sendBtn = document.getElementById('submitBtn');
  const span = document.getElementsByClassName('close')[0];
  const result = document.getElementById('result');
  const form = document.getElementById('form');
  const nameAlert = document.getElementById('name-alert');
  const emailAlert = document.getElementById('email-alert');
  const deptAlert = document.getElementById('dept-alert');
  const bodyAlert = document.getElementById('body-alert');
  const resultDetail = document.getElementById('detail');

  const inputValueClear = () => {
    form.name.value = '';
    form.name.setAttribute('class', '');
    form.email.value = DOMAIN;
    form.email.setAttribute('class', '');
    form.dept.value = '';
    form.dept.setAttribute('class', '');
    form.body.value = '';
    form.body.setAttribute('class', '');
    result.innerHTML = '';
  };

  // When the user clicks on <span> (x), close the modal
  span.onclick = () => {
    if (result.innerHTML !== LOADING) {
      modal.style.display = 'none';
      if (result.innerHTML === RESULT_OK) inputValueClear();
    }
  };

  // When the user clicks anywhere outside of the modal, close it
  window.onclick = event => {
    if (event.target === modal) {
      if (result.innerHTML !== LOADING) {
        modal.style.display = 'none';
        if (result.innerHTML === RESULT_OK) inputValueClear();
      }
    }
  };

  sendBtn.addEventListener('click', async event => {
    event.preventDefault();
    const name = form.name.value.trim();
    form.name.value = name;
    const email = form.email.value.trim();
    form.email.value = email;
    const dept = form.dept.value.trim();
    form.dept.value = dept;
    const body = form.body.value.trim();
    form.body.value = body;

    // validation check
    if (validation(name, email, dept, body)) return;

    result.innerHTML = LOADING;
    modalContent.setAttribute('class', 'request-loading');
    modal.style.display = 'block';
    const jsonData = JSON.stringify({ name, email, dept, body });

    try {
      const res = await fetch(URL, {
        method: 'POST',
        mode: 'cors',
        cache: 'no-cache',
        body: jsonData,
        headers: {
          'Content-Type': 'application/json; charset=utf-8'
        }
      });
      console.log('Response!!', res.status);
      modalContent.setAttribute('class', 'request-success');
      result.innerHTML = RESULT_OK;
      resultDetail.innerHTML = RESULT_OK_DETAIL;
    } catch (error) {
      console.log(error);
      modalContent.setAttribute('class', 'request-fail');
      result.innerHTML = RESULT_NG;
      resultDetail.innerHTML = RESULT_NG_DETAIL;
    }
  });

  const validation = (name, email, dept, body) => {
    let validationResult = false;
    if (!name.length) {
      form.name.setAttribute('class', 'alert-red');
      nameAlert.hidden = false;
      validationResult = true;
    } else {
      form.name.setAttribute('class', 'alert-green');
      nameAlert.hidden = true;
    }
    if (!email.length || !/^[^@]+@sample.com$/.test(email)) {
      form.email.setAttribute('class', 'alert-red');
      emailAlert.hidden = false;
      validationResult = true;
    } else {
      form.email.setAttribute('class', 'alert-green');
      emailAlert.hidden = true;
    }
    if (!dept.length) {
      form.dept.setAttribute('class', 'alert-red');
      deptAlert.hidden = false;
      validationResult = true;
    } else {
      form.dept.setAttribute('class', 'alert-green');
      deptAlert.hidden = true;
    }
    if (!body.length) {
      form.body.setAttribute('class', 'alert-red');
      bodyAlert.hidden = false;
      validationResult = true;
    } else {
      form.body.setAttribute('class', 'alert-green');
      bodyAlert.hidden = true;
    }
    return validationResult;
  };

  form.name.addEventListener('input', event => {
    const name = event.target.value.trim();
    if (!name) {
      event.target.setAttribute('class', 'alert-red');
      nameAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      nameAlert.hidden = true;
    }
  });

  form.email.addEventListener('input', event => {
    const email = event.target.value.trim();
    event.target.value = email;
    if (!email || !/^[^@]+@sample.com$/.test(email)) {
      event.target.setAttribute('class', 'alert-red');
      emailAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      emailAlert.hidden = true;
    }
  });

  form.dept.addEventListener('input', event => {
    const dept = event.target.value.trim();
    event.target.value = dept;
    if (!dept) {
      event.target.setAttribute('class', 'alert-red');
      deptAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      deptAlert.hidden = true;
    }
  });

  form.body.addEventListener('input', event => {
    const body = event.target.value.trim();
    if (!body) {
      event.target.setAttribute('class', 'alert-red');
      bodyAlert.hidden = false;
    } else {
      event.target.setAttribute('class', 'alert-green');
      bodyAlert.hidden = true;
    }
  });
})();

AWS SES(Simple Email Service)を設定

※ドメインはRoute53で取得済みの前提
※併せてACM(AWS Certificate Manager)で証明書を取得済み
※SESは送信のみの設定です。

  1. AWSへログイン
  2. SESのコンソールへ移動
  3. リージョンはバージニア北部を選択
  4. 左のナビゲーションからDomainsを選択
    image.png
  5. image.png をクリック
  6. ↓のように入力。自分のドメインとDKIM設定をチェックして、image.pngをクリックimage.png
  7. 次のモーダルではCNAMEやTXTが表示され、登録しろと指示が出る。Route53を利用している場合は、このモーダル上でDNSの登録がすべて完了する。
  8. こんな感じになればOK!(※現時点ではサンドボックス上での制限された利用が可能)
    image.png
  9. 左のナビゲーションでEmail Addressesを選択
    image.png
  10. Veryfy a New Email Addressでサンドボックス上で利用できるメールアドレスを登録する。
  11. 登録したメールアドレスをSend a Test Emailで登録したドメインからメールが送られるかテストして正常な動作を確認

最後に起動するLambdaを作成

  1. Lambdaのコンソールページへ移動
  2. image.png をクリック
  3. image.png を選択
  4. image.png 適当な関数名を入力
  5. ランタイムはNode.js 12.xを選択
  6. そのほかはデフォルトのままでOK
  7. image.png をクリック
  8. 環境変数へ配信元のアドレスを設定
    image.png
  9. 実行ロールにはSESの権限を許可
    image.png
  10. 関数コードへ↓のコードを実装
index.js
'use strict'
const SES = require("aws-sdk/clients/ses");
const ses = new SES({ region: "us-east-1" }); // 米国東部(バージニア北部)
const FROM = process.env.FROM; // 環境変数から取得

exports.handler = async (event) => {
    console.log(event);
    const TO = [event.email];
    const params = {
        Destination: {
            ToAddresses: TO
        },
        Message: {
            Body: {
                Text: {
                    Data: [
                        event.dept + ' ' + event.name + '',
                        ' ',
                        'Hoge Hoge Companyです。',
                        'お問い合わせしていただきありがとうございます。',
                        '下記の内容で承りました。',
                        ' ',
                        '[お問い合わせ内容]' + "\n" + event.body,
                    ].join("\n"),
                    Charset: "utf-8"
                }
            },
            Subject: {
                Data: '受付完了:Webからの問い合わせ',
                Charset: "utf-8"
            }
        },
        // From
        Source: FROM
    };

    const result = {statusCode: 200};
    try {
        const response = await ses.sendEmail(params).promise();
        console.log('Response: ', response);
        result.body = 'OK!!!!!';
    } catch (error) {
        console.log('Error: ', error);
        result.statusCode = error.code;
        result.body = error.message;
    }
    return result;
};

process.envでは環境変数で設定したキーと値が利用可能(暗号化も可能)

最後に起動するLambdaのテスト

  1. image.png テストイベントの選択をクリック
  2. ↓のようにテストを作成(※emailキーにはSESで登録したアドレスを記載すること)
    image.png
  3. image.png をクリックしてテスト実行
  4. 成功となればOK。失敗ならログを確認。
    image.png
  5. テストでemailキーに指定したアドレスにメールが配信されていればOK

最初に起動するLambdaを作成

  1. Lambdaのコンソールページへ移動
  2. image.png をクリック
  3. image.png を選択
  4. image.png 適当な関数名を入力
  5. ランタイムはNode.js 12.xを選択
  6. そのほかはデフォルトのままでOK
  7. image.png をクリック
  8. 環境変数へ配信元のアドレスと受付先のアドレスを入力
    image.png
  9. 実行ロールはSESとLambdaの権限を追加
    image.png
  10. 関数コードへ↓のコードを実装
index.js
'use strict'
const SES = require("aws-sdk/clients/ses");
const ses = new SES({ region: "us-east-1" }); // 米国東部(バージニア北部)
const TO = [process.env.TO]; // 環境変数からの値を取得
const FROM = process.env.FROM;

// 次のLambdaを起動するための設定
const Lambda = require("aws-sdk/clients/lambda");
const lambda = new Lambda({ region: "ap-northeast-1" });

exports.handler = async (event) => {
    const name = event.form.name;
    const email = event.form.email;
    const dept = event.form.dept;
    const body = event.form.body;
    const sesParams = {
        Destination: {
            ToAddresses: TO
        },
        Message: {
            Body: {
                Text: {
                    Data: [
                        '[名前] : ' + name,
                        '[メールアドレス] : ' + email,
                        '[所属部署] : ' + dept,
                        '[お問い合わせ] : ' + "\n" + body,
                    ].join("\n"),
                    Charset: "utf-8"
                }
            },
            Subject: {
                Data: 'Webからの問い合わせ',
                Charset: "utf-8"
            }
        },
        // From
        Source: FROM
    };

    // 次のLambdaに送るデータ
    const payload = { name, email, dept, body };
    console.log('payload: ', payload);
    const lambdaParams = {
        FunctionName: "inpuirySendMailForm_2nd", // 最後に起動するLambdaの名称
        InvocationType: "Event",
        Payload: JSON.stringify(payload)
    };

    const result = {statusCode: 200};
    try {
        const response = await ses.sendEmail(sesParams).promise();
        console.log('Response: ', response);
        const callLambda = await lambda.invoke(lambdaParams).promise();
        console.log("Lambda Response: ", callLambda);
        result.body = 'OK!!!!!';
    } catch (error) {
        console.log('Error: ', error);
        result.statusCode = error.code;
        result.body = error.message;
    }
    return result;

};

最初に起動するLambdaのテスト(次のLambdaも起動する)

  1. image.png テストイベントの選択をクリック
  2. ↓のようにテストを作成
    image.png
  3. image.png をクリックしてテスト実行
  4. 成功となればOK。失敗ならログを確認。
    image.png
  5. テストでemailキーに指定したアドレスに受付完了メールが配信され、環境変数でTOに指定した受付先アドレスへ問い合わせ内容が配信されていればOK

APIGatewayの設定

  1. image.pngをクリック
  2. REST API構築を選択
    image.png
  3. API名説明を入力してAPIの作成をクリック(他はデフォルト)
    image.png
  4. アクション⇒リソースの作成を選択⇒リソース名を入力(リソースパスは自動入力)⇒CORSを有効化⇒リソースの作成をクリック
  5. アクション⇒メソッドの作成⇒POSTを選択⇒セットアップで最初に起動するLambda関数名を入力⇒保存をクリック
  6. 統合リクエストをクリック⇒マッピングテンプレートを追加⇒Content-Typeはapplication/json⇒テンプレートには↓を入力⇒保存をクリック
{
    "form": {
        "name":  "$util.escapeJavaScript($input.path('$.name'))",
        "email": "$util.escapeJavaScript($input.path('$.email'))",
        "dept":  "$util.escapeJavaScript($input.path('$.dept'))",
        "body":  "$util.escapeJavaScript($input.path('$.body'))"
    }
}

APIGatewayのテスト

  1. テストを実行する
    image.png
  2. Lambdaが起動してメールが配信されればOK

APIGatewayのステージを作成

今回はv1というステージを作成

APIGatewayのデプロイ

リソース⇒アクション⇒APIのデプロイを選択⇒v1ステージを選択して⇒デプロイをクリック

発行されたURLをjavascriptのURL変数へ割り当て

全体の動作確認

問い合わせフォームへSESで登録したアドレスと必要事項を入力し、送信ボタンをクリックして動作を確認

実用化に向けて

  • LambdaのSDK呼び出しは動作確認ができているバージョンが呼び出されるようにする必要あり。
  • SESの上限緩和申請が必要。
  • APIGatewayでリソースポリシーを指定して不要なアクセスを防ぐ

最後に

かなり大雑把に書きました。
小規模利用なら十分かと思います。
役に立つかな?
計画性なく作ってしまったのでここからブラッシュアップ

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

AWSでsshコマンドで接続できない場合の対処

AWSでEC2インスタンスを作成した際にキーペアを作成してダウンロードしたかと思います。
※キーペアとは拡張子.pemのことです。

キーペアが存在しているディレクトリにまでcdコマンドを使い移動します。

$ cd downloads
rooter:downloads ユーザー名
私の場合downloadsディレクトリに中にキーペアが存在しています。

ディレクトリまで移動ができましたら下記sshコマンド実行!
$ ssh -i "book.pem" ec2-user@52.218.324.29

スクリーンショット 2020-01-14 19.24.14.png

接続できました!

@の後ろは人によるパブリックアドレスが違います。
下記画像でいうところのIPv4パブリックIPと赤枠で囲っている部分です。

スクリーンショット 2020-01-14 19.19.02.png

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

AWSでsshコマンドで接続できない場合2STEPで対処しよう!

以前AWSを使用していてsshで接続ができていたのに、寝て起きたら接続できなくなり焦ったのでまとめてみました。(; ・`д・´)

1.まずAWSでEC2インスタンスを作成した際にキーペアを作成してダウンロードしたかと思います。
※キーペアとは拡張子.pemのことです。

キーペアが存在しているディレクトリにまでcdコマンドを使い移動します。

$ cd downloads
rooter:downloads ユーザー名
$ pwd
User/ユーザ名/downloads
※私の場合downloadsディレクトリの中にキーペアが存在しています。

2.ディレクトリまで移動ができましたら下記sshコマンド実行!
$ ssh -i "book.pem" ec2-user@13.218.324.29
※book.pemとは私のキーペア名です。

スクリーンショット 2020-01-14 19.24.14.png

接続できました!

@の後ろはパブリックアドレスが人により違います。
下記画像でいうところのIPv4パブリックIPと赤枠で囲っている部分です。

スクリーンショット 2020-01-14 19.19.02.png

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

【AWS】SSM Session Managerの利用方法について

Session Manager用のEC2ロールを作成する

アタッチするポリシー

  • AmazonSSMManagedInstanceCore
    System Managerサービスコア機能にアクセスするために必要なアクセス許可
  • AmazonSSMDirectoryServiceAccess
    Windows Server の EC2 インスタンスを Microsoft AD ディレクトリに結合する場合にのみ必要なアクセス許可
  • CloudWatchAgentServerPolicy
    メトリクスを読み取り、インスタンスにデータのログを記録して、Amazon CloudWatch に書き込むために、インスタンスにCloudWatchエージェントをインストールして実行する場合に必要なアクセス許可

EC2へのロール割り当て

設定したいEC2インスタンスを選択し、IAMロールの割り当てを押します。
image.png

設定したいIAMロールを選択し、適用をクリックします。

EC2の再起動を行い、設定を反映させます。
再起動できない場合は、SSMサービスの再起動でも良いそうです!
※ 15分ほど待てば反映されます。

接続してみる

AWS System Managerのセッションマネージャーの項目に移動し、セッションの開始を選択します。
image.png

正しく接続されていると思います。

プライベートEC2に接続する為には

VPC内のプライベートなEC2を作成して以下の手順をおこなってください。

1.SSMエージェントの確認

SSMエージェントがインスタンスにインストールされていることを確認します。

2.IAMロールの作成

上記で作成したSystem Manager用のIAMロールを作成してください。

3.IAMロールの割り当て

上記で作成したSystem Manager用のIAMロールをEC2に割り当ててください。

4.VPC IDとSubnet IDの確認

Amazon EC2コンソールを開いてからインスタンスを選択し、VPC IDSubnet IDをメモします。

5. エンドポイントの作成 × 3個

System Manager用のVPCエンドポイントを3つ作成します。

1個目 : com.amazonaws.region.ssm

こちらのページで作成します。
image.png

サービスを名前で検索をクリックし、
Service Namecom.amazonaws.region.ssm を入力し、検証を押します。
regionの値は自分の環境で置き換えてください。
東京の場合:com.amazonaws.ap-northeast-1.ssm

VPC で、ご自身のインスタンスの VPC ID を選択します。
サブネット でインスタンスの Subnet ID を選択します。
image.png

Enable Private DNS Nameこのエンドポイントで有効にする を選択します。
Security Group をクリックし、既存のセキュリティグループを選択するか、新しいセキュリティグループを作成します。セキュリティグループはポート 443 でインスタンスからのインバウンドトラフィックを許可する必要があります。
今回は、新しくセキュリティーグループを作成します。
image.png

セキュリティーグループの作成をクリックし、適当な「名前」と「説明」を入力します。
また、VPCは現在、対象としているVPCを選択してください。
セキュリティーグループの新規作成が完了したら、インバウンドのルールを設定します。
image.png

タイプでHTTPSを選択し、
ソースは、操作したいEC2インスタンスが所属するSubNetのCRIDを選択します。
image.png

新しいセキュリティグループを作成する場合は、Group ID をメモします。

エンドポイントの作成画面へ戻り、先ほど作成したセキュリティーグループを選択します。
image.png

2個目 : com.amazonaws.region.ec2messages

Service Namecom.amazonaws.region.ec2messages を入力し、検証を押します。
regionの値は自分の環境で置き換えてください。
東京の場合:com.amazonaws.ap-northeast-1.ec2messages
VPCとサブネット・セキュリティーグループも1個目と同様に選択します。

3個目 : com.amazonaws.region.ssmmessages

Service Namecom.amazonaws.region.ssmmessages を入力し、検証を押します。
regionの値は自分の環境で置き換えてください。
東京の場合:com.amazonaws.ap-northeast-1.ssmmessages
VPCとサブネット・セキュリティーグループも1個目と同様に選択します。

接続してみます

選択する欄に表示され接続する事ができました。

参考サイト

Systems Manager を使用してインターネットアクセスなしでプライベート EC2 インスタンスを管理できるように、VPC エンドポイントを作成するにはどうすればよいですか?

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

無駄なEBSスナップショットをLambdaで定期削除する

「不要になったAMIを登録解除したけど、EBSスナップショットは消し忘れちゃって無駄なスナップショットがたくさん残っちゃってる...」ってこと結構あると思います。
こういう明らかに不要なスナップショットは、Lambdaの定期実行などを利用して自動的に削除されるようにしちゃいましょう。

無駄なスナップショットを残しておくと、スナップショットの管理が煩雑になるし、そのスナップショットにも費用がかかるので、消しちゃうに越したことはありません。

まずAMI登録解除時に消し忘れたスナップショットを一括削除するLambdaは以下のようになります。

import boto3

ec2 = boto3.client("ec2")

def lambda_handler(event, context):
    #AMIと共に作られたsnapshotを抽出
    response = ec2.describe_snapshots(
            Filters=[
                {
                    'Name': 'description',
                    'Values': [
                        'Created by CreateImage*',
                    ]
                },
                {
                    'Name': 'owner-id',
                    'Values': ['************'] #AWSアカウントID
                }
            ]
        )


    is_unnecessary = {}
    for snapshot in response['Snapshots']:
        is_unnecessary[snapshot['SnapshotId']] = True

    #現在登録中のAMIを抽出
    response = ec2.describe_images(
        Filters=[
                {
                    'Name': 'owner-id',
                    'Values': ['************'] #AWSアカウントID
                }
            ]
    )

    #現在登録中のAMIで使われているsnapshotのみFalseにする
    for image in response['Images']:
        for bdm in image['BlockDeviceMappings']:
            try:
                is_unnecessary[bdm['Ebs']['SnapshotId']] = False
            except KeyError:
                continue


    i = 0
    for snapshot_id in is_unnecessary.keys():
        if is_unnecessary[snapshot_id]:
            ec2.delete_snapshot(SnapshotId=snapshot_id)
            i = i + 1

    print('Delete ' + str(i) + ' Snapshots')

これをCloudWatch Eventsを使って定期実行させます。
cron式を使えば簡単に定期実行させることができます。
Schedule Expressions for Rules
例えば月次実行とかにしたい場合は0 0 1 * ? *とかで大丈夫です。

これで定期的に無駄なEBSスナップショットが削除されるようになります。

※注意: ここでいう「不要なスナップショット」はAMI登録解除時に消し忘れたスナップショットのことだけを指します。各々が手動で作成したスナップショットは、タグ付けなどをして自己で管理してください。

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

CodebuildでS3に静的ホスティングを自動化

概要

  • 10分程で設定可能です。
  • AWS Codebuildでビルドを行う。
  • Amazon S3に静的ホスティングをする。
  • GithubをWebhookして、上記を自動化する。

登場人物の紹介

AWS Codebuild

codebuild.jpg

AWS CodeBuild は、クラウドで動作する完全マネージド型のビルドサービスです。CodeBuild はソースコードをコンパイルし、ユニットテストを実行して、すぐにデプロイできるアーティファクトを生成します。CodeBuild により、独自のビルドサーバーのプロビジョニング、管理、スケーリングが不要になります。Apache Maven、Gradle などの一般的なプログラミング言語とビルドツール用のパッケージ済みのビルド環境を提供します。CodeBuild のビルド環境をカスタマイズして、独自のビルドツールを使用することもできます。CodeBuild​ はピーク時のビルドリクエストに合わせて自動的にスケーリングします。 公式

要するに、『ビルドを行い、ユニットテストを実行し、すぐにデプロイできるバイナリファイル的なものを作成する』です。

Amazon S3

Amazon S3.png

Amazon S3は、Amazon Simple Storage Service はインターネット用のストレージサービスです。また、ウェブスケールのコンピューティングを開発者が簡単に利用できるよう設計されています。
Amazon S3 のウェブサービスインターフェイスはシンプルで、いつでも、ウェブのどこからでも容量に関係なくデータを格納および取得できます。これにより、すべての開発者が、スケーラブルで信頼性が高く、かつ高速で安価なデータストレージインフラストラクチャを利用できるようになります。このインフラストラクチャは、Amazon が使用しているウェブサイトのグローバルネットワークと同じものです。このサービスの目的は、規模の拡大や縮小のメリットを最大限に活かし、開発者に提供することです。

要するに、『ほぼ無限大に入るストレージ』です。公式

Github

github.png

省略

以上で登場人物の紹介を終わります。

具体的な設定等

ディレクトリ構造

AWSでCodeBuildする際は、ディレクトリ構造を正確に把握しておくことが非常に重要です。
私の場合は、以下のようなディレクトリ構造になっています。
今回、AWS Codebuildでビルドを行い、デプロイしようとしているのは、frontディレクトリの中身です。 これらのコードをbackendやdatabaseも含めてGithubの任意のリポジトリにPushしておきます。
スクリーンショット 2020-01-14 12.06.03.png

package.jsonのscript部分の内容

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

buildspec.ymlの内容

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 10
  pre_build:
    commands:
      - echo Installing source NPM dependencies...
      - cd front
      - yarn install
  build:
    commands:
      - yarn build
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Distributing to S3...
      - aws s3 sync --exact-timestamps --delete build s3://[デプロイ先バケット名]

ディレクトリ構造によっては、pre_buildのcd frontなどが不必要 (各々のディレクトリ構造に合わせて下さい)。

CodeBuild設定

  1. AWSマネジメントコンソールにログイン
  2. ログイン後、CodeBuildサービスを選択し、プロジェクトの作成を押下
  3. 任意のプロジェクト名を入力
    スクリーンショット 2020-01-14 12.13.49.png

  4. 送信元の設定
    ・ソースプロバイダ・・Github
    ・リポジトリ・・Githubアカウントのリポジトリ
    ・Githubリポジトリ・・先ほどのコードをPushしたリポジトリを選択
    スクリーンショット 2020-01-14 12.14.47.png

  5. プライマリソースのウェブフックイベント設定
    ・ウェブフックオプショナル・・・チェック
    ・イベントタイプ・・・PULL_REPULL_REQUEST_MERGEDを選択(任意のイベントタイプを選択可)

スクリーンショット 2020-01-14 12.15.25.png

6. 環境の設定
・環境イメージ・・・マネージド型イメージ
・オペレーティングシステム・・・Ubuntsu
・ランタイム・・・Standard
・イメージ・・・最新のもの
・イメージのバージョン・・・最新のもの
・環境タイプ・・・Linux

スクリーンショット 2020-01-14 12.15.53.png

7. サービスロールの設定
以下の画像では、既存のサービスロールとなっていますが、初めて使用する方は、新しいサービスロールを選択してください。選択すると、自動でロールのARNが入力されます。

スクリーンショット 2020-01-14 12.16.37.png

8. Buildspec設定&アーティファクト設定
・ビルド仕様・・・buildspecファイルを使用
・Buildspec名オプショナル・・・front/.buildspec.yml(ディレクトリ構造を紹介した際に、frontの階層の下に、.buildspec.ymlファイルを置いているのがわかるように、今回ビルドする際の設定ファイルをfront/.buildspec.ymlに置いているので、ここを指定します。)
・アーティファクト・・・特に設定しない

スクリーンショット 2020-01-14 12.17.15.png

以上でCodebuildの設定が終了です。

Amazon S3設定

  1. AWSマネジメントコンソールにログイン
  2. ログイン後、Amazon S3サービスを選択し、バケットの作成を押下
  3. 任意のバケット名を入力し、作成

スクリーンショット 2020-01-14 12.22.59.png

4. 作成後、バケットのプロパティの、Static website hostingを選択
スクリーンショット 2020-01-14 12.23.32.png

5. Static website hosting設定
・インデックスドキュメント・・・任意のルートファイル(大抵はindex.htmlとなる。)

スクリーンショット 2020-01-14 12.23.54.png

6. バケットのプロパティのアクセス権限設定のブロックパブリックアクセス設定
以下の画像のように設定する。
スクリーンショット 2020-01-14 12.31.52.png

7. バケットのプロパティのアクセス権限設定のバケットポリシー設定(セキュリティレベルは自己のサービスなどに合わせて、変更お願いします。)
スクリーンショット 2020-01-14 12.33.05.png

以上でAmazon S3の設定が終了です。

動作確認

Githubのリポジトリに対して、PullRequestがマージされると、CodeBuildが自動で走り、以下の画像のように全てSuccessとなると成功です。S3のホスティングしているURLにアクセスすると、任意のものが表示されるのを確認してください。

スクリーンショット 2020-01-14 12.25.08.png

以上で全ての設定が終了です。

次回はバックエンド側(Go)のデプロイフローとして、CodePipeline, CodeBuild, CodeDeploy, ECSを使用した方法について書こうと思います。 

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

AWS Amplify フレームワークの使い方Part4〜API実践編〜

はじめに

今回は、AmplifyのAPI設定後、Nuxt.jsのアプリケーション内でどのように利用しているかを中心に書いていきます。

Nuxt.jsの構成

基本的にDB取得のAPIの呼び出しはstoreのactionでのみ行っています。取得したDBデータをmutationでstateに書込み、必要に応じてgettersから取得する形にしています。

準備

store/index.jsの上部に以下を宣言します。

store/index.js
import { API, graphqlOperation } from 'aws-amplify'
import * as gqlQueries from '../graphql/queries' // read
import * as gqlMutations from '../graphql/mutations' // create, update, delete
import * as gqlSubscriptions from '../graphql/subscriptions' // 監視

importしたこれらを使っていくことで、CRUDの実行とデータの監視を行うことができます。
API設定時に、自動生成されるgraphqlフォルダ内には、3つのファイルが作られており、その中に記載されているAppSyncを実行するqueryやmutationを使っていきます。

以下、user情報を取得することを例に解説していきます。

Queries

getUser

idを指定してユーザーの情報を取得します。まず、queris.jsには以下のように定義されているので、index.jsから以下のように呼び出すことで、user情報が取得できます。

graphql/queries.js
export const getUser = `query GetUser($id: ID!) {
  getUser(id: $id) {
    id
    name
    age
    createdAt
    updatedAt
  }
}
`
store/index.js
const user = await API.graphql(
  graphqlOperation(gqlQueries.getUser, {
    id: 'tanaka' // userテーブルの取得したいデータのID
  })
)
const userData = user.data.getUser // ユーザーのDB情報

返り値

以下のように取得しますが、GraphQLで取得したデータは様々な情報を含んだオブジェクトで返ってくるため、user.data.getUserとすることで、実際にほしいユーザー情報を取得できます。

補足

基本的には、自動生成されたgraphqlに加工を加えたものを利用していますが、イレギュラーで必要な値だけ取得したいときは、以下のようqueryを自分で宣言した取得もできます。

index.js
// 例:IDがkatoのニックネームだけの情報が欲しい場合

// クエリを個別に宣言
const _query = `query GetUser($id: ID!) {
  getUser(id: $id) {
    name
  }
}
`
// 実行
const user = await API.graphql(
  graphqlOperation(_query, {id : 'tanaka'})
)
const userData = user.data.getUser // name情報のみが入っています。

listUsers

ユーザーの一覧情報を取得したいときは、queris.jsに宣言されているlistを使います。

graghql/queris.js
export const listUsers = `query ListUsers(
  $filter: ModelUserFilterInput 
  $limit: Int
  $nextToken: String
) {
  listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
    items {
      id
      name
      age
      createdAt
      updatedAt
    }
    nextToken
  }
}
`
store/index.js
const users = await API.graphql(
  graphqlOperation(gqlQueries.listUsers)
)
const usersList = users.data.listUsers.items

返り値

listの場合は、const usersList = users.data.listUsers.itemsにすることで、user情報をlist型で取得することができます。

filter

filterを宣言することで、返り値をフィルタリングすることができます。filterの特徴として、データはすべて取得した上で、返り値にフィルタリングをかけるだけのため、データの取得コストには差はありません。(SQLのクエリで必要な情報だけ取得するのとは違う)

limit

取得するデータの個数です。指定しない場合のデフォルト値は10(多分)となっています。スキーマ設計時にソートキーを指定してない場合は、sortの順番としては、IDのアルファベット順で返ってきます。

nextToken

次のデータの取得するためのトークン情報です。例えば、100個データがあって、はじめの10個を取得して、次の10個のデータを取得したい場合は、はじめの10個のデータを取得した際のnextTokenを次の10このデータを取得するquery内に引きすとして渡すことで、次の10個のデータが取得できます。

Mutations

createUser

createは以下のように行います。

graghql/mutations.js
export const createUser = `mutation CreateUser($input: CreateUserInput!) {
  createUser(input: $input) {
    id
    name
    createdAt
    updatedAt
  }
}
`
store/index.js
const user = await API.graphql(
  graphqlOperation(gqlMutations.createUser, {
    input: {
      name: '田中太郎',
    }
  })
const userData = user.data.createUser // createしたユーザー情報

返り値

mutations.jsで宣言したものが、返り値として返ってきます。

id

idについては、graphqlのスキーマで IDを設定している場合は、自動で一意なIDを勝手に書き込んでくれます。IDを設定していない場合は、値を渡す必要があります。

createdAt, updateAt

自動で入力してくれます。便利ですね。別記事で詳細はまとめる予定ですが、@keyでソートキーに指定する場合は、自動入力の機能は使えないので注意が必要です。(ソートキーに設定するためにnon-nullにする必要があり、non-nullの場合は自動入力の機能が使えないため)

updateUser

updateしたいデータのIDとupdateしたい情報を渡すだけで更新が可能です。

graghql/mutations.js
export const updateUser = `mutation UpdateUser($input: UpdateUserInput!) {
  updateUser(input: $input) {
    id
    name
    createdAt
    updatedAt
  }
}
`
store/index.js
// 例:
const user = await API.graphql(
  graphqlOperation(gqlMutations.updateUser, {
    input: {
      id: 'tanaka',
      name: '田中次郎'
    }
  })
)
const userData = user.data.updateUser // 更新したユーザー情報

返り値

updateした情報だけが返ってくるのではなく、mutation.js内で宣言したデータがすべて返り値として返ってきます。

deleteUser

削除も簡単で、削除したいデータのIDを渡すだけで実行できます。nameだけ削除したいなどのカラム指定の削除はできません。

graghql/mutations.js
export const deleteUser = `mutation DeleteUser($input: DeleteUserInput!) {
  deleteUser(input: $input) {
    id
    name
    createdAt
    updatedAt
  }
}
`
store/index.js
const user = await API.graphql(
  graphqlOperation(gqlMutations.deleteAnswer, {
    input: { id: 'tanaka' }
  })
)
const userData = user.data.deleteAnswer // 削除したユーザー情報

返り値

deleteしたuser情報のmutation.js内で宣言したデータがすべて返り値として返ってきます。

Subscriptions

onCreateUser,onUpdateUser,onDeleteUser

それぞれ、user情報のcreate、update、deleteを検知してそのユーザー情報を取得できます。
現在作成しているサービスでは、リアルタイム性は求められていないので、未検証な部分です。

おわりに

これで、最低限の基本的なCRUDはできるようになったかと思います。AmplifyのAPIの肝はここではなく、スキーマ設定での @model、@auth、@key、@connectionなどが何よりも重要になってきますので、そのあたりについては別記事で詳しくまとめていきたいと思います。

関連記事

AWS amplify フレームワークの使い方Part1〜Auth設定編〜
AWS Amplify フレームワークの使い方Part2〜Auth実践編〜
AWS Amplify フレームワークの使い方Part3〜API設定編〜

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

マストドン構築1日目 on AWS

経緯

半年ほど前に AWS & Docker ド素人の状態から
なんとかインスタンスをたてて運用していました。

EC2の上にDockerで本番運用していましたが、だんだん辛くなってきたのでDockerから降ろすことにしました。
その作業ログです。

環境 / ツール

macOS Mojavi 10.14.6
AWS

参考にしたもの

マストドンGitHub
https://github.com/tootsuite/mastodon
公式ドキュメント
https://docs.joinmastodon.org/admin/prerequisites/

AWSでEC2インスタンスの作成

環境を合わせるため、以前の構築時に使用したAMIを利用します。
なぜeks用Ubuntuを利用したのかは謎です。半年前の自分に問い詰めたいです。
Ubuntuであれば大丈夫だと思います。
スクリーンショット 2020-01-13 16.55.57.png

  • あと半年間はAWS無料枠でいけるのでt2.microです。
  • セキュリティグループは80番と443番と22番ポートをあけます。
  • EC2インスタンスのたて方はAWSの公式チュートリアルが参考になります。

サーバー内での作業

$ ssh ubuntu@3.112.***.*** -i ~/.ssh/hoge.pem

rootユーザーで作業します。

$ sudo su -

以下、公式ドキュメントの手順通りに進めます。
https://docs.joinmastodon.org/admin/prerequisites/

システムパッケージを更新。

$ apt update && apt upgrade -y

viもvimも入っていなかったのでいれます。

$ apt install vim

fail2banをインストール

fail2banをインストールして、繰り返しログイン試行をブロック。

$ apt install fail2ban

/etc/fail2ban/jail.conf を編集。

[DEFAULT]
destemail = your@email.here
sendername = Fail2Ban
[sshd]
enabled = true
port = 22
[sshd-ddos]
enabled = true
port = 22

最後にfail2banを再起動。

$ systemctl restart fail2ban

ファイアウォールをインストールし、SSH、HTTP、HTTPSポートのみをホワイトリストに登録。

$ apt install -y iptables-persistent

rules.v4ファイルを編集します。

$ vim /etc/iptables/rules.v4
*filter
# Allow all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow all outbound traffic - you can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
# Allow SSH connections
# The -dport number should be the same port number you set in sshd_config
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
# Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Reject all other inbound - default deny unless explicitly allowed policy
-A INPUT -j REJECT
-A FORWARD -j REJECT

COMMIT

手動で読み込みます。

$ iptables-restore < /etc/iptables/rules.v4

以下のドキュメントを参考にして
必要なものをインストールしていきます。

https://docs.joinmastodon.org/admin/install/

Node.jsをインストール

$ curl -sL https://deb.nodesource.com/setup_8.x | bash -

Yarnをインストール

$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

システムパッケージをインストール

$ apt update
$ apt install -y \
  imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core \
  g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf \
  bison build-essential libssl-dev libyaml-dev libreadline6-dev \
  zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev \
  nginx redis-server redis-tools postgresql postgresql-contrib \
  certbot python-certbot-nginx yarn libidn11-dev libicu-dev libjemalloc-dev

Mastodonユーザーを作成

$ adduser --disabled-login mastodon

ユーザーを切り替えます。

$ su - mastodon

Rubyのインストール

rbenvを使用してRubyのバージョンを管理します。

$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ cd ~/.rbenv && src/configure && make -C src
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ exec bash
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

正しいRubyバージョンをインストール。

$ RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.6.5
$ rbenv global 2.6.5

ruby_2.6.0に同梱されているデフォルトのgemバージョンは最新のバンドラーと互換性がないため、gemを更新。

$ gem update --system

Bundlerをインストール

$ gem install bundler --no-document

rootユーザーに戻る。

$ exit

番外 ~PostgreSQLのバージョンは9.6~

事情: Docker上にあったPostgreSQLのバージョンは9.6でした。
UbuntuにPostgreSQLをインストールするとデフォルトでバージョン10が入るため、いったん10を削除して
9.6バージョンを指定して入れる必要があります。
(その場合は、別途aptリポジトリ※後述を作成しなければいけませんでした)

以下これから新規でインスタンスを建てる場合には、不要な工程です。
需要が謎ですが、一応ハマりポイントだと感じたので自分用の備忘ログです。

/etc/postgresql 以下で

$  ls
10

=> 「9.6に揃えたいなぁ」

$ apt remove postgresql postgresql-contrib

(参照) https://www.postgresql.org/download/linux/ubuntu/

ubuntuのaptリポジトリを追加。
18.04なので

$ vim /etc/apt/sources.list.d/pgdg.list
deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main

リポジトリ署名キーをインポートし、パッケージリストを更新。

$ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update

9.6を指定してインストール。

$ apt-get install postgresql-9.6

PostgreSQLの設定

以下を参考にPostgreSQLの設定を行います。
https://pgtune.leopard.in.ua/#/

こんな便利なジェネレーターツールがあったんですね。
DBのバージョンやインスタンスのサイズを入力していきます。
PostgreSQLの設定については別途記事
PostgreSQLの設定 ~PGTuneを使ってみて~
に、まとめてみました。
スクリーンショット 2020-01-14 15.18.24.png

$ vim /etc/postgresql/9.6/main/postgresql.conf

設定が完了したら、再起動です。

$ systemctl restart postgresql

Mastodonが使用できるPostgreSQLユーザーを作成。

$ sudo -u postgres psql
CREATE USER mastodon CREATEDB;
\q

次回予告

2日目はマストドンのセットアップです。
作業が進み次第、Qiitaに作業ログを残したいと思います。

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

AWS IAMロール、IAMポリシーについて整理(初心者向け)

AWSサービスを利用する際に、(AWS)IAMのロールとポリシーについてよく分からなかったので整理しました。

AWS初心がまとめる記事なので、解釈にずれが生じていたり、理解が浅い部分もあると思いますが
ざっくりした理解には役立つかと思います。

ロールやポリシーの整理の前に、まずAWS IAMというサービスについて整理します。

IAMとは?

「Identity and Access Management」の略称で
(AWS)リソースへのアクセスを安全に管理するためのウェブサービスです
IAM を使用して、リソースを使用するために認証 (サインイン) され、許可された (アクセス許可を持つ) ユーザーを制御します。」(AWS引用)

AWSのサービスはたくさんあって、いろんなサービスを組み合わせたり、いろんなアカウントでサービスを利用するため、IAMを使えば安心してサービスを管理・利用できるようになるわけですね。
(自分で書きながら、やっとわかった気がしますw)

続いて、ロールとポリシーについてもまとめていきます。

IAMポリシーとは?

  • 「誰が」
  • 「どのAWSサービスの」
  • 「どのリソースに対して」
  • 「どんな操作を」
  • 「許可する(許可しない)」』

という内容をまとめたルールです。
設定項目と選択肢は、AWSが予め用意してくれているので、各項目を自分で選んで組み合わせるだけのアクセス設定のイメージです。

ポリシーでは結構細かくアクセス制限を設定することができるんですね。
この説明でやっとわかりました!

IAMロールとは?

複数のIAMポリシーをまとめたもの

例えば、
「Amazon S3を利用できるユーザーは UserA, UserBにして、
UserAはデータの更新(編集)はできるけど、UserBはデータの追加だけで編集まではできないよ」的な感じでしょうか。

ポリシーもいろんなバリエーションがあるから、サービスごとにある程度分けて管理するにはロールがあると便利そうです。

ついでにIAMロール、ポリシー関連の資料には「アタッチ」という言葉がたくさん出てきますので、それについても私なりの解釈を少し説明します。

アタッチとは?

『付着する、帰属する、取り付ける、添付する、加える、所属させる、などの意味を持つ英単語。 名詞形は「アタッチメント」(attachment)、対義語は「デタッチ」(detach)。ITの分野では、システム上で何らかの主体に対象を取り込んで有効にする動作や操作などをこのように呼ぶ。』(e-Words)とあり、

イメージ的には部品の取り付け工事機能を使えるようにする操作(機能の有効化)をまとめて表現した言葉のようです。

ひとまず、これでスッキリしました。
もっと詳しい説明は以下のページが参考になると思いますので、
そちらも合わせてごらんください。

追加で他にもわかりやすいページをご存じの方がいらっしゃれば、コメントいただけるとありがたいです。

参考ページ
AWSのIAMロールとポリシーの違い
AWS IAMポリシーを理解する

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

AWS IAMロール、ポリシーについて整理

AWSサービスを利用する際に、(AWS)IAMのロールとポリシーについてよく分からなかったので整理しました。

AWS初心がまとめる記事なので、解釈にずれが生じていたり、理解が浅い部分もあると思いますが
ざっくりした理解には役立つかと思います。

ロールやポリシーの整理の前に、まずAWS IAMというサービスについて整理します。

IAMとは?

「Identity and Access Management」の略称で
(AWS)リソースへのアクセスを安全に管理するためのウェブサービスです
IAM を使用して、リソースを使用するために認証 (サインイン) され、許可された (アクセス許可を持つ) ユーザーを制御します。」(AWS引用)

AWSのサービスはたくさんあって、いろんなサービスを組み合わせたり、いろんなアカウントでサービスを利用するため、IAMを使えば安心してサービスを管理・利用できるようになるわけですね。
(自分で書きながら、やっとわかった気がしますw)

続いて、ロールとポリシーについてもまとめていきます。

ポリシーとは?

  • 「誰が」
  • 「どのAWSサービスの」
  • 「どのリソースに対して」
  • 「どんな操作を」
  • 「許可する(許可しない)」』

という内容をまとめたルールです。
設定項目と選択肢は、AWSが予め用意してくれているので、各項目を自分で選んで組み合わせるだけのアクセス設定のイメージです。

ポリシーでは結構細かくアクセス制限を設定することができるんですね。
この説明でやっとわかりました!

ロールとは?

複数のポリシーをまとめたもの

例えば、
「Amazon S3を利用できるユーザーは UserA, UserBにして、
UserAはデータの更新(編集)はできるけど、UserBはデータの追加だけで編集まではできないよ」的な感じでしょうか。

ポリシーもいろんなバリエーションがあるから、サービスごとにある程度分けて管理するにはロールがあると便利そうです。

ついでにIAMロール、ポリシー関連の資料には「アタッチ」という言葉がたくさん出てきますので、それについても私なりの解釈を少し説明します。

アタッチとは?

『付着する、帰属する、取り付ける、添付する、加える、所属させる、などの意味を持つ英単語。 名詞形は「アタッチメント」(attachment)、対義語は「デタッチ」(detach)。ITの分野では、システム上で何らかの主体に対象を取り込んで有効にする動作や操作などをこのように呼ぶ。』(e-Words)とあり、

イメージ的には部品の取り付け工事機能を使えるようにする操作(機能の有効化)をまとめて表現した言葉のようです。

ひとまず、これでスッキリしました。
もっと詳しい説明は以下のページが参考になると思いますので、
そちらも合わせてごらんください。

追加で他にもわかりやすいページをご存じの方がいらっしゃれば、コメントいただけるとありがたいです。

参考ページ
AWSのIAMロールとポリシーの違い
AWS IAMポリシーを理解する

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

AWS IAMロール、IAMポリシーについて整理

AWSサービスを利用する際に、(AWS)IAMのロールとポリシーについてよく分からなかったので整理しました。

AWS初心がまとめる記事なので、解釈にずれが生じていたり、理解が浅い部分もあると思いますが
ざっくりした理解には役立つかと思います。

ロールやポリシーの整理の前に、まずAWS IAMというサービスについて整理します。

IAMとは?

「Identity and Access Management」の略称で
(AWS)リソースへのアクセスを安全に管理するためのウェブサービスです
IAM を使用して、リソースを使用するために認証 (サインイン) され、許可された (アクセス許可を持つ) ユーザーを制御します。」(AWS引用)

AWSのサービスはたくさんあって、いろんなサービスを組み合わせたり、いろんなアカウントでサービスを利用するため、IAMを使えば安心してサービスを管理・利用できるようになるわけですね。
(自分で書きながら、やっとわかった気がしますw)

続いて、ロールとポリシーについてもまとめていきます。

IAMポリシーとは?

  • 「誰が」
  • 「どのAWSサービスの」
  • 「どのリソースに対して」
  • 「どんな操作を」
  • 「許可する(許可しない)」』

という内容をまとめたルールです。
設定項目と選択肢は、AWSが予め用意してくれているので、各項目を自分で選んで組み合わせるだけのアクセス設定のイメージです。

ポリシーでは結構細かくアクセス制限を設定することができるんですね。
この説明でやっとわかりました!

IAMロールとは?

複数のIAMポリシーをまとめたもの

例えば、
「Amazon S3を利用できるユーザーは UserA, UserBにして、
UserAはデータの更新(編集)はできるけど、UserBはデータの追加だけで編集まではできないよ」的な感じでしょうか。

ポリシーもいろんなバリエーションがあるから、サービスごとにある程度分けて管理するにはロールがあると便利そうです。

ついでにIAMロール、ポリシー関連の資料には「アタッチ」という言葉がたくさん出てきますので、それについても私なりの解釈を少し説明します。

アタッチとは?

『付着する、帰属する、取り付ける、添付する、加える、所属させる、などの意味を持つ英単語。 名詞形は「アタッチメント」(attachment)、対義語は「デタッチ」(detach)。ITの分野では、システム上で何らかの主体に対象を取り込んで有効にする動作や操作などをこのように呼ぶ。』(e-Words)とあり、

イメージ的には部品の取り付け工事機能を使えるようにする操作(機能の有効化)をまとめて表現した言葉のようです。

ひとまず、これでスッキリしました。
もっと詳しい説明は以下のページが参考になると思いますので、
そちらも合わせてごらんください。

追加で他にもわかりやすいページをご存じの方がいらっしゃれば、コメントいただけるとありがたいです。

参考ページ
AWSのIAMロールとポリシーの違い
AWS IAMポリシーを理解する

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

【これからプログラミング&クラウドを始める人向け】AWS Cloud9 を利用して Ruby の開発環境を作ってみる② - AWS Cloud9 の環境構築

はじめに

この記事は 【これからプログラミング&クラウドを始める人向け】AWS Cloud9 を利用して Ruby の開発環境を作ってみる① - AWSアカウント準備編 の続きになります。

AWS Cloud9 のセットアップ

  • 前回作成した IAM ユーザーを利用してコンソールに入り Cloud9 と検索し選択
    image.png

  • AWS Cloud9 のコンソールから「Create environment」を選択
    image.png

  • 環境名を入力 (説明文は任意入力なので無しでもOK)
    image.png

  • 以下図のように Configure settings を行い「Next step」を選択
    image.png

  • 確認画面が表示され、「Create enviroment」を選択
    image.png

  • 選択後以下のような画面が表示される (今回は約2分ほどで構築が完了)
    image.png

  • 完了後、以下のような画面が表示される。これで環境構築は完了です。
    image.png

AWS Cloud9 の画面説明

  • 左側がファイル、右側がテキストエディタ、右下がターミナルとなります
    image.png

  • エディタ領域はタブ型になっています
    image.png

  • これらの設定については右側の歯車アイコンで操作が可能です
    image.png

  • ちなみにブラウザでの表記なので Chrome の翻訳アドオンで日本語化ができます
    image.png

その他 Tips

  • 次回以降コンソールからアクセスした場合、「Open IDE」から構築した環境にアクセス可能です
    image.png

  • EC2 のコンソール画面から自動停止を待たずに環境停止(=課金停止)を行うことも可能です
    image.png

  • Tab Size の設定
    テキストファイルのタブを開いた状態で右下の「Spaces」をクリックすると「Tab Size」が変更できます
    ここで Tab Size を「2」に変更することでタブキーを押したときのスペースの入力回数が設定できます
    image.png

  • スペース等の不可視文字の可視化
    スペースや改行に印をつける設定です。これもテキストファイルのタブを開いた状態で行います
    右下の歯車キーをクリックして Show Invisibles にチェックマークをつけます
    image.png

まとめ

前回作成した IAM ユーザー を使用してクラウド上に IDE を構築し初期設定を行いました。
びっくりするくらいサクッと立ち上がるのでこれからプログラミングを勉強していきたい人に是非試してみてほしいです。
次回以降はプログラムのバージョン管理について説明していきたいと思います。お楽しみに!

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

【これからプログラミング&クラウドを始める人向け】AWS Cloud9 を利用して Ruby の開発環境を作ってみる① - AWSアカウント準備編

はじめに

前置き

プログラミングを勉強しよう!と思ったときに躓くポイントでもある開発環境の構築
今回は AWS を利用してクラウド上に開発環境を作っていく手法を紹介していきます
勉強をスタートしたタイミングで同時にクラウドを触ることでモダンな開発環境も理解していきましょう

AWS Cloud9 とは

  • Cloud9 というブラウザで利用できる IDE (統合開発環境)をAWSが買収
  • AWS の各種サービスと連携しやすいサービスとして2017年末にリリース
  • クラウド最大手からクラウド上での開発サービスを提供し、開発環境もクラウド上で持つトレンドが出来つつある

利用にあたって必要なもの

  • AWS 登録のために以下の登録が必要です

    • メールアドレス
    • クレジットカード(課金が発生しない無料利用も可能)
    • ユーザー認証用の電話番号
    • ブラウザの設定
    • 広告ブロック用のアドオンを導入している場合はオフ

AWS Cloud9 の利用料金の話

  • 課金の仕様

    • AWS Cloud9 の利用そのものは課金対象外(=無料)
    • AWS Cloud9 の実行環境であるサーバ料金(EC2)は課金対象(=有料)
      • ただし無料利用枠内であれば無料
    • 新規アカウント作成から12ヶ月の期間、月間750時間まで無料利用枠で利用可能
      • 1ヶ月間1日24時間利用したとしても無料利用枠内で使用可能
  • 無料利用枠についての参考情報 (公式)

AWS アカウントの準備

ここからは AWS のアカウントが作成できていることを前提に進めていきます

AWS を利用する際に使うアカウントについて

  • ルートアカウント (初回登録で作成されたアカウント)
    • 請求に関連する操作も含め権限範囲が非常に広く、日常利用をしないことが推奨されます
  • IAM ユーザー (このあとに作成するアカウント)
    • 開発など日常的に使用するアカウント
    • IAM ( Identity and Access Management ) という機能を利用して権限の制御が可能

今回作成する IAM ユーザーについて

  • AWS の管理者ユーザー を作成
    • AWS 初学者が一人で利用することが前提
    • 今回作成するユーザーで権限のない操作はルートアカウントで操作を行う

IAM ユーザーの作成

  • ログイン後に検索窓から「IAM」と入力
    image.png

  • IAM の管理画面遷移後に左側のユーザーを選択し、「ユーザーを追加」をクリック
    image.png

  • ユーザー名を入力

  • AWS マネジメントコンソールへのアクセスを選択

  • パスワードのリセットが必要を選択
    image.png

  • グループの作成をクリック
    image.png

  • グループ名を入力し、ポリシー(今回は「AdoministratorAccess」を選択)
    image.png

  • 作成したグループを選択し、次のステップに進みます
    image.png

  • 今回はタグの項目は空欄で進めます
    image.png

  • 内容を確認して「ユーザーの作成」をクリックします
    image.png

  • これでユーザーの作成が完了します。.csv のダウンロードでアカウント情報をDLできます
    image.png

  • .csv ファイルはこのようになっています。今回はプログラムによるアクセスを想定していないいので一部空欄となっています
    image.png

  • 該当情報でアクセスできることを確認しましょう。(初回のパスワード修正が要求されます)
    image.png

  • IAM ユーザーのアクセスは右上にユーザー名が表示されます
    image.png

利用料アラートの設定 (無料利用枠内のため)

目的

  • 想定外の請求を防ぐために設定を推奨します (特に個人利用の場合)
  • 無料利用枠範囲内でのアラートを受信
    • メールをチェックしないと意味がなくなるので要注意

設定方法

  • ルートアカウントでサインインをして「マイ請求ダッシュボードをクリック」
    image.png

  • ダッシュボードから「Biling の設定」をクリック
    image.png

  • 設定項目で各種チェック項目にチェック並びにメールアドレスを入力し「設定の保存」をクリック
    image.png

まとめ

この記事では開発環境構築のために AWS のアカウント作成と無料で使い切るための設定を紹介していきました。
入門者には手間に感じる部分かもしれませんがクラウド上の開発環境を利用する AWS Cloud9 は非常に強力なサービスなので、ぜひ試してみてください!
次回以降の記事から AWS Cloud9 の構築を進めていきます。お楽しみに!

続き→ 【これからプログラミング&クラウドを始める人向け】AWS Cloud9 を利用して Ruby の開発環境を作ってみる② - AWS Cloud9 の環境構築

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

CloudfrontにAWS WAFのManagedRuleを適用してみた

目的

AWS WAFが便利と聞いておりましたが、まだ実運用したことがなかったので環境に適用してみました。
設定完了がゴールです。

構成図

前回の構成図にWAFを追加した形です。
Arch3.png

手順

AWS WAFの作成のため、WEB ACLsを作成します。

WAF1.PNG
リソース名を入力します。

WAF3.PNG

resource typeを選択します。cloudfront構築済環境ですので、cloudfrontを選択し、対象のresourceを選択します。

WAF2.PNG

次に進み、ルールを選択します。今回はmanaged rule groupを選択します。
WAF4.PNG

Add managed rule groupsからルールを選択します。
今回はS3静的サーバレス環境、かつ初めてなので「Admin protection」のみを適用しますが、WordPressやAP+DB構成を採用している場合は別の構成も検討した方がよさそうです。

WAF5.PNG

「Default web ACL action for requests that dont't match any rules」と聞かれるので「Allow」を選択します。
試しておりませんが、blockを選択するとアクセスできなくなると思われます。

WAF6.PNG

Set rule priorityを聞かれるのでルールの優先順位を設定します。今回は一件なのでデフォルトのまま、次へを選択します。

WAF7.PNG

configure metricsを聞かれるので、チェックしてメトリックを設定します。

WAF8.PNG

設定を確認し、create web ACLsをクリックします。

WAF9.PNG

気づき

1.設定の確認が難しそう
ACLの動作確認を行なう方法は、選択したルールに合わせた脆弱性診断を実施することだと考えられますが、簡単には実施できないので代替手段を検討すべきだと考えております。

2.利用料金
他のAWSサービスと同様に、使った分だけ課金されます。
今回設定した内容だとWebACL1つ、ルール一つなので\$5+\$1+\$0.6=$6.6/月ほどの利用料金となる予定です。
他のライセンスに比べて安いてすが、個人サイトでどこまで守るかは要検討だと考えております。Wordpress環境の場合PHP対策のルールやSQLiを利用した方がよいと考えてます。

https://aws.amazon.com/jp/waf/pricing/

リソースタイプ 料金        
Web ACL 5.00USD、月あたり (時間で案分)
ルール        1.00USD、月あたり (時間で案分)
リクエスト 0.60USD 100 万リクエストあたり
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS ElasticBeanstalk 環境を切り替える方法(EB CLI)

はじめに

一つのサービスを提供する場合でも、Blue-Green Deploymentをする時は複数の環境を使用することになります。
EB CLIを使って、 ElasticBeanstalk の環境を切り替える方法をすぐに確認できるようにまとめさせていただきました。

関連リンク

関連リンクを下記に載せておくので、必要であれば参考にしてください。。

EB CLI

Elastic Beanstalk コマンドラインインターフェイス(EB CLI)の略。
Elastic Beanstalk をターミナルからコマンドを操作することで、ローカルリポジトリからの環境の作成、更新、およびモニタリングを簡素化することができる。

数個の EB CLI のコマンドさえ覚えておけば、アプリケーションをデプロイできてしまう素晴らしいサービス・コマンドラインです。

インストール・設定・基本事項

個人的にまとめるよりも、公式にてまとめられているものが非常にわかりやすく理解しやすいと思いますので、リンク先を載せておきます。

環境を切り替える方法(同じアカウント内での変更)

$ eb use ${環境名}

環境を切り替える方法(別のアカウント内での変更)

$ eb use ${環境名} --profile ${別アカウント名}

別アカウントの名前を確認する方法は、下のリンクを参照してみてください。

まとめ

まとめてはみたものの、結局は前に書いた記事と同じ方法+αになってしまいました。

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

EC2 インスタンスの作成手順

マネジメントコンソールへログインし、EC2のダッシュボードへ移動します。

インスタンスの作成

1.EC2>インスタンスのダッシュボードへ移動し、「インスタンスの作成」ボタンをクリック。
スクリーンショット 2020-01-14 6.11.35.png

2.次にマシンの選択。初学者の方は基本的の無料利用枠の下記のマシンを選択。
スクリーンショット 2020-01-14 6.14.24.png

3.インスタンスタイプの選択。こちらも無料利用枠でデフォルトで設定されている「t2.micro」を選択しましょう。
スクリーンショット 2020-01-14 6.17.02.png

4.インスタンスの詳細を設定しましょう。
※VPCを先に設定したりしますがまだ設定してないのでデフォルトのVPCを利用します。後ほど設定します。
スクリーンショット 2020-01-14 6.20.53.png

5.ストレージの追加。ここも基本的に変更を加える箇所はないので次へ
スクリーンショット 2020-01-14 6.24.47.png

6.タグの追加。名前を割り振りましょう
スクリーンショット 2020-01-14 6.29.12.png

7.セキュリティグループの設定。webサーバにつなげるために「HTTP」「HTTPS」も追加します。
スクリーンショット 2020-01-14 6.31.41.png

8.最後に作成するインスタンスの詳細が出るので確認しましょう。
また、起動ボタンを押したらSSH接続する際に必要なキーペアの作成画面が出るので作成しましょう。
作成後インスタンスを作成できます。
※キーペアの作成画面は今回は省かせていただきます。スクリーンショット 2020-01-14 6.38.04.png

作成が完了しました。
スクリーンショット 2020-01-14 6.43.10.png

「running」になることを確認しましょう。
スクリーンショット 2020-01-14 6.46.53.png

インスタンスが無事作成できました!!

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

バーチャルホストとやり方

サイトを5個立ち上げたとき、やったこと。

AWS Beanstalkの利用

バーチャルホスト化

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

バーチャルホストのやり方

サイトを5個立ち上げたとき、やったこと。

AWS Beanstalkの利用

バーチャルホスト化

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

LambdaでCloudWatchLogsのログ保存期間を変更する

0. 筆者環境

AWS Lambda: Python 3.8

1. 概要

今回はCloudWatchLLogsの特定のロググループに対してログ保存期間の変更が必要になったので、
他環境への汎用も考えLambdaを叩けば一括で変更できるような仕組みを作った。

2. 使い方

Lambdaを作成、Lambdaの環境変数に以下を設定し実行。

days: 変更したいログ保存期間
pattern: 変更したいロググループの名前(正規表現)
region: 対象のリージョン(東京リージョンならap-northeast-1でよい)

image.png

3. こーど

Gistにあげることにしました。
簡素なコードなので以下挙動の概要のみ。

  1. ロググループを全て取得(get_Loggroups)
  2. ログ保存期間とロググループのlist作成(create_Policies)
  3. ログ保存期間設定(set_Logretention)

https://gist.github.com/tunalight/63d2f5260563de5103f844a1f4010b3d

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

Amazon EKS のチュートリアルで Kubernetes を理解する #02 アプリのデプロイ

はじめに

本記事は、以下の内容の続きになります。

前回、k8sクラスタを作成したので、そこにアプリケーションをデプロイしてみます。

前提条件

  • k8sのクラスタが作成されており、ワーカーノードが稼働していること。

チュートリアル:Guestbook

Kubernetesのサンプルアプリケーションである Guestbook を利用します。
フロントエンドの画面と、Redis(マスター/スレーブ構成)からなるアプリケーションです。

Guestbook の詳細については、以下のページを参照してください。

Pod/Serviceの登録

PodやServiceの定義ファイルは、すでに上記GitHubに登録されているので、それを利用してデプロイしていきます。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-controller.json
replicationcontroller/redis-master created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-master-service.json
service/redis-master created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-controller.json
replicationcontroller/redis-slave created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/redis-slave-service.json
service/redis-slave created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-controller.json
replicationcontroller/guestbook created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook-go/guestbook-service.json
service/guestbook created

登録されたサービスの状態を確認すると、以下のようになっています。

$ kubectl get services -o wide
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)          AGE   SELECTOR
guestbook      LoadBalancer   10.100.197.132   xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx.us-west-2.elb.amazonaws.com   3000:31789/TCP   10s   app=guestbook
kubernetes     ClusterIP      10.100.0.1       <none>                                                                    443/TCP          63m   <none>
redis-master   ClusterIP      10.100.21.24     <none>                                                                    6379/TCP         43s   app=redis,role=master
redis-slave    ClusterIP      10.100.203.61    <none>                                                                    6379/TCP         26s   app=redis,role=slave

登録したサービスが一通り確認できますが、 guestbook のサービスのみ外部に公開される設定になっています。

また、kubectl get コマンドにより、PodやNodeの情報を確認することができます。
Guestbook のアプリケーションをクラスタにデプロイした結果、Guestbookのコンテナは3つ動作しており(レプリカ:3)、Redisは、マスタのコンテナが1つ(レプリカ:1)、スレーブのコンテナが2つ(レプリカ:2)、動作していることが分かります。

$ kubectl get pod,node
NAME                     READY   STATUS    RESTARTS   AGE
pod/guestbook-4gp4c      1/1     Running   0          13m
pod/guestbook-8d7k2      1/1     Running   0          13m
pod/guestbook-xfvck      1/1     Running   0          13m
pod/redis-master-q8568   1/1     Running   0          25m
pod/redis-slave-4g8c8    1/1     Running   0          14m
pod/redis-slave-xwjtn    1/1     Running   0          14m

NAME                                                STATUS   ROLES    AGE   VERSION
node/ip-192-168-0-148.us-west-2.compute.internal    Ready    <none>   73m   v1.14.7-eks-1861c5
node/ip-192-168-61-197.us-west-2.compute.internal   Ready    <none>   73m   v1.14.7-eks-1861c5
node/ip-192-168-88-66.us-west-2.compute.internal    Ready    <none>   73m   v1.14.7-eks-1861c5

さらに kubectl describe を使うことで、Podの構成や稼働状態などの詳細情報を確認できます。

$ kubectl describe pod guestbook

Web画面の表示

先の kubectl get services -o wide コマンドで確認した内容で、guestbookEXTERNAL-IP の部分をコピーしておきます。
また、PORT(S) の部分を見ると、このアプリケーションが、3000番ポートで公開されていることが分かります。

そのため、ブラウザを開き、以下のURLでアクセスすると、Guestbookの画面が表示されます。
これで、k8s上にデプロイしたアプリケーションが動作していることを確認できました。

http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxx.us-west-2.elb.amazonaws.com:3000
amazon-eks-guestbook01.jpg

まとめ

今回は、k8sクラスタにアプリケーションをデプロイしてみました。
k8sが生成するLoadBalancerを介して、冗長化構成で動作しているアプリケーションにアクセスすることができました。

PodやServiceの定義ファイルの内容は、ここでは説明しませんが、実際にデプロイした内容と定義の内容を、合わせて確認すると、定義されている内容も理解しやすいかと思います。

次は、k8sクラスタのモニタリングを行ってみます。

  • Amazon EKS のチュートリアルで Kubernetes を理解する #03 メトリクス&ダッシュボード
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon EKS のチュートリアルで Kubernetes を理解する #01 クラスタ作成

はじめに

これまで Kubernetes をちゃんと触ったことが無かったのですが、2019年11月に マネージド型ノードグループがリリース されて、ノードの管理などもより簡単になったそうなので、Aamazon EKS のチュートリアルを元に、Kubernetes の理解をしていこうかと思います。
今回、クラスタの作成には eksctl を利用します。

環境

開発環境は以下の通りです。

環境/ツール バージョン
macOS Catalina(10.15.2)
Python 3.7.6
Homebrew 2.2.2
awscli 1.17.0

Kubernetesクラスタ構築手順

何はともあれ、まずはクラスタを構築してみます。

eksctl のインストール

最初に、 Amazon EKS での k8sクラスタの管理用ツールである eksctl をインストールします。

$ brew tap weaveworks/tap
$ brew install weaveworks/tap/eksctl
$ eksctl version
[ℹ]  version.Info{BuiltAt:"", GitCommit:"", GitTag:"0.12.0"}

クラスタの新規作成

eksctl を利用して、k8sクラスタを作成します。
2020年1月時点では、ワーカーノードとして、Fargateを利用するのか、EC2を利用するのかが選択できますが、
ここでは、EC2を利用することとします。

eksctl create cluster \
--name dev \
--version 1.14 \
--region us-west-2 \
--nodegroup-name standard-workers \
--node-type t3.medium \
--nodes 3 --nodes-min 1 --nodes-max 4 \
--managed
オプション 説明
--name dev "dev"という名称で、k8sのクラスタを作成します。
--version 1.14 k8sのバージョンは、1.14を使用します。
--region us-west-2 AWSの環境はオレゴンリージョンを利用します。
--nodegroup-name standard-workers "standard-workers"という名称で、ノードグループを作成します。
--node-type t3.medium ノードとして、"t3.medium" のインスタンスを利用します。
--nodes 3 --nodes-min 1 --nodes-max 4 初期環境として3ノードで作成します。最小は1ノード、最大は4ノードです。
--managed マネージド型ノードグループ を利用します。

クラスタ作成のコマンドを実行すると、以下のような内容がコンソールに表示されます。
クラスタ作成には、10~15分程度かかります。

[ℹ]  eksctl version 0.12.0
[ℹ]  using region us-west-2
[ℹ]  setting availability zones to [us-west-2c us-west-2b us-west-2a]
[ℹ]  subnets for us-west-2c - public:192.168.0.0/19 private:192.168.96.0/19
[ℹ]  subnets for us-west-2b - public:192.168.32.0/19 private:192.168.128.0/19
[ℹ]  subnets for us-west-2a - public:192.168.64.0/19 private:192.168.160.0/19
[ℹ]  using Kubernetes version 1.14
・・・・・・
[ℹ]  kubectl command should work with "/Users/takanorig/.kube/config", try 'kubectl get nodes'
[✔]  EKS cluster "dev" in "us-west-2" region is ready

作成されたクラスタを、AWSコンソール画面で見ると、以下のような感じになります。

amazon-eks-dev01.jpg

クラスタの作成と共に、kubectlの接続設定ファイルである kubeconfig も、以下に出力されます。

/Users/$USERNAME/.kube/config

関連ツールのインストール

k8sクラスタを操作するために、kubectl および aws-iam-authenticator をインストールします。
kubectl は、k8sクラスタをコマンドラインから操作するためのツールで、アプリケーションのデプロイや、クラスタのリソース管理などを行うことができます。
aws-iam-authenticator は、「AWS IAM Authenticator for Kubernetes」のことで、k8sクラスタの認証に IAM を使用してアクセスできるようになります。

kubectl のインストール

まず、他のツールなどのインストールのために、すでに別の kubectl がインストールされている可能性があるため、既存の環境を確認します。

$ which kubectl
/usr/local/bin/kubectl

私の場合は、Docker と合わせて kubectl がインストールされていました。
バージョンが合っていれば、そのまま使えるとは思いますが、本チュートリアルの操作では、新たにAWSが提供する媒体をインストールすることとします。

ここでは、以下にインストールします。
実際の開発や運用時は、/usr/local/bin など、適切なディレクトリにインストールしてください。

$ mkdir -p /Users/$USERNAME/MyWork/amazon-eks/bin
$ cd /Users/$USERNAME/MyWork/amazon-eks/bin
$ curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/darwin/amd64/kubectl
$ chmod +x ./kubectl

※ バージョンは、k8sのバージョンに合ったものをダウンロードしてください。

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

$ ./kubectl version --short
Client Version: v1.14.7-eks-1861c5
Server Version: v1.14.9-eks-c0eccc

パスを設定しておきます。
ここでは、一時的に指定するだけですが、永続化する場合は、~/.zprofile などにPATHの指定を記載してください。

export PATH=/Users/$USERNAME/MyWork/amazon-eks/bin:$PATH

aws-iam-authenticator のインストール

aws-iam-authenticator は、Homebrewを利用してインストールします。

$ brew install aws-iam-authenticator

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

$ aws-iam-authenticator version  
{"Version":"v0.4.0","Commit":"c141eda34ad1b6b4d71056810951801348f8c367"}

クラスタの状態確認

kubectl get コマンドを利用して、クラスタの状態を確認できます。
現状、クラスタを作成しただけなので、k8s本体だけが動作している状況です。

$ kubectl get svc

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   26m

クラスタの構成

eksctl を利用して作成した場合、クラスタは以下のような構成になります。

amazon-eks-on-aws-architecture-diagram.jpg
出典:https://aws.amazon.com/jp/quickstart/architecture/amazon-eks/

  • マルチAZ構成で、1つのVPC内に Publicサブネットx3、Privateサブネットx3 が作成されます。
  • k8sノードが配置されるのは、Privateサブネットの方になります。

AWSコンソールで確認すると、以下のような内容で、VPC/サブネットが作成されていました。

amazon-eks-dev02.jpg
amazon-eks-dev03.jpg

クラスタの削除

Amazon EKS でクラスタを作成した場合、作成したk8sクラスタごとに 0.20 USD/時間 の料金が発生します(ワーカーノードとなるEC2インスタンスの費用は別)。
そのため、不要なクラスタは、削除しておきましょう。

$ eksctl delete cluster --name=dev

まとめ

AWSコンソールからだと、各パラメータの指定なども理解していないと難しい部分がありますが、
eksctl を利用することで、ほぼコマンド1つで、k8sクラスタやワーカーノードを作成することができました。
このように作成された内容を元に、構成などを確認すると理解もしやすいです。

次は、k8sクラスタにアプリケーションをデプロイしてみます。

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

CFnの戻り値(Ref,Fn::GetAtt属性)が少しわかったので共有

CFnの戻り値(Ref,Fn::GetAtt属性)の説明

戻り値とは、指定されると何かしらの値を返してくれる値と言うイメージです。

自動販売機でボタン押すと指定したものに準じて、飲み物を提供してくれる的な感じ?いい例が浮かびません(笑)

GetAttはリソースごとに決められている物理ID(*)を引っ張ってくる感じです

なのでリファレンスを見に行かないとわかりません。
Fn::GetAtt属性リファレンスです。

パラメータ
logicalNameOfResource
必要な属性を含むリソースの論理名 (論理 ID とも呼ばれます)。

attributeName
必要としている値のある、リソース固有の属性の名前です。各リソースタイプで使用できる属性の詳細については、リソースの参照ページに説明されています。

CFnのテンプレートの中で決めたリソース名のことを論理名と言います。
attributeNameはリソース固有の属性名で、指定されると物理IDとして返してくれます
(※)物理IDとはリソースが作られたときにAWSによって割り振られたIDのこと。下記画像で言うとこのネットワークインターフェイスID
スクリーンショット 2020-01-14 0.35.28.png

!RefはCFnのテンプレートの中で決めたリソース名の物理IDを返します。

Ref属性リファレンスです。

組み込み関数 Ref は、指定したパラメータまたはリソースの値を返します。

パラメータの論理名を指定すると、それはパラメータの値を返します。

リソースの論理名を指定すると、それはそのリソースを参照するために通常使用できる値を返します (物理 ID)。

以上を踏まえてEIPをを関連付けするときの方法を見ていきます。
下記は”AWS::EC2::EIP”リファレンスの引用です。
ここに表示されているものが戻り値(参照値)になります。
リソース(例 AWS::EC2::EIP)ごとに決まっています。他のリソースで!GetAttで指定されると参照されます。

戻り値

Refは
Elastic IP アドレスを このリソースの論理 ID を組み込みの Ref 関数に渡すと、Ref は次を返します: 。

For more information about using the Ref function, see Ref.

Fn::GetAtt
Fn::GetAtt 組み込み関数は、このタイプの指定された属性の値を返します。以下には、利用可能な属性とサンプル戻り値のリストが示されます。

Fn::GetAtt 組み込み関数の使用方法の詳細については、「Fn::GetAtt」を参照してください。

AllocationId
Amazon VPC で使用するアドレスの割り当てを表すために AWS によって割り当てられた ID。この値は、VPC の Elastic IP アドレスに対してのみ返されます。たとえば、eipalloc-5723d13e と指定します。

例)

Resources:
  ControlPortAddress:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc      
  AssociateControlPort:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ControlPortAddress.AllocationId (読み:論理IDのAllocationIdという戻り値を参照)
      NetworkInterfaceId: !Ref controlXface(読み:論理名(controlXface)で作成された物理ID(※)を値として利用する)

controlXface:(これが作られたときに物流IDが付与される)
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Ref SubnetId
      Description: Interface for controlling traffic such as SSH
      GroupSet: 
      - !Ref SSHSecurityGroup
      SourceDestCheck: true
      Tags:
      - Key: Network
        Value: Control

参考
AWS::EC2::EIPAssociation
AWS::EC2::EIP
Ref
Fn::GetAtt

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