使用 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 容器来搭建一个…...
游戏策划的分类
P3游戏策划分类 1.程序2.美术3.策划 程序:一般分为客户端程序和服务器程序 客户端程序一般负责游戏的前端画面表现 服务器程序负责游戏的后端运算 美术:角色原画,角色模型动作,场景原画,场景模型,UI设计&a…...
Edge-TTS在广电系统中的语音合成技术的创新应用
Edge-TTS在广电系统中的语音合成技术的创新应用 作者:本人是一名县级融媒体中心的工程师,多年来一直坚持学习、提升自己。喜欢Python编程、人工智能、网络安全等多领域的技术。 摘要 随着人工智能技术的快速发展,文字转语音(Te…...
python学opencv|读取图像(四十七)使用cv2.bitwise_not()函数实现图像按位取反运算
【0】基础定义 按位与运算:两个等长度二进制数上下对齐,全1取1,其余取0。按位或运算:两个等长度二进制数上下对齐,有1取1,其余取0。 按位取反运算:一个二进制数,0变1,1变0。 【1】…...
一文讲解Java中Object类常用的方法
在Java中,经常提到一个词“万物皆对象”,其中的“万物”指的是Java中的所有类,而这些类都是Object类的子类; Object主要提供了11个方法,大致可以分为六类: 对象比较: public native int has…...
【算法篇·更新中】C++秒入门(附练习用题目)
一.二分 1.二分查找 我们来看这样一道题: 有一个保证有序的数组a,它的长度为n。现在我们需要知道这个序列是否含有x。 数据范围:保证n<1e9 我们看到这道题之后,第一时间想到的就是暴力枚举了,可是我们发现直接枚举…...
面向对象编程 vs 面向过程编程
面向对象编程 vs 面向过程编程:深入解析这两种编程范式的区别 在当今软件开发领域,编程范式的选择对于项目的可维护性和可扩展性至关重要。面向对象编程(OOP)和面向过程编程(POP)是两种根本的编程思想。本…...
【Rust自学】16.2. 使用消息传递来跨线程传递数据
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 16.2.1. 消息传递 有一种很流行而且能保证安全并发的技术(或者叫机制ÿ…...
【四川乡镇界面】图层shp格式arcgis数据乡镇名称和编码2020年wgs84无偏移内容测评
本文将详细解析标题和描述中提到的IT知识点,主要涉及GIS(Geographic Information System,地理信息系统)技术,以及与之相关的文件格式和坐标系统。 我们要了解的是"shp"格式,这是一种广泛用于存储…...
人物传记之新月篇
相关故事链接(及时更新):Python的那些事第四篇:编程中的智慧之光控制结构-CSDN博客 Python的那些事第五篇:数据结构的艺术与应用-CSDN博客 目录 1. C语言程序:增强版加密与解密工具 2. Python程序&#x…...
TypeScript中的函数:类型安全与高级特性
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
DDD 和 TDD
领域驱动设计(DDD) DDD 是一种软件开发方法,强调通过与领域专家的密切合作来构建一个反映业务逻辑的模型。其核心思想是将业务逻辑和技术实现紧密结合,以便更好地解决复杂的业务问题。 DDD 的关键概念: 1. 领域模型 …...
【C语言分支与循环结构详解】
目录 ---------------------------------------begin--------------------------------------- 一、分支结构 1. if语句 2. switch语句 二、循环结构 1. for循环 2. while循环 3. do-while循环 三、嵌套结构 结语 -----------------------------------------end----…...
FaceFusion
文章目录 一、关于 FaceFusion预览 二、安装三、用法 一、关于 FaceFusion FaceFusion 是行业领先的人脸操作平台 github : https://github.com/facefusion/facefusion官方文档:https://docs.facefusion.io/Discord : https://discord.com/invite/facefusion-1141…...
图论——单源最短路的扩展应用
acwing1137.选择最佳路线 本题有两个解决思路 1.建立虚拟源点,连接虚拟源点和 w w w个可作为起点的点,边权为0,这样只需要从虚拟源点开始做一遍最短路算法便可。 2.反向建边,把所有的add(a,b,c)变成add(b,a,c),这样只…...
Linux shell脚本笔记-One
前言 本文主要汇总有关shell脚本常用的知识点,有时候使用忘记某些用法指令,特此汇总方便后续查阅。 一.shell脚本编写的头部定义: 定义的shell脚本头部有多种写法,具体根基实际系统结构处理,如下: #!/bin/sh ÿ…...
【C语言----函数详解】
目录 ----------------------------------------begin-------------------------------------- 引言 一、函数是什么 二、函数的定义和声明 1. 函数的定义 2. 函数的声明 三、函数的调用 四、函数参数传递 五、函数的返回值 六、递归函数 七、函数指针 八、总结 ---…...
QT交叉编译环境搭建(Cmake和qmake)
介绍一共有两种方法(基于qmake和cmake): 1.直接调用虚拟机中的交叉编译工具编译 2.在QT中新建编译套件kits camke和qmake的区别:CMake 和 qmake 都是自动化构建工具,用于简化构建过程,管理编译设置&…...
Zemax 中的屋脊棱镜建模
光学棱镜是一种透明的光学元件,其表面平坦且抛光,可以折射光线。最常见的棱镜类型是三棱镜,它有两个三角形底座和三个矩形或略呈梯形的表面。棱镜通常由玻璃或丙烯酸等材料制成。当光线以一定角度进入棱镜时,它会在穿过棱镜时发生…...
CUDA学习-内存访问
一 访存合并 1.1 说明 本部分内容主要参考: 搞懂 CUDA Shared Memory 上的 bank conflicts 和向量化指令(LDS.128 / float4)的访存特点 - 知乎 1.2 share memory结构 图1.1 share memory结构 放在 shared memory 中的数据是以 4 bytes(即 32 bits)作为 1 个 word,依…...
Nginx 开发总结
文章目录 1. Nginx 基础概念1-1、什么是 Nginx1-2、Nginx 的工作原理1-3、Nginx 的核心特点1-4、Nginx 的常见应用场景1-5、Nginx 与 Apache 的区别1-6、 Nginx 配置的基本结构1-7、Nginx 常见指令 2. Nginx 配置基础2-1、Nginx 配置文件结构2-2、全局配置 (Global Block)2-3、…...
目标跟踪之sort算法(3)
这里写目录标题 1 流程1 预处理2 跟踪 2 代码 参考:sort代码 https://github.com/abewley/sort 1 流程 1 预处理 1.1 获取离线检测数据。1.2 实例化跟踪器。2 跟踪 2.1 轨迹处理。根据上一帧的轨迹预测当前帧的轨迹,剔除到当前轨迹中为空的轨迹得到当前…...
C++/stack_queue
目录 1.stack 1.1stack的介绍 1.2stack的使用 练习题: 1.3stack的模拟实现 2.queue的介绍和使用 2.1queue的介绍 2.2queue的使用 2.3queue的模拟实现 3.priority_queue的介绍和使用 3.1priority_queue的介绍 3.2priority_queue的使用 欢迎 1.stack 1.1stack…...
小程序电商运营内容真实性增强策略及开源链动2+1模式AI智能名片S2B2C商城系统源码的应用探索
摘要:随着互联网技术的不断发展,小程序电商已成为现代商业的重要组成部分。然而,如何在竞争激烈的市场中增强小程序内容的真实性,提高用户信任度,成为电商运营者面临的一大挑战。本文首先探讨了通过图片、视频等方式增…...
「Unity3D」在Unity中使用C#控制显示Android的状态栏
Unity打包的Android默认都是全屏,如果想要在真机上显示状态栏,就需要额外设置,有两种方式: 第一种,使用Android的Java代码去控制,然后以插件的方式放到Unity中,被C#调用。第二种,使…...
基于51单片机和ESP8266(01S)、LCD1602、DS1302、独立按键的WiFi时钟
目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、延时2、定时器03、串口通信4、DS13025、LCD16026、独立按键 四、主函数总结 系列文章目录 前言 之前做了一个WiFi定时器时钟,用八位数码管进行显示,但是定时器时钟的精度较低࿰…...
AI 浪潮席卷中国年,开启科技新春新纪元
在这博主提前祝大家蛇年快乐呀!!! 随着人工智能(AI)技术的飞速发展,其影响力已经渗透到社会生活的方方面面。在中国传统节日 —— 春节期间,AI 技术也展现出了巨大的潜力,为中国年带…...
STM32 LED呼吸灯
接线图: 这里将正极接到PA0引脚上,负极接到GND,这样就高电平点亮LED,低电平熄灭。 占空比越大,LED越亮,占空比越小,LED越暗 PWM初始化配置 输出比较函数介绍: 用这四个函数配置输…...
机器学习day4
自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测 import numpy as np import torch import torch.nn as nn import torch.optim as optimizer import matplotlib.pyplot as pltclass1_points np.array([[2.1, 1.8],[1.9, 2…...
《哈佛家训》
《哈佛家训》是一本以教育为主题的书籍,旨在通过一系列富有哲理的故事和案例,传递积极的人生观、价值观和教育理念。虽然它并非直接由哈佛大学官方出版,但其内容深受读者喜爱,尤其是在家庭教育和个人成长领域。 以下是《哈佛家训…...
为什么redis会开小差?Redis 频繁异常的深度剖析与解决方案
文章目录 导读为什么redis会开小差?1.连接数过多2.bigkey3.慢命令操作4.内存策略不合理5.外部数据双写一致性6.保护机制未开启7. 数据集中过期8. CPU饱和9. 持久化阻塞10. 网络问题结论 导读 提起分布式缓存,想必大多数同学脑海中都会浮出redis这个名字…...
window中80端口被占用问题
1,查看报错信息 可以看到在启动项目的时候,8081端口被占用了,导致项目无法启动。 2,查看被占用端口的pid #语法 netstat -aon|findstr :被占用端口#示例 netstat -aon|findstr :8080 3,杀死进程 #语法 taikkill /pid…...
< OS 有关 > 阿里云:轻量应用服务器 的使用 :轻量化 阿里云 vpm 主机
原因: < OS 有关 > 阿里云:轻量应用服务器 的使用 :从新开始 配置 SSH 主机名 DNS Tailscale 更新OS安装包 最主要是 清除阿里云客户端这个性能杀手-CSDN博客 防止 I/O 祸害系统 操作: 查看进程&#x…...
解决报错“The layer xxx has never been called and thus has no defined input shape”
解决报错“The layer xxx has never been called and thus has no defined input shape”(这里写自定义目录标题) 报错显示 最近在跑yolo的代码时遇到这样一个错误,显示“the layer {self.name} has never been called”.这个程序闲置了很久,每次一遇到…...
Microsoft Visual Studio 2022 主题修改(补充)
Microsoft Visual Studio 2022 透明背景修改这方面已经有很多佬介绍过了,今天闲来无事就补充几点细节。 具体的修改可以参考:Microsoft Visual Studio 2022 透明背景修改(快捷方法)_material studio怎么把背景弄成透明-CSDN博客文…...
PyCharm接入DeepSeek实现AI编程
目录 效果演示 创建API key 在PyCharm中下载CodeGPT插件 配置Continue DeepSeek 是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的 AI 模型。DeepSeek-V3 是 DeepSeek 公司推出的最新一代 AI 模型。其前身是 DeepSeek-V2.5,经过持续的…...
C++ 包装器与绑定器的应用之回调函数的实现
回调函数的实现 在消息队列和网络库的框架中,当接收到消息(报文)时,回调用户自定义的函数对象,把消息(报文)参数传给它,由它决定如何处理。 queue参考文章:C queue(STL queue&…...
一文读懂DeepSeek-R1论文
目录 论文总结 摘要 1. 引言 1.1. 贡献 1.2. 评估结果总结 2. 方法 2.1. 概述 2.2. DeepSeek-R1-Zero:在基础模型上进行强化学习 2.2.1. 强化学习算法 2.2.2. 奖励建模 2.2.3. 训练模板 2.2.4. DeepSeek-R1-Zero 的性能、自我进化过程和顿悟时刻 2.3. …...
文献阅读 250128-Tropical forests are approaching critical temperature thresholds
Tropical forests are approaching critical temperature thresholds 来自 <Tropical forests are approaching critical temperature thresholds | Nature> 热带森林正在接近临界温度阈值 ## Abstract: The critical temperature beyond which photosynthetic machinery…...
【python】python基于机器学习与数据分析的二手手机特性关联与分类预测(源码+数据集)【独一无二】
👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 python基于机器学习与数据分析的二手手机特性关联与…...
构建可靠的时间序列预测模型:数据泄露检测、前瞻性偏差消除与因果关系验证
在时间序列分析领域中,存在多种可能影响分析结果有效性的技术挑战。其中,数据泄露、前瞻性偏差和因果关系违反是最为常见且具有显著影响的问题。 数据泄露:隐蔽的系统性错误 数据泄露是指在预测时理论上无法获取的信息,通过某种…...
Spring Boot - 数据库集成05 - 集成MongoDB
Spring Boot集成MongoDB 文章目录 Spring Boot集成MongoDB一:使用前的准备1:依赖导入 & 配置2:实体类创建 二:核心 - MongoRepository三:核心 - MongoTemplate1:集合操作2:文档操作(重点)3&…...
计算机网络之运输层
本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 04 传输层 在网上看到其他人做了相关笔记,就不再多余写了,直接参考着学习吧。 王道考研 计算机网络笔记 第五章:传输层_王道计算机网络传输层_Baret-H的博客-CSDN博…...
DeepSeekMoE:迈向混合专家语言模型的终极专业化
一、结论写在前面 论文提出了MoE语言模型的DeepSeekMoE架构,目的是实现终极的专家专业化(expert specialization)。通过细粒度的专家分割和共享专家隔离,DeepSeekMoE相比主流的MoE架构实现了显著更高的专家专业化和性能。从较小的2B参数规模开始&#x…...
知识库管理驱动企业知识流动与工作协同创新模式
内容概要 知识库管理在现代企业中扮演着至关重要的角色,其价值不仅体现在知识的积累,还在于通过优质的信息流动促进协作与创新。有效的知识库能够将分散的信息整合为有序、易于访问的资源,为员工提供实时支持,进而提升整体工作效…...
二叉树-堆(补充)
二叉树-堆 1.二叉树的基本特性2.堆2.1.堆的基本概念2.2.堆的实现2.2.1.基本结构2.2.2.堆的初始化2.2.3.堆的销毁2.2.4.堆的插入2.2.5.取出堆顶的数据2.2.6.堆的删除2.2.7.堆的判空2.2.8.堆的数据个数2.2.9.交换2.2.10.打印堆数据2.2.11.堆的创建2.2.12.堆排序2.2.13.完整代码 3…...
Java面试题2025-并发编程基础(多线程、锁、阻塞队列)
并发编程 一、线程的基础概念 一、基础概念 1.1 进程与线程A 什么是进程? 进程是指运行中的程序。 比如我们使用钉钉,浏览器,需要启动这个程序,操作系统会给这个程序分配一定的资源(占用内存资源)。 …...
【方法论】ChatGPT与DeepSeek的联合应用,提升工作效率的新解决方案
标题:ChatGPT与DeepSeek的联合应用,提升工作效率的新解决方案 【表格】ChatGPT与DeepSeek联合应用流程 阶段工具主要任务优势备注初稿生成ChatGPT基于用户输入生成初步内容高效、快速生成内容,适应多种主题适合生成长篇文章、报告、分析等验…...
RoboMaster- RDK X5能量机关实现案例(一)识别
作者:SkyXZ CSDN:https://blog.csdn.net/xiongqi123123 博客园:https://www.cnblogs.com/SkyXZ 在RoboMaster的25赛季,我主要负责了能量机关的视觉方案开发,目前整体算法已经搭建完成,实际方案上我使用的上…...
5分钟带你获取deepseek api并搭建简易问答应用
目录 1、获取api 2、获取base_url和chat_model 3、配置模型参数 方法一:终端中临时将加入 方法二:创建.env文件 4、 配置client 5、利用deepseek大模型实现简易问答 deepseek-v3是截止博文撰写之日,无论是国内还是国际上发布的大模型中…...