인생은 고통의 연속

폐쇄망에서 Gradle 프로젝트 빌드하기(offline mode) 본문

프로그래밍/Java_Spring

폐쇄망에서 Gradle 프로젝트 빌드하기(offline mode)

gnidoc 2025. 1. 7. 16:38
    반응형

    폐쇄망... 이것도 k-culture 같은거 아닐까요?

    한국에서는 매우 당연히 여기는 개발 방식? 환경? 문화? 중에 하나가 바로 폐쇄망입니다.
    저도 회사생활을 처음 시작했을때 주어진 미션이 바로 이거였습니다.

    (인터넷이 안되는) 폐쇄망 환경의 서버에서 API서버를 빌드/배포할 수 있게 프로젝트를 구성해라!

     

    요즘엔 프로젝트 설정이나 CI/CD가 오픈소스로 많이 보급되고 통합되면서 이런 자잘한 DevOps관련 세팅이 쉬워졌지만
    Spring Boot가 나오기 전인 2014년 전까지는 남의 컴퓨터에서 프로젝트가 정상적으로 빌드가 되게 하는것도 일이었습니다.
    (제 컴퓨터에선 되는데 왜 꼭 다른 사람 컴퓨터에선 안될까요...)
    Spring MVC로 백엔드 개발 커리어를 시작하신분들이라면 공감하실겁니다 ㅎㅎ...
    (Spring은 2001년에 나왔는데 그보다 더 이전이면 개발 커리어를 90년대부터 시작하셨겠네요...)

    왜 다른사람 컴퓨터에선 빌드가 안되는가...

     

    암튼 한국 회사에서 개발자(또는 엔지니어)로 일을 한다면 폐쇄망을 한번쯤 겪어보실텐데요.
    그러다보니 보통 처음 겪는 문제가 바로 폐쇄망 환경에서 프로젝트를 세팅하는것입니다.
    내 PC에서 다 개발했는데 폐쇄망에 있는 서버에 배포해야되는거죠.

     

    폐쇄망 불편한데 왜 쓸까요?

    보통 대기업이나 금융권(은행,증권사) 또는 규모가 큰 서비스를 운영하는 기업들은 "ISMS"라고 하는 "정보보호 관리체계 인증"을 받습니다.
    하는 궁극적인 이유는 바로 기업이 고객의 개인정보를 잘 보호하라고 법적으로 의무화한거죠.

    해당 제도는 2001년에 시행되었고 
    2010년에 "PIMS"라고 하는 "개인정보보호 관리체계 인증"이 시행되고
    2013년에 추가적으로 "PIPL"라는 "개인정보보호 인증"이 나왔다가
    2016년에 PIMS, PIPL인증이 하나로 통합되고
    2018년에 최종적으로 ISMS-P(ISMS+PIMS+PIPL)으로 통합되었습니다.
    그래서 지금은 ISMS와 ISMS-P 제도 2개만 남았습니다.
    (-P 쓰기 귀찮으니 이후부턴 ISMS라고 합쳐서 언급하겠습니다)

    일반적으로 자율적으로 인증하지만 
    어느정도 기업(서비스)의 규모가 커진다면 의무적으로 인증해야됩니다.
    - 인증대상: https://isms.kisa.or.kr/main/ispims/target/

     

    KISA 정보보호 및 개인정보보호관리체계 인증 ISMS-P 인증대상

    자율신청자 의무대상자 기준에 해당하지 않으나 자발적으로 정보보호 및 개인정보보호 관리체계를 구축·운영하는 기업·기관은 임의신청자로 분류되며, 임의신청자가 인증 취득을 희망할 경

    isms.kisa.or.kr

    그런데 ISMS인증에서 거진 필수조건 중 하나가 바로 "망분리"입니다.

     

    망분리?

    네트워크(망)을 물리적(ex - PC) 또는 논리적(ex - VDI)으로 분리하는게 바로 망분리입니다.
    (예전에는 대부분 물리적으로만 허용했었지만 많이 완화되어서 논리적으로도 많이 허용되고 있습니다.)
    망분리를 하는 이유는 ISMS인증 기준 중 "2.6.7. 인터넷 접속 통제" 때문입니다.

    인터넷을 통한 정보 유출, 악성코드 감염, 내부망 침투 등을 예방하기 위하여 주요 정보시스템, 주요 직무 수행 및 개인정보 취급 단말기 등에 대한 인터넷 접속 또는 서비스(P2P, 웹하드, 메신저 등)를 제한하는 등 인터넷 접속 통제 정책을 수립·이행하여야 한다.

     

    즉, 인터넷으로 중요한 데이터가 유출될 수 있으니 인터넷 접근을 통제해야된다는 겁니다.
    (통제라고 써있지만 뭐 사실상 차단이죠 ㅎㅎ...)

    보통 서버가 직접 해킹 당하기보단 업무용PC가 해킹 당하면서 서버까지 propagation(전파)되는 경우가 많은데 
    애초에 인터넷을 차단하면 해킹당해도 인터넷이 안되니깐 데이터를 빼갈 수 없게하는거죠.
    그래서 이 기준을 충족하기 위해서 사무용PC, 개발용PC, 서버를 물리적으로 분리(각각 별도 PC로 사용)해서 쓰기도 하고
    사무용PC를 지급하고 VDI라고 하는 가상 데스크톱으로 개발용PC를 대체하기도 합니다.
    그래서 보통은 사무용PC에서 개발용VDI를 접속해서 서버로 접근합니다.
    좀 보안을 더 강화한 경우에는 
    사무용PC에서 사무용VDI를 통해 개발용VDI를 통해 상용VDI를 통해서 상용서버(production)를 접근하기도 합니다.
    (가끔 더 심하게(?) 구성되어있는 회사도 있습니다...) 

    보기만해도 이렇게 끔찍한데 실제론 얼마나 더 고통스러울까요?

     

    망분리와 함께 발생하는 개발환경의 어려움(feat. 개발자의 고통)

    이게 그나마 최근에는 AI 덕분에(?) 망분리 규정이 더 완화되어서 
    금융권도 논리적으로 분리가 가능해지고 AWS와 같은 클라우드도 도입도 어느정도는 가능해졌는데
    그래도 개발자입장에서는 여전히 불편하긴 합니다.

    (쓰다보니깐 현타와서 접은글로 남겨둡니다)

    더보기

    1. VDI

    일단 VDI 자체부터 얘길해보자면 
    대부분 VDI를 베어메탈(물리서버) 그대로 쓰기보단 베어메탈 위에 VM을 생성해서 사용자에게 할당하다보니
    실제 사용자는 OS 위에 OS를 띄운걸 네트워크를 통해서 접근하게 됩니다.
    그래서 키보드 입력이 1~2초 뒤에 입력되거나 마우스가 생각처럼 안움직여지는 입/출력 딜레이는 기본이죠.
    특히 PPT로 도형을 그려야되면 진짜 매우 고통스럽죠.

    문서 작업을 많이해야되는 경우에는 처음부터 로컬에서 문서를 작성해서 VDI로 복사하는 경우가 많습니다.
    그리고 VDI와의 네트워크 연결 끊김 현상도 어느정도 감수해야되구요.
    또한 Pycharm이나 AWSCLI 같은걸 VDI에 설치해야되면 파일을 VDI로 넣는거부터가 일이고
    특히 GB단위의 대용량 파일이라면 별도의 파일전송 시스템을 만들어야되는 경우도 있습니다.

    2. 고질적인 한/영 전환키 문제
    지급받은 PC는 맥북인데 윈도우VDI를 들어가서 putty로 리눅스 서버에 ssh로 붙는다고 치면
    고질적인 한/영 전환키로 한/영이 안바뀌는 문제부터 
    한글 입력이 씹히는 현상도 있고 자소가 분리되는 문제도 있습니다.

    3. UNIX 호환성
    그렇다고 한/영키 문제로 맥북을 포기하고 윈도우PC로 개발하자니 
    맥OS의 UNIX 호환성으로 인한 편리함 때문에 포기하기가 쉽지 않습니다.
    (사실 감성보단 이 부분 때문에 요즘 개발자들이 맥북을 쓰는거죠)
    특히 윈도우에서 microk8s 같은거 설치해야된다면 재부팅도 많이 해야되고 설치하는것도 어렵습니다.
    (맥북에선 그냥 brew install하면 되는데...)

    4. 터미널
    그리고 윈도우면 대부분 ssh 프로그램으로 putty를 사용하실텐데
    맥북의 좋은 기본 터미널(또는 iterm)을 버리자니 깝깝합니다.
    물론 윈도우 기본 cmd나 powershell도 ssh를 이젠 지원하지만
    cmd는 자동완성이나 기본 조작(ex - tab, 화살표키)이 불편하고
    powershell은 일단 색깔부터 블루스크린 느낌나서 싫은데 
    둘 다 중간에 창크기를 조절하거나 테마 같은걸 적용하면 텍스트가 깨지는 경우가 많습니다.

    그러면 서버 들어가서 리눅스 환경에서 tmux + zsh 조합으로 쓰면 되지 않냐고 하시는데 
    tmux은 cpu 많이 잡아먹어서 여러명이 쓰면 서버가 힘들어하고
    zsh은 또 ssh로 들어가는 서버마다 설정해줘야되서 귀찮습니다.
    이런거 때문에 ansible로 환경 통일해주는것도 애매하구요.

    5. WSL2 이슈
    윈도우환경에서 위와 같은점들이 어렵다면 WSL2쓰면 된다곤 하지만
    WSL2도 결국 윈도우에서 리눅스(우분투)를 VM으로 띄우는거라서
    녹스(Nox)나 블루스택 같이 PC로 안드로이드 에뮬레이터를 실행해야된다면 WSL2를 쓸 수 없습니다.
    단일 머신에서 중복되는 가상화 프로그램(VM)을 실행할 순 없으니깐요.
    물론 회사PC에선 마주치기 어려운 케이스지만 
    안드로이드 개발자가 디버깅을 위해서 서버를 로컬에서 실행해야되는 상황이라면 겪을 수도 있겠죠?
    (저도 알고 싶지 않았습니다...)


    아무튼 망분리 환경에서 살면 개발자는 이런 고통과 고민들에서 살아야합니다...

     

    아무리 그래도 아웃바운드(outbound) 정도는 인터넷을 열어줘야하는거 아닐까요?

    서버로 접근하는 인바운드(inbound)에 대한 통제는 많이들 이해(?)해주십니다.
    서버를 운영해보셨다면 22번 포트인 ssh를 any open(=0.0.0.0)할 용기있는 사람은 잘 없겠죠...?
    "이게 왜 문제지?"라고 생각하신다면
    리눅스 서버 띄워서 22번 포트를 any open으로 열어두고 접근할때마다 경고 알림오게 세팅해보세요.
    진짜~ 쉴새없이 알림이 옵니다.
    해커입장에선 하나만 걸리면 내부 네트워크 통해서 다른 장치도 접근 가능하니깐요.

    그렇다고 이런걸 또 막겠다고 로그인 횟수 제한 걸면 
    오히려 관리자인 본인이 로그인 못하는 상황이 벌어지는 재밌는 상황도 발생합니다.
    참 보안이란게 이래서 어렵습니다 ㅎㅎ;;

    그리고 요즘엔 와이파이(홈네트워크)로 PC, TV, 에어컨, 홈캠 등을 다 연동해서 쓰니
    만약에 내부 네트워크에 누군가 들어온다면 더욱 치명적일겁니다.
    만약 PC가 해킹(=인바운드)되서 웹캠이나 홈캠으로 몰래 영상을 찍어서 실시간으로 밖으로 빼간다면...?(=아웃바운드)
    어휴... 생각만 해도 끔찍합니다.

    그렇다고 인바운드를 완전히 막을수도 없고 해킹 공격을 100% 완벽하게 막을수없으니 
    결국에는 최소한 데이터가 외부로 유출되지 않게 물리적으로 인터넷이라도 차단하는겁니다.
    적어도 해킹 당하더라도 아웃바운드로 해커가 데이터를 가지고 나가진 못하도록요.

     

    그럼 ssh만 막으면 되지 않을까요? 인터넷 없으면 불편한데...

    그리고 꼭 ssh가 아니라도 해커가 원하는 명령어를 원격으로 실행할 수 있다면 서버에서 어떻게든 데이터는 빼갈 수 있습니다.
    아웃바운드가 any open으로 열려있다면 ftp나 rsync 같은 명령어만 실행해도 서버 내의 데이터를 밖으로 빼갈 수 있으니깐요.
    비교적 최근에 가장 많이 이슈화됐던 보안취약점인 Log4j가 있었죠.

    Log4j는 단순히 서버의 log를 출력하기 위해 쓰는 대표적인 라이브러리였는데 
    이 취약점을 응용(?)하면 유저가 서버의 명령어를 실행할 수 있었습니다.

    사실 Log4j 취약점이 유명세를 탄건 취약점 등급도 높았지만(최고인 10등급)
    최초로 취약점이 확인된게 바로 전세계적으로 유명한 마인크래프트(게임)였던것도 있습니다.

     

    “치명적 소프트웨어 취약점 발견…거의 모든 서버 위험”

    인터넷 서버에 광범위하게 사용되는 소프트웨어에 치명적 보안 취약점이 발견돼 전 세계 보안업계에 비상이...

    news.kbs.co.kr

     

    아래 영상을 보시면 마인크래프트 유저가 별도의 인증없이 윈도우 서버에서 실행 중인 마인크래프트 서버를 통해서 whoami 명령어나 메모장을 실행하는걸 보실 수 있습니다.

     

    이런걸 막겠다고 유저가 서버의 일부 명령어를 실행 못하게 차단한다고 크게 달라지지 않습니다.
    애초에 명령어로 실행이 되니깐 아래처럼 그냥 단순하게 파일을 생성해서 그 파일을 실행하면 되거든요. 

    echo "rsync -h" > rsync_test.sh
    sh rsync_test.sh

     

    그렇다고 완전히 명령어를 차단하자니 해당 명령어가 필요할땐 어떡하죠...?
    결국 이렇게 막기만 하면 그냥 실무자만 일할때 불편해질뿐입니다.

    그래도 Log4j 취약점은 버전만 올리면 대응가능한 비교적 간단한(?) 취약점이긴 했는데 
    저도 그냥 버전올리면 되지 않나 싶었지만 막상 올리니깐 빌드부터 안되더라구요...
    그래서 조치하는데 1~2일 정도 걸렸던거 같습니다.
    언제나 의존성은 다루기 쉽지 않네요.

     

    보안취약점만 빠르게 대응 가능하면 되지 않을까요? (공급망 공격)

    일단 개발자도 잠은 자야죠. 
    이런 취약점을 24시간 대응하기도 쉽지 않습니다. 특히 레거시 프로젝트라면 더더욱요!
    프로젝트가 오래될수록 라이브러리 버전 하나 올리는것도 쉽지 않습니다.

    게다가 Log4j는 의도치 않은 실수(?)였지만 
    의도적으로 저런 악성코드를 오픈소스에 심어서 공격하는 방식인 공급망 공격이 또 있습니다.
    공급망 공격은 SW 설치/업데이트 배포 과정에 침입해 정상 소프트웨어인 것처럼 꾸민 악성 SW를 사용자 기기에 설치하는 방식을 말하는데
    관련해서 최근에 발생했던 사건 중 가장 기억남는 뉴스는 이거였던거 같습니다.
    - 2년을 기다린 xz 라이브러리를 활용한 공급망 공격: https://news.hada.io/topic?id=14122

     

    xz 오픈소스 공격의 전체 타임라인 정리 | GeekNews

    2년 넘게 "Jia Tan"이라는 이름을 사용하는 공격자가 xz 압축 라이브러리에 성실하고 효과적인 기여자로 활동하여 최종적으로 커밋 권한과 관리자 권한을 부여받음.그 권한을 사용하여 Debian, Ubuntu,

    news.hada.io

     

    위 내용을 요약하면 
    오픈소스인 xz 라이브러리에 2년간 열심히 기여해서 관리자 권한을 받아서 
    서버에 로그인하지 않아도 원격으로 명령어를 실행할 수 있는 백도어 기능을 몰래 머지했다가 걸린건데
    이런거보면 참 해커들 열심히 삽니다...

    암튼 이런식으로 의도적으로 백도어를 몰래 설치할 수 있기 때문에 
    이런 의존성까지 모두 하나하나 확인할 수 있는게 아니라면 
    가장 쉬운 방법으로 물리적으로 인터넷 아웃바운드를 차단하게 좋습니다.
    네트워크 레벨로 차단하면 애초에 물리적으로 차단되니깐 관리하기 좋죠 ㅎㅎ

    그래서 다시 처음으로 돌아가서 
    이런 보안 사고를 예방하기 위해서 폐쇄망에서 프로젝트를 빌드/배포해야되니 이런 요건이 생기는거죠.

    (인터넷이 안되는) 폐쇄망 환경의 서버에서 API서버를 빌드/배포할 수 있게 프로젝트를 구성해라!

     

     

    근데 왜 폐쇄망에서 빌드되게 해야할까요?

    누군가는 이렇게 물어봅니다.

    그냥 맥북에서 빌드해서 jar/war파일만 서버로 옮겨서 배포하면 되지 않나요? 왜 굳이 인터넷이 안되는 환경에서 빌드를 해야될까요?

     

    일단 위에 글을 다 보셨다면 
    공급망 공격도 있기 때문에 빌드하는 과정 중에 코드나 인증 정보가 외부로 유출될수도 있고
    맥북에서 빌드가 된다고 꼭 서버에서도 문제없이 배포 및 동작한다는 보장이 없거든요.

    "JVM이면 OS 상관없이 되는거 아닌가요?"라고 하실수도 있는데
    배포판(OpenJDK, OracleJDK, Corretto 등)마다 약간씩 다를 수 있고
    사용하는 라이브러리 중에 binary형태(=실행파일)로 연동되는 라이브러리가 있다면 또 다를 수 있습니다.

    예를 하나 들자면 
    요즘은 MySQL 오픈소스가 잘되어있지만 옛날엔 MySQL Driver도 기업마다 커스텀해서 유료로 쓰는 경우가 있었습니다.
    (굳이 MySQL Driver 산다기보단 솔루션/제품에 패키지로 끼워서 팔았죠)
    그래서 윈도우에서 쓰는 드라이버에선 빌드가 되는 설정이 리눅스에서는 안되는 경우도 있었습니다.
    이래서 보통 상용(production)과 같은 환경으로 구성한 스테이징(stage) 환경에서 빌드/배포/테스트를 하죠.
    (물론 요즘엔 빌드용 서버나 CI/CD를 따로 구축합니다만 그럼 글이 너무 길어지니...)
    가끔 열받는건 스테이징에서 되길래 배포했는데 상용에서 안되는 케이스가 간혹 있다는건데
    차라리 스테이징에서 발견하면 다행이죠.
    상용에 배포까지해서 동작은 하는데 런타임 중에 에러나는 케이스가 발견된다면 상상하기도 싫네요 ㅎㅎ

    암튼 그럼 이런거까지 고려하려면 스테이징 환경에서 빌드를 해야되는데
    스테이징도 상용과 같다면 인터넷이 안되겠죠...?
    그럼 maven central repository도 접근이 안될텐데 어떻게 라이브러리(=의존성)를 땡겨와야할까요?

     

    수제 Gradle?

    예전에는 오픈소스라고 해봤자 Spring에 MySQL만 쓰기 때문에 
    모든 라이브러리를 maven central repository에서 직접 jar파일로 다운받아서 특정 폴더(libs)에 넣고
    gradle에서 빌드할때 해당 폴더 내에 jar파일을 모두 사용하도록 사용했습니다.

    하지만 요즘은 오픈소스 라이브러리들도 또 다른 하위 의존성이 존재하다보니
    모든 jar를 다운받고 그 의존성 트리를 하나하나 명시해서 관리하기는 쉽지 않습니다.
    (당장 Spring Boot만해도 하위 의존성이 어마어마하죠)

    특히 Java개발자라면 사실 Java를 쓴다기보단 Spring을 쓰실텐데
    Spring Boot도 plugin(ex - spring dependency management)이 없으면 빌드하기가 쉽지 않습니다.
    그럼 이 plugin도 jar로 받아와야할텐데 알아서 gradle이 plugin으로 인식하진 않을테고 또 의존성을 관리해야 되겠죠...?
    이거 삽질해보신분들은 아시겠지만 이쯤되면 gradle을 쓸 필요가 없는 수준입니다.
    거의 수제 gradle을 만드시는 느낌입니다.(아니 빌드 자동화툴이라매?? 왜 이리 해주는게 없어??)
    그럼 어떻게 해야될까요?

     

    Gradle - 의존성 다운로드

    일단 의존성으로 사용하는 jar파일 다운로드 받는것부터 해보면
    결국 gradle도 필요한 의존성들을 PC 어딘가에 다운로드 받아서 컴파일합니다.(이걸 의존성 캐시라고 하죠)
    그럼 의존성 다운로드하는 경로(=의존성 캐시 경로)를 통일시켜서 git에 올려두면 되지 않을까요?
    이 글에선 프로젝트 내에 libs 폴더로 만들어서 관리할겁니다.


    예제는 아래 github에서 확인하면 됩니다.

    • 예제: https://github.com/gnidoc327/gradle-offline-demo
      • intellij에서 gradle + spring boot 로 생성한 기본 프로젝트입니다.
      • MacOS를 기준으로 작업해서 셋업하는 스크립트도 MacOS기준으로 되어있습니다.
      • 예제에는 없지만 binary 의존성도 libs에 넣고 경로만 잘 지정하면 되겠죠?  

    git clone 받고 README에 setup까지 따라하신 뒤에 
    아래 명령어를 통해서 빌드하시면 libs 폴더가 생기고 거기에 모든 의존성이 설치됩니다.

    ./gradlew clean build --project-cache-dir libs --gradle-user-home libs

    이게 다 뭐시여...

    gradle은 기본적으로 .gradle 폴더로 설정과 캐시를 관리하는데
    이게 홈 디렉토리인 ~/.gradle과 프로젝트 하위 .gradle 2개가 있습니다.
    2개의 경로의 차이점은 아래와 같습니다.

    • 홈 디렉토리(전역)의 .gradle
      • 전역 설정 파일과 초기화 스크립트
      • 다운로드된 의존성 캐시
      • 로그 파일
      • JDK 프로비저닝 파일
      • Gradle 배포판
    • 프로젝트 디렉토리의 .gradle
      • 빌드 캐시
      • 증분 빌드를 위한 임시 파일
      • 프로젝트별 설정

    프로젝트에서 필요한 의존성도 기본적으로 홈 디렉토리에 다운로드(=캐시)되는데
    실제로 빌드과정에선 프로젝트 디렉토리에서 홈디렉토리에 있는 의존성을 바라보고 빌드가 실행되기 때문에
    결국 둘 다 libs 폴더에 몰아넣어줘야합니다.
    그리고 lombok(+junit) 같이 개발용 라이브러리도 결국 빌드할때 필요하므로
    annotationProcessor로만 선언하면 빌드할때 다운로드가 되지 않기 때문에
    implementation로도 추가적으로 선언해서 libs에 다운로드되게 합니다.

    

    그래서 project-cache-dir, gradle-user-home 둘 다 libs로 설정한거죠.
    하지만 우린 gradle을 직접 쓰는게 아니고 gradlew 스크립트를 사용하기 때문에
    gradle-wrapper.properties도 변경해줘야합니다.

    gradlew 스크립트를 보시면 처음에 gradle 설치부터 진행하는데
    해당 스크립트는 gradle/wrapper/gradle-wrapper.properties 설정을 참고해서 동작합니다.
    원래는 프로젝트를 생성하면 기본은 아래처럼 생성이 됩니다.

    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists

     


    이걸 아래처럼 바꿔서 gradlew 스크립트에서도 libs 폴더를 바라보게 하고 애초에 홈디렉토리를 바라보지 못하도록 합니다.

    distributionBase=PROJECT
    distributionPath=libs
    zipStoreBase=PROJECT
    zipStorePath=libs

     


    이렇게까지 설정하면 gradlew 스크립트와 gradle build과정에서 모든 의존성과 관련된 파일은 libs 폴더를 바라보게 됩니다.
    그래서 명령어 실행 후 libs 폴더보시면 이렇게 생겼는데
    적당히 .tmp랑 .log 같은것만 gitignore로 추가하면 되지 않을까 싶네요.
    여기까지가 의존성 경로를 libs로 변경해서 다운로드하는 방법이었습니다.

    그럼 이제 이걸 가지고 인터넷이 안되는 환경에서 빌드를 해야겠죠?

     

    Gradle - offline mode를 활용한 빌드

    그럼 이제 랜선을 뽑고 libs에 있는 캐시된 의존성 파일로 빌드가 되는지 확인하면 되겠죠?
    하지만 현재 PC에선 캐시된 어딘가에 있는 경로를 사용할지도 모르니
    깔끔하게 분리된 환경인 컨테이너에서 아웃바운드를 막고 테스트하는게 더욱 확실합니다!

    그래서 docker를 활용해서 빌드할껀데
    docker-compose.yml보시면 

    docker-compose.yml


    현재 프로젝트를 /app 경로에 마운트(복사)를 하고 (로컬에서 빌드한 결과물인 build, .gradle 제외하고 복사)
    마스커레이드(masquerade)를 꺼서 아웃바운드(인터넷) 통신을 막습니다.
    이러면 인터넷이 안되니 외부에서 의존성을 다운받지 않고 
    아무것도 없는 깨끗한 컨테이너 환경(의존성 캐시가 하나도 없는 환경)에서 libs만 바라보고 빌드할 수 있겠죠?

    하지만 여기서 문제는 gradlew로 빌드할 경우엔 처음부터 gradle를 다운받아서 설치부터 진행하게 됩니다.
    기본적으로 https://services.gradle.org/distributions 에서 다운로드를 받는데 이러면 또 인터넷이 필요합니다.
    그래서 gradle-wrapper.properties에서 로컬 경로에 있는 zip파일로 설치되게끔 추가적인 설정이 필요합니다.
    주석으로 남겨놨으니 distributionUrl만 아래처럼 로컬경로로 바꿔주시면 됩니다.

    #distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
    distributionUrl=file:///app/gradle/wrapper/gradle-8.11.1-bin.zip


    여기까지 진행하셨으면 이제 docker compose up -d 로 실행하시면 
    gradle build가 offline 모드로 실행되면서 libs 폴더에 있는 캐시들을 가지고 결과물(output)인 jar를 생성하고

    ./gradlew build --project-cache-dir libs --gradle-user-home libs --offline

    offline 빌드 성공!


    해당 output을 실행하면서 spring boot 서버가 실행됩니다.

    java -jar build/libs/gradle-offline-demo-0.0.1-SNAPSHOT.jar

    jar 파일이 실행되면서 내장톰캣으로 8080포트로 실행된 모습


    springdoc도 연동되어있어서 정상적으로 빌드 및 실행이 된다면 아래 URL로 swagger 접속이 가능합니다.

    • http://localhost:8080/swagger-ui/index.html

     

    결론

    요즘은 nexus 같은 private registry 를 쓰거나
    인터넷이 되는 proxy 서버를 두거나
    CI/CD를 따로 구축해서 빌드용 서버가 따로 있거나
    container 이미지째로 빌드해서 이미지로 배포하다보니 걱정할 필요가 없는 문제이지만

    private registry를 구축할 여건이 되지 않거나
    서버 장비 자체가 hypervisor가 지원되지 않거나
    OS버전이나 기타 문제로 container를 사용할 수 없는 상황이라면
    offline build가 필요할 것 같아서 작성해봤습니다.

    그럼 폐쇄망에서 서비스 개발/운영하시는분들 모두 화이팅하세요!

    반응형
    Comments