nextcloud誤刪檔案,還原失敗

慘,我的錯。

解決方式:
1. 到nextcloud該使用者目錄,找出<user>/files_trashbin/files 複製到 <user>/files
2. 執行 php occ files:scan –all

若出現 Exception during scan: “files/xxx”) is locked 則進入資料庫,刪除資料表資料 DELETE FROM oc_file_locks WHERE 1;

刪除之後,再次執行 php occ files:scan –all 還原檔案後,原本分享的狀態可能會不見,要自己重新設定了。

nextcloud還原甘苦談

幾天前我想把nextcloud 25.0.11 升級到 26.0.5 ,這麼多年來從18版一路高歌升到25版,都沒遇到問題,這次卻栽了無法升級,原因卡在資料庫那段,這問題似乎無解,在不想等待的情況下,我決定還原回去。

2023/9/22 發現我25.0.11升級會出錯,官方馬上釋出25.0.12 ,但我升級還是失敗,原來資料庫mariadb 10.3.2版本有問題,我直接docker pull mariadb:10.3 後面不接了,這樣就從25->26能升級成功

還原步驟:

我先將restic 所備份最新版nextcloud備份的snapshot掛載起來,接著停用目前的資料庫(mariadb 10.3.2 多個容器共用);再將nextcloud的相關volume、資料庫還原,結果失敗,失敗原因不明,docker看log也看不出來,web登入出現「Nextcloud the server was unable to complete your request」錯誤,真真急死人;後來爬文,歸納問題可能有三種:目錄權限沒設定好、資料庫問題與ldap無法連線。

不屈不撓檢查才發現原來是我偷懶,當初所有容器所用到的資料庫都統一讓唯一的資料庫容器管理,現在要還原,我停止資料庫容器後,只有還原其中nextcloud的資料庫,其他資料庫不還原,導致重新啟動資料庫容器時,nextcloud資料庫居然有一些資料表損毀(ENGINE=InnoDB)。

好在損毀的資料表是oc_authtoken,只要重建就好了,但是重建失敗,mysql會說該資料表已在,但是query時,mysql又說沒有該資料表,網上有位苦命工程師說建資料表時,遇到這情形,請改用ENGINE=MyISAM格式就能成功,我照著做之後,nextcloud又恢復往日雄風還原成功,但重建的資料表MyISAM跟其他使用InnoDB的資料表格格不入,以後要找一天將資料庫修復才行。
這故事告訴我們,還原資料庫容器要全部都還原,不可只還原其中一部分資料庫。

CREATE TABLE `oc_authtoken` (   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,   `uid` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '',   `login_name` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '',   `password` longtext COLLATE utf8_bin,   `name` longtext COLLATE utf8_bin NOT NULL,   `token` varchar(200) COLLATE utf8_bin NOT NULL DEFAULT '',   `type` smallint(5) unsigned NOT NULL DEFAULT '0',   `remember` smallint(5) unsigned NOT NULL DEFAULT '0',   `last_activity` int(10) unsigned NOT NULL DEFAULT '0',   `last_check` int(10) unsigned NOT NULL DEFAULT '0',   `scope` longtext COLLATE utf8_bin,   `expires` int(10) unsigned DEFAULT NULL,   `private_key` longtext COLLATE utf8_bin,   `public_key` longtext COLLATE utf8_bin,   `version` smallint(5) unsigned NOT NULL DEFAULT '1',   `password_invalid` tinyint(1) NOT NULL DEFAULT '0',   PRIMARY KEY (`id`),   UNIQUE KEY `authtoken_token_index` (`token`),   KEY `authtoken_last_activity_idx` (`last_activity`),   KEY `authtoken_uid_index` (`uid`),   KEY `authtoken_version_index` (`version`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

以上重建table失敗

CREATE TABLE `oc_authtoken` (   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,   `uid` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '',   `login_name` varchar(64) COLLATE utf8_bin NOT NULL DEFAULT '',   `password` longtext COLLATE utf8_bin,   `name` longtext COLLATE utf8_bin NOT NULL,   `token` varchar(200) COLLATE utf8_bin NOT NULL DEFAULT '',   `type` smallint(5) unsigned NOT NULL DEFAULT '0',   `remember` smallint(5) unsigned NOT NULL DEFAULT '0',   `last_activity` int(10) unsigned NOT NULL DEFAULT '0',   `last_check` int(10) unsigned NOT NULL DEFAULT '0',   `scope` longtext COLLATE utf8_bin,   `expires` int(10) unsigned DEFAULT NULL,   `private_key` longtext COLLATE utf8_bin,   `public_key` longtext COLLATE utf8_bin,   `version` smallint(5) unsigned NOT NULL DEFAULT '1',   `password_invalid` tinyint(1) NOT NULL DEFAULT '0',   PRIMARY KEY (`id`),   UNIQUE KEY `authtoken_token_index` (`token`),   KEY `authtoken_last_activity_idx` (`last_activity`),   KEY `authtoken_uid_index` (`uid`),   KEY `authtoken_version_index` (`version`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

改成MyISAM就成功重建table

Podman容器備份

備份手法很多,最簡單就是整個主機定期做快照,加上離線備份,這樣就沒甚麼問題了。
但是我最近思考一件事,若我們想將podman上的容器軟體改成docker或是改回實體機使用, 這樣就比較棘手了。

我自己因為遇到podman storage要改不同位置,依照我以前文章更改storage路徑的做法再改一次路徑,podman居然整個崩潰了,搞到還要執行 podman system reset 才能恢復使用,但是reset後,原本的容器軟體全部出問題,若我有乖乖針對容器軟體進行備份,就不會這麼難堪了,最後只能整個VM還原。

所以我想了又想,覺得還是要直接備份容器的「images」與「volumes」。

爬文爬了幾天,修修改改後,我終於融合一個能備份容器以及其volume的腳本,很適合容器母體(host)為Linux使用,該腳本會備份所有的images、volume與執行容器指令。

按此下載podman容器備份腳本(需要輸入guest/guest,下載後請記得修改開頭幾行之備份路徑)

執行備份,先備份images,然後掃描volume,查詢volume若與images互通款取,也一併備份,最後追蹤最初容器執行之指令,也一併圈禁備份起來。

備份後,每個目錄將包含容器使用之images、volumes與rush.sh(啟用指令)

後來我發現volume很適合restic增量備份方式,可以減少我這腳本full backup很耗空間的作法。

因此又改寫了一份針對volume能增量備份的腳本,兩種可以同時搭配使用,例如每個月一次full backup,剩下的定期每日或每周進行restic備份。

按此下載podman容器使用restic備份腳本(需要輸入guest/guest,下載後請記得修改開頭幾行之備份路徑)

備份軟體restic 進行forget時, 出現pack 錯誤

因為要減少備份snapshots數量, 因此執行forget + prune 瘦身指令

restic forget --keep-last 3 --prune -r <repository>

但是出現某某snapshots中有某些 pack missing 錯誤

some pack files are missing from the repository, getting their blobs from the repository index: [c3622b51e9c118521af211cd3a98f580f5902824b6b665e62d61061acedbe71d]

很慘, 於是只好找一下該 pack, 看看是哪些檔案不見

restic find --pack c3622b51e9c118521af211cd3a98f580f5902824b6b665e62d61061acedbe71d -r <repository>

最後終於找到某些snapshots遺失哪些檔案, 但依舊無法解決問題, 現主時只好忍痛直接將有問題的snapshots移除,等以後再研究解決方案

restic -r <repository>  forget <snapshot id>

2023/2/8 後續

儘管forget該snapshots, 以為成功了.
但是再次執行備份,還是出現這個pack missing錯誤.
於是爬文, 發現必須先刪除相關packs , 再重跑以下步驟:
restic rebuild-index
restic backup –force
restic prune

詳細處理方式請查看 https://feeding.cloud.geek.nz/posts/removing-corrupted-data-pack-restic-backup/ 或是我下載好該網頁內容


解決 oracle 11G 出現的ORA-01555 問題

oracle我很不熟, 但在公司使用exp指令備份oracle 11g資料時, 遇到以下錯誤訊息

EXP-00056: ORACLE error 1555 encountered
ORA-01555: snapshot too old: rollback segment number  with name "" too small
ORA-22924: snapshot too old

爬了文, 解法有三, 我從最簡單的解法開始, 第一種,第二種都失敗, 直到第三種方式才成功.
我建議從第三種方式開始解,該方法不需修改系統設定, 影響較小.

  • 方法一
    修改 undo_retention秒數, 把時間拉大,
    **我是用備份的方式測試, 發現改了之後還是一樣出錯
SQL>  show parameter undo;

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
undo_management                      string      AUTO
undo_retention                       integer     21600
undo_tablespace                      string      UNDOTBS1
SQL> ALTER SYSTEM SET UNDO_RETENTION = 43200;
SQL>
System altered.

SQL>
  • 方法二
    直接加大undo tablespace ,
    這要小心點, 有很多眉角, 似乎牽涉到小心舊undo tablesapce 是否已呈現”OFFLINE”, 且新的undo tablespace 呈現”ONLINE’
# 新增undo tablespace 
create undo tablespace UNDOTBS2 datafile '/.../oradata/undotbs02.dbf' size 30000M autoextend on next 100m maxsize unlimited;
#生效
alter system set undo_tablespace = UNDOTBS2 scope=both;

#查看狀態
select tablespace_name, status, count(*) from dba_rollback_segs group by tablespace_name, status; 
select status,segment_name from dba_rollback_segs where status not in ('OFFLINE') and tablespace_name='UNDOTBS1';
select tablespace_name, status, count(*) from dba_rollback_segs group by tablespace_name, status;
#確認舊的已是offline , 就可以刪掉舊的
Drop tablespace UNDOTBS1 including contents and datafiles;
#再次查看是否已變成新的undo tablespace
show parameter undo
select tablespace_name tablespace, status, sum(bytes)/1024/1024 sum_in_mb, count(*) counts
from dba_undo_extents
group by tablespace_name, status order by 1,2;
  • 方法三
# 先建立一個資料表, 用來儲存有問題的資料ID(ROWID)
SQL> CREATE TABLE CORRUPTED_ROWS (CORRUPTED_ROWID ROWID, ERROR_NUMBER NUMBER);

Table created.

接下來將有錯誤資料表table schema , 欄位類型為clob , blob 欄位列出來, 問題一定在這幾個欄位之中

# 以下紅色部分請改為有問題資料表與欄位名稱, 若沒有出現錯誤, 就換下一個欄位(fieldname)試試看
SET TIMING ON
DECLARE 
   ERROR_1578  EXCEPTION;
   ERROR_1555  EXCEPTION;
   ERROR_22922 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ERROR_1578,  -1578);
   PRAGMA EXCEPTION_INIT(ERROR_1555,  -1555);
   PRAGMA EXCEPTION_INIT(ERROR_22922, -22922);
   N NUMBER;
BEGIN
   FOR ROW IN (SELECT ROWID, fieldname FROM user.table)
   LOOP
      BEGIN
         N:=DBMS_LOB.INSTR(ROW.fieldname, HEXTORAW('889911'));
	  EXCEPTION
         WHEN ERROR_1578  THEN 
              INSERT INTO CORRUPTED_ROWS VALUES (ROW.ROWID, 1578);
			  COMMIT;
         WHEN ERROR_1555  THEN 
              INSERT INTO CORRUPTED_ROWS VALUES (ROW.ROWID, 1555);
			  COMMIT;
         WHEN ERROR_22922 THEN 
              INSERT INTO CORRUPTED_ROWS VALUES (ROW.ROWID, 22922);
			  COMMIT;
       END;
   END LOOP;
END;
/

接下來查詢是否真有問題資料

SELECT * FROM CORRUPTED_ROWS;
#執行以下這行指令應該要出錯
SELECT fieldname FROM user.table WHERE ROWID IN (SELECT CORRUPTED_ROWID FROM CORRUPTED_ROWS);

清空有問題的資料欄位

#若欄位型態是clob就用empty_clob(), 若為blob,就改成empty_blob(), 有幾個欄位出問題就清空幾個
update xxx.table set fieldname = empty_clob()where ROWID IN (SELECT CORRUPTED_ROWID FROM CORRUPTED_ROWS);

若不敢清空, 可以跳過有問題的資料備份

exp system@yourinstance BUFFER=81920 file=/tmp/backup.dmp  tables=user.table QUERY=\"WHERE rowid NOT IN \(SELECT CORRUPTED_ROWID FROM CORRUPTED_ROWS\)\"

參考資料

https://www.modb.pro/db/47021

Linux 掛載nfs , cifs (smb) 讓 restic 備份, 遇到備份效率緩慢問題

我已經使用 restic 做離線備份半年了 , 效果還不錯, 但是遇到掛載nfs的資料, 效率就有點差了,
當然備份效率差的原因之一, 是我暴力備份docker or podman 裡面storage 資料, 那邊檔案超級多, 又是透過 nfs 方式備份, 就算restic 每次只做差異性, 還是很慢.

後來想想, 我的虛擬伺服器 proxmox ve 也有遇到掛載nfs效率問題, 解法就是使用soft link, 所以自己使用mount指令掛載nfs應該也是可以改用soft方式, 以下列出一些心得

  • cifs 掛載可用cache=loose
mount -t cifs -o username=xxx,password=xxx,domain=yyy,ro,cache=loose  ...
  • nfs 可用soft
mount -t nfs -o ro,soft ...
#查看
nfsstat -m

使用restic做離線備份, 充滿儀式感,老闆也開心

前陣子發現restic備份的好處, 尤其拿來當離線備份.

IT同事進行離線備份時, 有意無意被老闆路過, 看到滿滿備份畫面咻咻跑來跑去, 充滿了儀式感.
老闆開心, IT同仁也有成就感.


restic指令備份, 不夠自動化, 為了讓IT同事做少少的工作, 又讓IT同事與其他路過的同事覺得好專業, 因此寫了簡單的restic backup script , 可以協助掛載來源目錄, 掛載後,進行restic備份.

檔案請到這裡下載, 下載後請看readme.md檔案

centos 7 啟用NFS server

服務器端啟用nfs server

yum install nfs-utils

## 分享目錄
mkdir -p /mnt/docker
vi etc/exports
=================
#只讀ro,  10.192.130.4可使用
/mnt/docker      10.192.130.4(ro,sync,no_root_squash,no_all_squash)
#讀寫rw,  10.192.130.0/24網段可使用
/mnt/docker      10.192.130.0/24(rw,sync,no_root_squash,no_all_squash)
=================

#啟用
firewall-cmd --zone=public --add-service=nfs --permanent

## rockylinux
#firewall-cmd --add-service={nfs,nfs3,mountd,rpc-bind} --permanent

firewall-cmd --reload
systemctl enable nfs 
systemctl start nfs

## rockylinux
#systemctl enable nfs-server rpcbind
#systemctl start nfs-server rpcbind

#查看狀態(windows,linux通用)
showmount -e localhost

用戶端(linux)

mount -t nfs <server ip>:/mnt/docker  <本地要掛載的目錄>
或是
mount -t nfs4 <server ip>:/mnt/docker  <本地要掛載的目錄>

用戶端(windows)

mount -o anon  \\ip\mount_path  z:
1 2 3