Slide 1

Slide 1 text

Ruby for IaC Genki Sugawara Roppongi.rb#2

Slide 2

Slide 2 text

$ whoami • ܙൺणͷํ͔Βདྷ·ͨ͠ • github: winebarrel • twitter: @sgwr_dts • Rubyͱ͔AWSͱ͔

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Agenda 1. Codenize.tools 2. How to make tools 3. Ruby tools at Work

Slide 6

Slide 6 text

Codenize.tools

Slide 7

Slide 7 text

Codenize.tools • https://codenize.tools/ • AWSͳͲͷίʔυԽπʔϧू • Datadog΍MySQLͷπʔϧ΋͋Γ·͢ • ۀ຿ͰࠔͬͨλΠϛϯάͰద౰ʹ࡞੒

Slide 8

Slide 8 text

ίʔυԽʁ

Slide 9

Slide 9 text

ίʔυԽ αʔϏεͷεςʔλεΛίʔυͰදݱ͢Δ͜ͱ …ͱউखʹߟ͑ͯ·͢ɻ ʢग़య͕Α͘Θ͔Βͳ͍ʣ

Slide 10

Slide 10 text

ίʔυԽ RoadworkerʹΑΔ Route53ͷίʔυԽͷҰྫ hosted_zone "example.com." do rrset "example.com.", "A" do ttl 300 resource_records( "127.0.0.1", "127.0.0.2" ) end end

Slide 11

Slide 11 text

DEMO

Slide 12

Slide 12 text

Կ͕͏Ε͍͠ͷ͔ʁ • ςΩετΛमਖ਼͢Δ͚ͩͷ ҆શͰ؆୯ͳঢ়ଶมߋ • GitΛ࢖ͬͨཤྺ؅ཧ • ϓϧϦΫΤετΛ࢖ͬͨϫʔΫϑϩʔ

Slide 13

Slide 13 text

ίʔυԽྫᶃ: Security Group ec2 "vpc-XXXXXXXX" do security_group "default" do description "default VPC security group" ingress do permission :tcp, 22..22 do ip_ranges( "0.0.0.0/0", ) end

Slide 14

Slide 14 text

ίʔυԽྫᶄ: IAM user "bob", :path => "/developer/" do groups( "Admin" ) policy "bob-policy" do {"Version"=>"2012-10-17", "Statement"=> [{"Action"=> ["s3:Get*",

Slide 15

Slide 15 text

ίʔυԽྫ: MySQLϢʔβ user "scott", "%" do on "*.*" do grant "USAGE" end on "test.*" do grant "SELECT" grant "INSERT" end

Slide 16

Slide 16 text

͜Μͳ΋ͷΛࡉʑͱ࡞͍ͬͯ·͢

Slide 17

Slide 17 text

ͳͥ࡞͔ͬͨʁ • ΦϖϛεͰMXϨίʔυ͕ਧͬඈͿ • ChefͬΆ͘DNSΛ؅ཧ͍ͨ͠ • Roadworker࡞ͬͨ • ࣅͨΑ͏ͳͷ࡞ͬͨ

Slide 18

Slide 18 text

ͳͥRubyʁ Rubyͷॊೈੑ͕ ཉ͔͔ͬͨ͠Β …ͱ͍͏ϙϦγʔ͕͋ͬͨΘ͚Ͱ͸ͳ͘

Slide 19

Slide 19 text

ͳͥRubyʁ • جຊతʹ͸ChefͷӨڹ • Puppet΍Chefʹ׳Ε͍ͯͨͷͰ YAMLͳͲΛ࢖͏ൃ૝͕ͳ͔ͬͨ • ޙ͔ΒʮԿͰ΋ग़དྷΔͳʯͱࢥͬͨ

Slide 20

Slide 20 text

Α͍ͱ͜Ζ΋͋Γѱ͍ͱ͜Ζ΋͋Γ

Slide 21

Slide 21 text

Α͍ͱ͜Ζ • ͖Ε͍ɻͰ͢ΑͶʁ ʢJS○NͭΒ͍ʣ • ॊೈ • Πϯλʔωοτ͔ΒIPϦετΛऔಘ • ແؔ܎ͳπʔϧͷઃఆΛύʔε • etc...

Slide 22

Slide 22 text

ѱ͍ͱ͜Ζ • ॊೈ • Τϥʔ͕Θ͔Γʹ͍͘ ʢվળ͸ͯ͠·͢…ʣ

Slide 23

Slide 23 text

ͦΕTerraformͰʢry

Slide 24

Slide 24 text

ͦΕTerraformͰʢry • ͦ΋ͦ΋࡞Γ࢝Ίͨͱ͖͸ ͳ͔ͬͨΜͰ͢Α • ϦϦʔε͞Εͨͱ͖ͷؾ࣋ͪ ʘ(^o^)ʗ

Slide 25

Slide 25 text

ͦΕTerraformͰʢry ϙϦγʔͷҧ͍͸͋Γ·͢: • DSL͕ঢ়ଶ 㱻 tfstate • αʔϏε͔ΒΤΫεϙʔτ • ޷͖ͳํΛ࢖͏ͱΑ͍ͱࢥ͍·͢ʂ

Slide 26

Slide 26 text

How to make tools

Slide 27

Slide 27 text

Apply • API→Hash • DSL→Hash • Hashಉ࢜Λൺֱ • ࠩ෼ΛຒΊΔAPIΛͨͨ͘ • ͪΐ͏͔ΜͨΜ

Slide 28

Slide 28 text

Export • API→Hash • Hash→DSL • ͪΐ͏͔ΜͨΜ

Slide 29

Slide 29 text

ྫ: Mappru • https://github.com/winebarrel/mappru vpc "vpc-12345678" do route_table "foo-rt" do subnets "subnet-12345678" route destination_cidr_block: "0.0.0.0/0", gateway_id: "igw-12345678" route destination_cidr_block: "192.168.100.101/32", network_interface_id: "eni-12345678"

Slide 30

Slide 30 text

Apply: API→Hash require 'aws-sdk' resource = Aws::EC2::Resource.new rt = resource.route_tables.first p tr.routes #=> #,

Slide 31

Slide 31 text

Apply: API→Hash aws-sdkͷग़ྗ͸ ͍͍ͩͨHashʹ͠΍͍͢Ͱ͢

Slide 32

Slide 32 text

Apply: API→Hash class Mappru::Exporter ... def export result = {} @resource.route_tables.each do |rt| vpc_id = rt.vpc_id name = rt.tags... result[vpc_id][name] = export_route_table(rt) end result end def export_route_table(rt) { route_table_id: rt.id, subnets: export_subnets(rt.associations), }

Slide 33

Slide 33 text

Apply: API→Hash {"vpc-xxxxxxxx"=> {"foo"=>{:route_table_id=>"rtb-xxxxxxxx", :routes=>[], :subnets=>[]}, "bar"=> {:route_table_id=>"rtb-xxxxxxxx", :routes=> [{:destination_cidr_block=>"0.0.0.0/0", :gateway_id=>"igw-xxxxxxxx", :network_interface_id=>nil, :vpc_peering_connection_id=>nil, :nat_gateway_id=>nil}, {:destination_cidr_block=>"192.168.100.102/32", :gateway_id=>nil, :network_interface_id=>"eni-xxxxxxxx", :vpc_peering_connection_id=>nil, :nat_gateway_id=>nil}], :subnets=>["subnet-xxxxxxxx", "subnet-xxxxxxxx"]}}}

Slide 34

Slide 34 text

Apply: DSL→Hash class Mappru::DSL::Context attr_reader :result def initialize(&block) @result = {} instance_eval(&block) end def vpc(vpc_id, &block) @result[vpc_id] = Mappru::DSL::Context::VPC.new(vpc_id, &block).result end end

Slide 35

Slide 35 text

Apply: DSL→Hash class Mappru::DSL::Context::VPC attr_reader :result def initialize(vpc_id, &block) @vpc_id = vpc_id @result = {} instance_eval(&block) end def route_table(name, &block) @result[name] = Mappru::DSL::Context::VPC::RouteTable.new(@vpc_id, name, &block).result end end

Slide 36

Slide 36 text

Apply: ൺֱɾAPIίʔϧ ΤΫεϙʔτͯ͠ class Mappru::Client def apply(file) expected = load_file(file) actual = Mappru::Exporter.export(@client, @options) walk_vpcs(expected, actual) end

Slide 37

Slide 37 text

Apply: ൺֱɾAPIίʔϧ VPCΛൺֱͯ͠ def walk_vpcs(expected, actual) expected.each do |vpc_id, expected_rts| actual_rts = actual.delete(vpc_id) if actual_rts walk_vpc(vpc_id, expected_rts, actual_rts) end end end

Slide 38

Slide 38 text

Apply: ൺֱɾAPIίʔϧ ࠩҟ͕͋ͬͨΒAPIΛͨͨ͘ def walk_vpc(vpc_id, expected, actual) expected.each do |rt_name, expected_rt| actual_rt = actual.delete(rt_name) unless actual_rt actual_rt = @driver.create_route_table( vpc_id, rt_name, expected_rt)

Slide 39

Slide 39 text

Export: Hash→DSL class Mappru::DSL::Converter def convert output_vpcs(@exported) end ...(தུ)... def output_vpc(vpc_id, rt_by_name) route_tables = output_route_tables(rt_by_name).strip <<-EOS vpc #{vpc_id.inspect} do #{route_tables} end EOS end

Slide 40

Slide 40 text

πʔϧͷ࢓૊Έ • શମతʹ͍ͨͨ͜͠ͱ΍ͬͯ·ͤΜ • DSL·ΘΓ͕एׯख͙ؒΒ͍ • YAMLͱ͔࢖͑͹΋ͬͱָʹ࡞ΕΔ

Slide 41

Slide 41 text

ͪͳDslhͱ͍͏΋ͷ͕͋Γ·ͯ͠ Dslh.eval do glossary do title "example glossary" GlossDiv { title "S" } end end # => {"glossary"=> # {"title"=>"example glossary", # "GlossDiv"=> # {"title"=>"S"}}}

Slide 42

Slide 42 text

ͪͳDslhͱ͍͏΋ͷ͕͋Γ·ͯ͠ • https://github.com/winebarrel/dslh • HashΛRubyͰॻ͚ΔṖgem • HashΛRubyʹม׵͢Δ͜ͱ΋ग़དྷΔ • ָʹDLSॻ͚ΔͷͰօ͞Μ΋ੋඇ…

Slide 43

Slide 43 text

Ruby tools at Work

Slide 44

Slide 44 text

Ұ਎্ͷ౎߹ʹΑΓ ࠷ۙ͸৽͍͠AWSΞΧ΢ϯτͰ ৽͍͠αʔό؀ڥΛ࡞Δ͜ͱ͕ଟ͍Ͱ͢

Slide 45

Slide 45 text

AWS؀ڥΛ࡞Δ

Slide 46

Slide 46 text

AWS؀ڥΛ࡞Δ ϦϙδτϦ࡞Δ

Slide 47

Slide 47 text

AWS؀ڥΛ࡞Δ KumogataͰVPCΛ࡞Δ template do AWSTemplateFormatVersion "2010-09-09" Resources do myVPC do Type "AWS::EC2::VPC" Properties do CidrBlock "10.0.0.0/16" EnableDnsSupport true EnableDnsHostnames true Tags [_{ Key "Name" Value "foo" }] end end # (ུ)

Slide 48

Slide 48 text

AWS؀ڥΛ࡞Δ • ͬ͘͟Γͱͨ͠VPCߏ੒͚ͩςϯϓϨʔτ͔Β࡞Δ • VPCɺSubnetɺRouteTable… • Ϧιʔε͸CFnͷελοΫͱ͸ؔ࿈͚ͮͳ͍ • RouteTable͸ΤΫεϙʔτ͓ͯ͘͠ • ࡉʑͱ͍Ζ͍Ζʢsshͷ౿Έ୆ͱ͔NATͱ͔ʣ

Slide 49

Slide 49 text

AWS؀ڥΛ࡞Δ route_table ├── foo_private_az-a.rtbl ├── foo_private_az-c.rtbl └── foo_public.rtbl vpc "vpc-xxxxxxxx" do route_table "healthcare private az-a" do subnets "subnet-xxxxxxxx" route destination_cidr_block: "0.0.0.0/0", nat_gateway_id: ... route destination_cidr_block: "10.10.0.0/16", vpc_peering_connection_id: ...

Slide 50

Slide 50 text

AWS؀ڥΛ࡞Δ IAMͷϩʔϧΛ࡞Δ iam ├── groups ├── policies ├── roles │ ├── admin.iam ├── templates └── users ├── sugawara.iam role "admin", :path=>"/" do attached_managed_policies( "arn:aws:iam::aws:policy/AdministratorAccess"

Slide 51

Slide 51 text

AWS؀ڥΛ࡞Δ • εΠον͢Δϩʔϧ • ؅ཧϢʔβ • etc..

Slide 52

Slide 52 text

AWS؀ڥΛ࡞Δ PackerͰAMI࡞Δ • جຊతʹAmazon Linux͔Β • EC2༻ͱECS༻ͷ2छྨ • ϓϩϏδϣχϯά͸֎෦γΣϧεΫϦϓτ ʢ্هͰڞ௨ʣ • جຊతͳπʔϧ͸ग़དྷΔ͚ͩೖΕΔํ਑

Slide 53

Slide 53 text

AWS؀ڥΛ࡞Δ αʔόͷϩʔϧ(໾ׂ)͝ͱʹ * Security GroupΛ࡞Δ security_group/ ├── foo │ ├── app-internal.group │ ├── db-default.group

Slide 54

Slide 54 text

AWS؀ڥΛ࡞Δ IPΞυϨεͱ͔͸ม਺ʹ·ͱΊ·͢ OFFICE = %w(10.0.0.1/32 10.0.0.2/32) ec2 "vpc-xxxxxxxx" do security_group "rproxy" do description "rproxy" ingress do permission :tcp, 80..80 do ip_ranges( *OFFICE,

Slide 55

Slide 55 text

AWS؀ڥΛ࡞Δ αʔόͷϩʔϧ(໾ׂ)͝ͱʹ * IAMϩʔϧΛ࡞Δ iam ├── roles │ ├── ec2-app-internal.iam │ ├── ec2-rproxy.iam

Slide 56

Slide 56 text

AWS؀ڥΛ࡞Δ ڞ௨ͷϙϦγʔ͸ςϯϓϨʔτʹ·ͱΊ·͢ɻ template "EC2Base" do attached_managed_policies( "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess" ) end role "ec2-base", :path=>"/" do include_template "EC2Base"

Slide 57

Slide 57 text

AWS؀ڥΛ࡞Δ RakeλεΫͰαʔόΛىಈ $ bundel exec rake ec2-bootstrap:new vim্ཱ͕͕ͪͬͯ ςϯϓϨʔτΛฤूͯ͠อଘ͢Δͱ ͦΕʹैͬͯΠϯελϯεΛͨͯΔRakeλεΫ

Slide 58

Slide 58 text

AWS؀ڥΛ࡞Δ

Slide 59

Slide 59 text

AWS؀ڥΛ࡞Δ Itamae੔උ itamae ├── cookbooks │ ├── bind-utils │ ├── datadog │ ├── datadog-docker │ ├── dstat │ ... └── roles ├── app-internal ├── base ├── batch ...

Slide 60

Slide 60 text

AWS؀ڥΛ࡞Δ capͰαʔόʹద༻ bundel exec cap itamae:dry-run ROLES=rproxy bundel exec cap itamae:apply ROLES=rproxy

Slide 61

Slide 61 text

AWS؀ڥΛ࡞Δ αʔόʹద༻͢ΔϨγϐ͸ base/default.rbͱ ͦͷαʔόͷRoleλάͰܾఆ itamae/roles/base/default.rb + itamae/roles//default.rb

Slide 62

Slide 62 text

AWS؀ڥΛ࡞Δ include_cookbookͰ ΫοΫϒοΫΛinclude͢Δ include_cookbook 'nginx' #=> itamae/cookbooks/default.rb

Slide 63

Slide 63 text

AWS؀ڥΛ࡞Δ Itamaeʹϩʔϧ΍ΫοΫϒοΫͱ͍͏ ࢓૊Έ͸ͳ͍ͷͰࣗલͰ࡞ΓࠐΈ… require 'pathname' module RecipeHelper ITAMAE_DIR = Pathname('..').expand_path(__FILE__) def include_role(name) recipe_file = 'default.rb' include_recipe ITAMAE_DIR.join('roles', name, recipe_file).to_s end def include_cookbook(name) recipe_file = 'default.rb' include_recipe ITAMAE_DIR.join('cookbooks', name, recipe_file).to_s end

Slide 64

Slide 64 text

AWS؀ڥΛ࡞Δ rake͡Όͳͯ͘capΛ࢖͍ͬͯΔͷ͸ αʔό্Ͱitamae localΛ࣮ߦ͍ͯ͠ΔͨΊ

Slide 65

Slide 65 text

AWS؀ڥΛ࡞Δ cap itamae:apply • αʔό্͔ΒGitHubͷϦϙδτϦΛ tarballͱͯ͠औಘˠద༻ cap itamae:apply USL_LOCAL=1 • ϩʔΧϧ؀ڥͷϦϙδτϦσΟϨΫτϦΛ tarballͱͯ͠S3ʹΞοϓϩʔυ • αʔό্ͰS3͔ΒtaballΛμ΢ϯϩʔυˠద༻

Slide 66

Slide 66 text

AWS؀ڥΛ࡞Δ Πϯϑϥ৘ใ؅ཧ༻ͷYAML ϧʔτʹஔ͍͓͍͍ͯͯΖΜͳՕॴͰར༻͢Δ ip_address: nat: - 52.11.22.33 # ax-a - 52.22.33.44 # az-c ssh_gw: - 52.123.111.222 ygp_office: - 122.11.12.13 elb: - ...

Slide 67

Slide 67 text

AWS؀ڥΛ࡞Δ ELBͷϗετ໊Λ * Itamaeͷnginx.confͰ࢖͏ * Roadworker(Route53)ͷdns_nameͰ࢖͏ ΦϑΟεͷIPΞυϨεϦετΛ * Itamaeͷnginx.confͰ࢖͏ * Piculet(SG)ͷΞΫηε੍ޚͰ࢖͏ etc...

Slide 68

Slide 68 text

AWS؀ڥΛ࡞Δ ൿಗ஋ʹ͍ͭͯ͸credstashͳͲΛ࢖ͬͯ·͢ • https://github.com/fugue/credstash • https://github.com/winebarrel/gcredstash • https://github.com/adorechic/rcredstash • https://github.com/adorechic/secret_env • https://github.com/adorechic/secret_console see KMS/DynamoDBΛ࢖ͬͨൿີύϥϝʔλͷอ؅ - Qiita

Slide 69

Slide 69 text

AWS؀ڥΛ࡞Δ • Route53ΛΤΫεϙʔτ • Bucket PolicyΛΤΫεϙʔτ • CloudFrontΛΤΫεϙʔτ • DatadogΛΤΫεϙʔτ • MySQLϢʔβΛΤΫεϙʔτ • etc... ϦϙδτϦʹAWSઃఆΛͲΜͲΜ௥Ճ

Slide 70

Slide 70 text

AWS؀ڥΛ࡞Δ ͋ͱ͸RakeλεΫΛ੔͑ͯ README.meΛ੔͑ͯ ։ൃϝϯόʔʹҾ͖౉͠…

Slide 71

Slide 71 text

Έ͍ͨͳײ͡Ͱ πʔϧΛ׆༻͍ͯ͠·͢

Slide 72

Slide 72 text

No content