종합실습

  • 구성도 스크린샷 2023-11-27 오후 10 41 29

1. 백엔드 모듈 구성 (SpringBoot)

  • build.gradle.kts
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web:2.6.3")
    testImplementation(platform("org.junit:junit-bom:5.9.1"))
    testImplementation("org.junit.jupiter:junit-jupiter")
    implementation("io.springfox:springfox-swagger2:2.9.2")
    implementation("io.springfox:springfox-swagger-ui:2.9.2")
}


  • CitronServiceApplication.java
package com.citron.cit;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CitronServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(CitronServiceApplication.class, args);
    }
}


  • config/SwaggerConfig.java
package com.citron.cit.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.citron.cit"))
                .paths(PathSelectors.any())
                .build();
    }

    public ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Citron project")
                .version("1.8.0")
                .description("Citron Project swagger api")
                .build();
    }
}


  • controller/MemberService.java
package com.citron.cit.controller;

import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MemberService {

    @GetMapping("/members")
    @ApiOperation("회원조회")
    public void get() {

    }

    @PostMapping("/members")
    @ApiOperation("회원등록")
    public void register() {

    }

    @DeleteMapping("/members")
    @ApiOperation("회원삭제")
    public void delect() {

    }
}


스크린샷 2023-11-29 오후 1 05 09 서버 실행 확인

스크린샷 2023-11-29 오후 1 15 31 beanstalk에서도 이렇게 뜨는지 확인할 예정!

스크린샷 2023-11-29 오후 3 52 42 github 리포지토리와 연동

2. VPC 구성

스크린샷 2023-11-29 오후 1 39 30 스크린샷 2023-11-29 오후 1 39 53 NAT 게이트웨이 생성!

스크린샷 2023-11-29 오후 1 46 46 스크린샷 2023-11-29 오후 1 45 28 스크린샷 2023-11-29 오후 1 45 14

3. beanstalk 생성

환경 생성 스크린샷 2023-11-29 오후 1 48 23

스크린샷 2023-11-29 오후 1 49 40 vpc는 꼭 이 단계에서 설정해주어야 함!

스크린샷 2023-11-29 오후 1 50 13

private 선택

스크린샷 2023-11-29 오후 3 26 36

스크린샷 2023-11-29 오후 3 28 29 EC2 인스턴스 자동 생성

4. baston host 역할의 EC2 생성

스크린샷 2023-11-29 오후 3 48 02

스크린샷 2023-11-29 오후 3 49 11

public 영역 선택 & 퍼블릭 IP 자동 할당 활성화


bastonhost 인스턴스 연결

  • 다운받은 키페어를 ~/.ssh 로 이동
  • 터미널에서 ~/.ssh 로 이동
ssh -i bk.pem ec2-user@[public IP 주소]

스크린샷 2023-12-01 오후 6 09 50

Permissions 0644 for 'key.pem' are too open. 오류

chmod 400 key.pem

명령어로 권한 변경!

5. citron-application-env 연결

  • private 서브넷에 연결했기 때문에 바로는 접속할 수 없음
  • public에 있는 bastonhost를 통해서 private에 있는 beanstalk 연결!

bastonhost에 접속하여 키 복사

ssh -i bk.pem ec2-user@[public IP 주소]

# 루트로 접속
sudo su -
cd /home/ec2-user
cd .ssh

vi bk.pem
# 복사한 키 붙여넣기

chmod 400 bk.pem

beanstalk에서 키 설정

  • [beanstalk] - [Citron-application-env] - [구성] - [서비스 액세스 구성] - [EC2 키 페어] 등록

ssh 연결

  • 프라이빗 IPv4 로 연결!!
  • bastonhost 안에서 연결!!
# 위에서 키를 생성한 곳에서 실행
# /home/ec2-user/.ssh

# ls 로 bk.pem 있는지 확인하고
ssh -i bk.pem ec2-user@[private IP 주소]

스크린샷 2023-12-01 오후 6 27 10

연결 확인

  • elastic beanstalk은 기본적으로 5000번 포트 사용
  • 앞단에 nginx 80 포트가 5000번으로 연결
netstat -an | grep 5000
# tcp6       0      0 :::5000                 :::*                    LISTEN

로그

  • /var/log 에서 확인 가능
  • beanstalk 페이지 - [로그] 에서도 요청 가능

6. RDS 연결

DB 서브넷 그룹 생성

스크린샷 2023-12-01 오후 6 53 48

스크린샷 2023-12-01 오후 6 54 41

private 서브넷 선택

데이터베이스 생성

MySQL 8.0.35 프리티어 버전

스크린샷 2023-12-01 오후 6 57 35

스크린샷 2023-12-01 오후 6 58 09


보안그룹 - 인바운드 규칙에 3306 포트가 있어야 함! 없으면 추가!

스크린샷 2023-12-01 오후 7 03 27

데이터베이스 연결

  • IntelliJ에서 beanstalk 애플리케이션 - 데이터베이스 연결

스크린샷 2023-12-01 오후 7 07 55

스크린샷 2023-12-01 오후 7 08 55

host: DB 엔드포인트


  • private에 연결했기 때문에 중간에 터널링 필요
    • [SSH/SSL] - [SSH 터널 사용] 체크 - [ssh 구성] 추가
    • bastonhost public IP 주소를 host로

스크린샷 2023-12-01 오후 7 12 35

스크린샷 2023-12-01 오후 7 13 02

연결 성공


스크린샷 2023-12-01 오후 7 15 20

intelliJ 내에서 스키마, 테이블, 열 등등 생성 가능

스크린샷 2023-12-01 오후 7 15 57

7. S3 정적 웹 호스팅

S3 버킷 생성

스크린샷 2023-12-01 오후 8 15 36

스크린샷 2023-12-01 오후 8 16 27 버킷 - 속성 - 정적 웹 호스팅 활성화

기본적인 웹 생성

# npx 설치 
npm install npx -g

# 앱 생성
npx create-react-app citron-app

citron-app 폴더가 생성됨! open 하기

스크린샷 2023-12-01 오후 8 28 32


  • 기본적인 리액트 페이지 생성됨
npm start

스크린샷 2023-12-01 오후 8 29 20


  • 빌드
npm run build
  • 생성된 build 폴더 내의 index.html 파일을 S3에 올려주면 됨!


  • serve 로 실행
# serve 설치
npm install -g serve

# 실행
serve -s build
  • build 된 것을 띄우는 것!

S3에 업로드

  • 프로젝트 내 build 폴더 내의 모든 파일을 객체에 업로드!

스크린샷 2023-12-01 오후 8 33 52

스크린샷 2023-12-01 오후 8 34 42

권한 수정

  • 버킷 - [권한] - [퍼블릭 액세스 차단] - [비활성] 으로 수정 스크린샷 2023-12-01 오후 8 36 23

  • 정책 추가

스크린샷 2023-12-01 오후 8 37 18

스크린샷 2023-12-01 오후 8 37 40

웹 확인

[속성] - [정적 웹 사이트 호스팅] - [엔드포인트] 링크로 확인

스크린샷 2024-01-21 오후 4 59 43

S3 정적 웹호스팅 Access Denied 오류

버킷 정책에서 resources를 "Resource": "arn:aws:s3:::citron124.com"에서 ` “Resource”: “arn:aws:s3:::citron124.com/*”` 으로 변경!

8. CI/CD

  • CodePipeline으로 Spring Boot 배포

github 연동

code build

  • build 명령 삽입
  build:
    commands:
      - ./gradlew build

artifacts:
  files:
    - "**/*"

code deploy

  • public에 있다면 beanstalk으로 바로 배포가 가능하지만, private에 만들었기 때문에 Code Deploy로 ELB를 묶어서 배포할 것

  • 환경 구성 - Amazon EC2 Auto Scaling 그룹 선택
  • 로드밸런서 선택
    • Beanstalk에 대한 ELB 생성 후 선택
    • ELB를 통해 private 서브넷 안에 있는 인스턴스와 연결하여 배포

codedeploy-agent 설치

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html

  • 참고하여 설치!

  • 설치하면 /var/log/aws/codedeploy-agent 에서 로그 파일 확인할 수 있음

DownloadBundle 단계에서 AccessDenied 오류

  • IAM 사용자에 S3FullAccess 권한 부여!

BeforeInstall 단계에서 오류

  • CodeDeploy agent was not able to receive the lifecycle event. Check the CodeDeploy agent logs on your host and make sure the agent is running and can connect to the CodeDeploy server. 오류 발생
  • /var/log/aws/codedeploy-agent에서 codedeploy-agent.log 를 확인했더니 Appspec.yml 파일에서 version을 0.2 -> 0.0으로 수정하라고 나와있음
  • 수정하여 푸시했더니 해결!

AllowTraffic 단계에서 시간이 너무 오래걸리는 오류

  • code deploy의 보안 그룹의 인바운드 규칙에서 HTTP:80 을 허용하지 않고 있었음
  • 유형: HTTP, 포트는 자동으로 80, 모든 IPv4에 대해 허용 으로 규칙 생성하여 해결!

code pipeline

스크린샷 2024-01-23 오전 4 30 46 스크린샷 2024-01-23 오전 4 30 56

8. Route53

  • Route53에서 도메인 구입하여 등록 가능
  • 외부 업체에서 구매했다면 [DNS 관리] - [호스팅 영역 생성]

스크린샷 2024-01-23 오전 4 35 08

CloudFront

  • CDN을 이용하는 경우

스크린샷 2024-01-23 오전 4 38 03

스크린샷 2024-01-23 오전 4 42 26

Route53

  • S3를 이용하는 경우
  • 구매한 도메인이 S3 엔드포인트에 연결됨!