擺脫控制,用 Docker 自建 Gitea (Gogs) 線上代碼協作平臺

GitHub 被收購的事情實在是最近的熱點了,淺羽也發表過自己的看法了。社區的響應不一,大的項目自然是自己託管;中小項目,有的遷移到 GitLab 和 BitBucket,有的甚至遷移到了 SourceForge(實在是世道變了);很多個人也選擇遷移,當然還有些開發者不爲所動。除了遷移到其他平臺,還有一種選擇就是自建。在自建的方案上,其實 GitLab(對應 GitLab CE)和 BitBucket 都有開放原始碼的版本,但是對於大部分人來說也許太重了,而且對於伺服器的要求也不低。在這種情況下,基於 Go 語言的一個 GitHub 的開源實現 Gogs 就是一個非常好的選擇,而 Gitea 則是由於 Gogs 更新速度太慢 fork 出來的一個項目。

目前 Gogs 的開發基本已經停止,而 Gitea 仍在活躍更新。有意思的是 Gitea 目前仍未實現自託管在自己的平臺上託管的代碼似乎不用於正式開發,它的原始碼仍在 GitHub 上。

(後面還有:使用 Docker 搭建 Gitea、設定主機 SSH 透傳、從 Gogs 升級)

使用 Docker 搭建 Gitea

Gitea 是原生的 Go 應用,實際上搭建肯定是簡單的。然而追求更簡單是人類的天性,所以能用 Docker 的就決不用 Native,更何況 Gitea 還有官方 Docker Image 呢。

所以我們寫一個 docker-compose.yml:

version: '2'
services:
    code.rbq.zone:
        image: 'gitea/gitea:latest'
        ports:
            - '10022:22'
            - '127.0.0.1:10080:3000'
        volumes:
            - ./code.rbq.zone:/data
    restart: 'unless-stopped'

然後執行:

# docker-compose up -d

Gitea 就已經在運行了。那麼接下來要做的就是配一個 Nginx 的反代,淺羽就不再贅述,只給出一個參考:

server {
    listen 80;
    listen [::]:80;
    server_name code.rbq.zone;

    location / {
        return 301 https://$server_name$request_uri;
    }

    location /.well-known {
        root /var/www/html;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name code.rbq.zone;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/code.rbq.zone/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/code.rbq.zone/privkey.pem;
    include /etc/nginx/ssl.conf;

    client_max_body_size 10G;

    location / {
        proxy_pass http://localhost:10080;
        proxy_redirect off;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
    }
}

到這裏,訪問域名就可以看到 Gitea 的安裝頁面了。保持所有的參數不要動,直接完成安裝即可。

設定 SSH 透傳

用戶故事

說實話,這是只有在 Docker 搭建的時候才會遇到的問題。注意到前面,我們開放了容器的 22 端口並且映射到主機的 10022 端口沒有?這也就意味着當我們設定 Git Remote 的時候,不能優雅地使用

ssh://[email protected]/user/project.git

的形式,必須是

ssh://[email protected]:port/user/project.git

的形式。解決這個問題的方法,就是去做 SSH 透傳。那麼需求有了,怎麼做呢?

原理分析

由於整個過程使用 SSH 實現的,因此我們自然要去看一下容器裏面的 ~git/.ssh/authorized_key 檔案。它大概長成這樣:

# gitea public key
command="/app/gitea/gitea serv key-1 --config='/data/gitea/conf/app.ini'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3N......
# gitea public key
command="/app/gitea/gitea serv key-2 --config='/data/gitea/conf/app.ini'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3N......

發現問題了嗎?最開始帶着一個 command。這個 command 的作用是在 SSH 用戶連接的時候帶起這個二進制,並且傳給它正確的參數。那麼簡單的做法就是我們也在同樣的地方寫一個 Script,這樣所有到主機(Port 22)的 SSH 連接都會執行這個 Script,而它的作用就是把所有的參數透過 SSH 回給 Container(Port 10022)中的原始 Script。那麼思路有了,具體的作業流程就很清楚了。

設定透傳

首先我們先寫一個 Script 放到主機上的 /app/gitea/gitea,內容是:

#!/bin/sh
ssh -p 10022 -o StrictHostKeyChecking=no [email protected] \
"SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

這樣就達到我們的目的了。記得加可執行權限。

接着給主機增加一個 git 用戶,記下 UID 和 GID,比如淺羽這裏是 1001:1002。接下來需要讓 Container 中的 git 用戶也使用相同的 UID 和 GID,並且把 ~git/.ssh 資料夾掛載到 Container 中。於是我們的 docker-compose.yml 就變成了:

version: '2'
services:
    code.rbq.zone:
        image: 'gitea/gitea:latest'
        ports:
            - '10022:22'
            - '127.0.0.1:10080:3000'
        volumes:
            - ./code.rbq.zone:/data
            - /home/git/.ssh:/data/git/.ssh
    environment:
            - USER_UID=1001
            - USER_GID=1002
    restart: 'unless-stopped'

這樣重啓容器之後,還需要授權主機的 git 用戶到容器的 git 的 SSH 訪問:

su git -c ssh-add-key git@localhost -p 10022

做完之後就可以愉快地假裝 Gitea 在監聽主機的 22 端口啦。

番外:從 Gogs 升級

從 Gogs 已經常年不更新,所以遷移到 Gitea 能獲得更好的體驗。遷移的主要要注意的問題是找對目錄名。然而淺羽主要想提醒的是一個坑,在官方 Repository 上也有一個 Issue。從 Gogs 遷移的時候,需要先遷移到 Gitea 1.0.x 版本,然後再更新 Gitea,否則會持續報錯

Failed to initialize ORM engine: migrate: do migrate: Error 1146: Table ‘gogs.repo_unit’ doesn’t exist

問題的來源應該是在之後的 Gitea 更新了表名,從沿用 Gogs 的前綴變成了使用自己的前綴。更有意思的是,在討論串中 Pull Request #3359 更新了英文手冊的內容,然而作爲中國團隊開發和維護 Gitea,其中文手冊卻遲遲沒有提到這一點。

看樣子得等淺羽哪天有興趣了去更新一下…


發表於

發表在

在〈擺脫控制,用 Docker 自建 Gitea (Gogs) 線上代碼協作平臺〉中有 2 則留言

  1. 「pham」的個人頭像

    謝謝分享,一直沒弄懂使用gitea的built-in SSH server 和主機的SSH server之間的差別,你這樣講解就清楚多了。

    1. 「櫻川 浅羽」的個人頭像

      可能需要澄清一下:Gitea 的 built-in SSH server,並不是指 Gitea 的 container image 內的 sshd,而是指 Gitea 自身提供的一套 SSH 的伺服器實作。
      SSH 是一種協定而非具體的實作。Linux distro 中通常使用 OpenSSH Server 作爲 SSH 伺服端(SSH Daemon / sshd),但也有一些環境下會使用諸如 Dropbear 等替代。Gitea 也提供了一種自己實作的 SSH 伺服端,針對不方便使用系統內建的 sshd 或系統無內建 sshd 的場景使用,稱爲 built-in SSH server。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

此網站使用 Akismet 以減少垃圾留言。 瞭解你留言資料會被如何處理.