404 Not Found

404 Not Found


nginx

نشر حاويات Docker

Docker هو ثورة في النشر—ابنأ مرة واحدة، شغّل في أي مكان، وودع عذر "يعمل على جهازي".

1. ما ستتعلمه


2. قصة حقيقية لمهندس عمليات

(1) نقطة الألم: بيئة غير متسقة

بعد أن انتهى Bob من تصحيح OrderFlow محليًا ونشرها في الإنتاج، فشلت الخدمة في البدء بسبب عدم تطابق إصدارات مشغل MySQL واختلاف إصدارات JDK واختلاف تهيئات المنطقة الزمنية. كل نشر يتطلب نصف يوم لاستكشاف اختلافات البيئة، وأصبحت عبارة "يعمل على جهازي" شعار الفريق. لأسوأ من ذلك، فإن الخوادم الثلاثة تتطلب ثلاث عمليات نشر يدوية منفصلة، وغالبًا ما يتم تجاهل التهيئات.

(2) حل Docker

يقوم Docker بتعبئة التطبيق وجميع تبعياته في صورة واحدة:

DOCKERFILE
FROM eclipse-temurin:17-jre-alpine
COPY target/orderflow-service.jar /app/app.jar
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

انشر بأمر واحد، مما يضمن بيئة متسقة بالكامل.

(3) الإيرادات

بعد أن بدأ Bob في استخدام Docker، تم بناء الصورة محليًا مرة واحدة، وجميع الخوادم الثلاثة سحبت وشغلت من نفس المصدر، مما ألغى عدم اتساق البيئة. أمر Docker Compose واحد يبدأ البيئة الكاملة—OrderFlow وMySQL وRedis—ويمكن للموظفين الجدد تشغيلها محليًا في خمس دقائق فقط.


3. Dockerfile متعدد المراحل

(1) عملية البناء متعددة المراحل

100%
graph TD
    A["المرحلة 1: البناء<br/>Maven + JDK 17<br/>ترجمة + تغليف"] --> B["orderflow-service.jar"]
    B --> C["المرحلة 2: التشغيل<br/>JRE 17 Alpine<br/>نسخ JAR فقط"]
    C --> D["الصورة النهائية<br/>~150MB"]

(1) ▶ مثال: Dockerfile متعدد المراحل

DOCKERFILE
# المرحلة 1: البناء
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build

# نسخ pom.xml أولاً لتخزين التبعيات مؤقتًا
COPY pom.xml .
RUN mvn dependency:go-offline -B

# نسخ المصدر والبناء
COPY src ./src
RUN mvn package -DskipTests -B

# المرحلة 2: التشغيل
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# إنشاء مستخدم غير جذر
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# نسخ JAR من مرحلة البناء
COPY --from=builder /build/target/*.jar app.jar

# تعيين الملكية
RUN chown -R appuser:appgroup /app
USER appuser

# كشف المنفذ
EXPOSE 8080

# فحص الصحة
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1

# تشغيل التطبيق
ENTRYPOINT ["java", \
  "-XX:+UseG1GC", \
  "-XX:MaxRAMPercentage=75.0", \
  "-jar", "app.jar"]

الناتج:

TEXT
// التنفيذ ناجح
الصورة الأساسية الحجم الأمان حالات الاستخدام
eclipse-temurin:17 ~450MB متوسط التطوير/الاختبار
eclipse-temurin:17-jre-alpine ~170MB متوسط-عالي موصى به للإنتاج
gcr.io/distroless/java17-debian12 ~100MB الأقصى الأمان المطلق

(2) ▶ مثال: صورة Distroless

DOCKERFILE
# المرحلة 1: البناء
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

# المرحلة 2: تشغيل Distroless
FROM gcr.io/distroless/java17-debian12
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

الناتج:

TEXT
// التنفيذ ناجح
💡 نصيحة: صور Distroless لا تحتوي على صدفة (لا sh/bash)، لذا لا يمكنك استخدام docker exec -it للدخول في وضع التصحيح. يوصى بها للإنتاج، لكن استخدم صورة Alpine للتصحيح.


4. .dockerignore وتحسين الصورة

(1) ▶ مثال: .dockerignore

TEXT
# .dockerignore
.git
.github
.idea
*.md
target/
!target/*.jar
node_modules/
*.log
.env
docker-compose*.yml
Dockerfile*
استراتيجية التحسين التأثير الوصف
.dockerignore استبعاد الملفات غير الضرورية تقليل سياق البناء عدم إرسال .git و.idea إلخ
نسخ pom.xml أولاً تخزين التبعيات مؤقتًا تخطي التنزيل إذا لم تتغير التبعيات
بناء متعدد المراحل تقليل حجم الصورة النهائية لا يتضمن Maven أو المصدر
JRE بدلاً من JDK توفير ~200MB لا حاجة لأدوات الترجمة في وقت التشغيل
صورة أساسية Alpine انخفاض بمقدار ~280MB نظام Linux مصغر باستخدام musl libc

5. تنسيق Docker Compose متعدد الخدمات

(1) ▶ مثال: OrderFlow + MySQL + Redis

YAML
# docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: orderflow
      DB_PASSWORD: ${DB_PASSWORD:-orderflow123}
      REDIS_HOST: redis
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: orderflow
      MYSQL_USER: orderflow
      MYSQL_PASSWORD: ${DB_PASSWORD:-orderflow123}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root123}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

volumes:
  mysql-data:
  redis-data:

الناتج:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) ▶ مثال: البدء والتحقق

BASH
# بناء وبدء جميع الخدمات
docker compose up -d --build

# التحقق من حالة الخدمات
docker compose ps

# عرض سجلات التطبيق
docker compose logs -f app

# اختبار واجهة برمجة التطبيقات
curl http://localhost:8080/actuator/health

# إيقاف جميع الخدمات
docker compose down -v

الناتج:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours
الأمر الوصف
docker compose up -d البدء في الخلفية
docker compose down الإيقاف وحذف الحاوية
docker compose logs -f app عرض سجلات التطبيق
docker compose ps عرض حالة الخدمات
docker compose build بناء الصورة فقط

6. حقن متغيرات البيئة

(1) ▶ مثال: تخريد التهيئة

YAML
# application-prod.yml (في صورة Docker)
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME:orderflow}
    password: ${DB_PASSWORD:defaultpass}
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
طريقة التهيئة الأولوية السيناريوهات المطبقة
Docker Compose environment عالية نشر سهل
ملف .env متوسط التطوير المحلي
K8s ConfigMap / Secret عالية الإنتاج
TEXT
# ملف .env
DB_PASSWORD=secure_password_here
DB_ROOT_PASSWORD=root_secure_here
REDIS_PASSWORD=redis_secure_here

7. مثال شامل: Dockerization الكامل لـ OrderFlow

DOCKERFILE
# Dockerfile
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080 8081
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-XX:+UseG1GC", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
YAML
# docker-compose.yml
version: "3.9"
services:
  app:
    build: .
    ports:
      - "8080:8080"
      - "8081:8081"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: orderflow
      DB_PASSWORD: ${DB_PASSWORD:-orderflow123}
      REDIS_HOST: redis
      MANAGEMENT_SERVER_PORT: "8081"
    depends_on:
      mysql: { condition: service_healthy }
      redis: { condition: service_healthy }
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: orderflow
      MYSQL_USER: orderflow
      MYSQL_PASSWORD: ${DB_PASSWORD:-orderflow123}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root123}
    volumes: [mysql-data:/var/lib/mysql]
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s; timeout: 5s; retries: 10

  redis:
    image: redis:7-alpine
    volumes: [redis-data:/data]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s; timeout: 3s; retries: 5

volumes:
  mysql-data:
  redis-data:

❓ أسئلة شائعة

س لماذا تكون صور Docker كبيرة جدًا؟
ج افتراضيًا، يتم استخدام صورة JDK الكاملة (~450MB). طرق التحسين: 1) استخدام JRE بدلاً من JDK (~170MB)؛ 2) استخدام الصورة الأساسية Alpine؛ 3) استخدام Distroless (~100MB)؛ 4) استخدام صورة GraalVM الأصلية (~50MB).
س كيف أقوم بتصحيح تطبيق في حاوية Docker؟
ج 1) docker compose logs -f app عرض السجلات؛ 2) docker compose exec app sh دخول الحاوية (صورة Alpine)؛ 3) تصحيح عن بُعد: ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"].
س ما العلاقة بين depends_on وhealthcheck؟
ج depends_on يضمن فقط ترتيب بدء الحاويات؛ ولا يضمن أن الخدمة جاهزة. عند استخدامه مع condition: service_healthy، سينتظر Docker Compose نجاح فحص الصحة قبل بدء الخدمة المعالة.
س كيف يمكنني تقليل وقت بناء الصورة؟
ج 1) أولاً، انسخ pom.xml وmvn dependency:go-offline للاستفادة من تخزين طبقات Docker المؤقت؛ 2) استخدم .dockerignore لاستبعاد الملفات غير ذات الصلة؛ 3) استخدم تركيب ذاكرة التخزين المؤقت BuildKit.
س هل يمكن استخدام Docker Compose في الإنتاج؟
ج Docker Compose مناسب للتطوير المحلي والنشر البسيط. لبيئات الإنتاج، نوصي بـ Docker Swarm أو Kubernetes، والتي تقدم ميزات مثل التحجيم التلقائي والتحديثات المتداولة والشفاء الذاتي.
س ما هي فخاخ صورة Alpine؟
ج 1) تستخدم musl libc بدلاً من glibc، لذا قد تكون بعض المكتبات الأصلية غير متوافقة؛ 2) بيانات المنطقة الزمنية تتطلب تثبيتًا إضافيًا؛ 3) حل DNS به خطأ في التزامن العالي (مشكلة ndots). يوصى بـ Alpine لسيناريوهات JRE فقط.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة ⭐): اكتب Dockerfile لـ OrderFlow، وابنِ الصورة، وشغّلها محليًا، وتحقق من أن /actuator/health يُرجع "UP".

  2. تمرين متقدم (الصعوبة: ⭐⭐): اكتب ملف docker-compose.yml لتنسيق الخدمات الثلاث—OrderFlow وMySQL وRedis—وقم بتهيئة healthcheck وdepends_on لضمان بدء الخدمات بالترتيب الصحيح.

  3. تحدٍ (الصعوبة: ⭐⭐⭐): قم بتحسين الصورة لتكون أقل من 150 MB، واستخدم الصورة الأساسية Distroless، وقم بتهيئة معلمات ذاكرة JVM (MaxRAMPercentage)، وأضف مستخدمًا غير جذر وتهيئات متعلقة بالأمان.

Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%