记一次harbor数据库表异常恢复过程

问题概述

由于某些操作,导致客户的harbor镜像仓库无法进行镜像同步,经过排查,发现数据库异常,报如下错误:

error-1

error-2

问题排查

查看/var/log/postgresql.log日志,发现数据库频繁报如下错误:

1
2
STATEMENT: SELECT COUNT(1) FROM quota AS a JOIN quota_usage AS b ON a.id = b.id WHERE 1=1
ERROR: MultiXactId 29362 has not been created yet -- apparent wraparound

于是进入harbor的数据库进行查看。(harbor默认使用postgresql作为数据库)

1
2
3
4
5
6
docker exec -it harbor-db bash
postgres [ / ]$ psql
psql (9.6.21)
Type "help" for help.

postgres=#

进入registry数据库

1
\c registry

看到connected说明已经连上了该数据库

查看该数据库中的所有表

1
\dt
1
2
3
4
5
6
7
                 List of relations
Schema | Name | Type | Owner
--------+--------------------------+-------+----------
...(省略)
public | quota | table | postgres
public | quota_usage | table | postgres
...(省略)

先看一下表数据还在不在

1
select * from quota_usage;

发现报同样的错误

pg-1

那么这里就可以确定是这个表异常了导致harbor UI上显示异常,并且由于镜像同步时会去查询该表,所以导致查询的时候异常了无法进行镜像同步的操作。

问题处理

手动恢复该表即可

方法一

清空quota_usage表数据

1
truncate table quota_usage;

重新恢复数据

1
2
3
4
5
6
7
8
INSERT INTO quota_usage (id, reference, reference_id, used, creation_time, update_time)
SELECT id,
reference,
reference_id,
'{"storage": 0}',
creation_time,
update_time
FROM quota;

这里插入数据的命令可以参考harbor官方github代码

方法二

如果无法清空表,可以手动删除该表重新创建

删除quota_usage

1
drop table quota_usage;

新建quota_usage

1
2
3
4
5
6
7
8
9
10
CREATE TABLE quota_usage
(
id SERIAL PRIMARY KEY NOT NULL,
reference VARCHAR(255) NOT NULL,
reference_id VARCHAR(255) NOT NULL,
used JSONB NOT NULL,
creation_time timestamp default CURRENT_TIMESTAMP,
update_time timestamp default CURRENT_TIMESTAMP,
UNIQUE (reference, reference_id)
);

这里创建表的命令参考github代码

然后再恢复数据

1
2
3
4
5
6
7
8
INSERT INTO quota_usage (id, reference, reference_id, used, creation_time, update_time)
SELECT id,
reference,
reference_id,
'{"storage": 0}',
creation_time,
update_time
FROM quota;

恢复Harbor UI显示异常

当这一步操作完之后,镜像同步就可以操作了,但是UI还是显示异常

harbor-1

可以看到,右侧存储显示为0Byte,此时我们需要一个操作去触发更新存储的大小

这里也有两种方法恢复存储的显示

方法一(不推荐)

手动上传镜像到项目中,即可自动更新对应项目的存储大小

例如这里我们push一个busybox镜像到test项目中

docker-push

可以看到项目的存储大小自动更新为正确的值

harbor-2

但是此方法需要对每个项目都执行push操作,比较麻烦,推荐使用方法二

方法二(推荐)

对harbor执行垃圾清理即可

gc-1

由于垃圾清理会去扫描所有的项目,所以就能够触发存储大小的更新

等待垃圾清理完毕,就可以看到,项目的存储大小全都恢复正常了

harbor-3

postgresql相关使用技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 进入数据库
psql -h IP地址 -p 端口 -U 数据库名

# 查看数据库
\l

# 选择数据库
\c database

# 查看该某个库中的所有表
\dt

# 查看某个库中的某个表结构:
\d 表名

## 基于表1插入表2
INSERT INTO quota_usage (id, reference, reference_id, used, creation_time, update_time)
SELECT id,
reference,
reference_id,
'{"storage": 0}',
creation_time,
update_time
FROM quota;

## 基于表1 插入 表2 并筛选已存在的值
insert into quota_usage (id,reference,reference_id,creation_time,update_time,used) select q.id,q.reference,q.reference_id,q.creation_time,q.update_time,'{"storage": 0}' from quota q where not exists (SELECT id FROM quota_usage qu WHERE qu.id = q.id);

## 删除表
drop table quota_usage;

## 清空表数据
truncate table quota_usage;

## 创建表
CREATE TABLE quota_usage
(
id SERIAL PRIMARY KEY NOT NULL,
reference VARCHAR(255) NOT NULL,
reference_id VARCHAR(255) NOT NULL,
used JSONB NOT NULL,
creation_time timestamp default CURRENT_TIMESTAMP,
update_time timestamp default CURRENT_TIMESTAMP,
UNIQUE (reference, reference_id)
);

## 更新某个字段
update quota_usage set used='{"storage": 0}' where id=1;

## 删除某行数据
delete from quota_usage where id=1;