AWS EKS + ECR & Docker & Jenkins 활용 클라우드 CI /CD 배포

InSeok·2023년 3월 4일
0

AWS / K8S의 사양

  • 최소 2CPU, 2GB RAM 을 요구
  • OS는 Linux를 기준
  • 최소 사양 기준 t3.small(intel), t3a.small(AMD)
  • t3.small : 온디맨드 시간당 $0.026 / 30일 약 $18.72
  • t3.medium : 온디맨드 시간당 $0.052 / 30일 약 $37.44
  • 마스터노드 + 워커노드 : $56.16

Amazon EKS

  • 마스터 노드를 AWS가 직접 관리해주는 K8S만을 위한 서비스
  • 마스터 노드를 AWS에서 직접 관리해주기 때문에 실질적으로 사용자는 워커 노드만 생성하고 관리하면 된다.
  • 1개 노드 기준 : 시간당 $0.1 / 30일 약 $72
  • 마스터 노드를 따로 구성하지 않아도 된다는 점에서 비용적으로 절감이 된다. 더 많은 클러스터가 구성될 경우 비용 절감에 도움

Amazon ECR

  • 프라이빗하게 구성할 수 있는 컨테이너 레포지토리 서비스
  • 사내 개발물을 공개된 Docker Hub에 올리기엔 껄끄러울 수 있다. 그래서 사내 서버에 레포지토리를 구성하거나, ECR과 같은 서비스를 이용할 수 있다.
  • 프라이빗 레포지토리를 생성하고, 애플리케이션 이미지, 다양한 이미지 및 버전을 레포지토리에 등록할 수 있고, 레포지토리 엔드포인트로 클러스터에서 이미지를 다운로드할 수 있다.
  • 데이터 업로드는 무료, 다운로드시 요금이 부과되는 시스템

EC2 설정

패키지 매니저 업데이트

sudo apt-get update

자바 설치

sudo apt install openjdk-11-jre-headless

젠킨스 설치

  • 젠킨스 레파지토리를 패키지 매니저에 추가

wget -q -O - [https://pkg.jenkins.io/debian-stable/jenkins.io.key](https://pkg.jenkins.io/debian-stable/jenkins.io.key) | sudo apt-key add -

sudo sh -c 'echo deb [https://pkg.jenkins.io/debian-stable](https://pkg.jenkins.io/debian-stable) binary/ > /etc/apt/sources.list.d/jenkins.list'

  • 젠킨스 레파지토리 링크를 추가해준후에, 패키지매니저 업데이트 sudo apt-get update
  • 젠킨스 설치 sudo apt-get install jenkins
  • 설치가 성공적으로 마치면, 상태 확인 sudo service jenkins status

젠킨스 설정

  • public ipv4 주소:8080 으로 접속해보면 아래와 같은 화면이 뜬다.
  • 젠킨스를 처음 설치 했다면 initialAdminPassword를 입력해야한다.
  • 아래 명령어를 통해 얻을 수 있다.
    • sudo cat /var/lib/jenkins/secrets/initialAdminPassword
  • 패스워드 입력후 다음 화면에서 install을 선택
  • 설치후에 First Admin User를 설정해줘야한다.

젠킨스 Gradle 설정

  • 젠킨스 관리 > Global Tool Cofiguration > Gradle

젠킨스 User 관리자 자격 할당

  • 쿠버네티스 클러스터와 젠킨스는 쉘스크립트를 통해 상호작용하므로 젠킨스 유저에게 관리자 자격을 부여해야한다.

젠킨스 User 관리자 역할 부여

sudo 실행시 password 물어보지 않도록 설정

  • /etc/sudoeres 파일을 vi 로 연다
    • sudo vi /etc/sudoers
  • 파일 끝 부분에 아래 명령어를 추가한다.
    • jenkins ALL=(ALL) NOPASSWD: ALL
  • 아래 명령어로 jenkins user로 로그인
    • sudo su - jenkins

Docker 설치

  • 도커 설치 sudo apt install [docker.io](http://docker.io/)
  • 젠킨스는 애플리케이션의 도커 이미지를 빌드하기 위해 도커에 접근한다.
  • 따라서, 젠킨스에서 도커를 사용할 수 있게 Docker Group에 Jenkins user를 추가해줘야 한다.
    • sudo usermod -aG docker jenkins

AWS CLI 설치

  • EC2에서 eksctl 을 사용하기 위해 AWS CLI를 설치한다.
    • sudo apt install awscli
    • aws --version
  • AWS CLI 설정
    • AWS CLI 가 AWS 환경과 소통하고 인증할 수있도록 설정
    • aws configure 명령어를 입력하면 아래 내용을 입력해야한다.
    • 해당 정보는 AWS → 보안자격증명 에서 확인가능

1. AWS Access Key ID [None]: 
2. AWS Secret Access Key [None]:
3. Default region name [None]: 가용영역에서 확인
4. Default output format [None]: json

Kubectl 설치

  • kubectl 설치

curl -LO "[https://storage.googleapis.com/kubernetes-release/release/$](https://storage.googleapis.com/kubernetes-release/release/$)(curl -s [https://storage.googleapis.com/kubernetes-release/release/stable.txt](https://storage.googleapis.com/kubernetes-release/release/stable.txt))/bin/linux/amd64/kubectl"

  • kubectl 쓰기권한추가

chmod +x ./kubectl

  • kubectl 이동

sudo mv ./kubectl /usr/local/bin

  • 설치확인

kubectl version

Eksctl 설치

  • Aws EKS Clusters 를 생성하기 위해 eksctl을 설치한다.

curl --silent --location "[https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$](https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$)(uname -s)_amd64.tar.gz" | tar xz -C /tmp

  • eksctl 이동

sudo mv /tmp/eksctl /usr/local/bin

  • 설치 확인

eksctl version

eksctl 활용해서 EKS Cluster 생성

eksctl create --name 클러스터 이름 --version 버전 --region 지역명(한국은 ap-northeast-2) --nodegroup-name 노드그룹이름 --node-type 노드타입 --nodes 노드개수 --nodes-min 지정할 노드 최소개수 --nodes-max 지정할 노드 최대개수 --ssh-access --ssh-public-key 워커노드접속에 사용할 키 --managed

  • 위의 명령어를 입력하고 20~30분 정도 기다리면 아래와 같이 완료화면이뜬다.

  • EKS에 접속하면 아래와 같이 클러스터가 등록되어있다.
  • eksctl에서 설정한 노드 1개도 생성되어있는것을 확인할 수 있다.


kubectl 1.24.x 버전에서 eks와 연동해서 사용할 경우 발생하는 오류이며 kubectl 버전을 1.23.6 버전을 내리니 문제가 해결되었다.

couldn't get current server API group list: ~~~
getting credentials: decoding stdout: no kind "ExecCredential" is registered for version "client.authentication.k8s.io/v1alpha1" in scheme "pkg/client/auth/exec/exec.go:62"

Plugins

  • Manages > Manage Plugins
  • 다음을 설치한다.
    1. CloudBees AWS Credentials Plugin
    2. Docker Pipeline
    3. Amazon ECR plugin
    4. Kubernetes Plugin
    5. Kubernetes CLI

GitHub Webhook 설정

  • Repository -> Settings -> Webhooks
  • 젠킨스 주소에 /github-webhook/을 붙여준다.
  • Content type : application/json 선택

Credentails 세팅

AWS Credentials 세팅

  • IAM -> Users -> Security credentials -> Create Access Key 를 생성
  • Manage Jenkins → Manage Credentials
    • Stores → global → Add Credentials
    • Kind: AWS Credentials
    • Scope: Global
    • Access Key ID: 방금생성한 access key
    • Secret Access Key: 방금 생성한 Secret key

GitHub Username & psswd

  • GitHub Settings → development settings → generate accessToken

  • GitHub username과 Password도 Global Credentials에 추가해준다. (username Password 방식)

Jenkins Pipeline 작성

  • new Item 에서 Pipeline 선택
  • Pipeline 메뉴에서 Pipeline script from SCM 을 선택하고 Github 정보를 넣는다.(branch는 main으로 변경)
  • Build Triggers 는 GitHub hook trigger for GITScm polling 을 선택
    • push를 날리게 되면, GitHub에서 webhook 메세지를 Jenkins에 보내게 되며, webhook 메세지를 받은 Jenkins는 이때부터 빌드를 진행

쿠버네티스 서비스 어카운트

  • Kubernetes CLI Plugin은 Jenkins에서 사용하는 jenkins(리눅스 유저)가 EKS로 로그인 할 수 있게 도와준다.
  • 이떄, Jenkins에서 EKS 로그인에 필요한 credentials가 필요하다.

서비스 어카운트(Service Account)

  • 서비스 아카운트 : 쿠버네티스 클러스터 및 오브젝트의 정보를 조회하기 위한 계정
  • 아래 명령어로 쿠버네티스 Service Account를 생성한다.
    • kubectl create serviceaccount jenkins-deployer
  • 아래 멸령어를 통해 서비스 아카운트 계정인 jenkins-deployer에 쿠버네티스 클러스터에 대한 admin 권한을 부여한다.
  • kubectl create clusterrolebinding jenkins-deployer-role --clusterrole**==**cluster-admin --serviceaccount**=**default:jenkins-deployer
    • clusterrolebinding : 생성되는 오브젝트가 clusterrolebinding임을 나타냄
    • jenkins-deployer-role: 생성되는 오브젝트 이름
    • --clusterrole**==**cluster-admin : cluster-admin 역할을 부여
    • --serviceaccount**=**default:jenkins-deployer : default에 있는 jenkins-deployer 서비스어카운트에 이 권한을 부여
💡 Kubernetes 1.22 버전 이후부터는 서비스 어카운트를 생성해도 자동으로 secret이 생성되지않는다. 따라서 아래의 명령어를 참고하여 `secret.yaml` 파일을 생성한다.

  • secret.yaml 파일생성후 아래명령어로 secret 생성
    • kubectl create -f ./secret.yaml
  • 아래 명령어로 생성한 secret 내용과 token을 확인할 수 있다.
    • kubectl describe secret mysecretname
  • token을 복사하여 Jenkins에 등록한다.
  • Manage Jenkins -> Manage Credentials -> 아무거나 (global) 선택 -> Add Credentials 선택
    • Kind : Secret text
    • Secret: 복사한 token 입력

Jenkinsfile 생성 및 Git push 이벤트 수행 시 웹훅 → Jenkins 트리거 → CI /CD수행

  • GitHub 최상단 디렉터리에 아래 파일들을 생성한다.

Dockerfile

FROM openjdk:11-jre-slim
WORKDIR /backend
RUN ./gradlew build
WORKDIR /build/libs
COPY {빌드된 jar 파일이름} ./
CMD nohup java -jar {빌드된 jar 파일이름} & #jar파일 백그라운드 실행
EXPOSE 8080

Jenkinsfile

pipeline {
    agent any

    environment{
        REGION = 'ap-northeast-2'
        EKS_API = {EKS Cluster Api Server Endpoint}
        EKS_CLUSTER_NAME = {EKS Cluster 이름}
       EKS_JENKINS_CREDENTIAL_ID = 'kubectl-deploy-credentials'
       ECR_PATH = {ECR로 가서 Repository의 URI을 가져오되 /repository-name 은 제거}
       ECR_IMAGE = 'ecr 이미지 이름'
       AWS_CREDENTIAL_ID = 'AWSCredentials'

    }
    stages {
        stage('Clone Repository'){
            checkout scm
        }
        stage('Docker Build'){
        docker.withRegistry("https://${ECR_PATH}", "ecr:${REGION}:${AWS_CREDENTIAL_ID}"){
            image = docker.build("${ECR_PATH}/${ECR_IMAGE}")
            }
        }
        stage('Push to ECR'){
            docker.withRegistry("https://{ECR_PATH}", "ecr:${REGION}:${AWS_CREDENTIAL_ID}"){
                image.push("v${env.BUILD_NUMBER}")
            }
        }
        stage('CleanUp Images'){
            sh"""
            docker rmi ${ECR_PATH}/${ECR_IMAGE}:v$BUILD_NUMBER
            docker rmi ${ECR_PATH}/${ECR_IMAGE}:latest
            """
        }
        stage('Deploy to k8s'){
        withKubeConfig([credentialsId: "{EKS_JENKINS_CREDENTIAL_ID}",
                        serverUrl: "${EKS_API}",
                        clusterName: "${EKS_CLUSTER_NAME}"]){
            sh "sed 's/IMAGE_VERSION/v${env.BUILD_ID}/g' service.yaml > output.yaml"
            sh "aws eks --region ${REGION} update-kubeconfig --name ${EKS_CLUSTER_NAME}"
            sh "kubectl apply -f output.yaml"
            sh "rm output.yaml"
             }
        }
}

service.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
  labels:
    app: hello-world
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
        - name: springboot-app
          image: {ECR_Path값/repository 이름}:IMAGE_VERSION
          imagePullPolicy: Always
          ports:
            -containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: hello-world-service
spec:
  type: LoadBalancer
  selector:
    app: hello-world
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

References

https://velog.io/@woo6_0/AWS-내-K8S를-위한-서비스-비용-정리

https://jhooq.com/aws-kubernetes-jenkins-pipeline/

http://incredible.ai/engineering/2021/10/18/Jenkins-Github-ECR-Kubernetes/#27-plugins

https://nyyang.tistory.com/113

profile
백엔드 개발자

0개의 댓글