发布于 2026-01-05 6 阅读
0

🐳 如何使用 Nginx 将静态网站容器化,通过 Certbot 自动续订域名 SSL 证书,并将其部署到 DigitalOcean?

🐳 如何使用 Nginx 将静态网站容器化,通过 Certbot 自动续订域名 SSL 证书,并将其部署到 DigitalOcean?

介绍

欢迎回来,朋友们!👋 我们今天的议程非常重大……但别担心,它会很有趣,也很有启发性!

📌本文目标:

  1. 快速了解 Docker Compose(不“深入”,但接近了);
  2. 配置 Nginx、Certbot 和前端的 Docker 容器;
  3. 编写简单的静态网站(使用Parcel.js打包工具);
  4. 将完成的项目推送到git仓库;
  5. 将项目部署到DigitalOcean服务器;

毫不谦虚地说,我建议您将这篇文章添加到您的书签中,因为您在其他任何地方都找不到如此详细的部署过程描述。

……我们开始!🔥

什么是 Docker Compose?

请遵循 Docker 官方文档

Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。使用 Compose,您可以通过 YAML 文件配置应用程序的服务。然后,只需一条命令,即可根据配置创建并启动所有服务。

特征:

  • 单个主机上的多个独立环境;
  • 创建容器时保留卷数据;
  • 仅重新创建已更改的容器;
  • 变量以及在不同环境之间移动作品;

它是如何运作的?🙄

  1. 使用以下方式定义应用程序的环境Dockerfile
  2. 明确定义构成应用程序的各项服务docker-compose.yml,以便它们能够在隔离的环境中一起运行;
  3. 运行docker-compose up和组合功能会启动并运行您的整个应用程序;

项目结构


$ tree .
.
├── .editorconfig
├── .gitignore
├── .prettierignore
├── Makefile
├── docker-compose.prod.yml
├── docker-compose.yml
├── frontend
│   ├── .dockerignore
│   ├── Dockerfile
│   ├── package.json
│   └── src
│       ├── common
│       │   ├── robots.txt
│       │   └── sitemap.xml
│       ├── css
│       │   ├── reset.css
│       │   └── style.css
│       ├── html
│       │   └── index.html
│       ├── images
│       │   └── logo.png
│       └── js
│           └── index.js
└── webserver
    ├── nginx
    │   ├── default.conf
    │   ├── nginx.conf
    │   └── site.com.conf
    └── register_ssl.sh
Enter fullscreen mode Exit fullscreen mode

没时间看文章,但又想马上找到答案?🤔

没问题!我特意在我的 GitHub 上创建了一个仓库,其中包含了本文将要讨论的项目结构

GitHub 标志 koddr / example-static-website-docker-nginx-certbot

使用 Docker、Nginx 和 Certbot 的示例静态网站

只需git clone阅读说明即可README

Docker Compose 配置

我们来看一下docker-compose.yml这个文件。这是主文件,其中包含容器的基本配置:

# ./docker-compose.yml

version: "3.7"

services:
  nginx:
    container_name: nginx
    image: nginx:alpine
    networks:
      - nginx_net
    volumes: # 💡
      - ./webserver/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./webserver/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./webserver/certbot/conf:/etc/letsencrypt
      - ./webserver/certbot/www:/var/www/certbot
    ports:
      - 80:80
    restart: unless-stopped
    command: /bin/sh -c "while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g 'daemon off;'" # 💡

  certbot:
    container_name: certbot
    image: certbot/certbot
    networks:
      - nginx_net
    volumes:
      - ./webserver/certbot/conf:/etc/letsencrypt
      - ./webserver/certbot/www:/var/www/certbot
    restart: unless-stopped
    entrypoint: /bin/sh -c "trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;" # 💡
    depends_on:
      - nginx

networks:
  nginx_net:
    name: nginx_net
Enter fullscreen mode Exit fullscreen mode

💡了解这一点很有用:

  • 指令中的项目volumes应按如下方式编写:

<local dir|file><container dir|file>

例如,./webserver/nginx/nginx.conf:/etc/nginx/nginx.conf意思是:将nginx.conf文件从本地文件夹复制./webserver/nginx到容器文件夹/etc/nginx

如果要复制包含所有文件的文件夹,只需指定卷,如下所示:
./my-project/folder:/var/www/folder

  • command容器指令nginx帮助我们每 6 小时重启一次 Nginx,并下载新的 SSL 证书(如果有的话);
  • entrypoint容器指令certbot帮助我们每 12 小时检查一次是否需要新的 SSL 证书;

生产环境配置

好的!是时候docker-compose.prod.yml👌了

这是一个覆盖生产环境的文件(可能存储在不同的 git 仓库中或由不同的团队管理)[...] 运行时docker-compose up会自动读取覆盖文件。

# ./docker-compose.prod.yml

version: "3.7"

services:
  frontend:
    container_name: frontend
    build:
      context: ./frontend
    volumes:
      - static:/frontend/build

  nginx:
    volumes:
      - static:/usr/share/nginx/html # 💡
      - ./webserver/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./webserver/nginx/site.com.conf:/etc/nginx/sites-enabled/site.com.conf # ⚠️
      - ./webserver/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./webserver/certbot/conf:/etc/letsencrypt
      - ./webserver/certbot/www:/var/www/certbot
    ports:
      - 80:80
      - 443:443
    depends_on:
      - frontend # 💡

volumes:
  static:
Enter fullscreen mode Exit fullscreen mode

💡了解这一点很有用:

  • 我们已将所有构建文件放入其中/usr/share/nginx/html/,但您可以选择容器上的任何文件夹;
  • 最佳实践是在构建完所有前端文件后启动 Nginx,因此我们设置了depends_on一个指令,指定要等待创建的容器名称;

⚠️别忘了:

  • 改为site.com您的域名(或项目名称);

Docker

Nginx 和 Certbot


$ tree ./webserver
.
├── nginx
│   ├── default.conf
│   ├── nginx.conf
│   └── site.com.conf
└── register_ssl.sh
Enter fullscreen mode Exit fullscreen mode

获取和更新 SSL 证书的脚本(register_ssl.sh)最有意思。不过,我把它留给你们自己研究(作为作业)。

为了更好地理解,我将 Nginx 配置分为三个文件:main(nginx.conf)、get SSL(default.conf)和生产域(site.com.conf)。

为了避免文章篇幅过长,我建议您只阅读最后两个配置。Nginx 主配置请参见此处

  • 配置获取 SSL 证书并将 HTTP 重定向到 HTTPS(default.conf):
# ./webserver/nginx/default.conf

# Config for get SSL and redirect to HTTPS
server {
  listen      80;
  server_name .site.com;

  # Allow only for register SSL (Certbot)
  location ^~ /.well-known/acme-challenge { root /var/www/certbot; }

  # Redirect to HTTPS
  location / { return 301 https://site.com$request_uri; }
}
Enter fullscreen mode Exit fullscreen mode
  • 生产域的配置(site.com.conf):
# ./webserver/nginx/site.com.conf

# Redirect to non-WWW
server {
  listen      443 ssl http2;
  server_name www.site.com;

  # SSL
  ssl_certificate     /etc/letsencrypt/live/site.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/site.com/privkey.pem;

  # Additional Nginx options
  include /etc/letsencrypt/options-ssl-nginx.conf;

  # Diffie-Hellman parameter for DHE ciphersuites
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

  # Redirect to HTTPS
  location / { return 301 https://site.com$request_uri; }
}

# Config for HTTPS
server {
  listen      443 ssl http2;
  server_name site.com;

  # Root & index.html
  root /usr/share/nginx/html;
  index index.html;

  # SSL
  ssl_certificate     /etc/letsencrypt/live/site.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/site.com/privkey.pem;

  # Additional Nginx options
  include /etc/letsencrypt/options-ssl-nginx.conf;

  # Diffie-Hellman parameter for DHE ciphersuites
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

  # Security headers
  add_header X-Frame-Options "SAMEORIGIN" always;
  add_header X-XSS-Protection "1; mode=block" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header Referrer-Policy "no-referrer-when-downgrade" always;
  add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

  # dot files
  location ~ /\.(?!well-known) { deny all; }

  # SEO files
  location = /robots.txt { log_not_found off; }
  location = /sitemap.xml { log_not_found off; }
  location = /favicon.ico { log_not_found off; }

  # Assets, media
  location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {
    expires 7d;
  }

  # SVG, fonts
  location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {
    add_header Access-Control-Allow-Origin "*";
    expires 7d;
  }

  # Frontend files
  location / {
    try_files $uri $uri/ /index.html;
  }
}
Enter fullscreen mode Exit fullscreen mode

⚠️别忘了:

  • 更改site.com为您的域名;

前端

前端(静态网站)


$ tree ./frontend
.
├── .dockerignore
├── Dockerfile
├── package.json
└── src
    ├── common
    │   ├── robots.txt
    │   └── sitemap.xml
    ├── css
    │   ├── reset.css
    │   └── style.css
    ├── html
    │   └── index.html
    ├── images
    │   └── logo.png
    └── js
        └── index.js
Enter fullscreen mode Exit fullscreen mode

让我们更详细地探讨一些文件。

  • 要从容器中排除的忽略文件和文件夹列表(.dockerignore):
# ./frontend/.dockerignore

# Files
.dockerignore
Dockerfile
*.log

# Folders
.cache/
node_modules/
build/
Enter fullscreen mode Exit fullscreen mode
  • Docker 容器指令(Dockerfile):
# ./frontend/Dockerfile

FROM node:alpine

LABEL maintainer="Your Name"

WORKDIR /frontend
COPY package*.json ./
RUN npm install --only=production
COPY . .
RUN npm run build:prod
Enter fullscreen mode Exit fullscreen mode
  • 最后,Node.js 指令和依赖项(package.json):
// ./frontend/package.json

{
  "name": "frontend",
  "version": "1.0.0",
  "description": "Your project description.",
  "main": "./src/js/index.js",
  "scripts": {
    "build": "parcel build ./src/html/*.html -d ./build",
    "copy": "cp -R ./src/common/* ./build", // 💡
    "build:prod": "npm run build && npm run copy"
  },
  "author": "Your Name",
  "dependencies": {
    "parcel-bundler": "^1.12.4"
  }
}
Enter fullscreen mode Exit fullscreen mode

💡了解这一点很有用:

  • copy该命令帮助我们将文件(这些文件不会包含在最终的软件包中,但也很重要)从文件夹复制./src/common./build目标文件夹;

将项目推送到 Git

将代码存储在版本控制系统 (VCS) 中被认为是一种良好的做法,例如 GitHub/Bitbucket 等版本控制系统,或者您自己的版本控制系统,例如Gitea

因此,遵循以上最佳实践:

✅ 在版本控制系统中创建代码仓库;
✅ 将所有更改添加到提交中;
✅ 将提交推送到代码仓库;

部署到 DigitalOcean

  • 登录您的 DO 帐户;

还没有账号?通过我的推荐链接加入 DigitalOcean 吧(你赚100 美元,我赚 25 美元)。这是我给你的奖励!😉

  • 点击顶部的绿色按钮“创建”,然后选择“水滴”:

DigitalOcean Deploy 1

  • 选择“市场”选项卡,然后选择“ Docker ”:

DigitalOcean Deploy 2

  • 向下滚动,选择方案、存储、附加选项和数据中心区域(根据您的意愿选择任意区域);
  • 好的,滚动到“身份验证”部分,然后单击“新建 SSH 密钥”:

DigitalOcean Deploy 3

☝️提示:我建议为每个新服务器创建一个新的 SSH 密钥,因为这样比所有服务器使用同一个密钥更安全!

  • 按照右侧说明操作,生成新的 SSH 密钥并填写表格:

DigitalOcean Deploy 4

  • 重新检查 Droplet 的选项,然后点击底部的“创建 Droplet ”👍
  • 接下来,转到“ Droplets ”列表并添加您的域名:

DigitalOcean Deploy 5

  • 输入域名并选择服务器:

DigitalOcean Deploy 6

  • 为域名添加两条“ A ”记录(“ @ ”和“ www ”):

DigitalOcean Deploy 7

  • 通过 SSH 连接到您的服务器:
$ ssh root@<droplet IP>
Enter fullscreen mode Exit fullscreen mode
  • 克隆你的仓库并进入项目文件夹:
$ git clone https://github.com/user/project-name.git
$ cd project-name
Enter fullscreen mode Exit fullscreen mode
  • 请通过启动获取 SSL 证书的过程来检查 Certbot 的配置test mode
$ make certbot-test DOMAINS="site.com www.site.com" EMAIL=mail@site.com
Enter fullscreen mode Exit fullscreen mode

DOMAINS请使用您的域名(WWW 和非 WWW)指定变量。

  • 如果看到Congratulations!以下内容,请开始获取 SSL 证书的流程production mode
$ make certbot-prod DOMAINS="site.com www.site.com" EMAIL=mail@site.com
Enter fullscreen mode Exit fullscreen mode
  • 现在,检查 Nginx 和前端配置:
$ make deploy-test
Enter fullscreen mode Exit fullscreen mode
  • 控制台没有错误?您的静态网站已准备好上线:
$ make deploy-prod
Enter fullscreen mode Exit fullscreen mode

搞定!我们已经用 Nginx 和 Certbot 构建了容器化的静态网站,并部署到了 DigitalOcean!🎉

照片由

[标题] chuttersnap https://unsplash.com/photos/9cCeS9Sg6nU
[1] Jeffrey Blum https://unsplash.com/photos/FQ06bmigBqg
[2] John Barkiple https://unsplash.com/photos/l090uFWoPaI

PS

如果你想在这个博客上看到更多类似的文章,请在下方留言并订阅我。谢谢!😻

❗️您可以通过Boosty平台支持我,既可以长期支持,也可以一次性支持。所有收益都将用于支持我的开源软件项目,并激励我为社区创作新的产品和文章。

在Boosty上支持我

当然,您也可以帮助我让开发者的工作变得更好!只需以贡献者的身份加入我的项目即可。很简单!

我最需要你帮助(和点赞)的项目👇

  • 🔥 gowebly:一款新一代 CLI 工具,可轻松使用 Go 在后端创建出色的 Web 应用程序,使用 htmx、hyperscript 或 Alpine.js 以及最流行的 CSS 框架在前端进行开发。
  • create-go-app:通过运行一条 CLI 命令,创建一个包含 Go 后端、前端和部署自动化功能的新生产就绪项目。

我的其他一些小项目:yatrgosljson2csvcsv2api

文章来源:https://dev.to/koddr/how-to-dockerize-your-static-website-with-nginx-automatic-renew-ssl-for-domain-by-certbot-and-deploy-it-to-digitalocean-4cjc