前言

完全自动化的 CICD 确实好,代码提交后就自动构建自动发布新版本,实现不停机更新的情况下,还能随时回滚,这搁谁不喜欢啊~

但理想很丰满,现实往往很骨感,不是所有开发/生产环境都具备部署 CICD 的条件

先说结论,这些 CICD 服务都有一些问题,要么就是网络不通,要么就是太重太麻烦不具备部署条件(服务器都在内网,无法直连)

所以我在工作过程中,「创新」了一种 CICD 的平替方案,通过一个脚本,实现一键发布!

PS: 由于篇幅关系,无法在文章里贴出全部代码,有需要的同学可以在公众号后台回复「半自动 CICD 脚本」获取

关于 CICD

现在常见的 CICD 服务都具备一定门槛,咱们讨论一下:

  • Github Actions: 最适合开源项目使用,不用部署配置,完全免费 不过在生产环境往往因为网络问题用不了
  • GitLab CI/CD: 很重,需要部署和配置 GitLab 服务
  • Jenkins: 很重,需要部署和配置 Jenkins 服务
  • Azure DevOps 和 AWS CodePipeline: 这俩依赖它家的云服务,而且都是国外的,基本不用考虑的

在这些常用的之外,还有一些其他不入流的,这里也一并看看:

  • 国内的 Gitee 流水线: 这个类似 Github Actions,不过却是收费的,打个工而已,难道还得自费上班?直接 pass
  • CircleCI: 云原生 CI/CD 服务,提供与 GitHub 和 Bitbucket 的集成,国外使用应该很不错,但国内网络环境肯定是不允许的
  • Bitbucket Pipelines: 与 Bitbucket 仓库紧密集成,适合使用 Bitbucket 进行代码托管的团队,与 Github 类似的情况,不用考虑了

PS: 有时候不得不感叹,国内国外仿佛两个世界…

除了这些之外,我还找到一个轻量级的开源 CICD 项目: https://github.com/flowci/flow-core-x

这个看起来不错,感觉可以用在 HomeLab 或者 NAS 上,到时来尝试一下。

解决方案

我的解决方案是用「脚本 + docker」实现一键发布、不停机更新、随时回滚版本~

基本思路,我画了个简单的图,方便理解

graph LR
A([本地])-->B1(打 TAG)
A-->B2(docker build)
B2-- 推送镜像 -->C[(镜像仓库)]
A-- SSH连接 -->D{服务器}
C-- 拉取镜像 -->D
D-- 启动 -->E([线上服务])

这个方案只需要简单的配置,之后就可以一键发布了,所以我称之为「半自动 CICD」

原理是本地 git 仓库打版本 tag(如: v0.0.1),然后运行脚本会自动识别这个版本 tag,构建镜像之后打上同样的 tag,再推送远程镜像仓库,到了服务器上再拉下来启动,完事~(就是这么简单朴素)

如何使用

使用这个方案的前提是:

  • 使用 git 管理代码
  • 使用 docker 部署项目
  • 需要有一个私有的 docker 镜像仓库,可以自建,也可以使用阿里云这类私有镜像服务(免费)
  • 服务器能访问到 docker 镜像仓库(内网的话可以自建)

修改 compose 配置

在使用脚本之前,需要一点小小的配置,后面就可以解放生产力了~

还是以基于 DjangoStarter 框架 的项目为例

compose.yaml 配置文件

services:
# 省略无关内容
app:
image: ${APP_IMAGE_NAME}:${APP_IMAGE_TAG}
container_name: $APP_NAME-app

.env 环境变量

APP_PORT=9876
APP_NAME=meta-hub
APP_IMAGE_NAME=meta-hub
APP_IMAGE_TAG=v0.0.2

到时脚本运行时会自动修改 .env 里的版本 APP_IMAGE_TAG

打 tag

在本地开发完成之后

使用 git tag 功能给 commit 打版本 tag

例如:

git tag v0.1.1

运行脚本

这次的脚本我是用 Python 编写的,不过没有其他外部依赖,完全使用标准库实现,还算比较方便的

python scripts/build_docker.py

PS: 后续我会考虑使用 C# 或者 Go 重新写这个脚本,支持 AOT,作为一个工具添加到系统 PATH,使用起来更方便

脚本

接下来放一个简化版本的脚本

由于篇幅关系,无法在文章里贴出全部代码,有需要的同学可以在公众号后台回复「半自动 CICD 脚本」获取

这个脚本,总共一百多行,麻雀虽小五脏俱全,实现了完整的功能。

一开始我是用的 paramiko.SSHClient 来建立 SSH 连接的,不过后面觉得还是不要引入额外的复杂度比较好,最终简化成这样,直接使用系统自带的 SSH 命令。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Docker镜像构建、推送和远程部署脚本 功能:
1. 获取最新git tag作为版本号
2. 构建Docker镜像并推送到配置的镜像仓库
3. SSH连接到远程服务器进行自动部署 配置项(环境变量或默认值):
- REGISTRY_URL: 镜像仓库地址,如: registry.example.com
- REGISTRY_NAMESPACE: 镜像仓库命名空间
- IMAGE_NAME: 镜像名称
- REMOTE_HOST: 远程服务器配置,如: user@server-ip -p 2022
- REMOTE_PROJECT_PATH: 远程项目路径
""" import os
import sys
import subprocess
import threading
from typing import Optional, Tuple # 默认配置
DEFAULTS = {
'REGISTRY_URL': 'registry.example.com',
'REGISTRY_NAMESPACE': 'namespace',
'IMAGE_NAME': 'image-name',
'REMOTE_HOST': 'host-name', # 远程服务器地址或~/.ssh/config中的Host别名
'REMOTE_PROJECT_PATH': '/path/to/project',
} def get_config(key: str) -> str:
"""获取配置值,优先使用环境变量,否则使用默认值"""
return os.environ.get(key, DEFAULTS.get(key, '')) def _reader_thread(pipe, lines_list, stream_to_print_to):
"""在独立线程中读取管道输出"""
try:
for line in iter(pipe.readline, ''):
lines_list.append(line)
if stream_to_print_to:
# 实时打印
stream_to_print_to.write(line)
stream_to_print_to.flush()
finally:
pipe.close() def run_cmd(cmd: str, show_output: bool = True) -> Tuple[int, str, str]:
"""
执行命令并实时显示输出,同时捕获输出内容。
返回状态码、stdout和stderr。
"""
if show_output:
print(f"执行: {cmd}") process = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True
) stdout_lines = []
stderr_lines = [] stdout_thread = threading.Thread(
target=_reader_thread,
args=(process.stdout, stdout_lines, sys.stdout if show_output else None)
)
stderr_thread = threading.Thread(
target=_reader_thread,
args=(process.stderr, stderr_lines, sys.stderr if show_output else None)
) stdout_thread.start()
stderr_thread.start() stdout_thread.join()
stderr_thread.join() returncode = process.wait() stdout = ''.join(stdout_lines)
stderr = ''.join(stderr_lines) if returncode != 0:
print(f"\n错误: 命令执行失败 (返回码: {returncode})")
# 错误输出已经被实时打印,这里不再重复打印
sys.exit(1) return returncode, stdout, stderr def get_latest_tag() -> str:
"""获取最新git tag"""
_, tag, _ = run_cmd("git describe --tags --abbrev=0")
tag = tag.strip()
if not tag:
print("错误: 没有找到git tag")
sys.exit(1)
print(f"最新tag: {tag}")
return tag def deploy_to_remote(version: str) -> None:
"""部署到远程服务器"""
host = get_config('REMOTE_HOST')
remote_path = get_config('REMOTE_PROJECT_PATH') print(f"\n 通过SSH连接到 {host} 进行部署...") # 1. 更新远程 .env 文件
print(f"\n 更新远程.env文件...")
update_cmd = f'ssh {host} "sed -i \'s/^TAG=.*/TAG={version}/\' {remote_path}/.env"'
run_cmd(update_cmd) # 2. 重启远程容器
print(f"\n 重启远程容器...")
restart_cmd = f'ssh {host} "cd {remote_path} && docker compose up -d"'
run_cmd(restart_cmd) print("\n 远程部署完成!") def main():
print(" 开始Docker镜像构建、推送和部署流程\n") # 1. 获取最新tag
version = get_latest_tag() # 2. 构建镜像
print("\n 构建Docker镜像...")
run_cmd("docker compose build app") # 3. 打tag
registry = get_config('REGISTRY_URL')
namespace = get_config('REGISTRY_NAMESPACE')
image_name = get_config('IMAGE_NAME')
registry_image = f"{registry}/{namespace}/{image_name}:{version}" print(f"\n️ 给镜像打tag...")
run_cmd(f"docker tag {image_name} {registry_image}") # 4. 推送镜像
print(f"\n 推送镜像到仓库...")
run_cmd(f"docker push {registry_image}")
print(f"镜像已推送: {registry_image}") # 5. 远程部署
deploy_to_remote(version) print("\n 所有任务已完成!") if __name__ == "__main__":
main()

小结

真的是解放生产力啊,这个方案极大降低了部署的工作量

这个方法值得推广,我决定把这个脚本内置在「DjangoStarter 框架」中~

极大提高项目部署的生产力!分享一个半自动化的CICD实现方案的更多相关文章

  1. vue项目部署后页面加载首次很慢的优化方案

    参考: vue项目首次加载特别慢需要怎么配置? 1.看看你的依赖包是不是全局引入的,改为组件内按需引入,可大大降低加载时长.或者将组件引入方式改为cdn引入.需要注意的是,两种引入方式不能共存. 2. ...

  2. Vue-cli项目部署到Nginx

    项目环境: 0. Nginx使用 以windows版为例,下载niginx压缩包并解压到任意目录,双击nginx.exe,在浏览器中访问http://localhost,如果出现Welcome to ...

  3. 分享一个大型进销存供应链项目(多层架构、分布式WCF多服务器部署、微软企业库架构)

    项目源码下载:  WWW.DI81.COM 分享一个大型进销存供应链项目(多层架构.分布式WCF多服务器部署.微软企业库架构) 这是一个比较大型的项目,准备开源了.支持N家门店同时操作.远程WCF+企 ...

  4. (转) Spring读书笔记-----部署我的第一个Spring项目

    一.Spring介绍 Spring是一个轻量级的Java EE容器,它也是一种从实际需求出发,着眼于轻便,灵活,易于开发,易测试和易部署的轻量级开发框架.Spring它完成了大量开发中的通用步骤,留给 ...

  5. Spring读书笔记-----部署我的第一个Spring项目

    一.Spring介绍 Spring是一个轻量级的Java EE容器,它也是一种从实际需求出发,着眼于轻便,灵活,易于开发,易测试和易部署的轻量级开发框架.Spring它完成了大量开发中的通用步骤,留给 ...

  6. Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享)

    Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享) 常用的两款AI可视化交互应用比较: Gradio Gradio的优势在于易用性,代码结构相比Str ...

  7. Git.Framework 框架随手记-- 分享一个"比较垃圾"的项目

    本文主要分享一个Git.Framework 开发的一个项目的部分源码,此项目代码"比较垃圾",所以请各位码农,码畜,码神,码圣勿喷!发此文只为记录工作问题以及分享问题! 一. 项目 ...

  8. springboot+shiro 一个项目部署多个,session名冲突问题

    问题 前几天遇到一个比较奇怪的问题, 一个项目部署多个,端口不同.启动之后在同一浏览器中进行登录,后一个登录的会把前一个登录的挤掉,导致只能登录一个. 原因 是因为sessionid相同,然后修改了s ...

  9. 分享一个基于Abp Vnext开发的API网关项目

    这个项目起源于去年公司相要尝试用微服务构建项目,在网关的技术选型中,我们原本确认了ApiSix 网关,如果需要写网关插件需要基于Lua脚本去写,我和另外一个同事当时基于这个写了一个简单的插件,但是开发 ...

  10. 【微信支付】分享一个失败的案例 跨域405(Method Not Allowed)问题 关于IM的一些思考与实践 基于WebSocketSharp 的IM 简单实现 【css3】旋转倒计时 【Html5】-- 塔台管制 H5情景意识 --飞机 谈谈转行

    [微信支付]分享一个失败的案例 2018-06-04 08:24 by stoneniqiu, 2744 阅读, 29 评论, 收藏, 编辑 这个项目是去年做的,开始客户还在推广,几个月后发现服务器已 ...

随机推荐

  1. DRAM的读写操作、刷新、恢复的原理

    这一节湖科大教书匠讲得特别好,原理梳理的很清晰,建议去b站看一看 写这个只为了自己复习方便一点 对读操作会破坏数据的理解 预充电利用列线上的寄生电容,使得每列的电压保持在\(Vcc/2\) 进行读操作 ...

  2. Codeforces Round 971 (Div. 4)

    C. The Legend of Freya the Frog 因为是从x开始跳,贪心的取肯定是直接用max(a,b)/d向上取整然后再乘2,但是要注意,如果再x到达之前,y已经是到达了,也就是某次以 ...

  3. 基于Lighthouse搭建高颜值的YesPlayMusic网易云播放器

    本文介绍了如何使用腾讯云的Lighthouse轻量应用服务器来搭建一个高颜值的第三方网易云播放器. 项目简介 本文使用的是YesPlayMusic项目,这是一款高颜值的第三方网易云播放器,它完全可以作 ...

  4. Windows7、Windows10跳过创建用户并直接用Administrator身份登录

    windows7 windows10跳过创建用户并直接用Administrator身份登录 一.操作方法: 在界面设置按 按 shift+f10 然后输入 lusrmgr.msc 用户管理控制台开启a ...

  5. this 和super 关键字的区别

    this关键字 (1) 每个类的每个非静态方法(没有被static修饰)都会隐含一个this关键字,它指向调用这个方法的对象:当在方法中使用本类属性时,都会隐含地使用this关键字,当然也可以明确使用 ...

  6. 网络开发中的Reactor(反应堆模式)和Proacrot(异步模式)

    服务器程序重点处理IO事件,即:用户的请求读出来,反序列化,回调业务处理,回写.如果在按照面向过程的思路去写,就发挥不出CPU并发优势.那么有没有更优雅的设计方式呢? 有的兄弟,有的. Reactor ...

  7. 康谋分享 | aiSim5激光雷达LiDAR模型验证方法(二)

    aiSim中的LiDAR是一种基于光线追踪的传感器,能够模拟真实LiDAR发射的激光束,将会生成LAS v1.4标准格式的3D点云,包含了方位角.俯仰角和距离等. aiSim能够模拟LiDAR单态(M ...

  8. PC端网页/web通过自定义协议唤起启动windows桌面应用

    PC端网页/web通过自定义协议唤起启动windows桌面应用 步骤: 写注册表 调用 Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\ ...

  9. 什么是AC自动机?如何实现?

    什么是AC自动机? 是基于 Trie树 和 KMP失配指针 的一种高效多模式匹配算法.AC自动机能够一次构建,随后在遍历文本时同时匹配多个敏感词. AC自动机算法的典型应用是敏感词匹配,在各大社交媒体 ...

  10. VMware workstation 部署微软MDT系统

    一.环境准备 1. VMware Workstation 虚拟机配置 新建虚拟机 类型:Microsoft Windows Server 2022 Standard 内存:4GB+ 硬盘:100GB( ...