这篇文章与其说是分享,不如直接说是我部署过程的记录,虽然文中写到的东西在其他博客都能找到,但是我仍然还是记录下我使用到的部署流程,方便我以后换服务器,或是其他特殊原因,需要重新部署时查阅

前言

昨天心血来潮,把服务器又折腾了一番,之前采用Nginx + Docker方式部署,所有映射及转发都采用手动配置,当然这些对于我来说并没什么难度,而现在采用baota + Nginx + Docker方式部署,目前使用的是静态博客,以及fcircle + twikoo部署都更为便捷,下面分享内容主要以文字为主,可能不会截图,这部分部署相信大家都会

安装

由于我的服务器并不是纯净系统,所以不能直接安装宝塔,当然网络上也有人说可以通过 docker 安装 centos 方式来安装,但是我没试,而采用粗暴方式,重装系统(记得备份),我这里选择宝塔专享版的镜像直接安装

安装完后,发现这个专享版什么也不带,需要自己安装Apache、Nginx,当然php也不能少

部署

部署 hexo

源代码拉到服务器,然后hexo g编译,宝塔配置目录绑定即可,eg:/www/wwwroot/hexo_blog/public

部署 fcircle 鱼塘

Docker部署

之前部署在vercle上,总感觉不太稳定,现改为私有部署,我这里采用 docker 部署,官网

  1. 安装 Docker CE 之前需要删除 Docker 旧版本
    1
    yum remove docker docker-common docker-selinux docker-engine
  2. 安装 Docker 所需的软件包
    1
    yum install -y yum-utils device-mapper-persistent-data lvm2
  3. 设置 docker yum 源
    1
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  4. 安装 DOCKER CE
    1
    2
    yum install docker-ce
    yum list docker-ce --showduplicates | sort -r #要安装特定版本的Docker CE,请列出repo中的可用版本
  5. 启动 Docker
    1
    2
    systemctl start docker
    systemctl status docker
1
yum install -y git

宝塔中自带python,在软件商店直接安装即可,我这里采用服务器安装模式

  1. 安装依赖
    1
    yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel yum vim gcc
  2. 安装 python
    1
    2
    3
    4
    wget https://www.python.org/ftp/python/3.8.8/Python-3.8.8.tgz
    tar -zxf Python-3.8.8.tgz && cd Python-3.8.8
    ./configure --prefix=/usr/local/python3
    make && make install
  3. 建立软连接
    1
    2
    ln -s /usr/local/python3/bin/python3.8 /usr/bin/python3
    ln -s /usr/local/python3/bin/pip3.8 /usr/bin/pip3
  4. 验证(出现版本号即安装成功)
    1
    python3 --version
  5. 安装 yaml
    1
    pip3 install pyyaml

找到进程号,杀掉即可

1
ps aux

kill 掉 PID 即可,也可通过fcircle项目中python3 deploy.py脚本一键取消运行

  1. 克隆仓库
1
git clone https://github.com/Rock-Candy-Tea/hexo-circle-of-friends
  1. 拉取镜像
1
docker pull yyyzyyyz/fcircle:latest
  1. 运行部署脚本
    进入hexo-circle-of-friends根目录,运行一下脚本,选择docker -> 部署即可
1
python3 deploy.py

尝试访问:curl 127.0.0.1:8000/all,有数据即为部署成功,再使用域名反向代理即可

部署 twikoo

官网

  1. 拉取镜像
    1
    docker pull imaegoo/twikoo
  2. 运行容器
    1
    docker run --name twikoo -e TWIKOO_THROTTLE=1000 -p 8080:8080 -v /root/twikoo/data:/app/data -d imaegoo/twikoo

部署卜算子

  1. 搭建docker + docker-compose环境;
  2. 宝塔添加静态页面,绑定域名busuanzi.lovelu.top,别忘记添加解析;
  3. 进入/www/wwwroot/busuanzi.lovelu.top下,新建docker-compose.yaml,内容如下;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "2"
services:
redis:
image: "redis:alpine"
volumes:
- /root/busuanzi/redis:/data

bsz:
image: "xcsoft/busuanzi:latest"
ports:
- "8999:8080"
volumes:
- /root/busuanzi:/app/expose
links:
- redis
depends_on:
- redis
environment:
LOG_ENABLE: true
API_SERVER: http:\/\/127.0.0.1:8999\/api

端口根据自己需要,因为我 8080 占用,所以改用 8999,记得添加解析,以及防火墙配置
其中你需要修改API_SERVER为你的地址,例如你的网址为busuanzi.lovelu.top,那么就需要填写https:\/\/busuanzi.lovelu.top\/api
修改完成后,执行docker-compose up -d4. 宝塔设置反代
http://127.0.0.1:8999进行代理即可

另外有个坑,记录一下
这个的 id 名为:busuanzi_page_pv
我的原来是:busuanzi_value_page_pv
如果你的也是busuanzi_value_site_pv,需要更改一下。butterfly 主题可以到themes/butterfly/layout/includes/header/post-info.pug文件中修改

部署百度统计

  • 网站域名根目录,新建info.php,内容如下
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
<?php

// 设置允许所有来源的请求
header("Access-Control-Allow-Origin: *");

// 设置API密钥和网站ID
$apiKey = '';
$secretKey = '';
$siteId = '';
$code = '';

// 通过授权码获取Access Token函数
function getAccessToken($apiKey, $secretKey, $code) {
$url = "https://openapi.baidu.com/oauth/2.0/token";
$params = array(
'grant_type' => 'authorization_code',
'code' => $code,
'client_id' => $apiKey,
'client_secret' => $secretKey,
'redirect_uri' => 'oob',
);

$query = http_build_query($params);
$response = file_get_contents($url . '?' . $query);
if ($response === false) {
die('Error fetching access token.');
}
$data = json_decode($response, true);

return $data;
}

// 刷新Access Token函数
function refreshAccessToken($apiKey, $secretKey, $refreshToken) {
$url = "https://openapi.baidu.com/oauth/2.0/token";
$params = array(
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $apiKey,
'client_secret' => $secretKey,
);

$query = http_build_query($params);
$response = file_get_contents($url . '?' . $query);
if ($response === false) {
die('Error refreshing access token.');
}
$data = json_decode($response, true);

return $data;
}

// 持久化存储令牌
function saveTokens($accessToken, $refreshToken) {
file_put_contents('tokens.json', json_encode(array(
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
)));
}

// 从文件中加载令牌
function loadTokens() {
if (!file_exists('tokens.json')) {
return null;
}
return json_decode(file_get_contents('tokens.json'), true);
}

// 检查并刷新令牌
function checkAndRefreshTokens($apiKey, $secretKey) {
$tokens = loadTokens();
if ($tokens === null) {
global $code;
$tokens = getAccessToken($apiKey, $secretKey, $code);
saveTokens($tokens['access_token'], $tokens['refresh_token']);
} else {
$tokens = refreshAccessToken($apiKey, $secretKey, $tokens['refresh_token']);
saveTokens($tokens['access_token'], $tokens['refresh_token']);
}
return $tokens;
}

// 定义获取数据的函数
function getData($startDate, $endDate, $metrics, $accessToken, $siteId) {
$url = "https://openapi.baidu.com/rest/2.0/tongji/report/getData";
$params = array(
'access_token' => $accessToken,
'site_id' => $siteId,
'method' => 'overview/getTimeTrendRpt',
'start_date' => $startDate,
'end_date' => $endDate,
'metrics' => $metrics,
);

$query = http_build_query($params);
$fullUrl = $url . '?' . $query;

$response = file_get_contents($fullUrl);
if ($response === false) {
die('Error fetching data.');
}
return json_decode($response, true);
}

// 定义缓存文件路径
$cacheFile = 'data_cache.json';
$cacheTime = 60; // 缓存时间,单位:秒

// 获取Access Token并刷新令牌
$tokens = checkAndRefreshTokens($apiKey, $secretKey);
$accessToken = $tokens['access_token'];

// 检查缓存文件是否存在且未过期
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
$data = json_decode(file_get_contents($cacheFile), true);
} else {
// 准备数据
$data = array(
'today_uv' => null,
'today_pv' => null,
'yesterday_uv' => null,
'yesterday_pv' => null,
'last_month_pv' => null,
'last_year_pv' => null,
);

// 获取最近31天的数据
$startDate = date('Ymd', strtotime('-31 days'));
$endDate = date('Ymd');
$monthData = getData($startDate, $endDate, 'pv_count,visitor_count', $accessToken, $siteId);

// 处理并提取最近31天的数据
$last31DaysPV = 0;
if (isset($monthData['result']['items'][1])) {
$dataPoints = $monthData['result']['items'][1];
foreach ($dataPoints as $point) {
// $last31DaysPV += $point[0];
$last31DaysPV += intval($point[0]);
}
}

// 获取一整年的数据
$startDate = date('Ymd', strtotime('-1 year'));
$endDate = date('Ymd');
$yearData = getData($startDate, $endDate, 'pv_count,visitor_count', $accessToken, $siteId);

// 处理并提取所需数据
if (isset($yearData['result']['items'][1])) {
$dataPoints = $yearData['result']['items'][1];
$today = date('Y/m/d');
$yesterday = date('Y/m/d', strtotime('-1 day'));
$lastMonth = date('Y/m/d', strtotime('-30 days'));

foreach ($yearData['result']['items'][0] as $index => $date) {
if ($date[0] == $today) {
$data['today_uv'] = $dataPoints[$index][1];
$data['today_pv'] = $dataPoints[$index][0];
} elseif ($date[0] == $yesterday) {
$data['yesterday_uv'] = $dataPoints[$index][1];
$data['yesterday_pv'] = $dataPoints[$index][0];
} elseif ($date[0] == $lastMonth) {
$data['last_month_pv'] = $dataPoints[$index][0];
}
}

$data['last_year_pv'] = array_sum(array_column($dataPoints, 0));
}

// 添加最近31天的PV总和
$data['last_month_pv'] = $last31DaysPV;

// 保存数据到缓存文件
file_put_contents($cacheFile, json_encode($data));
}

// 返回JSON数据
header('Content-Type: application/json');
echo json_encode($data);

?>
  • 再来说说页头几个参数怎么获取

获取apiKey和secretKey数据导出服务

获取code,将其中{CLIENT_ID}替换为你的apiKey

1
http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri=oob&scope=basic&display=popup

获取siteId,siteid为你的网站id,查看百度统计报告的url中会显示siteid

以上信息修改完后,立即访问一次,防止过期

  • 防止泄露token

宝塔站点中添加访问限制

1
2
3
名称:禁止访问tokens
后缀:tokens.json
目录:baidutongji

然后尝试访问一次tokens.json,如果访问失败了就大功告成。

一套部署下来,服务器内存占用还是比较客观,下面再去部署一个图床吧

参考:HeoAkilar安知鱼

以上我的部署就完成了,剩下就是配置反向代理以及 SSL 了