<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Loading

      極大提高項目部署的生產(chǎn)力!分享一個半自動化的CICD實現(xiàn)方案

      前言

      完全自動化的 CICD 確實好,代碼提交后就自動構(gòu)建自動發(fā)布新版本,實現(xiàn)不停機更新的情況下,還能隨時回滾,這擱誰不喜歡啊~

      但理想很豐滿,現(xiàn)實往往很骨感,不是所有開發(fā)/生產(chǎn)環(huán)境都具備部署 CICD 的條件

      先說結(jié)論,這些 CICD 服務(wù)都有一些問題,要么就是網(wǎng)絡(luò)不通,要么就是太重太麻煩不具備部署條件(服務(wù)器都在內(nèi)網(wǎng),無法直連)

      所以我在工作過程中,「創(chuàng)新」了一種 CICD 的平替方案,通過一個腳本,實現(xiàn)一鍵發(fā)布!

      PS: 由于篇幅關(guān)系,無法在文章里貼出全部代碼,有需要的同學(xué)可以在公眾號后臺回復(fù)「半自動 CICD 腳本」獲取

      關(guān)于 CICD

      現(xiàn)在常見的 CICD 服務(wù)都具備一定門檻,咱們討論一下:

      • Github Actions: 最適合開源項目使用,不用部署配置,完全免費 ?? 不過在生產(chǎn)環(huán)境往往因為網(wǎng)絡(luò)問題用不了
      • GitLab CI/CD: 很重,需要部署和配置 GitLab 服務(wù)
      • Jenkins: 很重,需要部署和配置 Jenkins 服務(wù)
      • Azure DevOps 和 AWS CodePipeline: 這倆依賴它家的云服務(wù),而且都是國外的,基本不用考慮的

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

      • 國內(nèi)的 Gitee 流水線: 這個類似 Github Actions,不過卻是收費的,打個工而已,難道還得自費上班?直接 pass
      • CircleCI: 云原生 CI/CD 服務(wù),提供與 GitHub 和 Bitbucket 的集成,國外使用應(yīng)該很不錯,但國內(nèi)網(wǎng)絡(luò)環(huán)境肯定是不允許的
      • Bitbucket Pipelines: 與 Bitbucket 倉庫緊密集成,適合使用 Bitbucket 進行代碼托管的團隊,與 Github 類似的情況,不用考慮了

      PS: 有時候不得不感嘆,國內(nèi)國外仿佛兩個世界…

      除了這些之外,我還找到一個輕量級的開源 CICD 項目: https://github.com/flowci/flow-core-x

      這個看起來不錯,感覺可以用在 HomeLab 或者 NAS 上,到時來嘗試一下。

      解決方案

      我的解決方案是用「腳本 + docker」實現(xiàn)一鍵發(fā)布、不停機更新、隨時回滾版本~

      基本思路,我畫了個簡單的圖,方便理解

      graph LR A([本地])-->B1(打 TAG) A-->B2(docker build) B2-- 推送鏡像 -->C[(鏡像倉庫)] A-- SSH連接 -->D{服務(wù)器} C-- 拉取鏡像 -->D D-- 啟動 -->E([線上服務(wù)])

      這個方案只需要簡單的配置,之后就可以一鍵發(fā)布了,所以我稱之為「半自動 CICD」

      原理是本地 git 倉庫打版本 tag(如: v0.0.1),然后運行腳本會自動識別這個版本 tag,構(gòu)建鏡像之后打上同樣的 tag,再推送遠程鏡像倉庫,到了服務(wù)器上再拉下來啟動,完事~(就是這么簡單樸素)

      如何使用

      使用這個方案的前提是:

      • 使用 git 管理代碼
      • 使用 docker 部署項目
      • 需要有一個私有的 docker 鏡像倉庫,可以自建,也可以使用阿里云這類私有鏡像服務(wù)(免費)
      • 服務(wù)器能訪問到 docker 鏡像倉庫(內(nèi)網(wǎng)的話可以自建)

      修改 compose 配置

      在使用腳本之前,需要一點小小的配置,后面就可以解放生產(chǎn)力了~

      還是以基于 DjangoStarter 框架 的項目為例

      compose.yaml 配置文件

      services:
      	# 省略無關(guān)內(nèi)容
        app:
          image: ${APP_IMAGE_NAME}:${APP_IMAGE_TAG}
          container_name: $APP_NAME-app
      

      .env 環(huán)境變量

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

      到時腳本運行時會自動修改 .env 里的版本 APP_IMAGE_TAG

      打 tag

      在本地開發(fā)完成之后

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

      例如:

      git tag v0.1.1
      

      運行腳本

      這次的腳本我是用 Python 編寫的,不過沒有其他外部依賴,完全使用標準庫實現(xiàn),還算比較方便的

      python scripts/build_docker.py
      

      PS: 后續(xù)我會考慮使用 C# 或者 Go 重新寫這個腳本,支持 AOT,作為一個工具添加到系統(tǒng) PATH,使用起來更方便

      腳本

      接下來放一個簡化版本的腳本

      由于篇幅關(guān)系,無法在文章里貼出全部代碼,有需要的同學(xué)可以在公眾號后臺回復(fù)「半自動 CICD 腳本」獲取

      這個腳本,總共一百多行,麻雀雖小五臟俱全,實現(xiàn)了完整的功能。

      一開始我是用的 paramiko.SSHClient 來建立 SSH 連接的,不過后面覺得還是不要引入額外的復(fù)雜度比較好,最終簡化成這樣,直接使用系統(tǒng)自帶的 SSH 命令。

      #!/usr/bin/env python3
      # -*- coding: utf-8 -*-
      """
      Docker鏡像構(gòu)建、推送和遠程部署腳本
      
      功能:
      1. 獲取最新git tag作為版本號
      2. 構(gòu)建Docker鏡像并推送到配置的鏡像倉庫
      3. SSH連接到遠程服務(wù)器進行自動部署
      
      配置項(環(huán)境變量或默認值):
      - REGISTRY_URL: 鏡像倉庫地址,如: registry.example.com
      - REGISTRY_NAMESPACE: 鏡像倉庫命名空間
      - IMAGE_NAME: 鏡像名稱
      - REMOTE_HOST: 遠程服務(wù)器配置,如: 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',  # 遠程服務(wù)器地址或~/.ssh/config中的Host別名
          'REMOTE_PROJECT_PATH': '/path/to/project',
      }
      
      
      def get_config(key: str) -> str:
          """獲取配置值,優(yōu)先使用環(huán)境變量,否則使用默認值"""
          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]:
          """
          執(zhí)行命令并實時顯示輸出,同時捕獲輸出內(nèi)容。
          返回狀態(tài)碼、stdout和stderr。
          """
          if show_output:
              print(f"執(zhí)行: {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錯誤: 命令執(zhí)行失敗 (返回碼: {returncode})")
              # 錯誤輸出已經(jīng)被實時打印,這里不再重復(fù)打印
              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:
          """部署到遠程服務(wù)器"""
          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鏡像構(gòu)建、推送和部署流程\n")
      
          # 1. 獲取最新tag
          version = get_latest_tag()
      
          # 2. 構(gòu)建鏡像
          print("\n?? 構(gòu)建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?? 所有任務(wù)已完成!")
      
      
      if __name__ == "__main__":
          main()
      

      小結(jié)

      真的是解放生產(chǎn)力啊,這個方案極大降低了部署的工作量

      這個方法值得推廣,我決定把這個腳本內(nèi)置在「DjangoStarter 框架」中~

      posted @ 2025-07-11 12:26  程序設(shè)計實驗室  閱讀(737)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 熟女人妻aⅴ一区二区三区电影| 日韩精品 在线 国产 丝袜| 亚洲欧美日韩在线码| 精品国产迷系列在线观看| 清新县| 国产精品成人一区二区三| 国产视频最新| 久久男人av资源站| 久久精品国产99国产精品| 久久精品国产久精国产| 欧美激情a∨在线视频播放| 蜜臀av一区二区国产在线| 肇州县| 国产亚洲精品久久久久婷婷图片| 精品人妻日韩中文字幕| 天天摸天天碰天天添| 精品国产迷系列在线观看| 精品一区二区三区东京热| 日韩一区二区三区精彩视频| 中文字幕无码av不卡一区| 国产对白叫床清晰在线播放| 日本边添边摸边做边爱喷水| 日本午夜精品一区二区三区电影| 国产亚洲精品久久久久5区| 国产极品粉嫩尤物一线天| 久久毛片少妇高潮| 一区二区不卡国产精品| 免费观看欧美猛交视频黑人| 99精品热在线在线观看视| 亚洲综合无码明星蕉在线视频| 不卡一区二区国产在线| 真实国产乱啪福利露脸| 亚洲最大福利视频网| 国产精品无码一区二区在线| 亚洲国产精品一区二区久| 40岁大乳的熟妇在线观看| 综合图区亚洲欧美另类图片| 熟女精品国产一区二区三区| 国产精品视频一区二区不卡| 中文字幕亚洲制服在线看| 在国产线视频A在线视频|