Wall of Text #10: Cloud

จากข่าว SCB Easy ล่มเพราะไปใช้ AWS ผมเดาไว้ใน Facebook ว่าเค้าน่าจะเอา ELB มาแล้ว load balance on premise แล้ว Link พัง น้องเค้าถามว่าเข้าใจว่าหมายถึงอะไร

ย้ำอีกทีว่าเรื่องที่เกี่ยวกับธนาคาร ผมก็เดามั่วไปเท่านั้นแหละ

On Premises

เพิ่งมารู้ว่าการเขียนว่า On Premise นี่ไม่ถูกต้อง ต้องเขียนว่า On Premises เสมอ เพราะ Premises แปลว่าที่ตั้งและมันไม่ใช่รูปพหูพจน์

สำหรับธนาคารแล้ว Server ต่างที่ให้บริการจะอยู่ในศูนย์ข้อมูลของธนาคารเอง เค้าก็เลยเรียกว่า On Premises ก็คือมันอยู่ในที่ตั้งของเรา

มาร์เก็ตติ้งของ Cloud มักจะใช้คำว่า On Premises หมายรวมไปถึงระบบที่ไม่ใช่ Cloud ทั้งหมดถึงมันจะไม่อยู่ที่ออฟฟิศของเราก็ตาม ซึ่งไม่ว่ามันจะถูกหรือผิดมันก็เห็นภาพชัดเจนดีเวลาเราบอกว่าย้ายระบบจาก On Premises ขึ้น Cloud ก็คือย้ายจาก server เดิมไปยังผู้ให้บริการ cloud

Cloud

ทีนี้ถามว่าทำไมธนาคารถึงจะอยากใช้ Cloud? Cloud คืออะไร…

มีตำนานเล่าว่านานมาแล้วมีอุกกาบาตตกลงทุ่งข้าวสาลี ในช่วง Black Friday มีคนเข้ามาซื้อของที่เว็บ Amazon.com เป็นจำนวนมาก Amazon ก็เลยจะต้องจัดซื้อ server มารองรับผู้ใช้งานมหาศาลไม่ให้ล่ม

ทีนี้พอนอกเทศกาล เครื่องมันก็ปล่อยว่างไว้ ทำยังไงดี? Amazon ก็เลยพัฒนา Amazon EC2 ขึ้นมาปล่อยเช่าเครื่อง

ในความเป็นจริงแล้วเรื่องข้างบนขี้โม้ทั้งเพ เพราะ Werner Vogels CTO ของ AWS ก็บอกว่าถ้าเอาเครื่องเหลือมาขายน่ะ 2 เดือนก็ได้ลูกค้ามาจนเครื่องไม่เหลือแล้ว และบริการตัวแรกของ AWS คือ S3 ไม่ใช่ EC2

ไม่ว่าจะจริงหรือไม่จริงแต่เรื่องมีคนเข้าเว็บเยอะบางเวลาเป็นเรื่องจริงที่ทุกคนมีปัญหา ไม่ใช่แค่ Amazon

ระบบลงทะเบียนมหาวิทยาลัยจะมีคนเข้าเยอะมากในช่วงเปิดลงทะเบียน กับเกรดออก

ธนาคารจะมีคนเข้าเยอะในช่วงปลายเดือนไปจนถึงต้นเดือนที่เงินเดือนออก

Startup จะมีคนเข้าเยอะในช่วงเปิดตัวผลิตภัณฑ์ใหม่

กรมสรรพากรจะมีคนเข้าเยอะในช่วงฤดูยื่นภาษี

การที่ระบบล่มส่งผลต่อการดำเนินธุรกิจของหน่วยงานเหล่านี้โดยตรง แล้วจะให้เค้าทำอย่างไร?

ถ้าสุดท้ายแล้วทุกคนซื้อ server มาเพื่อรองรับ spike load เหล่านี้คงจะสิ้นเปลืองมาก และไม่คุ้มทุนที่จะทำ

Cloud อนุญาตให้เราเช่าเครื่องได้เพียงยิง API เข้าไปสั่งเปิดเครื่อง แล้วรอ 2 นาทีให้เครื่องเปิดเสร็จ

Keyword สำคัญคือเช่า เราไม่ต้องมีเงินก้อนที่จะจัดซื้อเครื่องเซิร์ฟเวอร์ราคาแพงอีกต่อไปเพียงแค่จ่ายเงินค่าเช่าเท่านั้นก็สามารถมีเซิร์ฟเวอร์นับร้อยเครื่องได้

ยิ่งไปกว่านั้น สัญญาการเช่าปัจจุบันอยู่ที่ 1 วินาที (ขั้นต่ำ 10) นาที นั่นแปลว่าเซิร์ฟเวอร์นับร้อยเครื่องที่เปิดมารองรับผู้ใช้งานนั้น พอไม่ได้ใช้แล้วจะปิดเมื่อไรก็ได้ แล้วจะไม่เสียค่าอะไรอีกเลย

ไม่ว่าคิดบัญชียังไง Cloud ก็ดูจะคุ้มค่ากว่าเห็นๆ ใช้เท่าไหน จ่ายเท่านั้น เหมือนค่าไฟฟ้าที่ไม่เปิดไฟก็ไม่เสีย

แต่ราคาของ Cloud ไม่ถูก ราคาค่าเช่ามันแพงกว่าค่าเช่าซื้อเครื่องเซิร์ฟเวอร์ไว้เองด้วยซ้ำ แล้วทำไมมันถึงยังน่าสนใจ?

Scale

สิ่งที่ Cloud computing ต้องการเสนอขายไม่ใช่การให้เช่าระบบคอมพิวเตอร์ แต่เค้ากำลังจะขาย “ความสามารถที่จะ scale”

นอกเหนือไปจากการที่เราจะสามารถขยับจาก 0-100 server ได้ในเวลาไม่กี่นาทีแล้วนั้น ผู้ให้บริการ Cloud ยังมีบริการอื่นๆ อีกที่พร้อมจะโตไปกับโปรแกรมของเรา

ถ้าต้องการฐานข้อมูล AWS มีฐานข้อมูลระดับเดียวกับที่ Amazon ใช้ภายในให้บริการ หรือจะเป็น MySQL มาตรฐานก็ใช้ได้

ถ้าต้องการ Message queue AWS ก็มี API เปิดให้ใช้โดยไม่ต้องซื้อซอฟต์แวร์ Message queue ราคาแพง

ถ้าต้องการเก็บข้อมูล AWS มีระบบเก็บข้อมูลไว้ใช้ที่ขนาดแทบไม่จำกัด สามารถเปิดอ่านพร้อมกันได้ 5,500 ครั้งต่อวินาทีต่อโฟล์เดอร์

บริการเหล่านี้ Cloud มีให้เนื่องจากเป็นระบบที่ Application ส่วนมากจำเป็นต้องใช้ แล้วเราจะพัฒนาขึ้นเองทำไมในเมื่อ Cloud ทำให้แล้ว ในราคาที่ถูกกว่า รองรับผู้ใช้งานได้มหาศาล และไม่ต้องนั่งเฝ้าเซิร์ฟเวอร์เอง

Migrating to Cloud

นั่นคือเหตุผลที่ทุกคนกำลังย้ายไป Cloud ในขณะนี้ ถ้าบริษัทไม่ได้ใหญ่มากจนสามารถทำระบบแบบ Cloud ได้ในราคาที่คุ้มทุน และไม่ติดข้อกำหนดอะไรที่ต้องเอาข้อมูลไว้ On Premises การใช้ Cloud ทำให้ระบบมีเสถียรภาพได้ในราคาที่คุ้มค่า

ทีนี้จาก Diagram ของ SCB ซึ่งไม่มีบอกว่าระบบไหนอยู่ที่ไหน ผมเดาว่าสิ่งที่เกิดขึ้นคือ ด้านหน้าของ Experience API โดยปกติแล้วจะมีระบบ Load balancer ที่กระจาย load ให้กับ server ด้านหลังอีกที (Load balancer ทำแค่กระจายโหลดจึงใช้เครื่องน้อยกว่าเครื่องที่ประมวลผล)

ทีนี้ระบบบางส่วนน่าจะถูกย้ายขึ้นไปบน Cloud (ซึ่งเราไม่อาจจะเดาได้ว่ามีอะไรบ้าง) แต่ที่เห็นจากภายนอกคือ Load balancer ถูกย้ายไปด้วย

แต่ไม่ใช่ทั้งแอพที่ถูกย้ายไป ระบบที่อยู่บน Cloud ยังต้องเรียกข้อมูลจากระบบเดิมอยู่ ซึ่งการเชื่อมต่อระหว่าง Cloud กลับมาระบบ On Premises ก็มีอยู่สามวิธีคือ

  1. เรียกผ่านอินเทอร์เน็ตปกติ ข้อเสียคือไม่สามารถเรียกเครื่องที่ไม่มี IP จริงได้
  2. ติดตั้ง VPN แล้วเชื่อมทั้งสองระบบเข้าด้วยกัน
  3. ลากสายจาก Cloud มายังสถานที่เรา ซึ่งแน่นอนว่าแพงมาก

นอกเหนือจาก 1 แล้ว ปัญหาที่เกิดขึ้นได้คือการเชื่อมต่อจะเกิดคอขวดขึ้น เนื่องจากถ้าใช้ VPN การเชื่อมต่อทั้งหมดจะต้องวิ่งผ่าน VPN server ที่ต้องเข้ารหัสข้อมูลก่อนส่งไปปลายทาง หรือสายที่เชื่อมต่อไปยัง Cloud ก็อาจจะมีความเร็วจำกัด

ที่ยังเดาอะไรไม่ได้คือถ้าหากปัญหาอยู่ที่ตรงนี้จริงก็ควรจะสามารถตัดสินใจ Rollback ได้ไม่ยากเพียงแค่สลับไปใช้ Load balancer ชุดเดิม ก็เป็นไปได้ว่า Application อาจจะถูกย้ายไปแล้วทำให้สลับกลับไม่ได้ (เพราะต้องย้ายข้อมูลกลับมาด้วย)

#HugOps

ในคอนเสิร์ท ถ้า Sound Engineer ทำงานออกมาดีจะไม่มีใครรู้เลยว่ามีเค้าอยู่ แต่ถ้าไมค์หอนเมื่อไรทุกคนจะด่าทันที

ในช่วงเวลาที่แอพล่มแบบนี้ก็เช่นกัน เราอาจจะอารมณ์เสียที่เข้าแอพแล้วใช้งานไม่ได้

แต่ Operation ที่กำลังกู้ระบบ โปรแกรมเมอร์ที่นั่งจ้องหาบั๊ก Support ที่รับสายจนคอแห้ง ก็เป็นมนุษย์เหมือนกับเรา

โปรดใจดีกับ Operation

Mesh VPN ด้วย CJDNS

ทำงานมามีปัญหาหนึ่งกวนใจมานาน คือเวลา work from home แล้วพบว่าลืม push code จากเครื่องที่ออฟฟิศมา หลายๆ ทีถ้าจะเอาให้ได้งานก็จะเขียนใหม่ไปเลยแล้วไป reset ที่ทำงาน ซึ่งก็ไม่ productive เท่าไร

ทีนี้พอมี home server แล้วรู้สึกว่ามันดีมากๆ ที่เราสามารถ ssh กลับบ้านได้จากที่ทำงาน โดยเฉพาะเวลาที่ IoT หรือ Network ภายในบ้านมีปัญหาเราก็จะ troubleshoot ได้ทันทีโดยไม่ต้องพยายามบอกทางโทรศัพท์ เลยคิดว่าอยากจะ ssh กลับไปเครื่องที่ออฟฟิศบ้าง

ทีนี้จะทำยังไง?

สมัยก่อนย้ายตึก บริษัทมี Fix IP และมี dev server ภายใน ก็เคยคิดว่าจะ Jump ผ่านเครื่อง dev server แต่ก็ยังไม่เคยลองทำสักที ปัญหาหนึ่งคือ IP เครื่องเราไม่ fix และเราไม่อยากบอกรหัสให้คนอื่นเข้าไปอ่าน IP ออกมา

พอหลังจากบริษัทย้ายมา WeWork แล้วไม่น่าจะใช้วิธีนี้ได้อีก เพราะบริษัทไม่ได้ถือ Public IP ที่ออฟฟิศอีกแล้ว

ก็เลยคิดว่าต้องใช้ VPN จากบนเครื่อง วิธีง่ายที่สุดคือทำ OpenVPN server แล้วให้ทุกเครื่องต่อแบบ always on แต่ไม่ค่อยชอบ OpenVPN สักเท่าไร เลยอยากลองหาอะไรแปลกๆ ดู

Mesh VPN

ไอเดียของ VPN ที่เราอยากได้คือ VPN ที่ไม่มี server กลาง

VPN ทั่วไปจะเป็นแบบในภาพนี้

ก็คือจะมี VPN server เครื่องหนึ่ง จากนั้นทุกคนต่อเข้า Server นี้ ถ้าเครื่องนี้ตายก็คือจบ เป็น Single point of failure

ถ้าการใช้งานใน scale แบบในภาพ อาจจะเป็นไปได้ว่า Site 1, 2, 3 มี Public IP และสามารถต่อหากันตรงได้อยู่แล้ว แล้วทำไมจะต้องวิ่งผ่าน Server กลางด้วย? ส่วน Road warrior อาจจะมีสาขาที่ทำงานประจำอยู่แล้ว ก็ให้ต่อผ่าน server ของสาขาที่ประจำอยู่ด้วยโดยตรงก็ได้

บน Mesh VPN สมมุติว่าเราให้ Site 1, 2, 3 เชื่อมต่อ VPN หากันเองทั้งหมด และเชื่อมต่อหา server กลางด้วย ส่วน Road warrior ให้เชื่อมต่อกับสาขาตัวเองเท่านั้น และ Server กลาง

เมื่อทำ Topology นี้แล้วเราสามารถเชื่อมต่อหากันได้หมด เช่น Road warrior ที่ต่อกับ Site 1 สามารถคุยกับ Site 3 ได้

ที่พิเศษหน่อยคือ Mesh VPN จะอนุญาตให้เรา route package ผ่านเครื่องอื่นๆ ยังไงก็ได้ เช่น สมมุติว่า Internet ที่ Site 2 หลุดแต่ Road warrior ต่อเน็ตผ่านมือถือและยังสามารถเข้าถึง Site 2 server ได้อยู่

จากภาพนี้ ถ้า Site 1 ต้องการติดต่อ Site 2 ระบบก็ยังสามารถทำงานได้ตามปกติ โดย Route packet ผ่าน VPN server > Road warrior > Site 2

CJDNS

CJDNS เป็นโปรแกรม VPN ที่สามารถทำระบบ Mesh VPN ได้

สำหรับบน Mac ติดตั้งผ่าน homebrew ได้เลย ส่วนวิธีการติดตั้งบน Docker จะยุ่งยากเล็กน้อยแต่ก็ไม่ได้ยากเกินไป

  1. Clone Dockerfile มาจาก https://github.com/chpio/docker-cjdns
  2. Build image (docker build -t cjdns .)
  3. รันโดยใช้ docker-compose.yml ไฟล์นี้
version: '2.1'
services:
  cjdns:
    image: cjdns:latest
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
    ports:
      - 10100:10100/udp
    volumes:
      - data:/data/cjdns
    devices:
      - /dev/net/tun
    sysctls:
      net.ipv6.conf.all.disable_ipv6: 0
volumes:
  data: {}

Note: Port ที่ใช้แต่ละเครื่องไม่เหมือนกัน โปรแกรมจะสุ่มให้เมื่อ Start ครั้งแรก

ที่จำเป็นต้อง build Docker เองเนื่องจากโปรแกรม compile ด้วย -march=native ซึ่งทำให้ CPU ที่มี instruction set ไม่ตรงกับเครื่องที่ build รันไม่ได้

เมื่อรันครั้งแรก โปรแกรมจะสร้าง configuration file มา ให้เราเพิ่มในส่วน interfaces.UDPInterface[0].connectTo ดังนี้

"ip:port": {
    "login": "default-login",
    "password": "",
    "publicKey": "",
    "peerName": "remote name (anything)"
}

โดยตรง ip:port ให้ระบุ Public IP ของเครื่องปลายทาง ส่วน port ให้ระบุตาม section interfaces.UDPInterface[0].bind ของเครื่องปลายทาง

ช่อง password ให้ระบุตาม authorizedPasswords[0].password ของเครื่องปลายทาง

ช่อง publicKey ให้ระบุ publicKey ของเครื่องปลายทาง ซึ่งจะปรากฏใน field publicKey บน config file ปลายทาง

เมื่อตั้งค่าเรียบร้อยแล้วให้ restart cjdns อีกครั้งหนึ่งแล้วทดลอง ping เครื่องปลายทางดู โดยจะทราบ IP ได้จากช่อง ipv6

$ ping -6 fc00:1234:5678:9012:3456:7890:1234:5678
PING fc00:1234:5678:9012:3456:7890:1234:5678 56 data bytes
64 bytes from fc00:1234:5678:9012:3456:7890:1234:5678: icmp_seq=1 ttl=42 time=4.44 ms
64 bytes from fc00:1234:5678:9012:3456:7890:1234:5678: icmp_seq=2 ttl=42 time=4.26 ms
64 bytes from fc00:1234:5678:9012:3456:7890:1234:5678: icmp_seq=3 ttl=42 time=3.96 ms
64 bytes from fc00:1234:5678:9012:3456:7890:1234:5678: icmp_seq=4 ttl=42 time=3.95 ms
64 bytes from fc00:1234:5678:9012:3456:7890:1234:5678: icmp_seq=5 ttl=42 time=4.38 ms
64 bytes from fc00:1234:5678:9012:3456:7890:1234:5678: icmp_seq=6 ttl=42 time=5.35 ms
^C
--- fc00:1234:5678:9012:3456:7890:1234:5678 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 13ms
rtt min/avg/max/mdev = 3.947/4.387/5.348/0.475 ms

ข้อน่าสังเกตคือ CJDNS ใช้ IPv6 เท่านั้น แถมเป็นเลขยาวๆ ที่ตั้งเองไม่ได้ด้วย

ที่เป็นแบบนี้เพราะ IPv6 ที่เห็นนั้นคือ SHA512 ของ Public Key ของเราแล้วตัดเหลือ 16 bytes แรก ทำให้เวลาให้ IP ใครไปเหมือนให้ key fingerprint ไปในตัว

ทั้งนี้ Public Key ที่ Valid สำหรับการใช้งานใน CJDNS จะขึ้นด้วย fc เสมอ (โปรแกรมจะ brute force ออกมาให้เวลาเปิดครั้งแรก) เนื่องจากบล็อค fc00::/7 เป็นช่วง Unique local address

เพื่อให้ใช้งานง่าย เราอาจจะเอา IP นี้ไปไว้ใน DNS (ใช้ Public DNS ทั่วๆ ไปก็ได้ที่รองรับ IPv6) จะได้ไม่ต้องจำ และคนข้างนอกที่ไม่ได้เชื่อมต่อเครือข่ายเรา ถึงจะทราบ IP แต่ก็ต่อไม่ได้อยู่ดี