이 글은 Springboot war 파일 배포 글의 후속편입니다.
위 글에서 나는 파일을 jar 방식으로 배포했고 문제 없이 동작했었다. 그런데...... ??? 후.. build.gradle에 여러 의존성을 추가하고 커밋하고.. 한 이후 갑자기 빌드가 실패하기 시작했다.. 아직도 정확히 어떤 커밋이 영향을 준건지는 잘 모르겠다😑 커밋 지워버림.. 그래도 정석적인 방법으로 다시 구현해볼 수 있었고, 보이는 것과 같이 결국 build에 성공했다 ! 이 짜릿했던 순간을 함께 알아보자 😆
결론적으로 말하면 war 파일을 jar 형식으로 배포하지 말자.. 유효한 jarfile이 아니기 때문에 아래 오류를 볼 수 있다.
Error: Invalid or corrupt jarfile /app.war
WAS 와 WS / 내장, 외장 톰캣
WAS와 WS, 내장/외장 톰캣에 대해 공부하다가 맨날 헷갈렸던 여러 개념들을 더 꼼꼼히 정리해볼 수 있었다. TMI 성 설명 주의
WAS는 동적인 웹 애플리케이션을 실행하고 관리하는 소프트웨어 환경을 제공하는 서버이고, WS는 정적인 리소스를 관리한다. 주로 WS(Apache, Nginx)의 사용이유는 WAS(Tomcat)가 해야 할 일의 부담을 줄이기 위해서이다. 따라서 WAS 앞에 웹 서버를 둬서 웹 서버에서는 정적인 문서만 처리하도록 하고, WAS는 애플리케이션의 로직만 수행하도록 기능을 분배하여 서버의 부담을 줄일 수 있다.
추가로 springboot에서의 내장 tomcat에서 어떤 일이 일어나고 있는지 보자. 먼저 WAS는 servlet Container가 처리하는 방식이다.
그럼 이 서블릿이 무엇이냐.. 서블릿이란, 웹 기반의 요청에 대한 동적인 처리가 가능한 Server Side에서 돌아가는 자바 프로그램이다. 서블릿이 없다면, 서버 TCP/IP 연결대기, HTTP 요청 메시지와 바디 내용 파싱, 비즈니스 로직 실행, HTTP 응답 메시지 생성, TCP/IP 응답 전달 및 소켓 종료 작업을 우리가 직접 구현해야 한다.
그리고 이 서블릿은 서블릿 컨테이너에서 관리한다. 서블릿 컨테이너는 서블릿 객체들의 서블릿 객체의 생명주기(생성/초기화/호출/종료)를 관리해주며 싱글톤 & 멀티스레드 방식으로 동작한다. (싱글스레드 아니다 용어 헷갈림 주의..)
그리고 이 서블릿 객체를 실행시키는 것이 바로 스레드이고, 스레드는 스레드풀에 존재한다. 미리 스레드를 생성해놓고 재사용 하는 방식을 고려하게 되는데, 이를 스레드 풀에서 꺼내서 사용한다.
그래서 WAS, 즉 톰캣이 하는 게 뭔데?
spring boot의 경우 내장 WAS(기본: tomcat)을 사용하여 이러한 연동을 자동으로 처리한다. 그렇기 때문에, spring boot를 통해 서버를 띄우면 WAS위에 spring이 올라가서 구동된다고 표현한다.
만약 jar파일이 아닌 war파일을 사용하여 배포한다면, WAS서버를 따로 구성하고 spring의 web.xml 파일을 통해 servlet에 대한 정보를 제공한다. WAS는 이를 통해 servlet에 대한 정보를 얻어 servletcontainer가 관리하게 둔다.
외장 tomcat을 이용해 볼륨 마운트
외장 톰캣을 이용해 배포하려고 시도해봤다면 아래 단계는 이해하고 왔을 것이다. 간단하게 살펴보자면,,
우선 tomcat의 /usr/local/tomcat/webapps/ 폴더 아래 ROOT.war 파일을 넣게 되면 톰캣이 자동으로 이를 압축 해제시켜 최상위 디렉토리에 (/) 할당해준다. 아래와 같이 압축 해제된 ROOT 파일 아래에는 기본적으로 META-INF, WEB-INF, org 를 비롯한 파일들이 존재해야 한다.
따라서 우리가 빌드한 war 파일을 /usr/local/tomcat/webapps/ 아래로 COPY해주면 된다. 그리고 마지막으로 CMD 를 이용해 tomcat 내에서 서버를 실행해주자.
FROM tomcat:10.1.17
RUN apt-get update
RUN apt-get install -y tzdata
ARG WAR_FILE=build/libs/koala.war
COPY ${WAR_FILE} /usr/local/tomcat/webapps/ROOT.war
ENV TZ=Asia/Seoul
CMD ["catalina.sh", "run"]
이상하다.. 이번엔 War를 잘 실행 할 수 있도록 다 연결하였는데 에러 없이 실행이 안된다...
파일도 잘 들어가 있고,, ROOT.war 압축도 잘 해제되었는데 404..... 뇌절
Tomcat 버전 이슈
해당 글을 통해 힌트를 찾을 수 있었다.. 바로 Springboot 3 버전에서는 Tomcat 8버전이 아닌 Tomcat 10.1.7 버전이 필요하다.
따라서 다음과 같이 tomcat 버전을 올려주었다.
FROM tomcat:10.1.17
RUN apt-get update
RUN apt-get install -y tzdata
ARG WAR_FILE=build/libs/koala.war
COPY ${WAR_FILE} /usr/local/tomcat/webapps/ROOT.war
ENV TZ=Asia/Seoul
CMD ["catalina.sh", "run"]
그런데 또 맞이한 에러.. 아니 빌드 파일이 없댄다.
COPY failed: file not found in build context or excluded b .dockerignore: stat build/libs/*.war : file does not exist
./gradlew clean build를 프로젝트에서 실행하고 그걸 가져가야 하는데..??
위 Dockerfile에서 RUN ls 명령어를 곳곳에 넣어 확인하였다. 그런데 우리 프로젝트의 폴더명들은 전혀 나오지 않았다. 당연하겠지? 나는 지금 tomcat 이미지 안에 있는 거니까..
따라서 RUN ./gradlew clean build 를 실행해도 ./gradlew not found 오류를 볼 수 있었다.
아래와 같은 명령어들을 추가해 한 번 확인해보자.
FROM tomcat:10.1.17
RUN ls
RUN ./gradlew clean build
RUN apt-get update
RUN apt-get install -y tzdata
RUN ls
ARG WAR_FILE=build/libs/koala.war
COPY ${WAR_FILE} /usr/local/tomcat/webapps/ROOT.war
ENV TZ=Asia/Seoul
CMD ["catalina.sh", "run"]
드디어 결론이다..!
gradle builder 를 통해 내 프로젝트를 빌드할 수 있었다.
Dockerfile에서 WORKDIR 명령문으로 gradle:latest 이미지의 /app 디렉토리로 들어간다.
이후 호스트(현재 내 프로젝트)에서 이미지로 파일을 모두 복사한 후 war 파일을 만들고,
그 후에 COPY 명령어를 통해 gradle:latest 이미지에서 tomcat:10.1.17 이미지로 war 압축파일을 옮긴다.
# Gradle 빌드 단계
FROM gradle:latest as builder
WORKDIR /app
COPY . /app
RUN gradle clean build -x test
# Tomcat 이미지에 WAR 파일 복사
FROM tomcat:10.1.17
RUN apt-get update \
&& apt-get install -y tzdata
ENV TZ=Asia/Seoul
COPY --from=builder /app/build/libs/koala.war /usr/local/tomcat/webapps/ROOT.war
CMD ["catalina.sh", "run"]
이렇게 두 단계를 거쳐 gradle build -> 외장 톰캣 deploy 하면 해결된다 !
만약 현재 dockerfile과 배포로 헤매고 있다면.. container에 대한 이해와 was, ws에 대한 이해를 기반으로 접근하면 오류가 생기더라도 꼭 해결할 수 있을 거다 😉
'BackEnd > JAVA\SPRING' 카테고리의 다른 글
[SpringSecurity] Koala 프로젝트 간단한 인증처리 (0) | 2024.02.13 |
---|---|
[Java|Spring] Koala 프로젝트 구현 과정 중 이슈 정리 (0) | 2024.02.01 |
[SPRING] 서비스 추상화 (0) | 2023.10.19 |
[SPRING] AOP (0) | 2023.10.16 |
[SPRING] enum 타입, 역할과 책임의 분리 (0) | 2023.10.02 |