GitHub ایکشنز، انگریس، اور خودکار TLS کا استعمال کرتے ہوئے GKE پر ایک عارضی PR پیش نظارہ ماحول کیسے بنایا جائے

ہر وہ ٹیم جو سافٹ ویئر فراہم کرتی ہے آخر کار اسٹیجنگ ماحول بناتی ہے۔ اور اسٹیجنگ ماحول بنانے والی ہر ٹیم کو ایک ہی مسائل کا سامنا کرنا پڑتا ہے۔

سٹیجنگ مشترکہ ہے۔ ایک ماحول، کئی پل کی درخواستیں۔ آپ کسی کو جانچنے کے لیے ایک آدھی مکمل خصوصیت تعینات کرتے ہیں۔ اس کے اوپر ایک اور شخص رکھا گیا ہے۔ ایک تیسرا شخص ڈیٹا بیس کی منتقلی چلاتا ہے۔ جمعرات کی دوپہر تک، کوئی بھی نہیں جان سکے گا کہ برانچ کا کون سا ورژن درحقیقت چل رہا ہے، اور "یہ اسٹیج پر کام کرتا ہے” کا بیان مکمل طور پر غیر معلوماتی ہے۔

حل واقف ہے۔ سلیک چینلز جہاں لوگ سٹیجنگ پر اصرار کرتے ہیں جیسے کانفرنس رومز، گھومنے والی تعیناتی کے نظام الاوقات، یا دوسرا سٹیجنگ ماحول، تیسرا سٹیجنگ ماحول، ہر ایک آخری کی نسبت پروڈکشن کنفیگریشن سے آگے بڑھتا ہے۔

وہاں بہتر نمونے موجود ہیں، اور Vercel، Netlify، اور Shopify جیسی کمپنیوں کی پلیٹ فارم ٹیموں نے انہیں معیاری بنا دیا ہے۔ ہر پل کی درخواست کے لیے ایک منفرد تجربہ فراہم کریں۔.

PR کھولنے سے آپ کی درخواست کی ایک نئی، الگ تھلگ کاپی خود بخود تعینات ہو جاتی ہے۔ جب آپ کسی اور کمٹ کو آگے بڑھاتے ہیں تو اسے اپ ڈیٹ کیا جائے گا۔ اگر آپ PR کو ضم یا بند کرتے ہیں تو سب کچھ حذف ہو جائے گا۔ کوئی قطار نہیں، کوئی بلنگ نہیں، کوئی کلیئرنس نہیں، کوئی بہاؤ نہیں۔

یہ وہی ہے جو ہم اس ٹیوٹوریل میں بنا رہے ہیں۔ نقاد کے نقطہ نظر سے ایسا ہی لگتا ہے۔ اصل وقت میں کام کرنے والے URLs کے ساتھ پل کی درخواستوں میں ظاہر ہونے والے بوٹ تبصرے۔

کیا تعمیر کرنا ہے

اس ہینڈ بک کے اختتام تک، آپ کے پاس ایک ورکنگ سسٹم ہوگا جو اس طرح نظر آئے گا:

  • پل کی درخواست کھولنے سے ایک GitHub ایکشن ورک فلو متحرک ہوتا ہے جو کہ خاص طور پر اس PR کے لیے ٹیگ کردہ کنٹینر امیج بناتا ہے۔

  • تصویر کو گوگل آرٹفیکٹ رجسٹری میں دھکیل دیا گیا ہے۔

  • ایک وقف Kubernetes نام کی جگہ (pr-42، pr-43وغیرہ) ایک GKE کلسٹر کے اندر بنائے گئے ہیں اور تعیناتی سے پہلے وسائل کے کوٹے کے تابع ہیں۔

  • ایپلی کیشنز کو ان کے اپنے نام کی جگہ پر تعینات کیا جاتا ہے، تمام دیگر PR ماحول سے مکمل طور پر الگ تھلگ۔

  • مشترکہ انگریس کنٹرولر روٹنگ pr-42.preview.yourdomain.com خود بخود جاری کردہ درست TLS سرٹیفکیٹ کا استعمال کرتے ہوئے اس نام کی جگہ پر۔

  • بوٹ پل کی درخواست کے لائیو یو آر ایل پر تبصرے کرتا ہے اور ہر دھکے کے ساتھ اسی تبصرے کو اپ ڈیٹ کرتا ہے۔

  • ایک نئی کمٹ کو آگے بڑھانا آپ کے ماحول کو اپ ڈیٹ شدہ کوڈ کے ساتھ دوبارہ تعینات کرتا ہے۔

  • PR کو بند کرنے یا ضم کرنے سے نام کی جگہ اور اس کے اندر موجود ہر چیز حذف ہو جاتی ہے۔

  • طے شدہ ٹاسک سڑن میں چھوٹ جانے والے کسی بھی ماحول کو پکڑتا ہے۔

آپ تین کلیدی ڈیزائن فیصلوں کے ذریعے Terraform کا استعمال کرتے ہوئے بنیادی ڈھانچے کی تعمیر بھی کریں گے، ہر ایک اپنے حصے کے ساتھ۔

  1. کوئی سروس اکاؤنٹ کلید نہیں: GitHub ایکشنز کام کے بوجھ کی شناخت فیڈریشن کے ذریعے گوگل کلاؤڈ کو تصدیق کرتی ہے۔ اسٹوریج بلنگ کے لیے ایک قلیل المدتی OIDC ٹوکن تصدیق شدہ۔ GitHub رازوں میں سروس اکاؤنٹ JSON کیز آج کل CI/CD پائپ لائنوں میں سب سے زیادہ عام سیکیورٹی مسائل میں سے ایک ہیں اور ضروری نہیں ہیں۔

  2. ایک PR کے بجائے ایک لوڈ بیلنسر: ایک مشترکہ انگریس کنٹرولر تمام ماحول کو میزبان نام کے ذریعے روٹ کرتا ہے، اور ایک وائلڈ کارڈ DNS ریکارڈ مستقبل کے تمام میزبان ناموں پر مشتمل ہوتا ہے۔ سادہ ورژن ہر ماحول کے لیے کلاؤڈ لوڈ بیلنس فراہم کرتا ہے، جو جلدی مہنگا ہو جاتا ہے۔

  3. تھیوریم فرض کرنا ناکامی: جب PR ختم ہوجاتا ہے، تو سڑن چلائی جاتی ہے۔ تاہم، شیڈیولڈ ریپرز نام کی جگہوں کو بھی حذف کر دیتے ہیں جن کے PR غائب ہو چکے ہیں یا ان کی زیادہ سے زیادہ زندگی گزر چکی ہے۔ کیونکہ خودکار کلین اپ بھی جو 95% موثر ہیں پھر بھی لیک ہوتے ہیں اور لیک آپ کے بل پر ظاہر ہوتے ہیں۔

ہم جس فن تعمیر کے ساتھ کام کر رہے ہیں وہ ہے:

پورے نظام کا آرکیٹیکچر ڈایاگرام۔ GitHub ایکشنز ورک لوڈ آئیڈینٹی فیڈریشن اور آرٹفیکٹ رجسٹری کے ذریعے GKE کلسٹرز بناتا اور تعینات کرتا ہے، جب کہ براؤزر وائلڈ کارڈ DNS، ایک مشترکہ Google کلاؤڈ لوڈ بیلنسر، اور TLS کو ہینڈل کرنے والے سرٹیفکیٹ مینیجر کے ساتھ ایک NGINX Ingress کنٹرولر کے ذریعے فی پل درخواست نام کی جگہوں تک پہنچتے ہیں۔

خاکہ ایک ہی نظام کے ذریعے دو الگ الگ راستوں کا پتہ لگاتا ہے۔

نیلے رنگ میں نشان زد راستے خودکار ہیں۔ پل کی درخواست کام کے بوجھ کی شناخت فیڈریشن کے ذریعے گوگل کلاؤڈ کو تصدیق کرتی ہے اور ایک GitHub ایکشن کو متحرک کرتی ہے جو PR-ٹیگ شدہ تصویر کو آرٹیفیکٹ رجسٹری میں دھکیلتی ہے اور اسے GKE کلسٹر کے اندر ایک مخصوص نام کی جگہ پر تعینات کرتی ہے۔

ہر نام کی جگہ (pr-42، pr-43وغیرہ) ان کی اپنی تعیناتی، خدمات، اور پوڈز ہر چیز سے مکمل طور پر الگ تھلگ ہیں۔

اس کے آگے دو اور خودکار حصے ہیں۔ ایک ایسا بوٹ ہے جو پل کی درخواست کے پیش نظارہ یو آر ایل کو پوسٹ اور اپ ڈیٹ کرتا ہے، اور دوسرا ایک طے شدہ ریپر ہے جو یتیم نام کی جگہوں کو تلاش اور حذف کرتا ہے۔

سبز نقطے والا راستہ عوامی نقل و حمل ہے۔ پیش نظارہ URL کے لیے براؤزر کی درخواستیں ایک واحد وائلڈ کارڈ DNS ریکارڈ کے ذریعے ایک GCP لوڈ بیلنس کو حل کرتی ہیں، جو پھر اسے مشترکہ NGINX Ingress کنٹرولر کو بھیج دیتا ہے۔ وہ کنٹرولر درخواست کے مواد کو پڑھتا ہے۔ Host ہیڈر تلاش کریں اور اسے صحیح نام کی جگہ پر لے جائیں۔ cert-manager Let’s Encrypt سے سرٹیفکیٹ کی درخواست کرنے کے لیے اس کے ساتھ چلتا ہے تاکہ ہر ماحول اپنا درست TLS سرٹیفکیٹ حاصل کر سکے۔

اہم تفصیلات کیا ہیں؟ ایسا نہیں ضرب ایک لوڈ بیلنسر، ایک DNS ریکارڈ، اور ایک Ingress کنٹرولر کھلی پل کی درخواستوں کی تعداد سے قطع نظر تمام ماحول میں کام کرتا ہے۔

انڈیکس

شرطیں

شروع کرنے سے پہلے، یقینی بنائیں کہ آپ کے پاس درج ذیل ہیں:

  • گوگل کلاؤڈ پروجیکٹ ادائیگی کو چالو کر دیا گیا ہے۔ اگر آپ کے پاس یہ نہیں ہے تو console.cloud.google.com پر ایک بنائیں۔ اس ٹیوٹوریل کو چلانے کی لاگت تقریباً $5-$10 ہے، اس بات پر منحصر ہے کہ آپ کلسٹر کو کتنی دیر تک رکھتے ہیں۔ آخر میں صفائی کے حصے کو مت چھوڑیں۔

  • gcloud سی ایل آئی انسٹال اور تصدیق شدہ۔ چلائیں gcloud auth login لاگ ان کریں اور gcloud config set project YOUR_PROJECT_ID براہ کرم اپنے پروجیکٹ کی طرف اشارہ کریں۔

  • ٹیرافارم ورژن 1.5 یا اس سے اوپر۔ اسے چیک کریں terraform version.

  • kubectl انسٹال آپ کے Kubernetes کلسٹر کے ساتھ تعامل کے لیے CLI۔

  • ڈاکر تعمیرات کو CI کے حوالے کرنے سے پہلے ٹیسٹ کرنے کے لیے مقامی طور پر انسٹال کیا جاتا ہے۔

  • GitHub ذخیرہ جو آپ کے مالک ہیں۔ چونکہ یہ ہر چیز کا محرک ہے جو ہم بناتے ہیں، اس لیے اسے ایک ذخیرہ ہونا چاہیے جہاں ہم شاخوں کو آگے بڑھا سکتے ہیں اور پل کی درخواستیں کھول سکتے ہیں۔

  • ڈوکر کا بنیادی علم (تصویر، پرت، ڈاکر فائل) کوبرنیٹس (پوڈ، تعیناتی، خدمت، نام کی جگہ) اور گٹ ہب آپریشنز (ورک فلو، ٹاسک، مرحلہ)۔ آپ کو ماہر ہونے کی ضرورت نہیں ہے، لیکن آپ کو معلوم ہونا چاہیے کہ یہ چیزیں کیا ہیں۔

  • کنٹرول ورژن 3 چیک کریں (انگریس کنٹرولر اور سرٹ مینیجر انسٹال کرنے کے لیے) helm version.

  • ڈومین کا نام جس کا آپ انتظام کرتے ہیں۔اس میں DNS ریکارڈز شامل کرنے کی صلاحیت ہے۔ اس سے کوئی فرق نہیں پڑتا کہ آپ کہاں رجسٹرڈ ہیں۔ پیش نظارہ کے لیے وقف کردہ ایک ذیلی ڈومین کی وضاحت کریں، جیسا کہ: preview.yourdomain.comاور اس کے نیچے پورا ماحول موجود ہے۔

ڈومین کی ضروریات کے بارے میں نوٹ: یہ پروجیکٹ کا ناقابل فراموش حصہ ہے۔ آئیے انکرپٹ کو تصدیق کرنی چاہیے کہ آپ سرٹیفکیٹ جاری کرنے سے پہلے اپنے ڈومین کو کنٹرول کرتے ہیں، اور یہ عوامی انٹرنیٹ پر ایک حقیقی HTTP درخواست کر کے ایسا کرتا ہے۔ کوئی راستہ نہیں .local ڈومین، آئٹم /etc/hostsیا وہ ڈومین جو آپ کے مالک نہیں ہیں کام نہیں کریں گے۔

جب کوئی فالتو ڈومین نہ ہو۔ .dev یا .xyz اس کی لاگت ایک سال میں چند پاؤنڈ ہے اور بالکل اس قسم کے پروجیکٹ کے لیے استعمال کرنے کے قابل ہے۔

ابتدائی طور پر توجہ دینے کے قابل ایک اور چیز ہے: gke-gcloud-auth-plugin. تازہ ترین ورژن gcloud پہلی بار جب آپ اپنے GKE کلسٹر سے جڑیں گے تو آپ کو اسے انسٹال کرنے کا کہا جائے گا۔ آپ اسے استعمال کرکے پہلے سے انسٹال کرسکتے ہیں:

gcloud components install gke-gcloud-auth-plugin

مرحلہ 1: پروجیکٹ کا ڈھانچہ ترتیب دیں۔

پہلے ڈائریکٹریز کو ترتیب دیں تاکہ آپ کے پاس ایک واضح ذہنی ماڈل ہو کہ سب کچھ کہاں ہے۔

mkdir -p pr-preview-envs/{app,terraform,k8s/base,.github/workflows}
cd pr-preview-envs
git init

ہر ڈائریکٹری پر مشتمل ہے:

  • app/: گو ایپلیکیشن اور متعلقہ ڈاکر فائل

  • terraform/: بنیادی ڈھانچہ بطور کوڈ: GKE کلسٹر، آرٹفیکٹ رجسٹری، کام کا بوجھ شناخت اتحاد سیٹ اپ

  • k8s/base/: بنیادی Kubernetes اشارہ کرتا ہے کہ تمام پیش نظارہ ماحول بنائے گئے ہیں۔

  • .github/workflows/: ماحول بنانے اور تباہ کرنے کے لیے دو GitHub ایکشن ورک فلوز

مرحلہ 5 میں ورک لوڈ آئیڈینٹی فیڈریشن سیٹنگز میں، آپ ریپوزٹری کا پورا نام درج کریں (owner/repo):

git remote add origin https://github.com/YOUR_USERNAME/pr-preview-envs.git

مرحلہ 2: درخواست مکمل کریں۔

آپ کو تقسیم کرنے کے لیے کچھ درکار ہے۔ ہم استعمال کریں گے جاؤاس منصوبے کی خاص اہمیت کی تین وجوہات ہیں:

سب سے پہلے، گو کو ایک واحد جامد طور پر منسلک بائنری میں مرتب کیا گیا ہے۔ انسٹال کرنے کے لیے کوئی رن ٹائم یا انٹرپریٹر نہیں ہے۔ اس کا مطلب ہے کہ کنٹینر کی بہت چھوٹی تصاویر، اور چھوٹی تصاویر کا مطلب ہے تیز دھکے اور تیز کھینچنا، جو کہ تصاویر بناتے وقت بہت اہم ہے۔ تمام عوامی PRs کے لیے وابستگی ہے۔.

دوسرا، جاؤ -ldflags -X میکانزم آپ کو تعمیر کے وقت اپنی بائنری میں اقدار کو انجیکشن کرنے کی اجازت دیتا ہے۔ ہم اسے PR نمبروں کو بیک کرنے اور Git کمٹ SHA کو براہ راست آپ کی ایپلیکیشن پر لاگو کرنے کے لیے استعمال کرتے ہیں، تاکہ آپ کو بخوبی معلوم ہو کہ آپ کی ایپ کیا چل رہی ہے۔

آخر میں، تعمیر کے اوقات تیز ہیں اور CI وقت مفت نہیں ہے۔

بنانا app/main.go:

package main

import (
	"fmt"
	"net/http"
	"os"
)

// These values are empty at compile time and get injected by the
// linker via -ldflags -X during the Docker build. See the Dockerfile.
var (
	prNumber = "local"
	gitSha   = "dev"
)

func handler(w http.ResponseWriter, r *http.Request) {
	// Kubernetes injects these via the Downward API — see deployment.yaml
	namespace := os.Getenv("POD_NAMESPACE")
	podName := os.Getenv("POD_NAME")

	if namespace == "" {
		namespace = "not-in-kubernetes"
	}
	if podName == "" {
		podName, _ = os.Hostname()
	}

	fmt.Fprintf(w, "Preview environment is livenn")
	fmt.Fprintf(w, "PR number  : %sn", prNumber)
	fmt.Fprintf(w, "Git SHA    : %sn", gitSha)
	fmt.Fprintf(w, "Namespace  : %sn", namespace)
	fmt.Fprintf(w, "Pod name   : %sn", podName)
}

func healthz(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintln(w, "ok")
}

func main() {
	http.HandleFunc("/", handler)
	http.HandleFunc("/healthz", healthz)

	fmt.Println("Server starting on port 8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		fmt.Fprintf(os.Stderr, "server error: %vn", err)
		os.Exit(1)
	}
}

اندر سے ایک گو ماڈیول شروع کرنا app/:

cd app
go mod init preview-app

آئیے اس پر ایک نظر ڈالتے ہیں کہ یہ کوڈ کیا کرتا ہے اور ہر حصہ وہاں کیوں ہے۔

var prNumber = "local" اور var gitSha = "dev" ڈیفالٹ قدر کے ساتھ پیکیج لیول سٹرنگ متغیر۔ ڈیفالٹ وہی ہے جو آپ دیکھتے ہیں اگر آپ بائنری کو مقامی طور پر بغیر ڈوکر کی تعمیر سے گزرتے ہیں۔

CI کی تعمیر کے دوران، Go لنکر CI کو اصل PR نمبر کے ساتھ اوور رائٹ کرتا ہے اور SHA کا ارتکاب کرتا ہے۔ یہ پورے پروجیکٹ میں سب سے زیادہ کارآمد ڈیبگنگ فیچر ہے۔ جب آپ پیش نظارہ ماحول تک پہنچیں گے، تو ایپ خود آپ کو بتائے گی کہ یہ کس کمٹ پر بنایا گیا تھا۔ اندازہ لگانے کی ضرورت نہیں۔

os.Getenv("POD_NAMESPACE") اور os.Getenv("POD_NAME") ماحولیاتی متغیرات کو پڑھیں جو Kubernetes کنٹینر میں داخل کرتا ہے۔ نیچے کی طرف API. ہم اسے مرحلہ 8 میں تعیناتی مینی فیسٹ میں ترتیب دیں گے۔

Downward API Pods کو Kubernetes API اسناد کی ضرورت کے بغیر اپنے بارے میں میٹا ڈیٹا (ان کا نام، نام کی جگہ، اور لیبل) پڑھنے کی اجازت دیتا ہے۔ یہ جاننے کا صاف ترین طریقہ ہے کہ آپ کی ایپ کہاں چل رہی ہے۔

کہ handler فنکشن روٹ پاتھ میں رجسٹرڈ۔ /. جواب میں PR نمبر، کمٹ SHA، نام کی جگہ، اور Pod کا نام لکھیں۔ اس سوال کا جواب دینے کے لیے یہاں چار نکات ہیں، "میں بالکل کیا دیکھ رہا ہوں؟”

کہ healthz فنکشن میں رجسٹرڈ ہے۔ /healthzیہ متن کے ساتھ HTTP 200 لوٹاتا ہے۔ ok. Kubernetes اس اختتامی نقطہ کو زندہ رہنے اور تیاری کی تحقیقات کے لیے استعمال کرتا ہے، اور اسے آپ کے تعیناتی مینی فیسٹ سے منسلک کرتا ہے۔

http.ListenAndServe(":8080", nil) پورٹ 8080 پر سرور شروع کریں۔ اگر سٹارٹ اپ ناکام ہو جاتا ہے، تو یہ ایک ایرر پرنٹ کرتا ہے اور غیر صفر کوڈ کے ساتھ باہر نکلتا ہے، اس لیے کوبرنیٹس کو معلوم ہے کہ کنٹینر خراب ہو گیا ہے اور اسے دوبارہ شروع کر سکتا ہے۔

آگے بڑھنے سے پہلے مقامی طور پر اس کی جانچ کریں۔

go run main.go

دوسرے ٹرمینل میں:

curl http://localhost:8080

آپ کو دیکھنا چاہئے PR number : local اور Git SHA : devاس کی وجہ یہ ہے کہ آپ ڈوکر بلڈ سے باہر چل رہے ہیں۔ یہ مقامی طرز عمل کی توقع ہے۔

کرل سے ٹرمینل آؤٹ پٹ ایپلیکیشن رپورٹنگ PR نمبر دکھا رہا ہے۔

مرحلہ 3: ایک ڈاکر فائل لکھیں۔

بنانا app/Dockerfile:

# -----------------------------------------------------------
# Stage 1: Build
# -----------------------------------------------------------
FROM golang:1.27-alpine AS builder
#Ensure the version matches what is in your go.mod file

# These are injected by the CI pipeline at build time
ARG PR_NUMBER=local
ARG GIT_SHA=dev

WORKDIR /app

# Copy go.mod first so Docker can cache the dependency layer
COPY go.mod .
RUN go mod download

COPY main.go .

# -w -s strips debug symbols (~30% smaller binary)
# -X overwrites package-level string variables at link time
RUN CGO_ENABLED=0 go build 
    -ldflags="-w -s -X main.prNumber=${PR_NUMBER} -X main.gitSha=${GIT_SHA}" 
    -o server main.go

# -----------------------------------------------------------
# Stage 2: Runtime
# -----------------------------------------------------------
FROM alpine:latest

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

WORKDIR /app
COPY --from=builder /app/server .

EXPOSE 8080
CMD ["./server"]

یہاں کچھ چیزیں چل رہی ہیں جو سمجھنے کے قابل ہیں۔

1۔ -ldflags -X انجکشن

-X main.prNumber=${PR_NUMBER}

کہ -X لنکر فلیگس لنک کے وقت پیکیج لیول سٹرنگ متغیرات کی قدریں سیٹ کرتے ہیں۔ نحو ہے: -X importpath.name=value. ہمارا متغیر ہے۔ package mainدرآمد کا راستہ یہ ہے: main.

یہ خصوصیت صرف اس کے ساتھ کام کرتی ہے: string یہ ایک متغیر ہے جس کا پہلے ہی ذریعہ میں اعلان کیا گیا ہے۔ آپ اس طرح نیا متغیر نہیں بنا سکتے۔ int یا bool. اسی لیے prNumber ایک تار ہے main.go اگرچہ تعداد موجود ہے۔

یہ تعیناتی مینی فیسٹ کے ذریعے ماحولیاتی متغیرات کو منتقل کرنے کے بجائے کیوں؟ میٹا ڈیٹا کی تعمیر ہے ویڈیونہیں تعیناتی. env var کو پاس کرنے سے کسی کو تصویر لگانے کی اجازت ملتی ہے۔ abc123 اگر آپ کے پاس ایک env var ہے جس کا دعویٰ ہے کہ وہ عہد ہے۔ def456ایپ آپ سے جھوٹ بولنے کو تیار ہے۔ اسے بائنری میں جلانے کا مطلب ہے کہ ہم صرف یہ بتا سکتے ہیں کہ تصویر اصل میں کیا ہے۔

کہ CGO_ENABLED=0 پرچم

پہلے سے طے شدہ طور پر، Go C لائبریریوں سے لنک کر سکتا ہے۔ اس صورت میں، نتیجے میں بائنری سسٹم کی C لائبریریوں کے رن ٹائم پر موجود ہونے پر منحصر ہے۔ الپائن استعمال کرتا ہے musl زیادہ عام کے بجائے glibcیہ عدم مطابقت "بلڈر مرحلے میں کام کرتا ہے، رن ٹائم مرحلے میں کریش” کی غلطیوں کی ایک عام وجہ ہے۔

ماحول CGO_ENABLED=0 ایک خالص Go مکمل طور پر جامد بائنری کو مجبور کرتا ہے جس میں کوئی بیرونی لائبریری انحصار نہیں ہے۔ تمام لینکس سسٹمز پر چلتا ہے، بشمول: scratch ایک تصویر جس میں کچھ بھی نہیں ہے۔

COPY go.mod . پہلے COPY main.go .

ڈوکر پرت کے لحاظ سے تصاویر کی پرت بناتا ہے اور ہر پرت کو کیش کرتا ہے۔ بس کاپی کریں۔ go.mod سب سے پہلے اور چلائیں go mod download سورس کوڈ کو کاپی کرنے سے پہلے کیشڈ انحصاری پرت بنائیں۔

بعد کی تعمیرات میں (اور یاد رکھیں کہ یہ پائپ لائن سب سے اوپر بنتی ہے) تمام عوامی PRs کے لیے وابستگی ہے۔)، Docker مندرجہ ذیل صورتوں میں مکمل طور پر ڈاؤن لوڈنگ انحصار کو چھوڑ دیتا ہے۔ go.mod یہ تبدیل نہیں ہوا ہے۔ درجنوں انحصاروں کے ساتھ ایک حقیقی پروجیکٹ میں، 20 سیکنڈ کی تعمیر اور 3 منٹ کی تعمیر کے درمیان فرق کو ٹیم کی طرف سے کیے جانے والے تمام دھکے سے ضرب دیا جاتا ہے۔

غیر جڑ صارف

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

کنٹینرز بطور ڈیفالٹ روٹ چلتے ہیں۔ اگر کسی حملہ آور کو آپ کی ایپلی کیشن میں کسی کمزوری کا پتہ چلتا ہے، تو کنٹینر کے اندر روٹ کے طور پر چلانے سے انہیں مزید بڑھنے کی گنجائش ملتی ہے۔ غیر مراعات یافتہ صارفین کو تخلیق کرنا اور ان میں تبدیل ہونا دھماکے کے رداس کو محدود کر دے گا۔ اس کی قیمت دو لائنیں ہیں، اور ایسا نہ کرنے کی کوئی وجہ نہیں ہے۔

مقامی طور پر بنائیں اور ٹیسٹ کریں۔

docker build --build-arg PR_NUMBER=99 --build-arg GIT_SHA=abc1234 -t preview-app:test .
docker run -p 8080:8080 preview-app:test

پھر curl http://localhost:8080 دوسرے ٹرمینل میں۔ اب کیا دیکھنا ہے۔ PR number : 99 اور Git SHA : abc1234. انجکشن کام کرتا ہے۔

کرل سے ٹرمینل آؤٹ پٹ جس میں ایپلیکیشن رپورٹنگ PR نمبر 99 اور Git SHA abc1234 کو ڈوکر بلڈ آرگیومینٹس کے ذریعے بلڈ ٹائم پر داخل کیا گیا ہے۔

مرحلہ 4: Terraform کا استعمال کرتے ہوئے بنیادی ڈھانچے کی فراہمی

اب کلاؤڈ انفراسٹرکچر بناتے ہیں۔ ہم ٹولز کے سیٹ کے بجائے Terraform استعمال کر رہے ہیں۔ gcloud کچھ غلط ہونے کے وقت ظاہر ہونے والی وجوہات کے لیے کمانڈ: Terraform کے ساتھ، terraform destroy یتیم وسائل خود بخود آپ کی تخلیق کردہ ہر چیز کو درست ترتیب میں ترتیب دیتے ہیں آپ سے کچھ بھی وصول کیے بغیر۔

بنانا terraform/variables.tf:

variable "project_id" {
  description = "GCP project ID"
  type        = string
}

variable "region" {
  description = "GCP region"
  type        = string
  default     = "europe-west2"
}

variable "zone" {
  description = "GCP zone. A zonal cluster is cheaper than a regional one."
  type        = string
  default     = "europe-west2-a"
}

variable "cluster_name" {
  description = "Name of the GKE cluster"
  type        = string
  default     = "pr-preview-cluster"
}

variable "github_repo" {
  description = "GitHub repository in owner/repo format, e.g. amina/pr-preview-envs"
  type        = string
}

variable "machine_type" {
  description = "Node machine type"
  type        = string
  default     = "e2-standard-2"
}

variable "node_count" {
  description = "Number of nodes in the pool"
  type        = number
  default     = 2
}

ابھی بنائیں terraform/main.tf:

terraform {
  required_version = ">= 1.5"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

# Look up the project so we can reference its numeric project number,
# which Workload Identity Federation requires (it doesn't accept the
# human-readable project ID).
data "google_project" "current" {}

# -----------------------------------------------------------
# Enable required APIs
# -----------------------------------------------------------
resource "google_project_service" "apis" {
  for_each = toset([
    "container.googleapis.com",
    "artifactregistry.googleapis.com",
    "iamcredentials.googleapis.com",
    "sts.googleapis.com",
  ])

  service            = each.value
  disable_on_destroy = false
}

# -----------------------------------------------------------
# Artifact Registry — where preview images live
# -----------------------------------------------------------
resource "google_artifact_registry_repository" "preview" {
  location      = var.region
  repository_id = "pr-preview"
  description   = "Container images for PR preview environments"
  format        = "DOCKER"

  depends_on = [google_project_service.apis]
}

# -----------------------------------------------------------
# GKE cluster
# -----------------------------------------------------------
resource "google_container_cluster" "primary" {
  name     = var.cluster_name
  location = var.zone

  # Create the cluster with a throwaway default pool, then remove it
  # and manage nodes through a separate node pool resource. This is the
  # standard pattern — it lets you resize or upgrade the node pool
  # without recreating the cluster control plane.
  remove_default_node_pool = true
  initial_node_count       = 1

  networking_mode = "VPC_NATIVE"
  ip_allocation_policy {}

  # Set to true before you use this for anything real.
  deletion_protection = false

  depends_on = [google_project_service.apis]
}

resource "google_container_node_pool" "primary_nodes" {
  name       = "${var.cluster_name}-pool"
  location   = var.zone
  cluster    = google_container_cluster.primary.name
  node_count = var.node_count

  node_config {
    machine_type = var.machine_type
    disk_size_gb = 50

    oauth_scopes = [
      "https://www.googleapis.com/auth/cloud-platform",
    ]

    labels = {
      purpose = "pr-preview-envs"
    }
  }

  management {
    auto_repair  = true
    auto_upgrade = true
  }
}

یہاں چند باتیں قابل ذکر ہیں۔

data "google_project" "current" یہ کچھ نہیں بناتا۔ یہ ڈیٹا کا ایک ذریعہ ہے جو فراہم کنندہ سے موجودہ معلومات کو پڑھتا ہے۔ یہ ضروری ہے کیونکہ ورک لوڈ آئیڈینٹی فیڈریشن پروجیکٹس کی شناخت کرتی ہے۔ عددی پروجیکٹ نمبریہ وہ پروجیکٹ ID نہیں ہے جسے آپ کنسول میں داخل کرتے ہیں۔ اسے دستی طور پر تلاش کرنے اور پیسٹ کرنے کے بجائے، Terraform اسے آپ کے لیے کھینچتا ہے۔

for_each کو google_project_service: 4 APIs کو فعال کرنے کے لیے 4 تقریباً ایک جیسے وسائل کے بلاکس لکھنے کے بجائے for_each سیٹ کے ذریعے اعادہ کریں اور فی آئٹم ایک وسیلہ بنائیں۔ disable_on_destroy = false طریقہ terraform destroy یہ ان APIs کو بند نہیں کرتا جیسا کہ آپ چاہتے ہیں، کیونکہ آپ کے پروجیکٹ میں دیگر چیزیں انہیں استعمال کر سکتی ہیں۔

remove_default_node_pool = true: GKE کو کلسٹر بناتے وقت ڈیفالٹ نوڈ پول بنانے کی ضرورت ہوتی ہے۔ اسے ترتیب دینے سے یہ فوری طور پر حذف ہوجاتا ہے اور اس کے اپنے نوڈ پول کو ایک علیحدہ وسیلہ کے طور پر متعین کرتا ہے۔ فائدہ یہ ہے کہ نوڈ پول کی تبدیلیاں (سائز، مشین کی قسم میں تبدیلی، اپ گریڈ) خود کلسٹر وسائل میں تبدیلیوں کو مجبور نہیں کرتی ہیں۔

node_count = 2 کے ساتھ e2-standard-2: 2 نوڈس کے ساتھ 2 vCPUs اور 8 GB ہر ایک۔ پیش نظارہ ماحول چھوٹا ہے، لیکن ایک ساتھ کئی چل رہے ہیں، اور GKE پر ان کے اپنے سسٹم پوڈ بھی ہیں۔ دو نوڈس آرام دہ ہیڈ روم فراہم کرتے ہیں۔ اگر آپ بیک وقت تقریباً 8 سے زیادہ پیش نظارہ تجربات کو فعال کرنے کا ارادہ رکھتے ہیں تو اسے بہتر بنائیں۔

deletion_protection = false: GKE نے اسے پروڈکشن کلسٹرز کے حادثاتی طور پر حذف ہونے سے روکنے کے لیے شامل کیا۔ ہم اسے غیر فعال کر دیں گے کیونکہ یہ ٹیوٹوریل کلسٹر ہے جسے آخری بار حذف کر دیا جائے گا۔ اگر آپ اسے حقیقی استعمال کے لیے ڈھال رہے ہیں، تو اسے اس پر سیٹ کریں: true.

مرحلہ 5: کام کے بوجھ کی شناخت فیڈریشن کے ساتھ بغیر کلیدی تصدیق کو ترتیب دیں۔

یہ سب سے اہم حصہ ہے، تو آئیے آہستہ آہستہ اس سے گزرتے ہیں۔

سروس اکاؤنٹ کا کلیدی مسئلہ

GitHub ایکشنز کو تصاویر کو آرٹفیکٹ رجسٹری میں آگے بڑھانے اور آپ کے GKE کلسٹر میں تعینات کرنے کی اجازت درکار ہوتی ہے۔ یہاں یہ ہے کہ زیادہ تر ٹیوٹوریلز اسے کیسے ہینڈل کرتے ہیں:

  1. GCP میں سروس اکاؤنٹ بنائیں

  2. JSON کلید بنائیں

  3. اس JSON کو ایک GitHub راز میں چسپاں کریں جس کا نام ہے: GCP_SA_KEY

  4. ورک فلو میں لوڈ کریں۔

یہ کام کرتا ہے، لیکن یہ سیکیورٹی کا مسئلہ بھی ہے اور اس کی وجہ یہ ہے:

سب سے پہلے چابیاں کبھی ختم نہیں ہوتیں۔ سروس اکاؤنٹ JSON کیز طویل المدت اسناد ہیں۔ یہ اس وقت تک درست ہے جب تک کہ کوئی اسے واضح طور پر منسوخ نہ کر دے، جس کا عملی طور پر مطلب یہ ہے کہ یہ ہمیشہ کے لیے درست ہے کیونکہ کوئی بھی اسے تبدیل کرنا یاد نہیں رکھتا ہے۔

دوسرا کلید ایک بیئرر ٹوکن ہے۔ وہ JSON فائل کس کو ملتی ہے۔ ہے آپ کا سروس اکاؤنٹ۔ کوئی دوسرا عوامل، IP پابندیاں یا میعاد ختم نہیں ہوتی۔ اپنی فائلوں کو کاپی کریں اور انہیں دنیا میں کہیں بھی استعمال کریں۔

تیسرا دھماکے کا رداس بہت بڑا ہے۔ GitHub کے راز ورک فلو کے سامنے ہیں۔ وہ راز کسی سمجھوتہ شدہ فریق ثالث ٹاسک، تعمیر میں بدنیتی پر مبنی انحصار، یا پل کی درخواست میں ترمیم شدہ ورک فلو کی وجہ سے افشا ہو سکتے ہیں۔ ایک بار انکشاف ہونے کے بعد، حملہ آوروں کو کلاؤڈ پروجیکٹ تک مسلسل رسائی حاصل ہوتی ہے۔

یہ نظریاتی نہیں ہے۔ سروس اکاؤنٹ کی کلید کا رساو کلاؤڈ سیکیورٹی کے واقعات کی سب سے عام بنیادی وجوہات میں سے ایک ہے۔

کام کا بوجھ شناختی فیڈریشن مسئلے کو کیسے حل کرتی ہے۔

ورک لوڈ آئیڈینٹی الائنس طویل عرصے سے چلنے والی چابیاں مکمل طور پر ہٹا دیں۔ بہاؤ مندرجہ ذیل ہے:

  1. GitHub ایکشنز مختصر زندگی پیدا کرتی ہیں۔ OIDC ٹوکن ورک فلو چلانے کے لیے، اس ٹوکن پر GitHub کے ذریعے دستخط کیے گئے ہیں اور اس میں تصدیق شدہ دعوے شامل ہیں جیسے چل رہا ذخیرہ، برانچ، ورک فلو، اور ایکٹر۔

  2. ورک فلو گوگل کلاؤڈ کی سیکیورٹی ٹوکن سروس کو وہ ٹوکن فراہم کرتا ہے۔

  3. Google GitHub کی عوامی کلیدوں کے خلاف ٹوکن دستخطوں کی تصدیق کرتا ہے اور قواعد کے خلاف دعووں کی تصدیق کرتا ہے۔ آپ خاص طور پر، ٹوکنز کو ایک ذخیرہ سے آنے کے لیے بنایا گیا ہے نہ کہ کسی اور چیز سے۔

  4. اگر تمام اندراجات مماثل ہیں، تو Google ایک مختصر مدت تک رسائی کا ٹوکن جاری کرتا ہے، جو عام طور پر تقریباً ایک گھنٹے کے لیے درست ہوتا ہے۔

  5. ورک فلو اس ٹوکن کو استعمال کرتا ہے اور یہ جلد ہی ختم ہو جائے گا۔

کہیں بھی کوئی راز محفوظ نہیں ہے۔ کوئی لیک، گردش یا کالعدم کرنے کے لیے کچھ بھی نہیں ہے۔ اگر کوئی آپ کے ذخیرے کو فورک کرتا ہے اور وہی ورک فلو چلاتا ہے، تو OIDC ٹوکن جو وہ تیار کریں گے ان کا اگر دعوی میں ذخیرہ نام ہے، تو Google کی تصدیق ناکام ہو جائے گی اور آپ کو کچھ نہیں ملے گا۔

اس میں شامل کریں terraform/main.tf:

# -----------------------------------------------------------
# Service account that GitHub Actions will impersonate
# -----------------------------------------------------------
resource "google_service_account" "github_actions" {
  account_id   = "github-actions-preview"
  display_name = "GitHub Actions — PR Preview Environments"
}

# Permission to push images to Artifact Registry
resource "google_project_iam_member" "artifact_writer" {
  project = var.project_id
  role    = "roles/artifactregistry.writer"
  member  = "serviceAccount:${google_service_account.github_actions.email}"
}

# Permission to create namespaces and deploy workloads in GKE.
# container.developer grants full access to Kubernetes API objects,
# but NOT permission to modify the cluster itself.
resource "google_project_iam_member" "gke_developer" {
  project = var.project_id
  role    = "roles/container.developer"
  member  = "serviceAccount:${google_service_account.github_actions.email}"
}

# -----------------------------------------------------------
# Workload Identity Federation
# -----------------------------------------------------------
resource "google_iam_workload_identity_pool" "github" {
  workload_identity_pool_id = "github-pool"
  display_name              = "GitHub Actions Pool"
  description               = "Identity pool for GitHub Actions OIDC"

  depends_on = [google_project_service.apis]
}

resource "google_iam_workload_identity_pool_provider" "github" {
  workload_identity_pool_id          = google_iam_workload_identity_pool.github.workload_identity_pool_id
  workload_identity_pool_provider_id = "github-provider"
  display_name                       = "GitHub OIDC Provider"

  # Map claims from the GitHub OIDC token to Google attributes
  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.actor"      = "assertion.actor"
    "attribute.repository" = "assertion.repository"
  }

  # CRITICAL: without this condition, ANY GitHub repository on the
  # internet could authenticate against your pool.
  attribute_condition = "assertion.repository == '${var.github_repo}'"

  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}

# Allow tokens from your specific repo to impersonate the service account
resource "google_service_account_iam_member" "github_impersonation" {
  service_account_id = google_service_account.github_actions.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.github.workload_identity_pool_id}/attribute.repository/${var.github_repo}"
}

آئیے اس حصے کو توڑ دیں جو لوگوں کو محافظ سے دور رکھتا ہے۔

attribute_mapping: GitHub کے OIDC ٹوکن میں دعووں کا ایک مجموعہ ہوتا ہے۔ یہ بلاک گوگل کو بتاتا ہے کہ کون سے دعوے پڑھے جائیں اور کس کو اندرونی طور پر کال کریں۔ assertion.sub ٹوکن کا موضوع، جیسے repo:owner/name:ref:refs/heads/main)۔ assertion.repository یہ مکمل ذخیرہ نام ہے۔ google.subject یہ درکار ہیں اور باقی اختیاری خصوصیات ہیں جن کے لیے آپ قواعد بنا سکتے ہیں۔

attribute_condition: اس سطور کو دو بار پڑھیں۔ کیونکہ غلط پڑھنا ایک سنگین خطرہ ہے۔

attribute_condition = "assertion.repository == '${var.github_repo}'"

اگر یہ شرط موجود نہیں ہے تو شناختی پول پر بھروسہ کیا جاتا ہے۔ GitHub OIDC جاری کنندہاس کا مطلب ہے کہ ہم GitHub پر موجود تمام ذخیروں پر بھروسہ کرتے ہیں۔ کوئی بھی شخص جو پول کے وسائل کے راستوں کو جانتا ہے (جو خفیہ نہیں ہیں اور ورک فلو فائل میں ظاہر ہوتے ہیں) سروس اکاؤنٹ کی توثیق اور نقالی کرسکتا ہے۔

یہ حالت اعتماد کو بالکل ایک ذخیرہ تک محدود کر دیتی ہے۔ نقشہ سازی کرتے وقت گوگل کو اب انتساب کی شرائط کی ضرورت ہوتی ہے۔ assertion.repositoryبالکل اس لیے کہ بہت سے لوگ اس کو غلط سمجھتے ہیں۔

principalSet://...: یہ شناخت دی جا رہی ہے۔ workloadIdentityUser کردار دائیں سے بائیں پڑھیں۔ github-pool جس کا attribute.repository اگر یہ آپ کے ذخیرے کے برابر ہے، تو آپ اس سروس اکاؤنٹ کی نقالی کرسکتے ہیں۔ براہ کرم نوٹ کریں کہ استعمال کرتے ہوئے data.google_project.current.numberایک عددی پروجیکٹ نمبر ہے، پروجیکٹ ID نہیں۔ یہاں پراجیکٹ آئی ڈی استعمال کرنے کے نتیجے میں ایک ایسی غلطی ہوگی جو واضح طور پر اس بات کی وضاحت نہیں کرتی ہے کہ کیا غلط ہو رہا ہے، جو کہ WIF میں سب سے عام غلطیوں میں سے ایک ہے۔

کیوں roles/container.developer اور پھر نہیں۔ roles/container.admin? container.developer کلسٹر کے اندر Kubernetes API اشیاء تک مکمل رسائی فراہم کرتا ہے (نام کی جگہیں بنائیں، تعیناتیاں لاگو کریں، وسائل کو حذف کریں)۔ container.admin آپ خود کلسٹر میں بھی ترمیم کر سکتے ہیں، بشمول نوڈ پول کا سائز تبدیل کرنا، اپ گریڈ کرنا، یا کلسٹر کو مکمل طور پر حذف کرنا۔

ہمارا ورک فلو استحقاق سے خالی ہونا چاہیے کیونکہ اسے خود کلسٹر کی ترتیب کو چھونے کی ضرورت نہیں ہے۔ یہ دراصل کم از کم اجازت ہے۔ انہیں بالکل وہی دیں جو انہیں کام کے لیے درکار ہے اور اس سے زیادہ کچھ نہیں۔

ابھی بنائیں terraform/outputs.tf:

output "cluster_name" {
  value = google_container_cluster.primary.name
}

output "cluster_zone" {
  value = var.zone
}

output "artifact_registry_url" {
  description = "Base URL for pushing images"
  value       = "${var.region}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.preview.repository_id}"
}

output "workload_identity_provider" {
  description = "Paste this into the GitHub Actions workflow"
  value       = "projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.github.workload_identity_pool_id}/providers/${google_iam_workload_identity_pool_provider.github.workload_identity_pool_provider_id}"
}

output "service_account_email" {
  description = "Paste this into the GitHub Actions workflow"
  value       = google_service_account.github_actions.email
}

output "get_credentials_command" {
  value = "gcloud container clusters get-credentials ${google_container_cluster.primary.name} --zone ${var.zone} --project ${var.project_id}"
}

آؤٹ پٹ آرائشی نہیں ہے۔ workload_identity_provider اور service_account_email GitHub ایکشنز ورک فلو میں پیسٹ کرنے کے لیے دونوں قدریں دستی طور پر کنفیگر کرنے کے لیے تکلیف دہ ہیں۔

مرحلہ 6: ٹیرافارم کنفیگریشن کا اطلاق کریں۔

اندر سے terraform/ ڈائریکٹری:

terraform init

یہ گوگل فراہم کنندہ کو ڈاؤن لوڈ کرے گا اور بیک اینڈ کو ترتیب دے گا۔ آپ کو پیغام نظر آئے گا "Terraform کامیابی سے شروع کیا گیا!”

اب دیکھیں کہ کیا بنایا جائے گا۔

terraform plan 
  -var="project_id=YOUR_PROJECT_ID" 
  -var="github_repo=YOUR_USERNAME/pr-preview-envs"

پلان آؤٹ پٹ پڑھیں۔ اس سے پہلے کہ Terraform کچھ بھی تخلیق کرتا ہے، یہ آپ کو بالکل وہی بتاتا ہے جو آپ بنانے کا ارادہ رکھتے ہیں۔ شامل کرنے کے لیے تقریباً 12 وسائل ہونے چاہئیں اور تبدیل کرنے یا حذف کرنے کے لیے کوئی وسائل نہیں ہونے چاہئیں۔

اگر یہ درست ہے:

terraform apply 
  -var="project_id=YOUR_PROJECT_ID" 
  -var="github_repo=YOUR_USERNAME/pr-preview-envs"

زمرہ yes جب اشارہ کیا گیا۔ کلسٹر بنانے میں تقریباً 5 منٹ لگتے ہیں۔

ٹپ: اس میں ٹائپ کریں -var اگر دہرایا جائے تو جھنڈے جلدی پرانے ہو جاتے ہیں۔ نامی ایک فائل بنائیں terraform.tfvars پر مشتمل project_id = "your-project-id" اور github_repo = "you/pr-preview-envs"Terraform اسے خود بخود آپ کے لیے منتخب کرے گا۔ اسے اگلی بار شامل کریں۔ .gitignore.

مکمل ہونے پر، Terraform آؤٹ پٹ پرنٹ کرتا ہے۔ اسے محفوظ کریںجیسا کہ آپ کو مرحلہ 9 میں اس کی ضرورت ہوگی:

artifact_registry_url = "europe-west2-docker.pkg.dev/your-project/pr-preview"
service_account_email = "github-actions-preview@your-project.iam.gserviceaccount.com"
workload_identity_provider = "projects/123456789/locations/global/workloadIdentityPools/github-pool/providers/github-provider"

آپ کسی بھی وقت دوبارہ پرنٹ کر سکتے ہیں: terraform output.

ٹیرافارم لاگو آؤٹ پٹ مطلوبہ اسناد دکھاتا ہے۔

جڑیں kubectl درج ذیل کمانڈ کا استعمال کرتے ہوئے نئے کلسٹر پر جائیں: get_credentials_command حساب کتاب:

gcloud container clusters get-credentials pr-preview-cluster 
  --zone europe-west2-a 
  --project YOUR_PROJECT_ID

چیک کریں:

kubectl get nodes

آپ کو دو نوڈس دیکھنا چاہئے۔ Ready صورت حال

kubectl گیٹ نوڈ کا آؤٹ پٹ دو GKE نوڈس تیار حالت میں دکھا رہا ہے۔

مرحلہ 7: دستی طور پر لائف سائیکل کی تصدیق کریں۔

اسے خودکار کرنے سے پہلے پہلے ہاتھ سے آزمائیں۔

یہ مصروف کام نہیں ہے۔ آٹومیشن بنیادی طور پر صرف اسکرپٹ ہیں جو کمانڈ چلاتے ہیں جو صارف داخل کر سکتے ہیں۔ اگر آپ نہیں جانتے کہ کمانڈ کیا ہے، تو آٹومیشن ناکام ہونے پر آپ اسے ڈیبگ نہیں کر سکتے، اور یہ عام طور پر ڈیمو سے پہلے رات 11 بجے ناکام ہو جاتا ہے۔ یہاں 10 منٹ بعد میں آپ کو گھنٹوں بچائیں گے۔

نام کی جگہ بنائیں۔

kubectl create namespace pr-test

پلیس ہولڈرز کو یہاں تعینات کریں۔

kubectl create deployment hello 
  --image=nginx:1.27-alpine 
  --namespace=pr-test

اسے چیک کریں:

kubectl get pods --namespace=pr-test

اب اہم حصہ کنٹینمنٹ کو ثابت کر رہا ہے۔

kubectl get pods --all-namespaces | grep hello

پھلی صرف درج ذیل جگہوں پر موجود ہے: pr-test. کچھ نہیں default یا شاید دیگر نام کی جگہیں بھی اس کے بارے میں جانتی ہیں۔ یہ وہ پراپرٹی ہے جس پر پورا پروجیکٹ قائم ہے۔ نام کی جگہ ایک حد ہے، اور ایک PR کے نام کی جگہ کے اندر موجود کوئی بھی چیز دوسرے PR کے لیے پوشیدہ ہے۔

اب اسے الگ کر لیں۔

kubectl delete namespace pr-test

دیکھیں کیا ہوتا ہے:

kubectl get namespace pr-test

صرف چند سیکنڈ کے لیے دیکھیں اور آپ دیکھیں گے۔ Terminatingجب آپ کلک کرتے ہیں، نام کی جگہ مکمل طور پر غائب ہو جاتی ہے۔ نام کی جگہ کو حذف کرنے سے اس کے اندر موجود تمام وسائل حذف ہو جاتے ہیں، بشمول تعیناتی، پوڈ، خدمات، ترتیب کے نقشے، اور راز۔ ایک ہی کمانڈ پوری صفائی کو مکمل کرتی ہے۔

یہ واحد خاصیت PR کے لیے مخصوص نام کی جگہوں کو پیش نظارہ ماحول کے لیے بہترین بناتی ہے۔ جدا کرنا احتیاط سے حذف کرنے کا سلسلہ نہیں ہے جسے صحیح طریقے سے کیا جانا چاہیے۔ یہ ایک ایسا حکم ہے جو پیچھے کچھ نہیں چھوڑتا۔

مرحلہ 8: Kustomize کا استعمال کرتے ہوئے ایک Kubernetes مینی فیسٹ لکھیں۔

تمام پیش نظارہ ماحول ایک ہی ایپلیکیشن کو تعینات کرتے ہیں۔ فرق صرف اتنا ہے pr-42 اور pr-43 نام کی جگہ اور تصویر کا ٹیگ۔

یہ مسئلہ ہے اپنی مرضی کے مطابق اسے حل کریں۔ مینی فیسٹ کو ایک بار اس طرح لکھیں: بنیاداس کے اوپر ہم ایک چھوٹی اعلانیہ تبدیلی کرتے ہیں ( چادر) ٹیمپلیٹ، بغیر سٹرنگ متبادل کے یا sed اسکرپٹس جو YAML میں ہیرا پھیری کرتی ہیں۔

کسٹمائز بلٹ ان ہے۔ kubectl (بس -k پرچم)، لہذا انسٹال کرنے کے لیے کوئی اضافی چیز نہیں ہے۔

بنانا k8s/base/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: preview-app
  labels:
    app: preview-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: preview-app
  template:
    metadata:
      labels:
        app: preview-app
    spec:
      containers:
        - name: preview-app
          # This is a placeholder. The overlay replaces it with the
          # real registry path and PR-specific tag.
          image: preview-app
          ports:
            - containerPort: 8080
          env:
            # Downward API: the pod learns its own identity
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 2
            periodSeconds: 5
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "200m"
              memory: "128Mi"

بنانا k8s/base/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: preview-app
spec:
  selector:
    app: preview-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: ClusterIP

بنانا k8s/base/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

یہاں جان بوجھ کر انتخاب کیا گیا ہے۔

replicas: 1: پیداوار میں، ہم دستیابی کے لیے متعدد نقلیں چلاتے ہیں۔ پیش نظارہ ماحول کو اعلی دستیابی کی ضرورت نہیں ہے۔ نوڈ کی گنتی اور بلوں کو صحت مند رکھنے کے لیے تمام کھلے PRs میں فی ماحول میں ایک نقل کو ضرب دیں۔

image: preview-app: یہ ایک پلیس ہولڈر ہے جس میں کوئی رجسٹری یا ٹیگ نہیں ہے۔ آپ اسے اس طرح کبھی نہیں کھینچیں گے۔ Kustomize کا امیج کنورٹر اس نام سے میل کھاتا ہے اور اسے عالمی سطح پر بدل دیتا ہے، اس لیے پہلے سے طے شدہ مینی فیسٹ کو رجسٹری کا URL جاننے کی ضرورت نہیں ہے۔

نیچے کی طرف API env بلاک: یہ یہیں ہے۔ POD_NAMESPACE اور POD_NAME میں سے ہوں۔ fieldRef رن ٹائم پر، یہ پوڈ کی اپنی تصریح سے فیلڈز پڑھتا ہے اور انہیں ماحولیاتی متغیرات کے طور پر بے نقاب کرتا ہے۔ کسی API کال، اسناد، یا سروس اکاؤنٹس کی ضرورت نہیں ہے۔

type: ClusterIP: صرف اندرونی استعمال کے لیے۔ ہر ایک LoadBalancer سروس ایک حقیقی گوگل کلاؤڈ لوڈ بیلنسر فراہم کرتی ہے جس میں فی گھنٹہ لاگت اور عوامی IPs شامل ہیں۔ PR بہ PR کی بنیاد پر ایسا کرنا مہنگا اور فضول ہے۔ ہم ایپ تک رسائی بذریعہ کریں گے: kubectl port-forward اب مرحلہ 13 میں، ایک مشترکہ انگریس کنٹرولر ایک لوڈ بیلنسر کے ذریعے تمام ماحول کو سنبھالتا ہے اور کام کرتا ہے۔

وسائل کی درخواستیں اور حدود: requests گنجائش کو محفوظ رکھیں تاکہ شیڈیولر کو معلوم ہو کہ پوڈز کو کتنی جگہ درکار ہے۔ limits محدود کریں کہ آپ کتنا استعمال کرسکتے ہیں۔ بغیر کسی حد کے، ایک بھاگا ہوا پیش نظارہ ماحول نوڈ پر موجود دیگر تمام ماحول کو ٹوٹنے کا سبب بن سکتا ہے۔ یہ کوئی آپشن نہیں ہے کیونکہ بہت سے قریب المدت ماحول ابھرتے رہتے ہیں۔

اپنے بنیادی رینڈرز کو صحیح طریقے سے جانچیں۔

kubectl kustomize k8s/base

پیش کردہ YAML کو لاگو کیے بغیر پرنٹ کریں۔ یہ ڈیبگنگ کے لیے مفید ہے۔

مرحلہ 9: اسپن اپ ورک فلو بنائیں

اب ہم سب کچھ جوڑتے ہیں۔

بنانا .github/workflows/preview-deploy.yml:

name: Deploy PR Preview

on:
  pull_request:
    types: [opened, synchronize, reopened]

# If someone pushes three commits quickly, cancel the older runs.
# Without this, three deploys race each other into the same namespace
# and whichever finishes last wins — which may not be the newest commit.
concurrency:
  group: preview-${{ github.event.pull_request.number }}
  cancel-in-progress: true

permissions:
  contents: read
  id-token: write   # REQUIRED for Workload Identity Federation
  pull-requests: write

env:
  PROJECT_ID: YOUR_PROJECT_ID
  REGION: europe-west2
  CLUSTER_NAME: pr-preview-cluster
  CLUSTER_ZONE: europe-west2-a
  REPOSITORY: pr-preview
  IMAGE_NAME: preview-app

jobs:
  deploy:
    name: Build and deploy preview
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set environment variables
        run: |
          PR_NUMBER=${{ github.event.pull_request.number }}
          SHORT_SHA=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-7)

          echo "PR_NUMBER=${PR_NUMBER}"   >> $GITHUB_ENV
          echo "SHORT_SHA=${SHORT_SHA}"   >> $GITHUB_ENV
          echo "NAMESPACE=pr-${PR_NUMBER}" >> $GITHUB_ENV
          echo "IMAGE_TAG=pr-${PR_NUMBER}-${SHORT_SHA}" >> $GITHUB_ENV
          echo "IMAGE_BASE=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_ENV

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

      - name: Set up gcloud
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker for Artifact Registry
        run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev --quiet

      - name: Build and push image
        run: |
          docker build 
            --build-arg PR_NUMBER="${PR_NUMBER}" 
            --build-arg GIT_SHA="${SHORT_SHA}" 
            -t "${IMAGE_BASE}:${IMAGE_TAG}" 
            ./app

          docker push "${IMAGE_BASE}:${IMAGE_TAG}"

      - name: Get GKE credentials
        uses: google-github-actions/get-gke-credentials@v2
        with:
          cluster_name: ${{ env.CLUSTER_NAME }}
          location: ${{ env.CLUSTER_ZONE }}

      - name: Create namespace
        run: |
          # --dry-run=client | apply makes this idempotent.
          # Running it on an existing namespace is a no-op, not an error.
          kubectl create namespace "${NAMESPACE}" 
            --dry-run=client -o yaml | kubectl apply -f -

      - name: Generate Kustomize overlay
        run: |
          mkdir -p k8s/overlays/"${NAMESPACE}"
          cat > k8s/overlays/"${NAMESPACE}"/kustomization.yaml <

دو راز شامل کریں۔

GitHub پر مخزن پر جائیں۔ ترتیب، راز اور متغیرات، عملاور نیا ذخیرہ راز. شامل کریں:

خفیہ نام قدر
WIF_PROVIDER کہ workload_identity_provider Terraform سے آؤٹ پٹ
WIF_SERVICE_ACCOUNT کہ service_account_email Terraform سے آؤٹ پٹ

GitHub مخزن کے خفیہ صفحہ کی فہرست WIF_PROVIDER اور WIF_SERVICE_ACCOUNT

نہ ہی کوئی سند ہے۔ فراہم کنندہ کا راستہ اور سروس اکاؤنٹ ای میل شناخت کنندہ ہیں، پاس ورڈ نہیں ہیں۔ وہ خود سے کچھ نہیں دیتے ہیں، کیونکہ OIDC ٹوکن اور انتساب کی شرائط درحقیقت رسائی کو کنٹرول کرتی ہیں۔ اسے راز کے طور پر محفوظ کرنا بالکل صاف ہے۔ اپ ڈیٹ PROJECT_ID ورک فلو میں مقامی اقدار env: اپنی ترتیبات کے مطابق بلاک کریں۔

ورک فلو کو سمجھیں۔

on: pull_request: types: [opened, synchronize, reopened]: یہ تین الگ الگ محرکات ہیں۔

  • opened چلتا ہے جب PR پہلی بار بنتا ہے۔

  • synchronize یہ آپ کے ماحول کو تازہ ترین رکھنے کے لیے مستقبل کی شاخوں میں ہر دھکے کے ساتھ چلتا ہے۔

  • reopened ہینڈلز بند اور دوبارہ کھولے گئے PRs۔ اس کے بغیر، PR کا ماحول تباہ ہو جائے گا اور اسے دوبارہ تعمیر کرنے کے لیے کچھ نہیں ہوگا۔

concurrency: اصل ڈیبگنگ کا وقت بچاتا ہے۔ یکے بعد دیگرے تین کمٹ کو آگے بڑھانے سے ایک ہی نام کی جگہ کو نشانہ بناتے ہوئے تین ورک فلو چلیں گے۔ وہ دوڑتے ہیں۔ آخری مکمل شدہ کوڈ جیت جاتا ہے، اور ضروری نہیں کہ یہ سب سے حالیہ کوڈ ہو۔ ایک تیز ابتدائی تعمیر آہستہ آہستہ بعد کی تعمیر کو پکڑ سکتی ہے، لہذا پیش نظارہ کا تجربہ خود بخود پرانا کوڈ چلا جائے گا جب کہ PR سبز ہے۔ PR نمبر کے لحاظ سے گروپ کریں۔ cancel-in-progress: true اس کا مطلب ہے کہ صرف تازہ ترین پھانسی رکھی گئی ہے۔

permissions: id-token: write: اس لائن کے بغیر اور کچھ کام نہیں کرے گا۔ یہ آپ کے ورک فلو کو GitHub سے OIDC ٹوکن کی درخواست کرنے کی اجازت دیتا ہے۔ اسے چھوڑ دو google-github-actions/auth یہ ایک غلطی کے ساتھ ناکام ہو جاتا ہے کہ ID ٹوکن حاصل نہیں کیا جا سکا۔ یہ سب سے عام WIF سیٹ اپ کی غلطی ہے۔

$GITHUB_ENV: تحریر KEY=value یہ فائل آپ کو کسی بھی متغیر میں متغیر استعمال کرنے کی اجازت دیتی ہے۔ فالو اپ قدم یہ فی الحال متغیر نہیں ہے۔ ایک قدم میں لکھے گئے تغیرات کو بعد میں ایک ہی قدم میں نہیں پڑھا جا سکتا۔ یہی وجہ ہے کہ سیٹ اپ سب سے اوپر اپنے قدم میں کیا جاتا ہے۔

github.event.pull_request.head.sha: اس سے زیادہ اس پر توجہ دیں۔ github.sha. کو pull_request واقعات، GitHub تصدیق کرتا ہے: عہد کو ضم کریں۔. یہ ایک عارضی عہد ہے جو آپ کی برانچ کو مین برانچ کے ساتھ جوائن کرتا ہے۔ github.sha انضمام کی کمٹ کا حوالہ دیتا ہے جو ذخیرہ کی تاریخ میں موجود نہیں ہے اور اسے تلاش کرنے کی کوشش کرنے والے کسی کو الجھن میں ڈال سکتا ہے۔ head.sha یہ PR برانچ پر اصل آخری کمٹ ہے جس کی ڈویلپر کو توقع ہے۔

--dry-run=client -o yaml | kubectl apply -f - یہ چوری کے قابل ایک چھوٹی سی چال ہے۔ kubectl create namespace اگر نام کی جگہ پہلے سے موجود ہے، تو یہ ناکام ہو جائے گی اور پہلے کے بعد تمام دھکے ختم کر دیے جائیں گے۔ یہ پائپ لائن ایک نام کی جگہ YAML تیار کرتی ہے اور پھر اسے کلسٹر سے منسلک کیے بغیر ایک ساتھ پائپ کرتی ہے۔applyاگر یہ غائب ہے تو اسے بنائیں، ورنہ کچھ نہ کریں۔ ساخت کے لحاظ سے بے حس۔

heredoc اوورلے: اوورلے فی PR کرنے کے بجائے (جو ناممکن ہے کیونکہ PR نمبر پہلے سے معلوم نہیں ہے)، ورک فلو رن ٹائم پر اوورلے بناتا ہے اور رنر کے حذف ہونے پر اسے رد کر دیتا ہے۔ یہ ایک عارضی ماحول کے لیے ایک عارضی ترتیب ہے۔

kubectl rollout status یہ ورک فلو کو ایماندار بنانے کے بارے میں ہے۔ اس کے بغیر اس وقت کام کامیاب ہو جاتا ہے۔ kubectl apply API سرور پر ایک پوڈ شروع ہونے سے پہلے ہونے کی اجازت ہے۔ تنازعات کی دہرائی جانے والی تصاویر اب بھی سبز چیک مارک دکھاتی ہیں۔ rollout status پوڈ یا تو اس وقت تک بلاک ہو جاتا ہے جب تک کہ یہ حقیقت میں تیار نہ ہو جائے یا 180 سیکنڈ کے بعد ناکام ہو جائے۔ سبز کا مطلب ہے کام کرنا۔

فورک پل کی درخواستوں پر نوٹ کریں۔

اسے عوامی طور پر تعینات کرنے سے پہلے، کچھ جاننے کے قابل ہے: کانٹے سے کھینچنے کی درخواست ~ نہیں وصول کریں id-token: write اجازت، جان بوجھ کر۔ GitHub اجنبیوں کو PRs کھولنے سے روکتا ہے جو آپ کی کلاؤڈ اسناد چوری کرتے ہیں۔

نجی ذخیروں یا ذاتی منصوبوں کے لیے، اس کا کوئی اثر نہیں ہے۔ عوامی ذخیروں کے لیے جو بیرونی تعاون کی اجازت دیتے ہیں۔ workflow_runبنیادی نمونہ وہ ہے جہاں ایک مراعات یافتہ ورک فلو ایک ناقابل اعتماد تعمیر مکمل ہونے کے بعد بنیادی ذخیرہ پر چلتا ہے۔ یہ اس ہینڈ بک کے دائرہ کار سے باہر ہے، لیکن اگر آپ اوپن سورس پروجیکٹ پر تعینات کر رہے ہیں تو اسے نہ چھوڑیں۔

مرحلہ 10: ڈیکمپوزیشن ورک فلو بنائیں

اسپن اپ اس کی صرف آدھی قدر ہے۔ ایک ایسا ماحول جو کبھی دور نہیں ہوتا ہے صرف آپ کے بادل کے بلوں کا ایک سست رساو ہے۔

بنانا .github/workflows/preview-teardown.yml:

name: Tear Down PR Preview

on:
  pull_request:
    types: [closed]

permissions:
  contents: read
  id-token: write

env:
  PROJECT_ID: YOUR_PROJECT_ID
  CLUSTER_NAME: pr-preview-cluster
  CLUSTER_ZONE: europe-west2-a

jobs:
  teardown:
    name: Delete preview namespace
    runs-on: ubuntu-latest

    steps:
      - name: Set namespace
        run: echo "NAMESPACE=pr-${{ github.event.pull_request.number }}" >> $GITHUB_ENV

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

      - name: Get GKE credentials
        uses: google-github-actions/get-gke-credentials@v2
        with:
          cluster_name: ${{ env.CLUSTER_NAME }}
          location: ${{ env.CLUSTER_ZONE }}

      - name: Delete namespace
        run: |
          echo "Deleting namespace ${NAMESPACE}"
          kubectl delete namespace "${NAMESPACE}" 
            --ignore-not-found=true 
            --wait=false

      - name: Confirm
        run: |
          echo "Preview environment for PR #${{ github.event.pull_request.number }} torn down."

یہاں کچھ تفصیلات آپ کے خیال سے کہیں زیادہ اہم ہیں۔

سب سے پہلے types: [closed] کور بھی مل جاتے ہیں۔ GitHub پر کوئی الگ کوڈ نہیں ہے۔ merged واقعہ کی قسم۔ پل کی درخواست انضمام چلتی ہے۔ closed پے لوڈ فیلڈز پر مشتمل ہے۔ merged: true. لہذا یہ واحد ٹرگر "مرج کامیاب” اور "ڈسکارڈ اینڈ ٹرمینیٹ” دونوں کو ہینڈل کرتا ہے۔ دونوں میں فرق کرنے کی ضرورت نہیں ہے کیونکہ دونوں کو ماحول کی تباہی کی ضرورت ہے۔

اگلا، --ignore-not-found=true: اگر نام کی جگہ موجود نہیں ہے (تعیناتی ناکام ہوگئی یا کسی نے اسے دستی طور پر حذف کردیا) kubectl delete یہ عام طور پر ایک خرابی کے ساتھ ختم ہوتا ہے اور ورک فلو کو سرخ رنگ میں نشان زد کیا جاتا ہے۔ یہ ایک غلط الارم ہے۔ مطلوبہ آخری حالت "نام کی جگہ موجود نہیں ہے” ہے، جو موجود نہیں ہے۔ یہ جھنڈا اس وقت کامیابی کا باعث بنتا ہے جب کوئی کام نہ ہو۔

آخر کار --wait=false: نام کی جگہ کو حذف کرنا غیر مطابقت پذیر ہے۔ Kubernetes اسے دکھاتا ہے۔Terminatingاس کے اندر موجود تمام وسائل کو حذف کرنے کے لیے ایک عمل انجام دیتا ہے۔ اس کے مکمل ہونے کا انتظار کرنے سے CI ایگزیکیٹر کو ایک منٹ یا اس سے زیادہ تاخیر ہو سکتی ہے، لیکن کوئی فائدہ نہیں ہوتا۔ ڈیلیٹ کو چلائیں اور کلسٹر کو اپنے وقت میں مکمل ہونے دیں۔

مرحلہ 11: پل کی درخواست کھولیں اور اسے عمل میں دیکھیں

عہد کریں اور ہر چیز کو آگے بڑھائیں۔

git add .
git commit -m "Add PR preview environment infrastructure"
git push -u origin main

اب ایک برانچ بنائیں اور کوئی قابل توجہ تبدیلیاں کریں۔

git checkout -b test-preview

ترمیم کریں app/main.go. ہینڈلر آؤٹ پٹ کی پہلی لائن کو تبدیل کریں:

fmt.Fprintf(w, "Preview environment is live — testing the pipelinenn")

کمٹ اور پش:

git add .
git commit -m "Test preview environment"
git push -u origin test-preview

GitHub پر ایک پل کی درخواست کھولیں اور جائیں: عمل ٹیگ آپ دیکھیں گے کہ "PR پیش نظارہ تعیناتی” چل رہی ہے۔

پل کی درخواست کے لیے GitHub ایکشنز کا خلاصہ یہ ظاہر کرتا ہے کہ تعیناتی PR پیش نظارہ ورک فلو کامیابی سے مکمل ہو گیا ہے۔

ترتیب میں مراحل سے گزریں۔ تصدیق کلید کے بغیر کامیاب ہو جاتی ہے، امیج کو بناتا اور آگے بڑھاتا ہے، نام کی جگہ بناتا ہے، کسٹمائز اوورلے کو رینڈر کرتا ہے (مکمل طور پر تصدیق شدہ مینی فیسٹ پرنٹ ہوتا ہے)، تعیناتی کا اطلاق ہوتا ہے، اور rollout status پوڈ تیار ہونے تک انتظار کریں۔

توسیع شدہ GitHub ایکشن لاگ جس میں تعیناتی کی تفصیلات کے مراحل کو دکھایا گیا ہے، رول آؤٹ کا انتظار کیا گیا ہے، اور کامیابی سے مکمل کیا گیا ہے۔

جب یہ سبز ہو جائے تو اسے ٹرمینل میں چیک کریں۔

kubectl get namespaces | grep pr-

تم اپنے ہو pr-1 (یا جو بھی نمبر آپ کے PR کو ملا)۔

kubectl get pods --namespace=pr-1

اب درخواست تک رسائی حاصل کریں۔

kubectl port-forward --namespace=pr-1 service/preview-app 8080:80

دوسرے ٹرمینل میں:

curl http://localhost:8080

آپ کو درج ذیل کو چیک کرنا چاہئے:

Preview environment is live — testing the pipeline

PR number  : 1
Git SHA    : a1b2c3d
Namespace  : pr-1
Pod name   : preview-app-7d9f8b6c4-xk2mp

ایپلیکیشن PR نمبر کی اطلاع دیتی ہے اور SHA کو تعمیر کے وقت بائنری میں بیک کیا جاتا ہے، اور اس نے نیچے کی طرف API کے ذریعے دریافت کیے گئے نام کی جگہیں۔ ان تمام اقدار کو پائپ لائن کے مختلف حصوں نے حل کیا ہے اور سبھی ان پر متفق ہیں۔ یہ اس بات کا ثبوت ہے کہ پوری زنجیر کام کرتی ہے۔

کرل سے ٹرمینل آؤٹ پٹ پیش نظارہ ماحول کی رپورٹنگ PR نمبر 1، کمٹ SHA، نام کی جگہ pr-1، اور Pod کا نام دکھا رہا ہے۔

اب اسی برانچ میں ایک اور کمٹ کو دبائیں اور دیکھیں کہ آیا آپ کا ماحول ٹھیک سے اپ ڈیٹ ہوتا ہے۔

GitHub ایکشن لاگ جس میں ایک دوسری کمٹ کو اسی برانچ میں دھکیلنے کے بعد دوبارہ چلتے ہوئے PR پیش نظارہ ورک فلو کو دکھایا گیا ہے۔

ٹرمینل جس میں kubectl پورٹ فارورڈنگ کو namespace pr-1 کے لیے چل رہا ہے اور نام کی جگہ تک پہنچنے کے لیے استعمال ہونے والی curl کمانڈ دکھا رہا ہے۔

دوسرے پش کے بعد، ہم تصدیق کرتے ہیں کہ ماحول کو اپنی جگہ پر کرل کے ٹرمینل آؤٹ پٹ کے ساتھ دوبارہ تعینات کر دیا گیا ہے جس میں گریٹنگ کا تازہ ترین متن اور نیا کمٹ SHA دکھایا گیا ہے۔

پھر پل کی درخواست کو بند کریں اور ٹیر ڈاؤن ورک فلو کو چلتے ہوئے دیکھیں۔

GitHub ایکشن سڑنا ورک فلو متحرک اور جاری ہے۔

GitHub ایکشن سڑنے والے ورک فلو کو مکمل کریں۔

kubectl get namespace pr-1

Terminatingاور پھر غائب ہو گیا۔

ٹرمینل آؤٹ پٹ دکھا رہا ہے kubectl get namespace ریٹرن

آپ کے پاس کیا ہے اور آپ کے پاس اب بھی کیا کمی ہے۔

بنیادی ڈھانچے کا مشکل کام ہو چکا ہے، اس لیے رکیں اور اسٹاک لیں۔ پل کی درخواستیں اب بغیر کسی دستی اقدامات کے واقعی الگ تھلگ ماحول بناتی ہیں، اور انہیں بند کرنے سے تمام نشانات ختم ہو جاتے ہیں۔ یہ پورا خودکار لائف سائیکل ہے۔

لیکن دوبارہ دیکھیں کہ ہم وہاں کیسے پہنچے۔

kubectl port-forward --namespace=pr-1 service/preview-app 8080:80

یہ ضروری ہے kubectl انسٹالیشن، کلسٹر کے لیے اسناد، اسے استعمال کرنے کی اجازت، اور اس بات کا علم کہ کن نام کی جگہوں کو کن پی آرز میں میپ کیا گیا ہے۔ یہ اس شخص کے لیے ٹھیک ہے جس نے پائپ لائن بنائی، لیکن ڈیزائنر کے لیے جو وقفہ کی تبدیلیوں کی جانچ کر رہا ہے، پروڈکٹ مینیجر کاپی کی ترامیم کا جائزہ لے رہا ہے، یا QA انجینئر اس کی منظوری دینے سے پہلے بہاؤ پر کلک کر رہا ہے۔

پیش نظارہ کے تجربے کی جھلکیاں یہ ہیں: جو بھی آپ تبدیلیوں کو دیکھ سکتے ہیں۔ ایک ایسا ماحول جس تک صرف انجینئر ہی رسائی حاصل کر سکتے ہیں حقیقت میں پیش نظارہ ماحول نہیں ہے۔ یہ ڈیبگنگ ٹول ہے۔

اس ٹیوٹوریل کا بقیہ حصہ اس خلا کو پُر کرے گا اور نتائج کو محفوظ طریقے سے چلائے گا۔ سب سے پہلے، تمام ماحول کے لیے عوامی HTTPS URLs ترتیب دیں۔ اس کے بعد ہم گارڈریلز بنانے اور صفائی کی طرف بڑھتے ہیں جو اسے آپ کے کلاؤڈ بل پر ایک پرسکون چیز بننے سے روکتا ہے۔

مرحلہ 12: ایک مستحکم IP ایڈریس محفوظ کریں۔

کچھ بھی انسٹال کرنے سے پہلے، آپ کو ایک بیرونی IP ایڈریس ریزرو کرنے کی ضرورت ہے۔

یہ اہم ہے کیونکہ Ingress کنٹرولر کو انسٹال کرنے سے درج ذیل قسم کی Kubernetes سروسز بنتی ہیں: LoadBalancerگوگل کلاؤڈ عوامی IP کے ساتھ فزیکل لوڈ بیلنس فراہم کرتا ہے۔ بنیادی طور پر آئی پی ہے۔ عارضی. اگر کسی سروس کو حذف کرکے دوبارہ بنایا جاتا ہے، تو آپ کو ایک مختلف سروس موصول ہوگی۔

وائلڈ کارڈ DNS ریکارڈ اس IP کی طرف اشارہ کرتے ہیں۔ اگر یہ بدل جاتا ہے، تو پیش نظارہ کے تمام تجربات ایک ہی وقت میں ٹوٹ جائیں گے، اور آپ کو اس وقت تک معلوم نہیں ہوگا جب تک کہ کوئی آپ کو یہ نہ کہے کہ لنک ٹوٹ گیا ہے۔ پہلے کسی IP کو محفوظ کرنے کا مطلب یہ ہے کہ IP آپ کے پروجیکٹ سے تعلق رکھتا ہے، لوڈ بیلنسر کا نہیں، اور آپ کلسٹر پر جو بھی آپریشن انجام دیتے ہیں وہ زندہ رہے گا۔

اس میں شامل کریں terraform/main.tf:

# -----------------------------------------------------------
# Static external IP for the Ingress controller
# -----------------------------------------------------------
resource "google_compute_address" "ingress" {
  name         = "pr-preview-ingress-ip"
  region       = var.region
  address_type = "EXTERNAL"

  depends_on = [google_project_service.apis]
}

اور آؤٹ پٹ terraform/outputs.tf:

output "ingress_ip" {
  description = "Point your wildcard DNS record at this address"
  value       = google_compute_address.ingress.address
}

آپ بھی چاہتے ہیں compute.googleapis.com اگر آپ کی API فہرست پہلے سے موجود نہیں ہے تو اسے اپ ڈیٹ کریں۔ for_each آپ کا google_project_service کرے گا:

  for_each = toset([
    "container.googleapis.com",
    "artifactregistry.googleapis.com",
    "iamcredentials.googleapis.com",
    "sts.googleapis.com",
    "compute.googleapis.com",
  ])

درخواست دیں:

cd terraform
terraform apply 
  -var="project_id=YOUR_PROJECT_ID" 
  -var="github_repo=YOUR_USERNAME/pr-preview-envs"

واپس آنے والے آئی پی کو نوٹ کریں۔

terraform output ingress_ip

اسے لکھ دیں۔ آپ اسے اگلے دو مراحل میں دو بار استعمال کریں گے۔

Terraform آؤٹ پٹ ingress_ip مخصوص پتے دکھا رہا ہے۔

اہم: یہ ہونا چاہیے۔ علاقہ کلسٹر والے علاقے میں ایک پتہ۔ region = var.region آپ کو فراہم کی. گوگل کلاؤڈ عالمی پتے بھی فراہم کرتا ہے جو عالمی HTTP(S) لوڈ بیلنسرز کے ذریعہ استعمال ہوتے ہیں۔

سروس کی قسم LoadBalancer ingress-nginx جو تخلیق کرتا ہے وہ ایک علاقائی لوڈ بیلنسر ہے اور عالمی پتوں کو پابند نہیں کرسکتا۔ ان کو ملانے سے سروس میں خلل پڑے گا۔ Pending یہ ہمیشہ کے لیے ان غلطیوں کی وجہ سے ہوتا ہے جو واضح طور پر کیوں نہیں بتاتی ہیں۔

مرحلہ 13: Ingress کنٹرولر انسٹال کریں۔

نہیں داخلہ ایک Kubernetes وسیلہ جو HTTP روٹنگ کے قواعد کو بیان کرتا ہے۔ "اس میزبان نام کی درخواستیں اس سروس پر جائیں گی۔” تاہم، Ingress وسائل صرف وضاحتی ہیں۔ کچھ اصل میں قوانین کو پڑھنا ہے اور ٹریفک کو روٹ کرنا ہے۔ وصول کرنے والا کنٹرولر.

ہم استعمال کریں گے وصول کریں-nginxNGINX پر مبنی کمیونٹی کے زیر انتظام کنٹرولر۔ یہ آپ کے کلسٹر میں ایک Pod کے طور پر چلتا ہے، Ingress وسائل کے لیے Kubernetes API کو دیکھتا ہے، اور جب بھی یہ تبدیل ہوتا ہے اس کی اپنی NGINX ترتیب کو دوبارہ لکھتا ہے۔

پیش نظارہ ماحول میں اس کام کو بنانے کے لیے تعمیراتی کلیدی نکات یہ ہیں: ایک کنٹرولر تمام نام کی جگہوں کو ہینڈل کرتا ہے۔. ہر پیش نظارہ ماحول اپنا داخلی وسیلہ بناتا ہے۔ کنٹرولر اسے اٹھاتا ہے، اس میزبان نام کے لیے روٹنگ کا اصول شامل کرتا ہے، اور ٹریفک کو صحیح سروس کی طرف لے جاتا ہے۔ کنٹرولر کو پرواہ نہیں ہے کہ کتنے ماحول موجود ہیں۔

ہم جو کرنے جا رہے ہیں اس سے اس کا موازنہ کریں۔ ہر پیش نظارہ ماحول میں LoadBalancer ایک سروس کے طور پر، ہر PR اپنا گوگل کلاؤڈ لوڈ بیلنس فراہم کرتا ہے جس کا بل گھنٹے کے حساب سے ہوتا ہے اور اپنا IP پتہ فراہم کرتا ہے، جسے DNS کو جاننے کی ضرورت ہے۔ 10 پبلک پی آر کا مطلب ہے 10 لوڈ بیلنسرز۔ مشترکہ انگریس کنٹرولر کے ساتھ، 10 عوامی PRs کا مطلب ہے 1۔

ہیلم کا استعمال کرتے ہوئے انسٹال کریں۔

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx 
  --namespace ingress-nginx 
  --create-namespace 
  --set controller.service.loadBalancerIP="YOUR_STATIC_IP" 
  --set controller.service.externalTrafficPolicy=Local

تبدیلی YOUR_STATIC_IP براہ کرم مرحلہ 12 میں پتہ استعمال کریں۔

اس سے پہلے کہ ہم جاری رکھیں، آئیے جھنڈوں کا تجزیہ کریں۔

--namespace ingress-nginx --create-namespace: کنٹرولرز کا اپنا نام کی جگہ ہے، پیش نظارہ ماحول سے الگ۔ یہ ایک مشترکہ بنیادی ڈھانچہ ہے، لہذا آپ کو اپنے پیش نظارہ ماحول کے پھٹنے میں نہیں پھنسنا چاہیے۔

controller.service.loadBalancerIP: GKE کو ہدایت کرتا ہے کہ لوڈ بیلنسر کو ایک نیا پتہ تفویض کرنے کے بجائے، آپ کے محفوظ کردہ ایک مخصوص پتے سے منسلک کرے۔ یہ وہ لائن ہے جو آپ کے DNS ریکارڈز کو قابل اعتماد بناتی ہے۔

controller.service.externalTrafficPolicy=Local: کلائنٹ کے اصل سورس آئی پی کو محفوظ رکھتا ہے۔ اس کے بغیر، Kubernetes درخواستوں کو انٹرمیڈیٹ نوڈس کے ذریعے روٹ کرے گا، اور جب درخواست NGINX تک پہنچ جائے گی، تو سورس IP کو نوڈ کے اندرونی ایڈریس پر دوبارہ لکھا جائے گا۔ رسائی لاگز اصل وزٹرز کے بجائے کلسٹر آئی پی دکھاتے ہیں۔

منفی پہلو یہ ہے کہ ٹریفک صرف ان نوڈس تک پہنچتی ہے جو دراصل کنٹرولر پوڈ چلاتے ہیں۔ یہاں ٹھیک ہے۔

ہیلم کی تنصیب سے ٹرمینل آؤٹ پٹ ingress-nginx کا ایک چارٹ دکھاتا ہے جو ingress-nginx نام کی جگہ پر کامیابی کے ساتھ تعینات کیا گیا ہے۔

تفویض کرنے کے لیے بیرونی IP چیک کریں۔

kubectl get service ingress-nginx-controller 
  --namespace ingress-nginx 
  --watch

یہ ظاہر کرتا ہے اس کے بعد یہ ایک یا دو منٹ کے لیے سٹیک آئی پی کو مرحلہ 12 سے ظاہر کرے گا جب کہ گوگل لوڈ بیلنس کی فراہمی کرتا ہے۔ جب یہ ظاہر ہو جائے تو Ctrl+C دبائیں۔

چیک کریں کہ آیا کنٹرولر نارمل ہے۔

kubectl get pods --namespace ingress-nginx

kubectl get service اور kubectl get pod سے آؤٹ پٹ ingress-nginx کنٹرولر کو دکھا رہا ہے جو مخصوص سٹیٹک بیرونی IP تفویض کے ساتھ چل رہا ہے۔

مرحلہ 14: لوڈ بیلنسر پر وائلڈ کارڈ ڈی این ایس کی وضاحت کریں۔

اس قدم میں تقریباً 2 منٹ لگتے ہیں اور یہ مجموعی پیٹرن اسکیل بنانے کا حصہ ہے۔

جہاں بھی آپ کے ڈومین کے DNS کا نظم ہو وہاں جائیں اور ایک DNS بنائیں۔ ریکارڈ:

میدان قدر
زمرہ A
نام/میزبان *.preview
قدر مرحلہ 1 میں جامد IP
ٹی ٹی ایل 300 (یا سپلائر کی طرف سے قابل قبول سب سے کم قیمت)

بس۔ یہ پروجیکٹ کے لیے مجموعی DNS کنفیگریشن ہے۔

DNS فراہم کنندہ انٹرفیس ایک وائلڈ کارڈ دکھا رہا ہے *. پیش نظارہ کے لیے ایک ریکارڈ جو اندراج لوڈ بیلنسر کے جامد IP ایڈریس کی طرف اشارہ کرتا ہے۔

یہاں ایک ریکارڈ کیوں کافی ہے۔ وائلڈ کارڈ DNS ریکارڈز مماثل ہیں۔ کوئی بھی اس مقام پر لیبل لگائیں۔ *.preview.yourdomain.com حل pr-1.preview.yourdomain.com، pr-42.preview.yourdomain.comاور pr-99999.preview.yourdomain.comوہ سب ایک ہی IP سے جڑتے ہیں، اور کوئی بھی ریکارڈ نہیں بناتا ہے۔

یہ ایک لمحے کے لیے توقف کے قابل ہے کیونکہ یہ ایک ڈیزائن کا فیصلہ ہے جو سسٹم کو سادہ رکھتا ہے۔ واضح طریقہ یہ ہے کہ PR کھولنے پر آپ کی CI پائپ لائن DNS ریکارڈ بنائے اور PR بند ہونے پر اسے حذف کر دیں۔ اس کا مطلب ہے ایک اور ناکامی، ایک اور لیک، اور آپ کی پائپ لائن کے DNS API اسناد کے یتیم وسائل کا ایک اور ذریعہ۔

وائلڈ کارڈ کا نقطہ نظر پورے کام کے زمرے کو ہٹا دیتا ہے۔ اس کا مطلب یہ ہے کہ DNS دستی طور پر ایک بار ترتیب دیا گیا ہے اور دوبارہ کبھی نہیں چھوا ہے۔

DNS میں درست ماحول کی طرف روٹنگ نہیں ہوتی ہے۔ تمام میزبان نام ایک ہی لوڈ بیلنسر اور Ingress کنٹرولر کو حل کرتے ہیں۔ Host ہر آنے والی درخواست کے ہیڈرز کو اس بات کا تعین کرنے کے لیے چیک کیا جاتا ہے کہ کس نام کی جگہ کی سروس اسے ہینڈل کرے۔

تصدیق کریں کہ ریکارڈ پروپیگنڈہ ہوا ہے۔

dig +short anything-at-all.preview.yourdomain.com

آپ کو اپنا جامد IP واپس حاصل کرنے کی ضرورت ہوگی۔ ذیلی ڈومین کو ابھی موجود ہونے کی ضرورت نہیں ہے۔ یہ وائلڈ کارڈز کا نقطہ ہے۔

کھودنے سے ٹرمینل آؤٹ پٹ، واضح طور پر بنائے گئے ذیلی ڈومینز کے لیے جامد IP آنے والی واپسی، وائلڈ کارڈ ریکارڈز کی جانچ کریں۔

اگر کچھ ظاہر نہیں ہوتا ہے، تو چند منٹ انتظار کریں اور دوبارہ کوشش کریں۔ ڈی این ایس کا پھیلاؤ عام طور پر تیز ہوتا ہے جب TTL کم ہوتا ہے، لیکن فوری نہیں ہوتا۔

مرحلہ 15: cert-manager انسٹال کریں اور ClusterIssuer بنائیں

اب HTTPS کے لیے۔

سرٹیفکیٹ مینیجر ایک Kubernetes کنٹرولر جو پورے سرٹیفکیٹ لائف سائیکل کو خودکار کرتا ہے۔ جاری کنندہ کے ساتھ تشریح کردہ Ingress وسائل کے لیے دیکھیں، سرٹیفکیٹ اتھارٹی سے سرٹیفکیٹ کی درخواست کریں، ڈومین کا کنٹرول ثابت کریں، سرٹیفکیٹ کو Kubernetes سیکرٹ میں اسٹور کریں، اور اس کی میعاد ختم ہونے سے پہلے اس کی تجدید کریں۔ صبح 3 بجے کوئی کرون جابز، دستی تجدید یا سرٹیفکیٹ کی میعاد ختم ہونے کے مسائل نہیں ہیں۔

براہ کرم انسٹال کریں:

helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install cert-manager jetstack/cert-manager 
  --namespace cert-manager 
  --create-namespace 
  --version v1.21.2 
  --set crds.enabled=true

--set crds.enabled=true cert-manager کے لیے حسب ضرورت وسائل کی تعریفیں انسٹال کریں۔ Certificate، Issuer، ClusterIssuer، CertificateRequest اور دیگر۔ یہ نئے وسائل کی اقسام کے ساتھ Kubernetes API کو بڑھاتا ہے۔ ان کے بغیر، سرٹیفکیٹ مینیجر کے پاس نگرانی کے لیے کچھ نہیں ہے۔ (اگر آپ استعمال کرتے ہوئے پچھلے ٹیوٹوریل کی پیروی کرتے ہیں۔ installCRDs=trueاس جھنڈے کا نام v1.15 میں بدل دیا گیا۔)

یقینی بنائیں کہ یہ چل رہا ہے۔

kubectl get pods --namespace cert-manager

آپ کو تین پوڈ نظر آئیں گے: کنٹرولر، ویب ہک، اور کینجیکٹر۔

کیوبیکٹل گیٹ پوڈ سے آؤٹ پٹ سرٹ مینیجر کنٹرولر، ویب ہک، اور کینجیکٹر پوڈز کو چلاتے ہوئے دکھا رہا ہے۔

ClusterIssuer بنائیں

نہیں جاری کرنے والا سرٹیفکیٹ مینیجر کو بتاتا ہے کہ سرٹیفکیٹ کہاں سے حاصل کرنا ہے۔ کوئی راستہ نہیں کلسٹر جاری کنندہ یہ کلسٹر وسیع پیمانے پر بھی درست ہے۔ یہ وہی ہے جس کی ہمیں ضرورت ہے۔ نام کی جگہ والے جاری کنندگان کو ہر ایک پیش نظارہ نام کی جگہ میں دوبارہ تخلیق کیا جانا چاہیے۔

بنانا k8s/cluster/letsencrypt-staging.yaml:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    # Let's Encrypt's staging environment. Certificates from this
    # server are NOT trusted by browsers, but the rate limits are
    # far more generous. Use this while debugging.
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: YOUR_EMAIL@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

اور k8s/cluster/letsencrypt-prod.yaml:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: YOUR_EMAIL@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

مندرجہ ذیل سبھی لاگو ہوتے ہیں:

kubectl apply -f k8s/cluster/letsencrypt-staging.yaml
kubectl apply -f k8s/cluster/letsencrypt-prod.yaml

یقینی بنائیں کہ آپ تیار ہیں۔

kubectl get clusterissuer

دونوں کو نظر آنا چاہیے۔ READY: True.

kubectl get Clusterissuer کا آؤٹ پٹ READY status True کے ساتھ letsencrypt-staging اور letsencrypt-prod دونوں دکھا رہا ہے۔

کیوں دو جاری کرنے والے؟

یہ ڈپلیکیٹ نہیں ہے۔ اس طرح آپ مایوس کن دوپہر سے بچتے ہیں۔

آئیے انکرپٹ پیداواری ماحول میں شرح کی سخت حدود کو نافذ کرتا ہے۔ یہاں اہم بات یہ ہے۔ 50 سرٹیفکیٹس فی رجسٹرڈ ڈومین فی ہفتہ. مزید برآں، میزبان ناموں کے ایک ہی سیٹ کے لیے فی ہفتہ 5 سرٹیفکیٹ ڈپلیکیٹس کی ایک حد ہے۔

ایک اچھا موقع ہے کہ اسے ترتیب دیتے وقت کچھ غلط کنفیگر ہو گیا ہے۔ اندراج کی تشریحات غلط ہیں، HTTP-01 چیلنجز کو صحیح طریقے سے روٹ نہیں کیا گیا ہے، یا DNS کا پروپیگنڈہ نہیں کیا گیا ہے۔ ہر ناکام کوشش سے آپ کا کوٹہ ختم ہو جائے گا، اور ایک بار جب حد پوری ہو جائے گی تو آپ کا اکاؤنٹ ایک ہفتے کے لیے بغیر اپیل کے بلاک کر دیا جائے گا۔

اسٹیجنگ جاری کرنے والوں کی پابندیاں بہت زیادہ نرم ہیں۔ آپ کا براؤزر ایک انتباہ ظاہر کرے گا کیونکہ اس سرٹیفکیٹ پر ایک غیر بھروسہ مند جڑ کے ذریعے دستخط کیے گئے ہیں، لیکن یہ انتباہ ہے۔ یہ اپنے آپ میں اس بات کا ثبوت ہے کہ پائپ لائن کام کر رہی ہے۔. اگر آپ کو ایک سرٹیفکیٹ انتباہ نظر آتا ہے جس میں کہا گیا ہے کہ "چلو انکرپٹ سٹیجنگ”، سب کچھ صحیح طریقے سے کام کر رہا ہے اور صرف ایک ہی قدم باقی رہ گیا ہے کہ جاری کنندگان کو تبدیل کیا جائے۔

استعمال کریں letsencrypt-staging اگلے پر سوئچ کریں جب تک کہ بہاؤ پوری طرح کام نہ کرے۔ letsencrypt-prod.

HTTP-01 چیلنجز کیسے کام کرتے ہیں۔

یہ سمجھنے کے قابل ہے کیونکہ یہ وہ جگہ ہے جہاں چیزیں غلط ہونے پر ٹوٹ جاتی ہیں۔

جب سرٹیفکیٹ مینیجر سرٹیفکیٹ کی درخواست کرتا ہے۔ pr-42.preview.yourdomain.comآئیے انکرپٹ کو اس بات کا ثبوت درکار ہے کہ آپ میزبان نام کو کنٹرول کرتے ہیں۔ HTTP-01 سولور استعمال کرنے کی ترتیب حسب ذیل ہے:

  1. آئیے انکرپٹ سرٹیف مینیجر کو ایک بے ترتیب ٹوکن فراہم کرتا ہے۔

  2. cert-manager ایک عارضی پوڈ، سروس، اور داخل ہونے کا اصول بناتا ہے جو کہ ٹوکن فراہم کرتا ہے۔ http://pr-42.preview.yourdomain.com/.well-known/acme-challenge/.

  3. آئیے Encrypt عوامی انٹرنیٹ پر اس URL کے لیے ایک حقیقی HTTP درخواست کرتا ہے۔

  4. اگر ٹوکنز مماثل ہوں تو ڈومین کا کنٹرول ثابت ہو جاتا ہے اور ایک سرٹیفکیٹ جاری کیا جاتا ہے۔

  5. سرٹیفکیٹ مینیجر عارضی وسائل کو حذف کرتا ہے اور سرٹیفکیٹ کو خفیہ طور پر محفوظ کرتا ہے۔

اس کے کام کرنے کے لیے، تین چیزوں کو پورا کرنے کی ضرورت ہے، اور اگر ایسا نہیں ہوتا ہے، تو یہاں چیک کرنے کے لیے تین چیزیں ہیں: DNS کو لازمی طور پر ہوسٹ ناموں کو لوڈ بیلنسر پر حل کرنا چاہیے، پورٹ 80 کو انٹرنیٹ سے قابل رسائی ہونا چاہیے (ہر چیز کو لوڈ بیلنس کی سطح پر HTTPS پر ری ڈائریکٹ نہ کریں – یہ سادہ HTTP ہے جو مسئلہ ہے)، اور Ingress کنٹرولر کو صحیح طریقے سے روٹ کیا جانا چاہیے۔

پیمانے پر نوٹ کریں۔

HTTP-01 فی میزبان نام ایک سرٹیفکیٹ جاری کرتا ہے، اور ہر پیش نظارہ ماحول کا ایک منفرد میزبان نام ہوتا ہے۔ یہ ایک سرٹیفکیٹ فی پل کی درخواست ہے۔

Let’s Encrypt کی 50 فی ہفتہ کی حد کے مقابلے، یہ سولو پروجیکٹس یا چھوٹی ٹیموں کے لیے بہترین ہے۔ ایک ہفتے میں 50 سے زیادہ PR شروع کرنے والی فعال ٹیمیں اپنی حد کو پہنچ جائیں گی۔

پیداوار کا جواب ہے۔ سنگل وائلڈ کارڈ سرٹیفکیٹ کے لیے *.preview.yourdomain.com کسی بھی نام کی جگہ پر اشتراک کیا جا سکتا ہے۔ وائلڈ کارڈز HTTP-01 کے ذریعے جاری نہیں کیے جا سکتے۔ وہ DNS-01 cert-manager فائل فراہم کرنے کے بجائے TXT ریکارڈ بنا کر کنٹرول کو ثابت کرتا ہے۔

کلاؤڈ DNS سرٹیفک مینیجر کو آپ کے Google سروس اکاؤنٹ کا استعمال کرتے ہوئے خودکار طور پر ایسا کرنے کی اجازت دیتا ہے۔ پھر، ہر پیش نظارہ نام کی جگہ پر نتیجے میں راز کو نقل کرنے کے لیے ریفلیکٹر جیسے ٹول کا استعمال کریں۔

یہ اس ہینڈ بک میں ضرورت سے زیادہ متحرک حصے ہیں، لیکن اگر PR والیوم اس کا جواز پیش کرتا ہے، تو یہ صحیح جواب ہے، اور یہ جاننا ضروری ہے کہ حد آپ تک پہنچنے سے پہلے موجود ہے۔

مرحلہ 16: ڈیفالٹ مینی فیسٹ میں استقبالیہ شامل کریں۔

اب ہم اپنی درخواست کا راستہ فراہم کرتے ہیں۔

بنانا k8s/base/ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: preview-app
  annotations:
    # Tells cert-manager to request a certificate for the hosts
    # listed under spec.tls, using this ClusterIssuer.
    cert-manager.io/cluster-issuer: letsencrypt-staging
    # Redirect HTTP to HTTPS once the certificate exists.
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - preview-host-placeholder
      secretName: preview-app-tls
  rules:
    - host: preview-host-placeholder
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: preview-app
                port:
                  number: 80

اسے اگلی بار شامل کریں۔ k8s/base/kustomization.yaml:

آپ نے اس فائل کو مرحلہ 8 میں دو وسائل کا استعمال کرتے ہوئے بنایا۔ Ingress شامل کریں۔

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml
  - ingress.yaml

اہم حصہ یہ ہے:

cert-manager.io/cluster-issuer: یہ ایک تبصرہ پورے سرٹیفکیٹ ورک فلو کو متحرک کرتا ہے۔ cert-manager اسے دیکھتا ہے اور میزبان نام پڑھتا ہے۔ spec.tlsباقی سب کچھ کرو۔ یہ حوالہ کے لیے مقرر ہے۔ letsencrypt-staging اب بہاؤ کی توثیق ہونے کے بعد، ہم اسے پیداوار میں منتقل کر دیں گے۔

ingressClassName: nginx: کنٹرولر کی شناخت کرتا ہے جو اس داخلے کو ہینڈل کرے۔ ایک کلسٹر بیک وقت متعدد کنٹرولرز چلا سکتا ہے، اور یہ فیلڈ کنٹرولرز کو ایک ہی وسائل کے لیے لڑنے سے روکتی ہے۔ پچھلے ٹیوٹوریل کا استعمال: kubernetes.io/ingress.class یہ تبصرہ اب استعمال نہیں ہوتا ہے۔

secretName: preview-app-tls: یہ وہ جگہ ہے جہاں سرٹیفکیٹ مینیجر جاری کردہ سرٹیفکیٹ کو محفوظ کرتا ہے۔ وہ خود بخود اسی نام کی جگہ میں بن جاتے ہیں جس میں Ingress ہے، لہذا ہر پیش نظارہ کے تجربے کی اپنی نام کی جگہ ہوتی ہے۔

preview-host-placeholder: جان بوجھ کر درست میزبان نام نہیں ہے۔ دو بار ظاہر ہوتا ہے: ایک بار spec.tls.hosts اور ایک بار spec.rules.hostدونوں کو اگلے مرحلے میں PR کے مطابق تبدیل کیا جائے گا۔ ظاہر ہے، کوئی ایسی چیز استعمال کرنے کا جو حقیقی ڈومین نہیں ہے اس کا مطلب ہے کہ اگر پیچ ناکام ہو جاتا ہے، تو آپ کو ٹھیک ٹھیک غلط راستے کی بجائے فوری طور پر نظر آنے والی غلطی ملے گی۔

pathType: Prefix کے ساتھ path: /: میزبان پر کسی بھی راستے سے میل کھاتا ہے۔ پیش نظارہ کا تجربہ ذیلی راستے کی بجائے پوری ایپلیکیشن فراہم کرتا ہے۔

مرحلہ 17: پل کی درخواست کے مطابق میزبان نام کو پیچ کریں۔

ہر ماحول کو اپنے میزبان نام کی ضرورت ہوتی ہے، جسے Ingress میں دونوں مقامات سے تبدیل کیا جاتا ہے۔ کسٹمائز اسے مندرجہ ذیل طور پر سنبھالتا ہے: JSON 6902 پیچ. عین مطابق کارروائیاں جو وسائل کے اندر درست راستوں کو نشانہ بناتی ہیں۔

مرحلہ 9 میں، ورک فلو نے ایک اوورلے بنایا جو نام کی جگہ اور تصویری ٹیگز ترتیب دیتا ہے۔ تخلیق شدہ اوورلے کو پیچ بلاک میں پھیلائیں۔ یہ ہے جو مکمل طور پر بنایا گیا تھا: kustomization.yaml PR 42 کے لیے:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: pr-42

resources:
  - ../../base

images:
  - name: preview-app
    newName: europe-west2-docker.pkg.dev/your-project/pr-preview/preview-app
    newTag: pr-42-a1b2c3d

patches:
  - target:
      kind: Ingress
      name: preview-app
    patch: |-
      - op: replace
        path: /spec/rules/0/host
        value: pr-42.preview.yourdomain.com
      - op: replace
        path: /spec/tls/0/hosts/0
        value: pr-42.preview.yourdomain.com

پیچ نحو لفظی طور پر پڑھتا ہے: ریسورس میچ میں kind: Ingress, name: preview-appدرج ذیل اقدار کو تبدیل کریں: /spec/rules/0/hostاور قدر تبدیل کریں۔ /spec/tls/0/hosts/0.

کہ 0 انڈیکس صف کی پوزیشن ہے۔ /spec/rules/0 یہ پہلا (اور واحد) اصول ہے۔ /spec/tls/0/hosts/0 پہلے TLS بلاک میں پہلا میزبان نام۔ اگر آپ بعد میں فی ماحول میں دوسرا میزبان نام شامل کرتے ہیں، تو آپ انڈیکس کو نشانہ بنانے والا ایک اور کام شامل کریں گے۔ 1.

JSON 6902 پیچ کیوں استعمال کریں اور اسٹریٹجک انضمام پیچ کیوں نہیں؟ اس کی وجہ یہ ہے کہ اشیاء کی صفوں کے لیے اسٹریٹجک انضمام مبہم ہیں۔ Kubernetes کو اندازہ لگانا ہوگا کہ آیا آپ کا مطلب ہے "اس فہرست کو تبدیل کریں” یا "موجودہ ایک میں ضم کریں” اور جواب کا انحصار فی فیلڈ ضم کلید پر ہے۔ کسی ایک قدر کو معلوم پوزیشن پر تبدیل کرنے کے لیے، ایک واضح استعمال کریں۔ op: replace آپ بالکل وہی کہتے ہیں جو آپ کا مطلب ہے، اور اگر راستہ موجود نہیں ہے تو اونچی آواز میں ناکام ہوجاتے ہیں۔

ورک فلو اس فائل کو رن ٹائم پر بناتا ہے، اس لیے یہ PR کے لیے مخصوص اوورلیز کا ارتکاب نہیں کرتا ہے۔ میں ان اقدامات کے بارے میں مرحلہ 19 میں لکھوں گا۔

مرحلہ 18: ResourceQuota اور LimitRange کے ساتھ Guardrail شامل کریں۔

ورک فلو کو مربوط کرنے سے پہلے، آئیے پیش منظر کے ماحول میں کلسٹر کو ختم کرنے کی صلاحیت کو غیر فعال کر دیں۔

فی الحال، 32 سی پی یوز کی درخواست کرنے والے مینی فیسٹ کو تعینات کرنے، 200 ریپلیکاز تک سکیل کرنے، یا 12 نقلیں بنانے سے پل کی درخواست کو روکنے والی کوئی چیز نہیں ہے۔ LoadBalancer سروس ایسا ہونے کے لیے کسی کے برے ارادے کی ضرورت نہیں ہے۔ نقل کی تعداد میں غلط انضمام، کاپی مینی فیسٹ، یا ٹائپ کی غلطیاں بھی اتنی ہی مؤثر ہیں۔

دو Kubernetes وسائل اس مسئلے کو حل کرتے ہیں اور مل کر کام کرتے ہیں۔

بنانا k8s/cluster/namespace-guardrails.yaml ایک ٹیمپلیٹ کے طور پر:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: preview-quota
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    pods: "10"
    services: "5"
    services.loadbalancers: "0"
    persistentvolumeclaims: "2"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: preview-limits
spec:
  limits:
    - type: Container
      # Applied to containers that don't specify their own
      defaultRequest:
        cpu: 50m
        memory: 64Mi
      default:
        cpu: 200m
        memory: 128Mi
      # Hard ceiling — a container asking for more is rejected
      max:
        cpu: "1"
        memory: 512Mi

ہر شخص کیا کرتا ہے

کوئی راستہ نہیں وسائل کا کوٹہ پورے نام کی جگہ کے کل استعمال کو محدود کرتا ہے۔ تمام پوڈز کے لیے درخواستیں اور حدود شامل کریں۔ کل اس تعداد سے زیادہ نہیں ہو سکتا۔ وہ درخواستیں جو نام کی جگہ کو کوٹہ سے آگے بڑھاتی ہیں، منظوری کے وقت کے لیے مقرر کیے جانے سے پہلے API سرور مسترد کر دے گی۔

کہ services.loadbalancers: "0" لائن نمایاں کرنے کے قابل ہے۔ یہاں تک کہ اگر کوئی مینی فیسٹ کا ارتکاب کرتا ہے، تو پیش نظارہ ماحول میں گوگل کلاؤڈ لوڈ بیلنس کی فراہمی ساختی طور پر ناممکن ہے۔ type: LoadBalancer. یہ براہ راست مالی ڈھال ہے۔ یہ نظام جو سب سے مہنگی غلطی کر سکتا ہے وہ اب ناقابل تلافی ہے۔

کوئی راستہ نہیں محدود رینج یہ پورے نام کی جگہ کے بجائے انفرادی کنٹینرز پر کام کرتا ہے۔ کنٹینرز کے لیے پہلے سے طے شدہ قدریں متعین کریں جو وسائل کی وضاحت نہیں کرتے اور زیادہ سے زیادہ قدر جس سے ایک کنٹینر تجاوز نہیں کرسکتا۔

کیوں ایک ساتھ تعینات؟

وسائل کوٹا کو محدود کرنا requests.cpu نام کی جگہ میں موجود ہے، اس نام کی جگہ میں موجود تمام کنٹینرز کو وسائل کی درخواستوں کا اعلان کرنا چاہیے۔. اگر Kubernetes کو نہیں معلوم کہ آپ کیا درخواست کر رہے ہیں، تو یہ اس بات کا تعین نہیں کر سکے گا کہ آیا یہ کوٹہ کے اندر فٹ بیٹھتا ہے، لہذا ان کے بغیر کوئی بھی پوڈ یکسر مسترد کر دیا جائے گا۔

اس کا مطلب یہ ہے کہ ResourceQuota کو اپنے طور پر شامل کرنے سے کوئی بھی مینی فیسٹ ٹوٹ جائے گا جو وسائل کی تفصیلات کو چھوڑ دیتا ہے، اور درحقیقت زیادہ تر مینی فیسٹ اسے چھوڑ دیتے ہیں۔

LimitRange پہلے سے طے شدہ اقدار فراہم کرکے اس مسئلے کو حل کرتا ہے۔ وسائل کی تفصیلات کے بغیر کنٹینرز defaultRequest اور default کوٹہ کی ضروریات کو پورا کرنے کے لیے قابل اجازت اوقات میں قدروں کو خود بخود انجیکشن کیا جاتا ہے۔

ایک کو دوسرے کے بغیر تعینات کرنا مبہم ناکامیوں کا سبب بنے گا۔ اگر آپ دونوں کو متعین کرتے ہیں، تو سسٹم پھر بھی نامکمل مینی فیسٹ کی اجازت دے گا، حالانکہ ان پر سخت پابندی ہے۔

نمبر کا سائز تبدیل کرنا

مندرجہ بالا اقدار ہماری طرح چھوٹی ایپلی کیشنز کے لیے ٹھیک ہیں۔ اس کو ایڈجسٹ کرنے کے لیے، نوڈ کی گنجائش سے پیچھے کی طرف کام کریں۔

دو e2-standard-2 نوڈ 4 vCPUs اور کل 16 GB فراہم کرتا ہے۔ یہاں، GKE سسٹم کے اجزاء کے لیے صلاحیت کا تقریباً 15% محفوظ رکھتا ہے۔ آئیے اسے 3.4 دستیاب vCPUs کہتے ہیں۔ کے ساتھ requests.cpu: "1" تین پیش نظارہ ماحول فی نام کی جگہ نظریاتی طور پر ایک کلسٹر کو آباد کر سکتے ہیں۔

ہماری درخواست صرف 50m CPUs کی درخواست کرتی ہے، لہذا 3 درحقیقت بہت زیادہ مناسب ہوگا۔ تاہم، کوٹہ نام کی جگہ کے لیے زیادہ سے زیادہ حد ہے۔ مئی آپ کو اس کے بارے میں پوچھنا ہوگا اور اسے "ایک PR ان سب کو کھا سکتا ہے” کے نیچے ترتیب دیں۔

اگر آپ 10 ہم آہنگی ماحول کی توقع کرتے ہیں، تو فی نام کی جگہ کوٹہ کو دستیاب صلاحیت کے تقریباً دسویں حصے میں ایڈجسٹ کریں یا مزید نوڈس شامل کریں۔

مرحلہ 19: تعیناتی ورک فلو کو اپ ڈیٹ کریں۔

یہاں مکمل ایک ہے۔ .github/workflows/preview-deploy.ymlاس سیکشن میں جو کچھ بھی شامل کیا گیا وہ اس طرح ظاہر ہوگا: # NEW. اس ورژن کے ساتھ مرحلہ 9 کو تبدیل کریں۔

name: Deploy PR Preview

on:
  pull_request:
    types: [opened, synchronize, reopened]

concurrency:
  group: preview-${{ github.event.pull_request.number }}
  cancel-in-progress: true

permissions:
  contents: read
  id-token: write
  pull-requests: write

env:
  PROJECT_ID: YOUR_PROJECT_ID
  REGION: europe-west2
  CLUSTER_NAME: pr-preview-cluster
  CLUSTER_ZONE: europe-west2-a
  REPOSITORY: pr-preview
  IMAGE_NAME: preview-app
  # NEW: the base domain for all preview environments
  PREVIEW_DOMAIN: preview.yourdomain.com
  # NEW: switch to letsencrypt-prod once the flow is proven
  CLUSTER_ISSUER: letsencrypt-staging

jobs:
  deploy:
    name: Build and deploy preview
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set environment variables
        run: |
          PR_NUMBER=${{ github.event.pull_request.number }}
          SHORT_SHA=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-7)

          echo "PR_NUMBER=${PR_NUMBER}"    >> $GITHUB_ENV
          echo "SHORT_SHA=${SHORT_SHA}"    >> $GITHUB_ENV
          echo "NAMESPACE=pr-${PR_NUMBER}" >> $GITHUB_ENV
          echo "IMAGE_TAG=pr-${PR_NUMBER}-${SHORT_SHA}" >> $GITHUB_ENV
          echo "IMAGE_BASE=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE_NAME }}" >> $GITHUB_ENV
          # NEW: the hostname this environment will be reachable at
          echo "PREVIEW_HOST=pr-${PR_NUMBER}.${{ env.PREVIEW_DOMAIN }}" >> $GITHUB_ENV

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

      - name: Set up gcloud
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker for Artifact Registry
        run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev --quiet

      - name: Build and push image
        run: |
          docker build 
            --build-arg PR_NUMBER="${PR_NUMBER}" 
            --build-arg GIT_SHA="${SHORT_SHA}" 
            -t "${IMAGE_BASE}:${IMAGE_TAG}" 
            ./app

          docker push "${IMAGE_BASE}:${IMAGE_TAG}"

      - name: Get GKE credentials
        uses: google-github-actions/get-gke-credentials@v2
        with:
          cluster_name: ${{ env.CLUSTER_NAME }}
          location: ${{ env.CLUSTER_ZONE }}

      - name: Create namespace
        run: |
          kubectl create namespace "${NAMESPACE}" 
            --dry-run=client -o yaml | kubectl apply -f -

          # NEW: label it so the reaper knows this namespace is ours
          kubectl label namespace "${NAMESPACE}" 
            app.kubernetes.io/managed-by=pr-preview 
            preview.pr="${PR_NUMBER}" 
            --overwrite

      # NEW: apply the quota and limits before anything deploys
      - name: Apply namespace guardrails
        run: |
          kubectl apply -f k8s/cluster/namespace-guardrails.yaml 
            --namespace="${NAMESPACE}"

      - name: Generate Kustomize overlay
        run: |
          mkdir -p k8s/overlays/"${NAMESPACE}"
          cat > k8s/overlays/"${NAMESPACE}"/kustomization.yaml <

نئے مرحلے کے بارے میں سمجھنے کے لئے کچھ چیزیں ہیں۔

کہ ~1 تبصرہ کردہ پیچ کے راستے میں: یہ ایک ٹائپو کی طرح لگ سکتا ہے، لیکن یہ نہیں ہے.

path: /metadata/annotations/cert-manager.io~1cluster-issuer

JSON پوائنٹر پاتھ استعمال کریں۔ / یہ ایک الگ کرنے والا ہے، تو لفظی طور پر / آپ کو چابی کے اندر سے فرار ہونے کی ضرورت ہے۔ فرار کی ترتیب یہ ہے: ~1. (اور ~ اپنے طور پر فرار ~0.) تبصرہ کلید ہےcert-manager.io/cluster-issuerاشارہ کرنے والا ہے۔ cert-manager.io~1cluster-issuer. اگر یہ غلط ہو جاتا ہے، تو پیچ خود بخود ایک غیر موجود راستے کو نشانہ بنائے گا۔

نام کی جگہ کا لیبل: کہ app.kubernetes.io/managed-by=pr-preview لیبلز وہ ہیں جو ریپر لیول 21 کو محفوظ بناتے ہیں۔ ایک کلین اپ آپریشن جو نام کی جگہوں کو حذف کرتا ہے جو نام کے پیٹرن سے مماثل ہے ایک خراب ریگولر ایکسپریشن ہے جو کسی بھی اہم چیز کو حذف کرنے کے لیے کچھ نہیں کرتا ہے۔ آپ کے لگائے گئے لیبلز کے ساتھ صرف نام کی جگہوں تک رسائی حاصل کرنے والے آپریشنز کے بدترین ہونے کا امکان بہت کم ہے۔

پہلے سے تعیناتی کی پٹی: ResourceQuota درخواست کی تعیناتی سے پہلے موجود ہونا چاہیے، بعد میں نہیں۔ اسے بعد میں لاگو کرنے کا مطلب ہے کہ یہ پہلی تعیناتی پر مکمل طور پر بچ جائے گا، اور آپ اسے صرف اس صورت میں دیکھیں گے جب آپ غیر نافذ شدہ حد سے تجاوز کر جائیں گے۔

continue-on-error: true براہ کرم سرٹیفکیٹ کا انتظار کریں۔ یہ جان بوجھ کر ہے۔ سرٹیفکیٹ جاری کرنے کا انحصار Let’s Encrypt پر ہوتا ہے، ایک بیرونی سروس جس کی رفتار کی حد ہوتی ہے اور بعض اوقات سست ہوتی ہے۔ اگر جاری کرنے میں تاخیر ہوتی ہے، تو ماحول اب بھی تعینات رہے گا اور HTTP پر کام کرتا رہے گا۔

لہذا، پورے ورک فلو کو ناکام کرنا غلط ہوگا۔ مسائل کی جانچ کریں اور جاری رکھیں۔ اگلے مرحلے میں PR کے تبصرے اس بات کی عکاسی کریں گے کہ آیا سرٹیفکیٹ واقعی تیار ہے۔

ماحول زندہ ہے۔ اب اس کے بارے میں لوگوں کو بتانے کا وقت آگیا ہے۔

اس مرحلہ کو اپنے تعیناتی کام کے اختتام پر شامل کریں۔

      - name: Comment preview URL on PR
        uses: actions/github-script@v7
        with:
          script: |
            const host = process.env.PREVIEW_HOST;
            const sha = process.env.SHORT_SHA;
            const ns = process.env.NAMESPACE;

            // A hidden marker so we can find our own comment later
            const marker="";

            const body = [
              marker,
              '### Preview environment is ready',
              '',
              `**https://${host}**`,
              '',
              `Commit `${sha}` · Namespace `${ns}``,
              '',
              'This environment updates on every push and is deleted when the PR closes.',
            ].join('n');

            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });

            const existing = comments.find(c => c.body.includes(marker));

            if (existing) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existing.id,
                body,
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body,
              });
            }

تو یہ طریقہ کیوں اختیار کیا؟

actions/github-script اس کی دیکھ بھال خود GitHub کرتی ہے اور ایک تصدیق شدہ Octokit کلائنٹ فراہم کرتا ہے جس کے سیٹ اپ کی ضرورت نہیں ہے۔ فریق ثالث کے اعمال ہیں جو کم سطروں کے ساتھ PR تبصرے پوسٹ کرتے ہیں، لیکن ورک فلو میں تمام فریق ثالث کی کارروائیاں جن میں تحریری رسائی ہوتی ہے وہ سپلائی چین انحصار ہیں۔

مرحلہ 5 میں طویل المدت اسناد کو ہٹانے کے لیے کی جانے والی کوششوں کو مدنظر رکھتے ہوئے، غیر آڈیٹ شدہ آپریشنز کو شامل کرنا جو ریپوزٹری میں لکھ سکتے ہیں ایک عجیب سودا ہوگا۔

پوشیدہ مارکر تبصرہ یہ وہ طریقہ کار ہے جو اسے تخلیق کرتا ہے۔ مشکل تبصرہ اسے GitHub کے مارک ڈاون میں کچھ نہیں کے طور پر پیش کیا گیا ہے، لیکن یہ کمنٹ باڈی میں ہے۔ ہر دوڑ کے لیے، ہم PR کی تفصیل درج کرتے ہیں، مارکر پر مشتمل آئٹمز تلاش کرتے ہیں، اور جب ہم انہیں تلاش کرتے ہیں تو ان کو اپ ڈیٹ کرتے ہیں۔

اس کے بغیر، 15 کمٹ کے ساتھ ایک PR کو تقریباً 15 ایک جیسے بوٹ تبصرے ملیں گے اور دھاگہ پڑھنے کے قابل نہیں ہو جائے گا۔ مبصرین بوٹ کے تبصروں کو مکمل طور پر نظر انداز کرنا شروع کر دیتے ہیں، جس سے مقصد ختم ہو جاتا ہے۔ اپ ٹو ڈیٹ رکھنے کے لیے ایک تبصرہ مفید ٹول اور شور کے درمیان فرق ہے۔

process.env.PREVIEW_HOST: ریکارڈ شدہ قیمت $GITHUB_ENV پچھلے مراحل میں، آپ اسے اپنی اسکرپٹ میں ماحولیاتی متغیر کے طور پر استعمال کر سکتے ہیں۔ یہ انٹرپولٹنگ سے زیادہ صاف ہے۔ ${{ }} اگر قدر میں اقتباسات یا بیک ٹِکس ہیں، تو یہ حیران کن طریقوں سے ٹوٹ سکتی ہے۔

context.issue.number کام pull_request واقعہ اس لیے پیش آیا کیونکہ GitHub پل کی درخواستوں کو ایک خاص قسم کے مسئلے کے طور پر دیکھتا ہے۔ یہ ہم کیا ہے issues یہ ایک API ہے جو آپ کو PR پر تبصرہ کرنے کی اجازت دیتا ہے۔

قدم کی ضرورت ہے pull-requests: writeیہ وہی ہے جو ورک فلو نے پہلے ہی اعلان کیا ہے۔

مرحلہ 21: ریپر بنائیں

حصہ 1 میں سڑنے والے ورک فلو کے بارے میں تکلیف دہ حقیقت یہ ہے: بالآخر، عملدرآمد ناکام ہو جائے گا۔

ناکامی کے طریقے دنیاوی ہیں۔ کوئی PR کو مارتا ہے جب کہ GitHub ایکشنز کو پریشانی ہو رہی ہے۔ ورک فلو فائلوں کو برانچ میں اس طرح ایڈٹ کیا جاتا ہے جس سے ٹیر ڈاؤن آپریشن ٹوٹ جاتا ہے۔ کوئی PR بند کیے بغیر برانچ کو ڈیلیٹ کر دیتا ہے۔ ذخیرہ PR کے ساتھ محفوظ کیا گیا ہے جو ابھی بھی کھلا ہے۔ GitHub پر ایونٹ کی ترسیل کا مسئلہ۔

ان میں سے کوئی بھی عام نہیں ہے۔ یہ سب آپ کے نام کی جگہ کو غیر معینہ مدت تک جاری رکھیں گے، کلسٹر کی گنجائش اور (اگر کافی ہے) حقیقی رقم استعمال کریں گے۔ ایک صفائی جو 95% وقت کام کرتی ہے اب بھی ایک لیک ہے۔

ٹھیک ہے کاٹنے والا: ایک طے شدہ کام جو وقتاً فوقتاً چلنے والے کاموں کا ان کاموں سے موازنہ کرتا ہے جنہیں چلایا جانا چاہیے اور کسی بھی فرق کو رد کر دیا جاتا ہے۔

بنانا .github/workflows/preview-reaper.yml:

name: Reap Orphaned Preview Environments

on:
  schedule:
    # 03:00 UTC daily
    - cron: '0 3 * * *'
  # Allow manual runs from the Actions tab
  workflow_dispatch:

permissions:
  contents: read
  id-token: write
  pull-requests: read

env:
  PROJECT_ID: YOUR_PROJECT_ID
  CLUSTER_NAME: pr-preview-cluster
  CLUSTER_ZONE: europe-west2-a
  # Delete environments older than this even if the PR is still open
  MAX_AGE_DAYS: 7

jobs:
  reap:
    name: Delete orphaned namespaces
    runs-on: ubuntu-latest

    steps:
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

      - name: Get GKE credentials
        uses: google-github-actions/get-gke-credentials@v2
        with:
          cluster_name: ${{ env.CLUSTER_NAME }}
          location: ${{ env.CLUSTER_ZONE }}

      - name: Reap
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail

          echo "Fetching open pull requests..."
          OPEN_PRS=$(gh pr list 
            --repo "${{ github.repository }}" 
            --state open 
            --limit 500 
            --json number 
            --jq '.[].number')

          echo "Open PRs: ${OPEN_PRS:-none}"
          echo ""

          # Only consider namespaces we created ourselves.
          NAMESPACES=$(kubectl get namespaces 
            -l app.kubernetes.io/managed-by=pr-preview 
            -o jsonpath="{range .items[*]}{.metadata.name}{"t"}{.metadata.labels.preview.pr}{"t"}{.metadata.creationTimestamp}{"n"}{end}")

          if [ -z "${NAMESPACES}" ]; then
            echo "No preview namespaces found. Nothing to do."
            exit 0
          fi

          NOW=$(date -u +%s)
          MAX_AGE_SECONDS=$(( MAX_AGE_DAYS * 86400 ))
          DELETED=0

          while IFS=$'t' read -r NS PR_NUM CREATED; do
            [ -z "${NS}" ] && continue

            CREATED_EPOCH=$(date -u -d "${CREATED}" +%s)
            AGE=$(( NOW - CREATED_EPOCH ))
            AGE_DAYS=$(( AGE / 86400 ))

            REASON=""

            # Rule 1: the PR is no longer open
            if ! echo "${OPEN_PRS}" | grep -qx "${PR_NUM}"; then
              REASON="PR #${PR_NUM} is not open"
            # Rule 2: the environment has outlived MAX_AGE_DAYS
            elif [ "${AGE}" -gt "${MAX_AGE_SECONDS}" ]; then
              REASON="environment is ${AGE_DAYS} days old (max ${MAX_AGE_DAYS})"
            fi

            if [ -n "${REASON}" ]; then
              echo "DELETING ${NS} — ${REASON}"
              kubectl delete namespace "${NS}" 
                --ignore-not-found=true 
                --wait=false
              DELETED=$(( DELETED + 1 ))
            else
              echo "KEEPING  ${NS} — PR #${PR_NUM} open, ${AGE_DAYS}d old"
            fi
          done <<< "${NAMESPACES}"

          echo ""
          echo "Reaped ${DELETED} namespace(s)."

کہاں GITHUB_TOKEN آپ سے ہیں؟

غیر مساوی WIF_PROVIDER اور WIF_SERVICE_ACCOUNTمرحلہ 6 میں بنایا گیا ہے۔ GITHUB_TOKEN یہ وہ نہیں ہے جو آپ نے ترتیب دیا ہے۔. آپ کو یہ خفیہ صفحہ پر نہیں ملے گا، اس لیے آپ کو اسے وہاں شامل نہیں کرنا چاہیے۔

GitHub ہر ورک فلو کے عمل کے آغاز پر ایک نیا انسٹالیشن ٹوکن تیار کرتا ہے اور اسے مندرجہ ذیل طور پر بے نقاب کرتا ہے: secrets.GITHUB_TOKENکام ختم ہونے پر اسے منسوخ کریں۔ دائرہ کار کو مخزن کے طور پر بیان کیا گیا ہے جو ورک فلو کو چلاتا ہے اور کچھ نہیں۔

ریپر کو سمجھنا

ریپر گٹ ہب ایکشنز پر چلتا ہے، کلسٹر کے اندر نہیں۔ Kubernetes میں CronJob کو عوامی PR سے استفسار کرنے کے لیے کلسٹر راز کے طور پر ذخیرہ کردہ GitHub ٹوکن کی ضرورت ہوتی ہے۔ یہ طویل المدت اسناد ہیں جو کلسٹر میں رہتی ہیں، بالکل وہی ہے جس سے ہم نے مرحلہ 5 میں گریز کیا۔

پہلے سے طے شدہ ورک فلو میں GITHUB_TOKEN (اسکوپڈ، خودکار، قلیل مدتی) پہلے سے ہی ورک لوڈ آئیڈینٹی الائنس کے ذریعے GCP سے تصدیق شدہ۔ کوئی نیا راز نہیں ہے۔

لیبل سلیکٹر ایک حفاظتی طریقہ کار ہے۔ استفسار یہ ہے۔ -l app.kubernetes.io/managed-by=pr-previewیہ اس طرح کا نام کا نمونہ نہیں ہے: pr-*. یہ جان بوجھ کر کیا گیا انتخاب ہے۔ اگر نام کا پیٹرن مماثل ہے، تو کسی کے نام سے پروڈکشن نیم اسپیس کو حذف کر دیا جائے گا۔ pr-routing-service. ریپر صرف ان نام کی جگہوں کو دیکھ سکتا ہے جن پر تعیناتی ورک فلو میں واضح طور پر لیبل لگا ہوا ہے۔ اس کا مطلب ہے کہ آئٹمز کا وہ سیٹ جسے حذف کیا جا سکتا ہے وہ آئٹمز کا سیٹ ہے جو آپ نے بنایا ہے۔

اس کو ایڈجسٹ کرتے وقت محتاط رہیں۔ کسی بھی صفائی آٹومیشن کو ایک مثبت وائٹ لسٹ پر کام کرنا چاہیے جسے آپ کنٹرول کرتے ہیں، اور کبھی بھی ایسے پیٹرن پر کام نہیں کرنا چاہیے جو کسی ایسی چیز سے مماثل ہو جو آپ نے نہیں بنایا ہے۔

مزید برآں، دو مختلف مسائل کے لیے حذف کرنے کے دو اصول ہیں۔ اصول 1 اصل یتیموں کو پکڑتا ہے۔ ایک نام کی جگہ جس کے لیے PR بند ہے لیکن کوئی سڑنا نہیں چلایا گیا ہے۔ اصول 2 پرانے ماحول کو پکڑتا ہے۔ PR 3 ہفتوں سے کھلا ہے، 2 ہفتوں سے اچھوتا ہے، اور خاموشی سے کلسٹر کی صلاحیت کو تھامے ہوئے ہے۔ 7 دن ایک معقول ڈیفالٹ ہے۔ اگر کسی کو دوبارہ اپنے ماحول کی ضرورت ہو تو وہ صرف کمٹ کو آگے بڑھا سکتے ہیں اور اسے منٹوں میں دوبارہ تعینات کر دیا جائے گا۔

set -euo pipefail: -e کمانڈ ناکام ہونے پر ختم ہو جاتا ہے۔ -u غیر متعینہ متغیر کے بارے میں خرابی اور -o pipefail پائپ لائن ناکام ہو جائے گی اگر: کوئی بھی قدم ناکام ہوتا ہے، آخری قدم نہیں۔ اسکرپٹ میں جو کسی کام کو حذف کرتا ہے، خاموش ناکامی بدترین ممکنہ نتیجہ ہے۔ اگر کچھ غلط ہو جائے تو فوراً رک جائیں۔

grep -qx: -x جھنڈا پوری قطار سے مماثل ہونا چاہیے۔ اس کے بغیر PR 4 عوامی PR کے مطابق۔ 42 اور ریپر اپنے پیچھے یتیم نام کی جگہیں ہمیشہ کے لیے چھوڑ جائے گا۔ یہ ایک چھوٹا جھنڈا ہے، لیکن یہ ایک حقیقی بگ ہے۔

workflow_dispatch آپ ٹاسکس ٹیب سے Ripper کو دستی طور پر چلا سکتے ہیں۔ اسے پہلے چند بار کریں اور شیڈول پر بھروسہ کرنے سے پہلے آؤٹ پٹ پڑھیں۔ لاگ پرنٹ کیا جائے گا۔ KEEPING یا DELETING چونکہ ہر نام کی جگہ کی ایک وجہ ہوتی ہے، اس لیے آپ اس بات کو یقینی بنا سکتے ہیں کہ آپ کی منطق آپ کی توقعات سے میل کھاتی ہے اس سے پہلے کہ وہ بغیر توجہ کے چل سکے۔

مرحلہ 22: مکمل جانچ

عہد کریں اور ہر چیز کو آگے بڑھائیں۔

git add .
git commit -m "Add Ingress, TLS, PR comments, guardrails, and reaper"
git push

ایک برانچ بنائیں، قابل ذکر تبدیلیاں کریں، اور پل کی درخواست کھولیں۔

git checkout -b test-preview-urls
# edit app/main.go — change the greeting text
git add .
git commit -m "Test preview URLs"
git push -u origin test-preview-urls

PR کھولیں اور ایکشن ٹیب کو دیکھیں۔

تعیناتی ورک فلو نئے سرٹیفکیٹ اور تفصیل کے مرحلے کے ساتھ چل رہا ہے۔

مکمل ہونے کے بعد، آپ کا بوٹ تبصرہ URL کے ساتھ ظاہر ہوگا۔ اسے کھولو۔

بوٹ تبصرہ نیا URL دکھا رہا ہے۔

چونکہ یہ اب بھی اسٹیجنگ جاری کنندہ پر ہے، اس لیے آپ کو اپنے براؤزر میں سرٹیفکیٹ کی وارننگ نظر آئے گی۔ "آئیے انکرپٹ سٹیجنگ" کچھ ایسا ہے جیسے "آپ کا کنکشن نجی نہیں ہے" پبلشر کے ساتھ۔

کامیابی کے لیے یہ شرط ہے۔ اس کا مطلب ہے کہ DNS حل ہو گیا ہے، لوڈ بیلنس کو روٹ کر دیا گیا ہے، Ingress میزبان نام سے مماثل ہے، HTTP-01 چیلنج مکمل ہو گیا ہے، اور سرٹیفکیٹ جاری کر دیا گیا ہے۔ سب کچھ کام کر گیا۔ سرٹیفکیٹ ایک غیر بھروسہ مند جانچ ایجنسی سے آتا ہے۔

پیش نظارہ کے تجربے کے URL کے لیے براؤزر سیکیورٹی وارننگ یہ بتاتا ہے کہ سرٹیفکیٹ Let's Encrypt Staging کے ذریعے جاری کیا گیا تھا، جو کہ ایک قابل اعتماد اتھارٹی نہیں ہے۔

ایڈوانسڈ پر کلک کریں، وارننگ پاس کریں، اور تصدیق کریں کہ ایپلیکیشن درست PR نمبر اور نام کی جگہ کے ساتھ جواب دیتی ہے۔

پیش نظارہ کا تجربہ جو آپ کے براؤزر میں لوڈ ہوتا ہے آپ کی درخواست کا آؤٹ پٹ صحیح PR نمبر، کمٹ SHA، اور نام کی جگہ کے ساتھ دکھاتا ہے۔

کمانڈ لائن سے سرٹیفکیٹ چیک کریں۔

kubectl get certificate --namespace=pr-2 
#Ensure you use your own pr-number

آپ چاہتے ہیں READY: True. پھر Falseچیک کریں کہ کیا ہو رہا ہے۔

kubectl describe certificate preview-app-tls --namespace=pr-2
kubectl get challenges --all-namespaces

کمانڈ لائن سے سرٹیفکیٹ چیک کریں۔

زیر التوا مسائل جو حل نہیں ہوتے ہیں ان کا مطلب ہمیشہ ممکنہ DNS یا پورٹ 80 کنیکٹوٹی ہے۔ اگلا چیک کریں:

curl -I http://pr-2.preview.yourdomain.com/.well-known/acme-challenge/test

میں NGINX سے HTTP جواب چاہتا ہوں۔ 404 ٹھیک ہے کیونکہ یہ ثابت کرتا ہے کہ روٹنگ کام کرتی ہے۔ کنکشن ٹائم آؤٹ کا مطلب ہے کہ درخواست کلسٹر تک نہیں پہنچتی ہے۔

DNS قابل رسائی کو چیک کریں۔

پروڈکشن سرٹیفکیٹ پر سوئچ کریں۔

ایک بار جب آپ کی تیاری کا بہاؤ شروع سے ختم تک کام کرتا ہے، تو آپ ورک فلو کی ایک لائن کو تبدیل کرتے ہیں۔ env: بلاک:

  CLUSTER_ISSUER: letsencrypt-prod

اسے اس طرح تبدیل کریں: موجودہ PR برانچ میں اور اسے دھکا دیں۔ پل کی درخواستوں کو بند کرنے یا ضم کرنے کی ضرورت نہیں ہے۔ main پہلے آپ یہ دیکھنے کے لیے main.go فائل میں پیغام کو بھی تبدیل کر سکتے ہیں کہ آیا سب کچھ کام کرتا ہے۔

PR کے لیے ایک نیا عہد کریں۔ ماحول کو دوبارہ تعینات کیا جائے گا اور سرٹیفکیٹ مینیجر اصل سرٹیفکیٹ کی درخواست کرے گا، اور اس بار آپ کو اپنے براؤزر میں ایک تالا نظر آئے گا۔

سے یو آر ایل کھولیں: نجی نیویگیشن پین چیک کریں۔ آپ کے براؤزر نے اس میزبان نام کے لیے اسٹیجنگ سرٹیفکیٹ کے لیے ایک استثناء کیش کیا ہے، اور آپ کا باقاعدہ ٹیب بظاہر قابل اعتماد صفحہ دکھا سکتا ہے جو حقیقت میں صرف ایک یاد کردہ اوور رائڈ ہے اور اب بھی ظاہر ہے۔ not secure.

پیش نظارہ تجربہ HTTPS پر ایک درست سرٹیفکیٹ کے ساتھ بھرا ہوا ہے۔

چوکیوں کو چیک کریں۔

چیک کریں کہ آیا کوٹہ فعال ہے۔

kubectl describe resourcequota preview-quota --namespace=pr-2

kubectl کا آؤٹ پٹ وسائل کے کوٹے کی وضاحت کرتا ہے، نام کی جگہ pr-1 کے لیے پیش نظارہ کوٹہ دکھاتا ہے، استعمال شدہ فہرست اور CPU، میموری، پوڈز، سروسز، اور لوڈ بیلنسرز کے لیے سخت حدود۔

ہر وسائل کے لیے استعمال کی حدیں اور سخت حدود ظاہر کی جاتی ہیں۔ پھر اسے توڑنے کی کوشش کریں۔

kubectl scale deployment/preview-app --replicas=50 --namespace=pr-2
kubectl get events --namespace=pr-2 | grep -i quota

تعیناتی درمیانی راستے پر بڑھ جاتی ہے اور پھر ایک ایونٹ کے ساتھ رک جاتی ہے جس میں یہ وضاحت کی جاتی ہے کہ کوٹہ ختم ہو گیا ہے۔ یہ ایک چوکیدار ہے جو اپنا کام کرتی ہے۔ نام کی جگہ محدود ہے اور باقی کلسٹر متاثر نہیں ہوتا ہے۔

ٹرمینل آؤٹ پٹ صرف جزوی طور پر 50 replicas پر تعیناتی دکھا رہا ہے، ایک Kubernetes ایونٹ کے ساتھ یہ اطلاع دی گئی ہے کہ درخواست نام کی جگہ کوٹہ سے تجاوز کر گئی ہے۔

ٹیئر ڈاؤن اور ریپر کو چیک کریں۔

پل کی درخواست کو بند کریں۔ گلنے کا کام کا بہاؤ شروع ہوتا ہے اور نام کی جگہ منتقل ہوتی ہے: Terminating.

Github ایکشنز ٹیر ڈاون ورک فلو ٹاسک مکمل ہو گیا ہے۔

پھر نام کی جگہ میں ریپر کی جانچ کریں۔ نہیں کرنا چاہیے انہیں بغیر لیبل کے دستی طور پر بنائیں کو ٹچ کریں۔

kubectl create namespace pr-9999

kubectl ٹرمینل تخلیق کریں نام کی جگہ pr-9999 دکھا رہا ہے (پی آر-پیش نظارہ مینجمنٹ لیبل کے بغیر نام کی جگہ بنانا)

Tasks کے ٹیب سے Ripper کو دستی طور پر چلائیں۔

GitHub ایکشنز انٹرفیس ایک Reap Orphaned Preview Environments کا ورک فلو دکھا رہا ہے جس میں ایک ورک فلو ایگزیکیوشن بٹن ہے جو workflow_dispatch کے ذریعے دستیاب ہے۔

نظر انداز کیا جانا چاہئے pr-9999 مکمل طور پر کیونکہ اس نام کی جگہ میں کچھ بھی نہیں ہے۔ app.kubernetes.io/managed-by=pr-preview برانڈ لاگ میں بھی اس کا ذکر نہیں ہے۔

ریپر کے لیے GitHub ایکشن لاگ یہ ظاہر کرتا ہے کہ اسے کام کرنے کے لیے پیش نظارہ نام کی جگہ نہیں ملتی ہے، بغیر لیبل والے pr-9999 نام کی جگہ کو مکمل طور پر نظر انداز کرتے ہوئے

اسے خود ترتیب دیں۔

kubectl delete namespace pr-9999

ٹیسٹ اس سے زیادہ اہم ہے جتنا کہ ظاہر ہوتا ہے۔ میں نے ابھی تصدیق کی ہے کہ آٹو ڈیلیٹ آپریشن کسی بھی چیز کو چھو نہیں سکتا جو اس نے نہیں بنایا ہے۔

اس کی قیمت کتنی ہے؟

لاگت کا کنٹرول اس پیٹرن کے لئے نصف دلیل ہے، لہذا اس کے بارے میں مخصوص رہیں.

مسلسل چل رہا ہے۔ europe-west2یہاں تقریباً ماہانہ اعداد و شمار ہیں:

عنصر تخمینہ لاگت
GKE کنٹرول ہوائی جہاز ~$74/مہینہ (ایک زون کلسٹر، کچھ اکاؤنٹس پر ایک کلسٹر پر مفت درجے کا اطلاق ہوتا ہے)
2 × e2-standard-2 نوڈ ~$97/مہینہ
علاقائی لوڈ بیلنسر ~$18/ماہ + ڈیٹا پروسیسنگ
جامد IP (استعمال میں) منسلک ہونے کے دوران مفت
آرٹفیکٹ رجسٹری ذخیرہ ~$0.10/GB/مہینہ
آئیے سرٹیفکیٹ کو خفیہ کرتے ہیں۔ مفت
GitHub ایکشنز (عوامی ذخیرہ) مفت

میں یہ کہوں گا کہ ہر ماہ جاری رہنے والے انفراسٹرکچر کے لیے تقریباً $190 فی مہینہ ہے، قطع نظر اس کے کہ آپ کتنے پیش نظارہ ماحول چلا رہے ہیں۔

کون سے نمبر اہم ہیں؟ ایسا نہیں پی آر کی تعداد پر مبنی ترازو۔ ایک لوڈ بیلنسر تمام ماحول کو سپورٹ کرتا ہے۔ ایک DNS ریکارڈ میں تمام میزبان نام ہوتے ہیں۔ سند مفت ہے۔ 11 ویں پیش نظارہ ماحول کی معمولی قیمت پوڈ کے ذریعہ استعمال ہونے والی نوڈ کی گنجائش ہے (اس سائز کے اطلاق کے لئے روزانہ چند پینس)۔

ایک بولی نقطہ نظر سے اس کا موازنہ کریں۔ LoadBalancer ماحولیات کے لیے مخصوص خدمات: اگر آپ 10 پبلک پی آرز انسٹال کرتے ہیں، تو صرف لوڈ بیلنس ہر ماہ تقریباً $180 کا اضافہ کرے گا، اور یہ اس سے پہلے کہ کوئی اسے صاف کرنا بھول جائے۔ کہ services.loadbalancers: "0" ResourceQuota میں لائن خاص طور پر ایسی غلطیوں کو روکنے کے لیے موجود ہے۔

آپ کے ذاتی منصوبوں پر پیسہ بچانے کے دو طریقے ہیں۔ استعمال کریں سپاٹ نوڈشامل کریں spot = true کو node_config ٹیرافارم میں نوڈ ٹائم کو تقریباً 60-70% کی چھوٹ کے لیے بلاک کریں۔ پیش نظارہ ماحول ایک مثالی جگہ کام کا بوجھ ہے۔ جب ایک نوڈ کا دوبارہ دعوی کیا جاتا ہے، تو پوڈ کو دوبارہ ترتیب دیا جائے گا اور، بدترین صورت میں، پیش نظارہ کا تجربہ مختصر طور پر دوبارہ شروع ہو جائے گا۔ یا، چونکہ کوئی بھی صبح 3 بجے PRs کا جائزہ نہیں لے رہا ہے، اس لیے آپ نوڈ پول کو راتوں رات صفر کرنے کے لیے ایک طے شدہ ورک فلو استعمال کرتے ہیں۔

صفائی

مکمل ہونے کے بعد:

# Delete every preview namespace
kubectl delete namespaces -l app.kubernetes.io/managed-by=pr-preview

# Remove the shared infrastructure
helm uninstall cert-manager --namespace cert-manager
helm uninstall ingress-nginx --namespace ingress-nginx
kubectl delete namespace cert-manager ingress-nginx

ہیلم ریلیز کو ہٹا دیں۔ پہلے چل رہا ہے terraform destroy. Ingress کنٹرولر کی سروس گوگل کلاؤڈ لوڈ بیلنس کی مالک ہے جس کے بارے میں Terraform نہیں جانتا۔ اس کی وجہ یہ ہے کہ Kubernetes نے اسے بنایا، Terraform نہیں۔ اگر آپ سب سے پہلے کلسٹر کو تباہ کرتے ہیں، تو اس کا لوڈ بیلنس الگ ہو سکتا ہے اور خاموشی سے آپ کے پروجیکٹ سے کسی اور چیز کے بغیر چارج کیا جا سکتا ہے۔

اگر آپ اسے بعد میں دوبارہ بنانے کا ارادہ رکھتے ہیں۔

پوری نہ بھاگو terraform destroy. صرف مہنگے حصوں کو الگ کریں۔

cd terraform 
terraform destroy 
-var="project_id=YOUR_PROJECT_ID" 
-var="github_repo=YOUR_USERNAME/pr-preview-envs"

اس کی وجہ یہ ہے: جب آپ کام کے بوجھ والے شناختی پول کو حذف کرتے ہیں، تو Google اسے فوری طور پر نہیں ہٹاتا ہے۔ یہ دکھا رہا ہے۔ DELETED ہم آپ کی شناخت 30 دنوں تک اپنے پاس رکھتے ہیں۔ ٹیرافارم اسے بھول گیا، لیکن گوگل نے ایسا نہیں کیا۔ اس ونڈو کے اندر اسٹیک کو دوبارہ بنانے سے درج ذیل مسائل پیدا ہوتے ہیں۔

Error: Error creating WorkloadIdentityPool: googleapi: Error 409: Requested entity already exists

تقریباً تمام قیمت کلسٹر اور اس کے نوڈس کی ہے۔ شناختی پولز، فراہم کنندگان، سروس اکاؤنٹس، آئی اے ایم بائنڈنگز، جامد IPs، اور آرٹفیکٹ رجسٹری ریپوزٹریز کو جگہ پر رکھنے کی کوئی قیمت نہیں ہے۔ بعد میں دوبارہ تعمیر کرنا بس ہے۔ terraform applyاور آپ کا WIF_PROVIDER اور WIF_SERVICE_ACCOUNT GitHub پر راز درست رہتے ہیں۔ اپ ڈیٹ کرنے کی ضرورت نہیں۔

اگر آپ اپنے کلسٹر کو برقرار رکھنا چاہتے ہیں اور بیکار نوڈس کی ادائیگی بند کرنا چاہتے ہیں، تو اس کے بجائے نوڈ پول کو صفر پر پیمانہ کریں۔

gcloud container clusters resize pr-preview-cluster
--node-pool pr-preview-cluster-pool
--num-nodes 0
--zone europe-west2-a

چونکہ آپ ابھی بھی GKE کنٹرول طیارے کے لیے ادائیگی کرتے ہیں، اس لیے آپ اسے ہٹانے کے بجائے رقم بچا سکتے ہیں۔

ایک بار جب سب کچھ ہو جائے تو، ان اقدامات پر عمل کریں:

cd terraform
terraform destroy
-var="project_id=YOUR_PROJECT_ID"
-var="github_repo=YOUR_USERNAME/pr-preview-envs" 

پھر یقینی بنائیں کہ کچھ بھی باقی نہیں ہے۔

gcloud compute forwarding-rules list
gcloud compute addresses list

دونوں خالی ہونے چاہئیں یا ان میں صرف دوسرے پراجیکٹس سے تعلق رکھنے والے وسائل ہونے چاہئیں۔ وائلڈ کارڈ DNS ریکارڈز کو بھی ہٹانا نہ بھولیں۔

نتیجہ

آپ نے کچھ ایسا بنایا ہے جو زیادہ تر انجینئرنگ ٹیموں کے پاس نہیں ہے یا وہ کسی وینڈر کو ادا نہیں کرتی ہیں۔

میں نے ایک گو ایپلی کیشن لکھی ہے جو لنکر جھنڈوں کے ذریعہ مرتب وقت پر انجیکشن کے اپنے بلڈ میٹا ڈیٹا کی اطلاع دیتی ہے ، لہذا چلانے والا ماحول ہمیشہ جانتا ہے کہ یہ کس کمٹ سے آیا ہے۔ میں نے اسے ملٹی اسٹیج ڈوکر فائل میں لپیٹ دیا ہے جو ایک چھوٹی سی مستحکم طور پر منسلک غیر جڑ امیج بناتا ہے۔

Terraform کا استعمال کرتے ہوئے ایک GKE کلسٹر اور آرٹفیکٹ رجسٹری ذخیرہ فراہم کیا۔ اس کا مطلب ہے کہ آپ ایک ہی کمانڈ سے پورے ماحول کو دوبارہ بنا اور حذف کر سکتے ہیں۔ کام کے بوجھ کی شناخت کا فیڈریشن قائم کریں تاکہ GitHub ایکشنز ایک طویل مدتی سروس اکاؤنٹ کلید کے بجائے مختصر مدت کے OIDC ٹوکن کا استعمال کرتے ہوئے گوگل کلاؤڈ سے تصدیق کر سکے جو پراپرٹی کی شرائط کی بنیاد پر بالکل ایک ریپوزٹری تک محدود ہے۔

ہم نے ایک بار ایک بنیادی Kubernetes مینی فیسٹ لکھا اور رن ٹائم پر تیار کردہ Kustomize اوورلے کو استعمال کیا تاکہ ایک ہی ایپلیکیشن کو مختلف نام کی جگہوں پر مختلف امیج ٹیگز اور ہر پل کی درخواست کے لیے مختلف میزبان ناموں کے ساتھ تعینات کیا جا سکے۔ اپنے تمام ماحول کے سامنے ایک مشترکہ انگریس کنٹرولر رکھ کر اور ایک مستحکم، جامد IP فراہم کرنے سے، ایک لوڈ بیلنسر کسی بھی تعداد میں پیش نظارہ پیش کرتا ہے۔ آپ نے ایک وائلڈ کارڈ DNS ریکارڈ متعین کیا۔ اس کا مطلب یہ ہے کہ آپ کے نئے ماحول کو کسی بھی DNS کام کی ضرورت نہیں ہوگی۔ روٹنگ ہے۔ Host کلسٹر کے اندر ہیڈر۔ اس بات کو یقینی بنانے کے لیے کہ تمام ماحول درست خودکار تجدید TLS سرٹیفکیٹ حاصل کر سکتے ہیں، ہم نے ایک سرٹیفکیٹ مینیجر کو شامل کیا اور اس بات کو یقینی بنایا کہ پائپ لائن خود کو ایک مقررہ PR تفصیل کے ساتھ تشہیر کرتی ہے جو اپ ڈیٹ ہوتی ہے۔

ہم نے ہر نام کی جگہ کو ResourceQuota اور LimitRange تک محدود کر دیا، جس میں ایک لائن بھی شامل ہے جو ساختی لحاظ سے سب سے مہنگی غلطی کو ناممکن بناتی ہے۔ اور ہم نے ایک ایسا ریپر بنایا ہے جو خوشی کے راستے سے چھوٹ جانے والی چیزوں کو پکڑتا ہے، اور صرف نام کی جگہوں پر کام کرتا ہے جہاں ہم یہ ثابت کر سکتے ہیں کہ اسے بنایا گیا تھا۔

نتیجہ الگ تھلگ پیداوار میں عوامی طور پر قابل رسائی ماحول ہے۔ بنیادی ہر تبدیلی کے لیے ڈویلپر کی ضرورت نہیں ہوتی کہ وہ درخواست کرے، درخواست کرے، یا سلیک چینل میں منظم کرنا یاد رکھے۔

میں یہاں سے کہاں جاؤں؟

ایسی کئی سمتیں ہیں جنہیں آپ دریافت کر سکتے ہیں۔ عملی طور پر آپ اپنی ٹیم کے تجربے کو کس حد تک بہتر بنا سکتے ہیں اس کا ایک موٹا حکم یہ ہے:

  1. ہر ماحول کے لیے بیج کا ڈیٹا۔ ہم نے جو بنایا ہے اس کی سب سے بڑی حد یہ ہے کہ ایپلیکیشن بے وطن ہے۔ حقیقی دنیا کی ایپلی کیشنز کو ڈیٹا بیس کی ضرورت ہوتی ہے۔ عام پیٹرن عارضی ڈیٹا بیس فی نیم اسپیس ہیں جو حذف شدہ سنیپ شاٹس سے سیڈ ہوتے ہیں، یا فی ماحول اسکیما آئسولیشن کے ساتھ مشترکہ ڈیٹا بیس ہوتے ہیں۔ پیش نظارہ ماحول میں یہ سب سے مشکل مسئلہ ہے اور جہاں باقی انجینئرنگ کی زیادہ تر کوششیں ہوں گی۔

  2. پیش نظارہ کے خلاف ٹیسٹ سویٹ چلائیں۔ ایک بار جب آپ کے پاس اپنے ماحول میں حقیقی URL ہو جائے تو، آپ CI میں اس پر اینڈ ٹو اینڈ ٹیسٹ چلا سکتے ہیں۔ یہ فرضی URLs کے خلاف جانچ کے مقابلے میں زیادہ مضبوط سگنل ہے۔

  3. DNS-01 کے ذریعے وائلڈ کارڈ سرٹیفکیٹ۔ جب PR والیوم حد تک پہنچ جاتا ہے تو Let's Encrypt شرح کو محدود کرنے کی ٹوپی ہٹا دی جاتی ہے، جیسا کہ مرحلہ 15 میں بیان کیا گیا ہے۔

  4. GitOps پر جائیں۔ کام کا بہاؤ چلتا ہے کے مقابلے میں kubectl apply آپ کو پیش کردہ مینی فیسٹ کو براہ راست کسی ایسے ذخیرہ میں بھیجنے دیتا ہے جو Argo CD یا Flux کوآرڈینیٹ کرتا ہے۔ کلسٹر اسٹیٹ پھر گٹ ہسٹری پر مشتمل ہوگی، اور آپ کی CI پائپ لائن کو کلسٹر رائٹ تک رسائی کی ضرورت نہیں ہوگی۔

  5. اپنے ماحولیاتی میٹرکس کا جائزہ لیں۔ اپنے نام کی جگہ کو لاگت کی نمائش والے ٹول سے جوڑ کر آپ کہیں اور استعمال کریں گے، آپ کندھے کی بجائے نمبروں کے ساتھ "پیش نظارہ ماحول میں میری ماہانہ لاگت کی اصل قیمت کیا ہے" کا جواب دے سکتے ہیں۔

پروجیکٹ فائل کا ڈھانچہ

pr-preview-envs/
├── app/
│   ├── main.go                      — Go HTTP server reporting build metadata
│   ├── go.mod                       — Go module definition
│   └── Dockerfile                   — Multi-stage build with ldflags injection
├── terraform/
│   ├── main.tf                      — GKE, Artifact Registry, WIF, static IP
│   ├── variables.tf                 — Input variables
│   └── outputs.tf                   — Cluster, WIF, and ingress IP values
├── k8s/
│   ├── base/
│   │   ├── deployment.yaml          — Deployment with probes and Downward API
│   │   ├── service.yaml             — ClusterIP Service
│   │   ├── ingress.yaml             — Ingress with cert-manager annotation
│   │   └── kustomization.yaml       — Base Kustomize config
│   └── cluster/
│       ├── letsencrypt-staging.yaml — Staging ClusterIssuer
│       ├── letsencrypt-prod.yaml    — Production ClusterIssuer
│       └── namespace-guardrails.yaml — ResourceQuota and LimitRange
└── .github/
    └── workflows/
        ├── preview-deploy.yml       — Build, deploy, certificate, PR comment
        ├── preview-teardown.yml     — Delete namespace on PR close
        └── preview-reaper.yml       — Scheduled orphan cleanup

اس ٹیوٹوریل کے لیے تمام سورس فائلیں ساتھ والے GitHub ریپوزٹری میں دستیاب ہیں۔ https://github.com/Amiynarh/pr-preview-envs.

Scroll to Top