مقدمة: لماذا Docker وGo هما الثنائي المثالي؟
Go (Golang) وDocker هما أداتان غيرتا نهج تطوير ونشر خدمات الواجهة الخلفية. يتم تجميع Go في ملف ثنائي ثابت واحد، بينما يسمح Docker بتغليف هذا الملف الثنائي في بيئة معزولة. معًا، يقدمان حاويات بسيطة وسريعة وآمنة يمكن تشغيلها في أي مكان: من كمبيوتر المطور إلى مجموعة Kubernetes الإنتاجية.
في هذه المقالة، سننتقل من الصفر إلى تطبيق Go جاهز للإنتاج ومعبأ في Docker. ستتعلم كيفية كتابة Dockerfile، تحسين حجم الصورة، استخدام البناء متعدد المراحل، وتشغيل الحاويات مع خدمة Go.
1. الأساسيات: تطبيق Go وDockerfile بسيط
لنبدأ بخادم HTTP بسيط بلغة Go. لننشئ ملف main.go:
package main
import ( "fmt" "net/http")
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go inside Docker!") })
fmt.Println("Server is running on :8080") http.ListenAndServe(":8080", nil)}الآن لنكتب Dockerfile بسيطًا:
FROM golang:1.22-alpine AS builder
WORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN go build -o server .
FROM alpine:3.19RUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=builder /app/server .EXPOSE 8080CMD ["./server"]لنقم ببناء الصورة وتشغيل الحاوية:
docker build -t go-server .docker run -p 8080:8080 go-serverالآن افتح المتصفح وانتقل إلى http://localhost:8080 - سترى ترحيبًا من تطبيق Go الذي يعمل داخل الحاوية.
لماذا البناء متعدد المراحل؟
في المرحلة الأولى، نستخدم صورة Go الرسمية مع مجموعة كاملة من أدوات التجميع. في المرحلة الثانية، نستخدم Alpine Linux البسيط، حيث يتم نسخ الملف الثنائي النهائي فقط. هذا يسمح بالحصول على صورة بحجم حوالي 15-20 ميجابايت بدلاً من 500+ ميجابايت إذا تركنا صورة Go الكاملة.
2. تحسين الصورة: تقليل الحجم وسرعة البناء
الصورة الصغيرة لا توفر المساحة فحسب، بل توفر أيضًا تحميلًا أسرع وسطح هجوم أصغر. دعنا ننظر إلى التقنيات الرئيسية لتطبيقات Go.
2.1. استخدام .dockerignore
أنشئ ملف .dockerignore في جذر المشروع:
.git*.mdDockerfile.dockerignorenode_modules/test/هذا يمنع نسخ الملفات غير الضرورية إلى سياق البناء ويسرع العملية.
2.2. تخزين التبعيات مؤقتًا
في Dockerfile الخاص بنا، نقوم أولاً بنسخ go.mod و go.sum فقط، ثم نقوم بتشغيل go mod download. يقوم Docker بتخزين كل طبقة مؤقتًا، وطالما لم تتغير هذه الملفات، لن يتم إعادة تحميل التبعيات في كل مرة يتغير فيها الكود.
2.3. CGO والتجميع الثابت
يستخدم Go افتراضيًا CGO لبعض العمليات (مثل العمل مع الشبكة عبر استدعاءات النظام). للحصول على ملف ثنائي ثابت تمامًا بدون تبعيات على libc، استخدم علامات التجميع:
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .هذا يضمن أن الملف الثنائي سيعمل على أي توزيعة Linux، بما في ذلك الصور البسيطة مثل scratch.
2.4. Scratch - أخف صورة أساسية
إذا كان تطبيقك لا يتطلب شل، ping، أو أدوات مساعدة أخرى، استخدم الصورة الفارغة scratch:
FROM scratchCOPY --from=builder /app/server /EXPOSE 8080CMD ["/server"]سيكون حجم هذه الصورة مساويًا لحجم الملف الثنائي نفسه - غالبًا أقل من 10 ميجابايت.
3. Docker Compose: تطبيق Go متعدد الحاويات
نادرًا ما تتكون التطبيقات الحقيقية من