11번가 클레임개발팀이 한 대로 운영하는 팀 배치 서버에서, Job 이 도는 중에 배포가 겹쳐 jar 파일이 바뀌며 배치가 실패하던 문제를 심볼릭 링크로 해결한 기록이다. 배포 후처리에서 새 디렉터리로 jar 를 복사하고 링크를 갈아끼운 뒤, 실행 시 readlink 로 현재 링크가 가리키는 경로를 읽는 방식이다.
핵심 포인트- 증상은 Job 실행 중 배포가 진행되면 NoClassDefFoundError 나 ClassNotFoundException 이 터지며 배치가 비정상 종료되는 것이었다.
- 원인은 실행 중인 jar 파일이 배포로 업데이트되거나 제거되면서 클래스 로딩이 깨지는 것이다.
- 빌드와 배포는 사내 시스템을 그대로 쓰고, 배포 후처리 단계에만 심볼릭 링크 전략을 넣어 해결했다.
- 배포 시마다 타임스탬프 이름의 새 디렉터리를 만들어 jar 를 복사하고, 심볼릭 링크를 그 파일로 옮긴다. 실행 중이던 기존 jar 는 건드리지 않아 그대로 유지된다.
- Job 실행 스크립트에는 readlink 한 줄을 추가해 링크가 현재 가리키는 jar 경로를 가져오게 했다.
상세 정리- 11번가는 전사 배치 서버와 별도로 팀 전용 배치 서버를 운영하며, 최종 목표는 공동 관리하는 레거시 배치를 팀 전용으로 이관하는 것이다.
- 스케줄러는 Jenkins 를 쓰고, 잡 설정에 명시한 jobName 과 jobParameter 를 읽어 Job 을 실행하는 구조다.
- 기존에는 특정 배포 경로에 빌드된 jar 를 그대로 덮어쓰는 방식이라, 실행 중인 파일과 배포 대상 파일이 같은 경로를 공유했다.
- 심볼릭 링크는 대상 파일에 대한 참조를 갖고 있어 링크 파일을 대상 파일처럼 쓸 수 있다. ln 명령에 옵션을 주어 만든다.
- 링크 교체에는 세 옵션을 함께 쓴다. 링크 이름을 일반 파일로 취급하는 옵션, 기존 대상 파일을 강제로 지우는 옵션, 하드 링크가 아닌 심볼릭 링크를 만드는 옵션이다.
- 디렉터리 이름은 date 명령으로 연월일시분초를 붙여 생성한다.
- 배포할 때마다 디렉터리와 jar 가 쌓여 서버 용량을 압박하므로, 최근 10개만 남기고 지우는 정리 스크립트를 함께 둔다.
- 정리 스크립트는 배포 경로의 디렉터리 개수를 세고, 10개를 제외한 나머지 개수만큼 오래된 것부터 추출해 삭제한다.
- 최종 실행 스크립트는 기존과 동일하게 jobName 과 jobParameter 를 읽되, readlink 로 얻은 경로의 jar 를 실행한다.
왜 읽나스케줄러가 도는 배치 서버에 배포를 겹쳐 넣다 실패를 겪은 팀에게, 애플리케이션 수정 없이 쉘 스크립트만으로 무중단을 만드는 방법을 보여주는 짧은 사례.