본문 바로가기

ECS with gradle

개발 중에는 Java + Gradle 프로젝트에서 결과물을 확인하기 위해, ssh 플러그인을 사용해 개발 서버에 배포하고 있다. 실서버 ECS 배포를 위해 이미지를 만들고 ECR 에 푸시하고 Task 를 배포하는 플러그인이 있나 열심히 검색해 보았으나, 음... 생각만큼 지원이 덜 되어 있는거 같아 그냥 AWS Cli 명령으로 도배했다. 난 포기했지만 생각 있으면 써보시길...


'com.bmuschko.docker-remote-api'

'com.patdouble.awsecr'


build.gradle 로 처리하려는 것들을 나열해 봤다.


  1. 인스턴스 수량 증가
  2. Docker 이미지 빌드
  3. 기존 ECR latest 이미지 태그 변경 -> backup
  4. 이미지 ECR 로 푸시
  5. 서비스 업데이트 (배포)
  6. 인스턴스 수량 감소


이 모든걸 원클릭으로 끝내버리고 싶었는데, 배포 완료 시점을 알아야 했고 그로 인한 서브 작업들이 꼴도 보기 싫어서 그만 두었다. (꽤 지긋지긋해졌다) 저 얼마되지도 않는 작업을 실행하는데 필요한 오라질 도구가 참 많다. Docker, AWS Cli, ECS-Cli, PowerShell, ... 물론 Linux 환경이라면 PowerShell 하나 정도는 줄일 수 있겠지만 그러자고 Linux 환경을 하나 더 구축하는 것 역시 오라질이다. 그리고 ECR 이미지 태그를 변경하는 것 역시 포기했다. PowerShell 에서 AWSPowerShell 을 설치해야 되는데 에러 투성이다. 포기하고 backup 은 push 를 두 번 하는 방향으로; AWS Cli 로 통합하지 못하고 AWS Cli, ECS-Cli, PowerShell 을 다 깔 수 밖에 없게끔 만들어 놓은 이유가 참으로 궁금하다. docker 가 PowerShell 을 사용한 원죄가 있긴 하지만... 그렇게 따지면 윈도우를 만든... ㅡ.ㅡ;


아무튼 내가 구축하려는 구성은 기본 인스턴스 2 대, 배포시 4 대, 배포후 다시 2 대.

Cluster, Service, Task, ECR 등은 이미 콘솔에 생성되어 있다. (구성 내용 생략)


그리하여 실행할 gradle Task 는 3 개 정도 되겠다.

1,2,3,4 번을 묶어 backup 이 어찌될지 모르니 푸시까지 하나 (createImageNpushToECR), 5 번 배포 하나(ECSDeploy), 6 번 인스턴스 감소 하나(ECSInstanceDown).


* build.gradle


ext {
    clusterName = 'clusterName'
    serviceName = 'serviceName'
    taskName = 'taskName'
    stackName = 'EC2ContainerService-now-web'
    accountId = '8707500123456'
    region = 'ap-northeast-1'
    tagName = '870750012345.dkr.ecr.ap-northeast-1.amazonaws.com/web:latest'
    instanceMinCnt = 2
    instanceMaxCnt = 4
    getInstanceCnt = 0
}
 
task createImageNpushToECR() {
    group = 'docker'
    dependsOn war
    doFirst {
        exec { commandLine 'cmd''/c''aws ecr get-login --no-include-email --profile profileName > temp.cmd' }
        exec { commandLine 'cmd''/c''call temp.cmd' }
        exec { commandLine 'cmd''/c''del temp.cmd' }
    }
    doLast {
        exec { commandLine 'cmd''/c''docker build -t '+tags+' .' }
        exec { commandLine 'docker''push', tags }
    }
}
 
task ECSDeploy {
    group = 'docker'
    doLast {
        exec { commandLine 'cmd''/c''aws ecs update-service --force-new-deployment --profile profileName --cluster ' + clusterName + '  --service ' + serviceName + ' --task-definition ' + taskName }
    }
}
 
task ECSInstanceCnt {
    group = 'docker'
    doLast {
        new ByteArrayOutputStream().withStream { cnt ->
            exec {
                commandLine 'cmd''/c''aws ecs list-container-instances --cluster ' + clusterName + ' | findstr /R /C:"[' + accountId + ']" | find /c /v ""'
                standardOutput = cnt
            }
            getInstanceCnt = cnt.toString().toInteger()
        }
        println '# Current Web Instance Cnt : ' + getInstanceCnt
    }
}
 
task ECSInstanceUp {
    group = 'docker'
    dependsOn ECSInstanceCnt
    doLast {
        if (instanceMaxCnt > getInstanceCnt) {
            exec { commandLine 'cmd''/c''aws ecs update-service --cluster ' + clusterName + '  --service ' + serviceName + ' --deployment-configuration minimumHealthyPercent=50,maximumPercent=200' }
            exec { commandLine 'cmd''/c''ecs-cli configure --cluster ' + clusterName + ' --default-launch-type EC2 --region ' + region + ' --cfn-stack-name ' + stackName + ' --config-name ' + clusterName }
            exec { commandLine 'cmd''/c''ecs-cli scale --capability-iam --size ' + instanceMaxCnt + ' --cluster ' + clusterName + ' --aws-profile profileName' }
        }
    }
}
 
task ECSInstanceDown {
    group = 'docker'
    dependsOn ECSInstanceCnt
    doLast {
        if (instanceMinCnt < getInstanceCnt) {
            exec { commandLine 'cmd''/c''aws ecs update-service --cluster ' + clusterName + '  --service ' + serviceName + ' --deployment-configuration minimumHealthyPercent=100,maximumPercent=200' }
            exec { commandLine 'cmd''/c''ecs-cli configure --cluster ' + clusterName + ' --default-launch-type EC2 --region ' + region + ' --cfn-stack-name ' +  stackName + ' --config-name ' + clusterName }
            exec { commandLine 'cmd''/c''ecs-cli scale --capability-iam --size ' + instanceMinCnt + ' --cluster ' + clusterName + ' --aws-profile profileName' }
        }
    }
}
cs


대충 이렇다. 지금 딱 필요한 만큼만 꾸역꾸역 만들어 놓긴 했는데... 맘엔 안들지만 그래도 그냥 쓸란다.

간단하게 설명하자면...

  • createImageNpushToECR : war 파일 만들고 ECR 에 로그인 후 Docker 이미지 만들고 푸시.
  • ECSDeploy : Service 업데이트로 최신 이미지(latest) 배포
  • ECSInstanceCnt : 현재 Cluster 의 인스턴스 수량 (2개 설정)
  • ECSInstanceUp : 인스턴스 증가시킬 수량인 4 개 보다 작으면 4 개로 증설
  • ECSInstanceDown : 인스턴스 감소시킬 수량인 2 개 보다 크면 2 개로 감소


ECR 로그인은 gradle 소스에 있고 ECS-Cli 자격 증명 역시 aws profile 로 통합했다. AWS Cli 를 위한 자격 증명은 잘 등록해 보자... aws configure...


명령어도 윈도우 명령어로 바꿔 넣느라 삽질 좀 했다. 후...

개발에만 집중할 수 있게 해준다더니, 빡이쳐서 개발을 할 수가 없다. 아오...