## 들어가기 **배포 작업 중에는 반복되는 작업이 많다**. 대표적인 반복 작업들은 다음과 같다. >0. AWS 인스턴스에 docker & docker-compose 설치 >1. Jar 파일 빌드 >2. Jar 파일을 docker 이미지로 빌드 >3. Docker 이미지를 `.tar` 파일로 저장 >4. `.tar` 파일을 AWS 인스턴스로 전송 >5. AWS 인스턴스에서 `.tar` 파일 docker 이미지로 읽기 >6. docker-compose로 docker container 생성 1번 작업은 [[1. MSA의 정의와 필요한 경우|MSA]] 개발 중에 AWS 인스턴스를 생성할 때마다 해야 하고, 2-7번은 마이크로서비스에 변경이 생겨 수정했을 때마다 해야 한다. 이런 반복 작업은 마이크로서비스가 1-2개일 때는 괜찮아도 마이크로서비스가 늘어날 수록 점점 더 괴로운 작업이 된다. CI/CD, '지속적인 개발/지속적인 배포'의 중요성이 대두된 것도 이런 맥락이다. **이 글에서는 윈도우 powershell에서 Shell 프로그래밍으로 배포 작업을 반-자동화[^1]하는 방법을 다룬다**. CI/CD 도구가 존재함에도 굳이 Shell 프로그래밍을 하는 이유는 (1) 재밌을 거 같아서, (2) CI/CD 도구에서도 Shell 프로그래밍이 필요해서 이 두 가지이다. ## 기본 함수 만들기 ### AWS 인스턴스 Config 반환 함수 #### 코드 ```powershell # cicd/shell/config/XXX.ps1 function get-config {     $ppkKeyPath = [ppk 파일 경로]     $remoteUser = [user 이름, ec2에서는 ec2-user가 기본값]     $remoteHost = [퍼블릭 IPv4]     return $ppkKeyPath, $remoteUser, $remoteHost } ``` #### 사용 코드 ```powershell . "[프로젝트 디렉터리 경로]/cicd/shell/config/XXX.ps1" $ppkKeyPath, $remoteUser, $remoteHost = get-config ``` - `$ppkKeyPath`, `$remoteUser`, `$remoteHost` 이 세 가지 값은 AWS 인스턴스 원격 조종에 계속해서 쓰이는 값이기 때문에 한 파일에서 일괄적으로 관리하는 것이 좋다. ### AWS 인스턴스 원격 조종 함수 >[!Requirement] >- [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html)의 pscp.exe와 plink.exe (둘 다 환경 변수 등록) #### 코드 ```powershell # cicd/shell/aws-util.ps1 function remote-command-to-aws {     param(         [string]$configPath,         [string]$command     )         . $configPath     $ppkKeyPath, $remoteUser, $remoteHost = get-config     $result = & plink -ssh -i $ppkKeyPath $remoteUser@$remoteHost $command     $status = $LASTEXITCODE     return $status, $result } ``` #### 사용 코드 ```powershell . "[프로젝트 디렉터리 경로]/cicd/shell/aws-util.ps1" $status, $result = remote-command-to-aws -configPath "[config 파일 경로]" -command "date" Write-Output "status: $status" Write-Output "result: $result" ``` - `-command`로 입력 받은 명령어를 보낸 다음 status와 명령어 수행 결과를 반환하는 함수이다. - `-command`로 여러 명령을 한 번에 실행시키고 싶으면 `명령 1 && 명령 2 && ...` 이런 식으로 명령을 작성하면 된다. ### AWS 인스턴스 파일 복사 함수 #### 코드 ```powershell # cicd/shell/aws-util.ps1 function copy-file-to-aws {     param(         [string]$configPath,         [string]$localFilePath,         [string]$remoteFilePath     )         . $configPath     $ppkKeyPath, $remoteUser, $remoteHost = get-config     $result = & pscp -i $ppkKeyPath $localFilePath $remoteUser@$remoteHost:$remoteFilePath     $status = $LASTEXITCODE     return $status, $result } ``` #### 사용 코드 ```powershell . "[프로젝트 파일 경로]/cicd/shell/aws-util.ps1" $status, $result = remote-command-to-aws -configPath "[config 파일 경로]" -localFilePath "[로컬 파일 경로]" -remoteFilePath "원격 파일 경로" Write-Output "status: $status" Write-Output "result: $result" ``` - `-localFilePath`에 있는 파일을 `-remoteFilePat`로 복사하는 함수이다. ### Docker 이미지 빌드 > tar 파일 생성 함수 #### 코드 ```powershell # cicd/shell/docker-util.ps1 function write-tar-from-project {     param (         [string]$projectPath,         [string]$tarBasePath     )     $name = (Split-Path -Path $projectPath -Leaf)     docker build -t $name $projectPath     docker save -o "$tarBasePath/$name.tar" $name } ``` #### 사용 코드 ```powershell . "[(전체) 프로젝트 디렉터리 경로]/cicd/shell/docker-util.ps1" write-tar-from-project -projectPath "[(대상) 프로젝트 디렉터리 경로]" -tarBasePath "[.tar 파일 저장하는 디렉터리 경로]" ``` - 프로젝트 경로를 입력하면 경로에 있는 `dockerFile`로 도커 이미지를 빌드 후 이미지로 tar 파일을 생성하는 함수이다. - dockerFile의 인자는 따로 없다고 가정하고 작성했다. - 이미지와 tar 파일은 모두 프로젝트의 이름으로 자동 설정된다. ### Jar 파일 빌드 > Docker 이미지 생성 > tar 파일 생성 함수 #### 코드 ```powershell # cicd/shell/docker-util.ps1 function write-tar-from-java-project {     param (         [string]$projectPath,         [string]$tarBasePath     )     if (Test-Path "$projectPath/build" -PathType Container) { Remove-Item -Path "$projectPath/build" -Recurse -Force } $projectPath/gradlew bootBuildImage $name = (Split-Path -Path $projectPath -Leaf) docker save -o "$tarBasePath/$name.tar" $name } ``` #### 사용 코드 ```powershell . "[(전체) 프로젝트 디렉터리 경로]/cicd/shell/docker-util.ps1" write-tar-from-java-project -projectPath "[(대상) 프로젝트 디렉터리 경로]" -tarBasePath "[.tar 파일 저장하는 디렉터리 경로]" ``` - `clean` 대신 build 디렉터리를 통째로 지우는 이유는 윈도우에서 `clean`을 했을 때 가끔 build 디렉터리가 안 지워져서 중지되는 경우가 있기 때문이다. - `bootBuildImage` 명령어를 통해서 이미지를 빌드하기 때문에 Dockerfile을 따로 작성하지 않아도 된다. ## 작업 자동화 함수 ### 특정 프로젝트 배포 ### 특정 마이크로서비스 배포 ### 전체 배포 [^1]:자동화가 아닌 반-자동화라고 적은 이유는 Git commit과 별개로 ps1 파일을 직접 실행시켜야 하고(1), dockerFile이나 docker-compose 파일을 직접 작성해야 하기(2) 때문이다.