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

news2025/1/31 9:15:44

使用 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=60
    
    VOLUME ["/etc/letsencrypt", "/var/log/certbot"]
    
    CMD ["/usr/local/bin/start.sh"]
    
    
  2. add-domain.sh
    在容器内用于添加新域名并申请证书的核心脚本:

    #!/bin/bash
    
    new_domain=$1
    backend_url=${2:-"http://localhost:8080"}  # 默认后端地址
    local_ip=$3  # 第三参数: 本地IP地址
    
    # 检查参数
    if [ -z "$new_domain" ]; then
        echo "用法: 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; then
        echo "错误: 无效的域名格式"
        exit 1
    fi
    
    # 检查域名是否已存在
    if grep -q "^${new_domain}$" /etc/letsencrypt/domains.txt; then
        echo "域名 ${new_domain} 已存在"
        exit 0
    fi
    
    # 添加新域名
    echo "$new_domain" >> /etc/letsencrypt/domains.txt
    echo "已添加域名: $new_domain"
    
    # 为新域名获取证书
    if ! /usr/local/bin/deploy-certbot.sh; then
        echo "证书获取失败,移除域名配置"
        sed -i "\|^${new_domain}$|d" /etc/letsencrypt/domains.txt
        exit 1
    fi
    
    # 创建nginx配置
    if [ -z "$local_ip" ]; then
        cat <<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
    else
        cat <<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; then
        echo "Nginx配置检查失败,移除配置文件"
        rm -f /etc/nginx/vhost/${new_domain}.conf
        sed -i "\|^${new_domain}$|d" /etc/letsencrypt/domains.txt
        exit 1
    fi
    
    # 重新加载nginx配置
    nginx -s reload
    
    echo "域名 ${new_domain} 配置完成"
    
    
  3. deploy-certbot.sh
    该脚本读取 /etc/letsencrypt/domains.txt 里的域名,通过 DNS-01 方式批量申请或更新证书。

    #!/bin/bash
    
    # 检查aliyun配置文件
    if [ ! -f "/etc/letsencrypt/aliyun.ini" ]; then
        echo "错误: 未找到阿里云DNS凭证文件"
        exit 1
    fi
    
    # 检查domains.txt是否存在且不为空
    if [ ! -s "/etc/letsencrypt/domains.txt" ]; then
        echo "警告: 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}" == "#" ]] && continue
        
        echo "正在处理域名: $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/certbot
    
        if [ $? -eq 0 ]; then
            echo "成功为 $domain 获取/更新证书"
        else
            echo "为 $domain 获取/更新证书失败"
            # 如果获取证书失败,则退出脚本
            exit 1
        fi
    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" ]; then
        echo "错误: 未设置阿里云访问密钥环境变量"
        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_upgrade
          map $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.coloradmin.cn/o/2286848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DeepSeek R1:中国AI黑马的崛起与挑战

文章目录 技术突破&#xff1a;从零开始的推理能力进化DeepSeek R1-Zero&#xff1a;纯RL训练的“自我觉醒”DeepSeek R1&#xff1a;冷启动与多阶段训练的平衡之道 实验验证&#xff1a;推理能力的全方位跃升基准测试&#xff1a;超越顶尖闭源模型蒸馏技术&#xff1a;小模型的…

MFC开发,给对话框添加垂直滚动条并解决鼠标滚动响应的问题

无论在使用QT或者MFC进行界面开发时&#xff0c;都会出现在一个对话框里面存在好多的选项&#xff0c;导致对话框变得非常长或者非常大&#xff0c;就会显现的不美观&#xff0c;在这种情况下通常是添加一个页面的滚动条来解决这个问题&#xff0c;下面我们就来介绍给MFC的对话…

php接口连接数据库

框架&#xff1a;https://www.thinkphp.cn/doc 创建网站 域名自己写 创建文件夹&#xff0c;“test”拉取框架&#xff0c;地址栏输入 composer create-project topthink/think5.1.* tp5 会自动创建一个tp5文件夹 根目录选择刚刚创建拉框架的文件夹 以test为示例 “D:\test\…

【卫星通信】链路预算方法

本文介绍卫星通信中的链路预算方法&#xff0c;应该也适用于地面通信场景。 更多内容请关注gzh【通信Online】 文章目录 下行链路预算卫星侧参数信道参数用户侧参数 上行链路预算链路预算计算示例 下行链路预算 卫星侧参数 令卫星侧天线数为 M t M_t Mt​&#xff0c;每根天线…

解析静态链接

文章目录 静态链接空间与地址分配相似段合并虚拟地址分配符号地址确定符号解析与重定位链接器优化重复代码消除函数链接级别静态库静态链接优缺点静态链接 一组目标文件经过链接器链接后形成的文件即可执行文件,如果没有动态库的加入,那么这个可执行文件被加载后无需再进行重…

【C语言】函数递归

目录 1. 什么是递归 1.1 递归的思想&#xff1a; 1.2 递归的限制条件 2. 递归的限制条件 2.1 举例1&#xff1a;求n的阶乘 2.1.1 分析和代码实现 2.1.2 画图推演 2.2 举例2&#xff1a;顺序打印⼀个整数的每⼀位 2.2.1 分析和代码实现 2.2.2 画图推演 3. 递归与迭代…

从0到1:C++ 开启游戏开发奇幻之旅(二)

目录 游戏开发核心组件设计 游戏循环 游戏对象管理 碰撞检测 人工智能&#xff08;AI&#xff09; 与物理引擎 人工智能 物理引擎 性能优化技巧 内存管理优化 多线程处理 实战案例&#xff1a;开发一个简单的 2D 射击游戏 项目结构设计 代码实现 总结与展望 游戏…

线性调整器——耗能型调整器

线性调整器又称线性电压调节器&#xff0c;以下是关于它的介绍&#xff1a; 基本工作原理 线性调整器的基本电路如图1.1(a)所示,晶体管Q1(工作于线性状态,或非开关状态)构成一个连接直流源V和输出端V。的可调电气电阻,直流源V由60Hz隔离变压器&#xff08;电气隔离和整流&#…

C语言编译过程全面解析

今天是2025年1月26日&#xff0c;农历腊月二十七&#xff0c;一个距离新春佳节仅一步之遥的日子。城市的喧嚣中&#xff0c;年味已悄然弥漫——能在这个时候坚持上班的人&#xff0c;真可称为“牛人”了吧&#xff0c;哈哈。。。。 此刻&#xff0c;我在重新审视那些曾被遗忘的…

谈谈出国留学文书PS写作中的注意事项

在上期&#xff0c;小编介绍出国留学文书PS正文写作的几个可以采用的技巧。总之在正文的写作中&#xff0c;要避免将PS写成个人简历的repetition。也就是说不要将你目前所做的事情再次在PS中重述一遍&#xff0c;留学PS不是对你的工作经历或者学习经历的重复&#xff0c;而是需…

汇编基础语法及其示例

1.汇编指令 1.1汇编指令的基本格式 <opcode>{<cond>}{s} <Rd> , <Rn> , <shifter_operand> <功能码>{<条件码>}{cpsr影响位} <目标寄存器> , <第一操作寄存器> , <第二操作数> 注&#xff1a;第一操作寄存器…

学习ASP.NET Core的身份认证(基于JwtBearer的身份认证10)

基于Cookie传递token的主要思路是通过用户身份验证后&#xff0c;将生成的token保存到Response.Cookies返回客户端&#xff0c;后续客户端访问服务接口时会自动携带Cookie到服务端以便验证身份。之前一直搞不清楚的是服务端程序如何从Cookie读取token进行认证&#xff08;一般都…

Ollama 运行从 ModelScope 下载的 GGUF 格式的模型

本文系统环境 Windows 10 Ollama 0.5.7 Ollama 是什么&#xff1f; Ollama 可以让你快速集成和部署本地 AI 模型。它支持各种不同的 AI 模型&#xff0c;并允许用户通过简单的 API 进行调用 Ollama 的安装 Ollama 官网 有其下载及安装方法&#xff0c;非常简便 但如果希…

SpringBoot 整合 SSM

文章目录 SpringBoot 整合 SSM第一步&#xff1a;使用 Spring Initializr 创建项目第二步&#xff1a;现在配置类中配置数据库第三步&#xff1a;进行 MyBatis 相关操作编写数据表对应的实体类创建 mapper 接口利用 MyBaitsX 插件快速创建 xml 文件创建 Mapper 接口 SQL 实现在…

护眼好帮手:Windows显示器调节工具

在长时间使用电脑的过程中&#xff0c;显示器的亮度和色温对眼睛的舒适度有着重要影响。传统的显示器调节方式不仅操作繁琐&#xff0c;而且在低亮度下容易导致色彩失真。因此&#xff0c;今天我想为大家介绍一款适用于Windows系统的护眼工具&#xff0c;它可以帮助你轻松调节显…

基于Python的人工智能患者风险评估预测模型构建与应用研究(下)

3.3 模型选择与训练 3.3.1 常见预测模型介绍 在构建患者风险评估模型时,选择合适的预测模型至关重要。不同的模型具有各自的优缺点和适用场景,需要根据医疗数据的特点、风险评估的目标以及计算资源等因素进行综合考虑。以下详细介绍几种常见的预测模型。 逻辑回归(Logisti…

【NLP251】意图识别 与 Seq2Seq

Seq2Seq模型作为从RNN演进到Transformer和Attention机制的关键中间阶段&#xff0c;它不仅承前启后&#xff0c;还为我们深入理解这些复杂的模型架构提供了重要的基础。接下来&#xff0c;我们将详细探讨Seq2Seq模型的原理及其在自然语言处理领域中的应用。 1. 原理及网络框架 …

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验六----流域综合处理(超超超详细!!!)

流域综合处理 流域综合治理是根据流域自然和社会经济状况及区域国民经济发展的要求,以流域水流失治理为中心,以提高生态经济效益和社会经济持续发展为目标,以基本农田优化结构和高效利用及植被建设为重点,建立具有水土保持兼高效生态经济功能的半山区流域综合治理模式。数字高程…

unity使用内置videoplayer打包到安卓手机进行视频播放

1.新建UI&#xff0c;新建RawImage在画布当作视频播放的显示载体 2.新建VideoPlayer 3.新建Render Texture作为连接播放器视频显示和幕布的渲染纹理 将Render Texture同时挂载在VideoPlayer播放器和RawImage上。这样就可以将显示的视频内容在RawImage上显示出来了。 问题在于&a…