CRA를 통해 생성 된 두 개의 TypeScript 앱과 npm
테스트 / 린트를 실행하고 이후 단계를 위해 앱을 빌드 하는 일련의 명령을 실행 하는 CI 파이프 라인이 있습니다 .
time npm install --no-optional --unsafe-perm
npm test -- --coverage
npm run tsc
npm run lint
export REACT_APP_VERSION=$VERSION
export REACT_APP_COMMIT=$GIT_COMMIT
npm run build
npm run build-storybook
CI 파이프 라인은 Jenkins에서 실행되며 필요에 따라 실행기를 가져 오기 위해 kubernetes 플러그인을 사용하고 있습니다. 스크립트가 병렬로 실행 app1
하고 app2
우리의 다음과 같은 논리를 통해 Jenkinsfile
:
stage('Frontend - App1') {
agent {
kubernetes {
label 'node'
defaultContainer 'jnlp'
yamlFile 'infrastructure/scripts/ci/pod-templates/node.yaml'
idleMinutes 30
}
}
environment {
CI = 'true'
NPMRC_SECRET_FILE_PATH = credentials('verdaccio-npmrc')
}
steps {
dir('frontend/app1') {
container('node') {
sh 'cp $NPMRC_SECRET_FILE_PATH ~/.npmrc'
sh 'chmod u+rw ~/.npmrc'
sh '../../infrastructure/scripts/ci/build-frontend.sh'
}
publishHTML(target: [
allowMissing : false,
alwaysLinkToLastBuild: false,
keepAll : true,
reportDir : 'coverage',
reportFiles : 'index.html',
reportName : "Coverage Report (app1)"
])
junit 'testing/junit.xml'
stash includes: 'build/**/*', name: 'app1-build'
stash includes: 'storybook-static/**/*', name: 'app1-storybook-build'
}
}
}
그래서 우리가보고있는 것에. 어제 반복해서 동일한 증상을 보았습니다.에 대한 프런트 엔드 단계 app1
가 완료 (두 개 중 더 작음), app2
테스트 실행 중에 신비롭게 중지됩니다 (Jenkins의 마지막 로그인 줄은 항상 PASS src/x/y/file.test.ts
이지만 항상 동일한 테스트 는 아닙니다 ). . 파이프 라인 시간 초과 (또는 지루한 개발자)로 인해 죽기 전에 한 시간 동안이 상태로 유지됩니다.
우리는 kubectl exec -it node-blah sh
멈춰있는 단계를 실행하고있는 포드로 이동하여 진단을 받았습니다. 달리기 ps aux | cat
는 우리에게 다음을 제공합니다.
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
node 1 0.0 0.0 4396 720 ? Ss+ 08:51 0:00 cat
node 17 0.0 0.0 0 0 ? Z 08:51 0:00 [sh] <defunct>
node 32 0.0 0.0 0 0 ? Z 08:51 0:00 [sh] <defunct>
node 47 0.0 0.0 0 0 ? Z 08:51 0:00 [sh] <defunct>
node 664 0.0 0.0 0 0 ? Z 09:04 0:00 [sh] <defunct>
.
.
.
node 6760 0.0 0.0 4340 108 ? S 10:36 0:00 sh -c (pid=$$; { while [ \( -d /proc/$pid -o \! -d /proc/$$ \) -a -d '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8' -a \! -f '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt' ]; do touch '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-log.txt'; sleep 3; done } & jsc=durable-508a7912908a6919b577783c49df638d; JENKINS_SERVER_COOKIE=$jsc 'sh' -xe '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/script.sh' > '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-log.txt' 2>&1; echo $? > '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt.tmp'; mv '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt.tmp' '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt'; wait) >&- 2>&- &
node 6761 0.0 0.0 4340 1060 ? S 10:36 0:00 sh -c (pid=$$; { while [ \( -d /proc/$pid -o \! -d /proc/$$ \) -a -d '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8' -a \! -f '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt' ]; do touch '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-log.txt'; sleep 3; done } & jsc=durable-508a7912908a6919b577783c49df638d; JENKINS_SERVER_COOKIE=$jsc 'sh' -xe '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/script.sh' > '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-log.txt' 2>&1; echo $? > '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt.tmp'; mv '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt.tmp' '/home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/jenkins-result.txt'; wait) >&- 2>&- &
node 6762 0.0 0.0 4340 812 ? S 10:36 0:00 sh -xe /home/jenkins/workspace/app_master/frontend/app2@tmp/durable-f617acc8/script.sh
node 6764 0.0 0.0 20096 2900 ? S 10:36 0:00 /bin/bash ../../infrastructure/scripts/ci/build-frontend.sh
node 6804 0.0 0.5 984620 38552 ? Sl 10:37 0:00 npm
node 6816 0.0 0.0 4356 836 ? S 10:37 0:00 sh -c react-app-rewired test --reporters default --reporters jest-junit "--coverage"
node 6817 0.0 0.4 877704 30220 ? Sl 10:37 0:00 node /home/jenkins/workspace/app_master/frontend/app2/node_modules/.bin/react-app-rewired test --reporters default --reporters jest-junit --coverage
node 6823 0.4 1.3 1006148 97108 ? Sl 10:37 0:06 node /home/jenkins/workspace/app_master/frontend/app2/node_modules/react-app-rewired/scripts/test.js --reporters default --reporters jest-junit --coverage
node 6881 2.8 2.6 1065992 194076 ? Sl 10:37 0:41 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 6886 2.8 2.6 1067004 195748 ? Sl 10:37 0:40 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 6898 2.9 2.5 1058872 187360 ? Sl 10:37 0:43 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 6905 2.8 2.4 1054256 183492 ? Sl 10:37 0:42 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 6910 2.8 2.6 1067812 196344 ? Sl 10:37 0:41 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 6911 2.7 2.6 1063680 191088 ? Sl 10:37 0:40 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 6950 0.8 1.9 1018536 145396 ? Sl 10:38 0:11 /usr/local/bin/node /home/jenkins/workspace/app_master/frontend/app2/node_modules/jest-worker/build/child.js
node 7833 0.0 0.0 4340 804 ? Ss 10:59 0:00 sh
node 7918 0.0 0.0 4240 652 ? S 11:01 0:00 sleep 3
node 7919 0.0 0.0 17508 2048 ? R+ 11:01 0:00 ps aux
node 7920 0.0 0.0 4396 716 ? S+ 11:01 0:00 cat
PS의 매뉴얼에서 :
S interruptible sleep (waiting for an event to complete)
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
그래서 이것이 보여주는 것은 테스트가 정상적으로 실행되기 시작하고 자식 프로세스를 생성하여 병렬로 실행 한 다음 어떤 이유로 든 40 초 정도 후에 해당 프로세스가 모두 절전 모드로 전환되어 더 이상 아무것도 수행하지 않는다는 것입니다.
우리는 추가 조사를 위해 포드에 원하는 것을 쉽게 설치할 수 없다는 어색함 (쉬운 루트 액세스 없음) ...하지만 제안 된 이론 / 다음 단계 환영합니다!
** 편집하다 **
idleMinutes
오늘 우리는 문제를 되 돌린 후 다시 발생하는 것을 여러 번 보았으므로 범인이 아닌 것 같습니다 . 이전에 다른 빌드에서 사용되지 않은 kubernetes의 새로운 노드에서 스크립트가 실행 중인지 확인할 수있었습니다. 그래서 지금은이 시작을 위해 최근에 변경된 것이 무엇인지 전혀 모릅니다.
이것에 대해 내 머리를 좀 더 부딪 쳤고, 나는 근본 원인이 포드에서 과도한 메모리를 사용하는 테스트라고 확신합니다. 어제 몇 빌드 ENOMEM
에서 동일한 방식으로 멈춰지기 전에 로깅 중에 오류가 인쇄되는 것을 본 것은 운이 좋았 습니다. 왜 우리가 이것을 항상 보지 않았는지 설명 할 수는 없지만 (우리는 되돌아 가서 이전 예제를 확인했지만 거기에 없었습니다), 그것이 우리를 올바른 길로 인도했습니다.
좀 더 깊이 파고 들면서, 저는 kubectl top pods
노드 포드 중 하나가 미쳐가는 것을 잡기 위해 제 시간 에 실행 했습니다.이 특정 순간에 node-thk0r-5vpzk
사용 3131Mi
중인 것을 볼 수 있으며 포드에 대한 제한을 다음과 같이 설정했습니다 3Gi
.
Jenkins의 해당 빌드를 돌아 보면 이제 멈춘 상태이지만 ENOMEM
로깅 이 없음을 알았습니다 . 후속 kubectl top pods
명령은 이제 메모리가에서 합리적인 수준으로 감소했음을 보여 주 node-thk0r-5vpzk
었지만, 이상한 수면 상태의 모든 자식 프로세스가 아무것도하지 않았으므로 이미 손상이 이미 수행되었습니다.
이것은 또한 (잠재적으로) 내가 idleMinutes
동작을 도입 한 후 문제가 더 일반적이 된 이유를 설명합니다. 메모리 누수가있는 경우 동일한 포드를 계속 재사용 npm test
하면 메모리 한계에 도달 할 가능성이 점점 더 높아집니다. 겁나. 현재 우리의 수정 사항은 --maxWorkers
설정을 사용하는 작업자 수를 제한하는 것이 었는데, 이로 인해 3Gi
한계 보다 훨씬 낮습니다 . 우리는 또한 우리가 --detectLeaks
만연한 메모리 사용량을 해결하기 위해 수정할 수있는 테스트에 이상한 점이 있는지 확인하기 위해 메모리 사용량을 약간 조사 할 계획 입니다.
비슷한 문제를 발견하면 다른 사람에게 도움이되기를 바랍니다. 미친 DevOps 세상의 또 다른 날 ...
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다