코알못

[Docker] Docker 빌드를 위한 Jenkins 활용 - Jenkins로 Docker 빌드 본문

ETC

[Docker] Docker 빌드를 위한 Jenkins 활용 - Jenkins로 Docker 빌드

코린이s 2023. 3. 18. 21:43
728x90

이번시간에는 Jenkins로 CI/CD 실습을 진행하도록 해보자!

우선 첫 시간에 만든 배포 서버 즉 서비스 서버를 Jenkins 가 접근 할 수 있도록 배포서버 접속 할때 사용하는 pem 파일(개인키)를 열어 복사하고 [이전 시간]에 했던 방법대로 private key 를 deploy-key 명으로 등록한다.

키 항목을 확인 하면 아래와 같이 추가한 deploy-key가 나온다.

배포 서버에서는 AWS ECR 로그인해서 docker 를 통해 이미지 내려받고 컨테이너 실행 하기에 AWS 설정, docker 설치가 되어 있어야 한다.

저번 시간에 docker 설치까지 해두었으니 AWS 설정을 진행하도록 한다.

아래와 같이 설정 하려고 하니 설치가 안되어 있다고 나온다. 우선 설치를 진행하자!

$ aws configure
Command 'aws' not found, but can be installed with:
sudo snap install aws-cli  # version 1.15.58, or
sudo apt  install awscli   # version 1.22.34-1
See 'snap info aws-cli' for additional versions.
$ sudo apt  install awscli

 

aws 설정 명령어를 입력하며 이전시간에 발급받은 aws key 를 입력한다.

$ aws configure
AWS Access Key ID [None]: abc
AWS Secret Access Key [None]: abc
Default region name [None]: ap-northeast-2
Default output format [None]:

 

이제 도커 이미지 저장소인 ECR 를 생성한다.

'AWS Console > ECR > repositories > 리포지토리 생성'을 클릭 한다.

모두 기본으로 두고 '프라이빗', '이름' 부분만 선택하고 리포지토리생성을 클릭한다!

도커 빌드 및 ECR 에 푸시 하는 부분을 정의 한다.

아래는 이전 시간에 작성한 build.gradle 파일이며 여기서 to 부분만 제거한다.

plugins{
...
id 'com.google.cloud.tools.jib' version '3.3.1'
}
...
jib {
  from {
    image = 'openjdk:11' // base image
  }
  //to {
  //  image = '<dockerhub user name>/corin' // image name
  //  tags = ['1.1'] // tag
  //}
  container {
    entrypoint = ['java','-jar', 'app.jar'] // entrypoint
    jvmFlags = ['-Xms512m', '-Xmx512m'] // java option > 현 메모리에 맞게 옵션 설정후 테스트 진행
	ports = ['8080'] // expose
    labels = [version:project.version, name:project.name, group:project.group] // label >> docker inspect 로 상세 정보 확인시 볼 수 있다.
    creationTime = 'USE_CURRENT_TIMESTAMP'
    format = 'Docker'
  }
  extraDirectories {
    paths {
      path {
        from = file('build/libs')
      }
    }
  }
}

그 이유는 배포에 관련된 부분은 Jenkins 파일에서 한번에 보기 위함이다.

이제 Jenkins 파일을 작성해보자!

Jenkinsfile

def mainDir="<gradlew exist main directory>"
def ecrLoginHelper="docker-credential-ecr-login"
def region="<AWS Region>"
def ecrUrl="<AWS ECR URL>"
def repository="<ECR Repository Name>"
def deployHost="<Deploy Server Private IP>"

pipeline {
    agent any

    stages {
        stage('Pull Codes from Github'){
            steps{
                checkout scm
            }
        }
        stage('Build Codes by Gradle') {
            steps {
                sh """
                cd ${mainDir}
                ./gradlew clean build
                """
            }
        }
        stage('Build Docker Image by Jib & Push to AWS ECR Repository') {
            steps {
                withAWS(region:"${region}", credentials:"aws-key") {
                    ecrLogin()
                    sh """
                        curl -O https://amazon-ecr-credential-helper-releases.s3.us-east-2.amazonaws.com/0.4.0/linux-amd64/${ecrLoginHelper}
                        chmod +x ${ecrLoginHelper}
                        sudo mv ${ecrLoginHelper} /usr/local/bin/
                        cd ${mainDir}
                        ./gradlew jib -Djib.to.image=${ecrUrl}/${repository}:${currentBuild.number} -Djib.console='plain'
                    """
                }
            }
        }
        stage('Deploy to AWS EC2 VM'){
            steps{
                sshagent(credentials : ["deploy-key"]) {
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@${deployHost} \
                     'aws ecr get-login-password --region ${region} | docker login --username AWS --password-stdin ${ecrUrl}/${repository}; \
                      docker run -d -p 80:8080 -t ${ecrUrl}/${repository}:${currentBuild.number};'"
                }
            }
        }
    }
}

하나씩 설명하면 아래와 같다.

우선 아래 파이프라인을 실행할 에이전트 유형을 고를수 있다. 

현재 설정은 any로 모든 agent 에서 실행 가능하다는 의미로

pipeline {
    agent any

아래와 같이 설정할 경우 label이 node 인 agent 만 파이프라인 실행 가능한 의미이다.

사실 언제 쓰이는지는 잘 모르겠다.. 차차 배워가는것으로 한다.

pipeline {
  agent { label 'node-1' }

'Pull Codes from Github'는 scm(Software Configuration Management)을 통해 main 코드를 checkout 한다는 의미이다.

 

stage('Pull Codes from Github'){
            steps{
                checkout scm
            }
        }

'Build Codes by Gradle' 는 받은 소스코드의 gradlew 경로에 가서 clean build 를 실행한다.

 stage('Build Codes by Gradle') {
            steps {
                sh """
                cd ${mainDir}
                ./gradlew clean build
                """
            }
        }

'Build Docker Image by Jib & Push to AWS ECR Repository' 는 ecr helper를 통해 aws-key 참고하여 ecr 에 접근가능하도록 하며 curl 호출시 ecr helper 실행 파일이 생성되며 bin 파일로 이동시킨다. (없으면 .. 된다.)

그런 다음 docker 파일을 빌드 하고 ECR 저장소에 푸시 한다.

stage('Build Docker Image by Jib & Push to AWS ECR Repository') {
            steps {
                withAWS(region:"${region}", credentials:"aws-key") {
                    ecrLogin()
                    sh """
                        curl -O https://amazon-ecr-credential-helper-releases.s3.us-east-2.amazonaws.com/0.4.0/linux-amd64/${ecrLoginHelper}
                        chmod +x ${ecrLoginHelper}
                        sudo mv ${ecrLoginHelper} /usr/local/bin/
                        cd ${mainDir}
                        ./gradlew jib -Djib.to.image=${ecrUrl}/${repository}:${currentBuild.number} -Djib.console='plain'
                    """
                }
            }
        }

'Deploy to AWS EC2 VM' 는 deploy-key 를 통해 배포 서버에 ssh 접속하며

 -o StrictHostKeyChecking=no 는 yes/no 를 나오지 않도록 한다.

그 뒤에 aws ecr 명령어를 통해 docker 이미지를 가져오고 컨테이너를 실행한다.

stage('Deploy to AWS EC2 VM'){
            steps{
                sshagent(credentials : ["deploy-key"]) {
                    sh "ssh -o StrictHostKeyChecking=no ubuntu@${deployHost} \
                     'aws ecr get-login-password --region ${region} | docker login --username AWS --password-stdin ${ecrUrl}/${repository}; \
                      docker rm --force app; \
                      docker run --name app -d -p 80:8080 -t ${ecrUrl}/${repository}:${currentBuild.number};'"
                }
            }
        }

이제 해당 서비스 소스 파일을 github 에 올려보자!

github > new repository 누른뒤 저장소를 생성한다.

아래 코드를 입력하여 소스 코드를 git 에 푸시 한다.

echo "# app" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/works-code/app.git
git push -u origin main

푸시 할때 username/password 입력 창이 뜨는데 'remote: Support for password authentication was removed on August 13, 2021.' 오류 발생시 패스워드 입력 방식에서 토큰 입력 방식으로 변경 되어 발생하는 이슈로 하단 참고 사항을 참고한다.

이제 Jenkins Item을 아래 부분만 수정하고 생성한다.

에러 나는 'stderr: No ECDSA host key is known for github.com and you have requested strict checking.' 부분은 아래 참고 사항 참고한다.

이제 빌드를 눌러 진행하고 아래 #1 > console output을 눌러 빌드 상황을 본다. 

만약 빌드 에러가 발생 한다면 아래 참고 사항을 참고하며 저자는 15번 시도 끝에 성공 했다.

확인해보자!

이미지 배포가 정상적으로 되었는지 먼저 확인 한다!
빌드 로그를 보면 15번 이미지 태그로 올라간것을 볼 수 있으며 ECR 에서 확인 하면 정상적으로 15 태그로 올라간것을 볼 수 있다.

다음으로 서비스/배포 서버에 이미지가 정상적으로 받아졌고 컨테이너가 정상적으로 떠있는지 본다!

이미지 15번 태그이며 컨테이너가 해당 이미지 기반으로 UP 상태 인것을 볼 수 있다.

$ docker images
REPOSITORY                                               TAG       IMAGE ID       CREATED         SIZE
123.dkr.ecr.ap-northeast-2.amazonaws.com/dev-olivia-app   15        2d4310b0de24   7 minutes ago   689MB
$ docker ps -a
CONTAINER ID   IMAGE                                                                 COMMAND               CREATED         STATUS         PORTS                                   NAMES
1f800ec7e481   123.dkr.ecr.ap-northeast-2.amazonaws.com/dev-olivia-app:15   "java -jar app.jar"   6 minutes ago   Up 6 minutes   0.0.0.0:80->8080/tcp, :::80->8080/tcp   suspicious_mirzakhani

이제 서비스를 호출해보면 정상적으로 문구를 출력한다!

$ curl http://localhost:80
hello corin

끝!! 

# 참고사항

1) remote: Support for password authentication was removed on August 13, 2021.

- 해결 : github > setting > 하단 Developer settings 클릭 > personal access tokens > new token 클릭 하여 토큰을 생성하면 토큰이 나오는데 복사하여 username 은 저자의 경우 works-code 입력, password는 token 을 입력하여 로그인 하면 된다.

2) stderr: No ECDSA host key is known for github.com and you have requested strict checking.

- 해결 : 아래와 같이 'Known host file' > 'No verification' 로 변경하면 된다.

이제 오류가 뜨지 않는다!

3) java.lang.NoSuchMethodError: No such DSL method 'withAWS'

- 해결 : pipline AWS Steps 를 설치한다.

4) java.lang.NoSuchMethodError: No such DSL method 'sshagent' found among steps

- 해결 : ssh agent 플러그인을 설치 한다.

5) dial unix /var/run/docker.sock: connect: permission denied

- 해결 : 유저가 docker socket 에 대한 권한이 없기 때문

$ chmod 666 /var/run/docker.sock
728x90
Comments