1 EC2 생성 for Jenkins

AWS에서 EC2 생성하는 방법인데.. 너무나 기초적이라서.. 알면 패스해도 됩니다.

1.1 Creating Key-Pair

https://console.aws.amazon.com/ec2/ 접속후,
Network & Security -> Key Pairs 메뉴를 선택하고 Create Key Pair 버튼을 선택합니다.
이름은 적절하게 선택하고, RSA, .pem 을 선택후 생성합니다.

이후 .pem 파일은 ~/.ssh 로 이동시키고, 400 으로 읽기만 할수 있도록 권한 조정을 합니다.

$ mv *.pem ~/.ssh/
$ chmod 400 ~/.ssh/zeta.pem

1.2 Creating Security Group

Security Group은 Firewall 역활을 합니다.
EC2 -> Network % Security -> Security Groups 누르고, Create Security Group 버튼을 누릅니다.

Inbound Rules에서 SSH, HTTP, HTTPS 추가하고, Custom TCP 에서 포트 8080도 추가합니다.

1.3 EC2 Instance 생성

Intance는 Community AMIs -> Ubuntu 선택후 20.04 또는 18.04 같은 적절한 버젼을 선택합니다.
저는 20.04 버젼인 ami-09e67e426f25ce0d7 인스턴스를 기반으로 생성했습니다.
중요한건 Configure Security Group 설정시, 바로 이전에 만들었던 webserver를 선택합니다.

이것만 설정하고, 바로 생성합니다.
설정후 SSH 접속은 다음과 같이 합니다.

Username이 OS마다 다른데, centos, ubuntu, root, ec2-user 등등 OS마다 다릅니다.

$ ssh -i ~/.ssh/zeta.pem ubuntu@54.167.240.88

그리고 위에서 Jenkins 설치하듯이 하면 됩니다.

2. Jenkins

2.1 Installation

Ubuntu Package 에서 기본적으로 제공하는 Jenkins의 경우 old version 을 제공하고 있습니다.
최신 버전의 Jenkins 를 설치하기 위해서는 Jenkins에서 제공하는 패키지를 설치해야 합니다.

Dependencies 설치가 필요한데, 초기 AWS Instance에서 필요합니다.

$ sudo apt install ca-certificates
$ sudo apt install openjdk-11-jdk

Jenkins 설치 ..

$ wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -
$ sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
$ sudo apt update
$ sudo apt install jenkins

2.2 Docker Permission for Jenkins

Jenkins에서 docker를 실행할 수 있도록 해줘야 합니다.

sudo usermod -a -G docker jenkins
sudo chmod 777 /var/run/docker.sock

2.3 Starting Jenkins

systemctl 로 시작하고, status를 통해서 상태를 확인합니다.

$ sudo systemctl start jenkins
$ sudo systemctl status jenkins

2.4 OpenSSH

$ sudo apt-get install openssh-server
$ sudo systemctl enable ssh
$ sudo systemctl start ssh

설치이후 ssh user@localhost 같은 명령어로 테스트 해봅니다.

2.5 Configure Firewall (Optional)

아래 코드는 ssh (port 22) 그리고 8080 포트를 여는 명령어 입니다.
AWS는 Security Group에서 하면 됨으로 패스합니다.

$ sudo apt-get install ufw
$ sudo ufw enable
$ sudo ufw allow ssh
$ sudo ufw allow 8080

2.6 Setting Up Jenkins

http://jenkins_server_ip_address:8080 으로 접속시 다음과 화면이 보이고, 암호를 넣어야 합니다.
암호는 아래에서 보이는 명령어로 꺼내서 복사 붙여넣기 합니다.

$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword
bf283■■■■■■■■■■■■■■■■■■■ee24

이후 설치하는 방식 2가지 버튼이 있는데,
그냥 Install suggested plugins 를 누르면 대부분의 경우에서 다 잘됩니다.
아래는 Install suggested plugins 를 선택했을때의 화면입니다.
아래 plugin 모두 설치합니다.

이후 Admin User를 생성합니다.

모두 설치가 완료되면 다음과 같은 화면이 나옵니다.

2.7 Plugins

  1. Manage Jenkins -> Manage Plugins
  2. 다음을 설치 합니다.
    1. CloudBees AWS Credentials Plugin
    2. Docker Pipeline
    3. Amazon ECR plugin
    4. Kubernetes CLI

2.8 Set AWS Credentials

  1. 가장 쉽게 credentials을 알아내는 방법은.. cat ~/.aws/credentials 명령어로 이미 설정되어 있는 credentials 을 꺼내는 것입니다.
  2. 또는 IAM -> Users -> Security credentials -> Create Access Key 를 생성할수 있습니다.

그래서 필요한건 ACCESS KEY 그리고 SECRET KEY 두개 입니다.

Jenkins에서 설정은 다음과 같이 합니다.

  1. Dashboard -> Manage Jenkins -> Manage Credentials
  2. 아래와 같은 화면에서 Stores -> (global) 누릅니다. -> 누르면 왼쪽에 Add Credentials 를 누릅니다.

생성은 다음과 같이 참고해서 만듭니다.
ID (jenkins-aws-anderson-credentials) 는 Jenkins Pipeline에서 다시 사용 됩니다.

3. Jenkins + Github Webhook

3.1 Webhook 설정

Gtihub Repository에 들어가서, 다음과 같이 설정 합니다.
위치는 Repository -> Settings -> Webhooks

  • Jenkins 주소에 /github-webhook/ 을 붙여줍니다.
    • 예) http://34.227.49.74:8080/github-webhook/
    • 중요한점은 끝에 슬래쉬가 반드시 들어가야 합니다.

3.2 Creating GitHub Personal Access Token

Github의 우측상단에 자신의 프로필 사진을 누르고, Setting -> Developer Settings -> Personal Access Tokens 메뉴를 누릅니다.
이름 넣어주고, scopes은 다음과 같이 선택합니다.

  • repo
  • admin:repo_hook

생성하면 랜덤 문자열 같은게 생성 됩니다.
복사해서 Jenkins에 복사해줍니다.

3.2 Jenkins Credential

Jenkins 초기화면에서 Manage Jenkins -> Configure System 메뉴에서 GitHub 계정을 설정하는 곳을 찾아서 설정해줍니다.
이름은 적절하게 만들어주고, API URL은 https://api.github.com 으로 되어 있는데 그냥 default값 사용하고,
Manage hooks 체크박스 눌러줍니다.

Credential 추가를 눌러서, Github에서 생성한 personal access token을 secret에다가 넣고, Github ID도 넣어줍니다.

3.3 Jenkins GitHub 설정

New Item 생성을 하며, Freestyle 을 선택합니다 .

4. Jenkins Pipeline and ECR

4.1 신규 아이템 생성

  1. Jenkins -> Dashboard 에서 New Item 을 눌러서 새로운 아이템을 생성합니다.
  2. Pipeline 을 선택합니다.

  1. Pipeline 메뉴에서 Pipeline script from SCM 을 선택하고 Github 정보를 넣습니다.
    1. Repository URL: http로 시작하는 github repository 주소. .git으로 끝남
    2. Credentials: 위에서 설정한 credential 추가
    3. Branch Specifier: master 인지 main 인지 잘 설정해야 함
    4. Script Path: jenkinsfile 의 파일 위치를 설정

Repository URL에 git 주소를 넣어줍니다.
여기서는 https://github.com/AndersonJo/jenkins-tutorial.git 넣었습니다.
추가적으로 branch 입력시 master 인지 main 인지도 확실하게 해줘야 합니다.

Credential 생성시에는 Login ID, Password 로 생성합니다.
(Secret Text로 하면 Jenkins 버그로 인해서 dropbox에서 보이지 않는 일이 발생합니다.
반드시 Github UserID 그리고 Password로 해야 합니다.
또한 유저ID는 이메일이 아니라 Github UserID로 해야 합니다.
예를 들어 제 Github UserID는 AndersonJo 입니다. )

관련된 버그는 링크 에서 확인 가능합니다.

Build Triggers 에서는 GitHub hook trigger for GITScm polling 을 선택합니다.
GitHub에 코드가 push되면 빌드를 하도록 설정하는 것 입니다.

push를 날리게 되면, GitHub에서 webhook 메세지를 Jenkins에 보내게 되며,
webhook 메세지를 받은 Jenkins는 이때부터 빌드를 진행하게 됩니다.

4.2 Jenkinsfile & Docker Build & Push to ECR

jenkinsfile 은 그냥 텍스트 파일이고, 크게 3가지로 구성되어 있습니다.
Groovy syntax 를 갖고 있으며, stage, 그리고 step 명령어 구조화하며,
credentials을 통해서 secret key등을 환경변수에서 가져올 수 있습니다.

자세한 것은 Documentation을 참고 합니다.

Jenkinsfile 파일은 해당 github repository에 넣으면 됩니다. (Jenkins 어딘가 X) Git Push를 하면, webhook으로 Jenkins에서 전달받게 되고, 해당 git repository를 checkout하게 됩니다.
이후 Jenkins는 모두 다 다운받은후 -> 아래 빌드를 순차적으로 하게 됩니다.

node {
    stage('Clone Repository'){
        checkout scm
    }

    stage('Build to ECR'){

    }
    stage('Kubernetes'){
        
    }
}

아래 그림은 성공적으로 진행됐을 경우의 Jenkins 화면입니다.

5. Kubernetes and Jenkins

5.1 Get EKS Cluster Token

Jenkins에서 EKS Cluster에 deploy할 수 있도록 Cluster Token을 Jenkins에 넣어 줘야 합니다.
두가지 중의 하나입니다.
그냥 default로 있는 secret 사용하던가 또는 새로 Service Account를 만들어서 사용하는 방법입니다.
중요한건 default로 사용하던 새로운 Service Account 사용하던, Token 잘 꺼내서 넣어주면 됩니다.

$ kubectl create sa jenkins-deployer
$ kubectl create clusterrolebinding jenkins-deployer-role --clusterrole==cluster-admin --serviceaccount=default:jenkins-deployer
$ kubectl get secrets
NAME                           TYPE                                  DATA   AGE
default-token-7b6nf            kubernetes.io/service-account-token   3      16h
jenkins-deployer-token-vgsfj   kubernetes.io/service-account-token   3      2m41s

다음 명령어로 새로 만들어진 credentials을 복사합니다.

$ kubectl describe secret jenkins-deployer-token-vgsfj

5.2 Credentials 을 Jenkins에 등록

  • Manage Jenkins -> Manage Credentials -> 아무거나 (global) 선택 -> Add Credentials 선택
  • 여기서는 이름을 kubectl-deploy-credentials 로 하였음

5.3 다필요없고 제일 쉬운 방법

문제가 되는 부분이 jenkins 유저로 돌아갈때 kubectl 이 안되는 문제가 있는데..
아직까지 이걸 되게 만드려면 매우 어려움이 있다. 따라서 그냥 제일 쉽게 하는 방법은 그냥 jenkins 유저로 접속해서 미리 인증 받아놓는 것이다.

먼저 기존 ACCESS KEY 그리고 SECRET KEY를 파악한다.

$ cat ~/.aws/credentials 
[default]
aws_access_key_id = AKIA4A27BB2QXSFEVPOE
aws_secret_access_key = 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

이후 jenkins 유저로 접 속해서 인증한다

$ sudo su jenkins
$ aws configure
AWS Access Key ID [****************VPOE]: 
AWS Secret Access Key [****************5Vq/]: 
Default region name [us-east-1]: 
Default output format [json]:

aws eks --region <us-west-2> update-kubeconfig --name <cluster_name> 명령어로 authentication 한다

$ aws eks --region us-east-1 update-kubeconfig --name My-EKS

현재까지는 이게 가장 쉬운 방법이고, EKS와 기존 plugin이 작동을 잘 안함.
일단은 이렇게 쓰는게 쉽게 해결하는 방법.

왜 안되냐하면.. EKS는 내부적으로 aws-iam-authenticator 를 사용하는데..
그냥 Kubernetes에 맞춰져 있는 플러그인 사용시 EKS 인증이 안됨.

5.4 Kubernetes Plugin

Jenkins에서 Kubernetes plugin을 설치합니다.

  • Kubernetes Plugin
  • Kubernetes CLI

5.5 Pipeline

  • EKS_API: EKS Cluster -> 당신의 Cluster -> API server endpoint
    • 예) https://D5B994D3CB7B91D8FED3D60B2A7674FA.gr7.us-east-1.eks.amazonaws.com
  • EKS_CLUSTER_NAME: EKS Cluster의 이름
    • 예) EKS-AI-Cluster
  • EKS_NAMESPACE: 적용하려는 kubernetes의 namespace
  • EKS_JENKINS_CREDENTIAL_ID
    • kubectl get secrets
    • kubectl describe secret default-token-k7bst
  • ECR_REGION: ECR Region을 적으면 됨
  • ECR_PATH: ECR로 가서 Repository의 URI을 가져오되 /repository-name 은 제거한다
    • 만약 826443632289.dkr.ecr.us-east-1.amazonaws.com/jenkins-ecr 이라면
      826443632289.dkr.ecr.us-east-1.amazonaws.com 까지만 적는다
REGION = 'us-east-1'
EKS_API = 'https://D5B994D3CB7B91D8FED3D60B2A7674FA.gr7.us-east-1.eks.amazonaws.com'
EKS_CLUSTER_NAME='EKS-Cluster'
EKS_NAMESPACE='default'
EKS_JENKINS_CREDENTIAL_ID='kubectl-deploy-credentials'
ECR_PATH = '826443632289.dkr.ecr.us-east-1.amazonaws.com'
ECR_IMAGE = 'jenkins-ecr'

node {
    stage('Clone Repository'){
        checkout scm
    }

    stage('Build to ECR'){
        // Docker Build and Push to ECR
        docker.withRegistry("https://${ECR_PATH}", "ecr:${REGION}:jenkins-aws-anderson-credentials"){
            def image = docker.build("${ECR_PATH}/${ECR_IMAGE}:${env.BUILD_ID}")
            image.push()
        }
    }
    stage('Kubernetes'){
        withKubeConfig([credentialsId: "kubectl-deploy-credentials",
                        serverUrl: "${EKS_API}",
                        clusterName: "${EKS_CLUSTER_NAME}"]){

            sh "sed 's/IMAGE_VERSION/${env.BUILD_ID}/g' nginx-deployment.yaml > output.yaml"
            sh "aws eks --region ${REGION} update-kubeconfig --name ${EKS_CLUSTER_NAME}"
            sh "kubectl apply -f output.yaml"
        }
    }
}

5.5 최종 확인

$ kubectl get svc
NAME                         TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)        AGE
kubernetes                   ClusterIP      10.100.0.1       <none>                                                                    443/TCP        17h
nginx-service-loadbalancer   LoadBalancer   10.100.160.221   a171eb183b72a45d8a93a11eab57c1bb-1295299660.us-east-1.elb.amazonaws.com   80:31063/TCP   13h

external IP로 들어간후.. nginx text 내용 변경하면.. 변경된 내용 배포됨