لماذا Go و Docker هما الثنائي المثالي؟
\n\nGo (أو Golang) هي لغة صُممت منذ البداية للهندسة السحابية الحديثة. حتى قبل أن يصبح Docker المعيار الفعلي، كانت Go قادرة على الترجمة إلى ملف ثنائي ثابت لا يحتاج إلى أي تبعيات. أما Docker فهي تقنية تتيح لك حزم التطبيق مع بيئته الكاملة في حاوية خفيفة. عندما تجمع بين هاتين التقنيتين، تحصل على حل شبه مثالي: حجم صورة ضئيل، بدء فوري، وغياب تام لتعارضات التبعيات.
\n\nفي هذا المقال، لن نلقي محاضرات نظرية عن الحاويات. بدلاً من ذلك، سنكتب تطبيق Go بسيط، نبنيه كصورة Docker، ونحسّنه باستخدام multi-stage build، ونتأكد من أن حاويتنا جاهزة للإنتاج. كل هذا مع أمثلة كود حقيقية يمكنك تشغيلها الآن.
\n\nالخطوة 1: كتابة تطبيق Go بسيط
\n\nلنبدأ بإنشاء خادم HTTP بسيط بلغة Go. سيكون هذا هو تطبيقنا التجريبي. أنشئ ملف main.go بالمحتوى التالي:
package main\n\nimport (\n "fmt"\n "log"\n "net/http"\n "os"\n)\n\nfunc main() {\n port := os.Getenv("PORT")\n if port == "" {\n port = "8080"\n }\n\n http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {\n fmt.Fprintf(w, "مرحباً من حاوية Go! 🐹")\n })\n\n log.Printf("الخادم يعمل على المنفذ %s", port)\n log.Fatal(http.ListenAndServe(":"+port, nil))\n}\n\n\nهذا الكود كلاسيكي: يستمع إلى منفذ (يمكن تعيينه عبر متغير البيئة PORT) ويستجيب لأي طلب بترحيب بسيط. الآن نريد أن يعمل هذا التطبيق داخل Docker، بغض النظر عن نظام التشغيل الذي تعمل عليه الحاوية.
الخطوة 2: أول Dockerfile - النهج البسيط
\n\nأسهل طريقة "لحاوية" تطبيق Go هي استخدام صورة Go الكاملة، ونسخ الكود إليها، ثم تشغيله. أنشئ ملف Dockerfile:
# استخدام صورة Go الرسمية\nFROM golang:1.22\n\n# تعيين دليل العمل\nWORKDIR /app\n\n# نسخ ملفات المشروع\nCOPY go.mod go.sum ./\nRUN go mod download\nCOPY . .\n\n# بناء التطبيق\nRUN go build -o main .\n\n# تشغيل التطبيق\nCMD ["./main"]\n\n\nقم ببناء الصورة وتشغيلها:
\n\ndocker build -t go-app:v1 .\ndocker run -p 8080:8080 go-app:v1\n\n\nافتح المتصفح وانتقل إلى http://localhost:8080. هل يعمل؟ ممتاز. لكن هناك مشكلة: حجم الصورة. دعنا نتحقق:
docker images go-app:v1\n\n\nسترى شيئاً مثل 1.2 GB. هذه كارثة! الصورة تحتوي على مترجم Go كامل، ومدير حزم، وأدوات نظام - كل ما لا تحتاجه لتشغيل الملف الثنائي النهائي. في الإنتاج، ستستغرق هذه الصورة وقتاً طويلاً للتحميل ومساحة كبيرة.
الخطوة 3: Multi-stage build - سر الصور الخفيفة
\n\nيدعم Docker multi-stage build. هذا يعني استخدام عدة تعليمات FROM في Dockerfile واحد، والمرحلة الأخيرة فقط هي التي تدخل في الصورة النهائية. المرحلة الأولى هي "مرحلة البناء" وتحتوي على المترجم وجميع الأدوات. المرحلة الثانية هي صورة مصغرة، مثل scratch (صورة فارغة) أو alpine.
لنعد كتابة Dockerfile الخاص بنا:
\n\n# المرحلة 1: البناء\nFROM golang:1.22 AS builder\n\nWORKDIR /app\n\n# تخزين التبعيات مؤقتاً\nCOPY go.mod go.sum ./\nRUN go mod download\n\n# نسخ الكود المصدري وبناءه\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -o main .\n\n# المرحلة 2: الصورة النهائية\nFROM alpine:latest\n\nRUN apk --no-cache add ca-certificates\n\nWORKDIR /root/\n\n# نسخ الملف الثنائي فقط من المرحلة الأولى\nCOPY --from=builder /app/main .\n\nEXPOSE 8080\n\nCMD ["./main"]\n\n\nماذا حدث هنا؟ أنشأنا مرحلتين: