Railshot
AI deployment & operations platform
نشر تطبيق ويب مبني محليًا على بيئات سحابية ومحلية مُعدّة، ثم تشغيله.
منصة نشر وتشغيل بالذكاء الاصطناعي تنقل تطبيق ويب مبنيًا محليًا إلى بيئات سحابية أو محلية مُعدّة وتواصل تشغيله.
عنقود التشغيل وعناقيد تطبيقات العملاء منفصلة، والمسار العام مفصول عن التحكم في النشر.
في K3s الخاص بالتشغيل، توضع لوحة المعلومات وAPI المنتج وbuild controller على عُقد بدور platform، وJobs الخاصة بـ runner على عقدة بدور build، بينما يتولى Argo CD النشر. وتعمل تطبيقات العملاء في namespace مخصص من عنقود K3s + Cilium منفصل بعقدة واحدة لكل هدف مسجّل.
البناء → نشر الصورة
يطلب API المنتج تشغيل GitHub Actions، فيتسلّم runner على عقدة build الـ job ويفحصه. ولا ينشر release job المستضاف على GitHub إلى GHCR إلا bundle اجتاز الفحوص نفسها.
API المنتج · cd.js → bridge.py
railshot-systemتشغيل · تطبيق: workflow_dispatchloop job · self-hosted runnerrailshot-deploy.ymlيطلب API المنتج تشغيل GitHub Actions بـ tenant وapp وsource commit وtarget. ولا يستدعي API أي runner Job مباشرة أبدًا.
build controller · CronJob
railshot-systemتشغيل · تطبيق: تجديد runner Jobrunner Job · hostNetwork · Docker socketrailshot-buildينشئ CronJob الخاص بـ build controller (كل دقيقة، على عقدة platform) runner Jobs في namespace المسمّى railshot-build. ولا يسمح Role الخاص به إلا بـ get وlist وcreate على Job، وget وupdate وpatch على Secret تسجيل runner الوحيد، ولا يستطيع إنشاء namespaces أو Secrets أخرى.
runner Job · hostNetwork · Docker socket
railshot-buildتشغيل · تطبيق: تسلّم الـ jobloop job · self-hosted runnerrailshot-deploy.ymlيتسلّم runner على عقدة دور build (بـ hostname محدد وtoleration من نوع NoSchedule) الـ loop job بحسب وسوم self-hosted ويشغّله. ويعمل مصدر العميل في حاويات Docker المقيّدة القائمة للبناء والفحص.
runner Job · hostNetwork · Docker socket
railshot-buildبيانات: bundle مُختبَرrelease job · GitHub-hostedrailshot-deploy.ymlيترك loop job الـ bundle الذي اجتاز الفحوص نفسها، ولا يأخذ release job إلا ذلك الـ bundle.
release job · GitHub-hosted
railshot-deploy.ymlبيانات: النشر بحسب digestGHCRينشر release job مستضاف على GitHub (ubuntu-24.04) صورة الـ bundle المُختبَر إلى GHCR دون إعادة بنائها. ولا يحدث نشر الصور أبدًا داخل عنقود تطبيقات العميل.
config Git → Argo → namespace العميل
يحمل config Git الموارد المطلوبة، ويحمل GHCR بايتات الصورة. ولا يطبّق Argo CD إلا على namespace العميل المسجّل.
API المنتج · cd.js → bridge.py
railshot-systemبيانات: workload commit · pushمستودع config Gitيشغّل cd.js الملف bridge.py، الذي لا يودِع workload.json ولا يدفعه إلا حين يتفق سجل النشر والهدف المسجّل، ثم يعيد قراءة revision البعيدة. ويحمل Git الموارد المطلوبة، ويحمل GHCR بايتات الصورة.
مستودع config Gitبيانات: revisionArgo CD · Application
argocdلا يقرأ Application إلا مستودع config والمسار وrevision المقيّدة الخاصة به.
API المنتج · cd.js → bridge.py
railshot-systemتشغيل · تطبيق: Application syncArgo CD · Applicationargocdيُضبط Application المسمّى عبر server-side apply، ويُطلب sync لـ revision واحدة محددة دون prune. ويحمل ServiceAccount المسمّى railshot-product الخاص بـ API المنتج كلًّا من صلاحيات registration bootstrap التي ثبّتها المشغّل (create لـ Secrets وAppProjects وApplications في argocd، وget وupdate وescalate على Roles مسمّاة) وصلاحيات get وpatch تُضاف بالاسم الدقيق لكل هدف مُتحقَّق منه. ولا يمكن حصر create في Kubernetes بالاسم، لذا يُبقي التحقق من مُدخلات المنتج الأسماءَ ضمن النطاق أيضًا.
Argo CD · Application
argocdتشغيل · تطبيق: التطبيق على namespace<app namespace>يطبّق Argo CD على namespace المحدد في عنقود العميل ببيانات اعتماد cluster Secret المسجّل. ولا يسمح AppProject إلا بأنواع ضمن namespace، ولا بموارد على مستوى العنقود.
Deployment · Pod · صورة digest
<app namespace>بيانات: السحب بحسب digestGHCRتُسحب صورة الـ digest المنشورة بـ pull Secret مُشار إليه بالاسم في namespace نفسه. ولا تدخل قيم Secret أبدًا في المُخرَج ولا في config Git.
مسار الطلب العام
تمر طلبات التطبيق: نقطة دخول L7 لدى المزوّد → NodePort على العقدة → Service → Pod التطبيق. وفرض السياسة في Cilium ضابط منفصل، لا قفزة تحمل الطلب. والدخول إلى لوحة التشغيل مسار منفصل.
مستخدم التطبيقبيانات: HTTPS 443نقطة دخول L7 لدى المزوّد
يصل مستخدمو التطبيق إلى نقطة دخول L7 لدى المزوّد عبر hostname المسجّل. ويطبّق كل مزوّد نقطة دخوله الخاصة.
نقطة دخول L7 لدى المزوّدبيانات: NodePortNodePort Service · Local
<app namespace>تمرّر نقطة الدخول إلى العنوان الخاص للعقدة وNodePort. وNodePort هو المخصَّص عند تسجيل الهدف.
NodePort Service · Local
<app namespace>بيانات: إلى PodDeployment · Pod · صورة digest<app namespace>يرسل Service الطلب إلى Pod التطبيق (Deployment) على العقدة نفسها. ومع externalTrafficPolicy Local، يصل عنوان المصدر إلى فحص CIDR المسجّل في NetworkPolicy دون تغيير.
Cilium · Helm · VXLAN
kube-systemتشغيل · تطبيق: فرض السياسةNetworkPolicy · CIDRs المدرجة<app namespace>يفرض Cilium سياسة NetworkPolicy: الدخول فقط من CIDRs المسجّلة إلى منفذ التطبيق، ولا خروج افتراضيًا إلا إلى عنوان قاعدة بيانات معتمد. وتظل جدران الحماية السحابية وقواعد المضيف مهمة.
مستخدم Railshotبيانات: لوحة المعلومات · APIdashboard · nginx
railshot-systemيدخل المستخدمون عبر نقطة دخول التشغيل إلى لوحة nginx، ويقف nginx أمام API المنتج. ولا يكون Service الخاص باللوحة NodePort إلا عند إعطاء node port معتمد، ويبقى API من نوع ClusterIP.
التحقق · المراقبة
لا يستند حكم النشر إلا إلى حالة Argo وفحص URL العام. أما تشخيصات Pod والسجلات الحالية ومقاييس التشغيل فإشارات منفصلة لا تغيّره أبدًا.
API المنتج · cd.js → bridge.py
railshot-systemمراقبة: revision · health · الصورArgo CD · Applicationargocdيتطلب deployed وجود sync revision الخاصة بـ Application، وSynced وHealthy، وoperation Succeeded، وأن تساوي مراجع الصور المسجّلة في حالة Argo Application صورَ digest الخاصة بـ Deployment المراجَع. وليس بوابة مباشرة على imageID لدى Pod، ولا تُثبت حالة Argo الخضراء وحدها هذا التطابق.
API المنتج · cd.js → bridge.py
railshot-systemمراقبة: فحص HTTPS العامنقطة دخول L7 لدى المزوّديُفحص URL العام المسجّل دون إعادة توجيه. ولا يُسجَّل succeeded إلا بحكم Argo وهذا الفحص معًا.
API المنتج · cd.js → bridge.py
railshot-systemمراقبة: تشخيص Pod · ليس الحكمDeployment · Pod · صورة digest<app namespace>بعد حكم Argo والفحص العام، تُقرأ Deployment وReplicaSets وPods على حدة ببيانات الاعتماد المسجّلة، ويُضاف imageID والجاهزية تشخيصًا بقيمة ready أو progressing أو unavailable. ولا يغيّر التشخيص الفاشل أو المختلف حكم deployed أو succeeded أبدًا.
API المنتج · cd.js → bridge.py
railshot-systemمراقبة: ذيل السجل · 100 سطر لكل PodDeployment · Pod · صورة digest<app namespace>فقط لـ operation المنشورة حاليًا، بالـ config الموثوق وبيانات الاعتماد المسجّلة. ولكل Pod حالي (ثلاثة على الأكثر) يجب أن يساوي digest الصورة العاملة الـ digest المتوقع، ثم يُقرأ ما يصل إلى 100 سطر منقّح لكل Pod ضمن حد للبايتات. وتُجيب المحاولة الأقدم بـ superseded، ومن دون إعداد بـ not_configured. وليس هذا تجميعًا للسجلات.
VM المراقبةمراقبة: scrape · منافذ NodePortrailshot-observability
يجمع Prometheus على VM المراقبة بيانات node-exporter وkube-state-metrics عبر منفذي NodePort مقيّدين بجدار الحماية. والقيم المفقودة أو القديمة غير معروفة، لا صفر.
VM المراقبةمراقبة: Blackbox probeنقطة دخول L7 لدى المزوّد
يعني probe_success=1 استجابة 2xx في موضع الفحص ذاك. ولا يثبت وصول كل مستخدم ولا النسخة المنشورة، ولا يرقّي published إلى deployed أبدًا.
تثبيت المشغّل · التسجيل
تثبيت العنقود، وbootstrap التسجيل ومُدخلات الهدف، وSecrets، وإعداد تجديد بيانات الاعتماد، كلها مُدخلات للمشغّل، وليست أبدًا جزءًا من حمولة تطبيق المستأجر.
bootstrap المشغّلتشغيل · تطبيق: توليد platform · Secretrailshot-system
يولّد المشغّل إعلان platform باستخدام render-platform.py، ويوفّر إعداد التشغيل الخاص وSecrets. ولا يُدرج MCP البعيد إلا عند توفير صورته، ولا البوابة الشخصية (WireGuard) إلا مع --personal.
bootstrap المشغّلتشغيل · تطبيق: bootstrap التسجيلAppProject · cluster Secret
argocdيثبّت المشغّل Role الخاص بـ bootstrap التسجيل وRole فارغًا للتسجيلات في argocd، ويُعِدّ مُدخلات الهدف. ثم تكتب أدوات التسجيل المساعدة التي يشغّلها API المنتج (environment.py، applications.py) الـ Secret المسمّى railshot-<target> ببيانات اعتماد محددة النطاق (clusterResources false، مع إدراج namespaces المسموح بها)، وتُلحق الأسماء الدقيقة المُتحقَّق منها بـ Role التسجيلات. ويربط التسجيل لكل تطبيق namespace باسم app-<id> بـ AppProject يحمل الاسم نفسه.
bootstrap المشغّلتشغيل · تطبيق: تثبيت K3s · CiliumCilium · Helm · VXLAN
kube-systemيثبّت install-k3s.sh خادم K3s بعقدة واحدة (مع تعطيل flannel وnetwork policy وtraefik وservicelb، والإبقاء على kube-proxy)، ويُثبَّت Cilium بوصفه Helm release (kubeProxyReplacement false، وVXLAN، وتعطيل Hubble).
CronJob للتجديد · كل 2 h
argocdتشغيل · تطبيق: TokenRequest · 6 hrailshot-argocd SA · رمز مميّز<app namespace>يطلب CronJob المسمّى railshot-credentials رمزًا جديدًا صالحًا ست ساعات لـ SA العميل كل ساعتين. ولا يسمح Role على جهة العميل إلا بـ token create لـ SA الخاص به. والساعات الست هي TTL للرمز الواحد، لا موعد انتهاء وصول المشغّل.
كل الروابط بالسماكة نفسها. اختر مسارًا لإبراز طريقه وحده، مع خطوات مرقّمة.
- خط متصل · اتجاه تدفق الطلبات والـ bundles والصور والإعلانات
- خط متقطع · طلبات التشغيل، التطبيق، التثبيت، بيانات الاعتماد
- شرطة طويلة ونقطة · قراءة الحالة والسجلات وprobes والمقاييس، بمعزل عن الحكم
- خط أسود سميك · المسار المختار، والأرقام تبيّن الترتيب
- مربع · namespace، واسمه في الترويسة (ليس عقدة)
- شريحة داخلية · مكوّن في ذلك الـ namespace
- مربع متقطع · خارج العناقيد
- railshot-system · المنتج
- argocd · سلطة النشر
- railshot-build · سلطة Docker على المضيف
- GitHub · عنقود العميل
قرار التوزيع بتاريخ October 2 في docs/architecture (عقدتا دور) ومخطط README العام تاريخ، وهذه الخريطة تتبع الإعلانات والشيفرة عند 645e28a. والأسهم تُظهر السبب والاتجاه، لا دليلًا على أن كل مسار مزوّد يعمل الآن.
- Namespaces والعُقد
- الـ namespace نطاق تسمية: ليس عتادًا، ولا حدًّا أمنيًا بحد ذاته. ودورا platform وbuild توزيع جدولة عبر nodeSelector وtaint، ولا تحدد هذه الخريطة عدد عُقد عنقود التشغيل. وتوزيع Argo CD على العُقد فحص منفصل أيضًا.
- سلطة عقدة build
- تعمل runner Jobs بـ hostNetwork ومقبس Docker على المضيف وNET_ADMIN وroot. ولا يُزيل namespace أو taint أو RBAC سلطة Docker على المضيف، فهذا ليس عزلًا عن مسؤول VM ولا sandbox لشيفرة عشوائية.
- namespace العميل
- يستخدم كل هدف مسجّل namespace وAppProject غير افتراضيين (مثلًا tenant-demo، أو app-<id> لكل تطبيق). ولا يُنشأ إلا Deployment لخدمة واحدة (مع تعطيل التحميل التلقائي لرمز ServiceAccount) وNodePort Service وNetworkPolicy، إضافة إلى PVC أو migration Job اختياريين، ولا يُشار إلى Secrets إلا بالاسم (pull Secret وDATABASE_URL معتمد)، ولا تُنشأ أبدًا.
- نقطة دخول AWS
- Route 53 Alias → ALB مشترك HTTPS 443 → العنوان الخاص لعقدة العميل وNodePort. وتعيد تطبيقات كثيرة استخدام عقدة واحدة، لكلٍّ منها namespace وApplication وNodePort خاص بها.
- نقطة دخول GCP
- HTTPS LB عام من نوع EXTERNAL_MANAGED → NEG نطاقي من نوع GCE_VM_IP_PORT → NodePort الخاص بالـ VM القائم. وتأتي الشهادات من Certificate Manager مع تفويض DNS. ولا تملك الوحدة الـ VM ولا الـ VPC، ويبقى الـ VM الواحد نقطة إخفاق وحيدة.
- نقطة دخول OpenStack
- Amphora TLS listener 443 مع سياسات L7 حسب Host والمسار → HTTP NodePort. والعنوان الافتراضي VIP خاص، وOctavia وK3s وقاعدة البيانات وDNS خارج الوحدة. ويحتاج الوصول العام وجاهزية Amphora في الموقع إلى قبول منفصل، ولم يُتحقَّق بعد من أي تطبيق في الموقع بهذه الوحدة.
- عبر المزوّدين
- نقاط الدخول الثلاث تطبيقات منفصلة، بلا failover أو نسخ متماثل تلقائي بين المزوّدين. وعنقود تطبيقات العميل مثال على هدف مسجّل، لا ادعاء بأن النشر نفسه يعمل على كل سحابة في آن واحد.
- خيارات المشغّل
- لا تنضم البوابة الشخصية (WireGuard) إلى platform إلا حين يفعّل المشغّل --personal، وليست مسارًا مطلوبًا لسحابات العملاء. وفي وضع direct المدعوم، يمكن أن يتشارك VM المراقبة مع VM التحكم في التشغيل.