Skip to content

Commit

Permalink
Merge branch 'Master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
yooonwodyd authored Nov 15, 2024
2 parents dbcb183 + 77ef08a commit 9a2f2db
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 28 deletions.
71 changes: 55 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,66 @@
1. 검색 - 심규민
2. 로그인 및 회원 가입 + 작가 + 팔로우 - 윤재용
3. 채팅 + 파일 - 박한솔
4. 상품(피드), 좋아요, 감상평 - 주보경
4. 상품(피드), 좋아요, 감상평 - 주보경
5. 인프라(ci/cd, https, 모니터링) - 김동현

## 진행상황
1. ERD 설계에 따라 Entity 구현. 세부 구현사항이 남아있음
2. RULE.md에 각종 프로젝트 규칙 정의
3. 매주 월요일 정기회의
4. 다음주까지 각자 1차 구현 후, 회의를 통해 step 3 조별 멘토링을 통해 추가 피드백을 통해 수정.
## CI/CD 및 인프라 구축
![CI/CD 과정 - GitHub Actions를 활용한 배포 프로세스](https://velog.velcdn.com/images/hyunn/post/986c6af9-b694-44c2-a554-dd51e091fde0/image.png)
### CI/CD 과정 - GitHub Actions를 활용한 배포 프로세스

1. **GitHub Actions 트리거**
- Master 브랜치에 코드가 푸시되면 GitHub Action이 자동으로 동작합니다.
2. **도커 이미지 생성 및 업로드**
- 프로젝트를 빌드하고, 도커 이미지를 생성한 후 도커 허브에 이미지를 업로드합니다.
3. **배포 스크립트 실행**
- EC2-1 서버에 접속하여 배포 스크립트(`deploy.sh`)를 실행합니다.
https://github.com/donghyuun/katecam-infra/blob/main/ec2-dual-zero-downtime-lb/deploy.sh

## 리뷰사항
연휴 이후 다음주 부터 본격적인 개발에 들어가려 합니다. 열심히하겠습니다! 감사합니다.
#### (deploy.sh 스크립트 동작)

1. **백엔드 서버의 포트 검사**
- EC2-1, EC2-2에서 현재 동작 중인 백엔드 서버(도커 컨테이너)가 사용 중인 포트(8080 또는 8081)를 검사합니다.
- 사용하지 않는 포트(8081 또는 8080)를 확인합니다.
2. **새 서버 컨테이너 배포**
- 도커 허브에서 최신 이미지를 PULL하여, EC2-1, EC2-2의 사용 가능한 포트에 새 백엔드 서버(도커 컨테이너)를 실행합니다.
3. **헬스 체크 수행**
- 새로운 서버의 정상 동작을 확인하기 위해 `/actuator/health` 엔드포인트로 헬스 체크를 수행합니다.
- 이 과정에서 DB 연결 상태도 함께 확인합니다.
4. **트래픽 분산 대상 변경 (Blue-Green 배포)**
- 헬스 체크가 성공하면 NGINX 설정을 업데이트하여 트래픽을 새 서버로 분산합니다.
- BLUE-GREEN 방식으로 무중단 배포를 수행합니다.
5. **NGINX 리로드**
- 변경된 NGINX 설정을 적용하기 위해 NGINX를 리로드합니다.
6. **기존 서버 종료 및 삭제**
- 기존에 EC2-1, EC2-2에서 실행 중이던 컨테이너를 종료하고 삭제하여 이전 서버를 정리합니다.

### 인프라 구성 - EC2 서버 구성 및 모니터링

# 5주차 github 코드리뷰 질문
(윤재용)
몇몇 컨트롤러에 대한 E2E 테스트를 작성하였습니다.
처음에는 @WithMockUser 를 사용해서 테스트를 진행하려고 했는데, Header를 검증하다보니 불가능했습니다.
저희의 요구사항이 특정 url이 아니라면 헤더에 토큰이 필요하다보니 사용이 불가능하였기에 JwtTestUtils 클래스를 통해 테스트 유저를 사용하였습니다.
#### EC2 - 1

이런 전체 테스트를 처음 구현하다 보니
현재 작성한 테스트가 E2E 테스트라고 불려도 될 지 잘 모르겠습니다..!
추가로 테스트코드의 개선점이 있을지 궁금합니다.
- **백엔드 서버1** (도커 컨테이너)
- **Nginx** - 80 포트로 들어오는 요청을 백엔드 서버 1, 2에 대해 로드밸런싱
- **MySQL** - 백엔드 서버 1, 2가 참조하는 DB
- **Prometheus** - 백엔드 서버 1, 2의 메트릭 수집
- **Grafana** - Prometheus가 수집한 메트릭 시각화 (URL: http://golden-ratio.duckdns.org:3000/) (admin/admin)
- **Dozzle** - EC2-1, EC2-2의 도커 컨테이너 로그 시각화 (URL: http://golden-ratio.duckdns.org:7070/)

#### EC2 - 2

- **백엔드 서버2** (도커 컨테이너)
- **Dozzle** - (Agent) EC2-2의 도커 컨테이너 로그를 EC2-1의 Dozzle에게 전달

#### 서버 1개일 때 vs 2개일 때(LB 적용) TPS 간단 비교 (Swagger GET 기준)

- TPS 테스트 결과: 두 개의 서버로 로드밸런싱 적용 시 성능이 향상됨

### EC2 인스턴스 생성 - Terraform 이용

1. **Terraform을 이용한 EC2 인스턴스 생성**
- Terraform 스크립트를 사용해 AWS에서 EC2 인스턴스를 생성하고, 관련 리소스(보안 그룹, VPC, 서브넷, 키 페어 등)를 설정하였습니다.

2. **생성 절차**
1. **AWS CLI**를 사용해 AWS 계정과 로컬 환경 연결 후 인증 설정
2. 테라폼 스크립트 작성 후 실행(ec2.tf)
https://github.com/donghyuun/terraform-study/blob/main/ec2.tf
- 리소스 설정이 완료된 EC2 인스턴스가 생성됩니다.
20 changes: 10 additions & 10 deletions src/main/java/com/helpmeCookies/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.helpmeCookies.global.config;

import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // 허용할 도메인 (모든 도메인 허용: "*")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE") // 허용할 HTTP 메서드
.allowedHeaders("*") // 허용할 헤더
.allowCredentials(true); // 인증 정보 허용 여부
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://1.618.s3-website.ap-northeast-2.amazonaws.com", "http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Authorization", "Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers","cross-control-allow-origin")
.allowCredentials(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.helpmeCookies.global.security;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;


import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
public class CorsLoggingFilter extends OncePerRequestFilter {

private static final Logger logger = LoggerFactory.getLogger(CorsLoggingFilter.class);

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

// 요청 헤더 로깅, cors
String origin = request.getHeader("Origin");
if (origin != null) {
logger.info("Request Origin Header: {}", origin);
}

String requestMethod = request.getHeader("cross-control-allow-origin");
if (requestMethod != null) {
logger.info("cross-control-allow-origin: {}", requestMethod);
}

// 필터 체인을 통해 요청 처리
filterChain.doFilter(request, response);

// cors 관련 응답 헤더 로깅
String responseHeader = response.getHeader("Access-Control-Allow-Origin");
if (responseHeader != null) {
logger.info("Response Access-Control-Allow-Origin Header: {}", responseHeader);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.helpmeCookies.global.security;

import static org.springframework.security.config.Customizer.*;

import java.util.Arrays;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand All @@ -12,6 +17,9 @@
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Controller;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import lombok.RequiredArgsConstructor;

Expand Down Expand Up @@ -65,9 +73,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
http.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.cors(AbstractHttpConfigurer::disable);
http.cors(withDefaults());
http.authorizeHttpRequests((authorize) ->
authorize
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers(
"/swagger-ui/**",
"/swagger-resources",
Expand Down Expand Up @@ -95,5 +104,4 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

}

0 comments on commit 9a2f2db

Please sign in to comment.