ããã«ã¡ã¯ãEngineeringãã¼ã ã®ç³æ(kamatama41)ã§ããEngineeringãã¼ã ã®ä¸»ãªå½¹å²ã¯ã¤ã³ãã©æ§ç¯ãç£è¦ãããã©ã¼ãã³ã¹æ¹åãªã©ã®ããããDevOpsãSREã¨è¨ãããé åã«ãªãã¾ãã Quipperã§ã¯ç¾å¨ã°ãã¼ãã«åãã§ããQuipper (School, Video)ã¨æ¥æ¬åãã®ã¹ã¿ãã£ãµããªã¨ããäºã¤ã®ãããã¯ããéç¨ãã¦ããããããã®ãããã¯ãã¯AWSä¸ã«æ§ç¯ãããTerraformã使ã£ã¦æ§æ管çããã¦ãã¾ããããã§ãç§éãTerraformãéç¨ããããã«å·¥å¤«ãã¦ããç¹ãç´¹ä»ãããã¨æãã¾ãã
Terraformã¨ã¯?
Vagrantãªã©ãæä¾ããHashiCorp社製ã®ã¤ã³ãã©æ§æ管çãã¼ã«ã§ãAWSãGCPãªã©ãæä¾ãã¦ããå種ã¯ã©ã¦ããµã¼ãã¹ããªã½ã¼ã¹ã¨ããåä½ã§æ§ç¯ãã³ã¼ãååºæ¥ããã¼ã«ã«ãªãã¾ãã
(AWSã®EC2ã¤ã³ã¹ã¿ã³ã¹ã5å°ç«ã¡ä¸ããããã®è¨å®ä¾)
resource "aws_instance" "app" { count = 5 ami = "ami-408c7f28" instance_type = "t1.micro" }
ãªãTerraformã使ãã®ã?
ã³ã¼ãåãããã¨ã§GitHubã«ããã³ã¼ãã¬ãã¥ã¼ããã¹ãããããã¤ã¨ããCIã®ãµã¤ã¯ã«ãé©ç¨ã§ããããã«ãããããããInfrastructure as Codeãå®è·µããããã«ä½¿ã£ã¦ãã¾ãã
ãã£ã¬ã¯ããªæ§æ
Terraformã®ãã¡ã¤ã«ç¾¤ã管çããGitHubãªãã¸ããªã¯ã2016å¹´10æç¾å¨ä»¥ä¸ã®ãããªæ§æãåã£ã¦ãã¾ãã
âââ environments â  âââ common.tfvars â  âââ management â  âââ production â  âââ staging â  âââ main.tf â  âââ output.tf â  âââ terraform.tfvars âââ modules â  âââ base_network â  â  âââ vpc.tf â  â  âââ subnet.tf â  â  âââ variables.tf â  â  âââ ... â  âââ reverse_proxy â  â  âââ ec2.tf â  â  âââ security_group.tf â  â  âââ variables.tf â  â  âââ ... â  âââ mongodb â  â  âââ ... â  âââ ...
Quipperã§ã¯æ¬çªç°å¢ãã¹ãã¼ã¸ã³ã°ç°å¢ã社å
ã·ã¹ãã ç¨ç°å¢ã¨ãã£ãç°å¢ãã¨ã«VPCãç«ã¦ã¦ãã¾ãããä»ç°å¢ã¸ã®å½±é¿ãæå°éã«ããããVPCãã¨ã«ç¬ç«ããstateãã¡ã¤ã«ãç¨æãã¦ãã¾ãã
environments/(production|staging|management)
ã®åãã£ã¬ã¯ããªããããããã®ç°å¢ã«ãããTerraformã³ãã³ãã®å®è¡ãã£ã¬ã¯ããªã«ãªãã¾ããmain.tf
ã«ã¯å¾è¿°ã®modulesãå¼ãã ããã®ç°å¢åºæã®ãªã½ã¼ã¹ãå®ç¾©ãã¾ãã
terraform.tfvars
ã¯ãã®ç°å¢ã®ã¿ã§ä½¿ãå¤æ°ãè¨è¿°ããä¸é層ä¸ã®common.tfvars
ã¯å
¨ç°å¢ã§å
±éãã¦ä½¿ãå¤æ°ãè¨è¿°ãTerraformã®-var-file
ãªãã·ã§ã³ã使ã£ã¦èªã¿è¾¼ããããã«ãã¦ãã¾ãã
ã¾ããVPC IDãªã©ã®ç°å¢éã§å
±æãããå¤ã¯output.tf
ã«è¨è¿°ãã¦terraform_remote_stateã使ã£ã¦åç
§ãã¦ãã¾ãã
modules
ã¯å
±éã§ä½¿ãmodule群ã管çããããã®ãã£ã¬ã¯ããªã§ããåmoduleã¯reverse_proxy
ã¨ãã£ããã¼ã«åä½ã§ä½ãããã®ä¸ã¯ec2.tf
ã¨ãã£ãAWSã®ãªã½ã¼ã¹åä½ã§ãã¡ã¤ã«ãä½ã£ã¦ãã¾ããvariables.tf
ã§moduleã«æ¸¡ãããã¹ãå¤æ°ã管çãã¦ãã¾ãã
ã©ããã¼ã¹ã¯ãªãã
plan.sh
, apply.sh
, refresh.sh
, import.sh
ã¨ãã£ãTerraformã³ãã³ãã®ã©ããã¼ã¹ã¯ãªãããç¨æãã¦ãéçºè
ãCircleCIã¯ãã®ã¹ã¯ãªããçµç±ã§Terraformã³ãã³ããå®è¡ããããã«ãã¦ãã¾ããã¹ã¯ãªããå
ã§ã¯ä»¥ä¸ã®ãããªç°å¢ã®åãæ¿ãããå®å
¨æ§ãé«ããããã®å¦çãè¡ãã¾ãã
- ç°å¢ã«å¯¾å¿ããstateãã¡ã¤ã«ããªã¢ã¼ã(S3 backend)ããåå¾ãã
- åå¾å¾ãä¸åº¦ãªã¢ã¼ãã¨ã®é¢é£ä»ããç¡å¹ã«ãã
- planãrefreshãªã©ãå®è¡ããã¨ãã«ãS3ä¸ã®stateãã¡ã¤ã«ãå³åº§ã«æ´æ°ãããªãããã«ãããã
- æ´æ°ããå ´åã¯ãå®è¡åå¾ã®diffã¨yes/noããã³ãããåºããããã§ãå度é¢é£ä»ãã¦ãremote pushãã
- åå¾å¾ãä¸åº¦ãªã¢ã¼ãã¨ã®é¢é£ä»ããç¡å¹ã«ãã
terraform get
ã§moduleãèªã¿è¾¼ãapply
ã³ãã³ããCircleCI以å¤ã§ã¯ã§ããªãããã«ããªãã¼ããã- å®è¡å*1ã«ãã¼ã«ã«ã®
.terraform
ãã£ã¬ã¯ããªãæ¶ã- éãç°å¢ã®stateãã¡ã¤ã«ã§refreshãªããããã£ã¦ãã¾ã£ãæ¥ã«ã¯...((((ï¼ï¾Ðï¾))))
ä¾1: plan.sh
å®éã®ãã®ã¨ã¯ã¡ãã£ã¨éãã¾ããæ¦ããããªæãã§ãã
#!/bin/bash -xe ENV=$1 ENV_PATH=./environments/${ENV} S3_BUCKET=quipper-terraform-state-files # åå¦ç rm -rf .terraform # remote stateãåå¾ããªã¢ã¼ãã¨ã®é¢é£ä»ããç¡å¹ã«ãã terraform remote config \ -backend=S3 -backend-config="bucket=$S3_BUCKET" -backend-config="key=${ENV}.tfstate" terraform remote config -disable -state=./.terraform/${ENV}.tfstate # moduleèªã¿è¾¼ã¿ terraform get ${ENV_PATH} # å¥ç°å¢ã®remote stateæ´æ° terraform refresh -state=./.terraform/${ENV}.tfstate \ --target=data.terraform_remote_state.production \ --target=data.terraform_remote_state.staging \ --target=data.terraform_remote_state.management \ -var-file=./environments/common.tfvars \ -var-file=./environments/${ENV}/terraform.tfvars \ ${ENV_PATH} # planå®è¡ terraform plan \ -state=./.terraform/${ENV}.tfstate \ -refresh=false \ -var-file=./environments/common.tfvars \ -var-file=./environments/${ENV}/terraform.tfvars \ ${ENV_PATH} # å¾å¦ç rm -rf .terraform
(使ç¨ä¾)
$ ./scripts/plan.sh staging
ä¾2: refresh.sh
å®éã®ãã®ã¨ã¯ã¡ãã£ã¨éãã¾ãã(ry
#!/bin/bash -xe ENV=$1 RESOURCE=$2 ENV_PATH=./environments/${ENV} S3_BUCKET=quipper-terraform-state-files # åå¦ç rm -rf .terraform # remote stateãåå¾ããªã¢ã¼ãã¨ã®é¢é£ä»ããç¡å¹ã«ãã terraform remote config \ -backend=S3 -backend-config="bucket=$S3_BUCKET" -backend-config="key=${ENV}.tfstate" terraform remote config -disable -state=./.terraform/${ENV}.tfstate # moduleèªã¿è¾¼ã¿ terraform get ${ENV_PATH} # refreshå®è¡ terraform refresh \ -state=./.terraform/${ENV}.tfstate \ -backup=./.terraform/${ENV}.tfstate.backup \ --target=${RESOURCE} \ -var-file=./environments/common.tfvars \ -var-file=./environments/${ENV}/terraform.tfvars \ ${ENV_PATH} # refreshåå¾ã®diffãåºã, OKã ã£ããstateãã¡ã¤ã«ãremoteã«pushãã diff -u ./.terraform/${ENV}.tfstate.backup ./.terraform/${ENV}.tfstate || true read -p 'Are you sure? [Y/n]' ANSWER case $ANSWER in '' | [Yy]* ) terraform remote config \ -state=./.terraform/${ENV}.tfstate -pull=false \ -backend=S3 -backend-config="bucket=$S3_BUCKET" -backend-config="key=${ENV}.tfstate" terraform remote push ;; * ) echo "Importing was aborted." esac # å¾å¦ç rm -rf .terraform
(使ç¨ä¾)
$ ./scripts/refresh.sh staging module.reverse_proxy
CircleCI
å®éã«åç°å¢ã¸ã®applyãè¡ã£ãããGitHubã¸ã®pushãã¨ã«planãå®è¡ãããã¨å¤§æ´»èºã§ããapplyæ¹æ³ã¯ç°å¢ãã¨ã«release/staging
ã¨ãã£ããªãªã¼ã¹ãã©ã³ããç¨æãã¦ãããmasterãããããã®ãã©ã³ãã¸ã®Pull Requestãä½ããã¼ã¸ããã¨ããéç¨ã«ãã¦ãã¾ããã¾ãplanãapplyã®çµæã¯ããããã®PRã¸é½åº¦POSTãããä»çµã¿ã«ãªã£ã¦ãããçµæã®ç¢ºèªãç°¡åã«åºæ¥ãããã«ãªã£ã¦ãã¾ãã
tfenv
éçºè
ãCircleCIã®éã§å©ç¨ããTerraformã®ãã¼ã¸ã§ã³ãåãããã¨è¨ãã®ãçµæ§éè¦ãªãã¤ã³ãã§ããç°ãªã£ããã¼ã¸ã§ã³ã§stateãã¡ã¤ã«ãæ´æ°ãã¦ãã¾ãã¨äºææ§ããªãã£ããããå ´åãããããã§ãããããå®ç¾ããããã«tfenvã¨ããTerraformã®ãã¼ã¸ã§ã³ããã¸ã¡ã³ããã¼ã«ãä½ãã¾ããããã®ãã¼ã«ã®.terrafom-version
ã¨ããæ©è½*2ã使ãã¨ããªãã¸ããªå
ã§Terraformã³ãã³ãã使ãæã®ãã¼ã¸ã§ã³ãæãããã¨ãã§ãã¾ããQuipper School/Videoã¨ã¹ã¿ãã£ãµããªã¯ããããã¯ãéã§ãã¼ã¸ã§ã³0.6ã¨0.7ãæ··å¨ãã¦ããææããã£ãã®ã§é常ã«å½¹ç«ã¡ã¾ããã
ã¾ã¨ã
ããããç´ä½æ²æã失æ(主ã«tfstateã¾ãã)ãããã¾ããããç¾å¨ã¯çµæ§å®å¿ãã¦éç¨ã§ããä»çµã¿ã«ãªã£ãã¨æãã¾ããä½ãã®åèã«ãªãã°å¹¸ãã§ãã
â Quipperæ¥æ¬ãªãã£ã¹ã§ã¯ä»²éãåéãã¦ãã¾ããæ¯éãæ°è»½ã«ãå¿åãã ãããâ