Dailymotion ใช้ Kubernetes อย่างไร: การปรับใช้แอปพลิเคชัน
พวกเราที่ Dailymotion เริ่มใช้ Kubernetes ในการผลิตเมื่อ 3 ปีที่แล้ว แต่การนำแอปพลิเคชันไปใช้งานในหลายคลัสเตอร์เป็นเรื่องสนุก ดังนั้นในช่วงไม่กี่ปีที่ผ่านมา เราจึงพยายามปรับปรุงเครื่องมือและขั้นตอนการทำงานของเรา
มันเริ่มต้นที่ไหน
เราจะกล่าวถึงวิธีการปรับใช้แอปพลิเคชันของเราในคลัสเตอร์ Kubernetes หลายแห่งทั่วโลกในที่นี้
ในการปรับใช้ออบเจ็กต์ Kubernetes หลายรายการพร้อมกัน เราใช้
นอกจากนี้เรายังเขียนสคริปต์ Python ขนาดเล็กไว้บน Helm เพื่อตรวจสอบ สร้างแผนภูมิ เพิ่มความลับ และปรับใช้แอปพลิเคชัน งานทั้งหมดนี้ดำเนินการบนแพลตฟอร์ม CI ส่วนกลางโดยใช้อิมเมจนักเทียบท่า
มาเข้าประเด็นกันดีกว่า
บันทึก. ในขณะที่คุณอ่านข้อความนี้ ผู้สมัครรุ่นแรกสำหรับ Helm 3 ได้รับการประกาศแล้ว เวอร์ชันหลักประกอบด้วยการปรับปรุงมากมายเพื่อแก้ไขปัญหาบางอย่างที่เราพบในอดีต
ขั้นตอนการพัฒนาแผนภูมิ
เราใช้การแยกสาขาสำหรับแอปพลิเคชัน และเราตัดสินใจที่จะใช้วิธีการเดียวกันกับแผนภูมิ
- สาขา dev ใช้เพื่อสร้างแผนภูมิที่จะทดสอบบนคลัสเตอร์การพัฒนา
- เมื่อมีการส่งคำขอดึงไปที่ เจ้านายพวกมันจะถูกตรวจสอบในการจัดเตรียม
- สุดท้ายนี้ เราสร้างคำขอดึงเพื่อยืนยันการเปลี่ยนแปลงในสาขา แยง และนำไปใช้ในการผลิต
แต่ละสภาพแวดล้อมมีพื้นที่เก็บข้อมูลส่วนตัวของตัวเองซึ่งจัดเก็บแผนภูมิของเราและเราใช้
ที่เก็บแผนภูมิในสภาพแวดล้อมที่แตกต่างกัน
เป็นที่น่าสังเกตว่าเมื่อนักพัฒนาพุชสาขา dev เวอร์ชันของแผนภูมิจะถูกส่งไปยัง dev Chartmuseum โดยอัตโนมัติ ดังนั้น นักพัฒนาทุกคนจึงใช้พื้นที่เก็บข้อมูล dev เดียวกัน และคุณต้องระบุเวอร์ชันของแผนภูมิอย่างระมัดระวัง เพื่อไม่ให้ใช้การเปลี่ยนแปลงของผู้อื่นโดยไม่ตั้งใจ
นอกจากนี้ สคริปต์ Python เล็กๆ ของเราจะตรวจสอบออบเจ็กต์ Kubernetes กับข้อกำหนด Kubernetes OpenAPI โดยใช้
คำอธิบายทั่วไปของขั้นตอนการพัฒนาแผนภูมิ
- ตั้งค่างานไปป์ไลน์ตามข้อกำหนด
gazr.io สำหรับการควบคุมคุณภาพ (ผ้าสำลี, การทดสอบหน่วย) - การพุชอิมเมจนักเทียบท่าด้วยเครื่องมือ Python ที่ใช้งานแอปพลิเคชันของเรา
- การตั้งค่าสภาพแวดล้อมตามชื่อสาขา
- กำลังตรวจสอบไฟล์ Kubernetes yaml โดยใช้ Kubeval
- เพิ่มเวอร์ชันของแผนภูมิและแผนภูมิหลักโดยอัตโนมัติ (แผนภูมิที่ขึ้นอยู่กับแผนภูมิที่มีการเปลี่ยนแปลง)
- การส่งแผนภูมิไปยัง Chartmuseum ที่ตรงกับสภาพแวดล้อม
การจัดการความแตกต่างระหว่างคลัสเตอร์
สหพันธ์คลัสเตอร์
มีอยู่ช่วงหนึ่งที่เราเคยใช้
เพื่อแก้ไขปัญหา เราเริ่มจัดการคลัสเตอร์อย่างเป็นอิสระ ซึ่งทำให้กระบวนการง่ายขึ้นอย่างมาก (เราใช้เวอร์ชันแรกของการรวมศูนย์ บางอย่างอาจมีการเปลี่ยนแปลงในวินาที)
แพลตฟอร์มกระจายทางภูมิศาสตร์
ปัจจุบันแพลตฟอร์มของเรากระจายอยู่ใน 6 ภูมิภาค - 3 แห่งในพื้นที่และ 3 แห่งในระบบคลาวด์
ค่า Helm ทั่วโลก
ค่า Helm ทั่วโลก 4 ค่าช่วยให้คุณสามารถระบุความแตกต่างระหว่างคลัสเตอร์ได้ แผนภูมิทั้งหมดของเรามีค่าขั้นต่ำที่เป็นค่าเริ่มต้น
global:
cloud: True
env: staging
region: us-central1
clusterName: staging-us-central1
ค่านิยมสากล
ค่าเหล่านี้ช่วยกำหนดบริบทสำหรับแอปพลิเคชันของเราและใช้เพื่อวัตถุประสงค์ต่างๆ: การตรวจสอบ การติดตาม การบันทึก การโทรภายนอก การปรับขนาด ฯลฯ
- "คลาวด์": เรามีแพลตฟอร์ม Kubernetes แบบไฮบริด ตัวอย่างเช่น มีการปรับใช้ API ของเราในโซน GCP และในศูนย์ข้อมูลของเรา
- "env": ค่าบางค่าอาจมีการเปลี่ยนแปลงสำหรับสภาพแวดล้อมที่ไม่ใช้งานจริง เช่น คำจำกัดความของทรัพยากรและการกำหนดค่าการปรับขนาดอัตโนมัติ
- "ภูมิภาค": ข้อมูลนี้ช่วยระบุตำแหน่งของคลัสเตอร์และสามารถใช้เพื่อระบุตำแหน่งข้อมูลใกล้เคียงสำหรับบริการภายนอก
- "clusterName": ถ้าและเมื่อใดที่เราต้องการกำหนดค่าสำหรับแต่ละคลัสเตอร์
นี่เป็นตัวอย่างที่เฉพาะเจาะจง:
{{/* Returns Horizontal Pod Autoscaler replicas for GraphQL*/}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}
ตัวอย่างเทมเพลตหมวกกันน็อค
ตรรกะนี้ถูกกำหนดไว้ในเทมเพลตตัวช่วยเพื่อหลีกเลี่ยงความยุ่งเหยิงของ Kubernetes YAML
ประกาศรับสมัคร
เครื่องมือการปรับใช้ของเราอิงจากไฟล์ YAML หลายไฟล์ ด้านล่างนี้คือตัวอย่างวิธีที่เราประกาศบริการและโทโพโลยีการปรับขนาด (จำนวนเรพลิกา) ในคลัสเตอร์
releases:
- foo.world
foo.world: # Release name
services: # List of dailymotion's apps/projects
foobar:
chart_name: foo-foobar
repo: [email protected]:dailymotion/foobar
contexts:
prod-europe-west1:
deployments:
- name: foo-bar-baz
replicas: 18
- name: another-deployment
replicas: 3
คำจำกัดความของบริการ
นี่คือโครงร่างของขั้นตอนทั้งหมดที่กำหนดเวิร์กโฟลว์การปรับใช้ของเรา ขั้นตอนสุดท้ายจะปรับใช้แอปพลิเคชันกับกลุ่มผู้ปฏิบัติงานหลายกลุ่มพร้อมกัน
แล้วความลับล่ะ?
ในส่วนของความปลอดภัย เราติดตามความลับทั้งหมดจากสถานที่ต่างๆ และจัดเก็บไว้ในห้องนิรภัยที่ไม่เหมือนใคร
เครื่องมือการปรับใช้ของเราจะดึงค่าลับจาก Vault และเมื่อถึงเวลาปรับใช้ ให้แทรกค่าเหล่านั้นลงใน Helm
ในการดำเนินการนี้ เราได้กำหนดการแมประหว่างข้อมูลลับในห้องนิรภัยกับข้อมูลลับที่แอปพลิเคชันของเราต้องการ:
secrets:
- secret_id: "stack1-app1-password"
contexts:
- name: "default"
vaultPath: "/kv/dev/stack1/app1/test"
vaultKey: "password"
- name: "cluster1"
vaultPath: "/kv/dev/stack1/app1/test"
vaultKey: "password"
- เราได้กำหนดกฎทั่วไปที่ต้องปฏิบัติเมื่อบันทึกความลับในห้องนิรภัย
- ถ้าความลับมีผล ไปยังบริบทหรือคลัสเตอร์เฉพาะคุณต้องเพิ่มรายการเฉพาะ (ที่นี่บริบทคลัสเตอร์1 มีค่าของตัวเองสำหรับความลับ stack-app1-password)
- มิฉะนั้นจะใช้ค่า โดยค่าเริ่มต้น.
- สำหรับแต่ละรายการในรายการนี้ค่ะ ความลับของ Kubernetes มีการแทรกคู่คีย์-ค่า ดังนั้นเทมเพลตลับในแผนภูมิของเราจึงเรียบง่ายมาก
apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
{{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
name: "{{ .Chart.Name }}"
labels:
chartVersion: "{{ .Chart.Version }}"
tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: Opaque
ปัญหาและข้อจำกัด
การทำงานกับที่เก็บข้อมูลหลายแห่ง
ตอนนี้เราแยกการพัฒนาแผนภูมิและแอปพลิเคชันออกจากกัน ซึ่งหมายความว่านักพัฒนาจะต้องทำงานในที่เก็บ git สองแห่ง: หนึ่งแห่งสำหรับแอปพลิเคชัน และอีกหนึ่งแห่งสำหรับการกำหนดการปรับใช้กับ Kubernetes พื้นที่เก็บข้อมูล 2 git หมายถึง 2 เวิร์กโฟลว์ และเป็นเรื่องง่ายสำหรับมือใหม่ที่จะสับสน
การจัดการแผนภูมิทั่วไปเป็นเรื่องยุ่งยาก
ดังที่เราได้กล่าวไปแล้ว แผนภูมิทั่วไปมีประโยชน์อย่างมากในการระบุการขึ้นต่อกันและปรับใช้หลายแอปพลิเคชันได้อย่างรวดเร็ว แต่เราใช้ --reuse-values
เพื่อหลีกเลี่ยงการส่งผ่านค่าทั้งหมดทุกครั้งที่เราปรับใช้แอปพลิเคชันที่เป็นส่วนหนึ่งของแผนภูมิทั่วไปนี้
ในเวิร์กโฟลว์การจัดส่งอย่างต่อเนื่อง เรามีเพียงสองค่าที่เปลี่ยนแปลงเป็นประจำ คือ จำนวนเรพลิกา และแท็กรูปภาพ (เวอร์ชัน) ค่าอื่น ๆ ที่เสถียรกว่านั้นจะถูกเปลี่ยนด้วยตนเองซึ่งค่อนข้างยาก ยิ่งไปกว่านั้น ข้อผิดพลาดประการหนึ่งในการปรับใช้แผนภูมิทั่วไปอาจนำไปสู่ความล้มเหลวร้ายแรง ดังที่เราได้เห็นจากประสบการณ์ของเราเอง
การอัพเดตไฟล์การกำหนดค่าหลายไฟล์
เมื่อนักพัฒนาเพิ่มแอปพลิเคชันใหม่ เขาจะต้องเปลี่ยนไฟล์หลายไฟล์: การประกาศแอปพลิเคชัน รายการความลับ การเพิ่มแอปพลิเคชันเป็นการขึ้นต่อกันหากรวมอยู่ในแผนภูมิทั่วไป
สิทธิ์ของ Jenkins ขยายออกไปในห้องนิรภัยมากเกินไป
ตอนนี้เรามีอันหนึ่ง
กระบวนการย้อนกลับไม่ได้เป็นแบบอัตโนมัติ
หากต้องการย้อนกลับ คุณต้องรันคำสั่งบนหลายคลัสเตอร์ ซึ่งเต็มไปด้วยข้อผิดพลาด เราดำเนินการนี้ด้วยตนเองเพื่อให้แน่ใจว่ามีการระบุรหัสเวอร์ชันที่ถูกต้อง
เรากำลังก้าวไปสู่ GitOps
เป้าหมายของพวกเรา
เราต้องการคืนแผนภูมิไปยังพื้นที่เก็บข้อมูลของแอปพลิเคชันที่ปรับใช้
ขั้นตอนการทำงานจะเหมือนกับการพัฒนา ตัวอย่างเช่น เมื่อแบรนช์ถูกพุชไปที่มาสเตอร์ การปรับใช้จะถูกทริกเกอร์โดยอัตโนมัติ ความแตกต่างที่สำคัญระหว่างแนวทางนี้กับขั้นตอนการทำงานปัจจุบันก็คือ ทุกอย่างจะถูกจัดการในคอมไพล์ (ตัวแอปพลิเคชันและวิธีการปรับใช้ใน Kubernetes)
มีข้อดีหลายประการ:
- มาก ชัดเจนยิ่งขึ้น สำหรับนักพัฒนา การเรียนรู้วิธีใช้การเปลี่ยนแปลงในแผนภูมิท้องถิ่นทำได้ง่ายกว่า
- สามารถระบุข้อกำหนดการปรับใช้บริการได้ สถานที่เดียวกันกับรหัส บริการ.
- การจัดการการลบแผนภูมิทั่วไป. บริการนี้จะมีการเปิดตัว Helm ของตัวเอง ซึ่งจะช่วยให้คุณสามารถจัดการวงจรการใช้งานของแอปพลิเคชัน (ย้อนกลับ อัปเกรด) ในระดับที่เล็กที่สุด เพื่อไม่ให้ส่งผลกระทบต่อบริการอื่นๆ
- ประโยชน์ของคอมไพล์ สำหรับการจัดการแผนภูมิ: เลิกทำการเปลี่ยนแปลง บันทึกการตรวจสอบ ฯลฯ หากคุณต้องการเลิกทำการเปลี่ยนแปลงในแผนภูมิ คุณสามารถทำได้โดยใช้ git การปรับใช้เริ่มต้นโดยอัตโนมัติ
- คุณอาจพิจารณาปรับปรุงขั้นตอนการพัฒนาของคุณด้วยเครื่องมือเช่น นั่งร้านซึ่งนักพัฒนาสามารถทดสอบการเปลี่ยนแปลงในบริบทที่ใกล้เคียงกับการใช้งานจริงได้
การโยกย้ายสองขั้นตอน
นักพัฒนาของเราใช้ขั้นตอนการทำงานนี้มาเป็นเวลา 2 ปีแล้ว ดังนั้นเราจึงต้องการให้การย้ายข้อมูลไม่ยุ่งยากเท่าที่จะเป็นไปได้ ดังนั้นเราจึงตัดสินใจเพิ่มขั้นกลางระหว่างทางไปสู่เป้าหมาย
ขั้นตอนแรกนั้นง่าย:
- เราคงโครงสร้างที่คล้ายกันสำหรับการตั้งค่าการปรับใช้งานแอปพลิเคชัน แต่อยู่ในออบเจ็กต์เดียวที่เรียกว่า DailymotionRelease
apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
name: "app1.ns1"
environment: "dev"
branch: "mybranch"
spec:
slack_channel: "#admin"
chart_name: "app1"
scaling:
- context: "dev-us-central1-0"
replicas:
- name: "hermes"
count: 2
- context: "dev-europe-west1-0"
replicas:
- name: "app1-deploy"
count: 2
secrets:
- secret_id: "app1"
contexts:
- name: "default"
vaultPath: "/kv/dev/ns1/app1/test"
vaultKey: "password"
- name: "dev-europe-west1-0"
vaultPath: "/kv/dev/ns1/app1/test"
vaultKey: "password"
- 1 รุ่นต่อแอปพลิเคชัน (ไม่มีแผนภูมิทั่วไป)
- แผนภูมิในพื้นที่เก็บข้อมูล git ของแอปพลิเคชัน
เราได้พูดคุยกับนักพัฒนาซอฟต์แวร์ทุกคนแล้ว กระบวนการย้ายข้อมูลจึงได้เริ่มต้นขึ้นแล้ว ระยะแรกยังคงควบคุมโดยใช้แพลตฟอร์ม CI ฉันจะเขียนโพสต์อีกครั้งเกี่ยวกับระยะที่สองในเร็วๆ นี้: วิธีที่เราย้ายไปยังเวิร์กโฟลว์ GitOps ด้วย
ที่นี่เราได้พยายามอธิบายความคืบหน้าของเราในเวิร์กโฟลว์การปรับใช้แอปพลิเคชันในช่วงหลายปีที่ผ่านมา ซึ่งนำไปสู่ความคิดเกี่ยวกับแนวทาง GitOps เรายังไม่ถึงเป้าหมายและจะรายงานผลลัพธ์ แต่ตอนนี้เรามั่นใจว่าเราทำสิ่งที่ถูกต้องเมื่อเราตัดสินใจที่จะลดความซับซ้อนของทุกสิ่งและนำมันเข้าใกล้นิสัยของนักพัฒนามากขึ้น
ที่มา: will.com