0%

事情源于我要给新装的Ubuntu安装Python3.10, 结果在官网居然没有发现安装包

找了半天反正没找到, 只有源码没有发行版

没辙, 从源码编译安装吧

安装依赖

1
2
apt update
apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev

下载对应版本的Python, 例如这里是3.10.8

1
wget https://www.python.org/ftp/python/3.10.6/Python-3.10.8.tgz

解压

1
tar -xf Python-3.10.*.tgz

检查配置

1
2
cd Python-3.10.*/
./configure --enable-optimizations

编译

1
make -j $(nproc)

安装到/usr/bin/python

1
make altinstall

https://computingforgeeks.com/how-to-install-python-on-ubuntu-linux-system/

docker-compose 使用3.7版本,然后加入 deploy,例如:

1
2
3
4
5
6
7
8
9
10
version: "3.7"
services:
redis:
image: redis:alpine
container_name: testredis
deploy:
resources:
limits:
cpus: '0.50'
memory: 500M

限制 CPU 使用率 50%, 500MB 内存

需要注意的是,启动时需增加 –compatibility 选项

1
docker-compose --compatibility up -d

否则会报错

1
WARNING: Some services (mysql, rsnmp) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.

注:

–compatibility是docker-compose 1.20.0加入,主要目的就是用来将deploy中的数据限制、replicas与重启策略直接转译为version 2的语法

https://www.cnblogs.com/yjt1993/p/12402394.htm
https://www.jianshu.com/p/ba5518476057

笔者使用 Docker 部署了一个 Jenkins 镜像,最近升级镜像版本时遇到问题。

现在记录解决方案

java.lang.NoClassDefFoundError: org/jruby/javasupport/proxy/InternalJavaProxy

参考该链接解决

https://community.jenkins.io/t/jenkins-war-update-caused-issue-when-ruby-runtime-plugin-is-installed/3282

There may be deprecated plugins installed on your system that need to be removed.

The Jenkins project stopped distributing the Ruby runtime plugin, the Python runtime plugin, and plugins that depend on those two plugins in January 2022. You can find a detailed list of plugins and more information in the blog post:

大意是旧版本包含了已经弃用的插件。

1
Ruby runtime plugin

这个插件在 2.373 版本已经弃用了,需要卸载之前安装的该插件。

启动旧版本docker镜像后,我们发现在控制台面板无法卸载。

解决方案是,进入到$JENKINS_HOME/plugins目录,然后移除ruby-runtime*

即这两个文件

然后更新镜像版本,一切正常

前言

在计算机科学中, 字符串搜索是一个很常见的任务. 给定两个字符串A和B, 例如

1
2
A -> ABC ABCDAB ABCDABCDABDE
B -> ABCDABD

我们怎么知道 A 是否包含有 B ?

有很多种算法可以完成这个任务, Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)是比较著名的一种.

这个算法由高德纳(Donald Knuth)和沃恩·普拉特(英语:Vaughan Pratt)在1974年构思,同年詹姆斯·H·莫里斯(英语:James H. Morris)也独立地设计出该算法,最终三人于1977年联合发表.

没错! 起头的那个 K 就是高德纳老爷子

该算法的巧妙之处在于, 一个词在不匹配时本身就包含足够的信息来确定下一个匹配可能的开始位置,此算法利用这一特性以避免重新检查先前配對的字符。(来自维基百科)

emmm….

我觉得,上面的话似乎不太好懂,我个人的理解是,该算法设计了一种方法,利用已经匹配过的信息,来减少重新匹配的次数。

以下的文章基于阮一峰的网络日志,特地记录了下学习的过程,以加强理解。

算法流程

如上图,首先字符串“BBC ABCDAB ABCDABCDABDE”的第一个字符与搜索词“ABCDABD”的第一个字符进行比较,因为B与A不匹配,所以我们将搜索词后移一位。

因为B与A不匹配,我们继续后移。

如此直到字符串有一个字符与搜索词的第一个字符匹配到为止。

接着比较字符串和搜索词的下一个字符,还是相同。

直到字符串有一个字符,与搜索词对应的字符不相同为止。

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把”搜索位置”移到已经比较过的位置,重比一遍。

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是”ABCDAB”。KMP算法的想法是,设法利用这个已知信息,不要把”搜索位置”移回已经比较过的位置,继续把它向后移,这样就提高了效率。

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

已知空格与D不匹配时,前面六个字符”ABCDAB”是匹配的。查表可知,最后一个匹配字符B对应的”部分匹配值”为2,因此按照下面的公式算出向后移动的位数

1
移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2(”AB”),对应的”部分匹配值”为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

因为空格与A不匹配,继续后移一位。

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

如何计算部分匹配表

首先,要了解两个概念:”前缀”和”后缀”。 “前缀”指除了最后一个字符以外,一个字符串的全部头部组合;”后缀”指除了第一个字符以外,一个字符串的全部尾部组合。

“部分匹配值”就是”前缀”和”后缀”的最长的共有元素的长度。以”ABCDABD”为例,

字符串 前缀 后缀 最长共有元素 最长共有元素的长度
A 空集 空集 0
AB A B 0
ABC A, AB BC, C 0
ABCD A, AB, ABC BCD, CD, D 0
ABCDA A, AB, ABC, ABCD BCDA, CDA, DA, A A 1
ABCDAB A, AB, ABC, ABCD, ABCDA BCDAB, CDAB, DAB, AB, B AB 2
ABCDABD A, AB, ABC, ABCD, ABCDA, ABCDAB BCDABD, CDABD, DABD, ABD, BD, D 0

“部分匹配”的实质是,有时候,字符串头部和尾部会有重复。比如,”ABCDAB”之中有两个”AB”,那么它的”部分匹配值”就是2(”AB”的长度)。搜索词移动的时候,第一个”AB”向后移动4位(搜索串长度-部分匹配值),就可以来到第二个”AB”的位置。

Python 实现

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from typing import List


def KMP(text: str, pattern: str, search_all: bool = True) -> List[int]:
"""KMP字符串搜索算法"""

t_len = len(text)
p_len = len(pattern)

# 模式串长度小于待搜索的字符串
if t_len < p_len:
return []

i = 0
result = []
while i < t_len:
is_match = True
# 匹配最后一部分时, 起始坐标加上模式串的
# 长度大于文本长度时, 注定匹配失败, 所以
# 不用多此一举匹配最后部分
if i + p_len > t_len:
break

for j in range(len(pattern)):
if text[i + j] != pattern[j]:
# 第一位就不匹配
if j == 0:
is_match = False
i += 1
break
# 其余位不匹配时, 计算偏移值(对应的部分匹配值)
is_match = False
p_offset = partial_match(pattern[:j])
# 移动位数 = 已匹配的字符数 - 偏移值(对应的部分匹配值)
i += j - p_offset
break

if is_match:
result.append(i)
if not search_all:
break
i += p_len - partial_match(pattern)

return result

def partial_match(p: str) -> int:
"""计算部分匹配值

例如:
in: "ABCDAB"
out: 2

假设我们输入"ABCDAB", 则可知道该字符串的长度为 6, 最大下标(max_index)为 5

字符: A B C D A B
下标: 0 1 2 3 4 5

前缀和后缀的可选范围

Prefix: 0 1 2 3 4
Suffix: 1 2 3 4 5

于是我们知道

i prefix suffix diffrence(前缀和后缀的差值)
-
0 0 5 5
1 0, 1 4, 5 4
2 0, 1, 2 3, 4, 5 3
3 0, 1, 2, 3 2, 3, 4, 5 2
4 0, 1, 2, 3, 4 1, 2, 3, 4, 5 1

我们最多需要进行 5 次循环, i 表示当前循环的次数, prefix 和 suffix 分别
对应了该次循环时每个字符的下标, diffrence 表示了该次循环时, prefix 和
suffix 对应的下标的差值, 显然可知

diffrence = max_index - i

我们需要取最长的共有元素的长度, 很显然我们从大到小搜索效率最高, 我们只需要
搜索到一个有效值, 该值即是最优解. 对应上图即为从下往上搜索.
"""

if len(p) == 1:
return 0

max_macth_length = 0
max_index = len(p) - 1
i = max_index - 1

# 从大到小搜索仅需要搜索到一个即是最优值
while i > -1:
is_match = True
diff = max_index - i
for j in range(i + 1):
if p[j] != p[j + diff]:
is_match = False
break
if is_match:
max_macth_length = i + 1
break
i -= 1
return max_macth_length

测试

1
2
3
text = "ABC ABCDAB ABCDABCDABDEABCDABD"
pattern = "ABCDABD"
print(KMP(text, pattern))

输出

1
[15, 23]

参考链接

https://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

ERROR odoo odoo.modules.loading: Database odoo not initialized

在使用 docker-compose 部署 odoo 时报错

1
ERROR odoo odoo.modules.loading: Database odoo not initialized, you can force it with -i base

解决方案

数据库容器增加环境变量

1
POSTGRES_DB=postgres

例如

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
version: '3.1'

services:

odoo:
image: odoo:15.0
depends_on:
- postgres
ports:
- "8069:8069"
volumes:
- ./odoo_web_data:/var/lib/odoo
- ./config/odoo.conf:/etc/odoo/odoo.conf
- ./addons:/mnt/extra-addons
environment:
- HOST=postgres
- USER=odoo
- PASSWORD=xxxxxxxxx

postgres:
image: postgres:15rc2-alpine3.16
environment:
- POSTGRES_DB=postgres
- POSTGRES_PASSWORD=xxxxxxxxx
- POSTGRES_USER=odoo
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- ./odoo_db_data:/var/lib/postgresql/data/pgdata

https://github.com/odoo/odoo/issues/27447

https://github.com/lefilament/ansible_role_odoo_docker/issues/7

最近在腾讯云购置了一台2C2G的服务器用来放置 Gitlab。但是发现 Gitlab 的 docker 镜像最少需要 4G 内存才能跑起来,所以尝试下启用 SWAP 来用硬盘空间代替内存空间

PS:腾讯云轻量服务器在内存消耗完毕时,会发生硬盘IO读写高的故障,导致无法通过SSH连接到服务器。笔者已经在两台服务器上测试过了,确实会发生这个BUG。

SWAP

windows 连接共享打印机时有可能发生的错误以及解决方案

0x00004005

解决方式为手工添加打印机

注意这个选项如何填写, 比如打印机共享在192.168.1.48下,像这样

那么我们应当填写的值为

1
\\192.168.1.48\EPSON L1800 Series

像这样

然后选择打印机对应的驱动, 可以通过 windows update 从 windows 下载,也可以自己下载然后选择从磁盘安装

然后我连接的是这个打印机,所以我选择这个驱动

设置一个打印机名字

这一步问你是否共享这个打印机,如果不共享,选第一个选项就 OK。

然后就设置好了, 可以打印个测试页测试一下。

https://www.youtube.com/watch?v=ZTYPBpGzB_g

Gitlab 里有一个配置选项 external_url

顾名思义这里配置的是从外部机器访问 Gitlab 的链接,可以是域名或ip地址

在配置这个选项的时候需要注意一个问题,就是这个配置的是里 gitlab 最近的访问路径。

比如我们有如下一个结构

基于 Docker 容器的结构

访客 -> nginx 容器 -> gitlab 容器

此时,访客假如在浏览器输入网址https://gitlab.wudinaonao.com,浏览器将请求发送给 nginx 容器,nginx容器通过反向代理连接到 gitlab 容器

这个时候就要注意了,external_url 这个选项并不能设置为https://gitlab.wudinaonao.com(并不是绝对的)

为什么?

因为在上述结构里 https://gitlab.wudinaonao.com 并不是离 gitlab 最近的访问路径, nginx 通过反向代理向 gitlab 发出的请求才是离 gitlab 最近的链接.

例如, 如果我们通过 docker-compose 来配置 nginx 和 docker 镜像.

1
2
3
4
5
6
7
8
9
10
version: "3.1"
services:
Gitlab:
# 省略其他配置 ...

Nginx:
# 省略其他配置 ...
depends_on:
- Gitlab

在上述容器组中, Nginx 容器可以通过 Gitlab 来访问 Gitlab 容器, 可以在 Nginx 容器里使用ping命令测试ping gitlab

那么在这样的结构里, external_url的值就应当配置为http://Gitlab

并不是绝对的

为什么设置成http://Gitlab并不是绝对的, 因为通过 Nginx 反向代理时, 其实可以更改请求头的 host 选项!

只需要将 hostexternal_url 匹配即可

基于动态DNS的结构

访客 -> 路由器 -> 内网主机

如果 gitlab 主机假设在一个私网里, 我们通过端口映射的方式来访问, 这种情况下, 我们需要设置

external_url 的值为 内网主机的 ip 地址

详细来说, 假设我们的路由器对外有一个公网ip, 我们通过动态dns的方式绑定了一个域名 abc.com, 然后我们将内网里的gitlab主机端口暴露出去, 比如为 8080

那么此时我们可以通过 http:abc.com:8080 的方式来访问内网的 gitlab 主机吗 ?

并不可以.

是因为 abc.com 并不是距离 gitlab 最近的请求, 所以我们需要设置 external_url 的值为 内网主机的 ip 地址

此时才可以正常访问.

https://blog.csdn.net/atlasun/article/details/115749373