iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
DevOps

關於我幫新公司建立整套部屬流程那檔事系列 第 20

EP20 - 整合 Jenkins 自動部署到 EKS

之前我們自動部署的時候,
沒有在 Jenkins Server 上 config aws,
而是起一個 Docker 去做各項 aws 的操作,
今天我們部署到 EKS,
也打算如法炮製,
製作一個部署用的容器,
透過這容器在部署的時候可以自動部署到 EKS。

撰寫部署用容器

撰寫容器時要注意
一般來說
也不建議在建置的時候
將帳號密碼這類機敏資訊包進去

而是要另外 mount 上去
這也是為什麼我們另外包一層的時候
也沒有順便包進去
怕的就是我們包了一個過高權限的 iam 進容器
結果容器落到有心人手裡
而造成其他問題

Gitlab 開新專案

建立新 Group

https://ithelp.ithome.com.tw/upload/images/20211002/20141518cBm4UjDmkT.png

Create Group

https://ithelp.ithome.com.tw/upload/images/20211002/20141518oeWatP3oUP.png

輸入 Group 名稱

https://ithelp.ithome.com.tw/upload/images/20211002/20141518clmrnzrjZi.png

建立新專案

https://ithelp.ithome.com.tw/upload/images/20211002/201415188r4SkWKW2i.png

Create Blank Porject

https://ithelp.ithome.com.tw/upload/images/20211002/20141518BcxrfGVVvf.png

輸入專案名稱

https://ithelp.ithome.com.tw/upload/images/20211002/20141518qPcoKzrHNN.png

複製專案名稱

https://ithelp.ithome.com.tw/upload/images/20211002/20141518EiP1sLRaT0.png

側欄選擇 Settings > Access Token

https://ithelp.ithome.com.tw/upload/images/20211002/20141518u1OzMixl9w.png

輸入建立 Token 所需資訊並創建

因為我們有 container,所以下面兩個也要勾選
https://ithelp.ithome.com.tw/upload/images/20211002/20141518IeaJxoQOlr.png

保存好 Token 備用

https://ithelp.ithome.com.tw/upload/images/20211002/201415186McMMMUHDF.png

Gitlab 串接 Jenkins

Jenkins 新增作業

https://ithelp.ithome.com.tw/upload/images/20211002/20141518XrQUDcLJLu.png

填寫 Pipeline 名稱

https://ithelp.ithome.com.tw/upload/images/20211002/20141518wxDvvLCzEq.png

勾選 Build when a change is pushed to GitLab.

點按進階,並按下 Generate 產生 Token

https://ithelp.ithome.com.tw/upload/images/20211002/20141518BKyYMCbzjH.png

Pipeline -> Definition 選擇 Pipeline from SCM

https://ithelp.ithome.com.tw/upload/images/20211002/20141518RYxQi68ZAt.png

Gitlab 專案頁面左側 Settings 選擇 Webhook

https://ithelp.ithome.com.tw/upload/images/20211002/20141518lJQtWnFMih.png

貼上 Pipeline 網址和 Token 並按下 Add hook

https://ithelp.ithome.com.tw/upload/images/20211002/20141518UvgcprIW6T.png

製作容器

因為我們只會在 Jenkins Server 上使用
所以就先沒有 push 上 ECR
只要 Jenkins Server 上有執行過
在部署的時候
就可以抓取 local(Jenkins)上的 eks-deploy 直接執行

Clone 專案

cd /vagrant_data/project/
git clone https://[使用者名稱]:[Access Token]@[Git Repository]

製作容器

FROM mikesir87/aws-cli
MAINTAINER Mark_Mew

RUN sed -i 's/stable\/updates/stable-security/g' /etc/apt/sources.list \
  && apt-get update -qq \
  && apt-get install -y curl \
  && apt-get upgrade -y \
  && apt-get clean

RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
  && install -m 0755 kubectl /usr/local/bin/kubectl

Jenkinsfile

pipeline {
    agent any
    stages {
        stage('Build Docker Image') {
            steps {
                echo 'Building...'
                script {
                    def customImage = docker.build("eks-deploy:${env.BUILD_ID}", "./")
                }
            }
        }
        stage('Clean Workspace') {
            steps {
                echo 'Cleaning....'
                cleanWs()
            }
        }
    }
    post {
        // Clean after build
        always {
            cleanWs(cleanWhenNotBuilt: false,
                    deleteDirs: true,
                    disableDeferredWipeout: true,
                    notFailBuild: true,
                    patterns: [[pattern: '.gitignore', type: 'INCLUDE'],
                               [pattern: '.pyc', type: 'INCLUDE'],
                               [pattern: '.propsfile', type: 'EXCLUDE']])
        }
    }
}

Checkin 進 Git

git add Dockerfile 
git add Jenkinsfile 
git commit -m "add container and p
git push

Portal Pipeline 調整

調整 IAM Jenkins 權限

之前我們在電腦中手動部署
因為是 administrator 的關係
因此可以任意操作
但是當我們是使用 IAM 的話
就需要再另外加權限上去了

stage/main.tf

更新 kubeconfig

resource "aws_iam_user_policy" "jenkins_update_kube_config" {
    name = "JenkinsUpdateKubeConfig"
    user = aws_iam_user.jenkins.name
    
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "eks:DescribeCluster",
      "Effect": "Allow",
      "Resource": "arn:aws:eks:ap-northeast-1:[你的AWS Account數字]:cluster/*"
    }
  ]
}
EOF
}

stage/main.tf

讀取 eks 資源權限

其實這功能非必要
只是覺如果只是 read only 權限
是可以放一下

resource "aws_iam_policy" "eks_limit_access" {
    name        = "EKSLimitAccess"
    path        = "/"
    description = ""

    policy = jsonencode({
      Version = "2012-10-17"
      "Statement": [
        {
          "Action": [
            "eks:ListNodegroups",
            "eks:DescribeFargateProfile",
            "eks:UntagResource",
            "eks:ListTagsForResource",
            "eks:ListAddons",
            "eks:DescribeAddon",
            "eks:ListFargateProfiles",
            "eks:DescribeNodegroup",
            "eks:DescribeIdentityProviderConfig",
            "eks:ListUpdates",
            "eks:DescribeUpdate",
            "eks:TagResource",
            "eks:AccessKubernetesApi",
            "eks:DescribeCluster",
            "eks:ListClusters",
            "eks:DescribeAddonVersions",
            "eks:ListIdentityProviderConfigs"
          ],
          "Effect": "Allow",
          "Resource": "*"
        }
      ]
    })
}

resource "aws_iam_user_policy_attachment" "jenkins_eks_limit_access" {
    user       = aws_iam_user.jenkins.name
    policy_arn = aws_iam_policy.eks_limit_access.arn
}

執行配置

terraform apply

調整 Jenkinsfile 並進版控

Portal 新增 Jenkinsfile

增加 Deploy to EKS 這個 stage
也因為我們之前有另外建一個資料夾
在資料底下做事情
但...其實它也沒有非一定要的存在
也可以順便把它移除了

pipeline {
  agent any
  
  stages {
    stage('Run Test') {
        steps {
            echo 'Run Python Unittest ...'
            script {
                withDockerContainer(image: 'python:3.7.10-buster', args: '-u root:root') {
                    sh """
                    apt-get install libpq-dev
                    pip install --user -r requirements.txt
                    python manage.py test
                    rm -rf __pycache__
                    rm -rf */__pycache__
                    rm -rf */*.pyc
                    """
                }
            }
        }
    }
    stage('Archieve Project') {
        steps {
            echo 'Archieve...'
            sh 'git archive --format=tar.gz --output ./portal.tar.gz HEAD'
        }
    }
    stage('Upload to S3') {
        steps {
            echo 'Upload...'
            sh "docker run --rm -v ${WORKSPACE}:/app -v /usr/local/src/aws_docker_file/.aws:/root/.aws mikesir87/aws-cli aws s3 cp /app/portal.tar.gz s3://ithome-ironman-markmew-jenkins/portal/stage/portal-${env.BUILD_ID}.tar.gz"
        }
    }
    stage('Build and Push Docker Image') {
        steps {
            script {
                echo 'Building...'
                def customImage = docker.build("portal:${env:BUILD_ID}", "./")
                echo 'Validating... aws'
                sh 'docker run --rm -v /usr/local/src/aws_docker_file/.aws:/root/.aws mikesir87/aws-cli aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [你的 AWS 10 碼 ID].dkr.ecr.ap-northeast-1.amazonaws.com'
                echo 'Tag docker image'
                sh "docker tag portal:${env:BUILD_ID} [你的].dkr.ecr.ap-northeast-1.amazonaws.com/portal:${env:BUILD_ID}"
                sh "docker tag portal:${env:BUILD_ID} [你的 AWS 10 碼].dkr.ecr.ap-northeast-1.amazonaws.com/portal:latest"
                echo 'Push docker image'
                sh "docker push [你的 AWS 10 碼 ID].dkr.ecr.ap-northeast-1.amazonaws.com/portal:${env:BUILD_ID}"
                sh "docker push [你的 AWS 10 碼 ID].dkr.ecr.ap-northeast-1.amazonaws.com/portal:latest"
            }
        }
    }
    stage('Deploy to EKS') {
        steps {
            echo 'Applying portal to eks ...'
            script {
                withDockerContainer(image: 'eks-deploy:latest',
                                    args:"-u root:root -v /usr/local/src/aws_docker_file/.aws:/root/.aws -v ${WORKSPACE}/deploy.yaml:/aws/deploy.yaml") {
                    sh 'aws eks --region ap-northeast-1 update-kubeconfig --name aws-stage-cluster'
                    sh 'kubectl apply -f deploy.yaml'
                }
            }
        }
    }
    stage('Deploy') {
        steps {
            echo 'Deploy ...'
            sh "docker run --rm -v /usr/local/src/aws_docker_file/.aws:/root/.aws mikesir87/aws-cli aws deploy create-deployment --application-name ithome-ironman-portal --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name ithome-ironman-portal --description \"codedeploy test\" --s3-location bucket=ithome-ironman-markmew-jenkins,bundleType=tgz,key=portal/stage/portal-${env.BUILD_ID}.tar.gz"
        }
    }
  }
}

調整 Jenkins 組態

Jenkinsfile 來源改成 SCM
https://ithelp.ithome.com.tw/upload/images/20211002/20141518T9FGASJyh2.png

Check-in git

git add deploy.yaml
git add Jenkinsfile
git commit -m "add Jenkinsfile for CI"
git push

昨天我們嘗試手動部署到 EKS
今天更近一步
使用容器將我們需要的東西包起來去執行 kubectl 的指令
通常來說只要能做到手動部署
其實自動部署也就差不多了

這邊會多做一步
另外包一層 docker 去執行
主要是因為我們沒有特別做乾淨的 Jenkins AMI
就算有可能也意義不大
現實中不太有機會時常從乾淨的環境開始重建
比較多會是做災難演練
因此在做完備份後
從備份中復原
之前我們有提過如何復原 Snapshot
未來有機會會再提如何建立備份

大致上陽春版的部署已經完成
我們有了陽春版的 EC2 部署
也有了陽春版的 EKS 部署
明天我們會提高難度
進行不同的部署


上一篇
EP19 - RE:從零開始學習本機操作 EKS 並手動部署
下一篇
EP21 - 持續部署使用 Octopus Deploy 首部曲,建置 Octopus 基礎設施
系列文
關於我幫新公司建立整套部屬流程那檔事30

尚未有邦友留言

立即登入留言