20210502のAWSに関する記事は19件です。

[AWS & Rails & PostgreSQL]EC2のサーバー環境構築

はじめに AWSのEC2でRailsアプリをデプロイしたのでメモとして残しておきます。 今回はEC2のサーバー環境構築をしていきます 自分の手順書としてのメモですので画像はありません。あしからず。 前提としてEC2にログインできていて新しいユーザーを作ってログインできている状態とします。 Nginxを設定する まずサーバーをアップデートします。 サーバー $ sudo yum update Nginxをインストール サーバー $ sudo amazon-linux-extras install nginx1.12 -y Nginxの起動 サーバー $ sudo systemctl start nginx Nginxサービスを有効化します。(インスタンス起動時自動起動) サーバー $ sudo systemctl enable nginx 起動しているか確認(ステータスを表示) サーバー $ systemctl status nginx 必要なプラグインをインストール サーバー $ sudo yum install git make gcc-c++ patch openssl-devel libyaml-devel libffi-devel libicu-devel libxml2 libxslt libxml2-devel libxslt-devel zlib-devel readline-devel ImageMagick ImageMagick-devel epel-release PostgreSQLをインストール サーバー $ sudo amazon-linux-extras install postgresql11 先ほどの方法でインストールされるのは、PostgreSQLのクライアントだけで、サーバはインストールされない。 postgresql-serverをインストールする サーバー $ sudo yum install postgresql-server postgresql-contrib postgresql-devel サーバーを初期化します。 サーバー $ sudo postgresql-setup initdb Node.jsをインストール サーバー # ダウンロード $ curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash - #インストール $ sudo yum install -y nodejs Yarnをインストール サーバー $ curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo $ sudo yum install -y yarn rbenvをインストール サーバー $ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile ruby-buildをインストール サーバー $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build $ rbenv rehash Rubyをインストール ローカル環境にて制作したアプリをどのバージョンのRubyで制作したか確認してください。 アプリのディレクトリ内でruby -vでバージョン確認です。 ローカル $ ruby -v ruby 2.6.6 筆者は2.6.6でインストールします。 サーバー $ rbenv install -v 2.6.6 $ rbenv global 2.6.6 $ rbenv rehash $ ruby -v 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Web Services 基礎からのネットワーク&サーバー構築 8-4(173p) http error 500 解決メモ

「Amazon Web Services 基礎からのネットワーク&サーバー構築」の8-4(173ページ)でWordPressの初期設定画面が出るはずのところが「http error 500」が出てしまい、ちょっちハマってしまったので解決メモを残しておきます。 $ cd ~ $ cd wordpress でwordpressディレクトリに移動して、 $ sudo amazon-linux-extras install php7.3 このコマンドを実行します(このコマンドはPHPをアップデートするもの)。 これで動くようになりました。 何が原因だったのか、結論から言うとPHPのバージョンが足りていませんでした。 WordPressの最新版は、PHPバージョン5.6.20以降でしか動きません。 しかし、wordpressディレクトリで、 $ yum list installed | grep php こちらのコマンドを実行してインストールされているPHPのバージョンを確認してみると、僕の環境ではPHP5.4とバージョンが足りていなかったためwordpressが正常に動作しなかったというわけです。 その他、使ったコマンド PHPのエラーを確認するコマンド sudo less /var/log/httpd/error_log | tail -20
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】ELBについてまとめてみた

前提として RDSは、「エンドポイント通信」という機能で、 AZ(1a)のRDSに障害が起きた場合は、 自動的にAZ(1c)のRDSに繋ぐことができます。 しかし、EC2はダウンした時にサポートできるものがないため、 こちらがダウンしたらRDSも使えなくなります。 これを単一障害点と言います。 「じゃあ、RDSみたいにEC2もAZ(1c)に複製すればいいじゃん」と考えるのですがこちらはNGです。 なぜダメなのかというと以下の3点の理由からです。 ①アクセスポイントが複数になってしまう。 →上記の例で言うと、10.0.0.0/24と10.0.1.0/24の2つです。 ②サーバーが正常かどうかチェックしないといけない。 →AZ(1a)がエラーのときは、AZ(1c)に転送したり、通信自体をキャンセルできるようにしないといけない。 ③セキュリティの問題 →直接WEBサーバーにアクセスできるというのはセキュリティ面から見て不安であり、負荷がかかってしまう。 上記の問題を解決するために使われるのがELB ELBとは? ELBは、Elastic Load Balancingという名前です。 主な機能としては3つです。 ①ELBにはDNSのアクセスポイントが付与されます。 つまり、アクセスポイントを1つにして負荷を分散させることができます。 ②ヘルスチェック機能がある 異常なインスタンスを発見したら通信をキャンセルしてくれます。 ③ELBに証明書を付与することでSSL通信の終端になってくれる HTTPS通信を復号する処理を担当してくれる。 つまり、internet gatewayの段階ではHTTPSだが、サブネット内ではHTTP通信となる。 構成図 よく使われる構成図 ①ELBのみパブリックサブネットに配置する ②FrontendとAPIを分けて3層で構築する Privateサブネット内にELBを配置します(=内部ロードバランサーと言います) では実際に作ってみましょう! 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サブネット内にEC2を構築 ■ルート コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「インスタンスを起動」を選択 ■ステップの流れ STEP1 Amazon Linux 2 AMI STEP2 t2.micro STEP3 「ネットワーク」、「サブネット」、「パブリックIP」を変更 STEP4 そのままでOK STEP5 Name=WEBSERVER1で設定 STEP6 セキュリティグループを変更 STEP7 キーペアの作成 AMIの作成 作成したWEBSERVER1を停止してAMIを作成します。 ■ルート コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「インスタンスの状態」から停止を選択 コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「アクション」から「イメージを作成」を選択 では作成したAMIを使ってインスタンスを作成しましょう。 ステップ5のタグは、Name=WEBSERVER2とします。 6. Publicサブネットをインターネットに接続するためにInternet Gatewayを作成 ■ルート コンソールで「VPC」と検索→左ペイン内の「インターネットゲートウェイ」を選択→「インターネットゲートウェイの作成」を選択 これではVPCとアタッチできていないので、インターネットへ接続できません。 なのでアタッチさせましょう! これでアタッチしました。 7. EC2がインターネット接続するためにルートテーブルを作成 ■ルート コンソールで「VPC」と検索→左ペイン内の「サブネット」を選択→「Public-Subnet1」を選択→「ルートテーブル」を選択→「ルート」へ移動→「ルートを編集」を選択 8. 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 ___|\___|___| 9. PrivateサブネットにRDSを構築 RDSには、エンドポイント通信という機能がありました。 もし通信障害などが発生して、親のRDSが使えなくなった時子のRDSに接続してくれる機能です。 つまり、このことから異なるAZにサブネットがないとRDSは使えません。 ということで複数のサブネットにまたがる形でRDSは構築されるので、 サブネットグループというものが必要になります。 ■ルート コンソールで「RDS」と検索→左ペイン内の「サブネットグループ」を選択→「DB サブネットグループを作成」を選択 それでは次に、DBを作成していきましょう。 ■ルート コンソールで「RDS」と検索→左ペイン内の「データベース」を選択→「データベースの作成」を選択 次にRDSで作ったセキュリティグループを修正します。 ■ルート コンソールで「VPC」と検索→左ペイン内の「セキュリティグループ」を選択→「RDS-SG-1」を選択 DBは重要な情報が入っているため誰でも彼でもアクセスできると困ります。 なので、PublicサブネットのEC2(セキュリティグループ→WEB-SG-1)からしか触れないように修正します。 10. 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 11. ロードバランサーを導入 ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「ロードバランサーの作成」を選択 12. RDSのサイトアドレスをロードバランサーのDNSに書き換え ターミナル // ①RDSへログイン $ mysql -h database-1.xxxxxxxxxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -u wordpress -p // ②WordPressのテーブルを使用 MySQL [(none)]> USE 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'); 13. EC2のセキュリティグループのアクセス許可をロードバランサーからに変更する ■ルート コンソールで「EC2」と検索→左ペイン内の「セキュリティグループ」を選択→「WEB-SG-1」を選択 14. RDSも冗長化構成へ変更 ■ルート コンソールで「RDS」と検索→左ペイン内の「データベース」を選択→「database-1」を選択
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud9でenvironment is running out of disk spaceが出た時の解決方法

概要 RailsチュートリアルをCloud9で開発していたところ、下記のエラーが出るようになり、ファイルの編集やターミナル等が使用できなくなりました。 our environment is running out of disk space. Please free up space or resize your EBS volume (see how) 原因 原因はAmazon EBS ボリュームの容量不足です。容量を空ければ解決できます。 解決手順 1. EBS ボリュームの確認 ターミナルでdf -hコマンドで容量を確認します。 $ df -h /dev/xvda1の使用容量を減らします。現在は99%です。 2. EBS ボリュームを増やす EC2にアクセスし、Name列がcloud9のインスタンスID列の値をコピーします。 Elastic Block Storeのボリュームにアクセスし、コピーした値で検索をかけます。 ボリュームを選択した状態で上部の「アクション」ボタンをクリックし、ボリュームのサイズを30に変更します。(本記事を書いた時点では30は無料枠ですが、ご自身でご確認ください) EC2にアクセスし、、Name列がcloud9のインスタンスを選択し、「インスタンスの状態」ボタンをクリックし、インスタンスを再起動します。 Cloud9にアクセスし、ターミナルでdf -hコマンドで容量を確認します。/dev/xvda1の使用容量が大幅に減っていれば成功です。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TerraformでLambda自体は作るけど、ソースコードやデプロイは別でやりたい

TerraformでLambda自体は作るけど、ソースコードやデプロイは別でやりたい 背景 TerraformでAWSのLambda関数を作成していたときに、「LambdaのソースコードもTerraformで管理するの?」って思ったのがきっかけです。 まずTerraformはインフラ構成をコードで管理するツールです。ただそこにアプリケーションのソースコードが入ってもいいのかという議論があると思います。規模が小さい場合などは、Terraformですべて管理するという方法で問題ないかもしれませんが、下記の図のような、複数のサービスで構成されるサービスの場合、Terraformで管理したいスコープとCI/CDの単位に差が出てくると思います。 ということでこの記事では、TerraformでLambda関数の宣言はするが、サービスのソースコードやデプロイは分離した状態を目指します。 方針 Terraformでは空のLambdaのみを宣言、作成し、ソースコードはアップロードしません。 デプロイはaws cliを利用してS3にソースコードをアップロード、Lambdaの参照を切り替えるという実装にします。 実装 Terraform側 TerraformでLambda関数と必要な権限、S3バケット等を作成します。 本来変数にすべきところもすべてベタ書きにしていますがご了承ください。 コードの解説はコメントで書きます。 main.tf # providerの宣言 provider "aws" { region = "ap-northeast-1" profile = "default" } # S3バケットを生成 # CI/CD側でlambdaのソースコードを格納するための箱 # バケット名は変更してください resource "aws_s3_bucket" "test_bucket" { bucket = "okamoto-tf-test-bucket" acl = "private" tags = { Name = "okamoto-tf-test-bucket" } versioning { enabled = false # 本番運用する場合はtrue } } # ロールを生成 resource "aws_iam_role" "lambda" { name = "iam_for_lambda" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "lambda.amazonaws.com" }, "Effect": "Allow", "Sid": "" } ] } EOF } # 初回のみ利用する空のLambdaのファイルを生成 data "archive_file" "initial_lambda_package" { type = "zip" output_path = "${path.module}/.temp_files/lambda.zip" source { content = "# empty" filename = "main.py" } } # 生成した空のLambdaのファイルをS3にアップロード resource "aws_s3_bucket_object" "lambda_file" { bucket = aws_s3_bucket.test_bucket.id key = "initial.zip" source = "${path.module}/.temp_files/lambda.zip" } # Lambda関数を生成 # ソースコードは空のLambdaのファイルのS3を参照 resource "aws_lambda_function" "lambda_test" { function_name = "lambda_test" role = aws_iam_role.lambda.arn handler = "main.handler" runtime = "python3.8" timeout = 120 publish = true s3_bucket = aws_s3_bucket.test_bucket.id s3_key = aws_s3_bucket_object.lambda_file.id } これでterraform planを実行して生成されるリソースを確認します。 実行結果 $ terraform plan Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_iam_role.lambda will be created + resource "aws_iam_role" "lambda" { + arn = (known after apply) + assume_role_policy = jsonencode( { + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" } + Sid = "" }, ] + Version = "2012-10-17" } ) + create_date = (known after apply) + force_detach_policies = false + id = (known after apply) + managed_policy_arns = (known after apply) + max_session_duration = 3600 + name = "iam_for_lambda" + path = "/" + unique_id = (known after apply) + inline_policy { + name = (known after apply) + policy = (known after apply) } } # aws_lambda_function.lambda_test will be created + resource "aws_lambda_function" "lambda_test" { + arn = (known after apply) + function_name = "lambda_test" + handler = "main.handler" + id = (known after apply) + invoke_arn = (known after apply) + last_modified = (known after apply) + memory_size = 128 + package_type = "Zip" + publish = true + qualified_arn = (known after apply) + reserved_concurrent_executions = -1 + role = (known after apply) + runtime = "python3.8" + s3_bucket = (known after apply) + s3_key = (known after apply) + signing_job_arn = (known after apply) + signing_profile_version_arn = (known after apply) + source_code_hash = (known after apply) + source_code_size = (known after apply) + timeout = 120 + version = (known after apply) + tracing_config { + mode = (known after apply) } } # aws_s3_bucket.test_bucket will be created + resource "aws_s3_bucket" "test_bucket" { + acceleration_status = (known after apply) + acl = "private" + arn = (known after apply) + bucket = "okamoto-tf-test-bucket" + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + force_destroy = false + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + request_payer = (known after apply) + tags = { + "Name" = "okamoto-tf-test-bucket" } + website_domain = (known after apply) + website_endpoint = (known after apply) + versioning { + enabled = false + mfa_delete = false } } # aws_s3_bucket_object.lambda_file will be created + resource "aws_s3_bucket_object" "lambda_file" { + acl = "private" + bucket = (known after apply) + bucket_key_enabled = (known after apply) + content_type = (known after apply) + etag = (known after apply) + force_destroy = false + id = (known after apply) + key = "initial.zip" + kms_key_id = (known after apply) + server_side_encryption = (known after apply) + source = "./.temp_files/lambda.zip" + storage_class = (known after apply) + version_id = (known after apply) } Plan: 4 to add, 0 to change, 0 to destroy. Changes to Outputs: + arn = (known after apply) + aws_s3_bucket_object_etag = (known after apply) + aws_s3_bucket_object_id = (known after apply) + aws_s3_bucket_object_version_id = (known after apply) + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + website_domain = (known after apply) + website_endpoint = (known after apply) ───────────────────────────────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. 問題なさそうであれば、続けてterraform applyで適応していきます。 実行結果 $ terraform apply Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_iam_role.lambda will be created + resource "aws_iam_role" "lambda" { + arn = (known after apply) + assume_role_policy = jsonencode( { + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" } + Sid = "" }, ] + Version = "2012-10-17" } ) + create_date = (known after apply) + force_detach_policies = false + id = (known after apply) + managed_policy_arns = (known after apply) + max_session_duration = 3600 + name = "iam_for_lambda" + path = "/" + unique_id = (known after apply) + inline_policy { + name = (known after apply) + policy = (known after apply) } } # aws_lambda_function.lambda_test will be created + resource "aws_lambda_function" "lambda_test" { + arn = (known after apply) + function_name = "lambda_test" + handler = "main.handler" + id = (known after apply) + invoke_arn = (known after apply) + last_modified = (known after apply) + memory_size = 128 + package_type = "Zip" + publish = true + qualified_arn = (known after apply) + reserved_concurrent_executions = -1 + role = (known after apply) + runtime = "python3.8" + s3_bucket = (known after apply) + s3_key = (known after apply) + signing_job_arn = (known after apply) + signing_profile_version_arn = (known after apply) + source_code_hash = (known after apply) + source_code_size = (known after apply) + timeout = 120 + version = (known after apply) + tracing_config { + mode = (known after apply) } } # aws_s3_bucket.test_bucket will be created + resource "aws_s3_bucket" "test_bucket" { + acceleration_status = (known after apply) + acl = "private" + arn = (known after apply) + bucket = "okamoto-tf-test-bucket" + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + force_destroy = false + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + request_payer = (known after apply) + tags = { + "Name" = "okamoto-tf-test-bucket" } + website_domain = (known after apply) + website_endpoint = (known after apply) + versioning { + enabled = false + mfa_delete = false } } # aws_s3_bucket_object.lambda_file will be created + resource "aws_s3_bucket_object" "lambda_file" { + acl = "private" + bucket = (known after apply) + bucket_key_enabled = (known after apply) + content_type = (known after apply) + etag = (known after apply) + force_destroy = false + id = (known after apply) + key = "initial.zip" + kms_key_id = (known after apply) + server_side_encryption = (known after apply) + source = "./.temp_files/lambda.zip" + storage_class = (known after apply) + version_id = (known after apply) } Plan: 4 to add, 0 to change, 0 to destroy. Changes to Outputs: + arn = (known after apply) + aws_s3_bucket_object_etag = (known after apply) + aws_s3_bucket_object_id = (known after apply) + aws_s3_bucket_object_version_id = (known after apply) + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + website_domain = (known after apply) + website_endpoint = (known after apply) Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:yes aws_iam_role.lambda: Creating... aws_s3_bucket.test_bucket: Creating... aws_iam_role.lambda: Still creating... [10s elapsed] aws_s3_bucket.test_bucket: Still creating... [10s elapsed] aws_s3_bucket.test_bucket: Creation complete after 16s [id=okamoto-tf-test-bucket] aws_s3_bucket_object.lambda_file: Creating... aws_s3_bucket_object.lambda_file: Creation complete after 1s [id=initial.zip] aws_iam_role.lambda: Still creating... [20s elapsed] aws_iam_role.lambda: Still creating... [30s elapsed] aws_iam_role.lambda: Creation complete after 33s [id=iam_for_lambda] aws_lambda_function.lambda_test: Creating... aws_lambda_function.lambda_test: Creation complete after 6s [id=lambda_test] Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: arn = "arn:aws:s3:::okamoto-tf-test-bucket" aws_s3_bucket_object_etag = "6582ea9c16331bd37b52bf2c8bcca596" aws_s3_bucket_object_id = "initial.zip" aws_s3_bucket_object_version_id = "null" bucket_domain_name = "okamoto-tf-test-bucket.s3.amazonaws.com" bucket_regional_domain_name = "okamoto-tf-test-bucket.s3.ap-northeast-1.amazonaws.com" hosted_zone_id = "Z2M4EHUR26P7ZW" id = "okamoto-tf-test-bucket" region = "ap-northeast-1" 生成されたS3バケットとLabmdaをAWSコンソール上から確認します。 バケットが生成されzipファイルが置かれているとことがわかります。 空のLambdaが生成されています。 これでTerraform側は終了です。 デプロイ側 今回はlambdaのソースコードをsrc/main.py、パッケージ情報をrequirements.txtに、デプロイコマンドscripts/deploy.shで管理します。 フォルダ構成は以下のようになっています。 ├── requirements.txt ├── scripts │   └── deploy.sh └── src └── main.py またrequirements.txt、main.pyは以下です。最小限の構成です。 requirements.txt requests==2.25.1 main.py import requests def handler(*args, **kwargs): res = requests.get('https://api.github.com') return res.status_code 以下のドキュメントを参考にしてデプロイシェルを書きます。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-package.html deploy.sh # ディレクトリ情報の取得 CURRENT_PATH=$(cd $(dirname $0); pwd) ROOT_PATH=$CURRENT_PATH/.. # pythonパッケージのインストール pip install --target ./package -r $ROOT_PATH/requirements.txt --system # pythonパッケージのzip化 cd package zip ../deployment-package.zip -r ./* cd .. # ソースコードをzipファイルに追加 zip -g deployment-package.zip -j $ROOT_PATH/src/main.py # S3にアップロードしてlambda関数の参照を書き換える aws s3 cp ./deployment-package.zip s3://okamoto-tf-test-bucket/ aws lambda update-function-code --function-name lambda_test --s3-bucket okamoto-tf-test-bucket --s3-key deployment-package.zip # デプロイ時の一時ファイルを削除 rm deployment-package.zip rm -r package それでは実行します。 $ bash ./scripts/deploy.sh AWSコンソールを確認します。 deployment-package.zipがアップロードされていることがわかります。 うまくpackageされていることがわかります。 また実行してrequestsパッケージを参照しても落ちることなく実行できます。 また当然インフラ構成には変更がないのでデプロイ後もterraform planで差分が出ることはありません。 $ terraform plan aws_iam_role.lambda: Refreshing state... [id=iam_for_lambda] aws_s3_bucket.test_bucket: Refreshing state... [id=okamoto-tf-test-bucket] aws_s3_bucket_object.lambda_file: Refreshing state... [id=initial.zip] aws_lambda_function.lambda_test: Refreshing state... [id=lambda_test] No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and the remote system(s). As a result, there are no actions to take. まとめ 後半部分は、ただaws cliを使ってLambdaを更新しただけですが、TerraformとLambdaのソースコード・デプロイを分離させることができました。 今回作成したソースコードはこちらにあげておきます。 https://github.com/Cohey0727/terraform_labmda_sample
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TerraformでLambda自体は作るけど、ソースコードの管理やデプロイは別でやりたい

TerraformでLambda自体は作るけど、ソースコードの管理やデプロイは別でやりたい 背景 TerraformでAWSのLambda関数を作成していたときに、「LambdaのソースコードもTerraformで管理するの?」って思ったのがきっかけです。 まずTerraformはインフラ構成をコードで管理するツールです。ただそこにアプリケーションのソースコードが入ってもいいのかという議論があると思います。規模が小さい場合などは、Terraformですべて管理するという方法で問題ないかもしれませんが、下記の図のような、複数のサービスで構成されるサービスの場合、Terraformで管理したいスコープとCI/CDの単位に差が出てくると思います。 ということでこの記事では、TerraformでLambda関数の宣言はするが、サービスのソースコードやデプロイは分離した状態を目指します。 方針 Terraformでは空のLambdaのみを宣言、作成し、ソースコードはアップロードしません。 デプロイはaws cliを利用してS3にソースコードをアップロード、Lambdaの参照を切り替えるという実装にします。 実装 Terraform側 TerraformでLambda関数と必要な権限、S3バケット等を作成します。 本来変数にすべきところもすべてベタ書きにしていますがご了承ください。 コードの解説はコメントで書きます。 main.tf # providerの宣言 provider "aws" { region = "ap-northeast-1" profile = "default" } # S3バケットを生成 # CI/CD側でlambdaのソースコードを格納するための箱 # バケット名は変更してください resource "aws_s3_bucket" "test_bucket" { bucket = "okamoto-tf-test-bucket" acl = "private" tags = { Name = "okamoto-tf-test-bucket" } versioning { enabled = false # 本番運用する場合はtrue } } # ロールを生成 resource "aws_iam_role" "lambda" { name = "iam_for_lambda" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "lambda.amazonaws.com" }, "Effect": "Allow", "Sid": "" } ] } EOF } # 初回のみ利用する空のLambdaのファイルを生成 data "archive_file" "initial_lambda_package" { type = "zip" output_path = "${path.module}/.temp_files/lambda.zip" source { content = "# empty" filename = "main.py" } } # 生成した空のLambdaのファイルをS3にアップロード resource "aws_s3_bucket_object" "lambda_file" { bucket = aws_s3_bucket.test_bucket.id key = "initial.zip" source = "${path.module}/.temp_files/lambda.zip" } # Lambda関数を生成 # ソースコードは空のLambdaのファイルのS3を参照 resource "aws_lambda_function" "lambda_test" { function_name = "lambda_test" role = aws_iam_role.lambda.arn handler = "main.handler" runtime = "python3.8" timeout = 120 publish = true s3_bucket = aws_s3_bucket.test_bucket.id s3_key = aws_s3_bucket_object.lambda_file.id } これでterraform planを実行して生成されるリソースを確認します。 実行結果 $ terraform plan Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_iam_role.lambda will be created + resource "aws_iam_role" "lambda" { + arn = (known after apply) + assume_role_policy = jsonencode( { + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" } + Sid = "" }, ] + Version = "2012-10-17" } ) + create_date = (known after apply) + force_detach_policies = false + id = (known after apply) + managed_policy_arns = (known after apply) + max_session_duration = 3600 + name = "iam_for_lambda" + path = "/" + unique_id = (known after apply) + inline_policy { + name = (known after apply) + policy = (known after apply) } } # aws_lambda_function.lambda_test will be created + resource "aws_lambda_function" "lambda_test" { + arn = (known after apply) + function_name = "lambda_test" + handler = "main.handler" + id = (known after apply) + invoke_arn = (known after apply) + last_modified = (known after apply) + memory_size = 128 + package_type = "Zip" + publish = true + qualified_arn = (known after apply) + reserved_concurrent_executions = -1 + role = (known after apply) + runtime = "python3.8" + s3_bucket = (known after apply) + s3_key = (known after apply) + signing_job_arn = (known after apply) + signing_profile_version_arn = (known after apply) + source_code_hash = (known after apply) + source_code_size = (known after apply) + timeout = 120 + version = (known after apply) + tracing_config { + mode = (known after apply) } } # aws_s3_bucket.test_bucket will be created + resource "aws_s3_bucket" "test_bucket" { + acceleration_status = (known after apply) + acl = "private" + arn = (known after apply) + bucket = "okamoto-tf-test-bucket" + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + force_destroy = false + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + request_payer = (known after apply) + tags = { + "Name" = "okamoto-tf-test-bucket" } + website_domain = (known after apply) + website_endpoint = (known after apply) + versioning { + enabled = false + mfa_delete = false } } # aws_s3_bucket_object.lambda_file will be created + resource "aws_s3_bucket_object" "lambda_file" { + acl = "private" + bucket = (known after apply) + bucket_key_enabled = (known after apply) + content_type = (known after apply) + etag = (known after apply) + force_destroy = false + id = (known after apply) + key = "initial.zip" + kms_key_id = (known after apply) + server_side_encryption = (known after apply) + source = "./.temp_files/lambda.zip" + storage_class = (known after apply) + version_id = (known after apply) } Plan: 4 to add, 0 to change, 0 to destroy. Changes to Outputs: + arn = (known after apply) + aws_s3_bucket_object_etag = (known after apply) + aws_s3_bucket_object_id = (known after apply) + aws_s3_bucket_object_version_id = (known after apply) + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + website_domain = (known after apply) + website_endpoint = (known after apply) ───────────────────────────────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. 問題なさそうであれば、続けてterraform applyで適応していきます。 実行結果 $ terraform apply Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_iam_role.lambda will be created + resource "aws_iam_role" "lambda" { + arn = (known after apply) + assume_role_policy = jsonencode( { + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" } + Sid = "" }, ] + Version = "2012-10-17" } ) + create_date = (known after apply) + force_detach_policies = false + id = (known after apply) + managed_policy_arns = (known after apply) + max_session_duration = 3600 + name = "iam_for_lambda" + path = "/" + unique_id = (known after apply) + inline_policy { + name = (known after apply) + policy = (known after apply) } } # aws_lambda_function.lambda_test will be created + resource "aws_lambda_function" "lambda_test" { + arn = (known after apply) + function_name = "lambda_test" + handler = "main.handler" + id = (known after apply) + invoke_arn = (known after apply) + last_modified = (known after apply) + memory_size = 128 + package_type = "Zip" + publish = true + qualified_arn = (known after apply) + reserved_concurrent_executions = -1 + role = (known after apply) + runtime = "python3.8" + s3_bucket = (known after apply) + s3_key = (known after apply) + signing_job_arn = (known after apply) + signing_profile_version_arn = (known after apply) + source_code_hash = (known after apply) + source_code_size = (known after apply) + timeout = 120 + version = (known after apply) + tracing_config { + mode = (known after apply) } } # aws_s3_bucket.test_bucket will be created + resource "aws_s3_bucket" "test_bucket" { + acceleration_status = (known after apply) + acl = "private" + arn = (known after apply) + bucket = "okamoto-tf-test-bucket" + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + force_destroy = false + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + request_payer = (known after apply) + tags = { + "Name" = "okamoto-tf-test-bucket" } + website_domain = (known after apply) + website_endpoint = (known after apply) + versioning { + enabled = false + mfa_delete = false } } # aws_s3_bucket_object.lambda_file will be created + resource "aws_s3_bucket_object" "lambda_file" { + acl = "private" + bucket = (known after apply) + bucket_key_enabled = (known after apply) + content_type = (known after apply) + etag = (known after apply) + force_destroy = false + id = (known after apply) + key = "initial.zip" + kms_key_id = (known after apply) + server_side_encryption = (known after apply) + source = "./.temp_files/lambda.zip" + storage_class = (known after apply) + version_id = (known after apply) } Plan: 4 to add, 0 to change, 0 to destroy. Changes to Outputs: + arn = (known after apply) + aws_s3_bucket_object_etag = (known after apply) + aws_s3_bucket_object_id = (known after apply) + aws_s3_bucket_object_version_id = (known after apply) + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + website_domain = (known after apply) + website_endpoint = (known after apply) Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:yes aws_iam_role.lambda: Creating... aws_s3_bucket.test_bucket: Creating... aws_iam_role.lambda: Still creating... [10s elapsed] aws_s3_bucket.test_bucket: Still creating... [10s elapsed] aws_s3_bucket.test_bucket: Creation complete after 16s [id=okamoto-tf-test-bucket] aws_s3_bucket_object.lambda_file: Creating... aws_s3_bucket_object.lambda_file: Creation complete after 1s [id=initial.zip] aws_iam_role.lambda: Still creating... [20s elapsed] aws_iam_role.lambda: Still creating... [30s elapsed] aws_iam_role.lambda: Creation complete after 33s [id=iam_for_lambda] aws_lambda_function.lambda_test: Creating... aws_lambda_function.lambda_test: Creation complete after 6s [id=lambda_test] Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: arn = "arn:aws:s3:::okamoto-tf-test-bucket" aws_s3_bucket_object_etag = "6582ea9c16331bd37b52bf2c8bcca596" aws_s3_bucket_object_id = "initial.zip" aws_s3_bucket_object_version_id = "null" bucket_domain_name = "okamoto-tf-test-bucket.s3.amazonaws.com" bucket_regional_domain_name = "okamoto-tf-test-bucket.s3.ap-northeast-1.amazonaws.com" hosted_zone_id = "Z2M4EHUR26P7ZW" id = "okamoto-tf-test-bucket" region = "ap-northeast-1" 生成されたS3バケットとLabmdaをAWSコンソール上から確認します。 バケットが生成されzipファイルが置かれているとことがわかります。 空のLambdaが生成されています。 これでTerraform側は終了です。 デプロイ側 今回はlambdaのソースコードをsrc/main.py、パッケージ情報をrequirements.txtに、デプロイコマンドscripts/deploy.shで管理します。 フォルダ構成は以下のようになっています。 ├── requirements.txt ├── scripts │   └── deploy.sh └── src └── main.py またrequirements.txt、main.pyは以下です。最小限の構成です。 requirements.txt requests==2.25.1 main.py import requests def handler(*args, **kwargs): res = requests.get('https://api.github.com') return res.status_code 以下のドキュメントを参考にしてデプロイシェルを書きます。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-package.html deploy.sh # ディレクトリ情報の取得 CURRENT_PATH=$(cd $(dirname $0); pwd) ROOT_PATH=$CURRENT_PATH/.. # pythonパッケージのインストール pip install --target ./package -r $ROOT_PATH/requirements.txt --system # pythonパッケージのzip化 cd package zip ../deployment-package.zip -r ./* cd .. # ソースコードをzipファイルに追加 zip -g deployment-package.zip -j $ROOT_PATH/src/main.py # S3にアップロードしてlambda関数の参照を書き換える aws s3 cp ./deployment-package.zip s3://okamoto-tf-test-bucket/ aws lambda update-function-code --function-name lambda_test --s3-bucket okamoto-tf-test-bucket --s3-key deployment-package.zip # デプロイ時の一時ファイルを削除 rm deployment-package.zip rm -r package それでは実行します。 $ bash ./scripts/deploy.sh AWSコンソールを確認します。 deployment-package.zipがアップロードされていることがわかります。 うまくpackageされていることがわかります。 また実行してrequestsパッケージを参照しても落ちることなく実行できます。 また当然インフラ構成には変更がないのでデプロイ後もterraform planで差分が出ることはありません。 $ terraform plan aws_iam_role.lambda: Refreshing state... [id=iam_for_lambda] aws_s3_bucket.test_bucket: Refreshing state... [id=okamoto-tf-test-bucket] aws_s3_bucket_object.lambda_file: Refreshing state... [id=initial.zip] aws_lambda_function.lambda_test: Refreshing state... [id=lambda_test] No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and the remote system(s). As a result, there are no actions to take. まとめ 後半部分は、ただaws cliを使ってLambdaを更新しただけですが、TerraformとLambdaのソースコード・デプロイを分離させることができました。 今回作成したソースコードはこちらにあげておきます。 https://github.com/Cohey0727/terraform_labmda_sample
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutter for Web + AWS S3 + CloudFront + 独自ドメインで始める爆速PWA

はじめに Flutter for Webを使っておよそ3時間でPWAサービスをリリースします! ちょっと前までFlutterの名前も知らず、知ってからもiOS/Androidクロスプラットフォームという印象でした しかし、2021年3月のFlutter2リリースで正式にWeb対応したということを知りまして、将来性しか感じておりません! ということで独自ドメインとAWSを併用して爆速でPWAサービスをリリースしてみました PWAとは Progressive Web Appsの略で、ウェブサイトにも関わらずネイティブアプリのような挙動を実現する機能です アプリストアにリリースすることもなく、スマホのホーム画面にインストールできたり、プッシュ通知ができたりします ブラウザのUIがなくなる分、画面を最大限使えるのが個人的にはイチオシのポイントです ↓は今回作成したサンプルページの例ですが、PWA化した方が上下の表示領域が広いです . できることは限られますが、ストアの審査も要らずにネイティブ風のアプリが作れるのはアツいのではないでしょうか ちなみにサンプルはこちらに置いておきます 開発環境 Flutter 2.0.6 MacOS Big Sur 11.2.3 AndroidStudio 4.1.2 ちなみにApple M1チップですが、特に問題ありませんでした 作る Flutterのサンプルアプリを作る Flutterの導入 Flutterそのもののインストールについては公式もありますし、他の方も書いていると思うので割愛します Flutter公式を参照しながら、 $ flutter doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 2.0.6, on macOS 11.2.3 20D91 darwin-arm, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3) [✓] Xcode - develop for iOS and macOS [✓] Chrome - develop for the web [✓] Android Studio (version 4.1) [✓] Connected device (1 available) • No issues found! コマンドが全て問題なく通るようにしてあればOKです 加えて、AndroidStudioをエディタとして使うのが個人的に好きです こちらもやり方は割愛しますが、AndroidStudioにFlutterとDartのプラグインを入れることになります ※参考【Flutter】 アプリ開発入門 Hello Flutter!! Flutterプロジェクトを作成する AndroidStudioを起動し、「Flutter Application」を選択してプロジェクトを作成します Web向けのリリースだけならば、iOSやAndroidのコードについてはチェックを外してもOKです プロジェクトを作成したら、ターミナルでFlutter for Webを有効化します $ flutter config --enable-web Flutter for Webが有効化されていると、デバッグデバイスにChromeが追加されます $ flutter devices 1 connected device: Chrome (web) • chrome • web-javascript • Google Chrome 90.0.4430.93 動かしてみる AndroidStudioに戻ってFlutterプロジェクトを開くと、デバッグ対象にChromeが追加されています この状態でデバッグボタンを押せば、勝手にChromeが起動してアプリが動きます doctorをオールOKにするのがやや手こずりますが、環境構築はものすごく簡単ですね... ビルドする 動くことが確認できたら、Web向けにビルドします。下記のコマンドを叩くだけ $ flutter build web するとbuild/web/以下にindex.htmlに一通り出力されます しかもこの時、manifest.jsonが生成されていまして、なんとデフォルトでPWA対応しています。 あらやだFlutter恐ろしい子 もしindex.htmlがないよ!みたいなエラーが出たら、下記のコマンドを叩いて再度やってみてください $ flutter create . アプリルート以下のwebディレクトリが丸ごとできていないケースがあるようです Flutterの出番は一旦ここまでです AWS S3 + CloudFrontでFlutter for Webを静的ホスティングする ちなみにS3への直アクセス制限(Restrict Bucket Access)もやります S3バケットを作成する 適当な名前でS3バケットを作成します パブリックアクセスはブロックでOKです。後ほどCloudFront経由のみを許可します Flutter for Webをアップロードする 作成したS3バケットに、flutter build webで生成されたbuild/web/以下のファイルを全てアップロードします CloudFrontからS3にアクセスする CloudFrontのディストリビューション作成から、新規作成します OriginDomainName : 先ほど作成したS3バケット Restrict Bucket Access : Yes ※OriginDomainNameでS3を指定すると出てくる Origin Access Identity : Create a New Identity Grant Read Permissions on Bucket : Yes 作成に成功すると、S3の「アクセス許可」の「バケットポリシー」に下記が追加されます { "Version": "2008-10-17", "Id": "PolicyForCloudFrontPrivateContent", "Statement": [ { "Sid": "1", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity xxxxxxxxxx" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::バケット名/*" } ] } ちなみにS3静的ホスティング時のポリシーが残っているとアクセス拒否されることがあるようなので、S3にアップロードした時点でホスティングして確認した人はご注意ください そのほか画像には無いですが、下記の設定をしています 「General」→「Edit」→Default Root Object : index.html 「Behaviors」→「Edit」→Viewer Protocol Policy : Redirect HTTP to HTTPS この時点でCloudFrontのDomainNameからアクセスできることを確認してください ドメインを取得する お好みの独自ドメインを取得します 今回はムームードメインで適当に「flutter-pwa-sample.site」を取得しました 184円/年と激安 HTTPS対応する PWA化するにはHTTPS化が必要です Route53でホストゾーンを作る 取得したドメインを使ってRoute53でホストゾーンを作ります 証明書を発行する ホストゾーンができたら証明書を作成します CloudFrontから証明書を参照するために、ここだけ北部バージニアで行ってください AWS Certificate Managerで取得したドメインを入力し、 「DNSの検証」 「Route53でのレコードの作成」をすると、先ほどのホストゾーンにCNAMEレコードが追加され、自動的に検証が始まります 「発行済み」になればOKです。私の場合は数分程度で完了しました CloudFrontに証明書を紐づける CloudFrontのディストリビューション設定を開き、下記を設定します Alternate Domain Names : ドメイン名 Custom SSL Certificate : 先ほど作成したSSL証明書 ルートレコードを作る Route53でドメイン名ルートのレコードを作ります 独自ドメイン側を設定 最後に、独自ドメイン側にRoute53のネームサーバを設定して終了です Route53には最終的に4レコードあることになりますが、NSレコードの値を独自ドメイン側にも設定します ムームードメインの場合は、「コントロールパネル」→「ドメイン名」→「ネームサーバの設定変更」から、↓のような感じで設定すればOKです 末尾のピリオドは不要なのでご注意ください 数日かかるかも!と書かれていますが、私の場合はごはん食べている間に反映されていました 完了! ということで、証明書発行やネームサーバ設定の反映待ちの時間を入れても2~3時間で、PWAサービスを公開することに成功しました! Safariの場合はサイトを開いて「ホーム画面に追加」するだけで、ネイティブアプリのような見た目で表示することができるようになります . FlutterはUIの構築もやりやすく、個人や少人数チームであれば超有力な選択肢になると思っています (今回はWebですが、Win/MacOS/Linuxへもリリースできる) Web歴1年ちょいのまだまだ素人ですが、精進していきたいと思います
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

覚えておきたいAWSの用語

はじめに 自分の学習メモ用です。 とりあえず単語だけ並べています。 将来的に解説の記事をかければと思っています。 IAM ユーザー、グループ、ポリシー、ロール VPC CIDRブロック、サブネット、アベイラビリティーゾーン
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

キャッシュサーバーを配置する ハンズオン

準備・注意 前回のハンズオンの続きから EC2が2台立ち上がっており、RDSも利用可能、WebページもHTTPS通信で閲覧できること このハンズオンでは料金が約10〜11円/時間かかります 手順 今回はHTTPS通信でELBとの間にCloudFrontを設置して、EC2のWebページをキャッシュしている状態にしていきます 1, Route53でドメイン名とELBが紐づくレコードを修正して一旦、alb.aws-demo.gaに変更 2, CloudFront作成 3, Route53に独自のドメイン名とblog.aws-demo.gaが紐づくレコードを追加 以上の設定でクライアントが blog.aws-demo.ga にアクセスしたら、Route53の設定でCloudFrontに通信が流れて、CloudFront(alb.〜)からRoute53でELB配下のEC2のWebページが表示される。 ACMはblog.aws-demo.gaのようにドメイン名を固定できるが、ワイルドカード(*.aws-demo.ga)で作成することで複数のドメインをまかなえる。 CloudFrontでもHTTPS通信をするので、証明書が必要ですが、CloudFrontの証明書は発行元リージョンは北米のバージニアリージョンでアタッチします。(※ELBの証明書は日本の東京で発行します) ↓ 以降のデモでは両方のACMをワイルドカードに作り直します。 Route53でドメイン名とELBが紐づくレコードを修正して、alb.aws-demo.gaに変更 Route53< ホストゾーンを開く ハンズオンで設定したホストゾーンで、2つ(プライマリとセカンダリ)のフェイルオーバールーティングを変更していく チェックボックスにチェックをいれ、編集ボタンを選択 レコード名にblogと入っている文字をalb(任意)に変更して変更の保存を選択 もう一つのレコードも同じように変更したら一旦放置して次の行程に進みます ACMに*を使用し再作成 ロードバランサーにアタッチされている証明書が(blog.aws-demo.ga)という固定の名前となっているため、どのサブドメインでも使用可能なように*の証明書を新しく作成していきます EC2< ロードバランサーの画面から 既存のリスナーを一度削除し、リスナーを新しく追加していきます プロコトル:ポートをHTTPSを選択 デフォルトアクションの設定を転送先を指定し、ターゲットグループ(TG-1)を選択 下の新しいACM証明書をリクエストを選択してACMの設定をしていく 証明書のリクエストでドメイン名を*.aws-demo.gaと入力し次へを選択 検証方法をDNSの検証のまま次へを選択 タグは追加せず、確定とリクエストを選択 検証< タブを開き、Route53でのレコードの作成を選択し作成する 続行を選択すると検証保留中となります Route53の画面に戻る 古い固定ドメイン(blog.~~~)で始まるドメインのレコードが残っているので削除します ACMの画面に戻る 新しく作成した*で始まる証明書の状況が発行済みに変わっています。 blogで始まる証明書はアクションから削除しておきます リスナー追加画面に戻る デフォルトのSSL証明書に作成した*から始まるものを選択し、保存します (確認)この時点でalb.aws-demo.gaのURLから閲覧できることを確認します CloudFront作成 準備 CloudFront用の証明書を作成するために、まず現在のリージョンを米国東部(バージニア北部)リージョンに変更 証明書のプロビジョニングを今すぐ始めるを選択します 証明書のリクエストを選択 ドメイン名の追加で上と同様に*.aws-demo.gaと入力 進めていき、検証でRoute53でのレコードの作成を選択し検証用のレコードを追加する 証明書の状況が発行済みとなっていることを確認します 作成 CloudFrontでcreate Distributionを選択 delivery methodを選択する画面でWebのGet Startedを選択します ※RTMP・・・Adobeが開発しているストリーミングのプロトコルのこと Origin Dmain Name(Originはどのドメインか)を今回はalb.aws-demo.gaと入力 Origin Protocol PolicyのHTTPS Onlyにチェックをいれる Viewer Protocol PolicyのHTTPS Onlyにチェックをいれる Cache PolicyのCreate a new policyを選択 今回はテスト用に短いTTLのキャッシュポリシーを作成します InfoのNameにShortCasheと名付け TTL Settingsの数値を60(秒)と設定し他の項目はデフォルトのままCreate cache policyを選択 Cashe Policy選択画面に戻り更新ボタンを押すと、今作成したShortCacheがあるので選択 SSL Certificateの設定項目をCustom SSLにチェックをいれ、入力欄に*.aws-demo.ga(バージニアで作成した証明書)を選択 Alternate Domain Names(CNAMEs)の入力欄にblog.aws-demo.gaを入力 以上の設定でCloudFrontを作成します StatusがDeployedとなれば完了(5分ほどかかる) 完了したCloudFrontを選択すると設定内容を確認できる (確認)Domain Nameにある文字列にアクセスしてもCloudFrontにアクセスできる Route53に独自のドメイン名とblog.aws-demo.gaが紐づくレコードを追加 Route53でレコードを作成していく ルーティングポリシーはシンプルルーティングを選択して次へ シンプルなレコードを定義を選択 レコード名はこれまでの通りblog.aws-demo.gaとなるよう入力 値/トラフィックのルーティング先はCloudFront distributionへのエイリアスを選択 リージョンを米国東部(バージニア北部)を選択すると先ほど作成したCloudFrontのドメイン名が候補に出てきます あとはデフォルトの状態でレコードを定義を選択し、レコードの作成を行います ↓(確認)↓ blog.〜始まるドメインはCloudFrontと紐付き、キャッシュされた状態 alb.〜で始まるドメインは直接ロードバランサーのドメインと紐付いた状態となっています テスト blogで始まるドメインとalbで始まるドメインの比較を行います。 EC2インスタンスにログインしてindex.phpを編集して反映されているか確認します。 すぐに変更が反映されるのが、alb.〜から始まる方、 blogで始まる方はすぐには反映されません。 これはキャッシュポリシーで設定した60秒を経過しないとキャッシュの期限が切れるまで反映されません。 60秒経過するとalb.〜で始まるドメイン名のページにも変更が反映されます。 参考 この記事はAWS初学者を導く体系的な動画学習サービス 「AWS CloudTech」の課題カリキュラムで作成しました。 https://aws-cloud-tech.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】NATゲートウェイとは、インターネットゲートウェイとの違い

プログラミング勉強日記 2021年5月2日 NATゲートウェイとは  NATゲートウェイについて調べてみるとAWSの公式ドキュメントでは以下のように出てくる。 ネットワークアドレス変換 (NAT) ゲートウェイを使用して、プライベートサブネットのインスタンスからはインターネットや他の AWS のサービスに接続できるが、インターネットからはこれらのインスタンスとの接続を開始できないようにすることができます。 引用:https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-gateway.html  VPC内に構成したプライベートサブネットからインターネットに接続するために必要なものがNATゲートウェイ。(昨日の記事でプライベートサブネットについて触れている。) インターネットゲートウェイとは  VPC内からインターネットに接続するためのゲートウェイ。インターネットゲートウェイを使うことでVPC内のシステムがグローバルIPを使えるようになる。 NATゲートウェイとインターネットゲートウェイの使い分け VPC内からインターネット通信をする場合はインターネットゲートウェイ プライベートサブネットからインターネット通信をする場合はNATゲートウェイ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon AthenaでCTASが失敗したときの対応方法

概要 Amazon AthenaにおいてCTASでデータ作成をしているのですが、CTASが失敗したときに何が起き得るか、どうすればよいか、というトラブルシュートの話を整頓しました。 CTASとは Athenaで2018年に利用できるようになった仕組みです。 Parquet形式への変換を、パーティションも考慮し簡単に作成することができます。 参考記事:Amazon Athena が待望のCTAS(CREATE TABLE AS)をサポートしました! どういう運用をしているか パーティションが100個を超えるテーブルのデータ作成 CTASにおけるパーティションは100個が上限なので、何回かに分けてCTASをする必要がある 今回のケースにおいては、こうしたデータ作成を月に1回行っています。 エラーが起きるとどういう状態になるか 2通りに分かれます エラーの種類自体は様々ですが、エラーメッセージにかかわらず「成功しなかった」時点でこれらの確認は必要となろうかと思います。 1レコードも入らない ファイル作成自体が失敗しているケースです。 この場合、単純に再実行をすればよいので楽ですね。 中途半端にINSERTされる 運用を始めて数ヵ月して観測しました。 エラーメッセージを見るとそういうケースがありそうな記述はあるのですが、それまでエラーになっても「1レコードも入らない」しか体験したことがなかったので、このケースの対応も考えることとなりました。 この手の運用のチェックとして「xxxレコード以上入ったらOK」という方法だけを取ってしまうと、チェックがすり抜けることとなります。もちろん、そのような事故が起きた訳ではありませんが「ほんの少しだけ欠損する」というケースがあるため「なんだ入ってるじゃん、このまま行こう」などと誘惑に負けてはなりません。ステータスが失敗になっている以上、失敗なのでやり直すべきです。 対応方法 2021.04時点では、AthenaのCTASを利用すると、作成されたParquetファイル名の頭に「YYYYMMDD_HHMMSS」といった作成日時(っぽいもの)が入ります。また、当然s3に作成されたファイルのタイムスタンプがあります。 これらを利用して、対象ファイルを削除していきます。 基本方針 エラーメッセージを見ると If a data manifest file was generated at 's3://xxxxxxxxxxx/result/xxxxxxxxxx.csv', you may need to manually clean the data from locations specified in the manifest. Athena will not delete data in your account. とあるので、この通りにやれば良いは良いはずです。 ただAWS側から「手動で消してね」と言われている以上、こちらの責任として諸々確認をしたい気持ちになります。 対象データを実際に確認する CTASをしようとしてエラーになったテーブルのデータの状況を確認します。 今回のケースでは月次の作業となるため、YYYY-MM に注目して影響調査をしています。 対象パーティションが明確な場合 以下の通り直接そのパーティションにlsとgrepをします。 (もちろん、grepなしで事前にlsをしていますが記事上は割愛) aws s3 ls s3://valuesccg-example-bucket/athena/data/tablename/partitionkey=aaaa/ |grep 2021-03 2021-03-02 10:52 124711 s3://valuesccg-example-bucket/athena/data/tablename/partitionkey=aaaa/20210302_105142_00026_3pshx_032aa1ba-db43-45a1-8733-18ebd2fde5a8 2021-03-02 10:52 118498 s3://valuesccg-example-bucket/athena/data/tablename/partitionkey=aaaa/20210302_105142_00026_3pshx_11e4b164-e334-4c4d-8bcd-6140a0aac2d8 2021-03-02 10:52 114402 s3://valuesccg-example-bucket/athena/data/tablename/partitionkey=aaaa/20210302_105142_00026_3pshx_1a83aa8a-facb-49aa-a54c-2b816e9e6626 ... この時「エラーになったのにデータが作成されている!」という状況であれば、消す必要があります。 対象パーティションが不明確な場合 こっちをやれば前述のものを兼ねるのでこっちだけやれば良い説はありますが、たとえば3個のCTASのうち1個だけ失敗、というケースにおいては他2つの結果は返ってきてしまうので、両方やるとリカバリのミスを減らせると思います。 aws s3 ls s3://valuesccg-example-bucket/athena/data/tablename/ --recursive |grep 2021-03 ※結果割愛 対象ファイルを消す さて、実際に消していくのですが、マニフェストファイル(xxxxx-manifest.csv)の中身は、対象ファイル名が羅列されています。 単純な方法として、マニフェストファイルの頭に aws s3 rm をつけたり、ファイル自体を読み込んで aws s3 rm をしていくスクリプトを書く形になるかと思います。 しかし、これが1000個とか10000個になってくると、結構時間がかかります。 そこで、上記の確認をそのまま削除条件にしてしまえばよいわけで、以下の通りのコマンドで消しに行きます。ファイル名に日時があるので、簡単に対象を指定することができます。 #aws s3 rm s3://valuesccg-example-bucket/athena/data/tablename/partitionkey=aaaa/ --exclude "*" --include "*20210302_*" --recursive --dryrun もちろん --dryrun をつけて、事前に対象ファイルを確認しましょう。 そして、--dryrunをして確認したファイルの数と、マニフェストのレコード数が一致していたら、より安心して消しにいくことができます(より確実にするには、整形してソートをしてdiffをとるとよいと思います)。 雑感 知った当初は(たまたまか)エラーが少なかったこともあり便利でCTASを多用していたのですが、こうしたエラーの確率に振り回されてしまうほか、スキャンが発生するため結構お金もかかります。デメリットというかトレードオフというか。 というわけで今後はCTASを用いない方法へと切り替えていこうかなと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pycryptoを入れようとするとRuntimeError: autoconf errorが出る

自分用メモ 発生したこと pip install pycrypto を実行すると raise RuntimeError("autoconf error") RuntimeError: autoconf error と、他にも色々エラーが出て入れられない。 環境 AWS Linux 解決策 Step1. gccをインストール pycryptoをインストールするにはgccが必要なようなので、以下のコマンドでインストール sudo yum install gcc Step2. python3-develをインストール sudo yum install python3-devel 再び pip install pycrypto で行けた。 参考 他にも pip install python-dev-tools もしたのでもしかしたらいるかもしれない(問題の切り分けができていません) お世話になった先人の知識たち https://techacademy.jp/magazine/23338 http://y0m0r.hateblo.jp/entry/20130707/1373192687
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Former2 CLIでAWSリソースからテンプレートファイルを出力してみる

概要 AWSで既に作成された環境をFormer2 CLIを使用してテンプレートファイルとして出力したい Former2のGUIでやりたい方はこの記事が丁寧なのでオススメです 説明 Former2とは? 既存のAWSリソースからIaCのテンプレートファイルを作成するサードパーティ製ツール GCPでいうところのTerraformer former2はCloudFormationだけでなく、TerraformやAWS CDKのテンプレートファイルとして出力できる 以下対応しているテンプレートファイルの一覧 CloudFormation Terraform Troposphere CDK (Cfn Primitives) - TypeScript, Python, Java, C# CDK for Terraform - TypeScript Pulumi - TypeScript Diagram - embedded version of draw.io Former2 CLIとは? その名の通りFormer2をCommandLineで使用するためのライブラリ 公式に Former2 CLIはまだ実験的なものなので使用は自己責任で、と書いてあるのでリスクが怖い場合使わなければいいと思います。(今のところ自分の環境は特に不具合なしです) former2/README.md at master · iann0036/former2 Former2 CLIが対応しているAWSのリソース一覧 former2/README.md at master · iann0036/former2 大抵のサービスは対応しているようにみえる 内容 AWS開発用に自作したDocker環境で行います I-s-23/aws-container-env: aws-cli development environment to Remote Container AWSでIaCやCICDを行うために普段使いしている環境で以下の通り必要な物は揃っておりますのでよかったら使って下さい aws-cli sam-cli cloudFormation(Linterや入力補完などの設定) Former2 CLI node.js python3 作業手順 AWSアカウント作成&最低限の設定を行う AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ - Qiita この記事を参考にGUIで行いました。 Former2 CLIが使用するIAMのユーザー作成&権限付与 Docker環境でFormer2 CLI実行 Former2 CLIが使用するIAMのユーザー作成&権限付与 Former2 CLI公式がReadOnlyAccessの権限を推奨しているのでReadOnlyAccess権限を付与 ポリシーでReadOnlyAccess検索すればAWSのリソースすべてに対するReadOnlyAccessを検索可能なのでそれを押下する(コンソールだと下の方にReadOnlyAccess権限があります) Docker環境でFormer2 CLI実行 RemoteContainer 環境を使用する GitHubからRemoteContainer 環境のリポジトリをCloneする I-s-23/aws-container-env: aws-cli development environment to Remote Container git clone git@github.com:I-s-23/aws-container-env.git VSCodeでCloneしたリポジトリを開く コマンドパレットを開く Windows:Ctrl + Shift + P MAC:Cmd + Shift + P Remote-Containers; Open Folderin Containerと入力する .devcontainerディレクトリの一つ上の階層を選択 これでRemoteContainer の環境が起動します aws-cliの認証を行う コンテナ内で以下のコマンドを実行してAWSの認証を通します コマンド押下後、AWS Access Key IDとAWS Secret Access Keyの入力を求められるので、事前に作成したIAMユーザーのアクセスキーを入力します aws configure Former2 CLIでテンプレートファイルを出力する --output-cloudformationでCloudFormationで出力するテンプレートファイル名を指定 --output-terraformはTerraformで出力する場合のオプション オプションについての詳細 以下のコマンドはFormer2 CLIが対応しているAWS内のすべてのリソースを出力するコマンドなので実行に時間がかかります former2 generate \ --output-cloudformation "cloudformation.yml" \ --output-terraform "terraform.hcl" \ --output-raw-data "debug.json" \ --sort-output コマンドの実行結果です(アカウント IDなど秘匿情報は手動で隠しております) この記事の内容しか行っていないのでCloudTrailTrailの設定やIAMユーザー、Log出力のS3バケットくらいしかないですね cloudformation.yml AWSTemplateFormatVersion: "2010-09-09" Metadata: Generator: "former2" Description: "" Resources: IAMAccessKey: Type: "AWS::IAM::AccessKey" Properties: Status: "Inactive" UserName: "AWSTESTUser" IAMAccessKey2: Type: "AWS::IAM::AccessKey" Properties: Status: "Active" UserName: "AWSTESTUser" CloudTrailTrail: Type: "AWS::CloudTrail::Trail" Properties: TrailName: "cloudtrail-management-events" S3BucketName: !Ref S3Bucket IncludeGlobalServiceEvents: true IsMultiRegionTrail: true EnableLogFileValidation: false IsLogging: true IAMUser: Type: "AWS::IAM::User" Properties: Path: "/" UserName: "AWSTESTUser" ManagedPolicyArns: - "arn:aws:iam::aws:policy/ReadOnlyAccess" OpsWorksUserProfile: Type: "AWS::OpsWorks::UserProfile" Properties: AllowSelfManagement: false IamUserArn: "arn:aws:iam::************:user/AWSTESTUser" SshUsername: "AWSTESTUser" S3StorageLens: Type: "AWS::S3::StorageLens" Properties: StorageLensConfiguration: AccountLevel: BucketLevel: {} Id: "default-account-dashboard" IsEnabled: true S3BucketPolicy: Type: "AWS::S3::BucketPolicy" Properties: Bucket: !Ref S3Bucket PolicyDocument: Version: "2012-10-17" Statement: - Sid: "AWSCloudTrailAclCheck20150319" Effect: "Allow" Principal: Service: "cloudtrail.amazonaws.com" Action: "s3:GetBucketAcl" Resource: !Sub "arn:aws:s3:::${S3Bucket}" - Sid: "AWSCloudTrailWrite20150319" Effect: "Allow" Principal: Service: "cloudtrail.amazonaws.com" Action: "s3:PutObject" Resource: !Sub "arn:aws:s3:::${S3Bucket}/AWSLogs/************/*" Condition: StringEquals: "s3:x-amz-acl": "bucket-owner-full-control" S3Bucket: Type: "AWS::S3::Bucket" Properties: BucketName: "aws-cloudtrail-logs-************-b134f084" ElastiCacheUser: Type: "AWS::ElastiCache::User" Properties: UserId: "default" UserName: "default" Engine: "redis" AccessString: "on ~* +@all" NoPasswordRequired: true Terraformの形式でもちゃんと出力されているようです terraform.hcl # https://www.terraform.io/downloads.html provider "aws" { region = "us-east-1" } resource "aws_iam_access_key" "IAMAccessKey" { status = "Inactive" user = "AWSTESTUser" } resource "aws_iam_access_key" "IAMAccessKey2" { status = "Active" user = "AWSTESTUser" } resource "aws_cloudtrail" "CloudTrailTrail" { name = "management-events" s3_bucket_name = "aws-cloudtrail-logs-***********-b134f084" include_global_service_events = true is_multi_region_trail = true enable_log_file_validation = false enable_logging = true } resource "aws_iam_user" "IAMUser" { path = "/" name = "AWSTESTUser" tags {} } resource "aws_opsworks_user_profile" "OpsWorksUserProfile" { allow_self_management = false user_arn = "arn:aws:iam::***********:user/AWSTESTUser" ssh_username = "AWSTESTUser" } resource "aws_s3_bucket_policy" "S3BucketPolicy" { bucket = "aws-cloudtrail-logs-***********-b134f084" policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AWSCloudTrailAclCheck20150319\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"cloudtrail.amazonaws.com\"},\"Action\":\"s3:GetBucketAcl\",\"Resource\":\"arn:aws:s3:::aws-cloudtrail-logs-***********-b134f084\"},{\"Sid\":\"AWSCloudTrailWrite20150319\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"cloudtrail.amazonaws.com\"},\"Action\":\"s3:PutObject\",\"Resource\":\"arn:aws:s3:::aws-cloudtrail-logs-***********-b134f084/AWSLogs/***********/*\",\"Condition\":{\"StringEquals\":{\"s3:x-amz-acl\":\"bucket-owner-full-control\"}}}]}" } resource "aws_s3_bucket" "S3Bucket" { bucket = "aws-cloudtrail-logs-***********-b134f084" } もちろん特定のサービスを指定して出力することも可能です --servicesで対象にしたいAWSのリソースを指定 former2/README.md at master · iann0036/former2 --exclude-servicesで対象外にしたいAWSのリソースを指定 EC2を対象としてテンプレートファイルを出力する場合の例 former2 generate \ --output-cloudformation "cloudformation.yml" \ --services "EC2" \ --sort-output EC2関連のリソースのみがテンプレートファイルとして出力されているのがわかります cloudformation.yml AWSTemplateFormatVersion: "2010-09-09" Metadata: Generator: "former2" Description: "" Resources: EC2NetworkInterface: Type: "AWS::EC2::NetworkInterface" Properties: Description: "" PrivateIpAddress: !GetAtt EC2Instance.PrivateIp PrivateIpAddresses: - PrivateIpAddress: !GetAtt EC2Instance.PrivateIp Primary: true SubnetId: "subnet-836082d9" SourceDestCheck: true GroupSet: - "sg-4b844008" EC2NetworkInterfaceAttachment: Type: "AWS::EC2::NetworkInterfaceAttachment" Properties: NetworkInterfaceId: "eni-0686f848cd1ac4064" DeviceIndex: 0 InstanceId: !Ref EC2Instance DeleteOnTermination: true EC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: "ami-00f045aed21a55240" InstanceType: "t2.micro" AvailabilityZone: "ap-northeast-1c" Tenancy: "default" SubnetId: "subnet-836082d9" EbsOptimized: false SecurityGroupIds: - "sg-4b844008" SourceDestCheck: true BlockDeviceMappings: - DeviceName: "/dev/xvda" Ebs: Encrypted: false VolumeSize: 8 SnapshotId: "snap-0ac31d416f3b41c6c" VolumeType: "gp2" DeleteOnTermination: true HibernationOptions: Configured: false EnclaveOptions: Enabled: false EC2Volume: Type: "AWS::EC2::Volume" Properties: AvailabilityZone: !GetAtt EC2Instance.AvailabilityZone Encrypted: false Size: 8 VolumeType: "gp2" SnapshotId: "snap-0ac31d416f3b41c6c" MultiAttachEnabled: false EC2VolumeAttachment: Type: "AWS::EC2::VolumeAttachment" Properties: VolumeId: !Ref EC2Volume InstanceId: !Ref EC2Instance Device: "/dev/xvda" 所感 AWSを使っているけどIaCはしていない、というプロジェクトなどで特にオススメのツールです AWS公式のツールではないらしいが、OSSなので最高 多種多様なAWSのリソースに対応していて便利 テンプレートファイルの出力形式に関してCloudFormationやTerraformだけでなくAWS CDKとかにも対応しているのが素晴らしい 初めて使うAWSのリソースとかをとりあえずGUIで設定後、Former2でIaCにするのが便利な使い方と思った。 自分はCloudFormationしかわかっていないので同じリソースでTerraformのテンプレートファイルとかと比較すればTerraformの勉強にもよさそうだと思いました 余談 Former2 CLIのインストールに関して 本記事のようにRemoteContainer のDocker環境でなくともnpmかFormer2公式のコンテナを使用することも可能 どちらの場合でもAWSの認証を通さないと使えないのでaws-cliのインストール推奨 ~/.aws/credentialsにAWSの認証情報があればいいので、この部分をコンテナ内にマウントする方法もある npmでのインストール方法 npmの場合ローカル環境にNode.jsが必要 npm install -g former2 公式のDockerを使用 公式のコンテナの使用方法 docker build https://github.com/iann0036/former2.git#master:cli -t iann0036/former2:latest 経緯 今回は社内や社外からFormer2が使えそうか知りたいという意見がきたのでQiitaとして投稿しました あと Former2の使い方説明の記事は何個かあったがFormer2 CLIの記事が見つからなかったというのもあります ちなみに弊社でFormer2の需要が発生した経緯は以下の感じです 協力会社と開発したAWS環境で業務効率化のためCICDを行いたいという依頼がくる 協力会社も弊社もGUIで環境設定や変更を行っていたらしく、CloudFormationやaws-cliの使い方がわからないという返答 誰もCLIでの操作方法を知らないのであればTerraformerのように既存のAWSリソースからIaC化すればいいと考えて調査する Former2がよさそうと気付いて試す 参考資料 AWS CloudFormation を開発・テストする際に役立つツール纏め - Qiita former2/README.md at master · iann0036/former2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Athenaのスキャンされるデータ量の検証とParquetのcount(1)が便利という話

概要 Amazon Athenaを本格的に運用に乗せてくると、データを更新した際などのレコード件数などの確認は重要となってきます。 しかしながら、扱いが困るくらい大きなデータだからこそAthenaなどを使っているわけで、データの確認に都度お金が掛かると、必要な事とはいえ心理的に阻害されることがあるかもしれません。 今回、運用していて「なるほどこのケースだとData scannedが0になるのか」と気づいた点がいくつかあったので、スキャンされるデータ量の検証もしつつ、まとめてみました。 お断り 公式ドキュメントにおいては 特定ケースでスキャン量がゼロになる とまで明言されているものを見つけられなかったので(あるかもしれません)2021年5月時点での仕様、かつ私が扱っているデータセット要因によるものである可能性もあります。 結論 Parquet形式のデータの場合、whereやgroup byなどを付けない単純なcount(1)のSELECTであればData scanned 0KBでレコード数が確認できる。 パーティションのキーであればwhereで指定しても、group by をしても同様にData scanned 0KBとなる。 おさらい(Athenaの料金とスキャン) 公式にわかりやすくまとめられています。 https://aws.amazon.com/jp/athena/pricing/ 一方で「スキャン」という単語に統一されており、「課金体系はスキャンされるデータ量だよ~」と説明はできますが「じゃあ例えばこういうクエリっていくらかかるの?」と問われると、回答が難しいと感じます。 というわけで、実際に見てみました。 スキャンの発生検証 以下のパターンで試していきます。 対象テーブルと調査クエリ 50MB_NOT_PARTITIONED_TABLE レコード数 250,000件 パーティションなし TSV 実行クエリ 1. SELECT * FROM 50MB_NOT_PARTITIONED_TABLE limit 10; 2. SELECT * FROM 50MB_NOT_PARTITIONED_TABLE limit 1000; 3. SELECT * FROM 50MB_NOT_PARTITIONED_TABLE limit 10000; 4. SELECT * FROM 50MB_NOT_PARTITIONED_TABLE ; 5. SELECT column1 FROM 50MB_NOT_PARTITIONED_TABLE limit 10; 6. SELECT column1 FROM 50MB_NOT_PARTITIONED_TABLE limit 1000; 7. SELECT column1 FROM 50MB_NOT_PARTITIONED_TABLE limit 10000; 8. SELECT column1 FROM 50MB_NOT_PARTITIONED_TABLE ; 9. SELECT count(1) FROM 50MB_NOT_PARTITIONED_TABLE ; 250MB_PARTITIONED_TABLE_TSV レコード数 800,000件 パーティションあり 指定したパーティション内のレコード数200,000件/67.99MB) TSV 実行クエリ 1. SELECT * FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' limit 10; 2. SELECT * FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' limit 1000; 3. SELECT * FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' limit 10000; 4. SELECT * FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' ; 5. SELECT column1 FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' limit 10; 6. SELECT column1 FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' limit 1000; 7. SELECT column1 FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' limit 10000; 8. SELECT column1 FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' ; 9. SELECT count(1) FROM 250MB_PARTITIONED_TABLE_TSV where partitionkey = 'xxx' ; 10. SELECT count(1) FROM 250MB_PARTITIONED_TABLE_TSV ; 11. SELECT partitionkey,count(1) FROM 250MB_PARTITIONED_TABLE_TSV group by 1 ; NGB_PARTITIONED_TABLE_PARQUET レコード数 200,000,000件 パーティションあり 指定したパーティション内のレコード数 1,900,000 万件/ 容量未調査) Parquet 実行クエリ 1. SELECT * FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' limit 10; 2. SELECT column1 FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' limit 10; 3. SELECT column1 FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' limit 1000; 4. SELECT column1 FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' limit 10000; 5. SELECT column1 FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' ; 6. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' ; 7. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET ; 8. SELECT partitionkey,count(1) FROM NGB_PARTITIONED_TABLE_PARQUET group by 1 ; 9. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' and notpartitionkey = 'yyy'; 10. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey = 'xxx' and notpartitionkey is not null; 11. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where notpartitionkey = 'yyy'; --※このクエリは危険です 12. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where notpartitionkey is not null;--※このクエリは危険です 13. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey is not null ; 14. SELECT count(1) FROM NGB_PARTITIONED_TABLE_PARQUET where partitionkey like '%'; 実行結果 上記で書かれたSQLについて、10回程度実行し平均、最大、最少を求めてます。 試行回数も少ないのと、どうもテーブルへの格納状況によっても変わるのかな~という感じもしており(※ただの感覚です)参考程度かもです。 50MB_NOT_PARTITIONED_TABLE No. select limit 平均(MB) 最大(MB) 最小(MB) 1 * 10 1.37 2.42 0.80 2 * 1000 1.68 2.39 1.20 3 * 10000 3.88 5.60 2.80 4 * (なし) 51.38 51.38 51.38 5 column1 10 11.38 21.97 3.69 6 column1 1000 10.31 14.69 7.30 7 column1 10000 14.32 32.98 7.32 8 column1 (なし) 51.38 51.38 51.38 9 count(1) - 51.38 51.38 51.38 カラムを指定するよりも * によるSELECTのほうがスキャン量が少なくなる、という面白い結果になりました(違うサイズのカラムなどを選択しても同様でした)。 おおよそ limit 1000 くらいまではあまりスキャン量は変わらないようです。 TSVの場合は、count(1) としてもそのテーブルをすべてスキャンすることとなるようです。 250MB_PARTITIONED_TABLE_TSV No. select limit where 平均(MB) 最大(MB) 最小(MB) 1 * 10 partitionkey='xxx' 1.56 1.95 1.29 2 * 1000 partitionkey='xxx' 1.367 1.95 1.29 3 * 10000 partitionkey='xxx' 4.69 9.13 3.9 4 * (なし) partitionkey='xxx' 67.99 67.99 67.99 5 column1 10 partitionkey='xxx' 29.87 39.58 17.99 6 column1 1000 partitionkey='xxx' 30.23 57.57 17.99 7 column1 10000 partitionkey='xxx' 35.27 39.58 17.99 8 column1 (なし) partitionkey='xxx' 67.99 67.99 67.99 9 count(1) - partitionkey='xxx' 67.99 67.99 67.99 10 count(1) - (なし) 256.89 256.89 256.89 11 partitionkey,count(1) - (なし) 256.89 256.89 256.89 中身はTSVなので、基本的に結果は前項と同じになります。 where句を外すと、partitionkeyでgroup by しようが当然すべてのデータをスキャンすることとなります。 NGB_PARTITIONED_TABLE_PARQUET No. select limit where 平均(MB) 最大(MB) 最小(MB) 1 * 10 partitionkey='xxx' 8.92 12.69 5.00 2 column1 10 partitionkey='xxx' 0.40 0.82 0.12 3 column1 1000 partitionkey='xxx' 0.99 3.28 0.23 4 column1 10000 partitionkey='xxx' 0.51 0.68 0.25 5 column1 (なし) partitionkey='xxx' 3.28 3.28 3.28 6 count(1) (なし) partitionkey='xxx' 0.00 0.00 0.00 7 count(1) - (なし) 0.00 0.00 0.00 8 partitionkey,count(1) - (なし) 0.00 0.00 0.00 Parquetにすると、列数にもよりますがスキャン量が激減します。 limitをかけても、その項目におけるフルスキャンが走る、ような挙動が時々ありました(No.3の最大がそれ)。 count(1) は、whereをつけずにテーブル全体に行ったり、パーティションを指定した場合にスキャンが0KBになります。 count(1)はパーティションキーでgroup by をしてもスキャンが0KBになります。 以下おまけ。 No. select where 結果(MB) 9 count(1) partitionkey = 'xxx' and notpartitionkey = 'yyy' 6.05 (何度やっても) 10 count(1) partitionkey = 'xxx' and notpartitionkey is not null 6.05 (何度やっても) 11 count(1) notpartitionkey = 'yyy' ※ 12 count(1) notpartitionkey is not null ※ 13 count(1) partitionkey is not null 0.00 14 count(1) partitionkey like '%' 0.00 パーティションキー以外をwhere句につけると、スキャンが発生します。これは指定するカラムによって変わり、SELECTですべて取得した時の値と一致しているように見えます(9と10で結果が一致しているのも、どういう挙動かが分かる感じがしますね)。 ※はパーティションキーを指定していないので、数秒でキャンセルしても数十MBに。やっちゃだめです。 パーティションキーは、それ自体をwhere句に入れあれこれいじってもスキャン量は0のままのようでした。 雑感 スキャン量を減らす、ということは、そのままパフォーマンスチューニングに繋がるわけです。こうした挙動の肌感覚を持っておくことはAthenaを使っていくうえで重要と考えています。 ちなみにParquetが強いのは、こちらのドキュメント を見ると、Predicate pushdown関連の恩恵なのかなと思います。統計情報のみで完結する、ということなのだと思うのですが、リソース課金ではなくスキャンデータ量課金の場合、こうした恩恵に預かれるのですね。 そして、リソース課金のサービスも多いなか、必然、スキャンデータ量課金だと使う側の工夫や設計も変わってくるはずで、そのあたりが面白いなと感じます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSを理解する[webサーバー編]

概要 はじめに、この記事はAWS初学者が備忘録として作成しています。 ですが、私のような初学者のために少しでもなればと思い、公開しています。 不適切や、わかりにくい点を見つけた場合はご指摘頂ければ幸いです。 ネットワークを構築する webサーバーソフトをインストールする?今ここ WEBサーバーソフトをインストールする Apache HTTP Serverのインストール sshで接続後に以下のコマンドを実行する $ sudo yum -y install httpd 上記で指定しているhttpdはApacheを構成する実行ファイル名(つまりApacheがインストールされる) 加えて-yオプションはユーザーの確認なしにインストールする指定です。 Apacheを起動する $ sudo systemctl start httpd.service systemctlコマンドは指定したコマンドを起動(start)、停止(stop)、再起動(restart)するコマンド 自動起動するように構成する $ sudo systemctl enable httpd.service systemctlコマンドは、自動起動について設定(enable)、設定解除(disable)、設定の確認(list-unit-files)を指定するコマンド 正しく構成されたか確認する $ sudo systemctl list-unit-files -t service 実行結果において、httpd.serviceがenabledになっていれば、自動起動が有効になった証。 プロセスを確認する $ ps -ax Linuxでは、psコマンドを使用すると、実行中のプロセスを確認でき、-axオプションは-a(全て)と -x(他の端末に結びつけられているプロセスも表示する) 上記の代わりに以下のコマンドを時効すると、httpdを含む行だけを出力できる。 $ ps -ax | grep httpd ネットワークの待受状態を確認する エンドユーザーがwebブラウザを通じて、このサーバーにアクセスすると、Apacheはそれに対応するコンテンツを返します。 これを実現するためにhttpdはポートを開けて待機していて、以下のコマンドにてポートの状況を確認できる。 $ sudo lsof -i -n -P ファイアウォールを構成する webブラウザからwebサーバーに接続する時には、接続先のパブリックIPアドレスを指定します。 現在のままだとポート80番がファイアウォールによってブロック(セキュリティーグループでポート22番だけ通す設定にしたため)されているため、接続できません。 ポートを通す 以下のインバウンドルールを追加する タイプ: HTTP ポート: 80番 ソース: 0.0.0.0/0 (全てのホストを示す) インバウンドルールを追加したことによってパブリックIPアドレスにアクセスした際にTest Pageが表示されることを確認してください。 ドメイン名と名前解決 ドメインの階層について ドメイン名はピリオドで区切られた構造をしており、区切られた部分をラベルと呼びます。 また、www.sample.co.jpを例に各ラベルの呼び名は以下です。 ec2-11-111-11-11.apnortheast1.compute.amazonaws.com: ドメイン ec2-11-111-11-11: 第5レベルドメイン(パブリックIPアドレスをもとに自動的に設定) apnortheast1: 第4レベルドメイン(AWS内で定義) compute: 第3レベルドメイン(AWS内で定義) amazonaws: 第2レベルドメイン(AWSのドメイン) com:トップレベルドメイン(comドメイン) 名前解決は、ルートDNSサーバーから始まり、トップレベルドメインのDNSサーバー、第2レベルのDNSサーバー、第3レベルのDNSサーバー、というように階層的に処理される。 DNSサーバーを構成する 作成したVPCのアクションより「DNSホスト名を編集」を選択し、有効化すると、起動したインスタンスにDNS名が割り当てられます。 パブリックIPアドレスを割り当てていない時はパブリックDNSは空欄となり、プライベートDNSしか付与されていません。 また、パブリックIPアドレスは、インスタンスが起動する度にランダムなものが設定されるのに伴い、パブリックDNSもランダムなものになる。 Elastic IPを用いてパブリックIPアドレスを固定化することよにって、パブリックIPアドレス、パブリックDNS共に固定化されます。 nslookupする 以下のコマンドでドメインからIPアドレスを調べることができます。 $ nslookup ec2-11-111-111-111.ap-northeast-1.compute.amazonaws.com そうすると以下のような返答があったと思います。 Server: 11.1.1.2 Address: 11.1.1.2#53 Non-authoritative answer: Name: ec2-11-111-111-111.ap-northeast-1.compute.amazonaws.com Address: 11.1.2.22 上記でいうAddress:11.1.1.12/#53の#53はDNSウェルノウンポートであり、ポート53で通信しているということになります。 逆にnslookupnにIPアドレスを渡すとドメインを調べることもできます。 正引きと逆引きについて 「正引き」と「逆引き」は、1対1の対応をするとも限りません。サーバーには別名を付けることもできるためです。 また、サーバーからnslookupコマンドを実行することも可能です。 正引き(DNSからIPアドレスを調べること) あるホスト「www.example.co.jp」に対して、「18.177.32.60」と「18.177.32.61」の2つのIPアドレスを設定することもできます。 このように複数のIPアドレスを設定した場合、エンドユーザーは、「18.177.32.60」か「18.177.32.61」のどちらかにアクセスします。 結果として、負荷分散できます(このような負荷分散方式を「DNSラウンドロビン」と言います)。 逆引き(IPアドレスからDNSを調べること) 「18.177.32.60」や「18.177.32.61」に対する逆引きは、両方とも「www.example.co.jp」に設定すると名前が重複してしまうので、片方は「www1.example.co.jp」、もう片方は「www2.example.co.jp」というように、異なる名前を付けるのが一般的です。 このような構成のとき、「www.example.co.jp→18.177.32.60、18.177.32.61」ですが、その逆引きは、「18.177.32.60→www1.example.co.jp」「18.177.32.61→www2.example.co.jp」であり、一致しません。 本記事に関する用語集 Apache: webサーバーソフトウェアのこと ポートを開ける(ポートを通す): ブロックされているポートで通信できるようにすること ドメイン: IPアドレスを人間がわかりやすいようにしたしたもの DNSサーバー(Domain Name System): IPアドレスとドメイン名を変換しれくれるもの 名前解決: DNSを用いて、あるドメイン名からそれに対応するIPアドレスを引き出すこと パブリックDNS: インターネットから参照できるDNS名 プライベートDNS: VPC内から参照できるDNS名 Route53: 独自ドメイン名を利用するためのAWSのサービス 独自ドメイン: オリジナルのドメイン **nslookupコマンド: サーバーに名前解決させるコマンド(windowsやMacなど多くLinuxのディストリビューションに標準インストールされている) digコマンド: DNSサーバーに問い合わせることでドメイン名からIPアドレスを調べるコマンド(nslookupコマンドは昔ながらのコマンドで最近のUNIX系の環境ではdigコマンドが使用できる) Linux: OSの一種でオープンソース UNIX: LinuxはUNIXを参考に作られたと言われており、OSの一種で使用するのにライセンスが必要 不可分散(サーバーロードバランス): 並列に運用されている機器間で、負荷がなるべく均等になるように処理を分散して割り当てること。 tips IPアドレスと同様にICANNが統括管理しており、トップレベルのドメイン名ごとに、それぞれの事業者が管理しています。 世界中に分散したDNSサーバー群で構成された大きな分散型データベースで、自分が担当する範囲のIPアドレスとドメイン名の変換だけをする。(管轄外の名前解決が必要になった時は、他のDNSサーバーへと問い合わせする。) ドメイン名はDNSサーバーが解決することからDNS名と呼ばれることもある サーバーやネットワーク機器など、通信可能なホストに名付けるために使われることから、ホスト名やDNSホスト名と呼ばれることもある。 DNSは基本的にUDPで通信しますが、それにはパケットサイズに制限があるので大きなサイズになるとTCPでの通信に切り替えられます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS ソリューション]Amazon CloudWatch のモニタリングフレームワークを試してみる

はじめに 本記事では、AWS ソリューション実装で提供しているAmazon CloudWatch のモニタリングフレームワークを試してみましたので、その内容について簡単にまとめたものです。 より詳細は、下記に記載されています。 実装ガイド Amazon CloudWatch のモニタリングフレームワークとは EC2内で稼働している Apache の主要なパフォーマンスメトリクスやログを事前に設定された CloudWatch のダッシュボードで、即座に監視できるようにするソリューションで、トラフィックパターンの分析、サーバーをスケールアップするかスケールアウトするかの判断、Apache ワークロードに関するボトルネックやその他のパフォーマンスの問題の検出などができるようになります。 ソリューションの主な機能 Apache のワークロード用に構成された CloudWatch のダッシュボードの実装機能 タグ付けメカニズムを使って、モニタリングするインスタンスをダッシュボードに自動で追加(削除)する機能 特定のワークロード用の CloudWatch エージェントの構成ファイル ソリューションの全体構成 実装ガイドより抜粋 追加されるリソース CloudFormation Systems Manager CloudWatch Lambda IAM ソリューションを実装した場合のコスト バージニア北部 で算出すると 1ヶ月あたり $3.16 内訳( 実装ガイドより): AWS Service Dimension Total Cost Amazon CloudWatch 1 Dashboard $3.00 AWS Lambda 15,000 requests with average billed duration of 500ms and memory allocated 512MB $0.06 AWS SSM 1 Advanced parameter storage cost + 10,000 API interactions cost $0.10 Total $3.16 ソリューションを実装してみる 1 下記のリンクにアクセス Amazon CloudWatch のモニタリングフレームワーク 2 AWS コンソールで起動する ボタンを押下 3 CloudFormation の管理コンソールが起動され、スタックの作成 に遷移するので、そこで東京リージョンを選択して、次へ 4 スタックの詳細を指定で、スタックの名前を入力して、それ以外はそのままで次へ 設定 デフォルト 説明 スタックの名前 - 任意で設定してください(必須) Deploy Yes CloudWatchダッシュボードをデプロイしてApacheワークロードをモニターしますか? Apache Tagging Schema {"Key":"CW-Dashboard","Value":"Apache"} Aacheワークロードインスタンスを識別するためのタグ付けについて Apache Demo Instance Yes 必要な設定とApacheワークロードが実行されているデモ用のEC2インスタンスが必要ですか? 5 スタックオプションの設定で、そのまま次へ 6 レビューで、下記2つにチェックをつけて、スタックの作成を押下 7 CloudFormation の管理コンソール > スタック でスタックの作成が完了していることを確認 8 デモ用で構築した EC2 の Apache には、http://{EC2のグローバルIP} でアクセスできます ソリューションを使用してみる 1 EC2 管理コンソールにアクセスして、 CW-Monitoring-Framework-Stack でデモ用のEC2を検索 2 デモ用のEC2を選択し、タグ > タグを管理 を押下 3 タグを管理で、キーに CW-Dashboard を、値に Apache を設定して 保存 を押下 4 約5分後、CloudWatch 管理コンソール > ダッシュボードにアクセスして ApacheDashboard-ap-northeast-1 が自動で実装されていることを確認 5 自動で実装された ApacheDashboard-ap-northeast-1 を押下して、ダッシュボードを確認 ソリューションを削除する 1 CloudFormation の管理コンソール > スタック で、[ネストされた]のラベルがないスタックを選択して削除 2 CloudWatch Logs の管理コンソールで作成された下記のロググループを削除 /aws/lambda/XXDashboardHandlerXX /aws/lambda/XXhelperProviderframeworkXX /aws/lambda/XXTagHandlerXX /aws/lambda/XXApach-HelperXX /cw-monitoring-framework/apache/access /cw-monitoring-framework/apache/error linux/var/log/boot linux/var/log/cron linux/var/log/messages linux/var/log/secure さいごに 詳細は、実装ガイドにありますが、本記事が、このソリューションの検証を開始する時の参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS + お名前.com でSSL化してみた

はじめに 以前、AWS + お名前.comで独自ドメインのwordpressブログを開設したのですが、サイトのSSL化を行っていませんでした。セキュリティ上よろしくないので、今回、SSL化を行った時のことを簡単にメモしました。 SSLとは SSLとは、インターネット上の通信を暗号化の仕組みです。トランスポート層のプロトコルであり、HTTPやFTPなどのプロトコルで行われる通信を暗号化して安全に送受信するためのプロトコルです。 サイトにSSLを導入すると、訪問者のブラウザとサーバー間のデータ通信が暗号化されます。また、サイトのURLがhttpからhttpsに変わります。このsはsecureを意味します。 SSLの目的 意図した接続先かどうか(なりすましでないか)の証明(認証) 通信内容の暗号化 SSLとSSHの違い SSLと似た名前でSSHというものがあります。 SSHはAWSで作成したサーバーにアクセスする時などで耳にすることがあると思いますが、 SSLとSSHはどちらも通信の暗号化に関係していますが、その違いを大雑把にいうと SSL:インターネット用 SSH:遠隔操作用 の通信を暗号化しています。 SSLの暗号化の仕組み SSLではデータの暗号化・復号化をするために、特定のブラウザとサーバーでのみ使用可能な「共通鍵」を用いて送受信を行う。そのため、データが不正アクセスで流出したとしても、共通鍵で守られているので、データの悪用を防止できる。 1. ブラウザがサーバーにSSL通信を要求すると、サーバーがブラウザにサーバ証明書(公開鍵含む)を送信。このとき、サーバーは秘密鍵を所持。 2. ブラウザはサーバーから受け取った公開鍵で共通鍵を作成し、暗号化した共通鍵をサーバーに送信。 3. サーバーは暗号化された共通鍵を秘密鍵で複合。これにより、ブラウザとサーバはどちらも共通鍵を持った状態。 4. 共通鍵で暗号化したデータをブラウザとサーバー間でやり取りする。 SSL化導入の手順 AWSの構成図 基本的に、以下の参考記事通りに行えばSSL化することができます。 * [AWS] 徹底図解!お名前.comで取得したDNSをAWS Route53/Cloudfrontで管理するまでの手順 * AWSxお名前.comで作成したサイトをSSL化[具体例あり] そのため、ここでは簡単な流れのみを説明します。 (なお、以下の流れは、既にお名前.comで独自ドメインを取得してDNS設定まで完了していることを想定しています。) 1. Certificate ManagerでSSL証明書を発行 (AWS) 2. お名前.comに外部のCNAMEを設定 ・[DNSレコード設定用ネームサーバー変更確認]のチェックボックスにチェックを入れないように注意。(Route 53のNSの4つの値を既に登録しているので、チェックを入れると初期化されてしまう。初期化してしまった場合、再設定する必要あり。) 3. Route 53でCNAMEのレコードを作成 (AWS) 4. SSL証明書が発行されたことの確認 (AWS) 5. ロードバランサーの設定 (AWS) ・Application Load Balancerを利用 ・httpリクエストされた時にhttpを返さずに、httpsにリダイレクトされるように設定するのをお忘れなく! 6. Route 53の作成済みのAレコード(Elastic IP)を選択して、エイリアスを設定(AWS) 7. 「https://ドメイン名」で表示されるか確認 関連用語の説明 SSL(Secure Sockets Layer):インターネット上の通信の暗号化の仕組み。トランスポート層のプロトコルであり、HTTPやFTPなどのプロトコルで行われる通信を暗号化して安全に送受信するためのプロトコル。 SSH(Secure Shell):暗号化された遠隔ログインシステム。 DNS(Domain Name System):ドメイン名とIPアドレスを紐づけるシステム。人間がわかりやすいように、IPアドレス(数値の羅列)をドメイン名(文字の羅列)に変換。このシステムを担当するのがDNSサーバーで、「フルサービスリゾルバ」と「ネームサーバー」の2種がある。 フルサービスリゾルバ:ドメイン情報を教えて欲しいという問い合わせをネームサーバーにするサーバー。問い合わせ結果を一定期間保存することで、再度同じ名前で問い合わせがあった時に高速化・ネームサーバ負荷の軽減を図ることができる。この保存時間をTTL(Time To Live)と呼ぶ。 ネームサーバー:ドメインとIPアドレスを紐づける台帳(ゾーンファイル)を持ったサーバー。フルサービスリゾルバからのドメイン情報を教えて欲しいという問い合わせに対して、返答する。 DNSレコード:ドメインとIPアドレスを紐づけるゾーンファイル(台帳)に書かれた一行ごとの情報。A/AAAA/MX/NS/CNAME/TXTレコードといった種類がある。 CNAMEレコード:正規ホスト名に対する別名を定義するレコード。特定のホスト名を別のドメイン名に転送するときなどに利用。例)ドメイン名:abc.com、ホスト名:www と VALUE:www.onamae.com とした場合、⇒ "http://www.abc.com/"にアクセスすると"http://www.onamae.com/"に転送される。 ロードバランサー(ELB:Elastic Load Balancing):他のサービスへのトラフィックを、仮想サーバーなどの複数のターゲットに自動的に分散して、安定稼働をサポートするサービス。複数のサーバーへの負荷を均一化して、障害への耐性を高める。 Application Load Balancer:AWS ELBのうち最新で高機能。基本的にこれを利用。HTTPトラフィックおよびHTTPSトラフィックの負荷分散、柔軟なアプリケーション管理が可能。 まとめ 無事に自作のブログをSSL化することができてよかったです。AWSの1年間の無料利用枠であれば、ブログを安く運営することができ、勉強にもなるのでみなさんもやってましょー。 参考 SSLとは? セキュリティ上の意味や仕組み、メリットをわかりやすく解説! https://wa3.i-3-i.info/diff384security.html https://qiita.com/satodayo/items/b45573b7165235a15152 DNSレコードとは?基本の仕組みを理解しよう https://help.onamae.com/answer/7883] AWSでWebサイトをHTTPS化 全パターンを整理してみました AWSでSSL化する方法を伝授!!! [AWS] 徹底図解!お名前.comで取得したDNSをAWS Route53/Cloudfrontで管理するまでの手順 Application Load Balancer を使い HTTP リクエストを HTTPS にリダイレクトするにはどうすればよいですか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS]EC2の設定

はじめに 自作のRailsアプリケーションをAWSのEC2に上げたので備忘録として残しておきます。 画像なしで説明しますのであしからず。 手始めにAWSのサービス検索欄から「EC2」と検索し「EC2」を選択します。 インスタンスを作成 画面左から「インスタンス」を選択して、その後「インスタンスの起動」を選択してください 1.AMIの選択 「Amazon Linux 2 AMI (HVM), SSD Volume Type」「64 ビット(x86)」を選択します。 2.インスタンスタイプの選択 無料利用枠(1年未満のユーザー)の「t2.micro」を選択 3.インスタンスの設定 インスタンス数:「1」 購入のオプション:「チェックなし」 ネットワーク:「VPC_for_アプリ名」 サブネット:「アプリ名_Subnet_1a」 自動割り当てパブリック IP:「サブネット設定を使用(無効)」 配置グループ:「チェックなし」 キャパシティーの予約:「開く」 ドメイン結合ディレクトリ:「ディレクトリなし」 IAM ロール:「なし」 シャットダウン動作:「停止」 停止 - 休止動作:「チェックなし」 終了保護の有効化:「チェック入れる」 モニタリング:「チェックなし」 テナンシー:「共有」 Elastic Inference:「チェックなし」 クレジット仕様:「チェックなし」 ファイルシステム:「空白」 4.ストレージの追加 初期設定で次へ 5.タグの追加 キー:「Name」 値:「アプリ名-instance」 6.セキュリティグループの設定 セキュリティグループの割り当て:「既存のセキュリティグループを選択する」 以前作成した「アプリ名-SecurityGroup」にチェック 「確認と作成」を選択する 「起動」を選択 すると「既存のキーペアを選択するか、新しいキーペアを作成します」というポップアップが表示されます。 選択ボックスから「新しいキーペアの作成」を選択し 空白のキーペア名に「アプリ名」を入力 「キーペアのダウンロード」を選択 すると「アプリ名.pem」がダウンロードされます。 そして「確認と作成」を選択します。 Elastic IPを作成して紐付ける 画面左から「Elastic IP」を選択して、「Elastic IP アドレスの割り当て」を選択 以下の様に記述 ※初期設定のままで良い ネットワークボーダーグループ: 「ap-northeast-1」 IPv4 アドレスプール:「Amazon の IPv4 アドレスプール」 「割り当て」を選択 次にElastic IPをインスタンスに紐付けします。 先ほど作ったElastic IPを選択して「アクション」ボタンをクリックします。 その後「アドレスの関連付け」を選択します。 以下の様に記述 リソースタイプ:「インスタンス」 インスタンス:「アプリ名-instance」 プライベート IP:クリックして表示されるIPを選択 再関連付け:チェック入れる 「関連付ける」をクリックしてください EC2にログインする準備 作成したインスタンスが起動している事を確認します。 「インスタンス」をクリックして作成したインスタンスがrunningになっていることを確認する ここでElastic IPアドレスをメモしておいてください EC2にログインするために、ここからはコンソール画面を使います。 下準備としてこれらをインストールします。 1. python 2. pip 3. awscli ※awsをPCのコンソール上から扱うためのもの ターミナルで以下のコマンドを入力します brew install python sudo easy_install pip sudo easy_install nose sudo easy_install tornado sudo -H pip install awscli --upgrade --ignore-installed six これからEC2インスタンスへのログイン設定を行います。 AWSではec2-userという名前のユーザが用意されていてデフォルトになっています。 以下の手順で進めます。 1 . ec2-userでEC2へのログイン 2 . 新たなユーザを作成 3 . 作成したユーザにec2-userと同様の権限を与える 4 . 新たなユーザで再度ログインする キーペアの移動、権限の変更 pemファイルを~/.ssh/へ移動させる $ mkdir .ssh/ $ mv {ダウンロードしたディレクリのパス}/アプリ名.pem .ssh/ キーペアの権限を変更してアクセス権を与えます。 $ cd .ssh/ $ cdmod 600 アプリ名.pem EC2へログイン 公開鍵を利用してec2-userとしてログインします。 先ほどメモしたElastic IPを使います。 $ ssh -i アプリ名.pem ec2-user@{Elastic IP} yesかnoを聞かれたら、yesと入力してください これでログイン成功です。 新規ユーザー登録 サーバー [ec2-user@ip-10-0-0-246 ~]$ sudo adduser ユーザ名 新規ユーザのパスワード登録 サーバー [ec2-user@ip-10-0-0-246 ~]$ sudo passwd ユーザ名 作成したユーザーにマスター権限を与える viを使って設定を編集します。visudoコマンドを使って行います。 ※viでファイルを指定しても良いがvisudoの方が簡単 サーバー [ec2-user@ip-10-0-0-246 ~]$ sudo visudo ファイルの中で以下の様になっている部分を探し作成したユーザに権限を与える記述をします。 #ここを探す ## Allow root to run any commands anywhere root ALL=(ALL) ALL   ユーザ名 ALL=(ALL) ALL ←ここを追加 shift+ZZで保存して終了します。 ユーザーの切り替え サーバー [ec2-user@ip-10-0-0-246 ~]$ sudo su - ユーザ名 [ユーザ名@ip-10-0-0-246 ~] SSH通信でインスタンスへログイン 続いてSSH通信を使ってインスタンスにログインします。 以下の流れで行います。 1 . ローカルで鍵の生成 2 . その鍵をどの通信の認証時に使用するか等を設定 3 . サーバーとの認証処理を行う。 4 . 実際に新ユーザーでログイン 1.ローカルで鍵の作成 ローカルで作業するので新しくターミナルを開きます。 ローカル $ cd .ssh $ ssh-keygen -t rsa ------------ 以下のメッセージが表示されたら、「アプリ名_key_rsa」と入力 Enter file in which to save the key ():{アプリ名}_key_rsa そのままEnter ※passは指定しない Enter passphrase (empty for no passphrase): そのままEnter Enter same passphrase again: これで「アプリ名_key_rsa」と「アプリ名_key_rsa.pub」が生成される ローカル $ ls アプリ名_key_rsa アプリ名_key_rsa.pub 2.鍵をどの通信の認証時に使用するか等を設定 viで設定ファイルを編集する ローカル $ vi config 以下の様に編集する Host アプリ名_key_rsa Hostname Elastic IP #自分の設定に合わせる Port 22 User ユーザ名 #作成したのユーザー名 IdentityFile ~/.ssh/アプリ名_key_rsa #秘密鍵の設定 shift + ZZで保存して終了 鍵の中身をターミナル上に出力、ssh-asa〜localまで全文コピーしておく ローカル $ cat アプリ名_key_rsa.pub ssh-rsa ~ ~ ~ ~.local 3.サーバーとの認証処理 次はサーバー側で作業をします。 サーバーにログインしていたターミナルに移ります。 サーバー $ mkdir .ssh $ chmod 700 .ssh $ cd .ssh $ vi authorized_keys ここで「アプリ名_key_rsa.pub」でコピーしたssh-rsa ~~~ .localを貼り付けます。 ssh-rsa ~ ~ ~ ~.local shift + ZZで保存して終了します。 以下を入力して新ユーザーに権限を与えます $ chmod 600 authorized_keys これで権限が付与されました。 新ユーザでログインする ローカル $ ssh アプリ名_key_rsa ログインできればユーザー設定は終了です。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3 + CloudFront のCDNでPageSpeedInsightにキャッシュポリシーを怒られたときの対処メモ

この記事は何 自作中のwebシステムの画像配信にS3+CloudFrontを使っているのですが、サイトパフォーマンス可視化のためにPageSPeedInsightを試した際、良く見かけますが ↓ の感じで怒られました。その対処です。 結論 CloudFrontでTTL設定をしても、それはCloudFront側のキャッシュ設定なだけで、クライアント側のキャッシュ設定はファイルにメタデータを付与する必要がある。 バケット内の既存ファイルのメタデータ編集はAWSマネジメントコンソールから簡単に実行できる。 今後追加するファイルへの対処方法はケースバイケースだが、自分の場合はLambdaでバケットにファイルを追加するシステム構成のため、そのアップロードときに適切なメタデータを付与するようにLambdaを修正することで対処した。 Lambdaの編集方法 最終形態 Python3のLambdaの場合 s3.upload_file(Filename=filename, Bucket=bucket, Key=key, ExtraArgs={'ACL':'public-read', 'ContentType': 'image/webp', 'CacheControl': 'max-age=72000'}) 躓いたところ boto3のリファレンスをみたところ、ExtraArgsの使い方は下記の通り s3.upload_file( 'FILE_NAME', 'BUCKET_NAME', 'OBJECT_NAME', ExtraArgs={'Metadata': {'mykey': 'myvalue'}} ) Cache-Controlを追加するんだから、こんな感じ?とトライしてみたところ、aws内特有のユーザ定義ヘッダとして追加されてしまった。ユーザ定義ヘッダとシステムヘッダの二種類があり、追加すべきは後者の模様。結果的に、最終形態欄に記載したような状態が適切な結果をもたらした。 s3.upload_file(Filename=filename, Bucket=bucket, Key=key, ExtraArgs={'ACL':'public-read', 'Metadeta': {'Content-Type': 'image/webp', 'Cache-Control': 'max-age=72000'}})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む