แนวทางที่ดีกว่าในการปรับใช้ Google Cloud อย่างต่อเนื่อง
เผยแพร่แล้ว: 2022-03-11การปรับใช้อย่างต่อเนื่อง (CD) คือแนวปฏิบัติในการปรับใช้โค้ดใหม่กับการผลิตโดยอัตโนมัติ ระบบการปรับใช้อย่างต่อเนื่องส่วนใหญ่จะตรวจสอบว่ารหัสที่จะปรับใช้นั้นทำงานได้โดยการรันหน่วยและการทดสอบการทำงาน และหากทุกอย่างดูดี การปรับใช้จะพร้อมใช้งาน การเปิดตัวมักจะเกิดขึ้นเป็นขั้นตอนเพื่อให้สามารถย้อนกลับได้หากโค้ดไม่ทำงานตามที่คาดไว้
ไม่มีปัญหาการขาดแคลนบล็อกโพสต์เกี่ยวกับวิธีการใช้ไปป์ไลน์ซีดีของคุณเองโดยใช้เครื่องมือต่างๆ เช่น สแต็ค AWS, สแต็ค Google Cloud, ไปป์ไลน์ Bitbucket ฯลฯ แต่ฉันพบว่าส่วนใหญ่ไม่เหมาะกับความคิดของฉันว่าซีดีไปป์ไลน์อะไรดี ควรมีลักษณะดังนี้: ไฟล์ที่สร้างก่อน และทดสอบและปรับใช้เฉพาะไฟล์ที่สร้างไฟล์เดียว
ในบทความนี้ ฉันจะสร้างไปป์ไลน์การปรับใช้แบบต่อเนื่องที่ขับเคลื่อนด้วยเหตุการณ์ ซึ่งสร้างก่อนแล้วจึงเรียกใช้การทดสอบกับอาร์ติแฟกต์สุดท้ายในการปรับใช้ของเรา ซึ่งไม่เพียงแต่ทำให้ผลการทดสอบของเรามีความน่าเชื่อถือมากขึ้น แต่ยังทำให้ไปป์ไลน์ซีดีขยายได้อย่างง่ายดายอีกด้วย มันจะมีลักษณะดังนี้:
- มีการคอมมิตกับที่เก็บซอร์สของเรา
- สิ่งนี้จะกระตุ้นการสร้างรูปภาพที่เกี่ยวข้อง
- การทดสอบจะดำเนินการกับสิ่งประดิษฐ์ที่สร้างขึ้น
- หากทุกอย่างดูดี รูปภาพจะถูกนำไปใช้งานจริง
บทความนี้อนุมานว่าอย่างน้อยคุ้นเคยกับ Kubernetes และเทคโนโลยีคอนเทนเนอร์แล้ว แต่ถ้าคุณไม่คุ้นเคยหรือสามารถใช้การทบทวน โปรดดู Kubernetes คืออะไร คู่มือการปรับใช้คอนเทนเนอร์และการปรับใช้
ปัญหาเกี่ยวกับการตั้งค่าซีดีส่วนใหญ่
นี่คือปัญหาของฉันกับไปป์ไลน์ซีดีส่วนใหญ่: พวกเขามักจะทำทุกอย่างในไฟล์บิลด์ โพสต์บล็อกส่วนใหญ่ที่ฉันได้อ่านเกี่ยวกับเรื่องนี้จะมีรูปแบบต่างๆ ของลำดับต่อไปนี้ในไฟล์บิลด์ใดก็ตามที่พวกเขามี ( cloudbuild.yaml
สำหรับ Google Cloud Build, bitbucket-pipeline.yaml
)
- ทำการทดสอบ
- สร้างภาพ
- พุชอิมเมจไปที่คอนเทนเนอร์ repo
- อัปเดตสภาพแวดล้อมด้วยภาพใหม่
คุณไม่ได้ทำการทดสอบกับสิ่งประดิษฐ์ขั้นสุดท้ายของคุณ
การทำสิ่งต่าง ๆ ตามลำดับนี้ แสดงว่าคุณทำการทดสอบ หากสำเร็จ คุณต้องสร้างอิมเมจและดำเนินการกับไปป์ไลน์ที่เหลือ จะเกิดอะไรขึ้นหากกระบวนการสร้างเปลี่ยนภาพของคุณในลักษณะที่การทดสอบจะไม่ผ่านอีกต่อไป ในความคิดของฉัน คุณควรเริ่มต้นด้วยการสร้างสิ่งประดิษฐ์ (อิมเมจคอนเทนเนอร์สุดท้าย) และสิ่งประดิษฐ์นี้ไม่ควรเปลี่ยนระหว่างบิลด์กับเวลาที่นำไปใช้กับการผลิต สิ่งนี้ทำให้มั่นใจได้ว่าข้อมูลที่คุณมีเกี่ยวกับสิ่งประดิษฐ์ดังกล่าว (ผลการทดสอบ ขนาด ฯลฯ) นั้นถูกต้องเสมอ
สภาพแวดล้อมการสร้างของคุณมี “กุญแจสู่อาณาจักร”
ด้วยการใช้สภาพแวดล้อมการสร้างของคุณเพื่อปรับใช้อิมเมจของคุณกับสแต็กการใช้งานจริงของคุณ คุณจะอนุญาตให้มันเปลี่ยนสภาพแวดล้อมการผลิตของคุณได้อย่างมีประสิทธิภาพ ฉันมองว่าสิ่งนี้เป็นสิ่งที่แย่มาก เพราะใครก็ตามที่มีสิทธิ์เขียนในที่เก็บซอร์สของคุณสามารถทำอะไรก็ได้ที่พวกเขาต้องการในสภาพแวดล้อมการผลิตของคุณ
คุณต้องรันไปป์ไลน์ทั้งหมดอีกครั้งหากขั้นตอนสุดท้ายล้มเหลว
หากขั้นตอนสุดท้ายล้มเหลว (เช่น เนื่องจากปัญหาข้อมูลรับรอง) คุณต้องเรียกใช้ไปป์ไลน์ทั้งหมดอีกครั้ง โดยใช้เวลาและทรัพยากรอื่นๆ ที่สามารถใช้ทำอย่างอื่นได้ดีกว่า
สิ่งนี้นำฉันไปสู่จุดสุดท้ายของฉัน:
ขั้นตอนของคุณไม่เป็นอิสระ
โดยทั่วไปแล้ว การมีขั้นตอนที่เป็นอิสระช่วยให้คุณมีความยืดหยุ่นมากขึ้นในไปป์ไลน์ของคุณ สมมติว่าคุณต้องการเพิ่มการทดสอบการทำงานให้กับไปป์ไลน์ของคุณ การมีขั้นตอนของคุณในไฟล์บิลด์เดียว คุณต้องให้สภาพแวดล้อมของบิลด์สร้างสภาพแวดล้อมการทดสอบที่ใช้งานได้และเรียกใช้การทดสอบในนั้น หากขั้นตอนของคุณเป็นอิสระ คุณสามารถมีทั้งการทดสอบหน่วยและการทดสอบการใช้งานที่เริ่มต้นโดยเหตุการณ์ "สร้างด้วยภาพ" จากนั้นพวกเขาจะวิ่งคู่ขนานในสภาพแวดล้อมของตนเอง
การติดตั้งซีดีในอุดมคติของฉัน
ในความคิดของฉัน วิธีที่ดีกว่าในการแก้ไขปัญหานี้คือการมีขั้นตอนอิสระที่เชื่อมโยงกันโดยกลไกเหตุการณ์
มีข้อดีหลายประการเมื่อเทียบกับวิธีก่อนหน้า:
คุณสามารถดำเนินการอิสระหลายอย่างในเหตุการณ์ต่างๆ
ดังที่กล่าวไว้ข้างต้น การสร้างภาพใหม่ที่ประสบความสำเร็จจะเป็นเพียงการเผยแพร่งาน "งานสร้างที่ประสบความสำเร็จ" ในทางกลับกัน เราสามารถเรียกใช้หลายสิ่งได้เมื่อเหตุการณ์นี้ถูกทริกเกอร์ ในกรณีของเรา เราจะเริ่มหน่วยและการทดสอบการใช้งาน คุณยังสามารถนึกถึงสิ่งต่างๆ เช่น การแจ้งเตือนนักพัฒนาเมื่อมีการทริกเกอร์เหตุการณ์ที่ล้มเหลวของบิลด์ หรือหากการทดสอบไม่ผ่าน
แต่ละสภาพแวดล้อมมีชุดสิทธิของตนเอง
ด้วยการให้แต่ละขั้นตอนเกิดขึ้นในสภาพแวดล้อมของตัวเอง เราจึงไม่จำเป็นต้องให้สภาพแวดล้อมเดียวมีสิทธิ์ทั้งหมด ตอนนี้สภาพแวดล้อมการสร้างสามารถสร้างได้เท่านั้น สภาพแวดล้อมการทดสอบสามารถทดสอบได้เท่านั้น และสภาพแวดล้อมการปรับใช้สามารถใช้งานได้เท่านั้น วิธีนี้ช่วยให้คุณมั่นใจได้ว่าเมื่อสร้างภาพแล้วจะไม่เปลี่ยนแปลง สิ่งประดิษฐ์ที่ผลิตขึ้นคือสิ่งที่จะสิ้นสุดในกองการผลิตของคุณ นอกจากนี้ยังช่วยให้ตรวจสอบได้ง่ายขึ้นว่าขั้นตอนใดของไปป์ไลน์ของคุณกำลังทำสิ่งใด เนื่องจากคุณสามารถเชื่อมโยงชุดข้อมูลประจำตัวหนึ่งชุดกับขั้นตอนเดียวได้
มีความยืดหยุ่นมากขึ้น
ต้องการส่งอีเมลถึงใครบางคนในแต่ละรุ่นที่ประสบความสำเร็จหรือไม่? เพียงเพิ่มสิ่งที่ตอบสนองต่อเหตุการณ์นั้นและส่งอีเมล ง่าย—คุณไม่จำเป็นต้องเปลี่ยนรหัสบิวด์และไม่ต้องฮาร์ดโค้ดอีเมลของผู้อื่นในที่เก็บซอร์สของคุณ
การลองใหม่ทำได้ง่ายขึ้น
การมีขั้นตอนอิสระยังหมายความว่าคุณไม่จำเป็นต้องรีสตาร์ทไปป์ไลน์ทั้งหมดหากขั้นตอนหนึ่งล้มเหลว หากเงื่อนไขความล้มเหลวเป็นแบบชั่วคราวหรือได้รับการแก้ไขด้วยตนเอง คุณสามารถลองทำตามขั้นตอนที่ล้มเหลวอีกครั้ง ซึ่งช่วยให้ไปป์ไลน์มีประสิทธิภาพมากขึ้น เมื่อขั้นตอนบิลด์ใช้เวลาหลายนาที เป็นการดีที่จะไม่ต้องสร้างอิมเมจใหม่เพียงเพราะคุณลืมให้สิทธิ์การเขียนในสภาพแวดล้อมการปรับใช้ของคุณไปยังคลัสเตอร์ของคุณ
การปรับใช้ Google Cloud อย่างต่อเนื่อง
Google Cloud Platform มีเครื่องมือทั้งหมดที่จำเป็นในการสร้างระบบดังกล่าวในระยะเวลาอันสั้นและใช้โค้ดเพียงเล็กน้อย
แอปพลิเคชันทดสอบของเราเป็นแอปพลิเคชัน Flask อย่างง่ายที่ให้บริการข้อความคงที่ แอปพลิเคชันนี้ปรับใช้กับคลัสเตอร์ Kubernetes ที่ให้บริการกับอินเทอร์เน็ตในวงกว้าง
ฉันจะใช้ไปป์ไลน์เวอร์ชันง่าย ๆ ที่ฉันแนะนำก่อนหน้านี้ โดยพื้นฐานแล้วฉันลบขั้นตอนการทดสอบออก ดังนั้นตอนนี้จึงมีลักษณะดังนี้:
- มีการคอมมิตใหม่กับที่เก็บซอร์ส
- สิ่งนี้จะกระตุ้นการสร้างอิมเมจ หากสำเร็จ จะถูกพุชไปยังที่เก็บคอนเทนเนอร์ และเหตุการณ์จะถูกเผยแพร่ไปยังหัวข้อ Pub/Sub
- สคริปต์ขนาดเล็กสมัครรับข้อมูลจากหัวเรื่องนั้นและตรวจสอบพารามิเตอร์ของรูปภาพ—หากตรงกับสิ่งที่เราขอ สคริปต์นั้นจะถูกนำไปใช้กับคลัสเตอร์ Kubernetes
นี่คือภาพกราฟิกของไปป์ไลน์ของเรา
การไหลเป็นดังนี้:
- มีคนผูกมัดกับที่เก็บของเรา
- สิ่งนี้จะทริกเกอร์บิลด์ระบบคลาวด์ซึ่งสร้างอิมเมจ Docker ตามที่เก็บต้นทาง
- บิลด์ระบบคลาวด์จะพุชอิมเมจไปยังที่เก็บคอนเทนเนอร์และเผยแพร่ข้อความไปยังคลาวด์ผับ/ย่อย
- ซึ่งจะทริกเกอร์ฟังก์ชันระบบคลาวด์ซึ่งจะตรวจสอบพารามิเตอร์ของข้อความที่เผยแพร่ (สถานะของบิลด์ ชื่อของภาพที่สร้างขึ้น ฯลฯ)
- หากพารามิเตอร์ดี ฟังก์ชันระบบคลาวด์จะอัปเดตการปรับใช้ Kubernetes ด้วยอิมเมจใหม่
- 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 ของเราเข้าถึงโค้ดฟังก์ชันระบบคลาวด์ได้มากขึ้น เพื่อให้พวกเขาสามารถแก้ไขหรือปรับปรุงได้
หากคุณมีคำถาม ข้อสังเกต หรือการปรับปรุงใดๆ โปรดติดต่อในความคิดเห็นด้านล่าง