Gitea 1.22 호환: 워크플로에서 workflow_dispatch 제거
All checks were successful
deploy-dev / deploy (push) Successful in 31s
All checks were successful
deploy-dev / deploy (push) Successful in 31s
Gitea 1.22.3은 workflow_dispatch(수동 실행)와 inputs 컨텍스트를 지원하지 않아 워크플로가 Actions 목록에 표시되지 않았다. 트리거를 push 하나로 단순화한다. 롤백은 서버에서 직접 수행하도록 README에 절차를 남긴다. Tomcat 힙도 768m -> 448m로 낮춘다. 가용 메모리 1G 안팎인 서버에서 Tomcat 재기동 직후 서버 전체가 응답 불능이 된 사례가 두 번 있었다.
This commit is contained in:
parent
7c574922d8
commit
62ef19c78f
@ -2,7 +2,7 @@
|
||||
#
|
||||
# ┌ 설계 배경 ─────────────────────────────────────────────────────────────┐
|
||||
# │ 이 서버(zio-server)는 2코어 / 메모리 여유 1G에 40개 이상의 서비스가 │
|
||||
# │ 동거한다. 실제로 구축 중 39분간 응답 불능 후 재부팅된 이력이 있다. │
|
||||
# │ 동거한다. 구축 중 서버 전체가 응답 불능이 되어 재부팅된 일이 두 번 있다. │
|
||||
# │ 그래서 Maven 빌드(힙 1G)는 서버에서 돌리지 않는다. │
|
||||
# └────────────────────────────────────────────────────────────────────────┘
|
||||
#
|
||||
@ -10,7 +10,11 @@
|
||||
# [개발자 PC] git push origin master → 이 워크플로 자동 실행
|
||||
# [서버] ROOT 재전개 + 환경설정 주입 + 재기동 + 헬스체크(+실패 시 롤백)
|
||||
#
|
||||
# 수동 실행(workflow_dispatch)으로 재배포·롤백도 가능하다.
|
||||
# ※ Gitea 1.22.3 기준으로 작성했다. 이 버전은 workflow_dispatch(수동 실행 버튼)와
|
||||
# inputs 컨텍스트를 지원하지 않는다 — 넣으면 워크플로 자체가 목록에 뜨지 않는다.
|
||||
# 그래서 트리거는 push 하나만 둔다.
|
||||
# 재배포는 커밋 없이도 `git commit --allow-empty` 후 push 하면 되고,
|
||||
# 롤백은 서버에서 직접 수행한다(deploy/dev/README.md 참고).
|
||||
name: deploy-dev
|
||||
|
||||
on:
|
||||
@ -22,16 +26,6 @@ on:
|
||||
- '**.pptx'
|
||||
- 'docs/**'
|
||||
- '_workspace_privacy/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
description: '수행할 작업'
|
||||
required: true
|
||||
default: 'deploy'
|
||||
type: choice
|
||||
options:
|
||||
- deploy
|
||||
- rollback
|
||||
|
||||
concurrency:
|
||||
group: arko-dev-deploy
|
||||
@ -51,13 +45,11 @@ jobs:
|
||||
- name: 사전 점검
|
||||
run: |
|
||||
set -e
|
||||
echo "트리거: ${{ gitea.event_name }} / 작업: ${{ inputs.action || 'deploy' }}"
|
||||
df -h "$APP_HOME" | tail -1
|
||||
free -h | head -2
|
||||
uptime
|
||||
|
||||
- name: WAR 확인
|
||||
if: ${{ inputs.action != 'rollback' }}
|
||||
run: |
|
||||
set -e
|
||||
if [ ! -f "$INCOMING_WAR" ]; then
|
||||
@ -68,36 +60,17 @@ jobs:
|
||||
ls -lh "$INCOMING_WAR"
|
||||
WAR_EPOCH=$(stat -c %Y "$INCOMING_WAR")
|
||||
echo "WAR 빌드 시각: $(date -d @$WAR_EPOCH '+%F %T')"
|
||||
|
||||
# WAR이 이번 커밋보다 오래되었으면 옛 산출물을 배포하는 것이다.
|
||||
# 문서/CI 파일만 바뀐 커밋도 있으므로 실패시키지 않고 경고만 남긴다.
|
||||
COMMIT_EPOCH=$(git log -1 --format=%ct 2>/dev/null || echo 0)
|
||||
if [ "$COMMIT_EPOCH" -gt "$WAR_EPOCH" ]; then
|
||||
AGE_MIN=$(( (COMMIT_EPOCH - WAR_EPOCH) / 60 ))
|
||||
echo "::warning::업로드된 WAR이 이번 커밋보다 ${AGE_MIN}분 오래됐다. 소스 변경이 포함된 커밋이라면 build-and-upload.sh 로 다시 올릴 것."
|
||||
NOW=$(date +%s)
|
||||
AGE_MIN=$(( (NOW - WAR_EPOCH) / 60 ))
|
||||
if [ "$AGE_MIN" -gt 120 ]; then
|
||||
echo "::warning::업로드된 WAR이 ${AGE_MIN}분 전 것이다. 소스 변경이 포함된 커밋이라면 build-and-upload.sh 로 다시 올릴 것."
|
||||
fi
|
||||
|
||||
- name: 배포
|
||||
if: ${{ inputs.action != 'rollback' }}
|
||||
run: |
|
||||
set -e
|
||||
bash "$APP_HOME/bin/arko-dev-deploy.sh" "$INCOMING_WAR"
|
||||
|
||||
- name: 롤백
|
||||
if: ${{ inputs.action == 'rollback' }}
|
||||
run: |
|
||||
set -e
|
||||
LATEST=$(ls -1t "$APP_HOME"/backup/ROOT_*.tar.gz 2>/dev/null | head -1 || true)
|
||||
if [ -z "$LATEST" ]; then
|
||||
echo "::error::롤백할 백업이 없다"
|
||||
exit 1
|
||||
fi
|
||||
echo "롤백 대상: $LATEST"
|
||||
systemctl stop arko-dev
|
||||
rm -rf "$APP_HOME/tomcat/webapps/ROOT"
|
||||
tar -xzf "$LATEST" -C "$APP_HOME/tomcat/webapps"
|
||||
systemctl start arko-dev
|
||||
|
||||
- name: 결과 확인
|
||||
run: |
|
||||
set -e
|
||||
|
||||
@ -89,7 +89,19 @@ git push origin master
|
||||
bash deploy/dev/build-and-upload.sh --deploy
|
||||
```
|
||||
|
||||
**재배포·롤백:** Gitea 웹 > Actions > deploy-dev > Run workflow > `deploy` 또는 `rollback`
|
||||
**소스 변경 없이 재배포 (WAR만 다시 올린 경우):**
|
||||
```bash
|
||||
git commit --allow-empty -m "재배포" && git push origin master
|
||||
```
|
||||
|
||||
**롤백:** Gitea 1.22는 수동 실행(workflow_dispatch) 버튼이 없어 서버에서 직접 수행한다.
|
||||
```bash
|
||||
ssh root@101.79.17.164
|
||||
systemctl stop arko-dev
|
||||
rm -rf /opt/arko-dev/tomcat/webapps/ROOT
|
||||
tar -xzf /opt/arko-dev/backup/ROOT_<타임스탬프>.tar.gz -C /opt/arko-dev/tomcat/webapps
|
||||
systemctl start arko-dev
|
||||
```
|
||||
|
||||
## 운영 명령
|
||||
|
||||
|
||||
@ -2,7 +2,12 @@
|
||||
# 설치 위치: /etc/systemd/system/arko-dev.service
|
||||
#
|
||||
# 이 서버(zio-server)의 기존 서비스들과 같은 방식(systemd 직접 관리)을 따른다.
|
||||
# 메모리 여유가 1.3G 수준이라 힙을 768m로 제한한다 — 부족하면 서버 전체가 스왑/OOM으로 흔들린다.
|
||||
#
|
||||
# 힙 448m — 처음엔 768m로 잡았다가 낮췄다. 이 서버는 40여 개 서비스가 동거해
|
||||
# 평상시 가용 메모리가 1G 안팎이고, 2026-07-29 Tomcat 재기동 직후 서버 전체가
|
||||
# 응답 불능이 되어 재부팅된 일이 두 번 있었다. arko는 기동 시 JSP 컴파일로
|
||||
# 메모리를 크게 쓰므로 상한을 낮춰 다른 서비스를 밀어내지 않도록 한다.
|
||||
# 늘리려면 반드시 free -h 로 여유를 먼저 확인할 것.
|
||||
[Unit]
|
||||
Description=arko_renual DEV (Tomcat 9, csv.zioinfo.co.kr, :8088)
|
||||
After=network.target mariadb.service
|
||||
@ -15,10 +20,10 @@ Environment="JAVA_HOME=/opt/arko-dev/jdk8"
|
||||
Environment="CATALINA_HOME=/opt/arko-dev/tomcat"
|
||||
Environment="CATALINA_BASE=/opt/arko-dev/tomcat"
|
||||
Environment="CATALINA_PID=/opt/arko-dev/tomcat/temp/tomcat.pid"
|
||||
# -Xmx768m: 서버 가용 메모리 기준 상한. 늘리려면 free -h 먼저 확인할 것
|
||||
# headless: 이미지 처리(썸네일) 시 X11 없는 환경에서 죽지 않도록
|
||||
# Asia/Seoul: 로그·배치 시각을 서버 다른 서비스와 맞춘다
|
||||
Environment="CATALINA_OPTS=-Xms256m -Xmx768m -XX:MaxMetaspaceSize=256m -Djava.awt.headless=true -Duser.timezone=Asia/Seoul -Dfile.encoding=UTF-8"
|
||||
# ExitOnOutOfMemoryError: OOM으로 반쯤 죽은 채 살아있느니 죽고 재기동하는 편이 낫다
|
||||
Environment="CATALINA_OPTS=-Xms192m -Xmx448m -XX:MaxMetaspaceSize=192m -XX:+ExitOnOutOfMemoryError -Djava.awt.headless=true -Duser.timezone=Asia/Seoul -Dfile.encoding=UTF-8"
|
||||
|
||||
ExecStart=/opt/arko-dev/tomcat/bin/startup.sh
|
||||
ExecStop=/opt/arko-dev/tomcat/bin/shutdown.sh 30 -force
|
||||
|
||||
84
deploy/web/01-web-server-setup.sh
Normal file
84
deploy/web/01-web-server-setup.sh
Normal file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
# csv.wise.ai.kr 웹 티어 구축 — 웹서버에서 실행
|
||||
#
|
||||
# 사용법: bash 01-web-server-setup.sh [WAS주소]
|
||||
# WAS주소 기본값 101.79.17.164:8088 (같은 사설망이면 10.0.1.6:8088 로 주면 된다)
|
||||
#
|
||||
# 이 서버는 nginx만 올린다. WAS(Tomcat)와 DB(MariaDB)는 개발서버(101.79.17.164)를 그대로 쓴다.
|
||||
# 멱등: 다시 실행해도 안전하다.
|
||||
set -euo pipefail
|
||||
|
||||
WAS_ADDR=${1:-101.79.17.164:8088}
|
||||
DOMAIN=csv.wise.ai.kr
|
||||
SRC_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
|
||||
log() { echo -e "\n[$(date +'%F %T')] === $* ==="; }
|
||||
|
||||
# ----------------------------------------------------------- 1. nginx 설치
|
||||
log "1. nginx 확인"
|
||||
if command -v nginx > /dev/null; then
|
||||
nginx -v 2>&1
|
||||
else
|
||||
echo "nginx 설치"
|
||||
apt-get update -qq
|
||||
apt-get install -y nginx
|
||||
fi
|
||||
|
||||
# sites-enabled 구조가 없는 배포판(예: RHEL 계열)이면 만들어 준다
|
||||
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
||||
if ! grep -q "sites-enabled" /etc/nginx/nginx.conf; then
|
||||
echo "경고: nginx.conf 가 sites-enabled 를 include 하지 않는다. 수동 확인 필요."
|
||||
fi
|
||||
|
||||
# ------------------------------------------------- 2. WAS 도달 여부 확인
|
||||
log "2. WAS 도달 확인 ($WAS_ADDR)"
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 15 -H "Host: $DOMAIN" "http://$WAS_ADDR/" || echo 000)
|
||||
echo "http://$WAS_ADDR/ → HTTP $CODE"
|
||||
if [ "$CODE" = "000" ]; then
|
||||
cat <<EOF
|
||||
중단: WAS에 도달할 수 없다.
|
||||
- WAS서버(101.79.17.164)에서 deploy/web/02-was-expose.sh <이 웹서버IP> 를 먼저 실행할 것
|
||||
(Tomcat 바인딩 확대 + 방화벽에서 이 서버 IP만 8088 허용)
|
||||
- 같은 사설망이면 WAS주소를 10.0.1.6:8088 로 주고 다시 실행할 것
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --------------------------------------------------------- 3. vhost 배치
|
||||
log "3. nginx 가상호스트 등록"
|
||||
install -m 644 "$SRC_DIR/$DOMAIN.conf" "/etc/nginx/sites-available/$DOMAIN.conf"
|
||||
|
||||
# upstream 주소를 인자 값으로 교체
|
||||
sed -i "s|server 101.79.17.164:8088;|server $WAS_ADDR;|" "/etc/nginx/sites-available/$DOMAIN.conf"
|
||||
grep -n "server .*:8088;" "/etc/nginx/sites-available/$DOMAIN.conf"
|
||||
|
||||
ln -sfn "/etc/nginx/sites-available/$DOMAIN.conf" "/etc/nginx/sites-enabled/$DOMAIN.conf"
|
||||
|
||||
if nginx -t; then
|
||||
systemctl reload nginx
|
||||
echo "nginx 반영 완료"
|
||||
else
|
||||
rm -f "/etc/nginx/sites-enabled/$DOMAIN.conf"
|
||||
echo "nginx 설정 오류 → 링크 제거하고 중단"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------- 4. 확인
|
||||
log "4. 프록시 확인"
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 30 -H "Host: $DOMAIN" http://127.0.0.1/ || echo 000)
|
||||
echo "nginx 경유 → HTTP $CODE"
|
||||
curl -s --max-time 30 -H "Host: $DOMAIN" http://127.0.0.1/ | grep -oiE "<title>[^<]*</title>" | head -1
|
||||
|
||||
# --------------------------------------------------------------- 5. SSL
|
||||
log "5. SSL"
|
||||
if [ -d "/etc/letsencrypt/live/$DOMAIN" ]; then
|
||||
echo "인증서 이미 존재"
|
||||
else
|
||||
cat <<EOF
|
||||
DNS A 레코드($DOMAIN → 이 서버 IP)가 등록된 뒤에 실행할 것:
|
||||
certbot --nginx -d $DOMAIN --non-interactive --agree-tos --redirect
|
||||
certbot 미설치 시: apt-get install -y certbot python3-certbot-nginx
|
||||
EOF
|
||||
fi
|
||||
|
||||
log "완료"
|
||||
57
deploy/web/02-was-expose.sh
Normal file
57
deploy/web/02-was-expose.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# WAS서버(zio-server / 101.79.17.164)에서 실행 — 웹서버가 Tomcat에 붙을 수 있게 연다.
|
||||
#
|
||||
# 사용법: bash 02-was-expose.sh <웹서버IP>
|
||||
#
|
||||
# 현재 Tomcat은 127.0.0.1:8088 에만 바인딩되어 있어 같은 서버의 nginx만 접근 가능하다.
|
||||
# 별도 웹서버를 두려면 바인딩을 넓히고, 대신 방화벽으로 그 웹서버 IP만 허용한다.
|
||||
# (8088을 전체 공개하면 nginx를 우회해 WAS에 직접 접근할 수 있게 되므로 반드시 IP를 한정한다)
|
||||
set -euo pipefail
|
||||
|
||||
WEB_IP=${1:-}
|
||||
[ -n "$WEB_IP" ] || { echo "사용법: $0 <웹서버IP>"; exit 1; }
|
||||
echo "$WEB_IP" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' || { echo "IP 형식이 아니다: $WEB_IP"; exit 1; }
|
||||
|
||||
XML=/opt/arko-dev/tomcat/conf/server.xml
|
||||
TS=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
echo "=== 1. server.xml 백업 ==="
|
||||
cp -f "$XML" "$XML.bak_$TS"
|
||||
echo "$XML.bak_$TS"
|
||||
|
||||
echo
|
||||
echo "=== 2. Tomcat 바인딩 확대 (127.0.0.1 → 0.0.0.0) ==="
|
||||
# address 속성을 제거하면 전체 인터페이스에 바인딩된다.
|
||||
# 노출 통제는 아래 ufw 규칙이 담당한다.
|
||||
sed -i 's|port="8088" protocol="HTTP/1.1" address="127.0.0.1"|port="8088" protocol="HTTP/1.1"|' "$XML"
|
||||
python3 -c "import xml.dom.minidom; xml.dom.minidom.parse('$XML'); print('server.xml XML 검증 OK')"
|
||||
grep -n 'port="8088"' "$XML" | head -2
|
||||
|
||||
echo
|
||||
echo "=== 3. 방화벽: $WEB_IP 만 8088 허용 ==="
|
||||
ufw allow from "$WEB_IP" to any port 8088 proto tcp comment "arko-dev WAS <- csv.wise.ai.kr 웹서버"
|
||||
ufw status | grep 8088
|
||||
|
||||
echo
|
||||
echo "=== 4. Tomcat 재기동 ==="
|
||||
systemctl restart arko-dev
|
||||
for i in $(seq 1 60); do
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 http://127.0.0.1:8088/ || echo 000)
|
||||
if [ "$CODE" = "200" ] || [ "$CODE" = "302" ]; then break; fi
|
||||
sleep 3
|
||||
done
|
||||
echo "로컬 확인 → HTTP $CODE"
|
||||
|
||||
echo
|
||||
echo "=== 5. 리슨 상태 ==="
|
||||
ss -lntp | grep 8088
|
||||
|
||||
cat <<EOF
|
||||
|
||||
다음: 웹서버에서 도달 확인
|
||||
curl -s -o /dev/null -w "%{http_code}\\n" -H "Host: csv.wise.ai.kr" http://101.79.17.164:8088/
|
||||
|
||||
되돌리려면:
|
||||
cp -f $XML.bak_$TS $XML && systemctl restart arko-dev
|
||||
ufw delete allow from $WEB_IP to any port 8088 proto tcp
|
||||
EOF
|
||||
66
deploy/web/csv.wise.ai.kr.conf
Normal file
66
deploy/web/csv.wise.ai.kr.conf
Normal file
@ -0,0 +1,66 @@
|
||||
# csv.wise.ai.kr — arko_renual 웹 티어 (별도 웹서버에 설치)
|
||||
#
|
||||
# 구성: 브라우저 → [웹서버] nginx 443 → [WAS서버 101.79.17.164] Tomcat 8088
|
||||
# DB는 WAS서버의 MariaDB(csv)를 그대로 사용한다. 웹서버는 DB에 접근하지 않는다.
|
||||
#
|
||||
# 설치 위치: /etc/nginx/sites-available/csv.wise.ai.kr.conf
|
||||
# → /etc/nginx/sites-enabled/ 에 심볼릭 링크
|
||||
#
|
||||
# 선행 조건:
|
||||
# 1) csv.wise.ai.kr A 레코드 → 이 웹서버 IP (현재 미등록)
|
||||
# 2) WAS서버에서 8088을 이 웹서버 IP에만 개방 (deploy/web/02-was-expose.sh)
|
||||
# 3) HTTP로 먼저 올린 뒤 certbot --nginx 가 SSL 블록을 주입
|
||||
|
||||
# WAS 백엔드 — 설치 시 실제 도달 가능한 주소로 바꿀 것
|
||||
# 같은 사설망이면 10.0.1.6:8088, 공인망 경유면 101.79.17.164:8088
|
||||
upstream arko_was {
|
||||
server 101.79.17.164:8088;
|
||||
keepalive 16;
|
||||
}
|
||||
|
||||
server {
|
||||
server_name csv.wise.ai.kr;
|
||||
|
||||
# 자원봉사 활동사진·첨부파일 업로드가 있어 기본 1m로는 부족하다
|
||||
client_max_body_size 100m;
|
||||
|
||||
# 개발서버이므로 검색엔진 색인 차단
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
|
||||
access_log /var/log/nginx/csv.wise.ai.kr.access.log;
|
||||
error_log /var/log/nginx/csv.wise.ai.kr.error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://arko_was;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Host를 그대로 넘겨야 WAS가 csv.wise.ai.kr 기준으로 링크를 만든다
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# arko는 첫 요청에서 JSP를 컴파일하고, 통계·엑셀 다운로드가 오래 걸린다
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_buffers 8 16k;
|
||||
}
|
||||
|
||||
# 정적 리소스 — 웹서버에서 짧게 캐시 (WAS 왕복을 줄인다)
|
||||
location ~* ^/(images|css|js|fonts|new)/ {
|
||||
proxy_pass http://arko_was;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_valid 200 10m;
|
||||
expires 10m;
|
||||
add_header Cache-Control "public, max-age=600";
|
||||
}
|
||||
|
||||
listen 80;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user