Group Study (2022-2023)/Spring 입문

6주차 레퍼런스

민휘 2023. 1. 12. 18:57

추가 공부 참여율이 떨어지는 듯하여, 6주차에 추가 공부 없이 레퍼런스 문서로 질문과 답을 동시에 드리는 방식으로 변경했습니다.

이번 주는 웹 애플리케이션이 어떻게 동작하는지, 어떻게 배포하고 관리하는지 알아보겠습니다. 더 나아가 이러한 배포 프로세스를 자동화하는 CI/CD에 대해 알아보겠습니다. 우리가 개발한 서비스 코드가 웹 애플리케이션의 전체 프로세스에서 어느 지점에 속하는지, 서비스 코드가 동작하기 위해 어떤 자원이 필요한지 파악해보시면 좋겠습니다🤗

자바 웹 애플리케이션 배포

마지막 실습에서는 우리가 그동안 열심히 작성한 웹 서버 애플리케이션을 배포하는 작업을 했습니다. 스크립트 코드를 읽어보면, jar 파일을 생성하고 nohup 명령어로 실행하는 것을 보셨을거에요. 이제 jar 파일이 무엇인지, 자바 웹 프로젝트는 어떻게 배포하는 것인지 알아보겠습니다.

jar로 웹 애플리케이션 배포하기

jar는 자바 프로젝트의 압축 파일 포맷입니다. 자바 클래스 파일과 리소스 및 메타 데이터를 모아서 배포하기 위해 사용됩니다. 설명이 다소 추상적인데요, 이후 빌드를 설명할 때 자세히 얘기하겠지만 그냥 실행 파일이라고 생각하시면 쉽습니다.

실습에서는 자바 프로젝트를 인스턴스로 클론해서 ./gradlew build 명령어로 직접 jar 파일을 만들었는데요. 스프링 부트 프로젝트의 배포는 jar 파일만 있으면 가능하기 때문에, intellij에 있는 로컬 프로젝트로 jar 파일을 생성해 인스턴스에 직접 업로드하여 실행해도 배포가 가능합니다. (배포 자동화를 사용하지 않은 원시적인 방법) 특히 스프링 부트는 내장 서버를 가지고 있으므로, 바로 실행 가능한 jar 파일을 만들 수 있습니다. intellij에서 gradle 도구를 사용하면 클릭 몇번으로 간단하게 jar 파일을 만들 수 있습니다. 방법은 아래 링크를 참고하세요.

null

[Spring Boot] Gradle jar 빌드 및 배포하기

컴파일, 빌드, 배포

자바 프로젝트를 서버에 반영하려면, 컴파일 -> 빌드 -> 배포 과정을 수행해야 합니다. 하나씩 알아볼까요?

✅컴파일

소스 코드를 기계가 읽을 수 있는 언어로 번역하는 일을 뜻합니다. 코드를 바로 기계어로 번역하는 C와 달리, Java는 바이너리 코드로 번역됩니다. JVM이 바이너리 코드를 해석해 실행하므로 Java는 OS에 종속적이지 않다는 점, 기억 나시나요? 자바가 성공적인 프로그래밍 언어로 빠르게 자리잡을 수 있게 한 중요한 요소 중 하나입니다.

✅빌드

빌드는 소스 코드를 실행 가능한 독립적인 소프트웨어로 변환하는 과정을 말합니다. 크게 보자면, 앞에서 설명한 컴파일도 빌드에 포함됩니다.

우선 .java 파일을 컴파일해서 .class 파일을 생성합니다(컴파일). 클래스 파일들끼리 연결하고, 리소스는 클래스가 참조할 수 있는 적절한 위치로 옮기고, 메타 데이터 파일을 모아 묶어주는 과정을 거칩니다. 복잡한 과정을 거쳐 빌드가 최종적으로 반환하는 산출물이 바로 JAR, WAR와 같은 실행 파일입니다.

이 빌드 과정을 도와주는 도구로 Gradle과 Maven이 있습니다. 이번 스터디의 실습에서는 Gradle을 사용했습니다. 저희가 스프링 프로젝트를 생성할 때 Gradle을 선택했던 것 기억하시나요? 외부 라이브러리가 필요할 때마다 build.gradle에 의존성을 추가해 새로 빌드했었습니다. build.gradle은 프로젝트 빌드 정보를 개발자가 직접 작성할 수 있는 스크립트인데요, gradle이 이 스크립트 내용을 바탕으로 빌드해줍니다.

결국 알아야 할 것은 빌드의 산출물이 jar 파일이고, 복잡한 빌드 과정을 도와주는 도구가 Gradle이라는 것입니다.

✅배포

빌드된 실행 파일(jar 혹은 war)을 사용자가 접근할 수 있는 환경(운영 서버)에 배치하는 일을 말합니다. 우리는 운영 서버를 AWS EC2 서비스를 사용해 인스턴스로 구축하고, 코드를 클론해 jar 파일을 인스턴스에 생성했습니다. 인스턴스에서 jar를 실행해서, 스프링 부트 프로그램의 프로세스가 인스턴스의 8080번 포트에서 실행되도록 만들었습니다. 사용자는 인스턴스에 접속해 우리가 만든 기능을 사용할 수 있게 됩니다. 이 작업을 배포라고 칭합니다.

Java의 빌드와 배포

Compile, Build, Deploy 개념과 차이

JAR와 WAR

이제 배포가 어떤 작업인지 이해할 수 있게 되었습니다! 👏👏

그런데 jar는 알겠는데, war는 또 무엇일까요? jar와 마찬가지로 빌드의 산출물인 것 같은데, jar와 어떤 차이점이 있길래 war가 존재하는지 알아보겠습니다.

null

JAR와 WAR는 빌드의 결과물로, 소스 코드와 자원을 압축한 패키징 파일입니다. 이때 JAR와 WAR는 사용되는 목적이 다르기 때문에 포함하고 있는 내용이 다릅니다. JAR는 일반적인 자바 프로그램이 동작할 수 있도록 Class와 같은 Java 리소스와 속성 파일을 포함하고 있습니다. 자바로 짠 구구단이나 계산기 같은 간단한 프로그램도 JAR로 만들어 간편하게 공유할 수 있어요. 반면 WAR는 웹 응용 프로그램을 위한 포맷이기 때문에 자바 코드(JAR, CLASS) 뿐만 아니라 JSP, SERVLET, XML, HTML, JAVASCRIPT 등의 웹 관련 자원을 포함하고 있습니다.

JAR 파일은 원하는 구조로 구성이 가능하며 JRE(Java Runtime Environment)만 가지고도 실행이 가능합니다. 반면 WAR은 WEB-INF 및 META-INF 디렉토리로 사전 정의된 구조를 사용하며 WAR 파일을 실행하려면 Tomcat 등의 웹 서버 또는 웹 컨테이너(WAS)가 필요합니다. 이는 일반적인 자바 프로그램과 구분되는 웹 응용 프로그램의 특성 때문입니다.

마지막 문장에서 톰캣, WAS 등 익숙하지 않은 단어가 등장했는데요. 다음 챕터인 <웹 애플리케이션이 동작하는 방법>에서 더 자세히 살펴보도록 하겠습니다😺

Spring Boot의 Executable JAR

잠깐! JAR는 일반적인 자바 프로그램을 위한 배포 파일이고 WAR가 웹 응용 프로그램을 위한 포맷이라면, 우리는 어떻게 JAR 파일을 올리고 실행한 것만으로 웹 애플리케이션을 배포한 것일까요? 그 이유는 바로 스프링 부트가 제공하는 Executable JAR에 있습니다. (앞에서 스프링 부트는 내장 서버를 가지고 있다고 한 것 기억하시나요? 바로 그 얘기입니다)

스프링 부트가 나오기 이전의 스프링은 WAR 파일을 생성한 후 외장 WAS에 배포하여 웹 애플리케이션을 구동했습니다. 이 방법은 아파치 톰캣(웹 애플리케이션을 실행하는데 필요한 컨테이너)을 따로 설치하고 애플리케이션을 올려야 하므로, 저희처럼 규모가 작은 프로젝트는 리소스 낭비입니다. 또 WAR 파일은 파일 하나에 서비스를 위한 모든 자원이 포함되어 있으므로 무겁고, 파일의 추가나 변경이 일어날 경우 새로 컴파일해야 합니다.

스프링 부트의 목표 중 하나가 이러한 스프링의 배포를 효율적으로 변경하여 Stand alone application 형태를 만드는 것었습니다. 그래서 등장한 것이 바로 Executable(Runnable) JAR입니다. 이 파일에는 내장 톰캣이 존재하므로, JAR 파일을 빌드하고 단순히 실행하는 것만으로 웹 애플리케이션을 구동할 수 있습니다.

내장 톰캣을 가진 JAR로 배포하는 것은 여러모로 개발자에게 도움이 됩니다. 우선 외장 톰캣을 설치해서 설정하고 sw 설치하는 등의 작업 없이 JAR 파일만 있으면 JRE만 있다면 어느 환경에서든 실행이 가능합니다. 개발자는 서버 구축과 배포에 신경을 덜 써도 되니 애플리케이션 개발에만 집중할 수 있게 됩니다. 또, 실행의 간편함 덕분에 JAR 파일은 도커 이미지로 만들어 배포하는 경우에 유리합니다. (JDK 이미지를 다운받고 jar 파일만 추가하면 됩니다!) 따라서 runnable jar 형태는 마이크로 서비스로 아키텍처를 구성할 때 적합하다는 장점이 있어 많이 사용됩니다.

OKKY - All That Developer

[삽질로그] Spring boot에서 실행 가능한(Executable) JAR 만들기

Docker - jar파일을 실행하는 도커이미지 만들기(Dockerfile 이용)

웹 애플리케이션이 동작하는 방법

이번 챕터는 웹 애플리케이션이 동작하는 방법에 대해 알아보겠습니다. 웹 서버, WAS, 아파치 톰캣, 서블릿 등에 대해 다룹니다.(이미 알고 계신 분들은 넘기셔도 좋습니다! 양이 좀 많아서리..)

웹 애플리케이션의 특징

구구단 출력 프로그램과 웹 애플리케이션의 가장 큰 차이점은 뭘까요? 바로 인터넷으로 통신을 한다는 점입니다. 내가 짠 구구단 출력 프로그램은 엔트리 포인트(main 메소드)에 넣고 직접 실행 버튼을 클릭하면 원하는 결과를 얻을 수 있죠. 여기서는 네트워크를 이용한 통신을 할 필요 없이 자신의 컴퓨터 안에서 모든 작업이 이루어집니다.

하지만 웹 애플리케이션은 다릅니다. 클라이언트와 서버 구조로 구성해야 하고, 이 둘은 HTTP 프로토콜을 이용해 통신합니다. 서버는 클라이언트의 요청을 받고 내부에서 적절한 처리를 하여 응답할 수 있어야 합니다. 여기서 적절한 처리를 담당하는 것이 저희가 지금까지 개발한 스프링 프로젝트입니다. 저희가 작성한 코드에서 HTTP 요청이나 응답을 직접 다루는 코드, 기억 나시나요?

안 나시는게 정상입니다😝 저희가 작성한 Controller를 보면 메소드마다 담당하는 요청이 있습니다. 클라이언트의 HTTP 요청 URI가 메소드에서 설정해놓은 것과 같으면 해당 메소드가 실행되고, return으로 응답 객체를 지정합니다. 이 코드는 스프링 MVC 패턴에 의해 엄청나게 추상화되어 개발자가 편하게 사용 가능하도록 만들어놓았습니다. 그래서 저희가 개발할 때는 손쉽게 요청이나 응답을 다룰 수 있었습니다. 하지만 이 코드가 실제로 HTTP 요청을 받아서 클라이언트에게 응답하는 작업을 하지는 않습니다.

그렇다면 실제로 HTTP 요청을 받아서 관련 리소스를 할당하고, 우리가 작성해놓은 프로그램을 실행해서 응답을 반환하는 작업은 누가 해주는 것일까요? 웹 애플리케이션의 동작 방법을 살펴보면서 알아보도록 하겠습니다.

웹 애플리케이션의 동작 방법

null

우선 큰 그림부터 볼게요. 클라이언트와 서버 구조가 보이시나요? 클라이언트는 웹 브라우저에 프레젠테이션 파일(html 등)을 사용자에게 보여주며, 사용자의 요청을 서버에게 전달하는 인터페이스 역할을 합니다. 그리고 Web Server, WAS, DB가 HTTP 요청을 처리할 수 있는 서버 단에 위치합니다. DB는 직관적으로 우리가 작성한 처리 로직에서 데이터를 관리하는 서버라는 것을 알 수 있을 것 같아요. 그렇다면 Web Server와 WAS는 무엇일까요? 우리가 작성한 스프링 프로젝트는 어디에 배포해야 할까요?

Web Server와 WAS의 개념적 구분

구글링을 해보면 가장 많이 보이는 정보는 이것입니다. Web Server는 정적 컨텐츠, WAS는 동적 컨텐츠를 제공한다는 것!

  • Web Server는 클라이언트로부터 HTTP 요청을 받고 응답한다. 정적 컨텐츠(HTML, CSS, JS, 이미지, 영상 등 별도의 처리 없이 바로 내려주는 자원)을 요청한 경우 해당 파일을 반환한다. 동적 컨텐츠(비즈니스 로직이나 DB 등 외부 서버 조회가 필요)를 요청한 경우 필요한 정보를 포함해 WAS에 요청을 토스한다.
  • WAS는 동적 컨텐츠를 반환(유저마다 다른 정보를 보여주는 페이지 등)한다. 특히 애플리케이션 로직을 수행한다. 웹 서버는 요청을 받아 응답하는 기능밖에 없으므로 복잡한 연산은 WAS가 한다.

현재 서버를 운영하는 아키텍처를 보면 틀린 말은 아닙니다. 하지만 이 설명으로는 이해하기 어려운 개념이 있습니다.

실제 운영 환경에서 WAS는 사실 Web Server와 Web Container로 구성되며 여기서 WAS는 아파치 톰캣이고 WAS 앞에 NGinx나 아파치 웹 서버를 따로 두어 부하를 줄인다고 합니다. 이 설명.. 이해가 가시나요? 저는 이 설명이 너무나도 당황스러웠는데요. 아니 WAS 앞에 웹 서버를 둔다고 했는데 왜 또 WAS 안에 웹 서버가 있지.. 둘이 뭔가 다른건가? (하지만 다들 그러려니 넘어가길래 저도 오랫동안 의문을 묵혀왔답니다)

null
null

웹 서버와 WAS의 차이는 아직 개발자들 사이에서 의견이 분분한데요. 구글링을 하다가 좋은 링크를 발견해서 공유합니다. 글의 내용을 풀어보자면 다음과 같습니다.

사실 웹 서버와 WAS는 그 시작점이 다릅니다. 웹 서버는 HTTP 프로토콜 규약을 사용하는 서버(TCP 연결 수립 및 소켓 생성)였고, 앱 서버(WAS)는 폐쇄적인 사용자 그룹을 위해 폐쇄적인 프로토콜을 사용하는 서버였습니다. 웹 초기에는 정적 컨텐츠를 제공하는 것으로도 사용자들의 요구사항을 만족시켰으므로 웹 서버만으로 운영이 가능했습니다. 시간이 지나 웹으로 제공하는 서비스가 많아지면서 애플리케이션 로직도 돌리고 싶고 DB에 데이터도 저장하고 싶어졌습니다. 하지만 웹 서버는 HTTP 요청을 하면 단순히 파일을 찾아 응답하는 일밖에 하지 못합니다. 그래서 WAS에 웹 서버를 도입해 HTTP 프로토콜을 사용해 더 복잡한 작업을 할 수 있는 서버를 만든 것입니다. 실제 WAS로 사용하는 제품인 Apache Tomcat을 뜯어보면 HTTP 요청을 받고 응답하는 웹 서버와 애플리케이션 로직을 실행할 수 있는 Web Container로 구성됩니다.

결론적으로 말하고 싶은 것은, Web Server와 WAS는 단순히 제공하는 컨텐츠의 종류에 따라 다른 것이 아니고! HTTP 프로토콜을 사용하는 부분과 애플리케이션 로직을 돌리는 부분 중 어디에 더 방점을 둘 것이냐에 따라 구분할 수 있다는 것입니다.

Web Server와 애플리케이션의 데이터 교환

HTTP 요청은 누가 받고, 애플리케이션 로직은 어디에서 돌리는지 알았으니 이번에는 이 둘 사이를 중개해주는 미들웨어에 대해서 알아보겠습니다.

HTTP 프로토콜 기반의 웹 서버와 다양한 언어로 구현된 프로그램이 데이터를 주고 받는 인터페이스를 CGI라고 합니다. 이 규약에 따라 C, C++, Perl, Visual Basic, shell script, Python 등의 언어로 이 인터페이스를 사용할 프로그램을 개발자가 만들었습니다. 하지만 CGI는 Java를 지원하지 않았으며, 모든 요청마다 스레드를 생성해 CGI 구현체 생성하는 등 성능 이슈가 있었습니다. 그래서 등장한 것이 Servlet입니다.

Servlet은 Java를 사용해 웹 페이지를 동적으로 생성하는 서버 측 인터페이스입니다. 그렇다면 Servlet 구현체가 저희가 개발한 서버 측 프로그램이 되겠죠? 우리는 몰랐지만 서블릿 프로그래밍을 하고 있었던 것입니다. 스프링 부트에서는 DispatchServlet이라는 이름으로 서블릿 매핑 구현체를 지원하고 있습니다. 저희가 서블릿을 의식적으로 개발하지 않아도 스프링 부트가 알아서 추상화해주기 때문에 개발자는 편하게 개발할 수 있습니다.

그렇다고 서블릿을 몰라도 되는 것은 아닙니다. 서블릿은 자바 웹 개발에서 모르면 정말 큰일 나는 개념입니다. 혹시 JSP/Servlet 프로그래밍을 들어보신 적이 있나요? 스프링 이전에 자바 웹 개발에서 주로 사용되던 기술이며 스프링의 뿌리가 되었는데요. 이 서블릿 구현체 클래스를 하나 하나 xml로 직접 설정해주어야 합니다. 번거로운 작업이기 때문에 스프링이 DispatcherServlet이라는 프론트 컨트롤러를 두어서, 어떤 요청에 대해 어떤 서블릿을 호출할 지 지정하는 작업을 추상화한 것입니다.

잠시 서블릿 프로그래밍의 코드를 빌려와 서블릿에 대해 이해해보도록 하겠습니다.

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        // super.doGet(req, resp);
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter pw = resp.getWriter();
        pw.println("<html>");
        pw.println("<title>");
        pw.println("제목입니다.");
        pw.println("</title>");
        pw.println("<body>");
        pw.println("Hello World kk <br> Welcome to hell");     
        pw.println("</body>");
        pw.println("</html>");

    }
}
<servlet>
  <servlet-name>Hello</servlet-name> //servlet 이름 명시
  <servlet-class>servlet.HelloServlet</servlet-class> //패키지.클래스 이름을 등록 해줌
</servlet>

<servlet-mapping>
  <servlet-name>Hello</servlet-name> //servlet의 name을 매핑
  <url-pattern>/hello</url-pattern>  // / 반드시 필요!! (주소 뒤에 값이라서)
</servlet-mapping>

HttpServlet을 상속하여 HelloServlet을 만들었습니다. 이 서블릿을 실행하면 get 요청에 대해 html 파일을 응답합니다. 아래 xml은 HelloServlet 구현체를 등록하는 설정인데요, /hello 로 들어오는 요청에 대해서는 HelloServlet이 로직 처리를 담당합니다. 이러한 서블릿 구현체들이 모여서 하나의 웹 애플리케이션을 구성하고 있습니다.

그렇다면 이렇게 많은 Servlet을 생성하고 관리해주는 작업은 누가 할까요? 바로 Servlet Container입니다. (후하 여기까지 끌고 오기 위해 정말 많은 개념이 등장했네요 이제 거의 막바지!!)

null

Servlet 구현체들은 요청이 새로 들어올 때마다 생성될 필요가 전혀 없습니다. 서버가 뜰 때 딱 한번 서블릿 구현체들을 생성해 초기화하고, 요청이 들어올 때마다 이 서블릿 구현체를 실행하면 됩니다. 그래서 서블릿의 생명 주기는 생성 및 초기화(init), 구현체의 메소드 실행(service), 메소드 실행 후 리소스 반납(destroy)로 구성되고, 서블릿 컨테이너는 이러한 생명 주기를 관리해주는 역할을 합니다. 또 HTTP 요청을 받으면 WAS가 해당 요청을 처리할 스레드를 스레드 풀에서 꺼내 서블릿 구현체의 service()를 호출하도록 컨테이너에 요청합니다.

이렇게 서블릿을 관리해주는 서블릿 컨테이너가 우리가 처음 WAS의 내부 구조에서 보았던 Web Container입니다. (사진 참고) 그리고 이 서블릿 컨테이너를 제공하는 실제 제품이 바로 아파치 재단에서 제공하는 TomCat입니다. (드디어 톰캣 등장😎) 제가 앞에서 executable JAR가 내장 톰캣을 가지고 있어서 따로 웹서버를 설치하고 세팅할 필요 없이 배포할 수 있다고 말씀 드렸는데요. 톰캣은 서블릿 컨테이너를 제공하는 WAS입니다. 그래서 톰캣이 있어야만 저희가 개발했던 서블릿 구현체를 서버에 올리고 실행할 수 있었던 것입니다.

null

정리를 해보자면, Web Server는 HTTP 요청을 받기 위해 TCP 연결을 하고 소켓을 생성합니다. WAS는 해당 요청을 처리할 스레드를 풀에서 꺼냅니다. 스레드가 해당 요청을 처리해줄 서블릿을 Web Container에 요청해 실행하고, 그 결과를 Web Server에게 반환합니다. 이 흐름에서 저희가 작성한 코드는 서블릿 구현체가 되는 것입니다. 정리가 좀 되시나요?

사실 웹 애플리케이션의 동작 구조는 워낙 옛날부터 기술이 축적되면서 등장하는 기술들이 많아서, 처음 보시는 분들은 이게 도대체 뭔가!! 왜 이해가 안 가는것인가!! 이걸 왜 알아야하나!! 하실텐데요. 백엔드 프레임워크는 이런 복잡한 절차들을 모두 추상화해서 개발자가 애플리케이션 코드에만 집중할 수 있게 만든 아주 친절하고 고마운 기술입니다. 그래서 스프링 같은 프레임워크로 처음 웹 개발에 입문하신 분들은 자기가 작성한 코드가 어떻게 동작하는지 모르고 개발하시는 경우가 많습니다. (저도 이걸 안지 얼마 안됐어요) 하지만 당장 스프링 시큐리티만 봐도 내부 동작 방식을 이해하려면 서블릿이 무엇인지 알아야 한답니다. 스프링의 사용법을 배우는 것이 아니라, 스프링을 활용해 웹 개발을 하고 싶다면 웹 애플리케이션 개발 기술의 연혁을 살펴보면서 어떻게 기술이 추상화되는지 짚어보시는 것도 좋다고 생각합니다. 개발하다보면 언제가는 이런 핵심 개념들을 마주치게 되어있거든요!

CGI, Servlet, Servlet Container?

[JSP] 서블릿(Servlet)이란?

웹 서버, WAS, 웹 컨테이너

서블릿 컨테이너, 스프링부트 동작 과정

WAS 앞에 붙이는 Web Server의 역할

사실 위에서 마무리하고 싶었는데요.. 아파치랑 엔진엑스 얘기를 안하면 아쉬우니까 덧붙이도록 하겠습니다. 초장에서 WAS 앞에 Web Server가 따로 있던 그림 기억하시나요? 제가 그 그림을 보면서 WAS에 어차피 웹 서버가 있는데 뭐하러 웹 서버를 따로 붙이는지 의아했다고 말씀 드렸는데요. WAS 앞에 웹 서버를 따로 붙이는 이유를 살펴보겠습니다.

  1. 책임 분할을 통한 서버 부하 방지
    정적 컨텐츠는 Web Server, 동적 컨텐츠는 WAS가 담당하여 WAS의 부담을 줄일 수 있습니다. (근데 실험 결과에 의하면 요즘 톰캣 기능이 좋아져서 딱히 성능 차이는 없다네요)
  2. 여러 대의 WAS를 묶어 로드 밸런싱하고 헬스 체크
    WAS는 프로젝트 구성에 따라서 여러개를 두고 운영할 수 있습니다. 여러 대의 WAS 앞에 웹 서버를 두어 클라이언트로부터 오는 요청을 받고, 여러 WAS로 요청을 분산해주면 됩니다.
  3. 실제 서버를 외부에 노출하지 않기
    WAS는 애플리케이션 로직이나 DB 로직을 포함하고 있기 때문에 클라이언트가 직접 WAS로 접근하는 것은 보안적으로 좋지 않습니다. WAS 앞에 웹 서버를 두면 실제 서버를 외부로부터 숨길 수 있습니다.

이렇게 웹 서버를 WAS 앞에 두어 숨기는 것을 두고 리버스 프록시 서버라고 부릅니다. 이 내용은 CD 부분에서 다루도록 할게요!

무중단 배포란? 무중단 배포를 위한 환경 이해하기

CI/CD - 지속적인 통합과 배포

앞에서 이야기 했듯이 스프링 부트의 배포는 jar를 생성해 인스턴스 업로드하고 실행하면 끝입니다.하지만 이 방법에는 여러 단점이 있기 때문에 (jar 업로드하는데 시간 오래 걸리고 개발자가 귀찮음), 배포를 자동화해서 서버를 운영합니다. 이번 실습에서는 Shell Script를 기반으로 배포를 자동화해보았는데요. 교재에 나와있듯 이 방식은 수동으로 Test & Build를 해야하는데, 사소한 변경 사항이 생기더라도 즉시 실서버에 반영하려면 인스턴스에 접속해서 테스트하고 빌드하는 스크립트를 실행해야 합니다.

실제로 페이스북처럼 규모가 큰 IT 회사에서는 하루에 1000번에 달하는 배포가 이루어진다고 하는데요, 이때 모든 배포를 개발자가 직접 스크립트를 실행해야만 하는 비효율적인 방식으로 이루어지지 않는다는 것은 쉽게 유추할 수 있습니다. 그렇다면 배포를 자동화하기 위해 무엇을 도입해야 할까요? 바로 CI/CD 입니다. (CI/CD는 교재 9장, 10장에서 실습해보실 수 있습니다. 관심 있으신 분들은 토이 프로젝트 때 진행해보세요!)

CI는 코드 버전 관리를 하는 VCS 시스템(Git)에 Push가 발생하면 자동으로 테스트와 빌드가 수행되어 배포 파일을 만드는 과정을 뜻합니다. CD는 CI로 만들어진 빌드 결과를 자동으로 운영 서버에 중단 없이 배포하여 사용자에게 서비스하는 것을 칭합니다. CI/CD를 나누어 놓았지만, 사실 둘은 함께 구축되는 경우가 대부분입니다. 기능 배포를 자동화하는 목적은 개발한 기능을 빠르게 유저에게 제공하여 더 나은 서비스를 하는 것이 목적이기 때문입니다.

교재 9장에서 구축한 CI 프로세스입니다. 애플리케이션이 동작하는 EC2 앞에 Travis CI, Code Deploy, S3를 붙여 jar을 생성하여 보관하고, 배포 요청을 날리는 일련의 과정을 만들었습니다.

null

  1. 코드의 변경 사항을 깃허브 특정 브랜치에 푸시한다.
  2. Travis CI가 이를 감지하여 변경된 내용으로 Jar 파일을 다시 빌드하고, s3 버킷에 빌드 파일을 업로드한다.
    • Travis가 빌드한 Jar를 S3에 올리기 설정 : travis.yml
    • CodeDeploy는 Jar 파일을 인식하지 못하므로 Jar+기타 설정 파일을 모아 zip 파일로 업로드하도록 설정했다.
  3. Travis CI가 빌드 결과물을 업로드한 후 Code Deploy에 배포 요청을 보낸다.
    • Travis CI의 홈 서버가 요청을 날려준다. Travis CI는 travis.yml의 내용을 보고 Code Deploy 서비스에 요청을 날린다.
  4. Code Deploy는 s3로부터 zip를 받아서 EC2에 배포한다.
    • EC2 인스턴스에 Code Deploy 에이전트를 설치해서 EC2가 Code Deploy의 요청을 받을 수 있게 했다.
    • EC2 인스턴스는 Code Deploy로부터 전달받은 파일 전체를 지정된 경로에 옮겨서 jar 파일을 실행한다. 설정 : appspec.yml
    • 이후 jar 파일을 실행할 때 처리하는 일련의 명령어들은 deploy.sh에 명시해둔다.
  5. EC2는 프로젝트를 운영하며 서버 주소로 오는 요청을 받아서 처리한다.

교재 10장에서 구축한 CD도 간단하게 살펴보겠습니다. CI까지 구축한 방식으로는 배포를 진행할 때마다 기존 서비스를 할 수 없기 때문에 서비스 중단이 발생합니다. 서비스 중단 없이 배포를 진행하는 무중단 배포 구축 과정을 CD라고 합니다.

클라이언트는 Nginx라는 웹서버로 요청을 받게 합니다. EC2 인스턴스 안의 여러 포트에 스프링 부트 애플리케이션을 실행할 수 있도록 설정을 해둡니다. Nginx는 현재 서비스하는 포트 하나를 바라보고 있습니다.

null

  1. 현재 Nginx는 서비스 포트인 8081을 바라보고 있음. 8082번 포트에 새로 빌드한 파일을 배포하려고 한다.
  2. 8082번에 파일을 배포하는 동안에도 서비스는 중단되지 않는다. 엔진엑스는 8081번을 바라보고 있기 때문이다.
  3. 배포가 끝나고 정상적으로 8082번의 프로세스가 구동 중인지 확인한다.
  4. 8082 포트에서 애플리케이션이 정상 구동 중이면 nginx reload 명령어를 통해 8081 대신 8082를 바라보도록 한다.
  5. nginx reload는 0.1초 이내에 완료된다. 이후 요청부터는 엔진엑스가 8082번으로 요청을 전달한다.

CI/CD 파이프라인을 현업에서 어떻게 구성하는지는 아래 영상을 참고하세요.

https://www.youtube.com/watch?v=0Emq5FypiMM

CI/CD 파이프라인을 구축하기 위해 다양한 툴을 사용하는데요. Jenkins, Github Action 등 자기가 개발하고 있는 애플리케이션의 특성과 사용할 수 있는 자원을 확인해보고 적절한 툴을 선택하는 것이 좋습니다. CI/CD 파이프라인을 구축하는 일을 포함해, 애플리케이션과 서비스를 빠른 속도로 제공할 수 있도록 조직의 역량을 향상시키는 문화 철학을 DevOps라고 하는데요. 왜 우리가 DevOps를 알아야 하는지, DevOps를 실천할 수 있는 방법은 무엇인지 아래 영상을 통해 감을 잡아보세요.

https://www.youtube.com/watch?v=MwveZf5tOuw&list=PLpkj8RKr48wZMPKR292FOoahqxVDi6d6R&index=25

레퍼런스 분량이 상당히 많았는데요, 저의 설명이 부족하거나 잘 이해가 되지 않으실 수 있으니! 키워드 정도만 뽑아두었다가 나중에 스스로 구글링하면서 찾아보시면 좋습니다. 수고 많으셨어요💕

'Group Study (2022-2023) > Spring 입문' 카테고리의 다른 글

5주차 레퍼런스  (0) 2023.01.12
4주차 레퍼런스  (0) 2023.01.12
3주차 레퍼런스  (0) 2023.01.12
2주차 레퍼런스  (0) 2023.01.12
1주차 레퍼런스  (0) 2023.01.12