20191009のAWSに関する記事は12件です。

開発環境から始めるdocker環境の浸透

dockerによるCIと、コンテナクラスタリング構築の記録を残していきます。

スマートフォンベースのECサイトのプロジェクトへ配属になりました。
プロジェクトはオーソドックスなLAMP構成+jQueryによるインタラクションが付与されたものですが、中〜大規模のPVを持っており、決済機能も存在する事から
品質管理やリリース手順などは整備されていました。

ライフサイクルとしては成熟期を過ぎた印象があり、プロダクトとしては一定の完成度に達していたので
バックエンドより、ユーザが直接関わるUI部分の改修に比重が置かれ
インフラ部分については、現状動いているのだから、コストを掛けて改修するほど優先度は高くない、といった状況でした。

そんな中で、合間を見てdocker環境を構築し、現場に浸透させて行った記録です。
docker環境の構築の助けになれば幸いです。
(記録になるので、ベストプラクティスから外れている部分も多いかと思いますが、ご容赦下さい)

当時の開発環境

dev.jpg
開発環境は、VirtualBox(Vagrant)でCentOSベースのイメージを一つ起動し
一つのOSの内に
・サービス内で展開している、三種類の2層webアプリケーション(VirtuaHostによる統合)
・上記3アプリケーションから参照するmysql
・DB負荷軽減の為のRedisサーバ
が同居しています。

その他、CDNとしてAWS S3を採用している為、擬似S3となるminoサーバを
ホストマシン上で起動しています。
ホストマシン上のGitHubのリポジトリを、Vagrantのイメージにマウントし、エディタで開発を行なっていました。

開発環境の問題点

前述の通りインフラ構築は逆風の雰囲気でしたが
開発チームからは、開発環境の問題の指摘が挙げられていました。

1.Virtualboxの起動速度

Virtualboxは、仮想環境内でOSを起動する為起動にリソースを消費しますが
dockerはホストOSの機能を共有して、docker仮想環境内で
アプリケーションが格納されたコンテナを起動する為、
「OSを起動する」部分のオーバーヘッドが無く、起動が高速になります。
docker導入のメリットとして訴求しました。

2.サービス環境と開発環境で、構成が異なり、健全ではない

■開発環境は、3つの2層webアプリケーションを1つのEC2インスタンスに集約していましたが
商用環境は、AWS EC2を3台に分け(別途、multi-AZも実施している)
それぞれのアプリケーションを分けて配置していました。
■開発環境はCentOS6(何故かOSのメッセージ全般がドイツ語)でしたが、
商用はAmazonLinuxでした。

dockerでコンテナ毎にサーバを構築する事で、商用と同じ3サーバ構成にする事で、解決を試みました。
OS差分の問題は、AmazonLinuxのDockerイメージを使うことも考えましたが
開発陣からの、「商用と同じ環境」をより納得させる為に、
商用で稼働しているAmazonLinuxを、Dockerイメージとして使うことで、心理的安全性を担保する事にしました。

将来的にAWS ECSによる、コンテナクラスタのデプロイを想定し
その際は、alpineで再構築する予定だったので、この対応は繋ぎという形でした。
(正直、そのままalpineで構築しておけばよかった)

3.Githubリポジトリが2つ存在し、連携しにくい

配属以前は、SASS/JS/HTMLが別リポジトリで管理されていました。
フロントのリポジトリで、gulpを使用したhtml+SASS+JSのモックを作成し
バックエンドのリポジトリに(ZIPか何かで)移植、viewファイルへ反映を行なっていましたが
・2つのリポジトリとのバージョン整合が大変
・移植コストが大きい。大規模なフロントエンド開発がもう無さそう
という事を考慮し、リポジトリをバックエンドに統合する事にしました。
統合により
・バックエンドのJSやSASSの変更をgulpで検知しコンパイル、ブラウザに即時(又はリロード)で反映
・PHPソースとJS/CSSが正しくバージョン管理されているので、将来的には継続的デプロイとして、GitHubへのPUSH時、JS・SASSを自動でコンパイルし、dockerimageの自動デプロイと、JSやCSS,画像ファイルのS3への自動反映
(AWS codebuildを想定)
が可能だと見積りました。

プロトタイプ作成

プロトタイプを見せる事が説得力に繋がると感じたので、業務の合間にdockerの構築を進めていくことになりました。

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

Amazon SES + mailx で「Error in certificate: Peer's certificate issuer is not recognized.」が出た時の対処法

1.前書き

Amazon SESとmailxでメール送信できる仕組みを作ったのですが、
メール送信のたびに「Error in certificate: Peer's certificate issuer is not recognized.」が出力していました。
メール送信自体は成功しているので問題は無いのかもしれませんが、エラーが出たままなのは精神衛生上よろしくないので、四苦八苦しながら対処しました。

これは、その時の対処法を記載しています。

2.環境

  • Centos7.6
  • mailx-12.5-19

前提

  • Amazon SES には送信元メールアドレス登録&承認済み
  • mailx はインストール済み
  • ~/.mailrc には以下のように設定
~/.mailrc
set smtp-use-starttls
set smtp=smtp://email-smtp.us-east-1.amazonaws.com:587
set smtp-auth=login
set smtp-auth-user=<Access key ID>
set smtp-auth-password=<Secret access key>
set ssl-verify=ignore
set nss-config-dir=/etc/pki/nssdb/
set from=<From Mail Address>

3.調査

エラーが出るタイミング

mail -v で確認したところ、Amazon SESのメールサーバへTLS通信を開始した際に出力されているようです。

>>> STARTTLS
220 Ready to start TLS
Error in certificate: Peer's certificate issuer is not recognized.  --★
Comparing DNS name: "email-smtp.us-east-1.amazonaws.com"
SSL parameters: cipher=AES-256-GCM, keysize=256, secretkeysize=256,
issuer=CN=Amazon,OU=Server CA 1B,O=Amazon,C=US
subject=CN=email-smtp.us-east-1.amazonaws.com

Google翻訳にかけると、「Error in certificate: Peer's certificate issuer is not recognized.(証明書のエラー:ピアの証明書発行者が認識されません。)」って言っているので、怪しいのは証明書のよう。
~/.mailrcでも指定した、証明書のデータベース/etc/pki/nssdb/を確認してみます。

$ sudo certutil -L -d /etc/pki/nssdb/

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

何も入っていませんでした。
指定した証明書のデータベースに証明書が無いために起こったエラーだったことが判ります。

4.対処

Amazonの証明書をインポートして対処します。

4-1 Firefox の証明書データベースを流用する場合

Firefoxから取得済みの証明書をインポートする方法が一番手軽だったので、その方法で対処していきます。
※別途Firefoxのインストールと、GUI環境が必要となります。

1. Firefox の現在の証明書を確認

Firefox が所持している証明書を確認します。

$ certutil -L -d ~/.mozilla/firefox/<xxxxxxxx>.default/

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

※Firefoxにも証明書が無い場合、一度FirefoxでAmazonのサイトを閲覧しましょう。
 閲覧後に再度確認すると、「Amazon」の証明書が追加されるはずです。

$ certutil -L -d ~/.mozilla/firefox/<xxxxxxxx>.default/

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

Amazon                                                       ,,

2. Firefox の証明書データベースを流用

新たに証明書データベース用のディレクトリを作成し、そこにFirefoxの証明書データベースをコピーします。

$ mkdir -m700 ~/.certs
$ cp -p ~/.mozilla/firefox/<xxxxxxxx>.default/{cert8.db,key3.db,secmod.db} ~/.certs

3. Amazonの証明書に信頼性を指定する

Firefoxから流用した証明書は信頼性が何も指定されていません。
そのため、証明書をエクスポート→インポートし、インポート時に信頼性の指定を行います。

$ certutil -L -n 'Amazon'  -d ~/.certs -a > amazon.cert.asc  # 証明書エクスポート
$ certutil -A -t "C,," -n 'Amazon'  -d ~/.certs -i amazon.cert.asc  # 信頼性を指定して証明書インポート

こうすることで、「Trust Attributes」の項目がC,,1に変更されます。

$ certutil -L -d ~/.certs/

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

Amazon                                                       C,,

4-2 直接ルート証明書をインポートする場合

えー、FirefoxもGUI環境も用意しなきゃならないの? という場合は、直接証明書をインポートする方法があります。

1. 新規の証明書データベース格納ディレクトリを作成する

$ mkdir -m700 ~/.certs
$ certutil -N -d ~/.certs
Enter a password which will be used to encrypt your keys.
The password should be at least 8 characters long,
and should contain at least one non-alphabetic character.

Enter new password: <空エンター>
Re-enter password: <空エンター>

2. Amazonからルート証明書をダウンロードする

こちらからルート証明書を入手します。

$ wget https://www.amazontrust.com/repository/AmazonRootCA1.pem
$ mv AmazonRootCA1.pem ~/.certs/

3. 入手したルート証明書をインポートする

「Trust Attributes」の項目をC,,1の形でインポートします。

$ certutil -A -n "Amazon" -t "C,," -d ~/.certs -i ~/.certs/AmazonRootCA1.pem -a
$ certutil -L -d ~/.certs/

Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

Amazon                                                       C,,

5.確認

上記の対処が完了したら.mailrcの設定を変更して、メールを送信してみましょう。

~/.mailrc
set nss-config-dir=/etc/pki/nssdb/
  ↓
set nss-config-dir=~/.certs/
$ echo -e "test mail" | mail -v -s "test mail title" -r "<From Mail Address>" "<To Mail Address>"
~~
>>> STARTTLS
220 Ready to start TLS
Comparing DNS name: "email-smtp.us-east-1.amazonaws.com"
SSL parameters: cipher=AES-256-GCM, keysize=256, secretkeysize=256,
issuer=CN=Amazon,OU=Server CA 1B,O=Amazon,C=US
subject=CN=email-smtp.us-east-1.amazonaws.com
~~

「Error in certificate: Peer's certificate issuer is not recognized.」が出力されなければ対処完了です。

6.まとめ

以上、対処法でした。
TLS通信における証明書の役割など、まだぼんやりとしか理解してないので、今後の課題になりそうです。
ですが、エラーが解決できると気持ちいいですね。

以下、参考にさせて頂いたサイト

Linuxからプロセスの動作状況をスマホに通知する
certutilによる証明書管理 [Fedora14]


  1. Cは「サーバ証明書発行元として信頼しているCAの証明書」を示す。 

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

LambdaでRedshiftのスナップショット取得&クラスター削除

やりたいこと

Redshiftのランニングコストをできるだけ抑えたい。

AWSのビッグデータ基盤であるRedshiftは、手軽に使えてしかも他のDWHサービスと比べて安価という非常に優良なサービスです。

とはいえ、一番小さいdc2.largeのインスタンスでも一時間あたり最低0.314$、一ヶ月ずっと起動しっぱなしの場合だとおよそ25000円程度なので、個人が学習目的で常用するには少し躊躇われるお値段。

少なくとも、使わない時間は停止しておきたいのですが、このRedshift、RDSとは違って停止という概念がありません。
インスタンスを削除して、必要なときにスナップショット(要するにバックアップですね)から復元するという手段を取るしかない。

まぁこの作業もAWSのコンソール上から数クリックでできてしまうものなので、さほど手間というわけでもないんですが、削除し忘れてそこそこの金額を取られてしまうのもアレなので、できることならこのスナップショットの取得→削除の流れをLambdaで自動でやってしまいたいよね、というのが今回のお題です。

結論

というわけで今回も結論から。言語はPython3.7。

import boto3
import time
from botocore.client import ClientError
from datetime import datetime, timedelta, tzinfo

redshift = boto3.client('redshift')
snapshot_prefix = "testinstance"
clusterid = "testinstance"
generation = 2

def delete_snapshot():
    # 手動スナップショットの情報を取得
    snapshots = redshift.describe_cluster_snapshots(
        ClusterIdentifier=clusterid,
        SnapshotType='manual'
    )

    # Snapshotが管理世代より少ない場合はスキップ
    if len(snapshots['Snapshots']) <= generation:
        return

    # SnapshotCreateTime の降順にソート
    snapshot_sorted = sorted(snapshots['Snapshots'], key=lambda x:x['SnapshotCreateTime'], reverse=True)

    # 管理世代(generation)以前のスナップショットを削除
    for snapshot in snapshot_sorted[generation:]:
        try:
            response = redshift.delete_cluster_snapshot(
                SnapshotIdentifier=snapshot['SnapshotIdentifier'],
                SnapshotClusterIdentifier=clusterid
            )
            return

        except ClientError as e:
            pass


def delete_cluster():
    snapshotid = "-".join([snapshot_prefix, datetime.now().strftime("%Y%m%d-%H%M%S")])

    # スナップショットを取得してクラスターを
    for i in range(0, 5):
        try:
            response = redshift.delete_cluster(
                ClusterIdentifier=clusterid,
                SkipFinalClusterSnapshot=False,
                FinalClusterSnapshotIdentifier=snapshotid
            )
            return True

        except ClientError as e:
            print(str(e))

        time.sleep(1)


def lambda_handler(event, context):
    try:
        # クラスターの存在確認
        response = redshift.describe_clusters(ClusterIdentifier=clusterid)

    except ClientError as e:
        print(str(e))

    else:
        # スナップショット削除
        delete_snapshot()

        # クラスター削除
        delete_cluster()

詳細

やっていることは簡単で、
 ・特定のプレフィックスがついたスナップショットを、指定した世代数だけ残して削除
 ・指定したインスタンス名のRedshiftインスタンスを、スナップショットを取得してから削除
だけです。

あとはLambdaのトリガーで、日次で起動してやれば良いだけ。
これで、うっかり削除し忘れても安心ですね!

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

RailsのmigrateをAWS Fargateに移行した時の話

前提

  • ECSでRailsアプリが起動されていること
  • migrateはfargateでやってなかった
  • テスト環境でやること

作業内容

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/ecs_run_task_fargate.html
これ見ながら作業する。実際は、コマンドでやると何をしているのかよくわからないので、まずはGUIでやる。把握した後にコード化するというのが良い。そうすることで、全てのオプションやらパラメータの世界が見えてくる。

念の為、新規DBを作成

t2.microなどで良い
何らかのエラーが出たときの切り分けにしたい。
インスタンスが作れたら、アプリケーションデプロイ用のタスクにDBの環境変数を設定する(System Managerが使えるなら ECS containerから valueFrom で呼び出せるのでjson自体をgit管理できる。AWSでマネージドされているのが良い)

taskを定義する

実際は、コマンドなどを付与したりするのでENTRYPOINTで設定している。
これは、Initial,Migrate,Seedそれぞれリビジョンで分けるだけで良い。Initial,Seed,Migrateの順で作る。
※なお、実際の運用ではInitialやらSeedは使わない。これは、今回新規でRDSを立ち上げたので必要になっただけ。LATESTリビジョンだけがあれば良い。

key value
ネットワークモード awsvpc
互換性が必要 FARGATE
タスクメモリ(GB) 0.5GB
タスクCPU(vCPU) 0.25 vCPU

containerの環境設定(Initial)

key value
エントリポイント bundle,exec,rails,db:create
作業ディレクトリ /app(アプリがある場所)
  "containerDefinitions": [
    {
      "entryPoint": [
        "bundle",
        "exec",
        "rails",
        "db:create"
      ],
      "workingDirectory": "/app"
    }
  ]

containerの環境設定(Migrate)

key value
エントリポイント bundle,exec,rails,db:migrate
作業ディレクトリ /app(アプリがある場所)
  "containerDefinitions": [
    {
      "entryPoint": [
        "bundle",
        "exec",
        "rails",
        "db:migrate"
      ],
      "workingDirectory": "/app"
    }
  ]

containerの環境設定(Seed)

key value
エントリポイント bundle,exec,rails,db:seed,a=b
作業ディレクトリ /app(アプリがある場所)
  "containerDefinitions": [
    {
      "entryPoint": [
        "bundle",
        "exec",
        "rails",
        "db:seed",
        "a=b"
      ],
      "workingDirectory": "/app"
    }
  ]

taskを実行する

  • VPCを設定、subnetを設定
  • ネットに繋がるものを設定しておく

2つエラーが発生した

  • メモリ不足エラー → メトリクスをチェックして、スペックの問題ならスペックを上げる
    • コンテナインスタンスのリソースから、メモリ使用量を見る
  • ECRからイメージをpullしてこれず、エラー → インターネットにつながるネットワークを設定する

結果を見る

  • PROVISIONING→PENDING→RUNNING→STOPのようなフローだったような気がする
  • タスクのStoppedの中でログを確認する。
  • Migrateタスクならば、以下のようなログが吐かれているだろう。
2019-10-09 18:16:45D, [2019-10-09T09:16:45.290941 #1] DEBUG -- : [1m[35m (572.8ms)[0m [1m[35mCREATE DATABASE "test_database" ENCODING = 'unicode'[0m
2019-10-09 18:16:45Created database 'test_database'
  • 実際にアプリケーション側でも確認する。
  • 問題がなければCodeDeployやCIなどで連携する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudFormationでCodeBuildのBuildSpecのファイル名指定方法

はじめに

BuildSpecの初期値のファイル名「buildspec.yml」を変更する方法がわからず、サポートに問い合わせしたため備忘録として記録。
開発/本番環境でファイル名を変えたかった。

方法

SourceプロパティのBuildSpecに指定する。
下記は「my-buildspec.yml」を指定した例

Resources:
  MyProject:
    Type: AWS::CodeBuild::Project
    Properties: 
      Artifacts: 
        Type: CODEPIPELINE
      Environment: 
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:2.0-1.10.0
        ImagePullCredentialsType: CODEBUILD
        Type: LINUX_CONTAINER
      ServiceRole: <サービスロールの ARN>
      Source: 
        BuildSpec: my-buildspec.yml
        Type: CODEPIPELINE

参考

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-project-source.html

ひとこと

ドキュメントを見て理解できずにサポートにお世話になってしまう。

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

【また】AWS認定プラクティショナー試験を受けた結果…不合格でした

こんにちは。
本日(2019/10/9)、またまたとある事情(業務命令)でAWS認定プラクティショナー試験を受けてきましたので、その感想をば書かせて頂ければと思います。

前の記事
https://qiita.com/Watachan/items/d72a2d409c5c00db8603

タイトル通り、試験結果はまたまた不合格でした。
この…バカがッ(自分が)!!

前回の反省点


もちろん前の状態のまま受けるわけもなく、ちゃんと反省点を考えてみました。

・業務で使った知識を過信
・ホワイトペーパーの流し読み
・模擬試験の結果が良かったので、あまり問題を追求していなかった
・完全に舐めていた

対策


ということで、今回は「AWS認定資格試験テキスト AWS認定 クラウドプラクティショナー」という本を(会社の経費で)買い、徹底的に読んで挑みました。
模擬試験…は前回の結果があるからもう信用しないことにしました。

出題された問題について


覚えている限り書きます。

・AWSの利点
・AWSの請け負う責任はどんなものがあるか
・DDoS攻撃を防ぐ時はどうするか
・AWS SnowballとAWS Snowmobileの違いと内容
・コンピューティングリソースにはどんなものがあるか
・AWS内の通信サービスはどんなものがあるか
・ハイブリッド構成の概要と利点
・ダイレクトコネクトの説明
・サードパーティシステムの利点(なぜAWSマーケットプレイスで選んだほうが良いのか?)
・AWS X-Rayの説明をせよ
・AWS Rekognitionの説明をせよ

…おい待て!何だX-RayとかRekognitionって…知らんぞ!そんなサービスは!!

結果


よし終わった…と思って終了をクリックすると「不合格」の文字が…
最初はちょっと信じられなくて固まっていましたが、ため息とともに試験会場を後にしました。

まとめ


・参考書は流し読みしないほうが良い
・似通ったサービス名を同時に出されて「どっちが正しいか」と聞いてくる印象が強かったので、サービス概要は性格に覚えるべきかもしれない…例えば、AWS SnowballとAWS Snowmobileはどちらもデータ転送を行うサービスだけれど、違いはペタバイト単位かテラバイト単位で送る…など
・出題範囲外の問題も結構出ていた印象…問題のガチャガチャ要素がでかいかもしれないが、少しは試験外のサービスにも目を通しておいても良いかも
・やっぱり模擬問題の問題は全く出ないので、正直受けるだけ無駄…お祭りのくじびきみたいな一見さんをひっかけるための汚い仕掛けのようにも思える

遠吠え


今回、ちゃんと勉強(?)したつもりで挑んだのに何でたった700点の上限を超えられない…?こんなバカな!
勉強方法がおかしかったと言われても、どの項目の何が間違っていたなんて点数しか明かされないから文句のつけようがないし…

正直、もう受けたくないってのが本音です
いずれ滅び去るシステム専用の試験問題を高いお金を払ってやるなんてどう考えても馬鹿げてるし
AWSに限らず、資格試験全般を受けたくないです…

まぁ、全部会社のお金なんですけどね…

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

AWS WorkSpacesでmacからのキーボード操作ができない

突然リーダーから「ちょっと本番環境見てくれや〜」って言われて、
いつも通りWorkSpacesにログインしたもののキーボードが反応せず大変困ったので備忘録を。

環境

MacBook Pro (Retina, 15-inch, Mid 2015)
macOS Catalina バージョン 10.15 Beta
Amazon WorkSpaces Version 2.5.9.1186

これのおかげ

06.png

https://forums.aws.amazon.com/thread.jspa?threadID=278583

対処法

システム環境設定からセキュリティとプライバシーを選択
01.png

アクセシビリティ > プライバシーを選択
02.png

Amazon WorkSpacesを一度削除
03.png

Amazon WorkSpacesを追加
04.png
05.png

WorkSpaceを起動してログイン。
キーボードで入力できるようになっていた!!

突然キーボードが反応しなくなるのは辛い
同僚のPCから確認し、なんとか依頼は完了させました。

参考

https://forums.aws.amazon.com/thread.jspa?threadID=278583

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

【AWS】EC2へのデプロイの手順<備忘録>

はじめに

プログラミング初心者です。自分のための備忘録として記録を残します。

環境

  • macOS10
  • Rails 5.2.3
  • Nginx
  • Unicorn
  • Postgresql
  • Sidekiq
  • Redis

EC2インスタンス作成

  • Amazon Linux AMI 2018.03.0(HVM,SSD Volume Typeを選択
  • t2.microを選択(無料枠を利用の場合)
  • キーペアの作成


パブリックDNSが生成される。
*https://qiita.com/Quikky/items/2897573a42fd71cfc47fを参考にしました。

rbenvインストールの準備

$ sudo yum install git
$ sudo yum -y install gcc
$ sudo yum -y install gcc-c++
$ sudo yum -y install zlib-devel
$ sudo yum -y install openssl-devel
$ sudo yum -y install readline-devel

rbenvのインストール

$ mkdir ~/.rbenv
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ mkdir ~/.rbenv/plugins ~/.rbenv/plugins/ruby-build
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ cd ~/.rbenv/plugins/ruby-build
$ sudo ./install.sh
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

Rubyのインストール

$ rbenv install 2.4.6
$ rbenv rehash
$ rbenv global 2.4.6
$ ruby -v

Bundlerのインストール

$ rbenv exec gem install bundler -v 2.0.1
$ rbenv rehash

GitHubでプロジェクトを作成

GitHubにPush

Mac上で
$ git remote add origin <URLアドレス>                    
$ git add .                                          
$ git commit -m "1st push"                           
$ git push -u origin master                          

GitHubからEC2にPull

git clone <URLアドレス>
cd <プロジェクトのディレクトリ>                       

Railsのインストール

gem i -v 5.2.3 rails
gem list rails                     
$ bundle install                   

上記でエラーが出たので以下のコマンド実施

$ gem install pg -v '1.1.4' --source 'https://rubygems.org/'

postgreSQLのインストール

$ yum install postgresql
$ sudo yum install postgresql-devel                   

SQLite3のエラー回避

sudo yum install sqlite-devel                    

インスタンスのセキュリティグループのインバウンド設定

  • カスタムTCP/IP port 3000 0.0.0.0/0 を設定追加。

EC2インスタンス内でPostgres起動

sudo yum install -y postgresql-server
sudo /sbin/service postgresql initdb
sudo /sbin/service postgresql start                    

EC2のインスタンス内でのPostgresの設定

マスターユーザー名:<プロジェクト名>
マスターパスワード:<パスワード>
postgres=# create role <username> with createdb login password '<password>';
postgres=# create database [database_name] owner [user_name];

$ sudo -u postgres psql
postgres=# create role <プロジェクト名> with createdb login password '<パスワード>';
postgres=# create database <プロジェクト名>_production owner <プロジェクト名>;
postgres=# create database <プロジェクト名>_development owner <プロジェクト名>;
postgres=# create database <プロジェクト名>_test owner <プロジェクト名>;
postgres=# \q                 

DB(Postgres)の作成

.bash_profile
export POSTGRES_USERNAME="<プロジェクト名>"
export POSTGRES_PASSWORD="<パスワード>"

プロジェクトのディレクトリ
rails db:create                     
 /var/lib/pgsql9/data/pg_hba.conf

local all all peer

     ↓

local all all md5

に変更。                     
rails db:create
rails db:migrate                    

SMTPの設定

環境変数を.bash_profileに設定します。

EMAIL_ADDRESS
EMAIL_PASSWORD

設定おわったら、それを反映させるため以下のコマンドを実行

source .bash_profile                     
  • RedisサーバをEC2に立ち上げる。
  • ElasticCache選択
  • 作成

primary endpointが生成。

VPCの設定で、Redisのインバウンド設定をセキュリティーグループにする

  • sg-694f3715のインバウンドルールの編集で追加
  • カスタムTCP:TCP
  • port:6379
  • source 0.0.0.0/0

以下のファイル設定

config/initializers/sidekiq.rb
config/redis.yml                    

プロジェクトを再起動してSidekiqを起動

bundle exec sidekiq                      

Nginxをインストール

$ sudo yum install nginx

Mac上の/usr/local/etc/nginx/nginx.confをEC2環境にコピーする。

scp -i ~/.ssh/<プロジェクト名> nginx.conf ec2-user@XXXXXXXXXXXXXXXXXXXXX.amazonaws.com:~/nginx.conf
sudo cp ~/nginx.conf /etc/nginx/

ポート番号や、rootディレクトリを修正する。

Unicornの起動

unicorn -c config/unicorn.rb -E production -D                      

Nginx起動

$ sudo service nginx start                     

VPCの設定で、port80(HTTP)を追加

ホームディレクトリの権限を744(drwxr--r--)に変更する。

chmod 755 ~/                     

Nginxのリスタート

$ sudo service nginx restart

production 環境のDBを構築する。

$ rails db:migrate RAILS_ENV=production                      

Unicornの再起動

$ kill [pid]
$ unicorn -c config/unicorn.rb -E production -D                     

アセットパイプラインでエラーが発生。


config/environments/production.rb内の
config.assets.compileをfalseからtrueに変更                      

*https://kakuyon.hatenablog.com/entry/2018/07/15/033059を参考にしました。

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

[AWS CDK]thisがエラーになる

new s3.Bucket使用としたらthisがエラー(TS2345)になった。

cdk-stack.ts
import * as cdk from '@aws-cdk/core';
import * as s3 from '@aws-cdk/aws-s3';

export class CdkStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
        super(scope, id, props);

        // this ok
        const serviceName: string = this.node.tryGetContext("serviceName");

        // this error
        const originBucket = new s3.Bucket(this, `${serviceName}BucketId`, {
            bucketName: `${serviceName}-bucket`
        });
    };
}

解決策

package.json内でaws-cdkのバージョンが混合していると発生するエラーらしいのでバージョンを揃える

package.json
  "devDependencies": {
    "@aws-cdk/core": "^1.11.0",
    "@aws-cdk/aws-s3": "^1.12.0",
    "aws-cdk": "^1.11.0",
    "@types/node": "^12.7.11"
  }

package.json
  "devDependencies": {
    "@aws-cdk/core": "^1.12.0",
    "@aws-cdk/aws-s3": "^1.12.0",
    "aws-cdk": "^1.12.0",
    "@types/node": "^12.7.11"
  }

参考

https://github.com/aws/aws-cdk/issues/542

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

Amazon Web Services (AWS)サービスの正式名称・略称・読み方まとめ #9 (移行と転送)

Amazon Web Services (AWS)のサービスで正式名称や略称はともかく、読み方がわからずに困ることがよくあるのでまとめてみました。

Amazon Web Services (AWS) - Cloud Computing Services
https://aws.amazon.com/

まとめルールについては下記を参考ください。

Amazon Web Services (AWS)サービスの正式名称・略称・読み方まとめ #1 (コンピューティング) - Qiita
https://qiita.com/kai_kou/items/a6795dbab7e707b0d1a6

間違いや、こんな呼び方あるよーなどありましたらコメントお願いします!

Migration & Transfer - 移行と転送

AWS Application Discovery Service

AWS Database Migration Service (AWS DMS)

AWS DataSync

AWS Migration Hub

AWS Server Migration Service (SMS)

AWS Snowball

AWS Transfer for SFTP (AWS SFTP)

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

CloudFront で IPv6対応の APIを作る際の罠

IPv6なAPIをつくりたいぞ!

APIGateway x Lambda で APIをつくるのは定石だが、IPv6対応させようとした瞬間に悩む。
そう、APIGatewayが、IPv6対応していないのである!
仕方がないので、手前にCloudFrontを置いてやるのが、これまた定石。

APIGatewayの手前のCloudFrontって、エラー時とかタイムアウト時のキャッシュ周りで癖が強いから、ちゃんと設定しないと、ユーザが無限エラーにハマったりで大変だよね、、とかあるけど、それはまた別の話

独自ドメインを振りたいが?

さて、CloudFront => APIGateway => Lambda したところで、当然ドメインを振りたいわけだ。
AWSあるある、マネージドサービスにドメイン振る場合はRoute53必須問題。

だが、そんなことは百も承知。
しかも私は賢いので、CNAMEではなく、ALIASレコードで華麗にマネージドサービスに別名をつけるのさ、ふふん。

と思っていたんですけどね、補完候補にCloudFrontのDistが出てこねーんだわ。。
仕方なく、手動で入れるとちゃんと認識する、なんでやねん。
(あと地味にCloudFront側のCNAMEリストにALIASレコードと同じ名前の定義を入れないとダメって初見だと気づかんよね...)

あとはHTTPS対応かな?

独自ドメインにする場合、ACMに証明書を登録しておかないとCloudFront側の設定が進めなくなって詰む、ガッテム!
しかし、私は賢いので、ACMにちゃんとGeoTrustの証明書をインポートしてあるのさ、ふふん。

と思ってたんですけどね、補完候補にACMで登録したはずの証明書がでてこねーんだわ。。
調べていくと、CloudFrontが参照するACMはバージニアリージョンのACMなんだってさ!へぇ!
ファック!!と殺意を覚えつつも、バージニアのACMに証明書を入れたら、無事認識してめでたしめでたし。

にしても、CloudFront遅すぎない?

ここまでCloudFrontの設定を変えて更新を繰り返しているのだが、どうにも遅い。
設定をミリ弄って更新を押すと、都度10分〜30分は帰ってこない。
こんな状態でトライ&エラーとかやってられるかという感じだ。
(CNAME/ACMに依存する設定をかえると、30分かかる感じだった)

ちなみに、deploy now みたいなステータスでアクセスすると、変な挙動したり、レスポンスが先祖返りすることがあるが、そういうものみたいなので、この状態での動作を気にしてはいけない。

IPアドレス問題

もう疲れたので終わりにしたいけど、なんかね、APIGatewayで $context.identity.sourceIp を使ってたんですよ。
なんとこいつが、CloudFrontのIPアドレスで入ってきて、クライアントのIPが取れねぇw
ELB挟んだりするのと同じと言われると、なるほど、と思うが...いやいやいや。

で、どうすりゃえーねんというと、 X-Forwarded-For ヘッダーを信用して、APIGateway側のマッピングテンプレートでこいつをLambdaに渡してやれということだ。(カンマ区切りで中継IPが全てはいるので、Lambda側でパースが必要。)
ちなみに、$context.identity.sourceIp は IPv6を受け取れないが、X-Forwarded-For にはIPv6が入る。なるほどね。

IPv6 Only のエッジを分けておきたい

普通はDual StackのAPIでおわり、、なんだが、用途として特殊で、IPv4とIPv6のエッジを明示的に指定できるようにしておきたかったのだ。

Route53のALIASレコードでAとAAAAを違う名前でつけておけば実現できる、そう思っていた時期が私にもありました。
これやるとね、Aしか振ってない名前でIPv4/IPv6両方いけるんですよねww(逆も然り)
色々やったけど、ダメだったので、多分AWSのバグ仕様なんだと思います。

苦肉の策として、CloudFrontのDistributeを二系統つくって、AのALIASとAAAAのALIASをそれぞれ別に指定してやった。そしたら、期待通りの動き、パーフェクトゲームだ。

おわりに

こうして、期待するAPI構築が終わり(実際にはCORS問題もあったが、
Serverless Frameworkが優秀すぎて、数分で解決した)、私は帰宅するのであった。

そうそう、「クラウドはIPv6対応万全だ、アプリケーション開発者が怠けてるだけだ」と口を大にしてゆう人を、非開発系のIPv6推進者(笑)の方でよく見かける。

君らは自分でクラウドのIPv6使って開発してから、そういうこと言おうな?
とマジで思う。

IPv6は普及してきたといっても、実際にコンテンツを開発/運用する上で足りてないものが、まだまだたくさんある。みんな、色んな罠や沼で精神汚染されて、苦しむといい。
その苦しみが、IPv6で自在にコンテンツを開発・運用するための糧になる。
というか、今失敗しないと、永久にIPv4の呪縛からは解かれないからな。

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

【AWS】CloudFormationでIAM RoleのPolicy定義に変数を用いる【小ネタ】

はじめに

CloudFormationのResourceのPropertyの指定において、Typeが「Json」のものがいくつかあります。
IAM Roleの「AssumeRolePolicyDocument」や「Policies」などがこれに該当します。

しかしながら、IAM Entityに対して指定するPolicyドキュメントでCFnの関数を使いたいことが多く(PolicyドキュメントのResourceに、他のCFn Stackで作成したリソースを指定する、など)、JSONでは指定が難しいことも多々あります。

Typeが「Json」となっているProperyには、同等の内容をyamlとして定義して渡すことも可能です。
この仕様を利用して、PolicyドキュメントでCFn関数を指定することが可能です。

AWS::IAM::Role - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-assumerolepolicydocument
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-policies

AWS::IAM::Role Policy - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html#cfn-iam-policies-policydocument

IAM Role定義サンプル

以下の2つのCloudFormationテンプレート定義は等価です。

JSON形式でPolicyドキュメントを指定する

Resources:
  # AWS::IAM::Role - AWS CloudFormation
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  SampleAdminRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
              },
              "Action": "sts:AssumeRole"
            }
          ]
        }
      Description:
        Describe what you want
      Policies: 
        - PolicyName: AllowAllActions
          PolicyDocument:
            {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Action": "*",
                        "Resource": "*"
                    }
                ]
            }

yaml形式でPolicyドキュメントを指定する

AWSTemplateFormatVersion: '2010-09-09'
Description: Describe what you want.

Parameters:
  # Define what you want.

Resources:
  # AWS::IAM::Role - AWS CloudFormation
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  SampleAdminRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: "arn:aws:iam::123456789012:root"
            Action: "sts:AssumeRole"
      Description:
        Describe what you want
      Policies: 
        - PolicyName: AllowAllActions
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "*"
                Resource: "*"

AssumeRolePolicyDocumentのPrincipalでCFn関数を使う

上記のように、TypeとしてJsonが指定されているPropertyをyamlに変換したことで、CFn関数を利用しやすくなります。
先ほどの例ですと、AssumeRoleする主体となるPrincipalのAWSアカウントIDを、Parameterとして渡したくなると思います。
この場合、以下のように!Sub関数を利用することが可能です。

cfn-iam-role-policy-with-function.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: Describe what you want.

Parameters:
  # Define what you want.
  PrincipalAwsAccountId:
    Description:
      An AWS account id will be a principal to invoke the AssumeRole API.
    Type: Number
    Default: 123456789012

Resources:
  # AWS::IAM::Role - AWS CloudFormation
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  SampleAdminRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: !Sub arn:aws:iam::${PrincipalAwsAccountId}:root
            Action: "sts:AssumeRole"
      Description:
        Describe what you want
      Policies: 
        - PolicyName: AllowAllActions
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "*"
                Resource: "*"

Fn::Sub - AWS CloudFormation
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html

CFnスタック作成時に、下記のようにPrincipalAwsAccountIdパラメータを指定することで、任意のAWSアカウントIDに対応することができるようになります。

$ aws cloudformation create-stack \
  --stack-name iam-role-policy-with-function \
  --template-body file://cfn-iam-role-policy-with-function.yml \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameters ParameterKey=PrincipalAwsAccountId,ParameterValue=987654321098,UsePreviousValue=true

おわりに

CDKが出たので、今後はCDKを使うことでこのような用途はProgrammaticに解決できるようになると思います。
しかしながら、いきなり全て移行することは難しいため、このようなテクニックはまだ暫く必要になるでしょう。

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