@@ -105,6 +105,7 @@ getpw() { kubectl -n "$NS" get secret stack-db -o jsonpath="{.data.$1}" | base64
105105# 배포가 돌도록, 스택 네임스페이스에 그 경로만 붙인 임시 도우미 Pod를 띄워 읽고 끝나면 지운다.
106106LEDGER_POD=ledger-helper
107107ledger_cleanup () { kubectl -n " $NS " delete pod " $LEDGER_POD " --ignore-not-found --wait=false > /dev/null 2>&1 || true ; }
108+ # 뜨는 데 몇 초 걸리므로 띄우기(start)와 기다리기(wait)를 나눠, 그 사이에 대장이 필요 없는 단계를 먼저 진행한다.
108109start_ledger_helper () { # $1: NFS 서버, $2: 계정 대장 경로
109110 kubectl -n " $NS " delete pod " $LEDGER_POD " --ignore-not-found --wait=true --timeout=60s > /dev/null 2>&1 || true
110111 # kube_share NFS 마운트를 NAS가 허용하는 노드는 csid-dgu-desktop 하나뿐이다 — config-server
131132 - name: kube-share
132133 nfs: {server: "$1 ", path: "$2 "}
133134YAML
134- kubectl -n " $NS " wait --for=condition=Ready pod/" $LEDGER_POD " --timeout=5m > /dev/null
135135}
136+ wait_ledger_helper () { kubectl -n " $NS " wait --for=condition=Ready pod/" $LEDGER_POD " --timeout=5m > /dev/null; }
136137ledger () { kubectl -n " $NS " exec " $LEDGER_POD " -- " $@ " ; }
137138BASE=$( mktemp) ; trap ' rm -f "$BASE"; ledger_cleanup' EXIT
138139
@@ -156,6 +157,8 @@ echo "config-server nodePort $CONFIG_NODEPORT 사용 가능, 화면 호스트 $F
156157step " 네임스페이스 $NS "
157158kubectl create namespace " $NS " --dry-run=client -o yaml | kubectl apply -f -
158159kubectl label namespace " $NS " ailab.dgu/proposed-stack=" $STACK " --overwrite > /dev/null
160+ # 계정 대장 확인(아래 MySQL 앞 단계)에 쓸 도우미 Pod를 지금 띄워 두고, 뜨는 동안 Secret 준비 단계를 진행한다.
161+ start_ledger_helper " $NFS_SERVER " " $KUBE_SHARE "
159162
160163step " 사용자 컨테이너 우선순위 등급"
161164# 노드 디스크가 쪼들리면 kubelet이 Pod를 쫓아낸다. 그 순서는 자원 요청 초과 여부와 우선순위로
@@ -194,19 +197,6 @@ description: "스택 구성요소 — 사용자 컨테이너보다도 늦게 축
194197YAML
195198echo " ailab-stack-component 적용"
196199
197- step " 계정 대장 확인 (임시 도우미 Pod)"
198- start_ledger_helper " $NFS_SERVER " " $KUBE_SHARE "
199- USED=$( ledger awk -F: -v lo=" $UID_MIN " -v hi=" $UID_MAX " ' $3>=lo && $3<=hi {n++} END {print n+0}' /kube_share/passwd)
200- [ " $USED " = 0 ] || { echo " 운영 계정 대장에 UID $UID_MIN ~$UID_MAX 계정이 ${USED} 개 있음. 대역을 옮겨야 함" ; exit 1; }
201- # config-server는 nfs.kubeSharePath를 /kube_share로 마운트해 passwd/group/shadow를 둔다. 운영 경로의
202- # 하위 디렉터리를 스택 전용으로 쓰므로 테스트 계정이 운영 대장에 섞이지 않는다. 비어 있으면
203- # config-server가 처음 계정을 만들 때 기본 파일로 채운다.
204- # (실운영도 같은 방식이다 — 원본 경로는 NAS가 마운트를 거부해 격리 방식으로 되돌렸다. 위 UID_MAX가
205- # 이제 겹치지 않으므로 세 스택과 다를 게 없다.)
206- STACK_KUBE_SHARE=" $KUBE_SHARE /exp-$STACK "
207- ledger mkdir -p " /kube_share/exp-$STACK "
208- echo " UID $UID_MIN ~$UID_MAX 비어 있음, 스택 계정 대장 /kube_share/exp-$STACK "
209-
210200step " SSH 키 복사 ($PROD_NS → $NS )"
211201for s in nas-ssh-key farm-ssh-key farm-ad-ssh-key; do
212202 TYPE=$( kubectl -n " $PROD_NS " get secret " $s " -o jsonpath=' {.type}' )
@@ -277,6 +267,20 @@ kubectl -n "$NS" create secret generic config-server-api-token --from-literal=to
277267 --dry-run=client -o yaml | kubectl apply -f - > /dev/null
278268echo " $( [ " $TOKEN_NEW " = 1 ] && echo 새로 생성 || echo 기존 값 유지) "
279269
270+ step " 계정 대장 확인 (임시 도우미 Pod)"
271+ # 스택 구성요소를 하나라도 올리기 전에 확인한다(이 앞 단계는 Secret 준비뿐이다).
272+ wait_ledger_helper
273+ USED=$( ledger awk -F: -v lo=" $UID_MIN " -v hi=" $UID_MAX " ' $3>=lo && $3<=hi {n++} END {print n+0}' /kube_share/passwd)
274+ [ " $USED " = 0 ] || { echo " 운영 계정 대장에 UID $UID_MIN ~$UID_MAX 계정이 ${USED} 개 있음. 대역을 옮겨야 함" ; exit 1; }
275+ # config-server는 nfs.kubeSharePath를 /kube_share로 마운트해 passwd/group/shadow를 둔다. 운영 경로의
276+ # 하위 디렉터리를 스택 전용으로 쓰므로 테스트 계정이 운영 대장에 섞이지 않는다. 비어 있으면
277+ # config-server가 처음 계정을 만들 때 기본 파일로 채운다.
278+ # (실운영도 같은 방식이다 — 원본 경로는 NAS가 마운트를 거부해 격리 방식으로 되돌렸다. 위 UID_MAX가
279+ # 이제 겹치지 않으므로 세 스택과 다를 게 없다.)
280+ STACK_KUBE_SHARE=" $KUBE_SHARE /exp-$STACK "
281+ ledger mkdir -p " /kube_share/exp-$STACK "
282+ echo " UID $UID_MIN ~$UID_MAX 비어 있음, 스택 계정 대장 /kube_share/exp-$STACK "
283+
280284step " MySQL"
281285render " $HERE /mysql.yaml" | kubectl apply -f -
282286kubectl -n " $NS " rollout status statefulset/mysql --timeout=10m
@@ -325,36 +329,9 @@ else
325329 echo " 임시 디스크 사용"
326330fi
327331
328- step " config-server ($RELEASE , $IMAGE )"
329- # 운영 릴리스 값($BASE, 사전 확인에서 읽음)에 스택마다 달라야 하는 값만 덮어쓴다.
330- # 첫 설치가 실패한 릴리스는 upgrade가 받지 않을 수 있으므로 지우고 다시 설치한다.
331- if helm -n " $NS " status " $RELEASE " -o json 2> /dev/null | grep -q ' "status":"failed"' ; then
332- helm -n " $NS " uninstall " $RELEASE " --wait > /dev/null && echo " 실패한 이전 설치 정리"
333- fi
334- helm upgrade --install " $RELEASE " " $ROOT /config-server/Chart" -n " $NS " -f " $BASE " \
335- --set namespace=" $NS " --set config.namespace=" $NS " \
336- --set image.repository=" ${IMAGE%:* } " --set image.tag=" ${IMAGE##*: } " --set image.pullPolicy=IfNotPresent \
337- --set service.nodePort=" $CONFIG_NODEPORT " \
338- --set nfs.kubeSharePath=" $STACK_KUBE_SHARE " \
339- --set infra.adminBeInternalUrl=" http://admin-prod.$NS " \
340- --set accounts.uidMin=" $UID_MIN " --set accounts.uidMax=" $UID_MAX " --set accounts.prefix=" $PREFIX " \
341- --set accounts.sharedGidMin=" $SHARED_GID_MIN " --set accounts.sharedGidMax=" $SHARED_GID_MAX " \
342- --set nodeport.min=" $NP_MIN " --set nodeport.max=" $NP_MAX " \
343- --set verifyMode=" $VERIFY_MODE " --set faultInjection=" $FAULT_INJECTION " \
344- --set redis.host=" redis-bg-master.$NS .svc.cluster.local" \
345- --set db.host=infra-mysql --set logDb.host=log-mysql \
346- --set imageStore.claimName= \
347- --set controller.enabled=true \
348- --set priorityClassName=ailab-stack-component \
349- --wait --timeout 10m
350- if [ " $TOKEN_NEW " = 1 ] || [ " $KH_CHANGED " = 1 ]; then
351- # 토큰·호스트 키 Secret은 Pod 템플릿에 드러나지 않아 helm 업그레이드만으로는 새 값을 읽지 않는다.
352- kubectl -n " $NS " rollout restart deployment/" $RELEASE " deployment/" $RELEASE -controller" > /dev/null
353- kubectl -n " $NS " rollout status deployment/" $RELEASE " --timeout=10m
354- kubectl -n " $NS " rollout status deployment/" $RELEASE -controller" --timeout=10m
355- fi
356-
357332step " admin_be"
333+ # admin_be·프론트엔드는 적용만 하고, 준비 확인은 config-server를 올린 뒤 "롤아웃 대기"에서 한꺼번에 한다 —
334+ # 하나씩 기다리면 각자 뜨는 시간이 그대로 더해진다. admin_be는 기동할 때 config-server를 부르지 않는다.
358335ADMIN_IMAGE=$BE_IMAGE
359336# 운영 admin_be는 설정 파일을 이미지가 아니라 admin-prod-config Secret으로 받는다. 같은 파일을 복사해
360337# 같은 위치에 넣고, 운영 자원을 가리키는 값만 아래 SPRING_APPLICATION_JSON으로 덮어쓴다.
443420render " $HERE /admin-be.yaml" | sed -e " s|__ADMIN_IMAGE__|$ADMIN_IMAGE |" -e " s|__CONFIG_HASH__|$CONFIG_HASH |" \
444421 | API_EGRESS=" $API_EGRESS " awk ' { if (index($0, "__API_EGRESS__")) print ENVIRON["API_EGRESS"]; else print }' \
445422 | kubectl apply -f -
423+ echo " admin_be 이미지: ${ADMIN_IMAGE##* [@:]} "
424+
425+ step " 프론트엔드"
426+ if [ -n " $FE_IMAGE " ]; then
427+ render " $HERE /admin-fe.yaml" | sed -e " s|__FE_IMAGE__|$FE_IMAGE |" -e " s|__FE_HOST__|$FE_HOST |" | kubectl apply -f -
428+ if [ " $STACK " = " operation" ]; then
429+ kubectl apply -f " $HERE /main-entry.yaml"
430+ fi
431+ else
432+ echo " 프론트엔드 이미지가 없어 건너뜀"
433+ fi
434+
435+ step " config-server ($RELEASE , $IMAGE )"
436+ # 위에서 적용만 해 둔 admin_be·프론트엔드가 뜨는 동안 config-server를 올린다. helm은 전처럼 준비될 때까지
437+ # 기다린다(--wait) — 제때 못 뜨면 릴리스가 failed로 남아 다음 배포가 지우고 다시 설치한다.
438+ # 운영 릴리스 값($BASE, 사전 확인에서 읽음)에 스택마다 달라야 하는 값만 덮어쓴다.
439+ # 첫 설치가 실패한 릴리스는 upgrade가 받지 않을 수 있으므로 지우고 다시 설치한다.
440+ if helm -n " $NS " status " $RELEASE " -o json 2> /dev/null | grep -q ' "status":"failed"' ; then
441+ helm -n " $NS " uninstall " $RELEASE " --wait > /dev/null && echo " 실패한 이전 설치 정리"
442+ fi
443+ helm upgrade --install " $RELEASE " " $ROOT /config-server/Chart" -n " $NS " -f " $BASE " \
444+ --set namespace=" $NS " --set config.namespace=" $NS " \
445+ --set image.repository=" ${IMAGE%:* } " --set image.tag=" ${IMAGE##*: } " --set image.pullPolicy=IfNotPresent \
446+ --set service.nodePort=" $CONFIG_NODEPORT " \
447+ --set nfs.kubeSharePath=" $STACK_KUBE_SHARE " \
448+ --set infra.adminBeInternalUrl=" http://admin-prod.$NS " \
449+ --set accounts.uidMin=" $UID_MIN " --set accounts.uidMax=" $UID_MAX " --set accounts.prefix=" $PREFIX " \
450+ --set accounts.sharedGidMin=" $SHARED_GID_MIN " --set accounts.sharedGidMax=" $SHARED_GID_MAX " \
451+ --set nodeport.min=" $NP_MIN " --set nodeport.max=" $NP_MAX " \
452+ --set verifyMode=" $VERIFY_MODE " --set faultInjection=" $FAULT_INJECTION " \
453+ --set redis.host=" redis-bg-master.$NS .svc.cluster.local" \
454+ --set db.host=infra-mysql --set logDb.host=log-mysql \
455+ --set imageStore.claimName= \
456+ --set controller.enabled=true \
457+ --set priorityClassName=ailab-stack-component \
458+ --wait --timeout 10m
459+ if [ " $TOKEN_NEW " = 1 ] || [ " $KH_CHANGED " = 1 ]; then
460+ # 토큰·호스트 키 Secret은 Pod 템플릿에 드러나지 않아 helm 업그레이드만으로는 새 값을 읽지 않는다.
461+ kubectl -n " $NS " rollout restart deployment/" $RELEASE " deployment/" $RELEASE -controller" > /dev/null
462+ kubectl -n " $NS " rollout status deployment/" $RELEASE " --timeout=10m
463+ kubectl -n " $NS " rollout status deployment/" $RELEASE -controller" --timeout=10m
464+ fi
465+
466+ step " 롤아웃 대기 (admin_be·Redis·프론트엔드)"
467+ # config-server와 같이 뜨던 것들이라, 차례로 확인해도 걸리는 시간은 가장 느린 것 하나만큼이다.
446468kubectl -n " $NS " rollout status deployment/admin-prod --timeout=10m
447469kubectl -n " $NS " rollout status deployment/redis-bg-master --timeout=5m
448470kubectl -n " $NS " rollout status deployment/admin-redis --timeout=5m
449- echo " admin_be 이미지: ${ADMIN_IMAGE##* [@:]} "
471+ if [ -n " $FE_IMAGE " ]; then
472+ kubectl -n " $NS " rollout status deployment/ailab-frontend --timeout=5m
473+ fi
450474
451475step " 기준 데이터 확인 (스택 admin DB)"
452476# 신청 화면에 필요한 서버·자원 그룹·노드·GPU·이미지와 메일 문구는 최초 1회 스택에 심어 두면 된다.
@@ -473,17 +497,6 @@ smy -e "UPDATE web_admin.container_image SET image_version = REGEXP_REPLACE(imag
473497smy -e " SELECT CONCAT(image_version, ' (', COUNT(*), ')') FROM web_admin.container_image
474498 WHERE image_name LIKE '%dguailab/decs' GROUP BY image_version ORDER BY image_version" | sed ' s/^/ /'
475499
476- step " 프론트엔드"
477- if [ -n " $FE_IMAGE " ]; then
478- render " $HERE /admin-fe.yaml" | sed -e " s|__FE_IMAGE__|$FE_IMAGE |" -e " s|__FE_HOST__|$FE_HOST |" | kubectl apply -f -
479- kubectl -n " $NS " rollout status deployment/ailab-frontend --timeout=5m
480- if [ " $STACK " = " operation" ]; then
481- kubectl apply -f " $HERE /main-entry.yaml"
482- fi
483- else
484- echo " 프론트엔드 이미지가 없어 건너뜀"
485- fi
486-
487500step " 검증 (admin_be·프론트엔드 연결, 접두어 제한)"
488501CS_POD=$( running_pod " $NS " app=containerssh-config-server)
489502kubectl -n " $NS " exec -i " $CS_POD " -- env NAME=" ${PREFIX} 000" PREFIX=" $PREFIX " UID_MIN=" $UID_MIN " UID_MAX=" $UID_MAX " FE=" ${FE_IMAGE: +http:// ailab-frontend} " FE_HOST=" $FE_HOST " python - << 'PY '
0 commit comments