一个简单的jenkins+harbor+k8s的cicd
# Kubernetes CI/CD 环境搭建指南
由于学习K8S有一段时间了,之前使用Jenkins Pipeline实现的CI/CD也已经很久没有更新。现在想尝试一些新的东西。
## 网络配置概览
| IP地址 | 主机名 | 备注 |
|-------------|----------------|------------|
| 172.31.7.11 | master1-7-11 | 主Master |
| 172.31.7.21 | jenkins-7-21 | Jenkins |
| 172.31.7.22 | gitlab-7-22 | GitLab |
| 172.31.7.10 | harbor-7-10 | Harbor |
备注:系统均为Ubuntu
---
# 一、准备Jenkins环境(特别是JDK)
### 安装JDK
```bash
apt install openjdk-17-jdk
下载最新稳定版Jenkins安装包
安装长期支持版本Jenkins
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc]" \
https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update
sudo apt-get install jenkins
修改Jenkins配置文件 /etc/default/jenkins
vim /etc/default/jenkins
NAME=root
JAVA_ARGS="-Djava.awt.headless=true"
JENKINS_USER=root
JENKINS_GROUP=root
JENKINS_HOME=/var/lib/$NAME
修改Jenkins systemd服务文件 /usr/lib/systemd/system/jenkins.service
vim /usr/lib/systemd/system/jenkins.service
User=root # 将服务启动的用户改为root
Group=root # 服务启动的组改为root
重启Jenkins服务:
systemctl restart jenkins
systemctl daemon-reload

二、安装GitLab
下载GitLab安装包
去清华大学镜像站下载喜欢的版本。
配置并启动GitLab
参考这篇博客进行安装与配置。
创建用户并修改密码,创建测试项目

### 1. **Kubernetes Deployment YAML 文件**
这个YAML文件定义了一个Kubernetes部署(Deployment)和服务(Service),用于在Kubernetes集群中运行一个Tomcat应用。
```yaml
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: item1-tomcat-app1-deployment-label
name: item1-tomcat-app1-deployment
namespace: item1
spec:
replicas: 2
selector:
matchLabels:
app: item1-tomcat-app1-selector
template:
metadata:
labels:
app: item1-tomcat-app1-selector
spec:
imagePullSecrets:
- name: harbor-secret
containers:
- name: item1-tomcat-app1-container
image: harbor.xtec.com/item1/tomcat:8.5.43_20250117_013112
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits:
cpu: 1
memory: "512Mi"
requests:
cpu: 500m
memory: "512Mi"
volumeMounts:
- name: item1-images
mountPath: /usr/local/nginx/html/webapp/images
readOnly: false
- name: item1-static
mountPath: /usr/local/nginx/html/webapp/static
readOnly: false
volumes:
- name: item1-images
nfs:
server: 172.31.7.20
path: /data/k8sdata/item1/images
- name: item1-static
nfs:
server: 172.31.7.20
path: /data/k8sdata/item1/static
---
kind: Service
apiVersion: v1
metadata:
labels:
app: item1-tomcat-app1-service-label
name: item1-tomcat-app1-service
namespace: item1
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
nodePort: 30092
selector:
app: item1-tomcat-app1-selector
功能说明:
- 定义了一个名为
item1-tomcat-app1-deployment
的部署,使用两个副本(replicas: 2)。 - 每个容器使用
harbor.xtec.com/item1/tomcat:8.5.43_20250117_013112
镜像,并暴露8080端口。 - 使用NFS挂载卷来存储静态资源。
- 定义了一个名为
item1-tomcat-app1-service
的服务,类型为NodePort
,将集群内部的8080端口映射到节点上的30092端口。
2. Dockerfile
这个Dockerfile基于Harbor镜像仓库中的基础Tomcat镜像构建一个新的Tomcat应用镜像。
FROM harbor.xtec.com/pub-images/tomcat:8.5.43_20250107_235050
# 创建并设置 Tomcat 应用目录
RUN mkdir -p /data/tomcat/webapps/myapp && \
chown -R tomcat:tomcat /data/tomcat/webapps/myapp
# 清空默认应用目录并复制文件
COPY catalina.sh /apps/tomcat/bin/catalina.sh
COPY server.xml /apps/tomcat/conf/server.xml
COPY app1.tar.gz /data/tomcat/webapps/myapp/app1.tar.gz
COPY run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh
# 解压应用包并设置权限
RUN tar -xzf /data/tomcat/webapps/myapp/app1.tar.gz -C /data/tomcat/webapps/myapp/ && \
rm /data/tomcat/webapps/myapp/app1.tar.gz && \
chown -R tomcat:tomcat /data/ /apps/ && \
chmod +x /apps/tomcat/bin/catalina.sh /apps/tomcat/bin/run_tomcat.sh
# 清理 Tomcat 工作目录
RUN rm -rf /apps/tomcat/work/Catalina/localhost/*
# 健康检查
HEALTHCHECK CMD curl -f http://localhost:8080 || exit 1
# 暴露端口
EXPOSE 8080 8443
# 启动命令
CMD ["/apps/tomcat/bin/run_tomcat.sh"]
功能说明:
- 基于基础Tomcat镜像创建新的镜像。
- 设置应用目录并复制必要的配置文件和应用程序包。
- 解压缩应用程序包并设置正确的权限。
- 清理Tomcat的工作目录以确保干净启动。
- 添加健康检查以确保Tomcat正常运行。
- 暴露8080和8443端口,并使用自定义脚本启动Tomcat。
3. 镜像构建脚本 (build-command.sh
)
这个脚本用于构建和推送Docker镜像到Harbor镜像仓库。
#!/bin/bash
# build-command.sh - 构建和推送 Docker 镜像脚本
# Author: orochw
# Updated: 2025-01-16
HARBOR="harbor.xtec.com"
REPO="item1"
IMAGE="tomcat"
FIXED_TAG="8.5.43"
DOCKERFILE_DIR="/opt/k8s-data/dockerfile/web/item1/tomcat-app1"
# 确保接收时间戳参数
if [ -z "$1" ]; then
echo "ERROR: Date timestamp is missing!"
exit 1
fi
DATE=$1
FULL_IMAGE="$HARBOR/$REPO/$IMAGE:${FIXED_TAG}_$DATE"
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
ERROR_exit() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1"
exit 1
}
# 检查 Docker 是否可用
docker -v >/dev/null 2>&1 || ERROR_exit "Docker 未安装或不可用,请检查环境!"
# 构建 Docker 镜像
log "开始构建 Docker 镜像: $FULL_IMAGE"
docker build -t $FULL_IMAGE $DOCKERFILE_DIR || ERROR_exit "Docker 构建失败!"
# 推送 Docker 镜像
log "开始推送 Docker 镜像: $FULL_IMAGE"
docker push $FULL_IMAGE || ERROR_exit "Docker 推送失败!"
log "Docker 镜像构建和推送完成: $FULL_IMAGE"
功能说明:
- 确保传递了时间戳参数,并生成完整的镜像名称。
- 检查Docker是否可用。
- 构建并推送镜像到指定的Harbor仓库。
4. CI/CD 脚本
这个脚本实现了从代码克隆、打包、传输、镜像构建、更新Kubernetes配置到应用更改的整个CI/CD流程。
#!/bin/bash
# 配置部分
HARBOR="harbor.xtec.com"
REPO="item1"
IMAGE="tomcat"
FIXED_TAG="8.5.43"
K8S_CONTROLLER="172.31.7.11"
GIT_URL="git@172.31.7.22:item1/app1.git"
SRC_DIR="/data/gitdata/item1/app1"
DEST_DIR="/opt/k8s-data/dockerfile/web/item1/tomcat-app1"
K8S_YAML="/opt/k8s-data/yaml/item1/tomcat-app1/tomcat-app1.yaml"
# 记录开始时间
starttime=$(date +'%Y-%m-%d %H:%M:%S')
# 函数定义
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
ERROR_exit() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1"
exit 1
}
code_clone() {
log "开始拉取代码分支: $BRANCH"
mkdir -p $(dirname $SRC_DIR)
rm -rf $SRC_DIR
git clone -b $BRANCH $GIT_URL $SRC_DIR || ERROR_exit "Git 克隆失败!"
log "代码拉取完成"
}
package_file() {
log "开始打包代码文件"
mkdir -p $DEST_DIR
tar -czf $DEST_DIR/app1.tar.gz -C $SRC_DIR . || ERROR_exit "文件打包失败!"
log "文件打包完成: $DEST_DIR/app1.tar.gz"
}
copy_file() {
log "开始复制文件到 K8S 控制器: $K8S_CONTROLLER"
ssh root@$K8S_CONTROLLER "mkdir -p $DEST_DIR"
scp -r $DEST_DIR/* root@$K8S_CONTROLLER:$DEST_DIR || ERROR_exit "文件复制失败!"
log "文件成功复制到 $K8S_CONTROLLER:$DEST_DIR"
}
build_image() {
log "开始构建和推送 Docker 镜像: $FULL_IMAGE"
ssh root@$K8S_CONTROLLER <<EOF
cd $DEST_DIR
chmod +x build-command.sh
./build-command.sh $DATE || exit 1
EOF
[ $? -ne 0 ] && ERROR_exit "Docker 镜像构建或推送失败!"
log "Docker 镜像构建并推送完成: $FULL_IMAGE"
}
update_k8s_yaml() {
log "开始更新 Kubernetes YAML 文件"
ssh root@$K8S_CONTROLLER <<EOF
if [ -f $K8S_YAML ]; then
sed -i "s|image: $HARBOR/$REPO/$IMAGE:.*|image: $FULL_IMAGE|g" $K8S_YAML
if ! grep -q 'imagePullSecrets' $K8S_YAML; then
sed -i '/containers:/i \ imagePullSecrets:\n - name: harbor-secret' $K8S_YAML
fi
else
echo "ERROR: Kubernetes YAML 文件不存在: $K8S_YAML"
exit 1
fi
EOF
[ $? -ne 0 ] && ERROR_exit "Kubernetes YAML 文件更新失败!"
log "Kubernetes YAML 文件更新完成"
}
apply_k8s_changes() {
log "应用 Kubernetes 部署更新"
ssh root@$K8S_CONTROLLER "kubectl apply -f $K8S_YAML" || ERROR_exit "Kubernetes 部署更新失败!"
log "Kubernetes 部署更新完成"
}
rollback_last_version() {
log "开始回滚到上一个版本"
ssh root@$K8S_CONTROLLER "kubectl rollout undo deployment/item1-tomcat-app1-deployment -n item1" || ERROR_exit "回滚失败!"
log "回滚成功"
}
# 显示用法
usage() {
echo "Usage: $0 {deploy|rollback_last_version} [branch]"
}
# 参数处理
OPERATION=${1:-deploy}
BRANCH=${2:-main} # 默认分支为 main
DATE=$(date +%Y%m%d_%H%M%S)
FULL_IMAGE="$HARBOR/$REPO/$IMAGE:${FIXED_TAG}_$DATE"
# 主逻辑
case $OPERATION in
deploy)
code_clone
package_file
copy_file
build_image
update_k8s_yaml
apply_k8s_changes
;;
rollback_last_version)
rollback_last_version
;;
*)
usage
exit 1
;;
esac
# 显示总耗时
endtime=$(date +'%Y-%m-%d %H:%M:%S')
start_seconds=$(date --date="$starttime" +%s)
end_seconds=$(date --date="$endtime" +%s)
log "总执行时间: $((end_seconds - start_seconds)) 秒"
功能说明:
- code_clone: 从指定Git仓库拉取特定分支的代码。
- package_file: 将代码打包成
.tar.gz
格式。 - copy_file: 将打包好的文件复制到Kubernetes控制器节点。
- build_image: 在Kubernetes控制器上构建并推送Docker镜像。
- update_k8s_yaml: 更新Kubernetes YAML文件中的镜像地址,并添加
imagePullSecrets
部分。 - apply_k8s_changes: 应用更新后的YAML文件到Kubernetes集群。
- rollback_last_version: 回滚到上一个版本。
- 主逻辑: 根据传入的操作类型(
deploy
或rollback_last_version
)执行相应的操作。
脚本中的函数及其作用
以下是脚本中定义的主要函数及其作用:
-
log()
:- 作用: 打印带时间戳的日志信息,便于调试和追踪脚本执行过程。
- 示例调用:
log "开始构建 Docker 镜像: $FULL_IMAGE"
-
ERROR_exit()
:- 作用: 当发生错误时打印错误信息并退出脚本执行,确保问题不会被忽略。
- 示例调用:
ERROR_exit "Docker 构建失败!"
-
code_clone()
:- 作用: 从指定Git仓库克隆代码到本地目录,以便后续步骤可以访问这些代码进行打包和构建。
- 示例调用:
code_clone
-
package_file()
:- 作用: 将克隆下来的代码打包成压缩文件(如
.tar.gz
),以便传输到Kubernetes控制器节点。 - 示例调用:
package_file
- 作用: 将克隆下来的代码打包成压缩文件(如
-
copy_file()
:- 作用: 使用
scp
命令将打包好的文件复制到Kubernetes控制器节点上的指定目录。 - 示例调用:
copy_file
- 作用: 使用
-
build_image()
:- 作用: 在Kubernetes控制器节点上运行
build-command.sh
脚本,实际执行镜像的构建和推送操作。 - 示例调用:
build_image
- 作用: 在Kubernetes控制器节点上运行
-
update_k8s_yaml()
:- 作用: 更新Kubernetes YAML配置文件,确保其引用的是最新构建的镜像。
- 示例调用:
update_k8s_yaml
-
apply_k8s_changes()
:- 作用: 应用更新后的Kubernetes YAML配置文件,触发集群内的部署更新。
- 示例调用:
apply_k8s_changes
-
rollback_last_version()
:- 作用: 回滚到前一个稳定版本,使用Kubernetes的
rollout undo
命令实现。 - 示例调用:
rollback_last_version
- 作用: 回滚到前一个稳定版本,使用Kubernetes的
这些函数共同构成了一个完整的自动化流程,使得从代码提交到生产环境部署变得更加高效且可管理。
四、创建Jenkins任务
1. 安装SSH插件及配置凭据
插件安装
- 步骤:在Jenkins中安装SSH插件。
- 截图:
远程主机设置
- 创建新的凭据:
- 登录Jenkins -> 系统管理 -> 凭据 -> 全局 -> 新增凭据
- 结果如图:
- 系统管理 -> 系统 -> SSH Remote Hosts
- 设置如图:
- 设置如图:
2. 创建任务
- 新建任务:选择“自由风格项目”类型。
- 截图:
- 配置Git仓库地址和分支。
- 构建触发器:可以设置定时构建、轮询SCM等触发条件。
- 构建环境:如果需要,可以设置环境变量等。
- 构建:添加执行Shell脚本的步骤,调用前面准备好的CI/CD脚本进行构建、打包、推送镜像以及更新Kubernetes YAML文件等操作。
- 截图:
五、测试CI/CD流程
更新测试
修改index.html
- 对应用的
index.html
文件进行了修改,作为版本更新的一部分。 - 触发Jenkins任务执行更新流程。
- 控制台输出显示了从克隆代码、打包、传输文件、构建和推送Docker镜像、更新Kubernetes YAML文件到最后应用更改的全过程,并且最终结果显示为SUCCESS。
Started by user root
Running as SYSTEM
Building in workspace /var/lib/jenkins/workspace/bash-update-item1-tomcat-app1-deploy
...
Successfully built a524b7dbb530
Successfully tagged harbor.xtec.com/item1/tomcat:8.5.43_20250117_025158
...
deployment.apps/item1-tomcat-app1-deployment configured
service/item1-tomcat-app1-service unchanged
[2025-01-17 02:52:09] Kubernetes ������������������
[2025-01-17 02:52:09] ���������������: 11 ���
Finished: SUCCESS
查看更新前后的结果
- 截图:
测试回滚
Started by user root
Running as SYSTEM
Building in workspace /var/lib/jenkins/workspace/bash-update-item1-tomcat-app1-deploy
...
deployment.apps/item1-tomcat-app1-deployment rolled back
[2025-01-17 01:32:03] ������������
[2025-01-17 01:32:03] ���������������: 0 ���
Finished: SUCCESS