Gitlab支持通过配置CI/CD实现自动化部署我们的代码项目,主要核心就是配置gitlab-ci.yml文件以及在目标服务器上配置gitlab-runner(❣️为什么是在目标服务器?因为配置文件中的脚本就是在runner所在服务器执行的,所以把runner部署在目标服务器,就可以使用cp命令,是本地拷贝,不跨服务器,也省了配置SSH免密登录等步骤)
一,项目根目录中新增gitlab-ci.yml(详细解释可查阅这里)
示例1(复杂版)
default:
before_script:
- echo "start deploy"
- echo $CI_COMMIT_REF_NAME
# 阶段
stages:
- install
- buildDev
- buildTest
- buildProd
- deployDev
- deployTest
- deployProd
# 第一种共享方式:全局缓存(在 GitLab CI/CD 中,每个阶段(job)默认是独立的环境,不同阶段之间的文件系统是不共享的,除非您明确地使用 Artifacts 或 Cache 来传递所需的文件)
cache:
paths:
- node_modules/
# 安装依赖
install:
stage: install
# 此处的tags必须填入之前注册时自定的tag
tags:
- install
# 规定仅在指定分支提交时才触发此阶段
only:
- branches@dev
- branches@test
- branches@prod
# 规定仅在package.json提交时才触发此阶段
except:
changes:
- package.json
# 执行脚本
script:
- echo "start install"
- rm -rf ./node_modules
- npm install
# 第二种共享方式:单次文件传递
#artifacts:
# paths:
# - node_modules/
# expire_in: 1 hour # 根据需要设置过期时间
# 打包dev分支
buildDev:
stage: buildDev
tags:
- buildDev
# 规定仅在dev分支提交时才触发此阶段
only:
- dev
script:
- echo "start dev build"
- rm -rf ./dist
- npm run build:dev
# 将此阶段产物传递至下一阶段
artifacts:
paths:
- dist/
# 打包test分支
buildTest:
stage: buildTest
tags:
- buildTest
# 规定仅在test分支提交时才触发此阶段
only:
- test
script:
- echo "start test build"
- rm -rf ./dist
- npm run build:test
# 将此阶段产物传递至下一阶段
artifacts:
paths:
- dist/
# 打包prod分支
buildProd:
stage: buildProd
tags:
- buildProd
# 规定仅在prod分支提交时才触发此阶段
only:
- prod
script:
- echo "start prod build"
- rm -rf ./dist
- npm run build:prod
# 将此阶段产物传递至下一阶段
artifacts:
paths:
- dist/
# 部署dev项目
deployDev:
stage: deployDev
tags:
- deployDev
only:
# 规定仅在dev分支提交时才触发此阶段
- dev
script:
- echo "start deploy Dev"
# 删除远端服务器的dist目录下的所有文件
- ssh -p 33953 root@17.215.219.70 'rm -rf /home/web/appointment_h5/dist'
# 把当前dist目录下的所有文件传输到远端服务器的dist目录
- scp -r -P 33953 ./dist root@17.215.219.70:/home/web/appointment_h5
- echo "deploy Dev successfully"
# 部署test项目
deployTest:
stage: deployTest
tags:
- deployTest
only:
# 规定仅在test分支提交时才触发此阶段
- test
script:
- echo "start deploy Test"
# 删除远端服务器的dist目录下的所有文件
- ssh -p 33953 root@17.215.219.70 'rm -rf /home/web/appointment_h5/dist'
# 把当前dist目录下的所有文件传输到远端服务器的dist目录
- scp -r -P 33953 ./dist root@17.215.219.70:/home/web/appointment_h5
- echo "deploy Test successfully"
# 部署prod项目
deployProd:
stage: deployProd
tags:
- deployProd
only:
# 规定仅在prod分支提交时才触发此阶段
- prod
script:
- echo "start deploy Prod"
- pwd
# 删除远端服务器的dist目录下的所有文件
- ssh -p 33953 root@17.215.219.70 'rm -rf /home/web/appointment_h5/dist'
# 把当前dist目录下的所有文件传输到远端服务器的dist目录
- scp -r -P 33953 ./dist root@17.215.219.70:/home/web/appointment_h5
- echo "deploy Prod successfully"
示例2(简单版)
build:
stage: build
script:
- echo "start build"
test:
stage: test
script:
- echo "start test"
deploy:
stage: deploy
script:
- echo "start deploy"
二,在gitlab上查看gitlab-runner配置方法
打开安装教程,菜单路径:Admin / CI/CD / Runners,Url: https://你的gitlab域名/admin/runners
以Linux操作系统为例,安装脚本如下
# Download the binary for your system
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# Give it permission to execute
sudo chmod +x /usr/local/bin/gitlab-runner
# Create a GitLab Runner user
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
# Install and run as a service(备注:执行本行命令时报错 gitlab-runner: command not found,需要加上路径)
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
#Command to register runner
#备注:这一步需要你输入项目描述、tags、以及部署的执行方式,tags就是.gitlab-ci.yml中指定的,需要一一对应,也可以定义一个通用的,复杂版中示例的tags是buildDev, buildProd, buildTest, deployDev, deployProd, deployTest, install,执行方式是shell
sudo gitlab-runner register --url https://gitlab.yourdomain.com/ --registration-token pP4NhXS9A7HQC1Fxi9Yk
三,注册完成后, 就会在Runners列表中看到
四,推送代码,触发runner,实现自动构建和部署
向gitlab-ci.yml指定的分支(或不区分分支,对应上述gitlab-ci.yml简单版配置)推送代码,gitlab就会监听仓库指定分支的变化,从而触发runner,执行gitlab-ci.yml上的script脚本,这样就会把项目自动部署到服务器上
五,查看CI/CD过程日志
在项目Build中找到Pipelines,可以看到全部过程,点击Passed可以看到详细流程(简单版)
六,配置gitlab服务器免密登录应用服务器,方便脚本拷贝文件(该章节适用于runner为独立服务器,不和应用服务器在一起的情况)
#在GitLab服务器上执行以下命令,会在/root/.shh/目录下生成两个文件,它们就是私钥 (id_rsa) 与公钥 (id_rsa.pub)文件
ssh-keygen -t rsa
#在GitLab服务器上执行以下命令将公钥拷贝到应用服务器上
scp /root/.ssh/id_rsa.pub root@172.24.142.64:/root/.ssh/id_rsa_gitlab.pub
#在应用服务器上执行以下命令,将GitLab服务器的公钥内容写入authorized_keys文件
cat /root/.ssh/id_rsa_gitlab.pub >> /root/.ssh/authorized_keys
#在GitLab服务器上执行以下命令,可以发现不用输入用户名密码就可以登录到应用服务器了
ssh 172.24.142.64
七,调整gitlab-runner权限,以完成对目标服务器的操作(该章节适用于runner安装在目标服务器,属于本地操作)
默认安装的gitlab-runner,用户是gitlab-runner,组是gitlab-runner,而常规情况下应用目录都是在www组www用户下(Nginx),所以就会出现拷贝文件失败的情况,提示没有权限,如下
Running with gitlab-runner 17.5.3 (12030cf4)
on wonderbyte_cicd t1_tCTjo, system ID: s_9ec25d1764c9
Preparing the "shell" executor
00:00
Using Shell (bash) executor...
Preparing environment
00:00
Running on iZp0wf6tdgetcihaqjpu4nZ...
Getting source from Git repository
00:00
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /home/gitlab-runner/builds/t1_tCTjo/0/root/test-cicd/.git/
Checking out 91c3dbdd as detached HEAD (ref is main)...
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:01
$ echo "start test deploy"
start test deploy
$ cp -r ./* /home/gitlab-test
cp: cannot create regular file ‘/home/gitlab-test/README.md’: Permission denied
Cleaning up project directory and file based variables
00:00
ERROR: Job failed: exit status 1
方案一:更改目录所有者
#将 /home/gitlab-test 目录的所有者更改为 gitlab-runner 用户:
sudo chown -R gitlab-runner:gitlab-runner /home/gitlab-test
方案二:更改目录权限
#如果不想更改所有者,可以调整目录权限以允许 gitlab-runner 用户写入:
sudo chmod -R 755 /home/gitlab-test
#或者,为了更宽松的权限:
sudo chmod -R 775 /home/gitlab-test
方案三:添加 gitlab-runner 用户到目标目录的组
#假设 /home/gitlab-test 目录属于某个特定组(例如 www-data),您可以将 gitlab-runner 用户添加到该组:
sudo usermod -aG www-data gitlab-runner
最终我选择了方案三,变更下runner的用户组,这样就可以正常执行cp命令了
Running with gitlab-runner 17.5.3 (12030cf4)
on wonderbyte_cicd t1_tCTjo, system ID: s_9ec25d1764c9
Preparing the "shell" executor
00:00
Using Shell (bash) executor...
Preparing environment
00:00
Running on iZp0wf6tdgetcihaqjpu4nZ...
Getting source from Git repository
00:01
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /home/gitlab-runner/builds/t1_tCTjo/0/root/test-cicd/.git/
Checking out 4e3030bd as detached HEAD (ref is main)...
Skipping Git submodules setup
Executing "step_script" stage of the job script
00:00
$ echo "start test deploy"
start test deploy
$ cp -rf ./* /home/gitlab-test
$ echo "deploy test successfully"
deploy test successfully
Cleaning up project directory and file based variables
00:00
Job succeeded
八,使用 systemd 管理 Gunicorn,实现完全自动化(基于Python,Flask, Flask-SocketIO的项目实践)
systemd 是现代Linux系统中的初始化系统和服务管理器。通过创建一个systemd服务单元文件,可以管理Gunicorn进程的启动、停止和重启。
a.在目标服务器上创建一个systemd服务文件,例如 /etc/systemd/system/my_flask_app.service:
[Unit]
Description=Gunicorn instance to serve my_flask_app
After=network.target
[Service]
User=your_username
Group=www-data
WorkingDirectory=/path/to/your_project
Environment="PATH=/path/to/your_project/venv/bin"
ExecStart=/path/to/your_project/venv/bin/gunicorn -b 0.0.0.0:6060 --worker-class eventlet -w 1 app:app
[Install]
WantedBy=multi-user.target
#注意:
#User 和 Group:设置为运行应用程序的用户和组,确保有权限访问项目目录和文件。
#WorkingDirectory:你的项目目录路径。
#Environment:指定Python虚拟环境的路径。
#ExecStart:Gunicorn启动命令,指向正确的应用入口(根据你的wsgi.py,可能是 wsgi:app 或 wsgi:socketio_server)。
b.重新加载 systemd 配置并启动服务
sudo systemctl daemon-reload
sudo systemctl start my_flask_app
sudo systemctl enable my_flask_app # 开机自启
c.验证服务状态
sudo systemctl status my_flask_app
d.修改gitlab-runner用户的sudo免密码交互权限
#使用visudo命令编辑sudoers文件,以避免语法错误:
sudo visudo
#在sudoers文件末尾,添加以下行:
gitlab-runner ALL=NOPASSWD: /bin/systemctl restart openai-test.service, /bin/chown www -R /home/wwwroot/openai-test
#gitlab-runner:替换为实际运行GitLab Runner的用户。
#ALL:表示从任何主机都可以执行。
#NOPASSWD::表示不需要密码。
#/bin/systemctl restart openai-test.service:具体允许执行的命令,多个命令用逗号隔开。
e.修改gitlab-ci.yml脚本命令
# 示例一,未考虑环境搭建及依赖安装,只复制代码及重启服务,已验证可执行
# 阶段
stages:
- deployDev
# 部署dev项目
deployDev:
stage: deployDev
tags:
# 规定使用哪个Runner
- centos_runner
only:
# 规定仅在dev分支提交时才触发此阶段
- dev
script:
- echo "start deploy Dev"
# 把当前目录下的所有文件传输到本地项目目录(Runner在本地)
- cp -rf ./* /home/wwwroot/openai-test
- sudo systemctl restart openai-test.service
- sudo chown www -R /home/wwwroot/openai-test
- echo "deploy Dev successfully"
# 示例二,首先判断环境情况,再复制代码和重启服务,未验证
# 定义阶段
stages:
- deployDev
# 部署dev项目
deployDev:
stage: deployDev
tags:
- centos_runner # 确保Runner标签正确
only:
- dev # 仅在dev分支触发
script:
- echo "Start deploying Dev"
# 1. 复制代码到目标目录
- cp -rf ./* /home/wwwroot/openai-test/
# 2. 检查是否为首次部署
- |
if [ ! -f /home/wwwroot/openai-test/.deploy_done ]; then
echo "First deployment: installing dependencies and starting service."
cd /home/wwwroot/openai-test
# 创建虚拟环境(如果不存在)
if [ ! -d "venv" ]; then
python3 -m venv venv
fi
# 激活虚拟环境
source venv/bin/activate
# 升级pip
pip install --upgrade pip
# 安装依赖
pip install -r requirements.txt
# 重启systemd服务
sudo systemctl restart openai-test.service
# 创建标志文件,标记首次部署完成
sudo touch .deploy_done
else
echo "Subsequent deployment: restarting service."
# 重启systemd服务
sudo systemctl restart openai-test.service
fi
- echo "Deploy Dev successfully"
通过以上配置,就完成了对项目的自动部署及自动重启,整个过程无需人工干预,只需提交代码,后面就全自动了
九,题外:本地项目开发早于仓库的创建,仓库创建后,并没有将代码提交到主干main(不太会,也没查),就直接创建了个分支dev,将代码提交到分支dev上,然后又从分支dev合并到主干main,第一次主干是空的,合并很顺利,但后续再更新合并时,总会提示存在冲突,但想来主干的代码完全是从分支合并过来的,不应该有冲突,遂查之,是由于主干和分支的基线不一致导致的,通过以下命令强制将main指向dev
a. 强制将 main 分支指向 dev 分支
# 切换到 dev 分支并确保是最新的
git checkout dev
git pull origin dev
# 切换到 main 分支
git checkout main
# 将 main 分支重置为与 dev 分支一致
git reset --hard dev
# 强制推送到远程 main 分支
git push origin main --force
这样,main 分支将完全与 dev 分支同步,后续 dev 分支的更新应该可以顺利合并到 main,而不会产生冲突。
b.确保未来的合并策略
1). 从 main 分支创建 dev 分支:
确保 dev 分支是从 main 分支正确派生的,这样可以保持两者的提交历史一致。
git checkout main
git pull origin main
git checkout -b dev
2). 在 dev 分支上进行开发:
所有的开发工作都在 dev 分支上进行,避免在 main 分支上直接进行修改。
3). 定期同步 main 到 dev(如果有必要):
虽然在目前的情况下 main 仅通过 dev 更新,但如果有其他原因需要同步,可以定期将 main 分支的更改合并回 dev。
git checkout dev
git pull origin main
4). 使用合并请求(Merge Request)进行合并:
在 GitLab 上,通过创建合并请求将 dev 分支的更改合并到 main,这样可以在合并前检查潜在的冲突,并在合并过程中更好地管理代码。