引言
在大中型的公司中,都有比较完善的CICD流程,开发者往往只需要简单的配置即可实现自己的集成&部署需求。但是使用这些完善的工具,并不能让我们对CICD有深入的理解。如果想要实现对CICD深入的理解,最好的方式还是自己从0到1来实现,接下来介绍如何搭建一套适用于个人开发者的CICD流水线。
CICD简介
CI
持续集成(Continuous Integration,简称CI)是一种软件开发实践,它通过自动化的构建、测试和部署过程,保证在开发过程中每次代码变更都能够被快速地集成到主干代码中。
持续集成通过将代码集成、构建和测试自动化,可以快速地发现问题并及时修复,提高了代码质量和开发效率,减少了代码合并和发布的风险。它要求开发人员经常提交代码,并尽早地将代码集成到主干分支中,以保证代码的稳定性和可靠性。
CI强调的是开发者将自己开发的代码合并到主干这一过程,使用CI工具可以对开发者提交的代码做漏洞扫描、执行单元测试、构建用于测试的预览页面/包。
CD
持续部署(Continuous Deployment,简称CD)是一种软件开发实践,它在持续集成的基础上,将代码自动化地部署到生产环境中,实现了代码的快速交付和发布。
在传统的软件开发流程中,开发完成后需要进行一系列的手动测试、人工部署等操作才能将代码发布到生产环境中,这个过程通常需要耗费很长时间,还存在人为的错误和风险。而使用持续部署可以自动化地完成这些操作,实现代码的快速、可靠的发布,提高了软件开发的效率和质量。
持续部署强调的是将已经开发好的软件部署到生产环境(production)的流程。主要目标是实现测试、验证、部署的自动化。
容器化(Dockerize)前端应用
Dockerize是指将应用程序或服务打包成Docker镜像的过程。Dockerize的好处是可以将应用程序与其依赖项打包到一个容器中,使得应用程序的部署和运行变得更加简单和可靠。此外,Dockerize还可以提高应用程序的可移植性和可扩展性,使得应用程序可以在不同的环境中运行,而无需担心环境差异性和依赖项的问题。
1. 使用vite-cli
初始化react应用
yarn create vite
生成的React应用程序目录结构如下:
2. 创建nginx.conf
文件,配置nginx服务
server {
listen 80;
#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
这份nginx配置定义了一个HTTP服务器,监听80端口,具体配置如下:
- listen 80:指定服务器监听的端口为80
- server_name:指定服务器的域名
- access_log和error_log:分别指定访问日志和错误日志的路径和格式
- location /:指定根路径的访问规则,将请求的根路径映射到/usr/share/nginx/html目录下,并指定默认的首页文件为index.html或index.htm。
- error_page和location = /50x.html:指定当服务器返回500、502、503、504错误时,将请求重定向到50x.html页面,并将50x.html页面的路径设置为html目录下。
3. 创建Dockerfile
文件,配置构建流程
Dockerfile包含了一系列指令,这些指令告诉Docker如何构建容器镜像 Dockerfile中的指令可以用来指定基础镜像、安装软件包、设置环境变量、挂载宿主机文件……
# build stage
FROM node:16 as build-stage
WORKDIR /app
COPY . /app
RUN npm install && npm run build
# production stage
FROM nginx:latest as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY --from=build-stage /app/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
build阶段:
- 将Node.js 16镜像设为
build
阶段的基础镜像,将工作目录设置为/app
- 将宿主机根目录的所有文件复制到镜像的工作目录,这里如果想避免复制
node_modules
目录,可以通过在根目录下创建.dockerignore
文件实现,使用方法与.gitignore
类似 - 安装依赖,并执行构建,完成后会将构建产物保存在
/dist
目录
production阶段:
- 将
build-stage
阶段中构建好的应用程序复制到Nginx的默认目录:/usr/share/nginx/html
- 将项目根目录中的
nginx.conf
文件复制到镜像的/etc/nginx/conf.d
目录下,覆盖默认配置 - 指定容器监听的端口为80,并以守护进程的方式启动Nginx服务
采用多阶段构建过程可以有效的减小镜像的体积,同时保证应用程序的可靠性和兼容性。
4. 执行构建命令
应用的构建使用docker build
命令,当然该命令使用的前提是机器上已经有Docker的环境,具体的安装流程可以搜索相关文章(很多)。
docker build -t react-cicd-demo .
命令中的
-t
用于指定构建的镜像名称,如果不指定版本则默认构建出的版本为latest
版本。可以在镜像名称后面加上版本,如-t react-cicd-demo:1.0.0
。.
用于指定构建镜像的上下文路径,即Dockerfile所在的目录。
构建完成后,可以通过docker images
查看构建好的镜像是否在本地镜像仓库。
5. 运行应用程序镜像
docker run -d -p 8323:80 --name react-cicd-app react-cicd-demo
此时访问:http://localhost:8323/
,可以看到React应用成功运行。
相关命令
docker logs react-cicd-app
可以查看容器运行的日志
docker stop react-cicd-app
可以将容器关闭
使用gitlab-runner实现CICD
部署Docker版gitlab
1. 拉取gitlab社区版
镜像
docker pull gitlab/gitlab-ce
2. 创建配置目录
sudo mkdir -p /gitlab/data /gitlab/logs /gitlab/config
gitlab社区版产生的数据和配置最好使用宿主机的磁盘存储,这样即便是镜像丢失也不会导致数据丢失,上面的三个目录分别用于保存数据、日志、配置。
3. 运行gitlab社区版
容器
sudo docker run -d \
--publish 8443:443 --publish 80:80 --publish 2222:22 \
--hostname 192.168.1.103 \
--name gitlab-app \
--privileged=true \
--restart unless-stopped \
--volume gitlab/config:/etc/gitlab \
--volume gitlab/logs:/var/log/gitlab \
--volume gitlab/data:/var/opt/gitlab \
-log-driver=none \
gitlab/gitlab-ce:latest
具体参数含义解释如下:
-d:
指定容器以守护进程的形式运行,即后台运行
--publish
端口映射,将容器的443、80、22端口映射到宿主机的8443、80、2222端口
--host
指定宿主机的ip,如果采用ip访问,则启动后访问地址为"http:// your ip address"
--name
指定镜像的名称
--privileged=true
以特权模式运行,即容器内的进程拥有主机的root权限
--restart unless-stopped
指定容器在退出时自动重启,除非容器被手动停止
--volume
指定挂载目录,将创建的目录挂载到容器的指定位置(宿主机目录:容器目录)
-log-driver=none:
禁用Docker的日志记录功能。
镜像启动后可以使用docker ps
命令查看容器的运行状态
由于gitlab的镜像比较大,要启动的服务比较多,因此启动比较耗时,大概需要3分钟左右,在启动过程中,镜像的状态是
starting
,启动成功后镜像的status是healthy
。
4. 验证是否正确启动
当容器的运行状态更新为healthy
以后,访问创建时指定的ip地址就能访问到gitlab的主界面,此时会要求输入初始root账户密码。
至此,容器版本的gitlab部署完成。
部署gitlab-runner
GitLab Runner是一款强大的CI/CD运行工具,可以执行各种类型的作业,例如构建、测试、部署和发布。它可以与不同的编程语言和框架集成,例如Java、Python、Ruby和Node.js。GitLab Runner还支持Docker和Kubernetes,可以在容器中运行作业。
1. 拉取镜像
docker pull gitlab/gitlab-runner:latest
2. 创建gitlab-runnner实例
docker run -d --name gitlab-runner \
--restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
参数解释:
-d:
指定容器以守护进程的形式运行,即后台运行
--name
指定镜像的名称
--restart always
: 表示容器退出后总是重启。
-v /var/run/docker.sock:/var/run/docker.sock
:将主机的docker.sock文件挂载到容器中,从而使容器可以与宿主机Docker引擎通信,这个配置很关键,因为只有增加了这个配置,gitlab-runner才能将构建产生的容器部署到宿主机,这里可以将gitlab-runner容器视为宿主机的GUI。
3. 注册gitlab-runner
首先使用exec命令进入容器内部 docker exec命令
docker exec -it gitlab-runner /bin/bash
执行gitlab-runner register
命令开始注册流程
- Enter the GitLab instance URL (for example, gitlab.com/):
从【设置】-【CICD】中获取 - Enter the registration token: 从【设置】-【CICD】中获取
- Enter a description for the runner:
builder - Enter tags for the runner (comma-separated):
builder - Enter optional maintenance note for the runner:
可以为空 - Enter an executor: ssh, virtualbox, docker-ssh+machine, instance, parallels, shell, docker-ssh, docker+machine, kubernetes, custom, docker: docker
Enter the default Docker image (for example, ruby:2.7):
docker
配置流程主要是配置gitlab-runner的基本信息,前两项是配置gitlab的地址和token,可以从项目设置中的CICD项中获取。接下来的三项是配置runner的基本信息,按需填写即可,最后指定执行器的类型,这里输入docker即可。 配置完成后,刷新gitlab CICD配置页面就能看到runner的信息
需要指出的是,在配置完成后,还需要在runner列表页面,点击配置按钮,勾选上执行无标签的任务,否则创建的任务会一直处于pending
状态。
配置gitlab-ci.yml
gitlab-ci.yml
是GitLab CI/CD的配置文件,它用于定义CI/CD流程中的任务和执行顺序。根据当前前端项目的需求可以将任务拆分成三个部分:
- 安装依赖
- 构建生产环境静态文件
- 构建nginx镜像并运行
具体配置内容如下:
image: node:14
stages:
- init
- build
- deploy
cache:
key: ${CI_BUILD_REF_NAME}
paths:
- node_modules/
- dist/
init:
stage: init
script:
- npm install
build:
stage: build
script:
- npm run build
deploy:
image: docker
stage: deploy
script:
- whoami
- docker ps
- docker build -t front-devops-demo-image .
- if [ $(docker ps -aq --filter name=front-devops-demo-container) ]; then docker rm -f front-devops-demo-container;fi
- docker run -d -p 9000:80 --name front-devops-demo-container front-devops-demo-image
这段配置文件首先配置了CICD流程中的三个阶段init
、build
、deploy
。
在init阶段使用npm install
命令安装了项目依赖,在build阶段执行了npm run build
执行项目构建,在deploy阶段执行了镜像构建和具体部署操作,具体内容如下:
首先使用whoami
命令可以查看执行命令的用户(此处应为root用户),然后使用docker ps
查看了正在运行的镜像,这两个命令主要用于CICD环境的调试,等流程跑通后可以移除。
接下来判断是否已经启动名称包含front-devops-demo-container
的容器,如果启动则将其移除。最后,使用docker run
命令启动了构建好的容器并将容器的80
端口映射到宿主机的9000
端口。
实现自动化构建部署流程
编辑完成gitlab-ci.yml
并推送到gitlab仓库,gitlab就会自动识别项目中的构建配置文件,并调用gitlab-runner执行构建流程。构建流程成功执行后,访问宿主机的9000
端口就能访问到部署的应用。
总结
本文基于GitLab CI/CD,实现了一套容器化的前端项目部署,在搭建完成这套方案后,只要前端有代码更新并推送,就会触发CICD构建,并自动部署到前端容器,可以称得上是所见即所得(虽然会有延迟),但是本文提供的方案还是有很多可以优化的点比如:
- 可以将构建好的镜像推送到镜像仓,这样可以对镜像做更好的备份
- 可以将容器部署到K8S容器平台,实现弹性伸缩
- 可以在构建阶段加上单元测试以及eslint检查
希望对CICD感兴趣的同学可以按照本文的流程实践一下,相信会有一些收获,也推荐大家在这个基础上做一些优化,让CICD流程更加完善~