本文使用「署名 4.0 國際 (CC BY 4.0)」許可協定,歡迎轉載、或重新修改使用,但需要註明來源。 署名 4.0 國際 (CC BY 4.0)
本文作者: 蘇洋
建立時間: 2020年08月09日
統計字數: 8238字
閱讀時間: 17分鐘閱讀
本文鏈接: https://soulteary.com/2020/08/09/use-docker-to-build-a-stable-and-reliable-private-network-disk.html
前一陣 SIGIR 2020 國際資訊檢索研究和發展大會有一個有意思的需求,需要支援幾百位國內外學者能夠快速上傳自己的會議視訊,並支援對視訊進行快速的網路分發(線上播放)。
考慮到網路存取品質和檔案外連播放訴求,我們所熟知的成熟的國內網路硬碟服務被排除在外,又因爲國內存取海外網路硬碟服務不暢快,所以海外網路硬碟也被排除在外。我們之前常常使用的 SKYNAS (羣暉)映象因爲線上版本對人數有限制,所以也不能解決這個需求。
於是自建一個簡單的網路硬碟服務的需求也被提上了日程。
在做選型的時候,也遇到了一些客觀限制。
由於有外連播放訴求,需要讓檔案能夠直接對外提供服務,並需要考慮頻寬限制和儲存、流量成本,所以最好能夠將檔案上傳至物件儲存,或者直接使用 CDN 對外公開存取。最起碼能夠支援使用 API / CLI 進行同步。
由於存在多使用者視訊檔的上傳/更新管理,所以應用最好能夠支援 OAuth 授權自動建立賬號,或者支援 API / CLI 進行使用者建立,減少人爲幹預和「客服」環節。
由於我們需要同時提供全球使用者使用,所以程式最好還能夠根據地區額外提供不同的存取地址,讓使用者自主選擇近源存取,避免 CDN 排程出現意外狀況。
由於視訊上傳者來自全球各地,所以視訊內容需要後期審覈人員參與內容檢查,檔案至少要能夠提供完整的審覈列表。
綜合各種因素之後,我們選擇了 OwnCloud 的開源版本 NextCloud。
預設搭建一個 NextCloud 網路硬碟並不難,使用下面 下麪的 compose 設定,可以分分鐘啓動一個屬於你的網路硬碟。(慣例使用 Traefik)
version: "3.6"
services:
nextcloud:
image: nextcloud:19.0.1
restart: always
expose:
- 80
volumes:
# Linux 環境下使用
# - /etc/localtime:/etc/localtime:ro
# - /etc/timezone:/etc/timezone:ro
- ./data:/var/www/html/data:rw
extra_hosts:
- "nextcloud.lab.com:127.0.0.1"
- "nextcloud-cn.lab.com:127.0.0.1"
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.www-nextcloud.entrypoints=http"
- "traefik.http.routers.www-nextcloud.rule=Host(`nextcloud.lab.com`,`nextcloud-cn.lab.com`)"
- "traefik.http.routers.www-nextcloud.middlewares=https-redirect@file"
- "traefik.http.routers.ssl-nextcloud.entrypoints=https"
- "traefik.http.routers.ssl-nextcloud.tls=true"
- "traefik.http.routers.ssl-nextcloud.rule=Host(`nextcloud.lab.com`,`nextcloud-cn.lab.com`)"
- "traefik.http.routers.ssl-nextcloud.middlewares=content-compress@file"
- "traefik.http.services.www-nextcloud-backend.loadbalancer.server.scheme=http"
- "traefik.http.services.www-nextcloud-backend.loadbalancer.server.port=80"
networks:
traefik:
external: true
這裏例子中,我們使用 Traefik 系結了兩個域名到 NextCloud ,分別是:
nextcloud.lab.com
nextcloud-cn.lab.com
應用啓動之後,存取任意域名即可開始應用安裝,因爲要滿足「使用者自主選擇近源」站點存取,所以我們使用 nextcloud-cn.lab.com
進行安裝。
應用預設使用的數據庫爲 SQLite,可以滿足單人使用,但是在多人讀寫場景下,我們需要考慮數據安全,使用 MySQL 進行替換,在設定中新增下面 下麪的內容,重新啓動應用即可。
version: "3.6"
services:
nextcloud:
image: nextcloud:19.0.1
...
db:
image: mariadb
container_name: database
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
restart: always
volumes:
- ./db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=nextcloud
- MYSQL_PASSWORD=nextcloud
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
networks:
- traefik
networks:
traefik:
external: true
[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-06dBT4R5-1596980561218)(https://attachment.soulteary.com/2020/08/09/database-setting.png)]
如果你在安裝介面勾選了安裝辦公應用將能夠看到應用安裝介面。
一切就緒之後,會看到歡迎介面。
[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-NH404hr4-1596980561221)(https://attachment.soulteary.com/2020/08/09/welcome.png)]
爲了保障應用的健康執行,我們需要新增健康檢查指令碼,讓應用能夠在異常退出的時候嘗試自我恢復。
因爲程式除了會儲存檔案日誌外,還會持續在標準輸出中產生日誌,所以我們也需要對其標準輸出日誌進行限制,避免磁碟空間雙倍浪費。
version: "3.6"
services:
nextcloud:
image: nextcloud:19.0.1
restart: always
...
healthcheck:
test: ["CMD-SHELL", "curl -f localhost/status.php || exit 1"]
interval: 5s
retries: 12
logging:
driver: "json-file"
options:
max-size: "1m"
db:
image: mariadb
...
healthcheck:
test: mysqladmin ping -h localhost -p$$MYSQL_ROOT_PASSWORD && test '0' -eq $$(ps aux | awk '{print $$11}' | grep -c -e '^mysql$$')
interval: 5s
retries: 12
logging:
driver: "json-file"
options:
max-size: "1m"
不論是進入容器拷貝出當前設定,還是使用 docker cp
命令將設定直接複製到宿主機,當程式安裝完畢之後,預設的設定會類似這樣 config/config.php
:
<?php
$CONFIG = array (
'htaccess.RewriteBase' => '/',
'memcache.local' => '\\OC\\Memcache\\APCu',
'apps_paths' =>
array (
0 =>
array (
'path' => '/var/www/html/apps',
'url' => '/apps',
'writable' => false,
),
1 =>
array (
'path' => '/var/www/html/custom_apps',
'url' => '/custom_apps',
'writable' => true,
),
),
'instanceid' => 'oc12d1pw63hc',
'passwordsalt' => 'B9Wt09NV2wWOCGr+bFCOelMrQ1nmiJ',
'secret' => '6kHNmytBYJUPp3ee9L0NYBE+xnGtPTqlzAAUQ4sjkCNjg04c',
'trusted_domains' =>
array (
0 => 'nextcloud-cn.lab.com',
),
'datadirectory' => '/var/www/html/data',
'dbtype' => 'mysql',
'version' => '19.0.1.1',
'overwrite.cli.url' => 'http://nextcloud-cn.lab.com',
'dbname' => 'nextcloud',
'dbhost' => 'database',
'dbport' => '',
'dbtableprefix' => 'oc_',
'mysql.utf8mb4' => true,
'dbuser' => 'nextcloud',
'dbpassword' => 'nextcloud',
'installed' => true,
);
當有了設定之後,我們接下來就可以繼續進行定製化設定了。
應用預設只支援單個域名存取,當我們使用我們預期使用的 CDN 域名或者其他區域的域名進行存取的時候,會看到「通過不被信任的域名存取」的警告,並無法存取相關資原始檔和網路硬碟介面。
[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-qtXAonOS-1596980561222)(https://attachment.soulteary.com/2020/08/09/trusted_domains.png)]
這時我們需要修改組態檔中的 trusted_domains 欄位,將所有域名新增進去:
<?php
$CONFIG = array (
'trusted_domains' =>
array (
0 => 'nextcloud-cn.lab.com',
1 => 'nextcloud.lab.com',
),
...
然後將組態檔掛載到容器中:
version: "3.6"
services:
nextcloud:
image: nextcloud:19.0.1
restart: always
expose:
- 80
volumes:
...
- ./config.php:/var/www/html/config/config.php:rw
...
現如今的不論國內國外, CDN 產品早已支援「全站加速」模式,所以我們只需要將 CDN 加速域名和「區域存取」域名進行區分,即可解決「上/下行頻寬低成本擴容」、「區域加速存取」的需求。
這裏還可以做進一步優化,將使用者根據區域進行分堆,然後將上傳檔案從不同的區域分別同步於 OSS,再通過 OSS 搭配不同區域的 CDN 進行區域加速存取(推薦使用)。
一切就緒後,我們啓動應用,會發現程式無法正常執行,臨時去掉健康檢查後,我們會看到下面 下麪的提示。
[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-GB6Wv4O7-1596980561223)(https://attachment.soulteary.com/2020/08/09/dir_permissions.png)]
解決方法也很簡單,無需修改容器和啓動指令碼,只需要在組態檔中再多新增一行內容:
<?php
$CONFIG = array (
'config_is_read_only' => true,
然後再次啓動應用,使用非安裝域名存取,可以看到正常的登錄介面。
應用會在使用者建立第一次登錄時初始化使用者目錄。並在目錄中準備使用手冊、範例檔案,對於一場嚴肅的學術會議而言,這些內容最好去掉,可以省掉一些不必要的麻煩。
這裏需要修改設定爲:
<?php
$CONFIG = array (
'skeletondirectory' => '',
...
之前的文章中提到過我們的 HTTPS 最佳實踐,將 HTTPS 服務部分挪至 SLB 閘道器處統一管理,應用一律提供 HTTP 介面,所以這裏需要多新增一句設定,讓服務支援被 HTTPS 閘道器進行代理:
<?php
$CONFIG = array (
'overwriteprotocol' => 'https',
...
NextCloud 支援 OAuth Server 模式,但是卻不支援 OAuth Client 模式,所以我們並不能直接將其和我們現有的 OAuth Server 關聯在一起,所以這裏就要尋找支援「程式設計式」操作使用者的介面,或者改一個介面出來了。
慶幸的是程式自帶一個 CLI ,支援操作使用者資源
occ user:add
occ user:delete
...
這裏可以使用你熟悉的語言,做一個 OAuth Proxy Server,在容器外操作 NextCloud 關鍵命令如下:
# 建立使用者
OC_PASS=GENERATE_BY_YOUR_APP php /var/www/html/occ user:add 'username' --password-from-env --group='username' --display-name='username'
# 刪除使用者
php /var/www/html/occ user:delete username
爲了方便「伸手黨」,這裏將上面的設定彙總,最終的 compose 設定如下:
version: "3.6"
services:
nextcloud:
image: nextcloud:19.0.1
restart: always
expose:
- 80
volumes:
# Linux 環境下使用
# - /etc/localtime:/etc/localtime:ro
# - /etc/timezone:/etc/timezone:ro
- ./data:/var/www/html/data:rw
- ./config.php:/var/www/html/config/config.php:rw
extra_hosts:
- "nextcloud.lab.com:127.0.0.1"
- "nextcloud-cn.lab.com:127.0.0.1"
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.www-nextcloud.entrypoints=http"
- "traefik.http.routers.www-nextcloud.rule=Host(`nextcloud.lab.com`,`nextcloud-cn.lab.com`)"
- "traefik.http.routers.www-nextcloud.middlewares=https-redirect@file"
- "traefik.http.routers.ssl-nextcloud.entrypoints=https"
- "traefik.http.routers.ssl-nextcloud.tls=true"
- "traefik.http.routers.ssl-nextcloud.rule=Host(`nextcloud.lab.com`,`nextcloud-cn.lab.com`)"
- "traefik.http.routers.ssl-nextcloud.middlewares=content-compress@file"
- "traefik.http.services.www-nextcloud-backend.loadbalancer.server.scheme=http"
- "traefik.http.services.www-nextcloud-backend.loadbalancer.server.port=80"
# healthcheck:
# test: ["CMD-SHELL", "curl -f localhost/status.php || exit 1"]
# interval: 5s
# retries: 12
logging:
driver: "json-file"
options:
max-size: "1m"
db:
image: mariadb
container_name: database
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
restart: always
volumes:
- ./db:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=nextcloud
- MYSQL_PASSWORD=nextcloud
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
networks:
- traefik
healthcheck:
test: mysqladmin ping -h localhost -p$$MYSQL_ROOT_PASSWORD && test '0' -eq $$(ps aux | awk '{print $$11}' | grep -c -e '^mysql$$')
interval: 5s
retries: 12
networks:
traefik:
external: true
NextCloud 最終使用的設定內容:
<?php
$CONFIG = array (
'htaccess.RewriteBase' => '/',
'memcache.local' => '\\OC\\Memcache\\APCu',
'apps_paths' =>
array (
0 =>
array (
'path' => '/var/www/html/apps',
'url' => '/apps',
'writable' => false,
),
1 =>
array (
'path' => '/var/www/html/custom_apps',
'url' => '/custom_apps',
'writable' => true,
),
),
'instanceid' => 'oc12d1pw63hc',
'passwordsalt' => 'B9Wt09NV2wWOCGr+bFCOelMrQ1nmiJ',
'secret' => '6kHNmytBYJUPp3ee9L0NYBE+xnGtPTqlzAAUQ4sjkCNjg04c',
'trusted_domains' =>
array (
0 => 'nextcloud-cn.lab.com',
1 => 'nextcloud.lab.com',
),
'config_is_read_only' => true,
'default_language' => 'en',
'datadirectory' => '/var/www/html/data',
'dbtype' => 'mysql',
'version' => '19.0.1.1',
'overwrite.cli.url' => 'http://nextcloud-cn.lab.com',
'dbname' => 'nextcloud',
'dbhost' => 'database',
'dbport' => '',
'dbtableprefix' => 'oc_',
'mysql.utf8mb4' => true,
'dbuser' => 'nextcloud',
'dbpassword' => 'nextcloud',
'installed' => true,
);
NextCloud 是一款值得深挖的網路硬碟程式,設定非常靈活,應用支援的設定項也比較合理。
或許有一天,我會使用它替換掉正在使用的 SkyNAS 和一些臨時性的「圖牀/網路硬碟」吧。
–EOF
我現在有一個小小的折騰羣,裏面聚集了一些喜歡折騰的小夥伴。
在不發廣告的情況下,我們在裏面會一起聊聊軟體、HomeLab、程式設計上的一些問題,也會在羣裡不定期的分享一些技術沙龍的資料。
喜歡折騰的小夥伴歡迎掃碼新增好友。(請註明來源和目的,否則不會通過審覈)