基于Git WebHook+Docker的自动化部署

docker+webhook手摸手教你实现前端自动化部署 前端的项目部署,是将本地项目打包上线发布到服务器的行为,部署上线后可以通过公网ip或者域名进行访问。自动化部署是前端工程化的一部分,它包含持续集成,自动化构建,自动部署。如果不使用自动化部署的话,前端部署上线的流程是:本地打包 => 通过ftp工具链接到线上服务器 => 将打包后的文件拖动到服务器的项目目录下。这个流程看似简单,其实还是比较的麻烦的,而且在移动项目文件的时候还容易放错文件目录,造成一系列问题,那么有没有什么办法能够规避掉这些问题呢?...... 阅读详情

前言

很久之前,有个云服务器,上面挂着自己的 Blog,每次发布代码的时候,都需要手动到服务器上 pull 代码,很是麻烦。

于是,折腾了一下 Gitee 的 Hook,实现了自动部署的功能。

一些答复

为什么不用比较成熟的工具?比如:Jenkins、CI/CD。

1、资源问题,本身就一台小服务器,没那么多资源搞这个。

2、自己搞个简单的,免费,香。

是否会停服?

会有短暂停服,如果不像停服,需要改造一下策略。

是否支持多副本?

不支持,因为作者的需求比较简单,未实现多副本的方案。

环境

  • Docker
  • PHP
  • Nginx(需要支持Lua模块)
  • Lua

示例代码

talk-lucky/git-docker-deploy

具体流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wa9XEBo8-1630649595017)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ba3e75d5-1b50-45c7-964b-59beeab193e3/Untitled_Diagram.drawio_(1)].png)

具体实现

项目结构改造

这里的目的是将项目结构都改造成一致,方便后续脚本的统一。

.
├── src // 项目目录
└── version.txt // 项目版本

宿主机安装Git

因为需要拉取云端代码,需要 git。

配置自动化项目

这个项目可以放到代码仓库里。

本文将项目放在了 /src 目录下。

具体结构和释义如下:

/src
├── dockers
│   ├── docker-compose.yml // 使用Docker compose编排容器
│   ├── mysql // 存放mysql数据的目录
│   ├── redis // 存放redis数据的目录
│   ├── lua
│   │   └── hook.lua // 这是Nginx接收到webhook之后,执行的lua脚本
│   ├── projects
│   │   ├── api.demo.com
│   │   │   ├── Dockerfile // 生成镜像和容器的Dockerfile
│   │   │   └── publish.sh // 发布脚本,会拉取代码,根据Dockerfile创建镜像和容器
│   │   └── admin.demo.com
│   │       ├── Dockerfile
│   │       └── publish.sh
│   └── nginx
│       ├── nginx.conf // 重写了nginx.conf,用于支持后续的项目配置,这个会在一开始替换 /etc/nginx/nginx.conf
│       ├── webhook.conf // webhook的nginx conf,里面会指定需要执行的lua脚本
│       └── api.demo.com.d
│           └── api.demo.com.conf // api.demo.com的nginx conf
├── api.demo.com // 具体的项目目录
│   ├── src // 代码目录
│   └── version.txt // 项目版本,构建镜像时会使用
└── admin.demo.com // 具体的项目目录
    ├── src // 代码目录
    └── version.txt // 项目版本,构建镜像时会使用

宿主机安装Nginx

因为需要构建容器,所以直接在宿主机上安装了 Nginx,用于转发请求。

需要安装 lua 模块。

安装好之后,将自动化配置中的 nginx.conf 替换默认的 nginx.conf。

宿主机安装Docker和Docker compose

这个毋庸置疑了。

安装好之后,在 /src/dockers 目录下,运行 docker compose up -d。

配置Gitee

在项目的 管理→WebHooks 下添加WebHook。

在这里插入图片描述

编写Nginx Lua脚本

为了能够适应多个项目,这里会通过路由来辨别不同的项目。

大致结构是:http://webhook.demo.com/项目别名。

同时,每个项目都有回调时都有自己的密钥。

所以我们需要先定义项目和域名的映射表,项目和密钥的映射表。

-- 需要修改域名和对应别名
local mapping = {
    api = "api.demo.com",
    admin = "admin.demo.com"
}
-- 每个项目的密钥
local password = {
    api = "E491B741843DA372DE61FCF8EFBB5252",
    admin = "E491B741843DA372DE61FCF8EFBB5252"
}

接下来是判断请求和项目、密钥是否匹配。

可以先参考一下 Gitee 的请求头信息,如果是其它平台,则需要自行研究。

Request URL: http://webhook.demo.com/api
Request Method: POST
Content-Type: application/json
User-Agent: git-oschina-hook
X-Gitee-Token: E491B741843DA372DE61FCF8EFBB5252
X-Gitee-Timestamp: 1591321794260
X-Gitee-Ping: false
X-Gitee-Event: Push Hook
X-Git-Oschina-Event: Push Hook

可以看出,需要判断路由对应的项目是否存在,密钥是否存在。

local project = ngx.var.request_uri
project = string.gsub(project, "/", "")
if (not mapping[project]) then
    ngx.say('error project')
    ngx.log(ngx.ERR, 'webhook: error project')
    return 0
end
if (not password[project]) then
    ngx.say('error project')
    ngx.log(ngx.ERR, 'webhook: error project')
    return 0
end

当项目和密钥都存在之后,就需要判断请求体信息是否正确。

  • 是否是 master 分支变更
  • 密钥是否正确

请求体大致结构如下(原文太长了,这里精简了一下):

{
  "ref": "refs/heads/master",
  "before": "5f6477bb191fd11fb98988d96c84c1070a76e771",
  "after": "7826fb922fb2583705441ec6f77d4ccde24b3d14",
  "total_commits_count": 1,
  "commits_more_than_ten": false,
  "created": false,
  "deleted": false,
  "compare": "",
  "commits": [],
  "head_commit": {},
  "repository": {},
  "project": {},
  "user_id": 0,
  "user_name": "",
  "user": {},
  "pusher": {},
  "sender": {},
  "enterprise": null,
  "hook_name": "push_hooks",
  "hook_id": 373558,
  "hook_url": "",
  "password": "E491B741843DA372DE61FCF8EFBB5252",
  "timestamp": "1591321794260",
  "sign": ""
}

从上面的请求体可以看出,只需要判断是否存在 refs/heads/master 和密钥。

具体实现如下:

-- 请求头没问题之后,判断一下请求体是不是master分支
ngx.req.read_body()
local requestBody = ngx.req.get_body_data()
if (not requestBody) then
    ngx.say('error requestBody')
    ngx.log(ngx.ERR, 'webhook: error requestBody')
    return 0
end
if (not string.find(requestBody, 'refs/heads/master')) then
    ngx.say('error branch')
    ngx.log(ngx.ERR, 'webhook: error branch')
    return 0
end
-- 请求体中也有密钥,做第二次校验
if (not string.find(requestBody, password[project])) then
    ngx.say('error password')
    ngx.log(ngx.ERR, 'webhook: error password')
    return 0
end

接下来就是执行 Shell 脚本,创建容器了。

目前,项目都放在了 /src 目录下,同时每个项目都拥有自己的发布脚本,地址是 /src/docker/project/项目/publish.sh。

-- 查找项目的版本
-- 目前项目都放在 src 目录下,具体结构如下:
-- /src/域名/项目代码
local filePath = "/src/" .. mapping[project] .. "/version.txt"
local file = io.open(filePath, "r")
local version = "v1"
if (file) then
    io.input(file)
    tmpVersion = io.read()
    if (not tmpVersion) then
        version = tmpVersion
    end
end

-- 执行Shell脚本,创建容器
os.execute("nohup bash /src/docker/project/" .. mapping[project] .. "/publish.sh " .. version .. " > /var/log/nohup/nohup.log 2>&1 &")

最后,给以正确的响应。

ngx.say('success')

具体脚本如下:

-- 需要修改域名和对应别名
local mapping = {
    api = "api.demo.com",
    admin = "admin.demo.com"
}
-- 每个项目的密钥
local password = {
    api = "E491B741843DA372DE61FCF8EFBB5252",
    admin = "E491B741843DA372DE61FCF8EFBB5252"
}

-- 请求的结构是 http://webhook.demo.com/项目别名
-- 获取项目别名和对应的密钥,进行匹配
-- 请求头如下:
-- Request URL: http://webhook.demo.com/api
-- Request Method: POST
-- Content-Type: application/json
-- User-Agent: git-oschina-hook
-- X-Gitee-Token: E491B741843DA372DE61FCF8EFBB5252
-- X-Gitee-Timestamp: 1591321794260
-- X-Gitee-Ping: false
-- X-Gitee-Event: Push Hook
-- X-Git-Oschina-Event: Push Hook
local project = ngx.var.request_uri
project = string.gsub(project, "/", "")
if (not mapping[project]) then
    ngx.say('error project')
    ngx.log(ngx.ERR, 'webhook: error project')
    return 0
end
if (not password[project]) then
    ngx.say('error project')
    ngx.log(ngx.ERR, 'webhook: error project')
    return 0
end

-- 请求头没问题之后,判断一下请求体是不是master分支
ngx.req.read_body()
local requestBody = ngx.req.get_body_data()
if (not requestBody) then
    ngx.say('error requestBody')
    ngx.log(ngx.ERR, 'webhook: error requestBody')
    return 0
end
if (not string.find(requestBody, 'refs/heads/master')) then
    ngx.say('error branch')
    ngx.log(ngx.ERR, 'webhook: error branch')
    return 0
end
-- 请求体中也有密钥,做第二次校验
if (not string.find(requestBody, password[project])) then
    ngx.say('error password')
    ngx.log(ngx.ERR, 'webhook: error password')
    return 0
end

-- 查找项目的版本
-- 目前项目都放在 src 目录下,具体结构如下:
-- /src/域名/项目代码
local filePath = "/src/" .. mapping[project] .. "/version.txt"
local file = io.open(filePath, "r")
local version = "v1"
if (file) then
    io.input(file)
    tmpVersion = io.read()
    if (not tmpVersion) then
        version = tmpVersion
    end
end

-- 执行Shell脚本,创建容器
os.execute("nohup bash /src/docker/project/" .. mapping[project] .. "/publish.sh " .. version .. " > /var/log/nohup/nohup.log 2>&1 &")

ngx.say('success')

编写Dockerfile

位置:/src/dockers/project/你的项目/Dockerfile。

这里会直接将项目的代码拷贝到容器中,并且执行 composer install --no-dev。

FROM imjcw/php-fpm:7.3.18-fpm-alpine

COPY src /var/www/html/
WORKDIR /var/www/html

RUN set -e; \
    apk update; \
    apk add git; \
    cd /var/www/html; \
    rm -rf .git; \
    composer install --no-dev; \
    apk del git; \
    rm -rf /var/cache/apk/*

编写publish.sh

这里面指定了容器开放的端口,是预留给 Nginx 使用的。

大致流程是:

  • 进入项目
  • 拉取代码
  • 构建镜像
  • 停止之前的容器
  • 删除之前的容器
  • 构建新的容器
  • 删除不用的容器和镜像
#!/bin/bash

PROJECT="api.demo.com"

VERSION=""
if [ -n "$1" ]; then
    VERSION="$1"
else
    VERSION="v1"
fi

PROJECT_DIR="/src/${PROJECT}"
if [ ! -d "${PROJECT_DIR}" ]; then
    echo "项目目录不存在"
    exit -2
fi

# pull code
cd ${PROJECT_DIR}
git pull origin master

# build image
docker build -t imjcw/${PROJECT}:${VERSION} ./

# stop container
docker stop ${PROJECT}
docker rm ${PROJECT}

# run container
docker run -d --name ${PROJECT} --link mysql --link redis --net dockers_default -p 9000:9000 imjcw/${PROJECT}:${VERSION}

# rm <none> images
if test ! -z $(docker images -f "dangling=true" -q); then
    docker rmi $(docker images -f "dangling=true" -q)
fi

最后

为了能够实现,特地学了一下 LUA,感觉还不错。

github-webhook+docker实现项目可持续自动化部署 文章主要介绍了使用nginx+pm2+github-webhook+docker实现项目自动部署以及手动部署的实现流程,同时介绍了docker容器化技术的使用 阅读详情

相关推荐

jenkins的安装使用(docker+django)

以上内容,本人在工作之余花了快两周时间才搞成这样,基本算是成型了。自己摸索了很多,也借鉴了很多,中间各种问题,不过总算结果是好的。

Test20201990的博客 1383

docker + webhook 从零实现前端自动化部署

着手开发前,先介绍这次的主角docker简而言之,docker 可以灵活的创建/销毁/管理多个“服务器”,这些“服务器”被称为容器 (container)在容器中你可以做任何服务器可以做的事,例如在有 node 环境的容器中运行打包项目,在有 nginx 环境的容器中部署项目,在有 mysql 环境的容器中做数据存储等等一旦服务器安装了 docker ,就可以自由创建任意多的容器,上图中 docker 的 logo 形象的展示了它们之间的关系,🐳就是 docker,上面的一个个集装箱就是容器。

油墨香^-^的博客 2420

Jenkins in Docker远程部署

Jenkins in Docker 部署,发布 纯新手,最近在学习部署Jenkins 记录一下路线 目标: git commit之后由部署在docke中的Jenkins自动完成以下操作: 生成Jar包 生成项目docker镜像 上传到镜像仓库(harbor) docker启动项目镜像 准备工作 运行docker所有需要的东西 一个Jenkins的Dockerfile https://hub.docker.com/r/jesusperales/jenkins-docker-run-inside/doc

Helium3_3的博客 639

手把手教你用Jenkins CI 自动部署Docker + 使用阿里云镜像服务自动构建+ webhook触发...

Jenkins部分 首先,我们要有个Jenkins咯,下载链接:https://jenkins.io/download/ 我们安装官网教程安装好jenkins,安装教程略.... 嗯?不是说好手把手么?你妹的. 好好好,我们还是来手把手教程好了. 首先安装JDK8 添加安装源之后直接apt-get install就好,下面是ubuntu的安装命令,其他系统自己玩一下就好. sudo a...

812

AI+彗星插件:自动立体画打印标准化工作流全解析

光栅立体画通过柱镜光栅分离视线,借助左右视差形成裸眼3D效果,其核心在于打印文件与光栅参数的精确匹配。传统制作依赖手工绘制深度图和繁琐的交错合成,效率低且质量不稳定。随着AI单目深度估计技术成熟,配合彗星插件等自动化工具,深度图生成、主体分割、视差序列构建与交错合图等工序被大幅简化,实现了从原图到可打印光栅文件的标准化生产。这一流程不仅降低了经验门槛,也提升了批量化输出的稳定性。文章深入拆解了LPI、DPI换算、视差序列生成、打印覆合等关键环节,并结合可运行的Python脚本与常见问题排查指南,为图像技术开

weixin_33802505的博客 343

别再手动合并代码了!用Docker Compose 5分钟搞定Gitea私有Git服务器(附PostgreSQL配置)

本文详细介绍了如何使用Docker Compose在5分钟内快速搭建Gitea私有Git服务器,并配置PostgreSQL数据库以实现高效稳定的代码管理。通过轻量级的Gitea和强大的PostgreSQL组合,开发者可以轻松告别手动合并代码的烦恼,享受企业级私有Git服务的便利与安全。

weixin_29205061的博客 352

Git Webhook 项目使用教程

`git-webhook` 是一个基于 Go 语言实现的 Webhook 服务,专门用于处理与 Git 相关的事件。该项目由 NetEaseGame 开发并开源,旨在帮助开发者自动化处理 Git 仓库中的事件,如代码推送、分支创建和删除等。通过配置 Webhook,开发者可以在这些事件发生时触发特定的操作,如自动部署、通知等。 ## 2. 项目快速启动 ### 2.1 环境准备 在开始之前,...

gitblog_00584的博客 699

docker-webhook:一个轻巧,最小的webhook容器:anchor:

webhook :anchor: 轻巧,最小的容器 用法 可以从和中获得Docker映像。 如果您希望从拉GHCR,只需更换thecatlady/webhook与ghcr.io/thecatlady/webhook在下面的例子。 Docker Compose(推荐) 将以下卷和服务定义添加到docker-compose.yml文件: services : webhook : image : thecatlady/webhook container_name : webhook command : -verbose -hooks=hooks.yml -hotreload environment : - TZ=America/New_York # optional volumes : - /path/to/appdata/c

jenkins+docker+node+nestjs 自动化部署

本文章内容版本信息 jenkins: Jenkins 2.289.3 java: 1.8 node: 12 npm: 6 系统: ubuntu18 版本管理工具: gitee Jenkins https://www.jenkins.io/ Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,起源于Hudson(Hudson是商用的),主要用于持续、自动的构建/测试软件项目、监控外部任务的运行。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行.

Nodejs 3460

Globus数据下载全攻略:从零配置到批量下载大文件(附常见问题解决)

本文详细介绍了Globus数据下载的全流程,从零配置到批量下载大文件的实用技巧,包括环境准备、高级传输策略、性能优化及常见问题解决方案。Globus作为科研数据的高效传输工具,通过并行流传输和自动校验技术,显著提升大规模数据传输的效率和可靠性。

u9v0w的博客 662

树莓派CSI/DSI二合一接口深度玩法:同时驱动MIPI摄像头和屏幕的配置秘籍

本文详细解析了树莓派5上CSI/DSI二合一接口的深度玩法,提供了同时驱动MIPI摄像头和屏幕的完整配置秘籍。从硬件连接到软件设置,包括config.txt的优化配置,帮助用户充分利用树莓派5的双多功能接口,实现高效的双设备工作模式。

weixin_30687051的博客 573

开源项目常见问题解决方案:go-git-webhook

开源项目常见问题解决方案:go-git-webhook 一、项目基础介绍 go-git-webhook 是一个使用 Golang 编写的自动化部署和运维工具。该项目支持 GitHub、GitLab 和 GitOsc 等平台的 WebHook 功能,可以用于快速搭建并使用 WebHook 进行项目的自动化部署和运维。 主要编程语言 Golang 二、新手常见问题及解决步骤 问题一:如何安装和配置...

gitblog_01165的博客 415

PyQtWebEngine安装避坑指南:快速解决ModuleNotFoundError

本文详细解析了PyQt5开发中常见的ModuleNotFoundError: No module named 'PyQt5.QtWebEngineWidgets'错误。核心原因是PyQt5 5.11版本后,网页引擎组件被独立为PyQtWebEngine包。文章对比了退回旧版本、独立安装及使用Conda等多种解决方案,并提供了针对Windows、macOS和Linux不同平台的实战安装指南与验证方法,帮助开发者快速解决依赖问题。

weixin_29056101的博客 427

docker + jenkins + git + maven自动化构建与部署

docker + jenkins + git + maven自动化构建与部署。

569

Cursor集成Claude 4.6免费版:本地IDE合规调用指南

大语言模型编程助手(LLM Copilot)已成为现代开发标配,其核心能力依赖于上下文理解、代码推理与结构化输出等关键技术原理。Anthropic官方提供的Claude系列模型,尤其Claude 4.6,凭借200K超长上下文、tool use函数调用和严格可控的API设计,在工程实践中展现出显著技术价值。该能力可无缝嵌入本地IDE环境,支撑代码解释、跨文件分析、单元测试生成等高频开发场景。本文聚焦Cursor这一开源可审计IDE,详解如何合规复用Anthropic免费tier接口(/v1/messages

weixin_34095889的博客 537

STM32 使用IO口模拟I2C时序

上一篇《I2C协议详解》 我们了解了I2C的操作流程,这一篇,我们就使用I2C,来对EEPROM进行操作吧。 我们做两种选择: 1.时序由IO口模拟高低电平,需要了解协议并按照协议操作相应的IO口。 2.时序由硬件自行产生,不需要人工干预; 由硬件产生的I2C时序,我们借助Stm32Cube配置实现便可,我们这一篇,抛开Stm32Cube,手撕代码,根据I2C的时序,一步步地实现I2C对...

8937

Linux Docker+Jenkins部署

Linux Docker+Jenkins部署 1. Docker安装Jenkins过程 首先拉取镜像: docker pull jenkins (我安装的是Jenkins 2.275) 创建本地数据卷: mkdir -p /data/jenkins_home/ 修改用户授权: chown -R 1000:1000 /data/jenkins_home/ 启动容 器: docker run -d --name jenkins -p 7900:8080 -p 50000:50000 -v /data/jenk

Crashyi的博客 1256

STM32嵌入式开发实战:如何用miniLZO压缩算法节省70%存储空间(附完整工程)

本文详细介绍了如何在STM32嵌入式开发中使用miniLZO无损压缩算法实现高达70%的存储空间节省。通过对比分析不同轻量级算法,重点讲解miniLZO的内存优化配置、分块压缩策略和完整工程实现,帮助开发者有效解决嵌入式系统中的存储瓶颈问题。

ss78901的博客 881

docker+jenkins+git持续部署实践

写在前面以前搭建spring-cloud的时候 看到别人代码里面总有Dockerfile,然后了解到docker容器的概念,玩了两个星期玩不下去了,就放下了,无意间在网上看到一篇文章Docker持续部署,就想着自己也搭建一套玩玩。 流程网上那篇文章介绍的流程涉及到多个docker镜像,我这里为了入门就简单化了。 自动化部署效果就是: 你修改了本地的代码 然后push到git上面,git的w

我家猫叫奥利奥 1万+

康耐视visionpro-CogBlobTool工具详细说明

康耐视visionpro-CogBlobTool工具详细说明

视觉人机器视觉的博客 1726

第三章-python基础语法

有C语言基础应该这样学 Python。

qq_46187594的博客 1176

Jenkins自动化部署Java思路

请看文档 docker+jenkins+git搭建java自动化部署 https://blog.youkuaiyun.com/tellmewhyto/article/details/80963755?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribut...

nhfc99的专栏 190

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Jenkins + Git自动化部署流程搭建姿势大全~纯干货开箱即用

本文详细介绍了在CentOS 7系统上安装和配置Jenkins的两种方法:手动安装war包和使用Docker镜像。手动安装部分包括下载Jenkins war包、配置JDK、设置环境变量、创建systemctl服务管理Jenkins,以及调试和启动Jenkins服务。Docker安装部分则涉及拉取Jenkins镜像、创建挂载目录、启动容器等步骤。此外,文章还介绍了如何通过Jenkins Web UI进行基本配置,包括安装插件、配置全局工具、系统设置、凭据管理等。最后,文章提供了项目配置的指导,包括创建任务、配

weixin_55720157的博客 4270

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【vuejs项目部署】使用docker基于daocloud自动化部署到自己的主机

在前端开发中,部署项目是我们经常发生的事情 如果在你的公司中,项目部署需要你来负责,怎么优雅无痛的让你的项目自动上线在之前的文章中,写过几篇关于项目上线的方式,大家可以翻阅这个专栏去看。今天主要讲怎么优雅的部署vue项目,使用docker容器,配合git webhook 钩子,当我们触发到git hook 事件,就自动化部署。这里当然建议...

weixin_33670713的博客 499

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

使用Docker+Jenkins+Gitee自动化部署SpringBoot项目

使用Docker+Jenkins+Gitee自动化部署SpringBoot项目

wy971744839的博客 796

Docker下Gogs设置Webhook推送Spug,踩坑记录与解决方案

问题解决方案改回容器内监听3000端口,使用 Docker 映射Webhook 不能访问内网地址配置。

大唐锦绣的博客 813
上一篇: 在Colaboratory上运行Go程序
下一篇: Gitlab+Jenkins+SonarQube实现代码质量扫描自动化
IMJCW
博客等级 码龄10年 13粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值