CI/CD والبنية التحتية كرمز

البنية التحتية كرمز مع Terraform

4 دقيقة للقراءة

Terraform هو المعيار الصناعي لـ IaC. كل مقابلة DevOps/SRE ستختبر معرفتك.

مفاهيم Terraform الأساسية

إدارة الحالة

حالة Terraform تتتبع البنية التحتية:

# عمليات الحالة
terraform state list              # قائمة الموارد
terraform state show <resource>   # عرض التفاصيل
terraform state mv <src> <dst>    # نقل/إعادة تسمية
terraform state rm <resource>     # إزالة من الحالة
terraform state pull              # تنزيل الحالة
terraform state push              # رفع الحالة (خطير!)

Backend الحالة البعيدة

# backend.tf
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/infrastructure.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"  # قفل الحالة
  }
}

سؤال مقابلة: "لماذا تستخدم الحالة البعيدة مع القفل؟"

الجواب: الحالة البعيدة تُمكّن تعاون الفريق—الجميع يرى نفس الحالة. القفل (عبر DynamoDB) يمنع التعديلات المتزامنة التي قد تُفسد الحالة أو تسبب تغييرات متعارضة.

تصميم وحدات Terraform

بنية الوحدة

modules/
├── vpc/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── README.md
├── eks/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
└── rds/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

مثال وحدة قابلة لإعادة الاستخدام

# modules/vpc/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(var.tags, {
    Name = "${var.name}-vpc"
  })
}

resource "aws_subnet" "private" {
  count             = length(var.private_subnets)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnets[count.index]
  availability_zone = var.azs[count.index]

  tags = merge(var.tags, {
    Name = "${var.name}-private-${count.index + 1}"
    Type = "private"
  })
}

# modules/vpc/variables.tf
variable "name" {
  type        = string
  description = "بادئة اسم الموارد"
}

variable "cidr_block" {
  type        = string
  description = "كتلة CIDR لـ VPC"
}

variable "private_subnets" {
  type        = list(string)
  description = "كتل CIDR للشبكات الفرعية الخاصة"
}

# modules/vpc/outputs.tf
output "vpc_id" {
  value       = aws_vpc.main.id
  description = "معرف VPC"
}

output "private_subnet_ids" {
  value       = aws_subnet.private[*].id
  description = "معرفات الشبكات الفرعية الخاصة"
}

استخدام الوحدات

# environments/prod/main.tf
module "vpc" {
  source = "../../modules/vpc"

  name            = "prod"
  cidr_block      = "10.0.0.0/16"
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

module "eks" {
  source = "../../modules/eks"

  cluster_name = "prod-cluster"
  vpc_id       = module.vpc.vpc_id
  subnet_ids   = module.vpc.private_subnet_ids
}

مساحات عمل Terraform

# قائمة مساحات العمل
terraform workspace list

# إنشاء مساحة عمل
terraform workspace new staging

# التبديل لمساحة عمل
terraform workspace select prod

# مساحة العمل الحالية في الكود
resource "aws_instance" "web" {
  instance_type = terraform.workspace == "prod" ? "m5.large" : "t3.small"
}

بديل أفضل: استخدم مجلدات منفصلة لكل بيئة بدلاً من مساحات العمل لأنظمة الإنتاج. أسهل للفهم وأقل عرضة للأخطاء.

أنماط Terraform المتقدمة

مصادر البيانات

# جلب الموارد الموجودة
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.small"
}

الكتل الديناميكية

resource "aws_security_group" "web" {
  name = "web-sg"

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = "tcp"
      cidr_blocks = ingress.value.cidrs
    }
  }
}

استيراد الموارد الموجودة

# استيراد البنية التحتية الموجودة
terraform import aws_instance.web i-1234567890abcdef0

# توليد التكوين (Terraform 1.5+)
terraform plan -generate-config-out=generated.tf

أسئلة المقابلة

س: "كيف تتعامل مع الأسرار في Terraform؟"

# 1. متغيرات البيئة
# export TF_VAR_db_password="secret"
variable "db_password" {
  type      = string
  sensitive = true  # يُخفي في المخرجات
}

# 2. AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "prod/database/password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db.secret_string
}

# 3. مزود Vault
data "vault_generic_secret" "db" {
  path = "secret/database"
}

س: "فشل terraform apply في المنتصف. كيف تسترد؟"

  1. تحقق من الحالة: terraform state list - انظر ما تم إنشاؤه
  2. راجع الخطأ: افهم لماذا فشل
  3. أصلح وأعد: terraform apply متساوي القوة
  4. إذا الحالة تالفة: استرجع من نسخة احتياطية أو أصلح يدوياً
  5. Taint إذا لزم: terraform taint <resource> لإجبار إعادة الإنشاء

س: "كيف تمنع التدمير العرضي للموارد الحرجة؟"

resource "aws_db_instance" "production" {
  # منع التدمير
  lifecycle {
    prevent_destroy = true
  }

  # أو تجاهل التغييرات لسمات محددة
  lifecycle {
    ignore_changes = [tags]
  }
}

التالي، سنغطي استراتيجيات النشر—blue-green، canary، والنشر المتدرج. :::

اختبار

الوحدة 3: CI/CD والبنية التحتية كرمز

خذ الاختبار