使用 Docker + Nginx + Certbot 实现自动化管理 SSL 证书

使用 Docker + Nginx + Certbot 实现自动化管理 SSL 证书

在互联网安全环境日益重要的今天,为站点或应用部署 HTTPS 已经成为一种常态。然而,手动申请并续期证书既繁琐又容易出错。本文将以 Nginx + Certbot 为示例,基于 Docker 容器来搭建一个可以自动申请、自动续期的 SSL 证书服务,帮助你更轻松地管理多域名的 HTTPS。


一、方案简介

在这个方案中,我们使用官方 Nginx 镜像作为基础镜像,并在其中安装 Python3、Certbot 及阿里云 DNS 插件。通过以下几个关键脚本来实现自动部署及管理:

  1. start.sh:容器启动时执行,初始化阿里云 DNS 配置并启动 Nginx。
  2. add-domain.sh:添加新域名并为其申请证书,自动生成 Nginx 配置文件,最后重载 Nginx。
  3. deploy-certbot.sh:读取域名列表,一键申请或续期证书。
  4. domains.txt:存放所有需要申请 SSL 证书的域名列表。

同时,通过挂载相应目录,可以将证书、日志、Nginx 配置文件全部持久化到宿主机,不会因为容器的更新或重启而丢失。


二、准备工作

  1. 服务器已经安装 Docker(版本建议 >= 19.03)。

  2. 你拥有阿里云账号,并且已经开通了 AliyunDNSFullAccess 权限的 Access Key:

    • ALIYUN_ACCESS_KEY_ID
    • ALIYUN_ACCESS_KEY_SECRET
  3. 确保服务器安全组或本地防火墙已允许 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 目录等),可根据需求进行调整和定制。


四、主要文件和脚本说明

下面列举一些在本方案中常见或关键的文件结构与作用,仅供参考:

  1. 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"]
  2. 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} 配置完成"
  3. 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
  4. 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;"
    
  5. /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 目录:根据域名生成的各子配置文件

  6. 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

参数说明:

  1. -p 80:80 和 -p 443:443:将宿主机的 80/443 端口映射给容器,用于 HTTP/HTTPS。
  2. -e ALIYUN_ACCESS_KEY_ID / ALIYUN_ACCESS_KEY_SECRET:阿里云 DNS 插件需要的密钥。
  3. -e CERTBOT_EMAIL:用于跟证书签发相关的提示信息接收。
  4. -e RENEW_CRON_SCHEDULE:设置证书自动续期的 cron 表达式(默认为每月1日00:00 点执行 certbot renew)。
  5. -v /data/nginx-certbot/conf:/etc/nginx:持久化 Nginx 配置文件,方便宿主机直接修改。
  6. -v /data/nginx-certbot/certs:/etc/letsencrypt:持久化证书文件,让重启或更新不会丢失证书。
  7. 其他挂载可根据自身需求调整。

成功运行后,容器会自动加载已有 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]:可选,用于特定场景自动生成更多配置规则。

该脚本会完成以下操作:

  1. 将新域名写入 /etc/letsencrypt/domains.txt。
  2. 调用 /usr/local/bin/deploy-certbot.sh 进行 DNS-01 方式的证书申请。
  3. 成功后在 /etc/nginx/vhost/ 下写入对应的 vhost 配置文件,再重载 Nginx。

如果一切正常,可以在浏览器访问 https://hbteck.com 测试效果。


七、查看与管理证书

  1. 查看证书详情:

    docker exec nginx-certbot certbot certificates
    

    如果成功,会列出每个域名证书所在路径及过期时间。

  2. 手动重载 Nginx 配置:

    docker exec -it nginx-certbot /usr/sbin/nginx -s reload
    
  3. 手动执行续期:

    docker exec -it nginx-certbot certbot renew --deploy-hook 'nginx -s reload'
    

    一般不需要手动运行,脚本中已经配置了 cron 任务每天检查并每月1日00:00自动续期。


八、常见问题及解决方案

  1. 申请证书失败:

    • 请确认域名的 DNS 解析在阿里云上,并且 AccessKey 拥有 AliyunDNSFullAccess 权限。
    • 检查是不是填写了错误的 Access Key 信息。
    • 域名是否在 /etc/letsencrypt/domains.txt 中已存在,如果有问题可删除后重试。
  2. 80 和 443 端口冲突:

    • 如果宿主机已占用 80/443 端口,可修改映射端口,如 -p 8080:80 -p 8443:443,但需要相应修改 nginx.conf。
  3. 自定义 Nginx 配置:

    • 直接修改宿主机 /data/nginx-certbot/conf/nginx.conf 或 vhost/文件,完成后执行 docker exec nginx-certbot nginx -s reload。
  4. 脚本执行权限:

    • 如果提示权限不足,可在宿主机手动 chmod +x /data/nginx-certbot/conf/*.sh 等路径,或在 Dockerfile 中确保脚本均可执行。

九、总结

通过在 Docker 中整合 Nginx + Certbot + 阿里云 DNS 插件,我们可以轻松实现多域名自动化申请及续期 SSL 证书的流程。该方案具有以下优点:

  • DNS-01 验证方式无需对外暴露除 80/443 以外的端口,也不影响原有服务。
  • 对多域名、多环境非常灵活,可以快速添加新域名。
  • 证书续期完全自动化,无需人工干预。

只要按照上述步骤,下载配置,将 Access Key 写入环境变量,启动容器并执行 add-domain.sh 脚本,即可轻松接入新的 HTTPS 域名。祝你在实际生产环境中使用顺利,助力网站与应用更安全、稳定地运行!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10520.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

unity学习21:Application类与文件存储的位置

目录 1 unity是一个跨平台的引擎 1.1 使用 Application类&#xff0c;去读写文件 1.2 路径特点 1.2.1 相对位置/相对路径&#xff1a; 1.2.2 固定位置/绝对路径&#xff1a; 1.3 测试方法&#xff0c;仍然挂一个C#脚本在gb上 2 游戏数据文件夹路径&#xff08;只读&…

【Redis】hash 类型的介绍和常用命令

1. 介绍 Redis 中存储的 key-value 本身就是哈希表的结构&#xff0c;存储的 value 也可以是一个哈希表的结构 这里每一个 key 对应的一个 哈希类型用 field-value 来表示 2. 常用命令 命令 介绍 时间复杂度 hset key field value 用于设置哈希表 key 中字段 field 的值为…

基于51单片机和WS2812B彩色灯带的流水灯

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码四、主函数总结 系列文章目录 前言 用彩色灯带按自己想法DIY一条流水灯&#xff0c;谁不喜欢呢&#xff1f; 所用单片机&#xff1a;STC15W204S &#xff08;也可以用其他1T单片机&#xff0c;例如&#xff0c;S…

力扣017_最小覆盖字串题解----C++

题目描述 我们可以用滑动窗口的思想解决这个问题。在滑动窗口类型的问题中都会有两个指针&#xff0c;一个用于「延伸」现有窗口的 r 指针&#xff0c;和一个用于「收缩」窗口的 l 指针。在任意时刻&#xff0c;只有一个指针运动&#xff0c;而另一个保持静止。我们在 s 上滑动…

如何从客观角度批判性阅读分析博客

此文仅以个人博客为例&#xff0c;大量阅读朋友反馈给我的交流让我得知他们所理解我的博客所表达的意思并非我所想表达的&#xff0c;差异或大或小&#xff0c;因人而异。 观点与事实 只有从客观角度反复批判性阅读和分析&#xff0c;才能逐渐清晰观点和事实。 观点不等于事实…

深入理解MySQL 的 索引

索引是一种用来快速检索数据的一种结构, 索引使用的好不好关系到对应的数据库性能方面, 这篇文章我们就来详细的介绍一下数据库的索引。 1. 页面的大小: B 树索引是一种 Key-Value 结构&#xff0c;通过 Key 可以快速查找到对应的 Value。B 树索引由根页面&#xff08;Root&am…

Spring Boot项目如何使用MyBatis实现分页查询及其相关原理

写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油&#xff0c;冲鸭&#x…

LabVIEW温度修正部件测试系统

LabVIEW温度修正部件测试系统 这个基于LabVIEW的温度修正部件测试系统旨在解决飞行器温度测量及修正电路的测试需求。该系统的意义在于提供一个可靠的测试平台&#xff0c;用于评估温度修正部件在实际飞行器环境中的性能表现&#xff0c;从而确保飞行器的安全性和可靠性。 系统…

动态规划每日一练(四)

一、day1——最长数对链 题目链接&#xff1a; 646. 最长数对链 - 力扣&#xff08;LeetCode&#xff09;646. 最长数对链 - 给你一个由 n 个数对组成的数对数组 pairs &#xff0c;其中 pairs[i] [lefti, righti] 且 lefti < righti 。现在&#xff0c;我们定义一种 跟随…

对比category_encoders库和sklearn库中的OrdinalEncoder

OrdinalEncoder 是用来对数据中的分类特征进行编码、转换为整数标签的函数。 category_encoders库 from category_encoders import OrdinalEncoder 安装&#xff1a;pip install category_encoders --trusted-host pypi.tuna.tsinghua.edu.cn&#xff08;记得关闭代理&#x…

【PLL】杂散生成和调制

时钟生成 --》 数字系统 --》峰值抖动频率生成 --》无线系统 --》 频谱纯度、 周期信号的相位不确定性 随机抖动&#xff08;random jitter, RJ&#xff09;确定性抖动&#xff08;deterministic jitter,DJ&#xff09; 时域频域随机抖动积分相位噪声确定性抖动边带 杂散生成和…

理解神经网络:Brain.js 背后的核心思想

温馨提示 这篇文章篇幅较长,主要是为后续内容做铺垫和说明。如果你觉得文字太多,可以: 先收藏,等后面文章遇到不懂的地方再回来查阅。直接跳读,重点关注加粗或高亮的部分。放心,这种“文字轰炸”不会常有的,哈哈~ 感谢你的耐心阅读!😊 欢迎来到 brain.js 的学习之旅!…

Ubuntu下的Doxygen+VScode实现C/C++接口文档自动生成

Ubuntu下的DoxygenVScode实现C/C接口文档自动生成 Chapter1 Ubuntu下的DoxygenVScode实现C/C接口文档自动生成1、 Doxygen简介1. 安装Doxygen1&#xff09;方法一&#xff1a;2&#xff09;方法二&#xff1a;2. doxygen注释自动生成插件3. doxygen注释基本语法4. doxygen的生成…

Java内存模型 volatile 线程安全

专栏系列文章地址&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145290162 本文目标&#xff1a; 认识JMM认识volatile关键字&#xff1a;可见性和顺序性理解线程安全的概念 目录 Java内存模型可见性例子和volatilevolatile如何保证可见性原子性与单例模式…

【Proteus仿真】【51单片机】多功能计算器系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、LCD1602液晶显示 2、矩阵按键​ 3、加减乘除&#xff0c;开方运算 4、带符号运算 5、最大 999*999 二、使用步骤 基于51单片机多功能计算器 包含&#xff1a;程序&…

three.js+WebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值方面的问题)

上篇大家消化得如何了&#xff1f; 笔者说过&#xff0c;1级编号不同的两篇博文相对独立&#xff0c;所以这里笔者还是先给出完整代码&#xff0c;哪怕跟&#xff08;3&#xff09;没有太大区别。 这里我们把线的粗细调成5&#xff08;排除难选中的因素&#xff09;&#xff…

SAP内向交货单详解

【SAP系统研究】 #SAP #交货单 #内向交货单 一、内向交货单的概念 内向交货单,Inbound Delivery,是SAP系统中用于管理外部供应商或内部工厂向公司发货的文档。它记录了货物从供应商到公司仓库或生产地点的运输和接收过程。 内向交货单的主要功能有: (1)货物接收:用于…

扩展无限可能:Obsidian Web Viewer插件解析

随着 Obsidian 1.8.3 正式版的发布&#xff0c;备受期待的官方核心插件——Web Viewer 也终于上线。本文将从插件启用、设置以及应用场景三个方面详细介绍如何使用这一新功能&#xff0c;和大家一起更好地利用 Obsidian 进行内容管理和知识整理。 插件启用 Web Viewer作为官方…

如何在 ACP 中建模复合罐

概括 本篇博文介绍了 ANSYS Composite PrepPost (ACP) 缠绕向导。此工具允许仅使用几个条目自动定义高压罐中常见的悬垂复合结构。 ACP 绕线向导 将必要的信息输入到绕组向导中。重要的是要注意“参考半径”&#xff0c;它代表圆柱截面的半径&#xff0c;以及“轴向”&#x…

本地搭建deepseek-r1

一、下载ollama(官网下载比较慢&#xff0c;可以找个网盘资源下) 二、安装ollama 三、打开cmd&#xff0c;拉取模型deepseek-r1:14b(根据显存大小选择模型大小&#xff09; ollama pull deepseek-r1:14b 四、运行模型 ollama run deepseek-r1:14b 五、使用网页api访问&#x…