コンマ

メモ代わりにアウトプットしています。何か不備がありましたら、お気軽にコメント頂けると有り難いです。

EC2インスタンス起動時にRailsアプリケーションにCapistranoでセルフデプロイする方法

TR;DR

EC2インスタンス(Amazon Linux)起動時にRailsアプリケーションにCapistranoでセルフデプロイする方法を記載しています。

背景

AutoScalingでEC2インスタンスを立ち上げる際に以下の問題がありました。

  1. EC2(Nginx + Unicorn + Rails)インスタンスUnicornを手動で起動しなくてはいけない。

  2. AutoScalingで起動中のEC2インスタンスがあるとき、Capistranoでデプロイをしてしまうと最新のソースコードが反映されていない。

上記の問題を解決するために、EC2起動時にCapistranoでセルフデプロイをする処理を追加するように試みました。

以下はその方法をメモとして残しています。

前提条件

  • EC2にCapistranoでデプロイできる状態であること。

EC2起動時に自動実行する

今回は、デフォルトのec2-userでなく、deploy_userという別ユーザーで行うことを想定しています。

まず、EC2が起動する際に任意のシェルスクリプトを実行するようにします。

EC2で任意のシェルスクリプトを実行するためには/etc/init.d配下にスクリプトを配置する必要があります。

self-deployというスクリプト名で以下を記載しました。

内容はログを吐くようにしたのとcapistranoのデプロイコマンドを実行しています。

#!/bin/sh

#chkconfig:2345 85 15
#description:rails self deploy shell

# log path
LOG_DIR=/home/deploy_user/log
LOG_FILE=$LOG_DIR/rails.log

# make log directory
mkdir -p $LOG_DIR

start()
{
  echo "start $NAME"
sudo su - deploy_user -c "
rm -rf /var/www/<任意のディレクトリ>/shared/tmp
rm -rf /var/www/<任意のディレクトリ>/current/tmp
cd /var/www/<任意のディレクトリ>/current
# 以下はlocal用に作成したcapistranoのデプロイコマンドです。
bundle exec cap production_local deploy
" >> "$LOG_FILE" 2>&1
chown deploy_user:deploy_user -R $LOG_DIR
}

case "$1" in
start)
  start
  ;;
esac

/etc/init.d配下のシェルスクリプトを起動時に実行するようにする。

/etc/init.d配下のシェルスクリプトを起動時に実行するために

chkconfig --add <シェルスクリプト名>を実行します。

$ chkconfig --add self-deploy

以下で確認ができます。

$ chkconfig --list self-deploy
self-deploy  0:off  1:off  2:on  3:on  4:on  5:on  6:off

実行権限を付与します。

$ chmod +x /etc/init.d/self-deploy

セルフデプロイの環境設定

<Railsルート>/config/deploy/配下にcapistranoでデプロイする際に環境別にスクリプトを書くと思います。

上記で実行をしようとしている

bundle exec cap production_local deploy

config/deploy/production_local.rbに記載していることになります。

server 'localhost', user: 'deploy_user', roles: %w(app db web)
set :stage, :production
set :rails_env, 'production'
set :branch, 'master'

localhostにデプロイするようになっています。

この状態でEC2を起動したところ、以下のようなエラーが発生していました。

Caused by:
Net::SSH::AuthenticationFailed: Authentication failed for user deploy_user@localhost

Githubとの接続がうまくいってなかったようです。

capistranoでデプロイする際にGithub上でDeploy keysを設定したので

それを~/.ssh/authorized_keysに追加してあげました。

$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

再度、EC2を起動したところ正常にセルフデプロイされ、Unicornが起動されていました。

【Docker】Dockerの基本的事項メモ

TL;DR

ゼロからはじめる Dockerによるアプリケーション実行環境構築 | UdemyでDockerの講座を受講したのでメモ代わりにDockerの基本的事項 について記載しています。

関連情報

以下を参考にしています。

www.udemy.com

Dockerとは

  • Docker社が提供しているコンテナ型のアプリケーション実行環境
  • Docker自体はGo言語で書かれている

Dockerは従来の仮想化(ホスト型仮想化)とは違い、コンテナ型仮想化です。

従来の仮想化(ホスト型仮想化)とコンテナ型の仮想化の違い

f:id:koooosuke:20190430213418j:plain

上記の図のように ホスト型仮想化の場合、ホストOSとは別にゲストOSを別にして動作しています。
そのため、LinuxOSやWindowsOSといった別々の任意のOSをゲストOSとして動作させることができます。
ただし、仮想マシンをつくるためのイメージサイズが大きいことやコンテナ型仮想化と比較すると起動が遅い点がデメリットとしてあげられます。
一方、コンテナ型仮想化は、Docker Engineを介してコンテナを操作して、コンテナはゲストOSを持たず、ホストOSのカーネルを使用しています。
そのためカーネルを共有できないOSはコンテナで動作することができません。
例えば、ホストOSがLinuxOSの場合、Windowsコンテナを使用できないということになります。

仮想化のオーバーヘッドについて

ホスト型仮想化
リソース(CPUやメモリの使用率等)の面でオーバーヘッドが多いため、遅いです。

コンテナ型仮想化
コンテナはアプリケーション実行に必要なものだけを含み、ホストOSのカーネルを使用するため早いです。

アプリケーション実行の再現性

ホスト型仮想化
仮想化ごとにミドルウェアやライブラリをインストールし開発環境と差異が生まれ、稀にアプリケーションが動作しないことがあります。

コンテナ型仮想化
アプリケーションを実行するために必要なものをDockerイメージとしてあるので、Dockerイメージからコンテナを起動すれば環境が変わっても動作します。

OSの自由度

ホスト型仮想化
仮想マシン上で任意のOSを動作させることができます。

コンテナ型仮想化
ホストOSのカーネルを使用するので別のOSを動作させることができません。

分離レベル

ホスト型仮想化
ハードウェアレベルで仮想化されているので、いずれかの仮想化マシンに侵入されても他のマシンへの影響が低く、分離レベルが高いです。

コンテナ型仮想化
ホストOSの一プロセスとして動作するので、影響範囲が大きいため、分離レベルが低いです。

【AWS】メンテナンス時にALBで転送先を簡単に変える方法

TL;DR

ALB(Application Load Balancer)のリスナーのルールを使用して、ALBの転送先を変える方法を記載しています。

関連情報

以下を参考にしました。

docs.aws.amazon.com

特定のドメインの転送先を別転送先に変えたい場合(例えば、メンテナンス画面に変更させたい等)が少なからずあると思います。
ALBのリスナーのルールを使うことによって、簡単に変更することが可能だったので、メモ代わりにアウトプットしようと思いますmm
今回はまず、ALBのリスナー(HTTP : 80)で受けたときに、正常の場合EC2に転送します。
その後、メンテナンス画面にするためにルールを変更し、メンテナンス画面(擬似的に固定レスポンス)に変わるところまでを行います。

前提条件

  • ALB経由でEC2(webサーバー)にさばけること。

やってみます

1. リスナーのルールを作成する。(通常用)

現在、ALBではHTTP(port80)で受け、EC2に転送しています。

f:id:koooosuke:20190429235206p:plain

EC2ではwebサーバーとして、Nginxを起動しました。

f:id:koooosuke:20190429235633p:plain

ロードバランサー>リスナーでは以下のようになっています。 f:id:koooosuke:20190429235807p:plain

ルールの表示/編集からルールを編集していきます。 f:id:koooosuke:20190429235939p:plain

以下のようにパスで*を指定し、HTTP(port80)で受け取るALBのパスはEC2のターゲットグループに転送するようにルールを追加しました。 f:id:koooosuke:20190430000305p:plain

2. リスナーのルールを作成する。(メンテナンス用)

続いて、メンテナンス時に飛ばしたい転送先のルールを追加していきます。
今回は、擬似的に固定レスポンスを返すように以下のように、パスを*で指定し、固定レスポンスを返すようにルールを追加しました。
ただし、先程追加したルールの下にルールを追加するようにします。 f:id:koooosuke:20190430000825p:plain

3. ALBのルールを変え、転送先を変えてみる。

まず、現在のALBにつなぐと以下のようにEC2につながります。 f:id:koooosuke:20190429235633p:plain

ここで、ALBのルールの優先順位を固定レスポンス(メンテナンス用)を上位にするように変更してみます。 f:id:koooosuke:20190430001443p:plain

では、もう一度ALBにつないで確認をしてみると、以下のように先程のルールが適用されています。 f:id:koooosuke:20190430001644p:plain

こんな感じにパス指定やホストを指定したりして、ALBで簡単に転送先を変えることができました。

【Terraform】tfstateをS3バケットに保存する方法

TL;DR

terraform.tfstateをS3バケットに保存する方法を記載しています。

前提条件

【Terraform】TerraformでVPC/Subnet/EC2インスタンスを作ってみる - コンマ
コンテナ時代のWebサービスの作り方 - 楽描商店 - BOOTHを参考にterraformでEC2を作成しました。

terraform applyすると、カレントディレクトリにtfstateというファイルが生成されました。

$ cd ~/terraform_ec2/
$ ls
sample.tf       terraform.tfstate

tfstateには、terraform applyし反映したインフラ情報が入っています。

コンテナ時代のWebサービスの作り方 - 楽描商店 - BOOTHによると

暗号化されていないためリポジトリにpushするのはためらわれます。またもしもチームで開発を行う場合はtfstateがそれぞれの開発者のローカルに存在すると何が正しいのか分からなくなるため問題があります。なのでtfstateは特にこだわりがない場合はS3などのクラウドストレージに保存すると良いでしょう。

とのことなので、S3に保存してきます。

やってみます

  1. まずS3のバケット作成

  2. 次にtfstateをs3にuploadする設定を記述

$ cat sample.tf 
# ~/terraform_ec2/sample.tf

provider "aws" {
  region = "ap-northeast-1"
}
resource "aws_instance" "sandbox" {
  ami  = "ami-785c491f"
  instance_type = "t2.micro"
  subnet_id = "${aws_subnet.public_subnet.id}"
}
resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"
  instance_tenancy = "default"
}
resource "aws_subnet" "public_subnet" {
  vpc_id = "${aws_vpc.vpc.id}"
  cidr_block = "10.0.16.0/20"
  availability_zone = "ap-northeast-1a"
}
terraform {
    backend "s3" {
    bucket = "<S3のバケット名>"
    key    = "test/terraform.tfstate"
    region = "ap-northeast-1"
  } 
}

3.terraform applyをしてみる。

$ terraform apply

4.S3を確認してみます。 f:id:koooosuke:20190426224410p:plain

正常にS3にtfstateがアップロードされています。

【nand2tetris】1章 ブール論理(Not, And, Or/Xor/マルチプレクサ/デマルチプレクサ)

TL;DR

O'Reilly Japan - コンピュータシステムの理論と実装 1章 ブール論理(Not, And, Or/Xor/マルチプレクサ/デマルチプレクサ)について勉強したことをメモ代わりに記載しています。 また、ソースコードこちらにおいてあります。

関連情報

www.oreilly.co.jp

概要

1章 ブール論理では、最終的に論理ゲートの一つであるNANDを使って、他の基本論理ゲートNot,And,Or,Xor,マルチプレクサ,デマルチプレクサHDL(ハードウェア記述言語)で作成するというものです。

Nandゲート

基本となるNandゲートは、入力a, bに対してa=b=1ならば、0を出力し、それ以外は1を出力します。

Notゲート

では、早速Notゲートから作成してみます。 Notゲートは、入力0ならば1を出力し、入力1ならば0を出力する(インバータ)ので、Nandゲートから作成するためには、Nandゲートを1回通して上げればいけそうです。

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/01/Not.hdl

/**
 * Not gate:
 * out = not in
 */

CHIP Not {
    IN in;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=in, b=in, out=out);
}

Andゲート

AndゲートはNandゲートと反対になるので、入力a, bに対してa=b=1ならば、1を出力し、それ以外は0を出力します。 つまり、And = Not(Nand(a, b))になります。 Notゲートは先程、Nandゲートから作成したので以下のように作成できそうです。

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/01/And.hdl

/**
 * And gate: 
 * out = 1 if (a == 1 and b == 1)
 *       0 otherwise
 */

CHIP And {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here;
    Nand(a=a, b=b, out=w);
    Nand(a=w, b=w, out=out);
}

Orゲート

Orゲートは、入力a, bに対してa=b=0ならば、0を出力し、それ以外は1を出力します。
ソースコードは以下みたいにしました。

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/01/Or.hdl

 /**
 * Or gate:
 * out = 1 if (a == 1 or b == 1)
 *       0 otherwise
 */

CHIP Or {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=a, b=a, out=nota);
    Nand(a=b, b=b, out=notb);
    Nand(a=nota, b=notb, out=out);
}

Xorゲート

Xorゲートは入力a, bが互いに同じなら1を出力し、別ならば0を出力します。 ソースコードは以下みたいにしました。

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/01/Xor.hdl

/**
 * Exclusive-or gate:
 * out = not (a == b)
 */

CHIP Xor {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=a, b=b, out=w1);
    Nand(a=a, b=w1, out=w2);
    Nand(a=b, b=w1, out=w3);
    Nand(a=w2, b=w3, out=out);
}

マルチプレクサ

マルチプレクサは、入力a, b, selがあり、sel=0ならば出力がaになり、 sel=1ならば出力がbになります。 以下のようにしました。

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/01/Mux.hdl

/** 
 * Multiplexor:
 * out = a if sel == 0
 *       b otherwise
 */

CHIP Mux {
    IN a, b, sel;
    OUT out;

    PARTS:
    // Put your code here:
    Nand(a=sel, b=sel, out=w1);
    Nand(a=a, b=w1, out=w2);
    Nand(a=sel, b=b, out=w3);
    Nand(a=w2, b=w3, out=out);
}

デマルチプレクサ

デマルチプレクサは、入力in, selに対して、sel=0ならば、a=in, b=0を出力し、sel=1ならば、a=0, b=inの出力をします。 以下のようにしました。

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/01/DMux.hdl

/**
 * Demultiplexor:
 * {a, b} = {in, 0} if sel == 0
 *          {0, in} if sel == 1
 */

CHIP DMux {
    IN in, sel;
    OUT a, b;

    PARTS:
    // Put your code here:
    Nand(a=in, b=sel, out=w1);
    Nand(a=in, b=w1, out=w2);
    Nand(a=w2, b=w2, out=a);
    Nand(a=w1, b=w1, out=b);
}

一旦基本論理ゲートは完了したので、次回は 多ビットNot/And/Orゲート, 多ビットマルチプレクサ, 複数入力ゲートをやろうと思います。

【Terraform】TerraformでVPC/Subnet/EC2インスタンスを作ってみる

TL;DR

TerraformでVPC/Subnet/EC2インスタンスを作る方法を記載しています。

先日、コンテナ時代のWebサービスの作り方 - 楽描商店 - BOOTHを技術書店で入手したので、これを元にやってみますmm

関連情報

booth.pm

前提条件

  • Terraformをインストールしていること。
  • AWS CLIをインストールしていること。(AWS CLIで使用できる状態になっていること、ポリシー等)
  • .aws/credentialsにアクセスキー/シークレットキーを定義していること。

やってみます。

まず今回の作業ディレクトリを適当に作成しました。

$ mkdir ~/terraform_ec2/
$ cd ~/terraform_ec2/

Terraformは、.tfという拡張子のファイルに記述していくそうです。

では、実際にVPC/Subnet/EC2インスタンスを作成するコードを記述していきます。

# ~/terraform_ec2/sample.tf
provider "aws" {
  region = "ap-northeast-1"
}
resource "aws_instance" "sandbox" {
  ami  = "ami-785c491f"
  instance_type = "t2.micro"
  subnet_id = "${aws_subnet.public_subnet.id}"
}
resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"
  instance_tenancy = "default"
}
resource "aws_subnet" "public_subnet" {
  vpc_id = "${aws_vpc.vpc.id}"
  cidr_block = "10.0.16.0/20"
  availability_zone = "ap-northeast-1a"
}

では、早速、terraform initをして、初期化してプラグインをインストールします。

$ terraform init

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "aws" (2.7.0)...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 2.7"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

成功ぽいです。

続いて、terraoform planで、コードがただしいかどうか確認してみます。 プランは.tfファイルの適用差分とかもみれるので、applyする前に必ずするようにします。

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_instance.sandbox
      id:                               <computed>
      ami:                              "ami-785c491f"
      arn:                              <computed>
      associate_public_ip_address:      <computed>
      availability_zone:                <computed>
      cpu_core_count:                   <computed>
      cpu_threads_per_core:             <computed>
      ebs_block_device.#:               <computed>
      ephemeral_block_device.#:         <computed>
      get_password_data:                "false"
      host_id:                          <computed>
      instance_state:                   <computed>
      instance_type:                    "t2.micro"
      ipv6_address_count:               <computed>
      ipv6_addresses.#:                 <computed>
      key_name:                         <computed>
      network_interface.#:              <computed>
      network_interface_id:             <computed>
      password_data:                    <computed>
      placement_group:                  <computed>
      primary_network_interface_id:     <computed>
      private_dns:                      <computed>
      private_ip:                       <computed>
      public_dns:                       <computed>
      public_ip:                        <computed>
      root_block_device.#:              <computed>
      security_groups.#:                <computed>
      source_dest_check:                "true"
      subnet_id:                        "${aws_subnet.public_subnet.id}"
      tenancy:                          <computed>
      volume_tags.%:                    <computed>
      vpc_security_group_ids.#:         <computed>

  + aws_subnet.public_subnet
      id:                               <computed>
      arn:                              <computed>
      assign_ipv6_address_on_creation:  "false"
      availability_zone:                "ap-northeast-1a"
      availability_zone_id:             <computed>
      cidr_block:                       "10.0.16.0/20"
      ipv6_cidr_block:                  <computed>
      ipv6_cidr_block_association_id:   <computed>
      map_public_ip_on_launch:          "false"
      owner_id:                         <computed>
      vpc_id:                           "${aws_vpc.vpc.id}"

  + aws_vpc.vpc
      id:                               <computed>
      arn:                              <computed>
      assign_generated_ipv6_cidr_block: "false"
      cidr_block:                       "10.0.0.0/16"
      default_network_acl_id:           <computed>
      default_route_table_id:           <computed>
      default_security_group_id:        <computed>
      dhcp_options_id:                  <computed>
      enable_classiclink:               <computed>
      enable_classiclink_dns_support:   <computed>
      enable_dns_hostnames:             <computed>
      enable_dns_support:               "true"
      instance_tenancy:                 "default"
      ipv6_association_id:              <computed>
      ipv6_cidr_block:                  <computed>
      main_route_table_id:              <computed>
      owner_id:                         <computed>


Plan: 3 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

vpc/subnet/ec2のリソースが作られることが確認できました。

次にEC2の状態をを確認してみます。

$ aws ec2 describe-instances
{
    "Reservations": []
}

何もない状態です。

では、最後にterraform applyをしてコードを反映してみます。

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_instance.sandbox
      id:                               <computed>
      ami:                              "ami-785c491f"
      arn:                              <computed>
      associate_public_ip_address:      <computed>
      availability_zone:                <computed>
      cpu_core_count:                   <computed>
      cpu_threads_per_core:             <computed>
      ebs_block_device.#:               <computed>
      ephemeral_block_device.#:         <computed>
      get_password_data:                "false"
      host_id:                          <computed>
      instance_state:                   <computed>
      instance_type:                    "t2.micro"
      ipv6_address_count:               <computed>
      ipv6_addresses.#:                 <computed>
      key_name:                         <computed>
      network_interface.#:              <computed>
      network_interface_id:             <computed>
      password_data:                    <computed>
      placement_group:                  <computed>
      primary_network_interface_id:     <computed>
      private_dns:                      <computed>
      private_ip:                       <computed>
      public_dns:                       <computed>
      public_ip:                        <computed>
      root_block_device.#:              <computed>
      security_groups.#:                <computed>
      source_dest_check:                "true"
      subnet_id:                        "${aws_subnet.public_subnet.id}"
      tenancy:                          <computed>
      volume_tags.%:                    <computed>
      vpc_security_group_ids.#:         <computed>

  + aws_subnet.public_subnet
      id:                               <computed>
      arn:                              <computed>
      assign_ipv6_address_on_creation:  "false"
      availability_zone:                "ap-northeast-1a"
      availability_zone_id:             <computed>
      cidr_block:                       "10.0.16.0/20"
      ipv6_cidr_block:                  <computed>
      ipv6_cidr_block_association_id:   <computed>
      map_public_ip_on_launch:          "false"
      owner_id:                         <computed>
      vpc_id:                           "${aws_vpc.vpc.id}"

  + aws_vpc.vpc
      id:                               <computed>
      arn:                              <computed>
      assign_generated_ipv6_cidr_block: "false"
      cidr_block:                       "10.0.0.0/16"
      default_network_acl_id:           <computed>
      default_route_table_id:           <computed>
      default_security_group_id:        <computed>
      dhcp_options_id:                  <computed>
      enable_classiclink:               <computed>
      enable_classiclink_dns_support:   <computed>
      enable_dns_hostnames:             <computed>
      enable_dns_support:               "true"
      instance_tenancy:                 "default"
      ipv6_association_id:              <computed>
      ipv6_cidr_block:                  <computed>
      main_route_table_id:              <computed>
      owner_id:                         <computed>


Plan: 3 to add, 0 to change, 0 to destroy.

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_vpc.vpc: Creating...
  arn:                              "" => "<computed>"
  assign_generated_ipv6_cidr_block: "" => "false"
  cidr_block:                       "" => "10.0.0.0/16"
  default_network_acl_id:           "" => "<computed>"
  default_route_table_id:           "" => "<computed>"
  default_security_group_id:        "" => "<computed>"
  dhcp_options_id:                  "" => "<computed>"
  enable_classiclink:               "" => "<computed>"
  enable_classiclink_dns_support:   "" => "<computed>"
  enable_dns_hostnames:             "" => "<computed>"
  enable_dns_support:               "" => "true"
  instance_tenancy:                 "" => "default"
  ipv6_association_id:              "" => "<computed>"
  ipv6_cidr_block:                  "" => "<computed>"
  main_route_table_id:              "" => "<computed>"
  owner_id:                         "" => "<computed>"
aws_vpc.vpc: Creation complete after 3s (ID: vpc-058127339ddcbb085)
aws_subnet.public_subnet: Creating...
  arn:                             "" => "<computed>"
  assign_ipv6_address_on_creation: "" => "false"
  availability_zone:               "" => "ap-northeast-1a"
  availability_zone_id:            "" => "<computed>"
  cidr_block:                      "" => "10.0.16.0/20"
  ipv6_cidr_block:                 "" => "<computed>"
  ipv6_cidr_block_association_id:  "" => "<computed>"
  map_public_ip_on_launch:         "" => "false"
  owner_id:                        "" => "<computed>"
  vpc_id:                          "" => "vpc-058127339ddcbb085"
aws_subnet.public_subnet: Creation complete after 1s (ID: subnet-032cb10c161c0bbb0)
aws_instance.sandbox: Creating...
  ami:                          "" => "ami-785c491f"
  arn:                          "" => "<computed>"
  associate_public_ip_address:  "" => "<computed>"
  availability_zone:            "" => "<computed>"
  cpu_core_count:               "" => "<computed>"
  cpu_threads_per_core:         "" => "<computed>"
  ebs_block_device.#:           "" => "<computed>"
  ephemeral_block_device.#:     "" => "<computed>"
  get_password_data:            "" => "false"
  host_id:                      "" => "<computed>"
  instance_state:               "" => "<computed>"
  instance_type:                "" => "t2.micro"
  ipv6_address_count:           "" => "<computed>"
  ipv6_addresses.#:             "" => "<computed>"
  key_name:                     "" => "<computed>"
  network_interface.#:          "" => "<computed>"
  network_interface_id:         "" => "<computed>"
  password_data:                "" => "<computed>"
  placement_group:              "" => "<computed>"
  primary_network_interface_id: "" => "<computed>"
  private_dns:                  "" => "<computed>"
  private_ip:                   "" => "<computed>"
  public_dns:                   "" => "<computed>"
  public_ip:                    "" => "<computed>"
  root_block_device.#:          "" => "<computed>"
  security_groups.#:            "" => "<computed>"
  source_dest_check:            "" => "true"
  subnet_id:                    "" => "subnet-032cb10c161c0bbb0"
  tenancy:                      "" => "<computed>"
  volume_tags.%:                "" => "<computed>"
  vpc_security_group_ids.#:     "" => "<computed>"
aws_instance.sandbox: Still creating... (10s elapsed)
aws_instance.sandbox: Still creating... (20s elapsed)
aws_instance.sandbox: Still creating... (30s elapsed)
aws_instance.sandbox: Creation complete after 33s (ID: i-0348f4fd93bf66a85)

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

成功しました! EC2を確認してみると

$ aws ec2 describe-instances
{
    "Reservations": [
        {
            "Instances": [
                {
                    "Monitoring": {
                        "State": "disabled"
                    }, 
                    "PublicDnsName": "", 
                    "State": {
                        "Code": 16, 
                        "Name": "running"
                    }, 
                    "EbsOptimized": false, 
                    "LaunchTime": "2019-04-25T14:12:03.000Z", 
                    "PrivateIpAddress": "10.0.29.37", 
                    "ProductCodes": [], 
                    "VpcId": "vpc-058127339ddcbb085", 
                    "CpuOptions": {
                        "CoreCount": 1, 
                        "ThreadsPerCore": 1
                    }, 
                    "StateTransitionReason": "", 
                    "InstanceId": "i-0348f4fd93bf66a85", 
                    "EnaSupport": true, 
                    "ImageId": "ami-785c491f", 
                    "PrivateDnsName": "ip-10-0-29-37.ap-northeast-1.compute.internal", 
                    "SecurityGroups": [
                        {
                            "GroupName": "default", 
                            "GroupId": "sg-0f2ce45530720ae5b"
                        }
                    ], 
                    "ClientToken": "", 
                    "SubnetId": "subnet-032cb10c161c0bbb0", 
                    "InstanceType": "t2.micro", 
                    "CapacityReservationSpecification": {
                        "CapacityReservationPreference": "open"
                    }, 
                    "NetworkInterfaces": [
                        {
                            "Status": "in-use", 
                            "MacAddress": "06:5c:7f:5f:64:9a", 
                            "SourceDestCheck": true, 
                            "VpcId": "vpc-058127339ddcbb085", 
                            "Description": "", 
                            "NetworkInterfaceId": "eni-010e14e0518faffd9", 
                            "PrivateIpAddresses": [
                                {
                                    "Primary": true, 
                                    "PrivateIpAddress": "10.0.29.37"
                                }
                            ], 
                            "SubnetId": "subnet-032cb10c161c0bbb0", 
                            "Attachment": {
                                "Status": "attached", 
                                "DeviceIndex": 0, 
                                "DeleteOnTermination": true, 
                                "AttachmentId": "eni-attach-045910b4d5eee5a68", 
                                "AttachTime": "2019-04-25T14:12:03.000Z"
                            }, 
                            "Groups": [
                                {
                                    "GroupName": "default", 
                                    "GroupId": "sg-0f2ce45530720ae5b"
                                }
                            ], 
                            "Ipv6Addresses": [], 
                            "OwnerId": "636993573019", 
                            "PrivateIpAddress": "10.0.29.37"
                        }
                    ], 
                    "SourceDestCheck": true, 
                    "Placement": {
                        "Tenancy": "default", 
                        "GroupName": "", 
                        "AvailabilityZone": "ap-northeast-1a"
                    }, 
                    "Hypervisor": "xen", 
                    "BlockDeviceMappings": [
                        {
                            "DeviceName": "/dev/sda1", 
                            "Ebs": {
                                "Status": "attached", 
                                "DeleteOnTermination": true, 
                                "VolumeId": "vol-038a0eaa1391cf959", 
                                "AttachTime": "2019-04-25T14:12:03.000Z"
                            }
                        }
                    ], 
                    "Architecture": "x86_64", 
                    "RootDeviceType": "ebs", 
                    "RootDeviceName": "/dev/sda1", 
                    "VirtualizationType": "hvm", 
                    "HibernationOptions": {
                        "Configured": false
                    }, 
                    "AmiLaunchIndex": 0
                }
            ], 
            "ReservationId": "r-0d56d4e822f7ac81c", 
            "Groups": [], 
            "OwnerId": "636993573019"
        }
    ]
}

正常に作られていることが確認できました。 こんなに簡単に作れるとは思っていなかったので、驚きです。。。

【AWS】AWSのデフォルトKMSキーで暗号化したRDSスナップショットを別AWSアカウントに共有する方法

 TL;DR

AWSデフォルトのKMSキーで暗号化したRDSスナップショットを別AWSアカウントに共有する方法を記載しています。

関連情報

以下を参考にしました。

docs.aws.amazon.com

docs.aws.amazon.com

暗号化されていないRDSスナップショットを別アカウントに共有する方法は以下の記事で記載したのですが、 今回は暗号化されているRDSを別アカウント間で共有する方法をやってみます。 maedakosuke.hatenablog.com

また、上記のDB のスナップショットの共有 - Amazon Relational Database Serviceによると

スナップショットを共有する AWS アカウントのデフォルト AWS KMS 暗号化キーを使って暗号化されたスナップショットを共有することはできません。

とのことなので、すでにRDSをデフォルトのKMSキーを使用して、暗号化されている状態だと共有することができないぽいです。

なので、手順としては以下のような感じで試してみようと思います。

  1. スナップショットの取得(A)
  2. (A)をコピーしてデフォルトでないKMSキーで暗号化(B)
  3. (B)を共有
  4. (B)からDBを復元

前提条件

  • デフォルトのKMSキーで暗号化されたRDSが作成されていること。 f:id:koooosuke:20190425160030p:plain

やってみます。

1.まず現状のRDSのスナップショットを取得します。

f:id:koooosuke:20190425160929p:plain

暗号化されていない場合、この状態なら通常スナップショットを共有できるのですが、今の状態だと共有できません。 f:id:koooosuke:20190425161112p:plain

また、現状のRDSのKMSキーを別アカウントと共有するため、KMSキー詳細をみてみます。 f:id:koooosuke:20190425161629p:plain

KMSキーの詳細には、以下が記載されています。

このサービスで暗号化されたリソースを作成するときにマスターキーを定義しない場合、このマスターキーが使用されます。このキーの可用性、耐久性、セキュリティを確保するために、このキーの設定は変更できません。

とのことで、デフォルトのKMSキーは設定変更ができないので、まずKMSキーを新たに作成し、その暗号化キーに変更する必要がありそうです。

2.新たにKMSキーを作成します。

f:id:koooosuke:20190425162752p:plain

3.スナップショットをコピーして、新たに作成した暗号化キーでスナップショットを作成します。

スナップショットのコピー f:id:koooosuke:20190425163129p:plain

新たに作成したKMSキー(rds_key)をマスターキーとしてスナップショットをコピーします。(test-db-snapshot-new) f:id:koooosuke:20190425165910p:plain

4.test-db-snapshot-newを共有先のアカウントに共有します。 f:id:koooosuke:20190425170114p:plain

すると、共有先のAWSアカウントでスナップショットが共有ができていると思います。 f:id:koooosuke:20190425170415p:plain

ただ、この状態から復元はできないので、コピーをしようとするのですが、KMSキーが共有されておらず 権限がないため、コピーもできない状態です。そのため、KMSキーを共有していきます。 f:id:koooosuke:20190425170538p:plain

5.KMSキーを外部アカウントに対して共有します。

IAM>暗号化キーから外部アカウントAWSアカウントID番号を入力して、共有します。 f:id:koooosuke:20190425170820p:plain

6.共有先のAWSアカウントで共有済のスナップショットからコピーを作成し復元してみます。

共有されたスナップショットを復元することができなかったので、コピーを作成してみます。 f:id:koooosuke:20190425175729p:plain

次に、コピーしたスナップショットからDBを復元してみます。 f:id:koooosuke:20190425180837p:plain

正常に接続でき、復元できました。