แนวทางที่ดีกว่าในการปรับใช้ Google Cloud อย่างต่อเนื่อง

เผยแพร่แล้ว: 2022-03-11

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

ไม่มีปัญหาการขาดแคลนบล็อกโพสต์เกี่ยวกับวิธีการใช้ไปป์ไลน์ซีดีของคุณเองโดยใช้เครื่องมือต่างๆ เช่น สแต็ค AWS, สแต็ค Google Cloud, ไปป์ไลน์ Bitbucket ฯลฯ แต่ฉันพบว่าส่วนใหญ่ไม่เหมาะกับความคิดของฉันว่าซีดีไปป์ไลน์อะไรดี ควรมีลักษณะดังนี้: ไฟล์ที่สร้างก่อน และทดสอบและปรับใช้เฉพาะไฟล์ที่สร้างไฟล์เดียว

ในบทความนี้ ฉันจะสร้างไปป์ไลน์การปรับใช้แบบต่อเนื่องที่ขับเคลื่อนด้วยเหตุการณ์ ซึ่งสร้างก่อนแล้วจึงเรียกใช้การทดสอบกับอาร์ติแฟกต์สุดท้ายในการปรับใช้ของเรา ซึ่งไม่เพียงแต่ทำให้ผลการทดสอบของเรามีความน่าเชื่อถือมากขึ้น แต่ยังทำให้ไปป์ไลน์ซีดีขยายได้อย่างง่ายดายอีกด้วย มันจะมีลักษณะดังนี้:

  1. มีการคอมมิตกับที่เก็บซอร์สของเรา
  2. สิ่งนี้จะกระตุ้นการสร้างรูปภาพที่เกี่ยวข้อง
  3. การทดสอบจะดำเนินการกับสิ่งประดิษฐ์ที่สร้างขึ้น
  4. หากทุกอย่างดูดี รูปภาพจะถูกนำไปใช้งานจริง

บทความนี้อนุมานว่าอย่างน้อยคุ้นเคยกับ Kubernetes และเทคโนโลยีคอนเทนเนอร์แล้ว แต่ถ้าคุณไม่คุ้นเคยหรือสามารถใช้การทบทวน โปรดดู Kubernetes คืออะไร คู่มือการปรับใช้คอนเทนเนอร์และการปรับใช้

ปัญหาเกี่ยวกับการตั้งค่าซีดีส่วนใหญ่

นี่คือปัญหาของฉันกับไปป์ไลน์ซีดีส่วนใหญ่: พวกเขามักจะทำทุกอย่างในไฟล์บิลด์ โพสต์บล็อกส่วนใหญ่ที่ฉันได้อ่านเกี่ยวกับเรื่องนี้จะมีรูปแบบต่างๆ ของลำดับต่อไปนี้ในไฟล์บิลด์ใดก็ตามที่พวกเขามี ( cloudbuild.yaml สำหรับ Google Cloud Build, bitbucket-pipeline.yaml )

  1. ทำการทดสอบ
  2. สร้างภาพ
  3. พุชอิมเมจไปที่คอนเทนเนอร์ repo
  4. อัปเดตสภาพแวดล้อมด้วยภาพใหม่

คุณไม่ได้ทำการทดสอบกับสิ่งประดิษฐ์ขั้นสุดท้ายของคุณ

การทำสิ่งต่าง ๆ ตามลำดับนี้ แสดงว่าคุณทำการทดสอบ หากสำเร็จ คุณต้องสร้างอิมเมจและดำเนินการกับไปป์ไลน์ที่เหลือ จะเกิดอะไรขึ้นหากกระบวนการสร้างเปลี่ยนภาพของคุณในลักษณะที่การทดสอบจะไม่ผ่านอีกต่อไป ในความคิดของฉัน คุณควรเริ่มต้นด้วยการสร้างสิ่งประดิษฐ์ (อิมเมจคอนเทนเนอร์สุดท้าย) และสิ่งประดิษฐ์นี้ไม่ควรเปลี่ยนระหว่างบิลด์กับเวลาที่นำไปใช้กับการผลิต สิ่งนี้ทำให้มั่นใจได้ว่าข้อมูลที่คุณมีเกี่ยวกับสิ่งประดิษฐ์ดังกล่าว (ผลการทดสอบ ขนาด ฯลฯ) นั้นถูกต้องเสมอ

สภาพแวดล้อมการสร้างของคุณมี “กุญแจสู่อาณาจักร”

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

คุณต้องรันไปป์ไลน์ทั้งหมดอีกครั้งหากขั้นตอนสุดท้ายล้มเหลว

หากขั้นตอนสุดท้ายล้มเหลว (เช่น เนื่องจากปัญหาข้อมูลรับรอง) คุณต้องเรียกใช้ไปป์ไลน์ทั้งหมดอีกครั้ง โดยใช้เวลาและทรัพยากรอื่นๆ ที่สามารถใช้ทำอย่างอื่นได้ดีกว่า

สิ่งนี้นำฉันไปสู่จุดสุดท้ายของฉัน:

ขั้นตอนของคุณไม่เป็นอิสระ

โดยทั่วไปแล้ว การมีขั้นตอนที่เป็นอิสระช่วยให้คุณมีความยืดหยุ่นมากขึ้นในไปป์ไลน์ของคุณ สมมติว่าคุณต้องการเพิ่มการทดสอบการทำงานให้กับไปป์ไลน์ของคุณ การมีขั้นตอนของคุณในไฟล์บิลด์เดียว คุณต้องให้สภาพแวดล้อมของบิลด์สร้างสภาพแวดล้อมการทดสอบที่ใช้งานได้และเรียกใช้การทดสอบในนั้น หากขั้นตอนของคุณเป็นอิสระ คุณสามารถมีทั้งการทดสอบหน่วยและการทดสอบการใช้งานที่เริ่มต้นโดยเหตุการณ์ "สร้างด้วยภาพ" จากนั้นพวกเขาจะวิ่งคู่ขนานในสภาพแวดล้อมของตนเอง

การติดตั้งซีดีในอุดมคติของฉัน

ในความคิดของฉัน วิธีที่ดีกว่าในการแก้ไขปัญหานี้คือการมีขั้นตอนอิสระที่เชื่อมโยงกันโดยกลไกเหตุการณ์

มีข้อดีหลายประการเมื่อเทียบกับวิธีก่อนหน้า:

คุณสามารถดำเนินการอิสระหลายอย่างในเหตุการณ์ต่างๆ

ดังที่กล่าวไว้ข้างต้น การสร้างภาพใหม่ที่ประสบความสำเร็จจะเป็นเพียงการเผยแพร่งาน "งานสร้างที่ประสบความสำเร็จ" ในทางกลับกัน เราสามารถเรียกใช้หลายสิ่งได้เมื่อเหตุการณ์นี้ถูกทริกเกอร์ ในกรณีของเรา เราจะเริ่มหน่วยและการทดสอบการใช้งาน คุณยังสามารถนึกถึงสิ่งต่างๆ เช่น การแจ้งเตือนนักพัฒนาเมื่อมีการทริกเกอร์เหตุการณ์ที่ล้มเหลวของบิลด์ หรือหากการทดสอบไม่ผ่าน

แต่ละสภาพแวดล้อมมีชุดสิทธิของตนเอง

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

มีความยืดหยุ่นมากขึ้น

ต้องการส่งอีเมลถึงใครบางคนในแต่ละรุ่นที่ประสบความสำเร็จหรือไม่? เพียงเพิ่มสิ่งที่ตอบสนองต่อเหตุการณ์นั้นและส่งอีเมล ง่าย—คุณไม่จำเป็นต้องเปลี่ยนรหัสบิวด์และไม่ต้องฮาร์ดโค้ดอีเมลของผู้อื่นในที่เก็บซอร์สของคุณ

การลองใหม่ทำได้ง่ายขึ้น

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

การปรับใช้ Google Cloud อย่างต่อเนื่อง

Google Cloud Platform มีเครื่องมือทั้งหมดที่จำเป็นในการสร้างระบบดังกล่าวในระยะเวลาอันสั้นและใช้โค้ดเพียงเล็กน้อย

แอปพลิเคชันทดสอบของเราเป็นแอปพลิเคชัน Flask อย่างง่ายที่ให้บริการข้อความคงที่ แอปพลิเคชันนี้ปรับใช้กับคลัสเตอร์ Kubernetes ที่ให้บริการกับอินเทอร์เน็ตในวงกว้าง

ฉันจะใช้ไปป์ไลน์เวอร์ชันง่าย ๆ ที่ฉันแนะนำก่อนหน้านี้ โดยพื้นฐานแล้วฉันลบขั้นตอนการทดสอบออก ดังนั้นตอนนี้จึงมีลักษณะดังนี้:

  • มีการคอมมิตใหม่กับที่เก็บซอร์ส
  • สิ่งนี้จะกระตุ้นการสร้างอิมเมจ หากสำเร็จ จะถูกพุชไปยังที่เก็บคอนเทนเนอร์ และเหตุการณ์จะถูกเผยแพร่ไปยังหัวข้อ Pub/Sub
  • สคริปต์ขนาดเล็กสมัครรับข้อมูลจากหัวเรื่องนั้นและตรวจสอบพารามิเตอร์ของรูปภาพ—หากตรงกับสิ่งที่เราขอ สคริปต์นั้นจะถูกนำไปใช้กับคลัสเตอร์ Kubernetes

นี่คือภาพกราฟิกของไปป์ไลน์ของเรา

การแสดงกราฟิกของไปป์ไลน์

การไหลเป็นดังนี้:

  1. มีคนผูกมัดกับที่เก็บของเรา
  2. สิ่งนี้จะทริกเกอร์บิลด์ระบบคลาวด์ซึ่งสร้างอิมเมจ Docker ตามที่เก็บต้นทาง
  3. บิลด์ระบบคลาวด์จะพุชอิมเมจไปยังที่เก็บคอนเทนเนอร์และเผยแพร่ข้อความไปยังคลาวด์ผับ/ย่อย
  4. ซึ่งจะทริกเกอร์ฟังก์ชันระบบคลาวด์ซึ่งจะตรวจสอบพารามิเตอร์ของข้อความที่เผยแพร่ (สถานะของบิลด์ ชื่อของภาพที่สร้างขึ้น ฯลฯ)
  5. หากพารามิเตอร์ดี ฟังก์ชันระบบคลาวด์จะอัปเดตการปรับใช้ Kubernetes ด้วยอิมเมจใหม่
  6. Kubernetes ปรับใช้คอนเทนเนอร์ใหม่ด้วยอิมเมจใหม่

รหัสแหล่งที่มา

ซอร์สโค้ดของเราเป็นแอป Flask ที่เรียบง่ายมาก ๆ ที่ให้บริการเฉพาะข้อความคงที่ นี่คือโครงสร้างของโครงการของเรา:

 ├── docker │ ├── Dockerfile │ └── uwsgi.ini ├── k8s │ ├── deployment.yaml │ └── service.yaml ├── LICENSE ├── Pipfile ├── Pipfile.lock └── src └── main.py

ไดเร็กทอรี Docker มีทุกสิ่งที่จำเป็นในการสร้างอิมเมจ Docker รูปภาพนี้ใช้อิมเมจ uWSGI และ Nginx และเพียงแค่ติดตั้งการพึ่งพาและคัดลอกแอปไปยังเส้นทางที่ถูกต้อง

ไดเร็กทอรี k8s มีการกำหนดค่า Kubernetes ประกอบด้วยบริการเดียวและการปรับใช้หนึ่งรายการ การปรับใช้เริ่มต้นหนึ่งคอนเทนเนอร์ตามอิมเมจที่สร้างจาก Dockerfile จากนั้นบริการจะเริ่มโหลดบาลานเซอร์ที่มีที่อยู่ IP สาธารณะและเปลี่ยนเส้นทางไปยังคอนเทนเนอร์ของแอป

Cloud Build

การกำหนดค่าบิลด์ระบบคลาวด์สามารถทำได้ผ่านคอนโซลระบบคลาวด์หรือบรรทัดคำสั่งของ Google Cloud ฉันเลือกใช้คอนโซลระบบคลาวด์

สกรีนช็อตของคอนโซลคลาวด์

ที่นี่ เราสร้างอิมเมจสำหรับการคอมมิตใดๆ ในสาขาใดๆ แต่คุณสามารถมีอิมเมจที่แตกต่างกันสำหรับการพัฒนาและการผลิต เป็นต้น

หากบิลด์สำเร็จ บิลด์ระบบคลาวด์จะเผยแพร่อิมเมจไปยังรีจิสตรีคอนเทนเนอร์ด้วยตัวเอง จากนั้นจะเผยแพร่ข้อความไปยังหัวข้อผับ/ย่อยของ cloud-builds

บิลด์ระบบคลาวด์ยังเผยแพร่ข้อความเมื่อบิลด์อยู่ระหว่างดำเนินการและเมื่อบิลด์ล้มเหลว ดังนั้นคุณจึงสามารถตอบสนองต่อข้อความเหล่านั้นได้

เอกสารประกอบสำหรับการแจ้งเตือน Pub/Sub ของ Cloud build อยู่ที่นี่ และรูปแบบของข้อความสามารถพบได้ที่นี่

คลาวด์ผับ/ย่อย

หากคุณดูในคลาวด์ผับ/แท็บย่อยในคอนโซลระบบคลาวด์ คุณจะเห็นว่าบิลด์ระบบคลาวด์ได้สร้างหัวข้อที่เรียกว่าบิลด์ระบบคลาวด์ นี่คือที่ที่คลาวด์บิวด์เผยแพร่การอัปเดตสถานะ

ภาพหน้าจอของโครงการย่อย/ย่อย

ฟังก์ชั่นคลาวด์

สิ่งที่เราจะทำตอนนี้คือสร้างฟังก์ชันระบบคลาวด์ที่ทริกเกอร์ในข้อความใดๆ ที่เผยแพร่ไปยังหัวข้อการสร้างระบบคลาวด์ คุณสามารถใช้คอนโซลระบบคลาวด์หรือยูทิลิตีบรรทัดคำสั่งของ Google Cloud ได้อีกครั้ง สิ่งที่ฉันทำในกรณีของฉันคือฉันใช้คลาวด์บิวด์เพื่อปรับใช้ฟังก์ชันคลาวด์ทุกครั้งที่มีการเปลี่ยนแปลง

ซอร์สโค้ดสำหรับฟังก์ชันระบบคลาวด์อยู่ที่นี่

อันดับแรก มาดูโค้ดที่ปรับใช้ฟังก์ชันคลาวด์นี้:

 steps: - name: 'gcr.io/cloud-builders/gcloud' id: 'test' args: ['functions', 'deploy', 'new-image-trigger', '--runtime=python37', '--trigger-topic=cloud-builds', '--entry-point=onNewImage', '--region=us-east1', '--source=https://source.developers.google.com/projects/$PROJECT_ID/repos/$REPO_NAME']

ที่นี่เราใช้อิมเมจ Google Cloud Docker ซึ่งช่วยให้เรียกใช้คำสั่ง GCcloud ได้อย่างง่ายดาย สิ่งที่เราดำเนินการนั้นเทียบเท่ากับการรันคำสั่งต่อไปนี้จากเทอร์มินัลโดยตรง:

 gcloud functions deploy new-image-trigger --runtime=python37 --trigger-topic=cloud-builds --entry-point=onNewImage --region=us-east1 --source=https://source.developers.google.com/projects/$PROJECT_ID/repos/$REPO_NAME

เรากำลังขอให้ Google Cloud ปรับใช้ฟังก์ชันระบบคลาวด์ใหม่ (หรือแทนที่หากมีฟังก์ชันตามชื่อนั้นในภูมิภาคนั้นอยู่แล้ว) ที่จะใช้รันไทม์ Python 3.7 และจะถูกทริกเกอร์โดยข้อความใหม่ในหัวข้อ cloud-builds นอกจากนี้เรายังบอก Google ว่าจะหาซอร์สโค้ดสำหรับฟังก์ชันนั้นได้ที่ไหน (ที่นี่ PROJECT_ID และ REPO_NAME เป็นตัวแปรสภาพแวดล้อมที่กำหนดโดยกระบวนการสร้าง) เรายังบอกด้วยว่าฟังก์ชันใดที่จะเรียกเป็นจุดเริ่มต้น

โปรดทราบว่าเพื่อให้สิ่งนี้ทำงานได้ คุณต้องให้บัญชีบริการ cloudbuild ของคุณทั้ง “ผู้พัฒนาฟังก์ชั่นระบบคลาวด์” และ “ผู้ใช้บัญชีบริการ” เพื่อให้สามารถใช้งานฟังก์ชั่นคลาวด์ได้

นี่คือตัวอย่างความคิดเห็นบางส่วนของโค้ดฟังก์ชันระบบคลาวด์

ข้อมูลจุดเข้าใช้งานจะมีข้อความที่ได้รับในหัวข้อผับ/ย่อย

 def onNewImage(data, context):

ขั้นตอนแรกคือการรับตัวแปรสำหรับการปรับใช้เฉพาะนั้นจากสภาพแวดล้อม (เรากำหนดตัวแปรเหล่านั้นโดยการปรับเปลี่ยนฟังก์ชั่นคลาวด์ในคอนโซลคลาวด์

 project = os.environ.get('PROJECT') zone = os.environ.get('ZONE') cluster = os.environ.get('CLUSTER') deployment = os.environ.get('DEPLOYMENT') deploy_image = os.environ.get('IMAGE') target_container = os.environ.get('CONTAINER')

เราจะข้ามส่วนที่เราตรวจสอบว่าโครงสร้างของข้อความเป็นสิ่งที่เราคาดหวัง และเราตรวจสอบว่าการสร้างนั้นประสบความสำเร็จและสร้างสิ่งประดิษฐ์จากรูปภาพหนึ่งชิ้น

ขั้นตอนต่อไปคือตรวจสอบให้แน่ใจว่าอิมเมจที่สร้างขึ้นนั้นเป็นอิมเมจที่เราต้องการปรับใช้

 image = decoded_data['results']['images'][0]['name'] image_basename = image.split('/')[-1].split(':')[0] if image_basename != deploy_image: logging.error(f'{image_basename} is different from {deploy_image}') return

ตอนนี้ เราได้รับไคลเอ็นต์ Kubernetes และเรียกข้อมูลการปรับใช้ที่เราต้องการแก้ไข

 v1 = get_kube_client(project, zone, cluster) dep = v1.read_namespaced_deployment(deployment, 'default') if dep is None: logging.error(f'There was no deployment named {deployment}') return

สุดท้าย เราแก้ไขการปรับใช้ด้วยอิมเมจใหม่ Kubernetes จะดูแลการเปิดตัว

 for i, container in enumerate(dep.spec.template.spec.containers): if container.name == target_container: dep.spec.template.spec.containers[i].image = image logging.info(f'Updating to {image}') v1.patch_namespaced_deployment(deployment, 'default', dep)

บทสรุป

นี่เป็นตัวอย่างพื้นฐานที่แสดงให้เห็นว่าฉันชอบสิ่งต่างๆ ที่จะออกแบบสถาปัตยกรรมในไปป์ไลน์ซีดีอย่างไร คุณสามารถมีขั้นตอนเพิ่มเติมได้โดยเปลี่ยนสิ่งที่ pub/sub event ทริกเกอร์สิ่งที่

ตัวอย่างเช่น คุณสามารถเรียกใช้คอนเทนเนอร์ที่รันการทดสอบภายในอิมเมจและเผยแพร่เหตุการณ์เมื่อประสบความสำเร็จ และอีกเหตุการณ์หนึ่งเมื่อเกิดความล้มเหลว และตอบสนองต่อสิ่งเหล่านั้นโดยอัปเดตการปรับใช้หรือการแจ้งเตือนขึ้นอยู่กับผลลัพธ์

ไปป์ไลน์ที่เราสร้างขึ้นนั้นค่อนข้างเรียบง่าย แต่คุณสามารถเขียนฟังก์ชันระบบคลาวด์อื่นๆ สำหรับส่วนอื่นๆ ได้ (เช่น ฟังก์ชันระบบคลาวด์ที่จะส่งอีเมลไปยังนักพัฒนาที่ยืนยันรหัสที่ทำลายการทดสอบหน่วยของคุณ)

อย่างที่คุณเห็น สภาพแวดล้อมการสร้างของเราไม่สามารถเปลี่ยนแปลงอะไรในคลัสเตอร์ Kubernetes ได้ และโค้ดการปรับใช้ของเรา (ฟังก์ชันระบบคลาวด์) ไม่สามารถแก้ไขอิมเมจที่สร้างขึ้นได้ การแยกสิทธิ์ของเราดูดี และเราสามารถหลับสนิทได้โดยรู้ว่านักพัฒนาที่โกงจะไม่ทำให้คลัสเตอร์การผลิตของเราล่ม นอกจากนี้ เราสามารถให้นักพัฒนาที่เน้น ops ของเราเข้าถึงโค้ดฟังก์ชันระบบคลาวด์ได้มากขึ้น เพื่อให้พวกเขาสามารถแก้ไขหรือปรับปรุงได้

หากคุณมีคำถาม ข้อสังเกต หรือการปรับปรุงใดๆ โปรดติดต่อในความคิดเห็นด้านล่าง