前文
本文使用 Jenkins 结合 CodeBuild, CodeDeploy 实现 Serverless 的 CI/CD 工作流,用于自动化发布已经部署 lambda 函数。 在 AWS 海外区,CI/CD 工作流可以用 codepipeline 这项产品来方便的实现,
CICD 基本概念
- 持续集成( Continuous Integration,简称CI ) 是指在应用代码的新组件集成到共享存储库之后自动测试和构建软件的流程。这样一来,就可以打造出始终处于工作状态的应用“版本”。
- 持续交付( Continuous Deployment, 简称CD )是指将CI流程中创建的应用交付到类似生产环境的过程,在该过程中将对应用进行额外的自动化测试,以确保应用在部署到生产环境以及交付到真实用户手中时能够发挥预期作用。
架构综述
本文最终达到的效果为,源代码在 Github repo 中,每当有新的 commit,将自动触发 Jenkins CICD 工作流,Jenkin 会利用 CodeBuild 做构建,以及CodeDeploy 自动部署发布 lambda 新版本。
jenkins环境准备
jenkins EC2安装
先拉一个每月750小时免费的 EC2 实例t2.micro, 最后配置一个固定的弹性ip,后面如果时不时要用停止/启动EC2 实后ip不用再重复配置
登录EC2 安装jenkins, 我使用的是jenkins.war的方式部署为了避免jenkins 的使用环境上一些坑
jenkins.war的下载地址jenkins.war
安装jdk-17
更新OS
sudo yum update -y
设置yum源
sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
导入公钥
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
升级包
sudo yum upgrade
安装java
sudo dnf install java-17-amazon-corretto -y
把jenkins.war放到/usr/local/jenkins 目录下,编辑 /etc/init.d/jenkins 作为启动脚本
#!/bin/bash
# 在执行过程中若遇到使用了未定义的变量或命令返回值为非零,将直接报错退出
set -eu
# Default-Start: 2 3 4 5
# 检查参数个数
if [ "${#}" -lt 1 ]; then
echo " 脚本使用示例: service jenkins start|stop|restart "
exit
fi
# 获取脚本第一个参数
APP_OPT=${1}
# 端口
APP_PORT=9001
# 名称
APP_NAME=jenkins
# jar名 | war名
APP_JAR=${APP_NAME}.war
# 程序根目录
APP_JAR_HOME=/usr/local/jenkins
# 日志名
APP_LOG_NAME=app-jenkins
# 日志根目录
APP_LOG_HOME=/usr/local/jenkins/log
# 程序运行参数
JAVA_OPTS="--httpPort=${APP_PORT}"
echo "本次操作服务名:[${APP_NAME}]"
echo "本次操作选择:[${APP_OPT}]"
# 停止
function stop(){
echo "<-------------------------------------->"
echo "[${APP_NAME}] ... stop ..."
# 查看该jar进程
pid=`ps -ef | grep ${APP_NAME} | grep -v 'grep' | awk '{print $2}'`
echo "[${APP_NAME}] pid="${pid}
# 存在则kill,不存在打印一下吧
if [ "${pid}" ]; then
kill -9 ${pid}
# 检查kill是否成功
if [ "$?" -eq 0 ]; then
echo "[${APP_NAME}] stop success"
else
echo "[${APP_NAME}] stop fail"
fi
else
echo "[${APP_NAME}] 进程不存在"
fi
}
# 运行
function start(){
echo "<-------------------------------------->"
echo "[${APP_NAME}] ... start ..."
cd ${APP_JAR_HOME}
echo "当前路径:`pwd`"
# 赋予可读可写可执行权限
chmod 777 ${APP_JAR}
echo "启动命令: nohup java -jar ${APP_JAR} ${JAVA_OPTS} >> ${APP_LOG_HOME}/${APP_LOG_NAME}.log 2>&1 &"
sudo nohup java -jar ${APP_JAR} ${JAVA_OPTS} >> ${APP_LOG_HOME}/${APP_LOG_NAME}.log 2>&1 &
if [ "$?" -eq 0 ]; then
echo "[${APP_NAME}] start success"
else
echo "[${APP_NAME}] start fail"
fi
}
# 重启
function restart(){
echo "<-------------------------------------->"
echo "[${APP_NAME}] ... restart ..."
stop
sleep 3
start
}
# 多分支条件判断执行参数
case "${APP_OPT}" in
"stop")
stop
;;
"start")
start
;;
"restart")
restart
;;
*)
echo " 提示:不支持参数 命令 -> ${APP_OPT}"
;;
esac
jenkins工作目录磁盘空间挂载
因为我们使用的micro的EC2, 需要给jenkins 工作目录挂载更大的空间
Jenkins 默认是安装在/root/.jenkins 目录下,查看目录下使用和剩余空间
df -h /root/.jenkins/
将 /dev/nvme0n1p1 作为你的设备进行挂载并增加 /root/.jenkins 的空间,以下是相应的步骤: 步骤 1: 创建挂载点创建一个新的目录作为挂载点(例如 /mnt/jenkins_data):
sudo mkdir -p /mnt/jenkins_data
步骤 2: 挂载文件系统 将设备 /dev/nvme0n1p1 挂载到新创建的目录:
sudo mount /dev/nvme0n1p1 /mnt/jenkins_data
步骤 3: 更新 /etc/fstab 为了确保在重启后仍然保持挂载,需要更新 /etc/fstab 文件。打开该文件进行编辑:
sudo nano /etc/fstab
在文件末尾添加以下行:
/dev/nvme0n1p1 /mnt/jenkins_data ext4 defaults,nofail 0 2
确保根据实际文件系统类型(如 ext4)进行调整。 步骤 4: 移动 Jenkins 数据 如果 /root/.jenkins 中的数据需要迁移到新的挂载点:
sudo cp -r /root/.jenkins/* /mnt/jenkins_data/ sudo ln -s /mnt/jenkins_data /root/.jenkins
查看现在的工作空间大小
micro 免费的同时就得随时手动挂载确保空间足够,后面jenkins 交换空间不够也是这样解决的,
Dashboard -> 系统管理 -> 节点列表上, 如果资源不够是无法启动构建job的
启动jenkins
查看 Jenkins 初始密码
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
登录后安装必要的插件会花费一些时间
Github 准备
创建一个项目类似于我做实践用的
https://github.com/chenrui2200/aws-jenkins-codepipeline
develop setting 里生成token
保存该token后面会用到多次
配置webhooks
在项目内setting里配置webhooks , PayloadUrl 填写 <jenkins_url>/github-webhook/
点击保存
jenkins system configure 配置access token
把access token 录入到credentials
跳转system configuration
找到github server, 使用access token配置https://api.github.com
添加认证方式,在箭头处填写github access token
点击测试连接出现下面字样说明配置成功
项目文件准备
新建基于 python 的 lambda 函数。此 lambda 函数为我们的目标 lambda 函数,在本实验完成后,每当有新的 commit,都会触发此 lambda 函数进行自动化部署。 如您不清楚步骤,请参考创建您的第一个 Lambda 函数 此文重在搭建 CICD 流水线,代码会直接用默认生成的 python 代码 lambda_function.py。
publish 此 lambda,版本为1,并且创建别名(alias)。
除此以外,还需要添加另外两个文件
- appspec.template.yaml, 用于 codedeploy 配置文件。请将此文件当中的函数名以及 alias(别名)替换为自己对应的值。
- buildspec.yml,用于 codebuild 配置文件,修改替换文件中尖括号标注部分(去掉尖括号)。
buildspec.yml
version: 0.2
phases:
install:
runtime-versions:
python: 3.7
commands:
- echo pre Installing ...
pre_build:
commands:
- echo prebuild,Installing update ...
# 基本环境更新
#- yum update -y
build:
commands:
- echo Build started on `date`
post_build:
commands:
- ls
- mkdir build
- # 替换所有尖括号标注,替换时,去掉尖括号
- CurrentVersion=$(echo $(aws lambda get-alias --function-name <替换为自己的 lambda 函数 ARN> --name <替换为自己的lambda alias> --region <your_region> | grep FunctionVersion | tail -1 |tr -cd "[0-9]"))
- zip -r ./build/lambda.zip ./lambda_function.py
- aws lambda update-function-code --function-name <替换为自己的 lambda 函数 ARN> --zip-file fileb://build/lambda.zip --region <your_region> --publish
- TargetVersion=$(echo $(aws lambda list-versions-by-function --function-name <替换为自己的 lambda 函数 ARN> --region <your_region> | grep Version | tail -1 | tr -cd "[0-9]"))
- echo $CurrentVersion
- echo $TargetVersion
- sed -e 's/{{CurrentVersion}}/'$CurrentVersion'/g' -e 's/{{TargetVersion}}/'$TargetVersion'/g' appspec.template.yaml > appspec.yaml
- aws s3 cp appspec.yaml s3://<替换为自己的S3 Bucket名称>/jenkins/codedeploy/appspec.yaml
- # 替换 first-try-with-jenkins 为自己的codedeploy名称
- aws deploy create-deployment --application-name first-try-with-jenkins --deployment-group-name first-try-with-jenkins --s3-location bucket='<替换为自己的S3 Bucket名称>',key='jenkins/codedeploy/appspec.yaml',bundleType=YAML
artifacts:
type: yaml
files:
- appspec.yaml
appspec.template.yaml
version: 0.0
Resources:
- test: # Replace "MyFunction" with the name of your Lambda function
Type: AWS::Lambda::Function
Properties:
Name: "test" # Specify the name of your Lambda function
Alias: "beta" # Specify the alias for your Lambda function
CurrentVersion: "{{CurrentVersion}}" # Specify the current version of your Lambda function
TargetVersion: "{{TargetVersion}}" # Specify the version of your Lambda function to deploy
lambda_function.py
import json
def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
AWS 准备
Codebuild 准备
新建codebuild项目
【源】选择github, 点击管理认证凭证配置访问凭证
认证成功
其他选项不用动
选择EC2 相同的vpc
使用buildspec文件构建,文件名对应的是github项目里的buildspec.yml 文件名称
codebuild选择的role需要具备 s3 codebuild的权限
CodeDeploy 准备
计算平台选择lamda
创建部署组组
codedeploy选择的role需要具备 codedeploy 的权限
构建CI/CD 流程
创建一个 Jenkin 的项目,配置 codedeploy 相应信息
新建项目之前,先安装 codebuild 的插件。点击 系统管理 -- 插件管理(plugin)
新建一Jenkins个项目,点击“Create a new project” -- "freestyle project"
配置Github项目的地址,源代码管理选择Git方式。
上面credentials没有显示secret text类别的,需要安装Plain Credentials Plugin。您还需要安装Credentials Binding Plugin来传递凭据, 同时宿主机上需要安装 git
触发构建,选择 Github hook trigger for GITScm polling
配置 AK, SK , region, project-name
尝试提交push下代码, 看到push事件已经触发了jenkins
jenkins端插件已经触发了codebuild 去构建,提交者是codebuild-jenkins plugin
整个流程已经打通, 查看jenkins 的构建日志
Codebuild 日志也看到成功了
查看lambda 函数已经成功更新了