Создание модуля Go
В Go 1.13 были введены модули. Это означает, что больше не нужно размещать все проекты в одно рабочее пространство Go.
Для начала создаем новый каталог go-docker
, в котором будут храниться все файлы.
Затем инициализируем репозиторий Git и создаем модуль Go.
git init
git remote add origin [email protected]:Dirk94/go-docker.git
go mod init github.com/dirk94/go-docker
В директории находится файл go.mod
, который содержит все зависимости этого модуля, аналогично файлу package.json
в разработке Node.
Создание API
Модуль установлен, пришло время для создания API.
Мы будем использовать пакет маршрутизации thegorilla/mux
для API.
go get -u github.com/gorilla/mux
После выполнения команда добавится в качестве зависимости в файл go.mod
.
Затем создаем основной файл Go commands/runserver.go
.
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to this life-changing API.")
})
fmt.Println("Server listening!")
http.ListenAndServe(":80", r)
}
Все, что делает API — это возвращает сообщение “Welcome to this life-changing API”.
Проверим, работает ли программа перед тем, как перейти к контейнерам Docker. Для запуска сервера используем команду go run
.
go run commands/runserver.go
Server listening!
API работает! ?
Установка Docker
Начнем с создания образа Docker для этого проекта, который содержит набор инструкций, сообщающих Docker, какую среду нужно создать.
FROM golang:latest
WORKDIR /app
COPY ./ /app
RUN go mod download
ENTRYPOINT go run commands/runserver.go
Используем образ golang:latest
в качестве основы для этого нового пользовательского образа.
Копируем весь проект в директорию образа /app
, а затем загружаем зависимости с помощью go mod download
.
И, наконец, сообщаем Docker о запуске команды go run commands/runserver.go
.
Для создания этого образа запускаем следующую команду:
docker build -t go-docker-image .
Теперь у нас есть инструкции по созданию образа Docker, которые необходимо запустить.
docker run go-docker-image
Server listening!
Сервер выполняет прослушивание в контейнере Docker, однако при переходе на localhost
в браузере появляется сообщение об ошибке “Refused to connect”.
Происходит следующее: контейнер Docker прослушивает порт 80 на предмет входящих запросов, а операционная система хоста — нет. Таким образом, при отправке запроса GET на localhost
он не находит работающий сервер.
Ниже представлена диаграмма с описанием проблемы:
Чтобы это исправить, нужно сопоставить порт 80 контейнеров с портом 80 хоста.
docker run -p 80:80 go-docker-image
Диаграмма теперь будет выглядеть следующим образом:
Теперь при переходе на localhost
появляется сообщение “Welcome to this life-changing API”!
Изменение исходного кода
Нам необходимо внести некоторые изменения в API.
package main
import (
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to this life-changing API.\nIts the best API, its true, all other API's are fake.")
})
fmt.Println("Server listening!")
http.ListenAndServe(":80", r)
}
Добавляем новую строку в API. Попробуем запустить новый контейнер Docker.
docker run -p 80:80 go-docker-image
Но при переходе на localhost
мы видим то же самое сообщение.
Причина заключается в том, что образ Docker не изменился. Чтобы изменения вступили в силу, нам нужно перестроить образ.
docker build -t go-docker-image .
docker run -p 80:80 go-docker-image
Теперь появляется обновленное сообщение:
Установка горячей перезагрузки
Перестройка образа Docker после каждого изменения исходного кода отнимает слишком много времени, поэтому мы создадим более удобную систему.
Мы будем использовать пакет Compile Daemon, который автоматически перестроит и перезапустит приложение Go при изменении любого из исходных файлов Go.
FROM golang:latest
WORKDIR /app
COPY ./ /app
RUN go mod download
RUN go get github.com/githubnemo/CompileDaemon
ENTRYPOINT CompileDaemon --build="go build commands/runserver.go" --command=./runserver
Обновляем Dockerfile
, чтобы загрузить пакет CompileDaemon.
Затем изменяем точку входа, чтобы запустить программу CompileDaemon
. Указываем команду сборки и запуска программы, которые выполняются при каждом изменении файла Go.
Образ перестраивается при запуске:
docker build -t go-docker-image .
При запуске Docker добавляем флаг -v ~/projects/go-docker:/app
. Он устанавливает директорию хоста go-docker
в директорию /app
контейнера Docker.
При каждом изменении в директории go-docker
файлы в директории контейнера /app
также изменяются.
Последняя команда представлена ниже. Обратите внимание, что путь -v
не может быть относительным.
docker run -v ~/projects/go-docker:/app -p 80:80 go-docker-image
Во время запуска контейнера внесите изменения в исходный код, и вы увидите, что он автоматически обновляется. ??
Использование Docker compose
Сейчас нам приходится писать очень длинную команду docker run -v ~/projects/go-docker:/app -p 80:80 go-docker-image
для запуска контейнера.
Эта задача выполнима в подобном проекте, поскольку он содержит только один контейнер. Но предположим, что в проекте есть несколько контейнеров, которые нужно запустить. Выполнение всех команд docker run
отнимет очень много сил.
Решение — Docker Compose. С помощью этого инструмента можно указать, какие контейнеры нужно запускать при запуске команды docker-compose.
Для установки создаем файл docker-compose.yml
.
version: "3"
services:
go-docker-image:
build: ./
ports:
- '80:80'
volumes:
- ./:/app
Здесь указываем, что нужно создать образ go-docker-image
. Образ должен быть собран с использованием Dockerfile
в директории ./
.
Сопоставляем порты и устанавливаем громкость — на этот раз можно использовать относительный путь.
Для запуска контейнеров, указанных в файле docker-compose.yml
, запускаем docker-compose up
.
Вот и все! Мы создали рабочий API в Docker, который автоматически перезагружается при изменении файлов! ?
Исходный код можно просмотреть здесь — https://github.com/dirk94/go-docker
Читайте также:
- Идеальная настройка вашего Golang проекта
- Введение в каналы Golang
- Приключения Java-разработчика, решившегося изучать Go
Перевод статьи Dirk Hoekstra: Docker for Go Development with Hot Reload