GitLab CI/CD 是一个强大的工具,可以实现项目的自动化部署流程,从代码提交到部署只需几个步骤。本文将带你配置 GitLab CI/CD 完成一个前端项目的自动化部署。
前言
为什么使用cicd+docker?
目前我们公司开发环境使用的shell脚本部署,一是要登录服务器,二是要去手动执行脚本,要是部分同事不懂运行脚本的话还得教他,费时费力;但是搭建好了cicd之后只需要提交合并代码就会完成自动化部署不需要人工介入、使用docker是因为可以环境互不干扰(例如node14,node15,node16等不同版本都可以在一台服务器运行)
前置准备
GitLab 仓库 :确保代码已经推送到 GitLab 仓库。
部署服务器 :一台可以通过 SSH 访问的服务器,用于部署你的前端项目。
工具准备 :GitLab Runner、docker
项目文件:.gitlab-ci.yml、Makefile、dockerfile
持续集成 (CI)
持续集成是一种开发实践,开发人员将代码频繁地合并到共享的代码仓库中,通常每天多次。每次代码合并后,都会触发自动化的构建和测试流程。
- 频繁集成:开发人员定期将新代码合并到主分支。
- 自动化测试:每次合并都会运行测试,确保新代码不会破坏已有功能。
- 快速反馈:测试失败或构建失败时立即通知开发者。
- 发现问题更快,因为每次小的更改都会被立即验证。
- 减少了"大规模集成"带来的风险和时间消耗。
持续交付 (CD - Continuous Delivery)
持续交付是在持续集成的基础上,进一步自动化了将软件交付到生产环境之前的所有流程。代码在通过构建和测试后,自动部署到一个可运行的环境(通常是预发布环境),但部署到生产仍需要手动触发。
- 自动化部署:代码经过验证后可以自动部署到预发布环境。
- 人为审核:生产环境的部署通常需要手动批准。
- 可随时交付:开发团队确保软件在任何时间点都可以部署到生产。
- 缩短了发布周期。
- 提高了软件交付的可靠性和稳定性。
- 可轻松在不同环境(如开发、测试、预生产)中验证软件。
持续部署 (CD - Continuous Deployment)
持续部署是持续交付的进一步延伸,完全自动化了整个交付过程。通过所有测试的代码会自动部署到生产环境,无需人工干预。
- 全自动化:不需要人为批准,代码直接进入生产环境。
- 高测试要求:需要非常可靠的自动化测试体系。
- 快速交付:代码从开发到上线的周期极短。
- 开发团队可以快速将新功能交付到用户手中。
- 减少人为操作导致的错误。
- 提高了产品的敏捷性。
GitLab Runner 是什么?
GitLab Runner 是一个开源的应用程序,用于执行 GitLab CI/CD 中定义的任务(也称为作业)。它是 GitLab CI/CD 的核心组件之一,用于从 GitLab 获取作业、执行作业,并将结果反馈给 GitLab。
GitLab Runner 的功能
执行 CI/CD 流水线作业 :从 .gitlab-ci.yml 文件中获取任务配置并执行。
支持多种执行器 :支持不同的环境运行作业,如 Docker、Shell、Kubernetes 等。
跨平台支持 :可以安装在多种操作系统上,包括 Linux、Windows 和 macOS。
分布式运行 :可以在多个 Runner 上并行执行流水线任务。
负载均衡:当有多个 Runner 注册到 GitLab 项目时,任务会自动分配。
GitLab Runner 安装
GitLab Runner 教程
部署
.gitlab-ci.yml教程
要使用 GitLab CI/CD,您需要:
- 托管在 Git 仓库中的应用程序代码。
- 仓库根目录中名为 .gitlab-ci.yml 的文件,其中包含 CI/CD 配置。
这是我自己项目中的.gitlab-ci.yml
yml
stages:
- build # 编译
# - deploy-dev # 开发环境
- deploy-test # 测试环境
- deploy-online # 上线 - 推送镜像
build-job:
stage: build
tags:
- docker_test
variables:
GIT_CLEAN_FLAGS: -ffdx -e node_modules/
script:
- echo "Compiling the code..."
# - make build
- echo "Compile complete."
only:
- test
# - dev
build-online-job:
stage: build
tags:
- docker_online
variables:
GIT_CLEAN_FLAGS: -ffdx -e node_modules/
script:
- echo "Compiling the code..."
# - make build ENV=prod
- echo "Compile complete."
only:
- main
# deploy-dev-job:
# stage: deploy-dev
# # environment: production
# tags:
# - docker_dev
# variables:
# GIT_CLEAN_FLAGS: -ffdx -e dist/ -e node_modules/
# script:
# - echo "Deploying application..."
# - make docker-deploy ENV=dev
# - echo "Application successfully deployed."
# only:
# - dev
deploy-test-job:
stage: deploy-test
# environment: production
tags:
- docker_test
variables:
GIT_CLEAN_FLAGS: -ffdx -e dist/ -e node_modules/
script:
- echo "Deploying application..."
- make docker-deploy ENV=test
- echo "Application successfully deployed."
only:
- test
deploy-online-job:
stage: deploy-online
tags:
- docker_online
variables:
GIT_CLEAN_FLAGS: -ffdx -e dist/ -e node_modules/
script:
- echo "Compiling the code..."
- make deploy-online ENV=prod
- echo "Compile complete."
only:
- main
Makefile 是什么?
Makefile 是一种自动化构建工具的配置文件,用于定义如何编译和链接程序以及执行其他任务。它主要用于管理项目中的构建过程,例如将源代码编译为二进制文件。make 工具通过读取 Makefile 来执行任务。
尽管最初是为 C/C++ 项目设计的,Makefile 也可以用于任意任务的自动化,如压缩文件、运行测试、生成文档等。
Makefile教程
这是我自己项目中的Makefile
sh
REGISTRY?=www.github.com/xxx
APP=demo
BACKEND_PORT:= 9300
PORT:= 8300
# 获取最近的 Git 标签作为版本号
RAW_VERSION := $(shell git describe --tags --always 2>/dev/null || echo dev)
VERSION := $(shell echo $(RAW_VERSION) | awk -F '-' '{print $$1}')
# 读取 VERSION 文件中的版本号
# version := $(shell cat VERSION 2>/dev/null || echo last)
# 打印版本号
print_version:
@echo "The version is: $(VERSION)"
HOST := http://xxx.xxx.com
ifeq ($(ENV), dev)
HOST = xxx.xxx.com
endif
ifeq ($(ENV), test)
HOST = xxx.xxx.com
endif
ifeq ($(ENV), prod)
HOST = xxx.xxx.com
endif
IMAGES := ${REGISTRY}/${APP}:${VERSION}
.PHONY: build
## 构建应用
build: clean
@echo "Building..."
npm i; npm run build
# 验证环境变量
check-environment:
ifndef ENV
$(error ENV not set, allowed values - `staging` or `production`)
endif
.PHONY: docker-build
## 构建docker镜像
docker-build:
docker build -f Dockerfile2 -t ${IMAGES} .
.PHONY: docker-deploy
## 部署docker容器 - 测试环境
docker-deploy: docker-build
docker stop ${APP} || true; docker rm ${APP} || true; docker run -p ${PORT}:80 -d -e BACKENDURL="${HOST}:${BACKEND_PORT}/" --name ${APP} ${IMAGES}
.PHONY: deploy-online
## 部署docker容器 - 生产环境
deploy-online: docker-build
docker stop ${APP} || true; docker rm ${APP} || true; docker run -p ${PORT}:80 -d -e BACKENDURL="${HOST}:${BACKEND_PORT}/" --name ${APP} ${IMAGES}
.PHONY: docker-push
## 推送docker镜像 - 测试环境
docker-push: check-environment
docker build -f Dockerfile -t ${IMAGES} .
## docker push ${IMAGES}
.PHONY: help
## 帮助命令
help:
@echo "Usage: \n"
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
Dockerfile
Dockerfile
# 编译打包
FROM node:18-alpine as builder
WORKDIR /app
COPY package.json .
ENV NODE_OPTIONS=--openssl-legacy-provider
# RUN npm install
RUN npm config set registry https://registry.npmmirror.com/ && npm install
COPY . .
RUN npm run build
# 运行应用
FROM nginx:latest
COPY --from=builder /app/dist /usr/share/nginx/html
COPY container/nginx.conf.template /etc/nginx/nginx.conf.template
COPY container/init.sh /init.sh
RUN chmod 777 init.sh
EXPOSE 80
CMD ["/bin/bash", "/init.sh"]
dockerfile中包含的文件
container/nginx.conf.template
php
user root;
events {
worker_connections 4096; ## Default: 1024
}
http {
# 设置超时时间
proxy_connect_timeout 60s;
proxy_send_timeout 7200s;
proxy_read_timeout 7200s;
client_max_body_size 200M;
include /etc/nginx/mime.types;
sendfile on;
server {
listen 80;
listen [::]:80;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With,access-token,Access-Token,Refresh-Token,Accept,Content-Type,User-Agent;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# sse推送的特定接口的配置
location /api/admin/channelAccount/getMetrics {
rewrite ^/api/(.*)$ /$1 break; # 去除 /api 前缀
proxy_pass $BACKENDURL; #后端实际服务器地址
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
}
location ^~/api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_pass $BACKENDURL; #后端实际服务器地址
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With,access-token,Access-Token,Refresh-Token,Accept,Content-Type,User-Agent;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
}
}
}
container/init.sh
sh
#!/bin/bash
BACKENDURL=$BACKENDURL
export "BACKENDURL"=$BACKENDURL
envsubst '$BACKENDURL' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && nginx -g 'daemon off;'
gitlab创建runner
新建runner
服务器执行命令、执行器选择shell
sh
gitlab-runner register --url https://gitlab.xxx.com --token glrt-t3_zBQiLoAqysRDEgfiexUZ --executor "shell"
完成
提交代码到test分支后就可以看到构建记录了