第1步

  1. 搞一个2核2G的服务器
    我用学生300元的阿里云代金卷白嫖了一个2核2G的服务器
    配置:2核2G
    使用 CentOS Stream 10 64位 系统
  2. 咱们直接用阿里云专属镜像源装Docker
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
# 1. 安装必要的工具(Stream 10 最小化安装可能缺这些)
dnf install -y yum-utils device-mapper-persistent-data lvm2

# 2. 添加阿里云的 Docker 镜像源(适配 CentOS Stream 10)
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 3. 由于是 Stream 10,需要把源里的 $releasever 改成 10(否则会报错找不到源)
sed -i 's/$releasever/10/g' /etc/yum.repos.d/docker-ce.repo

# 4. 安装 Docker 核心(社区版)
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 5. 启动 Docker 并设置开机自启
systemctl start docker
systemctl enable docker

# 6. 配置阿里云镜像加速器(让你拉镜像像坐火箭)
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://kzwy2ejs.mirror.aliyuncs.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker

第2步

部署一个 Redis 7
AI 给的docker run -d --restart=always --name=my-redis -p 6379:6379 redis:7-alpine不能用
进入https://hub.docker.com/_/redis网址一探究竟
使用 docker pull redis:7.4.10-alpine

点击 “添加出方向规则”,按以下配置填写:

字段 填写内容
授权策略 允许
优先级 100
协议类型 自定义 TCP
访问来源(本实例) 留空或默认(表示从本机发起)
访问目的 0.0.0.0/0(允许访问所有外网 IP)
端口范围 1/65535(允许所有端口)
描述 允许所有出站流量

一直不行,更改镜像源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo mkdir -p /etc/docker # 创建 Docker 配置目录
# 配置镜像源
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.xuanyuan.me",
"https://dc.j8.work"
]
}
EOF

sudo systemctl daemon-reload # 重新加载 Docker 配置
sudo systemctl restart docker # 重启 Docker
docker info | grep -A 5 "Registry Mirrors" # 检查镜像源

使用 docker pull redis:7-alpine 命令拉取镜像
执行 docker run -d --restart=always --name=my-redis -p 6379:6379 redis:7-alpine 命令启动 Redis 容器
docker ps 命令查看容器运行状态
docker exec -it my-redis redis-cli ping 如果返回 PONG,恭喜你,一个运行在容器里的 Redis 7 已经就绪!

第3步

docker exec -it my-redis redis-cli 进入 Redis 命令行

1
2
3
4
SET user:1001 "zhangsan" # 设置键值对
GET user:1001 # 获取键对应的值
EXPIRE user:1001 60 # 设置键的过期时间
TTL user:1001 # 获取键的剩余过期时间

第4步

开发一个最小web应用
model
LogEntry.java

1
2
3
4
5
6
7
8
9
@Data
public class LogEntry {
// 日志id
private String id;
// 日志内容
private String content;
// 创建时间
private LocalDateTime createTime;
}

service
LogService.java

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
@Service
public class LogService {

@Autowired
private StringRedisTemplate redisTemplate;

private static final String LOGS_KEY = "work_logs";

// 获取今日已写条数
public Long getTodayCount() {
String todayKey = "stats:" + LocalDate.now().toString();
return redisTemplate.opsForHyperLogLog().size(todayKey);
}

// 新增日志
public void saveLog(String content) {
String id = UUID.randomUUID().toString();
String timestamp = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 存到 Redis 的 List 中(左侧插入,最新的在前面)
String logEntry = id + "|" + content + "|" + timestamp;
redisTemplate.opsForList().leftPush(LOGS_KEY, logEntry);
// 设置过期时间(30天自动清理)
redisTemplate.expire(LOGS_KEY, 30, java.util.concurrent.TimeUnit.DAYS);

// 统计今日条数
String todayKey = "stats:" + LocalDate.now().toString();
redisTemplate.opsForHyperLogLog().add(todayKey, id);
redisTemplate.expire(todayKey, 30, TimeUnit.DAYS);
}

// 获取最近 N 条日志
public List<LogEntry> getRecentLogs(int limit) {
List<String> rawList = redisTemplate.opsForList().range(LOGS_KEY, 0, limit - 1);
List<LogEntry> result = new ArrayList<>();
if (rawList != null) {
for (String raw : rawList) {
String[] parts = raw.split("\\|", 3);
if (parts.length == 3) {
LogEntry entry = new LogEntry();
entry.setId(parts[0]);
entry.setContent(parts[1]);
entry.setCreateTime(LocalDateTime.parse(parts[2],
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
result.add(entry);
}
}
}
return result;
}
}

controller
LogController.java

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
@Controller
public class LogController {

@Autowired
private LogService logService;

// 显示日志列表页面
@GetMapping("/logs")
public String listLogs(Model model) {
List<LogEntry> logs = logService.getRecentLogs(20);
model.addAttribute("logs", logs);
model.addAttribute("logs", logService.getRecentLogs(20));
model.addAttribute("todayCount", logService.getTodayCount());
return "logs"; // 对应 templates/logs.html
}

// 处理新增日志请求
@PostMapping("/logs/save")
public String saveLog(@RequestParam("content") String content) {
if (content != null && !content.trim().isEmpty()) {
logService.saveLog(content.trim());
}
return "redirect:/logs";
}

// 导出日志
@GetMapping("/logs/export")
public String exportLogs(Model model) {
List<LogEntry> logs = logService.getRecentLogs(1000);
model.addAttribute("logs", logs);
return "export"; // 新建一个 export.html 页面,用 <pre> 显示纯文本
}
}

templates
log.html

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
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>每日一记 - 工作日志</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 40px auto; padding: 20px; }
input[type="text"] { width: 70%; padding: 10px; }
button { padding: 10px 20px; background: #4CAF50; color: white; border: none; cursor: pointer; }
.log-item { border-bottom: 1px solid #eee; padding: 10px 0; }
.time { color: #888; font-size: 0.9em; }
</style>
</head>
<body>
<h2>📊 今日已写 <span th:text="${todayCount}">0</span></h2>
<h1>📝 每日一记</h1>

<form action="/logs/save" method="post">
<input type="text" name="content" placeholder="今天做了什么?" required>
<button type="submit">记录</button>
</form>

<h2>📋 最近日志</h2>
<div th:if="${logs != null && logs.size() > 0}">
<div class="log-item" th:each="log : ${logs}">
<div th:text="${log.content}">日志内容</div>
<div class="time" th:text="${log.createTime}">时间</div>
</div>
</div>
<p th:if="${logs == null || logs.size() == 0}" style="color: #888;">还没有日志,开始记录吧!</p>
</body>
</html>

第5步

开放6379端口
给Redis加密
docker exec -it my-redis redis-cli CONFIG SET requirepass "wgfun@Redis"
docker exec -it my-redis redis-cli -a "wgfun@Redis" ping 测试密码是否正确

安装JDK21
dnf install java-21-openjdk-devel -y
上传服务器
scp target/WorkLog-0.0.1-SNAPSHOT.jar root@115.28.210.219:/root/
启动SpringBoot项目
nohup java -jar /root/WorkLog-0.0.1-SNAPSHOT.jar > /root/app.log 2>&1 &
打开浏览器访问 http://115.28.210.219:8080/logs

第六步

思考