Openwrt 路由器 Tailscale 版本升級方法

自從上年11月開始推出小U自家的OpenWRT路由器以來,預載的Tailscale版本竟然一直停留在1.58版本,而官方版的Tailscale截至今日已經升級到1.68.2 計算小版本的話升級不止10次,但OpenWRT官方Package依然停留1.58。到底爲什麼會這樣?OpenWRT如何升級Tailscale。又是20分鐘快速文章,如有不足,敬請見諒。

什麼你還未有OpenWRT路由器?趕緊看看:

OpenWRT中的 Tailscale update 爲什麼這麼難?

OpenWRT是由社群維護的系統,Tailscale的套件爲什麼一直停留在半年,小U就沒有確實答案,但估計可以歸根是套件的維護者沒有更新,而又沒有新的志願者接過任務。

但其實Tailscale本身的執行程式Binary是包含了更新指令 tailscale update。在Linux系統,只需要執行 tailscale update,就可以自動升級tailscale版本。但爲什麼在Openwrt中會報錯:

opkg error: No space left on device #磁盤空間不足,無法升級

Openwrt 中的Tailscale 和“原生”的不同

仔細分析,小U發現 tailscale update 會自動按照平台(x86 / arm etc…) 抓取最新的程序壓縮包,用 1.68.1版本爲例,解壓縮這個升級壓縮包,發現裡面的程式竟然有50MB左右的大小:

那怪不得在儲存空間十分有限的無線路由器,會顯示空間不足。只有128MB ROM的Ups-WR30U (小米WR30U)爲例,本來可用空間就只有 ,那這個官方包當然是放不進去的。(有好幾十GB空間的軟路由例如Ups-Dragon和Ups-R2S就可以用tailscale update 順利升級)

Ups-WR30U 的可用ROM容量只有40MB左右

那問題來了,原本OpenWRT PACKAGE裡面提供的那個Tailscale是如何裝上去的?

找到OpenWRT 正在運行的Tailscale執行文件,竟然只有20MB,比官方的小了一半不止:

而且Linux用戶還會發現一個奇怪的地方,在 tailscale update 下載的壓縮包中,tailscale 和 tailscaled 兩個執行程序是不同大小的,而在OpenWRT中,tailscale 是來自 tailscaled 的“鏈接”,類似Windows中的快捷方式,也就是兩個程式是完全相同的??!!

如何縮減Tailscale體積

順藤摸瓜,找到Tailscale官方有提到如何壓縮Tailscale運行程式的方法,針對 OpenWRT 這類型容量有限的嵌入式設備:

Smaller binaries for embedded devices · Tailscale Docs

文章介紹了縮減體積的幾個步驟:

首先,Tailscale 允許將客戶端 (tailscale)和守護進程 (tailscaled)合併為單一二進制文件,類似於 busybox 的工作方式。使用此標誌可以從建置的二進制文件中省略調試信息和較少使用的功能。最後,使用 UPX壓縮 可將 Tailscale 二進制文件的大小從 23MiB 壓縮至 4.5MiB,約為原始大小的 20%。这样的构建方式对于嵌入式设备(如OpenWrt路由器)非常有用,因为它可以节省磁盘空间。

UPX(Ultimate Packer for Executables)是一個免費且開源的可執行文件壓縮工具,支持來自不同操作系統的多種文件格式。它的主要特點是高壓縮比,可以在保持可執行功能的同時大幅節省存儲空間。UPX 通常可以將程序和 DLL 文件的大小減少約 50% 到 70%,從而降低磁盤空間、網絡加載時間、下載時間以及其他分發和存儲成本。使用 UPX 壓縮的程序和庫是完全自包含的。

這和我們在Openwrt上看到的情況完全一樣:tailscale 和 tailscaled 被合併了一個執行Binary。也就是說,我們能按照以上的方法,自行把最新版本的Tailscale文件壓縮並替換到Openwrt裡面,就可以升級了!

但是,人手去進行以上步驟,需要你十分熟悉GO語言的編譯環境,以及使用UPX壓縮工具,這對於新手來說都十分勸退。有沒有可以站在巨人肩上的方法?

使用現成的已壓縮的TAILSCALE程式

GL.iNet 的論壇志願者 Admon 就設計了一套在GL.iNet路由器上用的Tailscale “迷你瘦身”升級版程式。此程式包含一個一鍵升級腳本,會自動抓取這個Github倉庫最新版本的Tailscale“迷你瘦身版”。

但是我們無需用到GL.iNET的任何相關腳本和設定,我們只要Tailscale“迷你瘦身版”就可以手動將Openwrt的Tailscale版本升級。因此,所有牌子的OpenWRT路由器,都可以受惠到以下這個升級方法。

手動升級OpenWRT TAILSCALE的方法

第一步,我們下載最新版本的已壓縮的TAILSCALE程式

因爲 Ups-WR30U 和 Ups-AX6000 都是linux-arm64架構的,所以下載這一個 binary程式:

Releases · Admonstrator/glinet-tailscale-updater (github.com)

沒有用過Linux的用戶會好奇爲什麼沒有 .exe 的結尾,在Linux中是沒有的,可以直接執行。

今天見到是19小時前更新的v1.68.2,是最新的版本。

第二步:我們要仿照OpenWRT的已有Tailscale

重命名 tailscaled-linux-arm64 爲 tailscaled。

第三步:停止現在Tailscale 的運行

到 System -> Startup -> Tailscale STOP

第四步:上傳 tailscaled 覆蓋原有的 tailscaled

使用 WinSCP 或 Finalshell 上傳壓縮瘦身版本的 tailscaled

最後重啓路由器,這時候執行 tailscale status 就會顯示是 1.68.1 版本。

暫時測試沒有發現壓縮版有何問題。

使用這個第三方Tailscale程式安全嗎?

小U不對任何第三方軟體做擔保,請大家自己分析衡量。以下是小U的分析:

這個壓縮版的Tailscale是透過以下的github action 自動排程編譯腳本自動生成的,解釋如下:

glinet-tailscale-updater/.github/workflows/build-tailscale.yaml at main · Admonstrator/glinet-tailscale-updater

name: Build smaller Tailscale binary
on:
  schedule:
    - cron: '30 0/12 * * *'
  workflow_dispatch:
env:
  SOFTWARE_NAME: "Tailscale"
  FILE_NAME: "tailscaled"
  REPO: "tailscale/tailscale"
  REPO_SMALL: "Admonstrator/glinet-tailscale-updater"
  GIT_AUTHOR_NAME: "Admonstator"
jobs:
  check-versions:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      TAG: ${{ steps.tag.outputs.TAG }}
      TAG_SMALL: ${{ steps.tag_small.outputs.TAG_SMALL }}
    steps:
      - name: Get latest ${{ env.SOFTWARE_NAME }} tag
        id: tag
        run: |
          latest_tag=$(
            curl -s "https://api.github.com/repos/${{ env.REPO }}/releases" \
            | jq -r 'sort_by(.published_at) | last | .tag_name'
          )
          echo "TAG=$latest_tag" >> "$GITHUB_OUTPUT"
          echo "Latest ${{ env.SOFTWARE_NAME }} Tag: $latest_tag"
      - name: Get latest ${{ env.SOFTWARE_NAME }} Small tag
        id: tag_small
        run: |
          latest_tag_small=$(
            curl -s "https://api.github.com/repos/${{ env.REPO_SMALL }}/releases/latest" \
            | grep -oP '"tag_name": "\K(.*)(?=")' || echo ""
          )
          echo "TAG_SMALL=$latest_tag_small" >> "$GITHUB_OUTPUT"
          echo "Latest ${{ env.SOFTWARE_NAME }} Small Tag: $latest_tag_small"
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    needs: check-versions
    if: needs.check-versions.outputs.TAG_SMALL != needs.check-versions.outputs.TAG
    env:
      TAG: ${{ needs.check-versions.outputs.TAG }}
    strategy:
      matrix:
        go-version: [stable]
        os: [linux]
        platform:
          - amd64
          - arm
          - arm64
          - mips
          - mipsle
    steps:
      - name: Checkout ${{ env.SOFTWARE_NAME }} repository
        uses: actions/checkout@v4
        with:
          repository: ${{ env.REPO }}
          ref: ${{ env.TAG }}
      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go-version }}
      - name: Download Go modules
        run: go mod download
      - name: Cross-compile
        run: |
          GOOS=${{ matrix.os }} GOARCH=${{ matrix.platform }} ./build_dist.sh \
          --extra-small --box \
          -o "${{ env.FILE_NAME }}-${{ matrix.os }}-${{ matrix.platform }}" ./cmd/${{ env.FILE_NAME }}
      - name: Upload built binary
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.FILE_NAME }}-${{ matrix.os }}-${{ matrix.platform }}
          path: ./${{ env.FILE_NAME }}-${{ matrix.os }}-${{ matrix.platform }}
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    needs:
      - build
      - check-versions
    if: needs.check-versions.outputs.TAG_SMALL != needs.check-versions.outputs.TAG
    env:
      TAG: ${{ needs.check-versions.outputs.TAG }}
    steps:
      - name: Get UPX latest version
        id: get-upx-version
        run: |
          echo "UPX_VERSION=$(
            curl -s https://api.github.com/repos/upx/upx/releases/latest \
            | jq -r '.tag_name' \
            | cut -c 2-
          )" >> "$GITHUB_ENV"
      - name: Download UPX
        run: |
          wget -q "https://github.com/upx/upx/releases/latest/download/upx-${{ env.UPX_VERSION }}-amd64_linux.tar.xz"
          tar --to-stdout -xf "upx-${{ env.UPX_VERSION }}-amd64_linux.tar.xz" \
            "upx-${{ env.UPX_VERSION }}-amd64_linux/upx" > "${PWD}/upx"
          chmod -v +x "${PWD}/upx"
      - name: Download built binaries
        uses: actions/download-artifact@v4
        with:
          pattern: ${{ env.FILE_NAME }}-*
      - name: Moving files
        run: |
          for dir in "${{ env.FILE_NAME }}-"*; do
            mv -v "${dir}" "${dir}.d"
            mv -v "${dir}.d/${{ env.FILE_NAME }}-"* .
            rmdir -v "${dir}.d"
          done
          chmod -v +x "${{ env.FILE_NAME }}-"*
      - name: Compress Binary with UPX
        run: |
          "${PWD}/upx" --lzma --best --no-progress "${{ env.FILE_NAME }}-"*
      - name: Create checksums
        run: |
          sha256sum "${{ env.FILE_NAME }}-"* > "checksums.txt"
      - name: Create version file
        run: |
          echo "${{ env.TAG }}" > "version.txt"
      - name: Checkout ${{ env.SOFTWARE_NAME }} Small repository
        uses: actions/checkout@v4
        with:
          path: tools
          repository: ${{ env.REPO_SMALL }}
      - name: Create tag in ${{ env.SOFTWARE_NAME }} Small repository
        run: |
          cd tools
          if git rev-parse --quiet --verify "refs/tags/${{ env.TAG }}"; then
            echo "Tag already exists"
            exit 0
          else
            echo "Tag does not exist, creating"
            git tag "${{ env.TAG }}"
            git push --tags
          fi
      - name: Create Release
        if: github.ref == 'refs/tags/${{ env.TAG }}'
        uses: ncipollo/release-action@v1
        with:
          name: Small ${{ env.SOFTWARE_NAME }} ${{ env.TAG }}
          tag: ${{ env.TAG }}
          token: ${{ secrets.GITHUB_TOKEN }}
          draft: false
          prerelease: false
          artifacts: |
            ${{ env.FILE_NAME }}-*
            checksums.txt
            version.txt
          body: |
            Small ${{ env.SOFTWARE_NAME }} build ${{ env.TAG }}

            For a complete changelog go to https://github.com/${{ env.REPO }}/releases/tag/${{ env.TAG }}

            This release was created by:

            * Building a combined binary of `tailscale` and `tailscaled`
            * Using the build option `--extra-small`
            * Compressing the binary with UPX

            To use both programs, rename `tailscaled-OS-ARCH` to `tailscaled` and create a symbolic (`ln -sv tailscaled tailscale`)

這段GitHub Actions的工作流程文件,旨在建立一個更小的Tailscale二進制文件。這是一個自動化的流程,包含定期檢查Tailscale和精簡版Tailscale的最新版本,並在發現新版本時進行編譯、壓縮和發佈。以下是對該流程的詳細說明:

首先,在時間表和手動觸發條件下執行工作流程。時間表設定為每12小時執行一次(CRON表達式:’30 0/12 * * *’)。

環境變量部分定義了各種所需的參數,包括軟體名稱、文件名稱、版本庫等。

任務1:check-versions

這個任務檢查兩個版本庫中最新的發佈版本。

  1. Get latest Tailscale tag:使用GitHub API獲取最新的Tailscale標籤(tag)。
  2. Get latest Tailscale Small tag:同樣地,獲取精簡版Tailscale的最新標籤。

任務2:build

這個任務負責編譯最新版本的Tailscale。

  1. Checkout Tailscale repository:檢出Tailscale版本庫的最新標籤。
  2. Setup Go:設置Go語言環境。
  3. Download Go modules:下載Go模組。
  4. Cross-compile:交叉編譯Tailscale,生成不同平台的二進制文件。
  5. Upload built binary:上傳編譯後的二進制文件。

任務3:publish

這個任務負責壓縮和發佈編譯後的二進制文件。

  1. Get UPX latest version:獲取最新版本的UPX(用於壓縮二進制文件)。
  2. Download UPX:下載並設置UPX。
  3. Download built binaries:下載之前編譯好的二進制文件。
  4. Moving files:移動和重命名文件。
  5. Compress Binary with UPX:使用UPX壓縮二進制文件。
  6. Create checksums:創建文件校驗和。
  7. Create version file:創建版本文件。
  8. Checkout Tailscale Small repository:檢出精簡版Tailscale版本庫。
  9. Create tag in Tailscale Small repository:在精簡版Tailscale版本庫中創建標籤。
  10. Create Release:在GitHub上創建新的發佈版本,包含編譯和壓縮後的二進制文件。

這個工作流程通過自動化編譯、壓縮和發佈過程,確保Tailscale的最新版本能夠快速有效地交付給用戶。

本文是小U的「玩開源、從OpenWRT開始」系列文章的其中一篇。自從Ups-WR30U開源路由器得到大家的熱烈支持,小U也收到了客戶的各種使用OpenWRT的問題查詢,順勢就着手把大家會遇到的,想要用的,都集結成這個系列的文章,希望讓各位從OpenWRT體驗和玩樂開源系統。不需要靠AI,你就能讓路由器變得更”聰明“!

如果你還未有OpenWRT路由器,不妨從 Ups-WR30U 上手吧:

歡迎你的留言討論:

你可以一針見血

by Upsangel
Logo