deploy_lqmd_retrieval_front_dev.sh 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #!/bin/bash
  2. set -euo pipefail
  3. ENV="dev"
  4. PROJECT_NAME="LQMDRetrievalFront"
  5. BASE_DIR="/home/lq/lq_workspace/${PROJECT_NAME}"
  6. SRC_DIR="${BASE_DIR}/source/${PROJECT_NAME}"
  7. COMPOSE_RUN_DIR="${BASE_DIR}/app/docker"
  8. COMPOSE_FILE_DIR="${BASE_DIR}/app/docker"
  9. COMPOSE_FILE_NAME="docker-compose.${ENV}.yaml"
  10. SERVICES=("LQMDRetrievalFront")
  11. VERSION_SOURCE_SERVICE="${SERVICES[0]}"
  12. COMPOSE_FILE_PATH="${COMPOSE_FILE_DIR}/${COMPOSE_FILE_NAME}"
  13. log_info() {
  14. echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*"
  15. }
  16. log_error() {
  17. echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2
  18. }
  19. require_path() {
  20. local type=$1
  21. local path=$2
  22. if [ "$type" = "file" ] && [ ! -f "$path" ]; then
  23. log_error "file does not exist: $path"
  24. exit 1
  25. fi
  26. if [ "$type" = "dir" ] && [ ! -d "$path" ]; then
  27. log_error "directory does not exist: $path"
  28. exit 1
  29. fi
  30. }
  31. get_current_version() {
  32. local service=$1
  33. local compose_file=$2
  34. local image_line
  35. image_line=$(grep -A 20 "^[[:space:]]*${service}:" "$compose_file" | grep -E "^[[:space:]]*image:" | head -1) || {
  36. log_error "image field not found for service: $service"
  37. exit 1
  38. }
  39. image_line=$(echo "$image_line" | sed 's/#.*$//')
  40. echo "$image_line" | sed -E 's/.*:([^:]*)$/\1/' | tr -d '[:space:]'
  41. }
  42. increment_version() {
  43. local ver=$1
  44. if [[ ! $ver =~ ^v[0-9]+\.[0-9]+$ ]]; then
  45. log_error "invalid version format, expected v0.01 or v1.2, got: $ver"
  46. exit 1
  47. fi
  48. local prefix="${ver%.*}"
  49. local last_digit="${ver##*.}"
  50. local len=${#last_digit}
  51. local num=$((10#$last_digit + 1))
  52. local new_last
  53. new_last=$(printf "%0${len}d" "$num")
  54. echo "${prefix}.${new_last}"
  55. }
  56. update_compose_version_for_services() {
  57. local compose_file=$1
  58. local new_version=$2
  59. shift 2
  60. local services=("$@")
  61. cp "$compose_file" "${compose_file}.bak"
  62. for svc in "${services[@]}"; do
  63. sed -i -E "/^[[:space:]]*${svc}:/,/^[[:space:]]*image:/ s/(image:[[:space:]]*.*:)([^[:space:]#]*)([[:space:]]*#.*)?$/\1${new_version}\3/" "$compose_file"
  64. log_info "updated $svc image version to $new_version"
  65. done
  66. }
  67. get_image_repository() {
  68. local service=$1
  69. local compose_file=$2
  70. local image_line
  71. image_line=$(grep -A 20 "^[[:space:]]*${service}:" "$compose_file" | grep -E "^[[:space:]]*image:" | head -1)
  72. image_line=$(echo "$image_line" | sed 's/#.*$//')
  73. echo "$image_line" | sed -E 's/^[[:space:]]*image:[[:space:]]*//' | sed -E 's/:[^:]*$//' | tr -d '[:space:]'
  74. }
  75. cleanup_old_images_for_services() {
  76. local compose_file=$1
  77. shift
  78. local services=("$@")
  79. for svc in "${services[@]}"; do
  80. local image_repo
  81. image_repo=$(get_image_repository "$svc" "$compose_file")
  82. mapfile -t all_tags < <(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${image_repo}:" | sed "s|${image_repo}:||" | sort -V)
  83. if [ ${#all_tags[@]} -le 3 ]; then
  84. log_info "$svc image count <= 3, skip cleanup"
  85. continue
  86. fi
  87. local to_delete=$(( ${#all_tags[@]} - 3 ))
  88. for (( i=0; i<to_delete; i++ )); do
  89. docker rmi "${image_repo}:${all_tags[$i]}" || log_error "failed to remove image ${image_repo}:${all_tags[$i]}"
  90. done
  91. done
  92. }
  93. check_all_containers_running() {
  94. local services=("$@")
  95. for i in {1..15}; do
  96. sleep 2
  97. local all_running=true
  98. for svc in "${services[@]}"; do
  99. local container_id
  100. container_id=$(docker compose -f "$COMPOSE_FILE_PATH" ps -q "$svc")
  101. local status
  102. status=$(docker inspect --format='{{.State.Status}}' "$container_id" 2>/dev/null || echo "not_found")
  103. if [ "$status" != "running" ]; then
  104. all_running=false
  105. log_info "$svc status: $status, waiting..."
  106. break
  107. fi
  108. done
  109. if [ "$all_running" = true ]; then
  110. log_info "all target services are running"
  111. return 0
  112. fi
  113. done
  114. return 1
  115. }
  116. stop_and_remove_containers() {
  117. local services=("$@")
  118. for svc in "${services[@]}"; do
  119. docker compose -f "$COMPOSE_FILE_PATH" stop "$svc" >/dev/null 2>&1 || true
  120. docker compose -f "$COMPOSE_FILE_PATH" rm -f "$svc" >/dev/null 2>&1 || true
  121. done
  122. }
  123. start_containers() {
  124. local services=("$@")
  125. docker compose -f "$COMPOSE_FILE_PATH" up -d "${services[@]}"
  126. }
  127. require_path file "$COMPOSE_FILE_PATH"
  128. require_path dir "$SRC_DIR"
  129. require_path dir "$COMPOSE_RUN_DIR"
  130. log_info "pull source code: $SRC_DIR"
  131. cd "$SRC_DIR"
  132. current_branch=$(git symbolic-ref --short HEAD)
  133. log_info "current branch: $current_branch"
  134. git pull origin "$current_branch"
  135. git log -1 --format="[latest commit] %H %ad %s" --date=format:'%Y-%m-%d %H:%M:%S'
  136. cd "$COMPOSE_RUN_DIR"
  137. CURRENT_VERSION=$(get_current_version "$VERSION_SOURCE_SERVICE" "$COMPOSE_FILE_PATH")
  138. NEW_VERSION=$(increment_version "$CURRENT_VERSION")
  139. log_info "version: $CURRENT_VERSION -> $NEW_VERSION"
  140. update_compose_version_for_services "$COMPOSE_FILE_PATH" "$NEW_VERSION" "${SERVICES[@]}"
  141. for svc in "${SERVICES[@]}"; do
  142. log_info "build service: $svc"
  143. DOCKER_BUILDKIT=1 docker compose -f "$COMPOSE_FILE_PATH" build "$svc" || {
  144. log_error "build failed, restore compose file"
  145. mv "${COMPOSE_FILE_PATH}.bak" "$COMPOSE_FILE_PATH"
  146. exit 1
  147. }
  148. done
  149. stop_and_remove_containers "${SERVICES[@]}"
  150. start_containers "${SERVICES[@]}"
  151. if check_all_containers_running "${SERVICES[@]}"; then
  152. log_info "deploy succeeded, running version: $NEW_VERSION"
  153. cleanup_old_images_for_services "$COMPOSE_FILE_PATH" "${SERVICES[@]}"
  154. rm -f "${COMPOSE_FILE_PATH}.bak"
  155. else
  156. log_error "new version failed, rollback to: $CURRENT_VERSION"
  157. stop_and_remove_containers "${SERVICES[@]}"
  158. mv "${COMPOSE_FILE_PATH}.bak" "$COMPOSE_FILE_PATH"
  159. start_containers "${SERVICES[@]}"
  160. check_all_containers_running "${SERVICES[@]}" || {
  161. log_error "rollback failed, manual check required"
  162. exit 1
  163. }
  164. exit 1
  165. fi
  166. log_info "deploy finished"