Jenkins 구동 EC2 Instance에 로컬 파일 복사
Jenkins로 자동으로 빌드 및 배포를 수행한다지만, 자동을 위한 수동 작업이 많이 필요한법이다.
Jenkins에서 내 github master 브랜치를 바라보고 있어서 내 github 원격 repo에 존재하는 파일들은 걱정이 없지만, 보안을 위해서 gitignore에 추가된 application-****.yml 파일들은 현재 Jenkins가 구동되는 vm에 없는 상황이다.
정상적인 빌드를 위해선 모든 application.yml 파일들이 필요하기에 로컬 환경에서 jenkins ec2 instance로 파일들을 밀어넣는 과정을 수행하겠음
jenkins가 구동되는 ec2 keypair가 존재하는 디렉토리로 이동한 후 powershell 스크립트를 열어 아래 명령어 실행
scp -i "my_key_0.pem" {복사할 파일} {접속 유저명, 보통 ubuntu}@{DNS}:~{내 프로젝트 경로상 필요한 곳}
Jenkins EC2 instance에서 내 github project가 clone & pull 되고있는 디렉토리에서, application.yml이 추가되어야 할 디렉토리인 src/main/resources 디렉토리를 찾는다. 나의 경우는 /jenkins/workspace/hh/src/main/resources 임
이 디렉토리에 원격 repo에는 없는 모든 application-****.yml을 넣을 것
scp -i "jenkins-ssh-key.pem" C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-deploy.yml ubuntu@{내 jenkins private dns}:/jenkins/workspace/hh/src/main/resources
dns 주소는 aws ec2 instance 탭에서 확인 가능
하나하나 다 치려니까 귀찮고, application.yml 파일이 하나 더 추가되면 다시 이짓을 해야하는 게 싫어서 powershell script 파일을 만들었다.
chatgpt의 힘을 빌려 powershell script 파일 작성하기
Q.
cd C:\\Users\\sbl\\Desktop\\aws\\EC2_freetier_keypair
scp -i "jenkins-ssh-key.pem" C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-deploy.yml ubuntu@{내 jenkins private dns}:/jenkins/workspace/hh/src/main/resources
scp -i "jenkins-ssh-key.pem" C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-deploy.yml ubuntu@{내 jenkins private dns}:/jenkins/workspace/hh/src/main/resources
scp -i "jenkins-ssh-key.pem" C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-local.yml ubuntu@{내 jenkins private dns}:/jenkins/workspace/hh/src/main/resources
scp -i "jenkins-ssh-key.pem" C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-secret.yml ubuntu@{내 jenkins private dns}:/jenkins/workspace/hh/src/main/resources
scp -i "jenkins-ssh-key.pem" C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-dev.yml ubuntu@{내 jenkins private dns}:/jenkins/workspace/hh/src/main/resources
위의 powershell 명령을 powershell 스크립트로 작성해줘
A.
물론이죠! 다음은 해당 scp 명령을 실행하는 PowerShell 스크립트의 한글 번역입니다:
powershell
Copy code
# 개인 키 파일 경로 설정
$privateKey = "C:\\Users\\sbl\\Desktop\\aws\\EC2_freetier_keypair\\jenkins-ssh-key.pem"
# 원격 서버 정보 설정
$remoteServer = "{내 jenkins private dns}"
$remoteUser = "ubuntu"
$remotePath = "/jenkins/workspace/hh/src/main/resources"
# 로컬 파일 경로 설정
$localFiles = @(
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-deploy.yml",
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-local.yml",
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-secret.yml",
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-dev.yml"
)
# 각 로컬 파일에 대해 scp 명령 실행
foreach ($localFile in $localFiles) {
$scpCommand = "scp -i `"$privateKey`" `"$localFile`" $remoteUser@$remoteServer:`"$remotePath`""
Invoke-Expression $scpCommand
}
이 스크립트를 .ps1 확장자로 저장하고 PowerShell에서 실행하십시오. 시스템에서 스크립트를 실행할 권한이 필요하므로 필요한 경우 스크립트 실행을 허용하도록 다음과 같은 PowerShell 명령을 실행할 수 있습니다:
powershell
Copy code
Set-ExecutionPolicy RemoteSigned
참고: PowerShell 환경에서 scp 명령이 사용 가능하다고 가정하며, 필요한 경우 추가 설정 또는 설치가 필요할 수 있습니다. 또한 실제 구성에 기반하여 경로 및 서버 정보를 조정하십시오.
모든 내용을 gpt가 해결 해 주지 않아서 나머지는 직접 작성했다.
# 로그 파일 경로 설정
$logFilePath = "C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\myresource\\jenkins\\send-property-to-jenkins-log.txt"
# 로그 기록
Start-Transcript -Path $logFilePath -Append
# jenkins ec2 private key 경로
$privateKey = "C:\\Users\\sbl\\Desktop\\aws\\jenkins-ssh-key.pem"
# jenkins ec2 정보
$remoteServer = "{내 jenkins private dns}" # dns 주소
$remoteUser = "ubuntu" # 접속할 user명
$projectName = "hh" # 프로젝트명, jenkins의 item 명과 같다.
$remotePath = "/jenkins/workspace/$projectName/src/main/resources/" # property 파일이 복사되어야 할 디렉토리
$pemRemotePath = "/jenkins/workspace/keys/$projectName" # deploy ec2에 접속 가능한 pem
# 내 컴퓨터에서 git ignore에 추가된 properties 경로들 {dev, local, secret, deploy}
$localFiles = @(
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-deploy.yml",
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-local.yml",
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-secret.yml",
"C:\\Users\\sbl\\programming\\IdeaProjects\\hanghaero\\src\\main\\resources\\application-dev.yml"
)
$pemFile = "C:\\Users\\sbl\\Desktop\\aws\\EC2_freetier_keypair\\ec2freetierkeypair.pem"
# 각 로컬 파일에 대해 scp 명령 실행
foreach ($localFile in $localFiles)
{
$scpCommand = "scp -i '$privateKey' '$localFile' $remoteUser@$( $remoteServer ):$( $remotePath )" #remoteServer 변수 사용간 @$remoteServer로 사용하면 해시테이블 표기법이 된다 주의, $($변수명) 문법은 변수를 정확히 표현하기 위함
Write-Output $scpCommand
Invoke-Expression $scpCommand
}
# pem파일 전송
$createDirCommand = "ssh -i '$privateKey' $remoteUser@$( $remoteServer ) 'sudo mkdir -p $pemRemotePath'"
Write-Output $createDirCommand
Invoke-Expression $createDirCommand
# pem파일이 복사될 폴더 권한 설정
$chmodCommand = "ssh -i '$privateKey' $remoteUser@$( $remoteServer ) 'sudo chmod 777 $pemRemotePath'"
Write-Output $chmodCommand
Invoke-Expression $chmodCommand
$scpCommand = "scp -i '$privateKey' '$pemFile' $remoteUser@$( $remoteServer ):$( $pemRemotePath )"
Write-Output $scpCommand
Invoke-Expression $scpCommand
#로그 기록 종료
Stop-Transcript
이제 로컬에서 이 powershell 스크립트를 정상 실행하면 build에 필요하지만 원격 repo에는 없는 모든 파일들이 jenkins가 구동되는 ec2 instance에 전송된다.
error : Permission denied
만약 permission denied 오류가 발생한다면?
/jenkins/workspace/hh/src/main/resources 디렉토리 즉, 복사하여 붙여 넣을 디렉토리에 대하여 아래 명령어로 접속하게된 접속 유저가 해당 디렉토리에 권한이 없는 것
scp -i "my_key_0.pem" {복사할 파일} {접속 유저명, 보통 ubuntu}@{DNS}:~{내 프로젝트 경로상 필요한 곳}
jenkins 가 켜진 ec2의 /jenkins/workspace/hh/src/main로 이동해서
chmod 777 resources
명령으로 write 권한을 풀어준다.
위의 권한 수정으로 추후 발생할 수 있는 github 오류가 존재한다.
dubious ownership 오류
fatal: detected dubious ownership in repository at '/jenkins/workspace/hh' To add an exception for this directory, call:
git config --global --add safe.directory /jenkins/workspace/hh
방금 github repository를 pull 해온 경로에 있는 폴더의 권한을 맘대로 777로 바꿔버려서 발생하는 오류인데, github입장에서는 쟤만 권한이 777인게 모호할 수 있긴 하다. github가 믿고 앞으로도 계속 project를 pull 해올 수 있도록 safe directory에 추가해줘야한다.
해결 방법은 에러 메세지가 시키는 대로 하면 된다.
git config --global --add safe.directory /jenkins/workspace/hh
이렇게 jenkins가 있는 ec2환경에 application-***.yml 파일들을 다 집어넣었다.
왜 Build Task : test에서 오류가 발생할까?
이러한 상황에서 build하게 되면 build task 중 test task 에서 에러가 발생할 것이다.
> Task :test
HanghaeroApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:143
Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1770
Caused by: org.hibernate.service.spi.ServiceException at AbstractServiceRegistryImpl.java:277
Caused by: org.hibernate.HibernateException at DialectFactoryImpl.java:190
원인은 CI/CD 파이프라인을 구축하는 이유와도 연관이 있는데,
test 코드에서 실패했기에 springboot 프로젝트 자체적으로 build 자체를 막아버린 것이다.
문제를 해결하기에 앞서 우선 빌드 과정 및 개발간에 테스트를 진행할 때는 test하는 환경을 따로 정의해주는 것이 좋다.
현재 프로젝트에는 test 환경을 위한 application.yml이 정의되어있지 않으니 기본적으로 지정된 profile의 설정들을 가지고 테스트 코드를 실행할 수 없었던 것.
환경설정의 문제가 아닌 실제 테스트 코드가 통과되지않음으로써 build가 실패하는 것은 CI/CD 파이프라인에 대하여 당연한 얘기지만, 지금 우리는 환경설정의 문제로 build가 되지 않고 있으므로, 환경설정을 진행해주겠다.
아래 디렉토리에 application-test.yml 파일을 생성한다.
아래 내용을 입력한다.
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb;MODE=MYSQL
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
Q. 왜 갑자기 h2 db를 사용하시나요? RDS(mysql)랑 연결할거 아닌가요?
A. 테스트 환경에서는 모든 다른 변수들과 독립적으로 진행될 수 있어야 진정한 단위 테스트입니다. 따라서 프로젝트 내에서 테스트환경일때 h2 종속성을 추가하여 repository 테스트 코드들은 h2 db와 작업하여 테스트 결과를 도출하도록 설정했습니다.
그리고 자동생성된 Test 어플리케이션에 Profile을 test로 지정하고, 기본적으로 정의된 context-load 테스트를 우선 삭제한다.
그리고 아직 추가되지 않은 h2 종속성을 build.gradle 파일에서 추가해줘야한다.
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-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
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'
testImplementation 'com.h2database:h2' // 테스트 환경일 때 h2 db 사용
}
이후로는 테스트 코드의 실패 없이 정상적으로 빌드할 수 있다.
Je> Task :test HanghaeroApplicationTests > contextLoads() FAILED java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:143 Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1770 Caused by: org.hibernate.service.spi.ServiceException at AbstractServiceRegistryImpl.java:277 Caused by: org.hibernate.HibernateException at DialectFactoryImpl.java:190
원인은 CI/CD 파이프라인을 구축하는 이유와도 연관이 있는데,
test 코드에서 실패했기에 springboot 프로젝트 자체적으로 build 자체를 막아버린 것이다.
우선 빌드 과정 및 개발간에 테스트를 진행할 때는 test하는 환경을 따로 정의해주는 것이 좋다.
환경설정의 문제가 아닌 실제 테스트 코드가 통과되지않음으로써 build가 실패하는 것은 당연하다는 얘기,
지금 우리는 환경설정의 문제로 build가 되지 않고 있으므로, 환경설정을 진행해주겠다.
다음과 같이 application-test.yml 파일을 생성하여 아래 내용을 입력한다.
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb;MODE=MYSQL
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
Q. 왜 갑자기 h2 db를 사용하시나요? RDS랑 연결할거 아닌가요?
A. 테스트 환경에서는 모든 다른 변수들과 독립적으로 진행될 수 있어야 진정한 단위 테스트입니다. 따라서 프로젝트 내에서 테스트환경일때 h2 종속성을 추가하여 repository 테스트 코드들은 h2 db와 작업하여 테스트 결과를 도출하도록 설정했습니다.
그리고 자동생성된 Test 어플리케이션에 Profile을 test로 지정하고, 기본적으로 정의된 context-load 테스트를 우선 삭제한다.
그리고 아직 추가되지 않은 h2 종속성을 build.gradle 파일에서 추가해줘야한다.
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-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
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'
testImplementation 'com.h2database:h2' // 테스트 환경일 때 h2 db 사용
}
이후 Jenkins 에서 지금 빌드를 누르면 테스트 코드의 실패 없이 정상적으로 빌드할 수 있다.