0%

问题描述

经常需要使用sudo命令,每次都需要输入密码,很麻烦。
使用修改/etc/sudoers文件,添加NOPASSWD参数,可以实现免密码。

操作步骤

  1. 打开终端,输入命令:

    1
    2
    3
    4
    	
    sudo chmod u+w /etc/sudoers
    sudo visudo
    sudo chmod u+w /etc/sudoers
  2. 修改过内容

    1
    %admin ALL=(ALL) NOPASSWD: ALL

应用场景,将github的开源代码同步到国内gitee源,需要同步所有分支以及提交。
使用git mirror方案。

将github项目更新到本地

命令如下,一定要加上–mirror参数

1
git clone --mirror https://github.com/xxx.git

更新并推送到gitee

push 时一定要带上–mirror

1
2
git fetch -p origin #更新到最新
git push --mirror https://gitee.com/xxx.git

这个脚本加入到定时脚本,后续就能定时同步代码了。

记录一次go代码性能调优实践。
由于希望远程看到调试信息,直接使用http方式开启go的pprof调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"log"
"net/http"
_ "net/http/pprof" //关键代码,透明的讲pprof加入到代码
)

func main() {
go func() {
//do something
}()

http.ListenAndServe("0.0.0.0:10397", nil) //开启端口可以远程访问。
}

浏览器方法就能看到如下页面
pprof
这个页面非常好用,可以实时看到程序实时运行效果。
如果需要看一段时间内的运行状态,需要用到go tool工具,go tool工具有两个方法,一个看pprof统计和trace。下面分开介绍。

pprof查看

1
2
curl "http://127.0.0.1:10397/debug/pprof/profile?seconds=10" -o p.out #获取最近10s的profile信息
go tool pprof -http=:8088 p.out #用浏览器查看刚刚profile文件

浏览器能直接看到刚刚profile信息
profile信息

切换菜单就能看到最常用的火焰图。
火焰图

trace 查看

1
2
3
curl "http://127.0.0.1:10397/debug/pprof/trace?debug=1&seconds=10" > t.out #获取远程trace文件
go tool trace t.out #查看trace信息

trace
trace

我的blog一直使用wordpress。服务器一直也蹭朋友的服务器。朋友的服务器终于要停服了。 我也需要为我很少更新blog找一个永不停服的服务器了。
最终选定方案为白嫖github page。 使用hexo创建静态的blog页面,然后通过github page免费发布,通过cloudflare CDN进行加加速。

hexo 创建静态blog

1
2
3
4
5
6
yarn global add hexo-cli  #全局安装hexo
mkdir blog && cd blog #创建一个空目录
hexo init #初始一个空项目
yarn install #更新依赖
hexo new "使用hexo和github 搭建免费blog系统" #创建一个新文章
yarn server # 启动本地服务查看blog页面

github 配置

在github创建一个公开项目。将刚创建项目提交github。
在github action 创建hexo编译发布脚本。
创建 deploy, 脚本每次master有提交时进行编译,然后将编译结果提交到blog分支。代码如下:

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
# This is a basic workflow to help you get started with Actions

name: Blog deploy

# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3

# Runs a set of commands using the runners shell
- name: Set Node
uses: actions/setup-node@v3
with:
node-version: 16

- name: Hexo build
run: |
yarn install
yarn clean
yarn build

- name: blog deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
publish_branch: blog
cname: blog.zhangwenjin.com

github page配置

如图所示
img

cloudflare 配置

在DNS中开启 Proxied 即可
如果启用了https,需要在在ssl配置中启用full模式,即github负责申请https证书,cloudflare负责转发

整个包只有70M,包含mysql和redis。
直接上dockerfile。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM php:7.4-fpm-alpine3.11
ADD repositories /etc/apk/repositories
ADD default.conf /
ADD index.html /
ADD run.sh /
RUN apk update && apk add nginx && \
apk add m4 autoconf make gcc g++ linux-headers && \
pecl install redis && docker-php-ext-enable redis && \
docker-php-ext-install pdo_mysql opcache mysqli && \
mkdir /run/nginx && \
mv /default.conf /etc/nginx/conf.d && \
touch /run/nginx/nginx.pid && \
chmod 755 /run.sh && \
apk del m4 autoconf make gcc g++ linux-headers

EXPOSE 80
EXPOSE 9000

ENTRYPOINT ["/run.sh"]

直接上代码。

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
<?php
/**
* 任务列表
*
*/

/**
CREATE TABLE `t_job` (
`job_id` int NOT NULL AUTO_INCREMENT COMMENT '任务id',
`at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '任务执行时间',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '任务状态,0 默认 1 执行中 2执行成功 -2 执行失败',
`lock_date` timestamp NULL COMMENT '任务锁定时间',
`cost` float NULL COMMENT '任务执行耗时',
`name` varchar(32) NOT NULL COMMENT '任务名称',
`params` text NULL COMMENT '任务参数',
`retry_cnt` int NULL COMMENT '任务重试次数',
PRIMARY KEY (`job_id`)
) COMMENT = '任务信息';
*/

class JobQueue implements Iterator
{
static $is_stop = false;
private $table_name = "t_job";
private $SLEEP_TIME = 2; // 如果没有数据,间隔1s获取

private $job_id = 0; //任务id
private $start_time = 0; //任务时间
private $err = "";

/**
* 数据库名称
*/
public function __construct($table_name = "")
{
if ($table_name) {
$this->table_name = $table_name;
}

}

/**
* first start, log start
*/
public function rewind(): void
{
$this->log("start loop");
}

/**
* return data, method, data
*
*/
public function current()
{
$db = di_get("db");
$sql = sprintf('SELECT * FROM `%s` WHERE `job_id`=%s', $this->table_name, $this->job_id);
$row = $db->getRow($sql);
$row['params'] = json_decode($row['params'], true); //解码
$this->start_time = $this->microtime_float();
$this->err = "";
return $row;
}

/**
* return key, after current
*/
public function key()
{
return $this->job_id;
}

/**
* 确认上一个任务完成,并记录时间和结果,如果需要重试,也这里处理
*/
public function next(): void
{
$db = di_get("db");
$cost = $this->microtime_float() - $this->start_time; //执行时间
if (empty($this->err)) {
$status = 2; //执行成功
$sql = sprintf("UPDATE `%s` SET cost=%s, status=2 WHERE `job_id`=%s", $this->table_name, $cost, $this->job_id);
} else {
if (is_string($this->err)) {
$status = -2;
$msg = $this->err;
} else {
$status = 2; //执行成功
$msg = json_encode($this->err);

}
$sql = sprintf("UPDATE `%s` SET `cost`=%s, `status`=%s, `msg`='%s' WHERE `job_id`=%s", $this->table_name, $cost, $status, $msg, $this->job_id);

}
$db->query($sql);
}

/**
* 获取一个项目,如果没有等等
*/
public function valid(): bool
{
$db = di_get("db");
while (true) {
$sql = sprintf("SELECT `job_id` FROM `%s` WHERE status=0 AND `at`<=current_timestamp ORDER BY `job_id` ASC LIMIT 1", $this->table_name);
$row = $db->getRow($sql);

if (!$row) {
sleep($this->SLEEP_TIME);
continue; //等待后看是否有消息
}

$job_id = $row['job_id'];
//lock
$sql = sprintf("UPDATE %s SET status=1, lock_date=current_timestamp WHERE status=0 AND `job_id`=%s", $this->table_name, $row['job_id']);
$sth = $db->query($sql);
$n = $sth->rowCount();
if ($n == 0) {
continue; //当前记录已经被其他进程锁定,重新获取
}
$this->job_id = $job_id;
return true; //开始获取数据
}
}

public function log($data)
{
$buf = $data;
if (!is_string($data)) {
$buf = json_encode($data);
}

$debug_info = debug_backtrace();
//var_dump($debug_info);
$file_path = $debug_info[0]['file'];
$line_no = $debug_info[0]['line'];
//$file_path = substr($file_path, strlen(APP_PATH));

$buf = sprintf("%s %s:%s %s\n", date("H:i:s"), $file_path, $line_no, $buf);

echo ($buf);
}

/**
* 获取时间戳
*/
public function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float) $usec + (float) $sec);
}

/**
* 增加任务
* @params afert 多久后执行,单位s
*/
public function add($name, $params, int $afert = 0)
{
$db = di_get("db");
$buf = json_encode($params);

if ($afert > 0) {
$sql = sprintf("INSERT INTO `%s` SET `at`= date_add(current_timestamp, interval %s second) , `name` = '%s', `params`='%s'", $this->table_name, $afert, $name, $buf);
} else {
$sql = sprintf("INSERT INTO `%s` SET `at`= current_timestamp, `name` = '%s', `params`='%s'", $this->table_name, $name, $buf);
}
$db->query($sql);
$job_id = $db->lastInsertId();

return $job_id;
}

/**
* 增加唯一任务,如果当前任务已经存在,不在添加
*/
public function add_unique($name, $params)
{
$db = di_get("db");

$sql = sprintf("SELECT name FROM `%s` WHERE `status` = 0 AND `name` ='%s'", $this->table_name, $name);
$row = $db->getRow($sql);
if ($row) {
return 0; //不在添加
}

return $this->add($name, $params, 0);
}

/**
* 确认任务信息
*/
public function done($err = "")
{
$this->err = $err;
}

/**
* 重新
*/
public function retry(int $after = 0)
{
$db = di_get("db");
if ($after > 0) {
$sql = sprintf("UPDATE `%s` SET `at`=date_add(current_timestamp, interval %s second), `status`=0, `retry_cnt` =`retry_cnt` + 1 WHERE `job_id`=%s", $this->table_name, $after, $this->job_id);
} else {
$sql = sprintf("UPDATE `%s` SET `at`=current_timestamp, `status`=0, `retry_cnt` =`retry_cnt` + 1 WHERE `job_id`=%s", $this->table_name, $this->job_id);
}
$db->query($sql);

}

/**
* 获取未完成的
*/
public function count()
{
$db = di_get("db");

$sql = sprintf("SELECT COUNT(*) FROM `%s` WHERE status=0", $this->table_name);
return intval($db->getOne($sql));
}
}

公司一个业务的Mysql user表总是莫名被修改。项目太老懒得去找具体原因了。
最终觉得偷懒使用mysql触发器,将所有user表的改动记录到一个user_log表,如果用户信息被修改,通过查询user_log就能帮用户数据恢复为以前版本。

具体操作如下:

  1. 创建user_log 表

    1
    2
    3
    4
    5
    6
    7
    8
    CREATE TABLE `t_user_log` (
    `lid` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `account_id` varchar(64) DEFAULT NULL,
    `id_shows` text,
    `cdate` datetime DEFAULT NULL,
    PRIMARY KEY (`lid`),
    KEY `account_id` (`account_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  2. 创建user表insert触发器

    1
    2
    3
    4
    5
    6
    7
    DELIMITER //
    CREATE TRIGGER add_user
    AFTER INSERT
    ON t_users
    FOR EACH ROW
    Insert into t_user_log(account_id, id_shows, cdate) VALUES(NEW.account_id, NEW.id_shows,NOW()) //
    DELIMITER ;
  3. 创建user表update触发器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    DELIMITER //
    CREATE TRIGGER update_user
    AFTER UPDATE
    ON t_users
    FOR EACH ROW
    BEGIN
    IF NEW.id_shows != OLD.id_shows THEN
    Insert into t_user_log(account_id, id_shows, cdate) VALUES(NEW.account_id, NEW.id_shows,NOW());
    END IF;
    END//
    DELIMITER ;

查询所有触发器
SHOW TRIGGERS;

安装centos蓝牙库:

1
yum install bluez.x86_64 bluez-compat.x86_64

安装蓝牙认证库:

1
yum -y install pygobject2 dbus-python-devel.x86_64 dbus-python

配置dhcpd:

1
2
3
4
5
6
7
8
9
10
11
vim /etc/dhcp/dhcpd.conf

ignore client-updates;

subnet 10.0.0.0 netmask 255.255.255.0 {
option routers 10.0.0.1;
option subnet-mask 255.255.255.0;
option domain-name-servers 10.16.0.222;

range dynamic-bootp 10.0.0.10 10.0.0.50;
}

上网设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
brctl addbr pan1
brctl stp pan1 off
echo "1">/proc/sys/net/ipv4/ip_forward
ifconfig pan1 10.0.0.1/24
iptables -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE
#iptables -D FORWARD 1
#watch -n 1 iptables -L -n -v
/etc/init.d/dhcpd restart

hciconfig hci0 up piscan
modprobe bnep
pand --listen --role NAP --master
nohup python /root/src/simple-agent.py > /tmp/blue-agent.log 2>&1

问题:公司租用多台阿里云主机,只有1台对外服务,其他服务器只能访问内网。内网机器偶尔需要访问外网,但又不希望开通外网访问权限。
解决方案:将阿里云服务器重新组建一个虚拟局域网,有外网权限的服务器做为网关,其他内网服务器通过网关就能访问外部网络。

穿透阿里的内网可选用ipip tunnel 或者gre模式,本文选用通用性比较好的gre模式。

外网机器:
a. 打开ipv4转发:

1
echo 1 > /proc/sys/net/ipv4/ip_forward

b. 增加转发规则:

1
iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE

c. 建立内网隧道:

1
2
3
4
modprobe ip_gre  #加载gre内核模块
ip tunnel add gre3 mode gre remote 10.10.10.3 local 10.10.10.2 ttl 64 #建立隧道
ip link set gre3 up #启动虚拟网卡
ip addr add 192.168.122.2 peer 192.168.122.3 dev gre3 #设定路由

内网需要上网机器:
a. 建立隧道

1
2
3
4
modprobe ip_gre  #加载gre内核模块
ip tunnel add gre1 mode gre remote 10.10.10.2 local 10.10.10.3 ttl 64 #建立隧道
ip link set gre1 up #启动虚拟网卡
ip addr add 192.168.122.3 peer 192.168.122.2 dev gre1 #设定路由

b. 设定默认路由

1
2
route del default
route add default gw 192.168.122.2

ps停用隧道方式:

1
2
ip link set gre1 down
ip tunnel del gre1

  1. rsyncd 服务搭建
  • vim /etc/rsyncd.conf 配置文件内容
    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
    uid = root                         
    gid = root
    use chroot = no
    read only = yes

    #limit access to private LANs
    hosts allow=10.10.1.2,10.10.1.3
    hosts deny=*
    max connections = 5

    pid file = /var/run/rsyncd.pid

    #This will give you a separate log file
    log file = /opt/logs/rsync.log

    #This will log every file transferred - up to 85,000+ per user, per sync
    transfer logging = yes

    log format = %t %a %m %f %b
    syslog facility = local3
    timeout = 300

    # MODULE OPTIONS
    [test]
    path = /opt/test
    list=yes
    ignore errors
    comment = test
    exclude = .svn
  • 启动服务rsync 服务
    1
    killall rsync;/usr/bin/rsync --daemon
  • 客户端同步
    1
    rsync -avzP rsync://10.132.35.216/test /opt/test
  1. 配置http代理(使用epel源)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    yum install 3proxy
    a. vim /etc/3proxy.cfg
    nscache 1000
    timeouts 1 5 30 60 180 1800 15 60

    daemon
    log /opt/logs/3proxy.log D
    logformat "- +_L%t.%. %N.%p %E %U %C:%c %R:%r %O %I %h %T"
    rotate 2

    auth iponly
    allow * 10.10.0.0/16 * *
    proxy -n -a -p6666
  2. 客户端启用http代理
  • wget 等代理
    1
    export http_proxy=http://10.10.1.2:6666
  • yum 启用http代理
    1
    2
    3
    4
    vim /etc/yum.conf

    [main]
    proxy=http://10.10.1.2:6666