Membuat Docker image kecil untuk aplikasi Go + Supervisor + Cron
Docker image untuk aplikasi berbasis Golang biasanya mempunyai ukuran yang cukup besar, untuk keperluan digunakan pada sebuah Containers as a service (CaaS)
akan memakan waktu untuk diupload.
Kamu dapat memperkecil ukuran image-nya dengan menggunakan konsep multi-stage builds, sehingga untuk aplikasi berbasis Golang berikut Supervisor dan Cron hanya berukuran sekitar 100 MB.
1: Struktur⌗
Struktur yang sesuai dari aplikasi berbasis Golang kamu sangat penting dalam hal ini, berikut struktur dasar folder dan file yang dapat kamu terapkan:
-> project-kamu/
----> resources/ (opsional)
----> storage/ (opsional)
----> public/ (opsional)
----> web/ (opsional)
----> cron-scripts/
--------> my-script.sh
----> supervisor-scripts/
--------> my-script.sh
----> logs/
--------> cron/
--------> supervisor/
----> src/
--------> go.mod
--------> go.sum
--------> main.go
----> .dockerignore
----> config.yml
----> cron-definition
----> docker-entrypoint
----> Dockerfile
----> supervisord.conf
Semua kode Golang hanya berada didalam folder “src” karena nantinya akan kita compile menjadi binary. Sedangkan seperti folder “resources”, “storage”, “public”, dan “web” berada diluar dikarenakan bersifat hanya akan digunakan ketika aplikasi dijalankan. Mengenai “config.yml” terserah kebutuhan aplikasi kamu dapat berada didalam folder “src” atau diluar.
2: Dockerfile⌗
Didalam Dockerfile perlu menerapkan konsep multi-stage builds supaya ukurannya kecil. Jika kamu yang pernah melakukan hal tersebut, mungkin akan mempunyai isi seperti berikut:
################################
# STEP 1 build executable binary
################################
FROM golang:1.15.6-alpine AS builder
WORKDIR $GOPATH/src/github.com/your-org/your-project
# Another definition
..................
# Build the binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags='-w -s -extldflags "-static"' -a \
-o /go/bin/your-project .
############################
# STEP 2 build a small image
############################
FROM scratch
# Copy our static executable
COPY --from=builder /go/bin/your-project /go/bin/your-project
WORKDIR $GOPATH/dist/github.com/your-org/your-project
# Run the binary.
ENTRYPOINT ["/go/bin/your-project"]
Ukuran Docker image dari definisi diatas akan sangat kecil. Tetapi dalam artikel ini kita juga membutuhkan untuk memasang Supervisor
dan Cron
, sehingga yang berbeda adalah pada STEP 2
, yaitu kamu tidak bisa menggunakan FROM scratch
, kamu bisa ganti menggunakan FROM alpine:latest
, yaitu seperti berikut:
################################
# STEP 1 build executable binary
################################
FROM golang:1.15.6-alpine AS builder
WORKDIR $GOPATH/src/github.com/your-org/your-project
# Copy and download dependency using go mod
ENV GO111MODULE=on
COPY ./src/go.mod .
COPY ./src/go.sum .
RUN go mod download
COPY ./src/ .
# Build the binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags='-w -s -extldflags "-static"' -a \
-o /go/bin/your-project .
############################
# STEP 2 build a ready image
############################
FROM alpine:latest
# Copy our static executable
COPY --from=builder /go/bin/your-project /go/bin/your-project
COPY docker-entrypoint /docker-entrypoint
RUN apk --update --no-cache add tzdata \
ca-certificates \
busybox-suid \
supervisor && \
update-ca-certificates && \
rm -rf /var/cache/apk/* && \
chmod +x /docker-entrypoint
ENTRYPOINT ["/docker-entrypoint"]
3: ENTRYPOINT⌗
ENTRYPOINT
akan dieksekusi saat nanti container dibuat dari image diatas. Didalam file docker-entrypoint
kamu isi seperti berikut:
#!/bin/sh
# Create folder to store cron and supervisor logs
mkdir -p /var/log/supervisor
mkdir -p /var/log/cron
# Append custom cron definition into root crontab
grep -qxF '# custom cron schedules' /etc/crontabs/root || cat /go/dist/github.com/your-org/your-project/cron-definition >> /etc/crontabs/root
# Run
/usr/sbin/crond -b & \
/usr/bin/supervisord -c /go/dist/github.com/your-org/your-project/supervisord.conf
4: Cron⌗
Cron akan dijalankan oleh user root
, maka isi cron-definition
nantinya akan di-append kedalam file /etc/crontabs/root
, isi file seperti berikut:
# custom cron schedules
*/10 * * * * /go/dist/github.com/your-org/your-project/cron-scripts/my-script.sh >> /var/log/cron/my-cron-script.log 2>&1
5: Supervisor⌗
Siapkan isi dari file supervisord.conf
seperti berikut:
[supervisord]
logfile=/var/log/supervisor/supervisord.log ; supervisord log file
logfile_maxbytes=50MB ; maximum size of logfile before rotation
logfile_backups=10 ; number of backed up logfiles
loglevel=error ; info, debug, warn, trace
nodaemon=true ; run supervisord NOT as a daemon
user=root ; default user
childlogdir=/var/log/supervisor/ ; where child log files will live
[supervisorctl]
[inet_http_server]
port=127.0.0.1:9001
[rpcinterface:supervisor]
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
[program:my-custom-program-name]
command=/bin/sh /go/dist/github.com/your-org/your-project/supervisor-scripts/my-script.sh
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/supervisor/my-script.log
stderr_logfile=/var/log/supervisor/my-script_error.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
6: Siap dipakai⌗
Sekarang build docker image terlebih dahulu dengan perintah sebagai berikut:
docker build --tag=project-kamu:1.15.6-1.0 .
Kemudian lanjutkan membuat container-nya dengan perintah sebagai berikut:
docker run --interactive --tty \
--name=my-project \
--memory=512m \
--volume=/path/to/project-kamu/logs/cron:/var/log/cron \
--volume=/path/to/project-kamu/logs/supervisor:/var/log/supervisor \
--volume=/path/to/project-kamu:/go/dist/github.com/your-org/your-project \
--detach \
--net=host \
project-kamu:1.15.6-1.0