1)Cors 설정은 리액트에서 프록시 설정으로 여기 프로젝트에서는 스프링 부트와 nginx 설정으로 해결 했다.
리액트(React) CORS처리
https://woochan-dev.tistory.com/94
주의 Nginx 연동 Cors 설정
모든 경로에 대해 cors 허용 으로 오류가 난다.
또한 restApi 페이지가 아닌경우 오류가 발생한다.
ex) registry.addMapping("/**") * registry.addMapping("/cart/**"), * registry.addMapping("/members/**")
nginx 연동 다음을 참조
https://macaronics.net/m02/linux/view/1819
package com.shop.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.util.StringUtils; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.resource.PathResourceResolver; import java.util.ArrayList; import java.util.List; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Value("${uploadPath}") String uploadPath; private final long MAX_AGE_SECS=3600; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/images/**") .setCachePeriod(60*10*6) // 1시간 .setCachePeriod(60*10*6) .addResourceLocations(uploadPath); registry .addResourceHandler("/upload/**") //jsp 페이지에서 /upload/** 이런주소 패턴이 나오면 발동 .addResourceLocations(uploadPath+"/upload/") .setCachePeriod(60*10*6) // 1시간 .resourceChain(true) .addResolver(new PathResourceResolver()); } @Override public void addCorsMappings(CorsRegistry registry) { /** ★★★★★ nginx 연동의 경우 든 경로에 대해 cors 허용 하면 안된다. * 또한, * 현재 프로젝트가 todo 와 api 인데 * restapi 가 아닌 경로까지 적용되면 restapi 아닌 페이지는 오규가 발생한다. /모든 경로에 대해 cors 허용 ex) registry.addMapping("/**") * registry.addMapping("/cart/**"), * registry.addMapping("/members/**") */ //★★★★★ nginx 연동의 경우 루트 /** 으로 설정이 안된다 다음과 같이 개별 설정 ★★★★★ registry.addMapping("/todo/**") .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) // 'Access-Control-Allow-Credentials' header 는 요청 시 자격 증명이 필요함 .maxAge(MAX_AGE_SECS) .allowedOrigins( "http://localhost:3000/" ,"https://ma7front.p-e.kr/" ).exposedHeaders("authorization"); //authorization 헤더를 넘기 위해 exposedHeaders 조건을 추가 registry.addMapping("/api/**") .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) // 'Access-Control-Allow-Credentials' header 는 요청 시 자격 증명이 필요함 .maxAge(MAX_AGE_SECS) .allowedOrigins( "http://localhost:3000/" ,"https://ma7front.p-e.kr/" ).exposedHeaders("authorization"); //authorization 헤더를 넘기 위해 exposedHeaders 조건을 추가 } }
2)주의
백엔드 프론트엔드 로 분리된
restapi 가 아닌 일반 웹개발 인경우 템플릿 페이지 뷰 반환시 개발환경에서 return "/member/memberLoginForm";
return "/~" return 다음 경로에 슬러시를 붙여도 정상 작동되나 배포시에는 오류가 난다.
이 오류 때문에 한참 헤메였음
@GetMapping(value = "/login/error") public String loginError(Model model){ model.addAttribute("loginErrorMsg", "아이디 또는 비밀번호를 확인해주세요"); return "member/memberLoginForm"; }
3)웹브라우저 확장 설치 프로그램에 대한 오류가 있을수 있으니 확인
소스 :
https://github.com/braverokmc79/jpa-shop-maven-and-gradle-front
0. 준비
실행환경..?
- Windows 10
- spring boot (Gradle, jar, java11)
미리 확인
- python 설치 확인 (cmd에서 python --version)
- aws cli v2 설치 (구글링 후 .msi 파일 다운, 설치)
1. aws cli 설정
AWS 콘솔에서 IAM-액세스 관리-사용자
[사용자 추가] 눌러서,
사용자이름: cli-user,
액세스유형: 프로그래밍 방식 액세스... 로 설정하고 [다음: 권한] 누름.
권한설정: 기존 정책 직접 연결-AdministratorAccess 선택 후 [다음:태그], [다음: 검토], [사용자 만들기]
이 다음에 표시되는 화면에서 액세스키ID, 비밀 액세스 키를 복사.
+
cmd에서
$ aws configure AWS Access Key ID [None]: <액세스키 ID> AWS Secret Access Key [None]: <비밀 액세스키> Default region name [None]: us-west-2 Default output format [None]: json
region name은 다른것도 가능..
한국 사용자가 많을 경우에는 ap-northeast-2를 쓰면 됨
+
이후에 pip로 ebcli 설치
pip install awsebcli --upgrade -user
+
환경변수(시스템 변수)
Path에 %USERPROFILE%/AppData/Roaming/Python/Python39/Scripts 추가.
+
마지막으로 cmd 재시작, 설치 확인
$ eb --version EB CLI 3.19.2 (Python 3.9.0)
2. 환경 초기화
cmd에서 백엔드 프로젝트 폴더로 이동한다.
이후 eb init 입력
$eb init TodoApplication-backend Select a default region 1) us-east-1 : US East (N. Virginia) 2) us-west-1 : US West (N. California) 3) us-west-2 : US West (Oregon) 4) eu-west-1 : EU (Ireland) 5) eu-central-1 : EU (Frankfurt) 6) ap-south-1 : Asia Pacific (Mumbai) 7) ap-southeast-1 : Asia Pacific (Singapore) 8) ap-southeast-2 : Asia Pacific (Sydney) 9) ap-northeast-1 : Asia Pacific (Tokyo) 10) ap-northeast-2 : Asia Pacific (Seoul) 11) sa-east-1 : South America (Sao Paulo) 12) cn-north-1 : China (Beijing) 13) cn-northwest-1 : China (Ningxia) 14) us-east-2 : US East (Ohio) 15) ca-central-1 : Canada (Central) 16) eu-west-2 : EU (London) 17) eu-west-3 : EU (Paris) 18) eu-north-1 : EU (Stockholm) 19) eu-south-1 : EU (Milano) 20) ap-east-1 : Asia Pacific (Hong Kong) 21) me-south-1 : Middle East (Bahrain) 22) af-south-1 : Africa (Cape Town) (default is 3): 10 Application TodoApplication-backend has been created. Select a platform. 1) .NET Core on Linux 2) .NET on Windows Server 3) Docker 4) Go 5) Java 6) Node.js 7) PHP 8) Packer 9) Python 10) Ruby 11) Tomcat (make a selection): 5 Select a platform branch. 1) Corretto 21 running on 64bit Amazon Linux 2023 2) Corretto 17 running on 64bit Amazon Linux 2023 3) Corretto 11 running on 64bit Amazon Linux 2023 4) Corretto 8 running on 64bit Amazon Linux 2023 5) Corretto 17 running on 64bit Amazon Linux 2 6) Corretto 11 running on 64bit Amazon Linux 2 7) Corretto 8 running on 64bit Amazon Linux 2 (default is 1): 1 Alert: You chose a deprecated platform branch. It might not be supported in the future. Cannot setup CodeCommit because there is no Source Control setup, continuing with initialization Do you want to set up SSH for your instances? (Y/n): n
이제 .elasticbeanstalk 이라는 폴더가 생겼다.
3. 백엔드 어플리케이션 설정
application.properties 파일 수정
# application.properties 파일 내용 spring.profiles.active=prod #애플리케이션 포트 설정 #server.port = 5000
application.properties
설정 주의 : 잘못 설정하면 당연히 배포 오류가 난다.
책에서는 application.properties 삭제하고 환경변수를 설정하라고 나오지만 다음과 같이 설정해도 된다.
application.properties
spring.profiles.active=prod
application-prod.properties
#애플리케이션 포트 설정 server.port = 5000 #spring.output.ansi.enabled=always #Mysql server spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://${rds.hostname}:${rds.port}/${rds.db.name} spring.datasource.username= ${rds.username} spring.datasource.password= ${rds.password} spring.jpa.open-in-view=false #create , update spring.jpa.hibernate.ddl-auto=update #파일 한 개당 최대 사이즈 spring.servlet.multipart.maxFileSize=20MB #요청당 최대 파일 크기 spring.servlet.multipart.maxRequestSize=100MB #상품 이미지 업로드 경로 itemImgLocation=/uploads/shop/item #리소스 업로드 경로 uploadPath=file:/uploads/shop/ #기본 batch size 설정 spring.jpa.properties.hibernate.default_batch_fetch_size=1000
책이랑 다르게 엘라스틱 빈스톡의 플랫폼을 [1) Corretto 21 running on 64bit Amazon Linux 2023] 로 선택했다.
(Java 8 running on 64bit amazon linux 옵션은 deprecataed 경고가 뜨기 때문...)
그래서 포트가 어떻게 되나... 여러번 시도 했었다.
근데 다른거 없었다. 포트 그냥 5000 쓰면 된다.
+
gadle.build의 dependencies에도 하나 추가한다.
# build.gradle 의 dependencies에 추가 runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'com.h2database:h2'가 있어도 그 아랫줄에 새로 끼워 넣으면 됨.
+
그리고 새 클래스 작성
package com.example.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HealthCheck { @GetMapping("/") public String healthCheck() { return "The service is up and running..."; } }
AWS 로드밸런서는 기본 경로인 "/"에 HTTP 요청을 보내서 어플리케이션이 동작하는지 확인한다.
일라스틱 빈스톡은 이를 기반으로 어플리케이션이 실행중인 상태인지, 주의가 필요한 상태인지 확인해준다.
또 이 상태를 AWS 콘솔 화면에 표시해준다.
이를 위해서 "/"에 간단한 API를 만들어주면 좋다.
+
이제 spring boot 프로젝트를 build한다.
# cmd로 프로젝트 폴더까지 이동한 후 $ gradle clean build Welcome to Gradle 7.3! Here are the highlights of this release: - Easily declare new test suites in Java projects - Support for Java 17 - Support for Scala 3 For more details see https://docs.gradle.org/7.3/release-notes.html Starting a Gradle Daemon (subsequent builds will be faster) > Task :test 2021-12-28 11:59:50.262 INFO 4408 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2021-12-28 11:59:50.272 INFO 4408 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down' 2021-12-28 11:59:50.289 INFO 4408 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2021-12-28 11:59:50.314 INFO 4408 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. BUILD SUCCESSFUL in 1m 11s 7 actionable tasks: 7 executed
그러면 build/libs 경로에 jar 파일이 생긴다.
버전 이름은 gradle.build 파일에서 설정할 수 있다.
+
.elasticbeanstalk/config.yml 에 deploy 부분 추가
# .elasticbeanstalk/config.yml 파일 내용 branch-defaults: default: environment: PROD-TODO-BACKEND # 추가 시작 deploy: artifact: build/libs/demo-0.0.3.jar # 추가 끝 global: application_name: TodoApplication-backend branch: null default_ec2_keyname: null default_platform: Corretto 11 running on 64bit Amazon Linux 2 default_region: us-west-2 include_git_submodules: true instance_profile: null platform_name: null platform_version: null profile: null repository: null sc: null workspace_type: Application
+
eb create 로 환경 생성
# cmd에서 프로젝트 폴더로 이동한 후 $ eb create --database --elb-type application --instance-type t2.micro Enter Environment Name (default is TodoApplication-backend-dev): PROD-TODO-BACKEND Enter DNS CNAME prefix (default is PROD-TODO-BACKEND22): Would you like to enable Spot Fleet requests for this environment? (y/N): N Enter an RDS DB username (default is "ebroot"): Enter an RDS DB master password: <비밀번호 입력> Retype password to confirm: <비밀번호 입력> Uploading: [##################################################] 100% Done... Environment details for: PROD-TODO-BACKEND Application name: TodoApplication-backend Region: us-west-2 Deployed Version: app-211228_143259 Environment ID: e-m32zspxdmx Platform: arn:aws:elasticbeanstalk:us-west-2::platform/Corretto 11 running on 64bit Amazon Linux 2/3.2.9 Tier: WebServer-Standard-1.0 CNAME: PROD-TODO-BACKEND22.us-west-2.elasticbeanstalk.com Updated: 2021-12-28 05:33:32.287000+00:00 Printing Status: 2021-12-28 05:33:30 INFO createEnvironment is starting. 2021-12-28 05:33:32 INFO Using elasticbeanstalk-us-west-2-490191556309 as Amazon S3 storage bucket for environment data. 2021-12-28 05:33:53 INFO Created target group named: arn:aws:elasticloadbalancing:us-west-2:490191556309:targetgroup/awseb-AWSEB-DZM8O8EKV3XS/e5c4b46318194e1d 2021-12-28 05:33:53 INFO Created security group named: sg-0ce0bec80d01e332c 2021-12-28 05:34:09 INFO Created security group named: awseb-e-m32zspxdmx-stack-AWSEBSecurityGroup-STVZ3AC9TCPA 2021-12-28 05:34:09 INFO Created Auto Scaling launch configuration named: awseb-e-m32zspxdmx-stack-AWSEBAutoScalingLaunchConfiguration-1N9SNDTFZLLM5 2021-12-28 05:34:09 INFO Created RDS database security group named: awseb-e-m32zspxdmx-stack-awsebrdsdbsecuritygroup-1vo76ck4ah7r0 2021-12-28 05:34:24 INFO Creating RDS database named: aa17sko6ijnmpkt. This may take a few minutes. 2021-12-28 05:36:29 INFO Created load balancer named: arn:aws:elasticloadbalancing:us-west-2:490191556309:loadbalancer/app/awseb-AWSEB-18IATENQI5FEY/208b51a89b8a681f 2021-12-28 05:36:44 INFO Created Load Balancer listener named: arn:aws:elasticloadbalancing:us-west-2:490191556309:listener/app/awseb-AWSEB-18IATENQI5FEY/208b51a89b8a681f/9e890169d65e24ce 2021-12-28 05:42:51 INFO Created RDS database named: aa17sko6ijnmpkt 2021-12-28 05:43:54 INFO Created Auto Scaling group named: awseb-e-m32zspxdmx-stack-AWSEBAutoScalingGroup-1J2JGBKYPBVNS 2021-12-28 05:43:54 INFO Waiting for EC2 instances to launch. This may take a few minutes. 2021-12-28 05:43:54 INFO Created Auto Scaling group policy named: arn:aws:autoscaling:us-west-2:490191556309:scalingPolicy:c3bc20fd-e70e-45ea-bc76-b5c8ec638995:autoScalingGroupName/awseb-e-m32zspxdmx-stack-AWSEBAutoScalingGroup-1J2JGBKYPBVNS:policyName/awseb-e-m32zspxdmx-stack-AWSEBAutoScalingScaleUpPolicy-GI0BN5M0F3WR 2021-12-28 05:43:54 INFO Created Auto Scaling group policy named: arn:aws:autoscaling:us-west-2:490191556309:scalingPolicy:8937a9a1-0781-4876-94f5-e21743b04e39:autoScalingGroupName/awseb-e-m32zspxdmx-stack-AWSEBAutoScalingGroup-1J2JGBKYPBVNS:policyName/awseb-e-m32zspxdmx-stack-AWSEBAutoScalingScaleDownPolicy-15LV9A4LA8XNU 2021-12-28 05:44:09 INFO Created CloudWatch alarm named: awseb-e-m32zspxdmx-stack-AWSEBCloudwatchAlarmLow-1RTKL2U1BUJJU 2021-12-28 05:44:09 INFO Created CloudWatch alarm named: awseb-e-m32zspxdmx-stack-AWSEBCloudwatchAlarmHigh-5BZ9YSG55UY3 2021-12-28 05:44:15 INFO Instance deployment successfully detected a JAR file in your source bundle. 2021-12-28 05:44:15 INFO Instance deployment successfully generated a 'Procfile'. 2021-12-28 05:44:18 INFO Instance deployment completed successfully. 2021-12-28 05:44:51 INFO Application available at PROD-TODO-BACKEND22.us-west-2.elasticbeanstalk.com. 2021-12-28 05:44:51 INFO Successfully launched environment: PROD-TODO-BACKEND
배포 완료..!
5. 배포 상태 확인
AWS 콘솔 - Elastic Beanstalk - 환경에서 배포 상태 확인.
+
URL로 접속해서 위에서 작성한 HealthCheck 클래스 내용이 잘 나오는지 확인.
5. RDS DB 연결 설정
elastic beanstalk-구성에서 연결된 데이터베이스의 엔드포인트 클릭.
해당 DB 상세에서 VPC 보안그룹 눌러서 이동
보안그룹 - 인바운드 규칙 - 인바운드 규칙 편집
규칙 추가 - 유형:MYSQL/Aurora, 소스: Anywhere - 저장
+
이후에 백엔드 어플리케이션으로 돌아와서 설정 추가
# application.properties 파일 내용 server.port: 5000 spring.jpa.database=mysql spring.jpa.show-sql=true spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:mysql://<엔드포인트 링크>:3306/<db이름> spring.datasource.username=<rds 사용자> spring.datasource.password=<rds 사용자 비번>
- rds 사용자/비번은 eb create에서 썼던 정보를 갖다 써도 된다..
- db이름은 보통 'ebdb'로 생성되는 듯.. 아니면 aws 콘솔 - rds - 구성에서 확인 가능
+
cmd에서 다시 build하고 재배포하기
$ gradlew clean build > Task :test 2021-12-28 20:33:12.849 INFO 14328 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 2021-12-28 20:33:12.854 INFO 14328 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2021-12-28 20:33:14.082 INFO 14328 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. BUILD SUCCESSFUL in 39s 8 actionable tasks: 8 executed
$ eb deploy Uploading: [##################################################] 100% Done... 2021-12-28 11:33:53 INFO Environment update is starting. 2021-12-28 11:33:58 INFO Deploying new version to instance(s). 2021-12-28 11:34:03 INFO Instance deployment successfully detected a JAR file in your source bundle. 2021-12-28 11:34:04 INFO Instance deployment successfully generated a 'Procfile'. 2021-12-28 11:34:07 INFO Instance deployment completed successfully. 2021-12-28 11:34:12 INFO New application version was deployed to running EC2 instances. 2021-12-28 11:34:12 INFO Environment update completed successfully.
6. 작동 확인
url로 접속
+
POSTMAN으로 API 응답 확인
+
DB에 적용 잘 되었는지 확인해봤다.
1.AWS 엘라스틱 빈스토어 배포
$ eb create --database --elb-type application --instance-type t2.micro
2.환경설정
$ eb setenv SPRING_PROFILES_ACTIVE=prod
3.프로젝트 클린및 빌들처리
$ gradlew clean build
4.AWS 엘라스틱 빈스토어 재배포
$ eb deploy
5. RDS 확인
$ aws rds describe-db-instances --region ap-northeast-2
{ "DBInstances": [ { "DBInstanceIdentifier": "awseb-e-fw8zsguxww-stack-awsebrdsdatabase-p27b8jditfzi", "DBInstanceClass": "db.t2.micro", "Engine": "mysql", "DBInstanceStatus": "available", "MasterUsername": "tododb", "DBName": "ebdb", "Endpoint": { "Address": "awseb-e-fw8zsguxww-stack-awsebrdsdatabase-p27b8jditfzi.c780mcakmy87.ap-northeast-2.rds.amazonaws.com", "Port": 3306, "HostedZoneId": "ZLA2NUCOLGUUR" }, "AllocatedStorage": 5, "InstanceCreateTime": "2024-02-13T01:47:26.571000+00:00", "PreferredBackupWindow": "15:38-16:08", "BackupRetentionPeriod": 1, "DBSecurityGroups": [], "VpcSecurityGroups": [ { "VpcSecurityGroupId": "sg-08145e4fc1f2fdcb6", "Status": "active" } ], "DBParameterGroups": [ { "DBParameterGroupName": "default.mysql8.0", "ParameterApplyStatus": "in-sync" } ], "AvailabilityZone": "ap-northeast-2c", "DBSubnetGroup": { "DBSubnetGroupName": "default", "DBSubnetGroupDescription": "default", "VpcId": "vpc-01e5c5e74ca650e53", "SubnetGroupStatus": "Complete", "Subnets": [ { "SubnetIdentifier": "subnet-0172dfd13a50c2eef", "SubnetAvailabilityZone": { "Name": "ap-northeast-2c" }, "SubnetOutpost": {}, "SubnetStatus": "Active" },
6. 오토 스케일링 그룹 확인
$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-2
{ "AutoScalingGroups": [ { "AutoScalingGroupName": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "AutoScalingGroupARN": "arn:aws:autoscaling:ap-northeast-2:975049930287:autoScalingGroup:d9eebc7e-71c4-48ff-a94d-87f3d4548ba2:autoScalingGroupName/awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "LaunchConfigurationName": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingLaunchConfiguration-idrd96br0tsC", "MinSize": 1, "MaxSize": 4, "DesiredCapacity": 1, "DefaultCooldown": 360, "AvailabilityZones": [ "ap-northeast-2a" ], "LoadBalancerNames": [], "TargetGroupARNs": [ "arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:targetgroup/awseb-AWSEB-KJQFRLQJ3D1Y/0f7f94aba89a2c98" ], "HealthCheckType": "EC2", "HealthCheckGracePeriod": 0, "Instances": [ { "InstanceId": "i-02ac4113c0cd0d4c8", "InstanceType": "t2.micro", "AvailabilityZone": "ap-northeast-2a", "LifecycleState": "InService", "HealthStatus": "Healthy", "LaunchConfigurationName": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingLaunchConfiguration-idrd96br0tsC", "ProtectedFromScaleIn": false } ], "CreatedTime": "2024-02-13T01:50:22.964000+00:00", "SuspendedProcesses": [], "VPCZoneIdentifier": "", "EnabledMetrics": [], "Tags": [ { "ResourceId": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "ResourceType": "auto-scaling-group", "Key": "Name", "Value": "MACARONICS-TODO-API-SERVICE", "PropagateAtLaunch": true }, { "ResourceId": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "ResourceType": "auto-scaling-group", "Key": "aws:cloudformation:logical-id", "Value": "AWSEBAutoScalingGroup", "PropagateAtLaunch": true }, { "ResourceId": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "ResourceType": "auto-scaling-group", "Key": "aws:cloudformation:stack-id", "Value": "arn:aws:cloudformation:ap-northeast-2:975049930287:stack/awseb-e-fw8zsguxww-stack/46413f40-ca11-11ee-a86e-02c6da73bbd4", "PropagateAtLaunch": true }, { "ResourceId": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "ResourceType": "auto-scaling-group", "Key": "aws:cloudformation:stack-name", "Value": "awseb-e-fw8zsguxww-stack", "PropagateAtLaunch": true }, { "ResourceId": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "ResourceType": "auto-scaling-group", "Key": "elasticbeanstalk:environment-id", "Value": "e-fw8zsguxww", "PropagateAtLaunch": true }, { "ResourceId": "awseb-e-fw8zsguxww-stack-AWSEBAutoScalingGroup-sZx4USt0K34f", "ResourceType": "auto-scaling-group", "Key": "elasticbeanstalk:environment-name", "Value": "MACARONICS-TODO-API-SERVICE", "PropagateAtLaunch": true } ], "TerminationPolicies": [ "Default" ], "NewInstancesProtectedFromScaleIn": false, "ServiceLinkedRoleARN": "arn:aws:iam::975049930287:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling", "CapacityRebalance": false, "TrafficSources": [ { "Identifier": "arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:targetgroup/awseb-AWSEB-KJQFRLQJ3D1Y/0f7f94aba89a2c98", "Type": "elbv2" } ] } ] }
7. 로드 밸런서 확인
$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-2
{ "LoadBalancers": [ { "LoadBalancerArn": "arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:loadbalancer/app/awseb--AWSEB-Regi9wc1D6fr/6ad76b5ff715f5ea", "DNSName": "awseb--AWSEB-Regi9wc1D6fr-1594608003.ap-northeast-2.elb.amazonaws.com", "CanonicalHostedZoneId": "ZWKZPGTI48KDX", "CreatedTime": "2024-02-13T01:43:35.630000+00:00", "LoadBalancerName": "awseb--AWSEB-Regi9wc1D6fr", "Scheme": "internet-facing", "VpcId": "vpc-01e5c5e74ca650e53", "State": { "Code": "active" }, "Type": "application", "AvailabilityZones": [ { "ZoneName": "ap-northeast-2c", "SubnetId": "subnet-0172dfd13a50c2eef", "LoadBalancerAddresses": [] }, { "ZoneName": "ap-northeast-2b", "SubnetId": "subnet-0352d5b3e03eb46ba", "LoadBalancerAddresses": [] }, { "ZoneName": "ap-northeast-2d", "SubnetId": "subnet-0376bc55c27aea134", "LoadBalancerAddresses": [] }, { "ZoneName": "ap-northeast-2a", "SubnetId": "subnet-0b1d168e6106cafde", "LoadBalancerAddresses": [] } ], "SecurityGroups": [ "sg-071d8686d5b0f60f0" ], "IpAddressType": "ipv4" } ] }
8. 로드 밸런서와 ASG 를 연결하는 타깃 그룹 확인
$ aws ec2 describe-instances --region ap-northeast-2
{ "TargetGroups": [ { "TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:targetgroup/awseb-AWSEB-KJQFRLQJ3D1Y/0f7f94aba89a2c98", "TargetGroupName": "awseb-AWSEB-KJQFRLQJ3D1Y", "Protocol": "HTTP", "Port": 80, "VpcId": "vpc-01e5c5e74ca650e53", "HealthCheckProtocol": "HTTP", "HealthCheckPort": "traffic-port", "HealthCheckEnabled": true, "HealthCheckIntervalSeconds": 15, "HealthCheckTimeoutSeconds": 5, "HealthyThresholdCount": 3, "UnhealthyThresholdCount": 5, "HealthCheckPath": "/", "Matcher": { "HttpCode": "200" }, "LoadBalancerArns": [ "arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:loadbalancer/app/awseb--AWSEB-Regi9wc1D6fr/6ad76b5ff715f5ea" ], "TargetType": "instance", "ProtocolVersion": "HTTP1", "IpAddressType": "ipv4" } ] }
9. CloudWatch ec2 인스턴스확인
$ aws elbv2 describe-target-groups --region ap-northeast-2
{ "Reservations": [ { "Groups": [], "Instances": [ { "AmiLaunchIndex": 0, "ImageId": "ami-027f129eeb456ff15", "InstanceId": "i-02ac4113c0cd0d4c8", "InstanceType": "t2.micro", "LaunchTime": "2024-02-13T01:50:26+00:00", "Monitoring": { "State": "disabled" }, "Placement": { "AvailabilityZone": "ap-northeast-2a", "GroupName": "", "Tenancy": "default" }, "PrivateDnsName": "ip-172-31-7-241.ap-northeast-2.compute.internal", "PrivateIpAddress": "172.31.7.241", "ProductCodes": [], "PublicDnsName": "ec2-52-79-249-222.ap-northeast-2.compute.amazonaws.com", "PublicIpAddress": "52.79.249.222", "State": { "Code": 16, "Name": "running" }, "StateTransitionReason": "", "SubnetId": "subnet-0b1d168e6106cafde", "VpcId": "vpc-01e5c5e74ca650e53", "Architecture": "x86_64", "BlockDeviceMappings": [ {
작동.. 잘된다.
..
끝!
하루 종일 헤맸다...
해결 과정을 정리해보자면..
1.
처음엔 책에서 하라는데로 Java 8 running on linux로 배포를 했는데..
플랫폼 deprecated가 떠서 종료하고 다시 init 했다.
디폴트로 되어있던 Corretto 11 running on Amazon linux 2를 골랐다.
2.
그 후엔 계속에서 502 Bad Gateway가 떴다. 로그를 확인했더니 아래와 같았다.
connect() failed (111: Connection refused) while connecting to upstream
6시간은 헤맸다. 계속 수정-빌드-배포를 반복했다. 열번 넘게 재배포 했는데 해결 못했다.....
연결 timeout 시간도 수정해보고.. application-prod.yaml에서 포트도 바꿔보고.. setenv PORT=5000 이런것도 써보고..
근데..? 아무튼 안됨.. ㅎㅎ..
...
..
밥먹고 천천히 다시 해보기로 결정 ㅠㅠ
3.
application-prod.yaml, application-dev.yaml으로 나눠서 작성했던걸 다 삭제하고,
setenv했던 SPRING_PROFILES_ACTIVE=prod변수도 삭제했다.
4.
application.properties를 다시 만들고, 단순하게 'server.port=5000'만 넣어서 재배포했더니
웬걸? 너무 잘되는거다.. 일단 포트가 문제가 아니었다는걸 알았다.
5.
이 다음은 rds db의 connection을 확인하려했다.
책에서는 ${rds.username} 이런 식으로 변수를 썼는데..
application.properties로 바꾸고 나서는 이 부분에서 자꾸 에러가 났기 때문이다.
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to open JDBC Connection for DDL execution
여차저차 알아보다가 [rds-인바운드규칙]을 설정하고 났더니 db 접속도 잘 되고 gradlew build도 잘 되었다.
aws에 재배포하고 접속했더니 성공이었다..! ㅠㅠ
출처 : https://senna.tistory.com/7
※ AWS 일래스틱 빈스톡을 이용한 프론트엔드 배포
1. eb 생성
$ eb init TodoApplication-frontend Select a default region 1) us-east-1 : US East (N. Virginia) 2) us-west-1 : US West (N. California) 3) us-west-2 : US West (Oregon) 4) eu-west-1 : EU (Ireland) 5) eu-central-1 : EU (Frankfurt) 6) ap-south-1 : Asia Pacific (Mumbai) 7) ap-southeast-1 : Asia Pacific (Singapore) 8) ap-southeast-2 : Asia Pacific (Sydney) 9) ap-northeast-1 : Asia Pacific (Tokyo) 10) ap-northeast-2 : Asia Pacific (Seoul) 11) sa-east-1 : South America (Sao Paulo) 12) cn-north-1 : China (Beijing) 13) cn-northwest-1 : China (Ningxia) 14) us-east-2 : US East (Ohio) 15) ca-central-1 : Canada (Central) 16) eu-west-2 : EU (London) 17) eu-west-3 : EU (Paris) 18) eu-north-1 : EU (Stockholm) 19) eu-south-1 : EU (Milano) 20) ap-east-1 : Asia Pacific (Hong Kong) 21) me-south-1 : Middle East (Bahrain) 22) il-central-1 : Middle East (Israel) 23) af-south-1 : Africa (Cape Town) 24) ap-southeast-3 : Asia Pacific (Jakarta) 25) ap-northeast-3 : Asia Pacific (Osaka) (default is 3): 10 Application TodoApplication-frontend has been created. It appears you are using Node.js. Is this correct? (Y/n): Y Select a platform branch. 1) Node.js 20 running on 64bit Amazon Linux 2023 2) Node.js 18 running on 64bit Amazon Linux 2023 3) Node.js 18 running on 64bit Amazon Linux 2 4) Node.js 16 running on 64bit Amazon Linux 2 (Deprecated) 5) Node.js 14 running on 64bit Amazon Linux 2 (Deprecated) (default is 1): 1 Cannot setup CodeCommit because there is no Source Control setup, continuing with initialization Do you want to set up SSH for your instances? (Y/n): n
2. api-config.js 설정
let backendHost; const hostname =window && window.location && window.location.hostname; if(hostname==="localhost"){ backendHost="http://localhost:8080"; // backendHost="http://mcaronics-todo-api-service.ap-northeast-2.elasticbeanstalk.com"; }else{ backendHost="http://mcaronics-todo-api-service.ap-northeast-2.elasticbeanstalk.com"; } export const API_BASE_URL = backendHost;
3. 리액트 프로젝트에서 다음과 같이 디렉토리 생성
윈도우에서는 Git bash 창을 띄어 실행
$ mkdir -p .platform/hooks/prebuild
4. 01_configure_swap_space.sh 파일 생성
vi .platform/hooks/prebuild/01_configure_swap_space.sh
#!/bin/bash SWAPFILE=/var/swapfile if [ -f $SWAPFILE ]; then echo "$SWAPFILE found, skip" exit; fi /bin/dd if=/dev/zero of=$SWAPFILE bs=2M count=2048 /bin/chmod 600 $SWAPFILE /sbin/mkswap $SWAPFILE /sbin/swapon $SWAPFILE
이와 같은 작업을 해 주는 이유는 우리가 t2/t3 계열의 메모리가 작은 인스턴스를 사용하기 때문이다.
메모리가 부족해 Out of Memory 에러가 나는 것을 방지하기 위해 Swap 파일을 만들어 준다.
Swap 이란 메모리가 부족할 때, 사용하지 않는 메모리의 일부를 디스크, 즉 파일로 옮겨 메모리를 확보하는 기술이다.
5. 파일권한 설정
Linux - Chmod +x [파일명] · chmod +x backup 명령어로 backup 파일에 실행할수 있는 권한을 준다.
$ chmod +x .platform/hooks/prebuild/01_configure_swap_space.sh
6.node 버전 확인후 package.json 에 engine 버전 추가
nvm 으로 프로젝트에 맞는 node 버전 변경하기
nvm 설치 및 node 설치 - 사용법(mac&windows)
출처: https://jang8584.tistory.com/295 [개발자의 길:티스토리]
https://jang8584.tistory.com/295
$ node --version
v21.6.1
package.json
{ "name": "front-end", "version": "0.1.0", "private": true, "engines": {"node": "21.6.1"},
노드 버전에 따라 설치가 안되는 경우가 있으니
reifyNode:node_modules/core-js-pure 에 걸릴 경우 node 버전 문제 따라서,
배포전 반드시 node_modules 디렉토리를 삭제후 다시 npm install 로 정상적으로 설치 되는지 확인할것
7.npm 을 이용해 소스코드를 빌드한다.
$ npm run build
8. 커밋
nodejs. 를 사용하는 일래스틱 빈스톡의 경우 커밋되지 않은 변경 사항들은 배포되지 않기 때문에
커밋을 해야 한다.
$ git add . && git commit -m "commit for front deployment"
만약
다음과 같은 오류가 있을 경우
[ERROR] An error occurred during execution of command [app-deploy] - [RunAppDeployPreBuildHooks]. Stop running the command. Error: Command .platform/hooks/prebuild/01_configure_swap_space.sh failed with error fork/exec .platform/hooks/prebuild/01_configure_swap_space.sh: no such file or directory
다음을 참조
git 에서 CRLF 개행 문자 차이로 인한 문제 해결하기
https://www.lesstif.com/gitbook/git-crlf-20776404.html
9. eb create
$ eb create --elb-type application --instance-type t3.micro
명령후 실행후 다음과 같은 설정후 10분정도 기다리면
$ eb create --elb-type application --instance-type t3.micro Enter Environment Name (default is TodoApplication-frontend-dev): MACARONICS-TODO-UI-SERVICE Enter DNS CNAME prefix (default is MACARONICS-TODO-UI-SERVICE): macaronics-todo-ui-service Would you like to enable Spot Fleet requests for this environment? (y/N): N Creating application version archive "app-240213_154243338938".
====>
$ eb create --elb-type application --instance-type t3.micro Enter Environment Name (default is TodoApplication-frontend-dev): MCARONICS-TODO-UI-SERVICE Enter DNS CNAME prefix (default is MCARONICS-TODO-UI-SERVICE): macaronics-todo-ui-service Would you like to enable Spot Fleet requests for this environment? (y/N): N Creating application version archive "app-240213_154619174060". Uploading: [##################################################] 100% Done... Environment details for: MCARONICS-TODO-UI-SERVICE Application name: TodoApplication-frontend Region: ap-northeast-2 Deployed Version: app-240213_154619174060 Environment ID: e-fpw56kwp7j Platform: arn:aws:elasticbeanstalk:ap-northeast-2::platform/Node.js 20 running on 64bit Amazon Linux 2023/6.1.0 Tier: WebServer-Standard-1.0 CNAME: macaronics-todo-ui-service.ap-northeast-2.elasticbeanstalk.com Updated: 2024-02-13 06:56:12.592000+00:00 Printing Status: 2024-02-13 06:56:10 INFO createEnvironment is starting. 2024-02-13 06:56:12 INFO Using elasticbeanstalk-ap-northeast-2-975049930287 as Amazon S3 storage bucket for environment data. 2024-02-13 06:56:38 INFO Created security group named: sg-01c2c8144a2795cb2 2024-02-13 06:56:38 INFO Created security group named: awseb-e-fpw56kwp7j-stack-AWSEBSecurityGroup-RQI3TUBU4KNB 2024-02-13 06:56:54 INFO Created Auto Scaling launch configuration named: awseb-e-fpw56kwp7j-stack-AWSEBAutoScalingLaunchConfiguration-Lkbufl6H2qnu 2024-02-13 06:56:54 INFO Created target group named: arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:targetgroup/awseb-AWSEB-WPQGZQSGGG53/86fa30d63221329f 2024-02-13 06:57:09 INFO Created Auto Scaling group named: awseb-e-fpw56kwp7j-stack-AWSEBAutoScalingGroup-XHGIL9funIiH 2024-02-13 06:57:09 INFO Waiting for EC2 instances to launch. This may take a few minutes. 2024-02-13 06:57:25 INFO Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-2:975049930287:scalingPolicy:79ae3320-1141-4829-810e-54700db04bf9:autoScalingGroupName/awseb-e-fpw56kwp7j-stack-AWSEBAutoScalingGroup-XHGIL9funIiH:policyName/awseb-e-fpw56kwp7j-stack-AWSEBAutoScalingScaleUpPolicy-GrnpVTfu2HF0 2024-02-13 06:57:25 INFO Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-2:975049930287:scalingPolicy:410c88e2-f370-47de-8e99-59b174af1c9b:autoScalingGroupName/awseb-e-fpw56kwp7j-stack-AWSEBAutoScalingGroup-XHGIL9funIiH:policyName/awseb-e-fpw56kwp7j-stack-AWSEBAutoScalingScaleDownPolicy-wwW9YwnOQK52 2024-02-13 06:57:25 INFO Created CloudWatch alarm named: awseb-e-fpw56kwp7j-stack-AWSEBCloudwatchAlarmHigh-cJDKsK1FtUCp 2024-02-13 06:57:25 INFO Created CloudWatch alarm named: awseb-e-fpw56kwp7j-stack-AWSEBCloudwatchAlarmLow-6ULjKFHlC0P4 2024-02-13 06:59:15 INFO Created load balancer named: arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:loadbalancer/app/awseb--AWSEB-jWgZhzivBSAA/8fa6f04d047f8f8a 2024-02-13 06:59:15 INFO Created Load Balancer listener named: arn:aws:elasticloadbalancing:ap-northeast-2:975049930287:listener/app/awseb--AWSEB-jWgZhzivBSAA/8fa6f04d047f8f8a/abb50fdb25e2b8ba 2024-02-13 07:00:07 WARN Instance deployment: The deployment used the default Node.js version for your platform version instead of the Node.js version included in your 'package.json'. 2024-02-13 07:00:12 INFO Instance deployment completed successfully. 2024-02-13 07:01:17 INFO Successfully launched environment: MCARONICS-TODO-UI-SERVICE
팁:
t2.micro 또는 t3.micro 인스턴스를 사용하는 경우 일래스틱 빈스톡 환경 설정이 중간에 멈추는 경우가 있다.
만약 일래스틱 빈스톡 상태가 10분 넘게 확인 상태로 변경되지 않는다면 EC2 화면으로 가 인스턴스 상태 > 인스턴스 종료 메뉴를 통해 실행 중인 EC2
인스턴스를 종료시키도록 하자.
10. eb 재 배포 방법
$ eb deploy
배포시 에러 로그 확인 방법
EB 인스턴스 삭제 방법
※프론트 리액트의 경우 AWS 대신에 React 앱을 무료로 배포하는 10가지 방법
https://devsnote.com/writings/2139
※ nginx 스프링부트 리액트 연동
1) 리액트를 빌드 한다.
$npm run build 한다.
여기서는 home/react/jpaApp/build 경로로 빌드 파일이 생성 되었다.
2) nginx 기본 루트 경로는 /usr/share/nginx/html; 인데 이 경로를 리액트 build 경로로 변경
다음과 같이 root 경로를 리액트 빌드 파일 경로로 변경 처리 한다 . root /home/react/jpaApp/build;
server { server_name ma7front.p-e.kr www.ma7front.p-e.kr; listen 80; listen [::]:80; location / { root /home/react/jpaApp/build; # nginx 기본 루트 경로 HTML파일이 위치할 경로 index index.html index.htm; try_files $uri /index.html; } }
ngix 를 재구동 한다.
$systemctl restart nginx
3)리액트 디렉토리 nginx 유저 권한으로 변경 시키기
만약에 500 에러가 나거나 권한 설정 에러 403에러가 나오면
nginx.conf 파일을 열고
$ vim /etc/nginx/nginx.conf
맨 상단의 user 권한을 확인한다
nginx.conf
user www-data; worker_processes auto; pid /run/nginx.pid; include /etc/nginx/modules-enabled/*.conf; ~
현재 유저 권한이 www-data 로 나와 있는데
react 디렉토리를 다음과 같이 nginx 유저 권한으로 변경 시킨다.
$chown -R www-data:www-data /home/react/
또는 react 서버 배포 유저로 명으로 변경후 nginx 재실행한다.
user nginx; worker_processes auto; pid /run/nginx.pid; include /etc/nginx/modules-enabled/*.conf; ~
$ systemctl restart nginx
upstream tomcat { ip_hash; server 127.0.0.1:5000; } server { server_name ma7server.p-e.kr www.ma7server.p-e.kr; ## 다음 woff2 파일들이 차단될 경우 설정 location ~* \.(eot|ttf|woff|woff2)$ { add_header Access-Control-Allow-Origin *; } ######################## 일반 웹 설정 ################### location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-NginX-Proxy true; proxy_pass http://tomcat; proxy_redirect off; charset utf-8; } ######################## /todo 시작되는 경로와 /api 로 시작되는 경로는 restapi jwt 설정 ################### location /todo { proxy_hide_header Access-Control-Allow-Origin; add_header 'Access-Control-Allow-Origin' '*' always; #모든 도메인 허용할경우 add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Cookie' always; add_header 'Access-Control-Max-Age' 1728000; proxy_pass http://tomcat ; proxy_redirect off; charset utf-8; } location /api { proxy_hide_header Access-Control-Allow-Origin; add_header 'Access-Control-Allow-Origin' '*' always; #모든 도메인 허용할경우 add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Cookie' always; add_header 'Access-Control-Max-Age' 1728000; proxy_pass http://tomcat ; proxy_redirect off; charset utf-8; } } ######################## front 설정 ################### server { server_name ma7front.p-e.kr www.ma7front.p-e.kr; listen 80; listen [::]:80; location / { root /home/react/jpaApp/build; # nginx 기본 루트 경로 HTML파일이 위치할 경로 index index.html index.htm; try_files $uri /index.html; } }
SSL certbot
다음 사이트에서 운영체제 서버환경등 플랫폼에 맞게 메뉴얼에 맞게 설치하면 된다.
=> ssl certbot 적용후 코드
upstream tomcat { ip_hash; server 127.0.0.1:5000; } server { server_name ma7server.p-e.kr www.ma7server.p-e.kr; location ~* \.(eot|ttf|woff|woff2)$ { add_header Access-Control-Allow-Origin *; } location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-NginX-Proxy true; proxy_pass http://tomcat; proxy_redirect off; charset utf-8; } location /todo { proxy_hide_header Access-Control-Allow-Origin; add_header 'Access-Control-Allow-Origin' '*' always; #모든 도메인 허용할경우 add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Cookie' always; add_header 'Access-Control-Max-Age' 1728000; proxy_pass http://tomcat ; proxy_redirect off; charset utf-8; } location /api { proxy_hide_header Access-Control-Allow-Origin; add_header 'Access-Control-Allow-Origin' '*' always; #모든 도메인 허용할경우 add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Cookie' always; add_header 'Access-Control-Max-Age' 1728000; proxy_pass http://tomcat ; proxy_redirect off; charset utf-8; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/ma7server.p-e.kr/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/ma7server.p-e.kr/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = ma7server.p-e.kr) { return 301 https://$host$request_uri; } # managed by Certbot server_name ma7server.p-e.kr www.ma7server.p-e.kr; listen 80; return 404; # managed by Certbot } ######################## front 설정 ################### server { server_name ma7front.p-e.kr www.ma7front.p-e.kr; listen 80; listen [::]:80; location / { root /home/react/jpaApp/build; # nginx 기본 루트 경로 HTML파일이 위치할 경로 index index.html index.htm; try_files $uri /index.html; } listen [::]:443 ssl ipv6only=on; # managed by Certbot listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/ma7front.p-e.kr/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/ma7front.p-e.kr/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = ma7front.p-e.kr) { return 301 https://$host$request_uri; } # managed by Certbot server_name ma7front.p-e.kr www.ma7front.p-e.kr; listen 80; return 404; # managed by Certbot }
※ CI/CD - Github
1. AWS 스프링부트 서버 GitHub Actions으로 배포 자동화해 보기
참조 :
https://goldenrabbit.co.kr/2023/07/05/github-actions으로-배포-자동화해-보기a-k-a-ci-cd-2편/
name: CI/CD # 1 깃허브 액션 이름 변경 on: push: branches: [ todoProject ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: distribution: 'corretto' java-version: '17' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: ./gradlew clean build #2 현재시간가져오기 - name: Get current time uses: josStorer/get-current-time@v2.0.2 id: current-time with: format: YYYY-MM-DDTHH-mm-ss utcOffset: "+09:00" # 3 배포용 패키지 경로 저장 - name: Set artifact run: echo "artifact=$(ls ./build/libs)" >> $GITHUB_ENV # 4 빈스토크 배포 - name: Beanstalk Deploy uses: einaregilsson/beanstalk-deploy@v20 with: aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} application_name: TodoApplication-backend environment_name: MACARONICS-TODO-API-SERVICE version_label: github-action-${{steps.current-time.outputs.formattedTime}} region: ap-northeast-2 deployment_package: ./build/libs/${{env.artifact}}
2. 리액트 GitHub Actions 으로 우분트 배포 자동화 하기
참조 : 다음 참조 사이트를 보고 설정 방법을 익힌다.
1) github actions를 활용한 자동배포 CI/CD
2) Deploy code to an Ubuntu server with GitHub actions
리눅스에서 다음과 같이 설정
1)디렉토리 GIT 소유권 관련 오류 해결
git config --global --add safe.directory /home/springboot/jpa-shop-maven-and-gradle
2)Git 인증 정보 등록해놓고 사용하기
git config --global credential.helper <옵션>
git config --global credential.helper store
3)+x 옵션 또는 755 권한으로 실행 권한을 추가해주자.
https://velog.io/@jinny-l/gradlew-permission-denied-issue
chmod +x ./gradlew
아래 워크 workflow 참고로 실행하고
sudo 시 비밀번호 입력에 대한 문제 때문에 오류가 발생할 수 있으니 다음과 같이 ssh 접속 유저명에 권한을 부여 한다.
react 계정에 sudo 권한 추가 echo 'react ALL=NOPASSWD: ALL' >> /etc/sudoers
ubuntu-react-deploy.yml
# 우분트22 workflow name: Deploy to Ubuntu-22 on Push Event # 작업이 실행되는 시기를 제어합니다. UI를 사용하여 수동으로 트리거되면 워크플로가 실행됩니다. # or API. on: push: branches : ["main"] # 워크플로 실행은 순차적으로 또는 병렬로 실행될 수 있는 하나 이상의 작업으로 구성됩니다. jobs: # 배포작업 "deploy" deploy: # 작업이 실행될 실행기 유형 runs-on: ubuntu-latest # 작업환경 : Ubuntu 최신버전 18 , 22 등 버전입력 하면 작동 안될수 있음 # 단계는 작업의 일부로 실행될 일련의 작업을 나타냅니다. steps: # Build 전 pre-code-check (코드 검사) - name: Checkout code uses: actions/checkout@v2 # 배포 셸을 사용하여 단일 명령을 실행합니다. - name: Deploy uses: appleboy/ssh-action@v0.1.10 with: host: ${{ secrets.REACT_SERVER_HOST }} username: ${{ secrets.REACT_SERVER_USER }} password: ${{ secrets.REACT_SERVER_PASSWORD }} port: ${{ secrets.REACT_SERVER_PORT }} script: 'cd /home/react/jpa-shop-maven-and-gradle-front && ls -ltr && sudo git stash && sudo git pull && npm install && npm run build && sudo systemctl restart nginx'
3. 스프링부트 그래들 GitHub Actions 으로 우분트 배포 자동화 하기
위 2번 참조후
1. build.gradle 샘플 참조
plugins { id 'java' id 'org.springframework.boot' version '3.2.3' id 'io.spring.dependency-management' version '1.1.4' id 'org.jetbrains.kotlin.jvm' } group = 'com.shop' version = '0.0.1-SNAPSHOT' java { } repositories { mavenCentral() } //Gradle에서 빌드하여 생성하는 jar 파일명 변경하기 bootJar{ archivesBaseName = 'ROOT' archiveFileName = 'ROOT.jar' archiveVersion = "0.0.0" } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' // implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' implementation group: 'com.github.gavlyukovskiy', name: 'p6spy-spring-boot-starter', version: '1.9.1' implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.3.0' implementation group: 'org.modelmapper', name: 'modelmapper', version: '3.2.0' //implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-data-redis' //jwt version: '0.12.4' implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.h2database:h2' runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' // ⭐ Spring boot 3.x이상에서 QueryDsl 패키지를 정의하는 방법 implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" // com.google.guava implementation group: 'com.google.guava', name: 'guava', version: '33.0.0-jre' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } //-plain.jar 를 생성 금지 jar { enabled = false } tasks.named('test') { useJUnitPlatform() } def querydslSrcDir = 'src/main/generated' clean { delete file(querydslSrcDir) } tasks.withType(JavaCompile) { options.generatedSourceOutputDirectory = file(querydslSrcDir) options.compilerArgs.add("-parameters") } kotlin { jvmToolchain(17) } // 테스트 코드를 제외한 빌드 수행 또는 gradle build -x test //https://velog.io/@may_yun/빌드시-테스트-코드-제외하기 tasks.withType(Test) { enabled = false } // release 샘플 1 => 명령어 ./gradlew release // 다음 릴리즈가 성공하면 build/libs/macaronics-app-0.1.0.jar 파일이 생성된다. task release(type: Jar) { // ./gradlew build 를 먼저 실행 dependsOn build archiveBaseName = 'macaronics-app' archiveVersion = '0.1.0' duplicatesStrategy = DuplicatesStrategy.EXCLUDE from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } with jar println "릴리즈 성공.....!!!!" } //1.AWS 엘라스틱 빈스토어 배포 => ./gradlew release //$ eb create --database --elb-type application --instance-type t2.micro //2.환경설정 //$ eb setenv SPRING_PROFILES_ACTIVE=prod //3.프로젝트 클린및 빌드처리 //$ gradlew clean build //4.AWS 엘라스틱 빈스토어 재배포 //$ eb deploy //5. RDS 확인 //$ aws rds describe-db-instances --region ap-northeast-2 // 릴리즈 과정 /** task release() { /* 3.프로젝트 클린및 빌드처리 ./gradlew build를 먼저 실행하라는 의미다. */ // dependsOn("build") // doLast { // def stdout = new ByteArrayOutputStream() // /* exec - 커맨드 라인 명령어; 파일시스템에서 실행하는 것과 같다. */ // exec { // /* 2.환경설정 $eb setenv SPRING_PROFILES_ACTIVE=prod */ // commandLine 'eb', 'setenv', 'SPRING_PROFILES_ACTIVE=prod' // standardOutput = stdout // } // /* 결과 출력을 위한 작업 */ // println "eb setenv SPRING_PROFILES_ACTIVE=prod : \n$stdout"; // exec { // /* 3.배포 $eb deploy */ // //commandLine 'eb', 'deploy' // //standardOutput = stdout // } // // println "eb deploy : \n$stdout"; // println "Release succeeded." // } //}
2.workflows (ma7server.yml)
name: ma7server/CI-CD # 1 우분트 배로 on: push: branches: [ todoProject ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: distribution: 'corretto' java-version: '17' #2 현재시간가져오기 - name: Get current time uses: josStorer/get-current-time@v2.0.2 id: current-time with: format: YYYY-MM-DDTHH-mm-ss utcOffset: "+09:00" # 3.배포 셸을 사용하여 단일 명령을 실행 또는 직접 스크립트 입력 합니다. - name: Deploy uses: appleboy/ssh-action@v0.1.10 with: host: ${{ secrets.SB_SERVER_HOST }} username: ${{ secrets.SB_SERVER_USER }} password: ${{ secrets.SB_SERVER_PASSWORD }} port: ${{ secrets.SB_SERVER_PORT }} script: 'cd /home/springboot/jpa-shop-maven-and-gradle && git pull && ./gradlew clean build && sh /home/springboot/jpa-shop-maven-and-gradle/ci-cd-script.sh'
3. 셀 스크립트 작성 sh /home/springboot/jpa-shop-maven-and-gradle/ci-cd-script.sh
nohup java -jar 실행 명령어를 workflows 에서 작성후 push 하면 github action 에서 리눅스에서 실행 종료 값이 반환 되지 않아 무한으로 실행 중으로 표시 된다.
따라서 리눅스에서 스크립트로 다음과 같이 작성해서 처리한다.
#!/bin/bash killall -9 java nohup java -jar /home/springboot/jpa-shop-maven-and-gradle/build/libs/ROOT.jar > Output.log 2>&1 &
※ CI/CD - Github Bitbucket pipelines 우분트 배포
1. bitbucket 에서 프로젝트 생성 후 생성 된 프로젝트에서 좌측메뉴의 파이프라인을 선택한다
여러가지 선택 템플릿이 존재하는데, 여기서는 커스텀으로 할것라 임의로 아무거나 선택하면 된다.
2. bitbucket-pipelines.yml 임의 이름으로 파일을 만든후 다음과 같이 작성한다.
# 이는 JavaScript용 샘플 빌드 구성입니다. # 더 많은 예시를 보려면 https://confluence.atlassian.com/x/14UWN에서 가이드를 확인하세요. # .yml 구성을 들여쓰려면 공백만 사용하세요. # ----- # Docker Hub의 사용자 지정 Docker 이미지를 빌드 환경으로 지정할 수 있습니다. image: atlassian/default-image:latest pipelines: default: - step: name: "SSH Deploy to production web" deployment: staging caches: - composer script: - echo "Deploying to production environment" - pipe: atlassian/ssh-run:0.2.2 variables: SSH_USER: $REACT_SSH_USER SERVER: $REACT_SSH_SERVER COMMAND: 'sh /home/react/bitbucket-react-deployment-script.sh' PORT : $REACT_SSH_PORT
매우 주의 : yml 코드 끝에 탭키나 줄바꿈 처리 등이 있으면 에러가 발생한다.
파이프라인 옵션에 관한 설명은 다음을 참조
https://velog.io/@banjjoknim/Bitbucket-Pipelines-살펴보기
$REACT_SSH_USER , $REACT_SSH_SERVER , $REACT_SSH_PORT 의 환경 변수가 있는데,
github actions 처럼 bitbucket 도 다음과 설정하면 된다.
3. 우분트서버 접속 변수 설정
맨 위의 우측 상단의 톱니 바퀴 모양에서 setting 를 클릭 -> Workspace setting 를 클릭한다.
->좌측 메뉴 맨 아래의 파이프라인의 Workspace variables 버튼을 클릭한다.
임의 변수명을 설정하고 값을 넣는다.
변수값들이 bitbucket-pipelines.yml 파일에서 다음과 같이 사용 되었다.
SSH_USER: $REACT_SSH_USER
SERVER: $REACT_SSH_SERVER
PORT : $REACT_SSH_PORT
4 Bitbutket 이 우분트서버에 접속 할수 있게 Fingerprint 를 설정해 준다.
프로젝트 -> 맨 하단 Repository setting -> SSH Keys
1)SSH Keys 에서 다음과 같이 키생성 버튼을 누르면 임의 SSH key 생성된다.
2)해당 키값을 리눅스의 ~/.ssh/authorized_keys 파일명으로 생성후 복사해서 넣는다.
3) 도메인 주소 혹은 아이피 주소 입력후 포트번호가 987 이라면 sample.com:987 값을 host address 에 입력후 Fetch 버튼을 누르면
fingerprint 가 생성된다.
4) add 버튼을 눌러 핑거프린트를 추가해 준다.
이 값이 있어야 권한 오류 없이 bitbucket 이 우분트 서버에 접속해서 CI/CD 작업을 진행 할수 있다.
5. 우분트 서버에서 : bitbucket 에 접속해서 소스를 다운 받을 수 있게 키값 등록 하기
다음을 참조해서 우분트 서버에서 키값을 생성후 상단 메뉴의 setting -> Workspace Setting -> SSH 키값을 등록 한다.
https://eminentstar.tistory.com/63
6. 우분트 서버에서 : Clone this repository HTTPS 로 다운 받을 때 비밀번호 설정하기
Clone this repository SSH 로 다운 받지 않고 HTTPS 로 다운받으면 비밀번호를 입력하라는 문구 나오는데,
다음을 참조해서 적용하도록 하자.
https://velog.io/@shj5508/Bitbucket-App-password-적용-방법
이때 git clone 을 하기 전에 다음을 먼저 실행하고 비밀번호를 입력하면 다음 부터는 입력하라는 문구가 나오지 않는다.
Credential 정보 저장
git config credential.helper store
credential.helper의 store 옵션을 주게되면 해당 git directory에선 반영구적으로 인증 절차가 생략됩니다.(저장된 credential 정보를 이용해 인증 처리)
7. 우분트 서버에서 : 셀 스크립트 작성하기
우분트 서버에서 다음과 같이 임의 셀스크립트 파일을 만든후 실행할 명령어를 입력한다.
여기서는 /home/react/bitbucket-react-deployment-script.sh 에서 파일을 생성 하였다.
bitbucket-react-deployment-script.sh
#!/bin/bash echo -e $PWD cd /home/react/bitbuket-front-end echo -e $PWD git pull npm install npm run build systemctl restart nginx
bitbucket 의 파이프라인의 bitbucket-pipelines.yml 파일에서 다음 코드가 bitbucket-react-deployment-script.sh 을 실행하는 명령어를 입력하면 된다.
COMMAND: 'sh /home/react/bitbucket-react-deployment-script.sh'
윈도우의 작업 환경에서 리액트 파일을 수정후 push 만 하면 자동으로 bitbucket 의 파이프라인이 우분트 서버에 패포까지 진행해서 처리해준다.
혹은 수동으로 우측 상단 메뉴에서 Run pipeline인 버튼을 눌러도 진행이 된다.
실행된 pipeline 목록에서 해당 메뉴를 클릭해서 들어가
오류가 있을시 좌측 상단에서 메뉴가 빨간색으로 표시되고 오류 메시지를 표시해 준다.
또한 , 우즉에서 build 에서 상세 보기를 통해 실행된 명령어들과 오류 발생시 해당 오류를 알 자세히 알 수 있다.
톰니바퀴 버튼을 누르면 bitbucket-pipelines.yml 파일을 수정할 수 있으며,
수정과 동시에 자동으로 실행 처리 된다.
댓글 ( 0)
댓글 남기기