This commit is contained in:
159
docker/deploy/deploy.sh
Executable file
159
docker/deploy/deploy.sh
Executable file
@@ -0,0 +1,159 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Deploy de um ambiente do ConectaSUS V2.
|
||||
#
|
||||
# ./deploy.sh <ambiente> <tag>
|
||||
#
|
||||
# Le os parametros do ambiente de docker/deploy/<ambiente>.env, troca a imagem
|
||||
# do container e so considera o deploy bem-sucedido depois de verificar que a
|
||||
# aplicacao responde. Se nao responder, volta sozinho para a imagem anterior.
|
||||
#
|
||||
# Roda a partir do runner, que usa o docker.sock do host — por isso todos os
|
||||
# caminhos de mount sao caminhos do HOST, nao do job.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
AMBIENTE="${1:-}"
|
||||
TAG="${2:-}"
|
||||
REGISTRY="${REGISTRY:-192.168.0.41:3001}"
|
||||
IMAGE_NAME="${IMAGE_NAME:-rkm/conectasus-api}"
|
||||
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-90}"
|
||||
|
||||
AQUI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
log() { echo "[deploy] $*"; }
|
||||
fail() { echo "[deploy][ERRO] $*" >&2; exit 1; }
|
||||
|
||||
[ -n "${AMBIENTE}" ] || fail "uso: $0 <ambiente> <tag>"
|
||||
[ -n "${TAG}" ] || fail "uso: $0 <ambiente> <tag>"
|
||||
|
||||
ENV_FILE="${AQUI}/${AMBIENTE}.env"
|
||||
[ -f "${ENV_FILE}" ] || fail "ambiente desconhecido: ${AMBIENTE} (esperado ${ENV_FILE})"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
set -a; . "${ENV_FILE}"; set +a
|
||||
|
||||
# Gabarito nao preenchido: melhor recusar do que implantar em lugar errado.
|
||||
if grep -q 'TROCAR' "${ENV_FILE}"; then
|
||||
fail "${ENV_FILE} ainda tem valores de gabarito (TROCAR)."
|
||||
fi
|
||||
|
||||
for v in CONTAINER_NAME CONFIG_DIR PORT_API PORT_SPA PORT_WS; do
|
||||
[ -n "${!v:-}" ] || fail "${v} nao definido em ${ENV_FILE}"
|
||||
done
|
||||
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
||||
|
||||
log "ambiente : ${AMBIENTE}"
|
||||
log "container: ${CONTAINER_NAME}"
|
||||
log "imagem : ${IMAGE}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Guarda a imagem atual para poder voltar. Vale o digest, nao a tag: a tag e
|
||||
# mutavel e pode ja apontar para outra coisa na hora do rollback.
|
||||
# ---------------------------------------------------------------------------
|
||||
ANTERIOR=""
|
||||
if docker inspect "${CONTAINER_NAME}" >/dev/null 2>&1; then
|
||||
ANTERIOR=$(docker inspect "${CONTAINER_NAME}" --format '{{.Image}}')
|
||||
log "imagem atual: ${ANTERIOR}"
|
||||
else
|
||||
log "container ainda nao existe; primeiro deploy"
|
||||
fi
|
||||
|
||||
log "puxando ${IMAGE}"
|
||||
docker pull "${IMAGE}" >/dev/null || fail "pull falhou (registry acessivel? credencial presente?)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Config do host. O shell do job pode rodar dentro de um container do
|
||||
# act_runner e, portanto, nao enxergar os caminhos do host. A verificacao
|
||||
# precisa passar pelo daemon Docker: e ele que avaliara o bind mount no
|
||||
# host e depois subira a aplicacao com esses mesmos arquivos.
|
||||
#
|
||||
# --mount, diferentemente de -v, recusa uma origem inexistente em vez de
|
||||
# criar um diretorio vazio silenciosamente.
|
||||
# ---------------------------------------------------------------------------
|
||||
log "validando configuracao no host Docker"
|
||||
if ! docker run --rm \
|
||||
--mount "type=bind,src=${CONFIG_DIR},dst=/config,readonly" \
|
||||
--entrypoint sh \
|
||||
"${IMAGE}" \
|
||||
-c '
|
||||
set -eu
|
||||
for f in .env connections.json config.js; do
|
||||
[ -f "/config/${f}" ] || {
|
||||
echo "arquivo obrigatorio ausente: /config/${f}" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
[ -d /config/storage ] || {
|
||||
echo "diretorio obrigatorio ausente: /config/storage" >&2
|
||||
exit 1
|
||||
}
|
||||
'
|
||||
then
|
||||
fail "configuracao invalida ou inacessivel no host Docker: ${CONFIG_DIR}"
|
||||
fi
|
||||
|
||||
subir() {
|
||||
local img="$1"
|
||||
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
|
||||
docker run -d \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
--restart unless-stopped \
|
||||
${NETWORK:+--network "${NETWORK}"} \
|
||||
-p "${PORT_API}:80" \
|
||||
-p "${PORT_SPA}:8080" \
|
||||
-p "${PORT_WS}:6001" \
|
||||
-v "${CONFIG_DIR}/.env:/var/www/api/.env:ro" \
|
||||
-v "${CONFIG_DIR}/connections.json:/var/www/api/connections.json:ro" \
|
||||
-v "${CONFIG_DIR}/config.js:/var/www/frontend/config.js:ro" \
|
||||
-v "${CONFIG_DIR}/storage:/var/www/api/storage" \
|
||||
"${img}" >/dev/null
|
||||
}
|
||||
|
||||
saudavel() {
|
||||
local fim=$((SECONDS + HEALTH_TIMEOUT))
|
||||
while [ ${SECONDS} -lt ${fim} ]; do
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "${CONTAINER_NAME}" 2>/dev/null)" != "true" ]; then
|
||||
return 1
|
||||
fi
|
||||
local spa api
|
||||
spa=$(docker exec "${CONTAINER_NAME}" curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/ 2>/dev/null || echo 000)
|
||||
api=$(docker exec "${CONTAINER_NAME}" curl -s -o /dev/null -w '%{http_code}' http://localhost:80/ 2>/dev/null || echo 000)
|
||||
# A API nao tem rota "/", entao 404 e resposta valida; 000 e que nao e.
|
||||
if [ "${spa}" = "200" ] && [ "${api}" != "000" ]; then
|
||||
log "saudavel (spa=${spa} api=${api})"
|
||||
return 0
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
log "subindo ${CONTAINER_NAME}"
|
||||
subir "${IMAGE}"
|
||||
|
||||
if saudavel; then
|
||||
log "deploy de ${AMBIENTE} concluido em ${IMAGE}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Nao respondeu. Um container no ar mas quebrado e pior do que a versao
|
||||
# anterior no ar, entao volta.
|
||||
# ---------------------------------------------------------------------------
|
||||
echo "::error::deploy de ${AMBIENTE} falhou na verificacao de saude"
|
||||
docker logs "${CONTAINER_NAME}" 2>&1 | tail -30 || true
|
||||
|
||||
if [ -z "${ANTERIOR}" ]; then
|
||||
fail "sem versao anterior para voltar; container deixado no ar para diagnostico"
|
||||
fi
|
||||
|
||||
log "revertendo para ${ANTERIOR}"
|
||||
subir "${ANTERIOR}"
|
||||
|
||||
if saudavel; then
|
||||
fail "deploy revertido: ${AMBIENTE} voltou para a versao anterior"
|
||||
fi
|
||||
|
||||
fail "deploy falhou E a reversao tambem nao respondeu — ${AMBIENTE} precisa de atencao manual"
|
||||
Reference in New Issue
Block a user