整合任务
可以将Build和Deploy整合成一个Pipeline任务;同时,若不需要外部调用来触发任务,可以将其中大部分参数固化,一个项目一个任务,以方便用户直接运行。
前端发布
前端采用Node技术开发,故需要在Jenkins上安装Node环境,以进行构建并生成最终的HTML文件;将其添加至 nginx:stable 作为基础镜像来生成项目镜像。
配置Node环境(220.140)
安装:
wget https://nodejs.org/dist/v14.15.4/node-v14.15.4-linux-x64.tar.xz tar Jxf node-v14.15.4-linux-x64.tar.xz -C /usr/local/ cd /usr/local/ mv node-v14.15.4-linux-x64 nodejs echo 'PATH=$PATH:/usr/local/nodejs/bin' > /etc/profile.d/nodejs.sh source /etc/profile.d/nodejs.sh node -v
安装插件:
npm --registry https://registry.npm.taobao.org install --ensure node-gyp -g npm --registry https://registry.npm.taobao.org install --unsafe-perm node-sass -g
基础镜像
这里就使用最新的nginx:stable作为源(内部Nginx版本为 18.0),来制作基础镜像:
docker pull nginx:stable docker history nginx:stable
mkdir ~/baseimg-nginx cd ~/baseimg-nginx/ vim nginx.conf
# Nginx Main Configure File.
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
server {
listen 8080;
location /seid {
add_header "Set-Cookie" "eid=$arg_eid";
return 200 "success";
}
location / {
root /data/www/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
}
vim Dockerfile
FROM nginx:stable as build
RUN mkdir -p /data/www && \
sed -i 's/^# alias/alias/g' ~/.bashrc && \
sed -i 's/^# export/export/g' ~/.bashrc && \
sed -i "/^alias mv/a\alias ll='ls -lh --time-style=\"+%Y/%m/%d %H:%M\" --color'" ~/.bashrc
COPY nginx.conf /etc/nginx/nginx.conf
FROM scratch
COPY --from=build / .
LABEL maintainer="Chris"
ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 8080
CMD ["nginx","-g","daemon off;"]
WORKDIR /data
docker build -t baseimg-nginx:1.18 . docker images
试运行:
docker run --name=nginx --rm -p 8080:8080 -d baseimg-nginx:1.18 dokcer ps
由于现在网站目录为空,访问会出现403;进入Container,生成个页面后刷新:
docker exec -it nginx /bin/bash echo "Hello, I'm run in docker." > /data/www/index.html
杀掉容器, 标签镜像:
docker kill nginx docker tag baseimg-nginx:1.18 nw-harbor.xxx.cc/library/baseimg-nginx:1.18 docker images
上传镜像:
docker push nw-harbor.xxx.cc/library/baseimg-nginx:1.18
Tips: Nginx官方的Docker容器中已将日志输出结果重定向到stdout和stderr,故不需再操作
创建任务
创建个Pipeline任务:
node{
// 用于前端项目(Nginx静态文件), 固化项目配置参数, 每项目一任务, 方便直接执行; 不建议外部调用
// 镜像仓库
String HarborUrl='nw-harbor.zongs365.cc'
String HarborUser='admin'
String HarborPasswd='Nw-Harbor123'
// 构建用: 项目Gitlab地址
String GIT_URL='http://git.zongsheng.tech/all/demo-front.git'
// Node NPM 构建用: 配置构建的环境 npm run build:
String Build_Env='prod'
// Docker镜像 推送,拉取用: 项目组名称,推送至此Harbor项目组 (e.g: ${HarborUrl}/${Group})
String Group='zongs-nw'
// Docker镜像 构建用,部署配置用,Docker镜像 推送,拉取用: 填写项目名称
String Project_name='demo-front'
// Docker镜像 构建用: 生成的前端包路径名称
String Target_name='dist.tar.gz'
// Docker镜像 构建用: 生成项目Docker镜像的时区
String TIME_ZONE='Asia/Shanghai'
// 项目tag(镜像构建的版本; 这里携带Jenkins任务构建版本)
String Project_Tag="ng-${env.BUILD_NUMBER}"
// 部署配置用: 部署至此命名空间
String NAMESPACE='default'
// 部署配置用: 运行的 Pod 数目
String REPLICAS_NUM='1'
// 部署配置用: 资源CPU需求
String REQUESTS_CPU='100m'
// 部署配置用: 资源Memory需求
String REQUESTS_MEM='128Mi'
// 部署配置用: 新创建的Pod状态为Ready持续了此时间后认为Available
String MINREADSECONDS='5'
// K8S YAML 模板文件所在服务器 (部署,服务模板文件)
String TemplateUrl='http://172.16.220.105/k8s'
// K8S Deployment YAML 模板文件名称
String Temp_Depoly='deployment-front.yaml'
// K8S Service YAML 模板文件名称
String Temp_SVC='svc.yaml'
// K8S 群集 操作主机 (用于SSH连接后执行 kubectl 命令)
String KubernetHost='[email protected]'
// 远端 操作主机 存放依模板生成的 YAML 文件
String Yaml_Path='/root/k8s_deploy'
// 用于SSH连接到 操作主机 的密钥文件 (若Jenkins运行用户可直接连接,则可留空)
String SSH_KeyFile='/data/jenkins/.ssh/id_rsa'
properties([
parameters([
string(name: 'GIT_BRANCH', defaultValue: 'master', description: '构建用: 填写Git分支地址', trim: false),
string(name: 'IS_SVC', defaultValue: 'No', description: '部署配置用: 是否创建服务(为 Yes 时部署svc.yaml)', trim: false),
string(name: 'SVC_PORT', defaultValue: '80', description: '部署配置用: 服务本身监听端口', trim: false),
string(name: 'TARGETPORT', defaultValue: '8080', description: '部署配置用: 服务后端项目端口(即项目本身所监听的端口)', trim: false)
])
])
if(!Project_name){
error "项目名为空"
}
if(SSH_KeyFile){
if(!fileExists("${SSH_KeyFile}")){
error "连接密钥文件 ${SSH_KeyFile} 不存在"
}
SSH_Command="ssh -i ${SSH_KeyFile} -p 22 -o StrictHostKeyChecking=no"
SCP_Command="scp -i ${SSH_KeyFile} -P 22 -o StrictHostKeyChecking=no"
}else{
SSH_Command="ssh -p 22 -o StrictHostKeyChecking=no"
SCP_Command="scp -P 22 -o StrictHostKeyChecking=no"
}
dir("${env.WORKSPACE}"){
stage('Git阶段'){
echo "1. 开始拉取代码 (${GIT_URL})"
git branch: params.GIT_BRANCH, credentialsId: 'nw-gitlab', url: "${GIT_URL}"
}
stage('Nodejs阶段'){
echo "2. 开始 Node npm 编译"
if(fileExists("${Target_name}")){
sh "rm -f ${Target_name}"
}
sh "npm --registry https://registry.npm.taobao.org install >/dev/null"
sh "npm run build:${Build_Env}"
sh "cd ./dist && tar -czf ../${Target_name} ./*"
sh 'cd ../'
}
stage('Docker阶段'){
echo "3. 开始执行Docker编译、推送、删除"
echo "--生成 Dockerfile"
sh "echo 'FROM ${HarborUrl}/library/baseimg-nginx:1.18' > Dockerfile"
sh "echo 'RUN ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime' >> Dockerfile"
sh "echo 'ADD ${Target_name} /data/www/' >> Dockerfile"
echo "--构建并推送 Docker 镜像 (${HarborUrl}/${Group}/${Project_name}:${Project_Tag})"
step([$class: 'DockerBuilderPublisher', dockerFileDirectory: "${env.WORKSPACE}", cloud: 'docker_w1', tagsString: "${HarborUrl}/${Group}/${Project_name}:${Project_Tag}", cleanImages: true, cleanupWithJenkinsJobDelete: false, pushCredentialsId: 'nw-harbor', pushOnSuccess: true])
}
stage('群集主机SSH验证'){
echo "验证k8s群集内主机 ${KubernetHost} 是否可连通,并确保YAML文件存放目录 ${Yaml_Path}"
sh "${SSH_Command} ${KubernetHost} mkdir -p ${Yaml_Path}"
}
stage('K8S-Deployment 配置'){
echo "从 ${TemplateUrl} 拉取 K8S-Deployment 模板文件"
sh "wget ${TemplateUrl}/${Temp_Depoly} -O ${Project_name}-deploy.yaml"
echo "按参数配置修改模板文件"
sh "sed -i 's#CI_PROJECT_NAME#${Project_name}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#NAMESPACE#${NAMESPACE}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#REPLICAS_NUM#${REPLICAS_NUM}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#REPOSITORY_BASE#${HarborUrl}/${Group}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#BUILD_IMAGE_VERSION#${Project_Tag}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#REQUESTS_CPU#${REQUESTS_CPU}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#REQUESTS_MEM#${REQUESTS_MEM}#g' ${Project_name}-deploy.yaml"
sh "sed -i 's#MINREADSECONDS#${MINREADSECONDS}#g' ${Project_name}-deploy.yaml"
echo "将修改的文件 ${Project_name}-deploy.yaml 传送至 k8s 群集内部机器 ${KubernetHost}"
sh "${SCP_Command} ${Project_name}-deploy.yaml ${KubernetHost}:${Yaml_Path}"
}
if(IS_SVC == 'Yes'){
stage('K8S-SVC 配置(可选项)'){
echo "从 ${TemplateUrl} 拉取 K8S-SVC 模板文件"
sh "wget ${TemplateUrl}/${Temp_SVC} -O ${Project_name}-svc.yaml"
echo "按参数配置修改模板文件"
sh "sed -i 's#CI_PROJECT_NAME#${Project_name}#g' ${Project_name}-svc.yaml"
sh "sed -i 's#NAMESPACE#${NAMESPACE}#g' ${Project_name}-svc.yaml"
sh "sed -i 's#SVC_PORT#${SVC_PORT}#g' ${Project_name}-svc.yaml"
sh "sed -i 's#TARGETPORT#${TARGETPORT}#g' ${Project_name}-svc.yaml"
echo "将修改的文件 ${Project_name}-svc.yaml 传送至 k8s 群集内部机器 ${KubernetHost}"
sh "${SCP_Command} ${Project_name}-svc.yaml ${KubernetHost}:${Yaml_Path}"
}
}
stage('部署至K8S'){
echo "部署应用 ${Project_name}-deploy.yaml"
sh "${SSH_Command} ${KubernetHost} kubectl apply -f ${Yaml_Path}/${Project_name}-deploy.yaml"
if(IS_SVC == 'Yes'){
echo "部署服务 ${Project_name}-svc.yaml"
sh "${SSH_Command} ${KubernetHost} kubectl apply -f ${Yaml_Path}/${Project_name}-svc.yaml"
}
}
}
}
同样的,第一次执行会失败,主要是因为IS_SVC参数需要提供,前面构建均正常:
再次执行,初次部署,将IS_SVC设为Yes,以创建服务:
执行成功:
K8S上验证:
kubectl get deployments.apps kubectl get pods kubectl get svc











