http://nagaoka.techtalk.jp/no53 https://github.com/hayajo/nds53-terraform
5FSSBGPSNͰߏங͢ΔϚϧνΫϥυϓϥοτϑΥʔϜΠϯϑϥετϥΫνϟԬ*5։ൃऀษڧձ /%4ୈճษڧձ
View Slide
ࣗݾհw )BZBUP*NBJࠓҪ൏ਓw !IBZBKPw Πϯϑϥ୲
5FSSBGPSN
ΠϯϑϥΛϓϩϏδϣχϯά͢Δπʔϧ
ଟ͘ͷϓϥοτϑΥʔϜαʔϏεΛαϙʔτw *BB4"84 ($1 "[VSF 0QFO4UBDLw 1BB4)FSPLVw 4BB4'BTUMZ .BJM(VO /FX3FMJDw ଞʹͨ͘͞Μw ֤ϓϥοτϑΥʔϜαʔϏεݻ༗ͷଟ͘ͷϦιʔεʹରԠ
*OGSBTUSVDUVSFBT$PEFΛ࣮ݱ͢Δw 5FSSBGPSN$POpHVSBUJPOϑΝΠϧʹΠϯϑϥߏΛهड़w ෳͷϓϥοτϑΥʔϜαʔϏεͰߏங͞ΕΔΠϯϑϥετϥΫνϟશମΛදݱw ϓϥοτϑΥʔϜαʔϏε͝ͱʹಠཱͨ͠πʔϧΛ͍Θ͚ͳͯ͘ྑ͍
5FSSBGPSNʹΑΔΠϯϑϥߏஙͷྲྀΕ
ྫ&$&*1Λߏங͢Δ5FSSBGPSN$POpHVSBUJPOprovider "aws" {region = "ap-northeast-1"}resource "aws_instance" "example" {ami = "ami-3bd3c45c"instance_type = "t2.micro"}resource "aws_eip" "example" {instance = "${aws_instance.example.id}"}IUUQTHJUIVCDPNIBZBKPOETUFSSBGPSN
࡞ۀڥͷॳظԽ$ terraform initInitializing provider plugins...- Checking for available provider plugins on https://releases.hashicorp.com...- Downloading plugin for provider "aws" (1.0.0)...Terraform has been successfully initialized!
࣮ߦܭըͷ֬ೝʢ̍ʣ$ terraform plan+ aws_eip.exampleid: instance: "${aws_instance.example.id}"vpc: + aws_instance.exampleid: ami: "ami-3bd3c45c"instance_type: "t2.nano"subnet_id: Plan: 2 to add, 0 to change, 0 to destroy.
࣮ߦܭըͷ֬ೝʢ̎ʣ$ terraform graph | dot -Tpng >tf.png && open tf.png
Πϯϑϥͷߏஙͱ֬ೝʢ̍ʣ$ terraform applyaws_instance.example: Creating...aws_instance.example: Creation complete after 16s (ID:i-0311a0b3adc495d2b)aws_eip.example: Creating...aws_eip.example: Creation complete after 1s (ID: eipalloc-a1c9a39b)Apply complete! Resources: 2 added, 0 changed, 0 destroyed
Πϯϑϥͷߏஙͱ֬ೝʢ̎ʣ$ terraform showaws_eip.example:id = eipalloc-a1c9a39binstance = i-0311a0b3adc495d2bpublic_ip = 13.115.163.200aws_instance.example:id = i-0311a0b3adc495d2bami = ami-3bd3c45cinstance_type = t2.nanow ঢ়ଶUFSSBGPSNUGTUBUFϑΝΠϧͰཧ͞ΕΔ ϩʔΧϧʢσϑΥϧτʣ4ɺ($4ɺFUDEͳͲͷόοΫΤϯυͰཧՄೳ όοΫΤϯυʹΑͬͯϩοΫՄೳ
Πϯϑϥͷߋ৽$ terraform plan~ aws_eip.exampleinstance: "i-0311a0b3adc495d2b" => "${aws_instance.example.id}"-/+ aws_instance.example (new resource required)id: "i-0311a0b3adc495d2b" => (forces new resource)ami: "ami-3bd3c45c" => "ami-dfd0c7b8" (forces new resource)instance_type: "t2.nano" => "t2.nano"subnet_id: "subnet-ec29e9a5" => Plan: 1 to add, 1 to change, 1 to destroy.ami = "ami-3bd3c45c" => "ami-dfd0c7b8"
Πϯϑϥͷഁغ$ terraform plan --destroy- aws_eip.example- aws_instance.examplePlan: 0 to add, 0 to change, 2 to destroy.$ terraform destroyaws_eip.example: Destroying... (ID: eipalloc-a1c9a39b)aws_instance.example: Destroying... (ID:i-0311a0b3adc495d2b)Destroy complete! Resources: 2 destroyed.
5FSSBGPSNͰߏங͢ΔϚϧνΫϥυϓϥοτϑΥʔϜΠϯϑϥετϥΫνϟ
ྫ,JOFTJTͱ#JH2VFSZͰߏங͢Δσʔλੳج൫IUUQTHJUIVCDPNIBZBKPOETUFSSBGPSN
࡞ۀڥͷॳظԽ
terraform {required_version = "~> 0.10.0"}provider "aws" {access_key = "${var.aws["access_key"]}"secret_key = "${var.aws["secret_key"]}"region = "${var.aws["region"]}"}provider "google" {credentials = "${file(var.google["credentials"])}"project = "${var.google["project"]}"region = "${var.google["region"]}"}
variable "aws" {default = {access_key = ""secret_key = ""region = "ap-northeast-1"}}variable "google" {default = {credentials = "credentials.json"project = ""region = ""}}variable "service_name" {default = "nds53"}
,JOFTJT4USFBN
resource "aws_kinesis_stream" "main" {name = "${var.service_name}-${terraform.workspace}"shard_count = 1}output "Kinesis stream" {value = "${aws_kinesis_stream.main.name}"}
#JH2VFSZ
resource "google_bigquery_dataset" "main" {# only [0-9a-zA-Z_]dataset_id = "${var.service_name}_${terraform.workspace}"}resource "google_bigquery_table" "main" {dataset_id = "${google_bigquery_dataset.main.dataset_id}"table_id = "${var.bq["table_id"]}"schema = "${file("bq/schema.json")}"}variable "bq" {default = {table_id = "myapp"}}output "BigQuery" {value = "${var.google["project"]}:${google_bigquery_dataset.main.dataset_id}:${google_bigquery_table.main.table_id}"}
[{ "name": "time", "type": "timestamp", "mode": "required" },{ "name": "tag", "type": "string", "mode": "required" },{ "name": "value", "type": "float", "mode": "required" }]CRTDIFNBKTPO
,.4
resource "aws_kms_key" "main" {}resource "aws_kms_alias" "main" {name = "alias/${var.service_name}-${terraform.workspace}"target_key_id = "${aws_kms_key.main.key_id}"}
-BNCEB
data "archive_file" "kinesis2bq" {type = "zip"source_dir = "lambda/kinesis2bq"output_path = "lambda/kinesis2bq.zip"}resource "aws_lambda_function" "kinesis2bq" {filename = "${data.archive_file.kinesis2bq.output_path}"function_name = "${var.service_name}-${terraform.workspace}-kinesis2bq"role = "${aws_iam_role.kinesis_lambda.arn}"handler = "kinesis2bq.handler"source_code_hash = "${data.archive_file.kinesis2bq.output_base64sha256}"runtime = "python2.7"timeout = 60# NOTE: ΩʔΛؚΊͯ4KB·Ͱenvironment {variables = {BQ_CREDENTIALS = "${data.aws_kms_ciphertext.bq_credentials.ciphertext_blob}"BQ_PROJECT = "${var.google["project"]}"BQ_DATASET = "${google_bigquery_dataset.main.dataset_id}"BQ_TABLE = "${google_bigquery_table.main.table_id}"}}}resource "aws_lambda_event_source_mapping" "kinesis2bq" {batch_size = 100 # default 100event_source_arn = "${aws_kinesis_stream.main.arn}"function_name = "${aws_lambda_function.kinesis2bq.arn}"starting_position = "TRIM_HORIZON"}
data "aws_kms_ciphertext" "bq_credentials" {key_id = "${aws_kms_key.main.id}"plaintext = "${file(var.bq["credentials"])}"}variable "bq" {default = {table_id = "myapp"credentials = "credentials.json" # }}
resource "aws_iam_role" "kinesis_lambda" {name = "${var.service_name}-${terraform.workspace}-kinesis_lambda"assume_role_policy = "${data.aws_iam_policy_document.kinesis_lambda_assume_role_policy.json}"}data "aws_iam_policy_document" "kinesis_lambda_assume_role_policy" {statement {actions = ["sts:AssumeRole",]principals {type = "Service"identifiers = ["lambda.amazonaws.com"]}}}resource "aws_iam_role_policy" "kinesis_lambda" {name = "${var.service_name}-${terraform.workspace}-kinesis_lambda"role = "${aws_iam_role.kinesis_lambda.id}"policy = "${data.aws_iam_policy_document.kinesis_lambda_role_policy.json}"}data "aws_iam_policy_document" "kinesis_lambda_role_policy" {statement {actions = ["kinesis:*"]resources = ["${aws_kinesis_stream.main.arn}"]}statement {actions = ["kms:Decrypt"]resources = ["${aws_kms_key.main.arn}"]}statement {actions = ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents",]resources = ["arn:aws:logs:*:*:*",]}}
1 import base642 import datetime3 import json4 import os5 import sys67 import boto389 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'vendor'))10 from gcloud import bigquery11 from oauth2client.service_account import ServiceAccountCredentials1213 BQ_CREDENTIALS = os.environ['BQ_CREDENTIALS']14 BQ_PROJECT = os.environ['BQ_PROJECT']15 BQ_DATASET = os.environ['BQ_DATASET']16 BQ_TABLE = os.environ['BQ_TABLE']1718 def handler(event, context):19 rows = []2021 for r in event['Records']:22 payload = r['kinesis']['data']23 try:24 data = json.loads(base64.b64decode(payload))25 row = []26 for key in ['time', 'tag', 'value']:27 if key == 'time':28 row.append(datetime.datetime.fromtimestamp(data[key]))29 else:30 row.append(data[key])31 rows.append(tuple(row))32 except Exception as e:33 print('Invalid data "{0}": {1}'.format(payload, e))34 pass3536 if len(rows) == 0:37 return3839 kms = boto3.client('kms')40 blob = base64.b64decode(BQ_CREDENTIALS)41 dec = kms.decrypt(CiphertextBlob = blob)42 keyfile_dict = json.loads(dec['Plaintext'])43 credentials = ServiceAccountCredentials.from_json_keyfile_dict(keyfile_dict)4445 bq = bigquery.Client(credentials = credentials, project = BQ_PROJECT)46 dataset = bq.dataset(BQ_DATASET)47 table = dataset.table(BQ_TABLE)48 table.reload()49 res = table.insert_data(rows)5051 print(res)
ߏங$ pip install gcloud -t lambda/kinesis2bq/vendor/$ test -e lambda/kinesis2bq/vendor/google/__init__.py || \> touch lambda/kinesis2bq/vendor/google/__init__.py$ terraform init$ terraform plan --var-file=config.tfvars$ terraform apply --var-file=config.tfvars$ terraform showࣄલ४උʢ3&"%.&NEࢀরʣߏஙͱ֬ೝ
ಈ࡞֬ೝ$ TEST_JSON=$(mktemp)$ test/gen_kinesis_records.sh $KINESIS_STREAM >$TEST_JSON$ cat $TEST_JSON | jq . -C | less -R$ aws kinesis put-records --cli-input-json file://$TEST_JSON,JOFTJTʹσʔλΛೖ#JH2VFSZͰσʔλΛ֬ೝ$ bq query "SELECT COUNT(time) FROM $BIGQUERY"˞,*/&4*[email protected]&".ͱ#*(26&3:UFSSBGPSNBQQMZͷ݁ՌͰஔ͖͑
·ͱΊ
·ͱΊw ଟ͘ͷϓϥοτϑΥʔϜαʔϏεΛαϙʔτͨ͠ɺΠϯϑϥͷϓϩϏδϣχϯάπʔϧw ΠϯϑϥશମΛίʔυͰهड़͠ɺόʔδϣϯཧɺڞ༗ɺ࠶ར༻Ͱ͖Δw ϓϥοτϑΥʔϜ͝ͱͷಠཱͨ͠πʔϧΛΘͳͯ͘ྑ͍w ࣄલͷ࣮ߦܭըͷ֬ೝʹΑΓෆҙͷมߋΛ͙͜ͱ͕Ͱ͖Δ