惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
Engineering at Meta
Engineering at Meta
V
V2EX
有赞技术团队
有赞技术团队
雷峰网
雷峰网
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
小众软件
小众软件
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
L
LINUX DO - 热门话题
Help Net Security
Help Net Security
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 【当耐特】
O
OpenAI News
Y
Y Combinator Blog
Hacker News: Ask HN
Hacker News: Ask HN
Latest news
Latest news
Simon Willison's Weblog
Simon Willison's Weblog
H
Hacker News: Front Page
Project Zero
Project Zero
T
Tailwind CSS Blog
K
Kaspersky official blog
T
Tor Project blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
WordPress大学
WordPress大学
The Cloudflare Blog
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
N
News and Events Feed by Topic
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
A
Arctic Wolf
博客园 - 聂微东
Cyberwarzone
Cyberwarzone
H
Hackread – Cybersecurity News, Data Breaches, AI and More
G
Google Developers Blog
The Last Watchdog
The Last Watchdog
PCI Perspectives
PCI Perspectives
S
Security Affairs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
F
Full Disclosure
Microsoft Security Blog
Microsoft Security Blog
P
Palo Alto Networks Blog
S
Secure Thoughts
A
About on SuperTechFans
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
Netflix TechBlog - Medium
P
Proofpoint News Feed

Chenxu's Blog

RAG检索优化:从 68% 到 82.2% 学习深度学习 北邮计算机研究生选课指北 学习机器学习 上海篇 提出一个好问题 写代码的环境 选购一台合适的设备 计算机的第零课 序言 内蒙古篇 山东篇 一篇对transformers的疑惑 浅析GPUStack 浅谈后端项目分层 浙江篇 河南篇 一些三端协同的开发工具记录 浅谈编程语言中的 GC 在 Windows 上安装 Rust 美化博客 消息队列 MyBatis 现代交换原理2024年题目 回忆版 北邮计科本科课程指北 ClkLog埋点用户分析系统研究报告 大数据梧桐实验分享 大数据HBase实验分享 国产向量数据库研究实践 —— 基于 Milvus 的电影推荐系统 服务器环境配置 大数据HDFS实验分享 SSE多服务之间推送数据 数据库系统原理 操作系统 编译原理 手把手教你gozero从开发到部署 (番外篇): 介绍 gtodolist 前端项目 天津篇 北京篇 手把手教你gozero从开发到部署 (5): gtodolist 之任务的删除 前端相关问题 vue3 + element-plus学习 git相关命令 人工智能原理 使用 MongoTemplate 时开启事务 MySQL Redis 力扣刷题笔记之动态规划 记录一次 VSCode 出现的各种奇怪的问题 手把手教你gozero从开发到部署 (4): 在 model 中自己编写函数实现数据库分页查询 记录平时用到的 windows 右键管理 记录平时使用的 linux 的指令 安装 gstore 并使用 java api 手把手教你gozero从开发到部署 (3): gtodolist 之任务的创建和修改 记录完成 gtodolist 中遇到的 bug 手把手教你gozero从开发到部署 (2): gtodolist 之 user 模块开发 手把手教你gozero从开发到部署 (1): gtodolist 项目说明和环境准备 快速入门 gozero 框架 vue部署在nginx上的相关问题 记一次安装scrapy的报错 将博客部署至服务器 Java CS143: Compliers 《MySQL必知必会》读书笔记(2) 初识 GORM 初识 gin 框架 Spring 《MySQL必知必会》读书笔记(1) 本地搭建博客 🤝友链 🙋🏻‍♂️关于 Browser and Device Check
手把手教你gozero从开发到部署 (番外篇): 记录第一次部署 CI/CD 的流程
Chenxu · 2023-09-02 · via Chenxu's Blog

个人理解的 CI/CD 就是持续集成和持续部署, 持续集成就是代码提交后自动构建, 持续部署就是自动部署到测试环境, 测试环境通过后自动部署到生产环境.

为了简化流程, 此项目是直接将构建后的二进制文件部署到服务器, 并执行脚本运行

新建工作流

首先选择要部署的仓库, Actions -> New workflow 新建一个工作流

选择一个合适的模板文件, 这里我们选择 go

他会为这个工作流生成一个 .github/workflows/go.yml 文件, 我们填写里面的配置文件, 在右侧的 Marketplace 我们可以搜索合适的插件

最后我的配置如下:

# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

# 当有 push 或者 pull request 到 main 分支时会触发任务
on:
    push:
        branches: ["main"]
    pull_request:
        branches: ["main"]

jobs:
    build-and-deploy:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3

            # 选择 go 的版本
            - name: Set up Go
              uses: actions/setup-go@v4
              with:
                  go-version: "1.20"

            # 构建
            - name: Build
              run: |
                  go mod tidy 
                  mkdir tmp
                  go build -o ./cmd/user-api ./app/user/cmd/api/user.go
                  go build -o ./cmd/user-rpc ./app/user/cmd/rpc/user.go
                  tar -zcvf ./cmd.tar.gz ./cmd                  

            # 部署到服务器
            # 这里使用插件 ssh-scp-ssh-pipelines
            # 详细说明见: https://github.com/marketplace/actions/ssh-scp-ssh-pipelines
            - name: Deploy To Server
              uses: cross-the-world/ssh-scp-ssh-pipelines@v1.1.4
              with:
                  host: ${{ secrets.HOST }}
                  port: ${{ secrets.PORT }}
                  user: ${{ secrets.USERNAME }}
                  pass: ${{ secrets.PASSWORD }}
                  first_ssh: |
                      cd /home/guochenxu/gtodolist/go
                      if [ -f "./cmd.tar.gz" ]; then rm ./cmd.tar.gz; fi                      
                  scp: |
                      './cmd.tar.gz' => /home/guochenxu/gtodolist/go/                      
                  last_ssh: |
                      cd /home/guochenxu/gtodolist/go
                      ./start.sh                      

上面配置文件中的 ${{ secrets.HOST }} 是仓库的环境变量, start.sh 是服务器上的启动脚本

配置仓库的环境变量

在该仓库里面选择 Settings -> Secret and variables -> Actions -> New repository secret 进行配置, 第一步的配置文件中的名字要和这里的环境变量的名字相同

编写服务器上的启动脚本

这个可以根据自己的需求自己编写

# 杀死进程
function kill_process(){
	for i do
		pid=`ps -ef | grep $i | grep -v grep | awk '{print $2}'`
		if [ -n "$pid" ]; then
		       kill -9 $pid;
		fi
 	done
}

kill_process user-rpc user-api

# 解压文件
if [ -f "./cmd.tar.gz" ]; then
	if [ -d "./cmd" ]; then
		rm -r ./cmd
	fi
	tar -zxvf ./cmd.tar.gz
	#chmod -R 755 ./cmd
	rm ./cmd.tar.gz
fi

# 启动服务
function start_service(){
	cmd=$1
	etc=$2
	logs=$3
	for file in $(ls $cmd)
	do
		bin=$cmd/$file
		config=$etc/${file}.yaml
		log=$logs/${file}.log
		nohup $bin -f $config > $log &
	done
}

start_service ./cmd ./etc ./logs

# 退出
exit 0

查看效果

到此就部署完毕, 现在只要提交代码就会自动构建, 然后将二进制文件部署到服务器上, 并运行启动脚本

服务部署成功并且正常运行

更新

因为编译后的大文件在往服务器传输的时候实在是太慢了 (平均花费半个多小时, 并且期间很容易出现网络问题), 所以改成直接上传源代码, 将编译的工作放到服务器上, 效率提高了将近 90%

事实上, 应当是把项目打包成镜像, 然后在服务器上拉去镜像运行的, 但是菜鸡不会 docker 😭

工作流更新:

换了两个新的插件, 一个传输文件 (scp 或者 rsync), 一个 ssh 登录执行命令

# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
    push:
        branches: ["main"]
    pull_request:
        branches: ["main"]

jobs:
    build-and-deploy:
        # 设置容器环境
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v3

            # # 设置 GO 版本
            # - name: Set up Go
            #   uses: actions/setup-go@v4
            #   with:
            #       go-version: "1.20"

            #    # 编译
            #    - name: Build
            #      run: |
            #        go mod tidy
            #        mkdir tmp
            #        go build -o ./cmd/user-api ./app/user/cmd/api/user.go
            #        go build -o ./cmd/user-rpc ./app/user/cmd/rpc/user.go
            #        go build -o ./cmd/task-api ./app/task/cmd/api/task.go
            #        go build -o ./cmd/task-rpc ./app/task/cmd/rpc/task.go
            #        tar -zcvf ./cmd.tar.gz ./cmd

            # 两种传输方式, 我的 scp 不知道为什么一直出问题, 所以就用了 rsync

            #    # 使用 scp 把文件传输到服务器上
            #    - name: Transfer to server
            #      uses: appleboy/scp-action@v0.1.4
            #      with:
            #        host: ${{ secrets.HOST }}
            #        username: ${{ secrets.USERNAME }}
            #        password: ${{ secrets.PASSWORD }}
            #        port: ${{ secrets.PORT }}
            #        source: "./cmd.tar.gz"
            #        target: /home/guochenxu/gtodolist/go/

            # 使用 rsync 把文件传输到服务器上
            - name: Transfer to server
              uses: up9cloud/action-rsync@master
              env:
                  HOST: ${{ secrets.HOST }}
                  USER: ${{ secrets.USERNAME }}
                  PORT: ${{ secrets.PORT }}
                  PASSWORD: ${{ secrets.PASSWORD }}

                  TARGET: /home/guochenxu/gtodolist/go/gtodolist
                  SOURCE: ./
                  ARGS: -avuz --progress --delete --exclude=./.git/

                  PRE_SCRIPT: |
                      echo start at:
                      date -u                      
                  POST_SCRIPT: "echo done at: && date -u"

            # 远程执行命令
            - name: Run Service On Server
              uses: fifsky/ssh-action@master
              with:
                  host: ${{ secrets.HOST }}
                  port: ${{ secrets.PORT }}
                  user: ${{ secrets.USERNAME }}
                  pass: ${{ secrets.PASSWORD }}
                  command: |
                      cd /home/guochenxu/gtodolist/go
                      ./start.sh                      

脚本更新:

之前的脚本挺多有问题的地方的, 这里做了两个更新:

  1. 把 rpc 服务和 api 服务分开放, 并且前后间隔一段时间启动, 防止出现找不到服务的问题;
  2. nohup 的错误输出重定向到标准输出的引用, 即 2>&1 (也可以选择直接丢弃输出, 2>/dev/null, 将输出重定向到黑洞文件), 防止远程 ssh 无法正常关闭.
# 杀死进程
function kill_process(){
        for i do
                pid=`ps -ef | grep $i | grep -v grep | awk '{print $2}'`
                if [ -n "$pid" ]; then
                       kill -9 $pid;
                fi
        done
}

kill_process user-api user-rpc task-api task-rpc

# 解压文件
#if [ -f "./cmd.tar.gz" ]; then
#       if [ -d "./cmd" ]; then
#               rm -r ./cmd
#       fi
#       tar -zxvf ./cmd.tar.gz
#       #chmod -R 755 ./cmd
#       rm ./cmd.tar.gz
#fi

# 编译
if [ -d "./cmd" ];then
        rm -rf ./cmd
fi
mkdir ./cmd
mkdir ./cmd/api
mkdir ./cmd/rpc

if [ -d "./gtodolist" ];then
        cd ./gtodolist
        go mod tidy

        go build -o ../cmd/api/user-api ./app/user/cmd/api/user.go
        go build -o ../cmd/rpc/user-rpc ./app/user/cmd/rpc/user.go
        go build -o ../cmd/api/task-api ./app/task/cmd/api/task.go
        go build -o ../cmd/rpc/task-rpc ./app/task/cmd/rpc/task.go

        cd ..
        rm -rf ./gtodolist
else
        echo "编译失败, 没有找到代码目录"
        exit 1
fi
echo "===== 编译完成 ====="

# 启动服务
function start_service(){
        rpc=$1
        api=$2
        etc=$3
        logs=$4

        # 先启动 rpc 服务
        for file in $(ls $rpc)
        do
                bin=$rpc/$file
                config=$etc/${file}.yaml
                log=$logs/${file}.log
                nohup $bin -f $config > $log 2>&1 &
        done

        # 睡眠 7s, 要不然 api 服务启动但是 rpc 没有启动就会报错
        sleep 7s

        # 后启动 api
        for file in $(ls $api)
        do
                bin=$api/$file
                config=$etc/${file}.yaml
                log=$logs/${file}.log
                nohup $bin -f $config > $log 2>&1 &
        done
}

start_service ./cmd/rpc ./cmd/api ./etc ./logs
echo "===== 启动完成 ====="

# 退出
echo "===== 部署成功 ! ====="
exit 0

更新后的效果

nginx 配置

最后补充一下 nginx 怎么配置后端端口转发的配置怎么写。

为了方便文件管理,我在/etc/nginx/conf.d目录下创建了todolist.conf文件,该文件就是 gtodolist 项目的 nginx 配置文件,文件内容如下:

# todolist
server {
	listen       80;
    listen       [::]:80;
    server_name todolist.chenxutalk.top;

	#把http的域名请求转成https
	return 301 https://$server_name$request_uri;
	# rewrite ^(.*) https://$server_name$1 permanent; #自动从http跳转到https

    root /home/guochenxu/gtodolist/web;
	include /etc/nginx/default.d/*.conf;

    # 前端配置
    location / {
    	root /home/guochenxu/gtodolist/web;
	    try_files $uri $uri/ /index.html; #解决页面刷新404问题
    	index  index.html index.htm;
    }

    # 后端匹配路径前端分别进行端口转发
	location /api/v1/user {
		proxy_pass  http://localhost:22301;
	}
	location /api/v1/task {
        proxy_pass  http://localhost:22302;
    }

    error_page 404 /404.html;
    location = /40x.html {
		root   /home/guochenxu/gtodolist/web;
	}
	error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }
}

server {
    listen 443 ssl;
    server_name todolist.chenxutalk.top;
    root /home/guochenxu/gtodolist/web;

    ssl_certificate /etc/nginx/cert.pem;
    ssl_certificate_key /etc/nginx/key.pem;

    ssl_session_timeout 5m;
    #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    error_page 404 /404.html;
    location / {
        root /home/guochenxu/gtodolist/web;
        try_files $uri $uri/ /index.html; #解决页面刷新404问题
        index index.html index.htm;
    }

    location /api/v1/user {
        proxy_pass  http://localhost:22301;
    }
    location /api/v1/task {
        proxy_pass  http://localhost:22302;
    }

    include /etc/nginx/default.d/*.conf;
}