Skip to content

Consul

consul เป็นโซลูชันที่ช่วยให้ทีมสามารถจัดการการเชื่อมต่อเครือข่ายระหว่างบริการและข้ามการเตรียมการและสภาพแวดล้อมคลาวด์หลายแห่งอย่างปลอดภัย ให้บริการค้นหาบริการ service mesh การจัดการทราฟฟิก การอัปเดตโครงสร้างพื้นฐานเครือข่ายอัตโนมัติ และฟังก์ชันอื่นๆ

เอกสารทางการ: Consul by HashiCorp

ที่อยู่โอเพนซอร์ส: hashicorp/consul

Consul เป็นเครื่องมือค้นหาและลงทะเบียนบริการโอเพนซอร์สจากบริษัท HashiCorp ใช้อัลกอริทึมการเลือกตั้ง Raft เครื่องมือนี้พัฒนาด้วยภาษา Go ดังนั้นการติดตั้งจึงสะดวกมาก Consul มีคุณสมบัติดังนี้

  • การค้นหาบริการ
  • การลงทะเบียนบริการ
  • การตรวจสอบสุขภาพ
  • การจัดเก็บคีย์-ค่า
  • หลายศูนย์ข้อมูล

ที่จริงแล้ว consul สามารถทำได้มากกว่าการค้นหาบริการ ยังสามารถใช้เป็นศูนย์การกำหนดค่าแบบกระจายได้ มีเครื่องมือโอเพนซอร์สประเภทเดียวกันอีกมากมาย เช่น zookeeper, nacos ที่นี่จะไม่แนะนำเพิ่มเติม

การติดตั้ง

สำหรับ Ubuntu ให้รันคำสั่งด้านล่างเพื่อติดตั้งด้วย apt

sh
$ wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
$ echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
$ sudo apt update && sudo apt install consul

หรือสามารถดาวน์โหลดแพ็กเกจการติดตั้งที่สอดคล้องกันจากเว็บไซต์ทางการ Install Consul เนื่องจาก consul พัฒนาด้วย go แพ็กเกจการติดตั้งเองก็มีเพียงไฟล์ไบนารีที่รันได้เพียงไฟล์เดียว การติดตั้งก็สะดวกมาก หลังจากติดตั้งสำเร็จ รันคำสั่งด้านล่างเพื่อดูเวอร์ชัน

sh
$ consul version

เอาต์พุตปกติก็ไม่มีปัญหา

Consul v1.16.1
Revision e0ab4d29
Build Date 2023-08-05T21:56:29Z
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)

เริ่มต้นอย่างรวดเร็ว

ต่อไปนี้จะแนะนำวิธีการสร้างโหนดเดียว consul อย่างรวดเร็ว โดยทั่วไปโหนดเดียวใช้สำหรับการทดสอบในช่วงการพัฒนา หากโหนดเดียวใช้งานได้没有问题 ส่วนใหญ่คลัสเตอร์หลายโหนดก็จะไม่มีปัญหาเช่นกัน การสร้างโหนดเดี่ยวนั้นง่ายมาก ใช้เพียงคำสั่งเดียว

sh
$ consul agent -dev -bind=192.168.48.141 -data-dir=/tmp/consul -ui -node=dev01

โดยทั่วไปจะมีเอาต์พุตดังนี้

==> Starting Consul agent...
               Version: '1.16.1'
            Build Date: '2023-08-05 21:56:29 +0000 UTC'
               Node ID: 'be6f6b8d-9668-f7ff-8709-ed57c72ffdec'
             Node name: 'dev01'
            Datacenter: 'dc1' (Segment: '<all>')
                Server: true (Bootstrap: false)
           Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, gRPC-TLS: 8503, DNS: 8600)
          Cluster Addr: 192.168.48.141 (LAN: 8301, WAN: 8302)
     Gossip Encryption: false
      Auto-Encrypt-TLS: false
           ACL Enabled: false
     Reporting Enabled: false
    ACL Default Policy: allow
             HTTPS TLS: Verify Incoming: false, Verify Outgoing: false, Min Version: TLSv1_2
              gRPC TLS: Verify Incoming: false, Min Version: TLSv1_2
      Internal RPC TLS: Verify Incoming: false, Verify Outgoing: false (Verify Hostname: false), Min Version: TLSv1_2
==> Log data will now stream in as it occurs:
2023-08-25T17:23:33.763+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.be6f6b8d-9668-f7ff-8709-ed57c72ffdec/server.dc1 from=<none> to=<none>
2023-08-25T17:23:33.767+0800 [INFO]  agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:be6f6b8d-9668-f7ff-8709-ed57c72ffdec Address:192.168.48.141:8300}]"

อธิบายความหมายอย่างง่าย

  • agent เป็นคำสั่งย่อย เป็นคำสั่งหลักของ consul consul agent คือการรัน proxy consul ใหม่ แต่ละ node เป็น proxy หนึ่งตัว

  • dev เป็นโหมดการทำงานของ agent มีสามแบบ dev, client, server

  • bind ที่อยู่การสื่อสาร LAN พอร์ตเริ่มต้น 8301 โดยทั่วไปค่านี้เป็นที่อยู่ภายในของเซิร์ฟเวอร์

  • advertise ที่อยู่การสื่อสาร WAN พอร์ตเริ่มต้น 8302 โดยทั่วไปค่านี้เป็นที่อยู่นอกของเซิร์ฟเวอร์

  • data-dir ไดเรกทอรีเก็บข้อมูล

  • config-dir ไดเรกทอรีเก็บการกำหนดค่า consul จะอ่านไฟล์ json ทั้งหมดในไดเรกทอรี

  • bootstrap ระบุว่า server ปัจจุบันเข้าสู่โหมด bootstrap ในการเลือกตั้ง raft จะโหวตให้ตัวเอง ในคลัสเตอร์ server ที่อยู่ในโหมดนี้ต้องมีไม่เกินหนึ่งตัว

  • bootstrap-expect จำนวน server ที่คาดหวังในคลัสเตอร์ ก่อนถึงจำนวนที่กำหนด คลัสเตอร์จะไม่เริ่มการเลือกตั้ง投票 ไม่สามารถใช้กับ bootstrap พร้อมกันได้

  • retry-join หลังจาก agent เริ่มต้น จะพยายามเข้าร่วมโหนดที่ระบุอย่างต่อเนื่อง还支持วิธีการค้นหาบริการของผู้ให้บริการต่อไปนี้

    aliyun aws azure digitalocean gce hcp k8s linode mdns os packet scaleway softlayer tencentcloud triton vsphere
  • ui รันเว็บแบ็กเอนด์

  • node ชื่อโหนดที่ทำงาน ต้องไม่ซ้ำกันในคลัสเตอร์

TIP

สำหรับความหมายพารามิเตอร์ agent เพิ่มเติม ไปที่ Agents - CLI Reference | Consul | HashiCorp Developer หมายเหตุพารามิเตอร์บางตัวใช้ได้เฉพาะรุ่นองค์กร

เมื่อรันสำเร็จแล้ว เข้าถึง 127.0.0.1:8500 ก็สามารถดูเว็บอินเทอร์เฟซได้

ไอคอน dev01 เป็นรูปดาว แสดงว่าเป็นโหนด leader

เมื่อออก เพื่อให้โหนดอื่นรับรู้ถึงการออกของโหนดปัจจุบัน ไม่แนะนำให้บังคับฆ่าโปรเซส สามารถใช้คำสั่ง

sh
consul leave

หรือ

sh
consul force-leave

หรือ ctrl+c ให้ consul agent ออกอย่างสง่างาม

แนวคิด

นี่เป็นแผนภาพคลัสเตอร์ consul ในภาพแบ่งออกเป็นสองส่วน คือ control plane และ data plane consul รับผิดชอบเฉพาะ control plane แบ่งเป็น service cluster และ client ใน service cluster แบ่งเป็น follower และ leader โดยรวมแล้ว คลัสเตอร์ consul ในภาพ構成เป็น data center หนึ่งแห่ง ต่อไปนี้จะอธิบายศัพท์บางคำ

  • Agent (ตัวแทน): หรือเรียกว่า node ก็เหมาะสมกว่า แต่ละ agent เป็น daemon ที่รันเป็นเวลานาน พวกมันเปิดเผยอินเทอร์เฟซ HTTP และ DNS ภายนอก รับผิดชอบการตรวจสอบสุขภาพและการซิงค์บริการ
  • Server (ตัวแทนบริการ): ในฐานะ consul server หน้าที่หลักมี participate ในการเลือกตั้ง Raft บำรุงรักษาสถานะคลัสเตอร์ ตอบคำถาม แลกเปลี่ยนข้อมูลกับศูนย์ข้อมูลอื่น และส่งต่อคำถามไปยัง leader และศูนย์ข้อมูลอื่น
  • Client (ตัวแทนลูกค้า): client เทียบกับ server แล้วไม่มีสถานะ ไม่ participate ในการเลือกตั้ง Raft สิ่งที่ทำคือส่งต่อคำขอทั้งหมดไปยัง server สิ่งเดียวที่เกี่ยวข้องกับพื้นหลังคือ LAN gossip pool
  • Leader (ผู้นำ): leader เป็นผู้นำของ server ทั้งหมด และมีได้เพียงคนเดียว leader ได้รับการเลือกตั้งผ่านอัลกอริทึมการเลือกตั้ง Raft แต่ละ leader มีวาระของตัวเอง ในระหว่างวาระ server อื่น ๆ ไม่ว่าจะได้รับคำขออะไรก็ต้องบอก leader ดังนั้นข้อมูลของ leader จึงทันสมัยที่สุด
  • Gossip (ข่าวลือ): Consul สร้างบนพื้นฐานของ Serf (ผลิตภัณฑ์อื่นของบริษัทเดียวกัน) ใช้โปรโตคอล gossip โปรโตคอลนี้ใช้สำหรับการสื่อสารแบบสุ่มระหว่างโหนด คล้าย UDP consul ใช้โปรโตคอลนี้เพื่อแจ้งเตือนซึ่งกันและกันในคลัสเตอร์บริการ
  • Data Center (ศูนย์ข้อมูล): คลัสเตอร์ consul ภายใน LAN หนึ่งแห่งเรียกว่า data center หนึ่งแห่ง consul รองรับหลายศูนย์ข้อมูล วิธีการสื่อสารหลายศูนย์ข้อมูลคือ WAN gossip

TIP

คำศัพท์และ术语เพิ่มเติมสามารถไปที่ Glossary | Consul | HashiCorp Developer เพื่อทำความเข้าใจ

ในคลัสเตอร์ consul จำนวน server ควรควบคุมอย่างเข้มงวด เพราะพวกมัน participate โดยตรงใน LAN gossip และ WAN gossip การเลือกตั้ง raft และต้องเก็บข้อมูล server มากขึ้น ต้นทุนการสื่อสารสูงขึ้น ส่วนจำนวน client มากหน่อยไม่มีปัญหา รับผิดชอบแค่ส่งต่อ ไม่ participate ในการเลือกตั้ง ใช้ทรัพยากรต่ำ ในคลัสเตอร์ในภาพ บริการต่างๆ ลงทะเบียนตัวเองกับ server ผ่าน client หากมี server ล้ม client จะค้นหา server อื่นที่ใช้ได้เอง

ตัวอย่างการสร้างคลัสเตอร์

ต่อไปนี้จะสร้างตัวอย่างคลัสเตอร์หลายโหนด consul อย่างง่าย เตรียมเครื่องเสมือนสี่เครื่องก่อน

ในเครื่องเสมือนสี่เครื่อง มีสาม server หนึ่ง client ทางการแนะนำว่าจำนวน server ควรเป็นจำนวนคี่ และควรมากกว่าหรือเท่ากับสามเครื่อง ที่นี่จะใช้ vm00-vm02 เป็น server vm03 เป็น client

สำหรับ server รันคำสั่งด้านล่างเพื่อสร้าง server agent

sh
consul agent -server -bind=vm_address -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent_name -ui

สำหรับ client รันคำสั่งด้านล่างเพื่อสร้าง client agent

sh
consul agent -client=0.0.0.0  -bind=vm_address -data-dir=/tmp/consul/ -node=agent_name -ui

คำสั่งที่รัน分别是

sh
# vm00
consul agent -server -bind=192.168.48.138 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent01 -ui -bootstrap

# vm01
consul agent -server -bind=192.168.48.139 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent02 -ui -retry-join=192.168.48.138

# vm02
consul agent -server -bind=192.168.48.140 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent03 -ui -retry-join=192.168.48.138

# vm03
consul agent -bind=192.168.48.140 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent03 -ui -retry-join=192.168.48.138

คำอธิบายพารามิเตอร์บางตัว

  • client 0.0.0.0 คืออนุญาตคำขอจากทุก来源 หากมีพารามิเตอร์ client เท่านั้น ไม่มีพารามิเตอร์ server หมายความว่า agent จะรันในโหมด client

เมื่อ agent ทั้งหมดรันดีแล้ว retry-join มีผลเท่ากับการรันคำสั่ง join อัตโนมัติ หลังจากล้มเหลวจะพยายามอย่างต่อเนื่อง เวลา重试เริ่มต้น 30 วินาที

sh
$ consul join 192.168.48.138

หลังจาก join เสร็จ แต่ละโหนดรู้จักกันแล้ว เนื่องจาก vm00 ระบุโหมด bootstrap ดังนั้นมันจึงเป็น leader เริ่มต้น หากไม่ได้ระบุโหมด bootstrap โหนดทั้งหมดที่ join จะระบุโหนดเป็น leader เริ่มต้น ก่อนที่ leader จะได้รับการเลือกตั้ง คลัสเตอร์ไม่สามารถทำงานได้ตามปกติ การเข้าถึงเว็บอินเทอร์เฟซจะคืนค่า 500 คำสั่งบางอย่างไม่สามารถทำงานได้ตามปกติ หากมีโหนดในคลัสเตอร์ระบุโหมด bootstrap แล้ว โหนดอื่นในคลัสเตอร์ไม่ควรระบุโหมด bootstrap อีก และโหนดอื่นไม่ควรใช้พารามิเตอร์ bootstrap-expect อีก หากใช้จะถูกปิดใช้งานอัตโนมัติ

这时ในโหนด leader (ที่จริงแล้วตอนนี้โหนดใดก็สามารถดูได้) รันคำสั่งด้านล่างเพื่อดูข้อมูลสมาชิก data center

sh
$ consul members
Node      Address              Status  Type    Build   Protocol  DC   Partition  Segment
agent01   192.168.48.138:8301  alive   server  1.16.1  2         dc1  default    <all>
agent02   192.168.48.139:8301  alive   server  1.16.1  2         dc1  default    <all>
agent03   192.168.48.140:8301  alive   server  1.16.1  2         dc1  default    <all>
client01  192.168.48.141:8301  alive   client  1.16.1  2         dc1  default    <default>
  • Node คือชื่อโหนด
  • Address คือที่อยู่การสื่อสาร
  • Status alive หมายถึงมีชีวิตอยู่ left หมายถึงออฟไลน์
  • Type ประเภท agent มีสองโหมด server และ client
  • Build เวอร์ชัน consul ที่โหนดนี้ใช้ consul สามารถทำงานร่วมกับโหนดเวอร์ชันต่าง ๆ ในช่วงหนึ่ง
  • Protocol หมายถึงเวอร์ชันโปรโตคอล Raft ที่ใช้ โปรโตคอลนี้ควร一致ทุกโหนด
  • DC Data Center ศูนย์ข้อมูล โหนดทั้งหมดในเอาต์พุตอยู่ใน data center dc1
  • Partition partition ที่โหนดสังกัด เป็นฟังก์ชันรุ่นองค์กร แต่ละโหนดสามารถสื่อสารกับโหนดใน partition เดียวกันเท่านั้น
  • Segment segment ที่โหนดสังกัด เป็นฟังก์ชันรุ่นองค์กร

เช่นเดียวกัน หากต้องการให้โหนดออก ควรใช้ consul leave ให้โหนดออกอย่างสง่างาม และแจ้งโหนดอื่นว่ากำลังจะออก สำหรับกรณีหลายโหนด การออกอย่างสง่างามของโหนดสำคัญมาก เพราะเกี่ยวข้องกับความสอดคล้องของข้อมูล

TIP

เครื่องเสมือนในการสาธิตปิด firewall ทั้งหมด ในสภาพแวดล้อมการผลิตจริงเพื่อความปลอดภัยควรเปิด为此ควร关注พอร์ตทั้งหมดที่ consul ใช้: Required Ports | Consul | HashiCorp Developer

ต่อไปทดสอบความสอดคล้องของข้อมูลอย่างง่าย ในเครื่องเสมือน vm00 เพิ่มข้อมูลด้านล่าง

sh
$ consul kv put sys_confg {"name":"consul"}
Success! Data written to: sys_confg

หลังจากบันทึก ผ่าน HTTP API เข้าถึงโหนดอื่นจะพบว่าข้อมูลมีอยู่เช่นกัน (value ในนั้นเป็น base64 encoding)

sh
$ curl http://192.168.48.138:8500/v1/kv/sys_confg
[{"LockIndex":0,"Key":"sys_confg","Flags":0,"Value":"ewogICJuYW1lIjoiY29uc3VsIgp9","CreateIndex":2518,"ModifyIndex":2518}]
$ curl http://192.168.48.139:8500/v1/kv/sys_confg
[{"LockIndex":0,"Key":"sys_confg","Flags":0,"Value":"ewogICJuYW1lIjoiY29uc3VsIgp9","CreateIndex":2518,"ModifyIndex":2518}]
$ curl http://192.168.48.140:8500/v1/kv/sys_confg
[{"LockIndex":0,"Key":"sys_confg","Flags":0,"Value":"ewogICJuYW1lIjoiY29uc3VsIgp9","CreateIndex":2518,"ModifyIndex":2518}]

ที่จริงแล้ว ฟังก์ชันการค้นหาและลงทะเบียนบริการที่ consul ให้ ผ่านโปรโตคอล gossip แจ้งเตือนโหนดอื่น และเมื่อโหนดใดเข้าร่วม data center ปัจจุบัน โหนดทั้งหมดจะรับรู้การเปลี่ยนแปลงนี้

ตัวอย่างการสร้างหลายศูนย์ข้อมูล

เตรียมเครื่องเสมือนห้าเครื่อง vm00-vm02 เป็นคลัสเตอร์ในตัวอย่างก่อนหน้า สังกัด data center dc1 ไม่ต้องยุ่งกับมัน vm03-vm04 สังกัด data center dc2 data center เมื่อ agent เริ่มต้น ค่าเริ่มต้นคือ dc1

TIP

ที่นี่เพื่อสาธิต สร้างเฉพาะ server ตัด client ออก

ก่อนอื่นเริ่มต้น vm03分别将其เป็น leader เริ่มต้น

sh
$ consul agent -server -datacenter=dc2 -bind=192.168.48.141 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent04 -ui -bootstrap

เริ่มต้น vm04 ให้มัน join โหนด vm03 อัตโนมัติ

sh
$ consul agent -server -datacenter=dc2 -bind=192.168.48.142 -client=0.0.0.0 -data-dir=/tmp/consul/ -node=agent05 -ui -retry-join=192.168.48.141

这时分别在 vm00 และ vm03 ดู members

sh
# vm00-vm02
$ consul members
Node      Address              Status  Type    Build   Protocol  DC   Partition  Segment
agent01   192.168.48.138:8301  alive   server  1.16.1  2         dc1  default    <all>
agent02   192.168.48.139:8301  alive   server  1.16.1  2         dc1  default    <all>
agent03   192.168.48.140:8301  alive   server  1.16.1  2         dc1  default    <all>

# vm03-vm04
$ consul members
Node     Address              Status  Type    Build   Protocol  DC   Partition  Segment
agent04  192.168.48.141:8301  alive   server  1.16.1  2         dc2  default    <all>
agent05  192.168.48.142:8301  alive   server  1.16.1  2         dc2  default    <all>

จะเห็นว่าฟิลด์ DC ต่างกัน เพราะที่นี่เป็นเครื่องเสมือนสาธิต ดังนั้นอยู่ใน subnet เดียวกัน ในความเป็นจริงสองศูนย์ข้อมูลอาจเป็นคลัสเตอร์เซิร์ฟเวอร์คนละที่ ต่อไปนี้ให้โหนดใดก็ได้ของ dc1 join โหนดใดก็ได้ของ dc2 ที่นี่ให้ vm01 join vm03

sh
$ consul join -wan 192.168.48.141
Successfully joined cluster by contacting 1 nodes.

หลังจาก join สำเร็จ รันคำสั่งดู WAN members

sh
$ consul members -wan
Node         Address              Status  Type    Build   Protocol  DC   Partition  Segment
agent01.dc1  192.168.48.138:8302  alive   server  1.16.1  2         dc1  default    <all>
agent02.dc1  192.168.48.139:8302  alive   server  1.16.1  2         dc1  default    <all>
agent03.dc1  192.168.48.140:8302  alive   server  1.16.1  2         dc1  default    <all>
agent04.dc2  192.168.48.141:8302  alive   server  1.16.1  2         dc2  default    <all>
agent05.dc2  192.168.48.142:8302  alive   server  1.16.1  2         dc2  default    <all>

$ consul catalog datacenters
dc2
dc1

เพียงโหนดใดก็ได้ของ dc1 join โหนดใดก็ได้ของ dc2 โหนดทั้งหมดของสองศูนย์ข้อมูลจะรับรู้การเปลี่ยนแปลงนี้ เมื่อดู members ก็เห็นโหนดของสองศูนย์ข้อมูล

ต่อไปพยายามเพิ่มข้อมูล KVหนึ่งชิ้นในโหนด vm00

sh
$ consul kv put name consul
Success! Data written to: name

ในโหนด vm01 พยายามอ่านข้อมูล จะเห็นว่าข้อมูลในศูนย์ข้อมูลเดียวกันซิงค์กัน

sh
$ consul kv get name
consul

แล้วไปที่ vm03 ในศูนย์ข้อมูลต่างกันพยายามอ่านข้อมูล จะพบว่าข้อมูลในศูนย์ข้อมูลต่างกันไม่ซิงค์กัน

sh
$ consul kv get name
Error! No key exists at: name

หากต้องการให้ข้อมูลหลายศูนย์ข้อมูลซิงค์กัน สามารถเรียนรู้ hashicorp/consul-replicate: Consul cross-DC KV replication daemon

การลงทะเบียนและการค้นพบบริการ

วิธีการลงทะเบียนบริการ consul มีสองแบบ การลงทะเบียนด้วยไฟล์การกำหนดค่าและการลงทะเบียนด้วย API เพื่อความสะดวกในการทดสอบ ที่นี่เตรียมบริการ Hello World ล่วงหน้า (ตัวอย่างในบทความ gRPC)部署สองชุด分别อยู่ในที่ต่างกัน สำหรับวิธีการลงทะเบียนด้วยไฟล์การกำหนดค่าสามารถไปที่ Register external services with Consul service discovery | Consul | HashiCorp Developer เพื่อเรียนรู้ ที่นี่แนะนำเฉพาะการลงทะเบียนผ่าน HTTP API

TIP

สำหรับบริการท้องถิ่น (อยู่กับ consul client) สามารถใช้การลงทะเบียน agent service ได้โดยตรง มิฉะนั้นควรใช้ catalog register เพื่อลงทะเบียน

consul ให้ SDK HTTP API สำหรับภาษาอื่น ๆ可以去 Libraries and SDKs - HTTP API | Consul | HashiCorp Developer เพื่อเรียนรู้ ที่นี่ดาวน์โหลด dependency ของ go

sh
go get github.com/hashicorp/consul/api

เมื่อเริ่มต้นบริการให้ลงทะเบียนบริการกับ consul อย่างกระตือรือร้น เมื่อปิดบริการให้ยกเลิกการลงทะเบียนกับ consul ต่อไปนี้เป็นตัวอย่าง

go
package main

import (
  consulapi "github.com/hashicorp/consul/api"
  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials/insecure"
  pb "grpc_learn/helloworld/hello"
  "log"
  "net"
)

var (
  server01 = &consulapi.AgentService{
        // ต้องไม่ซ้ำกัน
    ID:      "hello-service1",
    Service: "hello-service",
        //部署สองชุด พอร์ตหนึ่งคือ 8080 อีกพอร์ตคือ 8081
    Port:    8080,
  }
)

// ลงทะเบียนบริการ
func Register() {
  client, _ := consulapi.NewClient(&consulapi.Config{Address: "192.168.48.138:8500"})
  _, _ = client.Catalog().Register(&consulapi.CatalogRegistration{
    Node:    "hello-server",
    Address: "192.168.2.10",
    Service: server01,
  }, nil)
}

// ยกเลิกการลงทะเบียนบริการ
func DeRegister() {
  client, _ := consulapi.NewClient(&consulapi.Config{Address: "192.168.48.138:8500"})
  _, _ = client.Catalog().Deregister(&consulapi.CatalogDeregistration{
    Node:      "hello-server",
    Address:   "192.168.2.10",
    ServiceID: server01.ID,
  }, nil)
}

func main() {
  Register()
  defer DeRegister()

  // ฟังพอร์ต
  listen, err := net.Listen("tcp", ":8080")
  if err != nil {
    panic(err)
  }
  // สร้างเซิร์ฟเวอร์ gprc
  server := grpc.NewServer(
    grpc.Creds(insecure.NewCredentials()),
  )
  // ลงทะเบียนบริการ
  pb.RegisterSayHelloServer(server, &HelloRpc{})
  log.Println("server running...")
  // รัน
  err = server.Serve(listen)
  if err != nil {
    panic(err)
  }
}

โค้ดไคลเอนต์ใช้ custom resolver ของ consul เพื่อสอบถามบริการที่สอดคล้องกันจากศูนย์ลงทะเบียน แปลงเป็นที่อยู่จริง

go
package myresolver

import (
    "fmt"
    consulapi "github.com/hashicorp/consul/api"
    "google.golang.org/grpc/resolver"
)

func NewConsulResolverBuilder(address string) ConsulResolverBuilder {
    return ConsulResolverBuilder{consulAddress: address}
}

type ConsulResolverBuilder struct {
    consulAddress string
}

func (c ConsulResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
    consulResolver, err := newConsulResolver(c.consulAddress, target, cc)
    if err != nil {
       return nil, err
    }
    consulResolver.resolve()
    return consulResolver, nil
}

func (c ConsulResolverBuilder) Scheme() string {
    return "consul"
}

func newConsulResolver(address string, target resolver.Target, cc resolver.ClientConn) (ConsulResolver, error) {
    var reso ConsulResolver
    client, err := consulapi.NewClient(&consulapi.Config{Address: address})
    if err != nil {
       return reso, err
    }
    return ConsulResolver{
       target: target,
       cc:     cc,
       client: client,
    }, nil
}

type ConsulResolver struct {
    target resolver.Target
    cc     resolver.ClientConn
    client *consulapi.Client
}

func (c ConsulResolver) resolve() {
    service := c.target.URL.Opaque
    services, _, err := c.client.Catalog().Service(service, "", nil)
    if err != nil {
       c.cc.ReportError(err)
       return
    }
    var adds []resolver.Address
    for _, catalogService := range services {
       adds = append(adds, resolver.Address{Addr: fmt.Sprintf(fmt.Sprintf("%s:%d", catalogService.Address, catalogService.ServicePort))})
    }

    c.cc.UpdateState(resolver.State{
       Addresses: adds,
       // กลยุทธ์轮询
       ServiceConfig: c.cc.ParseServiceConfig(
          `{"loadBalancingPolicy":"round_robin"}`),
    })
}

func (c ConsulResolver) ResolveNow(options resolver.ResolveNowOptions) {
    c.resolve()
}

func (c ConsulResolver) Close() {

}

ไคลเอนต์ลงทะเบียน resolver เมื่อเริ่มต้น

go
package main

import (
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc/resolver"
    "grpc_learn/helloworld/client/myresolver"
    hello2 "grpc_learn/helloworld/hello"
    "log"
    "time"
)

func init() {
    // ลงทะเบียน builder
    resolver.Register(
       // ลงทะเบียน custom consul resolver
       myresolver.NewConsulResolverBuilder("192.168.48.138:8500"),
    )
}

func main() {

    // สร้างการเชื่อมต่อ ไม่มีการตรวจสอบการเข้ารหัส
    conn, err := grpc.Dial("consul:hello-service",
       grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
       panic(err)
    }
    defer conn.Close()
    // สร้างไคลเอนต์
    client := hello2.NewSayHelloClient(conn)
    for range time.Tick(time.Second) {
       // เรียกจากระยะไกล
       helloRep, err := client.Hello(context.Background(), &hello2.HelloReq{Name: "client"})
       if err != nil {
          panic(err)
       }
       log.Printf("received grpc resp: %+v", helloRep.String())
    }

}

เริ่มต้นเซิร์ฟเวอร์ก่อน แล้วเริ่มต้นไคลเอนต์ เซิร์ฟเวอร์มีสองตัว ให้บริการเดียวกัน แค่ที่อยู่ไม่เหมือนกัน กลยุทธ์การโหลดบาลานซ์ของไคลเอนต์คือ round_robin จากช่วงเวลาล็อกของเซิร์ฟเวอร์จะเห็นว่ากลยุทธ์มีผล

2023/08/29 17:39:54 server running...
2023/08/29 21:03:46 received grpc req: name:"client"
2023/08/29 21:03:48 received grpc req: name:"client"
2023/08/29 21:03:50 received grpc req: name:"client"
2023/08/29 21:03:52 received grpc req: name:"client"
2023/08/29 21:03:54 received grpc req: name:"client"
2023/08/29 21:03:56 received grpc req: name:"client"
2023/08/29 21:03:58 received grpc req: name:"client"
2023/08/29 21:04:00 received grpc req: name:"client"

ข้างต้นเป็นกรณีใช้งานอย่างง่ายของการใช้ consul ร่วมกับ gRPC เพื่อ實現การลงทะเบียนและการค้นพบบริการอย่างง่าย

Golang by www.golangdev.cn edit