08.05.2020 gitlab компиляция деплой ssh модули вендоринг ci-cd
Рассмотрим компиляцию проекта на GO в принципе и как это работает в Gitlab CI.
Для сборки нам необходимо вызвать go build -o binary_name.
Если в вашем проекте есть импорты из сторонних репозиториев, значит есть внешние зависимости. Для компиляции для GO потребует весь исходный код, включая код библиотек-зависимостей. Таким образом для компиляции необходимо обеспечить доступность в том числе сторонних библиотек, которые могут не хранится в вашем репозитории.
На данный момент актуальной версией является 1.14.2.
Начиная с версии GO 1.11 доступна функциональностей модулей (go modules).
Модули GO работают так, что сам GO подгружает все сторонние библиотеки
при вызове go run, go build или при явном подгрузке всех библиотек -
go get ./....
$GOPATH/pkg/mod
или в $HOME/go/pkg/mod, если переменная окружения $GOPATH
не установлена.
Существует подход для работы с зависимостями, называемый вендоринг.
Модули GO поддерживают вендоринг так,
что если указать параметр -mod=vendor_dir,
то зависимости будут скачаны в папку vendor_dir.
Данная папка может находится внутри вашего репозитория и быть
запушена в Git репозиторий вместе с вашим исходным кодом.
При использовании вендоринга, зависимости будут скачаны единожды и будут поставляться вместе с исходным кодом проекта, что позволит быстрее выполнять сборку.
Gitlab предоставляет возможность выполнять различные задачи после пуша в репозиторий.
Чтобы задействовать эту функциональность,
необходимо добавить в корень репозитория файл .gitlab-ci.yml.
Необходимо определить этапы (stages) и конкретные действия, выполняемые на этих этапах. Для каждого действия можно задать докер-образ.
Код вашего проекта будет автоматически скачан в папку /builds/{project_group}/{project_name}.
Это означает, что нет никакой необходимости скачивать его вручную.
В коде ниже я задал этап build и одноименное действие в нем:
stages:
— build
build:
image: rhaps1071/golang-1.14-alpine-git
stage: build
script:
— go get ./...
— GOARCH=amd64 GOOS=linux go build -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/binary
artifacts:
paths:
— binary
Здесь используется докер-образ моего авторства
rhaps1071/golang-1.14-alpine-git.
Он добавляет команду git в golang:1.14-alpine.
Данная доработка необходима для команды go get ./...,
которая подгружает зависимости и использует git clone.
Базовый образ основан на Alpine Linux, так как данный дистрибутив
весит всего несколько мегабайт.
Однако размер golang:1.14-alpine весит 370MB, что много,
но все же двухкратный выйгрыш по сравнению с golang:1.14 (809MB),
основанным на Ubuntu.
Я не использую вендоринг, поэтому для сборки мне необходимо скачать зависимости.
Для этого вызывается команда go get ./....
Как я сказал выше, код в Gitlab CI расположен в папке /builds/{project_name}/{project_folder},
которая находится вне $GOPATH.
Для того, чтобы это работало, в корне вашего проекта должен быть файл go.mod.
Создать его можно с помощью команды go mod init.
При отсутствии файла go.mod команда go get ./...
не сможет выяснить зависимости.
Если ваш проект не использует модули GO,
то ваш исходный код перед выполнением каких-либо команд нужно будет перенести в $GOPATH.
В Gitlab CI это копирование будет выглядеть так:
- cp /builds/* $GOPATH/src/
Инструкция artifacts в .gitlab-ci.yml позволяет сохранить какой-либо из файлов или папок
для скачивания, а также для использования на следующих этапах Gitlab CI.
Рассмотрим деплой полученного бинарника с помощью SSH.
deploy_stage:
image: kroniak/ssh-client
stage: deploy
environment:
name: stage
url: http://stage.project.com
when: manual
script:
— mkdir -p ~/.ssh
— echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
— chmod -R 700 ~/.ssh
— echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
— chmod 644 ~/.ssh/known_hosts
— echo "$CONFIG" > ./config.json
— scp -P$SSH_PORT -r ./config.json $SSH_USER@$SSH_HOST:/var/www/project/config/
— scp -P$SSH_PORT -r ./binary $SSH_USER@$SSH_HOST:~/binary_tmp
— ssh -p$SSH_PORT $SSH_USER@$SSH_HOST 'sudo service project stop && cp ~/binary_tmp /var/www/project/binary && sudo service project restart'
Здесь снова использован кастомизированный образ Alpine Linux — kroniak/ssh-client размером 12.1MB.
На этот раз в нем дополнительно предустановлен ssh-клиент,
благодаря чему осуществляется вызов команд ssh и scp.
Логика деплоя выглядит следующим образом:
$SSH_PRIVATE_KEY -
приватный ключ для доступа к серверу;$SSH_USER, $SSH_HOST, $SSH_PORT —
логин, и адрес сервера для деплоя;$SSH_KNOWN_HOSTS — запись для файла .ssh/known_hosts,
через который происходит валидация сервера;$CONFIG — содержимое файла конфигурации нашего сервиса в формате json;scp.
Можно было бы использовать rsync, так как он
копирует только изменившиеся файлы и копирует их в архивированном виде.
Однако когда речь об 1-2 файлах, то выйгрыша практически нет.Полученный файл .gitlab-ci.yml полностью выглядит следующим образом:
stages:
— build
— deploy
build:
image: rhaps1071/golang-1.14-alpine-git
stage: build
script:
— go get ./...
— GOARCH=amd64 GOOS=linux go build -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/binary
artifacts:
paths:
— binary
deploy_stage:
image: kroniak/ssh-client
stage: deploy
environment:
name: stage
url: http://stage.project.com
when: manual
script:
— mkdir -p ~/.ssh
— echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
— chmod -R 700 ~/.ssh
— echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
— chmod 644 ~/.ssh/known_hosts
— echo "$CONFIG" > ./config.json
— scp -P$SSH_PORT -r ./config.json $SSH_USER@$SSH_HOST:/var/www/project/config/
— scp -P$SSH_PORT -r ./binary $SSH_USER@$SSH_HOST:~/binary_tmp
— ssh -p$SSH_PORT $SSH_USER@$SSH_HOST 'sudo service project stop && cp ~/binary_tmp /var/www/project/binary && sudo service project restart'