← 기술 실천으로 돌아가기

CI/CD 실전

OpenShip 배포 실패: 2026 빌드·출시 점검 가이드

약 12분 읽기

OpenShip 배포 실패: 2026 빌드·출시 점검 가이드

공식 문서가 설명하는 OpenShip 배포 경로는 빌드, 전송, 컨테이너 실행, 도메인 연결, 운영의 5단계입니다. (openship.io)

배포 완료로 표시됐지만 도메인이 열리지 않는다면, 플랫폼을 재설치하지 마십시오. 먼저 로컬 빌드 → SSH 전송 → 컨테이너 시작 → 도메인 라우팅 → 의존 서비스 순서로 증거를 모으십시오. 원인이 로컬 Mac의 자원 부족이나 온라인 유지 문제일 때만 지속 접속이 가능한 원격 환경으로 옮기는 편이 빠릅니다.

이 글은 처음 OpenShip을 사용하는 독립 개발자, 직접 서버를 관리하는 운영 담당자, 로컬 Mac에서 배포하지만 자원이나 접속 시간이 부족한 원격 팀을 위한 안내서입니다.

첫 10분: 실패 지점을 다섯 층으로 고정합니다

OpenShip은 로컬 또는 클라우드에서 이미지를 만들고, SSH로 대상 서버에 전송한 뒤, 새 컨테이너와 라우팅을 구성하는 방식으로 동작합니다. 공식 설명상 운영 서버가 직접 빌드하지 않는 구조이므로, 서버에서 오류가 보인다고 곧바로 서버의 코드 문제라고 단정하면 안 됩니다. (openship.io)

다음 순서로 시간선을 기록하십시오.

  • 0분: 배포 명령, 커밋, 사용한 브랜치와 환경을 기록합니다.
  • 2분: 전체 빌드 로그와 종료 코드를 저장합니다.
  • 4분: SSH 접속과 파일 전송이 끝났는지 확인합니다.
  • 6분: 컨테이너 상태, 시작 로그, 헬스 체크 결과를 확인합니다.
  • 8분: 공개 DNS, 인증서, HTTP 응답을 외부 네트워크에서 확인합니다.
  • 10분: 데이터베이스 연결과 실제 읽기·쓰기 요청을 검증합니다.

이 기록이 없으면 “배포 성공”이라는 한 줄의 상태만 남습니다. 상태 한 줄은 원인 분석 자료가 아닙니다.

1단계: OpenShip 빌드 실패는 로그 끝부분부터 자르지 않습니다

어떤 로그를 먼저 봐야 합니까

빌드 실패라면 마지막 오류 한 줄만 복사하지 말고 다음 범위를 함께 저장하십시오.

openship deploy 2>&1 | tee deploy-<project>-<timestamp>.log
echo $?

확인할 항목은 네 가지입니다.

  1. 의존성 설치가 시작됐는지
  2. 실제 컴파일 또는 번들 명령이 실행됐는지
  3. 실행 시 요구되는 런타임 버전이 맞는지
  4. 환경 변수가 빌드 단계에 전달됐는지

OpenShip은 프로젝트의 프레임워크와 명령을 감지해 배포 설정을 만들 수 있지만, 자동 감지가 프로젝트의 모든 스크립트와 비밀값까지 보장한다는 뜻은 아닙니다. 공식 빠른 시작 문서도 초기화 후 환경 변수와 배포 설정을 별도로 확인하도록 안내합니다. (openship.io)

자원 부족과 코드 오류를 어떻게 나눕니까

다음 신호가 보이면 코드 설정을 먼저 의심하십시오.

  • 모듈을 찾을 수 없다는 오류
  • 존재하지 않는 스크립트 이름
  • 런타임 버전 불일치
  • 필수 환경 변수 누락
  • 빌드 산출물 경로 불일치

반대로 프로세스가 갑자기 종료되거나 운영 체제가 프로세스를 강제 종료한 흔적이 있고, 같은 커밋이 더 큰 환경에서 성공한다면 로컬 자원 부족 가능성이 있습니다. 이때는 프로젝트 설정 파일, 사용한 런타임 버전, 종료 코드를 함께 비교해야 합니다.

처리 결론: 전체 빌드가 시작되기 전에 멈추면 의존성·명령·환경 변수를 고칩니다. 컴파일 중 강제 종료되면 로컬 자원과 디스크 여유를 확인합니다.

수정 후 검증: 같은 커밋으로 다시 빌드하고, 로그에 산출물 생성과 정상 종료 코드가 모두 남는지 확인합니다. 산출물 파일명만 생겼다고 성공으로 보지 말고 다음 전송 단계까지 진행하십시오.

2단계: OpenShip SSH 연결은 접속 유형을 세 갈래로 나눕니다

SSH 문제는 “연결이 안 된다”로 묶으면 복구가 늦어집니다. 공식 배포 경로에서도 빌드된 이미지가 SSH를 통해 대상 서버로 전달되므로, 접속과 전송을 분리해 확인해야 합니다. (openship.io)

첫 연결 자체가 실패하는 경우

ssh -vvv \
  -i <KEY_PATH> \
  -p <PORT> \
  <USER>@<SERVER_IP>

여기서 남겨야 할 증거는 다음과 같습니다.

  • 주소와 포트가 실제 대상과 일치하는지
  • 개인 키 파일의 권한이 과도하게 열려 있지 않은지
  • 호스트 지문을 처음 접속한 서버와 비교했는지
  • 연결 시도 자체가 방화벽에서 차단되는지

키 권한은 다음처럼 확인할 수 있습니다.

ls -l <KEY_PATH>
chmod 600 <KEY_PATH>
ssh-keygen -lf <KEY_PATH>.pub

호스트 지문이 갑자기 달라졌다면 known_hosts를 무조건 삭제하지 마십시오. 서버 교체나 주소 변경인지 운영 담당자에게 확인한 뒤 갱신해야 합니다.

연결 후 전송이 중단되는 경우

접속은 되지만 이미지 전송이 중단되면 대상 서버의 디스크, 네트워크 끊김, 전송 대상 경로 권한을 확인합니다.

ssh -i <KEY_PATH> -p <PORT> <USER>@<SERVER_IP> 'df -h; id; docker info'

이 명령의 결과에는 디스크 여유, 실제 로그인 사용자, 컨테이너 엔진 접근 권한이 나타나야 합니다. 로그인 사용자가 docker 명령을 실행할 수 없다면 접속 성공과 배포 권한은 서로 다른 문제입니다.

연결 후 명령 실행 권한이 없는 경우

ssh -i <KEY_PATH> -p <PORT> <USER>@<SERVER_IP> \
  'whoami; id; docker ps'

처리 결론: 주소·포트 오류는 네트워크 설정을 고칩니다. 전송 중단은 디스크와 네트워크를 확인합니다. docker ps 권한 오류는 사용자 그룹이나 제한된 배포 계정을 조정합니다.

공개 SSH를 열어야 한다면 모든 포트를 허용하지 말고, 필요한 포트만 열고 허용할 주소 범위를 제한하십시오. 비밀번호 로그인과 관리자 직접 로그인을 그대로 유지하는 방식은 임시 장애 해결책이 될 수 있어도 운영 구조로 남기면 안 됩니다.

수정 후 검증: 대화형 접속, 비대화형 명령, 작은 파일 전송을 차례로 성공시킨 뒤 실제 배포를 다시 실행합니다.

3단계: 컨테이너가 시작 후 반복 재시작하는지 확인합니다

OpenShip의 배포 완료 표시는 서비스가 외부 요청에 응답한다는 뜻과 같지 않습니다. 컨테이너가 생성됐어도 입구 명령, 수신 주소, 포트 또는 환경 변수에서 실패할 수 있습니다.

docker ps -a --filter "name=<APP_NAME>"
docker logs --tail 200 <CONTAINER_NAME>
docker inspect <CONTAINER_NAME> \
  --format '{{json .State}}'

다음처럼 판별하십시오.

  • 즉시 종료: 실행 명령, 파일 경로, 필수 환경 변수 문제일 가능성이 큽니다.
  • 계속 재시작: 프로세스가 시작 직후 종료되거나 헬스 체크에 실패하는 상태입니다.
  • 실행 중이지만 외부 응답 없음: 애플리케이션이 127.0.0.1에만 바인딩됐거나 예상 포트와 실제 포트가 다를 수 있습니다.
  • 메모리 또는 디스크 오류: 컨테이너 로그와 호스트 자원 상태를 함께 비교해야 합니다.

컨테이너 안에서 실제 수신 상태도 확인합니다.

docker exec <CONTAINER_NAME> \
  sh -lc 'printenv | sort; ss -lntp'

AI SaaS라면 웹 화면만 열지 말고 대표 API 요청도 보내십시오.

curl -i https://<DOMAIN>/health
curl -i -X POST https://<DOMAIN>/<API_PATH> \
  -H 'Content-Type: application/json' \
  -d '<REQUEST_BODY>'

공식 사이트는 배포 버전을 변경 불가능한 산출물로 유지하고 이전 버전으로 되돌릴 수 있다고 설명합니다. 따라서 수정할 때 기존 버전을 먼저 보존하고, 새 버전이 실패하면 즉시 이전 버전으로 회귀할 수 있게 해야 합니다. (openship.io)

처리 결론: 재시작 횟수만 줄이는 방식으로 해결하지 마십시오. 시작 명령, 바인딩 주소, 포트, 환경 변수 중 실제 실패 항목을 고쳐야 합니다.

수정 후 검증: 컨테이너 상태가 안정적으로 유지되고, 내부 요청과 공개 HTTPS 요청이 모두 성공해야 다음 단계로 넘어갑니다.

OpenShip 배포 성공 후 도메인이 열리지 않을 때 확인할 순서

DNS부터 확인합니다

dig <DOMAIN> A
dig <DOMAIN> AAAA
dig <DOMAIN> CNAME

외부 확인 결과가 대상 서버 또는 OpenShip의 올바른 엣지 주소를 가리키지 않으면 애플리케이션을 다시 시작해도 해결되지 않습니다. 사내 DNS에서만 정상으로 보이는 경우에는 공용 네트워크와 다른 DNS 확인 결과를 비교해야 합니다.

인증서와 라우팅을 따로 봅니다

DNS가 맞으면 인증서 상태와 HTTP 응답을 확인합니다.

curl -I http://<DOMAIN>
curl -Iv https://<DOMAIN>
  • DNS 실패: 레코드와 TTL을 확인합니다.
  • 인증서 발급 실패: 도메인이 올바른 대상에 연결됐고 인증서 검증 요청이 차단되지 않았는지 확인합니다.
  • 404: 라우팅 경로 또는 애플리케이션의 기본 경로를 확인합니다.
  • 502 또는 504: 엣지는 도달했지만 뒤쪽 컨테이너가 응답하지 않는 상태일 수 있습니다.
  • 서버 내부에서만 성공: 공개 방화벽, 바인딩 주소, 프록시 라우팅을 우선 확인합니다.

OpenShip 공식 설명은 도메인 연결과 자동 HTTPS, 엣지 라우팅을 배포 흐름의 별도 단계로 구분합니다. (openship.io)

커뮤니티 이슈에는 비공개 주소에서 평문 HTTP로 대시보드를 열 때 브라우저의 보안 컨텍스트 기능이 작동하지 않았다는 개별 사례가 보고되어 있습니다. 이는 모든 환경의 보편적 결함으로 단정할 자료는 아니지만, 외부 접근이 필요한 대시보드를 평문 HTTP에 두지 말아야 한다는 점을 확인하는 단서로 사용할 수 있습니다. (github.com)

처리 결론: DNS, 인증서, 라우팅, 애플리케이션 응답을 한 번에 바꾸지 마십시오. 한 단계씩 변경하고 각 결과를 저장해야 원인을 되돌릴 수 있습니다.

수정 후 검증: 공용 DNS 결과, 인증서 유효성, HTTPS 응답 코드, 실제 API 응답을 하나의 기록에 남깁니다.

데이터베이스 연결 실패는 삭제보다 보존을 먼저 합니다

OpenShip 공식 기능 설명에는 PostgreSQL, Redis, MongoDB, MySQL 같은 의존 서비스를 애플리케이션과 분리된 네트워크에서 운영하는 경로가 포함되어 있습니다. (openship.io)

먼저 서비스가 살아 있는지 확인합니다.

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --tail 200 <DB_CONTAINER_NAME>

그다음 애플리케이션의 연결 문자열을 확인합니다.

docker exec <APP_CONTAINER_NAME> \
  sh -lc 'printenv | grep -E "DATABASE|REDIS|DB_HOST|DB_PORT"'

다음 세 가지를 구분하십시오.

  • 애플리케이션 코드 오류: 쿼리나 초기화 코드에서 실패합니다.
  • 자격 증명 오류: 사용자, 비밀번호, 데이터베이스 이름이 맞지 않습니다.
  • 의존 서비스 불가: 서비스가 중지됐거나 네트워크에서 이름을 찾지 못합니다.

복구 전에는 데이터베이스 백업 또는 스냅샷을 확보하십시오. 데이터 볼륨 삭제는 일반적인 해결책이 아닙니다. 잘못된 연결 문자열이나 네트워크 문제를 데이터 삭제로 해결할 수 없고, 복구해야 할 상태만 잃을 수 있습니다.

처리 결론: 먼저 백업을 확보하고, 연결 대상과 자격 증명, 네트워크 경로를 순서대로 수정합니다.

수정 후 검증: 애플리케이션의 읽기 요청뿐 아니라 테스트용 쓰기와 롤백 또는 복원 확인까지 완료해야 합니다.

증거에 따른 환경 변경 판단

다음 기준이면 계속 현재 환경에서 고치는 편이 낫습니다.

  • 빌드 로그에 코드 오류가 명확합니다.
  • SSH 접속은 되지만 권한 또는 경로만 잘못됐습니다.
  • 컨테이너 로그에 수정 가능한 시작 오류가 있습니다.
  • DNS 또는 인증서 설정이 잘못됐습니다.
  • 데이터베이스 서비스는 살아 있고 연결 문자열만 틀렸습니다.

다음 조건이면 빌드 노드나 배포 환경 변경을 검토하십시오.

  • 로컬 Mac에서 빌드 프로세스가 반복적으로 강제 종료됩니다.
  • 개발 장비가 절전이나 네트워크 변경으로 자주 끊깁니다.
  • 팀이 같은 환경에서 재현 가능한 로그를 남길 수 없습니다.
  • 장시간 빌드와 배포를 위해 개발자의 노트북을 계속 켜 두어야 합니다.
상황 우선 조치 환경 변경 판단
빌드 로그에 코드 오류가 있음 프로젝트 설정과 환경 변수 수정 변경하지 않음
로컬 자원 부족이 반복됨 자원 사용량과 빌드 산출물 비교 원격 빌드 환경 검토
SSH 인증만 실패함 키, 지문, 포트, 계정 확인 변경하지 않음
컨테이너가 재시작함 시작 명령과 포트 검증 서버 교체 전 로그 확보
도메인만 실패함 DNS와 HTTPS를 분리 점검 플랫폼 교체보다 라우팅 수정
의존 서비스 데이터가 위험함 백업 후 연결 정보 수정 데이터 볼륨 삭제 금지

OpenShip의 공식 설치 문서는 자체 서버에 설치하는 경우 Linux와 Docker를 요구하고, 설치 상태 확인 명령도 제공합니다. 환경을 옮길 때는 새 노드가 현재 프로젝트의 런타임, 컨테이너 엔진, 저장 공간 조건을 만족하는지 먼저 확인하십시오. (openship.io)

복구 후 6개 항목으로 재배포 확인을 진행합니다

다음 기록을 남기면 같은 장애가 다시 발생했을 때 팀원이 바로 이어서 조사할 수 있습니다.

  1. 빌드 성공: 커밋 식별자, 실행 명령, 전체 로그, 종료 코드
  2. 전송 성공: 대상 주소, 포트, 로그인 사용자, 전송 완료 시각
  3. 서비스 시작: 컨테이너 이름, 이미지 버전, 시작 로그
  4. 헬스 체크: 내부 요청과 공개 요청 결과
  5. 도메인 접근: DNS 결과, 인증서 상태, HTTP 응답
  6. 데이터 검증: 데이터베이스 읽기·쓰기, 백업 위치, 복원 확인
  7. 롤백 연습: 이전 버전으로 되돌린 결과와 재배포 절차

기록 템플릿은 다음처럼 간단히 유지하면 됩니다.

프로젝트:
커밋:
실패 층:
첫 오류 시각:
재현 명령:
관련 로그:
변경한 설정:
수정 후 검증:
롤백 결과:
담당자:

로컬 빌드 자원 부족이나 장비의 온라인 유지 문제가 실제 원인으로 확인됐다면, 플랫폼을 바꾸기보다 원격에서 접속하고 로그를 보존할 수 있는 Mac 빌드 환경을 준비하는 편이 운영상 자연스럽습니다. 반대로 장기적으로 고정된 고부하 작업이 필요하거나 물리 장치 연결이 필수라면 Mac 렌탈보다 직접 장비를 운영하는 편이 맞을 수 있습니다.

현재 Mac에서 계속 배포하면 절전, 네트워크 변경, 다른 개발 작업과의 자원 경쟁이 반복될 수 있습니다. 원격 Mac 지원 환경을 먼저 확인하고, 팀이 필요할 때만 접속하는 방식이라면 한국 Mac mini 렌탈로 별도 빌드 노드를 마련하는 선택이 더 안정적입니다. 핵심은 OpenShip을 버리는 것이 아니라, 빌드와 배포를 실행하는 장비를 항상 켜 둘 수 있는 환경으로 분리하는 것입니다.

M4 Mac mini에서 CI/CD를 돌리면 진짜 편합니다

Xcode, Fastlane, CocoaPods, and SPM are first-class on macOS. Mac mini M4 unified memory keeps signing and archiving smooth; ~4W standby power suits 24/7 build nodes.

View Kvmkit plans

기술 지원이나 선정 조언이 필요하신가요?

Mac 인스턴스나 CI/CD 파이프라인 문제는 고객센터를 먼저 확인하세요.