Docker — это широко используемое программное обеспечение для контейнеризации, которое позволяет разработчикам легко упаковывать приложения вместе с их окружениями, обеспечивая более быстрые циклы итераций и более высокую эффективность использования ресурсов, обеспечивая при этом одну и ту же желаемую среду при каждом запуске. Docker Compose — это инструмент управления контейнерами, который удовлетворяет современным требованиям к приложениям. Он позволяет запускать несколько взаимосвязанных контейнеров одновременно. Вместо ручного запуска контейнеров инструменты оркестрации дают разработчикам возможность одновременно управлять контейнером, масштабировать его и расширять.
Преимущества использования Nginx в качестве интерфейсного веб-сервера заключаются в производительности, конфигурируемости и завершении TLS, что освобождает приложение от выполнения этих задач. nginx-proxy
— это автоматизированная система для контейнеров Docker, которая упрощает процесс настройки Nginx в качестве обратного прокси. Его надстройка может сопровождать nginx-proxy
для автоматизации генерации и обновления сертификатов для прокси-контейнеров.
В этом руководстве вы развернете пример веб-приложения Go с gorilla / mux в качестве маршрутизатора запросов и Nginx в качестве веб-сервера, все внутри контейнеров Docker, управляемых Docker Compose. Вы будете использовать nginx-proxy
с надстройкой Let’s Encrypt в качестве обратного прокси. В конце этого руководства вы развернете веб-приложение Go, доступное в вашем домене несколькими маршрутами, с помощью Docker и защищенное сертификатами Let’s Encrypt.
Предварительные требования
- Сервер Ubuntu 22.04 с правами root и дополнительной учетной записью, отличной от root.
- Установите Docker;
- Установите Docker Compose;
- Полностью зарегистрированное доменное имя. В руководстве я буду его называть
your_domain
. - Запись DNS “A”,
your_domain
указывающая на общедоступный IP-адрес вашего сервера.
Создание примера веб-приложения Go
На этом шаге вы настроите свое рабочее пространство и создадите простое веб-приложение Go, которое позже разместите в контейнере. Приложение Go будет использовать мощный маршрутизатор запросов gorilla / mux, выбранный за его гибкость и скорость.
В этом руководстве вы сохраните все данные в ~/go-docker
. Выполните следующую команду, чтобы создать эту папку:
1 |
mkdir ~/go-docker |
Перейдите к нему:
1 |
cd ~/go-docker |
Вы сохраните свой пример веб-приложения Go в файле с именем main.go
. Создайте его с помощью текстового редактора:
1 |
nano main.go |
Добавьте следующие строки:
~/go-docker/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package main import ( "fmt" "net/http" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>This is the homepage. Try /hello and /hello/Sammy\n</h1>") }) r.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<h1>Hello from Docker!\n</h1>") }) r.HandleFunc("/hello/{name}", func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) title := vars["name"] fmt.Fprintf(w, "<h1>Hello, %s!\n</h1>", title) }) http.ListenAndServe(":80", r) } |
Сначала вы импортируете пакеты net/http
и gorilla/mux
, которые обеспечивают функциональность HTTP-сервера и маршрутизацию. gorilla/mux
Пакет реализует более мощный маршрутизатор запросов и диспетчер, сохраняя при этом совместимость интерфейса со стандартным маршрутизатором. Вы создаете экземпляр нового mux
маршрутизатора и сохраняете его в переменной r
.
Затем вы определяете три маршрута: /
, /hello
и /hello/{name}
. Первый (/
) служит домашней страницей, и вы добавляете сообщение для страницы. Второй (/hello
) возвращает посетителю приветствие. Для третьего маршрута (/hello/{name}
) вы указываете, что он должен принимать имя в качестве параметра и отображать приветственное сообщение со вставленным именем.
В конце вашего файла вы запускаете HTTP-сервер с помощью http.ListenAndServe
и даете ему указание прослушивать порт 80
, используя настроенный вами маршрутизатор.
Сохраните и закройте файл.
Перед запуском вашего приложения Go вам необходимо скомпилировать и упаковать его для выполнения внутри контейнера Docker. Go — это компилируемый язык, поэтому перед запуском программы компилятор преобразует программный код в исполняемый машинный код.
Вы настроили свое рабочее пространство и создали пример веб-приложения Go. Далее будет выполнено развертывание nginx-proxy
с автоматическим предоставлением сертификата Let’s Encrypt.
Развертывание nginx-прокси с помощью Let’s Encrypt
Важно, чтобы вы защитили свое приложение с помощью HTTPS. Для этого вы будете выполнять развертывание nginx-proxy
через Docker Compose вместе с его надстройкой Let’s Encrypt. Эта настройка защищает контейнеры Docker, проксируемые с помощью nginx-proxy
, и обеспечивает безопасность вашего приложения через HTTPS, автоматически обрабатывая создание и продление сертификата TLS.
Вы сохраните конфигурацию Docker Compose для nginx-proxy
в файле с именем nginx-proxy-compose.yaml
. Создайте его, выполнив:
1 |
nano <mark>nginx-proxy-compose.yaml</mark> |
Добавьте в файл следующие строки:
~/go-docker/nginx-proxy-compose.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
version: '3' services: nginx-proxy: restart: always image: jwilder/nginx-proxy ports: - "80:80" - "443:443" volumes: - "/etc/nginx/vhost.d" - "/usr/share/nginx/html" - "/var/run/docker.sock:/tmp/docker.sock:ro" - "/etc/nginx/certs" letsencrypt-nginx-proxy-companion: restart: always image: jrcs/letsencrypt-nginx-proxy-companion volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" volumes_from: - "nginx-proxy" |
В этом файле вы определяете два контейнера: один для nginx-proxy
и один для надстройки Let’s Encrypt (letsencrypt-nginx-proxy-companion
). Для прокси-сервера вы указываете образ jwilder/nginx-proxy
, предоставляете и сопоставляете порты HTTP и HTTPS, а также определяете тома, которые будут доступны контейнеру для сохранения данных, связанных с Nginx.
Во втором блоке вы указываете образ для конфигурации надстройки Let’s Encrypt. Затем вы настраиваете доступ к сокету Docker, определяя том, а затем существующие тома из контейнера прокси для наследования. Для обоих контейнеров установлено значение restart
свойства always
, которое предписывает Docker всегда поддерживать их в рабочем состоянии (в случае сбоя или перезагрузки системы).
Сохраните и закройте файл.
Разверните nginx-proxy
, выполнив:
1 |
docker compose -f <mark>nginx-proxy-compose.yaml</mark> up -d |
Docker Compose принимает пользовательский именованный файл с помощью флага -f
. up
Команда запускает контейнеры, а -d
флаг (отключенный режим) указывает ей запускать контейнеры в фоновом режиме.
Вы получите следующий результат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Output[+] Running 21/21 ⠿ letsencrypt-nginx-proxy-companion Pulled 6.8s ⠿ df9b9388f04a Pull complete 3.1s ⠿ 6c6cfd4eaf5b Pull complete 3.9s ⠿ 870307501973 Pull complete 4.3s ⠿ e8ff3435d14f Pull complete 4.5s ⠿ 5b78ba945919 Pull complete 4.8s ⠿ 973b2ca26006 Pull complete 5.0s ⠿ nginx-proxy Pulled 8.1s ⠿ 42c077c10790 Pull complete 3.9s ⠿ 62c70f376f6a Pull complete 5.5s ⠿ 915cc9bd79c2 Pull complete 5.6s ⠿ 75a963e94de0 Pull complete 5.7s ⠿ 7b1fab684d70 Pull complete 5.7s ⠿ db24d06d5af4 Pull complete 5.8s ⠿ e917373dbecf Pull complete 5.9s ⠿ 11e2be9775e9 Pull complete 5.9s ⠿ 9996fa75bc02 Pull complete 6.1s ⠿ d37674efdf77 Pull complete 6.3s ⠿ a45d84576e75 Pull complete 6.3s ⠿ a13c1f42faf7 Pull complete 6.4s ⠿ 4f4fb700ef54 Pull complete 6.5s [+] Running 3/3 ⠿ Network go-docker_default Created 0.1s ⠿ Container go-docker-nginx-proxy-1 Started 0.5s ⠿ Container go-docker-letsencrypt-nginx-proxy-companion-1 Started 0.8s |
Вы развернули nginx-proxy
и его компаньон Let’s Encrypt с помощью Docker Compose. Затем вы создадите Dockerfile
для своего веб-приложения Go.
Настройка веб-приложения Go
В этом разделе вы подготовите Dockerfile
, содержащий инструкции о том, как Docker создаст неизменяемый образ для вашего веб-приложения Go. Docker создает неизменяемый образ приложения, аналогичный снимку контейнера, используя инструкции, приведенные в Dockerfile
. Неизменяемость образа гарантирует одинаковую среду при каждом запуске контейнера, основанного на конкретном образе.
Создайте Dockerfile
с помощью текстового редактора:
1 |
nano Dockerfile |
Добавьте следующие строки:
~/go-docker/Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
FROM golang:alpine AS build RUN apk --no-cache add gcc g++ make git WORKDIR /go/src/app COPY . . RUN go mod init webserver RUN go mod tidy RUN GOOS=linux go build -ldflags="-s -w" -o ./bin/web-app ./main.go FROM alpine:3.17 RUN apk --no-cache add ca-certificates WORKDIR /usr/bin COPY --from=build /go/src/app/bin /go/bin EXPOSE 80 ENTRYPOINT /go/bin/web-app --port 80 |
Этот файл Dockerfile состоит из двух этапов. На первом этапе используется golang:alpine
база, которая содержит предустановленный Go для Alpine Linux.
Затем вы устанавливаете gcc
, g++
, make
и git
в качестве необходимых инструментов компиляции для вашего приложения Go. Вы задаете рабочему каталогу значение /go/src/app
, которое по умолчанию находится в GOPATH. Вы также копируете содержимое текущего каталога в контейнер. Первый этап завершается рекурсивной выборкой используемых пакетов из кода и компиляцией main.go
файла для выпуска без символа и отладочной информации (путем передачи -ldflags="-s -w"
).
Примечание: Когда вы компилируете программу Go, она сохраняет отдельную часть двоичного файла, который будет использоваться для отладки; однако эта дополнительная информация использует память, и ее необязательно сохранять при развертывании в производственной среде.
Второй этап основан на alpine:3.17
(Alpine Linux 3.17). Он устанавливает сертификаты доверенного центра сертификации, копирует скомпилированные двоичные файлы приложения с первого этапа в текущий образ, предоставляет порт 80
и устанавливает двоичный файл приложения в качестве точки входа в образ.
Сохраните и закройте файл.
Вы создали Dockerfile для своего приложения Go, который будет извлекать его пакеты, компилировать его для выпуска и запускать после создания контейнера. На следующем шаге вы создадите файл Docker Compose yaml
и протестируете приложение, запустив его в Docker.
Создание и запуск файла Docker Compose
Теперь вы создадите конфигурационный файл Docker Compose и запишете необходимую конфигурацию для запуска образа Docker, созданного вами на предыдущем шаге. Затем вы запустите его и проверьте, правильно ли он работает. В общем, конфигурационный файл Docker Compose определяет контейнеры, настройки, сети и тома, необходимые приложению. Вы также можете указать, что эти элементы запускаются и останавливаются как один.
Вы сохраните конфигурацию Docker Compose для веб-приложения Go в файле с именем go-app-compose.yaml
. Создайте его, выполнив:
1 |
nano <mark>go-app-compose.yaml</mark> |
Добавьте в этот файл следующие строки:
~/go-docker/go-app-compose.yaml
1 2 3 4 5 6 7 8 9 10 11 |
version: '3' services: go-web-app: restart: always build: dockerfile: Dockerfile context: . environment: - VIRTUAL_HOST=<mark>your_domain</mark> - LETSENCRYPT_HOST=<mark>your_domain</mark> |
Замените your_domain
оба раза на свое доменное имя. Сохраните и закройте файл.
Эта конфигурация Docker Compose содержит один контейнер (go-web-app
), который будет вашим веб-приложением Go. Приложение создается с использованием файла Dockerfile, созданного вами на предыдущем шаге, и в качестве контекста для сборки используется текущий каталог, содержащий исходный код. Кроме того, он устанавливает две переменные среды: VIRTUAL_HOST
и LETSENCRYPT_HOST
. nginx-proxy
используется VIRTUAL_HOST
, чтобы знать, из какого домена принимать запросы. LETSENCRYPT_HOST
указывает доменное имя для генерации сертификатов TLS и должно совпадать с VIRTUAL_HOST
, если вы не укажете домен с шаблоном.
Теперь запустите веб-приложение Go в фоновом режиме с помощью Docker Compose:
1 |
docker compose -f go-app-compose.yaml up -d |
Вывод, подобный этому, будет напечатан (этот вывод был усечен для удобства чтения):
1 2 3 4 5 6 7 8 9 10 11 |
OutputCreating network "go-docker_default" with the default driver Building go-web-app Step 1/13 : FROM golang:alpine AS build ---> b97a72b8e97d ... Successfully built 71e4b1ef2e25 Successfully tagged go-docker_go-web-app:latest ... [+] Running 1/1 ⠿ Container go-docker-go-web-app-1 Started |
Если вы просмотрите выходные данные, представленные после выполнения команды, Docker зарегистрирует каждый шаг создания образа приложения в соответствии с конфигурацией в вашем Dockerfile.
Теперь вы можете перейти на https://your_domain/
свою домашнюю страницу. По домашнему адресу вашего веб-приложения вы можете получить доступ к странице с помощью /
маршрута, определенного вами на первом шаге.
Теперь перейдите к https://your_domain/hello
. Загрузится сообщение, которое вы определили в своем коде для /hello
маршрута из шага 1.
Наконец, добавьте имя к адресу вашего веб-приложения, чтобы протестировать другой маршрут, например: https://your_domain/hello/Sammy
.
Примечание: Если вы получаете сообщение об ошибке о недействительных сертификатах TLS, подождите несколько минут, пока надстройка Let’s Encrypt предоставит сертификаты. Если через некоторое время вы все еще получаете ошибки, дважды проверьте введенные вами команды и конфигурацию на этом шаге.
Вы создали файл Docker Compose и написали конфигурацию для запуска вашего приложения Go внутри контейнера. Для завершения вы перешли к своему домену, чтобы проверить, что gorilla/mux
настройка маршрутизатора правильно обслуживает запросы к вашему веб-приложению Go с Docker.
Заключение
Теперь вы успешно развернули свое веб-приложение Go с помощью Docker и Nginx в Ubuntu 22.04. С Docker обслуживание приложений занимает меньше времени, поскольку среда, в которой выполняется приложение, гарантированно будет одинаковой при каждом его запуске. Пакет gorilla / mux содержит отличную документацию и предлагает более сложные функции, такие как присвоение имен маршрутам и обслуживание статических файлов. Для получения дополнительных сведений о контроле над модулем Go HTTP server, таких как определение пользовательских таймаутов, посетите официальную документацию.