Давайте розглянемо компіляцію проекту в 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 підтримують vendoring, так що якщо ви вкажете -mod=vendor_dir
, то залежності будуть завантажені в папкуvendor_dir
. Ця тека може знаходитися всередині вашого репозиторію і записуватися в репозиторій Git разом з вашим вихідним кодом.
При використанні вендорингу залежності будуть завантажені один раз і поставляються з вихідним кодом проекту, що дозволить швидше збирати.
Gitlab
Gitlab надає можливість виконання різних завдань після надсилання до репозиторію.
Щоб скористатися цим функціоналом, потрібно додати .gitlab-ci.yml
.
Компіляція в Gitlab
Вам потрібно визначити етапи та конкретні дії, які будуть виконані на цих кроках. Для кожної дії ви можете вказати образ Docker.
Код вашого проекту буде автоматично завантажений в папку/builds/{project_group}/{project_name}
. Це означає, що немає необхідності завантажувати його вручну.
У наведеному нижче коді я визначив крок збірки та однойменну дію в ньому:
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
Тут використано docker-образ мого авторства rhaps1071/golang-1.14-alpine-git.
Він додає команду git
до файлу golang:1.14-alpine
. Ця модифікація необхідна для команди go get ./...
, яка завантажує залежності та використовує git clone
.
Базовий образ заснований на Alpine Linux, так як важить цей дистрибутив всього кілька мегабайт.
Тим не менш, розмір golang:1.14-alpine
важить 370 МБ, що багато, але все одно вдвічі більше в порівнянні з golang:1.14
(809 МБ) на базі Ubuntu.
Я не використовую vendoring, тому мені потрібно завантажити залежності для збірки. Для цього викличте командуgo get ./...
.
Як я вже говорив вище, код в Gitlab CI знаходиться в файлі , який знаходиться за межами $GOPATH
/builds/{project_name}/{project_folder}
.
Для того, щоб це спрацювало, має бути . Ви go.mod
можете створити його за допомогою go mod init
кнопки . Якщо у вас немає файлу go.mod
, команда go get ./...
не зможе з'ясувати залежності.
Якщо у вашому проекті не використовуються модулі GO, вам потрібно буде перемістити вихідний код у файл $GOPATH
.
У Gitlab CI ця копія виглядатиме так:- cp /builds/* $GOPATH/src/
Інструкція Artifacts дозволяє зберегти будь-який з .gitlab-ci.yml
файлів або папок для скачування, а також для використання на наступних етапах Gitlab CI.
Розгортання SSH
Давайте розглянемо розгортання отриманого двійкового файлу за допомогою 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 — розміром 12,1 МБkroniak/ssh-client
. Цього разу він додатково встановлений разом із клієнтом ssh, завдяки якому ssh
scp
команди та .Логіка розгортання наступна:
- Увімкнено такі змінні Gitlab:
$SSH_PRIVATE_KEY
- приватний ключ для доступу до сервера;$SSH_USER
,$SSH_HOST
, -$SSH_PORT
логін та адреса сервера для розгортання;$SSH_KNOWN_HOSTS
- Запис для файла .ssh/known_hosts, за допомогою якого перевіряється сервер.$CONFIG
– вміст конфігураційного файлу нашого сервісу у форматі JSON; - При розгортанні спочатку заповніть всі необхідні файли даними зі змінних Gitlab;
- Всі необхідні файли копіюються на сервер за допомогою
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'