- 投稿日:2021-02-28T23:55:39+09:00
AWS日記24 (Cognito - OIDC)
はじめに
今回は AWS Cognito の OpenID Connect (OIDC) を試します。
OIDCを利用しGitHubとアカウント連携します。準備
[AWS Cognitoの資料]
AWS Cognito
ユーザープールへの OIDC ID プロバイダーの追加AWS SAM テンプレート作成
AWS SAM テンプレートで API-Gateway , Lambda, AWS Cognito の設定をします。
[参考資料]
AWS SAM テンプレートを作成するAWS Cognito設定用のSAMテンプレート
AWS Cognito の設定は以下の部分
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: Serverless Account Page Parameters: ApplicationName: Type: String Default: 'ServerlessAccountPage' UserPoolClientName: Type: String Default: 'GitHubOpenIdAuthClient' UserPoolDefaultDomainPrefixName: Type: String Default: 'test-user-pool-domain2021' UserPoolClientCallbackURL: Type: String Default: '' Resources: UserPool: Type: AWS::Cognito::UserPool Properties: AdminCreateUserConfig: AllowAdminCreateUserOnly: false UnusedAccountValidityDays: 7 AutoVerifiedAttributes: - 'email' MfaConfiguration: 'OFF' UserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: UserPoolId: !Ref UserPool ClientName: !Ref UserPoolClientName AllowedOAuthFlows: - 'implicit' AllowedOAuthFlowsUserPoolClient: true AllowedOAuthScopes: - 'openid' ExplicitAuthFlows: - 'ALLOW_REFRESH_TOKEN_AUTH' GenerateSecret: false CallbackURLs: - !Ref UserPoolClientCallbackURL UserPoolDomain: Type: AWS::Cognito::UserPoolDomain Properties: UserPoolId: !Ref UserPool Domain: !Ref UserPoolDefaultDomainPrefixName Outputs: APIURI: Description: "Amazon Cognito Domain" Value: !Join [ '', [ 'https://', !Ref UserPoolDomain, '.auth.', !Ref 'AWS::Region', '.amazoncognito.com'] ]GitHub OAuth App ラッパー用のSAMテンプレート
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: Serverless Cognito GitHub OpenId Page Globals: Function: Runtime: go1.x Timeout: 15 Environment: Variables: REGION: !Ref 'AWS::Region' ALGORITHM: !Ref Algorithm KEY_ID: !Ref KeyId CERT_PATH: !Ref CertPath PUB_KEY_PATH: !Ref PubKeyPath GITHUB_CLIENT_ID: !Ref GitHubClientId GITHUB_CLIENT_SECRET: !Ref GitHubClientSecret COGNITO_REDIRECT_URI: !Ref CognitoRedirectUri GITHUB_API_URL: !Ref GitHubUrl GITHUB_LOGIN_URL: !Ref GitHubLoginUrl Parameters: GitHubClientId: Type: String GitHubClientSecret: Type: String CognitoRedirectUri: Type: String GitHubUrl: Type: String Default: "https://api.github.com" MinLength: 1 GitHubLoginUrl: Type: String Default: "https://github.com/login" MinLength: 1 StageName: Type: String Default: "CognitoGitHubApiStage" StageName: Type: String Default: "CognitoGitHubApiStage" CertPath: Type: String Default: "jwtRS256.key" PubKeyPath: Type: String Default: "jwtRS256.key.pub" Algorithm: Type: String Default: "RS256" KeyId: Type: String Default: "jwtRS256" Resources: GithubOAuthApi: Type: AWS::Serverless::Api Properties: StageName: !Ref StageName OpenApiVersion: "2.0" OpenIdDiscovery: Type: AWS::Serverless::Function Properties: CodeUri: api/discovery/bin/ Handler: main MemorySize: 256 Events: GetResource: Type: Api Properties: Path: /.well-known/openid-configuration Method: get RestApiId: !Ref GithubOAuthApi Authorize: Type: AWS::Serverless::Function Properties: CodeUri: api/authorize/bin/ Handler: main MemorySize: 256 Events: GetResource: Type: Api Properties: Path: /authorize Method: get RestApiId: !Ref GithubOAuthApi Token: Type: AWS::Serverless::Function Properties: CodeUri: api/token/bin/ Handler: main MemorySize: 256 Events: GetResource: Type: Api Properties: Path: /token Method: get RestApiId: !Ref GithubOAuthApi PostResource: Type: Api Properties: Path: /token Method: post RestApiId: !Ref GithubOAuthApi UserInfo: Type: AWS::Serverless::Function Properties: CodeUri: api/userinfo/bin/ Handler: main MemorySize: 256 Events: GetResource: Type: Api Properties: Path: /userinfo Method: get RestApiId: !Ref GithubOAuthApi PostResource: Type: Api Properties: Path: /userinfo Method: post RestApiId: !Ref GithubOAuthApi Jwks: Type: AWS::Serverless::Function Properties: CodeUri: api/jwks/bin/ Handler: main MemorySize: 256 Events: GetResource: Type: Api Properties: Path: /.well-known/jwks.json Method: get RestApiId: !Ref GithubOAuthApi Outputs: CognitoGitHub: Description: "Cognito GitHub Api URL" Value: !Sub "https://${GithubOAuthApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}"フロントエンド用のSAMテンプレート
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: Serverless Cognito OpenId FrontPage Parameters: ApplicationName: Type: String Default: 'ServerlessCognitoOpenIdFrontPage' CognitoUrl: Type: String Default: 'https://your-domain-prefix.auth.your-region.amazoncognito.com' CognitoClientId: Type: String Default: 'abc012' Resources: FrontPageApi: Type: AWS::Serverless::HttpApi FrontPageFunction: Type: AWS::Serverless::Function Properties: FunctionName: ServerlessCognitoOpenIdFrontFunction CodeUri: bin/ Handler: main MemorySize: 256 Runtime: go1.x Description: 'Cognito OpenId Front Function' Environment: Variables: REGION: !Ref 'AWS::Region' COGNITO_URL: !Ref CognitoUrl COGNITO_CLIENT_ID: !Ref CognitoClientId Events: FrontPageApi: Type: HttpApi Properties: Path: '/' Method: get ApiId: !Ref FrontPageApi FrontProxyApi: Type: HttpApi Properties: Path: '/{proxy+}' Method: get ApiId: !Ref FrontPageApi Outputs: APIURI: Description: "URI" Value: !Join [ '', [ 'https://', !Ref FrontPageApi, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/'] ]Lambda関数作成
※ JWT周りの処理は github.com/dgrijalva/jwt-go を利用しました。
res.Algorithm = os.Getenv("ALGORITHM") res.KeyId = os.Getenv("KEY_ID") keyData, err := os.ReadFile(os.Getenv("PUB_KEY_PATH")) if err != nil { return res, err } key, err := jwt.ParseRSAPublicKeyFromPEM(keyData) if err != nil { return res, err } nBytes := (*(*key).N).Bytes() eBytes := []byte(strconv.Itoa((*key).E))終わりに
外部サービス(GitHub)の設定を行う必要があるため、SAMテンプレートを複数に分ける必要がありました。
GitHubだけでなく他の外部サービスにも応用ができるため、今後試そうと思います。
- 投稿日:2021-02-28T23:51:57+09:00
EC2のインスタンスタイプの選び方
はじめに
初めての投稿です。
EC2インスタンスを選択する際、様々なタイプがあって迷うと思います。
その際に参考になればという思いで記事を書きました。インスタンス起動しても直ぐに落ちていた
ただシステムにアクセスするだけで1日に何度もシステムダウンしていた。
原因
例えば簡単なページを表示する分には、無料枠の t2.micro で問題ない。
データベース等重いシステムを動かず場合は、これでは動かない。
CPUが直ぐにいっぱいになってシステムダウンしてしまう・・・
よく確認すると t2.micro = 1Gib って書いてある・・・これじゃ動かないよね。対策
なので、t2.micro => t3.medium に変更しました。
これでメモリは1Gib => 4Gib 単純に4倍!
料金は0.0152USD/時間 => 0.0544USD/時間最初は料金も安い事から t3a.medium を選択しましたが、今回のインスタンスタイプは1cだったので、このAZでは未対応だった為に選択できず t3.mediumを選択しました。
結果
これで無事にシステムダウンすることがなくなりました!
参考にさせて頂いた記事
https://pages.awscloud.com/rs/112-TZM-766/images/C2-07.pdf
- 投稿日:2021-02-28T23:46:04+09:00
一問一答で学ぶAWS責任共有モデル
AWSクラウドプラクティショナーの資格試験の学習を兼ね、
一問一答形式でAWS責任共有モデルについて記載しています。今後、追記予定です。
(内容が薄い&インフラ初心者ではあります。ご容赦の上、ご覧ください。)概要
Q.責任共有モデルって何?
A.AWS側の責任、ユーザ側の責任を定め、全体のセキュリティを担保しようという考え方
Q.元ネタは?
A.英語で、shared responsibility modelの日本語訳。
ただし、ここでのsharedは「共有」という意味合いより「分担」という意味合いの方が正確Q.何故こういった考え方が存在する?
A.責任の所在を明確するため
Q.責任の分界点を一言で言うと?
A.クラウド自体(AWS)か、クラウド内(ユーザー)か
Q.もう少し詳しく
A.AWS側は物理的なインフラや、仮想環境を動かすハイパーバイザーに対し、そしてユーザー側はOSより上のレイヤーに対し責任を負う
Q.ちなみに、ここでいうクラウドって何よ?
A.インターネット経由でさまざまなITリソースをオンデマンドで利用することが出来るサービスの総称
Q.オンデマンドって何?
A.ユーザの要求に応じて、サービスを提供すること
Q.他のクラウド事業者も同じような責任の分担?
A.サービスモデル(IaaS,PaaS,SaaS)によって異なるから、同じとは言い切れない
(国際的なクラウド事業者は、各々責任共有モデルに対応する責任分担について表明している)問題
Q.利用者がインストールしたOSやソフトウェアのトラブルの責任はどちらにある?
A.利用者側
- 投稿日:2021-02-28T22:46:57+09:00
[Ansible]EC2のメタデータを変数に格納する
- 投稿日:2021-02-28T22:37:03+09:00
AWS Amplifyの認証画面を多言語対応してみた
はじめに
Ionic(Angular)環境でAWS Amplifyの認証画面を多言語対応してみました。
Angular環境の場合は、ionタグの部分を無視して読んで下さい。
AWS Amplifyの環境構築は、こちら
=>[Ionic6 + Angular11]AWS AmplifyとIonicでさくっと認証画面を作成する
2021年2月時点
思い
- できるだけ、コストをかけずに認証画面を作成したい。
- 認証画面を自作するコストをかけたくないが、AWS Amplifyの認証画面がカスタマイズできず結局自作というケースも多いのでは?
完成画面
ライブラリのバージョン
- aws-amplify": 3.3.20
- @aws-amplify/ui-angular: 1.0.2
対応の流れ
- aws-amplifyにある I18nモジュールを読み込む。
- 辞書ファイルを用意し、I18nに渡す。
- 表示言語を設定する。
拡張
- 画面上から言語を切り替えられるようにした。
対応(変更点)
home.page.ts- import { Component, ChangeDetectorRef } from '@angular/core'; + import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { onAuthUIStateChange, CognitoUserInterface, AuthState, FormFieldTypes, } from '@aws-amplify/ui-components'; import { I18n } from 'aws-amplify'; import { amplifyVocabularies } from '../shared/const/amplify.vocabularies.const'; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) - export class HomePage { + export class HomePage implements OnInit, OnDestroy { title = 'amplify-angular-auth'; user: CognitoUserInterface | undefined; authState: AuthState; /** サインアップフィールド定義 */ signUpformFields: FormFieldTypes = [ { type: 'username' }, { type: 'password' }, { type: 'email' }, ]; + languageOptions = [ + { + label: '日本語', + value: 'ja', + }, + { + label: 'English', + value: 'en', + }, + ]; + labelSelectLanguage = 'Select Language'; + selectedLang = 'en'; - constructor(private ref: ChangeDetectorRef) {} + constructor(private ref: ChangeDetectorRef) { + const lang = sessionStorage.getItem('lang'); + if (lang === null) { + return; + } + I18n.putVocabularies(amplifyVocabularies); + I18n.setLanguage(lang); + this.selectedLang = lang; + this.labelSelectLanguage = I18n.get('Select Language'); + } ngOnInit() { onAuthUIStateChange((authState, authData) => { this.authState = authState; this.user = authData as CognitoUserInterface; this.ref.detectChanges(); }); } ngOnDestroy() { return onAuthUIStateChange; } + onChange(): void { + sessionStorage.setItem('lang', this.selectedLang); + location.reload(); + } + }home.page.html<ion-content [fullscreen]="true"> <div id="container"> <amplify-authenticator *ngIf="authState !== 'signedin'"> <amplify-sign-in slot="sign-in"> + <div slot="federated-buttons"> + <div class="select-language"> + {{labelSelectLanguage}} + <select [(ngModel)]="selectedLang" (change)="onChange()"> + <option *ngFor="let languageOption of languageOptions" [ngValue]="languageOption.value"> + {{languageOption.label}} + </option> + </select> + </div> + </div> </amplify-sign-in> <amplify-sign-up slot="sign-up" [formFields]="signUpformFields"> </amplify-sign-up> </amplify-authenticator> <div *ngIf="authState === 'signedin' && user" class="App"> <amplify-sign-out></amplify-sign-out> <div>Hello, {{user.username}}</div> </div> </div> </ion-content>言語用constと型定義
かなり長いので折りたたみ
amplify.vocabularies.const.ts// Ref https://github.com/aws-amplify/amplify-js/blob/main/packages/amplify-ui-components/src/common/Translations.ts /* eslint-disable @typescript-eslint/naming-convention */ const AuthStrings = { BACK_TO_SIGN_IN: 'Back to Sign In', CHANGE_PASSWORD_ACTION: 'Change', CHANGE_PASSWORD: 'Change Password', CODE_LABEL: 'Verification code', CODE_PLACEHOLDER: 'Enter code', CONFIRM_SIGN_UP_CODE_LABEL: 'Confirmation Code', CONFIRM_SIGN_UP_CODE_PLACEHOLDER: 'Enter your code', CONFIRM_SIGN_UP_HEADER_TEXT: 'Confirm Sign up', CONFIRM_SIGN_UP_LOST_CODE: 'Lost your code?', CONFIRM_SIGN_UP_RESEND_CODE: 'Resend Code', CONFIRM_SIGN_UP_SUBMIT_BUTTON_TEXT: 'Confirm', CONFIRM_SMS_CODE: 'Confirm SMS Code', CONFIRM_TOTP_CODE: 'Confirm TOTP Code', CONFIRM: 'Confirm', CREATE_ACCOUNT_TEXT: 'Create account', EMAIL_LABEL: 'Email Address *', EMAIL_PLACEHOLDER: 'Enter your email address', FORGOT_PASSWORD_TEXT: 'Forgot your password?', LESS_THAN_TWO_MFA_VALUES_MESSAGE: 'Less than two MFA types available', NEW_PASSWORD_LABEL: 'New password', NEW_PASSWORD_PLACEHOLDER: 'Enter your new password', NO_ACCOUNT_TEXT: 'No account?', USERNAME_REMOVE_WHITESPACE: 'Username cannot contain whitespace', PASSWORD_REMOVE_WHITESPACE: 'Password cannot start or end with whitespace', PASSWORD_LABEL: 'Password *', PASSWORD_PLACEHOLDER: 'Enter your password', PHONE_LABEL: 'Phone Number *', PHONE_PLACEHOLDER: '(555) 555-1212', QR_CODE_ALT: 'qrcode', RESET_PASSWORD_TEXT: 'Reset password', RESET_YOUR_PASSWORD: 'Reset your password', SELECT_MFA_TYPE_HEADER_TEXT: 'Select MFA Type', SELECT_MFA_TYPE_SUBMIT_BUTTON_TEXT: 'Verify', SEND_CODE: 'Send Code', SUBMIT: 'Submit', SETUP_TOTP_REQUIRED: 'TOTP needs to be configured', SIGN_IN_ACTION: 'Sign In', SIGN_IN_HEADER_TEXT: 'Sign in to your account', SIGN_IN_TEXT: 'Sign in', SIGN_IN_WITH_AMAZON: 'Sign in with Amazon', SIGN_IN_WITH_AUTH0: 'Sign in with Auth0', SIGN_IN_WITH_AWS: 'Sign in with AWS', SIGN_IN_WITH_FACEBOOK: 'Sign in with Facebook', SIGN_IN_WITH_GOOGLE: 'Sign in with Google', SIGN_OUT: 'Sign Out', SIGN_UP_EMAIL_PLACEHOLDER: 'Email', SIGN_UP_HAVE_ACCOUNT_TEXT: 'Have an account?', SIGN_UP_HEADER_TEXT: 'Create a new account', SIGN_UP_PASSWORD_PLACEHOLDER: 'Password', SIGN_UP_SUBMIT_BUTTON_TEXT: 'Create Account', SIGN_UP_USERNAME_PLACEHOLDER: 'Username', SUCCESS_MFA_TYPE: 'Success! Your MFA Type is now:', TOTP_HEADER_TEXT: 'Scan then enter verification code', TOTP_LABEL: 'Enter Security Code:', TOTP_ISSUER: 'AWSCognito', TOTP_SETUP_FAILURE: 'TOTP Setup has failed', TOTP_SUBMIT_BUTTON_TEXT: 'Verify Security Token', TOTP_SUCCESS_MESSAGE: 'Setup TOTP successfully!', UNABLE_TO_SETUP_MFA_AT_THIS_TIME: 'Failed! Unable to configure MFA at this time', USERNAME_LABEL: 'Username *', USERNAME_PLACEHOLDER: 'Enter your username', VERIFY_CONTACT_EMAIL_LABEL: 'Email', VERIFY_CONTACT_HEADER_TEXT: 'Account recovery requires verified contact information', VERIFY_CONTACT_PHONE_LABEL: 'Phone Number', VERIFY_CONTACT_SUBMIT_LABEL: 'Submit', VERIFY_CONTACT_VERIFY_LABEL: 'Verify', ADDRESS_LABEL: 'Address', ADDRESS_PLACEHOLDER: 'Enter your address', NICKNAME_LABEL: 'Nickname', NICKNAME_PLACEHOLDER: 'Enter your nickname', BIRTHDATE_LABEL: 'Birthday', BIRTHDATE_PLACEHOLDER: 'Enter your birthday', PICTURE_LABEL: 'Picture URL', PICTURE_PLACEHOLDER: 'Enter your picture URL', FAMILY_NAME_LABEL: 'Family Name', FAMILY_NAME_PLACEHOLDER: 'Enter your family name', PREFERRED_USERNAME_LABEL: 'Preferred Username', PREFERRED_USERNAME_PLACEHOLDER: 'Enter your preferred username', GENDER_LABEL: 'Gender', GENDER_PLACEHOLDER: 'Enter your gender', PROFILE_LABEL: 'Profile URL', PROFILE_PLACEHOLDER: 'Enter your profile URL', GIVEN_NAME_LABEL: 'First Name', GIVEN_NAME_PLACEHOLDER: 'Enter your first name', ZONEINFO_LABEL: 'Time zone', ZONEINFO_PLACEHOLDER: 'Enter your time zone', LOCALE_LABEL: 'Locale', LOCALE_PLACEHOLDER: 'Enter your locale', UPDATED_AT_LABEL: 'Updated At', UPDATED_AT_PLACEHOLDER: 'Enter the time the information was last updated', MIDDLE_NAME_LABEL: 'Middle Name', MIDDLE_NAME_PLACEHOLDER: 'Enter your middle name', WEBSITE_LABEL: 'Website', WEBSITE_PLACEHOLDER: 'Enter your website', NAME_LABEL: 'Full Name', NAME_PLACEHOLDER: 'Enter your full name', PHOTO_PICKER_TITLE: 'Picker Title', PHOTO_PICKER_HINT: 'Ancillary text or content may occupy this space here', PHOTO_PICKER_PLACEHOLDER_HINT: 'Placeholder hint', PHOTO_PICKER_BUTTON_TEXT: 'Button', IMAGE_PICKER_TITLE: 'Add Profile Photo', IMAGE_PICKER_HINT: 'Preview the image before upload', IMAGE_PICKER_PLACEHOLDER_HINT: 'Tap to image select', IMAGE_PICKER_BUTTON_TEXT: 'Upload', PICKER_TEXT: 'Pick a file', TEXT_FALLBACK_CONTENT: 'Fallback Content', CONFIRM_SIGN_UP_FAILED: 'Confirm Sign Up Failed', SIGN_UP_FAILED: 'Sign Up Failed', } as const; const InteractionsStrings = { CHATBOT_TITLE: 'ChatBot Lex', TEXT_INPUT_PLACEHOLDER: 'Write a message', VOICE_INPUT_PLACEHOLDER: 'Click mic to speak', CHAT_DISABLED_ERROR: 'Error: Either voice or text must be enabled for the chatbot', NO_BOT_NAME_ERROR: 'Error: Bot name must be provided to ChatBot', } as const; const AuthErrorStrings = { DEFAULT_MSG: 'Authentication Error', EMPTY_USERNAME: 'Username cannot be empty', INVALID_USERNAME: 'The username should either be a string or one of the sign in types', EMPTY_PASSWORD: 'Password cannot be empty', EMPTY_CODE: 'Confirmation code cannot be empty', SIGN_UP_ERROR: 'Error creating account', NO_MFA: 'No valid MFA method provided', INVALID_MFA: 'Invalid MFA type', EMPTY_CHALLENGE: 'Challenge response cannot be empty', NO_USER_SESSION: 'Failed to get the session because the user is empty', } as const; type AuthStrings = typeof AuthStrings[keyof typeof AuthStrings]; type InteractionsStrings = typeof InteractionsStrings[keyof typeof InteractionsStrings]; type AuthErrorStrings = typeof AuthErrorStrings[keyof typeof AuthErrorStrings]; export const amplifyVocabularies = { ja: { [AuthStrings.BACK_TO_SIGN_IN]: 'サインインに戻る', [AuthStrings.CHANGE_PASSWORD_ACTION]: '変更', [AuthStrings.CHANGE_PASSWORD]: 'パスワードの変更', [AuthStrings.CODE_LABEL]: '検証コード', [AuthStrings.CODE_PLACEHOLDER]: 'コードを入力してください', [AuthStrings.CONFIRM_SIGN_UP_CODE_LABEL]: '確認コード', [AuthStrings.CONFIRM_SIGN_UP_CODE_PLACEHOLDER]: 'コードを入力してください', [AuthStrings.CONFIRM_SIGN_UP_HEADER_TEXT]: 'サインアップの確認', [AuthStrings.CONFIRM_SIGN_UP_LOST_CODE]: 'コードを紛失しましたか?', [AuthStrings.CONFIRM_SIGN_UP_RESEND_CODE]: 'コードを再送', [AuthStrings.CONFIRM_SIGN_UP_SUBMIT_BUTTON_TEXT]: '確認', [AuthStrings.CONFIRM_SMS_CODE]: 'SMSコードの確認', [AuthStrings.CONFIRM_TOTP_CODE]: 'TOTPコードの確認', [AuthStrings.CONFIRM]: '確認', [AuthStrings.CREATE_ACCOUNT_TEXT]: 'アカウントを作成', [AuthStrings.EMAIL_LABEL]: 'メールアドレス*', [AuthStrings.EMAIL_PLACEHOLDER]: 'メールアドレスを入力してください', [AuthStrings.FORGOT_PASSWORD_TEXT]: 'パスワードを忘れましたか?', [AuthStrings.LESS_THAN_TWO_MFA_VALUES_MESSAGE]: '使用可能なMFAタイプが2つ未満', [AuthStrings.NEW_PASSWORD_LABEL]: '新しいパスワード', [AuthStrings.NEW_PASSWORD_PLACEHOLDER]: '新しいパスワードを入力してください', [AuthStrings.NO_ACCOUNT_TEXT]: 'アカウントがありませんか?', [AuthStrings.USERNAME_REMOVE_WHITESPACE]: 'ユーザー名に空白を含めることはできません', [AuthStrings.PASSWORD_REMOVE_WHITESPACE]: 'パスワードは空白で開始または終了できません', [AuthStrings.PASSWORD_LABEL]: 'パスワード*', [AuthStrings.PASSWORD_PLACEHOLDER]: 'パスワードを入力してください', [AuthStrings.PHONE_LABEL]: '電話番号*', [AuthStrings.PHONE_PLACEHOLDER]: '(555)555-1212', [AuthStrings.QR_CODE_ALT]: 'qrcode', [AuthStrings.RESET_PASSWORD_TEXT]: 'パスワードをリセット', [AuthStrings.RESET_YOUR_PASSWORD]: 'パスワードをリセットします', [AuthStrings.SELECT_MFA_TYPE_HEADER_TEXT]: 'MFAタイプを選択', [AuthStrings.SELECT_MFA_TYPE_SUBMIT_BUTTON_TEXT]: '検証', [AuthStrings.SEND_CODE]: 'コードを送信', [AuthStrings.CONFIRM]: '送信', [AuthStrings.SETUP_TOTP_REQUIRED]: 'TOTPを構成する必要があります', [AuthStrings.SIGN_IN_ACTION]: 'サインイン', [AuthStrings.SIGN_IN_HEADER_TEXT]: 'アカウントにサインインします', [AuthStrings.SIGN_IN_TEXT]: 'サインイン', [AuthStrings.SIGN_IN_WITH_AMAZON]: 'Amazonでサインイン', [AuthStrings.SIGN_IN_WITH_AUTH0]: 'Auth0でサインイン', [AuthStrings.SIGN_IN_WITH_AWS]: 'AWSでサインイン', [AuthStrings.SIGN_IN_WITH_FACEBOOK]: 'Facebookでサインイン', [AuthStrings.SIGN_IN_WITH_GOOGLE]: 'Googleでサインイン', [AuthStrings.SIGN_OUT]: 'サインアウト', [AuthStrings.SIGN_UP_EMAIL_PLACEHOLDER]: 'Eメール', [AuthStrings.SIGN_UP_HAVE_ACCOUNT_TEXT]: 'アカウントをお持ちですか?', [AuthStrings.SIGN_UP_HEADER_TEXT]: '新しいアカウントを作成します', [AuthStrings.SIGN_UP_PASSWORD_PLACEHOLDER]: 'パスワード', [AuthStrings.SIGN_UP_SUBMIT_BUTTON_TEXT]: 'アカウントの作成', [AuthStrings.SIGN_UP_USERNAME_PLACEHOLDER]: 'ユーザー名', [AuthStrings.SUCCESS_MFA_TYPE]: '成功! MFAタイプは次のようになります]: ', [AuthStrings.TOTP_HEADER_TEXT]: 'スキャンして確認コードを入力', [AuthStrings.TOTP_LABEL]: 'セキュリティコードを入力してください:', [AuthStrings.TOTP_ISSUER]: 'AWSCognito', [AuthStrings.TOTP_SETUP_FAILURE]: 'TOTPセットアップが失敗しました', [AuthStrings.TOTP_SUBMIT_BUTTON_TEXT]: 'セキュリティトークンの確認', [AuthStrings.TOTP_SUCCESS_MESSAGE]: 'TOTPを正常にセットアップします!', [AuthStrings.UNABLE_TO_SETUP_MFA_AT_THIS_TIME]: '失敗しました!現時点ではMFAを構成できません', [AuthStrings.USERNAME_LABEL]: 'ユーザー名*', [AuthStrings.USERNAME_PLACEHOLDER]: 'ユーザー名を入力してください', [AuthStrings.VERIFY_CONTACT_EMAIL_LABEL]: 'Eメール', [AuthStrings.VERIFY_CONTACT_HEADER_TEXT]: 'アカウントの回復には確認済みの連絡先情報が必要です', [AuthStrings.VERIFY_CONTACT_PHONE_LABEL]: '電話番号', [AuthStrings.VERIFY_CONTACT_SUBMIT_LABEL]: '送信', [AuthStrings.VERIFY_CONTACT_VERIFY_LABEL]: '検証', [AuthStrings.ADDRESS_LABEL]: 'アドレス', [AuthStrings.ADDRESS_PLACEHOLDER]: '住所を入力してください', [AuthStrings.NICKNAME_LABEL]: 'ニックネーム', [AuthStrings.NICKNAME_PLACEHOLDER]: 'ニックネームを入力してください', [AuthStrings.BIRTHDATE_LABEL]: '誕生日', [AuthStrings.BIRTHDATE_PLACEHOLDER]: '誕生日を入力してください', [AuthStrings.PICTURE_LABEL]: '画像のURL', [AuthStrings.PICTURE_PLACEHOLDER]: '画像のURLを入力してください', [AuthStrings.FAMILY_NAME_LABEL]: '家族名', [AuthStrings.FAMILY_NAME_PLACEHOLDER]: '家族の名前を入力してください', [AuthStrings.PREFERRED_USERNAME_LABEL]: '優先ユーザー名', [AuthStrings.PREFERRED_USERNAME_PLACEHOLDER]: '希望するユーザー名を入力してください', [AuthStrings.GENDER_LABEL]: '性別', [AuthStrings.GENDER_PLACEHOLDER]: '性別を入力してください', [AuthStrings.PROFILE_LABEL]: 'プロファイルURL', [AuthStrings.PROFILE_PLACEHOLDER]: 'プロファイルURLを入力してください', [AuthStrings.GIVEN_NAME_LABEL]: '名', [AuthStrings.GIVEN_NAME_PLACEHOLDER]: '名を入力してください', [AuthStrings.ZONEINFO_LABEL]: 'タイムゾーン', [AuthStrings.ZONEINFO_PLACEHOLDER]: 'タイムゾーンを入力してください', [AuthStrings.LOCALE_LABEL]: 'ロケール', [AuthStrings.LOCALE_PLACEHOLDER]: 'ロケールを入力してください', [AuthStrings.UPDATED_AT_LABEL]: '更新場所', [AuthStrings.UPDATED_AT_PLACEHOLDER]: '情報が最後に更新された時刻を入力してください', [AuthStrings.MIDDLE_NAME_LABEL]: 'ミドルネーム', [AuthStrings.MIDDLE_NAME_PLACEHOLDER]: 'ミドルネームを入力してください', [AuthStrings.WEBSITE_LABEL]: 'ウェブサイト', [AuthStrings.WEBSITE_PLACEHOLDER]: 'あなたのウェブサイトを入力してください', [AuthStrings.NAME_LABEL]: 'フルネーム', [AuthStrings.NAME_PLACEHOLDER]: 'フルネームを入力してください', [AuthStrings.PHOTO_PICKER_TITLE]: 'ピッカータイトル', [AuthStrings.PHOTO_PICKER_HINT]: '補助テキストまたはコンテンツがここのこのスペースを占める場合があります', [AuthStrings.PHOTO_PICKER_PLACEHOLDER_HINT]: 'プレースホルダーヒント', [AuthStrings.PHOTO_PICKER_BUTTON_TEXT]: 'ボタン', [AuthStrings.IMAGE_PICKER_TITLE]: 'プロフィール写真を追加', [AuthStrings.IMAGE_PICKER_HINT]: 'アップロードする前に画像をプレビューします', [AuthStrings.IMAGE_PICKER_PLACEHOLDER_HINT]: 'タップして画像を選択', [AuthStrings.IMAGE_PICKER_BUTTON_TEXT]: 'アップロード', [AuthStrings.PICKER_TEXT]: 'ファイルを選択', [AuthStrings.TEXT_FALLBACK_CONTENT]: 'フォールバックコンテンツ', [AuthStrings.CONFIRM_SIGN_UP_FAILED]: 'サインアップが失敗したことを確認します', [AuthStrings.SIGN_UP_FAILED]: 'サインアップに失敗しました', [InteractionsStrings.CHATBOT_TITLE]: 'ChatBot Lex', [InteractionsStrings.TEXT_INPUT_PLACEHOLDER]: 'メッセージを書く', [InteractionsStrings.VOICE_INPUT_PLACEHOLDER]: 'マイクをクリックして話します', [InteractionsStrings.CHAT_DISABLED_ERROR]: 'エラー:チャットボットに対して音声またはテキストのいずれかを有効にする必要があります', [InteractionsStrings.NO_BOT_NAME_ERROR]: 'エラー:ボット名をChatBotに提供する必要があります', [AuthErrorStrings.DEFAULT_MSG]: '認証エラー', [AuthErrorStrings.EMPTY_USERNAME]: 'ユーザー名を空にすることはできません', [AuthErrorStrings.INVALID_USERNAME]: 'ユーザー名は文字列またはサインインタイプのいずれかである必要があります', [AuthErrorStrings.EMPTY_PASSWORD]: 'パスワードを空にすることはできません', [AuthErrorStrings.EMPTY_CODE]: '確認コードを空にすることはできません', [AuthErrorStrings.SIGN_UP_ERROR]: 'アカウントの作成中にエラーが発生しました', [AuthErrorStrings.NO_MFA]: '有効なMFAメソッドが提供されていません', [AuthErrorStrings.INVALID_MFA]: '無効なMFAタイプ', [AuthErrorStrings.EMPTY_CHALLENGE]: 'チャレンジレスポンスを空にすることはできません', [AuthErrorStrings.NO_USER_SESSION]: 'ユーザーが空であるため、セッションを取得できませんでした', 'Select Language': '言語選択', }, };
課題
- Cognitoのエラーに関しては、多言語化できていない。
- エラー一覧があれば、言語用constに追加するだけでよいが見つからなかった。
- 言語切り替えにページリロードをしている。
- モジュールの説明を見る限りロード時に決めておくもののよう。
https://docs.amplify.aws/lib/utilities/i18n/q/platform/js参考
https://docs.amplify.aws/lib/utilities/i18n/q/platform/js
https://github.com/aws-amplify/amplify-js/blob/main/packages/amplify-ui-components/src/common/Translations.ts
- 投稿日:2021-02-28T22:33:47+09:00
AWSの障害を考える
2021/02/19(土)の夜間にAWSで障害が発生しました
ここのところ一年に一度のペースで大きな障害が発生しているので
自分なりに考察していきます2021/2/19日のAWS障害の詳細
- 障害時間 2021/02/19(土) 23:50~20(日) AM5:30
- ダウンタイム 4時間40分
障害が発生したリージョン/AZ
- 東京リージョン
- ap-northeast-1(apne1-az1)
影響を受けたサービス
- EC2
- EBS
原因
- 冷却ユニットの温度上昇の影響により、冷却ユニットの電源がダウンする
過去に発生した大きな障害(東京リージョン)
2019/8/23 EC2、EBS、RDS、Redshift、ElastiCache 、Workspacesで障害
- ダウンタイム 約4時間
- AWSの障害レポート
2020/4/20 SQS、Lambda、CloudWatch、CloudFormationで障害
- ダウンタイム 約4時間
※レポート見つからずちなみに2013年~2019年まで東京リージョンで大きな障害は無かったのである意味凄いなという感じです
反対にユーザは「東京リージョンは落ちない」という錯覚をしていた人がほとんどだと思います(海外リージョンはそれなりに障害が起きています)責任共有モデル
AWSには責任共有モデルがあり、AWSの責任とユーザの責任の境界線を明示的にしています
例えば、データの保護やバックアップは「ユーザ側でやってくださいね、AWSは責任を取りませんよ」というものですDesign for Failure(障害設計)
AWSは障害を見据えて設計してくださいと提唱しています
従いユーザは障害が発生することを考えての設計指針が必要ですSLAの条件
- EC2、EBSのSLA 99.99% (月間ダウンタイム 4.32 分)
- 要約すると東京リージョンの場合は2つのAZで接続不可にならないと適用されない
私の記憶ではAZが2つ同時に接続不可になったことは無いです
すべての障害対策は不可能
すべての障害対策をすることは不可能です
障害とは予測できない事が多いし、予期できないことが起こるから世の中でシステム障害が起きています予測できない障害に対してユーザ側は適材適所柔軟に対応するしか無いと考えます
ただし、予測の範囲内で障害設計することが大事です
万全な準備しておけば、障害への対応時間を短くできます完全な障害設計は不可能
完全な障害設計は不可能です
そもそも、AWSはサービスのアーキテクチャを開示していません
これが何を意味するかと言うと、アーキテクチャが分からないユーザは
分からない部分の対処をできるはずがないです(障害設計できない)例えばですが、AというAWSのサービスがあったとします
このAサービスがEC2のアプライアンス上で動いているとしたら
Aサービスの障害設計をしたとしても、EC2がダウン→Aサービスもダウンします故に完全な障害設計は不可能だと考えています
ユーザはできるだけの障害設計をする、障害に備えることしかできません実際の障害対応は想像より複雑
障害テストは割と0と1で考えている事が多いです
例えば「EC2がダウン→切替える」とかですこういう障害が起こってくれれば対応する側も助かるのですが
実際の障害は部分部分でゆっくり少しづつ死んでいくという事がありますEC2のCPUの負荷が少しづつ高くなる、EBSのパフォーマンスが低下していくなどで
結果的にサービスが不安定になるという、対応する側にとっては判断も難しく
複雑で嫌なパターンになりますAWSの障害復旧は早い
毎回数時間で障害復旧します
ダウンしたのは事実ですが、それを数時間で復旧までもっていくAWSは凄いなと思います
実際データセンタで障害が起きたら、このスピード感で対応ができるかと言うと、、、
正直難しいかなと思います実態を歪めない
バイアスやハロー効果で実態を歪めないでください
メガクラウドで障害起きると大きなニュースになります
どうしても印象が悪くなったり、クラウドはダメだってなりがちです例えば交通事故で言うと、飛行機の事故と、個人車の事故を比べるようなものです
実態としては、飛行機事故より個人車の事故のほうが圧倒的に多いですさいごに障害に対する考え
2020年に東証一部のシステムでも障害はありましたが、障害を防ぐことは不可能です
人間に体調を崩すなと言っているのと一緒です
システムはダウンする事が前提ですただ、障害に備えることは可能なので、RTO/RPOを考慮して障害設計をする、障害時のオペレーションを確立しておく事が重要です
前述に書いた「Well-Arc」を参考にするのも一つの手段です
AWSの10年以上の設計指針が積み上げられています
Well-Arcをすべて実行するのは不可能かもしれませんが参考にはなります
・ Well-Arcはこちら
- 投稿日:2021-02-28T22:27:05+09:00
【AWS】VPCを作成
はじめに
Amazon VPC (Virtual Private Cloud)の略で、AWS上に自身のプライベートネットワークを作成できます。
このネットワーク上にEC2やRDSといったAWSサービスを作成しシステムを構築できます。プライベートなNW空間になりますが、インターネットゲートウェイを作成しインターネットと接続したり、Direct Connectという機能を利用して社内のイントラと接続することも可能です。
前提
VPC作成前にリージョンとアベイラビリティゾーン(AZ)について簡単に触れておきます。
AWSはリージョンとAZという単位に分割して管理されています。リージョン
AWSの各サービスが提供されている世界各地の地域のことです。
例えば、日本はアジアパシフィック(東京)というリージョンに含まれています。他にもバージニア北部、オハイオといったリージョンが存在、リージョン間は完全に分離されており耐障害性を保っています。アベイラビリティゾーン(AZ)
AZとは、各リージョンに存在するデータセンターです。 各リージョンには複数のアベイラビリティゾーンが存在し、それぞれにサブネットやEC2といったサービスを配置することで冗長性を保つことができます。
今回作成するVPCの最終構成
今回はアジアパシフィック(東京)リージョンに以下のようなVPCを作成します。
インターネットと通信ができるパブリックサブネット、インターネットとの通信ができず、VPC内のみの通信が可能なプライベートサブネットを、各AZ内に作成します。
VPCの作成
VPCを作成していきます。
サービスからVPCを選択後、「VPCを作成」をクリック
VPCの設定
IPv4 CIDR ブロックでプライベートIPアドレスの範囲を指定します。
CIDRブロックサイズは/16~/28のサイズを指定可能で、
スラッシュの後の数字が小さいほど、割り振る IP アドレスの数が増えます。(/16だと65,536 個分、/28だと11個分)
IPv4 CIDR ブロックは本来、必要なIPアドレスを見積もりした上で設計しますが、今回は以下の通り設定します。名前タグ: rails_test IPv4 CIDR ブロック: 10.0.0.0/16サブネットの設定
サブネットは簡単にいうとVPCを細かく区切ったネットワーク空間です。
先ほど作成したVPCの中にサブネットを構築していきます。
サブネットマスク(16bit ~ 28bit)で割り当て空間の設定や、
配置するAZを指定します。左ペインのサブネットを選択後、「サブネット作成」ボタンをクリック。
VPC IDで作成したVPCを選択
下の図は以下の設定をしたものです。
サブネット名: Public-subnet-a アベイラビリティーゾーン: ap-northeast-1a IPv4 CIDR ブロック: 10.0.1.0/24同様の手順で他3つのサブネットも作成します。
サブネット名: Public-subnet-c アベイラビリティーゾーン: ap-northeast-1c IPv4 CIDR ブロック: 10.0.2.0/24サブネット名: Private-subnet-a アベイラビリティーゾーン: ap-northeast-1a IPv4 CIDR ブロック: 10.0.11.0/24サブネット名: Private-subnet-c アベイラビリティーゾーン: ap-northeast-1c IPv4 CIDR ブロック: 10.0.12.0/24インターネットゲートウェイの作成
VPCを作成しましたが、このままではインターネットに接続できません。
インターネットにはインターネットゲートウェイ(IGW)を経由して通信する必要があります。
まずはIGWを作成します。左ペインのインターネットゲートウェイを選択後、「インターネットゲートウェイの作成」をクリック
以下の通り設定名前タグ:igw-rails-test
作成直後はデタッチ状態のため、先ほど作成したVPCにアタッチを行います。
作成したインターネットゲートウェイの「アクション」よりVPCにアタッチをクリック
使用可能なVPCで作成したVPCを選択
IGWの作成、アタッチは完了です。
ルートテーブルの設定
ルートテーブルとは通信経路を決めたルートと呼ばれるルールを定義されたテーブル情報のことです。
IGWをアタッチしましたが、まだインターネットと通信することはできません。ルートテーブルにIGWへのルートを追加しインターネット接続できるようにします。左ペインのルートテーブルを選択、「ルートテーブルの作成」をクリック
名前タグ: Public-Route-Table VPC: (作成したVPCを指定)ルートの編集
ルートテーブル作成後、ルートタブの「ルートの編集」をクリック
送信先: 0.0.0.0/0 ターゲット: (アタッチしたIGW)※送信先の0.0.0.0/0は全てのアドレスとマッチするという意味です。全ての送信先に対してIGWを経由するという設定になります。
またルートテーブルにはlocalというテーブルがはじめから関連づけられています。これはVPC内のIPアドレスに対してのルート情報です。
先ほど、全ての送信先に対してIGWを経由と書きましたが、
ルートテーブルは最長プレフィックス一致(定義された最も具体的なルートが優先)が適用されるため、送信先がVPC内のIPアドレスの場合はlocal、それ以外はIGWを経由して通信することになります。サブネットの関連付け
作成したルートテーブルにサブネットを割り当てします。
サブネットの関連付けタブを選択後、「サブネットの関連付けの編集」ボタンをクリックサブネットID: Public-subnet-a、Public-subnet-cここで設定したサブネットが、パブリックサブネット(インターネットへのアクセスが可能)になります。
省略していますが、ターゲットがlocalのみ(IGWのルート情報がない)のルートテーブルを作成し、関連付けしたサブネットがプライベートサブネットとなります。
完成
最後に
今回は一般的なVPC作成について投稿しました。
作成したパブリックサブネットにEC2、プライベートサブネットにRDS等を配置しWebアプリケーションを構築することができます。
VPCには他にもネットワークACLで通信を制御したり等、さまざまな機能があるため、どんどん活用していきたいです。
- 投稿日:2021-02-28T22:24:00+09:00
【学習メモ】AWS DynamoDB
DynamoDbとは
Amazonが提供するNoSQLデータベースサービス。
特徴:信頼性が高い、スケーラビリテ、低レイテンシDynamoDbの基本概念
DynamoDbの構成
構想部分:テーブル、アイテム、属性。
DynamoDbはテーブル単位から設定する。
テーブル:データのコレクションのこと。
アイテム:各テーブルの中にアイテムを作ってデータを作成する。
属性:各アイテムは1つ以上の属性で構成される。
DynamoDbのプライマリーキー
テーブルを作成するとき、テーブル名を指定する以外、プライマリーキーも指定しないといけいない。
プライマリーキーはアイテムを認識するものなので、異なるアイテムは同じプライマリーキーを使っては行けない。プライマリーキーは2つある。
・パーティションキー(Partition Key)
・パーティションキー & ソートキー (Partition Key+Sort Key)⇨複合キーも呼ばれる。
※第一属性はパーティションキー、第二属性はソートキー。パーティションキーはストレージの場所を決まったので、ソートキーの重複は許さない。
DynamoDb Secondary Index
プライマリーキー以外、セカンダリーインデックスを使って、データの検索も可能。
一つのテーブルは一つor複数のセカンダリーインデックスを作れる。DynamoDb Secondary Index
・Global secondary index(GSI)
・Local secondary index(LSI)
★GSI
テーブルのパーティションキー、ソートキーと異なるインデックス。★LSI
テーブルのパーティションキーは一緒で、ソートキーは異なるインデックス。一つのテーブルは最大五つのGSI、五つのLSIを設定できる。
DynamoDB APIs
®︎Reference:https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.API.html
DynamoDB Streams
DynamoDB テーブルに保存された項目の追加・変更・削除の発生時の履歴をキャプチャできる機能。
※過去24時間以内のデータ変更の履歴を保存し、24時間を経過すると消去される。DynamoDbの使いところ
IoT:
・デバイスデータの保存。
・DynamoDbをAmazon Redshiftにデータウェアハウスに接続し、BI分析を実施。ゲーム:
・ゲームの行動記録。Advertisement Serving
- 投稿日:2021-02-28T21:13:50+09:00
Amazon Aurora Serverless
Amazon Aurora Serverless
- Amazon Aurora Serverless | AWS
- Aurora Serverless v1 の仕組み
- Aurora Serverlessの導入時に気をつけるべきこと
- Aurora Serverlessを実際に使ってみたメリットとデメリット
- AWS Aurora Serverlessを使ってみた感想
- [AWS] Aurora Serverless とは何か
- AWSが「Amazon Aurora Serverless」の次期バージョンを発表、SQL Serverからの移行を助ける機能も
- AWSが「Aurora Serverless v2」プレビュー発表、きめ細かな容量調整が可能に
- AWSがAurora Serverless v2のプレビューを発表
- 投稿日:2021-02-28T20:36:02+09:00
AWSとWordPressを使ってWebサイトを構築しよう①
本記事ではパート毎に分けて最終的にはタイトルの通り「AWSとWordpressを使ってWebサイトを構築」していきます。 初学者の方はゆっくりと確実に1つ1つ記事の通りに進めて頂けたら最終的にはWebサイトを構築することができます。 まずはWebサイト構築を始める前に理解しておくべきサーバーとネットワークについて抑えましょう。 目次 1.サーバー構築の考え方 2.ネットワーク構築の考え方 1. サーバー構築の考え方 そもそもサーバーとは簡単に言うと「何かを相手に提供する」ということです。 例えばコーヒーサーバーでしたらコーヒーを提供するためのサーバー。 それと同じようにコンピュータで言うサーバーとはクライアントにサービスを提供するためのサーバーです。 サーバーを構築する際にまずは”どんなサーバーが必要なのか”を考えます。 もしあなたが”自分のWebサイトを作成して公開したい”という時は、Webサイトを提供するためのWebサーバーが必要となります。 またWebサイトに書いた記事や画像を保存しておくためのデータベースも必要ですよね。なのでDBサーバーも必要です。 このようにどんなサービスを提供するかで構築するサーバーも変わっていきます。 とはいえWebサーバーだからWebサーバーというものがあるということではなく、正確に言うと「Windows Server」などのOSをインストールしたコンピュータにWeb機能をインストールさせたものをWebサーバーと言います。 2. ネットワーク構築の考え方 サーバーを構築した後にインターネットへ繋げるにはネットワークを通じて通信できるようにしてあげる必要があります。 インターネットはTCP/IPというプロトコルを使ってネットワーク上で通信を行っており、それぞれのサーバーにIPアドレス(こんな形「10.0.1.1」)という固有の番号を割り振って他の機器と重複しないように識別しています。 さらにインターネットへ接続するのにルーターという機器を使用しており、サーバーからルーターへ通信するように構成する必要があります。 また通常インターネット上ではWebサイトの住所を表す時に「https://www.google.co.jp」 このようなドメイン名と呼ばれるものを使用します。 実はこれは人間が見やすいように作成されたもので、IPアドレスに関連付けされて設定されています。 このような設定を行うのにDNSサーバーというサーバーの設定も必要です。 それでは次回からAmazonが提供しているクラウドサービスであるAWSサービスにWordPressをインストールしてWebサイトを構築していきましょう! 次の記事はこちらから AWSとWordPressを使ってWebサイトを構築しよう②
- 投稿日:2021-02-28T18:45:47+09:00
①NiceHashマイニング収益をAWS Lambda×SNSでメール通知する
目次
1. 背景
日々メインPCとは別にマイニングリグを稼働させて、NiceHashでマイニングをしているのですが、残高確認する際にNiceHashのダッシュボードを都度確認するのは面倒なので日次ジョブとして通知したいと思ってました。
業務ではコーディングや、AWSに触れる機会が一切ないので、勉強がてらAWS上でスマホへメール通知するシステムを構築してみました。
最重要なマイニングリグのヘルスチェックエラー通知はMySettingsから設定できる 1 仕様なので、今回は収益情報のみをメールの通知対象としました。2. 構成/構築手順
システム構成は、AWS Lambdaを中心とした基本的なサーバレスアーキテクチャです。
処理の流れ
1. EventBridge(CroudWatch Event)の日次実行cronがトリガーとなり、Lambda関数をキック
2. Lambdaでは、外部APIからマイニング収益情報を取得
3. S3バケットへ残高情報を書き込み、前日の残高情報を取得、残高の増減を算出
4. Amazon SNSへ値をpublishしてスマホへメールを通知
2-1.Lambdaの構築
2-1-1.IAMロールの作成
AWSサービス間を連携するために新規IAMロールを作成し必要なポリシーをアタッチする
・IAMを起動し、ユースケースLambda
を選択し「次のステップ」をクリック
・S3バケットへ残高情報を読み書きするためにAmazonS3FullAccess
、収益情報をメール通知するためにAmazonSNSFullAccess
ポリシーをロールにアタッチし「次のステップ」をクリック
・タグの追加は不要なので何も記入せず「次のステップ」をクリック
・ロール名は適当にNiceHash-Nortification
として「ロールの作成」をクリック
2-1-2.Lambda関数の作成
呼び出されるLambda関数本体を作成する
・サービスからLambdaを起動し、以下のように入力し「関数の作成」をクリック関数名:「NiceHash-Nortification-Mail」 ランタイム:「Python 3.6」#Python3系ならたぶんOK アクセス権限:「NiceHash-Nortification」#作成したIAMロール2-1-3.ソースコードのデプロイ
Lambdaで実行するプログラムをデプロイする
・以下4つのpythonファイルを新規に作成してコードをデプロイNiceHash-Nortification-MailNiceHash-Nortification-Mail/ ├ lambda_function.py ├ nicehash.py ├ marketrate.py └ s3inout.pyLambdaで呼び出されるメインプログラム
lambda_function.pyimport json import datetime import boto3 import nicehash import marketrate import s3inout #Function kicked by AWS Lambda def lambda_handler(event, context): client = boto3.client('sns') #Amazon SNS TOPIC_ARN = 'arn:aws:sns:xx-xxxx-x:xxxxxxxxxx:NiceHashSNS' # SNSのARNを指定 msg = create_message() subject = "NiceHash-Mining 日次収益通知" #Send a notification message to Amazon SNS response = client.publish( TopicArn = TOPIC_ARN, Message = msg, Subject = subject ) #Function to get a nortification message def create_message(): #NiceHash API host = 'https://api2.nicehash.com' organisation_id = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' # hogehoge key = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' # API Key Code secret = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' # API Secret Key Code market='BTC' #S3 bucket bucket_name = '[bucket_name]'#hogehoge key_name = '[balance_filename]'#hogehoge #Get mining information from NiceHash API PrivateApi = nicehash.private_api(host, organisation_id, key, secret) accounts_info = PrivateApi.get_accounts_for_currency(market) balance_row = float(accounts_info['totalBalance']) #Get currency_to_JPY_rate from CoinGecko API TradeTable = marketrate.trade_table(market) rate = TradeTable.get_rate() balance_jpy = int(balance_row*rate) #S3 dealer S3dealer = s3inout.s3_dealer(bucket = bucket_name, key = key_name) pre_balance = int(S3dealer.read_from_s3_bucket()) diff = balance_jpy - pre_balance S3dealer.write_to_s3_bucket(str(balance_jpy)) #Nortification message time_text = "時刻: " + str(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))))[:19] market_text = "仮想通貨: " + market rate_text = "単位仮想通貨価値: " + str(rate) + "円" balance_text = "現在の残高: " + str(balance_jpy) + "円" pre_balance_text = "昨日の残高: " + str(pre_balance) + "円" symbol = "+" if diff > 0 else "" diff_txt = "【日次収益: " + str(symbol) + str(diff) + "円】" mon_revenue = "推定月次収益: " + str(diff*30) + "円" ann_revenue = "推定年次収益: " + str(diff*365) + "円" msg = '\n'.join([time_text,market_text,rate_text,balance_text,pre_balance_text,diff_txt,mon_revenue,ann_revenue]) return msgNiceHash API (2-2で説明)
nicehash.pyfrom datetime import datetime from time import mktime import uuid import hmac import requests import json from hashlib import sha256 import optparse import sys class private_api: def __init__(self, host, organisation_id, key, secret, verbose=False): self.key = key self.secret = secret self.organisation_id = organisation_id self.host = host self.verbose = verbose def request(self, method, path, query, body): xtime = self.get_epoch_ms_from_now() xnonce = str(uuid.uuid4()) message = bytearray(self.key, 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray(str(xtime), 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray(xnonce, 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray(self.organisation_id, 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray(method, 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray(path, 'utf-8') message += bytearray('\x00', 'utf-8') message += bytearray(query, 'utf-8') if body: body_json = json.dumps(body) message += bytearray('\x00', 'utf-8') message += bytearray(body_json, 'utf-8') digest = hmac.new(bytearray(self.secret, 'utf-8'), message, sha256).hexdigest() xauth = self.key + ":" + digest headers = { 'X-Time': str(xtime), 'X-Nonce': xnonce, 'X-Auth': xauth, 'Content-Type': 'application/json', 'X-Organization-Id': self.organisation_id, 'X-Request-Id': str(uuid.uuid4()) } s = requests.Session() s.headers = headers url = self.host + path if query: url += '?' + query if self.verbose: print(method, url) if body: response = s.request(method, url, data=body_json) else: response = s.request(method, url) if response.status_code == 200: return response.json() elif response.content: raise Exception(str(response.status_code) + ": " + response.reason + ": " + str(response.content)) else: raise Exception(str(response.status_code) + ": " + response.reason) def get_epoch_ms_from_now(self): now = datetime.now() now_ec_since_epoch = mktime(now.timetuple()) + now.microsecond / 1000000.0 return int(now_ec_since_epoch * 1000) def algo_settings_from_response(self, algorithm, algo_response): algo_setting = None for item in algo_response['miningAlgorithms']: if item['algorithm'] == algorithm: algo_setting = item if algo_setting is None: raise Exception('Settings for algorithm not found in algo_response parameter') return algo_setting def get_accounts(self): return self.request('GET', '/main/api/v2/accounting/accounts2/', '', None) def get_accounts_for_currency(self, currency): return self.request('GET', '/main/api/v2/accounting/account2/' + currency, '', None) def get_withdrawal_addresses(self, currency, size, page): params = "currency={}&size={}&page={}".format(currency, size, page) return self.request('GET', '/main/api/v2/accounting/withdrawalAddresses/', params, None) def get_withdrawal_types(self): return self.request('GET', '/main/api/v2/accounting/withdrawalAddresses/types/', '', None) def withdraw_request(self, address_id, amount, currency): withdraw_data = { "withdrawalAddressId": address_id, "amount": amount, "currency": currency } return self.request('POST', '/main/api/v2/accounting/withdrawal/', '', withdraw_data) def get_my_active_orders(self, algorithm, market, limit): ts = self.get_epoch_ms_from_now() params = "algorithm={}&market={}&ts={}&limit={}&op=LT".format(algorithm, market, ts, limit) return self.request('GET', '/main/api/v2/hashpower/myOrders', params, None) def create_pool(self, name, algorithm, pool_host, pool_port, username, password): pool_data = { "name": name, "algorithm": algorithm, "stratumHostname": pool_host, "stratumPort": pool_port, "username": username, "password": password } return self.request('POST', '/main/api/v2/pool/', '', pool_data) def delete_pool(self, pool_id): return self.request('DELETE', '/main/api/v2/pool/' + pool_id, '', None) def get_my_pools(self, page, size): return self.request('GET', '/main/api/v2/pools/', '', None) def get_hashpower_orderbook(self, algorithm): return self.request('GET', '/main/api/v2/hashpower/orderBook/', 'algorithm=' + algorithm, None ) def create_hashpower_order(self, market, type, algorithm, price, limit, amount, pool_id, algo_response): algo_setting = self.algo_settings_from_response(algorithm, algo_response) order_data = { "market": market, "algorithm": algorithm, "amount": amount, "price": price, "limit": limit, "poolId": pool_id, "type": type, "marketFactor": algo_setting['marketFactor'], "displayMarketFactor": algo_setting['displayMarketFactor'] } return self.request('POST', '/main/api/v2/hashpower/order/', '', order_data) def cancel_hashpower_order(self, order_id): return self.request('DELETE', '/main/api/v2/hashpower/order/' + order_id, '', None) def refill_hashpower_order(self, order_id, amount): refill_data = { "amount": amount } return self.request('POST', '/main/api/v2/hashpower/order/' + order_id + '/refill/', '', refill_data) def set_price_hashpower_order(self, order_id, price, algorithm, algo_response): algo_setting = self.algo_settings_from_response(algorithm, algo_response) price_data = { "price": price, "marketFactor": algo_setting['marketFactor'], "displayMarketFactor": algo_setting['displayMarketFactor'] } return self.request('POST', '/main/api/v2/hashpower/order/' + order_id + '/updatePriceAndLimit/', '', price_data) def set_limit_hashpower_order(self, order_id, limit, algorithm, algo_response): algo_setting = self.algo_settings_from_response(algorithm, algo_response) limit_data = { "limit": limit, "marketFactor": algo_setting['marketFactor'], "displayMarketFactor": algo_setting['displayMarketFactor'] } return self.request('POST', '/main/api/v2/hashpower/order/' + order_id + '/updatePriceAndLimit/', '', limit_data) def set_price_and_limit_hashpower_order(self, order_id, price, limit, algorithm, algo_response): algo_setting = self.algo_settings_from_response(algorithm, algo_response) price_data = { "price": price, "limit": limit, "marketFactor": algo_setting['marketFactor'], "displayMarketFactor": algo_setting['displayMarketFactor'] } return self.request('POST', '/main/api/v2/hashpower/order/' + order_id + '/updatePriceAndLimit/', '', price_data) def get_my_exchange_orders(self, market): return self.request('GET', '/exchange/api/v2/myOrders', 'market=' + market, None) def get_my_exchange_trades(self, market): return self.request('GET','/exchange/api/v2/myTrades', 'market=' + market, None) def create_exchange_limit_order(self, market, side, quantity, price): query = "market={}&side={}&type=limit&quantity={}&price={}".format(market, side, quantity, price) return self.request('POST', '/exchange/api/v2/order', query, None) def create_exchange_buy_market_order(self, market, quantity): query = "market={}&side=buy&type=market&secQuantity={}".format(market, quantity) return self.request('POST', '/exchange/api/v2/order', query, None) def create_exchange_sell_market_order(self, market, quantity): query = "market={}&side=sell&type=market&quantity={}".format(market, quantity) return self.request('POST', '/exchange/api/v2/order', query, None) def cancel_exchange_order(self, market, order_id): query = "market={}&orderId={}".format(market, order_id) return self.request('DELETE', '/exchange/api/v2/order', query, None) if __name__ == "__main__": parser = optparse.OptionParser() parser.add_option('-b', '--base_url', dest="base", help="Api base url", default="https://api2.nicehash.com") parser.add_option('-o', '--organization_id', dest="org", help="Organization id") parser.add_option('-k', '--key', dest="key", help="Api key") parser.add_option('-s', '--secret', dest="secret", help="Secret for api key") parser.add_option('-m', '--method', dest="method", help="Method for request", default="GET") parser.add_option('-p', '--path', dest="path", help="Path for request", default="/") parser.add_option('-q', '--params', dest="params", help="Parameters for request") parser.add_option('-d', '--body', dest="body", help="Body for request") options, args = parser.parse_args() private_api = private_api(options.base, options.org, options.key, options.secret) params = '' if options.params is not None: params = options.params try: response = private_api.request(options.method, options.path, params, options.body) except Exception as ex: print("Unexpected error:", ex) exit(1) print(response) exit(0)CoinGecko API (2-2で説明)
marketrate.pyimport requests import json class trade_table: def __init__(self, market="BTC"): #currency-name conversion table self.currency_rename_table = {'BTC':'Bitcoin','ETH':'Ethereum','LTC':'Litecoin', 'XRP':'XRP','RVN':'Ravencoin','MATIC':'Polygon', 'BCH':'Bitcoin Cash','XLM':'Stellar','XMR':'Monero','DASH':'Dash'} self.market = self.currency_rename_table[market] def get_rate(self): body = requests.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=jpy') coingecko = json.loads(body.text) idx = 0 while coingecko[idx]['name'] != self.market: idx += 1 #Escape of illegal market_currency name if idx > 100: return "trade_table_err" #market-currency_to_JPY_rate else: return int(coingecko[idx]['current_price'])S3バケットへの収益情報の読み込み・書き出し(2-3で説明)
s3inout.pyimport boto3 class s3_dealer: def __init__(self, bucket = 'nice-hash-balance', key = 'balance_latest.txt'): self.bucket = bucket self.key = key #Get balance of the previous day def read_from_s3_bucket(self): S3 = boto3.client('s3') res = S3.get_object(Bucket=self.bucket, Key=self.key) body = res['Body'].read() return body.decode('utf-8') #Export balance def write_to_s3_bucket(self, balance): S3 = boto3.resource('s3') obj = S3.Object(self.bucket, self.key) obj.put(Body=balance)2-1-4.レイヤー作成
必要なモジュールをLambdaのレイヤーに取り込む
・2-1-3 記載のソースをデプロイしただけで実行するとrequests
モジュールが読み込めず以下エラーが発生してしまうため、外部モジュールをLayersへ定義する{ "errorMessage": "Unable to import module 'lambda_function': No module named 'requests'", "errorType": "Runtime.ImportModuleError" }・レイヤーファイルを作成するために、EC2でAmazon Linux AMIから新規インスタンスを作成する
・2-1-1の手順で、EC2のロールに対してS3のアクセスポリシーをアタッチ
※インターネット環境に接続されたWSLやUbuntu等のUNIXマシンであれば何でもOK
・EC2インスタンスへコンソール接続し、以下CLIコマンドを打鍵してレイヤーファイルを作成するec2-user[ec2-user@ip-xxx-xx-xx-xxx ~]$ su - [root@ip-xxx-xx-xx-xxx ~]# mkdir layer/ [root@ip-xxx-xx-xx-xxx ~]# cd layer [root@ip-xxx-xx-xx-xxx ~]# yum -y install gcc gcc-c++ kernel-devel python-devel libxslt-devel libffi-devel openssl-devel [root@ip-xxx-xx-xx-xxx ~]# yum -y install python-pip [root@ip-xxx-xx-xx-xxx ~]# pip install -t ./ requests [root@ip-xxx-xx-xx-xxx ~]# cd ../ [root@ip-xxx-xx-xx-xxx ~]# zip -r Layer.zip layer/・レイヤーファイルを、S3バケットへアップロード
ec2-user[root@ip-xxx-xx-xx-xxx ~]# chmod 777 Layer.zip [root@ip-xxx-xx-xx-xxx ~]# aws s3 cp Layer.zip s3://layerzip-s3・S3でEC2からアップロードしたレイヤーファイルのオブジェクトURLを取得
・LambdaでS3のオブジェクトURLから名前を適当に
ImportRequests
としてレイヤーを作成
・カスタムレイヤーから作成した
ImportRequests
を読み込む
2-1-5.タイムアウト値の延長
タイムアウトエラーを回避するためにタイムアウト値を変更する
・Lambdaはデフォルトだと、メモリ:128MB
、タイムアウト:3秒
になっているため、タイムアウトのみ「3秒⇒ 5秒」へ変更する
2-2.APIによる収益情報取得
外部APIからマイニング収益情報を取得する
・LambdaとNiceHash APIを連携するために、NiceHashへログインしてMySettingsからAPI Keysを発行する
・NiceHash API(nicehash.py)で収益情報を取得するために、lambda_function.py
の対象箇所に発行したAPI Keys、組織IDを入力するlambda_function.py#NiceHash API host = 'https://api2.nicehash.com' organisation_id = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' # hogehoge key = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' # API Key Code secret = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' # API Secret Key Code・NiceHash APIのみでは、円相場の情報は取得できないため、別の外部API CoinGecko API(marketrate.py)を呼び出して、各仮想通貨市場の日本円相場を取得する。
2-3.EventBridgeによるトリガー定義
日次ジョブとしてLambdaをキックするためのトリガーを定義する
・Lambdaから「トリガーの追加」をクリック
・EventBridgeを選択し、以下のように入力し「追加」をクリック
※AWSでcronを定義する際は、crontabとの違いや時差を考慮する必要があるため注意する 2ルール:「新規ルールの作成」 ルール名:DailyTrigger ルールタイプ:スケジュール式 スケジュール式:cron(0 15 * * ? *) # 毎日0:00に実行するcron2-4.S3バケットへの書き出し・読み込み
前日の収益との比較を行うため、S3バケットのファイルに対して書き出し・読み込みを行う
・S3バケットbucket_name
を作成して、前日の残高(円)を整数で記載したダミーファイルbalance_filename.csv
を予め格納しておくbucket_name/balance_filename.csv28583・LambdaとS3のサービス間で連携するために、
lambda_function.py
の対象箇所を編集するlambda_function.py#NiceHash API #S3 bucket bucket_name = '[bucket_name]' # S3バケット名 key_name = '[balance_filename.csv]' # 残高情報が記載されたファイル名2-5.Amazon SNSによるメール通知
取得した収益情報をメール通知するために、「SNSとメール」「LambdaとSNS」の連携を行う
・Amazon SNSを起動し、適当にNiceHashSNS
としてトピックを作成する
・作成したトピックNiceHashSNS
のARNをコピーする
・「SNSとメール」を連携するために、作成したトピックNiceHashSNS
を開き、「サブスクリプションの作成」をクリック
・プロトコル:Eメール
、エンドポイント:通知したいメールアドレス
を指定し、「サブスクリプションの作成」をクリック
・サブスクリプションを作成すると、指定したメールアドレスに通知メールが届くので、挿入されたリンクへアクセスしサブスクリプションのアクティベーションを行う
・「LambdaとSNS」を連携するために、lambda_function.py
の対象箇所を編集するlambda_function.py#Amazon SNS TOPIC_ARN = 'arn:aws:sns:xx-xxxx-x:xxxxxxxxxx:NiceHashSNS' # コピーしたSNSのARNを記載3. 実行結果
・毎日0:00になるとEventBridgeがLambdaをキックして、日次の通知メールが来るようになりました。
4. 終わりに
・APIで情報取得している割に、通知はメールというのは構成的にビミョかったので、この後にLINE APIを活用してLINE通知する構成に変更しました。次回は、LINE通知する構成について投稿予定です。
5. 更新履歴
ver. 1.0 初版投稿 2021/02/28
- 投稿日:2021-02-28T18:36:04+09:00
Golangはじめて物語(第8話: DynamoDBのデータモデリングのベストプラクティスに従った場合の実装方法)
はじめに
最近、DynamoDB のデータモデリングを勉強しているが、これに従ってみようとすると、あれ?なんか Golang での実装が難しくないか?と思って調べてみた。
※Python だったらディクショナリ型を使えばお手軽に扱える気がするのだけど。データモデリングのベストプラクティスについては、以下の記事を参考にすると良い。
何が難しいか?
いずれの記事でも、同じハッシュキーにできるものは1つのテーブルに寄せて、属性が異なるものはソートキーで種別を分ける、と書かれている。
例えば、あるハッシュキーに紐づく情報をqueryで一発で引くと、以下のようにpersonal
とcompany
といった異なる属性を持ったアイテムが取得されることになる。{ "Items": [ { "department": { "S": "sales" }, "grade": { "S": "A" }, "id": { "S": "00001" }, "type": { "S": "company" } }, { "birthdt": { "S": "19800101" }, "postalcode": { "S": "1740001" }, "id": { "S": "00001" }, "telno": { "S": "09000000000" }, "name": { "S": "Taro" }, "type": { "S": "personal" } } ], "Count": 2, "ScannedCount": 2, "ConsumedCapacity": null }これを Golang で実装しようとした場合、もちろん
GetItem()
を2回呼ぶという方法もあるが、それだとコードが冗長になってしまうケースがある。Query()
で一発で取得しようとしたらどうしたら良いだろうか?考えてみる
上記のJSONを、JSON-to-Go に食わせてみると、以下のように出力される。
type AutoGenerated struct { Items []struct { Department struct { S string `json:"S"` } `json:"department,omitempty"` Grade struct { S string `json:"S"` } `json:"grade,omitempty"` ID struct { S string `json:"S"` } `json:"id"` Type struct { S string `json:"S"` } `json:"type"` Birthdt struct { S string `json:"S"` } `json:"birthdt,omitempty"` Postalcode struct { S string `json:"S"` } `json:"postalcode,omitempty"` Telno struct { S string `json:"S"` } `json:"telno,omitempty"` Name struct { S string `json:"S"` } `json:"name,omitempty"` } `json:"Items"` Count int `json:"Count"` ScannedCount int `json:"ScannedCount"` ConsumedCapacity interface{} `json:"ConsumedCapacity"` }つまり、両方の属性を持った構造体を定義して、取得できなかったものは
omitempty
すれば良いようだ。
※omitempty
は、Unmarshal()
時に該当の属性がJSONに入っていなかった場合に、空値にしてくれる。上記は、あくまでもJSONへのマッピングなので、
DynamoDBAttributeValue
からUnmarshal()
するには、以下のように定義する。type item struct { Id string `dynamodbav:"id"` Type string `dynamodbav:"type"` Name string `dynamodbav:"name,omitempty"` BirthDt string `dynamodbav:"birthdt,omitempty"` TelNo string `dynamodbav:"telno,omitempty"` PostalCode string `dynamodbav:"postalcode,omitempty"` Department string `dynamodbav:"department,omitempty"` Grade string `dynamodbav:"grade,omitempty"` }ただし、
Query()
で複数レコード帰ってくるときは配列なので、さらに以下のように配列化するための構造体を作る。type items struct { Item []item }この構造体を活用して、以下のように
Query()
の結果をUnmarshal()
しよう。var items items result, err := ddb.Query(&dynamodb.QueryInput{ TableName: aws.String("table-name"), KeyConditionExpression: aws.String("#id = :id"), ExpressionAttributeNames: map[string]*string{ "#id": aws.String("id"), }, ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{ ":id": { S: aws.String(string(id)), }, }, }) err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &items.Item)これで、無事、
item
の配列にマッピングできた状態になる。さて、これであとは煮るなり焼くなり好きにすれば良いが、せっかくなので、2種類のレコードをお手軽に取得できるようにしてみよう。
※単純に JSON を返せば良いのであれば、さらにここから、別の JSON 定義でmarshal()
すれば良いが、今回は別の構造体にマッピングしてみる。type personal struct { Id string Name string BirthDt string TelNo string PostalCode string } type company struct { Id string Department string Grade string } type itemsInterface interface { getPersonal() personal getCompany() company } func (items items) getPersonal() personal { for _, item := range items.Item { if item.Type == "personal" { return personal{ Id: item.Id, Name: item.Name, BirthDt: item.BirthDt, TelNo: item.TelNo, PostalCode: item.PostalCode, } } } return personal{} } func (items items) getCompany() company { for _, item := range items.Item { if item.Type == "company" { return company{ Id: item.Id, Department: item.Department, Grade: item.Grade, } } } return company{} }あとは、
Unmarshal()
後に以下のように呼び出せばよい。personal := items.getPersonal() company := items.getCompany()これでもまだ冗長な気がするが、それでも単に
GetItem()
するよりは、スマートに作れている……かな……?
- 投稿日:2021-02-28T18:35:24+09:00
AWS認定 SysOps Administrator Associateで不合格になった話
はじめに
皆さん、こんにちは!!
ハンズラボのサムです!!
先日AWS認定 セキュリティ専門知識を合格したので、勢いに乗ってアソシエイトの中では簡単と言われるSysOps Administrator Associateを受けてきました!因みに今のバッジはこんな感じで、正直SAAもってるから余裕だと思ってました。
目的
AWS資格12冠ですが、ひとまずアソシエイト3冠しようというスタイルです!
スケジュール&試験対策
試験対策期間:3日間ぐらい
いつもどおり
* Udemyの模擬試験
* AWSの公式模試
で対策をやりました。実際のテストの雰囲気など
難易度としては専門知識とかと比べて問題の長さは短くSAAと同程度でしたが、SAAより若干掘り下げた問題が多かったように感じます。
それと、問題に登場するサービスが若干古かったので試験自体そろそろ更新なのかな?といった感じでした。問題量に対して試験時間は長いぐらいで見直ししても50分程度余ったので、もうちょっとゆっくり解いてもよかったかな〜?という印象でした。
そして、分からない問題よりわかる問題が多かったので受かっただろうと思ってました。結果
はい。表題の通り落ちました!!!!
誰だよ、SysOpsは簡単だって書いた人!!
点数は677点なので多分2-3問足りなかった感じ。敗因として
* CloudFormationの問題がわからないの多かった・・・
* 完全に慢心していた
以上!!!!はい、ちゃんと再度対策してサービス調べて勉強し直してきます!!!!
2週間後には再試験できるようになるのでリベンジしてきます。
皆様もネットに簡単と書いてあっても鵜呑みにしないように!!
満身は最大の敵です!!
- 投稿日:2021-02-28T17:40:37+09:00
AWS 認定クラウドプラクティショナー (CLF) 取得メモ
AWS 認定クラウドプラクティショナー(CLF)取得のためのメモ。
あくまで自分が取得するためのメモ。ポイントメモ
概念
スケールアップ:単体性能を向上
スケールアウト:台数を増加
- AWS Well-Architectedフレームワーク
- 運用上の優秀性、セキュリティ、信頼性、パフォーマンス効率、コスト最適化
セキュリティ系
- AWS Shield : DDOS保護サービス
- AWS WAF : マネージド型Webアプリケーションファイアウォール
- セキュリティルールは自分で定義する必要がある
- 適用サービスはCloudFront/ALB/API Gatewayから選択できる
- Amazon Inspector : 脆弱性診断を自動で行うことができるサービス
- Amazon GuardDuty : AWS インフラストラクチャとリソースに対してインテリジェントな脅威検出を提供するサービスです。AWS 環境内のネットワークアクティビティとアカウント動作を継続的にモニタリングし、脅威を特定します。
- AWS Artifact は、AWS のセキュリティおよびコンプライアンスレポートのオンデマンドでの利用と、特定のオンライン契約を提供するサービスです。
AWS テクノロジー
- リージョンにはアベイラビリティゾーンが2つ以上ある
- 同一リージョンのAZ同士は高速なプライベートネットワークで接続されている
- リージョンとは異なる場所に200以上のエッジロケーションがある
- エッジロケーションではRoute 53とCloudFrontを利用できる (AWS Shieldも)
コンピューティングサービス
- インスタンスタイプ : ファミリー/世代/サイズ t2.mircro
- ELBのタイプ
- Network Load Balancer : HTTP/HTTPSで利用する場合こちらを使う
- Classic Load Balancer : HTTP/HTTPSのプロトコル以外 Auto Scaling
- 垂直スケーリング : インスタンスタイプを変更
- 水平スケーリング : =AutoScaling 同一インスタンスのサーバを増加
- Auto Scaling ポリシー : 何を(起動設定)、どこで(Auto Scalingグループ)、いつ(いつのタイミングで起動/終了するか)
- EC2の料金について
- スポットインスタンス : スポットインスタンスとは、AWSサーバ上に存在し、使われていないEC2インスタンスに値段をつけ、その入札価格が現在のスポット価格(※長期ではなく、1回ごとの取引で決定され成立した市場価格)を上回っている限り、そのインスタンスを利用することができるというものです。中断の可能性があるので長時間処理などには向かない。
- 同一リージョン内であれば、他アカウントでもデータ転送料金は発生しない
ストレージ
- EBS
- 汎用SSD : 最大16,000IOPS
- プロビジョンドIOPS SSD : 最大64,000
- スループット最適化HDD, Cold HDD : コスト節約
- アベイラビリティゾーン内で自動的にレプリケートされる
- 使い始めた後にオンラインでボリュームタイプの変更が可能
- 1つのアベイラビリティゾーン
- EFS
- 完全マネージド型の NFS ファイルシステムを
- 複数のアベイラビリティゾーン
- S3
- S3 Glacier ストレージクラスに保存されているオブジェクトは、数分から数時間以内に取得できます。
- S3 Glacier Deep Archive ストレージクラスに保存されているオブジェクトは、12 時間以内に取得できます。
ネットワーク
- VPC
- リージョンを選択して作成。
- CIDRでVPCのプライベートIPアドレスの範囲を定義
- サブネット
- サブネットはアベイラビリティゾーンを選択して作成
- CIDRでサブネットのプライベートIPアドレスの範囲を定義
- セキュリティグループ
- インスタンスに対してのトラフィックを制御する仮想ファイアウォール
- 許可するインバウンドのポートと送信元を設定するホワイトリスト
- 送信元にはCIDRか他のセキュリティグループIDを指定できる
- セキュリティグループはステートフルであり、デフォルトですべてのインバウンドトラフィックを拒否する
- ネットワークACL
- サブネットに対してのトラフィックを制御する仮想ファイアウォール
- 拒否するインバウンドのポートと送信元を設定するブラックリスト
- 必要がなければ設定しない
- CloudFront
- エッジロケーションを使用するCDN
- Route 53
- シンプルルーティング/レイテンシベースのルーティング/場所に基づくルーティング
データベース
- RDS
- データベースのバックアップを管理しなくて良い
- バックアップ期間中の任意の特定時間のインスタンスを起動できる
- DMS
- データベース間でデータを移行できるサービス
- オンプレからの継続的なデータ移行を行いシステムのダウンタイムを最小限にできる
管理サービス
- Trusted Advisor
- コスト最適化、パフォーマンス、セキュリティ、フォールトトレランス、サービス制限をチェック
請求と料金
- ECO計算ツール : AWS導入検討の際のコスト比較
- AWS Budgets : では、サービス使用量が予算の量を超えた場合 (または超えることが予想される場合) に通知するカスタムアラートを設定できます。
AWS Cloud Practitioner Essentials (Japanese) で学習したメモ
- インスタンスストア : Amazon EC2 インスタンスを停止または削除すると、アタッチされたインスタンスストアに書き込まれたデータはすべて削除されます。
- Amazon Quantum Ledger Database (Amazon QLDB)…は台帳データベースサービスです。Amazon QLDB では、アプリケーションのデータに対して行われたすべての変更を包括的に確認できます。
- Amazon Textract は、電子化したドキュメントからテキストとデータを自動抽出する機械学習サービスです。
- Amazon Lex は、音声とテキストを使用して会話型インターフェイスを構築できるサービスです。
- Amazon Augmented AI (Amazon A2I)…コンテンツモデレーションやドキュメントからのテキスト抽出など一般的な機械学習のユースケース向けの、人によるレビューワークフローをビルトインで提供しています。
- AWS Cloud Adoption Framework
- プラットフォームパースペクティブ : 新しいソリューションを導入したり、オンプレミスのワークロードをクラウドに移行したりするための原則も含まれています。
- オペレーションパースペクティブ : ビジネスの関係者の要件を満たすことができるように IT ワークロードの運用と復旧に焦点を合わせます。
- 移行戦略
- リホスト
- リプラットフォーム
- リファクタリング/アーキテクチャの再設計
- 再購入 (Repurchase)
- 保持 (Retain) …ソース環境でビジネスに重要なアプリケーションを維持することです。
- リタイア
- Well-Architected フレームワーク
- 運用上の優秀性 : 運用上の優秀性は、システムを運用し監視してビジネス価値をもたらし、付随するプロセスと手順を継続的に向上させることができる能力です。
- セキュリティ : リスクの評価と軽減の戦略によってビジネス価値を生み出しながら、情報、システム、アセットを保護する能力です。
- パフォーマンス効率 : コンピューティングリソースを効率よく使用し、システム要件を満たし、需要の変更や技術の進化に応じて効率性を維持する能力を示します。
- 信頼性
- コスト最適化
AWS 認定クラウドプラクティショナー 模擬試験問題集復習用
- AWS Config はAWS リソースの設定を評価、監査、審査できるサービス。
- EC2 Dedicated hostはオンデマンドインスタンスを物理的に占有する場合に選択するオプション設定です。
- EFS : EC2インスタンスからLAN上にあるNASとして利用できる共有ファイルストレージとして提供されています。複数のEC2インスタンスから接続・共有可能なストレージとして機能する。
- Amazon Auroraの DB インスタンスでは常に自動バックアップが有効。
- Amazon S3 Transfer Acceleration : エッジロケーションを利用してクライアントと S3 バケットの間で、長距離にわたるファイル転送を高速、簡単、安全に行えるようになります。
- Amazon EMR は、Apache Hadoop や Apache Spark などのビッグデータフレームワークとして、大量のデータを処理および分析するマネージド型クラスタープラットフォームです。
- AWS CloudTrail は、AWS アカウントのガバナンス、コンプライアンス、運用監査、リスク監査を行うためのサービスです。
- 初めからマルチAZ構成のフォールトトレランスを考慮して設計されているAWSサービス → Dynamo DB/S3
- EC2のAWS無料利用枠 → 12ヶ月
- AWS Storage Gateway → 実質的に無制限なクラウドストレージへの、オンプレミスでのアクセス
- AWS OpsWorks Chef や Puppet のマネージド型インスタンスを利用できるようになる構成管理サービスです。
- AWS 料金計算ツールは、AWSユーザーが月間AWS請求書をより効率的に見積もるのに役立ちます。
- AWS Global Accelerator : ローカルまたは世界中のユーザーに提供するアプリケーションの可用性とパフォーマンスを改善します。
- AWS CodeCommit : フルマネージド型のソース管理サービスであり、企業が安全で拡張性の高いプライベートGitリポジトリを簡単にホストできるようにします。
- AWS Security Token Service (AWS STS) : AWS のサービスへのアクセスに使用できる一時的な限定権限認証情報を取得できます。
- Amazon Elastic Container Registry (ECR) : 開発者がDockerコンテナイメージをAWSクラウドに保存・管理することができます。
- AWSで提供されているデータベースサービスのうち、RDS、Redshift、ElastiCache Redisには自動バックアップ(自動スナップショット)の機能がある。
学習方法
- AWS ホワイトペーパー
- AWS トレーニング
- AWS Black Belt Online Seminar
- AWS クラウドサービス活用資料集トップ
- 10分間チュートリアル
- 投稿日:2021-02-28T17:22:36+09:00
【AWS】S3で静的WEBサイトホスティングを使ってWEBページを公開する
はじめに
S3とはSimple Storage Serviceの略で、その名の通りファイルをアップロードして保存することができるストレージサービスです。
しかしS3は単なるストレージサービスではなく、HTMLファイルをアップロードして簡単なWEBページを作成することができます。簡単な用語の説明
・バケット……オブジェクトを格納するためのコンテナのこと。いわゆる頂点となるフォルダ。
・オブジェクト……S3バケットに格納されるデータのこと。ファイルなど。用意するもの
次のHTMLファイル
index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> This is Test Page! </body> </html>手順
S3のコンソールを開きます。
バケットを作成
をクリックします。
バケット名はグローバルに一意である必要があります。つまり他のどんなS3バケットと名前が被ってはいけません。
リージョンはアジアパシフィック(東京)
を選択します。
今回は外部に向けてWEBページを公開したいので「パブリックアクセスのブロック」はオフにします。
他の設定はデフォルトのままでバケットを作成します。
バケットが作成されたのでバケットを開きます。
オブジェクトをアップロードします。
ファイルを追加
で用意したindex.htmlをアップロードします。
アップロードが成功しました。
バケットのプロパティを開きます。
下へスクロールして静的ウェブサイトホスティングを編集します。
静的ウェブサイトホスティングを有効にします。
インデックスドキュメントは「index.html」とします。
変更の保存
をクリックします
これだけではまだブラウザから目的のページにアクセスできません。
試しにオブジェクトのURLからアクセスしてみます。
このようなエラーが返されます。これはS3バケットの権限が足りていないため生じるエラーです。
バケットのアクセス許可
を開きます。
下へスクロールし、バケットポリシーを編集します。
画像のようにポリシーを記述します。
このJSONテキストは公式ドキュメントから参照可能です。
xxxxxxxx
の部分がバケット名です。{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::xxxxxxxx/*" ] } ] }各項目を簡単に見て行きましょう。
Version
は「ポリシーを処理するために使用される言語構文ルール」を指定します。2012-10-17
が現行バージョンです。
Statement
はポリシーの主要な要素です。
Sid
はステートメントIDの略で、ポリシーの任意の識別子です。
Effect
はステートメントの結果を許可するか拒否するかを指定します。許可する場合はAllow
、拒否する場合はDeny
です。
Principal
は「リソースへのアクセスを許可または拒否するユーザー、アカウント、サービス」などを指定します。*
はワイルドカードで、誰でもアクセスできることを意味します。
Action
は対象(リソース)に対して許可または拒否したいアクションを指定します。GetObject
はS3からオブジェクトを取得できるアクションです。その他アクションはこちら(英語版公式ドキュメント)。
Resource
はバケットやオブジェクトなどのARN(Amazon Resource Name)を指定します。バケットのARNの後ろに/*
と追記してあげることでバケットに格納されているオブジェクトすべてを指定しているわけです。さて、WEBページを公開する準備は整いました。
オブジェクトURLからページを開いてみましょう。
無事に表示されました。参考
この記事はAWS初学者を導く体系的な動画学習サービス
「AWS CloudTech」の課題カリキュラムで作成しました。
https://aws-cloud-tech.com
- 投稿日:2021-02-28T16:58:59+09:00
試しにAWSのEC2にwordpressを設置してみた
前提
データベースが必要であり、そのデータベースを設置するために2つのAZが必要になります。ここではap-northeast-1a,1cを使用します。データベースはap-northeast-1aに設置します。以下の構造を作ります。
注意
あくまで試しに作る環境なので、環境を作って動作を確認したら課金が発生する前に環境を削除したほうがいいです。
ap-northeast-1a(AZ)
PublicSubnet1 10.0.0.0/24
PrivateSubnet1 10.0.2.0/24 ここにデータベースを設置しますap-northeast-1c(AZ)
PublicSubnet2 10.0.1.0/24
PrivateSubnet2 10.0.3.0/24 ここにデータベースを設置します手順
1)以下のようにVPCを作成する。
Name: MyVPC
IPv4 CIDR: 10.0.0.0/21 このネットワークを手順2)で分割します2)作成したVPC内に下記のサブネットを作る。
PublicSubnet1 10.0.0.0/24
PublicSubnet2 10.0.1.0/24
PrivateSubnet1 10.0.2.0/24
PrivateSubnet2 10.0.3.0/243)EC2を作成する。
OS: AmazonLinux2
インスタンスタイプ: t2micro
ネットワーク: MyVPC1
サブネット: PublicSubnet1
自動割り当てパブリックIP: 有効
ストレージボリューム: とりあえず8GB(デフォルト)
タグ付け(任意)
セキュリティグループ設定でhttp通信を許可する。必要であれば、httpsも許可する。因みにサーバーにping応答させたければ、ICMPを許可してください。
キーペア: 既存のキーペアがあれば、それを使い、まだキーペアがなければ、キーペアを作成してください。4)インターネットGWを作成し、VPCへアタッチする。
5)PublicSubnet1のルートテーブルに以下を追加してください。
ルート: 0.0.0.0/0
ターゲット: 手順4)で作成したGW6)データベースのサブネットグループを以下のように作成してください。ここで以降の手順で作成するデータベースをどのAZのどのVPCのどのサブネットに割り当てるかを決定します。
名前: MysubnetGroup
説明: MysubnetGroup
VPC: MyVPC1
AZ: ap-northeast-1a,1c(前提として2つのAZが必要です。)
サブネット: PrivateSubnet1,27)以下のようにデータベースを作成してください。
データベース作成方法を選択: 標準作成
エンジンのオプション: MySQL
テンプレート: 開発/テスト
マスターユーザー名: wordpress
パスワード: 任意のパスワード
DBインスタンスサイズ: バースト可能クラス
可用性と耐久性: 「スタンバイインスタンスを作成しないでください」を選択する
接続: MyVPC1
追加の接続設定
サブネットグループ: MysubnetGroup
パブリックアクセス可能: なし(「あり」にするのは非常に危険です)
VPCセキュリティグループ: 新規作成
新しいVPCセキュリティグループ名: RDS-SG-1
AZ: ap-northeast-1a
追加設定
最初のデータベース名: wordpress他の設定項目はデフォルトで大丈夫です。
8)RDSセキュリティグループのインバウンドルールで接続元をwebサーバーからのみに限定してください。
もともと設定されている接続元のIPアドレスを消去する
「ソース」の検索窓でsgと入力する
Web-SG-1という名前のセキュリティグループを選択する
「保存」を押す9)作成したEC2環境にターミナルでログインし、apacheとphpをインストールしてください。
管理者権限にスイッチする
sudo su -
パッケージを最新状態にアップデートする
yum -y update
phpをインストールする
amazon-linux-extras install php7.2 -y
apacheとphpに必要なものをインストールする
yum -y install mysql httpd php-mbstring php-xml gd php-gd
apacheがサーバー起動後に自動で起動するように設定する
systemctl enable httpd.service
apacheを起動する
systemctl start httpd.service
apacheのapacheが起動しているかを確認する
systemctl status httpd.service
wordpressの最新パッケージをカレントディレクトリにダウンロードする
wget http://ja.wordpress.org/latest-ja.tar.gz ~/
カレントディレクトリにlatest-ja.tar.gzがあるかを確認する
ll
latest-ja.tar.gzを展開する
tar zxvf ~/latest-ja.tar.gz
wordpressというディレクトリが存在していることを確認する
ll
wordpressを/var/www/htmlにコピーする
cp -r ~/wordpress/* /var/www/html/
/var/www/html/配下のフォルダの所有者をapacheに変更する
chown apache:apache -R /var/www/html
以上でwordpressの閲覧が可能になります。
最後に
この記事はAWS初学者を導く体系的な動画学習サービス
「AWS CloudTech」の課題カリキュラムで作成しました。
https://aws-cloud-tech.com
- 投稿日:2021-02-28T16:32:55+09:00
AWS Lambda + API Gateway POSTのパラメータ取得でハマった
フロントエンドからPOST通信でアクセスするAPIをLambda+APIGatewayで作っていた時にハマった話を備忘録
申し込みフォームで登録後に完了メール送信を行うロジックを作っていた。
const onSubmit = async () => { const params = { to: email, name: name, email: email, tel: tel, zipcode: zipcode, address: prefecture + " " + address, message: message, }; const res = await axios.post( "https://sample/send", params, { headers: { "Content-Type": "multipart/form-data; charset=UTF-8" }, } ); }Lamda側でeventの中身をのぞいてみると。。。
リクエストbodyにJSON形式で渡されると思いきや、エンコードされた状態でパラメータが渡っていたため
Lambda側でどのようにパラメータ取得を行えばよいかわからず調べること1時間...{ 'body': 'eyJ0byI6InNhbXBsZUBnbWFpLmNvbSIsIm5hbWUiOiLlkI3liY0iLCJlbWFpbCI6InNhbXBsZUBnbWFpLmNvbSIsInRlbCI6Ijk5OTk5OTk5OTk5IiwiemlwY29kZSI6IjIxMDAwMDgiLCJhZGRyZXNzIjoi5p2x5Lqs6YO9IOS9j+aJgDHkuIHnm64iLCJtZXNzYWdlIjoi55Sz44GX6YCB44KK5LqL6aCFIn0=', 'isBase64Encoded': True }base64モジュールによりデコード処理を行うことで解決できることがわかった。
def send_mail(event, context): body = event["body"] #bodyのエンコードされた文字列をJSON文字列にデコードし、辞書型に変換 params = json.loads(base64.b64decode(body).decode('utf-8')) TO = params["to"] name = params["name"] email = params["email"] tel = params["tel"] zipcode = params["zipcode"] address = params["address"] message = params["message"] ##以下割愛HTTPの仕組みは奥が深い....
POSTするさいのHTTPヘッダーのContent-Typeも重要であることが分かった。
何がどう異なるのかは調べなければ...
- 投稿日:2021-02-28T15:48:46+09:00
AWS BatchでJavaのコンテナをLambda経由で起動する時にシステム環境変数を渡す
この記事の対象
Spring Boot
EC2コンテナ
AWS Batch
AWS Lambda (node.js 14)概要
Spring Bootで作成したバッチアプリケーションをAWS Batchに登録。
ジョブをLambda関数経由で呼び出す際にコンテナ起動パラメータを渡す方法です。なんでそんなことを?
Batch用に作成したJavaコードに、共通処理が多いため、複数のジョブ起動が実装されており、システムプロパティ(@ConditionalOnPropertyアノテーション)を利用して、実行時に起動するジョブを選択できるようにしました。
その際に、Lambda関数側でJavaのシステムプロパティを渡す方法です。
詳細
サンプルはAWS Lambda(node.js 14)です。
sample.jsconst AWS = require('aws-sdk'); module.exports = async function (param) { var batch = new AWS.Batch({ httpOptions: { timeout: 1200000 } }); try { const result = await batch.submitJob({ jobName: 【AWSバッチのジョブ定義名】, jobDefinition: 【AWSバッチのARN】, jobQueue: 【ジョブキューのARN】, containerOverrides: { environment: [ // オーバーライドするJavaシステムプロパティを設定 {name: 'JAVA_OPTS', value: '-Dbatch.execute=ExecuteJob001 -Dfile.encoding=UTF-8'} ] } }).promise() console.log(result) } catch (err) { console.log("submitJob error: " + err) return { statusCode: 500 } } };肝は、containerOverridesパラメータです。
コンテナ起動時の環境変数をオーバーライドすることが出来ます。
このパラメータにJAVA_OPTS環境変数を定義する事で、起動するバッチアプリを選択できるようになります。上記の例は、Javaコードで
@ConditionalOnProperty(value = {"batch.execute"}, havingValue = "ExecuteJob001")と定義したアプリを起動する場合の引数指定方法になります。
最後に
他にも色々やり方はあるんですが、コンテナイメージを生成する際に
mvn spring-boot:build-imageを利用して安易にコンテナイメージを生成しているため、こういった方法を取りました。
- 投稿日:2021-02-28T15:32:16+09:00
AWSではじめてhttps通信&CDNをしてみた。超ざっくりまとめてみた。
AWSでの証明書発行およびクライアントからの通信の暗号化(https)とCludFrontを利用したCDNの実現をしました。
実現したものは、こんなイメージです。
細かい作業過程は割愛しますが環境構築で理解した基本構成の概要を自分の理解を深めるために、超ざっくりと基本要素をまとめます。
これを見れば、何の目的のために何を作るのか、一目で思い出せるはず!
また、この記事が、これからAWSの基礎学習を始める方の理解の助けとなり、AWSを触るハードルを下げる助けとなれば幸いです。私も初心者です。みなさん頑張りましょう!
- 作図には AWS 公式アイコンセットを利用してます
- 全般的に概念的な説明のみとしています。
- 詳細な設定/オペレーションを知りたい方は、他サイトをご覧ください。
- この記事やクラスメソッドさんの記事に画面キャプチャ付きで細かく説明されてました!
超ざっくりまとめ
超ざっくりと説明すと、以下を行うだけです。
1. ELBでリスナー(https)登録(&SSL証明書の設定)
2. バージニアリージョンで、AWS Certificate Manager で証明書を作成
3. CloudFrontでDistributionの作成(※)
4. Route53でホストゾーンにAレコード登録(CloudFront, ELBのへのルーティング)
※Origin Domain Name(キャッシュ元情報)・証明書の登録、キャッシュに関する詳細設定を行うこれだけで、さくっと作れてしまいます。
CloudFrontの詳細設定方法は、この記事を参照ください。
おまけ
Amazon CloudFrontのキャッシュ機構を利用することで、グローバルなアクセスが必要なケースで、クライアントからの距離を近くし、レスポンスの向上を見込めます。
今回の記事では触れませんでしたが、AWS Global Accelerator を利⽤することによる⾮キャッシュコンテンツの⾼速化も図れ、ユースケースがいろいろと考えられる機能だと思います。(日本とネットワーク回線が細い海外拠点とのファイル共有など)
※上記は、『AWS のネットワークで知っておくべき10のこと』に分かりやすく説明されています
こちらは、別途勉強しておこうかと思います。
さいごに
この記事はAWS初学者を導く体系的な動画学習サービス「AWS CloudTech」の課題カリキュラムで作成しました。
このサービスは、テンポの良い/わかりやすい動画説明をもとに、気軽に実践を積み、自分の血肉とできるオンラインスクールです。
コミュニティも存在し、Slackで会員通しの情報交換/質問も気楽にできます。
書籍を購入するような値段で学習ができ、とてもお得です。(個人的な感想です)
では、また次回お会いしましょう!
- 投稿日:2021-02-28T15:21:01+09:00
AWS EC2 AmazonLinux2 とのファイルダウンロード・アップロード
よく忘れるのでメモ
前提
SSHでEC2に接続出来ている。EC2のホームディレクトリからファイルダウンロード
scp -i 秘密鍵の場所.pem ec2-user@xxx.xxx.xxx.xxx:/home/ec2-user/test.txt ./EC2のホームディレクトリへファイルアップロード
scp -i 秘密鍵の場所.pem test.txt ec2-user@xxx.xxx.xxx.xxx:/home/ec2-user/ディレクトリ毎操作するには -r を付ける
scp -r -i 秘密鍵の場所.pem ~以下省略~
- 投稿日:2021-02-28T13:40:32+09:00
AWS SSMでプライベートサブネットのAmazon EC2(Windows OS)へリモートアクセスする
AWSの本番環境でプライベートサブネットにEC2インスタンスにRDPしたいという場面は結構あると思います。社内環境で利用するサーバなので、インターネットからアクセスできる状態はよろしくないので、プライベートサブネットで構築しているというパターンです。メンテナンスなどでサーバにログインしたいときによくあるパターンは踏み台サーバを別パブリックサブネットに構築して、踏み台経由でのアクセスというケースですが、そもそも踏み台サーバ分の料金が...とか、踏み台サーバの管理って誰がするの...とかの悩みが尽きないと思います。そんなときに便利なAWS System Manager(SSM)を使ったリモートデスクトップ接続をお試ししたいと思います。
今回のゴール
こんな感じで、プライベートサブネットにデプロイしたEC2にSSM経由でリモートデスクトップ接続できることが目標です。
踏み台サーバなしで、直接PCからプライベートサブネットのEC2にアクセスできますので、シンプルかつ踏み台サーバない分料金がお安く済みます。前提条件
- EC2はWindows OSで構築する
- セキュリティグループはユーザPCからRDPできるように必要なポートを解放しておく
- ユーザPCにはAWS CLI をインストールしておく
- 利用するリージョンは東京
構築手順
①プライベートサブネットへEC2のデプロイ
②EC2をデプロイしたVPCにSSMと接続できるエンドポイントを構築
③EC2がSSM と接続できるようにロールを作成、適用し、SSM コンソール画面のマネージドインスタンスに該当EC2が表示されるようにする
④AWS CLIを利用するIAM ユーザを作成し、IDとシークレットキーを発行して、SSM用のロールをアタッチする
⑤ユーザPCからAWS CLI でコマンド実行し、SSM経由でセッションを張る
⑥Windows のリモートデスクトップ接続でEC2へ接続やってみる
①についてはいつものように割愛します。とりあえずWindows OSのEC2をデプロイできればOKです。
②ですがVPCのコンソールで[エンドポイント]から作成できます。
以下のように[エンドポイントの作成]を選択して、[サービスカテゴリ]から[AWSサービス]を選択し"com.amazonaws.ap-northeast-1.ec2messages"、"com.amazonaws.ap-northeast-1.ec2messages"、"com.amazonaws.ap-northeast-1.ssm"の3つのサービスを選択して、今回利用するVPCにエンドポイントを作成します。
作成後、しばらくしてステータスが「利用可能」になればOKです。
続いて③ですがEC2用にまずロールを作成します。必要なポリシーは"AmazonSSMManagedInstanceCore"です。
とりあえずこのポリシーがアタッチされたロールを作成し、以下のようにEC2へアタッチします。
ポリシーアタッチ後、数分後にSSM コンソール画面の[マネージドインスタンス]にアクセスし、該当EC2の[Pingの状態]が以下のようにオンラインになっていることを確認します。
オンラインになっていない場合は下記原因が考えられます。【想定される原因】
- VPCのDNS設定(DHCPオプション)がおかしい
- ②で作成したSSM向けのエンドポイントがおかしい
- EC2用のIAMロールが正しくアタッチされていないか、ロールのポリシーが正しくない
- デプロイしたEC2 でSSMエージェントが起動していない④ですがIAMユーザを作成し、CSVファイルなどでusercredential(ID/secret key)をDLして保存しておいてください。作成したIAMユーザに対して、SSMへ接続できるようにロール作成し、アタッチしていきます。
とりあえず今回ポリシーは"SSMFullAccess"にしておきます。
※FullAccessまではなくてもいいのですけどね...
今回はSSMでリモートデスクトップ接続のセッションを張るだけでなく、AWS CLI でEC2の起動もやっていたので、"AmazonEC2FullAccess"もアタッチされていますが、起動しているならこちらはなくてもOKです。⑤ですがAWS CLI がインストールされたPCからコマンド入力して、セッションを張っていきます。
AWS CLI のインストールはAWS 公式インストールガイドからインストールしておいてください。
AWS CLI をインストールすると[C:\Users\”ユーザ".aws] にフォルダが作成され、その中の「credentials」というファイルにAWS CLIを動作させるIAM ユーザの情報が書き込まれています。# credentials の中身 [default] aws_access_key_id = XXXX aws_secret_access_key = XXXXXこのcredentials のidとkey のところを④でDLしておいてたIAMユーザのものに書き換えておきます。
ちなみに同じフォルダにconfig というファイルも作成され、こちらには動作させる環境が記述されています。
# configの中身 [default] region = ap-northeast-1 output = json準備ができたらAWS CLIが正しくインストールされているかPCでコマンドプロンプトを立ち上げて以下コマンドで確認します。
#AWS CLIが正しくインストールされているか確認 aws --version出力に以下のように表示されていれば、正しくインストールされています。
aws-cli/2.1.26 Python/3.7.9 Windows/10 exe/AMD64 prompt/off続いてAWS CLI の設定が正しくされているを確認します。以下コマンドを入力します。
aws configure以下のように表示されていれば設定OKです。
AWS Access Key ID [****************GR4Y]: -->④で作成したIAMユーザのID AWS Secret Access Key [****************abKl]: -->④で作成したIAMユーザのsecret key Default region name [ap-northeast-1]: Default output format [json]:続いてEC2が起動していなければAWS CLIからEC2を起動します。
aws ec2 start-instances --instance-ids "インスタンスID"以下のように表示されてば正常に起動しています。
{ "StartingInstances": [ { "CurrentState": { "Code": 0, "Name": "pending" }, "InstanceId": "インスタンスID", "PreviousState": { "Code": 80, "Name": "stopped" } } ] }では起動したEC2に対して、SSMでセッションを張っていきます。
aws ssm start-session --target インスタンスID --document-name AWS-StartPortForwardingSession --parameters "portNumber=3389, localPortNumber=13389"以下のように表示されればセッションは正常に構築されています。
Starting session with SessionId: SSM_access-xxxxxxxxxxxxx Port 13389 opened for sessionId SSM_access-xxxxxxxxxx Waiting for connections...アクセス先は[localhost:13389]にして、ユーザ名とPWを入力します。
これでリモートデスクトップ接続できればOKです。リモートデスクトップ接続を終了するときにはコマンドプロンプトで[Ctrl]+[c]でコマンドを終了させましょう。リモートデスクトップ接続中に[Ctrl]+[c]してしまうとセッションが切れるため、リモートデスクトップ接続も切れてしまいます。
おまけ
マネージドインスタンスに表示されること= SSMでアクセスできる ということになります。
"マネージドインスタンスに表示される"状態にするために、ロールを作ったり、ポリシーをアタッチしたりしましたが実はもっと簡単に設定する方法があります、それが[高速セットアップ]です。高速セットアップについて
SSMコンソール画面の一番上に表示されている[高速セットアップ]を選択し、どのリージョンかを選択すれば、そのリージョン内のSSMエージェントインストール済みEC2に自動的にロールをアタッチして、マネージドインスタンスに表示されるようにしてくれるみたいです。
※エンドポイントは要自力作成SSMを使ったEC2の操作
今回構築しているリモートデスクトップ接続だけなく他にもSSMを使えば色々ことができます。
何個か参考までにご紹介します。Windows Update
サーバにログインして、コントロールパネルからWindows Update クリックしなくてもできます。
[Run Command]を選択し、コマンドドキュメントからAWS-InstallWindowsUpdate を選択して、該当インスタンスをターゲットにしてやるだけです。
サーバのイベントログの収集やレジストリの管理
ちょっとした確認ならリモートデスクトップ接続しなくても出来ちゃいます!
参考にしたサイト
https://business.ntt-east.co.jp/content/cloudsolution/column-try-27.html
https://aws.amazon.com/jp/premiumsupport/knowledge-center/ec2-systems-manager-vpc-endpoints/
- 投稿日:2021-02-28T13:33:40+09:00
AutoScaling/ELB/CloudWatchの組み合わせについて整理してみた
はじめに
AWSにて冗長性かつスケーラビリティのあるシステム構成を構築するにあたり、「AutoScaling」、「ELB (Elastic Load Balancing)」、「CloudWatch」の組み合わせが可能ですが、個人的に各々の設定の関連性で時に混乱してしまうため、整理をしてみました。
前提
整理をするにあたり、今回構築したAWSの構成図は以下の通りです。
「ELB (Elastic Load Balancing」、「AutoScaling」、「CloudWatch」を同時に考えると混乱してしまいますので、以下の2つの組み合わせごとに分解して整理をしてみます。
番号 組み合わせ 1 AutoScalingとALB 2 AutoScalingとCloudWatch ※1. 今回、ELBの種類としてはALB (Application Load Balancer) を選択しました。
※2. Private SubnetにマルチAZでのRDSを配置し、EC2インスタンスにはWordPressをインストールすることで、ブログサービスとして構築しましたが、今回のテーマにフォーカスするため、以降の図ではPrivate Subnetは省略します。
1. AutoScalingとALB
AutoScalingとALBについて主要な設定項目を構成図に記載しました。
各々の設定で関連性のある項目を赤字としています。
■ ALB側の設定 ~冗長化/負荷分散~
ALBでターゲットグループを設定することで、冗長性を実現し、ユーザからくるHTTPリクエストをターゲットグループ内で負荷分散します。仮にEC2#1がシャットダウンされた場合も、EC2#1のヘルスチェックステータスは「unused」になり、以降のHTTPリクエストはEC2#2のみで処理します。
以上のように、ALBのみでは冗長化/負荷分散が可能ですが、スケーラビリティは備えていません。
そこでスケーラビリティを加えるためにAutoScalingの設定を組み合わせます。■ AutoScaling側の設定 ~スケーラビリティ~
上図のAutoScaling Groupで赤字の設定をすることにより、ALBのターゲットグループにスケーラビリティを加えることができます。その際、AutoScaling GroupのヘルスチェックとしてALBの設定を指定します。以上より、仮にEC2#1がシャットダウンされて、HTTPリクエストを処理できるインスタンスがEC2#2の1台に減ったとしても、希望する容量として「2」に設定しているため、新たなインスタンスを1台、自動生成することが可能になります。2.AutoScalingとCloudWatch
AutoScalingとCloudWatchについて主要な設定項目を構成図に記載しました。
各々の設定で関連性のある項目を赤字としています。
上図の構成のように、AutoScalingとCloudWatchアラームを組み合わせることにより、より柔軟にスケーリング設定をすることができます。
■ CloudWatch側の設定
CloudWatch側ではAutoScalingGroupの名前とスケーリングのアクションを指定して、アラームを作成します。
今回の場合は、AutoScalingGroup内の平均CPU使用率が70%以上の時にアラームする「CPU_High」と30%以下の時にアラームする「CPU_Low」を作成しました。各々について、アラーム発動時にAutoScalingのアクション「CPU_Add」、「CPU_Remove」が実行されます。■ AutoScaling側の設定
AutoScaling側ではCloudWatchアラームを指定して、スケーリングポリシーを作成します。
今回の場合は、「CPU_High」のアラームが発動された際にインスタンスを1台追加する「CPU_Add」と、「CPU_Low」のアラームが発動された際にインスタンスを1台削除する「CPU_Remove」を作成しました。以上により、CloudWatchとAutoScalingを組み合わせることにより、AutoScaling内の平均CPU使用率に応じて、インスタンス数を最小2台、最大4台にスケールする構成とすることができました。
まとめ
今回の記事作成を通し、「AutoScaling」、「ELB (Elastic Load Balancing」、「CloudWatch」を組み合わせた構成における、各々の役割と設定項目の関連性を整理することができました。
以上、最後まで読んで頂きありがとうございました!
※この記事はAWS初学者を導く体系的な動画学習サービス「AWS CloudTech」の課題カリキュラムで作成しました。
- 投稿日:2021-02-28T13:26:56+09:00
EC2からMySQLでRDSに接続するが、「Access denied for user」を突き返される
前提
・単一のVPC内で、EC2インスタンス用のサブネットとRDS用のサブネットを設定している。
症状
$ mysql -h [RDSのエンドポイント] -P 3306 -u root -p Enter password: ERROR 1045 (28000): Access denied for user 'root'@'xx.x.x.xx' (using password: YES)Ruby on RailsのアプリケーションをAWSにデプロイしようとしており、手順を進めていたところ、EC2インスタンスからRDSにMySQLで接続しようとして、このエラーが。
複数のサイトや記事を見てこのコマンドに問題はなさそうだったので、以下の仮説を立てました。
- セキュリティグループが間違っている
- パスワードが間違っている
結論、どちらも不正解でした。
私の場合は不正解でしたが、セキュリティグループが間違っているパターンもあるでしょうし、パスワードが間違っているパターンもあると思います。
では、何が間違っていたのか。
解決
正解はユーザー名でした。
はい、RDSのパスワードを設定するときに同時に設定するマスターユーザーの事ですね。
ご丁寧に、DB インスタンスのマスターユーザーのログイン ID を入力します。
って書いてあるんですけどね。どの記事を見ても下記のように
$ mysql -h [RDSのエンドポイント] -P 3306 -u root -prootと書いてあるので、ここは疑いようもなくrootとしていたわけです。
つまり、これが正解。
$ mysql -h [RDSのエンドポイント] -P 3306 -u [マスターユーザー名] -pまあ、マスターユーザー名を記入する時によく読まなかったのが原因なんですが。
初心者なので、コマンドの意味(-h -P -u -p)を一つひとつ調べて意味を理解していったにも関わらず、なぜかユーザー名だけは疑いもなく打ち込んでいました。なぜなら、rootというユーザーを作成した覚えはなく、RDSを作成したときに自動的に作成されるデフォルトユーザーなのかな的な風に捉えていたからです。
補足
ちなみに、作成した後にこのユーザー名を確認するには、AWSにアクセスし、サービスからRDSを選択します。
確認したいインスタンスを選択し、設定タブを選択。その中に「マスターユーザー名」という欄があります。解決してみて
今回は、かなり時間を費やしてしまいました。ユーザー名が間違っているというところに行き着くまでに丸1日掛かってしまいました(正確には1.5日(;゚∀゚))。挫折するかと思いました。
でも、それだけ悩みに悩んだエラーが解決したときほど嬉しいことはなく、こういうのがあるからどんどん先に進めるんですよね。
いやぁ、プログラミングは面白いなあ。
- 投稿日:2021-02-28T11:22:12+09:00
VPCエンドポイントとプライベートリンク
VPCエンドポイントとプライベートリンクの違い
概念
AWSアーキテクチャアイコンの分類では、PrivateLinkはサービス、VPCエンドポイント(Gateway型とInterface型)はリソースとなっています。公式ドキュメントの記載を読むとPrivateLinkはVPCエンドポイントを包括するイメージの様です。
用途
PrivateLinkを利用する用途は3種類あると動画に説明があります。
Gateway型とInterface型の違い
Gateway型はグローバルIPアドレスをInterface型はプライベートIPアドレスを使用しており、以下はGateway型がグローバルIPアドレスを使用していることを示しています。CMANで検索すると、当該IPアドレスが確かに「Amazon Technologies Inc. 」に割り当てられていることが分かります。そしてInterface型はENIを使用していることからプリベートIPアドレスを使っていることは明らかです。
オンプレミスと別リージョンからもアクセスできるInterface型の方が、机上で調べた限り、便利でセキュアな接続方法に思います。参考情報
- 投稿日:2021-02-28T11:14:50+09:00
【自分用メモ】AWS認定デベロッパー-アソシエイト試験対策メモ
はじめに
本記事は、AWS-DVA試験の対策をしている上で覚えておくべきだと感じたことを
メモとして残しておくものです。
主に問題演習中に連続して間違えたものを記載しています。AWS CLI
・--dry-run 実行可能でも実行しない。権限設定を確認する際等に使用する。
・(EC2内で) http://169.254.169.254/ にアクセスするとmeta-dataを取得可能。
・MFA認証をCLIで実施可能
・認証情報を参照する順番
1. CLIオプション --region, --output, --profile
2. 環境変数 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
3. credentialsファイル ~/.aws/credentials (linux mac) C:/Users/user/.aws/credentials (win)
4. configファイル /.aws/cofig (linux mac) C:/Users/user/.aws/config (win)
5. コンテナのcredentials
6. EC2のInstance profile credentialAWS SDK
・認証情報を参照する順番
1. 環境変数
2. java system properties
3. デフォルトのcredential profileファイル
4. コンテナのcredentials
5. EC2のInstance profile credentialsECS
c
・ECS Task PlacementはECS with EC2のみ使用可能
3つのタイプ
Binpack - 使用するインスタンスの数を最小にしようとする most cost effective
Ramdom - タスクをランダムに配置する
Spread - インスタンスIDやAZに基づいてタスクを分散配置する
2つの制約
distinctInstance - それぞれのタスクは異なるコンテナインスタンスに配置する
memberOf - クラスタークエリ言語を使用して制約を定義可能 example:t2のインスタンスにのみタスクを配置するElastic Beanstalk
・環境でELB作成したあとはELBのタイプを変えることはできない。
・EB with Docker
シングルDockerの場合、コンテナを実行可能だが、ECSは使用しない。
マルチDockerの場合、ECSを使用する。
•環境のプラットフォームバージョンはコンソールまたはEBCLIから変更できるCodeCommit
・AWS SNS、Lambda、CloudWatch Event Rulesを使用して通知が可能。
・クロスアカウントアクセス有CodeBuild
・フックの順序
アプリケーションストップ
↓
バンドルをダウンロード
↓
beforeInstall
↓
afterInstall
↓
アプリケーションスタート
↓
サービスの検証
・CodeBuildコンテナは実行終了時に削除される
・Dockerイメージは、プライベートレジストリからのものも使用できるCodeDeploy
Appspec.yamlファイルの必須プロパティ
name, alias, currentversion, targetversionX-Ray
・BeanStalkでX-Ray デーモンを有効にする方法 - .ebextensions/xray-daemon.configを作成する
•サービスマップは個々の地域のデータではなく全ての地域のデータが表示される
•SQSと統合されている場合、リソースヘッダーはSQSのメッセージサイズ等に影響しない
•サンプリングルールのデフォルト - 1秒あたりに1つのリクエスト、ホスト毎の追加の5%の要求がリクエストされる
・X-Ray SDKが提供するもの -
インターセプター コードに追加して受信 HTTP リクエストをトレースする
クライアントハンドラー アプリケーションが他の AWS サービスの呼び出しに使用する AWS SDK クライアントを計測する
An HTTP クライアント 別の内部および外部 HTTP ウェブサービス呼び出しを計測するLambda
・タイムアウトは最大15分
・環境変数は最大4KB
・FunctionName - 関数の名前
・Layer - Lambda関数実行環境に追加された関数レイヤーのリスト
・Environment - Lambda関数実行中にアクセス可能な変数
・Handler - 関数を実行するために呼び出すコード内のメソッドの名前
・関数をスケジューリングするベストプラクティスはCloudWatch Iventを使用することDynamoDB
・WCU(Write Capacity Units)計算方法例
2KBのオブジェクトを毎秒10個書き込む - 2*10 = 20WCU
*4.5KBのオブジェクトを毎秒6個書き込む - 56 = 30WCU
2KBのオブジェクトを毎分120個書き込む - 2(120 / 60) = 4WCU・RCU(Read Capacity Units)計算方法例
*4KBの10個のアイテムを強力な整合性のある毎秒読み込みで読み込む - (4 / 4) * 10 = 10RCU
*12KBの16個のアイテムを結果整合性のある毎秒読み込みで読み込む - ( 12 / 4 ) * ( 16 / 2 ) = 24RCU
*6KBの10個のアイテムを強力な整合性のある毎秒読み込みで読み込む - ( 8 / 4 ) * 10= 20RCU・グローバルセカンダリインデックスでは一貫性のある読み取りをサポートしていない
・一貫性のある読み取りをDAXクラスターにリクエストした場合、結果はキャッシュされない
•DynamoDBストリームとLambdaトリガーの併用はベストプラクティスCloudWatch
・基本的なモニタリングにアラームを設定する場合 - 少なくとも5分
・詳細なモニタリングにアラームを設定する場合 - 少なくとも1分S3
・バケット内のプレフィックスごとに1秒あたり3500のPUTリクエストと、5500のGETリクエストを提供するIAM
・バージョニング機能あり
・明示的に拒否するポリシーがある場合、他の許可されたステートメントを上書きするKMS
- 投稿日:2021-02-28T10:50:50+09:00
AWSでUbuntuインスタンスを立ち上げ、指定したバージョンのPythonをソースからインストールする
この記事の目的
用途に合わせたPythonの環境をすばやく構築したい、そんな時のための手順を、備忘を兼ねて書き記します。
なお、本記事執筆時点(2021/02/27)での情報となります。AWSのサービス内容は常にアップデートされるため、内容が合致しない場合はご了承下さい。AWSでUbuntuインスタンスを作成
本記事ではAWSの無料利用枠の範囲で設定を行います。
また、AWSのアカウント取得やログインについては割愛します。1. AWSでインスタンスを選択し設定を行う
(1)AWS画面の左上の「サービス」をクリックし、「コンピューティング」の「EC2」をクリックします。
(2)左枠メニュー項目から「インスタンス」をクリックします。
(3)インスタンス一覧画面が表示されたら、右上の「インスタンスを起動」をクリックします。
(5)自分の好きなOSを選択して下さい。ここでは、「Ubuntu Server 18.04 LTS」を選択しました。
(6)「次のステップ:インスタンスの詳細の設定」をクリックします。
無料利用枠の対象であることを確認し、次へ進みます。
(7)「次のステップ: ストレージの追加」をクリックします。
(9)「次のステップ: セキュリティグループの設定」をクリックします。
タグは任意で設定して下さい。
(10)セキュリティグループの設定を行います。
以下赤枠の通り、タイプに「SSH」「HTTPS」の設定を入れます。
ちなみに「カスタムTCP」のポートは"8888"Jupyter Notebookのための設定で、一般的には不要と思います。。
「確認と作成」をクリックします。
(12)既存のキーペアまたは新しいキーペアを選択し、インスタンスの作成を実行して下さい。
インスタンスの作成手続きは以上です。2. 必要なビルドツール・ライブラリをインストールする
(1)作成したインスタンスにログインします。
SSHツールを使用し作成したインスタンスへログインします。
ここではTera Termを使用しました。ログインの仕方はAWSの一般的な手順となりますのでここでは省略します。インスタンスのOSがUbuntuなので、ユーザ名は「ubuntu」となります。
(2)以下を実行して必要なライブラリをインストールします。
sudo apt update sudo apt install build-essential libbz2-dev libdb-dev libreadline-dev libffi-dev libgdbm-dev liblzma-dev libncursesw5-dev libsqlite3-dev libssl-dev zlib1g-dev uuid-dev tk-devバージョンを指定してPythonをソースからインストール
1. Pythonのソースをダウンロードする
(1)Pythonの公式サイトへアクセスします。
AWSインスタンスではなく、ローカルPC端末のブラウザでアクセスします。Python公式サイト
https://www.python.org/(2)Pythonサイトのメニュー「Download」を選択し、さらに「All releases」をクリックします。
(3)ソースの一覧から欲しいバージョンのソースを選択しクリックします。
ここでは「Python 3.9.2」を選択しました。
(4)ソースのURLを取得します。
ソースの選択画面で、欲しいソースのリンクを右クリックしてURLを取得します。
ここでは「Gzipped source tarball」を選択しました。
右クリックし、「リンクのアドレスをコピー」します。
(5)AWSインスタンスのプロンプトで、取得したURLからソースをダウンロードします。
プロンプトでは以下のように実行してソースを取得します。wget https://www.python.org/ftp/python/3.9.2/Python-3.9.2.tgz(6)ソースを解凍します。
取得したファイルを以下のコマンドで解凍します。tar -xvf Python-3.9.2.tgz2. Pythonをインストールする
(1)解凍後、以下のコマンドでPythonフォルダへ移動します。
cd Python-3.9.2/(2)以下の各コマンドを順次実行し、インストールを行います。
./configure make sudo make install途中でエラーが発生せず、最後に「Successfully ...」と表示されたら完了です。
(3)pipを使用するため、以下のコマンドでpipライブラリをインストールします。
sudo apt install python-pip(4)以下のコマンドでPythonのバージョンを確認します。
想定通りのバージョンが表示されればOKです。./python -V(5)最後に、pythonコマンドのパスを設定します。
ここではインストールしたpythonのディレクトリが以下ですので、
次のようにコマンドを実行します。
sudo ln -s /home/ubuntu/Python-3.9.2/python /usr/bin/python上書きエラーが出る場合は、強制実行オプション[-f]を付けて実行します。
以上でインストールは完了です。
Pythonコマンドでバージョンの確認を行って下さい。関連情報
ご意見など
ご意見、間違い訂正などございましたらお寄せ下さい。
- 投稿日:2021-02-28T10:37:20+09:00
Clamav の memory allocation エラーの対処方法
# clamdscan --multiscan /上記コマンドで、/swapfile の memory allocation エラーとなる。
/swapfile: Can't allocate memory ERRORこれは別に問題ない。/swapfile が物理メモリの値を超えているため。
/swapfile を小さくするのは意味がないので、検査対象から外す。# vim /etc/clamd.d/scan.conf ...(snip)... ExcludePath ^/proc/ ExcludePath ^/sys/ # add this line ExcludePath ^/swapfile ...(snip)...デーモンリスタート
# systemctl restart clamd@scan.service
- 投稿日:2021-02-28T07:05:46+09:00
【AWS CloudFormation】「レプリケーション時間のコントロール (RTC)」等を含めたS3レプリケーション設定のテンプレートの書き方
前置き
現場で AWS の CloudFormation に初挑戦することになりました。
「レプリケーション時間のコントロール (RTC)」等を含めた、
S3レプリケーション設定のテンプレートの書き方で引っかかったことがあったので、投稿します。内容
以下のようなテンプレートファイルでスタックを作成し、実行しました。
S3BucketOriginal: Type: AWS::S3::Bucket Properties: BucketName: !Sub 'original-${AWS::AccountId}' VersioningConfiguration: Status: Enabled ReplicationConfiguration: Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/OriginBucketBackupRole' Rules: - Id: BackUpRule Status: Enabled DeleteMarkerReplication: Status: Disabled Destination: Bucket: !Sub 'arn:aws:s3:::backup-${AWS::AccountId}' EncryptionConfiguration: ReplicaKmsKeyID: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/BackupKey' Metrics: Status: Enabled EventThreshold: Minutes: 15 ReplicationTime: Status: Enabled Time: Minutes: 15 SourceSelectionCriteria: SseKmsEncryptedObjects: Status: Enabledすると、以下のようなエラーになってしまいました。
DeleteMarkerReplication cannot be used for this version of Cross Region Replication configuration schema. Please refer to S3 Developer Guide for more informationこのエラーは、以下CloudFormationのユーザーガイド「ReplicationRule」内の注記のとおりです。
前述のエラーメッセージ内のthis version
は、S3のレプリケーション設定のバージョンのことでした。以下のように、Filter を設定しないとS3のレプリケーション設定はV1になってしまうということでした。
・・・ ReplicationConfiguration: Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/OriginBucketBackupRole' Rules: - Id: BackUpRule Status: Enabled DeleteMarkerReplication: Status: Disabled Filter: # 追加 Prefix: "" # 追加 ・・・Filter を設定していないと、以下のように Replication Time Control (S3 RTC) の設定もエラーとなってしまいます。
ReplicationTime cannot be used for this version of the replication configuration schema. Please refer to S3 Developer Guide for more informationFilter を設定したテンプレートでスタックを実行して今度こそ・・・
と思ったら以下のようなエラーになってしまいました。Priority must be specified for this version of Cross Region Replication configuration schema. Please refer to the S3 Developer Guide for more informationPriority(優先度)は、 CloudFormation のユーザーガイドでは「必須:いいえ」となっているのに・・・
マネジメントコンソールからの設定だと、自動に設定してくれていれるんですね・・・
全く意識したことなかったです・・・その他にも、レプリケーション時間のコントロール (RTC)等の設定はマネジメントコンソール上だとチェックボックスでONにするだけなのですが、
CloudFormationのテンプレートでは時間数が必須入力する必要があります。
このあたり、マネジメントコンソールでは意識しないので難しい・・・Metrics: Status: Enabled EventThreshold: Minutes: 15 ReplicationTime: Status: Enabled Time: Minutes: 15結果
最終的にはテンプレートファイルを以下のようにすることで、レプリケーション設定を作成することが出来ました。
(BucketName、ReplicaKmsKeyID 辺りの指定方法は適宜置き換えください)Resources: S3BucketOriginal: Type: AWS::S3::Bucket Properties: BucketName: !Sub 'original-${AWS::AccountId}' VersioningConfiguration: Status: Enabled ReplicationConfiguration: Role: !Sub 'arn:aws:iam::${AWS::AccountId}:role/OriginBucketBackupRole' Rules: - Id: BackUpRule Status: Enabled DeleteMarkerReplication: Status: Disabled Filter: #追加 Prefix: "" #追加 Priority: 0 #追加 Destination: Bucket: !Sub 'arn:aws:s3:::backup-${AWS::AccountId}' EncryptionConfiguration: ReplicaKmsKeyID: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/BackupKey' Metrics: Status: Enabled EventThreshold: Minutes: 15 ReplicationTime: Status: Enabled Time: Minutes: 15 SourceSelectionCriteria: SseKmsEncryptedObjects: Status: Enabled
- 投稿日:2021-02-28T04:43:02+09:00
AWS初期設定メモ(2021/02/28時点)
IAMで作業用ユーザーを作成
省略。
二要素認証の設定
省略。
CloudWatchで料金アラートを設定
上記画像の「請求アラートを管理する」リンクを選択。
CloudWatchの画面が表示されるので、サイドメニューから「請求」を選択。
「アラームの作成」ボタンをクリック。
条件を設定する画面になるので、閾値を設定し、アラームを作成する。
(ここでは50 USDを超えた場合に設定し、それ以外はデフォルトで登録)
CloudTrailで操作ログをS3に保存する
1.メニューから CloudTrailの画面に遷移し、「証跡の作成」を選択。
- 「証跡名」を設定して作成
- マルチリージョンの証跡が有効になっていることを確認する。
- S3バケット上に保存される設定になっていることを確認する。
- 投稿日:2021-02-28T01:30:18+09:00
インフラエンジニア見習いによるAWS認定ソリューションアーキテクトアソシエイト(SAA-C02)合格体験記
始めに
皆さま、こんにちは。
スプラトゥーン3の発表に涙が止まらなかったハイカラスクエアのタコの@hiroki_tanakaです。
先日、AWS認定資格の1つであるAWS認定ソリューションアーキテクトアソシエイトを受験したので、
振り返りの意味も込めて試験までの道のりをご紹介させていただきます。自己紹介
- 社会人歴6年目エンジニア
- 得意領域はサーバサイドのアプリケーションレイヤ(RubyとJavaが好き)
- 2020年10月頃からインフラエンジニアに入門した。
- AWSはEC2やS3・VPCの存在を知っているが、自身で一から構築したことはない。
- 2020年11月にAWS認定クラウドプラクティショナーに合格した。(その時の話はこちらの記事を参照ください。)
AWS認定ソリューションアーキテクトアソシエイトとは
AWS認定ソリューションアーキテクトアソシエイトとは、公式サイトにあるようにAWSの分散システムの可用性・コスト効率・高耐障害性及びスケーラビリティの設計に関する1年以上の実務経験を持つAWS設計者を対象にしている資格です。
1年以上の実務経験者を対象にしている部分からわかるようにAWSの12ある資格の中では中堅にあたる資格です。
(巷では、AWS認定資格の登竜門的な位置付けを受けているようです。)そのため、個々のAWSサービスの理解は勿論必要なのですが、それだけでなく、
- 複数のAWSのサービスを使用して、安全で堅牢なアプリケーションを構築・デプロイするための知識
- 顧客の要件に基づきながら、AWSのアーキテクチャ設計原則に沿ってのシステム構築のノウハウ
といった可用性・コスト効率・耐障害性・スケーラビリティの4点について各システムでの最適な構成を理解していることが求められます。つまり、この資格を取得すると顧客のニーズを満たすAWS環境構築の設計・実装を行うための入り口に立てます。
クラウドプラクティショナーを取得した私にとって、次のステップとしてまさにピッタリの資格でした。ちなみに、
試験時間は130分で問題数は65問・最低合格点は720点(最低点数100点の1000点満点)となっています。
受験料は15,000円(税別)になります。
(ただ、私はクラウドプラクティショナーの合格特典で次の資格試験の受験料を50%OFFで受験することが出来ました。)勉強法
勉強期間は12月~2月までの3ヶ月間なのですが、途中サボっていた期間もあるので実質2ヶ月程度です。
基本的には平日の夜に30~1時間でしたが、受験直前は1日2〜3時間は勉強していました。参考書
これまでの自身の学習経験から私は参考書と紙とペンを用いた勉強方法が一番性に合っていると分かっていたので、
こちらのソリューションアーキテクトアソシエイトの参考書を紙に書き出しながら1周し、各AWSサービスの概要・使い方を頭に入れました。
その後、各章の練習問題・章末の模擬問題を4周しました。
問題を解く際は正解だけでなく、不正解の選択肢に関しても何故不正解なのか・どう直したら正解にできるのかを考えるようにしていました。Udemyの模擬試験
上記の参考書では問題数が少なかったので、模擬問題集をUdemyで購入し参考書で身につけた知識の確認を行いました。
(問題集の定価は12,000円なのですが、Udemyは頻繁にセールを行っているのでそこで購入しました。1610円で買うことが出来ました。)
この模擬問題集は非常に難易度が高く、実際の試験よりも難しく感じました。
そのため、この模擬問題集でしっかり合格点を出せるようになれば、高い確率で合格できると思います。1周目時点で私の正答率は50%程度でかなり焦ったのですが、落ち着いて1周目で間違えた問題の解説を頭に入れる→問題を解く→正誤問わず、気になった問題の解説を頭に入れる→問題を解くということを繰り返し行いました。
最終的には正答率は95~98%で安定し、安心して本試験に臨むことができました。AWSの模擬試験
AWSが提供している2000円(税抜)で受けられる問題数20問の模擬試験があるのですが、クラウドプラクティショナーの合格特典で無料で受験できるため受験しました。
ただ、こちらは本試験よりも難易度が低いことや受験結果の点数のみ送られてくるため、自分がどこを間違えたのかわからず復習ができません。
そのため、受けなくても良かったかな…と今は思っています。受験と結果
クラウドプラクティショナーの時同様に、コロナ禍のため自宅でのオンライン受験かテストセンターでのオフライン受験を選択できるのですが、自宅での受験はデスクだけでなく周りの壁や棚も整理する必要があることやそもそも自宅ではあまり集中できない可能性を考えて最寄りのテストセンターで受験しました。
また余談ですが、オンライン受験をする場合はPSIかピアゾンVUEのどちらかを選択できるのですが、日本語対応しているピアゾンVUEがオススメです。
PSIは試験官とのやり取りが全て英語のチャットで行われるため、日常会話程度の英語が出来ないと少しつらいかと思います。本試験はUdemyの模擬問題よりも難易度は低く感じたためか比較的スムーズに解答でき、試験時間130分のうち60分で1周することが出来ました。
そのため、一問一問落ち着いて見直し対応することができました。
(見直しは2周くらいしました。)
問題自体は私の場合は、CloudFront・EC2のAutoScaling・システム移行に関する問題が多かったです。試験後の簡単なアンケートに答えて、表示された結果は………
無事合格!ただ、試験中は「この感じなら900点は固いはず」と思って解答していたので、正直もっと点数が取れていると思っていました。
775点は合格ラインが720点なので、中々ギリギリです…笑とはいえ、合格できて本当に良かったです!(=^▽^=)
終わりに
正直、ソリューションアーキテクトアソシエイトはクラウドプラクティショナーから難易度が一気に上がり、途中勉強のモチベーションが下がった時期もありました。
(Udemyの模擬試験の1回目を終えた時はあまりの点数の取れなさに「これは受験を止めた方がいいんじゃないか…?」と悩みました笑)
ただ、勉強を通じてAWSのことを知れるのは本当に楽しく、合格まで走りきった時はより一層AWSのことが好きになりました.
次は同じAWS認定のアソシエイト資格であるSysOpsアドミニストレーターアソシエイトとデベロッパーアソシエイトを取得したいと思います。
目指せ、AWSアソシエイト資格三冠王!ヾ(`・ω・´)ゞ