Сначала сделал как описано в этой заметке для Go, но не заработало. Оказалось есть нюансы.
Оказалось, что Rust статически линкуется только с внешними зависимостями на Rust, а с С библиотеками линкуется динамически (libc и т.п.). Проверяется это через утилиту командной строки ldd. Из-за этого собранный бинарник не запускается в контейнерах собранных на базе alpine и scratch. Нужно ставить туда дополнительно libc, libgcc и т.п.
Но есть возможность собрать бинарник в котором все будет слинковано статически. Для этого используется библиотека musl и есть проект muslrust. В данном проекте предлагается собирать бинарник через специальный docker контейнер и нам для Github Actions это не сильно подходит.
Далее есть target x86_64-unknown-linux-musl. При сборке с данным таргетом библиотеки линкуются статически, поэтому его и бдуем использовать в нашем сценарии. Для этого нужно добавить всего два маленьких шага в наш pipeline:
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
# NB: sets CARGO_BUILD_TARGET evar - do not need --target flag in build
target: x86_64-unknown-linux-musl
После этого бинарник успешно запускается в scratch образе.
И вторая проблема связана с багом docker-compose. Если контейнер запущен и при docker-compose up происходит recreate, то контейнер автоматически не стартует. Данная проблема описана уже давно и без решения (возможно в одном из обновлений исправят). Поэтому в секции deploy вызываем два раза docker-compose:
- name: Run container
run: |
cd ~
docker-compose up -d --build --pull=always
docker-compose start &
Так же добавляем кэш зависимостей с помощью actions/cache
- name: Set up cargo cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
Ну и ниже полный файл сценария (где myapp – имя бинарника)
name: Rust
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl
- uses: taiki-e/setup-cross-toolchain-action@v1
with:
# NB: sets CARGO_BUILD_TARGET evar - do not need --target flag in build
target: x86_64-unknown-linux-musl
- name: Set up cargo cache
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- name: Build
run: cargo build --release
- name: Test
run: cargo test --release
- name: Copy binary
run: cp ./target/x86_64-unknown-linux-musl/release/myapp ./
- name: upload artifact
uses: actions/upload-artifact@v4
with:
name: my-artifact
path: |
Dockerfile.action
myapp
build-and-push-image:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Update cert
run: |
sudo update-ca-certificates
sudo cp /etc/ssl/certs/ca-certificates.crt ca-certificates.crt
mkdir zoneinfo
sudo cp -r /usr/share/zoneinfo/* ./zoneinfo
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- uses: actions/download-artifact@v4
with:
name: my-artifact
- name: set permissions
run: chmod a+x myapp
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile.action
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
runs-on: self-hosted
needs: build-and-push-image
steps:
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run container
run: |
cd ~
docker-compose up -d --build --pull=always
docker-compose start &
Файл Dockerfile.action берем из ранее указанной статьи про Go