เริ่มต้นรู้จักกับ Terraform
ในบทความนี้เราก็จะมาทำการรู้จักกับเครื่องมือยอดนิยมตัวนึงของ DevOps ที่ชื่อว่า Terraform กันนะครับ
Terraform ถูกพัฒนาขึ้นโดยบริษัท HashiCorp ซึ่งเครื่องมือนี้เป็นส่วนนึงของการบริหารจัดการ Infrastructure as a Code หรือที่เรียกย่อๆว่า IaC
Infrastructure as a Code (IaC) คืออะไร
ตามชื่อเลยครับ IaC คือการบริหารจัดการระบบโครงสร้างของ software ด้วยการเขียนโค้ด ก่อนที่จะมี IaC เราอาจจะต้องทำทุกอย่าง manual ยกตัวอย่างเช่นหากเราต้องการสร้าง web server สักตัวบน AWS สิ่งที่เราต้องทำคือ login เข้าไปยัง AWS console เลือกอิมเมจพิมพ์ฺและ click เพื่อ set ค่าต่างๆ สร้าง security group เพื่อเปิดพอร์ต ฯลฯ
กระบวนการเหล่านี้ซับซ้อนและไม่สามารถทำซ้ำได้ เช่นถ้าเราต้องการ web server อีกตัวก็ต้องทำ process เดิมซ้ำๆซึ่งเสียเวลา นี่แค่ตัวอย่างของ web server ง่ายๆนะครับ ถ้าเป็นระบบที่ซับซ้อนกว่านี้จะทำให้เสียเวลาไปอีกเท่าไหร่
IaC จึงถูกออกแบบมาเพื่อแก้ปัญหานี้ครับ การเขียน infrastructure เพื่อใส่ใน code จะช่วยลดระยะเวลาของ process ซ้ำๆนี้ได้ อีกทั้งยังเป็น document ที่ทำให้คนมาอ่านเข้าใจโครงสร้างได้ง่าย และเมื่อเป็น code เราก็จะสามารถใช้ประโยชน์จาก source control, versioning และอื่นๆอีกมากมายครับ
จะเริ่มต้นใช้ Terraform อย่างไรดี
จากที่ได้กล่าวไปแล้ว Terraform เป็นส่วน provision ของ Infrastructure as a Code สิ่งที่ terraform จะทำคืออ่าน code ของเราเพื่อสร้างทรัพยากรณ์ของระบบที่เราออกแบบขึ้นมาครับ
ตัว Terraform เองมีโครงสร้าง syntax ไม่กี่อย่าง ส่วนที่เหลือจะขึ้นอยู่กับ provider นั้นๆ (เช่น AWS, GCP, Azure) ว่ามีโครงสร้างของทรัพยากรณ์อย่างไร
ดังนั้น คุณไม่จำเป็นต้องจำรายละเอียดของวิธีสร้าง resource ว่าประกอบด้วยอะไรบ้าง ให้จำแค่ syntax หลักของ terraform ก็พอ ที่เหลือให้ reference กับ provider นั้นๆดูครับ
ตัวอย่าง syntax หลักๆของ Terraform
provider
เป็นตัวกำหนดว่าเราจะใช้ provider เจ้าไหนresource
เป็นการกำหนด resource ย่อยของ provider นั้นๆว่าต้องการให้มีอะไรบ้างvariable
เป็นการกำหนดค่าที่ปรับเปลี่ยนได้ในการ deploy infrastructure ตัวอย่างเช่น เราอยากใช้ code เดียวกันในการ deploy web server สอง environment แต่ละ environment เรากำหนดให้ค่า CPU ที่ต่างกัน เราสามารถกำหนดข้อมูลของ CPU ให้เป็น variable ได้ครับdata
ในบางกรณีเราอาจต้องการ refer ถึงข้อมูลที่อยู่กับ provider แล้ว (เช่น ค่า default ต่างๆ) เราสามารถใช้ส่วนของ data เพื่อทำการดึงข้อมูลนั้นๆมาได้
ลองเขียน terraform เพื่อ deploy Nginx ไปยัง Docker
เพื่อให้เข้าใจการทำงานของ Terraform ให้มากขึ้น เรามาลองใช้งานจริงด้วยการ Deploy container ไปยัง Docker ของเราดีกว่าครับ
ในบทเรียนนี้ผมจะใช้ Docker provider เป็นตัวอย่างนะครับเนื่องจากไม่ต้องเสียเวลา register account กับ Cloud provider เนื่องจากทุกอย่างจะอยู่บนเครื่องของเรา
ก่อนอื่นให้ตรวจสอบ version ของ terraform เราก่อน ต้องเป็น version 0.13 ขึ้นไปนะครับ
กำหนด terraform provider
เริ่มแรกเลย เราจะกำหนด terraform provider นะครับ แต่ก่อนอื่นสร้าง directory ที่เราจะใช้เป็น playground ก่อน
1mkdir terraform-demo && terraform-demo
provider ที่เราใช้จะเป็น Docker Provider
ให้สร้างไฟล์ชื่อ main.tf และใส่ script นี้ลงไปครับ
5 source = "kreuzwerker/docker"
12 host = "unix:///var/run/docker.sock"
จากโค้ดของบน บล้อคของ terraform (บรรทัด 2-9) จะเป็นการบอก terraform ว่าเราจะใช้ provider อะไรบ้าง ในทีนี้เราจะใช้แค่ docker provider นะครับ
ส่วนบรรทัด 11-13 จะเป็น provider บล็อคซึ่งใน บล้อคนี้จะเป็นการเซทค่าของ docker ตาม provider ที่เรา declare ข้างบน ในที่นี้จะเป็นการบอกว่าเราจะอ้างอิงถึง docker daemon ที่อยู่บนเครื่องของเราเองครับ
ถึงตอนนี้ให้รันคำสั่ง ข้างล่างครับ
คำสั่ง terraform init
จะเป็นการ download libraries ที่จำเป็นลงมาไว้บนเครื่องเรา ในที่นี้ Terraform จะทำการดาวโหลด docker provider ที่เรา declare ไว้ใน code ข้างบนและนำไปเก็บไว้ใน folder .terraform
.
กำหนด Nginx image และ รัน container
ในไฟล์ main.tf เดิมให้เพิ่ม script เข้าไปดังนี้ครับ
3resource "docker_container" "my_tf_container" {
4 image = docker_image.my_nginx_image.latest
5 name = "my_terraform_nginx_container"
13resource "docker_image" "my_nginx_image" {
จากโค้ดข้างบนจะเห็นได้ว่าเราใช้ resource block ซึ่งมี syntax ดังนี้
resource "<provider>_<resource>" "<resource_name>" {...}
<provider>
คือ docker ตามที่เราได้ declare ไว้ในช่วงต้นลองไฟล์ <resource>
คือ image และ container ตามที่ได้อธิบายไว้ใน docker provider document<resource_name>
คือชื่อที่เราตั้งขึ้นมาเพื่อใช้อ้างอิงภายในโค้ดของเราเอง จากตัวอย่างของเรา จะเห็นได้ว่า container resource จะอ้างอิงถึง image resource ด้วย <resource_name> {...}
ค่าต่างๆภายในปีกกานี้จะแตกต่างกันไปขึ้นอยู่กับ resource และ provider ค่าเหล่านี้เราจะต้อง refer ไปยัง document ของ provider เท่านั้น
นอกจากนี้จะสังเกตได้ว่า container จะถูก declare ขึ้นมาก่อน image ทั้งๆที่ refer ถึง image ซึ่งตัวอย่างนี้แสดงให้เห็นว่าลำดับก่อนหลังในการเขียน code ไม่สำคัญ เนื่องจาก terraform จะสามารถตัดสินใจได้เองว่าควรจะทำการสร้าง resource ไหนก่อนครับ
Terraform plan
คำสั่ง terraform plan
จะเป็นการตรวจสอบล่วงหน้าว่าหลังจาก deploy infrastructure ด้วยโค้ดชุดนี้แล้วจะเกิดอะไรขึ้นบ้าง แต่ยังไม่ใช่การ deploy จริงๆ
ในขั้นตอนนี้จะช่วยให้เราตรวจสอบว่า resources ในระบบจริงจะถูกเปลี่ยนแปลงอะไรบ้างหลังที่เราทำการ deploy ไปแล้ว
ลองรันคำสั่งข้างล่างเลยครับ
output ที่เราได้จะเป็นประมาณ screenshot ข้างล่างครับ
Terraform apply
หลังจากที่เราตรวจสอบ plan และแน่ใจแล้วว่าสิ่งที่เราต้องการ deploy นั้นถูกต้อง ให้รันคำสั่งข้างล่างได้เลยครับ
ระบบจะถามยืนยันว่าเราต้องการ deploy การเปลี่ยนแปลง infrastructure ตาม plan หรือเปล่า ให้เราพิมพ์ yes
และ enter
ผลลัพธ์ที่ได้ก็จะประมาณนี้
เท่านี้ก็เป็นอันเรียบร้อย Nginx ได้ถูก deploy ขึ้น server เรียบร้อย ลองรัน docker เพื่อ check process status และ image ได้เลยครับ
1docker images && docker ps
จะเห็นได้ว่าเรามี Nginx image อยู่ใน local repository และมี container ที่ชื่อ my_terraform_nginx_container
ตามที่เราได้โค้ดไว้
นอกจากนี้ถ้าไปยัง url http://localhost:8080 เราก็จะได้เห็น Nginx welcome page ครับ
Terraform state file
หลังจากที่ได้ทำการ apply แล้วจะสังเกตเห็นได้ว่า terraform สร้างไฟล์ที่ชื่อว่า terraform.tfstate
ขึ้นที่ root folder เสตจไฟล์นี้เป็นตัวบันทึกการเปลี่ยนแปลงที่ทำให้ terraform รู้ว่าในการ apply ครั้งหน้า terraform จะต้องอ้างอิงถึง resource ไหน ตัวอย่างเช่น เราอาจจะมี image และ container ที่ไม่ได้ถูกสร้างด้วย terraform อยู่ใน docker daemon ของเรา ถ้าเราลองสำรวจ state file นี้จะเห็นได้ว่า terraform เก็บข้อมูล รวมถึง id ของ resource ที่เราสร้างขึ้นมา
ใน live environment state file ควรจะถูกเก็บไว้ที่ remote (ซึ่งบทความนี้จะยังไม่ครอบคลุมถึงเนื้อหานี้) เพื่อให้แน่ใจว่า developer จะอ้างอิงถึง state file เดียวกันอีกทั้งจะไม่สามารถสร้างการเปลี่ยนแปลงพร้อมกันได้ เนื่องจาก state file จะมีการ lock ทุกครั้งในระหว่างทำการเปลี่ยนแปลง
4 "type": "docker_image",
5 "name": "my_nginx_image",
6 "provider": "provider[\"registry.terraform.io/kreuzwerker/docker\"]",
13 "id": "sha256:fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c8ed3c5f0cdce3f230bnginx:latest",
15 "latest": "sha256:fa5269854a5e615e51a72b17ad3fd1e01268f278a6684c8ed3c5f0cdce3f230b",
16 "name": "nginx:latest",
19 "pull_triggers": null,
20 "repo_digest": "nginx@sha256:859ab6768a6f26a79bc42b231664111317d095a4f04e4b6fe79ce37b3d199097"
22 "sensitive_attributes": [],
Terraform variable
จากโค้ดที่เราเขียนมา จะเห็นได้ว่าเราทำการ hardcode port ที่ map ออกมายัง host เป็น port 8080 ปัญหาก็คือเราไม่สามารถเปลี่ยนแปลง port นอกจากไปแก้ไขโค้ด ซึ่งในบางสถานการณ์ เราอาจอยากให้ environment ต่างๆใช้ port ที่ต่างกันไปเช่นเราอาจจะอยากให้ qa ของเราใช้พอร์ต 8000 และ production ใช้พอร์ต 9000 ในที่นี้ terraform variable
จะช่วยเราแก้ปัญหานี้ครับ
ให้ใส่ block ข้างล่างเพิ่มเข้าไปในโค้ดของเรา
1variable "nginx_external_port" {
3 description = "Port to map from nginx to docker host"
หลังจากนั้นให้เราเปลี่ยน nginx argument อ้างอิงถึง variable เรา
4 external = var.nginx_external_port
ไฟล์ main.tf ทั้งหมดของเราก็จะเป็นดังนี้ครับ
4 source = "kreuzwerker/docker"
11 host = "unix:///var/run/docker.sock"
14variable "nginx_external_port" {
16 description = "Port to map from nginx to docker host"
20resource "docker_container" "my_first_tf_container" {
21 image = docker_image.my_nginx_image.latest
22 name = "my_terraform_nginx_container_xx"
25 external = var.nginx_external_port
30resource "docker_image" "my_nginx_image" {
หลังจากนี้เราสามารถส่ง variable ผ่าน terraform command ได้เลย ในที่นี้เราจะให้ external port เป็น port 9000
1terraform apply -var 'nginx_external_port=9000'
หลังจาก apply แล้ว terraform ก็จะทำการเปลี่ยน external port ให้เป็น 9000 เราสามารถไปยัง Nginx welcome page ที่ port ใหม่ได้เลย http://localhost:9000
Terraform Destroy
คำสั่งสุดท้ายของเราก็คือ terraform destroy
ครับคำสั่งนี้ก็จะเป็นการลบ resources ทุกอย่างที่เราสร้างขึ้นมาด้วย terraform รันคำสั่งตามข้างล่าง
terraform จะถามเพื่อคอนเฟิร์มให้พิมพฺ์ yes
แล้ว enter
เท่านี้ก็เป็นอันเรียบร้อยครับ ถ้าเรารันคำสั่ง docker images && docker ps
จะพบว่า resources ของเราได้ถูกลบไปเรียบร้อยแล้ว
สรุป
ยินดีด้วยครับ ถึงตอนนี้คนที่ทำตามมาได้จนจบถือว่าได้เรียนรู้พื้นฐานของการใช้ terraform พอสมควรแล้ว ซึ่งสิ่งที่เราได้เรียนรู้ไปก็จะมีการใช้ command terraform init
เพื่อดาวโหลด library ที่จำเป็น terraform plan
เพื่อตรวจสอบสิ่งที่จะเปลี่ยนแปลงกับ resource บนระบบ terraform apply
เพื่อเปลี่ยนแปลง resource และการใช้ terraform destroy
เพื่อทำการลบ resources ทั้งหมดที่สร้างด้วย terraform ออกจากระบบ
นอกจากนี้เรายังได้เรียนรู้วิธีใช้ syntax provider
เพื่อกำหนดและปรับแต่งค่าของ provider ที่เราต้องการ resource
เพื่อกำหนดและอ้างอิงถึง resource ภายในที่เราสร้างขึ้น และใช้ variable
เพื่อทำให้เราสามารถกำหนดค่าต่างๆใน server environment ที่แตกต่างกันได้ หวังว่าบทความนี้จะเป็นประโยชน์ไม่มากก็น้อยนะครับ
สอบถาม ติชม เสนอแนะ ได้ที่ช่อง comment ด้านล่างเลยครับ