-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
2081 lines (1991 loc) · 129 KB
/
atom.xml
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://nightfury.top</id>
<title>NightFury</title>
<subtitle>行动胜于空想</subtitle>
<icon>https://nightfury.top/assets/favicon.ico</icon>
<link href="https://nightfury.top" />
<author>
<name>NightFury</name>
</author>
<updated>2024-07-05T08:09:11.000Z</updated>
<category term="Hardware Accelerator" />
<category term=" Software Configuration" />
<entry>
<id>https://nightfury.top/2024/07/05/%E5%88%A9%E7%94%A8Acme%E8%84%9A%E6%9C%AC%E8%87%AA%E5%8A%A8%E7%94%B3%E8%AF%B7SSL%E8%AF%81%E4%B9%A6/</id>
<title>利用Acme脚本自动申请SSL证书</title>
<link rel="alternate" href="https://nightfury.top/2024/07/05/%E5%88%A9%E7%94%A8Acme%E8%84%9A%E6%9C%AC%E8%87%AA%E5%8A%A8%E7%94%B3%E8%AF%B7SSL%E8%AF%81%E4%B9%A6/"/>
<content type="html"><p>在这篇博客中,我们将介绍如何在Linux服务器上使用 <code>acme.sh</code> 申请Let’s Encrypt证书,并配置自动续签任务。以下脚本将自动执行这些步骤,确保你的服务器能够使用免费的 SSL 证书并自动续签,完整代码开源在 <a href="https://github.com/szNightFury/Acme">szNightFury‘s Github</a>。</p>
<h3 id="前提条件"><a href="#前提条件" class="headerlink" title="前提条件"></a>前提条件</h3><ol>
<li>确保你有一个域名,并且能够修改其 DNS 记录。</li>
<li>确保你的服务器上已经安装了 Nginx(或其他Web服务器)。</li>
</ol>
<h3 id="脚本详解"><a href="#脚本详解" class="headerlink" title="脚本详解"></a>脚本详解</h3><h4 id="1-确保脚本以root身份运行"><a href="#1-确保脚本以root身份运行" class="headerlink" title="1. 确保脚本以root身份运行"></a>1. 确保脚本以<code>root</code>身份运行</h4><p>为了执行需要管理员权限的操作,脚本需要以<code>root</code>身份运行。如果当前用户不是<code>root</code>,脚本将退出并提示用户以<code>root</code>身份重新运行。</p>
<pre><code class="language-bash">if [ &quot;$EUID&quot; -ne 0 ]; then
echo &quot;请以 root 身份运行此脚本。&quot;
exit 1
fi
</code></pre>
<h4 id="2-检查系统类型"><a href="#2-检查系统类型" class="headerlink" title="2. 检查系统类型"></a>2. 检查系统类型</h4><p>脚本会检测当前操作系统类型,以便使用适当的软件包管理器安装所需的依赖项。</p>
<pre><code class="language-bash">if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
else
echo &quot;无法确定操作系统类型,请手动检查。&quot;
exit 1
fi
</code></pre>
<h4 id="3-安装socat"><a href="#3-安装socat" class="headerlink" title="3. 安装socat"></a>3. 安装<code>socat</code></h4><p><code>socat</code>是一个多功能的网络工具,用于建立独立的双向连接。脚本会检查是否已经安装了<code>socat</code>,如果没有,则根据操作系统类型进行安装。</p>
<pre><code class="language-bash">if ! command -v socat &amp;&gt; /dev/null; then
echo &quot;socat 未安装,正在安装 socat...&quot;
if [ &quot;$OS&quot; == &quot;debian&quot; ] || [ &quot;$OS&quot; == &quot;ubuntu&quot; ]; then
apt update
apt install -y socat
elif [ &quot;$OS&quot; == &quot;centos&quot; ]; then
yum install -y socat
else
echo &quot;不支持的操作系统。&quot;
exit 1
fi
if [ $? -ne 0 ]; then
echo &quot;socat 安装失败,请检查错误信息。&quot;
exit 1
fi
else
echo &quot;socat 已安装。&quot;
fi
</code></pre>
<h4 id="4-检查-Nginx-服务状态"><a href="#4-检查-Nginx-服务状态" class="headerlink" title="4. 检查 Nginx 服务状态"></a>4. 检查 Nginx 服务状态</h4><p>脚本会检查 Nginx 服务是否正在运行,如果是,则停止服务以释放 80 端口(如有其他程序占用 80 端口,请修改下面代码)</p>
<pre><code class="language-bash">nginx_status=$(systemctl is-active nginx)
if [ &quot;$nginx_status&quot; == &quot;active&quot; ]; then
echo &quot;nginx 服务正在运行,准备停止...&quot;
systemctl stop nginx
if [ $? -ne 0 ]; then
echo &quot;停止 nginx 失败,请检查错误信息。&quot;
exit 1
fi
else
echo &quot;nginx 服务未运行,无需停止。&quot;
fi
</code></pre>
<h4 id="5-检查端口-80-是否被占用"><a href="#5-检查端口-80-是否被占用" class="headerlink" title="5. 检查端口 80 是否被占用"></a>5. 检查端口 80 是否被占用</h4><p>脚本会检查端口 80 是否被占用,如果被占用,将提示用户并退出。</p>
<pre><code class="language-bash">if lsof -i:80 &amp;&gt; /dev/null; then
echo &quot;端口 80 被占用,无法继续。&quot;
exit 1
fi
</code></pre>
<h4 id="6-安装-acme-sh"><a href="#6-安装-acme-sh" class="headerlink" title="6. 安装 acme.sh"></a>6. 安装 <code>acme.sh</code></h4><p><code>acme.sh</code> 是一个纯 Unix Shell 脚本,用于从 Let’s Encrypt 申请 SSL 证书。脚本会检查是否已经安装了 <code>acme.sh</code>,如果没有,则进行安装。</p>
<pre><code class="language-bash">if [ ! -d &quot;$HOME/.acme.sh&quot; ]; then
echo &quot;正在安装 acme.sh...&quot;
curl https://get.acme.sh | sh
if [ $? -ne 0 ]; then
echo &quot;acme.sh 安装失败,请检查错误信息。&quot;
exit 1
fi
else
echo &quot;acme.sh 已安装。&quot;
fi
</code></pre>
<h4 id="7-设置默认-CA-为-Let’s-Encrypt"><a href="#7-设置默认-CA-为-Let’s-Encrypt" class="headerlink" title="7. 设置默认 CA 为 Let’s Encrypt"></a>7. 设置默认 CA 为 Let’s Encrypt</h4><pre><code class="language-bash">echo &quot;设置默认 CA 为 Let’s Encrypt...&quot;
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
</code></pre>
<h4 id="8-获取用户输入的域名"><a href="#8-获取用户输入的域名" class="headerlink" title="8. 获取用户输入的域名"></a>8. 获取用户输入的域名</h4><p>脚本会提示用户输入主域名和附加域名。</p>
<pre><code class="language-bash">read -p &quot;请输入主域名: &quot; main_domain
domains=($main_domain)
while true; do
read -p &quot;请输入附加域名(或按 Enter 键结束输入): &quot; additional_domain
if [ -z &quot;$additional_domain&quot; ]; then
break
fi
domains+=(&quot;$additional_domain&quot;)
done
</code></pre>
<h4 id="9-生成域名参数"><a href="#9-生成域名参数" class="headerlink" title="9. 生成域名参数"></a>9. 生成域名参数</h4><p>根据用户输入的域名生成域名参数,用于申请证书。</p>
<pre><code class="language-bash">domain_args=&quot;&quot;
for domain in &quot;$&#123;domains[@]&#125;&quot;; do
domain_args=&quot;$domain_args -d $domain&quot;
done
</code></pre>
<h4 id="10-申请测试证书"><a href="#10-申请测试证书" class="headerlink" title="10. 申请测试证书"></a>10. 申请测试证书</h4><pre><code class="language-bash">echo &quot;申请测试证书...&quot;
~/.acme.sh/acme.sh --issue $domain_args --standalone -k ec-256 --force --test
if [ $? -ne 0 ]; then
echo &quot;测试证书申请失败,请检查错误信息。&quot;
exit 1
fi
echo &quot;删除测试证书...&quot;
rm -rf &quot;$HOME/.acme.sh/$&#123;main_domain&#125;_ecc&quot;
</code></pre>
<p>如果机子只有 IPV6,则还需要加个参数<code>--listen-v6</code></p>
<h4 id="11-申请正式证书"><a href="#11-申请正式证书" class="headerlink" title="11. 申请正式证书"></a>11. 申请正式证书</h4><pre><code class="language-bash">echo &quot;申请正式证书...&quot;
~/.acme.sh/acme.sh --issue $domain_args --standalone -k ec-256 --force
if [ $? -ne 0 ]; then
echo &quot;正式证书申请失败,请检查错误信息。&quot;
exit 1
fi
</code></pre>
<p>同理,如果机子只有 IPV6,同样需要加个参数<code>--listen-v6</code></p>
<h4 id="12-安装证书"><a href="#12-安装证书" class="headerlink" title="12. 安装证书"></a>12. 安装证书</h4><pre><code class="language-bash">echo &quot;创建证书存储目录...&quot;
mkdir -p /etc/cert
echo &quot;安装证书...&quot;
~/.acme.sh/acme.sh --installcert -d &quot;$main_domain&quot; --fullchainpath /etc/cert/fullchain.pem --keypath /etc/cert/privkey.pem --ecc --force
if [ $? -ne 0 ]; then
echo &quot;证书安装失败,请检查错误信息。&quot;
exit 1
fi
</code></pre>
<h4 id="13-重新启动-Nginx-服务"><a href="#13-重新启动-Nginx-服务" class="headerlink" title="13. 重新启动 Nginx 服务"></a>13. 重新启动 Nginx 服务</h4><p>如果 Nginx 之前正在运行,脚本会重新启动 Nginx 服务。</p>
<pre><code class="language-bash">if [ &quot;$nginx_status&quot; == &quot;active&quot; ]; then
echo &quot;重新启动 nginx 服务...&quot;
systemctl start nginx
if [ $? -ne 0 ]; then
echo &quot;启动 nginx 失败,请检查错误信息。&quot;
exit 1
fi
fi
</code></pre>
<h4 id="14-设置自动续签任务"><a href="#14-设置自动续签任务" class="headerlink" title="14. 设置自动续签任务"></a>14. 设置自动续签任务</h4><p>为了确保证书在到期前自动续签,脚本会配置一个定时任务。</p>
<pre><code class="language-bash">echo &quot;设置自动续签任务...&quot;
crontab -l | grep -v &quot;acme.sh --cron&quot; | crontab -
(crontab -l 2&gt;/dev/null; echo &quot;0 0 * * * ~/.acme.sh/acme.sh --cron --home ~/.acme.sh &gt; /dev/null 2&gt;&amp;1&quot;) | crontab -
if [ $? -ne 0 ]; then
echo &quot;自动续签任务设置失败,请检查错误信息。&quot;
exit 1
fi
echo &quot;证书申请、安装和自动续签任务设置完成。&quot;
</code></pre>
<h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>通过这个脚本,你可以自动化从申请到安装以及配置自动续签 Let’s Encrypt 证书的整个过程。这将确保你的服务器始终使用有效的 SSL 证书,从而提高安全性。</p>
</content>
<category term="Configuration" scheme="https://nightfury.top/categories/Configuration/" />
<category term="Debian" scheme="https://nightfury.top/tags/Debian/" />
<category term="Ubuntu" scheme="https://nightfury.top/tags/Ubuntu/" />
<category term="SSL" scheme="https://nightfury.top/tags/SSL/" />
<category term="Linux" scheme="https://nightfury.top/tags/Linux/" />
<updated>2024-07-05T08:09:11.000Z</updated>
</entry>
<entry>
<id>https://nightfury.top/2024/07/05/Debian12%E9%83%A8%E7%BD%B2Cloudreve%E4%BA%91%E7%9B%98/</id>
<title>Debian12部署Cloudreve云盘</title>
<link rel="alternate" href="https://nightfury.top/2024/07/05/Debian12%E9%83%A8%E7%BD%B2Cloudreve%E4%BA%91%E7%9B%98/"/>
<content type="html"><p>Cloudreve 是一款开源的网盘系统,支持多种存储后端,可以方便地搭建私人网盘。</p>
<p>VPS 环境:Debian 12</p>
<p>CloudReve 版本:3.8.3</p>
<h2 id="步骤一:更新系统"><a href="#步骤一:更新系统" class="headerlink" title="步骤一:更新系统"></a>步骤一:更新系统</h2><p>首先,确保你的系统是最新的。运行以下命令更新软件包列表并升级所有已安装的软件包:</p>
<pre><code class="language-sh">sudo apt update &amp;&amp; sudo apt upgrade -y
</code></pre>
<h2 id="步骤二:下载-Cloudreve"><a href="#步骤二:下载-Cloudreve" class="headerlink" title="步骤二:下载 Cloudreve"></a>步骤二:下载 Cloudreve</h2><p>访问 <a href="https://github.com/cloudreve/Cloudreve/releases">Cloudreve的GitHub页面</a> 以获取最新版本的 Cloudreve。或者,你可以使用 curl 命令直接下载 3.8.3 版本(截止博客发表前的最新版本):</p>
<pre><code class="language-sh">curl -L -o cloudreve.zip https://github.com/cloudreve/Cloudreve/releases/download/3.8.3/cloudreve_3.8.3_linux_amd64.tar.gz
</code></pre>
<p>下载完成后,解压文件:</p>
<pre><code class="language-sh">mkdir /opt/cloudreve
tar -xvzf cloudreve_3.8.3_linux_amd64.tar.gz -C /opt/cloudreve
</code></pre>
<h2 id="步骤三:配置-Cloudreve"><a href="#步骤三:配置-Cloudreve" class="headerlink" title="步骤三:配置 Cloudreve"></a>步骤三:配置 Cloudreve</h2><p>进入 Cloudreve 的目录,并赋予可执行权限:</p>
<pre><code class="language-sh">cd /opt/cloudreve
chmod +x cloudreve
</code></pre>
<p>运行 Cloudreve 以生成默认配置文件:</p>
<pre><code class="language-sh">./cloudreve # 初次启动会生成管理员账号和密码, 请妥善保存好, 后面可以进面板修改账号和密码
</code></pre>
<p>初次运行后,会生成一个名为 <code>conf.ini</code> 的配置文件,你可以根据需要修改此文件。以下是一个示例配置:</p>
<pre><code class="language-ini">[System]
; 运行模式
Mode = master
; 监听端口
Listen = :5212
; 是否开启 Debug
Debug = false
; Session 密钥, 一般在首次启动时自动生成
SessionSecret = 23333
; Hash 加盐, 一般在首次启动时自动生成
HashIDSalt = something really hard to guss
; 实测只会生成上述上述五个配置, 下面的配置如有需要请自行选择
; 呈递客户端 IP 时使用的 Header
ProxyHeader = X-Forwarded-For
; SSL 相关
[SSL]
; SSL 监听端口
Listen = :443
; 证书路径
CertPath = C:\Users\i\Documents\fullchain.pem
; 私钥路径
KeyPath = C:\Users\i\Documents\privkey.pem
; 启用 Unix Socket 监听
[UnixSocket]
Listen = /run/cloudreve/cloudreve.sock
; 设置产生的 socket 文件的权限
Perm = 0666
; 数据库相关,如果你只想使用内置的 SQLite 数据库,这一部分直接删去即可
[Database]
; 数据库类型,目前支持 sqlite/mysql/mssql/postgres
Type = mysql
; MySQL 端口
Port = 3306
; 用户名
User = root
; 密码
Password = root
; 数据库地址
Host = 127.0.0.1
; 数据库名称
Name = v3
; 数据表前缀
TablePrefix = cd_
; 字符集
Charset = utf8mb4
; SQLite 数据库文件路径
DBFile = cloudreve.db
; 进程退出前安全关闭数据库连接的缓冲时间
GracePeriod = 30
; 使用 Unix Socket 连接到数据库
UnixSocket = false
; 从机模式下的配置
[Slave]
; 通信密钥
Secret = 1234567891234567123456789123456712345678912345671234567891234567
; 回调请求超时时间 (s)
CallbackTimeout = 20
; 签名有效期
SignatureTTL = 60
; 跨域配置
[CORS]
AllowOrigins = *
AllowMethods = OPTIONS,GET,POST
AllowHeaders = *
AllowCredentials = false
SameSite = Default
Secure = lse
; Redis 相关
[Redis]
Server = 127.0.0.1:6379
Password =
DB = 0
; 从机配置覆盖
[OptionOverwrite]
; 可直接使用 `设置名称 = 值` 的格式覆盖
max_worker_num = 50
</code></pre>
<h2 id="步骤五:创建-systemd-服务"><a href="#步骤五:创建-systemd-服务" class="headerlink" title="步骤五:创建 systemd 服务"></a>步骤五:创建 systemd 服务</h2><p>为了方便管理 Cloudreve,可以创建一个 systemd 服务。</p>
<p>创建一个名为 <code>cloudreve.service</code> 的文件:</p>
<pre><code class="language-sh">nano /usr/lib/systemd/system/cloudreve.service
</code></pre>
<p>在文件中添加以下内容(将下文 <code>PATH_TO_CLOUDREVE</code> 更换为程序所在目录,本文即 <code>/opt/cloudreve/</code>):</p>
<pre><code class="language-ini">[Unit]
Description=Cloudreve
Documentation=https://docs.cloudreve.org
After=network.target
After=mysqld.service
Wants=network.target
[Service]
WorkingDirectory=/PATH_TO_CLOUDREVE
ExecStart=/PATH_TO_CLOUDREVE/cloudreve
Restart=on-abnormal
RestartSec=5s
KillMode=mixed
StandardOutput=null
StandardError=syslog
[Install]
WantedBy=multi-user.target
</code></pre>
<p>保存并退出后,重新加载 systemd 服务:</p>
<pre><code class="language-sh"># 更新配置
systemctl daemon-reload
# 启动服务
systemctl start cloudreve
# 设置开机启动
systemctl enable cloudreve
</code></pre>
<h2 id="步骤六:访问Cloudreve"><a href="#步骤六:访问Cloudreve" class="headerlink" title="步骤六:访问Cloudreve"></a>步骤六:访问Cloudreve</h2><p>Cloudreve 默认监听在 5212 端口。你可以通过访问 <code>http://你的服务器IP:5212</code> 来访问 Cloudreve。</p>
<p>初次登录时,用管理员账户登录,然后点击头像里的管理面板做进一步的个性化设置。</p>
<h2 id="步骤七:配置Nginx反向代理(可选)"><a href="#步骤七:配置Nginx反向代理(可选)" class="headerlink" title="步骤七:配置Nginx反向代理(可选)"></a>步骤七:配置Nginx反向代理(可选)</h2><p>为了更加安全和便捷地访问 Cloudreve,可以使用Nginx配置反向代理并启用HTTPS。</p>
<p>在网站的 server 段添加以下内容(实际应用中推荐强制 https,然后在 443 端口反代 5212 端口):</p>
<pre><code class="language-nginx">location / &#123;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://127.0.0.1:5212;
# 如果不使用本地存储策略,可以注释掉
client_max_body_size 20G; # 设置理论最大文件尺寸(这里即20 GB)
&#125;
</code></pre>
<p>启用这个配置并重新加载Nginx:</p>
<pre><code class="language-sh">systemctl restart nginx
</code></pre>
<p>至此,你应该可以通过你的域名访问 Cloudreve 云盘了。现在你可以开始使用这个强大的网盘系统来管理和分享你的文件了。如果你在配置过程中遇到任何问题,可以参考<a href="https://docs.cloudreve.org/">Cloudreve的文档</a>获取更多帮助。</p>
</content>
<category term="Configuration" scheme="https://nightfury.top/categories/Configuration/" />
<category term="Debian" scheme="https://nightfury.top/tags/Debian/" />
<category term="Github" scheme="https://nightfury.top/tags/Github/" />
<category term="Cloudreve" scheme="https://nightfury.top/tags/Cloudreve/" />
<updated>2024-07-05T06:38:20.000Z</updated>
</entry>
<entry>
<id>https://nightfury.top/2024/07/04/Debian12%E7%B3%BB%E7%BB%9FHexo%E9%83%A8%E7%BD%B2%E5%88%B0VPS/</id>
<title>Debian12系统Hexo部署到VPS</title>
<link rel="alternate" href="https://nightfury.top/2024/07/04/Debian12%E7%B3%BB%E7%BB%9FHexo%E9%83%A8%E7%BD%B2%E5%88%B0VPS/"/>
<content type="html"><p>网上大部分教程都是将 Hexo 部署到 GitHub Pages 上面,本文主要介绍如何部署到 VPS。</p>
<p>VPS 环境:Debian 12</p>
<h1 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h1><p>网上流传的武功秘籍分为两种:</p>
<ul>
<li>将 Hexo 项目上传到 VPS 上面后执行 <code>hexo server</code>,之后配置 Nginx 反向代理,让域名指向 <a href="http://localhost:4000。">http://localhost:4000。</a></li>
<li>将 Hexo 在本地通过 <code>hexo generate</code> 生成静态文件,在通过 <code>hexo deploy</code> 部署到 VPS 上面,使用 Nginx 直接做 Web 服务器。</li>
<li>相比第二种方式,第一种每次写博客与更新博客时候的操作会很繁琐。所以我们使用第二种方式进行部署,这样既可以将静态文件 deploy 到 VPS 上,也可以上传到 Github 上用作备份,操作性和安全性上都要胜于前者。</li>
<li>而对于第二种方式而言,常用的又有 <code>git hook</code> 和 <code>rsync </code>两种自动部署解决方案。</li>
</ul>
<p>本文主要介绍 <code>git hook</code> 部署过程。</p>
<h1 id="Git-Hooks-自动部署"><a href="#Git-Hooks-自动部署" class="headerlink" title="Git Hooks 自动部署"></a>Git Hooks 自动部署</h1><h2 id="部署原理"><a href="#部署原理" class="headerlink" title="部署原理"></a>部署原理</h2><p>我们在本地编辑文本,然后使用 Git 远程部署到 VPS 的 Git 仓库。<code>hexo d</code> 命令实际上只 deploy 了本地的 public 文件夹,Git Hooks 实际上就是当 Git 仓库收到最新的 push 时,将 Git 仓库接受到的内容复制到 VPS 上的网站目录内。相当于完成了手动将 public 文件夹复制到 VPS 的网站根目录里。</p>
<h2 id="安装配置-Git"><a href="#安装配置-Git" class="headerlink" title="安装配置 Git"></a>安装配置 Git</h2><h3 id="安装-Git"><a href="#安装-Git" class="headerlink" title="安装 Git"></a>安装 Git</h3><p>通过 SSH 连接 VPS,执行:<code>apt-get install git</code>,完成后通过 <code>git --version</code> 查看 Git 版本,若显示版本信息则说明安装成功。</p>
<h3 id="创建-git-用户"><a href="#创建-git-用户" class="headerlink" title="创建 git 用户"></a>创建 git 用户</h3><p>执行:<code>adduser git</code>,根据提示设置密码,其他信息可以一路空格。</p>
<h3 id="赋予-git-用户-sudo-权限"><a href="#赋予-git-用户-sudo-权限" class="headerlink" title="赋予 git 用户 sudo 权限"></a>赋予 git 用户 sudo 权限</h3><p>安装 sudo:</p>
<pre><code class="language-shell">apt update
apt install sudo
</code></pre>
<p>执行:</p>
<pre><code class="language-shell">chmod 740 /etc/sudoers
nano /etc/sudoers
</code></pre>
<p>找到以下内容:</p>
<pre><code class="language-shell">## User privilege specification
root ALL=(ALL:ALL) ALL
# 在 root ALL=(ALL:ALL) ALL 这一行下面添加
git ALL=(ALL:ALL) ALL
</code></pre>
<p>保存退出后,修改回文件权限:</p>
<pre><code class="language-shell">chmod 440 /etc/sudoers
</code></pre>
<h3 id="关闭-git-用户-shell-权限"><a href="#关闭-git-用户-shell-权限" class="headerlink" title="关闭 git 用户 shell 权限"></a>关闭 git 用户 shell 权限</h3><p>我们也可以通过:</p>
<pre><code class="language-shell">ssh git@VPS IP
</code></pre>
<p>ssh 连接服务器,登录到服务器上,对服务器进行各种操作,这通常很不安全,也不合适,我们只需要能对仓库操作就可以了,不需要更大的权限。</p>
<p>因此我们关闭 git 用户 shell 权限,执行:</p>
<pre><code class="language-shell">nano /etc/passwd
</code></pre>
<p>将最后一行的 <code>git:x:1001:1001:,,,:/home/git:/bin/bash</code> 修改为 <code>git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell</code></p>
<p>这样,git 用户可以正常通过 ssh 使用 git,但无法登录 shell,因为我们为 git 用户指定的 git-shell 每次一登录就自动退出。</p>
<h3 id="初始化-git-仓库"><a href="#初始化-git-仓库" class="headerlink" title="初始化 git 仓库"></a>初始化 git 仓库</h3><pre><code class="language-shell">cd /home/git # 切换到git用户目录
mkdir blog.git # 创建git仓库文件夹,以blog.git为例
cd blog.git # 进入仓库目录
git init --bare # 使用--bare参数初始化为裸仓库,这样创建的仓库不包含工作区
</code></pre>
<p>注意:裸仓库没有工作区,因为服务器上的 Git 仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的 Git 仓库通常都以 .git 结尾。</p>
<h3 id="创建网站目录"><a href="#创建网站目录" class="headerlink" title="创建网站目录"></a>创建网站目录</h3><pre><code class="language-shell">cd /home/wwwroot/ # 切换目录
# 由于我想直接将博客放在/home/wwwroot/下,所以并没有创建blog目录
mkdir blog # 创建网站目录,以blog为例
</code></pre>
<h3 id="配置-SSH"><a href="#配置-SSH" class="headerlink" title="配置 SSH"></a>配置 SSH</h3><pre><code class="language-shell">cd /home/git # 切换到git用户目录
mkdir .ssh # 创建.ssh目录
cd .ssh
nano authorized_keys
</code></pre>
<p>然后将本地的公钥复制到 <code>authorized_keys</code> 文件里 (公钥即本地执行 <code>cat ~/.ssh/id_rsa.pub</code> (如 <code>C:/Users/Your User Name/.ssh/id_rsa.pub</code>)查看的内容)。</p>
<p>如果本地没有公钥,可以在本地用 <code>ssh-keygen</code> 命令生成一个新的 ssh 密钥对:</p>
<pre><code class="language-shell">ssh-keygen -t rsa -C &quot;[email protected]&quot;
</code></pre>
<p>当系统提示输入文件名时,可以按 <code>Enter</code> 键使用默认路径(通常是<code>~/.ssh/id_rsa</code>),也可以指定一个新的路径。</p>
<pre><code class="language-shell">Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa): [Press Enter]
</code></pre>
<p> 你可以选择为私钥设置一个密码短语,以增加额外的安全性,这里可以直接按 <code>Enter</code> 键跳过。</p>
<pre><code class="language-shell">Enter passphrase (empty for no passphrase): [Type a passphrase or press Enter]
Enter same passphrase again: [Repeat the passphrase or press Enter]
</code></pre>
<p>生成完成后,系统会告诉你密钥对已经生成并存储在指定路径中。</p>
<pre><code class="language-shell">Your identification has been saved in /home/username/.ssh/id_rsa.
Your public key has been saved in /home/username/.ssh/id_rsa.pub.
# Windows -&gt; C:/Users/username/.ssh/id_rsa, C:/Users/username/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:... [email protected]
The key&#39;s randomart image is:
+---[RSA 2048]----+
| .==+. |
| =.o. |
| o . |
| . . o. |
| . oSo.o. |
| E .oBo= . |
| . .++.+ o |
| o+.o . o |
| +=o . . |
+----[SHA256]-----+
</code></pre>
<p>注意:收集所有需要登录的用户的公钥,就是他们自己的 <code>id_rsa.pub</code> 文件,把所有公钥导入到 <code>/home/git/.ssh/authorized_keys</code> 文件里,一行一个。</p>
<p>通常这样配置后,<code>hexo -d</code> 部署就不需要输入密码了,而是通过证书校验身份。</p>
<h3 id="用户组管理"><a href="#用户组管理" class="headerlink" title="用户组管理"></a>用户组管理</h3><p>查看 Nginx 运行用户:</p>
<pre><code class="language-shell">cat /etc/nginx/nginx.conf | grep user
</code></pre>
<p>输出示例:</p>
<pre><code class="language-shell">user www-data;
</code></pre>
<p>查看 Php-fpm 运行用户:</p>
<pre><code class="language-shell">cat /etc/php/8.2/fpm/pool.d/www.conf | grep -E &#39;^(user|group) =&#39;
</code></pre>
<p>输出示例:</p>
<pre><code class="language-shell">user = www-data
group = www-data
</code></pre>
<p>创建用户组 <code>webgroup</code> 并添加 <code>git</code> 和 <code>www-data</code> 到用户组:</p>
<pre><code class="language-shell">groupadd webgroup
usermod -aG webgroup git
usermod -aG webgroup www-data
</code></pre>
<p>更改文件夹的组:</p>
<pre><code class="language-shell">chgrp -R webgroup /home/wwwroot
</code></pre>
<p>更改文件和目录的所有者:</p>
<pre><code class="language-shell">chown -R git:git /home/git
chown -R www-data:webgroup /home/wwwroot
</code></pre>
<p>设置文件夹和文件的权限:</p>
<pre><code class="language-shell">chmod -R 770 /home/git
chmod -R 770 /home/wwwroot
</code></pre>
<p>设置 SGID 位,确保新创建的文件或文件夹自动继承该组:</p>
<pre><code class="language-shell"># 只设置单层目录的方法
# chmod g+s /home/git
# chmod g+s /home/wwwroot
# 递归设置所有子目录的 SGID 位
find /home/git -type d -exec chmod g+s &#123;&#125; \;
find /home/wwwroot -type d -exec chmod g+s &#123;&#125; \;
</code></pre>
<h2 id="安装配置-Nginx"><a href="#安装配置-Nginx" class="headerlink" title="安装配置 Nginx"></a>安装配置 Nginx</h2><p>由于我之前已经装过了 Nginx,可以参考前面的教程,以下nginx安装内容来源于网络,仅供参考。</p>
<h3 id="安装-Nginx"><a href="#安装-Nginx" class="headerlink" title="安装 Nginx"></a>安装 Nginx</h3><p>执行:<code>apt-get install nginx</code>,若输入 <code>nginx -V</code> 可以看到 nginx 版本信息,则安装成功。</p>
<h3 id="配置-nginx"><a href="#配置-nginx" class="headerlink" title="配置 nginx"></a>配置 nginx</h3><p>执行:</p>
<pre><code class="language-shell">cd /etc/nginx/sites-available # 切换目录
cp default default.bak # 备份默认配置
nano default # 修改配置
</code></pre>
<p>参考配置文件内容:</p>
<pre><code class="language-shell">server &#123;
listen 80 default; # 默认监听80端口
root /home/wwwroot; # 网站根目录
server_name nightfury.top, www.nightfury.top; # 网址
access_log /var/log/nginx/blog_access.log;
error_log /var/log/nginx/blog_error.log;
error_page 404 = /404.html;
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ &#123;
root /home/wwwroot;
access_log off;
expires 1d;
&#125;
location ~* ^.+\.(css|js|txt|xml|swf|wav)$ &#123;
root /home/wwwroot;
access_log off;
expires 10m;
&#125;
location / &#123;
root /home/wwwroot;
if (-f $request_filename) &#123;
rewrite ^/(.*)$ /$1 break;
&#125;
&#125;
location /nginx_status &#123;
stub_status on;
access_log off;
&#125;
&#125;
</code></pre>
<p>保存退出后,启动 nginx:</p>
<pre><code class="language-shell">systemctl start nginx
</code></pre>
<p>设置开机自动启动:</p>
<pre><code class="language-shell">systemctl enable nginx
</code></pre>
<p>查看运行状态:</p>
<pre><code class="language-shell">systemctl status nginx
</code></pre>
<p>显示 running 表示成功运行。</p>
<h2 id="配置-Git-Hooks"><a href="#配置-Git-Hooks" class="headerlink" title="配置 Git Hooks"></a>配置 Git Hooks</h2><h3 id="创建-post-receive-文件"><a href="#创建-post-receive-文件" class="headerlink" title="创建 post-receive 文件"></a>创建 post-receive 文件</h3><p>root 用户下执行(或者 git 用户下执行,就不需要 <code>sudo -u git</code>)</p>
<pre><code class="language-shell">cd /home/git/blog.git/hooks # 切换到hooks目录下
sudo -u git nano post-receive
</code></pre>
<p>复制下面的内容到 <code>post-receive</code> 文件中:</p>
<pre><code class="language-shell">#!/bin/bash
echo &quot;post-receive hook is running...&quot;
GIT_REPO=/home/git/blog.git
TMP_GIT_CLONE=/tmp/blog
PUBLIC_WWW=/home/wwwroot
rm -rf $&#123;TMP_GIT_CLONE&#125;
git clone $GIT_REPO $TMP_GIT_CLONE
# 如果想每次更新都删除掉原有所有内容则用下面这条命令
# rm -rf $&#123;PUBLIC_WWW&#125;/*
# 如果想每次更新都删除掉原有所有内容但排除掉一些文件(深度为1)则用下面这条命令(比如排除掉 .mp4 和 .png 文件)
find $&#123;PUBLIC_WWW&#125; -maxdepth 1 -mindepth 1 ! -name &#39;.mp4&#39; ! -name &#39;*.png&#39; -exec rm -rf &#123;&#125; +
cp -rf $&#123;TMP_GIT_CLONE&#125;/* $&#123;PUBLIC_WWW&#125;
</code></pre>
<hr>
<p>为什么不直接将裸仓库克隆到 Web 根目录下呢?我之前也一直被这个问题困扰,感觉先克隆到 tmp 目录再拷贝到 Web 根目录是多此一举。后来我觉得可能是出于项目安全的考虑,在执行 cp 命令的时候,.git 作为隐藏目录不会被拷贝到 Web 根目录下,也就避免了将整个仓库历史暴露在 Web 服务中。</p>
<hr>
<p>赋予可执行权限:</p>
<pre><code class="language-shell">chmod +x post-receive
</code></pre>
<h2 id="本地操作"><a href="#本地操作" class="headerlink" title="本地操作"></a>本地操作</h2><h3 id="尝试连接"><a href="#尝试连接" class="headerlink" title="尝试连接"></a>尝试连接</h3><p>在本地打开 Git Bash:</p>
<pre><code class="language-shell">ssh git@VPS的ip
</code></pre>
<p>若默认端口不是 22,则需要在后面加上 -p 端口号:</p>
<pre><code class="language-shell">ssh git@VPS的ip -p 2024
</code></pre>
<p>由于前面配置了证书,所以正常应该是不需要输入密码了,如果仍然需要输入密码,请检查 <code>/home/git/.ssh</code> 和 <code>/home/git/.ssh/authorized_keys</code> 的权限和所属用户&#x2F;组是否正确,可以用如下命令查询 ssh 日志:</p>
<pre><code class="language-shell">journalctl -u sshd -xe
</code></pre>
<p>ssh 连接成功后返回结果应该如下:</p>
<pre><code class="language-shell">-&gt;ssh [email protected]
-&gt;Linux XXX #1 SMP PREEMPT_DYNAMIC Debian XXX
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to nightfury.top closed.
</code></pre>
<p>提示无法登录 shell 是正常的,因为我们在之前就为 git 用户指定了 git-shell 每次一登录就自动退出。</p>
<h2 id="配置-Hexo"><a href="#配置-Hexo" class="headerlink" title="配置 Hexo"></a>配置 Hexo</h2><p>打开本地博客根目录下的_config.yml 文件,找到最后的 deploy 配置,修改为:</p>
<pre><code class="language-shell">## Deployment
## Docs: https://hexo.io/docs/deployment.html
deploy:
- type: git
repo: [email protected]:szNightFury/szNightFury.github.io.git
branch: master
- type: git
repo: [email protected]:/home/git/blog.git
branch: master
</code></pre>
<p>到此,Hexo 建站就全部配置部署完毕了。</p>
</content>
<category term="Configuration" scheme="https://nightfury.top/categories/Configuration/" />
<category term="Hexo" scheme="https://nightfury.top/tags/Hexo/" />
<category term="Debian" scheme="https://nightfury.top/tags/Debian/" />
<category term="Github" scheme="https://nightfury.top/tags/Github/" />
<updated>2024-07-04T04:34:08.000Z</updated>
</entry>
<entry>
<id>https://nightfury.top/2024/07/04/Debian12%E9%85%8D%E7%BD%AENginx1.22%E4%B8%8EPhp8.2/</id>
<title>Debian12配置Nginx1.22与Php8.2</title>
<link rel="alternate" href="https://nightfury.top/2024/07/04/Debian12%E9%85%8D%E7%BD%AENginx1.22%E4%B8%8EPhp8.2/"/>
<content type="html"><h1 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h1><ul>
<li>操作系统:Debian 12</li>
<li>Nginx:1.22.1</li>
<li>Php:8.2.20</li>
<li>Php-fpm:php8.2-fpm</li>
</ul>
<h1 id="实验步骤"><a href="#实验步骤" class="headerlink" title="实验步骤"></a>实验步骤</h1><h2 id="安装程序包与依赖"><a href="#安装程序包与依赖" class="headerlink" title="安装程序包与依赖"></a>安装程序包与依赖</h2><pre><code class="language-shell"># 更新软件包列表
apt update
# 安装程序包
apt install nginx
apt install php php-fpm php-xml php-json php-curl php-mbstring
</code></pre>
<h2 id="配置Nginx"><a href="#配置Nginx" class="headerlink" title="配置Nginx"></a>配置Nginx</h2><pre><code class="language-shell"># 查看 php-fpm 监听配置
nano /etc/php/8.2/fpm/pool.d/www.conf
# 41行取消注释:
listen = /run/php/php8.2-fpm.sock
nano /etc/nginx/conf.d/xxx.conf
# 在 index.html 前面加入 index.php
# 增加:
location ~ \.php$ &#123;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock; # 注意路径与前面的监听配置一致
&#125;
</code></pre>
<h2 id="修改时区"><a href="#修改时区" class="headerlink" title="修改时区"></a>修改时区</h2><pre><code class="language-shell"># 查看当前时区
timedatectl
# 输出示例:
Local time: Tue 2024-07-03 14:00:00 UTC
Universal time: Tue 2024-07-03 14:00:00 UTC
RTC time: Tue 2024-07-03 14:00:00
Time zone: UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
# 设置 PRC 时区
timedatectl set-timezone Asia/Shanghai
# 验证时区更改
timedatectl
# 输出示例:
Local time: Tue 2024-07-03 22:00:00 CST
Universal time: Tue 2024-07-03 14:00:00 UTC
RTC time: Tue 2024-07-03 14:00:00
Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
# 配置 Php 时区
nano /etc/php/8.2/fpm/php.ini
# 979行增加
date.timezone = Asia/Shanghai
</code></pre>
<h2 id="重启服务"><a href="#重启服务" class="headerlink" title="重启服务"></a>重启服务</h2><pre><code class="language-shell">service php8.2-fpm restart # systemctl restart php8.2-fpm
service nginx restart # systemctl restart nginx
</code></pre>
<h2 id="调试步骤"><a href="#调试步骤" class="headerlink" title="调试步骤"></a>调试步骤</h2><pre><code class="language-shell"># 检查 Nginx 访问日志
tail -f /var/log/nginx/access.log
# 查看 Php-fpm 错误日志
tail -f /var/log/php8.2-fpm.log
</code></pre>
<h1 id="验收"><a href="#验收" class="headerlink" title="验收"></a>验收</h1><p>编写任意 php 文件,比如说简单的有 index.php:</p>
<pre><code class="language-html">&lt;h1&gt;
&lt;span&gt; Hello, this is test page &lt;/span&gt;
&lt;/h1&gt;
</code></pre>
<p>或者 php 探针:</p>
<pre><code class="language-php">&lt;?php
phpinfo();
?&gt;
</code></pre>
<p>访问该 php 地址,得到正确的返回结果。完结,Move On!</p>
</content>
<category term="Configuration" scheme="https://nightfury.top/categories/Configuration/" />
<category term="Debian" scheme="https://nightfury.top/tags/Debian/" />
<category term="Php" scheme="https://nightfury.top/tags/Php/" />
<category term="Nginx" scheme="https://nightfury.top/tags/Nginx/" />
<updated>2024-07-04T03:59:23.000Z</updated>
</entry>
<entry>
<id>https://nightfury.top/2023/09/11/table/</id>
<title>table</title>
<link rel="alternate" href="https://nightfury.top/2023/09/11/table/"/>
<content type="html"><table>
<thead>
<tr>
<th>refill</th>
<th>0</th>
<th>0</th>
<th>0</th>
<th>1</th>
<th>1</th>
<th>1</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th>0</th>
<th>0</th>
<th>0</th>
<th>1</th>
<th>1</th>
<th>1</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td>refillPush</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>push</td>
<td>push</td>
<td>push</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>push</td>
<td>push</td>
<td>push</td>
<td></td>
</tr>
<tr>
<td></td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td>refillOrPush</td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>refill</td>
<td></td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>reuse</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>2</td>
<td>2</td>
<td>2</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td>refillPop</td>
<td>refillPop</td>
<td>refillPop</td>
<td>refillPop</td>
<td>refillPop</td>
<td>refillPop</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td>orPush,orPop做判断</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td>reusePop</td>
<td></td>
<td></td>
<td></td>
<td></td>