Saudações.
Este tutorial ensina como compilar os pacotes originais do Alpine Linux usando os esquemas oficiais.
O propósito e utilidade desses procedimentos serve à personalização de programas na estapa de compilação como:
- Adicionar features omitidas no pacote oficial;
- Adicionar configurações;
- Ativar tunings de arquitetura (instruções nativas);
- Criar repositórios de pacotes próprios.
Tutoriais de base:
Pré-requisitos:
- VM/VPS/Host com instalação do Linux Alpine;
- Internet no servidor (sua VPS ou host);
1 – Preparando Alpine
Vamos baixar os pacotes de ferramentas e fontes. Iniciaremos logados como root.
1.1 – Ferramentas
Programas compiladores:
# Atualizar o sistema base
apk update;
apk upgrade;
# (No Host ou VM, se houver atualizacao de kernel, reinicie: reboot )
# Instalar ferramentas para o ambiente de compilacao:
apk add \
alpine-sdk abuild doas \
sudo bash openssl tar \
gnupg file grep binutils \
linux-headers strace \
git \
make pkgconf patch \
gcc g++ readline musl-dev musl-utils;
1.2 – Usuário para compilação
Os procedimentos de compilação devem ser realizados por usuário comum (sem privilégios de root).
# Criar usuário para rodar processos de compilacao
adduser -D builder;
# Adicionar no grupo de construtores (abuild)
addgroup builder abuild;
# Dar poder de SUDO para rodar algumas operacoes como root
echo "builder ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/builder;
1.3 – Alternar para modo usuário “builder”
De agora em diante vamos alterar para o modo usuário comum “builder“.
Você pode usar o “su” para obter shell (/bin/ash) como “builder” ou fazer logout do root e login nesse usuário.
# Rodar o shell como usuario builder
# Shell ash
su - builder -s /bin/ash;
# OU shell bash:
# su - builder -s /bin/bash;
Agora logado como “builder“:
# Conferir id:
# - Precisa ter uid diferente de zero
# - Precisa estar no grupo: abuild
id;
# uid=1000(builder) gid=1000(builder) groups=300(abuild),1000(builder)
# Entrar na pasta do usuário:
cd ~;
# Conferir:
pwd;
# /home/builder
1.4 – Gerar configuração e chave privada
A chave privada (RSA 4096 bits) é necessária para assinar digitalmente os pacotes que você gerar.
# Alternativa automatica (chave com caminho aleatorio):
# echo | abuild-keygen -a -i;
# Variaveis
# Contato responsavel
NAME="Eu Mesmo";
EMAIL="eu@mesmo.com";
# Caminho das chaves
PRIV="/home/builder/.abuild/builder-default.rsa";
PUBK="$PRIV.pub";
# Criar pasta da config do abuild:
mkdir -p .abuild;
mkdir -p /home/builder/packages;
# Gerar chave privada RSA 4096 bits
[ -s "$PRIV" ] || {
openssl genrsa \
-out $PRIV \
4096;
};
# Gerar chave publica derivada da privada
[ -s "$PUBK" ] || {
openssl rsa \
-in $PRIV \
-pubout -out $PUBK;
};
# Gerar configuracao de usuario compilador
(
echo "PACKAGER_PRIVKEY=\"$PRIV\"";
echo "PACKAGER=\"$NAME <$EMAIL>\"";
echo "MAINTAINER=\"$NAME <$EMAIL>\"";
echo 'USE_COLORS=1';
echo 'DABUILD_PACKAGES="/home/builder/packages"';
) > .abuild/abuild.conf;
# Colocar no caminho padrao:
sudo sh -c "cat .abuild/abuild.conf > /etc/abuild.conf";
# Copiar chave publica para a pasta de chaves do sistema:
sudo install -m 0644 -o root -g root "$PUBK" /etc/apk/keys/;
#sudo cp $PUBK /etc/apk/keys/;
O arquivo de configuração do abuilder será lido em /home/builder/.abuild/abuild.conf ou /etc/abuild.conf (caminho principal).
Diretórios para arquivos baixados (fontes, patchs, projetos git, etc):
# Diretório de cache para arquivos baixados
sudo mkdir -p /var/cache/distfiles;
sudo chgrp abuild /var/cache/distfiles;
sudo chmod g+w /var/cache/distfiles;
1.5 – Obter os fontes oficiais
Os fontes do Alpine são conjuntos de pacotes “aports” contendo os scripts que baixam os fontes originais, patches (correções), dependências e ferramentas para construir um pacote final pronto para uso.
# Baixando projeto aports do Alpine Linux
# - Na pasta do usuario
cd ~;
# - Clonar projeto git
git clone --depth 1 https://gitlab.alpinelinux.org/alpine/aports.git;
O projeto será baixado na pasta “aports“.
Diretórios em “./aports/“:
- main/ – Pacotes oficiais que fazem parte da distribuição Alpine Linux, 1.653 pacotes;
- community/: Pacotes de vários fabricantes e projetos diversos, 7.775 pacotes;
- testing/: Pacotes em fase de teste para aprovação (branch edge), 3.167 pacotes;
Explore os diretórios para se acostumar com o formato.
2 – Analisando o pacote Redis
Vou usar o Redis como base para analise e recompilação.
Ele se encontra em: /home/builder/aports/community/redis
2.1 – Arquivos do pacote redis
Arquivos dentro do pacote Redis:
- APKBUILD – Script shell POSIX, define variáveis e funções que serão executadas durante a construção do pacote.
- pkgname: Nome do pacote;
- pkgver: Versão;
- pkgrel: Número incremental por compilação, reinicia ao mudar o pkgver;
- pkgdesc: Descrição do pacote e projeto;
- url: Url do projeto;
- arch: Arquitetura, padrão “all”;
- license: Nome da licença usada (ex: MIT);
- depends: Nome dos pacotes necessários previamente para rodar o programa;
- makedepends: Pacotes necessários para compilar o pacote;
- checkdepends: Pacotes necessários para verificação do pacote;
- install: Nome dos scripts usados na instalação do pacote final;
- subpackages: Nome dos pacotes gerados pela compilação do projeto;
- source: Arquivos e URLs onde os fontes, patchs e artefatos do projeto estão;
- builddir: Diretório usado para trabalhar na construção do pacote;
- build(): Função de construção;
- check(): Função de verificação;
- package(): Função de empacotamento;
- Arquivos para o OpenRC:
- redis-sentinel.initd – Instalado em /etc/init.d/redis-sentinel;
- redis.initd – Instalado em /etc/init.d/redis;
- redis.confd – Instalado em /etc/conf.d/redis;
- Configurações final para uso do software:
- redis.logrotate – Instalado em /etc/logrotate.d/redis;
- Patchs e alterações do código original:
- redis.conf.patch – Altera a configuração padrão do projeto Redis;
- sentinel.conf.patch – Altera a configuração padrão do sentinel;
- Scripts do pacote pronto para execução no ambiente de destino:
- redis.pre-install – Script a executar antes da instalação;
- redis.post-install – Script a executar após a instalação;
Agora vamos compilá-lo:
# Entrar no projeto aports do Redis
cd ~/aports/community/redis/;
# Compilar:
# -r = instalar dependencias
# -c = saida colorida durante compilacao
abuild -r -c;
Verificar pacote pronto:
# Verificar pacotes prontos:
find /home/builder/packages/community;
2.2 – Criando pacote personalizando do Redis
Vou copiar o pacote abuild do Redis e criar o “Redis-Native” que será compilado com instruções especiais (extensões da CPU local) para melhor performance.
# Pasta do novo pacote
mkdir -p /home/builder/aports/community/redis-native;
# Copiar arquivos do pacote original
cp -rav \
/home/builder/aports/community/redis/* \
/home/builder/aports/community/redis-native;
# Entrar na nova pasta:
cd /home/builder/aports/community/redis-native;
# Renomear de 'redis' para 'redis-native'
sed -i '/^pkgname=redis$/s/redis/redis-native/' APKBUILD;
sed -i '/redis.initd/s/redis/redis-native/' APKBUILD;
sed -i '/redis.confd/s/redis/redis-native/' APKBUILD;
sed -i '/redis-sentinel.initd/s/sentinel/native-sentinel/' APKBUILD;
sed -i '/redis.logrotate/s/redis/redis-native/' APKBUILD;
# Adicionar builddir para redis-$ver
sed -i '/pkgdesc/a builddir=$srcdir/redis-$pkgver' APKBUILD;
# Renomear arquivos
mv redis.pre-install redis-native.pre-install;
mv redis.post-install redis-native.post-install;
mv redis.initd redis-native.initd;
mv redis.confd redis-native.confd;
mv redis-sentinel.initd redis-native-sentinel.initd;
mv redis.logrotate redis-native.logrotate;
Farei a alteração da função build() no arquivo APKBUILD para o seguinte código que optimizará para minha CPU:
#...
# Original:
#- build() {
#- export CFLAGS="$CFLAGS -DUSE_MALLOC_USABLE_SIZE -O2 -flto=auto"
#- make USE_JEMALLOC=no MALLOC=libc BUILD_TLS=yes all
#- }
build() {
# -mcpu=native: emite todo o ISA do CPU de build (LSE, PMULL, SHA2/3,
# FP16, FCMA, dot product, etc.) e já ativa o -mtune correspondente.
# -O3: libera vetorização mais agressiva; Redis compila estável em -O3.
# -flto=auto: LTO paralelo (já estava).
# -fno-semantic-interposition: permite inline de símbolos internos.
# -fno-plt: chamadas externas diretas, sem thunk da PLT.
export CFLAGS="$CFLAGS -DUSE_MALLOC_USABLE_SIZE \
-O3 \
-flto=auto \
-mcpu=native \
-fno-semantic-interposition -fno-plt";
export CXXFLAGS="$CFLAGS";
export LDFLAGS="$LDFLAGS -flto=auto -Wl,-O1 -Wl,--as-needed";
# Jemalloc bundled (default do Redis). Enorme ganho para carga real:
# fragmentação menor e alocação multi-thread mais rápida.
make \
USE_JEMALLOC=yes \
BUILD_TLS=yes \
all;
# Limpar binarios, remover tudo que nao é usado em producao
binlist="
redis-server
redis-cli
redis-sentinel
redis-benchmark
redis-check-aof
redis-check-rdb
";
for bin in $binlist; do
strip -s \
-R .comment \
-R .note.gnu.build-id \
-R .note.ABI-tag \
-R .gnu.version \
src/$bin;
done;
}
#...Construindo:
# Compilar:
# -r = instalar dependencias
# -c = saida colorida durante compilacao
abuild -r -c;
Verificar pacote pronto:
# Verificar pacotes prontos:
find /home/builder/packages/community;
x
3 – Construindo via Docker
O ideal é criar uma imagem de container que tenha todos os pacotes e preparativos prontos, assim você pula direto para a personalização e compilação do seu pacote.
.
“Comece fazendo o que é necessário,
depois o que é possível,
e de repente você estará
fazendo o impossível.”
São Francisco de Assis
Terminamos por hoje!
Patrick Brandão, patrickbrandao@gmail.com
