В этой небольшой заметке я постараюсь максимально компактно и практично описать этот процесс для новичков.
Про Docker в двух словах скажу так — он позволяет запустить контейнер с виртуальной операционной системой и вашим приложением, размещенным в ней. А так же, когда надоест, удалить напрочь этот контейнер, не заботясь о том, что Ваше приложение где-то тайком намусорило в системе.
Вот, собственно, и все преимущества от использования контейнеров docker для новичков :)
В данной заметке я сначала рассматриваю способ компиляции Вашей программы внутри образа, на этапе его сборки. Это самый очевидный способ для начинающих. Затем рассматривается создание образа и запуск контейнера из заранее скомпилированного файла программы.
Еще пару слов о том, с чем docker работает. Он хранит у себя образы (image) контейнеров, на основе которых Вы можете запустить сколько угодно экземпляров самих этих контейнеров и настроить им маршрутизацию по сети (мэппинг входящих портов и т.п). Образы собираются на основе других образов (можно сказать, есть наследование образов), которые есть в публичном доступе в репозитории docker. Для программы на Go лучшим готовым выбором будут образы golang:latest (на базе debian) и golang:alpine (на базе alpine linux). Другие названия образов можно посмотреть тут: hub.docker.com/_/golang
Сборка Вашей программы из исходников внутри образа Docker
Этот вариант используем, например, тогда, когда ваша основная ось — windows или macos, а вы собираете контейнер для linux. Чтобы не пересобирать компилятор go для других платформ на своей машине, просто компилируем исходники в целевом Docker-образе.
Чтобы собрать образ (image), нужно в папке исходников своей программы сделать текстовый файл Dockerfile, без расширения, с таким содержимым:
#имя базового образа
FROM golang:latest
#создаем папку, где будет наша программа
RUN mkdir -p /go/src/app
#идем в папку
WORKDIR /go/src/app
#копируем все файлы из текущего пути к файлу Docker на вашей системе в нашу новую папку образа
COPY . /go/src/app
#скачиваем зависимые пакеты через скрипт, любезно разработанный командой docker
RUN go-wrapper download
#инсталлируем все пакеты и вашу программу
RUN go-wrapper install
#запускаем вашу программу через тот же скрипт, чтобы не зависеть от ее скомпилированного имени
#-web - это параметр, передаваемый вашей программе при запуске, таких параметров может быть сколько угодно
#go-wrapper запускает set -x для того, чтобы отправить в stderr имя бинарника Вашей программы в момент ее запуска
CMD ["go-wrapper", "run", "-web"]
#пробрасываем порт вашей программы наружу образа
EXPOSE 8000
Особенностью этого файла для Go является наличие в базовом образе golang специального скрипта go-wrapper, который облегчает работу с компилированием, позволяя не думать о путях и именах файлов.
В папке с этим файлом и вашими исходниками запускаем команду:
docker build -t my-golang-app .
Точка в конце — это текущая директория с файлом Docker, не забываем ее указывать.
my-golang-app — это будет название образа, в котором скомпилируется ваша программа.
После выполнения этой команды можно посмотреть наличие успешно собранного образа командой
docker images
docker ps -a
Создать и запустить контейнер на базе собранного образа можно так:
docker run -d --rm --name my-running-app my-golang-app
my-running-app — это будет имя создаваемого контейнера на основе образа my-golang-app.
Еще тут можно использовать ключи:
-d | --detach - запускает контейнер в фоновом режиме и выводит только id свежесозданного контейнера. (по умолчанию == false)
-i | --interactive - запускает контейнер в интерактивном режиме (оставляет STDIN открытым, даже если контейнер запущен в неприкрепленном режиме)
-t | --tty - запускает псевдотерминал, часто используется с -i
-p | --publish=[] - пробрасывает порты контейнера в хост. Формат: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
-e | --env=[] - пробрасывает переменные окружения внутрь контейнера.
-v | --volume=[] - пробрасывает директорию файловой системы внутрь контейнера
Подробнее по ключам запуска docker и составу файла Dockerfile смотрите тут и тут.
Запуск скомпилированного бинарного файла программы в контейнере Docker
Нужно помнить, что файл перед помещением в контейнер должен быть верно скомпилирован под нужный образ и архитектуру.
Создаем и запускаем контейнер с бинарником:
#!/bin/bash
#для сборки CGO зависимых приложений под alpine
docker run --rm -v "$PWD":/go/src/myapp -w /go/src/myapp golang:alpine /bin/sh -c "apk add --update gcc musl-dev; go build -ldflags \"-s -w\" -a -o myapp_bin_name"
Альтернативный вариант — пакуем его в контейнер:
# -*- Dockerfile -*-
FROM alpine
COPY myapp_bin_name /app/
RUN apk add --update tzdata openssl musl-dev ca-certificates && rm -rf /var/cache/apk/* && cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime && echo "Europe/Moscow" > /etc/timezone && chmod +x /app/myapp_bin_name && mkdir -p /data
WORKDIR /app
VOLUME /data
CMD /app/myapp_bin_name
EXPOSE 8085
Создаем образ:
docker build -t mymegapp .
Запускаем:
docker run -d --rm --name my-running-app my-golang-app
Комментарии (16)
alexesDev
29.06.2017 15:25+2Вот только нужно создавать два контейнера. Один собирает приложение, другой чисто запускает. Скомпилированное приложение не нуждается в той куче мусора, что ставится для компиляции.
Alexeyco
29.06.2017 15:59А можно делать как делаю я с imagick. Качается весь паровозик для сборки, потом сборка приложения, потом грохаем из образа весь мусор, включая git. По максимуму. Заодно хотя бы узнаю, насколько неправильно я делаю.
Caravus
29.06.2017 16:36+1Теперь есть multi-stage builds
grossws
29.06.2017 18:27А docker hub его уже умеет? Когда его анонсировали на dockercon17, то я не нашел никакой информации об этом.
upd: имелось ввиду умеет ли docker hub в automated builds использовать multi-stage
Caravus
29.06.2017 18:29Не в курсе, не пользуюсь. Релизнули уже давно (несколько недель), так что должно всё быть ок, я думаю.
shcoderAlex
30.06.2017 04:53До сих пор не умеет.
Error parsing reference: «node:alpine as builder» is not a valid repository/tag: invalid reference format
subvillion
30.06.2017 05:52Чета хрень в посте
1й командой создаем бинарник:
#!/bin/bash #для сборки CGO зависимых приложений под alpine docker run --rm -v "$PWD":/go/src/myapp -w /go/src/myapp golang:alpine /bin/sh -c "apk add --update gcc musl-dev; go build -ldflags \"-s -w\" -a -o myapp_bin_name"
2й пакуем его в почти любой контейнер:
# -*- Dockerfile -*-
FROM alpine
COPY myapp_bin_name /app/
RUN apk add --update tzdata openssl musl-dev ca-certificates && \
rm -rf /var/cache/apk/* && \
cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime && \
echo "Europe/Moscow" > /etc/timezone && \
chmod +x /app/myapp_bin_name && \
mkdir -p /data
WORKDIR /app
VOLUME /data
EXPOSE 8085
CMD /app/myapp_bin_name
docker build -t mymegapp .
Если CGO нет — еще половину можно выкинуть. Ну и вендоринг тут нужен само собой, но это уже не про контейнеры.Caravus
30.06.2017 08:14-1Не хорошо под рутом запускать софт, даже в контейнере. Also, use UTC, Luke.
pfihr
30.06.2017 08:45+1Про таймзону — спасибо, хороший пример.
Статья не про сборку бинарника в пакет, а про сборку из исходников. Добавлю Ваш пример во вторую часть, про сборку из бинарника.
Shtucer
Настройка файла настройки?
По приведенной в статье ссылке на Докер хаб гораздо больше интересного, чем просто названия образов. Да чего уж, и вся статья там в один из тамошних параграфов уместилась....
pfihr
там в общем про любые контейнеры, но не конкретно про go.
для go есть особенность, например, скрипт в сборке, который сам подбирает и компилирует все зависимости.
Shtucer
Эммм, вы это серьёзно? Сверим ссылки, в статье вот такая: https://hub.docker.com/_/golang/
pfihr
да, там все что надо, но не по русски ;)