Kotlin: شرح نشر مشروع كوتلن
آخر تحديث: 2026-08-26
الكود مكتوب — الآن المحطة الأخيرة. يقوم تشارلي بتحويل OrderProcessor إلى حاويات، وينسق خط أنابيب CI/CD، ويضبط المراقبة والتنبيهات، مما يجعل النشر من git push إلى الإنتاج مؤتمتًا بالكامل.
1. ما ستتعلمه
- البناء متعدد المراحل في Docker:
gradle:jdk→ صورة JRE خفيفة - Docker Compose: OrderProcessor + PostgreSQL + Redis
- CI/CD: GitHub Actions للبناء والاختبار ودفع الصورة
- المراقبة: لوحة مقاييس Micrometer + Prometheus + Grafana
- تطبيق عملي لتشارلي: نشر بنقرة واحدة + فحوصات الصحة + قائمة جاهزية الإنتاج
2. قصة حقيقية لمهندس معماري
(1) المشكلة: خط نشر يدوي
كان فريق تشارلي ينشر يدويًا: SSH إلى الخادم → git pull → gradle build → java -jar → systemctl restart. كانت عملية النشر الواحدة تستغرق 30 دقيقة، مع حدوث 2-3 أخطاء بشرية شهريًا.
(2) حل CI/CD مؤتمت بالكامل
TEXT
📖 للعرض فقط
Before: git push → SSH → build → deploy (30 min, error-prone)
After: git push → GitHub Actions → Docker build → deploy (5 min, zero-touch)
التحويل إلى حاويات + CI/CD = يتحول النشر من 30 دقيقة من الجهد اليدوي إلى خط أنابيب مؤتمت بالكامل في 5 دقائق.
3. البناء متعدد المراحل في Docker
(1) Dockerfile
DOCKERFILE
# المرحلة 1: البناء
FROM gradle:8.5-jdk17 AS builder
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
COPY src ./src
RUN gradle bootJar --no-daemon -x test
# المرحلة 2: التشغيل (JRE خفيف)
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
# مستخدم غير جذر للأمان
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
(2) مقارنة بين البناء متعدد المراحل والمرحلة الواحدة
| البُعد | مرحلة واحدة | متعدد المراحل |
|---|---|---|
| حجم الصورة | ~800 ميجابايت (JDK + المصدر) | ~150 ميجابايت (JRE فقط) |
| الأمان | المصدر موجود في الصورة | المصدر غير موجود في صورة التشغيل |
| تخزين البناء المؤقت | بدون طبقات | تخزين مؤقت مستقل لكل مرحلة |
| وقت البناء | إعادة بناء كاملة كل مرة | إعادة استخدام طبقة التبعيات المخزنة مؤقتًا |
4. تنسيق Docker Compose
(1) docker-compose.yml
YAML
version: '3.8'
services:
order-processor:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=r2dbc:postgresql://postgres:5432/orderdb
- SPRING_DATASOURCE_USERNAME=order_user
- SPRING_DATASOURCE_PASSWORD=order_pass
- SPRING_REDIS_HOST=redis
- MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,prometheus
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- order-net
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_DB=orderdb
- POSTGRES_USER=order_user
- POSTGRES_PASSWORD=order_pass
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U order_user -d orderdb"]
interval: 5s
timeout: 5s
retries: 5
networks:
- order-net
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
networks:
- order-net
prometheus:
image: prom/prometheus:latest
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- order-net
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
depends_on:
- prometheus
networks:
- order-net
volumes:
pgdata:
networks:
order-net:
driver: bridge
(2) نشر بنقرة واحدة
BASH
# بدء جميع الخدمات
docker-compose up -d
# التحقق من الحالة
docker-compose ps
# عرض السجلات
docker-compose logs -f order-processor
# إيقاف الكل
docker-compose down
5. خط أنابيب CI/CD
(1) GitHub Actions
YAML
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle
uses: actions/cache@v3
with:
path: ~/.gradle/caches
key: gradle-${{ hashFiles('**/*.gradle.kts') }}
- name: Run tests
run: ./gradlew test
- name: Build JAR
run: ./gradlew bootJar
- name: Build Docker image
run: docker build -t order-processor:${{ github.sha }} .
- name: Push to registry
if: github.ref == 'refs/heads/main'
run: |
docker tag order-processor:${{ github.sha }} registry.example.com/order-processor:latest
docker push registry.example.com/order-processor:latest
- name: Deploy
if: github.ref == 'refs/heads/main'
run: |
ssh deploy@prod-server "docker pull registry.example.com/order-processor:latest && docker-compose up -d"
(2) مخطط خط أنابيب CI/CD
flowchart TD
A[git push] --> B[GitHub Actions]
B --> C[Checkout Code]
C --> D[Setup JDK 17]
D --> E[Cache Gradle]
E --> F[Run Tests]
F --> G{Tests Pass?}
G -->|Yes| H[Build JAR]
G -->|No| I[Notify Team]
H --> J[Build Docker Image]
J --> K{Main Branch?}
K -->|Yes| L[Push to Registry]
K -->|No| M[Stop]
L --> N[Deploy to Production]
N --> O[Health Check]
O --> P{Healthy?}
P -->|Yes| Q[Live]
P -->|No| R[Rollback]
6. المراقبة
(1) إعدادات Spring Boot Actuator
KOTLIN
// application.yml
// management:
// endpoints:
// web:
// exposure:
// include: health,info,prometheus,metrics
// metrics:
// export:
// prometheus:
// enabled: true
// endpoint:
// health:
// show-details: always
(2) مقاييس مخصصة باستخدام Micrometer
KOTLIN
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Timer
class OrderMetrics(registry: MeterRegistry) {
private val ordersCreated = Counter.builder("orders.created.total")
.description("Total orders created")
.register(registry)
private val orderProcessingTime = Timer.builder("orders.processing.time")
.description("Order processing time")
.register(registry)
fun recordOrderCreated() { ordersCreated.increment() }
fun <T> recordProcessingTime(block: () -> T): T {
return orderProcessingTime.recordCallable { block() } ?: block()
}
}
(3) مقاييس المراقبة
| المقياس | النوع | حد التنبيه |
|---|---|---|
orders_created_total |
عداد | — |
orders_processing_time |
مؤقت | P99 > 2 ثانية |
jvm_memory_used_bytes |
مقياس | > 80% |
db_connection_pool_active |
مقياس | > 90% |
http_server_requests_seconds |
مؤقت | P99 > 5 ثوانٍ |
disk_free_bytes / disk_total_bytes |
مقياس | المساحة الحرة < 10% |
7. فحوصات الصحة وجاهزية الإنتاج
(1) نقطة نهاية فحص الصحة
KOTLIN
// نقطة نهاية فحص صحة Spring Boot Actuator
// GET /actuator/health
// {
// "status": "UP",
// "components": {
// "db": { "status": "UP" },
// "redis": { "status": "UP" },
// "diskSpace": { "status": "UP" }
// }
// }
(2) قائمة جاهزية الإنتاج
| الفئة | عنصر الفحص | الحالة |
|---|---|---|
| الأمان | تشغيل بمستخدم غير جذر | ☐ |
| الأمان | لا أسرار مشفرة في الكود | ☐ |
| الأمان | HTTPS مُعد | ☐ |
| الموثوقية | نقطة نهاية فحص الصحة | ☐ |
| الموثوقية | إيقاف سلس (SIGTERM) | ☐ |
| الموثوقية | تجمع اتصالات قاعدة البيانات مُعد | ☐ |
| القابلية للرصد | مخرجات تسجيل مهيكلة | ☐ |
| القابلية للرصد | مقاييس Prometheus مكشوفة | ☐ |
| القابلية للرصد | قواعد التنبيه مُعدة | ☐ |
| الأداء | حجم كومة JVM مُعد | ☐ |
| الأداء | استراتيجية GC محددة | ☐ |
| النشر | صورة Docker < 200 ميجابايت | ☐ |
| النشر | خط أنابيب CI/CD | ☐ |
| النشر | استراتيجية التراجع | ☐ |
8. مثال كامل: عرض نشر بنقرة واحدة
KOTLIN
// ============================================
// OrderProcessor - محاكاة النشر
// الميزة: عرض Docker + CI/CD + فحص الصحة
// ============================================
import kotlin.system.measureTimeMillis
data class DeployResult(val service: String, val status: String, val time: Long)
class DeploySimulator {
private val services = mutableListOf<DeployResult>()
private var deployed = false
fun build(): DeploySimulator {
print(" Building Docker image...")
val time = measureTimeMillis { Thread.sleep(800) }
println(" Done (${time}ms)")
return this
}
fun test(): DeploySimulator {
print(" Running tests...")
val time = measureTimeMillis { Thread.sleep(300) }
println(" Passed (${time}ms)")
return this
}
fun push(): DeploySimulator {
print(" Pushing to registry...")
val time = measureTimeMillis { Thread.sleep(500) }
println(" Done (${time}ms)")
return this
}
fun deploy(service: String, port: Int): DeploySimulator {
print(" Deploying $service on port $port...")
val time = measureTimeMillis { Thread.sleep(400) }
services.add(DeployResult(service, "RUNNING", time))
println(" Running (${time}ms)")
return this
}
fun healthCheck(): DeploySimulator {
print(" Health check...")
val time = measureTimeMillis { Thread.sleep(200) }
val allHealthy = services.all { it.status == "RUNNING" }
println(if (allHealthy) " ALL HEALTHY" else " UNHEALTHY DETECTED")
return this
}
fun summary() {
println("\n=== Deployment Summary ===")
services.forEach { s ->
println(" ${s.service}: ${s.status} (${s.time}ms)")
}
println("\n Total services: ${services.size}")
println(" Health: ${if (services.all { it.status == "RUNNING" }) "ALL GREEN" else "ISSUES DETECTED"}")
deployed = true
}
fun isDeployed() = deployed
}
fun main() {
println("=== OrderProcessor CI/CD Pipeline ===\n")
println("[1/6] Build Stage:")
DeploySimulator()
.build()
.test()
println("\n[2/6] Push Stage:")
DeploySimulator().push()
println("\n[3/6] Deploy Stage:")
val deployer = DeploySimulator()
.deploy("postgres", 5432)
.deploy("redis", 6379)
.deploy("order-processor", 8080)
.deploy("prometheus", 9090)
.deploy("grafana", 3000)
println("\n[4/6] Health Check:")
deployer.healthCheck()
println("\n[5/6] Smoke Test:")
println(" GET /actuator/health -> 200 OK")
println(" GET /api/v1/orders -> 200 OK")
println("\n[6/6] Production Ready Checklist:")
val checks = listOf(
"Non-root user" to true,
"No hardcoded secrets" to true,
"Health endpoint exposed" to true,
"Prometheus metrics enabled" to true,
"Graceful shutdown configured" to true,
"Docker image < 200MB" to true,
"CI/CD pipeline active" to true,
"Rollback strategy defined" to true
)
checks.forEach { (item, passed) ->
println(" ${if (passed) "✅" else "❌"} $item")
}
val passCount = checks.count { it.second }
println("\n Result: $passCount/${checks.size} checks passed")
if (passCount == checks.size) {
println("\n 🚀 OrderProcessor is LIVE!")
}
}
المخرجات:
TEXT
📖 للعرض فقط
=== OrderProcessor CI/CD Pipeline ===
[1/6] Build Stage:
Building Docker image... Done (804ms)
Running tests... Passed (301ms)
[2/6] Push Stage:
Pushing to registry... Done (502ms)
[3/6] Deploy Stage:
Deploying postgres on port 5432... Running (401ms)
Deploying redis on port 6379... Running (401ms)
Deploying order-processor on port 8080... Running (401ms)
Deploying prometheus on port 9090... Running (401ms)
Deploying grafana on port 3000... Running (401ms)
[4/6] Health Check:
Health check... ALL HEALTHY
[5/6] Smoke Test:
GET /actuator/health -> 200 OK
GET /api/v1/orders -> 200 OK
[6/6] Production Ready Checklist:
✅ Non-root user
✅ No hardcoded secrets
✅ Health endpoint exposed
✅ Prometheus metrics enabled
✅ Graceful shutdown configured
✅ Docker image < 200MB
✅ CI/CD pipeline active
✅ Rollback strategy defined
Result: 8/8 checks passed
🚀 OrderProcessor is LIVE!
9. أمثلة عملية سريعة
▶ مثال: Dockerfile بسيط
DOCKERFILE
# Stage 1: بناء
FROM gradle:8.5-jdk17-alpine AS builder
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
RUN gradle dependencies --no-daemon
COPY src ./src
RUN gradle build -x test --no-daemon
# Stage 2: تشغيل
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -q --spider http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
BASH
# بناء الصورة
docker build -t order-processor:1.0.0 .
# تشغيل محلي
docker run -d -p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://host:5432/orders \
-e SPRING_DATASOURCE_PASSWORD=secret \
--name order-processor \
order-processor:1.0.0
**الإخراج:
TEXT
📖 للعرض فقط
Successfully built and tagged order-processor:1.0.0
Container order-processor started
▶ مثال: Docker Compose متعدد الخدمات
YAML
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: orders
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
order-processor:
build: .
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
SPRING_PROFILES_ACTIVE: docker
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orders
SPRING_DATASOURCE_USERNAME: app
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD:-changeme}
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
ports:
- "8080:8080"
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
volumes:
postgres-data:
BASH
# تشغيل كل الخدمات
docker-compose up -d
# عرض السجلات
docker-compose logs -f order-processor
# إيقاف وحذف
docker-compose down -v
**الإخراج:
TEXT
📖 للعرض فقط
order-processor | Started OrderProcessorApplication
postgres | database system is ready to accept connections
▶ مثال: GitHub Actions CI/CD
YAML
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
tags: ['v*']
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Run tests
run: ./gradlew test
- name: Build
run: ./gradlew build
build-and-push:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ secrets.REGISTRY }}/order-processor:${{ github.sha }}
${{ secrets.REGISTRY }}/order-processor:latest
deploy:
needs: build-and-push
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- name: Deploy to Production
run: |
echo "Deploying ${{ github.ref_name }} to production"
kubectl set image deployment/order-processor \
order-processor=${{ secrets.REGISTRY }}/order-processor:${{ github.sha }}
kubectl rollout status deployment/order-processor
**الإخراج:
TEXT
📖 للعرض فقط
✓ Tests passed
✓ Image built and pushed: registry.example.com/order-processor:abc123
✓ Deployment updated
▶ مثال: Kubernetes Deployment
YAML
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-processor
labels:
app: order-processor
spec:
replicas: 3
selector:
matchLabels:
app: order-processor
template:
metadata:
labels:
app: order-processor
spec:
containers:
- name: order-processor
image: registry.example.com/order-processor:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: production
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: order-processor
spec:
selector:
app: order-processor
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
BASH
kubectl apply -f k8s/deployment.yaml
kubectl get pods -l app=order-processor
kubectl logs -f deployment/order-processor
**الإخراج:
TEXT
📖 للعرض فقط
NAME READY STATUS RESTARTS AGE
order-processor-7d8f9c5b6-abc12 1/1 Running 0 2m
order-processor-7d8f9c5b6-def34 1/1 Running 0 2m
order-processor-7d8f9c5b6-ghi56 1/1 Running 0 2m
▶ مثال: Health Checks و Metrics
KOTLIN
import org.springframework.boot.actuate.health.Health
import org.springframework.boot.actuate.health.HealthIndicator
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.*
@Component
class DatabaseHealthIndicator(
private val dataSource: javax.sql.DataSource
) : HealthIndicator {
override fun health(): Health {
return try {
dataSource.connection.use { conn ->
if (conn.isValid(1)) Health.up().build()
else Health.down().withDetail("reason", "Connection invalid").build()
}
} catch (e: Exception) {
Health.down(e).build()
}
}
}
@Component
class OrderProcessorHealthIndicator {
var ordersProcessed: Long = 0
var lastProcessedAt: Long = 0
fun health(): Health =
if (ordersProcessed > 0 && (System.currentTimeMillis() - lastProcessedAt) < 60_000) {
Health.up()
.withDetail("ordersProcessed", ordersProcessed)
.withDetail("lastProcessedAt", lastProcessedAt)
.build()
} else {
Health.down().withDetail("reason", "No recent activity").build()
}
}
// Prometheus metrics
import io.micrometer.core.instrument.*
@Component
class OrderMetrics(private val registry: MeterRegistry) {
private val ordersCounter: Counter = Counter.builder("orders.processed.total")
.description("Total orders processed")
.register(registry)
private val processingTimer: Timer = Timer.builder("orders.processing.duration")
.description("Order processing duration")
.register(registry)
fun recordOrder() = ordersCounter.increment()
fun recordProcessing(durationNanos: Long) = processingTimer.record(durationNanos, java.util.concurrent.TimeUnit.NANOSECONDS)
}
**الإخراج:
TEXT
📖 للعرض فقط
GET /actuator/health
{
"status": "UP",
"components": {
"db": {"status": "UP"},
"orderProcessor": {"status": "UP", "details": {...}}
}
}
▶ مثال: Blue-Green Deployment
BASH
#!/bin/bash
# blue-green-deploy.sh
set -e
# الإعدادات
NEW_VERSION=${1:-1.0.0}
CURRENT=$(kubectl get service order-processor -o jsonpath='{.spec.selector.version}')
echo "Current version: $CURRENT"
echo "Deploying version: $NEW_VERSION"
# قرر: الانتشار إلى Blue أو Green
if [ "$CURRENT" == "blue" ]; then
TARGET="green"
else
TARGET="blue"
fi
echo "Deploying to $TARGET environment"
# 1. نشر النسخة الجديدة إلى البيئة المستهدفة
kubectl apply -f k8s/deployment-$TARGET.yaml
kubectl set image deployment/order-processor-$TARGET \
order-processor=registry/order-processor:$NEW_VERSION
# 2. انتظار الجاهزية
echo "Waiting for $TARGET to be ready..."
kubectl rollout status deployment/order-processor-$TARGET --timeout=5m
# 3. فحص الصحة
sleep 30 # انتظار للاستقرار
if ! curl -sf http://order-processor-$TARGET:8080/actuator/health > /dev/null; then
echo "Health check failed! Rolling back..."
kubectl delete deployment order-processor-$TARGET
exit 1
fi
# 4. تبديل Service إلى البيئة الجديدة
kubectl patch service order-processor \
-p "{\"spec\":{\"selector\":{\"version\":\"$TARGET\"}}}"
echo "Successfully switched to $TARGET (v$NEW_VERSION)"
# 5. حذف البيئة القديمة بعد فترة سماح
sleep 300
kubectl delete deployment order-processor-$CURRENT
**الإخراج:
TEXT
📖 للعرض فقط
Current version: blue
Deploying version: 1.0.1
Deploying to green environment
Waiting for green to be ready...
deployment "order-processor-green" successfully rolled out
Successfully switched to green (v1.0.1)
▶ مثال: إدارة الأسرار
BASH
# ❌ سيء: أسرار في نص عادي
# .env
DB_PASSWORD=mysecretpassword123
API_KEY=sk-abc123def456
# ✅ جيد: استخدام Kubernetes Secrets
kubectl create secret generic db-credentials \
--from-literal=url=jdbc:postgresql://db:5432/orders \
--from-literal=username=app \
--from-literal=password=mysecretpassword123
YAML
# في deployment.yaml - مرجع السر
env:
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
KOTLIN
// في الكود - قراءة من متغير البيئة
@Configuration
class SecurityConfig {
@Value("\${SPRING_DATASOURCE_PASSWORD}")
lateinit var dbPassword: String
}
**الإخراج:
TEXT
📖 للعرض فقط
Secret db-credentials created
Deployment using secret via valueFrom
❓ أسئلة شائعة
س ما فوائد البناء متعدد المراحل في Docker؟
ج الصورة النهائية تحتوي فقط على تبعيات التشغيل (JRE) — بدون كود مصدر أو أدوات بناء. يتقلص حجم الصورة من 800 ميجابايت إلى 150 ميجابايت، مع سطح هجوم أصغر وبدء تشغيل أسرع.
س كيف يمكن تحقيق نشر بدون توقف؟
ج النشر الأزرق-الأخضر أو التحديثات المتدرجة. النشر الأزرق-الأخضر يحافظ على بيئتين وبدل بينهما. التحديثات المتدرجة تستبدل النسخ واحدة تلو الأخرى. Kubernetes يدعم التحديثات المتدرجة بشكل أصلي.
س كيف يمكن التراجع عن نشر فاشل؟
ج ضع علامة على كل بناء صورة Docker بـ Git SHA، ثم تراجع ببساطة عبر
docker run registry.example.com/order-processor:<previous-sha>. التراجع المؤتمت عبر CI/CD أفضل حتى.س كيف تُدار الأسرار؟
ج لا تخزن الأسرار أبدًا في متغيرات البيئة أو ملفات الإعدادات. استخدم Vault أو AWS Secrets Manager أو Kubernetes Secrets. يقوم CI/CD بحقنها من مدير الأسرار.
س كيف تُراقب تطبيقات JVM؟
ج Spring Boot Actuator + Micrometer + Prometheus + Grafana هو المزيج القياسي. مقاييس JVM المحددة: ذاكرة الكومة، عدد/وقت GC، عدد الخيوط.
س كيف تُعد معاملات JVM للإنتاج؟
ج عيّن
-Xms و -Xmx لنفس القيمة (تجنب عبء تغيير حجم الكومة). استخدم G1GC (-XX:+UseG1GC). في الحاويات، أضف -XX:+UseContainerSupport.📖 ملخص
- البناء متعدد المراحل في Docker: مرحلة البناء بـ JDK، مرحلة التشغيل بـ JRE — يتقلص حجم الصورة من 800 ميجابايت إلى 150 ميجابايت
- تنسيق Docker Compose بنقرة واحدة: التطبيق + قاعدة البيانات + التخزين المؤقت + المراقبة
- خط أنابيب CI/CD: git push → اختبار → بناء → دفع → نشر، مؤتمت بالكامل في 5 دقائق
- Micrometer + Prometheus + Grafana: الثالوث لجمع المقاييس وتخزينها وتصويرها
- قائمة جاهزية الإنتاج: الأمان، الموثوقية، القابلية للرصد، الأداء، النشر — 8 فحوصات أساسية
- فحوصات الصحة هي الحد الأدنى للإنتاج: نقطة نهاية
/actuator/health+ Docker HEALTHCHECK
📝 تمارين
- مبتدئ (⭐): اكتب Dockerfile لـ OrderProcessor (مرحلة واحدة تكفي)، مبني على
eclipse-temurin:17-jre. تلميح:COPY build/libs/*.jar app.jar - متوسط (⭐⭐): اكتب docker-compose.yml يحتوي على OrderProcessor + PostgreSQL مع فحوصات الصحة مُعدة. تلميح:
depends_onمعcondition: service_healthy - متقدم (⭐⭐⭐): اكتب خط أنابيب CI/CD كامل في GitHub Actions يتضمن خطوات البناء والاختبار ودفع Docker والنشر. تلميح:
on: push: branches: [main]