20210516のAWSに関する記事は30件です。

[serverless]AWS Lambdaでnode-canvasを利用した画像生成をする

前書き OGPの生成などnode.jsで画像生成をしたいというケースは最近増えてきているのでは無いでしょうか。その中でもクライアントで実行可能なCanvasを利用したいケースというのも同様に多いと思います。 またサーバーレスの台頭と共にデプロイにserverlessを利用しているケースもあると思います。 私自身がその様な環境で試す中、実行するまでに躓いた箇所があったので実際に動く簡単なアプリを作りながら途中の問題の解決法を解説していこうと思います。 実行環境 デプロイ: serverless(テンプレート: aws-nodejs-typescript) 実行環境: AWS Lambda (node.js v14) ライブラリ: node-canvas その他: GitHub Actions 完成形 AWS Lambdaで関数を実行するとSlackにnode-canvasで生成した画像が投稿されるBotを作ります。 手順 1. serverlessテンプレートから雛形を生成 ※ serverlessが導入されていなければ npm install -g serverless を実行 sls create -t aws-nodejs-typescript -p sls-node-canvas-test 大体このような構成で生成されます。 . ├── handler.ts ├── package.json ├── serverless.ts ├── tsconfig.json └── webpack.config.js 2. テンプレートの不要部分削除とruntimeの更新 api-gateway 部分を削除 runtime を node.14 に変更 適用後の serverless.ts ↓ import type { Serverless } from 'serverless/aws'; const serverlessConfiguration: Serverless = { service: { name: 'sls-node-canvas-test', // app and org for use with dashboard.serverless.com // app: your-app-name, // org: your-org-name, }, frameworkVersion: '>=1.72.0', custom: { webpack: { webpackConfig: './webpack.config.js', includeModules: true } }, // Add the serverless-webpack plugin plugins: ['serverless-webpack'], provider: { name: 'aws', runtime: 'nodejs14.x', }, functions: { hello: { handler: 'handler.hello' } } } module.exports = serverlessConfiguration; 3. 依存関係のインストール npm install node-canvas @slack/web-api npm install 4. コードの実装 handler.ts を以下のように実装します。 SlackのOAuth Access Tokenを取得して各自入れてください 必要な権限は chat:write chat:write.public files:write です import 'source-map-support/register' import { createCanvas } from 'canvas' import { WebClient } from "@slack/web-api" const token = "Your OAuth Access Token"; const web = new WebClient(token); // 画像の生成 const createBufferImage = (): Buffer => { const canvas = createCanvas(100, 100) const ctx = canvas.getContext('2d') const text = "TEST" ctx.font = '30px' ctx.fillText(text, 50 - ctx.measureText(text).width / 2, 50) return Buffer.from(canvas.toDataURL().split(",")[1], "base64") } // slackにポスト const postSlack = async (image: Buffer) => { const res = await web.files.upload({ file: image, }); await web.chat.postMessage({ channel: "general", text: res.file.permalink, }); } export const hello = async () => { const image = createBufferImage() await postSlack(image) } 5. ローカルで実行する 以下のコマンドでローカルで動いているか実行してみます。 sls invoke local -f hello うまく行けば↓のように投稿されます。 6. とりあえず、デプロイしてみる 私はMacで開発してますが、以下のコマンドでとりあえずデプロイしてみます。 sls deploy デプロイできましたが、実行した所でエラーが発生しました。 ネイティブコードを含んでいるため、Macでビルドした物はAWS Lambda上では動きませんでした。 7. GitHub Actions でデプロイする node-gypのビルド処理がOSにより分かれるのでLinux系のOS上でビルドします。 Dockerでも良いですが、デプロイの自動化も同時にできるので今回は GitHub Actions を利用します。 .github/workflows/ci.yml に↓のファイルを作成する事で main ブランチにプッシュすると自動でデプロイされるようになります。 ※ 事前にGitHubリポジトリの設定ページからsecretにAWSのキーを登録しておいてください name: Deploy on: push: branches: [main] defaults: run: shell: bash jobs: deploy: runs-on: ubuntu-latest strategy: matrix: node-version: [14.x] steps: - uses: actions/checkout@v2 - name: Install dependencies & Deploy run: | sudo apt-get install -y build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev npm ci npx serverless deploy --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} デプロイされたので、実行してみます。今度は別のエラーが表示されました。 8. ビルド時に共有ライブラリをコピーする 手順7のエラーでは必要なsoファイルが見つからないとなっているので、sls deploy で実行されるwebpackのビルド時に含めるようにします。 libuuid.so.1 が見つからないとなっていますが、実際に追加すると libmount.so.1 と libblkid.so.1 も見つからないと同様のエラーが表示されるので追加します。 soファイルはディストリビューション等により置いてる場所は違いますが、GitHub Actionsの ubuntu-latest (ubuntu-20.04) では必要なファイルは /usr/lib/x86_64-linux-gnu/ に存在しているのでコピーします。 webpack.config.js にコピープラグインを追加して以下のように書き換えます。 npm install copy-webpack-plugin@6 --save-dev const path = require('path'); const slsw = require('serverless-webpack'); const nodeExternals = require('webpack-node-externals'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin'); module.exports = { context: __dirname, mode: slsw.lib.webpack.isLocal ? 'development' : 'production', entry: slsw.lib.entries, devtool: slsw.lib.webpack.isLocal ? 'cheap-module-eval-source-map' : 'source-map', resolve: { extensions: ['.mjs', '.json', '.ts'], symlinks: false, cacheWithContext: false, }, output: { libraryTarget: 'commonjs', path: path.join(__dirname, '.webpack'), filename: '[name].js', }, target: 'node', externals: [nodeExternals()], module: { rules: [ // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader` { test: /\.(tsx?)$/, loader: 'ts-loader', exclude: [ [ path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, '.serverless'), path.resolve(__dirname, '.webpack'), ], ], options: { transpileOnly: true, experimentalWatchApi: true, }, }, ], }, plugins: [ new CopyPlugin({ patterns: [ { from: "/usr/lib/x86_64-linux-gnu/libuuid.so.1", to: "node_modules/canvas/build/Release/libuuid.so.1" }, { from: "/usr/lib/x86_64-linux-gnu/libmount.so.1", to: "node_modules/canvas/build/Release/libmount.so.1" }, { from: "/usr/lib/x86_64-linux-gnu/libblkid.so.1", to: "node_modules/canvas/build/Release/libblkid.so.1" }, ], }), ], }; これでデプロイして実行すると... 別のエラーが表示されます。 9. node-canvasのバージョンを2.6.1にする 2021年5月16日現在、node-canvasの最新バージョンはv2.7.0で特にバージョンを指定せずに入れるとv2.7.0が入ると思いますが、手順8のエラーが解決できなかったので1つ前のv2.6.1を指定して入れ直します。 これでデプロイして実行し直すと実行には成功しますが、lambdaにはデフォルトフォントが存在しないので文字部分が豆腐で表示されています。 10. フォントを導入する 特に指定はありませんが、今回はNoto Sansを利用したいと思います。ダウンロードして NotoSans-Regular.ttf を handler.ts と同じ階層に配置します。 NotoSans-Regular.ttf を registerFont を使いフォントを読み込むように更新します。 import 'source-map-support/register' import { createCanvas, registerFont } from 'canvas' import { WebClient } from "@slack/web-api" // @ts-ignore import ttf from "./NotoSans-Regular.ttf" registerFont(ttf, { family: "Noto Sans" }) const token = "Your OAuth Access Token"; const web = new WebClient(token); // 画像の生成 const createBufferImage = (): Buffer => { const canvas = createCanvas(100, 100) const ctx = canvas.getContext('2d') const text = "TEST" ctx.font = '30px' ctx.fillText(text, 50 - ctx.measureText(text).width / 2, 50) return Buffer.from(canvas.toDataURL().split(",")[1], "base64") } // slackにポスト const postToSlack = async (image: Buffer) => { const res = await web.files.upload({ file: image, }); await web.chat.postMessage({ channel: "general", text: res.file.permalink, }); } export const hello = async () => { const image = createBufferImage() await postToSlack(image) } このままではwebpack でのビルド時にエラーが出るので file-loader を導入します。 npm install --save-dev file-loader webpack.config.js で ttf ファイルを file-loader で処理するように記述を追加します。 const path = require('path'); const slsw = require('serverless-webpack'); const nodeExternals = require('webpack-node-externals'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin'); module.exports = { context: __dirname, mode: slsw.lib.webpack.isLocal ? 'development' : 'production', entry: slsw.lib.entries, devtool: slsw.lib.webpack.isLocal ? 'cheap-module-eval-source-map' : 'source-map', resolve: { extensions: ['.mjs', '.json', '.ts'], symlinks: false, cacheWithContext: false, }, output: { libraryTarget: 'commonjs', path: path.join(__dirname, '.webpack'), filename: '[name].js', }, target: 'node', externals: [nodeExternals()], module: { rules: [ // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader` { test: /\.(tsx?)$/, loader: 'ts-loader', exclude: [ [ path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, '.serverless'), path.resolve(__dirname, '.webpack'), ], ], options: { transpileOnly: true, experimentalWatchApi: true, }, }, { test: /\.ttf$/i, loader: 'file-loader', }, ], }, plugins: [ new CopyPlugin({ patterns: [ { from: "/usr/lib/x86_64-linux-gnu/libuuid.so.1", to: "node_modules/canvas/build/Release/libuuid.so.1" }, { from: "/usr/lib/x86_64-linux-gnu/libmount.so.1", to: "node_modules/canvas/build/Release/libmount.so.1" }, { from: "/usr/lib/x86_64-linux-gnu/libblkid.so.1", to: "node_modules/canvas/build/Release/libblkid.so.1" }, ], }), ], }; デプロイして、実行... やっとできました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Golangはじめて物語(第8話:API Gateway+LambdaでJWTを実装する)

はじめに Golang での JWT の実装はライブラリが充実しているので、比較的簡単に実装できる。 しかし、ググって見つかるのは Gin や Echo と組み合わせたものが多く、AWS で API Gateway を使った実例というのが少なかったので、組み込んでみる。 本記事は、以下の知識があることを前提とする。 JWT に関する概要が分かっている(と言っても、あまり難しいことは知らなくてよくてこの記事くらいの概要が分かっていれば充分) Golang で API Gateway + Lambda の WebAPI を実装したことがある(手前味噌ではあるが、この記事を読んで理解ができていれば充分) なお、今回は IdP には頼らずに自分で秘密鍵を使ってトークンを払い出して検証するという方式を検証する。 トークン払い出しの実装 まずは、JWT のトークンを払い出す部分を実装する。 ライブラリは form3tech-oss/jwt-go を使用する。 import ( "context" "encoding/json" "time" jwt "github.com/form3tech-oss/jwt-go" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handler) } func handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { tokenString, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{ Subject: "[ユーザID等、識別子となるもの]", ExpiresAt: time.Now().Add(time.Minute * 1).Unix(), }).SignedString("[秘密鍵の文字列]") jsonBytes, _ := json.Marshal(struct { Token string `json:"token"` }{ Token: tokenString, }) return events.APIGatewayProxyResponse{ StatusCode: 200, IsBase64Encoded: false, Body: string(jsonBytes), }, nil } これだけ。とてもシンプル。 キモになるのは以下の部分で、それ以外は API Gateway のレスポンスを作っているだけである。 tokenString, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{ Subject: "[ユーザID等、識別子となるもの]", ExpiresAt: time.Now().Add(time.Minute * 1).Unix(), }).SignedString("[秘密鍵の文字列]") 見ての通り、jwt.NewWithClaims() は、暗号化方式、Claim、秘密鍵 を渡しているだけである。 秘密鍵の文字列は、当然のことながら平文で書いて Git に登録すべきではない。本記事では AWS で API Gateway を使う前提としているため、KMS で管理しているデータキーを使うのが良いだろう。 ExpiresAt は本記事では検証のために短く設定しているが、ここは要件に合わせて設定をしよう。 これを、API Gateway の Lambda 統合で呼び出すように設定をしよう(設定方法は冒頭の、第一話を参照)。 これでAPIを呼び出すと、以下のように JSON が返却される。 $ curl -i https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/auth {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjExMzUyODYsInN1YiI6IjAwMDAxIn0.QcJWTzH1wBe6QdbWJIECO3QMn9ZiE_bK9-Paft1YBy4"} これを、JWT.IO の Debugger に食わせてみると、以下のように、自分の作ったキーが復号されることが分かる。 ※右下の your-256-bit-secret と書かれているところに、上記で設定した秘密鍵の文字列を入力すると、正しい文字列になる。 トークン検証の実装 さて、続いてはトークンの検証だ。 本記事では、検証を行う必要がある API で、Authorization ヘッダに設定されたトークンを検証する。 ※実装を簡易にするために、Bearer の文字列は設定しない。 以下のような関数を作って検証をすることにしよう。 import ( "errors" "log" jwt "github.com/form3tech-oss/jwt-go" ) func checkToken(headers map[string]string) error { tokenString, headerIsNotNull := headers["Authorization"] if !headerIsNotNull { return errors.New("[Header]Authorization is not specified") } token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { b := []byte("[秘密鍵の文字列]") return b, nil }) if err != nil { log.Println(err) return errors.New("jwt.Parse() returned error") } if token.Claims.(jwt.MapClaims)["sub"] != "[ユーザID等、識別子となるもの]" { return errors.New("Invalid Subject") } return nil } キモになるのは、 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { b := []byte("[秘密鍵の文字列]") return b, nil }) で、これでトークンを構造体に割り付けることができる。 ここも、払い出し同様に、秘密鍵の文字列はシークレット用の処理を施すこと。 トークンから Claim を参照にするには、token.Claims.(jwt.MapClaims) を見ればよい。 あとは、好きに中身を検証しよう。 なお、ExpiresAt(exp) は、jwt.Parse() 内で勝手に検証してくれるため、自分で実装する必要はない。便利! ドキュメントには VerifyExpiresAt という関数もあるが、おそらくこれは、検証なしの Parse 関数を使った場合等に自分で実装をするためのものと思われる。 ともあれ、これを実装した API に対して、以下のようにデータを投げてみよう。 $ curl -i -X PUT -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjExMzUyODYsInN1YiI6IjAwMDAxIn0.QcJWTzH1wBe6QdbWJIECO3QMn9ZiE_bK9-Paft1YBy4' https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource 払い出した正しいトークンを投げた場合は正常に処理が行われ、そうでない場合は jwt.Parse() がエラーになった旨のログが出るだろう。ためしに、トークンの文字列を変更してみたり、払い出しから1分経過後にトークンを使ってみると分かりやすい。 でも、色々な API でトークンの検証するの面倒臭くない? そんな物臭なあなたに Lambda Authorizer。 というか、AWS で API Gateway を使うのであればこちらを採用する方が、マネージドサービスの恩恵を享受できるだろう。 Authorize 設定をした API について、共通的に検証用の Lambda 関数を呼び出してくれるという便利機能だ。 Terraformでは、Authorize 設定をしたいリソース/メソッドについて、以下の設定を行う。 resource "aws_api_gateway_method" "data_put" { rest_api_id = aws_api_gateway_rest_api.jwt_example.id resource_id = aws_api_gateway_resource.resource.id http_method = "PUT" authorization = "CUSTOM" authorizer_id = aws_api_gateway_authorizer.jwt.id } resource "aws_api_gateway_authorizer" "jwt" { name = "jwt_authorizer" type = "REQUEST" rest_api_id = aws_api_gateway_rest_api.jwt_example.id authorizer_uri = aws_lambda_function.authorizer.invoke_arn } aws_api_gateway_method の authorization は、 IAM や COGNITO_USER_POOLS による認証も可能だが、今回の Lambda の方式の場合は CUSTOM を設定する。 また、認証用の Lambda を別途 Terraform で定義して、aws_api_gateway_authorizer の authorizer_uri に ARN を設定する。 上記の設定をした Lambda Authorizer では、API Gateway が Lambda に渡してくるイベントが APIGatewayProxyRequest ではなくて APIGatewayCustomAuthorizerRequestTypeRequest になるので注意が必要だ。また、ハンドラの戻り値についても、APIGatewayCustomAuthorizerResponse を返す必要がある。 APIGatewayCustomAuthorizerResponse の PolicyDocument では、IAM のポリシードキュメント形式で返す必要があるが、結局は、APIGatewayCustomAuthorizerRequestTypeRequest の MethodArn のプロパティで、アクセスさせたいリソースとメソッドは簡単に抽出できるため、たいして難しくはない。捻ったことをしようとしない限りは、お決まりの形で Allow/Deny を返してあげれば良いだろう。 上記を踏まえて、以下のような Lambda のハンドラを用意する。 import ( "context" "errors" "log" jwt "github.com/form3tech-oss/jwt-go" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(handler) } func checkToken(headers map[string]string) error { tokenString, headerIsNotNull := headers["Authorization"] if !headerIsNotNull { return errors.New("[Header]Authorization is not specified") } token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { b := secretKey return b, nil }) if err != nil { log.Println(err) return errors.New("jwt.Parse() returned error") } if token.Claims.(jwt.MapClaims)["sub"] != "[ユーザID等、識別子となるもの]" { return errors.New("Invalid Subject") } return nil } func handler(ctx context.Context, request events.APIGatewayCustomAuthorizerRequestTypeRequest) (events.APIGatewayCustomAuthorizerResponse, error) { if err := checkToken(request.Headers); err != nil { log.Println(err) log.Println("Invalid Token.") return events.APIGatewayCustomAuthorizerResponse{ PrincipalID: "user", PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{ Version: "2012-10-17", Statement: []events.IAMPolicyStatement{ { Action: []string{"execute-api:Invoke"}, Effect: "Deny", Resource: []string{request.MethodArn}, }, }, }, }, nil } return events.APIGatewayCustomAuthorizerResponse{ PrincipalID: "user", PolicyDocument: events.APIGatewayCustomAuthorizerPolicy{ Version: "2012-10-17", Statement: []events.IAMPolicyStatement{ { Action: []string{"execute-api:Invoke"}, Effect: "Allow", Resource: []string{request.MethodArn}, }, }, }, }, nil } これで、先ほどと同様に $ curl -i -X PUT -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjExMzUyODYsInN1YiI6IjAwMDAxIn0.QcJWTzH1wBe6QdbWJIECO3QMn9ZiE_bK9-Paft1YBy4' https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/resource を投げ込むと、resource の PUT の API でチェックを実装しなくても、API Gateway がオーソライザーの Lambda を実行してチェックしてくれるぞ!なお、このケースでは、デフォルトの HTTP レスポンスコードは 403 で、 {"Message":"User is not authorized to access this resource with an explicit deny"} というワーディングが返される。 変更したい場合は、ゲートウェイのレスポンスから変更を行おう。 また、デフォルトではAPI Gatewayのキャッシュ機能がONであるため、トークン期限切れでも認証が通ってしまうことがある。嫌なのであれば、Terraform の aws_api_gateway_authorizer のリソースで authorizer_result_ttl_in_seconds の設定を変更しよう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSサービス一覧(2021年) 気になったやつだけ

自分のためにAWSサービス一覧をざくっと理解したいので、勉強用の備忘録として何回かに分けて書こうかと思います。 というか、AWSサービス一覧のページはいっぱいあり、以下のページが最新情報としてはよい。 【2021年】AWS全サービスまとめ https://dev.classmethod.jp/articles/aws-summary-2021/#toc-196 じゃ、上記をみればいいじゃんという話になるけど、 私が何をするかというと、上記サマリーページをサマリーしていこうかと、自分が覚えやすいように。 AWSサービスのカテゴリ分けとしては下記の形になるにかなと。 全部網羅するのはめんどくさいで、とりあえず、こんな感じでやっていこうかと。 インフラ編 コンピューティング コンテナ ストレージ データベース 移行と転送 ネットワーキングとコンテンツ配信 VMware Cloud on AWS アプリ編 開発者用ツール モバイル ビジネスアプリケーション アプリケーション統合 管理とセキュリティ編 管理とガバナンス セキュリティ、IDおよびコンプライアンス 機械学習・ロボット編 Machine Learning 分析 TensorFlow on AWS ロボット工学 その他 ブロックチェーン 衛生 メディアサービス 拡張現実(AR)とバーチャルリアリティ(VR) ゲーム開発
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】ピアソンVUEでAWS認定資格をオンライン受験すると不合格でも再受験が無料らしい

日本国内在住者の受験限定で、Pearson VUE のオンラインプロクタリングで受験すると、1回目の受験が不合格でも、2回目の受験が無料になるそうです。 公式ソース 2021年5月1日から2021年7月31日の AWS 認定の受験で、ピアソンVUEのオンラインプロクタリングを指定して予約する際に、プロモーションコード「AWS2021JP」を入力して適用するだけです。試験も試験監督員も日本語が選択いただけます。1回目の受験が不合格の場合には、2021年9月30日までの同試験の再受験(2回目)が無料になります。このプロモーションは日本国内在住者のみご利用いただけます。その他のプロモーションコードならびにバウチャーチケットの併用はできません(バウチャー支払い不可)。利用規約が適用されます。 でも、既に受験して合格実績のある人は、受験料半額クーポンを持っていると思うので、 これ併用出来ないからちょっと残念。 初めて受験される方などはこの機会に利用するのは良いかもしれませんね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】初めてのAWSデプロイ - 3~5分ごとにヘルパー関数が生成するリンクがHTTPとHTTPSで切り替わってしまう問題編 -

Laravel + vue.jsで作成したポートフォリオをデプロイするために初めてAWSを利用したところ様々な問題にぶつかり、解決に時間を要しました。 発生した問題 JSファイルやCSSファイルが正しく読み込まれない問題 ブラウザを問わず、数分毎にHTTPとHTTPSが切り替わってしまう問題(★ 本記事) 前回、JSファイルやCSSファイルが正しく読み込まれない問題を無事に解決しましたが、次なる問題が発生しました。 3~5分ごとにasset()関数が生成するリンクのHTTPとHTTPSが切り替わってしまう問題 タイトルだけだと何のことやらと思います。 前回、無事に信用するプロキシを記述し、ヘルパー関数が生成するリンクをhttpsにすることに成功。無事にJSやCSSが読み込まれた...と思いきや 「別のブラウザでもちゃんと表示されるのか確認しよう!」 そしてfirefox、Edge、Safariで開いてみた結果... フ ァ イ ル が 読 み 込 ま れ て い な い JSファイルもCSSファイルも読み込まれず、テキストだけの寂しい画面が現れました。Chromeではちゃんと読み込んでくれていたというのに一体どういうことなのでしょう。 今まで無事に読み込まれていたGoogleChromeも更新してみると なんとChromeもダメになってしまいました。ほんの5分前には正しく読み込まれていたというのに... 今までhttpsでリンクを作ってくれていたヘルパー関数が、元のhttpによるリンクを生成してしまっていたのです。 キャッシュの問題でしょうか...?しかしながら最初に開いたブラウザにキャッシュも何もないはず... 理由が解明されないままF5でページのリロードを繰り返した結果 3~5分程度経過すると再度asset()がhttpsで読み込むようになったのです。 症状のまとめ 3~5分のスパンでasset()が生成するリンクがhttpとhttpsで切り替わる。 この症状は、ブラウザを問わず発生する。 生成するリンクのスパンはブラウザごとに独立している(例えば、Chromeでhttpsリンクが生成されている時でも、safariではhttpリンクになっていることがある、など。) 時間差でリンク生成が切り替わるということなんて聞いたことがなく、検索をかけてもそれらしき記事やまとめが見つかりませんでした。 そのため出来ることを1つ1つ試して解決に繋げようと模索しました。 試したこと1 リンクの強制HTTPS化 前回信用するプロキシとしてapp/Http/Middleware/TrustProxies.phpへの記述を行いましたが、もしかしたら不十分だったかもしれないと考えました。 [Laravel]常時SSLなアプリケーションでのURL生成のベストプラクティスを考える laravelでリンクがhttpsにならない そこで上記記事を参考にし、forceSchemeにより本番環境の場合にリンク生成をhttpsとするように設定しました。 app/Providers/AppServiceProvider.php <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Routing\UrlGenerator; // 追記 class AppServiceProvider extends ServiceProvider { // 中略 /** * Bootstrap any application services. * * @return void */ public function boot(UrlGenerator $url) { // ★ 以下を追記!!! // 本番環境の時、URLをhttpsにする if (config('app.env') === 'production') { $url->forceScheme('https'); } } } 結果: ダメでした 状況は変わらず、3~5分のスパンでaseet()はhttpになったりhttpsになったり... 原因はどうもここではないようです。 試したこと2 APP_URLの修正 .envファイルのAPP_URLを修正すればもしかしたら正しく動作するかもしれません。 早速本番環境の.envファイルを修正していきます。 // 修正前 APP_URL=http://xxx.xx.x.xx // 修正後 APP_URL=https://xxx.xx.x.xx .envファイルを修正した後は以下コマンドでキャッシュをリフレッシュして、その後ブラウザをリロードします。 $ php artisan config:cache 結果: ダメでした httpsのリンクすら生成されなくなってしまいました。どうやら原因はここではなかったようです。 .envファイルは元に戻します。 試したこと3 そもそも本番環境になっているかのチェック 「試したこと1」で記述したapp/Providers/AppServiceProvider.phpを眺める中でふと思いました。 // 本番環境の時、URLをhttpsにする if (config('app.env') === 'production') 「そもそも本番環境になっているのか...?」 いや、まさか、そんなはずはない。 そう思いつつも、再び.envファイルを確認してみると... .env APP_ENV=local やってしまっていました...。デプロイしておきながら、ローカル環境のまま動いていました。 とんでもないことです。 早速修正します。 .env APP_ENV=production $ php artisan config:cache ブラウザをロードしてみると... 無事に表示されました! この後別のブラウザで時間をかけて何度もリロードしましたが、asser()がhttpリンクを生成することはなくなりました! 原因に気付けたもう1つの要因 原因に気付くことができた理由は実はもう1つあります。 私はローカル環境の時、SQLが発行された際にそのSQL文をログファイルに乗るように設定していました。 app/Providers/AppServiceProvider.php <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Routing\UrlGenerator; class AppServiceProvider extends ServiceProvider { // 中略 /** * Bootstrap any application services. * * @return void */ public function boot(UrlGenerator $url) { // 開発環境の時、SQLをログに表示する if (config('app.env') !== 'production') { \DB::listen(function ($query) { \Log::info("Query Time:{$query->time}s] $query->sql"); }); } // 本番環境の時、URLをhttpsにする if (config('app.env') === 'production') { $url->forceScheme('https'); } } } そして、原因が一向に掴めない私は、Laravelプロジェクトのlogファイル(storage/logs/laravel.log)を確認していました。 その中で、 (ローカルでしか表示されないはずのSQLログが表示されている...?) と気付けたところから解決に結びつけることができました。 調査しきれていないところ 結局のところ、裏でどういうロジックが働いて、時間経過によってasset()が生成するリンクがhttpになったりhttpsになったりするのかというのはわかりませんでした。 引き続き調査を行いますが、こういったところが怪しいといったものがあればご指摘いただけると幸いです。 まとめ しっかり.envファイルは確認すること、どんな小さなことからも原因を探すことが出来るということを学ぶことができたと思います。 次にデプロイする機会があればこういった部分もしっかりチェックしていきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数人でterraformを扱う際に気を付ける事

複数人でterraformを扱う際に気を付ける事 上の図の補足 上の図の補足をします。 10:00前の時点では、AさんとBさんのtfstateファイルは同じ状態だったとします。 Aさんが10:00にAWS上にリソースAをデプロイする為、terraform applyを実行したとします。 するとAWS上にはリソースAがデプロイされ、AさんのtfstateにはリソースAがデプロイされたことが記録されます。 この時点でAさんとBさんのtfstateファイルに差異が生まれます。 すると10:05という同じ時間にterraform planを行ったとしてもAさんとBさんの結果は異なり、 Bさんがterraform applyを実行しようとすると既にAWS上にリソースAは存在する為、エラーが発生します。 -解決法- backend設定① terraformには複数人で作業をするための設定としてbackend設定が存在します。 provider.tf terraform { backend "s3" { bucket = "tfstate-test" key = "tfstate" region = "ap-northeast-1" } } 予め、S3上でtfstate-testという名前のバケットを作成しておき、terraform initを実行します。 これでtfstateファイルを複数人で共有することができます。 -解決法- backend設定② 上記の設定だけでは、複数人が同時にapplyやdestroyすることを防げません。 そこでまず、dynamodbをデプロイしてください。 main.tf resource "aws_dynamodb_table" "terraform-state-lock" { name = "tfstate_lock" read_capacity = 1 write_capacity = 1 hash_key = "LockID" attribute { name = "LockID" type = "S" } } その後、backend設定を以下のように書き換えます。 provider.tf terraform { backend "s3" { bucket = "tfstate-test" key = "tfstate" region = "ap-northeast-1" dynamodb_table ="tfstate_lock" } } これで、terraform applyやterraform destroyを実行する際にはtfstateファイルがロックされ、複数人が同時にapplyやdestroyすることができなくなります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Route53で取得したドメインをWixで利用する方法

はじめに ドメイン周りの知識がなかったこともあり、 Route53で取得した独自ドメインをWixで使用するのに少し苦戦したため、方法を残しておく。 Route53で取得したドメインをWixで利用する方法 基本的にWixの既存のドメインを追加にある、登録済みのドメインを接続の指示に従う。 ネームサーバーを置き換えますという手順で注意が必要で、 Route53のネームサーバーを変更するには、 Route53のダッシュボードからドメインに移動し、該当するドメインを選択したあと、 ネームサーバーの追加/編集から変更することができる。 私はホストゾーンの設定のみを変更していたため、うまく行かなかった。。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MongoDB CompassからDocumentDBに接続する

経緯 AWSのDocumentDBを使って遊んでるんだけど、ローカルの開発で使ってるMongoDB Compassで接続できないかいろいろ試した結果できたのでまとめ。 前提 まずは、接続するマシンにMongoDB Compassを入れておくこと。WindowsもLinuxあるからお好きなので。 AWSのDocumentDBには設定用のEC2が接続できていること。 これはDocumentDBのAWS公式ドキュメントのAmazon EC2 を使用して接続を見ながらやればすぐできると思う。 下準備 EC2編 AWSのEC2のインスタンス概要に移動して接続するインスタンスまで移動して、接続ボタンをクリック SSHクライアントを選ぶ そこに書いてあるEC2接続用プライベートキーファイル(AWS-EC2_ExpampleA.pem)と、ユーザ名(初期値はec2-user)とインスタンスのURL(ec2-user@ec2-AA-BB-CC-DD.compute-1.amazonaws.com)を記録しておく。 プライベートキーファイルはEC2インスタンス作成時にしか表示できないので、保存してなかったらインスタンスを再度作成したほうが早い。 EC2のインスタンス概要の下にあるセキュリティをクリックしてインバウンドルールに追加 通常だと下記の1と3は設定済のはずなので、自分のマシンからMongoDBを接続するので2を追加してポートを通す。 SSH接続用(Port22)と自分のIPv4アドレスを追加 MongoDB接続用(Port27017)と自分のIPv4アドレスを追加 MongoDB接続用(Port27017)とインスタンスのIPv4アドレスを追加 下準備 DocumentDB AWSのDocumentDBに移動して、クラスタ→接続とセキュティをクリック DocumentDBのURL(docdb-YYYY-MM-DD-hh-mm-ss.cluster-sample.A1-east-1.docdb.amazonaws.com) DocumentDB作成時のID DocumentDB作成時のパスワード DocumentDBのURLは、「mongo シェルでこのクラスターに接続する」に書いてあるとこから引っ張ってくる。 MongoDB Compassの設定 MongoDB Compassを起動したら、New ConnectionにあるFill in connection fields individuallyをクリック Authenticationを None → Username/Password に変更する ここはDocumentDBの設定項目で、入力内容は以下の通り DocumentDBのURL : docdb-YYYY-MM-DD-hh-mm-ss.cluster-sample.A1-east-1.docdb.amazonaws.com DocumentDBのポート番号 : 27017 認証方法 : Username/Password UserName : DocumentDB作成時のID Password : DocumentDB作成時のパスワード More Optionをクリックして入力項目を切り替える 入力項目を切り替える SSLをNoneからUnvalidated(insecure)、SSH TunnelをNoneからUse Identity Fileに変更する ここはSSHの設定項目で、入力内容は以下の通り 主要箇所だけ記載 EC2インスタンスのURL : ec2-user@ec2-AA-BB-CC-DD.compute-1.amazonaws.com EC2のユーザ名 : ec2-user EC2接続用プライベートキー : AWS-EC2_ExpampleA.pem これで、入力項目は終わり。 接続 Connectボタンをクリックすると接続を開始する。 接続に成功すると以下のようなダイアログが表示される。 DocumentDBは純粋なMongoDBでないので、警告メッセージがでてるだけで問題ない。 アプリの左側にも警告メッセージ表示されてるけど通常使用には問題ない。 ここまでできればローカルでやってることと特に制限なくできる。 接続してしまえば、ちょっとしたDBの書き換えや抽出できるので便利になるので捗る。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DocumentDBにMongoDB Compassに接続する

経緯 AWSのDocumentDBを使って遊んでるんだけど、ローカルの開発で使ってるMongoDB Compassで接続できないかいろいろ試した結果できたのでまとめ。 前提 まずは、接続するマシンにMongoDB Compassを入れておくこと。WindowsもLinuxあるからお好きなので。 AWSのDocumentDBには設定用のEC2が接続できていること。 これはDocumentDBのAWS公式ドキュメントのAmazon EC2 を使用して接続を見ながらやればすぐできると思う。 下準備 EC2編 AWSのEC2のインスタンス概要に移動して接続するインスタンスまで移動して、接続ボタンをクリック SSHクライアントを選ぶ そこに書いてあるEC2接続用プライベートキーファイル(AWS-EC2_ExpampleA.pem)と、ユーザ名(初期値はec2-user)とインスタンスのURL(ec2-user@ec2-AA-BB-CC-DD.compute-1.amazonaws.com)を記録しておく。 プライベートキーファイルはEC2インスタンス作成時にしか表示できないので、保存してなかったらインスタンスを再度作成したほうが早い。 EC2のインスタンス概要の下にあるセキュリティをクリックしてインバウンドルールに追加 通常だと下記の1と3は設定済のはずなので、自分のマシンからMongoDBを接続するので2を追加してポートを通す。 SSH接続用(Port22)と自分のIPv4アドレスを追加 MongoDB接続用(Port27017)と自分のIPv4アドレスを追加 MongoDB接続用(Port27017)とインスタンスのIPv4アドレスを追加 下準備 DocumentDB AWSのDocumentDBに移動して、クラスタ→接続とセキュティをクリック DocumentDBのURL(docdb-YYYY-MM-DD-hh-mm-ss.cluster-sample.A1-east-1.docdb.amazonaws.com) DocumentDB作成時のID DocumentDB作成時のパスワード DocumentDBのURLは、「mongo シェルでこのクラスターに接続する」に書いてあるとこから引っ張ってこれいい。 MongoDB Compassの設定 MongoDB Compassを起動したら、New ConnectionにあるFill in connection fields individuallyをクリック Authenticationを None → Username/Password に変更する ここはDocumentDBの設定項目で、入力内容は以下の通り DocumentDBのURL : docdb-YYYY-MM-DD-hh-mm-ss.cluster-sample.A1-east-1.docdb.amazonaws.com DocumentDBのポート番号 : 27017 認証方法 : Username/Password UserName : DocumentDB作成時のID Password : DocumentDB作成時のパスワード More Optionをクリックして入力項目を切り替える 入力項目を切り替える SSLをNoneからUnvalidated(insecure)、SSH TunnelをNoneからUse Identity Fileに変更する ここはSSHの設定項目で、入力内容は以下の通り 主要箇所だけ記載 EC2インスタンスのURL : ec2-user@ec2-AA-BB-CC-DD.compute-1.amazonaws.com EC2のユーザ名 : ec2-user EC2接続用プライベートキー : AWS-EC2_ExpampleA.pem これで、入力項目は終わり。 接続 Connectボタンをクリックすると接続を開始する。 接続に成功すると以下のようなダイアログが表示される。 DocumentDBは純粋なMongoDBでないので、警告メッセージがでてるだけで問題ない。 アプリの左側にも警告メッセージ表示されてるけど通常使用には問題ない。 ここまでできればローカルでやってることと特に制限なくできる。 接続してしまえば、ちょっとしたDBの書き換えや抽出できるので便利になるので捗る。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambdaについて

AWS Lambdaがよくわからないので、勉強したことをまとめた。 AWS Lambdaとは? AWS Lambdaとは、ソースコードを書くだけでプログラムを実行できる、AWSのサービスの1種。 よって、開発者は、環境構築やサーバーの管理といった面倒で初心者がつまづきがちな作業から解放され、プログラムを書くことに注力できる。 Lambdaの特徴 サーバーの設定・管理が不要 Lambdaを使うと、ソースコードを書くだけでプログラムの実行が可能で、OSを準備したり、環境構築したりといった面倒な作業が不要。 また、OSのメンテナンスや更新、障害時の復旧、スケーラビリティの確保といった、サーバのメンテナンスもしなくてよい。 スケーラブル Lambdaはリクエストやトリガーがない間は実行されない。 逆にリクエストやトリガーが2つ以上あると、AutoScalingを使わなくても自動的に複数のLambda関数が実行される。 細かいリソース設定が可能 Lambdaを使用するときにはメモリを設定することになる。 メモリは128MBから3008MBまでの範囲で64MB刻みで設定できる。 このメモリの設定が料金に影響する。 ミリ秒単位の課金 Lambdaは未使用時には課金されず、使用した時間、メモリ、リクエスト数に対して課金される。 時間に対する課金はミリ秒単位なので無駄がない。 他のAWSサービスとの連携が可能 Lambdaは、他のAWSサービスのイベントをトリガーとして実行できる。 例えば、AutoScalingを使用したときや、S3にデータを保存したときなどが挙げられる。 参考文献:AWS認定資格試験テキスト・AWS認定クラウドプラクティショナー(SBクリエイティブ)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS WAF(v2)のログをAthenaとDatadogで閲覧する

AWS WAFのログ AWS WAFは以下のようなログを出力します。 デフォルトではOFFになっており、公式ではKinesis Firehose経由での配信がサポートされています。 こちらのログを、AthenaおよびDatadogで閲覧できるようにインフラを構築してみましょう。 { "id": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", "content": { "timestamp": "2021-05-15T00:00:00.000Z", "tags": [ "aws_account:123456789012", "env:production", "region:ap-northeast-1", "service:waf", "source:waf", "sourcecategory:aws" ], "service": "waf", "attributes": { "http": { "url_details": { "path": "/" }, "method": "POST", "request_id": "abcdefghij1234567890" }, "webaclId": "arn:aws:wafv2:ap-northeast-1:1234567890:regional/webacl/Sample/abcdefghij1234567890", "httpSourceId": "123456789012:abcdefghij1234567890:production", "httpSourceName": "APIGW", "system": { "action": "ALLOW" }, "network": { "client": { "ip": "100.100.100.100" } }, "httpRequest": { "country": "JP", "httpVersion": "HTTP/1.1", "args": "", "headers": [ { "name": "X-Forwarded-For", "value": "100.100.100.100" }, { "name": "X-Forwarded-Proto", "value": "https" }, { "name": "X-Forwarded-Port", "value": "443" }, ...(省略) ] }, "ruleGroupList": [ { "ruleGroupId": "AWS#AWSManagedRulesCommonRuleSet" } ], "terminatingRuleId": "Default_Action", "terminatingRuleType": "REGULAR", "formatVersion": 1, "timestamp": 1621036800 } } } 構成図 構成図は以下の通りとなります。 インフラ定義 Terraformで定義します。 コンソール画面でも構築可能ですので、参考にされる方はTerraformを読み替えてください。 S3 まずはWAFログを配置するS3バケットを作成します。 resource "aws_s3_bucket" "log_bucket" { bucket = "xxxxxxxxxxxxxxxxxxxx" // バケット名 acl = "private" } Glueテーブル Athenaで閲覧できるように、Glueでテーブルを作成します。 ログを配置するパスは s3://xxxxxxxxxxxxxxxxxxxx/waf/year=2021/month=05/day=15/hour=00/XXXXXXXXXX.parquet のようなルールとしました。(後述するFirehoseの配信設定で定義します) Partition Projection機能によるパーティションを設定しています。 year=${"$"}{year}/month=${"$"}{month}/day=${"$"}{day}/hour=${"$"}{hour} 年/月/日/時 をそれぞれパーティションキーとしました。 resource "aws_glue_catalog_database" "database" { name = "${var.service_name}_logs" } resource "aws_glue_catalog_table" "waf_catalog_table" { database_name = aws_glue_catalog_database.database.name name = "waf" parameters = { classification = "parquet" "projection.enabled" = true "projection.year.type" = "integer" "projection.year.digits" = "4" "projection.year.interval" = "1" "projection.year.range" = "2021,2099" "projection.month.type" = "enum" "projection.month.values" = "01,02,03,04,05,06,07,08,09,10,11,12" "projection.day.type" = "enum" "projection.day.values" = "01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31" "projection.hour.type" = "enum" "projection.hour.values" = "01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24" "storage.location.template" = "s3://${aws_s3_bucket.log_bucket.name}/waf/year=${"$"}{year}/month=${"$"}{month}/day=${"$"}{day}/hour=${"$"}{hour}" } partition_keys { name = "year" type = "string" } partition_keys { name = "month" type = "string" } partition_keys { name = "day" type = "string" } partition_keys { name = "hour" type = "string" } storage_descriptor { location = "s3://${aws_s3_bucket.log_bucket.name}/waf/" input_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat" output_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat" ser_de_info { name = "waf" serialization_library = "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe" parameters = { "serialization.format" = 1 } } columns { name = "timestamp" type = "bigint" } columns { name = "formatversion" type = "int" } columns { name = "webaclid" type = "string" } columns { name = "terminatingruleid" type = "string" } columns { name = "terminatingruletype" type = "string" } columns { name = "action" type = "string" } columns { name = "terminatingrulematchdetails" type = "array<struct<conditiontype:string,location:string,matcheddata:array<string>>>" } columns { name = "httpsourcename" type = "string" } columns { name = "httpsourceid" type = "string" } columns { name = "rulegrouplist" type = "array<struct<rulegroupid:string,terminatingrule:struct<ruleid:string,action:string>,nonterminatingmatchingrules:array<struct<action:string,ruleid:string>>,excludedrules:array<struct<exclusiontype:string,ruleid:string>>>>" } columns { name = "ratebasedrulelist" type = "array<struct<ratebasedruleid:string,limitkey:string,maxrateallowed:int>>" } columns { name = "nonterminatingmatchingrules" type = "array<struct<ruleid:string,action:string>>" } columns { name = "httprequest" type = "struct<clientIp:string,country:string,headers:array<struct<name:string,value:string>>,uri:string,args:string,httpVersion:string,httpMethod:string,requestId:string>" } } } Kinesis Firehose Firehoseの名前はaws-waf-logs-で始まる必要があります。 また、S3に配信するFirehoseとDatadogに配信するFirehoseをそれぞれ定義します。 S3配信用Firehoseは、Lambdaを挟むことによりDatadog配信用Firehoseにも送信するアーキテクチャとします。 (Lambdaの実装は後述します。) KMSキーなどの変数部分はそれぞれで定義方法を判断してください。 S3配信用Firehose ポイントは、ログを配置するS3のPrefixです。 ここは前述のGlue定義でパーティション分割のキーとなるので以下のように定義します。 prefix = "waf/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/hour=!{timestamp:HH}/" resource "aws_iam_role" "firehose_role" { name = "${var.service_name}-Firehose-Role" assume_role_policy = data.aws_iam_policy_document.firehose_assume_role.json } data "aws_iam_policy_document" "firehose_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["firehose.amazonaws.com"] } condition { test = "StringEquals" variable = "sts:ExternalId" values = [ var.aws_account_id ] } } } resource "aws_iam_policy" "firehose_policy" { name = "${var.service_name}-Firehose-Policy" policy = <<POLICY { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Action": [ "glue:GetTable", "glue:GetTableVersion", "glue:GetTableVersions" ], "Resource": "*" }, { "Sid": "", "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::${aws_s3_bucket.log_bucket.name}", "arn:aws:s3:::${aws_s3_bucket.log_bucket.name}/*", "arn:aws:s3:::%FIREHOSE_BUCKET_NAME%", "arn:aws:s3:::%FIREHOSE_BUCKET_NAME%/*" ] }, { "Sid": "", "Effect": "Allow", "Action": [ "lambda:InvokeFunction", "lambda:GetFunctionConfiguration" ], "Resource": "arn:aws:lambda:ap-northeast-1:${var.aws_account_id}:function:*:*" }, { "Sid": "", "Effect": "Allow", "Action": [ "logs:PutLogEvents" ], "Resource": [ "*" ] }, { "Sid": "", "Effect": "Allow", "Action": [ "kinesis:DescribeStream", "kinesis:GetShardIterator", "kinesis:GetRecords", "kinesis:ListShards" ], "Resource": "arn:aws:kinesis:ap-northeast-1:${var.aws_account_id}:stream/%FIREHOSE_STREAM_NAME%" }, { "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": [ "arn:aws:kms:ap-northeast-1:${var.aws_account_id}:key/%SSE_KEY_ID%" ], "Condition": { "StringEquals": { "kms:ViaService": "kinesis.%REGION_NAME%.amazonaws.com" }, "StringLike": { "kms:EncryptionContext:aws:kinesis:arn": "arn:aws:kinesis:%REGION_NAME%:${var.aws_account_id}:stream/%FIREHOSE_STREAM_NAME%" } } } ] } POLICY } resource "aws_iam_role_policy_attachment" "firehose_policy_0" { policy_arn = aws_iam_policy.firehose_policy.arn role = aws_iam_role.firehose_role.name } resource "aws_kinesis_firehose_delivery_stream" "waf_firehose" { destination = "extended_s3" name = "aws-waf-logs-${var.service_name}" extended_s3_configuration { role_arn = aws_iam_role.firehose_role.arn bucket_arn = aws_s3_bucket.log_bucket.arn prefix = "waf/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/hour=!{timestamp:HH}/" error_output_prefix = "waf/errors/!{firehose:random-string}/!{firehose:error-output-type}/!{timestamp:yyyy-MM-dd}/" compression_format = "UNCOMPRESSED" buffer_interval = 300 buffer_size = 128 kms_key_arn = var.kms_key_arn processing_configuration { enabled = true processors { type = "Lambda" parameters { parameter_name = "LambdaArn" parameter_value = "arn:aws:lambda:ap-northeast-1:${var.aws_account_id}:function:${var.waf_firehose_lambda_name}:$LATEST" } } } cloudwatch_logging_options { enabled = true log_group_name = "/aws/kinesisfirehose/aws-waf-logs-${var.service_name}" log_stream_name = "S3Delivery" } data_format_conversion_configuration { input_format_configuration { deserializer { open_x_json_ser_de {} } } output_format_configuration { serializer { parquet_ser_de {} } } schema_configuration { database_name = aws_glue_catalog_database.database.name table_name = aws_glue_catalog_table.waf_catalog_table.name role_arn = aws_iam_role.firehose_role.arn } } } server_side_encryption { enabled = true key_type = "AWS_OWNED_CMK" } } Datadog配信用Firehose Datadogに送信するためにはHTTPエンドポイントによる配信を使用します。 APIキーは変数で挿入する形としています。 resource "aws_iam_role" "firehose_role" { name = "DatadogForwarder-Firehose-Role" assume_role_policy = data.aws_iam_policy_document.firehose_assume_role.json } data "aws_iam_policy_document" "firehose_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["firehose.amazonaws.com"] } condition { test = "StringEquals" variable = "sts:ExternalId" values = [ var.aws_account_id ] } } } resource "aws_iam_policy" "firehose_policy" { name = "DatadogForwarder-Firehose-Policy" policy = <<POLICY { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Action": [ "glue:GetTable", "glue:GetTableVersion", "glue:GetTableVersions" ], "Resource": "*" }, { "Sid": "", "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::${aws_s3_bucket.log_bucket.name}", "arn:aws:s3:::${aws_s3_bucket.log_bucket.name}/*", "arn:aws:s3:::%FIREHOSE_BUCKET_NAME%", "arn:aws:s3:::%FIREHOSE_BUCKET_NAME%/*" ] }, { "Sid": "", "Effect": "Allow", "Action": [ "lambda:InvokeFunction", "lambda:GetFunctionConfiguration" ], "Resource": "arn:aws:lambda:ap-northeast-1:${var.aws_account_id}:function:*:*" }, { "Sid": "", "Effect": "Allow", "Action": [ "logs:PutLogEvents" ], "Resource": [ "*" ] }, { "Sid": "", "Effect": "Allow", "Action": [ "kinesis:DescribeStream", "kinesis:GetShardIterator", "kinesis:GetRecords", "kinesis:ListShards" ], "Resource": "arn:aws:kinesis:ap-northeast-1:${var.aws_account_id}:stream/%FIREHOSE_STREAM_NAME%" }, { "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": [ "arn:aws:kms:ap-northeast-1:${var.aws_account_id}:key/%SSE_KEY_ID%" ], "Condition": { "StringEquals": { "kms:ViaService": "kinesis.%REGION_NAME%.amazonaws.com" }, "StringLike": { "kms:EncryptionContext:aws:kinesis:arn": "arn:aws:kinesis:%REGION_NAME%:${var.aws_account_id}:stream/%FIREHOSE_STREAM_NAME%" } } } ] } POLICY } resource "aws_iam_role_policy_attachment" "firehose_policy_0" { policy_arn = aws_iam_policy.firehose_policy.arn role = aws_iam_role.firehose_role.name } resource "aws_kinesis_firehose_delivery_stream" "waf_datadog_forwarder" { name = "aws-waf-logs-datadog-forwarder" destination = "http_endpoint" http_endpoint_configuration { name = "Datadog" url = "https://aws-kinesis-http-intake.logs.datadoghq.com/v1/input" access_key = var.datadog_api_key_value role_arn = aws_iam_role.firehose_role.arn buffering_interval = 60 buffering_size = 4 retry_duration = 60 processing_configuration { enabled = false } request_configuration { content_encoding = "GZIP" common_attributes { name = "env" value = var.stage } } s3_backup_mode = "FailedDataOnly" } s3_configuration { bucket_arn = aws_s3_bucket.log_bucket.arn prefix = "/firehose/aws-waf-logs-datadog-forwarder" compression_format = "GZIP" kms_key_arn = var.kms_key_arn role_arn = aws_iam_role.firehose_role.arn } server_side_encryption { enabled = true key_type = "AWS_OWNED_CMK" } } Lambda S3配信用Firehoseから、Datadog転送用Firehoseにも送信するようにLambdaを使用します。 S3配信用Firehoseには、受領したデータをそのまま返します。 'use strict'; const AWS = require('aws-sdk'); const deliveryStreamName = 'aws-waf-logs-datadog-forwarder'; const firehose = new AWS.Firehose({ region: 'ap-northeast-1', }); module.exports.forwarder = async (event, context, callback) => { const data = event.records.map(record => { return { Data: Buffer.from(record.data, 'base64').toString('utf8'), } }); firehose.putRecordBatch({ DeliveryStreamName: deliveryStreamName, Records: data, }, (err) => { if (err) { console.error(err, err.stack); } }); const output = event.records.map(record => { return { recordId: record.recordId, result: 'Ok', data: record.data, }; }); callback(null, { records: output }); } LambdaのロールにFirehose権限を付与するのを忘れないようにしましょう。 { "Action": [ "firehose:PutRecordBatch" ], "Resource": [ "arn:aws:firehose:ap-northeast-1:123456789012:deliverystream/*" ], "Effect": "Allow" } おわりに 以上によりAthena、Datadogへのログ配信が可能になります。 Athenaではパーティションキーを使用して検索しましょう。 (例) select * from waf where year = '2021' and month = '05' and day = '15' and hour = '00'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud9の開発前準備(容量削減)

はじめに Cloud9にて開発を実施していく中でディスク不足が原因で様々な問題を引き起こしてしまったので、開発着手前に事前に設定しておくべき内容を記載しておきます。 ディスク不足によるエラーに関しては以下内容をご参照ください。 https://qiita.com/YotaHamasaki/items/a7ce6bc4039607e3a389 https://qiita.com/YotaHamasaki/items/a9b2093eeee44057aeef 設定内容 ①dockerイメージの削除 ②メモリの解放 ③swap領域設定 ④ボリュームの追加 ①dockerイメージの削除 ワークスペース作成時にはdockerイメージが初期状態で入っています。 これが容量を大きく圧迫しているので、dockerにて環境構築しない場合、削除してしまった方が良いと思います。 コマンドは以下になります。 $ docker rmi `docker images -q` ②メモリの解放 メモリの容量不足によりプログラム実行時にエラーが発生することがあるので、こちらも実施しておいた方が良いと思います。 $ sudo sh -c "echo 3 > /proc/sys/vm/drop_caches" ③swap領域設定 swap領域はHDD上の仮想的なメモリで、実メモリの使用量を超えた分を一時的に書き出す場所です。 こちら領域の設定をしておきましょう。 $ sudo dd if=/dev/zero of=/var/swap.1 bs=1M count=1024 $ sudo chmod 600 /var/swap.1 $ sudo mkswap /var/swap.1 $ sudo swapon /var/swap.1 $ sudo cp -p /etc/fstab /etc/fstab.ORG $ sudo sh -c "echo '/var/swap.1 swap swap defaults 0 0' >> /etc/fstab" ④ボリュームの追加 HDDの容量不足を防ぐために、HDDの容量追加を行います。 これはEC2インスタンスの設定変更にて、ボリュームを増やすことで実現します。 この手順としては以前記事にしていたので、そちらをご参照いただければ幸いです。 https://qiita.com/YotaHamasaki/items/a7ce6bc4039607e3a389 そしてインスタンスタイプの変更ができたら、次はターミナル上で以下コマンドを入力して、HDDの容量が設定した容量となります。 $ sudo growpart /dev/xvda 1 $ sudo resize2fs /dev/xvda1 無駄な容量を喰わないように開発前から準備する大切さを学びました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS VPCについて

VPC (Amazon Virtual Private Cloud)  VPCは、AWSクラウド内に論理的に分離されたセクションを作り、ユーザーが定義した仮想ネットワークを構築するサービス。  サブネットは、パプリックサブネットとプライベートサブネットに分けることでのセキュリティ面、複数のアベイラビリティゾーンでAWSサービスを利用して可用性を図る面では重要になる。 ・任意のIPアドレス範囲の洗濯をして仮想ネットワークを構築 ・サブネットの作成、ルートテーブルやネットワークゲートウェイの設定などネットワーキング環境を完全に制御可能 ・必要に応じてクラウド内外のネットワーク同士を接続することも可能 ・複数の接続オプションが利用可能(インターネット経由/VPN/専用線Direct Connect) サブネット VPCのIPアドレス、1つ以上のサブネットが必要になる。 IPv4は枯渇しつつ、今後はIPv6に変わっていく。 ネットワークACLを使用してトラフィックの設定を行える。 サブネット作成時にAZを指定する。作成後は変更ができない。 パブリックサブネット インターネットを接続する必要があるリソースの場合、インターネットの出入りができる。 パブリックサブネットからインターネットに接続するには、インターネットゲートウェイが必要です。 プライベートサブネット インターネットに接続しないリソースの場合、インターネットから出れるのみ!!!!!! プライベートからインターネットに繋げたい場合は、パブリックサブネットにNATゲートウェイを置き、NATゲートウェイからIGW(インターネットゲートウェイ)を通ってインターネットから出れるようになる。 インターネットから隔離することでセキュリティを高めることが可能です。 インターネットゲートウェイ(IGW) VPC内のリソースとインターネット間の通信を可能にするためにVPCにアタッチするゲートウェイ。 仮想プライートゲートウェイ(VGW) VPCがVPNやDirect Connectと接続するためのゲートウェイです。VGWも各VPCに1つだけアタッチすることができます。複数のVPNやDirect Connectと接続は可能。 ルートテーブル ネットワークトラフィックの経路を判断する際に使用される。(ルートと呼ばれる) VPC内部通信や、インターネット・オンプレミスネットワーク基盤など外部へび通信を実装できる。 1つのルートテーブルを複数のサブネットで共有することはできるが、1つのサブネットに複数のルートテーブルは適用できない。 ・セキュリティグループ(ステートフル)  ・EC2やELB、RDSなどインスタンス単位の通信制御に利用する。 ・ネットワークACL(ステートレス)  ・サブネットごとの通信制御に利用する。 CIDR サブネットマスクの値を設定し、同じネットワークとして扱うIPアドレスの個性を調整できる。 【重要】 VPCは、/16 〜 /28のCIDR範囲しか使用できない。 ある特定のIPアドレスだけ範囲指定したい場合は、/32を使用して制限することが可能。 サブネットマスク サブネット数 サブネット当たりのIPアドレス数 /18 4 16379 /20 16 4091 /22 64 1019 /24 256 251 /26 1024 59 /28 4096 11 VPCとオンプレミス接続 ・VPN接続 ・専用線接続(Direct Connect) Direct Connect  お客様のデータセンターやオフィスを専用線などを介してAWSへプライベートに接続するサービス。 物理的に自社オンプレ環境を接続することでAWS環境との専用線接続を実現することが可能。 VPNの方が安くて早く使用できるが、信頼性や品質はDirect Connectが良い。 VPN Direct Connect コスト 安価なベストエフォート回線利用可能 キャリアの専用線サービス契約が必要となりVPNより割高 リードタイム クラウド上での接続設定で可能なため即時使用可能 物理的対応が必要なため数週間かかる 帯域幅 暗号化のオーバヘッドにより制限がある ポート当たり1G/10Gbps 品質 インターネット経由のためネットワーク状態の影響を受ける キャリアにより高い品質が保証される 障害 自社で保持している範囲以外の確認は難しい 物理的に経路が保管されているため比較的容易 Direct Connect Gateway 1つのDirect Connect接続で拠点と複数のAWSアカウントやVPCに接続することが可能です。 VPCエンドポイント VPVエンドポイントは2種類あり、ゲートウェイ型とプライベートリンク型 VPCエンドポイント(ゲートウェイ型)は、ルートテーブルの指定されたターゲットとなるゲートウェイです。サポートされる AWS のサービスを宛先とするトラフィックをVPC内外で接続する際に使用します。以下の AWS のサービスがサポートされています。 ・Amazon S3
 ・DynamoDB VPCエンドポイント(プライベートリンク型)は、対象サービスを宛先とするトラフィックのエントリポイントとなるプライベート IP アドレスを持つ Elastic Network Interface です。以下のサービスがサポートされています。 ・Amazon API Gateway ・Amazon CloudWatch ・Amazon CloudWatch Events ・Amazon CloudWatch Logs ・AWS CodeBuild ・Amazon EC2 API ・Elastic Load Balancing API ・AWS Key Management Service ・Amazon Kinesis Data Streams ・Amazon SageMaker ランタイム ・AWS Secrets Manager ・AWS Service Catalog ・Amazon SNS ・AWS Systems Manager ・他の AWS アカウントによってホストされるエンドポイントサービス ・サポートされる AWS Marketplace パートナーサービス VPCピアリング VPCピアリングにより2つのVPC間でのトラフィックルーティングが可能になる。 ・異なるAWSアカウント間のVPC間を接続可能 ・一部のリージョン間の異なるVPC間の接続可能 ・単一障害点や帯域幅のボトルネックは存在しない VPCフローログ VPC内の通信解析には、VPCフローログを利用します。VPCフローログはAWSでの仮想ネットワークインターフェイスカードであるENI単位で記録されます。 【送信元/送信先アドレスとポート、プロトコル番号、データ量と許可/拒否】 VPC内にあるEC2インスタンスに繋がらない VPC内に設置したEC2インスタンスに対してインターネットからアクセスできない場合は、以下のような要因が考えられます。
 ・インターネットゲートウェイがサブネットに設定されていない。 
・ネットワークACLの設定でインターネットアクセス許可が設定されていない。 
・セキュリティグループの設定でインターネットアクセス許可が設定されていない。 
・パブリックIPアドレスが付与されていない。 インターネットアクセスができていない可能性があるため、VPCにインターネットゲートウェイが接続されていて、ルートテーブルがサブネットに対して正しく構成されていることを確認する必要があります。 Transit Gateway 複数のVPCとオンプレミスネットワークを中央ハブを介して接続するサービス。 Transit Gateway を使用すれば、中央のゲートウェイからネットワーク上にあるVPC、オンプレミスのデータセンター、リモートオフィスそれぞれに単一の接続を構築して管理することができます。Transit Gateway がハブの役割を果たし、トラフィックがスポークのように接続されたネットワーク間をどのようにルーティングするかをすべて制御します。このようなハブアンドスポーク型では、各ネットワークを Transit Gateway にのみ接続する必要があり、他のすべてのネットワークに接続する必要がないため、大幅に管理を簡略化して運用コストを削減できます。 新たに VPC を追加する場合は、Transit Gateway に接続するだけで、同じ Transit Gateway に接続されている他のネットワークにも自動的につながります。このように接続が簡単なため、成長に合わせてネットワークを難なくスケールできます。
 AWS managed VPN Amazon VPCにはリモートの顧客ネットワークとVPCの間にIPsec VPN接続を作成するオプションがあります。AWS managed VPNを利用することで、オンプレミス環境とVPC間とのサイト間接続を実行することができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JAWS-UG朝会 #20で登壇した振り返り

はじめに みなさんこんにちは! ハンズラボのサムです! 色々とあって記事にするまでに時間がかかってしまい申し訳ありません。 今回の記事は4月22日に参加したJAWS-UG朝会で登壇した時の振り返りです。 登壇の経緯 直近登壇したイベントの主催者である山下さんからの紹介で、 JAWS-UG朝会主催者の小倉さんを紹介して頂き登壇することになりました! こうやって色々な方とつながっていけるのはいいですね! ※前回の登壇のお話 登壇内容と反省会 テーマ:「AWS Transfer Familyを触ってみた !」 というテーマで5分のLT枠で登壇させていただきました。 こちらが当日使用したパワポです パワポを作ってる段階ですでに気がついたのですが致命的なミスに気が付きました。 どう考えても5分のLTで終わる内容ではなかったのです・・・。 自己紹介端折っても、パワポの内容端折っても辛うじて5分といった内容でした。 そもそもJAWS-UG朝会では ・5分間のLT ・20分間のセッション という形で選べたのですが、20分喋れる自信がなかったので5分のLTを選択したのですが完全に失敗でした... ※次回からはサービスの紹介なんかは20分枠にしたいです 時間に内容が縛られてしまった感じはあるので、リベンジするなら20分間AWS Transfer Familyについて語りたいですね! まとめ 朝活に登壇するとすごく朝から充実した気分になるので楽しかったです! 今の御時世どこか場所をかりて勉強会なんてのは少なくなったと思うのですが、 逆にオンラインになったお陰で登壇の敷居も下がったのかな?なんて考えたり。 対面とオンラインはそれぞれに良い点悪い点があるので一概には言えませんが、 いつかまた実地での勉強会なんかも参加できるような世の中になればなと思いました! イベントの主催者様、他登壇者様、見てくださった方有難うございました! 5月は用事があるので厳しいですが翌月以降リベンジしたいです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】CloudFrontとは

CloudFrontとは フルマネージド型CDN(コンテンツ・デリバリー・ネットワーク)サービスです。 100以上のエッジロケーションにコンテンツをコピーして配信する機能を持っています。(キャッシュ機能) 図にすると以下の通りです。 ユーザーはEC2から情報を取りにいくのではなく、 CloudFrontから情報を取りにいくようになります。 EC2からデータを取りに行かないようになるため、EC2への負荷が軽減されます。 設計に関して Originには、EC2やS3、ELBなど様々なリソースを配置できます。 CloudFrontには、Behaviorsでどのような振る舞いをするか設定し、Cache PolicyでTTL(Originの設定を何秒間ごとに読み込むかどうか時間を決める機能)を設定できます。 向いているデータ 向いていないデータ 頻繁にアクセスされるデータもしくはデータが多少古くても問題にならない時 変化が激しいデータもしくはアクセスがほぼない時 例)SNS のプロフィールなど 例)ビットコイン板情報 では実際にキャッシュサーバーを設置してみよう! アーキテクチャ これだけでは、クライアントとRoute53とCloudFrontとELBの構成が複雑なので簡略化した分かりやすい図を作りました。 そして、ELBとCloudFrontの2つにACM(AWS Certificate Manager)をアタッチして、HTTPS通信が可能になれば完了です! 1. AWSへログイン 2. 東京リージョンになっていることを確認 3. VPCを作成(IP:10.0.0.0/21) ■ルート コンソールで「VPC」と検索→左ペイン内の「VPC」を選択→「VPCを作成」を選択 4. サブネットを作成(IP:10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24) ■ルート コンソールで「VPC」と検索→左ペイン内の「サブネット」を選択→「サブネットを作成」を選択 Public Subnetを作成 WordPressに使うサブネットを作成します。 Private Subnetを作成 RDB(MySQL)に使うサブネットを作成します。 5. Publicサブネットをインターネットに接続するためにInternet Gatewayを作成 ■ルート コンソールで「VPC」と検索→左ペイン内の「インターネットゲートウェイ」を選択→「インターネットゲートウェイの作成」を選択 これではVPCとアタッチできていないので、インターネットへ接続できません。 なのでアタッチさせましょう! これでアタッチしました。 6. EC2がインターネット接続するためにルートテーブルを作成 ■ルート コンソールで「VPC」と検索→左ペイン内の「サブネット」を選択→「Public-Subnet1」を選択→「ルートテーブル」を選択→「ルート」へ移動→「ルートを編集」を選択 7. Publicサブネット内にEC2を構築 ■ルート コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「インスタンスを起動」を選択 ■ステップの流れ STEP1 Amazon Linux 2 AMI STEP2 t2.micro STEP3 「ネットワーク」、「サブネット」、「パブリックIP」を変更 STEP4 そのままでOK STEP5 Name=WEBSERVER1で設定 STEP6 セキュリティグループを変更 STEP7 キーペアの作成 SSH接続でログイン ターミナル // ①キーペアがあるところへ移動 $ cd desktop // ②キーペア発見 $ ls -l keypair.pem -rw-r--r--@ 1 ryo staff 1704 5 1 14:38 keypair.pem // ③権限を400に変更 $ chmod 400 keypair.pem // ④SSH接続 $ ssh -i keypair.pem ec2-user@(パブリックIPアドレス) __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| EC2にWordPressを導入 ターミナル // 権限が足りないので権限をrootに移動 $ sudo su - // EC2のパッケージを最新の状態にアップデート $ yum -y update // WordPressが動くために必要なツールをダウンロード(PHP, Apache, MySQL) $ amazon-linux-extras install php7.2 -y $ yum -y install mysql httpd php-mbstring php-xml gd php-gd // Apacheが再起動後も自動的に動くようにする $ systemctl enable httpd.service // Apacheをスタート状態に $ systemctl start httpd.service // カレントディレクトリにWordPressの最新パッケージを入れる $ wget http://ja.wordpress.org/latest-ja.tar.gz ~/ // あるか確認する $ ls -l total 16076 -rw-r--r-- 1 root root 16458939 Apr 21 00:00 latest-ja.tar.gz(あった) // 拡張子がgzなので展開しましょう $ tar zxvf ~/latest-ja.tar.gz // ディレクトリごと/var/www/htmlへコピーする $ cp -r ~/wordpress/* /var/www/html/ // /var/www/htmlをapacheのユーザーに権限変更 $ chown apache:apache -R /var/www/html AMIの作成 作成したWEBSERVER1を停止してAMIを作成します。 ■ルート コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「インスタンスの状態」から停止を選択 コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「アクション」から「イメージを作成」を選択 では作成したAMIを使ってインスタンスを作成しましょう。 ステップ5のタグは、Name=WEBSERVER2とします。 8. PrivateサブネットにRDSを構築 RDSには、エンドポイント通信という機能がありました。 もし通信障害などが発生して、親のRDSが使えなくなった時子のRDSに接続してくれる機能です。 つまり、このことから異なるAZにサブネットがないとRDSは使えません。 ということで複数のサブネットにまたがる形でRDSは構築されるので、 サブネットグループというものが必要になります。 ■ルート コンソールで「RDS」と検索→左ペイン内の「サブネットグループ」を選択→「DB サブネットグループを作成」を選択 それでは次に、DBを作成していきましょう。 ■ルート コンソールで「RDS」と検索→左ペイン内の「データベース」を選択→「データベースの作成」を選択 次にRDSで作ったセキュリティグループを修正します。 ■ルート コンソールで「VPC」と検索→左ペイン内の「セキュリティグループ」を選択→「RDS-SG-1」を選択 DBは重要な情報が入っているため誰でも彼でもアクセスできると困ります。 なので、PublicサブネットのEC2(セキュリティグループ→WEB-SG-1)からしか触れないように修正します。 9. ロードバランサーを導入 ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「ロードバランサーの作成」を選択 10. RDSのサイトアドレスをロードバランサーのDNSに書き換え ターミナル // ①RDSへログイン $ mysql -h database-1.xxxxxxxxxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -u (管理者) -p // ②WordPressのテーブルを使用 MySQL [(none)]> USE wordpress; // ③指定のカラム // errorが出た場合はEC22つにWordpressログインをしてからやり直してください MySQL [(none)]> SELECT * FROM wp_options WHERE option_name IN ('siteurl', 'home'); +-----------+-------------+-----------------------+----------+ | option_id | option_name | option_value | autoload | +-----------+-------------+-----------------------+----------+ | 2 | home | http://13.231.153.132 | yes | | 1 | siteurl | http://13.231.153.132 | yes | +-----------+-------------+-----------------------+----------+ // ④ロードバランサーのDNS名に置き換える MySQL [(none)]> UPDATE wp_options SET option_value = 'http://xx.xx.xx.xx' WHERE option_name IN ('siteurl', 'home'); 11. EC2のセキュリティグループのアクセス許可をロードバランサーからに変更する ■ルート コンソールで「EC2」と検索→左ペイン内の「セキュリティグループ」を選択→「WEB-SG-1」を選択 12. RDSも冗長化構成へ変更 ■ルート コンソールで「RDS」と検索→左ペイン内の「データベース」を選択→「database-1」を選択 13. freenomでドメインを購入 今回はデモのため無料でドメインを取得できるfreenomを使います。 こちらが分かりやすいのでこちらを参考にしてください。 購入した後は、「Services」 → 「My Domains」 -> 「Management Tools」 → 「NameServers」を開きます。 13. Route53の設定をします ■ルート コンソールで「Route53」と検索→左ペイン内の「ホストゾーン」を選択→「ホストゾーンの作成」を選択 このネームサーバーの値をFreenomの方に書き写します。 14. 取得したドメインでアクセスできるようにする ■ルート コンソールで「Route53」と検索→左ペイン内の「ホストゾーン」を選択→「aws-lesson.tk」を選択→「レコードを作成」を選択 これでロードバランサーとblog.aws-lesson.tkは結びつきました。 blog.aws-lesson.tkにアクセスしてみましょう! 成功です!! 15. エラーページを表示するためにフェイルオーバールーティングの設定をします これでプライマリの表示は完成しました。 次に障害が起きた時に備えてエラーページを作成しましょう。 そしてそのエラーページをセカンダリで表示させます。 ■ルート コンソールで「S3」と検索→左ペイン内の「バケット」を選択→「バケットを作成」を選択 ではここにエラーページをアップしましょう! 一番下に、「静的ウェブサイトホスティング」があるのでそれを選択します。 次にバケットポリシーを編集します。 ポリシー { "Version":"2012-10-17", "Statement":[ { "Sid":"PublicRead", "Effect":"Allow", "Principal": "*", "Action":["s3:GetObject","s3:GetObjectVersion"], "Resource":["arn:aws:s3:::blog.aws-lesson.tk/*"] } ] } これでエラーメッセージが表示されます。 16. それではRoute53でセカンダリの設定を行います ■ルート コンソールで「Route53」と検索→左ペイン内の「ホストゾーン」を選択→「aws-lesson.tk」を選択 フェイルオーバーに変更します。 まずはプライマリを変更します。 次にセカンダリを変更します。 17. HTTPS通信を可能にするためにロードバランサーでリスナーを追加 ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「LB-1」を選択→「リスナーの追加」を選択 「新しいACM証明書をリクエスト」で発行を行います。 Route53にあるかどうか確かめましょう。 ありました!! 18. セキュリティグループの修正でHTTPSも受け付けられるようにしよう ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「LB-1」を選択→「セキュリティグループ」を選択 これで完了です。 しかし、WordPressの仕様上表示が乱れてしまうのでここを直しましょう! 19. SSHコマンドでEC2と接続 1台目のEC2と2台目のEC2両方ともに行います。 ターミナル $ ssh -i keypair.pem ec2-user@52.193.252.134 $ sudo su - $ cd /var/www/html/ // wp-config.phpを開いて修正する $ vi wp-config.php // 「編集が必要なのはここまでです ! WordPress でのパブリッシングをお楽しみください。」の上にコードを挿入する if($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') { $_SERVER['HTTPS'] = 'on'; $_ENV['HTTPS'] = 'on'; } 20. ロードバランサーでHTTP通信ができないようにする ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「LB-1」を選択→「セキュリティグループ」を選択 21. Route 53の設定を変更する 以下のようにレコード名をalb.aws-lesson.tkに変更します。 ■ルート コンソールで「Route 53」と検索→左ペイン内の「ホストゾーン」を選択→「aws-lesson.tk」を選択 22. ロードバランサーで発行している証明書をどのサブドメインでも使えるようにする ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「リスナー」を選択 23. CloudFront用の証明書を発行 ■ルート コンソールで「AWS Certificate Manager」と検索→リージョン「バージニア北部」を選択 24. CloudFrontの設定 ■ルート コンソールで「CloudFront」と検索 キャッシュポリシーを作成します。 SSL証明書は先ほど作ったものを読み込みます。 そして、blog.aws-lesson.tkにアクセスできるよう、CNAMEsを設定します。 ドメインネームが発行されました 25. Route53でCloudFontのドメインとblog.aws-lesson.tkを接続 ■ルート コンソールで「Route53」と検索→左ペイン内の「ホストゾーン」を選択→「aws-lesson.tk」を選択 これで完成です!! 削除手順 S3 Route53 RDS CloudFront LB EC2 AMI AWS Certificate Manager(東京とバージニア2つ) VPC
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モダンな技術を使ってRailsアプリを開発、デプロイするまでのロードマップ2021

はじめに  この記事はモダンな技術(後述)を使用してRailsアプリケーションを開発し、AWS上にデプロイするまでの学習手順を詳細にまとめたものです。基本的に参考になる記事、書籍を編纂し、自分がわかりにくかったと感じた部分は自分で記事を書いています。リンクの記事を書いてくださった方々には本当に感謝です。 この記事の目的  新しいことを独学しようとすると一つ大きな問題があります。それは計画通りに実行するのが困難であるということです。自分のすべき作業が明確化されていないことによって、 1. どのくらいで終わるのかを数値化することが困難 2. エラーを予測できない 3. そもそも次になにをすべきかわからず、それを決定するのに時間を要する などの問題が発生します。私自身インフラの学習からアプリ開発、デプロイ完了までの期間が予定よりも2ヶ月近く伸びしまいました。  この記事は次にやるべきことを明確化することによって、以上のような計画錯誤を減らすことを目的としています。計画術についてはDaiGoさんの書籍が参考になるかと。 この記事で紹介する言語、フレームワーク、その他技術 フロント関連 HTML CSS Sass JavaScript jQuery Vue.js Ruby on Rails MySQL nginx Docker AWS IAM VPC ELB Route 53 RDS S3 ECR ECS CloudWatch Systems Manager Certificate Manager Terraform CircleCI 留意事項 前提知識は特に必要ありません。必要ないところは読み飛ばしてください。 環境はWSL(Windows Subsystem for Linux)です。Macを使用している方は導入に関する部分はご自身で調べてください。 技術の独学について  新しい技術を独学するには以下のような方法が効率的です(経験則ですが)。 基礎知識を身に着けます。具体的には"〇〇 入門"とかで検索すると入門者向けの記事が見つかるのでそれを読むとよいかと。 次にハンズオン学習を行います。言い換えればチュートリアルです。実際に触って基本操作を身に着けます。 チュートリアルをクリアしたら書籍を一冊読むなどして体系的知識を身に着けます。 5. 公式ドキュメントをみながら実践を行います。 Column 「学んだことはすぐに忘れてしまう!だから技術を学ぶにはとにかくスピードが大事なのだ!」  それは短期記憶向きの学習法しか実践していないからだと思います。長期的に記憶を維持したい場合には検索練習を行いましょう。検索練習に関してはパレオさんのブログを参照。個人的にはエクセルなんかに検索練習しておくと忘れたときにチートシートして使えるのでおすすめ。 「完全に理解しようとするのは効率が悪い!だから理解せずともどんどん先に進めるべきだ!」  確かにRailsチュートリアルとかちゃんと理解しながら進めようとするとめちゃくちゃ時間かかるんですよね。かといってこの言葉を鵜呑みにしてもエラーばっかでやる気が削がれてしまうのかなと。なのでハンズオン系の学習に関しては適切な難易度設定を意識してみるとよいかと思います。こちらもパレオさんのブログを参照下さい。 ロードマップ 1. ITに関する基礎知識を学ぶ  ようやくここから本題です。まずITに関する基礎知識を勉強しますが、正直ここはしっかり勉強しようとしなくていいです。なんせ情報が多いので。キタミ式等の書籍をざっくり読んで、後々わからない言葉等が出てきたときにその都度「そういえばそんなことあの本に書いてあったな...」で読み返せばいいと思います。 2. HTML, CSS, Sassの基礎知識、チュートリアル  Webページをつくるための言語です。Progateを利用して学習しましょう。 3. JavaScript, jQueryの基礎知識、チュートリアル  Webページを動的にするために使用します。Progateで。 4. エディターの導入、ショートカット、マルチカーソルの利用  ローカル(自分のPC)にエディターを導入します。色々ありますが迷ったらVSCodeを入れておけばよいかと。 またショートカット、マルチカーソルを利用するとコーディングを効率よく行えます。 Visual Studio Code キーボードショートカット一覧(チートシート) VSCodeのマルチカーソル練習帳 5. HTML、CSSを使ってモダンなコーディングができるようになる  "HTML5/CSS3モダンコーディング"という書籍をクリアすると基本的につくりたいようなページは自分で制作できるようになると思います。 6. CSSの設計方法を学ぶ  CSSはシンプル故にコードが煩雑になりやすいです。"CSS設計の教科書"という書籍を読みましょう。特にBEMとFLOCSSを抑えておくとよいかと思います。 7. Flexbox  FlexboxはCSSのレイアウト方法です。"HTML5/CSS3モダンコーディング"ではfloatプロパティを使って要素の横並びを行っていますが、Flexboxを使えば簡単に実装できます。実際にページを作る際に利用す際にチートシートをみて利用するとよいでしょう。 8. SQLの基礎知識、チュートリアル  データベースを操作するためのクエリ(一連の問い合わせ)です。Progateで。 9. Rubyの基礎知識、チュートリアル  Webアプリケーションのロジックをつくるための言語です。Progateで。 10. Ruby on Railsの超基礎知識、チュートリアル  Rubyで書かれたWebアプリケーションフレームワークです。Progateで。 11. コマンドライン操作の基礎知識、チュートリアル  コマンドを使ってファイル操作等を行います。ProgateのCommand Lineコースを利用して学習しましょう。 12. Gitの基礎知識、チュートリアル  ソースコードのバージョン管理システムです。Progateだけだと心許ないのでドットインストールのgit入門もクリアしておくとよいかと思います。 13. Ruby on Railsチュートリアル  最初の山場です。Ruby on Railsチュートリアルをクリアしましょう。わからなくなっても調べれば有益な情報がいくらでも出てくるので挫折の心配はないかと思います。 14. 開発環境を構築する  ローカル環境を構築します。WSLの基本については以下を参照。 【WSL入門】第1回 Windows 10標準Linux環境WSLを始めよう Ubuntuは無印版を推奨します。LTS版(20.04)ではVSCodeのいくつかの拡張機能をインストールできませんでした。 WSL でマウントしたファイルシステムでもパーミッションを扱えるようにする  WSLはlinuxがWindowsの全てのファイルを含むCドライブをマウントする形を取っていますが、デフォルトではCドライブのファイルにパーミッションが付与されていません(これによって度々エラーが発生します。因みにこれはつまりlinux上で開発を行えばよいのですが、これをすると高負荷な処理をした場合にWSLがフリーズしたりします)。 WSL2によるホストのメモリ枯渇を防ぐための暫定対処  またDocker等を使用して開発するとWSLのVmmemというプロセスがメモリ使用量が肥大化し続け、ホストメモリが枯渇するという問題があります。 WSLでRails環境を構築する場合は以下を参照。 [Rails] Windows10 で WSL を使って Rails 環境を構築したときのメモ パッケージマネージャについては以下を参照。 パッケージ管理ツールをまとめてみる 15. linuxの基礎知識  "linux標準教科書"というのが無料でダウンロードできるのでそちらをさくっと読んでしまうのがよいと思います。後は今後パーミッション、ソケット通信等を扱うのでそちらの知識やコマンドも合わせて抑えておくとよいです。 Linuxの権限確認と変更(chmod)(超初心者向け) インフラエンジニアじゃなくても押さえておきたいSSHの基礎知識 Unixドメインソケット PATHを通すために環境変数の設定を理解する (Mac OS X) Linuxディレクトリ構造 まとまった記事 “応用力”をつけるためのLinux再入門 16. Web技術の基礎知識  個人的には"イラスト図解式 この一冊で全部わかるWeb技術の基本"という書籍がよくまとまっていてわかりやすいと思いました。しかも安い。  上のような入門者向けの本を読んだら、次はWeb系エンジニア必読書と言われる"Webを支える技術"という書籍を読みましょう。 17. Ruby on Railsを体系的に学ぶ  "パーフェクトRuby on Rails"はRuby on Railsガイドをハンズオン形式で学習できるような内容となっています。この書籍を読むことでRailsの基本的な概念に対する理解を深め、Rails付属の機能を扱えるようになります。コンテナやCIに関する内容も参考になりますが、この段階ではまだ未学習(のはず)なので、後々読んでみるとよいかと思います。 18. JavaScript本格入門  "JavaScript本格入門"を読めばES6でJavaScriptをそれなりに書けるようになると思います。手元にあるとどうやって書くんだったかわからなくなったときに便利。 19. JavaScriptスタック JavaScriptのツール全般(Node, NPM, Yarn等)についての知識です。 JavaScript Stack from Scratch 日本語訳は以下 ゼロから始めるJavaScript生活 Webpackについては"webpack 実践入門"という書籍が参考になります。 20. Vue.jsの基礎知識、チュートリアル  SPAを構築するために使用します。あくまでRailsアプリにSPAを実装することが目的なので、基本のオプションとライフサイクル、コンポーネント間のデータフローを抑えておけば問題ないかと思います。学習の仕方としては"21Stepで体得 Vue.jsハンズオン"の1章、Step6, 11, 16を読んでやってみるとよいかと。 Railsへの導入方法は以下を参照。 Vue.js チュートリアル for Rails エンジニア(Vue3 version) またaxiosに関しては以下を参照。 [フロントエンド] axiosライブラリを使って、柔軟にHTTP通信を行う 21. ローカルで一度アプリケーションを作ってみる  ここで一旦Railsアプリケーションをつくってみましょう。この作業の目的は 1. 自分でRailsのコードが書けるようになること 2. アプリケーションの要項の洗い出し です。なのでこの段階ではアプリはガバでいいです。次つくるときにテスト駆動開発しましょう。  因みに要項を洗い出す場合にはマインドマップが役立ちます。 22. Dockerの基礎知識、チュートリアル  コンテナ仮想化を用いてアプリを開発するためのソフトです。私は"自宅ではじめる Docker入門"という書籍をやりました。Dockerfile, Docker Composeまでよくまとまっています。また"Docker/Kubernetes実践コンテナ開発入門"という書籍が有名?ですが入門者向きじゃないです。ただ(本番環境においても)コンテナ技術を使用するメリットを理解できるので、最初の章だけ読んでおけばいいんじゃないかと。 23. nginx、pumaの基礎知識  Railsアプリの前段にリバースプロキシとして使用するWebサーバです。pumaとソケット通信させて使用します。"nginx実践ガイド"という本を読みましょう。pumaの使い方に関しては以下を参照。 Pumaの使い方 まとめ 24. MySQL入門  RDBMSです。"MySQL徹底入門"という本を読みましょう。Docker Composeで扱う場合、主に関心事になるのは接続の仕方なのでユーザー管理の章を読んでおくといいと思います。因みに"基礎からのMySQL"という本もありますが、これは主にクエリについてまとめた本です(知らずに買いました)。  またRailsチュートリアルではサブクエリをそのまま文字列として記述していますが、ActiveRecord::Relationオブジェクトは最終的にサブクエリとして展開されるので、基本的にSQL文をそのまま書くことはないと思います(多分)。 参考 railsチュートリアル サブセレクトでなぜ高速化するのか 25. Dockerを使用した開発環境を構築する  ここが鬼門です。なんせRails, nginx, MySQL, Dockerの知識をここで集約する必要があるので。後々自分でも解説記事を書こうと思います。 参考 Docker + Rails + Puma + Nginx + MySQL 26. AWSインフラ構築入門  ここからは本番環境での運用を目的としたインフラを構築するための学習になってきます。"Amazon Web Services 基礎からのネットワーク&サーバー構築"か"AWSではじめるインフラ構築入門"またはその両方をクリアしておくとよいと思います。正直何も考えなくても手さえ動かせばできてしまうのでしっかり検索練習しときましょう。 初期設定まわりは以下を参照。 AWS-CLIの初期設定のメモ AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ リソースを削除する場合は以下を参考に。 全リージョンから全リソースを一括検索する方法【タグエディター】 全部消したと思って放っておくと一部のリソースが残っていてキャッシュから毎月数千円とかしょっぴかれてる場合があるのでご注意下さい(経験談)。 27. ECS, ECR基礎知識、チュートリアル 上で紹介した書籍ではコンテナ技術を使っていないのでECS, ECRも触っておきましょう。 初心者でもできる! ECS × ECR × CircleCIでRailsアプリケーションをコンテナデプロイ 28. Terraform入門、実践  TerraformとはIaC(Infrastructure as Code)を実現するためのものです。"実践Terraform"という書籍をクリアしましょう。正直Terraformを使用しなくてもデプロイはできるのですが、なぜTerraformを学習するとよいのかというと 1. 冪等性を利用して、破壊、再構築を何度でもできる。 2. 必要な情報のみをコード化して扱うのでロジックを理解しやすい。 3. 充実の公式ドキュメント の理由からです。  まず1について。インフラを運用する場合当然お金がかかります。因みにステージング環境と本番環境をESC、RDSのみ2つ用意し、それ以外の構成をホストベースルーティングで共有する環境を構築したとして、最低スペック、ほぼ未稼働で24時間あたりだいたい9ドル以上、日本円で1000円前後かかります。ただこれをコンソールで管理した場合、依存関係を認識しないとろくに削除もできないし、そもそもコンソールをいじっている時間だけ無駄です。  次に2について。これもすごく大きいです。コンソールだと必要な情報が視覚的にまとまってないし、エラーもキャッチしにくいのでロジックを理解するのが難しいんですよ。対してコードで扱えば情報を自分でまとめられるし、構築時にエラーがちゃんとでるし、何よりコードを書いている間に理解が進みます。  最後に3について。 公式ドキュメントがユースケース付きでまとまってて、すごく見やすいしわかりやすい。コード自体も別に難しくないので学習コストはそんなかかりません。それよりも必要な知識はAWSです。よってAWSに関しては以下を参照。 AWS IAM IAMロールに関しては以下を参照。 【図解/AWS】初心者にも分かりやすいIAM入門~ロールとグループとポリシーの違い,設計・設定手順について~ AWS IAMポリシーを理解する IAM ロールの PassRole と AssumeRole をもう二度と忘れないために絵を描いてみた JSONポリシーに関しては以下を参照。 IAM JSON ポリシーの要素のリファレンス Fargate ECS Fargateに関しては以下を参照。 基礎から応用までじっくり学ぶECS Fargateを利用したコンテナ環境構築 #Fargate "実践Terraform"ではFargate PV1.3.0を使用していますが、最新版は1.4.0で最新版を使用するにはエンドポイントが必要です。以下を参照。 【衝撃に備えろ】Fargate PV1.4のVPCエンドポイント変更点について ECRにpushしたコンテナをECSFargateで使うVPCエンドポイントTerraform例 Amazon ECS インターフェイス VPC エンドポイント (AWS PrivateLink) 秘匿情報 秘匿情報に関しては以下を参考に Terraform × パラメータストアでRDSの機密情報をセキュアに扱う AWS Systems Manager パラメータストア ホストベースルーティング ステージング環境については以下を参照。 まともなステージング環境"を考える リスナールールでサブドメインとターゲットグループを紐付けられます。ホストベースルーティングについては以下を参照。 【新機能】ALBのHost-based routingを試してみた TerraformでAWS上にHTTPS化したサブドメインを定義する タスク定義 ECSのタスク定義に関しては以下。 タスク定義パラメータ Terraform またTerraformに関しては以下を参照。 DRY Rails学習後にTerraformを学習してなんだこれ全然DRYじゃないじゃないかと思った方はいるはず。TerraformをDRYに書く方法に関しては以下を参照。 Terraformで複数台のEC2インスタンスを構築する場合のTIPS ただこの記事はやや古いので最新の構文に関しては公式ドキュメントを参照。 他にもDynamicやforを使用することでDRYに書くことができます。 ディレクトリ設計 ディレクトリ設計に関しては以下を参照。 Terraformのディレクトリパターン集 Code structure JSON TerraformにはJSONオブジェクトなるものがあり、Terraform内の変数使ってJSONを記述できます。 JSON Configuration Syntax 29. Dockerを使用してテスト及び本番環境の構築 本番用のDocker環境を構築する上で必要になってくる知識は、 1. Credentials 2. アセットパイプライン の2つです。 またテスト用の場合にはブラウザテストの知識が必要です。 Credentials Railsで環境毎に秘匿情報を扱うためのものです。詳細は以下を参照。 利用環境のセキュリティ Rails 6よりサポートされたMulti Environment Credentialsをプロジェクトに導入する Rails 5 から 6 にかけての secretes / credentials 周りの変遷 アセットパイプライン アセットパイプラインとはjsやcssのアセットを集約するためのフレームワークです。詳細はRailsガイドを参照。特に"production環境の場合"という項目が重要です。 またFargateを利用する場合、開発環境のようにDockerボリュームを利用できないのでnginxとRails間で同じボリュームをマウントしアセットを共有するということができません。よってAssetSyncというgemを使用し、s3からアセットを配布します。AssetSyncに関しては以下を参照。 Asset Sync Asset Sync を Webpacker と連携させる 【Rails】asset_syncを利用してCloudFront + S3からアセットファイルを配信する またCORSに関しては以下を参照。 オリジン間リソース共有 (CORS) Using CORS ブラウザテスト ブラウザを利用したシステムテストに関しては以下が参考になります。 2020年のRailsでブラウザテストを「正しく」行う方法(翻訳) Rails用Dockerfileを構築する Dockerfileの構築に関しては以下が参考になります。 効率的に安全な Dockerfile を作るには クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳) マルチステージビルドに関しては以下。 マルチステージビルドの利用 Multi-stage build でNode.jsのインストールをちょっぴり効率化する .dockerignoreに関しては以下。 .dockerignore file .dockerignoreが効かない?.gitignoreとは書き方が違うよ! またブラウザテストはdocker-seleniumを使用せずRailsと同じコンテナにchrome用のパッケージを入れることでも行えます。以下が参考になります。 Rails on Docker(alpine)でdocker-seleniumを使わないでSelenium+RSpec+Capybaraでテスト自動化してみる また本番環境用のDockerfile構築には以下が参考になります。 【Dockerfile全解説】Rails本番環境のための一番シンプルなDockerイメージを作る 30. CircleCIの基礎知識、チュートリアル CircleCI自体は難しくありません。ECS, ECRの場合、CircleCIのOrbsという機能を使えば簡単にデプロイできます。"CircleCI実践入門"という書籍を読みましょう。特に"Obrsを使った継続的デプロイの実践例"という項目をやっておくとよいと思います。 31. テスト(RSpec, Capybara) RSpec, Capybaraの書き方に関しては以下が参考になります。 使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 sleepやリトライに頼らない安定した feature spec を書こう Capybaraチートシート 公式ドキュメントは以下です。 RSpec Expectations 3.10 Capybara 32. アプリ開発開始 ここから実際にアプリをつくっていきます。新規プロジェクトを作成するときには以下の記事を参考に。 新規Railsプロジェクトの作成手順まとめ 33. 自動デプロイ Railsのディレクトリ内に.circleci/config.ymlをつくりCircleCIで自動テスト、自動デプロイを行えるようにします。以下の記事が参考になります。 【実践: 詳しくわかる】TerraformでCircleCIを通してAWSにECS環境を自動構築する方法 【CircleCI】【Terraform】【Rails】【AWS】Orbsを利用してECSでdb:migrateする方法 また公式のObrsは以下を参照。 circleci/aws-ecs circleci/aws-ecr 34. テスト駆動開発 あとはテスト駆動開発を進めていきます。 おわりに このロードマップは割とDeveloper Roadmapに沿ったものになっているかなと思います。改めてDeveloper Roadmapを見るとその有用性がわかります。ロードマップというのは順を追ってみていくのが普通ですが、このロードマップが真に有用である点は知識の依存関係がわかることです。目的の知識から逆順にたどっていくことで、それを理解するにはどんな知識が必要なのかある程度的を絞ることができます。独学をしてつまったときには一度、それにはどんな知識、技術が関わっているのかを考えてみるとよいかもしれません。  一から何かを独学しようなどというのはなかなか酔狂なことだと思いますが、この記事がそのような方の役に立てば幸いです。最後に改めて参考リンクの記事を書いてくださった方々に感謝です。有難うございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3のバケットポリシー書き方まとめ

最近AWSを勉強し始めたのですが、ポリシーを書くときに毎度迷子になるので バケットポリシーを中心としてリソースベースのポリシーの書き方をまとめておきます。 前提 例えばS3を静的ホスティング先として設定する場合、 ・ブロックパブリックアクセスのブロック無効化 ・静的ウェブサイトホスティングの有効化 これらを行いますが、それだけだとエンドポイントにアクセスすることはできません。 ↑アクセスするとこうなる 何が足りないかというと、バケットポリシー の設定です。 S3のバケットポリシーはデフォルトでは全拒否となっています。 バケットポリシー全貌 { "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1621068192000", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": "s3:*", "Resource": "arn:aws:s3:::${ここにバケットの名前を入れる}" } ] } 上記のように記述します。 各項目については、 こちらの公式リファレンスで確認ができます。 Version これはIAMポリシーの文法のバージョンです。 現在(2021/5/15)は 2012-10-17 です。 もう随分長いこと変わっていませんね。 Statement.Sid これはポリシードキュメントに対して任意で与える識別子です。ステートメント毎に割り当てることができ、ポリシー内で重複してはいけません。 Statement.Effect 許可の一覧であるAllow(ホワイトリスト)にするのか、禁止一覧であるDeny(ブラックリスト)にするのかを選べます。 Statement.Principal どの相手(Principal)に対して許可ないし拒否するかを指定します。 指定できる代表的なプリンシパルは下記にのようなものがあります。 AWSアカウント/ルートユーザー IAMユーザー IAMロール AWSサービス "Principal": { "AWS": "arn:aws:iam::AWSアカウントID:user/userネーム" } // 下記だと全てのユーザーに対して許可となる "Principal": { "AWS": "*" } Statement.Action 許可ないし拒否するアクションを指定します。 // s3全てのアクション "Action": "s3:*" // sqsのsedMessageアクション "Action": "sqs:SendMessage" Statement. Resource 一連のステートメントをどのリソースに反映するかを記述します。 // examplebucketという名前のs3に対してポリシーのステートメントが反映される。 "Resource": "arn:aws:s3:::examplebucket" // ワイルドカードも一部使用可能。下記はexamplebucketという名前のS3バケット内の全ての項目に対して反映される。 "Resource": "arn:aws:s3:::examplebucket/*" 応用的な記述 Condition を使用して、ポリシーの実行するタイミングの条件を指定することができます。 これによって、 - IP制限 - ユーザー名による制限 - HTTP Refererの制限 - MFAの要求 など細かいコントロールをすることができます。 { "Version": "2012-10-17", "Statement": [ { "Sid": "IPAllow", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": [ "arn:aws:s3:::DOC-EXAMPLE-BUCKET", "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*" ], "Condition": { "NotIpAddress": {"aws:SourceIp": "54.240.143.0/24"} } } ] } 上記は EffectがDeny(拒否)で NotIpAddressとしてIPアドレスが指定されているので、 54.240.143.0/24のIPアドレス以外は拒否する、という設定になります。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:*", "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET", "Condition": { "StringEquals": { "aws:username": "myname" } } } ] } 上記はAWSユーザー名が mynameの時のみ許可されます。 aws:usernameなどのポリシードキュメントで取得できるキーは、下記の公式ドキュメントに一覧があります。 タグやリージョン、日付など、色々なキーが取得できるようです。 AWS グローバル条件コンテキストキー ポリシージェネレーター 公式側でポリシーのジェネレーターなるものも用意されており非常に便利です。 土台をジェネレーターでサクッと作成し、細かい部分は修正していくといった利用をしていこうと思います。 参考資料 AWS公式リファレンス S3バケットポリシーの具体例で学ぶAWSのPolicyドキュメント IAMについて今更学ぶ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MacでEC2インスタンスにSSH接続する方法

1.Ec2インスタンス起動 2.sshフォルダ作成 3.下記コマンドで権限を600に変更 chmod 600 ○○.pemのパス名 4.ssh接続 ssh -i ○○.pemのパス名 ec2-user@パブリックIPアドレス 下記のような表示が出ればログイン成功です __| __|_ ) _| ( / Amazon Linux AMI ___|\___|___| https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/ 15 package(s) needed for security, out of 22 available Run "sudo yum update" to apply all updates. [ec2-user@ip-10-0-0-54 ~]$ 参考 AWSのEC2にmacからSSHする方法 https://qiita.com/nakm/items/695e41d8e71d0d281ac4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Action Textで画像が投稿されない問題を解決した話。

本番環境でActionTextで投稿された画像をAWSのEC2煮上げた画像をS3のバケットの中に保存しておくようにしたのですが、そこで本番環境でS3の方に保存されないというもんだいがおきました。 慌てて検証ツールを見てみるとそこでCROSの処理の問題であるというエラーメッセージが出てきました。 私その時CROSについて全く知らなかったので改めてググって調べてみました。 CROSとはなにか ブラウザがオリジン(HTMLを読み込んだサーバのこと)以外のサーバからデータを取得する仕組みです。 今回で言うところの、EC2でHTMLLを読み込まれていますが、画像に関してはS3にあるのでS3からHTMLとして画像を持ってくるといったことが必要になってきます。 その時に役に立つのがCROS処理です。 出典 今回の失敗 S3のCROS処理のポリシーを書いておくべき所が空欄だったので、保存ができませんでした。 #解決策 CROS処理を許可できるようにポリシーを書いていった所保存することができました。 私は以下のようにして書きました。 [ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ] そうした所、無事保存ができました。 いやー良かったよかった。 出典 お役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Active Storageで投稿した画像をS3に入れようとしたら権限関係で詰まった話。

AWSのS3に投稿された画像を保存できるようにしよとした時にかなり苦戦したのでそのお話をさせていただければと思います。 S3の権限については学習したはずなのにそのことについてすっかり忘れてしまっていたのでそのお話をさせていただければと思います。 まず、どうやってアクセス権限を得ることができるのかというと、 1つ目として、 EDITOR="vi" bin/rails credentials:edit コマンドを使用してシークレットアクセスキーとアクセスキーを入れて環境変数を有効にするコマンドである source ~/.zshrc コマンドを打って環境変数を有効化します。 2つめは、S3のコンソールに行って バケットからアクセス許可の項目に行っていただいて、そこにあるバケットポリシーにJSON形式でコードを書いて許可するやり方です。 私は以下のようにしてアクセスを許可しました。 それは、 { "Version": "2012-10-17", "Id": "Policy1544152951996", "Statement": [ { "Sid": "Stmt1544152948221", "Effect": "Allow", "Principal": { "AWS": "IAMユーザーのARN" }, "Action": [ "s3:*", "s3:PutObjectAcl", "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "バケットのARN/*" } ] といった感じです。 ひとつひとつ見ていくと、Actionの項目に、PutとGetとDeleteの項目を許可するようにアクセス制限が組まれています。 これをしてあげることで、S3に対して投稿やその投稿されている画像等のファイルをWeb上に表示できる仕組みができました。 私はこれでつまずいていました。 なので、このようなことがないように備忘録として保存しておきます。 ありがとうございました。今回AWSがより一層好きになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gateway Load Balancer とは

勉強前イメージ ALB作る時に新しいの見つけた。 なんのやつなんだろう 調査 gateway load balancer とは AWS Gateway Load Balancer 、略して GWLB は 元々サードパーティのセキュリティ製品などをAWSで利用する場合はNLBやVPC Peering、NATして連携したりしていました。 GWLBとGateway Load Balancer Endpoint(GWLBE) を利用することで スケーラブルで可用性の向上を図ります。 下記はロードバランサー作成時の画面です。 新しくGateway Load Balancerが追加されているのが確認できると思います。 構築のイメージ 左側がアプリケーションサーバが設置されてるVPC、右側がセキュリティ製品が設置されているVPCになります。 2つのVPCをGWLBとGWLBE(Gateway Load Balancer Endpoint)でつなぎます。 PrivateLinkなので、CIDRが重複しても問題ないです。 特徴 LBなのでスケールを行うことができ、可用性を実現 異なるVPCやAWSアカウント間で共有が可能 勉強後イメージ 新しいの出来てるなーって思ってた。 サードパーティのセキュリティ製品入れるときとかによいのね。 参考 AWS Gateway Load Balancerが東京リージョンに対応しました [新サービス]セキュリティ製品等の新しい展開方法が可能なAWS Gateway Load Balancerが発表されたので調査してみた [AWS Black Belt Online Seminar]AWS Gateway Load Balancer
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

改善:OpenAPIをS3ホスティングにベーシック認証追加

やりたいこと 前回の記事で未解決だった課題を解決したい Before s3を直接参照している、閲覧制御が出来ていない After CloudFront + Lambda@Edge 経由に変更し、ベーシック認証を追加 CloudFrontの設定 AWS S3で公開している静的サイトにBasic認証をかけるというサイトを参考に設定していきます ベーシック認証(Cloudfront Function) 続いてベーシック認証するためのLambdaを作っていきます。CloudFront Functionを選択します Lambda@Edgeの設定は省略します 動作確認 最後に アクセス制御ができたので、見せたい対象の方を簡単ですが制御できるようになりました。 これで作ったOpenAPIのコードは隠しつつ、ReDocだけを表示制御が実現しました。 SSL認証やドメイン設定は行なえませんでしたが、またいつか別の機会に試そうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】インスタンスの購入方式

プログラミング勉強日記 2021年5月16日 インスタンスの購入方式  インスタンスの購入方式に応じて割引価格が提供されるので、用途に応じて割引価格を利用した方がいい。 オンデマンドインスタンス  通常のインスタンス購入方式で、何も設定しなければオンデマンドインスタンスが起動される。長期契約はなく、コンピューティング性能に対して秒単位や時間単位で料金を支払う。起動・停止・休止・開始・再起動・終了などを自由に制御できる。 リザーブドインスタンス  1年または3年の期間を長期利用する予約する。キャパシティーを予約したうえで事前支払いをするか、後で支払うかを設定することができる。また、特定のAZまたはリージョンで使用するキャパシティーを予約できる2パターンある。割引率はインスタンスの種類や支払い形態に応じて違うが、通常のオンデマンド料金に比べると大幅に割引される。 リザーブドインスタンスの特徴  リザーブドインスタンスにはスタンダードとコンバーティブルという2つのタイプがある。 スタンダード コンバーティブル 利用期間と割引価格 1年(40%割引), 3年(60%割) 1年(31%割引), 3年(54%割引) AZ/インスタンスサイズ/ネットワーク変更可否 〇 〇 インスタンスファミリー/OS/テナンシー/支払いオプションの変更可否 × 〇 リザーブドインスタンスマーケットプレイスでの販売可否(途中でいらなくなったときに販売できるか) 〇 今後可能となる予定  コンバーティブル(convertible)はその名の通り、変更できる要素が多い。スタンダードの方が変更できる属性が少ないが、割引価格がより高くなっている。 スケジュールドリザーブドインスタンス  1年にわたって毎日、毎週、毎月ベースの指定された開始時間・期間で繰り返しキャパシティーを予約できる。(毎日1日2時間だけ使うといった指定した時間だけ) 継続的に実行されないが、定期的なスケジュールで実行されるバッチ処理などに向いている。 スポットインスタンス  オンデマンドの料金よりも安く利用できるインスタンス。AWS側の管理用に保持されている未使用のEC2インスタンスを一時的にユーザに貸している(万が一のために未使用のEC2インスタンスを複数持っている)。AWS用のインスタンスなので、途中で中断される可能性のある長期利用できないインスタンス。そのため、最大で90%割引で利用できる最も安いインスタンス。  実行時間に柔軟性がある場合や中断できる処理に利用する。 物理対応可能なインスタンス  物理サーバにインスタンスを起動して制御可能なタイプのインスタンス。これも値段は変わるが、値段よりも用途によって選ぶことが重要。 ハードウェア専有インスタンス(Dedicated Instances)  専用HWのVPCで実行されるEC2インスタンス。VPC構成をするときにDedicatedを選ぶとハードウェア専有インスタンスを選択できる。ホストHWで他のAWSアカウントに属するインスタンスから物理的に分離する。同じAWSアカウントのインスタンスとはHWを共有する可能性があるので注意が必要。 Dedicated Host  Dedicated Instancesと似ているが、同じAWSアカウントであっても別のIAMグループと物理的なサーバーを共有することがない。EC2インスタンス容量をユーザ専用として利用できる物理的なサーバー。サーバーに割り当てられてる既存のソフトウェアライセンス利用できる。 Bare Metal  特殊な仮想サーバーの方式で、アプリの基盤になるサーバーのメモリやプロセッサに直接アクセス可能なインスタンス。通常仮想サーバーは物理的なインスタンスのメモリやプロセッサに直接アクセスして設定はできないが、Bare Metalは物理的なサーバーをオンデマンド形式でアクセスできる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Organizationを使った環境別アカウントの作成とスイッチロールの導入

AWS Organizationを使ったアカウントの整備とスイッチロールの導入 したいこと 一つのAWSアカウントに本番環境や検証環境のサービスを同居させたくない 本番環境、ステージング環境、検証環境等を完全に分離させたい 検証環境は開発エンジニアがEC2等インスタンスを作成、削除を許容したいが本番環境はインフラエンジニアだけ許可などの制御がしたい 環境ごとにIAM発行する手間をなくしたい スイッチロールを使用しIAM1つだけで済むようにしたい AWS Organizationを使い環境ごとのグループ・アカウントを作成 グループ・アカウントを環境ごとに作成 それぞれのアカウントは独立するので検証アカウント内は本番アカウント内のサービスは表示されないので間違ったサーバー変更は起き得ない アカウントイメージ アカウント 内容 支払いアカウント 頂点に存在、本番、開発、検証の費用をまとめて払う為だけのアカウント 本番アカウント 本番環境動作アカウント、インフラエンジニアだけ ステージングアカウント ステージング環境動作アカウント、インフラエンジニアだけ 検証アカウント 検証環境動作アカウント、開発エンジニア、インフラエンジニア両方 スクリーンショット アカウントを細分化できました ※ メアドだけ黒塗り スイッチロール準備 今回は検証アカウント(toyscreation-sandbox)にEC2参照ロールを作成し ログインアカウントで作成したIAMがスイッチロールでEC2参照ロールを使う 検証環境のロール作成 検証アカウント(toyscreation-sandbox)にログインしロール作成 別のAWSアカウント アカウントIDはログインアカウントを選択 オプションは選択に応じて 権限ポリシーはサンプルとしてEC2のみ タグ追加は今回はスキップ ロールの作成、ロール名、ロール説明を入力 ログインアカウントのスイッチロール操作 IAMポリシー作成 ロール切替のポリシーが sts:AssumeRole になるのでポリシーを追加してユーザーに付与が必要 "TargetAccountID" にはスイッチ先のアカウントIDを入力 "TargetAccountRoleName" はスイッチ先のロールとして作成したロール名を指定 { "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::<TargetAccountId>:role/<TargetAccountRoleName>" } ] } タグの追加はスキップ ポリシーの確認、名前、説明を入力 IAMユーザー追加 rootユーザーはスイッチロールの利用は出来ないのでIAMでユーザーを追加 上記で作成したポリシーをIAMユーザに付与 スイッチロール設定 対象IAMでログイン アカウントメニューにロールの切り替えが表示 各項目を入力 1. アカウント:スイッチ先のアカウントIDを指定 2. ロール:スイッチ先のロール名を指定 3. 表示名:表示名を任意の文字列で指定 切り替え後ユーザーメニューに対象ロールが表示 確認 EC2の表示は許可されているが、それ以外は許可されていない(スクショはS3) まとめ 本番環境、ステージング環境、検証環境等を完全に分離させた アカウント別に環境を完全に分離出来た(ただし支払いは一本化) 各環境で必要なAWSサービスが動いているかわかりやすくなった スイッチロールを使い環境の切り替えが楽になった エンジニア別 x 環境別に権限をスイッチロールで切り替えれるようになった いいね!と思ったら LGTM お願いします
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

goofysでAmazon S3をLinuxサーバにマウントする

はじめに goofysを使用してLinuxにAmazon S3をマウントしてみました。 s3fsより動作が速いという特徴があるようです。 https://github.com/kahing/goofys#benchmark 環境 ・新規構築したAmazon Linux 2を使用 ・EC2のIAMロールにS3FullAccessを付与している 設定手順 1. goofysのインストール 最初にgoとfuseをインストールします。 $ sudo yum install -y golang fuse 続いて、goofysをインストールします。 記事投稿時点での最新バージョン(v0.24.0)を使用します。 $ sudo wget https://github.com/kahing/goofys/releases/download/v0.24.0/goofys -P /usr/local/bin/ $ sudo chmod 755 /usr/local/bin/goofys 2. マウント マウントポイントのディレクトリを作成します。 併せて、マウントポイントのユーザーとグループをec2-userとするため、uidとgidを確認しておきます。 $ sudo mkdir /mnt/goofys $ id ec2-user uid=1000(ec2-user) gid=1000(ec2-user) groups=1000(ec2-user),4(adm),10(wheel),190(systemd-journal) S3バケットをマウントします。 他のユーザーからもマウントしたボリュームにアクセスできるよう、allow_otherをオプションに追加します。 $ sudo /usr/local/bin/goofys [バケット名] /mnt/goofys -o allow_other,--uid=1000,--gid=1000 実行結果を確認します。 S3バケット内のオブジェクトが取得できています。 $ df -h mybucket 1.0P 0 1.0P 0% /mnt/goofys $ ll /mnt/goofys total 25 drwxr-x--- 1 ec2-user ec2-user 0 Jul 29 2020 object1 drwxr-x--- 1 ec2-user ec2-user 0 Jan 1 1970 object2 drwxr-x--- 1 ec2-user ec2-user 0 Jun 15 2020 object3 drwxr-x--- 1 ec2-user ec2-user 0 Mar 26 04:40 object4 drwxr-x--- 1 ec2-user ec2-user 0 Jun 5 2020 object5 drwxr-x--- 1 ec2-user ec2-user 0 Jan 1 1970 object6 3. S3へファイルをアップロード AWSマネジメントコンソールからtest.txtをアップロードして、サーバ上で結果を確認します。 $ ll /mnt/goofys total 25 drwxr-x--- 1 ec2-user ec2-user 0 Jul 29 2020 object1 drwxr-x--- 1 ec2-user ec2-user 0 Jan 1 1970 object2 drwxr-x--- 1 ec2-user ec2-user 0 Jun 15 2020 object3 drwxr-x--- 1 ec2-user ec2-user 0 Mar 26 04:40 object4 drwxr-x--- 1 ec2-user ec2-user 0 Jun 5 2020 object5 drwxr-x--- 1 ec2-user ec2-user 0 Jan 1 1970 object6 -rw-r--r-- 1 ec2-user ec2-user 4 May 15 23:24 test.txt 4. サーバ上でファイルを作成してAWSマネジメントコンソールから確認 test2.txtというファイルをマウントポイントに作成し、マネジメントコンソールから結果を確認します。 $ sudo touch /mnt/goofys/test2.txt $ ll /mnt/goofys total 25 drwxr-x--- 1 ec2-user ec2-user 0 Jul 29 2020 object1 drwxr-x--- 1 ec2-user ec2-user 0 Jan 1 1970 object2 drwxr-x--- 1 ec2-user ec2-user 0 Jun 15 2020 object3 drwxr-x--- 1 ec2-user ec2-user 0 Mar 26 04:40 object4 drwxr-x--- 1 ec2-user ec2-user 0 Jun 5 2020 object5 drwxr-x--- 1 ec2-user ec2-user 0 Jan 1 1970 object6 -rw-r--r-- 1 ec2-user ec2-user 0 May 15 23:25 test2.txt -rw-r--r-- 1 ec2-user ec2-user 4 May 15 23:24 test.txt マネジメントコンソールからもtest2.txtが追加されていることを確認できました。 5. 起動時の自動マウント設定 最後に、以下のコマンドでサーバが起動したときに自動的にマウントされるように設定します。 --dir-modeと--file-modeでマウントポイントの権限を設定できます。 $ echo "/usr/local/bin/goofys#mybucket /mnt/goofys fuse _netdev,allow_other,--dir-mode=0777,--file-mode=0666,--uid=1000,--gid=1000 0 0" | sudo tee -a /etc/fstab
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSを理解する[通信の仕組み編]

概要 はじめに、この記事はAWS初学者が備忘録として作成しています。 ですが、私のような初学者のために少しでもなればと思い、公開しています。 不適切や、わかりにくい点を見つけた場合はご指摘頂ければ幸いです。 ネットワークを構築する webサーバーソフトをインストールする webサーバーにWordPressをインストールする 通信の仕組みを理解する?今ここ S3を使って公開する TCP/IPとは IPアドレス IPアドレスは、ネットワーク上の通信先を一意に特定するもの IPアドレスにはパブリックIPアドレスとプライベートIPアドレスがある。インターネットを通じて通信するにはパブリックIPアドレスが必要。インターネットとは直接通信しないプライベートなネットワークの中で、プライベートIPアドレスを自由に利用できる IPアドレスを使った通信は、ルーターがパケットをバケツリレーのように転送することで実現している。パケットを次にどこに渡せば良いかというルーティング情報は、EGPやIGP使って交換する ポート番号 ポート番号を使うことで、一つのIPアドレスで同時に複数のアプリケーションが通信できる。 WebやDNS、メール送受信向けのSMTPなど、よく使われるサービスは利用するポート番号が決まっており、それをウェルノウンポートと呼ぶ Webサーバーは、ウェルノウンポートとしてポート80番を用いる DNS IPアドレスは数字の羅列で覚えにくいため、一般にブラウザからのアクセスは、ドメイン名を使う。 ドメイン名を使って通信する際には、ドメイン名がどのパブリックIPアドレスなのかを知る必要がある。ドメイン名とIPアドレスとの相互変換をすることを名前解決という 名前解決にはDNSサーバーを用いる HTTP WebサーバーとWebブラウザは、HTTPを用いてデータをやりとりする TCP/IPモデル これらの一連の処理は、TCP/IPモデルという通信モデルで構成されている。 役割ごとに4つの階層に分けたモデルです。各層には、その層の役割を果たす各プロトコルがありあます。 層 役割 代表的なプロトコル アプリケーション層 ソフトウェア同士が会話する HTTP, SSH, DNS, SMTP トランスポート層 データのやりとりの順番を制御したい、エラー改正したりするなど、信頼性を高めたデータの転送を制御する TCP, UDP インターネット層 IPアドレスの割り当て、ルーティングをする IP, ICMP, ARP インタフェース層 ネットワーク上で接続されている機器同士で通信する Enthernet, PPP 階層化がされている理由は、各階層を独立させるためです。 上位の階層破壊の階層がなんでも良く、また会の階層は上位の階層の内容は分からなくても良い、という構造になっています。 データのカプセル化 名前解決リクエストの作成(アプリケーション層) webブラウザは、アドレス欄に入力されたURL(http://www.exmaple.co.jp )からドメイン名(www.example.co.jp )を抜き出し、名前解決しようとします。 DNSサーバーに対して名前解決の処理を依頼するため、DNSプロトコルで定められたフォーマットに従い、名前解決のリクエストデータを作成します。 UDPカプセル化(トランスポート層) 上記のデータを送信するには、そのデータをトランスポート層に渡す必要があります。この時、上記のデータがトランスポート層のプロトコルでカプセル化されます。 カプセル化とは、郵便で言えば封筒に入れるイメージです。郵便の場合、手紙の内容はなんであれ、封筒に入れて宛先を書けば郵便局に処理を頼めます。郵便局は、その封筒の内容を知る必要がありません。 カプセル化もこれと同様に、中身に手を加えずに、さらに下の階層で処理ファできるようにデータを付与します。 トランスポート層のプロトコルは、TCPかUDPのいずれかです。DNSでは、一般にUDPを使う取り決めになっているため、UDPに引き渡します。 この時、送信元のポート番号、宛先番号、データのs長さ、エラーがないかどうかを調べるチェックサムなどの情報が追加されます。 dnsの場合、ウェルノウンポートは53番なので、宛先ポート番号は53が設定されます。 これらをUDPヘッダーと呼びます。 IPでカプセル化(インターネット層) 次にUDPでカプセル化されたデータをインターネット層に渡します。 インターネット層のプロトコルはIPです。 IPでのカプセル化では、送信元IPアドレス、宛先IPアドレス、データ長、生存時間などの情報が追加されます。これらをIPヘッダーと呼びます。 Ethernetでカプセル化(インタフェース層) IPでカプセル化する段階でIPアドレスが付くので、実際にLANや電話回線などの配線上に流すことができます。この時、それぞれの配線の規格に準拠したヘッダーがいくつか付けられます。 たとえばLANは、Ethernetという規格を使っています。 Ethernetでは、先頭に末尾に幾つかのデータが追加されます。これをEthernetフレームと言います。 これには送信元MACアドレスや、宛先MACアドレスなどの情報が含まれます。 EthernetとTCP/IPの関係 Ethernetで通信するパソコンやサーバー、各種ネットワーク機器などのホストには、雄一無二のMACアドレスと呼ばれる番号がふられています。パソコンやネットワーク機器には、05:0c:ce:d8:1b:a9のような文字列シールが貼られていることがありますが、これがMACアドレスです。 ARPでIPアドレスを解決する Ethernetでは、MACアドレスを頼りにデータを送信します。つまり05:0c:ce:d8:1b:a9宛に送信するというように宛先を特定します。 Ethernetのデータ通信では、Ethernetフレームだけを見て、中身のIPまでは見ません(そもそもEthernetでは、Ethernetフレームさえ付いていればどのようなデータも送受信できるので、中身がIPとは限りません) そうすると、どのMACアドレスを持つホストが、どのIPアドレスに対応しているのかという対応表が必要となります。これをカイケルするのがAPRというプロトコルです。 たとえば、10.0.1.10というIPアドレスにデータを送信したいとします。最初に送信するときは、このIPアドレスを持つホストがどのMACアドレスを持っているのかわかりません。 そこで、10.0.1.10というIPアドレスを持っているホストは誰ですか?と問い合わせます。(ARP要求) すると、そのIPアドレスを持っているホストは、僕です。macアドレスは05:0c:ce:d8:1b:a9ですという応答を返します。 これによって10.0.1.10と05:0c:ce:d8:1b:a9とが結びつき、対応表を作れます。 以降の通信では、この対応表を使って、もし、10.0.1.10が宛先なら、05:0c:ce:d8:1b:a9に送信するようにします。 UDPとTCP UDPでのデータ通信 UDPはステートレスプロトコルと呼ばれる、状態を保たず、送りっぱなしのプロトコルです。 UDPの構造は単純で送信元ポート番号、宛先ポート番号の他には、データの長さとチェックサム(データの総和を検算することで、データに誤りがないかを調べる仕組み)しかありません。 tips ARPはルーターを超えることができず、ARP要求で調べられMACアドレスとIPアドレスの対応は、自分が所属しているサブネット内に留まる。ルーターを超える場所にデータを転送するときは、そのルーターでまずはEthernetで届けます。 インターネットに向けて通信する場合、最寄りのルーターとはデフォルトゲートウェイ(IPアドレスが自動設定になっているときは、デフォルトゲートウェイも自動設定されます。)で指定されたものです。 クライアントが作ったEthernetフレームは、そのままDNSサーバーへ届くわけではなく、届くのは最寄りのルーターまでで、そこから先は、ルーターがEthernetヘッダーを付け替えて、DNSサーバーに向けて送信します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring-Bootアプリをパソコンを切ってもEC2上で起動させておく手順(デーモン化)

configurationタグの箇所3行を記入したjarファイルを作ります。これにより後述のsystemd enableとstartが実行可能になります。 pom.xml <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> </configuration> </plugin> </plugins> </build> ここからの手順はHogeをご自身のアプリ名に読み替えて下さい。 sudo vi /etc/systemd/system/Hoge-0.0.1-SNAPSHOT.service vimでUnitを作成します。vimの編集の際には一度aを押してINSERTと表示されてる状態にし下記スクショの内容を記入、作成完了後はEscキーを押下し:wqと入力して保存・終了します。 [Unit] Description = 任意のアプリ説明 After=syslog.target [Service] ExecStart = /home/ec2-user/Hoge-0.0.1-SNAPSHOT.jar Restart = no Type = simple User = ec2-user Group = ec2-user SuccessExitStatus = 143 [Install] WantedBy = multi-user.target sudo systemctl daemon-reload sudo chmod 744 Hoge-0.0.1-SNAPSHOT.jar sudo systemctl enable Hoge-0.0.1-SNAPSHOT.service sudo systemctl start Hoge-0.0.1-SNAPSHOT.service
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【コピペ】AWS Cloud9でLaravel環境を構築その弐

前回、Cloud9でLaravel環境を構築した。 【コピペ】AWS Cloud9でLaravel環境を構築その壱 だけど、MySQLに外部接続できなかったので、設定する。 マシンスペック Mac mini 2018 macOS Catalina(10.15.x) Intel Core-i7 3.2GHz 6コア メモリ 32GB SSD 512GB Laravel環境 Nginx 最新版 PHP(PHP-FPM) 7.2.x MySQL 5.7 Composer 最新版 Laravel 5.6 やること Cloud9のEC2インスタンスのMySQLに外部接続する 前提 【コピペ】AWS Cloud9でLaravel環境を構築その壱は構築済み。 EC2インスタンスのMySQLに外部接続する Cloud9環境を作成すると、セットでEC2インスタンスも作成される。 EC2の設定 EC2を開く。 インスタンスIDを選択する。 セキュリティグループを開く。 インバウンドルールを編集する。 ルールを追加して保存する。 ※参考 ec2のmySqlに外部サーバから接続する方法 AWS EC2のデータベースに外部からODBC経由で接続するための設定 rootで外部接続を許可 $ mysql -u root -p Enter password: cloud9#CLOUD9 mysql> select user, host from mysql.user; +---------------+-----------+ | user | host | +---------------+-----------+ | fuga | % | | fuga | localhost | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +---------------+-----------+ 5 rows in set (0.00 sec) 当然ながらrootは中からしかアクセスできないが、外部から接続可能にする。 mysql> GRANT ALL PRIVILEGES ON *.* TO root@'%' IDENTIFIED BY 'cloud9#CLOUD9'; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select user, host from mysql.user; +---------------+-----------+ | user | host | +---------------+-----------+ | fuga | % | | root | % | | fuga | localhost | | mysql.session | localhost | | mysql.sys | localhost | | root | localhost | +---------------+-----------+ 6 rows in set (0.00 sec) mysql> quit ※参考 外部のホストから接続できるようにする方法 DBクライアントから接続する パブリック IPv4 DNSをコピーする。 下記の接続情報で、お好きなDBクライアントで接続する。 ホスト ユーザー パスワード パブリック IPv4 DNS root cloud9#CLOUD9 ※参考 Sequel AceでMySQLに接続する(Sequel Proの後継) DBクライアントツールはDBeaverをおすすめしたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GithubActionsでOpenAPIをS3へアップロードしホスティング

やりたいこと GithubのmainブランチへOpenAPIのyamlをpushするとGithubActions起動 redoc-cliが動作。yaml→htmlを生成 s3バケットへhtmlをコピー s3を参照しOpenAPIがブラウザ上から見れるように 構築手順 s3バケット作成とサイト確認 CloudShell # バケット作成 $ aws s3 mb s3://<<バケット名>> make_bucket: <<バケット名>> # for-test-pageバケットのアクセスブロックを設定し、公開できる状態に $ aws s3api put-public-access-block --bucket <<バケット名>> --public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false" # 設定の反映を確認 $ aws s3api get-public-access-block --bucket <<バケット名>> { "PublicAccessBlockConfiguration": { "BlockPublicAcls": false, "IgnorePublicAcls": false, "BlockPublicPolicy": false, "RestrictPublicBuckets": false } } バケットポリシー用のjson作成 bucket-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::<<バケット名>>/*" } ] } CloudShell # バケットポリシーのアタッチ $ aws s3api put-bucket-policy --bucket <<バケット名>> --policy file://bucket-policy.json 本来Redocになるapp.htmlをホスティング確認のため適当な中身を作りs3へ手動アップロードして確認します app.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>test page</title> </head> <body> This is test page </body> </html> CloudShell # s3 アップロード $ aws s3 cp app.html s3://<<バケット名>>/app.html upload: ./app.html to s3://<<バケット名>>/app.html アップロードされたファイルが確認できました GithubActions動作用IAMユーザー作成 CloudShell # user作成 $ aws iam create-user --user-name <<ユーザー>> # キー発行 $ aws iam create-access-key --user-name <<ユーザー>> user-policy.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] } CloudShell # ポリシーの作成 $ aws iam create-policy --policy-name openapi-test-policy --policy-document file://user-policy.json # ポリシーのアタッチ $ aws iam attach-user-policy --user-name <<ユーザー>> --policy-arn arn:aws:iam::****:policy/openapi-test-policy Githubのリポジトリ作成、プロパティへIAM情報をシークレットへ登録 GithubActions作成 .github/workflows deploy_openapi.yaml name: OpenAPI Build and Push HTML to S3 on: push: branches: - master jobs: build: runs-on: ubuntu-latest strategy: matrix: node: ["12.x"] steps: - name: Checkout # 最新コードの取得 uses: actions/checkout@v2 - name: node setting # Redocでnpmを使うのでNodeの設定 uses: actions/setup-node@v1 with: node-version: ${{ matrix.node }} - name: install redoc # Redocのインストール run: npm install -g redoc-cli - name: build openAPI file # OpenAPIの定義をHTMLに変換 run: redoc-cli bundle ./openapi.yaml --output app.html # プロジェクトのディレクトリに合わせて変更してください。 - name: Output file contents # 問題があった場合に確認するために中身を出力 run: cat ./app.html - name: Configure AWS credentials # S3にファイルを配置するために権限の設定 uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_DEV_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_DEV_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: S3 copy # 生成したHTMLファイルをS3に配置する env: S3_BUCKET_NAME: ${{ secrets.AWS_DEV_BUCKET_NAME }} run: aws s3 cp ./app.html s3://$S3_BUCKET_NAME/app.html 動作確認 適当なOpenAPIファイルを作成しmasterへgit push GithubActionが動き出す s3にファイルがアップロードされ、ReDocがみえることを確認 最後に OpenAPIは見えるようになりました。 2つ課題は残っていて、いずれ解決したいなと思います。 - s3の前にCloudFrontを立てて署名付きURLに - ベーシック認証をつけてアクセスを制御 - バケットポリシーにIP制限つけるのもあり ベーシック認証を追加し改善を行ないました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2って何ぞや??

EC2ってよくわからないので、勉強したことを書いてみた。 EC2とは EC2とはAmazon Elastic Computing CloudというAWSのサービスの1種。 ちなみにElasticとは、「伸縮自在な」という意味で、需要に応じて必要な分だけ使えるということ。 EC2の特徴 必要な時に必要な数だけ稼働できる EC2は、インスタンスという仮想サーバーのようなものを起動させて使用することができる。 オンプレミス環境では事前に需要を予測して必要な分のサーバーをずっと稼働させることになるが、需要が少ない時は使わないサーバーを稼働させ続けることになり、無駄なコストがかかってしまう。 しかし、EC2インスタンスは、稼働数が需要に応じて柔軟に変化するため、サーバーの過不足が少なくなる。 インスタンスタイプが変更可能 EC2インスタンスには、インスタンスタイプというものがあり、メモリの最適化、高速コンピューティングといった利用目的や、必要なスペックに合わせて設定する。 インスタンスタイプは,'t2.micro'のように(ファミリー)(世代).(性能)の形式で表記する。 ファミリーが利用目的、世代がバージョン、性能がスペックを表す。 インスタンスタイプによってコストが異なるため、自分が求めるスペックに合わせたインスタンスタイプを選択するべき。 ここで重要なのは、事前に予算や必要なスペックを計算しておく必要はなく、稼働後もパフォーマンスをモニタリングしながら適切なインスタンスタイプを柔軟に変更できることである。 使用に時間がかからない オンプレミスの場合、サーバーの調達に時間がかかり、すぐに使用できるわけではない。 しかし、EC2インスタンスの場合、サーバーの調達が不要で、数分の手作業で稼働できてしまう。 世界中のリージョンで使用できる 世界中のリージョンで使用できるので、東京にいながらも地球の裏のサンパウロ(ブラジル)でインスタンスをデプロイすることもできる。 AMIから同じインスタンスを起動できる EC2インスタンスは、AMI(Amazon Machine Image, EC2インスタンスのテンプレート)から起動する。 1つのAMIからEC2インスタンスをいくつでも起動でき、同じ種類のインスタンスを複製することができる。 セキュリティグループでトラフィック制御ができる EC2インスタンスへのアクセスは、セキュリティグループで許可しているものだけできる。 EC2の料金体系 EC2の料金は次の3種類に対して課金される。 EC2の起動 データ転送 ストレージ 上で述べたとおり、オンプレミス環境の場合、稼働させているサーバーの分だけ常に課金される。 それに対してEC2インスタンスは、必要に合わせて必要な分だけ稼働し、稼働した分だけ課金される(従量課金)。 EC2の料金プランは、主に以下の4種類が挙げられる。 オンデマンドインスタンス 稼働したインスタンスの数と時間の分だけ課金されるオプション。 使わない分のインスタンスを停止させると、停止させている間の料金は課金されない。 リザーブドインスタンス インスタンスを使用する量があらかじめわかっている場合、1年や3年という期間で契約することで料金の割引が受けられるオプション。 スポットインスタンス 他のユーザーがインスタンスを使っていない期間は料金が安くなり、使っている期間は料金が高くなるといった具合に、料金が市場価格のように変動するオプション。 インスタンスを使用するユーザーが増えてきてキャパシティが減ってくると、AWSによって強制的にインスタンスの使用を止められてしまう。 そのため、途中で中断してはいけないインスタンスには向かないオプションである。 Dedicated Hosts EC2を使用するホストを占有するオプション。 インスタンスに対してではなく、ホストに対して課金される。 参考文献:AWS認定資格試験テキスト・AWS認定クラウドプラクティショナー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む