| Id |
Title |
Content |
Published |
Framework Id |
User Id |
Created |
Modified |
Actions |
| 1 |
Ubuntu24.04安装Apache2和PHP8 |
<p>先安装Aapache</p>
<pre class=""><code class="language-bash hljs">sudo apt install apache2</code></pre>
<p> </p>
<p>然后安装PHP的支持</p>
<pre class=""><code class="language-bash hljs">sudo apt install php libapache2-mod-php php-cli php-mysql php-curl php-intl php-gd php-mbstring php-xml php-zip -y
sudo systemctl restart apache2</code></pre>
<p> </p>
<p>最后重启Apache</p>
<pre class=""><code class="language-bash hljs">sudo systemctl restart apache2</code></pre>
<p> </p> |
1 |
34 |
1 |
1/4/26, 6:29 PM |
1/22/26, 10:55 AM |
View Edit Delete |
| 2 |
Ubuntu24.04安装MySQL8.4 |
<p>1、添加 MySQL 官方 APT 仓库</p>
<pre class=""><code class="language-bash hljs">wget https://dev.mysql.com/get/mysql-apt-config_0.8.35-1_all.deb
sudo dpkg -i mysql-apt-config_0.8.35-1_all.deb</code></pre>
<p> </p>
<p>2、更新软件源并安装</p>
<pre class=""><code class="language-bash hljs"># 如果之前有安装过其他版本的MySQL单纯只是apt remove mysql-server是不够的。需要先清理失效的源文件,如果没有就忽略
rm -f /etc/apt/sources.list.d/mysql.list
sudo apt update
sudo apt install mysql-server
# 进入MySQL
mysql </code></pre>
<p> </p>
<p>3、允许远程登录并给root设置登录密码</p>
<pre class=""><code class="language-sql hljs">-- MySQL 8以上用标准的DCL 语句(不用 UPDATE 系统表,无 FLUSH)
use mysql;
RENAME USER 'root'@'localhost' TO 'root'@'%';
ALTER USER 'root'@'%' IDENTIFIED WITH caching_sha2_password BY '123456';
-- 特殊情况再用以下方法
use mysql;
UPDATE user SET Host='%' WHERE User='root' AND Host='localhost';
ALTER USER 'root'@'%' IDENTIFIED WITH caching_sha2_password BY '你的强密码';
</code></pre>
<p> </p>
<p>4、刷新权限</p>
<pre class=""><code class="language-sql hljs">FLUSH PRIVILEGES;</code></pre>
<p> </p>
<p>5、创建可远程登录的新用户</p>
<pre class=""><code class="language-sql hljs">CREATE USER '帐号'@'%' IDENTIFIED BY '密码';</code></pre>
<p> </p>
<p>6、给新用户对应表的使用权</p>
<pre class=""><code class="hljs language-sql">GRANT ALL PRIVILEGES ON 表名.* TO '帐号'@'%';</code></pre>
<p> </p>
<p>7、刷新权限</p>
<pre class=""><code class="language-sql hljs">FLUSH PRIVILEGES;</code></pre>
<p> </p>
<p> </p> |
1 |
34 |
1 |
1/4/26, 6:38 PM |
7/17/26, 3:47 PM |
View Edit Delete |
| 3 |
Ubuntu24.04及以下版本安装配置SVN并设置钩子 |
<p>1、下载安装</p>
<pre class=""><code class="language-bash hljs">sudo apt install subversion</code></pre>
<p> </p>
<p>2、选择SVN服务文件及配置文件的放置位置</p>
<pre class=""><code class="language-bash hljs">sudo mkdir /svn</code></pre>
<p> </p>
<p>3、建立版本仓库</p>
<pre class=""><code class="language-bash hljs">sudo svnadmin create /svn/timeless</code></pre>
<p> </p>
<p>4、修改配置并修改对应参数(这么改就可以帐号密码登录了),顶格书写</p>
<pre class=""><code class="language-bash hljs">cd /svn/timeless/conf
sudo vim svnserve.conf</code></pre>
<p> </p>
<pre class=""><code class="language-bash hljs">anon-access = none
auth-access = write
password-db = passwd
authz-db = authz</code></pre>
<p> </p>
<p>5、设置用户密码</p>
<pre class=""><code class="language-bash hljs">sudo vim passwd</code></pre>
<p> </p>
<pre class=""><code class="language-bash hljs">[users]
yonghuming=mima</code></pre>
<p> </p>
<p>6、设置权限</p>
<pre class=""><code class="language-bash hljs">vim authz</code></pre>
<p> </p>
<pre class=""><code class="language-bash hljs">[aliases]
[groups]
admin = yonghuming
[/]
@admin = rw
</code></pre>
<p> </p>
<p>7、启动svn</p>
<pre class=""><code class="language-bash hljs">sudo svnserve -d -r /svn/</code></pre>
<p> </p>
<p>8、设置钩子</p>
<ul>
<li>先把设置钩子的路径确定好,并且checkout出来
<pre class=""><code class="language-bash hljs">svn co svn://192.168.1.1/svn/timeless /Projects/timeless ./ --username yonghuming --password mima</code></pre>
</li>
</ul>
<p> </p>
<ul>
<li>进入仓库目录下的的hooks目录
<pre class=""><code class="hljs language-bash">cd /svn/hooks/</code></pre>
</li>
</ul>
<p> </p>
<ul>
<li>修改/添加post-commit文件
<pre class=""><code class="language-bash hljs">#!/bin/bash
export LANG=en_US.UTF-8
#export PATH=$PATH:/var/www/console
/usr/bin/svn update /项目路径 --username yonghuming--password mima --no-auth-cache
</code></pre>
</li>
</ul>
<p> </p>
<ul>
<li>给post-commit执行权限
<pre class=""><code class="language-bash hljs">chmod 777 post-commit</code></pre>
</li>
</ul>
<p> </p>
<p>9、重启svn服务</p>
<pre class=""><code class="language-bash hljs">killall svnserve
sudo svnserve -d -r /svn/</code></pre>
<p><br /><br /></p>
<p><br /><br /></p> |
1 |
34 |
1 |
1/5/26, 3:03 PM |
1/22/26, 10:54 AM |
View Edit Delete |
| 4 |
Ubuntu中 禁止Apache2可以通过IP直接访问 |
<p>1、如果对应的ip或者域名没有配置虚拟主机,会默认访问第一个配置,第一个虚拟主机配置通常是在000-default.conf中,</p>
<pre class=""><code class="hljs language-bash">cd /etc/apache2/sites-available</code></pre>
<p> </p>
<p>2、编辑 000-default.conf</p>
<pre class=""><code class="language-bash hljs">vim 000-default.conf</code></pre>
<p> </p>
<p>3、在DocumentRoot /var/www/html下面增加</p>
<pre class=""><code class="language-bash hljs">RewriteEngine On
RewriteCond %{HTTP_HOST} !^(www\.)?你的域名\.com$ [NC]
RewriteRule ^ - [F]</code></pre>
<p> </p> |
1 |
34 |
1 |
1/6/26, 1:46 PM |
1/22/26, 10:47 AM |
View Edit Delete |
| 11 |
Ubuntu24.04安装Oracle JDK 25 |
<p>1、官网下载deb格式的安装包(x64 Debian Package)</p>
<pre class=""><code class="language-bash hljs">https://www.oracle.com/java/technologies/downloads/</code></pre>
<p> </p>
<p>2、将deb文件存放到/目录下去安装,不要在~目录下去安装,不然会产生权限问题</p>
<p> </p>
<p>3、给文件权限</p>
<pre class=""><code class="language-bash hljs">chmod 777 jdk-25_linux-x64_bin.deb</code></pre>
<p> </p>
<p>4、sudo apt install ./jdk-25_linux-x64_bin.deb</p>
<p><br /><br /></p> |
1 |
34 |
1 |
1/9/26, 2:00 AM |
1/22/26, 10:44 AM |
View Edit Delete |
| 12 |
Ubuntu 压缩/解压缩 |
<p>压缩</p>
<pre class=""><code class="language-bash hljs">tar -czvf 文件名.tar.gz 文件或目录</code></pre>
<p> </p>
<p>解压缩</p>
<pre class=""><code class="language-bash hljs">tar -xzvf 文件名.tar.gz</code></pre>
<p><br />ZIP解压缩</p>
<pre class=""><code class="language-sql hljs">apt update && apt install unzip
unzip xxxxx.zip</code></pre>
<p> </p> |
1 |
34 |
1 |
1/11/26, 7:42 AM |
7/6/26, 3:24 AM |
View Edit Delete |
| 13 |
Ubuntu24.04安装Docker下的Ubuntu16.04+Apache2+PHP7+MySQL5.7环境 |
<p>1、设置yml文件</p>
<p>docker-compose.yml</p>
<pre class=""><code class="language-yaml hljs">version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
container_name: ubuntu-apache-php
ports:
- "8080:80"
volumes:
- /Projects/PHP/qmjt_docker:/var/www/html
- /docker/qmjt/apache2/logs:/var/log/apache2
restart: unless-stopped
networks:
- timeless-network
depends_on:
- mysql # 确保 MySQL 服务先启动
mysql:
image: mysql:5.7 # 使用官方 MySQL 5.7 镜像
container_name: mysql57
restart: always
environment:
MYSQL_ROOT_PASSWORD: pass # 设置 root 用户密码
MYSQL_DATABASE: database # 容器启动时创建的默认数据库
MYSQL_USER: user1 # (可选)创建新用户
MYSQL_PASSWORD: pass1 # (可选)新用户的密码
ports:
- "3307:3306" # 将宿主机的 3307 端口映射到容器的 3306 端口,避免与宿主机上可能已运行的 MySQL 服务冲突
volumes:
- /docker/mysql57/mysql_data:/var/lib/mysql # 持久化数据卷
- /docker/mysql57/my.cnf:/etc/mysql/conf.d/my.cnf # 自定义配置文件(可选)
networks:
- timeless-network
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --default-authentication-plugin=mysql_native_password # 确保使用 PHP 7.2 兼容的认证插件
networks:
timeless-network:
name: timeless
driver: bridge
volumes:
mysql_data: # 命名卷,确保数据库数据持久化</code></pre>
<p> </p>
<p>2、设置Dockerfile文件</p>
<p>Dockerfile</p>
<pre class=""><code class="hljs language-bash">FROM ubuntu:16.04
# 设置非交互式前端,避免安装过程中提示
ENV DEBIAN_FRONTEND=noninteractive
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
# 安装Apache、PHP及常用扩展
RUN apt-get update && \
apt-get install -y --no-install-recommends \
software-properties-common \
apache2 \
&& add-apt-repository ppa:ondrej/php -y \
&& apt-get update && \
apt-get install -y \
php \
php-cli \
php-common \
php-intl \
php-curl \
php-gd \
php-json \
php-mbstring \
php-xml \
php-zip \
php-mysql \
libapache2-mod-php \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# 启用Apache的rewrite模块,并配置DocumentRoot
RUN a2enmod rewrite
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
# 将Apache设置为前台运行,这是容器保持运行的关键
CMD ["apache2ctl", "-D", "FOREGROUND"]
# 设置工作目录
WORKDIR /var/www/html
RUN chmod -R 777 /var/www/html</code></pre>
<p> </p>
<p>3、对应的000-default.conf文件,主要是要在默认的文件里面加上<Directory /var/www/html>这段</p>
<p>000-default.conf</p>
<pre class=""><code class="hljs language-bash"><VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
<Directory /var/www/html>
Options FollowSymLinks
AllowOverride All
Order allow,deny
allow from all
Require all granted
</Directory>
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>
</code></pre>
<p> </p>
<p>4、都设置好放在同一目录下后过后,用以下命令启动</p>
<pre class=""><code class="hljs language-undefined">docker compose up</code></pre>
<p> </p>
<p>5、该环境数据库的host要用mysql</p> |
1 |
34 |
1 |
1/12/26, 9:08 AM |
1/22/26, 10:37 AM |
View Edit Delete |
| 21 |
Ubuntu24.04中Nginx 配置vue3项目 |
<p>1、在/etc/nginx/sites-available/中新建一个文件比如timeless</p>
<pre class=""><code class="language-makefile hljs">server {
listen 80;
listen [::]:80;
error_log /var/log/nginx/timeless_error.log;
access_log /var/log/nginx/timeless_access.log;
# 项目路径
root /Projects/Vue3/xxxxx;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name domain.com www.domain.com;
# vue3的项目必须加try_files $uri $uri/ /index.html;
location / {
try_files $uri $uri/ /index.html;
}
# 反向代理,接口转发
location /api/ {
proxy_pass http://xxxx.com/;
}
}
</code></pre>
<p> </p>
<p>2、然后在/etc/nginx/sites-enabled中,把该文件软链过去</p>
<pre class=""><code class="hljs language-bash">ln -s ../sites-available/timeless</code></pre>
<p> </p>
<p>3、重启</p>
<pre class=""><code class="hljs language-undefined">systemctl reload nginx
systemctl restart nginx</code></pre>
<p> </p> |
1 |
34 |
1 |
1/13/26, 8:42 AM |
1/22/26, 10:41 AM |
View Edit Delete |
| 22 |
Ubuntu24.04下 svn的导入导出 |
<p>导出命令</p>
<pre class=""><code class="hljs language-bash">sudo svnadmin dump /svn/sports > /tmp/sports_repo.dump</code></pre>
<p> </p>
<p>导入命令</p>
<pre class=""><code class="hljs language-lua">sudo svnadmin create /svn/sports_new
sudo svnadmin load /svn/sports_new < /path/to/sports_repo.dump</code></pre> |
1 |
34 |
1 |
1/13/26, 9:22 AM |
1/22/26, 10:40 AM |
View Edit Delete |
| 23 |
Ubuntu24.04更换国内源 |
<p>1、切换到下面目录</p>
<pre class=""><code class="language-bash hljs">cd /etc/apt/sources.list.d</code></pre>
<p> </p>
<p>2、修改源文件</p>
<pre class=""><code class="language-bash hljs">sudo vim ubuntu.sources</code></pre>
<p> </p>
<p>3、替换以下清华镜像源,原来的可以删除</p>
<pre class=""><code class="language-makefile hljs">Types: deb
URIs: https://mirrors.ustc.edu.cn/ubuntu/
Suites: noble noble-updates noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: https://mirrors.aliyun.com/ubuntu/
Suites: noble noble-updates noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: https://repo.huaweicloud.com/ubuntu/
Suites: noble noble-updates noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIS: https://mirrors.tuna.tsinghua.edu.cn/ubuntu/
Suites: noble noble-updates noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg</code></pre>
<p> </p>
<p>4、测试,如果是从镜像源下载的,说明配置成功了</p>
<pre class=""><code class="hljs language-sql">sudo apt update
sudo apt -y upgrade</code></pre>
<p> </p> |
1 |
34 |
1 |
1/13/26, 9:27 AM |
4/16/26, 1:46 PM |
View Edit Delete |
| 24 |
Ubuntu24.04安装ElasticSearch |
<p>1、下载deb文件(默认端口9200、默认只支持https、安装信息中可以看到密码,默认账号elastic)</p>
<pre class=""><code class="hljs language-bash">https://www.elastic.co/downloads/elasticsearch</code></pre>
<p> </p>
<p>2、安装</p>
<pre class=""><code class="language-bash hljs">sudo dpkg -i elasticsearch-9.2.2-amd64.deb</code></pre>
<p> </p>
<p>3、将es添加到开机启动</p>
<pre class=""><code class="hljs language-bash">sudo systemctl enable elasticsearch.service</code></pre>
<p> </p>
<p>4、安装IK分词器</p>
<pre class=""><code class="language-bash hljs">cd /usr/share/elasticsearch
# 版本号写自己的es版本
bin/elasticsearch-plugin install https://get.infini.cloud/elasticsearch/analysis-ik/9.2.2
sudo systemctl restart elasticsearch</code></pre>
<p> </p>
<p>5、从https改为http访问</p>
<pre class=""><code class="language-bash hljs">vim /etc/elasticsearch/elasticsearch.yml</code></pre>
<p> </p>
<pre class=""><code class="language-yaml hljs"># 把true改为false就默认http了
xpack.security.http.ssl:
enabled: true
keystore.path: certs/http.p12
</code></pre>
<p> </p>
<pre class=""><code class="language-bash hljs">sudo systemctl restart elasticsearch</code></pre>
<p> </p>
<p><br /><br /></p> |
1 |
34 |
1 |
1/13/26, 10:33 AM |
1/22/26, 10:39 AM |
View Edit Delete |
| 44 |
Ubuntu24.04设置Nginx自动重启 |
<p>1、创建 override 目录</p>
<pre class=""><code class="language-bash hljs">sudo mkdir -p /etc/systemd/system/nginx.service.d</code></pre>
<p> </p>
<p>2、创建自动重启配置</p>
<pre class=""><code class="language-bash hljs">sudo tee /etc/systemd/system/nginx.service.d/restart.conf <<'EOF'
[Service]
# 进程异常退出时自动重启
Restart=on-failure
# 正常退出也重启(可选,更严格)
# Restart=always
# 退出后延迟 5 秒重启
RestartSec=5s
# 重启限制(防止频繁崩溃)
StartLimitInterval=60s
StartLimitBurst=5
# 优雅停止超时时间
TimeoutStopSec=10s
EOF</code></pre>
<p> </p>
<p>3、重载 systemd 配置</p>
<pre class=""><code class="language-bash hljs">sudo systemctl daemon-reload</code></pre>
<p> </p>
<p>4、重启 nginx 使配置生效</p>
<pre class=""><code class="language-bash hljs">sudo systemctl restart nginx</code></pre>
<p> </p>
<p>5、验证配置生效</p>
<pre class=""><code class="language-bash hljs">systemctl show nginx | grep -E "Restart|StartLimit|TimeoutStopSec"</code></pre> |
1 |
34 |
1 |
2/9/26, 7:50 AM |
2/9/26, 9:54 AM |
View Edit Delete |
| 56 |
Ubuntu24.04安装redis并设置初始密码和允许远程登录 |
<p>1、安装redis</p>
<pre class=""><code class="hljs language-undefined">sudo apt install redis-server</code></pre>
<p> </p>
<p>2、查看redis版本</p>
<pre class=""><code class="hljs language-css">redis-server --version</code></pre>
<p> </p>
<p>3、编辑配置文件,用于设置密码</p>
<pre class=""><code class="hljs language-bash">sudo vim /etc/redis/redis.conf</code></pre>
<p> </p>
<p>4、找到# requirepass foobared 这一行,去掉井号空格,让r定格写,把foobared替换成你的密码</p>
<pre class=""><code class="hljs language-undefined">requirepass timeless_password</code></pre>
<p>5、找到bind 127.0.0.1 -::-1 改为</p>
<pre class=""><code class="hljs language-bash">bind 0.0.0.0 ::</code></pre>
<p> </p>
<p>6、重启redis服务</p>
<pre class=""><code class="hljs language-undefined">sudo systemctl restart redis-server</code></pre> |
1 |
34 |
1 |
2/21/26, 2:08 PM |
2/24/26, 4:20 PM |
View Edit Delete |
| 57 |
Ubuntu24.04安装Milvus |
<p>1、创建目录并下载docker的yml文件</p>
<pre class=""><code class="language-bash hljs">sudo mkdir /Milvus && cd /Milvus
wget https://github.com/milvus-io/milvus/releases/download/v2.6.11/milvus-standalone-docker-compose.yml -O docker-compose.yml</code></pre>
<p> </p>
<p>2、根据服务器的实际情况修改yml文件</p>
<pre class=""><code class="language-yaml hljs">services:
etcd:
container_name: milvus-etcd
image: quay.io/coreos/etcd:v3.5.25
restart: unless-stopped
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- timeless
minio:
container_name: milvus-minio
image: minio/minio:RELEASE.2024-12-18T13-15-44Z
restart: unless-stopped
environment:
MINIO_ROOT_USER: minioadmin # ← 改密码改这里(连同 standalone 两行共 4 处一起改)
MINIO_ROOT_PASSWORD: minioadmin # ← 密码至少 8 位
ports:
- "9001:9001"
- "9000:9000"
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data # ← 官方原仓库,存量数据在这里
command: minio server /minio_data --console-address ":9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- timeless
standalone:
container_name: milvus-standalone
image: milvusdb/milvus:v2.6.11
restart: unless-stopped
command: ["milvus", "run", "standalone"]
security_opt:
- seccomp:unconfined
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
# ↓ 这两行是官方默认没有的(默认值恰好是 minioadmin)。
# 显式写出是为了:你改了上面 MinIO 密码时,把这两行改成一样的值即可
MINIO_ACCESSKEYID: minioadmin
MINIO_SECRETACCESSKEY: minioadmin
COMMON_SECURITY_AUTHORIZATIONENABLED: "true" # Milvus 鉴权:默认账号 root / Milvus,登录后立即改密
MQ_TYPE: woodpecker
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
interval: 30s
start_period: 90s
timeout: 20s
retries: 3
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- "etcd"
- "minio"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- timeless
# ---------- Attu 管理界面(非官方自带,不需要可整段删除) ----------
attu:
container_name: milvus-attu
image: zilliz/attu:v2.6
restart: unless-stopped
environment:
MILVUS_URL: milvus-standalone:19530 # 容器间互访用容器名,不是 localhost
ports:
- "8888:3000" # 浏览器访问 http://服务器IP:8888
depends_on:
- "standalone"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- timeless
networks:
timeless:
name: timeless
driver: bridge
</code></pre>
<p> </p>
<p>3、启动docker</p>
<pre class=""><code class="hljs language-undefined">docker compse up -d</code></pre>
<p> </p>
<p>4、浏览器访问</p>
<pre class=""><code class="language-python hljs">http://192.168.20.143:8888</code></pre>
<p> </p>
<p>5、默认帐号密码</p>
<pre class=""><code class="language-bash hljs">#默认ATTU的帐号密码,进去后需要立即改
默认帐号:root
默认密码:Milvus
默认端口:19530</code></pre>
<p> </p> |
1 |
34 |
1 |
2/24/26, 4:25 PM |
9/5/26, 2:02 PM |
View Edit Delete |
| 58 |
Ubuntu24.04安装Tesseract OCR |
<p>1、安装</p>
<pre class=""><code class="hljs language-bash">sudo apt update
sudo apt install -y tesseract-ocr
sudo apt install -y tesseract-ocr-chi-sim # 中文简体
sudo apt install -y tesseract-ocr-eng # 英文</code></pre>
<p> </p>
<p>2、验证安装</p>
<pre class=""><code class="language-bash hljs">tesseract --version</code></pre>
<p> </p>
<p>3、测试中文识别</p>
<pre class=""><code class="hljs language-bash">echo "测试中文" > test.txt
tesseract test.txt stdout -l chi_sim</code></pre>
<p> </p> |
1 |
34 |
1 |
2/24/26, 4:28 PM |
2/24/26, 4:29 PM |
View Edit Delete |
| 68 |
Ubuntu24.04安装TinyProxy实现内网穿透 |
<p>1、安装TinyProxy</p>
<pre class=""><code class="language-bash hljs">sudo apt install tinyproxy</code></pre>
<p> </p>
<p>2、编辑配置文件TinyProxy</p>
<pre class=""><code class="hljs language-bash">sudo vim /etc/tinyproxy/tinyproxy.conf</code></pre>
<p> </p>
<p>3、更改项</p>
<pre class=""><code class="language-bash hljs"># 找到Allow,增加内网服务器IP
Allow 192.168.1.145
# 出于安全考虑一般不用默认 端口 找到 Port 8888,修改为其他端口
Port 8989</code></pre>
<p> </p>
<p>4、重启TinyProxy</p>
<pre class=""><code class="language-bash hljs">sudo systemctl restart tinyproxy</code></pre> |
1 |
34 |
1 |
3/10/26, 7:45 PM |
3/11/26, 3:04 PM |
View Edit Delete |
| 69 |
Ubuntu 后台运行程序,并将日志输出到特定文件。 |
<p>nohup:忽略挂断信号(即使终端关闭,进程仍运行)</p>
<p>> nohup.log:将标准输出重定向到 nohup.log</p>
<p>2>&1:将错误输出也重定向到日志文件</p>
<p>&:放入后台运行</p>
<pre class=""><code class="hljs language-bash">nohup java -jar your-app.jar > nohup.log 2>&1 &</code></pre> |
1 |
34 |
1 |
3/15/26, 2:29 PM |
3/15/26, 2:31 PM |
View Edit Delete |
| 80 |
Ubuntu24.04更新设备号并重启网络 |
<pre class=""><code class="hljs language-bash">sudo truncate -s 0 /etc/machine-id
sudo rm -f /var/lib/dbus/machine-id
sudo systemd-machine-id-setup
sudo systemctl restart systemd-networkd</code></pre> |
1 |
34 |
1 |
7/2/26, 1:03 AM |
7/2/26, 1:05 AM |
View Edit Delete |
| 81 |
Ubuntu24.04配置MySQL8.4数据库主从复制 |
<p><strong>一、主库配置</strong></p>
<p>1、检查是否有ssl证书,默认应该包含ca.pem、server-cert.pem、server-key.pem三个文件</p>
<pre class=""><code class="language-bash hljs">ll /var/lib/mysql/*.pem
# ----------------------正常应该显示下列文件----------------------
-rw------- 1 mysql mysql 1705 Jul 19 17:11 /var/lib/mysql/ca-key.pem
-rw-r--r-- 1 mysql mysql 1112 Jul 19 17:11 /var/lib/mysql/ca.pem
-rw-r--r-- 1 mysql mysql 1112 Jul 19 17:11 /var/lib/mysql/client-cert.pem
-rw------- 1 mysql mysql 1705 Jul 19 17:11 /var/lib/mysql/client-key.pem
-rw------- 1 mysql mysql 1705 Jul 19 17:11 /var/lib/mysql/private_key.pem
-rw-r--r-- 1 mysql mysql 452 Jul 19 17:11 /var/lib/mysql/public_key.pem
-rw-r--r-- 1 mysql mysql 1112 Jul 19 17:11 /var/lib/mysql/server-cert.pem
-rw------- 1 mysql mysql 1705 Jul 19 17:11 /var/lib/mysql/server-key.pem</code></pre>
<p> </p>
<p>2、配置数据库服务器</p>
<pre class=""><code class="language-ruby hljs">vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 增加以下配置
# 主库唯一 ID
server-id = 1
# 启用二进制日志
log_bin = /var/log/mysql/mysql-bin.log
# 允许所有 IP 连接(生产环境建议改为具体 IP)
bind-address = 0.0.0.0
# 二进制日志格式(推荐 ROW)
binlog_format = ROW
# SSL 配置(添加以下 3 行)
ssl_ca = /var/lib/mysql/ca.pem
ssl_cert = /var/lib/mysql/server-cert.pem
ssl_key = /var/lib/mysql/server-key.pem
</code></pre>
<p> </p>
<p>3、重启主库</p>
<pre class=""><code class="hljs language-undefined">sudo systemctl restart mysql</code></pre>
<p> </p>
<p>4、验证 SSL 生效</p>
<pre class=""><code class="language-bash hljs"># TCP/IP + SSL 连接测试(必须显示加密套件)
mysql -u root -p -h 127.0.0.1 --ssl-mode=REQUIRED -e "SHOW STATUS LIKE 'Ssl_cipher';"
# ----------------------正常输出如下----------------------
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| Ssl_cipher | TLS_AES_128_GCM_SHA256 |
+---------------+------------------------+
</code></pre>
<p> </p>
<p>5、创建复制用户</p>
<pre class=""><code class="language-sql hljs">-- 创建复制用户(强制 SSL)
CREATE USER 'repl'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
-- 创建复制用户(非强制 SSL)
CREATE USER 'repl'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
-- 验证
SELECT user, host, plugin, ssl_type FROM mysql.user WHERE user='repl';</code></pre>
<p> </p>
<p>6、导出当前节点的所有数据</p>
<pre class=""><code class="hljs language-kotlin">mysqldump --single-transaction --source-data=2 -u root -p --all-databases > backup.sql</code></pre>
<p> </p>
<p>7、导出主库数据</p>
<pre class=""><code class="language-sql hljs">mysqldump -u root -p \
--all-databases \
--single-transaction \
--source-data=2 \
--routines \
--triggers \
--events \
--set-gtid-purged=OFF \
> master_data.sql</code></pre>
<p> </p>
<p>8、查看备份文件记录的节点</p>
<pre class=""><code class="hljs language-bash">head -50 master_data.sql | grep "SOURCE_LOG_FILE"
# --------------------预期输出--------------------------
CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='binlog.000008', SOURCE_LOG_POS=6627;</code></pre>
<p> </p>
<p><strong>二、从库配置</strong></p>
<p>1、从主库复制master_data.sql 文件以及、ca.pem文件到从库,ca.pem需放到/var/lib/mysql/路径下覆盖</p>
<pre class=""><code class="language-bash hljs"># 在主库操作复制到从库
root@leon:/var/lib/mysql# scp ca.pem root@192.168.20.137:/var/lib/mysql/
root@leon:/installpackage# scp master_data.sql root@192.168.20.137:/installpackage</code></pre>
<p> </p>
<p>2、导入数据</p>
<pre class=""><code class="hljs language-css">mysql -u root -p < master_data.sql</code></pre>
<p> </p>
<p>3、修改配置文件</p>
<pre class=""><code class="language-bash hljs">vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 增加以下配置
# 从库唯一 ID,必须不同于主库
server-id = 2
# 中继日志
relay-log = /var/log/mysql/mysql-relay-bin.log
# 开启只读模式
read_only = ON
</code></pre>
<p> </p>
<p>4、重启MySQL从库</p>
<pre class=""><code class="hljs language-undefined">sudo systemctl restart mysql</code></pre>
<p> </p>
<p>5、从库复制配置</p>
<pre class=""><code class="language-sql hljs">-- 重置复制配置
STOP REPLICA;
RESET REPLICA ALL;
-- 配置主库信息(使用 1.7 记录的值)
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '192.168.20.136', -- 替换主库IP地址
SOURCE_PORT = 3306, -- 替换为主库数据库端口
SOURCE_USER = 'repl', -- 替换为刚创建的复制专用帐号
SOURCE_PASSWORD = '123456', -- 替换刚创建的复制专用帐号的密码
SOURCE_LOG_FILE = 'binlog.000008', -- 替换为1.8实际值
SOURCE_LOG_POS = 6627, -- 替换为1.8实际值
SOURCE_SSL = 1,
SOURCE_SSL_CA = '/var/lib/mysql/ca.pem';
START REPLICA;</code></pre>
<p> </p>
<p>6、从库查看复制状态</p>
<pre class=""><code class="hljs language-sql">SHOW REPLICA STATUS\G;</code></pre> |
1 |
34 |
1 |
7/19/26, 4:35 PM |
7/19/26, 7:56 PM |
View Edit Delete |
| 82 |
Ubuntu24.04设置root可以ssh远程访问 |
<p>1、设置root密码</p>
<pre class=""><code class="hljs language-undefined">sudo passwd root</code></pre>
<p>2、修改ssh配置文件</p>
<pre class=""><code class="language-bash hljs">sudo vim /etc/ssh/sshd_config</code></pre>
<p>3、配置ssh允许root远程访问,找到#PermitRootLogin prohibit-password在下面添加一句话</p>
<pre class=""><code class="hljs language-bash">PermitRootLogin yes</code></pre>
<p>4、重启ssh</p>
<pre class=""><code class="hljs language-undefined">sudo systemctl restart ssh</code></pre>
<p> </p>
<p> </p> |
1 |
34 |
1 |
7/19/26, 5:06 PM |
7/19/26, 5:07 PM |
View Edit Delete |
| 86 |
Ubuntu24.04安装Nacos3.2.3 |
<p>1、下载Nacos的Zip文件</p>
<pre class=""><code class="language-ruby hljs">https://nacos.io/download/nacos-server/
或
wget "https://download.nacos.io/nacos-server/nacos-server-3.2.3.zip?file=nacos-server-3.2.3.zip" -O nacos-server-3.2.3.zip
</code></pre>
<p> </p>
<p>2、导入MySQL</p>
<pre class=""><code class="hljs language-bash"># 切换到Nacos的配置文件目录
cd nacos/conf
# 登录MySQL
mysql -uroot -p
</code></pre>
<pre class=""><code class="language-sql hljs"># 创建nacos用户
CREATE USER `nacos`@`%` IDENTIFIED WITH caching_sha2_password BY 'nacos';
GRANT Alter, Alter Routine, Create, Create Routine, Create Temporary Tables, Create View, Delete, Drop, Event, Execute, Grant Option, Index, Insert, Lock Tables, References, Select, Show View, Trigger, Update ON `nacos3`.* TO `nacos`@`%`;
FLUSH PRIVILEGES;
# 创建nacos数据库
CREATE DATABASE nacos3 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;</code></pre>
<pre class=""><code class="hljs language-bash"># 导入数据库
mysql -unacos -p nacos3< mysql-schema.sql</code></pre>
<p> </p>
<p>3、修改配置文件vim nacos/conf/application.properties</p>
<pre class=""><code class="language-yaml hljs"># 使用MySQL
nacos.plugin.datasource.log.enabled=true
spring.sql.init.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos3?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=nacos
db.password=nacos
# 设置使用端口
nacos.console.port=9090
# 服务端身份标识
nacos.core.auth.server.identity.key=nacos
nacos.core.auth.server.identity.value=nacos
# Ubuntu 生成命令 openssl rand -base64 32
# Windows生成命令 $bytes = New-Object byte[] 32; (New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes); [Convert]::ToBase64String($bytes)
# Nacos Token 签名密钥
nacos.core.auth.plugin.nacos.token.secret.key=tSZiqFk+O6P1oUQffkWgdLHjEVNkTDLQcwDqDbB9bsE=</code></pre>
<p> </p>
<p>4、启动Nacos</p>
<pre class=""><code class="language-bash hljs">cd nacos/bin
./startup.sh -m standalone</code></pre>
<p> </p>
<p>5、Docker安装</p>
<p>拉取镜像</p>
<pre class=""><code class="hljs language-bash"># slim是压缩版
docker pull nacos/nacos-server:v3.2.3
或者
docker pull nacos/nacos-server:v3.2.3-slim</code></pre>
<p>从docker映射配置和持久化文件</p>
<pre class=""><code class="language-bash hljs">docker create --name nacos-tmp nacos/nacos-server:v3.2.3
docker cp nacos-tmp:/home/nacos/logs ./
docker rm -f nacos-tmp</code></pre>
<p> </p>
<p>数据库导入</p>
<pre class=""><code class="hljs language-bash"># 采用和普通版一样的sql文件导入。</code></pre>
<p> </p>
<p>建持久化目录和日志文件目录</p>
<pre class=""><code class="language-css hljs"> mkdir -p nacos-server3.2.3/{data,logs}</code></pre>
<p> </p>
<p>配置docker-compose.yml文件</p>
<pre class=""><code class="language-yaml hljs">services:
nacos:
image: nacos/nacos-server:v3.2.3
container_name: nacos
ports:
- "8848:8848"
- "9848:9848"
- "8080:8080"
- "9849:9849"
environment:
MODE: standalone
SPRING_DATASOURCE_PLATFORM: mysql
MYSQL_SERVICE_HOST: 192.168.20.140
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_DB_NAME: nacos3
MYSQL_SERVICE_USER: nacos
MYSQL_SERVICE_PASSWORD: nacos
MYSQL_SERVICE_DB_PARAM: characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=true&serverTimezone=Asia/Shanghai
NACOS_AUTH_TOKEN: "tSZiqFk+O6P1oUQffkWgdLHjEVNkTDLQcwDqDbB9bsE=" # ⚠️ 生产环境必须更换
NACOS_AUTH_IDENTITY_KEY: nacos # ⚠️ 生产环境更换 key
NACOS_AUTH_IDENTITY_VALUE: nacos # ⚠️ 生产环境更换 value
volumes:
- ./nacos-server3.2.3/data:/home/nacos/data
- ./nacos-server3.2.3/logs:/home/nacos/logs
restart: always
</code></pre>
<pre class=""><code class="hljs language-bash">#先docker compose up 看看有没有明显的报错
docker compose up -d</code></pre>
<pre class="" contenteditable="false"> </pre> |
1 |
34 |
1 |
8/1/26, 4:57 PM |
8/17/26, 8:37 PM |
View Edit Delete |
| 87 |
Ubuntu24.04安装Nacos2.5.3 |
<p>1、下载Nacos的Zip文件</p>
<pre class=""><code class="language-ruby hljs">https://nacos.io/download/nacos-server/
或
https://hub.docker.com/r/nacos/nacos-server/tags?spm=5238cd80.2ef5001f.0.0.3f613b7cQPwjZ1&page=1&name=2.5.3</code></pre>
<p> </p>
<p>2、导入MySQL</p>
<pre class=""><code class="hljs language-bash"># 切换到Nacos的配置文件目录
cd nacos/conf
# 登录MySQL
mysql -uroot -p</code></pre>
<pre class=""><code class="hljs language-sql"># 创建nacos用户
CREATE USER `nacos`@`%` IDENTIFIED WITH caching_sha2_password BY 'nacos';
GRANT Alter, Alter Routine, Create, Create Routine, Create Temporary Tables, Create View, Delete, Drop, Event, Execute, Grant Option, Index, Insert, Lock Tables, References, Select, Show View, Trigger, Update ON `nacos`.* TO `nacos`@`%`;
FLUSH PRIVILEGES;
# 创建nacos数据库
CREATE DATABASE nacos CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;</code></pre>
<pre class=""><code class="hljs language-bash"># 导入数据库
mysql -unacos -p nacos3< mysql-schema.sql</code></pre>
<p> </p>
<p>3、修改配置文件vim nacos/conf/application.properties</p>
<pre class=""><code class="hljs language-ini"># 使用MySQL
spring.datasource.platform=mysql
spring.sql.init.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
db.user.0=nacos
db.password.0=nacos
# 服务端身份标识(生产环境需改)
nacos.core.auth.enabled=true
nacos.core.auth.server.identity.key=nacos
nacos.core.auth.server.identity.value=nacos
# Ubuntu 生成命令 openssl rand -base64 32
# Windows生成命令 $bytes = New-Object byte[] 32; (New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes); [Convert]::ToBase64String($bytes)
# Nacos Token 签名密钥(生产环境需改)
nacos.core.auth.plugin.nacos.token.secret.key=tSZiqFk+O6P1oUQffkWgdLHjEVNkTDLQcwDqDbB9bsE=</code></pre>
<p> </p>
<p>4、启动Nacos</p>
<pre class=""><code class="hljs language-bash">cd nacos/bin
./startup.sh -m standalone</code></pre>
<p> </p>
<p>5、Docker安装</p>
<p>拉取镜像</p>
<pre class=""><code class="language-bash hljs"># slim是压缩版,生产环境可用。
docker pull nacos/nacos-server:v2.5.3
或
docker pull nacos/nacos-server:v2.5.3-slim</code></pre>
<p> </p>
<p>从docker映射配置日志文件</p>
<pre class=""><code class="language-bash hljs">docker create --name nacos-tmp nacos/nacos-server:v2.5.3
docker cp nacos-tmp:/home/nacos/logs ./
docker rm -f nacos-tmp</code></pre>
<p> </p>
<p>建持久化目录和日志文件目录</p>
<pre class=""><code class="language-bash hljs"> mkdir -p nacos-server2.5.3/{data,logs}</code></pre>
<p> </p>
<p>数据库导入</p>
<pre class=""><code class="hljs language-bash"># 采用和普通版一样的sql文件导入。</code></pre>
<p> </p>
<p>配置docker-compose.yml文件</p>
<pre class=""><code class="language-yaml hljs">services:
nacos:
image: nacos/nacos-server:v2.5.3
container_name: nacos
ports:
- "8848:8848"
- "9848:9848"
environment:
MODE: standalone
SPRING_DATASOURCE_PLATFORM: mysql
MYSQL_SERVICE_HOST: 192.168.20.139
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_DB_NAME: nacos
MYSQL_SERVICE_USER: nacos
MYSQL_SERVICE_PASSWORD: nacos
MYSQL_SERVICE_DB_PARAM: characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=true&serverTimezone=Asia/Shanghai
NACOS_AUTH_ENABLE: "true"
NACOS_AUTH_TOKEN: "tSZiqFk+O6P1oUQffkWgdLHjEVNkTDLQcwDqDbB9bsE=" # ⚠️ 生产环境必须更换
NACOS_AUTH_IDENTITY_KEY: nacos # ⚠️ 生产环境更换 key
NACOS_AUTH_IDENTITY_VALUE: nacos # ⚠️ 生产环境更换 value
volumes:
- ./nacos-server2.5.3/data:/home/nacos/data
- ./nacos-server2.5.3/logs:/home/nacos/logs
restart: always
</code></pre>
<pre class=""><code class="hljs language-bash">#先docker compose up 看看有没有明显的报错
docker compose up -d</code></pre> |
1 |
34 |
1 |
8/8/26, 9:59 AM |
8/17/26, 8:37 PM |
View Edit Delete |
| 88 |
Ubuntu24.04自动更新SSL证书并重启Nginx |
<p>1、获取阿里云的AccessKey</p>
<p>2、当前的ssl证书存放在/etc/nginx/ssl路径下</p>
<p>3、将下面的脚本放在某个目录下,命名为ssl_auto_renew.sh,新的服务器用./ssl_auto_renew.sh setup,如果已经有配置过了,则只需要将新加的域名添加到DOMAINS里面,新增一行即可。</p>
<pre class=""><code class="language-bash hljs">#!/bin/bash
#=============================================================================
# SSL证书自动检测与续期脚本 (通用版, 支持任意多个域名)
# 环境: Ubuntu 24.04 + Nginx + 阿里云DNS + acme.sh + Let's Encrypt
#
# 功能:
# 1. 检测所有SSL证书的到期时间
# 2. 临近到期(默认30天)自动通过acme.sh + DNS-01申请免费证书
# 3. 自动部署到对应目录并重载Nginx
# 4. 完整日志记录 + 可选钉钉/邮件通知
#
# 用法:
# ./ssl_auto_renew.sh setup # 首次安装配置(安装acme.sh+签发证书+配置cron)
# ./ssl_auto_renew.sh check # 检查并续期(cron定时调用)
# ./ssl_auto_renew.sh status # 查看所有证书状态
# ./ssl_auto_renew.sh force # 强制续期所有证书
#
# 新增域名: 在 DOMAINS 数组中加一行即可, 证书可放在任意目录
#=============================================================================
set -o pipefail
# ========================== 配置区域(请修改) ==========================
# 阿里云AccessKey (需要AliyunDNSFullAccess权限)
# 获取: 阿里云控制台 -> 右上角头像 -> AccessKey管理 -> 创建AccessKey
ALI_KEY=""
ALI_SECRET=""
# 续期阈值(距到期多少天开始自动续期)
RENEW_DAYS=3
# 日志文件
LOG_FILE="/var/log/ssl_renew.log"
# ACME CA: letsencrypt 或 zerossl
ACME_CA="letsencrypt"
# 通知配置(可选, 留空则不通知)
# 钉钉/企业微信机器人webhook
WEBHOOK_URL=""
# 通知邮箱
NOTIFY_EMAIL="wy8817399@vip.qq.com"
# 域名配置(支持任意多个域名, 新增域名加一行即可)
# 格式: "主域名|附加SAN域名(逗号分隔,可空)|证书目录(绝对路径)|pem文件名|key文件名"
DOMAINS=(
"timeless.wang|www.timeless.wang|/etc/nginx/ssl/timeless.wang/www|timeless.wang.pem|timeless.wang.key"
)
# 示例: 新增一个完全不同根域名、不同目录的证书, 直接加一行:
# "blog.example.com||/etc/nginx/ssl/example.com/blog|blog.example.com.pem|blog.example.com.key"
# ========================== 配置结束(以下无需修改) ==========================
# ACME账户邮箱: 优先使用用户填写的 NOTIFY_EMAIL; 若为空, 使用域名下的默认邮箱
ACME_EMAIL="${NOTIFY_EMAIL:-ssl@timeless.wang}"
ACME_SH="$HOME/.acme.sh/acme.sh"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# --- 日志函数 ---
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"; }
info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"; }
success() { echo -e "${GREEN}[ OK ]${NC} $1" | tee -a "$LOG_FILE"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; }
error() { echo -e "${RED}[FAIL]${NC} $1" | tee -a "$LOG_FILE"; }
# --- 通知函数 ---
notify() {
local title="$1" content="$2"
if [[ -n "$WEBHOOK_URL" ]]; then
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"$title\",\"text\":\"**$title**\n\n$content\n\n$(date '+%Y-%m-%d %H:%M:%S')\"}}" \
> /dev/null 2>&1 || true
fi
if [[ -n "$NOTIFY_EMAIL" ]]; then
echo -e "$content" | mail -s "$title" "$NOTIFY_EMAIL" 2>/dev/null || true
fi
}
# --- 前置检查 ---
check_root() {
if [[ $EUID -ne 0 ]]; then
error "请以root用户运行此脚本"
exit 1
fi
}
check_config() {
if [[ -z "$ALI_KEY" || -z "$ALI_SECRET" ]]; then
error "请先编辑本脚本, 填入 ALI_KEY 和 ALI_SECRET"
echo ""
echo " 获取方式:"
echo " 1. 登录阿里云控制台 -> 右上角头像 -> AccessKey管理"
echo " 2. 创建AccessKey (建议仅授予 AliyunDNSFullAccess 权限)"
echo " 3. 将 AccessKey ID 填入 ALI_KEY"
echo " 将 AccessKey Secret 填入 ALI_SECRET"
exit 1
fi
# 如果用户填写了通知邮箱, 校验基本格式
if [[ -n "$NOTIFY_EMAIL" && ! "$NOTIFY_EMAIL" =~ ^[^@]+@[^@]+\.[^@]+$ ]]; then
error "通知邮箱 NOTIFY_EMAIL 格式无效: $NOTIFY_EMAIL"
error "留空则使用默认 ACME 邮箱, 或填写形如 admin@example.com 的有效地址"
exit 1
fi
}
# --- 安装acme.sh ---
install_acme() {
if [[ -f "$ACME_SH" ]]; then
info "acme.sh 已安装"
else
info "正在安装 acme.sh (优先官方源, 失败则切换Gitee国内镜像) ..."
# 方式1: 官方安装脚本(境外服务器可用)
curl -m 30 https://get.acme.sh | sh -s email="${ACME_EMAIL}" >/dev/null 2>&1
if [[ ! -f "$ACME_SH" ]]; then
warn "官方源安装失败(大陆服务器常见), 切换Gitee镜像 ..."
# 方式2: Gitee国内镜像安装
local tmp_dir="/tmp/acme_sh_install_$$"
if git clone --depth 1 https://gitee.com/neilpang/acme.sh.git "$tmp_dir" >/dev/null 2>&1; then
(cd "$tmp_dir" && sh ./acme.sh --install -m "${ACME_EMAIL}") >>"$LOG_FILE" 2>&1
rm -rf "$tmp_dir"
fi
fi
if [[ ! -f "$ACME_SH" ]]; then
error "acme.sh 安装失败(官方源和Gitee镜像均不可用)"
error "请检查服务器网络后重试, 或参考: https://github.com/acmesh-official/acme.sh/wiki/Install-in-China"
exit 1
fi
success "acme.sh 安装完成"
fi
# 设置默认CA
"$ACME_SH" --set-default-ca --server "$ACME_CA" 2>/dev/null || true
# 关闭自动升级(避免大陆服务器反复访问GitHub导致续期报错)
# --auto-upgrade 是 --upgrade 的子参数, 不能单独使用
"$ACME_SH" --upgrade --auto-upgrade 0 >/dev/null 2>&1 || true
}
# --- 确保ACME账户邮箱有效 ---
ensure_acme_account() {
local conf="$HOME/.acme.sh/account.conf"
if [[ -f "$conf" ]]; then
# 修正 account.conf 中可能残留的无效邮箱
sed -i "s/^SAVED_EMAIL=.*/SAVED_EMAIL='${ACME_EMAIL}'/" "$conf" 2>/dev/null || true
sed -i "s/^ACCOUNT_EMAIL=.*/ACCOUNT_EMAIL='${ACME_EMAIL}'/" "$conf" 2>/dev/null || true
fi
info "确保 ACME 账户邮箱正确: ${ACME_EMAIL}"
if ! "$ACME_SH" --register-account -m "$ACME_EMAIL" --server "$ACME_CA" 2>&1 | tee -a "$LOG_FILE"; then
warn "ACME 账户注册/更新未成功, 将在签发时重试"
fi
}
# --- 获取证书剩余天数(返回-1表示文件不存在) ---
get_days() {
local cert_file="$1"
if [[ ! -f "$cert_file" ]]; then
echo -1
return
fi
local expiry_date
expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
if [[ -z "$expiry_date" ]]; then
echo -1
return
fi
local expiry_epoch now_epoch
expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null)
now_epoch=$(date +%s)
echo $(( (expiry_epoch - now_epoch) / 86400 ))
}
# --- 签发/续期并部署证书 ---
# 参数: 主域名 附加SAN域名 证书目录(绝对路径) pem文件名 key文件名
deploy_cert() {
local domain="$1" san="$2" cert_dir="$3" pem_name="$4" key_name="$5"
local cert_file="$cert_dir/$pem_name"
local key_file="$cert_dir/$key_name"
# 构建域名参数
local domain_args="-d $domain"
if [[ -n "$san" ]]; then
IFS=',' read -ra san_array <<< "$san"
for s in "${san_array[@]}"; do
domain_args="$domain_args -d $s"
done
fi
# 设置阿里云DNS API密钥
export Ali_Key="$ALI_KEY"
export Ali_Secret="$ALI_SECRET"
# 签发证书(--force确保每次都重新签发)
info "正在签发证书: $domain ..."
if "$ACME_SH" --issue --dns dns_ali $domain_args --force 2>&1 | tee -a "$LOG_FILE"; then
success "证书签发成功: $domain"
else
error "证书签发失败: $domain"
# 清理可能残留的半成品目录, 避免下次误判为已有证书
rm -rf "$HOME/.acme.sh/${domain}" "$HOME/.acme.sh/${domain}_ecc"
return 1
fi
# 确保证书目录存在
mkdir -p "$cert_dir"
# 备份现有证书(如有)
if [[ -f "$cert_file" ]]; then
cp -f "$cert_file" "${cert_file}.bak.$(date +%Y%m%d%H%M%S)"
cp -f "$key_file" "${key_file}.bak.$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
fi
# 安装证书到目标目录(acme.sh会记住安装位置, 后续renew时自动部署)
# 注意: --reloadcmd 在批量部署时可能因其他域名尚未部署而nginx -t失败,
# 但证书文件会先于reloadcmd写入, 因此以文件是否生成为准, reloadcmd失败仅警告
info "部署证书: $domain -> $cert_dir/"
local install_rc=0
"$ACME_SH" --install-cert -d "$domain" \
--key-file "$key_file" \
--fullchain-file "$cert_file" \
--reloadcmd "nginx -t && systemctl reload nginx" \
2>&1 | tee -a "$LOG_FILE" || install_rc=1
if [[ -f "$cert_file" && -f "$key_file" ]]; then
chmod 644 "$cert_file"
chmod 600 "$key_file"
if [[ "$install_rc" -ne 0 ]]; then
warn "证书文件已写入, 但 reloadcmd 失败(批量部署时正常, 将在最后统一重载): $domain"
fi
success "证书部署完成: $domain"
return 0
else
error "证书部署失败, 文件未生成: $domain"
return 1
fi
}
# ========================== 主逻辑 ==========================
case "${1:-help}" in
# ---------- 首次安装配置 ----------
setup)
check_root
check_config
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
info "=========================================="
info " SSL证书自动续期 - 初始配置"
info "=========================================="
# 安装acme.sh
install_acme
# 确保ACME账户邮箱正确有效
ensure_acme_account
# 逐个签发并部署证书
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"
done
# 重载Nginx
info "重载 Nginx ..."
if nginx -t 2>&1 | tee -a "$LOG_FILE"; then
systemctl reload nginx
success "Nginx 已重载"
else
error "Nginx 配置测试失败, 请检查配置"
fi
# 配置cron定时任务(每天凌晨3点检查)
script_path=$(readlink -f "$0")
cron_entry="0 3 * * * ${script_path} check >> ${LOG_FILE} 2>&1"
if ! crontab -l 2>/dev/null | grep -qF "${script_path} check"; then
(crontab -l 2>/dev/null; echo "$cron_entry") | crontab -
success "已添加cron定时任务: 每天 03:00 自动检查证书"
else
info "cron定时任务已存在, 跳过"
fi
# 配置日志轮转
cat > /etc/logrotate.d/ssl_renew <<LOGROTATE_EOF
${LOG_FILE} {
weekly
rotate 8
compress
missingok
notifempty
create 0644 root root
}
LOGROTATE_EOF
success "已配置日志轮转: /etc/logrotate.d/ssl_renew"
success "=========================================="
success " 配置完成!"
success "=========================================="
echo ""
echo " 常用命令:"
echo " $0 status # 查看所有证书状态"
echo " $0 check # 手动检查并续期"
echo " $0 force # 强制续期所有证书"
echo ""
echo " 日志文件: $LOG_FILE"
echo " cron任务: 每天 03:00 自动检查"
echo " 新增域名: 编辑 DOMAINS 数组加一行, 再执行 $0 check 即可"
echo ""
;;
# ---------- 检查并续期(cron调用) ----------
check)
check_root
# 确保acme.sh已安装且ACME账户邮箱正确
install_acme
ensure_acme_account
info "=========================================="
info " 开始检查SSL证书 (共 ${#DOMAINS[@]} 个)"
info "=========================================="
failed_count=0
renewed_count=0
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
cert_file="$d_dir/$d_pem"
days_left=$(get_days "$cert_file")
if [[ "$days_left" -eq -1 ]]; then
warn "证书文件不存在, 开始签发: $d_main"
if deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"; then
((renewed_count++))
else
((failed_count++))
fi
elif [[ "$days_left" -le 0 ]]; then
warn "证书已过期(${days_left}天), 紧急续期: $d_main"
if deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"; then
((renewed_count++))
else
((failed_count++))
fi
elif [[ "$days_left" -le "$RENEW_DAYS" ]]; then
warn "证书即将到期(剩余${days_left}天), 开始续期: $d_main"
if deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"; then
((renewed_count++))
else
((failed_count++))
fi
else
success "证书正常(剩余${days_left}天): $d_main"
fi
done
# 如有证书被更新, 统一重载Nginx一次(确保所有域名使用最新证书)
if [[ "$renewed_count" -gt 0 ]]; then
info "本次有证书更新, 统一重载 Nginx ..."
if nginx -t 2>&1 | tee -a "$LOG_FILE"; then
systemctl reload nginx
success "Nginx 已重载"
else
error "Nginx 配置测试失败, 请检查配置"
fi
fi
# 运行acme.sh内置续期检查(作为双保险)
if [[ -f "$ACME_SH" ]]; then
info "运行acme.sh内置续期检查 ..."
"$ACME_SH" --cron 2>&1 | tail -5 | tee -a "$LOG_FILE" || true
fi
# 汇总
info "------------------------------------------"
info " 检查完毕: 续期 ${renewed_count} 个, 失败 ${failed_count} 个"
info "=========================================="
if [[ "$failed_count" -gt 0 ]]; then
error "有 ${failed_count} 个证书处理失败!"
notify "SSL证书续期告警" "有 ${failed_count} 个证书续期失败, 请检查日志: ${LOG_FILE}"
elif [[ "$renewed_count" -gt 0 ]]; then
success "成功续期 ${renewed_count} 个证书"
notify "SSL证书续期成功" "成功续期 ${renewed_count} 个证书"
fi
;;
# ---------- 查看证书状态 ----------
status)
echo "=============================================================================="
echo " SSL证书状态报告 $(date '+%Y-%m-%d %H:%M:%S') 共 ${#DOMAINS[@]} 个域名"
echo "=============================================================================="
printf " %-40s %-8s %-28s %s\n" "域名" "剩余" "到期时间" "状态"
echo "------------------------------------------------------------------------------"
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
cert_file="$d_dir/$d_pem"
days_left=$(get_days "$cert_file")
if [[ "$days_left" -eq -1 ]]; then
printf " %-40s %-8s %-28s %b\n" "$d_main" "N/A" "N/A" "${RED}不存在${NC}"
else
expiry_str=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
if [[ "$days_left" -le 0 ]]; then
status_str="${RED}已过期${NC}"
elif [[ "$days_left" -le "$RENEW_DAYS" ]]; then
status_str="${YELLOW}需续期${NC}"
else
status_str="${GREEN}正常${NC}"
fi
printf " %-40s %-8s %-28s %b\n" "$d_main" "${days_left}天" "$expiry_str" "$status_str"
fi
done
echo "=============================================================================="
echo " 续期阈值: ${RENEW_DAYS}天 | 日志: ${LOG_FILE}"
echo "=============================================================================="
;;
# ---------- 强制续期所有证书 ----------
force)
check_root
info "=========================================="
info " 强制续期所有证书 (共 ${#DOMAINS[@]} 个)"
info "=========================================="
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"
done
info "重载 Nginx ..."
if nginx -t 2>&1 | tee -a "$LOG_FILE"; then
systemctl reload nginx
success "Nginx 已重载"
fi
success "=========================================="
success " 强制续期完成!"
success "=========================================="
;;
# ---------- 帮助 ----------
*)
echo ""
echo " SSL证书自动检测与续期脚本 (通用版)"
echo " 环境: Ubuntu 24.04 + Nginx + 阿里云DNS + acme.sh + Let's Encrypt"
echo ""
echo " 用法: $0 {setup|check|status|force}"
echo ""
echo " setup 首次安装: 安装acme.sh + 签发所有证书 + 配置cron定时任务"
echo " check 检查证书有效期, 到期自动续期 (cron定时调用)"
echo " status 查看所有证书的当前状态(只读, 不做任何修改)"
echo " force 强制续期所有证书(忽略到期时间)"
echo ""
echo " 首次使用:"
echo " 1. 编辑本脚本, 填入 ALI_KEY 和 ALI_SECRET"
echo " 2. 运行: $0 setup"
echo " 3. 查看状态: $0 status"
echo ""
echo " 新增域名:"
echo " 在 DOMAINS 数组中按格式加一行, 再执行 $0 check 即自动签发部署"
echo " 格式: \"主域名|附加SAN域名|证书目录(绝对路径)|pem文件名|key文件名\""
echo ""
exit 1
;;
esac
</code></pre>
<p> </p>
<p>4、具体用法</p>
<pre class=""><code class="language-bash hljs">./ssl_auto_renew.sh setup # 首次安装配置(安装acme.sh+签发证书+配置cron)
./ssl_auto_renew.sh check # 检查并续期(cron定时调用)
./ssl_auto_renew.sh status # 查看所有证书状态
./ssl_auto_renew.sh force # 强制续期所有证书</code></pre>
<p> </p> |
1 |
34 |
1 |
8/16/26, 2:15 PM |
8/16/26, 2:19 PM |
View Edit Delete |
| 89 |
Ubuntu24.04安装MinIO |
<p>1、创建目录</p>
<pre class=""><code class="hljs language-bash">sudo mkdir -p /MinIO/Bin /MinIO/Data /MinIO/Conf</code></pre>
<p> </p>
<p>2、创建专用运行账号</p>
<pre class=""><code class="hljs language-bash">sudo useradd -r -s /sbin/nologin minio-user</code></pre>
<p> </p>
<p>3、下载二进制并安装到 /MinIO/Bin</p>
<pre class=""><code class="hljs language-bash">cd /tmp
wget https://dl.min.io/server/minio/release/linux-amd64/minio
wget https://dl.min.io/client/mc/release/linux-amd64/mc
sudo mv minio mc /MinIO/Bin/
sudo chmod +x /MinIO/Bin/minio /MinIO/Bin/mc</code></pre>
<pre class=""><code class="hljs language-bash"># 验证
/MinIO/Bin/minio --version
/MinIO/Bin/mc --version</code></pre>
<p> </p>
<p>4、设置目录权限</p>
<pre class=""><code class="hljs language-bash">sudo chown -R minio-user:minio-user /MinIO/Data /MinIO/Conf
sudo chmod 750 /MinIO/Data
sudo chmod 750 /MinIO/Conf</code></pre>
<p> </p>
<p>5、写配置文件 /MinIO/Conf/minio.env</p>
<pre class=""><code class="hljs language-makefile">sudo tee /MinIO/Conf/minio.env <<'EOF'
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD=adminTimeless
MINIO_VOLUMES="/MinIO/Data"
MINIO_OPTS="--address :9000 --console-address :9001"
EOF</code></pre>
<pre class=""><code class="hljs language-bash">sudo chown minio-user:minio-user /MinIO/Conf/minio.env</code></pre>
<pre class=""><code class="hljs language-bash">sudo chmod 600 /MinIO/Conf/minio.env</code></pre>
<p> </p>
<p>6、创建 systemd 服务</p>
<pre class=""><code class="hljs language-makefile">sudo tee /etc/systemd/system/minio.service <<'EOF'
[Unit]
Description=MinIO Object Storage
After=network.target
[Service]
User=minio-user
Group=minio-user
EnvironmentFile=/MinIO/Conf/minio.env
ExecStart=/MinIO/Bin/minio server $MINIO_VOLUMES $MINIO_OPTS
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF</code></pre>
<p> </p>
<p>7、启动并设置开机自启</p>
<pre class=""><code class="hljs language-lua">sudo systemctl daemon-reload
sudo systemctl enable --now minio
systemctl status minio --no-pager</code></pre>
<p><span class="md-plain md-expand" style="box-sizing: border-box; --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; --tw-rotate: 0; --tw-skew-x: 0; --tw-skew-y: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-color: rgb(59 130 246 / .5); --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-shadow-colored: 0 0 #0000; color: #333333; font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, 'Segoe UI Emoji', sans-serif; font-size: 16px; orphans: 4; white-space: pre-wrap; background-color: #ffffff;"> 看到 </span><span class="md-pair-s" style="box-sizing: border-box; --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; --tw-rotate: 0; --tw-skew-x: 0; --tw-skew-y: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-color: rgb(59 130 246 / .5); --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-shadow-colored: 0 0 #0000; color: #333333; font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, 'Segoe UI Emoji', sans-serif; font-size: 16px; orphans: 4; white-space: pre-wrap; background-color: #ffffff;" spellcheck="false"><code style="box-sizing: border-box; --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; --tw-rotate: 0; --tw-skew-x: 0; --tw-skew-y: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-color: rgb(59 130 246 / .5); --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-shadow-colored: 0 0 #0000; font-family: var(--monospace); vertical-align: initial; border: 1px solid #e7eaed; background-color: #f3f4f4; border-radius: 3px; padding: 0px 2px; font-size: 0.9em;">active (running)</code></span><span class="md-plain md-expand" style="box-sizing: border-box; --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; --tw-rotate: 0; --tw-skew-x: 0; --tw-skew-y: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-color: rgb(59 130 246 / .5); --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-shadow: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-shadow-colored: 0 0 #0000; color: #333333; font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, 'Segoe UI Emoji', sans-serif; font-size: 16px; orphans: 4; white-space: pre-wrap; background-color: #ffffff;"> 即成功。若失败,查看日志:</span></p>
<pre class=""><code class="hljs language-css">sudo journalctl -u minio -n 50 --no-pager</code></pre>
<p> </p>
<p>8、开放防火墙(如启用 UFW)</p>
<pre class=""><code class="hljs language-bash"># 云服务器还需在安全组放行这两个端口,建议限制来源 IP。
sudo ufw allow 9000/tcp # S3 API
sudo ufw allow 9001/tcp # Web 控制台</code></pre>
<p> </p>
<p>9、访问验证</p>
<pre class=""><code class="hljs language-markdown">- API:`http://服务器IP:9000`
- Web 控制台:`http://服务器IP:9001`(用 minio.env 里的账号密码登录)</code></pre>
<p> </p>
<p>10、Docker 安装</p>
<pre class=""><code class="hljs language-yaml">services:
minio:
image: minio/minio:latest
container_name: minio
restart: unless-stopped
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web 控制台
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: ChangeMe_StrongPwd123 # 至少 8 位,务必修改
volumes:
- /MinIO/Data:/data
- /MinIO/Conf/certs:/root/.minio/certs # 可选:HTTPS 证书目录,不用可删
command: server /data --console-address ":9001"
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
networks:
- timeless
networks:
timeless:
name: timeless
driver: bridge
</code></pre> |
1 |
34 |
1 |
8/17/26, 8:52 PM |
8/17/26, 8:52 PM |
View Edit Delete |
| 90 |
Ubuntu24.04在jdk8的环境安装seata2.2(MySQL8+nacos2.5.3) |
<p>1、创建目录</p>
<pre class=""><code class="hljs language-bash">mkdir -p /Seata && cd /Seata</code></pre>
<p> </p>
<p>2、下载并解压</p>
<pre class=""><code class="hljs language-bash">wget https://archive.apache.org/dist/incubator/seata/2.2.0/apache-seata-2.2.0-incubating-bin.tar.gz
tar -xzf apache-seata-2.2.0-incubating-bin.tar.gz
cd seata-server</code></pre>
<p> </p>
<p>3、MySQL创建seata用户</p>
<pre class=""><code class="hljs language-css">mysql -uroot -p</code></pre>
<pre class=""><code class="hljs language-sql">-- 1. 建库
CREATE DATABASE IF NOT EXISTS seata DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 2. 建用户并授权(Seata 专用账号,不要用 root)
CREATE USER IF NOT EXISTS 'seata'@'%' IDENTIFIED BY 'seata';
GRANT ALL PRIVILEGES ON seata.* TO 'seata'@'%';
FLUSH PRIVILEGES;</code></pre>
<p> </p>
<p>4、导入数据</p>
<pre class=""><code class="language-bash hljs">cat > seata_server.sql <<'EOF'
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `distributed_lock`
(
`lock_key` CHAR(20) NOT NULL,
`lock_value` VARCHAR(20) NOT NULL,
`expire` BIGINT,
primary key (`lock_key`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
CREATE TABLE IF NOT EXISTS `vgroup_table`
(
`vGroup` VARCHAR(255),
`namespace` VARCHAR(255),
`cluster` VARCHAR(255),
UNIQUE KEY `idx_vgroup_namespace_cluster` (`vGroup`,`namespace`,`cluster`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
EOF</code></pre>
<pre class=""><code class="language-css hljs">mysql -uroot -p seata < seata_server.sql</code></pre>
<p> </p>
<p>5、配置application.yml</p>
<pre class=""><code class="language-yaml hljs">"seata.store.db..vgroup-table": vgroup_table
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${user.home}/logs/seata}
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
console:
user:
username: seata
password: seata
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: file
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server # 注册到 Nacos 的服务名(客户端按此名查找 TC)
server-addr: 192.168.20.139:8848 # ★ Nacos连接地址
group: SEATA_GROUP # Nacos 中的分组名
namespace: "" # 命名空间 ID,留空 = public(注意:填 ID 不填名称)
cluster: default # 集群名,客户端配置必须与此一致
username: nacos # Nacos 控制台账号
password: nacos # Nacos 控制台密码
store:
# support: file 、 db 、 redis 、 raft
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.20.139:3306/seata?useSSL=true&serverTimezone=Asia/Shanghai
user: seata # Step 4 创建的 Seata 专用账号
password: seata # 同上
min-conn: 5 # 最小连接数
max-conn: 30 # 最大连接数
global-table: global_table # 对应的表名(和 DDL 一致,不用改)
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 100 # 单次查询最大行数
max-retry-size: 10 # 提交/回滚最大重试次数
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
csrf-ignore-urls: /metadata/v1/**
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/health,/error,/vgroup/v1/**
</code></pre>
<p> </p>
<p>6、下载MySQL驱动。</p>
<pre class=""><code class="language-bash hljs">mkdir -p /Seata/seata-server/lib/jdbc
wget -P /Seata/seata-server/lib/jdbc/ https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar</code></pre>
<p> </p>
<p>7、启动</p>
<pre class=""><code class="language-bash hljs">cd /Seata/seata-server/bin
./seata-server.sh</code></pre>
<p> </p>
<p>8、浏览器验证</p>
<pre class=""><code class="hljs language-cpp">http://192.168.20.139:7091/</code></pre>
<p> </p>
<p>9、配 systemd 后台运行</p>
<pre class=""><code class="hljs language-makefile"># 先探测 JAVA_HOME(systemd 不读 profile,必须显式写入服务)
JAVA_HOME_PATH=$(dirname $(dirname $(readlink -f $(which java))))
echo "JAVA_HOME = $JAVA_HOME_PATH"
# 注意:下面 heredoc 用的是不带引号的 << EOF,bash 会在写文件那一刻把 $JAVA_HOME_PATH
# 替换成变量值——所以上面两行赋值必须在 cat 之前执行,顺序不能颠倒(否则写入空值)。
cat > /etc/systemd/system/seata.service << EOF
[Unit]
Description=Seata Server (TC) 2.2.0
Documentation=https://seata.apache.org
After=network.target
[Service]
Type=forking
Environment=JAVA_HOME=$JAVA_HOME_PATH
WorkingDirectory=/Seata/seata-server
ExecStart=/Seata/seata-server/bin/seata-server.sh
ExecStop=/Seata/seata-server/bin/seata-server.sh stop
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF</code></pre>
<pre class=""><code class="hljs language-bash">systemctl daemon-reload
systemctl enable seata # 开机自启
systemctl start seata # 启动
sleep 10
systemctl status seata # 查看状态(应为 active (running),Main PID 是 java)
ss -lntp | grep 7091 # Console 端口在监听即成功
</code></pre> |
1 |
34 |
1 |
8/20/26, 2:58 PM |
8/22/26, 9:39 PM |
View Edit Delete |
| 91 |
Ubuntu24.04在jdk25的环境安装seata2.6(MySQL8+nacos3.2.3) |
<p>1、创建目录</p>
<pre class=""><code class="hljs language-bash">mkdir -p /Seata && cd /Seata</code></pre>
<p> </p>
<p>2、下载并解压</p>
<pre class=""><code class="language-bash hljs">wget https://downloads.apache.org/incubator/seata/2.6.0/apache-seata-2.6.0-incubating-bin.tar.gz
tar -xzf apache-seata-2.6.0-incubating-bin.tar.gz
cd apache-seata-2.6.0-incubating-bin/seata-server</code></pre>
<p> </p>
<p>3、MySQL创建seata用户</p>
<pre class=""><code class="hljs language-css">mysql -uroot -p</code></pre>
<pre class=""><code class="hljs language-sql">-- 1. 建库
CREATE DATABASE IF NOT EXISTS seata DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 2. 建用户并授权(Seata 专用账号,不要用 root)
CREATE USER IF NOT EXISTS 'seata'@'%' IDENTIFIED BY 'seata';
GRANT ALL PRIVILEGES ON seata.* TO 'seata'@'%';
FLUSH PRIVILEGES;</code></pre>
<p> </p>
<p>4、导入数据</p>
<pre class=""><code class="language-bash hljs">cat > seata_server.sql <<'EOF'
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `distributed_lock`
(
`lock_key` CHAR(20) NOT NULL,
`lock_value` VARCHAR(20) NOT NULL,
`expire` BIGINT,
primary key (`lock_key`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
CREATE TABLE IF NOT EXISTS `vgroup_table`
(
`vGroup` VARCHAR(255),
`namespace` VARCHAR(255),
`cluster` VARCHAR(255),
UNIQUE KEY `idx_vgroup_namespace_cluster` (`vGroup`,`namespace`,`cluster`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
EOF</code></pre>
<pre class=""><code class="language-css hljs">mysql -uroot -p seata < seata_server.sql</code></pre>
<p> </p>
<p>5、配置seata-server的application.yml</p>
<pre class=""><code class="language-yaml hljs"># Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
server:
port: 8091
spring:
application:
name: seata-server
main:
web-application-type: none
logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${user.home}/logs/seata}
extend:
logstash-appender:
# off by default
enabled: false
destination: 127.0.0.1:4560
kafka-appender:
# off by default
enabled: false
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
producer:
acks: 0
linger-ms: 1000
max-block-ms: 0
metric-appender:
# off by default
enabled: false
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: file
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa, seata
type: seata,nacos
seata:
server-addr: 192.168.20.140:8081 # 本机 namingserver
cluster: default
namespace: public
heartbeat-period: 5000
username: seata # 和 namingserver 的 console.user 一致
password: seata
nacos:
application: seata-server # 注册到 Nacos 的服务名(客户端按此名查找 TC)
server-addr: 192.168.20.140:8848 # ★ Nacos连接地址
group: SEATA_GROUP # Nacos 中的分组名
namespace: "" # 命名空间 ID,留空 = public(注意:填 ID 不填名称)
cluster: default # 集群名,客户端配置必须与此一致
username: nacos # Nacos 控制台账号
password: nacos # Nacos 控制台密码
store:
# support: file 、 db 、 redis 、 raft
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.20.140:3306/seata?useSSL=true&serverTimezone=Asia/Shanghai
user: seata # Step 4 创建的 Seata 专用账号
password: seata # 同上
min-conn: 5 # 最小连接数
max-conn: 30 # 最大连接数
global-table: global_table # 对应的表名(和 DDL 一致,不用改)
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 100 # 单次查询最大行数
max-retry-size: 10 # 提交/回滚最大重试次数
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
</code></pre>
<p> </p>
<p>6、下载驱动。</p>
<pre class=""><code class="language-bash hljs">cd /Seata/apache-seata-2.6.0-incubating-bin/seata-server/lib/jdbc
wget -P /Seata/seata-server/lib/jdbc/ https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar
# 1. 备份并移除老客户端(不能只改名留在 lib 里?其实改后缀为 .bak 即可,PropertiesLauncher 只认 .jar)
mv nacos-client-1.4.6.jar nacos-client-1.4.6.jar.bak
# 2. 下载 2.0.4(官方 PR #8097 指定的版本,走 gRPC 9848 端口,无需 v1 API)
wget https://repo1.maven.org/maven2/com/alibaba/nacos/nacos-client/2.0.4/nacos-client-2.0.4.jar
# 3. ★ nacos-client 2.x 多出两个依赖(reflections、javassist),Seata 发行包里没有,必须补上
# 缺 reflections 会报 NoClassDefFoundError: org/reflections/Reflections
# 缺 javassist 会报 NoClassDefFoundError: javassist/bytecode/ClassFile
wget https://repo1.maven.org/maven2/org/reflections/reflections/0.9.11/reflections-0.9.11.jar
wget https://repo1.maven.org/maven2/org/javassist/javassist/3.29.2-GA/javassist-3.29.2-GA.jar
# 4. 确认(nacos-client 2.0.4 约 14MB,reflections 约 130KB,javassist 约 500KB),然后重启 seata-server
ls -lh nacos-client* reflections* javassist*
</code></pre>
<p> </p>
<p>7、启动seata-server</p>
<pre class=""><code class="language-bash hljs">cd /Seata/apache-seata-2.6.0-incubating-bin/seata-server/bin
./seata-server.sh
#当前情况如果要停服务
kill $(pgrep -f seata-server.jar) 2>/dev/null;</code></pre>
<p> </p>
<p>8、配置seata-namingserver的application.yml</p>
<pre class=""><code class="hljs language-bash">cd /Seata/apache-seata-2.6.0-incubating-bin/seata-namingserver/conf
vim application.yml
</code></pre>
<pre class=""><code class="hljs language-yaml">#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
server:
port: 8081
spring:
threads:
virtual:
enabled: true
mvc:
pathmatch:
matching-strategy: ant_path_matcher
ai:
mcp:
server:
protocol: streamable
streamable-http:
mcp-endpoint: /mcp
name: mcp-server
version: 1.0.0
application:
name: seata-namingserver
logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${user.home}/logs/seata}
heartbeat:
threshold: 90000
period: 60000
console:
# console username and password configuration
user:
username: seata #加上帐号密码
password: seata
# When the console is deployed independently, configure the namingserver address
# namingserver:
# protocol: http
# addr:
# - 127.0.0.1:8081
seata:
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
csrf-ignore-urls: /naming/v1/**,/api/v1/naming/**
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/version.json,/naming/v1/health,/error
# MCP Server Configuration
mcp:
# Maximum query time interval, The unit is milliseconds, Default one day
query:
max-query-duration: 86400000
</code></pre>
<p> </p>
<p>9、启动seata-namingserver</p>
<pre class=""><code class="language-bash hljs">cd /Seata/apache-seata-2.6.0-incubating-bin/seata-namingserver/bin
./seata-namingserver.sh
#当前情况如果要停服务
kill $(pgrep -f seata-namingserver.jar) 2>/dev/null;</code></pre>
<p> </p>
<p>10、浏览器验证</p>
<pre class=""><code class="language-bash hljs">http://192.168.20.139:8081/</code></pre>
<p> </p>
<p>11、配 systemd 后台运行</p>
<pre class=""><code class="language-makefile hljs"># 先探测 JAVA_HOME(systemd 不读 profile,必须显式写入服务)
JAVA_HOME_PATH=$(dirname $(dirname $(readlink -f $(which java))))
echo "JAVA_HOME = $JAVA_HOME_PATH"
cat > /etc/systemd/system/seata.service << 'EOF'
[Unit]
Description=Seata Server (TC) 2.6.0
After=network.target seata-namingserver.service docker.service
Wants=seata-namingserver.service docker.service
[Service]
Type=forking
Environment=JAVA_HOME=/usr/lib/jvm/jdk-25
WorkingDirectory=/Seata/seata-server
ExecStart=/Seata/seata-server/bin/seata-server.sh
ExecStop=/Seata/seata-server/bin/seata-server.sh stop
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
</code></pre>
<pre class=""><code class="hljs language-javascript">cat > /etc/systemd/system/seata-namingserver.service << 'EOF'
[Unit]
Description=Seata NamingServer (Console) 2.6.0
After=network.target
[Service]
Type=forking
Environment=JAVA_HOME=/usr/lib/jvm/jdk-25
WorkingDirectory=/Seata/seata-namingserver
ExecStart=/Seata/seata-namingserver/bin/seata-namingserver.sh
ExecStop=/Seata/seata-namingserver/bin/seata-namingserver.sh stop
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
</code></pre>
<pre class=""><code class="language-sql hljs"># 加载刚加的service
systemctl daemon-reload
# 启动
systemctl start seata-namingserver
systemctl start seata
</code></pre>
<pre class=""><code class="hljs language-bash"># 设置为开机自动启动
systemctl enable seata-namingserver seata
</code></pre>
<p> </p> |
1 |
34 |
1 |
8/20/26, 9:20 PM |
8/21/26, 9:24 PM |
View Edit Delete |
| 92 |
Ubuntu24.04自动更新SSL证书并重启Apache |
<p>1、获取阿里云的AccessKey</p>
<p>2、当前的ssl证书存放在/etc/ssl/apache路径下</p>
<p>3、将下面的脚本放在某个目录下,命名为ssl_auto_renew.sh,新的服务器用./ssl_auto_renew.sh setup,如果已经有配置过了,则只需要将新加的域名添加到DOMAINS里面,新增一行即可。</p>
<pre class=""><code class="language-bash hljs">#!/bin/bash
#=============================================================================
# SSL证书自动检测与续期脚本 (Apache2版, 支持任意多个域名)
# 环境: Ubuntu 24.04 + Apache 2.4.58 + 阿里云DNS + acme.sh + Let's Encrypt
#
# 功能:
# 1. 检测所有SSL证书的到期时间
# 2. 临近到期(默认30天)自动通过acme.sh + DNS-01申请免费证书
# 3. 自动部署到对应目录并重载Apache2
# 4. 完整日志记录 + 可选钉钉/邮件通知
#
# 用法:
# ./ssl_auto_renew.sh setup # 首次安装配置(安装acme.sh+签发证书+配置cron)
# ./ssl_auto_renew.sh check # 检查并续期(cron定时调用)
# ./ssl_auto_renew.sh status # 查看所有证书状态
# ./ssl_auto_renew.sh force # 强制续期所有证书
#
# 新增域名: 在 DOMAINS 数组中加一行即可, 证书可放在任意目录
#=============================================================================
set -o pipefail
# ========================== 配置区域(请修改) ==========================
# 阿里云AccessKey (需要AliyunDNSFullAccess权限)
# 注意: 要求 wangyan.asia 和 timeless.wang 两个域名的DNS解析都托管在阿里云
# (timeless.wang 在Nginx服务器上已用阿里云DNS验证成功, 一般无需改动)
# 获取: 阿里云控制台 -> 右上角头像 -> AccessKey管理 -> 创建AccessKey
ALI_KEY=""
ALI_SECRET=""
# 续期阈值(距到期多少天开始自动续期)
RENEW_DAYS=3
# 日志文件
LOG_FILE="/var/log/ssl_renew.log"
# ACME CA: letsencrypt 或 zerossl
ACME_CA="letsencrypt"
# 通知配置(可选, 留空则不通知)
# 钉钉/企业微信机器人webhook
WEBHOOK_URL=""
# 通知邮箱
NOTIFY_EMAIL="wy8817399@vip.qq.com"
# 域名配置(支持任意多个域名, 新增域名加一行即可)
# 格式: "主域名|附加SAN域名(逗号分隔,可空)|证书目录(绝对路径)|pem文件名|key文件名"
# 注意: 文件名/路径与Apache配置中 SSLCertificateFile / SSLCertificateKeyFile 保持一致
DOMAINS=(
"timelesscake.wangyan.asia|wangyan.asia|/etc/ssl/apache/timelesscake|timelesscake.wangyan.asia.pem|timelesscake.wangyan.asia.key"
)
# 示例: 新增一个完全不同根域名、不同目录的证书, 直接加一行:
# "blog.example.com||/etc/ssl/apache/blog|blog.example.com.pem|blog.example.com.key"
# ========================== 配置结束(以下无需修改) ==========================
# ACME账户邮箱: 优先使用用户填写的 NOTIFY_EMAIL; 若为空, 使用域名下的默认邮箱
ACME_EMAIL="${NOTIFY_EMAIL:-ssl@wangyan.asia}"
ACME_SH="$HOME/.acme.sh/acme.sh"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# --- 日志函数 ---
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"; }
info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"; }
success() { echo -e "${GREEN}[ OK ]${NC} $1" | tee -a "$LOG_FILE"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"; }
error() { echo -e "${RED}[FAIL]${NC} $1" | tee -a "$LOG_FILE"; }
# --- 通知函数 ---
notify() {
local title="$1" content="$2"
if [[ -n "$WEBHOOK_URL" ]]; then
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"$title\",\"text\":\"**$title**\n\n$content\n\n$(date '+%Y-%m-%d %H:%M:%S')\"}}" \
> /dev/null 2>&1 || true
fi
if [[ -n "$NOTIFY_EMAIL" ]]; then
echo -e "$content" | mail -s "$title" "$NOTIFY_EMAIL" 2>/dev/null || true
fi
}
# --- Apache配置检测与重载 ---
apache_test() {
apache2ctl configtest 2>&1
return ${PIPESTATUS[0]}
}
apache_reload() {
if apache_test | tee -a "$LOG_FILE"; then
systemctl reload apache2
success "Apache2 已重载"
else
error "Apache2 配置测试失败, 请检查配置"
return 1
fi
}
# --- 前置检查 ---
check_root() {
if [[ $EUID -ne 0 ]]; then
error "请以root用户运行此脚本"
exit 1
fi
}
check_config() {
if [[ -z "$ALI_KEY" || -z "$ALI_SECRET" ]]; then
error "请先编辑本脚本, 填入 ALI_KEY 和 ALI_SECRET"
echo ""
echo " 获取方式:"
echo " 1. 登录阿里云控制台 -> 右上角头像 -> AccessKey管理"
echo " 2. 创建AccessKey (建议仅授予 AliyunDNSFullAccess 权限)"
echo " 3. 将 AccessKey ID 填入 ALI_KEY"
echo " 将 AccessKey Secret 填入 ALI_SECRET"
exit 1
fi
# 检查apache2ctl是否存在
if ! command -v apache2ctl >/dev/null 2>&1; then
error "未找到 apache2ctl 命令, 请确认已安装 Apache2"
exit 1
fi
# 如果用户填写了通知邮箱, 校验基本格式
if [[ -n "$NOTIFY_EMAIL" && ! "$NOTIFY_EMAIL" =~ ^[^@]+@[^@]+\.[^@]+$ ]]; then
error "通知邮箱 NOTIFY_EMAIL 格式无效: $NOTIFY_EMAIL"
error "留空则使用默认 ACME 邮箱, 或填写形如 admin@example.com 的有效地址"
exit 1
fi
}
# --- 安装acme.sh ---
install_acme() {
if [[ -f "$ACME_SH" ]]; then
info "acme.sh 已安装"
else
info "正在安装 acme.sh (优先官方源, 失败则切换Gitee国内镜像) ..."
# 方式1: 官方安装脚本(境外服务器可用)
# 注意: get.acme.sh 只是引导脚本, 内部会 git clone github, 大陆服务器会无限挂起,
# 必须用 timeout 强制限时, 超时后自动降级到Gitee镜像
timeout 90 bash -c "curl -m 30 https://get.acme.sh | sh -s email='${ACME_EMAIL}'" >/dev/null 2>&1
if [[ ! -f "$ACME_SH" ]]; then
warn "官方源安装失败/超时(大陆服务器常见), 切换Gitee镜像 ..."
# 清理官方源安装可能残留的半成品目录
[[ -d "$HOME/.acme.sh" ]] && rm -rf "$HOME/.acme.sh"
# 方式2: Gitee国内镜像安装
local tmp_dir="/tmp/acme_sh_install_$$"
if timeout 180 git clone --depth 1 https://gitee.com/neilpang/acme.sh.git "$tmp_dir" >/dev/null 2>&1; then
(cd "$tmp_dir" && sh ./acme.sh --install -m "${ACME_EMAIL}") >>"$LOG_FILE" 2>&1
rm -rf "$tmp_dir"
fi
fi
if [[ ! -f "$ACME_SH" ]]; then
error "acme.sh 安装失败(官方源和Gitee镜像均不可用)"
error "请检查服务器网络后重试, 或参考: https://github.com/acmesh-official/acme.sh/wiki/Install-in-China"
exit 1
fi
success "acme.sh 安装完成"
fi
# 设置默认CA
"$ACME_SH" --set-default-ca --server "$ACME_CA" 2>/dev/null || true
# 关闭自动升级(避免大陆服务器反复访问GitHub导致续期报错)
# --auto-upgrade 是 --upgrade 的子参数, 不能单独使用
"$ACME_SH" --upgrade --auto-upgrade 0 >/dev/null 2>&1 || true
}
# --- 确保ACME账户邮箱有效 ---
ensure_acme_account() {
local conf="$HOME/.acme.sh/account.conf"
if [[ -f "$conf" ]]; then
# 修正 account.conf 中可能残留的无效邮箱
sed -i "s/^SAVED_EMAIL=.*/SAVED_EMAIL='${ACME_EMAIL}'/" "$conf" 2>/dev/null || true
sed -i "s/^ACCOUNT_EMAIL=.*/ACCOUNT_EMAIL='${ACME_EMAIL}'/" "$conf" 2>/dev/null || true
fi
info "确保 ACME 账户邮箱正确: ${ACME_EMAIL}"
if ! "$ACME_SH" --register-account -m "$ACME_EMAIL" --server "$ACME_CA" 2>&1 | tee -a "$LOG_FILE"; then
warn "ACME 账户注册/更新未成功, 将在签发时重试"
fi
}
# --- 获取证书剩余天数(返回-1表示文件不存在) ---
get_days() {
local cert_file="$1"
if [[ ! -f "$cert_file" ]]; then
echo -1
return
fi
local expiry_date
expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
if [[ -z "$expiry_date" ]]; then
echo -1
return
fi
local expiry_epoch now_epoch
expiry_epoch=$(date -d "$expiry_date" +%s 2>/dev/null)
now_epoch=$(date +%s)
echo $(( (expiry_epoch - now_epoch) / 86400 ))
}
# --- 签发/续期并部署证书 ---
# 参数: 主域名 附加SAN域名 证书目录(绝对路径) pem文件名 key文件名
deploy_cert() {
local domain="$1" san="$2" cert_dir="$3" pem_name="$4" key_name="$5"
local cert_file="$cert_dir/$pem_name"
local key_file="$cert_dir/$key_name"
# 构建域名参数
local domain_args="-d $domain"
if [[ -n "$san" ]]; then
IFS=',' read -ra san_array <<< "$san"
for s in "${san_array[@]}"; do
domain_args="$domain_args -d $s"
done
fi
# 设置阿里云DNS API密钥
export Ali_Key="$ALI_KEY"
export Ali_Secret="$ALI_SECRET"
# 签发证书(--force确保每次都重新签发)
info "正在签发证书: $domain ..."
if "$ACME_SH" --issue --dns dns_ali $domain_args --force 2>&1 | tee -a "$LOG_FILE"; then
success "证书签发成功: $domain"
else
error "证书签发失败: $domain"
# 清理可能残留的半成品目录, 避免下次误判为已有证书
rm -rf "$HOME/.acme.sh/${domain}" "$HOME/.acme.sh/${domain}_ecc"
return 1
fi
# 确保证书目录存在
mkdir -p "$cert_dir"
# 备份现有证书(如有)
if [[ -f "$cert_file" ]]; then
cp -f "$cert_file" "${cert_file}.bak.$(date +%Y%m%d%H%M%S)"
cp -f "$key_file" "${key_file}.bak.$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
fi
# 安装证书到目标目录(acme.sh会记住安装位置, 后续renew时自动部署)
# 注意: --reloadcmd 在批量部署时可能因其他域名尚未部署而configtest失败,
# 但证书文件会先于reloadcmd写入, 因此以文件是否生成为准, reloadcmd失败仅警告
info "部署证书: $domain -> $cert_dir/"
local install_rc=0
"$ACME_SH" --install-cert -d "$domain" \
--key-file "$key_file" \
--fullchain-file "$cert_file" \
--reloadcmd "apache2ctl configtest && systemctl reload apache2" \
2>&1 | tee -a "$LOG_FILE" || install_rc=1
if [[ -f "$cert_file" && -f "$key_file" ]]; then
chmod 644 "$cert_file"
chmod 600 "$key_file"
if [[ "$install_rc" -ne 0 ]]; then
warn "证书文件已写入, 但 reloadcmd 失败(批量部署时正常, 将在最后统一重载): $domain"
fi
success "证书部署完成: $domain"
return 0
else
error "证书部署失败, 文件未生成: $domain"
return 1
fi
}
# ========================== 主逻辑 ==========================
case "${1:-help}" in
# ---------- 首次安装配置 ----------
setup)
check_root
check_config
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
info "=========================================="
info " SSL证书自动续期(Apache2版) - 初始配置"
info "=========================================="
# 安装acme.sh
install_acme
# 确保ACME账户邮箱正确有效
ensure_acme_account
# 逐个签发并部署证书
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"
done
# 重载Apache2
info "重载 Apache2 ..."
apache_reload
# 配置cron定时任务(每天凌晨3点检查)
script_path=$(readlink -f "$0")
cron_entry="0 3 * * * ${script_path} check >> ${LOG_FILE} 2>&1"
if ! crontab -l 2>/dev/null | grep -qF "${script_path} check"; then
(crontab -l 2>/dev/null; echo "$cron_entry") | crontab -
success "已添加cron定时任务: 每天 03:00 自动检查证书"
else
info "cron定时任务已存在, 跳过"
fi
# 配置日志轮转
cat > /etc/logrotate.d/ssl_renew <<LOGROTATE_EOF
${LOG_FILE} {
weekly
rotate 8
compress
missingok
notifempty
create 0644 root root
}
LOGROTATE_EOF
success "已配置日志轮转: /etc/logrotate.d/ssl_renew"
success "=========================================="
success " 配置完成!"
success "=========================================="
echo ""
echo " 常用命令:"
echo " $0 status # 查看所有证书状态"
echo " $0 check # 手动检查并续期"
echo " $0 force # 强制续期所有证书"
echo ""
echo " 日志文件: $LOG_FILE"
echo " cron任务: 每天 03:00 自动检查"
echo " 新增域名: 编辑 DOMAINS 数组加一行, 再执行 $0 check 即可"
echo ""
;;
# ---------- 检查并续期(cron调用) ----------
check)
check_root
# 确保acme.sh已安装且ACME账户邮箱正确
install_acme
ensure_acme_account
info "=========================================="
info " 开始检查SSL证书 (共 ${#DOMAINS[@]} 个)"
info "=========================================="
failed_count=0
renewed_count=0
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
cert_file="$d_dir/$d_pem"
days_left=$(get_days "$cert_file")
if [[ "$days_left" -eq -1 ]]; then
warn "证书文件不存在, 开始签发: $d_main"
if deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"; then
((renewed_count++))
else
((failed_count++))
fi
elif [[ "$days_left" -le 0 ]]; then
warn "证书已过期(${days_left}天), 紧急续期: $d_main"
if deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"; then
((renewed_count++))
else
((failed_count++))
fi
elif [[ "$days_left" -le "$RENEW_DAYS" ]]; then
warn "证书即将到期(剩余${days_left}天), 开始续期: $d_main"
if deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"; then
((renewed_count++))
else
((failed_count++))
fi
else
success "证书正常(剩余${days_left}天): $d_main"
fi
done
# 如有证书被更新, 统一重载Apache2一次(确保所有域名使用最新证书)
if [[ "$renewed_count" -gt 0 ]]; then
info "本次有证书更新, 统一重载 Apache2 ..."
apache_reload
fi
# 运行acme.sh内置续期检查(作为双保险)
if [[ -f "$ACME_SH" ]]; then
info "运行acme.sh内置续期检查 ..."
"$ACME_SH" --cron 2>&1 | tail -5 | tee -a "$LOG_FILE" || true
fi
# 汇总
info "------------------------------------------"
info " 检查完毕: 续期 ${renewed_count} 个, 失败 ${failed_count} 个"
info "=========================================="
if [[ "$failed_count" -gt 0 ]]; then
error "有 ${failed_count} 个证书处理失败!"
notify "SSL证书续期告警" "有 ${failed_count} 个证书续期失败, 请检查日志: ${LOG_FILE}"
elif [[ "$renewed_count" -gt 0 ]]; then
success "成功续期 ${renewed_count} 个证书"
notify "SSL证书续期成功" "成功续期 ${renewed_count} 个证书"
fi
;;
# ---------- 查看证书状态 ----------
status)
echo "=============================================================================="
echo " SSL证书状态报告 $(date '+%Y-%m-%d %H:%M:%S') 共 ${#DOMAINS[@]} 个域名"
echo "=============================================================================="
printf " %-40s %-8s %-28s %s\n" "域名" "剩余" "到期时间" "状态"
echo "------------------------------------------------------------------------------"
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
cert_file="$d_dir/$d_pem"
days_left=$(get_days "$cert_file")
if [[ "$days_left" -eq -1 ]]; then
printf " %-40s %-8s %-28s %b\n" "$d_main" "N/A" "N/A" "${RED}不存在${NC}"
else
expiry_str=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
if [[ "$days_left" -le 0 ]]; then
status_str="${RED}已过期${NC}"
elif [[ "$days_left" -le "$RENEW_DAYS" ]]; then
status_str="${YELLOW}需续期${NC}"
else
status_str="${GREEN}正常${NC}"
fi
printf " %-40s %-8s %-28s %b\n" "$d_main" "${days_left}天" "$expiry_str" "$status_str"
fi
done
echo "=============================================================================="
echo " 续期阈值: ${RENEW_DAYS}天 | 日志: ${LOG_FILE}"
echo "=============================================================================="
;;
# ---------- 强制续期所有证书 ----------
force)
check_root
info "=========================================="
info " 强制续期所有证书 (共 ${#DOMAINS[@]} 个)"
info "=========================================="
for line in "${DOMAINS[@]}"; do
IFS='|' read -r d_main d_san d_dir d_pem d_key <<< "$line"
deploy_cert "$d_main" "$d_san" "$d_dir" "$d_pem" "$d_key"
done
info "重载 Apache2 ..."
apache_reload
success "=========================================="
success " 强制续期完成!"
success "=========================================="
;;
# ---------- 帮助 ----------
*)
echo ""
echo " SSL证书自动检测与续期脚本 (Apache2版)"
echo " 环境: Ubuntu 24.04 + Apache 2.4.58 + 阿里云DNS + acme.sh + Let's Encrypt"
echo ""
echo " 用法: $0 {setup|check|status|force}"
echo ""
echo " setup 首次安装: 安装acme.sh + 签发所有证书 + 配置cron定时任务"
echo " check 检查证书有效期, 到期自动续期 (cron定时调用)"
echo " status 查看所有证书的当前状态(只读, 不做任何修改)"
echo " force 强制续期所有证书(忽略到期时间)"
echo ""
echo " 首次使用:"
echo " 1. 编辑本脚本, 确认 ALI_KEY / ALI_SECRET, 且域名DNS托管在阿里云"
echo " 2. 运行: $0 setup"
echo " 3. 查看状态: $0 status"
echo ""
echo " 新增域名:"
echo " 在 DOMAINS 数组中按格式加一行, 再执行 $0 check 即自动签发部署"
echo " 格式: \"主域名|附加SAN域名|证书目录(绝对路径)|pem文件名|key文件名\""
echo ""
exit 1
;;
esac
</code></pre> |
1 |
34 |
1 |
8/21/26, 9:36 PM |
8/21/26, 9:36 PM |
View Edit Delete |
| 95 |
ubuntu24.04安装EMQX |
<p>1、原生安装</p>
<pre class=""><code class="language-rust hljs">wget https://www.emqx.com/zh/downloads/enterprise/6.3.0/emqx-enterprise-6.3.0-ubuntu24.04-amd64.deb
sudo apt install ./emqx-enterprise-6.3.0-ubuntu24.04-amd64.deb
sudo systemctl start emqx</code></pre>
<p> </p>
<p>2、docker安装</p>
<pre class=""><code class="hljs language-yaml">docker pull emqx/emqx-enterprise:6.3.0
docker run -d --name emqx-enterprise -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx-enterprise:6.3.0</code></pre>
<p> </p>
<p>3、<span style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; font-size: 14px; white-space: nowrap;">docker-compose.yml<br /></span></p>
<pre class=""><code class="hljs language-yaml">services:
emqx-enterprise:
image: emqx/emqx-enterprise:6.3.0
container_name: emqx-enterprise
restart: unless-stopped
ports:
- "1883:1883" # MQTT TCP
- "8083:8083" # MQTT WebSocket
- "8084:8084" # MQTT WebSocket SSL
- "8883:8883" # MQTT SSL
- "18083:18083" # Dashboard 管理界面
volumes:
- emqx_data:/opt/emqx/data
- emqx_log:/opt/emqx/log
- emqx_etc:/opt/emqx/etc
volumes:
emqx_data:
emqx_log:
emqx_etc:</code></pre>
<p><span style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; font-size: 14px; white-space: nowrap;"> </span></p>
<p><span style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; font-size: 14px; white-space: nowrap;">4、启动后访问18083查看是否正常运行</span></p>
<pre class=""><code class="language-cpp hljs">http://192.168.20.157:18083/
默认帐号:admin
默认密码:public</code></pre> |
1 |
34 |
1 |
9/7/26, 8:01 PM |
9/7/26, 8:28 PM |
View Edit Delete |