build: registry pela VPN (10.99.0.3:3000), padrão da stack
Migra o RX para o mesmo padrão de scoreodonto/newwhats: imagens publicadas e
puxadas por 10.99.0.3:3000 (WireGuard), não git.clube67.com — o proxy HTTPS
público corta uploads em ~60s, então blobs grandes não sobem por ele. Mesmo
Gitea/storage; verificado que 10.99.0.3:3000/ruicesar/rx-scoreodonto:v2.2.0
entrega o mesmo image id que roda em prod, então o rollback para tags antigas
segue válido.
- build.yml: registry VPN; promote copia o manifest pela API v2 (buildx imagetools
não fala HTTP) e autentica com o docker login do runner (não há secret
REGISTRY_TOKEN neste repo).
- docker-compose.prod.yml: image → VPN; ${RX_TAG:?} no lugar de :-latest (sem a
tag o deploy ABORTA em vez de subir imagem indeterminada).
- deploy-prod.sh: pull pela VPN com fallback à imagem local de tag exata; poda de
imagens antigas ao fim (mantém as 2 mais recentes, best-effort).
Requer insecure-registries: ["10.99.0.3:3000"] no daemon da VPS1 e do runner
(já configurado para os outros projetos).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+36
-14
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: Login no registry
|
||||
run: |
|
||||
if [ -n "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.clube67.com -u ruicesar --password-stdin
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login 10.99.0.3:3000 -u ruicesar --password-stdin
|
||||
else
|
||||
echo "REGISTRY_TOKEN ausente — usando a credencial já presente no host."
|
||||
fi
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
- name: Publicar por COMMIT (:<sha> + :latest), com retry p/ 499
|
||||
run: |
|
||||
set -e
|
||||
REG=git.clube67.com/ruicesar
|
||||
REG=10.99.0.3:3000/ruicesar
|
||||
C="${{ steps.vars.outputs.commit }}"
|
||||
SRC="rxscoreodontocom-app:latest"
|
||||
DST="${REG}/rx-scoreodonto"
|
||||
@@ -80,33 +80,55 @@ jobs:
|
||||
echo "version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "commit=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login no registry
|
||||
- name: Credencial do registry (reusa o docker login do runner)
|
||||
env:
|
||||
REGISTRY: 10.99.0.3:3000
|
||||
ACCEPT: 'application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json'
|
||||
run: |
|
||||
set -e
|
||||
# `secrets.REGISTRY_TOKEN` não está definido neste repo; o curl abaixo precisa
|
||||
# da credencial do `docker login` do runner (~/.docker/config.json).
|
||||
if [ -n "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.clube67.com -u ruicesar --password-stdin
|
||||
AUTH=$(printf 'ruicesar:%s' "${{ secrets.REGISTRY_TOKEN }}" | base64 -w0)
|
||||
else
|
||||
AUTH=$(python3 -c "import json,os;print(json.load(open(os.path.expanduser('~/.docker/config.json')))['auths']['${REGISTRY}']['auth'])")
|
||||
fi
|
||||
[ -n "$AUTH" ] || { echo "ERRO: sem credencial (rode: docker login ${REGISTRY})"; exit 1; }
|
||||
echo "::add-mask::$AUTH"
|
||||
echo "REG_AUTH=$AUTH" >> "$GITHUB_ENV"
|
||||
echo "ACCEPT=$ACCEPT" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Integridade — a imagem do commit existe? (nunca buildar na tag)
|
||||
run: |
|
||||
set -e
|
||||
REG=git.clube67.com/ruicesar
|
||||
C="${{ steps.vars.outputs.commit }}"
|
||||
if ! docker buildx imagetools inspect "${REG}/rx-scoreodonto:$C" >/dev/null 2>&1; then
|
||||
echo "ERRO: ${REG}/rx-scoreodonto:$C não existe no registry."
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Basic ${REG_AUTH}" \
|
||||
-H "Accept: ${ACCEPT}" "http://10.99.0.3:3000/v2/ruicesar/rx-scoreodonto/manifests/$C")
|
||||
[ "$code" = "200" ] || {
|
||||
echo "ERRO: rx-scoreodonto:$C não existe no registry (HTTP $code)."
|
||||
echo " Faça 'git push origin main' (que builda o commit $C) ANTES de criar a tag."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
echo "OK: rx-scoreodonto:$C presente no registry."
|
||||
|
||||
- name: Promover — re-tag commit → versão (SEM rebuild) + conferir digest
|
||||
- name: Promover — copiar manifest commit → versão (SEM rebuild) + conferir digest
|
||||
run: |
|
||||
set -e
|
||||
REG=git.clube67.com/ruicesar
|
||||
C="${{ steps.vars.outputs.commit }}"; V="${{ steps.vars.outputs.version }}"
|
||||
DST="${REG}/rx-scoreodonto"
|
||||
docker buildx imagetools create -t "$DST:$V" "$DST:$C"
|
||||
dC=$(docker buildx imagetools inspect "$DST:$C" --format '{{.Manifest.Digest}}')
|
||||
dV=$(docker buildx imagetools inspect "$DST:$V" --format '{{.Manifest.Digest}}')
|
||||
A="Authorization: Basic ${REG_AUTH}"; REPO="ruicesar/rx-scoreodonto"
|
||||
# `docker buildx imagetools` não fala com registry HTTP (e não tem --insecure aqui),
|
||||
# então copiamos o manifest pela API v2. Byte a byte ⇒ mesmo digest.
|
||||
CT=$(curl -s -H "$A" -H "Accept: ${ACCEPT}" \
|
||||
-o manifest.json -w '%{content_type}' "http://10.99.0.3:3000/v2/${REPO}/manifests/$C")
|
||||
dC=$(curl -s -H "$A" -H "Accept: ${ACCEPT}" -o /dev/null -D - \
|
||||
"http://10.99.0.3:3000/v2/${REPO}/manifests/$C" | tr -d '\r' \
|
||||
| awk '/^[Dd]ocker-[Cc]ontent-[Dd]igest:/{print $2}')
|
||||
curl -sf -H "$A" -X PUT -H "Content-Type: ${CT}" \
|
||||
--data-binary @manifest.json "http://10.99.0.3:3000/v2/${REPO}/manifests/$V" >/dev/null
|
||||
dV=$(curl -s -H "$A" -H "Accept: ${ACCEPT}" -o /dev/null -D - \
|
||||
"http://10.99.0.3:3000/v2/${REPO}/manifests/$V" | tr -d '\r' \
|
||||
| awk '/^[Dd]ocker-[Cc]ontent-[Dd]igest:/{print $2}')
|
||||
rm -f manifest.json
|
||||
[ "$dC" = "$dV" ] || { echo "ERRO: digest divergiu ($V=$dV != $C=$dC)"; exit 1; }
|
||||
echo "Promovido sem rebuild: :$V e :$C apontam para o mesmo artefato (digest $dV)."
|
||||
|
||||
|
||||
+30
-2
@@ -31,8 +31,19 @@ echo " Versão exibida (/api/health → badge) injetada em runtime = $TAG"
|
||||
echo " Volumes preservados: uploads/ processed/ data/ dental-server/updates/ (auto-update dos clients)"
|
||||
echo "──────────────────────────────────────────────────────────────"
|
||||
|
||||
echo "[1/3] pull da imagem $TAG ..."
|
||||
docker compose $FILES pull app
|
||||
echo "[1/3] pull da imagem $TAG (registry pela VPN) ..."
|
||||
# Registry = 10.99.0.3:3000 (WireGuard). Se o pull falhar (VPN fora do ar), exigimos
|
||||
# a imagem local COM A TAG EXATA — nunca caímos para :latest.
|
||||
if ! docker compose $FILES pull app 2>/dev/null; then
|
||||
echo " pull indisponível — conferindo imagem local com a tag $TAG ..."
|
||||
img="10.99.0.3:3000/ruicesar/rx-scoreodonto:${TAG}"
|
||||
docker image inspect "$img" >/dev/null 2>&1 || {
|
||||
echo "ERRO: $img não existe nem no registry nem localmente."
|
||||
echo " Confira a VPN (ping 10.99.0.3)."
|
||||
exit 1
|
||||
}
|
||||
echo " ok: $img (local)"
|
||||
fi
|
||||
|
||||
echo "[2/3] subindo (sem build) ..."
|
||||
docker compose $FILES up -d --no-build app
|
||||
@@ -44,6 +55,23 @@ curl -s --max-time 10 https://rx.scoreodonto.com/api/health || echo "(ainda subi
|
||||
echo
|
||||
docker logs rx_scoreodonto_app --tail 5 2>&1 | grep -iE 'VERSION|listen|porta|ready' || true
|
||||
|
||||
echo "[poda] removendo imagens antigas (mantém as 2 versões mais recentes) ..."
|
||||
# Política "atual + 1 anterior": após deployar, apaga as imagens vX.Y.Z locais
|
||||
# além das 2 mais recentes. Só tags vX.Y.Z; nunca imagem em uso; best-effort.
|
||||
prune_old_images() {
|
||||
local keep=2 repo="10.99.0.3:3000/ruicesar/rx-scoreodonto" tag olds inuse
|
||||
inuse=$(docker ps --format '{{.Image}}' 2>/dev/null | sort -u)
|
||||
olds=$(docker images --format '{{.Tag}}' "$repo" 2>/dev/null \
|
||||
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | head -n -"$keep") || true
|
||||
[ -z "$olds" ] && return 0
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
printf '%s\n' "$inuse" | grep -qxF "$repo:$tag" && continue
|
||||
docker rmi "$repo:$tag" >/dev/null 2>&1 && echo " removida: $repo:$tag"
|
||||
done <<< "$olds"
|
||||
}
|
||||
prune_old_images || true
|
||||
|
||||
echo
|
||||
echo "✅ Deploy concluído (tag $TAG)."
|
||||
echo " Rollback: ./deploy-prod.sh <tag-anterior> <commit-anterior>"
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
# O `commit` também é injetado em runtime (vem do CI que buildou a imagem do commit).
|
||||
services:
|
||||
app:
|
||||
image: git.clube67.com/ruicesar/rx-scoreodonto:${RX_TAG:-latest}
|
||||
# Registry pela VPN (10.99.0.3:3000), não git.clube67.com — o proxy HTTPS corta
|
||||
# uploads grandes. `:?` (não `:-latest`): sem RX_TAG o deploy ABORTA, em vez de
|
||||
# subir uma imagem indeterminada. Requer insecure-registries no daemon da VPS1.
|
||||
image: "10.99.0.3:3000/ruicesar/rx-scoreodonto:${RX_TAG:?defina RX_TAG}"
|
||||
environment:
|
||||
- APP_VERSION=${RX_TAG:-dev}
|
||||
- APP_VERSION=${RX_TAG}
|
||||
- APP_ENV=PROD
|
||||
- GIT_COMMIT=${RX_COMMIT:-dev}
|
||||
|
||||
Reference in New Issue
Block a user