硬核干貨| 如何利用K3s低成本在流水線中添加測試
作者介紹
Petro Kashlikov,AWS技術客戶經理。十分熱衷于容器技術,并與客戶一起設計、部署和管理他們的工作負載/架構。
現代化的微服務應用程序堆棧、CI/CD流水線、Kubernetes作為編排引擎以及每天成千上百的部署……這些聽起來十分美好,直到你發現你的Kubernetes開發或staging環境被這些部署弄得混亂不堪并且一個開發團隊所做的更改會影響你的Kubernetes環境。在本文中,我們將了解為什么這些外部更改會影響我們的Kubernetes環境以及如何避免這一影響。
之所以會出現這一問題,是因為在將鏡像推送到鏡像倉庫并部署我們的資源之前,我們在流水線中進行了各種代碼檢查和鏡像掃描。但是,因為流水線內部沒有可用的Kubernetes集群,因此在流水線本身中沒有進行適當的集成或單元測試。實際上,我們是在部署后測試我們的更改。
面對這個問題,有一個解決方案就是在每次構建、測試更改時配置一個干凈的Kubernetes集群,然后再將其清除。但是這十分耗時也不劃算。相反,我們可以使用由Rancher Labs推出的開源、輕量級的Kubernetes發行版K3s,與Amazon和AWS CodePipeline一起解決這個問題。
什么是K3s?
K3s是一個開源、輕量的Kubernetes發行版,大小小于100MB,專為物聯網、邊緣和CI/CD環境設計。啟動時間僅需40秒左右。
更有趣的是,對于CI/CD用例,我們可以在Docker容器內運行K3s。Rancher還提供了另一個名為k3d的工具,它是一個輕量級的wrapper,可以在Docker容器內運行K3s。在這種情況下,這個package的大小約為10MB,啟動時間更快,約為15-20秒。
現在我們開始了解如何實現這一解決方案。
前期準備
要完成這一教程,我們需要:
- 一個AWS賬號
- 一個Github賬號
- 安裝并配置AWS CLI、kubectl、eksctl功能。你可以根據以下鏈接指引安裝eksctl:
https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html
配置Amazon EKS集群
有很多方法可以進行配置,包括使用AWS管理控制臺、AWS CLI等。我們推薦使用eksctl,但不管你喜歡什么方式都可以使用,并根據你的喜好修改節點類型和區域。集群配置一般需要15分鐘左右。
eksctl create cluster \
--name k3s-lab \
--version 1.16 \
--nodegroup-name k3s-lab-workers \
--node-type t2.medium \
--nodes 2 \
--alb-ingress-access \
--region us-west-2
在本練習中,我們使用 t2.medium 實例系列。如果你在生產環境中啟動Amazon EKS集群,請記住使用適當的實例類型。
集群配置完成后,我們使用命令來驗證它是否已經啟動,以及是否正確配置了 kubectl:
kubectl get nodes
我們的輸出應該如下所示:
NAME STATUS ROLES AGE VERSION
ip-192-168-12-121.ec2.internal Ready <none> 82s v1.16.8-eks-e16311
ip-192-168-38-246.ec2.internal Ready <none> 80s v1.16.8-eks-e16311
設置AWS CodePipeline
1、 設置ACCOUNT_ID變量:
ACCOUNT_ID=$(aws sts get-caller-identity --output text --query 'Account')
2、 在CodePipeline中,我們使用AWS CodeBuild來部署示例Kubernetes服務。這需要一個能與Amazon EKS集群交互的AWS IAM(身份識別與訪問管理)角色,并添加一個內聯策略,以便在CodeBuild階段使用。該策略將允許AWS CodeBuild通過kuebctl與Amazon EKS集群進行交互。執行以下命令來創建角色并附加策略:
TRUST="{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"arn:aws:iam::${ACCOUNT_ID}:root\" }, \"Action\": \"sts:AssumeRole\" } ] }"
echo '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "eks:Describe*", "Resource": "*" } ] }' > /tmp/iam-role-policy
aws iam create-role --role-name EksWorkshopCodeBuildKubectlRole --assume-role-policy-document "$TRUST" --output text --query 'Role.Arn'
aws iam put-role-policy --role-name EksWorkshopCodeBuildKubectlRole --policy-name eks-describe --policy-document file:///tmp/iam-role-policy
3、 既然我們已經創建了IAM角色,那么我們將為Amazon EKS集群添加這一角色到aws-auth ConfigMap。一旦包含此角色,kubectl就可以通過IAM角色與Amazon EKS集群進行交互。
ROLE=" - rolearn: arn:aws:iam::$ACCOUNT_ID:role/EksWorkshopCodeBuildKubectlRole\n username: build\n groups:\n - system:masters"
kubectl get -n kube-system configmap/aws-auth -o yaml | awk "/mapRoles: \|/{print;print \"$ROLE\";next}1" > /tmp/aws-auth-patch.yml
kubectl patch configmap/aws-auth -n kube-system --patch "$(cat /tmp/aws-auth-patch.yml)"
4、 接下來,我們將fork示例Kubernetes服務以便我們可以修改repo并觸發構建。登錄到Github并fork示例服務到所選賬戶。參考Kubernetes服務示例了解更多信息。鏡像倉庫被fork后,將其克隆到本地環境,這樣我們就可以使用我們最喜歡的IDE或文本編輯器來處理文件。
git clone https://github.com/YOUR-USERNAME/eks-workshop-sample-api-service-go.git
5、 為了讓CodePipeline能夠接收來自GitHub的回調,我們必須生成一個個人的訪問令牌。一旦創建,訪問令牌將被存儲在一個安全的程序集(enclave)中,并被重復使用。只有在第一次運行時,或者需要生成新的密鑰時,才需要這一步。
6、 接著,我們將使用AWS CloudFormation創建Codepipeline。導航到AWS管理控制臺以創建CloudFormation堆棧。控制臺啟動后,鍵入Github用戶名、個人訪問令牌(在前一個步驟已經創建)以及Amazon EKS集群名稱(k3s-lab)。然后,選擇確認并點擊創建堆棧。這一步大概需要花費10分鐘。
創建CodePipeline之后,我們可以在CodePipeline控制臺中創建狀態并使用以下命令驗證部署是否應用到我們的集群中:
kubectl describe deployment hello-k8s
將k3d添加到AWS Codepipeline
現在,我們在fork的repo里修改buidspec.yaml并使用k3d添加單元測試。
我們將逐步介紹所需的修改,這些修改可以手動完成。或者,在本節末尾也為你提供了完整的buildspec.yml文件。
1、 在CodeBuild環境中安裝k3d:
- curl -sS https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v1.7.0 bash
2、 在構建階段創建k3s集群并等待集群啟動,這大概需要花費20秒:
- k3d create
- sleep 20
3、 為k3s集群配置kubectl
- export KUBECONFIG="$(k3d get-kubeconfig --name='k3s-default')"
4、 默認情況下,Amazon EKS 集群節點被 eksctl 配置為可以訪問從 Amazon Elastic Container Registry (Amazon ECR) 鏡像庫中拉取鏡像。然而,非Amazon EKS 集群需要為此進行額外配置。在文檔中找到這個配置的說明。因為有幾個步驟,我把它們移到一個單獨的腳本(create_secret.sh)中,并在buildspec.yml文件里面調用:
- ./create_secret.sh
將文件create_secret.sh添加到forked repository的工作文件夾中,其上下文如下:
SECRET_NAME=$AWS_REGION-ecr-registry
TOKEN=`aws ecr get-authorization-token --output text --query authorizationData[].authorizationToken | base64 -d | cut -d: -f2`
echo "ENV variables setup done."
kubectl create secret docker-registry $SECRET_NAME \
--docker-server=https://$REPOSITORY_URI \
--docker-username=AWS \
--docker-password="${TOKEN}" \
--docker-email=DUMMY_DOCKER_EMAIL
kubectl patch serviceaccount default -p '{"imagePullSecrets":[{"name":"'$SECRET_NAME'"}]}'
5、 將我們的流水線應用程序資源到k3d集群并等待資源啟動,這大概需要花費20秒:
- kubectl apply -f hello-k8s.yml
- sleep 20
配置測試
在這一步中,我們可以運行我們的單元/集成測試。對于本例,我們已經提供了一個簡單的腳本來hit我們服務的endpoint。我們可以從我們的堆棧中部署其他必要的微服務或服務進行集成測試。
- ./unit_test.sh
將文件 unit_test.sh 添加到 forked repository 的工作文件夾中,其上下文如下:
#!/bin/sh
set -e
api_host=$(kubectl get svc hello-k8s -o json | jq -r .status.loadBalancer.ingress[].ip)
curl -m 2 $api_host
檢查測試是否成功
最后一步是檢查測試是否成功并且將我們的應用程序部署到Amazon EKS集群。如果測試失敗,我們的Codepipeline就會失敗,不會部署到Amazon EKS。CodeBuild有個內置的變量CODEBUILD_BUILD_SUCCEEDING來指示構建階段的狀態。我們會在代碼中使用它:
- bash -c "if [ /"$CODEBUILD_BUILD_SUCCEEDING/" == /"0/" ]; then exit 1; fi"
- echo Build stage successfully completed on `date`
buildspec.yml
---
version: 0.2
phases:
install:
commands:
- curl -sS -o aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-07-26/bin/linux/amd64/aws-iam-authenticator
- curl -sS -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/linux/amd64/kubectl
- curl -sS https://raw.githubusercontent.com/rancher/k3d/main/install.sh | TAG=v1.7.0 bash
- chmod +x ./kubectl ./aws-iam-authenticator
- export PATH=$PWD/:$PATH
- apt-get update && apt-get -y install jq python3-pip python3-dev && pip3 install --upgrade awscli
pre_build:
commands:
- TAG="$REPOSITORY_NAME.$REPOSITORY_BRANCH.$ENVIRONMENT_NAME.$(date +%Y-%m-%d.%H.%M.%S).$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8)"
- sed -i 's@CONTAINER_IMAGE@'"$REPOSITORY_URI:$TAG"'@' hello-k8s.yml
- $(aws ecr get-login --no-include-email)
build:
commands:
- docker build --tag $REPOSITORY_URI:$TAG .
- docker push $REPOSITORY_URI:$TAG
# Creating k3d cluster
- k3d create
# Waiting for cluster creation for 20 seconds
- sleep 20
# Configuring kubectl for k3d cluster
- export KUBECONFIG="$(k3d get-kubeconfig --name='k3s-default')"
# Creating secret as per https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-image-pull-secret-to-service-account
# to enable k3d cluster pull images from ECR
- ./create_secret.sh
# Applying our service and deployment manifest
- kubectl apply -f hello-k8s.yml
# Waiting for pods and service to come up
- sleep 20
# Running unit test
- ./unit_test.sh
post_build:
commands:
# Checking if build phase including unit test completed successfully, if not we don't proceed with deployment
- bash -c "if [ /"$CODEBUILD_BUILD_SUCCEEDING/" == /"0/" ]; then exit 1; fi"
- echo Build stage successfully completed on `date`
- CREDENTIALS=$(aws sts assume-role --role-arn $EKS_KUBECTL_ROLE_ARN --role-session-name codebuild-kubectl --duration-seconds 900)
- export KUBECONFIG=$HOME/.kube/config
- export AWS_ACCESS_KEY_ID="$(echo ${CREDENTIALS} | jq -r '.Credentials.AccessKeyId')"
- export AWS_SECRET_ACCESS_KEY="$(echo ${CREDENTIALS} | jq -r '.Credentials.SecretAccessKey')"
- export AWS_SESSION_TOKEN="$(echo ${CREDENTIALS} | jq -r '.Credentials.SessionToken')"
- export AWS_EXPIRATION=$(echo ${CREDENTIALS} | jq -r '.Credentials.Expiration')
- aws eks update-kubeconfig --name $EKS_CLUSTER_NAME
- kubectl apply -f hello-k8s.yml
- printf '[{"name":"hello-k8s","imageUri":"%s"}]' $REPOSITORY_URI:$TAG > build.json
artifacts:
files: build.json
所有更改都完成并且新文件已經在我們本地forked repository之后,我們需要提交這些更改,這樣CodePipeline可以接收它們,并將它們應用到我們的流水線中。
git add .
git commit -m "k3d modified pipeline"
git push
當我們推送這些更改之后,我們可以訪問CodePipeline控制臺并檢查流水線的狀態和日志。

在Build部分選擇Details。在這里,我們可以在Build Logs下檢查我們的流水線運行過程中發生了什么。

清 理
為了避免產生多余的費用,我們需要進行一些清理步驟:
1、 刪除為CodePipeline創建的CloudFormation堆棧。打開CloudFormation管理控制臺,選擇eksws-codeepipeline堆棧旁邊的方框,選擇刪除,然后在彈出的窗口中確認刪除。

2、 刪除Amazon ECR repository。打開Amazon ECR管理控制臺,選擇以eksws開頭的repository名稱旁邊的方框。選擇 "Delete",然后確認刪除。

3、清空并刪除 CodeBuild 用于構建工件的 Amazon S3 bucket。bucket 名稱以 eksws-codepipeline 開頭。
選擇 bucket,然后選擇 Empty。選擇Delete來刪除該 bucket。

4、 最后,使用以下命令刪除Amazon EKS集群:
eksctl delete cluster --name=k3s-lab
總 結
在本文中,我們探討了如何使用開源、輕量級的Kubernetes發行版K3s,將單元和集成測試添加到Amazon EKS CI/CD流水線中。如果你在Amazon EKS部署中使用了不同的CI/CD工具,你也可以輕松地將K3s納入其中。
浙公網安備 33010602011771號