Я создаю серверную часть golang с помощью gin и хочу настроить ее как службу в докере. Я хочу, чтобы при внесении изменений в код приложение перестраивалось с учетом этих изменений. Я пробовал использовать 2 пакета golang: github.com/cespare/reflex и github.com/cortesi/modd.
Для пакета cespare/reflex я обнаружил нерешенную проблему на GitHub: если вы используете плавное завершение работы для gin, который я использую, приложение не получает SIGTERM/SIGINT от рефлекса. Поэтому я перешел ко второму пакету. См. ниже:
Мой Dockerfile для бэкэнда golang:
FROM golang:1.22.5-alpine3.20 AS builder
WORKDIR /go/src/app
ENV GO111MODULE=on
RUN go install github.com/cortesi/modd/cmd/modd@latest
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN go build -o ./run .
FROM alpine:3.20
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/app/run .
EXPOSE 8080
CMD ["./run"]
Мой файл docker-compose.yaml частично:
services:
goapp:
build:
context: ./src
target: builder
image: goapp
[...]
volumes:
- ./src:/go/src/app
command: modd -f .docker/modd.conf
Мой файл modd.conf:
**/*.go {
prep: echo "file changed"
prep: go build -o ./run .
daemon +sigterm: ./run
}
Я также пытался добавить prep: pkill -f run || true
в начало modd.conf, но он просто убивает контейнер.
При указанной выше конфигурации modd не обнаруживает никаких изменений, внесенных в код.
Если у вас есть какое-либо решение того, что я пытаюсь сделать, пожалуйста, дайте мне знать. Меня также устраивают решения, которые используют другие пакеты, кроме упомянутых двух.
ну, я стараюсь избегать использования этих двух команд вручную; Я подключил том из каталога src к контейнеру, поэтому изменения происходили мгновенно как на моей машине, так и на контейнере; Я видел, что пытаюсь реализовать, работая на кого-то другого, но не могу запустить это
Помните, что контейнер сборки не будет доступен во время выполнения; вам понадобится набор инструментов go, установленный в окончательном образе, если вы хотите запустить go build
. Один из этих инструментов может оказаться полезным.
Есть ли особая причина не использовать docker Compose Watch?
Больше нет необходимости в сторонних приложениях. Начиная с версии docker compose 2.22.0 , вы можете просто использовать docker compose --watch.
По сути, вы даете команду докеру следить за различными файлами и папками и указываете ему, какие действия следует предпринять, если один из этих файлов или папок изменится.
Представьте себе простую настройку приложения следующим образом:
.
├── Dockerfile
├── app
│ ├── go.mod
│ └── main.go
├── docker-compose.yaml
└── resources
└── sample.html
Мы пишем очень упрощенный веб-сервер:
package main
import (
"flag"
"log/slog"
"net/http"
)
var bindAddress = flag.String("bind", ":8080", "HTTP server bind address")
var dir = flag.String("dir", "./", "Directory to serve")
// Do not use this code in production. There are security implications which are not mitigated for the sake of brevity. You have been warned!
func main() {
flag.Parse()
slog.Info("Starting server", "addr", *bindAddress, "dir", *dir)
http.Handle("GET /files/", http.StripPrefix("/files/", http.FileServer(http.Dir(*dir))))
err := http.ListenAndServe(*bindAddress, nil)
if err != nil && err != http.ErrServerClosed {
slog.Error("Failed to start server", "err", err)
}
}
Примечание. НЕ используйте приведенный выше код в производстве. Существуют последствия для безопасности, которые не смягчаются ради краткости. Вы были предупреждены!
Важными частями являются docker-compose.yaml
и Dockerfile
.
Сначала Dockerfile, чтобы мы поняли, что произойдет:
FROM golang:1.22-alpine3.20 AS builder
WORKDIR /app
ADD app/ .
RUN go build -o mycoolapp .
FROM alpine:3.20
COPY --from=builder /app/mycoolapp /usr/local/bin/mycoolapp
COPY resources/sample.html /usr/local/share/mycoolapp/sample.html
ENTRYPOINT [ "/usr/local/bin/mycoolapp" ]
CMD [ "-dir","/usr/local/share/mycoolapp/" ]
Итак, по сути, наш простой веб-сервер построен и будет считывать контент, который он обслуживает /usr/local/share/mycoolapp/
, в сервисном контейнере.
services:
my-service:
image: my-service:latest
build:
context: .
dockerfile: Dockerfile
develop:
watch:
- action: sync
path: resources/
target: /usr/local/share/mycoolapp/
- action: rebuild
path: app/
ports:
- "8080:8080"
environment:
- MY_ENV_VAR=foo
Так что же происходит, когда мы «запускаем» этот файл компоновки (docker compose up --watch
)?
my-service:latest
не существует на вашем компьютере, он встроен.resources
, это будет отражено в Docker, синхронизирующем эти изменения с каталогом /usr/local/share/mycoolapp/
в сервисном контейнере, который является каталогом по умолчанию, из которого веб-сервер в нашем контейнере настроен для обслуживания файлов. Однако файлы не добавляются в образ.app
, запускается полная перестройка, как если бы был вызван docker compose build
.
Контейнеры Docker обычно имеют изолированную файловую систему и не видят файлы на хосте; Можете ли вы использовать здесь простую цепочку инструментов Go без Docker? Go — компилируемый язык, и вам в любом случае придется пересобирать двоичный файл; достаточно ли
docker-compose build; docker-compose up -d
перестроить образ и контейнер?