ملاحظة perev. : مع هذا المقال ، الذي كتبه سكوت رانر ، مهندس في داو جونز ، نواصل سلسلة من المواد العديدة التي تشرح كيفية عمل Kubernetes ، وكيفية عمل مكوناتها الأساسية ، والترابط بينها واستخدامها. هذه المرة هذه ملاحظة عملية مع عينة من الكود لإنشاء خطاف في Kubernetes ، أظهره المؤلف "بحجة" إنشاء حاويات جانبية تلقائيًا.
(تصوير جوردون أ. ماكسويل ، الموجود على الإنترنت.)عندما بدأت دراسة حاويات السيارة الجانبية وشبكة الخدمة ، كنت بحاجة إلى فهم كيفية عمل الآلية الرئيسية - الإدراج التلقائي لحاوية السيارة المسحوبة. في الواقع ، في حالة استخدام أنظمة مثل Istio أو Consul ، عندما يتم نشر الحاوية مع التطبيق ، تظهر حاوية Envoy التي تم تكوينها بالفعل فجأة في جرابها
(يحدث موقف مماثل مع Conduit ، والذي كتبنا عنه في بداية العام - تقريبًا. Transl.) . ماذا؟ كيف؟ لذلك بدأ بحثي ...
بالنسبة لأولئك الذين لا يعرفون ، فإن الحاوية الجانبية عبارة عن حاوية يتم نشرها بجوار حاويات التطبيق من أجل "مساعدة" هذا التطبيق بطريقة ما. مثال على هذا الاستخدام هو وكيل لإدارة حركة المرور وإنهاء جلسات TLS ، حاوية لتدفق السجلات والمقاييس ، حاوية لمسح مشاكل الأمان ... الفكرة هي عزل الجوانب المختلفة للتطبيق بأكمله من منطق الأعمال باستخدام حاويات منفصلة لكل منها وظائف.
قبل المتابعة ، سأحدد توقعاتي. الغرض من هذه المقالة ليس لشرح التعقيدات وسيناريوهات الاستخدام لـ Docker و Kubernetes وشبكة الخدمة وما إلى ذلك ، ولكن لإثبات نهج واحد قوي لتوسيع إمكانات هذه التقنيات. المقالة مخصصة لأولئك الذين هم على دراية بالفعل باستخدام هذه التقنيات أو ، على الأقل ، قرأوا الكثير عنها. لتجربة الجزء العملي في العمل ، ستحتاج إلى جهاز تم تكوينه بالفعل من Docker و Kubernetes. أسهل طريقة للقيام بذلك هي
https://docs.docker.com/docker-for-windows/kubernetes/ (دليل Windows يعمل مع Docker لنظام التشغيل Mac).
(ملاحظة لاحقة: كبديل لمستخدمي أنظمة Linux و * nix ، يمكننا تقديم Minikube .)الصورة العامة
للبدء ، دعنا نلقي نظرة على Kubernetes قليلاً:
Kube Arch مرخص بموجب CC BY 4.0عندما تنوي نشر شيء ما على Kubernetes ، يجب عليك إرسال الكائن إلى kube-apiserver. غالبًا ما يتم ذلك عن طريق تمرير الوسائط أو ملف YAML إلى kubectl. في هذه الحالة ، يمر خادم API بعدة مراحل قبل وضع البيانات مباشرة في etcd وجدولة المهام المقابلة:

هذا التسلسل مهم لفهم كيفية عمل إدخال حاوية السيارة المسحوبة. على وجه الخصوص ، تحتاج إلى الانتباه إلى
التحكم في
القبول ، حيث تتحقق Kubernetes من الكائنات ، وإذا لزم الأمر ، تقوم بتعديل الكائنات قبل تخزينها
(لمزيد من التفاصيل حول هذه الخطوة ، راجع الفصل "التحكم في الوصول" في هذه المقالة - ترجمة.) . تسمح لك Kubernetes أيضًا بتسجيل
الخطافات الإلكترونية التي يمكنها إجراء عمليات التحقق
والطفرات المعرفة من قبل المستخدم.
ومع ذلك ، فإن عملية إنشاء وتسجيل الخطافات الخاصة بك ليست بهذه البساطة والتوثيق الجيد. اضطررت لقضاء عدة أيام في قراءة الوثائق وإعادة قراءتها ، بالإضافة إلى تحليل رمز Istio و Consul. وعندما يتعلق الأمر بالرمز لبعض استجابات واجهة برمجة التطبيقات ، أمضيت نصف يوم على الأقل في إجراء تجربة وخطأ عشوائيين.
بعد تحقيق النتيجة ، أعتقد أنه لن يكون من العدل عدم مشاركتها معكم جميعًا. إنه بسيط وفعال في نفس الوقت.
كود
اسم webhook يتحدث عن نفسه - إنها نقطة نهاية HTTP التي تنفذ API المحددة في Kubernetes. تقوم بإنشاء خادم API يمكن Kubernetes الاتصال به قبل التعامل مع عمليات النشر. اضطررت للتعامل مع الصعوبات هنا ، حيث لا تتوفر سوى أمثلة قليلة ، بعضها مجرد اختبارات وحدة من Kubernetes ، والبعض الآخر مخفي في منتصف قاعدة كود ضخمة ... وكلها مكتوبة في Go. لكنني اخترت خيارًا أكثر بأسعار معقولة - Node.js:
const app = express(); app.use(bodyParser.json()); app.post('/mutate', (req, res) => { console.log(req.body) console.log(req.body.request.object) let adminResp = {response:{ allowed: true, patch: Buffer.from("[{ \"op\": \"add\", \"path\": \"/metadata/labels/foo\", \"value\": \"bar\" }]").toString('base64'), patchType: "JSONPatch", }} console.log(adminResp) res.send(adminResp) }) const server = https.createServer(options, app);
( index.js )يمكن أن يكون المسار إلى API - في هذه الحالة
/mutate
- تعسفيًا (يجب أن يتوافق فقط مع YAML الذي تم تمريره إلى Kubernetes في المستقبل). من المهم بالنسبة له أن يرى ويفهم JSON المستلم من خادم API. في هذه الحالة ، لا نخرج أي شيء من JSON ، ولكن قد يكون مفيدًا في سيناريوهات أخرى. في الرمز أعلاه ، نقوم بتحديث JSON. هناك حاجة إلى شيئين لهذا:
- تعلم وفهم JSON Patch .
- تحويل تعبير JSON Patch بشكل صحيح إلى صفيف من وحدات البايت المشفرة باستخدام base64.
بمجرد القيام بذلك ، كل ما عليك فعله هو تمرير الاستجابة لخادم API بكائن بسيط للغاية. في هذه الحالة ، نضيف التسمية
foo=bar
أي جراب يأتي إلينا.
النشر
حسنًا ، لدينا رمز يقبل الطلبات من خادم Kubernetes API ويستجيب لها ، ولكن كيف يتم نشره؟ وكيف تجعل Kubernetes يعيد توجيه هذه الطلبات إلينا؟ يمكنك نشر نقطة النهاية هذه في أي مكان يمكنك فيه الوصول إلى خادم Kubernetes API. إن أبسط طريقة هي نشر الرمز إلى مجموعة Kubernetes نفسها ، وهو ما سنفعله في هذا المثال. حاولت أن أجعل المثال أبسط ما يمكن ، لذلك بالنسبة لجميع الإجراءات التي أستخدمها فقط Docker و kubectl. لنبدأ بإنشاء حاوية يتم فيها تشغيل الرمز:
FROM node:8 USER node WORKDIR /home/node COPY index.js . COPY package.json . RUN npm install # TLS CMD node index.js
( ملف Dockerfile )يبدو أن كل شيء بسيط للغاية هنا. خذ صورة المجتمع من العقدة وأفلت الرمز فيها. يمكنك الآن إجراء تجميع بسيط:
docker build . -t localserver
الخطوة التالية هي إنشاء نشر في Kubernetes:
apiVersion: apps/v1 kind: Deployment metadata: name: webhook-server spec: replicas: 1 selector: matchLabels: component: webhook-server template: metadata: labels: component: webhook-server spec: containers: - name: webhook-server imagePullPolicy: Never image: localserver
( loyd.yaml )لاحظ كيف ألمحنا إلى الصورة التي أنشأناها للتو؟ يمكن أن يكون أيضًا جرابًا ، وشيء آخر يمكننا توصيل الخدمة به في Kubernetes. الآن حدد هذه الخدمة:
apiVersion: v1 kind: Service metadata: name: webhook-service spec: ports: - port: 443 targetPort: 8443 selector: component: webhook-server
لذا في Kubernetes ، تظهر نقطة نهاية باسم داخلي يشير إلى حاويتنا. الخطوة الأخيرة هي إخبار Kubernetes أننا نريد من خادم API استدعاء هذه الخدمة عندما يكون جاهزًا لإجراء
الطفرات :
apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingWebhookConfiguration metadata: name: webhook webhooks: - name: webhook-service.default.svc failurePolicy: Fail clientConfig: service: name: webhook-service namespace: default path: "/mutate" # base64- rootCA.crt # `cat rootCA.crt | base64 | tr -d '\n'` # . caBundle: "==" rules: - operations: [ "CREATE" ] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"]
( هوك. yaml )يمكن أن يكون الاسم والمسار هنا أيًا ، لكنني حاولت أن أجعلها ذات معنى قدر الإمكان. يعني تغيير المسار الحاجة إلى تعديل الكود المقابل في JavaScript. إن سياسة Webhook
failurePolicy
أيضًا - فهي تحدد ما إذا كان يجب حفظ الكائن إذا
failurePolicy
الخطاف خطأ أو فشل. في هذه الحالة ، نطلب من Kubernetes عدم مواصلة المعالجة. أخيرًا ، القواعد: ستتغير اعتمادًا على مكالمات API التي تتوقع إجراءات من Kubernetes. في هذه الحالة ، بما أننا نحاول محاكاة إدخال حاوية جانبية ، فإننا نحتاج إلى اعتراض الطلبات لإنشاء جراب.
هذا كل شئ! بهذه البساطة ... ولكن ماذا عن الأمن؟ RBAC هو أحد الجوانب التي لم يتم تناولها في المقالة. أفترض أنك تقوم بتشغيل المثال في Minikube أو في Kubernetes ، والذي يأتي مع Docker لنظام التشغيل Windows / Mac. ومع ذلك ، سأخبرك عن عنصر ضروري آخر. يصل خادم Kubernetes API إلى نقاط النهاية فقط باستخدام HTTPS ، لذا يتطلب التطبيق شهادات SSL. ستحتاج أيضًا إلى إخبار Kubernetes بمعرفة المرجع المصدق لشهادة الجذر.
TLS
لأغراض العرض التوضيحي فقط (!!!) ، أضفت بعض التعليمات البرمجية إلى ملف
Dockerfile
لإنشاء
Dockerfile
جذري واستخدامه لتوقيع الشهادة:
RUN openssl genrsa -out rootCA.key 4096 RUN openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt \ -subj "/C=US/ST=New Jersey/L=Princeton /O=Dow Jones/OU=PIB/CN=*.default.svc/emailAddress=scott.rahner@dowjones.com" RUN openssl genrsa -out webhook.key 4096 RUN openssl req -new -key webhook.key -out webhook.csr \ -subj "/C=US/ST=New Jersey/L=Princeton /O=Dow Jones/OU=PIB/CN=webhook-service.default.svc/emailAddress=scott.rahner@dowjones.com" RUN openssl x509 -req -in webhook.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out webhook.crt -days 1024 -sha256 RUN cat rootCA.crt | base64 | tr -d '\n'
( ملف Dockerfile )يرجى ملاحظة ما يلي: الخطوة الأخيرة هي عرض سطر واحد مع مرجع مصدق جذر مشفر في base64. هذا هو بالضبط ما هو مطلوب لتكوين الخطاف ، لذلك في اختباراتك الإضافية ، تأكد من نسخ هذا السطر في حقل
caBundle
لملف
caBundle
.
Dockerfile
بإلقاء الشهادات مباشرة في
WORKDIR
، لذلك تأخذها JavaScript فقط من هناك وتستخدمها للخادم:
const privateKey = fs.readFileSync('webhook.key').toString(); const certificate = fs.readFileSync('webhook.crt').toString();
الآن يدعم الرمز إطلاق HTTPS ، كما أخبر Kubernetes بمكان العثور علينا ومركز الثقة الذي نثق به. يبقى فقط لدمجها كلها في كتلة:
kubectl create -f deployment.yaml kubectl create -f service.yaml kubectl create -f hook.yaml
تلخيص
Deployment.yaml
بتشغيل حاوية تخدم واجهة برمجة التطبيقات للربط عبر HTTPS وإرجاع تصحيح JSON لتعديل الكائن.- توفر
Service.yaml
نقطة نهاية للحاوية - webhook-service.default.svc
. - يخبر
Hook.yaml
خادم API بمكان العثور عليه: https://webhook-service.default.svc/mutate
.
دعونا نحاول في العمل!
يتم نشر كل شيء في مجموعة - لقد حان الوقت لتجربة الكود عمليًا ، والذي سنفعله بإضافة بود / نشر جديد. إذا كان كل شيء يعمل بشكل صحيح ، سيتعين على الخطاف إضافة تسمية إضافية:
apiVersion: apps/v1 kind: Deployment metadata: name: test spec: replicas: 1 selector: matchLabels: component: test template: metadata: labels: component: test spec: containers: - name: test image: node:8 command: [ "/bin/sh", "-c", "--" ] args: [ "while true; do sleep 30; done;" ]
( test.yaml ) kubectl create -f test.yaml
حسنًا ، لقد شهدنا
deployment.apps test created
... ولكن هل نجح ذلك؟
kubectl describe pods test Name: test-6f79f9f8bd-r7tbd Namespace: default Node: docker-for-desktop/192.168.65.3 Start Time: Sat, 10 Nov 2018 16:08:47 -0500 Labels: component=test foo=bar
عظيم! على الرغم من أن
test.yaml
أعطيت تسمية واحدة (
component
) ، فإن الجراب الناتج حصل على اثنين:
component
و
foo
.
واجب منزلي
لكن انتظر! هل سنستخدم هذا الرمز لإنشاء حاوية جانبية؟ لقد حذرت من أنني سأوضح
كيفية إضافة السيارة المسحوبة ... والآن ، بالمعرفة والرمز:
https://github.com/dowjones/k8s-webhook - لا تتردد في تجربة ومعرفة كيفية جعل السيارة المسحوبة تلقائيًا. الأمر بسيط للغاية: تحتاج فقط إلى إعداد تصحيح JSON الصحيح ، والذي سيضيف حاوية إضافية في اختبار النشر. تزامن سعيد!
ملاحظة من المترجم
اقرأ أيضًا في مدونتنا: