이번에는 Jenkins를 이용해 Build & Test를 진행하고 완성된 파일을 S3에 올린 후 CodeDeploy를 이용해 배포하는 방법을 알아보겠다.
이번 설정은 S3에 올리는 것을 jenkins에 설정했고, CodeDeploy는 AWS 콘솔을 통해 실행(수동)하도록 한 것이다. 만약 Jenkins에서 한번에 수행이 가능하게 하려면 Publish artifacts to S3 Bucket 가 아닌 Deploy an application to AWS CodeDeploy 플러그인을 이용해 설정해야 한다.
로드밸런싱 되어있는 묶음은 단일건보다 이벤트가 많다. 그리고 로드밸런싱을 Block하는 단계에서 특정값을 수정하지 않으면 배포가 굉장히 오래걸린다(서버 healthcheck가 오래걸리기 때문).
여기에는 그부분을 다루지 않고 단일 서버에 한해 수행하는걸 다뤘다.
다음에 로드밸런스에 적합한 설정을 따로 올리도록 하겠다.
그리고 원활한 배포를 위해선 다음 2개의 파일을 생성해야 한다.
프로젝트와 분리해서 설정해도 되지만, 관리가 복잡해질 수 있으니 한 프로젝트 안에 넣는 것을 추천한다.
생성할 파일은 2가지다.
appspec.yml << CodeDeploy 에서 수행할 명령어를 설정 (이름변경하면 안됨)
deploy.sh << 수행할 쉘 스크립트(이름변경해도 됨)
그래서 다음과 같이 생성해두었다.
S3에 저장할 때에는 zip 파일로 압축하여 올려야 하는데, 그때 다음과 같은 파일이 구성되어야 한다.
1) *.war (또는 *.jar)
2) appspec.yml
3) deploy-before.sh (앱 배포 전 수행할 쉘스크립트)
3) deploy.sh (수행할 쉘스크립트)
이 3개가 압축되어 zip파일로 올라가면, AWS에서 CodeDeploy를 통해 S3 => EC2로 복사되며너 지정한 폴더로 압축해제하여 파일이 위치하게 된다. 지정폴더는 appspec.yml에서 설정한다.
appspec.yml 에는 다음과 같이 설정한다.
os는 윈도우가 아니라면 linux를 적는다.
file은 S3에 업로드 된 파일이 EC2에 어디로 이동시킬지를 지정한다
source설정에 '/'가 되어있는 것은, Code Build / S3 / Github 등에서 받은 모든 파일을 의미하며, destination은 이동할 위치를 설정하는 옵션이다.
permissions는 build 폴더에 압축이 풀린 후 소유자를 지정한다. 만약 지정하지 않으면 root 로 지정된다.
hooks는 수행할 script 파일과 사용자를 지정한다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/build/
permissions:
- object: /
pattern: "**"
owner: ec2-user
group: ec2-user
hooks:
BeforeInstall:
- location: deploy-before.sh
runas: ec2-user
ApplicationStart:
- location: deploy.sh
runas: ec2-user
deploy-before.sh 는 앱 배포전 수행할 스크립트를 저장한다.
if [ -d /home/ec2-user/build ]; then
sudo rm -rf /home/ec2-user/build/
fi
sudo mkdir -vp /home/ec2-user/build/
내용을 보면 /home/ec2-user/build 의 폴더를 삭제하고 다시 생성하는데, CodeDeploy를 실행하다가 안에 배포관련 파일이 있으면 이미 파일이 존재한다고 에러가 발생하며 배포에 실패한다. 때문에 폴더째로 삭제한다음 다시 생성한다.
The deployment failed because a specified file already exists at this location: /home/ec2-user/build/appspec.yml
The deployment failed because a specified file already exists at this location: /home/ec2-user/build/appspec.yml |
deploy.sh 는 수행할 스크립트를 저장한다
cd /usr/local/tomcat
bin/shutdown.sh
cd webapps
rm -rf ROOT/
cp ~/build/test-app.war ./ROOT.war
cd ..
bin/startup.sh
이정도로 마무리하고 git에 올려두도록 하자.
이제부터 젠킨스에서 배포된 것을 S3에 저장하기 위해 설정하는 방법에 대해 알아보자.
젠킨스에서 배포가 완료된 것을 S3에 저장하기 위해선 S3접근 가능 유저의 Access Key 있어야 한다. 관련해서는 IAM에서 설정이 가능하다.
# IAM에서 S3에 접근가능한 사용자 만들기
IAM에 접속해서 사용자를 생성한 후 AmazonS3FullAccess 권한을 주고 보안 자격 증명을 이용해 접근할 수 있도록(Access Key) 할 것이다
사용자추가를 클릭한 후, 사용자 이름을 지정하고 액세스 유형에 '프로그래밍 방식 액세스'를 체크한다.
그룹을 선택한다. 그룹이 없다면 새로 생성한다.
마지막으로 검토하고 사용자 만들기를 누른다.
다음 화면이 나타나면 .csv 다운로드를 클릭해 반드시 파일을 다운로드 받아두자. 여기에 접속할 수 있는 정보가 적혀있다. 혹은 비밀 액세스 키에 표시를 눌러서 키를 안전한곳에 저장해두자.
# Jenkins에서 s3 업로드 연동
이제 Jenkins 에 접속한다
Jenkins 관리 => 플러그인 관리 로 들어간 후 s3로 검색하면 S3 publisher plugin이 보인다. 설치.
설치완료 후 재가동하고 난 뒤, Jenkins 관리 => 시스템설정 에 들어간다.
아래쪽에 보면 Amazon S3 profiles 라는 설정화면이 보인다. 여기다가 위에서 생성한 Access key를 입력한다.
입력하고 난 뒤에는 Test Connection 을 반드시 클릭하여 확인해보자. 성공하면 Check passed! 가 보일것이고, 실패하면 계정 생성과 입력이 제대로 되었는지 확인이 필요하다.
설정을 저장한다.
# Jenkins 프로젝트 추가
프로젝트를 추가하고 Git과 Build 설정을 한다. 여기선 브런치 이름을 release로 했다.
Build 된 파일과 appspec.yml, deploy-before.sh, deploy.sh 파일을 한곳에 모아 Zip파일로 압축시킨다.
빌드 후 조치에서 S3에 저장할 수 있게 설정한다.
Publish artifacts to S3 Bucket을 클릭한 뒤 다음과 같이 채워넣는다.
Source: Execute shell 에서 지정한 폴더위치와 zip파일을 입력한다.
Destination bucket: 옮겨질 S3 버킷을 지정한다. 만약 버킷이 없다면 에러가 난다(아래 참조)
Bucket Region: 버킷이 저장될 지역을 지정한다. ap-norheast-2 는 한국 리전이다.
만약 저장하고자 하는 S3위치에 버킷이 없다면 다음과 같은 에러가 나므로 반드시 확인하자
Publish artifacts to S3 Bucket Build is still running
Publish artifacts to S3 Bucket Using S3 profile: [S3 프로파일 설정이름]
Publish artifacts to S3 Bucket bucket=app-deploy, file=deploy.zip region=ap-northeast-2, will be uploaded from slave=false managed=false , server encryption false
ERROR: Failed to upload files
com.amazonaws.services.s3.model.AmazonS3Exception: The bucket is in this region: us-east-1. Please use this region to retry the request (Service: Amazon S3; Status Code: 301; Error Code: PermanentRedirect; Request ID: D45CCC5222A0BE40; S3 Extended Request ID: t1txoT7n85/5Our0Wq4caw7PuV79eed/nKV7lz4TJ7pK4MmF4gG1IaGyM4p/ZZx66FnzczEtFao=; Proxy: null), S3 Extended Request ID: t1txoT7n85/5Our0Wq4caw7PuV79eed/nKV7lz4TJ7pK4MmF4gG1IaGyM4p/ZZx66FnzczEtFao=
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1811)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleServiceErrorResponse(AmazonHttpClient.java:1395)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1371)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1145)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:802)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:770)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:744)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:704)
at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:686)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:550)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:530)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:5062)
at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:5008)
at com.amazonaws.services.s3.AmazonS3Client.initiateMultipartUpload(AmazonS3Client.java:3581)
at com.amazonaws.services.s3.transfer.internal.UploadCallable.initiateMultipartUpload(UploadCallable.java:444)
at com.amazonaws.services.s3.transfer.internal.UploadCallable.uploadInParts(UploadCallable.java:216)
at com.amazonaws.services.s3.transfer.internal.UploadCallable.call(UploadCallable.java:146)
at com.amazonaws.services.s3.transfer.internal.UploadMonitor.call(UploadMonitor.java:115)
at com.amazonaws.services.s3.transfer.internal.UploadMonitor.call(UploadMonitor.java:45)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Build step 'Publish artifacts to S3 Bucket' changed build result to UNSTABLE
Finished: UNSTABLE
여기까지 했다면 이제 Build를 수행하여 S3에 저장되는지 확인해보자. 정상작동 된다면 S3에 다음과같이 저장될 것이다.
다운로드 받아 압축해제하면 다음과 같이 3개 파일을 확인할 수 있다.
# AWS 계정 Role 추가
EC2 Instance와 CodeDeploy를 수행할 Role을 각각 생성한다.
## EC2 용 역할 생성 및 지정
IAM => 액세스 관리 => 역할 페이지로 들어간 후 역할 만들기를 클릭한다.
EC2 를 선택한 후 다음을 선택한다.
AmazonEC2RoleforAWSCodeDeploy 권한을 검색하여 추가한다.
이름을 지정한 후 역할을 생성한다.
생성한 역할을 EC2 에 지정한다.
EC2를 새로 생성한다면 IAM 역할에 지정을, 기존 인스턴스에 부여할 거라면 다음 스크린샷을 따라가면 된다.
(지정할 EC2에서 마우스 오른쪽 버튼을 눌러 다음 옵션을 선택한다)
## CodeDeploy 용 역할 생성 및 지정(IAM 설정)
이번엔 CodeDeploy 용 역할을 생성한다.
IAM => 액세스 관리 => 역할 페이지로 들어간 후 역할 만들기를 클릭한다.
CodeDeploy를 선택하고 다음으로 넘어간다.
권한이 지정되어 있기 때문에 바로 다음으로 넘기면 된다.
생성한다.
# EC2 에 CodeDeploy agent 설치하기
CodeDeploy로 배포할 EC2 에 접속.
다음 명령어 수행
sudo yum install -y ruby
wget https://aws-codedeploy-ap-northeast-2.s3.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
rm -rf install
제대로 설치 & 서비스가 작동하고 있는지 확인
sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID 1160
# CodeDeploy 생성하기
AWS 콘솔에서 CodeDeploy => 애플리케이션 메뉴로 이동한다.
애플리케이션 생성 버튼을 클릭한다.
이름을 지정하고, 컴퓨팅 플랫폼은 EC2/온프레미스 를 선택한 후 생성을 완료한다.
생성완료가 되면 배포그룹으로 화면으로 이동하는데 여기서 배포그룹을 생성해야 한다.
배포 그룹 생성을 클릭한다.
이름은 임의로 입력하고, 서비스 역할을 이전에 생성한 test-codedeploy로 등록한다.(셀렉트로 선택가능하다)
배포유형은 현재 위치를 선택한다.
환경구성에서는 Amazon EC2 인스턴스를 클릭하면 태그를 통해 어떤것을 배포할 것인지 등록이 가능하다.
태그를 입력하면 일치하는 인스턴스가 무엇인지 확인가능하게 도와준다.
앞으로 사용할 EC2에 CodeDeploy Agent를 설치해야 하는데, 여기서 설치옵션이 있다. 그런데 실질적으로 설치되는거 같진 않으니 기본값으로만 설정하고 실제로 설치는 아래 다루도록 하겠다.
배포설정은 배포구성에 대한 옵션이다. 관련 문서는 다음 사이트에 상세히 나와있다.
https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/deployment-configurations.html
간략하게 요약하자면,
- CodeDeployDefault.AllAtOnce:한 번에 가급적 많은 수의 인스턴스 어플리케이션 개정을 배포시도한다. 하나 이상의 인스턴스에 애플리케이션 개정이 배포되면 전체 배포 상태가 성공으로 표시.
- CodeDeployDefault.HalfAtATime:최대 절반의 인스턴스에 한번에 배포한다. 절반이상이 배포성공하면 전체 배포를 성공으로 표시.
- CodeDeployDefault.OneAtATime:한 번에 한 인스턴스에만 배포한다. 하나라도 틀리면 배포실패.
서버 환경에 따라 설정하면 되겠다.
마지막으로 로드 밸런서 설정이다. 이번 포스팅에는 로드밸런서를 제외한 설정이니 체크를 해제한다.
배포그룹을 생성한다.
# 배포 실행하기
이제 마지막으로 배포를 수행해보자.
AWS 콘솔에서 CodeDeploy => 애플리케이션 메뉴로 이동한다.
수행할 애플리케이션을 선택한 다음 애플리케이션 배포를 클릭한다.
배포 그룹은 방금 설정한 배포그룹을 지정하면 되고, 개정 위치는 빌드관련 파일을 s3에 올린 위치를 지정해주면 된다. S3에 들어가 파일을 클릭한다음 경로 복사 버튼을 누르면 경로가 복사되고, 개정 위치에 등록하면 된다.
개정파일형식은 .zip을 선택한다.
그 외 별도 설정은 생략하고 배포 생성을 누른다.
그럼 자동으로 배포를 수행한다.
EC2 에 접속해서 build 관련 폴더와 파일이 존재하는지 확인한다
[ec2-user@ip-10-0-124-1 ~]$ ls
build install
[ec2-user@ip-10-0-124-1 ~]$ cd build/
[ec2-user@ip-10-0-124-1 build]$ pwd
/home/ec2-user/build
[ec2-user@ip-10-0-124-1 build]$ ls -al
합계 102568
drwxr-xr-x 2 ec2-user ec2-user 70 7월 15 16:43 .
drwx------ 4 ec2-user ec2-user 124 7월 15 16:43 ..
-rw-r--r-- 1 ec2-user ec2-user 244 7월 15 16:43 appspec.yml
-rw-r--r-- 1 ec2-user ec2-user 105018641 7월 15 16:43 test-app.war
-rw-r--r-- 1 ec2-user ec2-user 128 7월 15 16:43 deploy.sh
-rw-r--r-- 1 ec2-user ec2-user 128 7월 15 16:43 deploy-before.sh
사이트에 접속해서 정상작동하는지 확인한다.
끝.
참조:
https://velog.io/@minholee_93/Jenkins-Springboot-Gradle-Github-CodeDeploy-ELB-2-1yk5ze6ky0
https://jojoldu.tistory.com/315
https://senticoding.tistory.com/90
'공부 > 프로그래밍' 카테고리의 다른 글
[aws] CodeDeploy 를 이용해 로드밸런서 환경에서 배포하기 (0) | 2020.07.23 |
---|---|
[aws] CodeDeploy 중 BeforeBlockTraffic 진행이 안될 때 (0) | 2020.07.21 |
[data] airflow 설치(DB: mysql) (0) | 2020.07.02 |
[springboot] @Valid 를 이용해 request 시 필수파라미터 체크하기 (0) | 2020.06.23 |
[aws] EC2 에 JAVA 버전 11로 업데이트 하기 (0) | 2020.06.18 |
댓글