دليل تقني

Infrastructure as Code مع 200 مورد: اللي ما تقوله لك دروس Terraform

أنماط IaC إنتاجية لأنظمة حقيقية. إدارة الـ State على نطاق واسع، تصميم الوحدات، CDK + Terraform هجين، كشف الانحراف، GitOps مع Flux، وإدارة 30+ خدمة AWS.

20 أبريل 202616 دقيقة للقراءةفريق هندسة أورنتس

الـ IaC مش "terraform init"

كل دورة Terraform تبدأ بنفس الطريقة: اكتب ملف .tf، نفذ terraform init، نفذ terraform apply، وشاهد الـ EC2 instance تظهر. هذا يوصلك من صفر لمورد واحد. ما يجهزك لإدارة 200+ مورد عبر 30 خدمة AWS مع فريق مهندسين كلهم يحتاجون يسوون تغييرات على البنية التحتية بأمان.

نحن ندير بنية تحتية لعدة أنظمة إنتاجية، من كلاسترات Kubernetes مع Pimcore و OpenSearch لمعماريات Serverless مع Lambda و DynamoDB و API Gateway. الأنماط في هذا المقال هي اللي نجت في الإنتاج. لطريقة نشر التطبيقات على هذي البنية التحتية، شوف صفحة خدماتنا السحابية.

إدارة الـ State على نطاق واسع

الـ Terraform State هو أهم ملف في بنيتك التحتية. يربط ملفات .tf بموارد حقيقية. ضيعه، و Terraform ما يعرف شو موجود. خربه، و Terraform ممكن يدمر موارد الإنتاج.

Remote State (ما فيه نقاش)

# backend.tf
terraform {
    backend "s3" {
        bucket         = "company-terraform-state"
        key            = "prod/platform/terraform.tfstate"
        region         = "eu-central-1"
        encrypt        = true
        dynamodb_table = "terraform-locks"
    }
}
القاعدةليش
Remote State في S3 (أو ما يعادله)ملفات State المحلية تضيع، ما تقدر تشاركها
تشفير في حالة السكونالـ State يحتوي على أسرار (كلمات مرور قواعد البيانات، مفاتيح API)
DynamoDB Lockingيمنع مهندسَين من تنفيذ apply بنفس الوقت
تفعيل الإصدارات على S3 bucketاسترجاع من فساد الـ State بالرجوع لنسخة سابقة

تنظيم الـ State

ملف State واحد ضخم لكل شي هو كارثة صيانة. قسمه حسب البيئة والنطاق:

terraform/
├── environments/
│   ├── prod/
│   │   ├── platform/       # EKS, VPC, الشبكات
│   │   ├── databases/      # RDS, ElastiCache, OpenSearch
│   │   ├── compute/        # Lambda, ECS, Fargate
│   │   ├── storage/        # S3 buckets, CloudFront
│   │   └── monitoring/     # CloudWatch, التنبيهات
│   ├── staging/
│   │   └── (نفس الهيكل)
│   └── dev/
│       └── (نفس الهيكل)
├── modules/
│   ├── vpc/
│   ├── eks-cluster/
│   ├── rds-postgres/
│   ├── opensearch/
│   ├── redis/
│   └── lambda-function/
└── global/
    ├── iam/                 # أدوار IAM، السياسات
    ├── route53/             # مناطق DNS
    └── ecr/                 # سجلات الحاويات

كل مجلد هو Terraform workspace منفصل بملف State خاص فيه. تغييرات على الشبكة ما تخاطر بكسر قاعدة البيانات. تغييرات على المراقبة ما تحتاج خطة تلمس كل مورد.

مراجع Cross-State

الـ Workspaces تحتاج تشير لبعض. الـ VPC workspace يطلع VPC ID. الـ database workspace يقرأها:

# في databases/main.tf
data "terraform_remote_state" "platform" {
    backend = "s3"
    config = {
        bucket = "company-terraform-state"
        key    = "prod/platform/terraform.tfstate"
        region = "eu-central-1"
    }
}

resource "aws_db_instance" "main" {
    vpc_security_group_ids = [data.terraform_remote_state.platform.outputs.db_security_group_id]
    db_subnet_group_name   = data.terraform_remote_state.platform.outputs.db_subnet_group_name
}

تصميم الوحدات

متى تستخرج وحدة

مش كل مورد يحتاج وحدة. استخرج لما:

  • نفس النمط مستخدم في 3+ أماكن (DRY)
  • مجموعة الموارد عندها حدود واضحة (VPC، كلاستر قاعدة بيانات)
  • التكوين عنده defaults منطقية تقلل التكرار

لا تستخرج لما:

  • مستخدم مرة وحدة (تجريد مبكر)
  • الوحدة راح يكون عندها 20+ متغير (كثير مفاتيح)
  • التجريد يخفي تفاصيل مهمة (الشبكات، الأمان)

تصميم واجهة الوحدة

الوحدة الجيدة عندها متغيرات مطلوبة قليلة، defaults منطقية، و outputs واضحة:

# modules/rds-postgres/variables.tf
variable "name" {
    description = "اسم مثيل قاعدة البيانات"
    type        = string
}

variable "vpc_id" {
    description = "الـ VPC للنشر فيها"
    type        = string
}

variable "subnet_ids" {
    description = "الشبكات الفرعية لمجموعة DB subnet"
    type        = list(string)
}

variable "instance_class" {
    description = "نوع مثيل RDS"
    type        = string
    default     = "db.t3.medium"
}

variable "engine_version" {
    description = "إصدار PostgreSQL"
    type        = string
    default     = "15.4"
}

variable "allocated_storage" {
    description = "التخزين بالـ GB"
    type        = number
    default     = 50
}

variable "multi_az" {
    description = "تفعيل النشر متعدد المناطق"
    type        = bool
    default     = false  # true للإنتاج، false للتجريب/التطوير
}

مستهلك الوحدة يكتب:

module "database" {
    source     = "../../modules/rds-postgres"
    name       = "pimcore-prod"
    vpc_id     = module.vpc.vpc_id
    subnet_ids = module.vpc.private_subnet_ids
    multi_az   = true
}

خمس أسطر بدل خمسين. الوحدة تتكفل بالـ Security Groups، Parameter Groups، مجموعات الشبكات الفرعية، التشفير، الاحتفاظ بالنسخ الاحتياطية، والمراقبة.

CDK + Terraform: النهج العملي الهجين

بعض الفرق تروح كلياً مع CDK. وبعضها كلياً مع Terraform. نحن نستخدم الاثنين، ويشتغل.

حالة الاستخدامالأداةليش
الشبكات، قواعد البيانات، الكلاستراتTerraformتصريحي، خطة قبل التطبيق، إدارة State
دوال Lambda + API GatewayCDKتجميع Lambda أفضل، API Gateway constructs
سياسات IAM المعقدةCDKمنطق TypeScript لسياسات شرطية
موارد KubernetesKustomize + FluxGitOps، حلقات Reconciliation
بنية تحتية ثابتةTerraformبسيط، مقروء، مفهوم

الحدود واضحة: Terraform يدير البنية التحتية اللي تتغير نادراً (VPC، RDS، كلاستر EKS). CDK يدير البنية التحتية اللي تتغير مع نشر التطبيقات (دوال Lambda، مسارات API). Kustomize + Flux يدير أعباء عمل Kubernetes.

يتعايشون عن طريق الـ Outputs. Terraform يطلع VPC ID، ونقطة نهاية الكلاستر، و connection string لقاعدة البيانات. CDK يقرأهم من SSM Parameter Store أو Terraform remote state.

مشكلة الانحراف (Drift)

الانحراف يصير لما أحد يغير البنية التحتية من الكونسول (ClickOps)، من أمر CLI، أو من أداة ثانية. الحالة الحقيقية تبتعد عن الـ Terraform State.

كشف الانحراف

# نفذ plan بانتظام (CI، وظيفة مجدولة)
terraform plan -detailed-exitcode

# أكواد الخروج:
# 0 = ما فيه تغييرات (الـ State يطابق الواقع)
# 1 = خطأ
# 2 = تغييرات مكتشفة (انحراف!)

شغل كشف الانحراف في CI بجدول (يومياً للإنتاج، أسبوعياً للتجريب). نبه لما ينكشف انحراف. لا تصلح تلقائي. تحقق أول.

أسباب الانحراف الشائعة

السببالوقاية
تغييرات من الكونسول (ClickOps)فرض سياسة "ممنوع تغييرات من الكونسول". استخدم SCPs للتقييد.
تغييرات Auto-Scalingتجاهل سمات Auto-Scaling في Terraform (lifecycle { ignore_changes })
تحديثات خدمات AWSثبت إصدارات الـ Provider. حدث بوعي.
Terraform فريق ثانيملفات State منفصلة لكل فريق/نطاق.
إصلاح يدوي أثناء حادثوثق التغيير. طبقه في Terraform بعد الحادث.
# تجاهل تغييرات Auto-Scaling (انحراف متوقع)
resource "aws_ecs_service" "app" {
    desired_count = 2

    lifecycle {
        ignore_changes = [desired_count]  # Auto-Scaling يغير هذي القيمة
    }
}

GitOps مع Flux

لأعباء عمل Kubernetes، نستخدم Flux لـ GitOps. حلقة الـ Reconciliation تستبدل kubectl apply بنموذج قائم على السحب.

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Git Repo    │────▶│  Flux       │────▶│  Kubernetes  │
│  (Manifests) │     │  Controller │     │  Cluster     │
└─────────────┘     └─────────────┘     └─────────────┘
                          │
                          │ يوفق كل 1 دقيقة
                          │ يكشف الانحراف
                          │ يطبق تلقائياً
# flux-system/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
    name: platform
    namespace: flux-system
spec:
    interval: 1m
    sourceRef:
        kind: GitRepository
        name: infrastructure
    path: ./kubernetes/resources/overlay/prod
    prune: true
    healthChecks:
        - apiVersion: apps/v1
          kind: Deployment
          name: pimcore
          namespace: production

Flux يستطلع Git repo كل دقيقة. إذا تغيرت الـ Manifests، يطبقها. إذا أحد غير مورد يدوياً (انحراف)، Flux يرجعه ليطابق Git. هذا reconciliation حقيقي، مش مجرد أتمتة نشر.

Sealed Secrets

الأسرار ما تنحط في Git كنص عادي. استخدم Bitnami Sealed Secrets:

# تشفير السر للكلاستر
kubeseal --cert sealed-secrets.pem \
    -f secrets/database-secrets.yaml \
    -o yaml > secrets/database-secrets-sealed.yaml

# ارفع النسخة المختومة (آمنة في Git)
# Flux يطبقها، الـ controller يفك تشفيرها داخل الكلاستر

لطريقة تعاملنا مع الأسرار في نشر Pimcore على Kubernetes بالتحديد، شوف دليل ترقية Pimcore اللي يغطي ترتيب النشر الكامل.

إدارة 30+ خدمة AWS

على مستوى المؤسسات، أنت تدير خدمات كثيرة. التنظيم مهم.

كتالوج الخدمات

الفئةالخدماتوحدة Terraform؟
الشبكاتVPC، شبكات فرعية، NAT، ALB، Route53نعم (وحدة vpc)
الحوسبةEKS، ECS Fargate، Lambdaنعم (لكل نوع خدمة)
قاعدة البياناتRDS PostgreSQL، DynamoDBنعم (وحدة rds-postgres)
التخزين المؤقتElastiCache Redisنعم (وحدة redis)
البحثOpenSearchنعم (وحدة opensearch)
التخزينS3، EFSمضمن (بسيط بما فيه الكفاية)
CDNCloudFrontمضمن
الرسائلSQS، MSK (Kafka)، RabbitMQمضمن
المصادقةCognitoCDK (تكوين معقد)
المراقبةCloudWatch، X-Rayمضمن
CI/CDECR، CodeBuildمضمن
الأمانIAM، KMS، Secrets ManagerWorkspace عام

استراتيجية التوسيم (Tagging)

كل مورد لازم يكون موسوم لتوزيع التكاليف، تحديد الملكية، وإدارة دورة الحياة:

locals {
    common_tags = {
        Environment = var.environment    # prod, staging, dev
        Project     = var.project_name   # pimcore, commerce, ai
        ManagedBy   = "terraform"
        Team        = var.team           # platform, backend, data
        CostCenter  = var.cost_center
    }
}

resource "aws_instance" "example" {
    tags = merge(local.common_tags, {
        Name = "pimcore-web-1"
        Role = "web"
    })
}

فلتر AWS Cost Explorer بتاق Project عشان تشوف بالضبط كم يكلف كل نظام. فلتر بـ ManagedBy عشان تلاقي الموارد المنشأة يدوياً (مش موسومة كـ "terraform").

الأخطاء الشائعة

  1. ملف State واحد لكل شي. terraform plan يلمس 200 مورد ياخذ دقائق وخطأ واحد يأثر على كل شي. قسم حسب النطاق والبيئة.

  2. بدون State Locking. مهندسَين ينفذون terraform apply بنفس الوقت. تغييرات واحد تضيع أو الـ State يتخرب. استخدم DynamoDB Locking.

  3. وحدات بـ 20+ متغير. إذا واجهة وحدتك معقدة بنفس درجة الموارد الخام، التجريد ما يضيف قيمة. خلي واجهات الوحدات صغيرة.

  4. إصلاح الانحراف تلقائياً. كشف الانحراف حلو. إصلاحه تلقائياً خطير. "الانحراف" ممكن يكون إصلاح صحيح أثناء حادث. تحقق قبل ما ترجع.

  5. أسرار في ملفات الـ State. الـ Terraform State يحتوي كل سمة لكل مورد، بما فيها كلمات مرور قواعد البيانات. شفر الـ State في حالة السكون وقيد الوصول.

  6. بدون تثبيت إصدار الـ Provider. تحديث provider يغير سلوك الموارد. ثبت الإصدارات في required_providers وحدث بوعي.

  7. ClickOps لـ "بس هالمرة". تغييرات الكونسول تسبب انحراف غير مرئي لحد أول terraform plan جاي. فرض Infrastructure-as-Code لكل شي.

  8. بدون توسيم. بدون تاقات، ما تقدر توزع التكاليف، ولا تحدد الملكية، ولا تلاقي الموارد المنشأة يدوياً.

أهم النقاط

  • قسم الـ State حسب النطاق والبيئة. الشبكات، قواعد البيانات، الحوسبة، والمراقبة لازم تكون workspaces منفصلة. تغييرات على واحد ما لازم تخاطر بالثاني.

  • الوحدات للأنماط، مش للتجريد. استخرج لما نفس مجموعة الموارد تظهر 3+ مرات. لا تسوي وحدات لموارد تستخدم مرة وحدة.

  • CDK + Terraform نهج عملي. Terraform للبنية التحتية الثابتة، CDK لـ Lambda/API Gateway، Kustomize + Flux لـ Kubernetes. كل أداة وين ما هي أقوى.

  • كشف الانحراف وظيفة مجدولة. شغل terraform plan يومياً في CI. نبه على الانحراف. تحقق قبل ما تصلح.

  • GitOps مع Flux يعطيك reconciliation حقيقي. مش مجرد أتمتة نشر. Flux يكشف ويرجع التغييرات اليدوية. Sealed Secrets تخلي بيانات الاعتماد آمنة في Git.

  • وسم كل شي. البيئة، المشروع، الفريق، مركز التكلفة، managed-by. بدون تاقات، توزيع التكاليف وتدقيق الموارد مستحيل.

نحن ندير بنية تحتية لـ النشر السحابي، منصات البرمجيات المخصصة، وأنظمة هندسة البيانات. إذا تحتاج مساعدة بالـ IaC على نطاق واسع، تواصل مع فريقنا أو اطلب عرض سعر.

المواضيع المغطاة

Terraform إنتاجدروس IaCInfrastructure as Code عمليTerraform AWSCDK مقابل Terraformإدارة Terraform StateGitOps Fluxوحدات Terraform

جاهز لبناء أنظمة ذكاء اصطناعي جاهزة للإنتاج؟

فريقنا متخصص في بناء أنظمة ذكاء اصطناعي جاهزة للإنتاج. خلينا نحكي كيف نقدر نساعد.

ابدأ محادثة