使用 Docker + Nginx + Certbot 实现自动化管理 SSL 证书
使用 Docker + Nginx + Certbot 实现自动化管理 SSL 证书
在互联网安全环境日益重要的今天,为站点或应用部署 HTTPS 已经成为一种常态。然而,手动申请并续期证书既繁琐又容易出错。本文将以 Nginx + Certbot 为示例,基于 Docker 容器来搭建一个可以自动申请、自动续期的 SSL 证书服务,帮助你更轻松地管理多域名的 HTTPS。
一、方案简介
在这个方案中,我们使用官方 Nginx 镜像作为基础镜像,并在其中安装 Python3、Certbot 及阿里云 DNS 插件。通过以下几个关键脚本来实现自动部署及管理:
- start.sh:容器启动时执行,初始化阿里云 DNS 配置并启动 Nginx。
- add-domain.sh:添加新域名并为其申请证书,自动生成 Nginx 配置文件,最后重载 Nginx。
- deploy-certbot.sh:读取域名列表,一键申请或续期证书。
- domains.txt:存放所有需要申请 SSL 证书的域名列表。
同时,通过挂载相应目录,可以将证书、日志、Nginx 配置文件全部持久化到宿主机,不会因为容器的更新或重启而丢失。
二、准备工作
-
服务器已经安装 Docker(版本建议 >= 19.03)。
-
你拥有阿里云账号,并且已经开通了 AliyunDNSFullAccess 权限的 Access Key:
- ALIYUN_ACCESS_KEY_ID
- ALIYUN_ACCESS_KEY_SECRET
-
确保服务器安全组或本地防火墙已允许 HTTP(80) 和 HTTPS(443)。
三、获取配置文件
以下操作假设我们把部署目录统一放在 /data/nginx-certbot 下。
# 1. 创建部署目录
mkdir -p /data/nginx-certbot
cd /data/nginx-certbot# 2. 通过临时容器获取镜像内的 Nginx 配置
docker run -d --name nginx_conf -it registry.cn-hangzhou.aliyuncs.com/hbteck/nginx-certbot:1.20.2# 3. 拷贝 Nginx 配置文件到本地 conf 目录
docker cp nginx_conf:/etc/nginx/ ./conf# 4. 删除临时容器
docker rm -f nginx_conf
此时,/data/nginx-certbot/conf 目录下已包含镜像内的默认 Nginx 配置文件(包括 nginx.conf、vhost 目录等),可根据需求进行调整和定制。
四、主要文件和脚本说明
下面列举一些在本方案中常见或关键的文件结构与作用,仅供参考:
-
Dockerfile
这是我们自定义镜像的核心文件。可自行修改或参考原始镜像中的配置。示例:# 使用官方nginx镜像作为基础镜像 FROM nginx:stable# 替换为国内源 RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list# 安装必要的软件包 RUN apt-get update && \apt-get install -y \python3 \python3-pip \cron \&& rm -rf /var/lib/apt/lists/*# 配置pip国内源 RUN pip3 config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && \pip3 config set install.trusted-host mirrors.aliyun.com# 安装certbot和阿里云DNS插件 RUN pip3 install certbot certbot-dns-aliyun# 创建必要的目录 RUN mkdir -p /etc/nginx/conf.d && \mkdir -p /etc/letsencrypt && \mkdir -p /etc/nginx/vhost && \mkdir -p /var/log/certbot# 复制配置文件 COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./deploy-certbot.sh /usr/local/bin/deploy-certbot.sh COPY ./add-domain.sh /usr/local/bin/add-domain.sh COPY ./start.sh /usr/local/bin/start.sh COPY ./domains.txt /etc/letsencrypt/domains.txt# 设置脚本权限 RUN chmod +x /usr/local/bin/deploy-certbot.sh && \chmod +x /usr/local/bin/start.sh && \chmod +x /usr/local/bin/add-domain.sh# 设置环境变量默认值 ENV RENEW_CRON_SCHEDULE="0 0 1 * * ?" ENV CERTBOT_EMAIL="admin@example.com" ENV DNS_PROPAGATION_SECONDS=60VOLUME ["/etc/letsencrypt", "/var/log/certbot"]CMD ["/usr/local/bin/start.sh"] -
add-domain.sh
在容器内用于添加新域名并申请证书的核心脚本:#!/bin/bashnew_domain=$1 backend_url=${2:-"http://localhost:8080"} # 默认后端地址 local_ip=$3 # 第三参数: 本地IP地址# 检查参数 if [ -z "$new_domain" ]; thenecho "用法: add-domain.sh <domain> [backend_url] [local_ip]"echo "示例: add-domain.sh example.com http://localhost:8080 192.168.1.1"exit 1 fi# 验证域名格式 if ! echo "$new_domain" | grep -P '(?=^.{4,253}$)(^(?:[a-zA-Z0-9](?:(?:[a-zA-Z0-9\-]){0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$)' > /dev/null; thenecho "错误: 无效的域名格式"exit 1 fi# 检查域名是否已存在 if grep -q "^${new_domain}$" /etc/letsencrypt/domains.txt; thenecho "域名 ${new_domain} 已存在"exit 0 fi# 添加新域名 echo "$new_domain" >> /etc/letsencrypt/domains.txt echo "已添加域名: $new_domain"# 为新域名获取证书 if ! /usr/local/bin/deploy-certbot.sh; thenecho "证书获取失败,移除域名配置"sed -i "\|^${new_domain}$|d" /etc/letsencrypt/domains.txtexit 1 fi# 创建nginx配置 if [ -z "$local_ip" ]; thencat <<EOT > /etc/nginx/vhost/${new_domain}.conf # upstream配置 upstream ${new_domain}_backend {server ${backend_url#http://};keepalive 32; }server {listen 443 ssl http2;server_name ${new_domain};ssl_certificate /etc/letsencrypt/live/${new_domain}/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/${new_domain}/privkey.pem;ssl_trusted_certificate /etc/letsencrypt/live/${new_domain}/chain.pem;# SSL配置ssl_session_timeout 1d;ssl_session_cache shared:SSL:50m;ssl_session_tickets off;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;ssl_prefer_server_ciphers off;# HSTS配置add_header Strict-Transport-Security "max-age=63072000" always;location / {proxy_pass ${backend_url};proxy_http_version 1.1;proxy_set_header Connection "";proxy_set_header Host \$host;proxy_set_header X-Real-IP \$remote_addr;proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto \$scheme;} }server {listen 80;server_name ${new_domain};return 301 https://\$server_name\$request_uri; } EOT elsecat <<EOT > /etc/nginx/vhost/${new_domain}.conf server {listen 443 ssl http2;server_name ${new_domain};ssl_certificate /etc/letsencrypt/live/${new_domain}/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/${new_domain}/privkey.pem;ssl_trusted_certificate /etc/letsencrypt/live/${new_domain}/chain.pem;# SSL配置ssl_session_timeout 1d;ssl_session_cache shared:SSL:50m;ssl_session_tickets off;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;ssl_prefer_server_ciphers off;# HSTS配置add_header Strict-Transport-Security "max-age=63072000" always;location / {proxy_pass ${backend_url};proxy_http_version 1.1;proxy_set_header Connection "";proxy_set_header Host \$host;proxy_set_header X-Real-IP \$remote_addr;proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto \$scheme;}error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}location /api/ {proxy_pass http://${local_ip}:18500/;proxy_set_header Host \$http_host;proxy_set_header X-Real-IP \$remote_addr;proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto \$scheme;proxy_set_header X-NginX-Proxy true;# 传递所有原始请求头信息proxy_pass_request_headers on;}location /nacos/ {proxy_pass http://${local_ip}:8848/nacos/;proxy_set_header Host \$http_host;proxy_set_header X-Real-IP \$remote_addr;proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto \$scheme;proxy_set_header X-NginX-Proxy true;}location /kafka/ {proxy_pass http://${local_ip}:9000/kafka/;proxy_set_header Host \$http_host;proxy_set_header X-Real-IP \$remote_addr;proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto \$scheme;proxy_set_header X-NginX-Proxy true;} }server {listen 80;server_name ${new_domain};return 301 https://\$server_name\$request_uri; } EOT fi# 检查nginx配置 if ! nginx -t; thenecho "Nginx配置检查失败,移除配置文件"rm -f /etc/nginx/vhost/${new_domain}.confsed -i "\|^${new_domain}$|d" /etc/letsencrypt/domains.txtexit 1 fi# 重新加载nginx配置 nginx -s reloadecho "域名 ${new_domain} 配置完成" -
deploy-certbot.sh
该脚本读取 /etc/letsencrypt/domains.txt 里的域名,通过 DNS-01 方式批量申请或更新证书。#!/bin/bash# 检查aliyun配置文件 if [ ! -f "/etc/letsencrypt/aliyun.ini" ]; thenecho "错误: 未找到阿里云DNS凭证文件"exit 1 fi# 检查domains.txt是否存在且不为空 if [ ! -s "/etc/letsencrypt/domains.txt" ]; thenecho "警告: domains.txt为空或不存在"exit 0 fi# 设置默认值 : ${DNS_PROPAGATION_SECONDS:="60"} : ${CERTBOT_EMAIL:="admin@example.com"}# 处理每个域名 while IFS= read -r domain || [ -n "$domain" ]; do# 跳过空行和注释[[ -z "$domain" || "${domain:0:1}" == "#" ]] && continueecho "正在处理域名: $domain"certbot certonly \--authenticator dns-aliyun \--dns-aliyun-credentials /etc/letsencrypt/aliyun.ini \--dns-aliyun-propagation-seconds "$DNS_PROPAGATION_SECONDS" \--non-interactive \--agree-tos \--email "$CERTBOT_EMAIL" \-d "$domain" \--expand \--keep-until-expiring \--preferred-challenges dns-01 \--config-dir /etc/letsencrypt \--logs-dir /var/log/certbot \--work-dir /var/lib/certbotif [ $? -eq 0 ]; thenecho "成功为 $domain 获取/更新证书"elseecho "为 $domain 获取/更新证书失败"# 如果获取证书失败,则退出脚本exit 1fi done < "/etc/letsencrypt/domains.txt"# 检查nginx配置并重新加载 nginx -t && nginx -s reload -
start.sh
容器启动时自动执行的脚本:#!/bin/bash# 检查必要的环境变量 if [ -z "$ALIYUN_ACCESS_KEY_ID" ] || [ -z "$ALIYUN_ACCESS_KEY_SECRET" ]; thenecho "错误: 未设置阿里云访问密钥环境变量"exit 1 fi# 创建 Aliyun 配置文件 mkdir -p /etc/letsencrypt cat <<EOT > /etc/letsencrypt/aliyun.ini dns_aliyun_access_key = $ALIYUN_ACCESS_KEY_ID dns_aliyun_access_key_secret = $ALIYUN_ACCESS_KEY_SECRET EOT# 设置严格的权限 chmod 600 /etc/letsencrypt/aliyun.ini# 设置默认值 : ${RENEW_CRON_SCHEDULE:="0 0 1 * *"} : ${CERTBOT_EMAIL:="admin@example.com"} : ${DNS_PROPAGATION_SECONDS:="60"}# 确保domains.txt存在 touch /etc/letsencrypt/domains.txt# 初始获取或更新 SSL 证书 /usr/local/bin/deploy-certbot.sh# 配置证书自动续期 echo "$RENEW_CRON_SCHEDULE /usr/local/bin/deploy-certbot.sh >> /var/log/certbot/renew.log 2>&1" > /etc/cron.d/certbot-renew chmod 0644 /etc/cron.d/certbot-renew# 创建日志目录 mkdir -p /var/log/certbot touch /var/log/certbot/renew.log# 启动 cron 服务 service cron start# 启动 nginx exec nginx -g "daemon off;" -
/etc/nginx/
-
nginx.conf:Nginx 的主配置文件
#=============================================start 全局块==================================================================#运行用户 #user nobody; #worker进程数量,通常设置为cpu核数相等 worker_processes 4;#全局错误文件 #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid位置 #pid logs/nginx.pid; #=============================================end 全局块=====================================================================#=============================================start events块================================================================ events {# 单个worker进程最大并发连接数worker_connections 1024; } #=============================================end events块==================================================================#=============================================start http块==================================================================http {#引入mime类型的定义文件include mime.types;default_type application/octet-stream;#设置日志格式#log_format main ' - [] "" '# ' "" '# '"" ""';#access_log logs/access.log main;sendfile on;#tcp_nopush on;#连接超时时间#keepalive_timeout 0;keepalive_timeout 65;#开启gzip压缩#gzip on;#上传大小限制client_max_body_size 512m;#自定义变量 $connection_upgrademap $http_upgrade $connection_upgrade {default keep-alive; #默认为keep-alive 可以支持 一般http请求'websocket' upgrade; #如果为websocket 则为 upgrade 可升级的。#default upgrade;#'' close;}#引入配置文件include vhost/*.conf; }#=============================================end http块================================================================= -
vhost 目录:根据域名生成的各子配置文件
-
-
domains.txt
- 存放所有需要申请证书的域名,每行一个。
五、运行容器
准备就绪后,可以执行以下命令来启动容器:
docker run -d \-p 80:80 \-p 443:443 \--name nginx-certbot \-e ALIYUN_ACCESS_KEY_ID=<你的阿里云AccessKeyId> \-e ALIYUN_ACCESS_KEY_SECRET=<你的阿里云AccessKeySecret> \-e CERTBOT_EMAIL=<你的邮箱> \-e RENEW_CRON_SCHEDULE="0 0 1 * *" \-v /data/nginx-certbot/certs:/etc/letsencrypt \-v /data/nginx-certbot/log-certbot:/var/log/certbot \-v /data/nginx-certbot/log:/var/log/nginx \-v /data/nginx-certbot/conf:/etc/nginx \-v /data/nginx-certbot/html:/usr/share/nginx/html \registry.cn-hangzhou.aliyuncs.com/hbteck/nginx-certbot:1.20.2
参数说明:
- -p 80:80 和 -p 443:443:将宿主机的 80/443 端口映射给容器,用于 HTTP/HTTPS。
- -e ALIYUN_ACCESS_KEY_ID / ALIYUN_ACCESS_KEY_SECRET:阿里云 DNS 插件需要的密钥。
- -e CERTBOT_EMAIL:用于跟证书签发相关的提示信息接收。
- -e RENEW_CRON_SCHEDULE:设置证书自动续期的 cron 表达式(默认为每月1日00:00 点执行 certbot renew)。
- -v /data/nginx-certbot/conf:/etc/nginx:持久化 Nginx 配置文件,方便宿主机直接修改。
- -v /data/nginx-certbot/certs:/etc/letsencrypt:持久化证书文件,让重启或更新不会丢失证书。
- 其他挂载可根据自身需求调整。
成功运行后,容器会自动加载已有 Nginx 配置并尝试运行部署脚本(如果 domains.txt 里已有域名)。
六、添加域名及证书
当容器正常运行后,通过以下命令添加新域名并生成对应的 HTTPS 配置:
docker exec nginx-certbot /usr/local/bin/add-domain.sh <domain> [backend_url] [local_ip]
示例:
docker exec nginx-certbot /usr/local/bin/add-domain.sh hbteck.com \http://172.19.160.13:17111/ 172.19.160.13
- :必填,目标域名。
- [backend_url]:可选,域名后端服务的转发地址,默认 http://localhost:8080。
- [local_ip]:可选,用于特定场景自动生成更多配置规则。
该脚本会完成以下操作:
- 将新域名写入 /etc/letsencrypt/domains.txt。
- 调用 /usr/local/bin/deploy-certbot.sh 进行 DNS-01 方式的证书申请。
- 成功后在 /etc/nginx/vhost/ 下写入对应的 vhost 配置文件,再重载 Nginx。
如果一切正常,可以在浏览器访问 https://hbteck.com 测试效果。
七、查看与管理证书
-
查看证书详情:
docker exec nginx-certbot certbot certificates如果成功,会列出每个域名证书所在路径及过期时间。
-
手动重载 Nginx 配置:
docker exec -it nginx-certbot /usr/sbin/nginx -s reload -
手动执行续期:
docker exec -it nginx-certbot certbot renew --deploy-hook 'nginx -s reload'一般不需要手动运行,脚本中已经配置了 cron 任务每天检查并每月1日00:00自动续期。
八、常见问题及解决方案
-
申请证书失败:
- 请确认域名的 DNS 解析在阿里云上,并且 AccessKey 拥有 AliyunDNSFullAccess 权限。
- 检查是不是填写了错误的 Access Key 信息。
- 域名是否在 /etc/letsencrypt/domains.txt 中已存在,如果有问题可删除后重试。
-
80 和 443 端口冲突:
- 如果宿主机已占用 80/443 端口,可修改映射端口,如 -p 8080:80 -p 8443:443,但需要相应修改 nginx.conf。
-
自定义 Nginx 配置:
- 直接修改宿主机 /data/nginx-certbot/conf/nginx.conf 或 vhost/文件,完成后执行 docker exec nginx-certbot nginx -s reload。
-
脚本执行权限:
- 如果提示权限不足,可在宿主机手动 chmod +x /data/nginx-certbot/conf/*.sh 等路径,或在 Dockerfile 中确保脚本均可执行。
九、总结
通过在 Docker 中整合 Nginx + Certbot + 阿里云 DNS 插件,我们可以轻松实现多域名自动化申请及续期 SSL 证书的流程。该方案具有以下优点:
- DNS-01 验证方式无需对外暴露除 80/443 以外的端口,也不影响原有服务。
- 对多域名、多环境非常灵活,可以快速添加新域名。
- 证书续期完全自动化,无需人工干预。
只要按照上述步骤,下载配置,将 Access Key 写入环境变量,启动容器并执行 add-domain.sh 脚本,即可轻松接入新的 HTTPS 域名。祝你在实际生产环境中使用顺利,助力网站与应用更安全、稳定地运行!
相关文章:
使用 Docker + Nginx + Certbot 实现自动化管理 SSL 证书
使用 Docker Nginx Certbot 实现自动化管理 SSL 证书 在互联网安全环境日益重要的今天,为站点或应用部署 HTTPS 已经成为一种常态。然而,手动申请并续期证书既繁琐又容易出错。本文将以 Nginx Certbot 为示例,基于 Docker 容器来搭建一个…...
粒子群算法 笔记 数学建模
引入: 如何找到全局最大值:如果只是贪心的话,容易被局部最大解锁定 方法有:盲目搜索,启发式搜索 盲目搜索:枚举法和蒙特卡洛模拟,但是样例太多花费巨量时间 所以启发式算法就来了,通过经验和规…...
【C语言】结构体与共用体深入解析
在C语言中,结构体(struct)和共用体(union)都是用来存储不同类型数据的复合数据类型,它们在程序设计中具有重要的作用。 推荐阅读:操作符详细解说,让你的编程技能更上一层楼 1. 结构体…...
es6.7.1分词器ik插件安装-和head插件连接es特殊配置
es6.7.1分词器ik插件安装-和head插件连接es特殊配置 如果对运维课程感兴趣,可以在b站上、A站或csdn上搜索我的账号: 运维实战课程,可以关注我,学习更多免费的运维实战技术视频 1.查看es6.7.1和es-head安装位置和es插件路径 [ro…...
java求职学习day18
常用的设计原则和设计模式 1 常用的设计原则(记住) 1.1 软件开发的流程 需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级 1.2 常用的设计原则 (1)开闭原则(Open Close Principle…...
单链表专题(上)
链表的定义与创建 线性表: 1. 物理结构上不一定是线性的 2. 逻辑结构上一定是线性的 链表是一种物理存储结构上非连续,非顺序的存储结构 链表也是线性表的一种,但是在物理结构上不是连续的 链表是由一个一个的节点组成,需要数…...
【stm32学习】STM32F103相关特性
| 名称 | 缩写 | 频率 | 外部连接 | 功能 | 用途 | 特性 | |--------------------|------|----------------|---------------|------------|--------------|----------------| | 外部高速晶体振荡器 | HSE | 4~16MHz …...
PostGIS笔记:PostgreSQL中表、键和索引的基础操作
创建、查看与删除表 在数据库中创建一个表,使用如下代码: create table streets (id serial not null primary key, name varchar(50));这里的表名是streets,id是主键所以非空,采用serial数据类型,这个数据类型会自动…...
蓝桥杯python语言基础(3)——循环结构
一、for语句 理解range函数 range(start, stop, step) start: 序列开始的数字(默认为0)。stop: 序列结束的数字(不包含stop)。step: 步长(默认为1)。 练习 输出在 l 和 r 之间的所有偶数: pri…...
微服务网关鉴权之sa-token
目录 前言 项目描述 使用技术 项目结构 要点 实现 前期准备 依赖准备 统一依赖版本 模块依赖 配置文件准备 登录准备 网关配置token解析拦截器 网关集成sa-token 配置sa-token接口鉴权 配置satoken权限、角色获取 通用模块配置用户拦截器 api模块配置feign…...
23【进制的理解】
很多人可能听过计算机的最底层是2进制执行,但是原理并不知道,我们今天先不讨论那么复杂的问题,先讨论什么是进制 1910,10并不是1个字符,而是2个字符,也就是说在10进制里面没有“10”这个字符,1…...
jemalloc 5.3.0的tsd模块的源码分析
一、背景 在主流的内存库里,jemalloc作为android 5.0-android 10.0的默认分配器肯定占用了非常重要的一席之地。jemalloc的低版本和高版本之间的差异特别大,低版本的诸多网上整理的总结,无论是在概念上和还是在结构体命名上在新版本中很多都…...
【Convex Optimization Stanford】Lec3 Function
【Convex Optimization Stanford】Lec3 Function 前言凸函数的定义对凸函数在一条线上的限制增值扩充? 一阶条件二阶条件一些一阶/二阶条件的例子象集和sublevel set关于函数凸性的扩展(Jesen Inequality)保持函数凸性的操作非负加权和 & 仿射函数的…...
深入 Rollup:从入门到精通(三)Rollup CLI命令行实战
准备阶段:初始化项目 初始化项目,这里使用的是pnpm,也可以使用yarn或者npm # npm npm init -y # yarn yarn init -y # pnpm pnpm init安装rollup # npm npm install rollup -D # yarn yarn add rollup -D # pnpm pnpm install rollup -D在…...
wangEditor富文本编辑器,Laravel上传图片配置和使用
文章目录 前言步骤1. 构造好前端模版2. 搭建后端存储3. 调试 前言 由于最近写项目需要使用富文本编辑器,使用的是VUE3.0版本所以很多不兼容,实际测试以后推荐使用wangEditor 步骤 构造好前端模版搭建后端存储调试 1. 构造好前端模版 安装模版 模版安…...
chrome源码剖析—进程通信
Chrome 浏览器采用多进程架构(multi-process architecture),这种架构使得每个浏览器标签、扩展、插件、GPU 渲染等都在独立的进程中运行。为了确保不同进程之间的高效通信,Chrome 使用 进程间通信(IPC, Inter-Process …...
JJJ:linux时间子系统相关术语
文章目录 墙上时间内核管理的各种时间无时钟滴答模式(tickless mode 或 no-tick mode)简要介绍具体实现动态时钟滴答 Dynamic Ticks完全无时钟滴答(Full Tickless) nohz sleep单触发模式 oneshot mode 墙上时间 真实世界的真实时…...
0 基础学运维:解锁 K8s 云计算运维工程师成长密码
前言:作为一个过来人,我曾站在技术的门槛之外,连电脑运行内存和内存空间都傻傻分不清,完完全全的零基础。但如今,我已成长为一名资深的k8s云计算运维工程师。回顾这段历程,我深知踏上这条技术之路的艰辛与不…...
大一计算机的自学总结:位运算的应用及位图
前言 不仅异或运算有很多骚操作,位运算本身也有很多骚操作。(尤其后几个题,太逆天了) 一、2 的幂 class Solution { public:bool isPowerOfTwo(int n) {return n>0&&n(n&-n);} }; 根据二进制表示数的原理&#…...
计算机毕业设计Django+Tensorflow音乐推荐系统 机器学习 深度学习 音乐可视化 音乐爬虫 知识图谱 混合神经网络推荐算法 大数据毕设
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
学习---3
有序数组的排序:一、暴力解法:思路:遍历数组,对每个数组元素进行平方,再用sort排序。时间复杂度:O(nlog n)二、双指针解法:思路:如果有序数组中有负数,那么这个负数平方之…...
基于三菱PLC农田灌溉及MCGS组态智能灌溉系统说明双万字
基于三菱PLC农田灌溉 包含说明一万 和MCGS组态农田智能灌溉系统说明一万前阵子回豫东老家帮我叔打理那三亩秋月梨果园,那浇地给我整得怀疑人生——三伏天顶着三十七八度的太阳,扛着铁锹跑遍地头开电磁阀,中午热得头晕就算了,晚上还…...
动态卷积在图像分割中的应用与优化策略
1. 动态卷积如何让图像分割更智能 第一次接触动态卷积这个概念时,我正被一个医学图像分割项目困扰。传统卷积神经网络在处理不同组织边界的细微变化时总是力不从心,直到尝试了动态卷积方案,分割精度直接提升了8%。这种"会思考的卷积核&…...
usearch的API测试数据生成:使用Faker创建模拟数据
usearch的API测试数据生成:使用Faker创建模拟数据 【免费下载链接】usearch Fastest Open-Source Search & Clustering engine for Vectors & 🔜 Strings in C, C, Python, JavaScript, Rust, Java, Objective-C, Swift, C#, GoLang, and Wolf…...
Phi-4-mini-reasoning快速上手:3步完成vLLM服务部署+Chainlit前端验证
Phi-4-mini-reasoning快速上手:3步完成vLLM服务部署Chainlit前端验证 1. 模型简介 Phi-4-mini-reasoning 是一个基于合成数据构建的轻量级开源模型,专注于高质量、密集推理的数据处理能力。作为Phi-4模型家族的一员,它经过专门微调以提升数…...
利润中心(Profit Center)和段(Segment)在 SAP 中关系非常紧密,但它们的设计目的和应用场景有本质区别
利润中心(Profit Center)和段(Segment)在 SAP 中关系非常紧密,但它们的设计目的和应用场景有本质区别。简单来说,段(Segment)是利润中心的一个上级归类。它们之间通常是“一对多”的…...
iOS 版本nethack如何更换图形包-iNetHack2
这个iNetHack2这个应该我都没有找到设置按钮。后来无意中在贴吧中看到的。原来它的设置竟然在iOS的系统设置之中,是我少见多怪了,这可能是我见过的App 第1个在系统设置中设置的。UI中的Tileset 设置成Tiles32的界面风格就与nethack官方的UI一致了。...
Ollama+Qwen2.5-VL搭建教程:打造你的智能视觉分析工具
OllamaQwen2.5-VL搭建教程:打造你的智能视觉分析工具 1. 引言:为什么选择Qwen2.5-VL 在当今AI技术快速发展的时代,视觉-语言多模态模型正成为解决复杂问题的关键工具。Qwen2.5-VL-7B-Instruct作为通义千问系列的最新成员,在视觉…...
算法模拟类题目解析
前言:最近开始偏系统的从简单到难一步步刷算法题,先从模拟题开始,下边附带题目与连接,感兴趣可刷刷也可看看我的思路。 一.字符串展开 链接:https://ac.nowcoder.com/acm/problem/16644 来源:牛客网 题意…...
Zabbix 6.0部署避坑指南:为什么你的Ubuntu安装总卡在数据库初始化这一步?
Zabbix 6.0部署避坑指南:为什么你的Ubuntu安装总卡在数据库初始化这一步? 如果你正在Ubuntu上部署Zabbix 6.0,却反复在数据库初始化这一步失败,这篇文章就是为你准备的。不同于常规的安装教程,我们将聚焦于那些看似简…...
