微服务CI/CD实践系列:
微服务CI/CD实践(一)环境预备及假造机创建
微服务CI/CD实践(二)服务器先决预备
微服务CI/CD实践(三)gitlab部署及nexus3部署
微服务CI/CD实践(四)Jenkins部署及环境配置
微服务CI/CD实践(五)Jenkins + Dokcer 部署微服务后端项目
微服务CI/CD实践(六)Jenkins + Dokcer 部署微服务前端VUE项目
微服务CI/CD实践(七)Minio服务器部署及应用
前端项目是基于NodeJS(Vue)框架开辟,我们通过打包成Docker镜像的方式进行部署,原理是先将项目打包成静态页面,然后再将静态页面直接copy到Nginx镜像中运行。构建部署流程如下:
- 拉代替码
- jenkins服务器进行nodejs编译
- 利用dockerfile构建镜像并打包镜像
- 上传镜像包
- 执行sh
一、先决条件
1.1 服务器先决条件
Jenkins 和 server服务器先决条件参考微服务CI/CD实践(二)服务器先决预备 和 微服务CI/CD实践(四)Jenkins部署及环境配置
1.2 项目配置
Dockerfile
利用Jenkins本地编译项目在构建镜像
- FROM nginx:latest
- # 将生成的静态页面文件复制到nginx的/usr/share/nginx/html/目录
- COPY dist/ /usr/share/nginx/html/
- # 将mime文件复制到nginx的/etc/nginx/目录 后续配置ng会使用
- COPY mime.types /etc/nginx/mime.types
- # 容器启动时运行的命令
- CMD ["nginx", "-g", "daemon off;"]
复制代码 也可以直接利用docker 镜像编译-构建镜像,此模式jenkins服务器可以不必要node环境
- # Install dependencies
- FROM node:18.20.4 as builder
- WORKDIR /app
- # Install pnpm
- RUN npm i -g pnpm
- # copy file for next stage
- COPY . /app
- RUN pnpm install && pnpm run build
- # copy dist from the first stage for Production
- FROM nginx:latest AS runner
- COPY --from=builder /app/dist/ /usr/share/nginx/html
- COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf
复制代码 不过此模式在docker-hub制止国内服务后可能无法正常拉取镜像。
Nginx配置文件
根据项目要求编写ng配置
- cd /data/container/nginx/etc
- vi nginx.conf
- # 编写配置并保存
- vi mime.types
- # 编写配置并保存
复制代码 以下为nginx.conf配置示例
- events {
- worker_connections 1024;
- }
- http {
- # 需要引入mime.types配置或者显示配置静态文件mimetype类型,否则运行后,浏览器会因为文件类型导致无法正常加载静态文件
- include mime.types;
- 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;
- tcp_nodelay on;
- keepalive_timeout 65;
- types_hash_max_size 2048;
- client_max_body_size 100m;
- server {
- listen 80;
- listen [::]:80;
- server_name localhost;
-
- # 设置 CORS 相关的响应头
- add_header 'Access-Control-Allow-Origin' '*' always;
- add_header 'Access-Control-Allow-Methods' '*' always;
- add_header 'Access-Control-Max-Age' 1728000 always;
- add_header 'Access-Control-Allow-Headers' '*' always;
- add_header 'Access-Control-Allow-Credentials' 'true' always;
-
- gzip on;
- gzip_buffers 32 4k;
- gzip_comp_level 6;
- gzip_min_length 100;
- gzip_types application/javascript text/css text/xml text/plain application/x-javascript image/jpeg image/gif image/png;
- gzip_disable "MSIE [1-6]\.";
- gzip_vary on;
- charset utf8;
- location / {
- root /usr/share/nginx/html;
- index index.html index.htm;
- try_files $uri $uri/ /index.html;
- if (!-e $request_filename) {
- rewrite ^/(.*) /index.html last;
- break;
- }
- }
- location ~ .*\.(jpg|png|js|css|woff2|ttf|woff|eot)$ {
- root /usr/share/nginx/html;
- }
- #error_page 404 /404.html;
- # redirect server error pages to the static page /50x.html
- #
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root /usr/share/nginx/html;
- }
-
- # 配置全局代理并统一处理CORS
- location /gateway-api/ {
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-Ip $remote_addr;
- proxy_set_header REMOTE-HOST $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_pass http://192.168.1.103:10000/;
- # 添加 CORS 相关的响应头
- add_header 'Access-Control-Allow-Origin' '*' always;
- add_header 'Access-Control-Allow-Methods' '*' always;
- add_header 'Access-Control-Max-Age' 1728000 always;
- add_header 'Access-Control-Allow-Headers' '*' always;
- add_header 'Access-Control-Allow-Credentials' 'true' always;
- # 处理 OPTIONS 请求
- if ($request_method = 'OPTIONS') {
- return 204;
- }
- }
- }
- }
复制代码 以下为mime.type示例
- types {
- text/html html htm shtml;
- text/css css;
- image/gif gif;
- image/jpeg jpeg jpg;
- application/javascript js;
- application/xml xml;
- application/json json;
- application/pdf pdf;
- application/rss+xml rss;
- application/atom+xml atom;
- text/mathml mml;
- text/plain txt;
- text/vnd.sun.j2me.app-descriptor jad;
- text/vnd.wap.wml wml;
- text/x-component htc;
- image/png png;
- image/svg+xml svg;
- image/tiff tif tiff;
- image/vnd.wap.wbmp wbmp;
- image/x-icon ico;
- image/x-jng jng;
- image/x-ms-bmp bmp;
- application/zip zip;
- application/tar tar;
- application/x-7z-compressed 7z;
- application/x-java-archive jar;
- application/x-rar-compressed rar;
- application/x-web-app-manifest+json webapp;
- application/xhtml+xml xhtml;
- application/x-msdownload exe dll;
- audio/midi mid midi kar;
- audio/mpeg mp3;
- video/mp4 mp4;
- video/mpeg mpeg mpg;
- video/webm webm;
- video/x-msvideo avi;
- video/x-ms-wmv wmv;
- video/x-ms-asf asx asf;
- video/x-flv flv;
- application/x-shockwave-flash swf;
- application/vnd.ms-excel xls;
- application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
- application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
- application/vnd.ms-fontobject eot;
- application/vnd.apple.mpegurl m3u8;
- application/x-font-ttf ttc ttf;
- application/x-httpd-php-source phps;
- }
复制代码 部署脚本
step1 界说入参
可以通过Jenkins任务将参数传入脚本中,我们界说了下面7个参数:
container_name : 容器名称
image_name : 镜像名称
version : 镜像版本
portal_port: 宿主主机端口映射
server_port: 容器内服务端口
portal_ssl_port: 宿主主机端口映射
serve_sslr_port: 容器内服务端口
step2 界说入参对参数进行查抄
将必传参数放在最前面,这里根据自己的实际情况判定,查抄是否传递参数。比如设置container_name、image_name、version 、portal_port、server_port5个参数必须传入,就设置参数的个数不能小于5。
- echo "param validate"
- if [ $# -lt 5 ]; then
- echo "you must use like this : /usr/docker-sh/your_script.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"
- exit
- fi
复制代码 step3 入参赋值
如果有参数传入,则赋值参数
- # 前五个参数是必传参数,无需判断直接赋值
- container_name="$1"
- image_name="$2"
- version="$3"
- portal_port="$4"
- server_port="$5"
- if [ "$6" != "" ]; then
- portal_ssl_port="$6"
- fi
- echo "portal_ssl_port=" $portal_ssl_port
- if [ "$7" != "" ]; then
- serve_sslr_port="$7"
- fi
复制代码 step4 制止并删除容器
- echo "执行docker ps"
- docker ps
- if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
- then
- echo $container_name "容器存在,停止并删除"
- echo "docker stop" $container_name
- docker stop $container_name
- echo "docker rm" $container_name
- docker rm $container_name
- else
- echo $container_name "容器不存在"
- fi
复制代码 step5 制止并删除镜像
- # 删除镜像
- echo "执行docker images"
- docker images
- if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
- then
- echo $image_name '镜像存在,删除镜像'
- docker rmi $(docker images -q $image_name 2> /dev/null) --force
- else
- echo $image_name '镜像不存在'
- fi
复制代码 step6 备份和加载安装包
- #bak image
- echo "bak image" $image_name
- BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
- mkdir -p "$BAK_DIR"
- cp "/opt/tmp/$container_name.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar
- echo "docker load" $image_name
- docker load --input /opt/tmp/$container_name.tar
复制代码 step7 执行运行镜像命令
- echo "docker run" $image_name
- docker run -d -p $portal_port:$server_port --name=$container_name --network=my-network -e TZ="Asia/Shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /data/container/nginx/etc/mime.types:/etc/nginx/mime.types -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone $image_name
复制代码 step8 执行删除安装包命令
- echo "remove tmp " $image_name
- rm -rf /opt/tmp/$container_name.tar
复制代码 以下为完整的安装部署脚本
- #!/usr/bin/env bashecho "param validate"
- if [ $# -lt 5 ]; then
- echo "you must use like this : /usr/docker-sh/your_script.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"
- exit
- fi
- container_name="$1"image_name="$2"version="$3"portal_port="$4"server_port="$5"if [ "$6" != "" ]; then portal_ssl_port="$6"fiecho "portal_ssl_port=" $portal_ssl_portif [ "$7" != "" ]; then serve_sslr_port="$7"fiecho "执行docker ps"
- docker ps
- if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
- then
- echo $container_name "容器存在,停止并删除"
- echo "docker stop" $container_name
- docker stop $container_name
- echo "docker rm" $container_name
- docker rm $container_name
- else
- echo $container_name "容器不存在"
- fi
- # 删除镜像
- echo "执行docker images"
- docker images
- if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
- then
- echo $image_name '镜像存在,删除镜像'
- docker rmi $(docker images -q $image_name 2> /dev/null) --force
- else
- echo $image_name '镜像不存在'
- fi
- #bak image
- echo "bak image" $image_name
- BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
- mkdir -p "$BAK_DIR"
- cp "/opt/tmp/$container_name.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar
- echo "docker load" $image_name
- docker load --input /opt/tmp/$container_name.tar
- echo "docker run" $image_name
- docker run -d -p $portal_port:$server_port --name=$container_name --network=my-network -e TZ="Asia/Shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /data/container/nginx/etc/mime.types:/etc/nginx/mime.types -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/Asia/Shanghai:/etc/timezone $image_name
- echo "remove tmp " $image_name
- rm -rf /opt/tmp/$container_name.tar
- echo "Docker Portal is starting,please try to access $container_name conslone url"
复制代码 二、Jenkins构建部署
2.1 创建项目
新建一个流水线任务
2.2 配置项目基本信息
创建完成项目,点击项目进入项目页面,点击左侧菜单》配置,进行项目基本配置
step1 项目构建汗青存储策略配置
根据项目实际情况配置存储策略
step2 配置参数化构建过程
Jenkins List Git Branches插件 构建选择指定git分支,点击添加参数选择List Git branchers选项进行Jenkins List Git Branches插件配置
Jenkins List Git Branches插件配置流程如下:
- 配置name
- 配置堆栈并选择凭据
- 选择Parameter Type
- 配置Branch Filter
2.3 界说 Pipeline script
step1 配置全局变量
- environment {
- REPOSITORY="http://192.168.1.101:8929/hka/hka-admin-wocwin.git"
- projectdir="hka-web-01"
- projectname="hka-admin-wocwin"
- }
复制代码 step2 获代替码
检出选择指定git分支的代码
- stages {
- stage('获取代码') {
- steps {
- echo "start fetch code from git:${REPOSITORY} ${branch}"
- deleteDir()
- checkout([
- $class: 'GitSCM',
- branches: [[name: '${branch}']],
- doGenerateSubmoduleConfigurations: false,
- extensions: [],
- userRemoteConfigs: [[
- credentialsId: '2',
- url: 'http://192.168.1.101:8929/hka/hka-admin-wocwin.git'
- ]]
- ])
- }
- }
复制代码 step3 编译项目
这里必要表现指定node的环境变量,否则执行编译命令会抛异常
- stage('Build NodeJS Vue') {
- steps {
- echo "build nodejs code"
- nodejs('node') {
- sh 'export PATH="/usr/local/nodejs/bin:$PATH"'
- sh 'node -v'
- sh 'npm -v'
- sh 'pnpm -v'
- sh 'pnpm install'
- sh 'pnpm run prod'
- }
- echo "build nodejs success"
- }
- }
复制代码 step4 删除汗青容器和镜像
如何没有在jenkins服务器运行容器可以忽略Delete Old Docker Container步骤
- stage('Delete Old Docker Container') {
- steps {
- echo "delete docker container"
- sh '''if [[ "$(docker inspect ${projectname} 2> /dev/null | grep ${projectname})" != "" ]];
- then
- echo ${projectname} "容器存在,停止并删除"
- echo "docker stop" ${projectname}
- docker stop ${projectname}
- echo "docker rm" ${projectname}
- docker rm ${projectname}
- else
- echo ${projectname} "容器不存在"
- fi'''
- }
- }
-
- stage('Delete Old Docker Image') {
- steps {
- echo "delete docker image"
- sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]];
- then
- echo ${projectname} \'镜像存在,删除镜像\'
- docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
- else
- echo ${projectname} \'镜像不存在,创建镜像\'
- fi'''
- }
-
- }
复制代码 step5 构建镜像
- stage('Build Docker Image') {
- steps {
- echo "start docker build ${projectname} code"
- sh 'docker build -t ${projectname} .'
- echo "save docker images tar"
- sh 'docker save -o ${projectname}.tar ${projectname}'
- }
-
- }
- stage('Delete New Docker Image') {
- steps {
- echo "delete docker image"
- sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]];
- then
- echo ${projectname} \'镜像存在,删除镜像\'
- docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
- else
- echo ${projectname} \'镜像不存在,创建镜像\'
- fi'''
- }
- }
复制代码 step6 上传镜像包’
这里的configName: ‘103’, 就是微服务CI/CD实践(四)Jenkins部署及环境配置### 2.2.4 全局系统配置 SSH Server配置
该流水线步骤会通过ssh将 镜像tar包上传到SSH Server配置的Remote Directory目次下
- stage('Upload img tar') {
- steps {
- sshPublisher(
- publishers: [
- sshPublisherDesc(
- configName: '103',
- transfers: [
- sshTransfer(
- cleanRemote: false,
- excludes: '',
- makeEmptyDirs: false,
- noDefaultExcludes: false,
- patternSeparator: '[, ]+',
- remoteDirectory: '',
- remoteDirectorySDF: false,
- removePrefix: '',
- sourceFiles: '${projectname}.tar'
- )
- ],
- usePromotionTimestamp: false,
- useWorkspaceInPromotion: false,
- verbose: false
- )
- ]
- )
- }
- }
复制代码 step7 执行安装部署脚本
这里的configName: ‘103’, 就是微服务CI/CD实践(四)Jenkins部署及环境配置### 2.2.4 全局系统配置 SSH Server配置
该步骤通过ssh远程执行sh安装部署脚本
- stage('Execute Command sh') {
- steps {
- sshPublisher(
- publishers: [
- sshPublisherDesc(
- configName: '103',
- transfers: [
- sshTransfer(
- execCommand: '/usr/docker-sh/publish_hka-admin-wocwin.sh hka-admin-wocwin hka-admin-wocwin latest 80 80',
- execTimeout: 300000
- )
- ],
- usePromotionTimestamp: false,
- useWorkspaceInPromotion: false,
- verbose: false
- )
- ]
- )
- }
- }
复制代码 以下为完整流水线界说
- pipeline { agent any environment { REPOSITORY="http://192.168.1.101:8929/hka/hka-admin-wocwin.git" projectdir="hka-web-01" projectname="hka-admin-wocwin" } stages {
- stage('获取代码') {
- steps {
- echo "start fetch code from git:${REPOSITORY} ${branch}"
- deleteDir()
- checkout([
- $class: 'GitSCM',
- branches: [[name: '${branch}']],
- doGenerateSubmoduleConfigurations: false,
- extensions: [],
- userRemoteConfigs: [[
- credentialsId: '2',
- url: 'http://192.168.1.101:8929/hka/hka-admin-wocwin.git'
- ]]
- ])
- }
- }
- stage('Build NodeJS Vue') {
- steps {
- echo "build nodejs code"
- nodejs('node') {
- sh 'export PATH="/usr/local/nodejs/bin:$PATH"'
- sh 'node -v'
- sh 'npm -v'
- sh 'pnpm -v'
- sh 'pnpm install'
- sh 'pnpm run prod'
- }
- echo "build nodejs success"
- }
- }
- stage('Delete Old Docker Container') {
- steps {
- echo "delete docker container"
- sh '''if [[ "$(docker inspect ${projectname} 2> /dev/null | grep ${projectname})" != "" ]];
- then
- echo ${projectname} "容器存在,停止并删除"
- echo "docker stop" ${projectname}
- docker stop ${projectname}
- echo "docker rm" ${projectname}
- docker rm ${projectname}
- else
- echo ${projectname} "容器不存在"
- fi'''
- }
- }
-
- stage('Delete Old Docker Image') {
- steps {
- echo "delete docker image"
- sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]];
- then
- echo ${projectname} \'镜像存在,删除镜像\'
- docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
- else
- echo ${projectname} \'镜像不存在,创建镜像\'
- fi'''
- }
-
- }
- stage('Build Docker Image') { steps { echo "start docker build ${projectname} code" sh 'docker build -t ${projectname} .' echo "save docker images tar" sh 'docker save -o ${projectname}.tar ${projectname}' } } stage('Delete New Docker Image') { steps { echo "delete docker image" sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; then echo ${projectname} \'镜像存在,删除镜像\' docker rmi $(docker images -q ${projectname} 2> /dev/null) --force else echo ${projectname} \'镜像不存在,创建镜像\' fi''' } } stage('Upload img tar') { steps { sshPublisher( publishers: [ sshPublisherDesc( configName: '103', transfers: [ sshTransfer( cleanRemote: false, excludes: '', makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'hka-admin-wocwin.tar' ) ], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false ) ] ) } } stage('Execute Command sh') { steps { sshPublisher( publishers: [ sshPublisherDesc( configName: '103', transfers: [ sshTransfer( execCommand: '/usr/docker-sh/publish_hka-admin-wocwin.sh hka-admin-wocwin hka-admin-wocwin latest 80 80 4413 4413', execTimeout: 300000 ) ], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false ) ] ) } } stage('Publish Results') { steps { echo "End Publish ${projectname}" } } }}
复制代码 2.4 构建部署项目
回到项目页面,点击参数化构建,选择用于构建的分支点击Build执行构建任务。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|