[QnA] SAP BTP CF 앱 배포 후 Crashed — 원인 5가지와 즉시 해결법
1. 개요 및 학습 목표
SAP BTP Cloud Foundry 환경에서 cf push 명령으로 애플리케이션을 배포한 뒤, BTP Cockpit이나 CLI에서 "Crashed" 상태를 마주하는 것은 개발자라면 누구나 한 번쯤 겪는 상황입니다. 이 글에서는 Crashed가 발생하는 가장 빈번한 5가지 원인을 Q&A 형식으로 분석하고, 각각에 대해 즉시 적용 가능한 해결법을 제시합니다.
이 글을 마치면 다음을 할 수 있습니다:
cf logs와cf events를 활용해 Crashed 원인을 빠르게 진단할 수 있다- 메모리 초과, 서비스 바인딩 오류, 포트 설정, 환경변수 누락, buildpack 문제를 각각 구분하고 해결할 수 있다
manifest.yml을 프로덕션 수준으로 구성할 수 있다- 배포 전 사전 점검 체크리스트를 갖출 수 있다
2. 선수 지식
이 튜토리얼을 따라가려면 아래 항목에 대한 기본적인 이해가 필요합니다.
- SAP BTP Trial 또는 Enterprise 계정 사용 경험
- Cloud Foundry CLI(
cf) 기본 명령어 —cf login,cf push,cf apps - Node.js, Java, 또는 CAP 프로젝트 중 하나의 기본 구조
manifest.yml파일의 역할에 대한 개념
3. 환경 / 버전 / 준비물
| 항목 | 권장 버전 / 설정 |
|---|---|
| SAP BTP | Cloud Foundry Environment (Trial 또는 Enterprise) |
| CF CLI | v8 이상 (2024년 기준 v8.8+) |
| Runtime | Node.js 18/20 LTS, Java 17, Python 3.11 등 |
| Buildpack | SAP 제공 또는 Cloud Foundry community buildpack |
| BTP Cockpit 접근 | Subaccount > Cloud Foundry > Spaces 접근 권한 |
CF CLI가 설치되어 있지 않다면 brew install cloudfoundry/tap/cf-cli@8(macOS) 또는 SAP Development Tools에서 다운로드할 수 있습니다. cf version으로 설치 여부를 확인하세요.
4. 핵심 개념 — Cloud Foundry 앱 생명주기와 Crashed 상태
Cloud Foundry에서 애플리케이션은 다음과 같은 생명주기를 거칩니다.
Staging (소스 코드 + buildpack = droplet 생성) → Starting (컨테이너 할당, 프로세스 시작) → Running (헬스체크 통과, 트래픽 수신)
"Crashed"는 Starting 또는 Running 단계에서 프로세스가 비정상 종료(exit code != 0)되거나, 헬스체크에 지정 시간 내 응답하지 못했을 때 부여되는 상태입니다. Cloud Foundry 컨트롤러는 자동으로 재시작을 시도하지만, 근본 원인이 해결되지 않으면 CrashLoopBackOff와 유사한 반복 실패에 빠집니다.
비유하자면, cf push는 택배를 보내는 행위이고, Staging은 포장 과정, Starting은 배송 출발입니다. Crashed는 배송지 주소가 잘못되었거나, 포장이 터져서 반송된 상태에 해당합니다. 반송 사유(로그)를 확인하지 않고 같은 택배를 다시 보내봤자 동일한 결과를 얻게 됩니다.
Crashed 원인을 파악하는 가장 기본적인 도구는 두 가지입니다:
cf logs APP_NAME --recent— 최근 로그 버퍼를 출력하여 에러 메시지와 스택 트레이스를 확인cf events APP_NAME— 앱에 발생한 이벤트(crash, update, restage 등)를 시간순으로 표시
이 두 명령을 먼저 실행하는 것이 모든 트러블슈팅의 출발점입니다.
5. 실전 Q&A — Crashed 원인 5가지와 해결법
Q1. 메모리 초과(Out of Memory)로 Crashed가 발생합니다
증상: cf events에 index: 0, reason: CRASHED, cell_id: ..., instance: ..., exit_description: out of memory 메시지가 표시됩니다. 앱이 시작 직후 또는 부하가 걸릴 때 반복적으로 죽습니다.
원인: manifest.yml에 설정된 메모리 한도가 앱 실제 사용량보다 낮을 때 Cloud Foundry의 컨테이너 cgroup이 프로세스를 강제 종료(OOMKill)합니다. Java 앱의 경우 JVM 힙 + 메타스페이스 + 스레드 스택이 합산되므로 특히 주의가 필요합니다.
해결법:
# manifest.yml — 메모리 설정 예시
applications:
- name: my-srv-app
memory: 512M # Trial 기본 256M은 Java에서 거의 항상 부족
disk_quota: 1G
instances: 1
buildpacks:
- java_buildpack
env:
JBP_CONFIG_OPEN_JDK_JRE: '{ jre: { version: 17.+ }, memory_calculator: { stack_threads: 100 } }'
JAVA_OPTS: '-Xmx384m -Xms256m'
- Node.js 앱은 일반적으로 256M이면 충분하지만, 대용량 JSON 처리 시 512M으로 올리는 것이 안전합니다
- Java/Spring Boot 앱은 512M 이상을 권장하며, SAP Cloud SDK 사용 시 768M~1G가 필요할 수 있습니다
cf app APP_NAME으로 현재 메모리 사용량을 실시간 확인하세요
Q2. 서비스 바인딩 오류로 앱이 시작되지 않습니다
증상: 로그에 VCAP_SERVICES 파싱 실패 또는 데이터베이스 연결 거부(Connection refused) 메시지가 나타납니다. 앱이 기동 중 예외를 던지며 종료됩니다.
원인: manifest.yml의 services 섹션에 선언한 서비스 인스턴스 이름이 실제 CF Space에 존재하지 않거나, 바인딩이 완료되지 않은 상태에서 앱이 해당 서비스 크리덴셜을 참조하려 할 때 발생합니다.
해결법:
REM CF CLI로 서비스 바인딩 상태 점검하기
cf services
REM 현재 Space의 서비스 인스턴스 목록 확인
cf env my-srv-app
REM VCAP_SERVICES 환경변수에 바인딩된 서비스 크리덴셜 확인
cf bind-service my-srv-app my-hana-hdi
REM 수동으로 서비스 바인딩 (manifest.yml에 누락된 경우)
cf restage my-srv-app
REM 바인딩 후 반드시 restage 필요
manifest.yml의services:항목과cf services출력의 인스턴스 이름이 정확히 일치하는지 확인하세요 (대소문자 구분)- XSUAA 서비스의 경우
xs-security.json의xsappname이 다른 Space/Subaccount와 충돌하면 바인딩 자체가 실패할 수 있습니다 - HDI Container 서비스가 아직 프로비저닝 중(creating)일 때
cf push를 실행하면 바인딩 실패가 발생합니다.cf service my-hana-hdi로 상태가succeeded인지 확인 후 배포하세요
Q3. 포트 설정 문제로 헬스체크가 실패합니다
증상: 로그에 특별한 에러가 없는데도 앱이 약 60초 후 Crashed 상태로 전환됩니다. cf events에 failed to accept connections within health check timeout 메시지가 표시됩니다.
원인: Cloud Foundry는 앱이 PORT 환경변수(일반적으로 8080)에 바인딩된 포트에서 TCP 연결을 수락하기를 기대합니다. 앱이 하드코딩된 다른 포트(예: 3000, 4004)로 리스닝하면 헬스체크가 실패합니다.
해결법:
- Node.js:
app.listen(process.env.PORT || 3000)으로 PORT 환경변수를 우선 사용 - Java/Spring Boot:
application.properties에server.port=${PORT:8080}설정 - CAP(cds): 기본적으로 PORT를 자동 인식하지만, 커스텀 서버를 사용하는 경우 명시적으로 설정 필요
manifest.yml에서 헬스체크 타임아웃을 조정할 수 있습니다:
# manifest.yml — 헬스체크 설정
applications:
- name: my-srv-app
memory: 512M
health-check-type: http # port(기본값) 대신 http 사용 가능
health-check-http-endpoint: / # HTTP 헬스체크 시 엔드포인트 지정
timeout: 180 # 기동이 느린 앱은 타임아웃을 늘림 (기본 60초)
env:
PORT: 8080
services:
- my-xsuaa
- my-hana-hdi
특히 Java 앱은 Spring Context 초기화에 시간이 걸리므로 timeout을 120~180초로 설정하는 것이 일반적입니다.
Q4. 환경변수 누락으로 런타임 에러가 발생합니다
증상: 로그에 undefined, null reference, 또는 특정 설정값을 찾지 못한다는 에러가 나타나며 앱이 시작 직후 종료됩니다.
원인: 애플리케이션이 process.env.API_KEY, System.getenv("DESTINATION_NAME") 등으로 환경변수를 참조하지만, 해당 값이 manifest.yml의 env: 섹션이나 cf set-env로 설정되어 있지 않은 경우입니다. 로컬 개발 환경의 .env 파일이나 default-env.json에만 값이 있고, CF 환경에는 누락된 것이 전형적인 패턴입니다.
해결법:
cf env APP_NAME으로 현재 설정된 환경변수를 전체 확인- 민감하지 않은 값은
manifest.yml의env:섹션에 직접 기재 - API 키, 시크릿 등 민감 정보는 User-Provided Service를 활용하는 것을 권장합니다:
cf cups my-secrets -p '{"API_KEY":"abc123","SECRET":"xyz789"}'생성 후services:에 바인딩하면VCAP_SERVICES를 통해 접근 가능 - Destination 서비스를 사용하는 경우, BTP Cockpit에서 Destination 설정이 완료되었는지 확인하세요
Q5. Buildpack 감지 실패 또는 호환성 문제가 있습니다
증상: Staging 단계에서 None of the buildpacks detected a compatible application 에러가 발생하거나, Staging은 성공했지만 런타임에서 의존성 관련 에러로 Crashed됩니다.
원인: buildpack을 지정하지 않으면 Cloud Foundry가 자동 감지를 시도하는데, 프로젝트 구조가 표준과 다르거나 루트에 필요한 파일(package.json, pom.xml 등)이 없으면 감지에 실패합니다. 또한 SAP BTP에서 제공하는 buildpack 버전과 런타임 버전 간 호환성 문제도 빈번합니다.
해결법:
manifest.yml에 buildpack을 명시적으로 지정하세요:
# manifest.yml — buildpack 명시 지정
applications:
- name: my-node-app
buildpacks:
- nodejs_buildpack # Node.js
# 또는
# buildpacks:
# - java_buildpack # Java / Spring Boot
# - sap_java_buildpack # SAP Java Buildpack (SAP BTP 전용)
# - staticfile_buildpack # HTML5 정적 파일
# - python_buildpack # Python
cf buildpacks로 현재 사용 가능한 buildpack 목록과 버전을 확인- Node.js의 경우
package.json의engines.node필드로 런타임 버전을 지정하면 buildpack이 해당 버전을 설치합니다 - SAP Java Buildpack(
sap_java_buildpack)은 SAP BTP 전용 기능(Destination, Connectivity 등)을 내장하므로, SAP 서비스를 사용하는 Java 앱은 communityjava_buildpack대신 이를 사용하는 것이 권장됩니다 - MTA(Multi-Target Application) 배포 시에는
mta.yaml의 모듈별 buildpack 지정도 확인하세요
6. 흔한 실수 / 트러블슈팅
FAQ 1: cf push는 성공이라고 나오는데 앱이 Crashed입니다. 왜 그렇습니까?
cf push의 "성공"은 Staging(droplet 생성)까지의 성공을 의미할 수 있습니다. Starting 단계에서의 실패는 별도로 확인해야 합니다. cf push 직후 cf app APP_NAME으로 상태를 반드시 확인하고, Crashed라면 cf logs APP_NAME --recent를 즉시 실행하세요.
FAQ 2: Trial 계정에서 메모리 한도를 늘릴 수 없습니다. 어떻게 해야 합니까?
SAP BTP Trial은 일반적으로 Space당 총 메모리가 제한됩니다(2024~2025년 기준 약 4GB). 실행 중인 다른 앱을 cf stop으로 중지하거나, 불필요한 앱을 cf delete하여 메모리를 확보하세요. cf org-usage 또는 BTP Cockpit에서 현재 메모리 사용량을 확인할 수 있습니다.
FAQ 3: cf logs에 아무것도 나오지 않습니다.
앱이 Staging 단계에서 실패한 경우 런타임 로그가 남지 않습니다. cf push 명령의 터미널 출력 자체를 확인하세요. 또한 --recent 플래그 없이 cf logs APP_NAME을 실행하면 실시간 스트리밍 모드가 되므로, 다른 터미널에서 cf restart APP_NAME을 실행하며 동시에 로그를 관찰하는 방법이 효과적입니다.
FAQ 4: cf restage와 cf restart의 차이가 무엇입니까?
cf restart는 기존 droplet(빌드 결과물)을 그대로 사용하여 컨테이너만 재시작합니다. cf restage는 소스부터 다시 빌드(Staging)하여 새 droplet을 생성합니다. 환경변수 변경이나 서비스 바인딩 변경 후에는 cf restage가 필요합니다.
7. 다음 단계 / 관련 주제
- Blue-Green 배포 —
cf push --strategy rolling을 활용한 무중단 배포로 Crashed 발생 시 자동 롤백 - Application Autoscaler — SAP BTP의 Autoscaler 서비스로 메모리/CPU 기반 자동 스케일링 설정
- SAP Cloud Logging — ELK 기반 중앙 로그 수집으로 Crashed 원인을 체계적으로 분석
- MTA 배포 —
cf deploy를 활용한 Multi-Target Application 배포로 서비스 바인딩 자동화 - CI/CD 파이프라인 — SAP Continuous Integration and Delivery 서비스로 배포 전 자동 테스트 및 검증