Docker

Создание модуля 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

Читайте также:


Перевод статьи Dirk Hoekstra: Docker for Go Development with Hot Reload