## 들어가기
**배포 작업 중에는 반복되는 작업이 많다**. 대표적인 반복 작업들은 다음과 같다.
>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) 때문이다.