通过docker-compose重新部署博客
长话短说
该博客是很多年前通过lnmp1.6+typecho1.1
一键部署的. 当时我在部署这个博客时, 连命令行都还不太会用. 现在我算是多少掌握一些技术, 计划把这直接部署在系统的网站迁移到docker-compose
.
迁移要求
保证原网站各项功能正常, 同时便于将来维护和新增功能.
需求要点
- 不能重装操作系统 (当前操作系统是早已过期的
CentOS 8
); docker
容器版本尽可能与当前所用软件(php+mysql+nginx
)的版本保持一致;- 在
2
的前提下,php+mysql+nginx
宜写进同一docker-compose.yml - 通过
acme.sh
自动化管理SSL证书, 避免证书过期的风险; - 不允许网站独占
80/443
端口. 通过nginx
的server_name
对来自不同域名的请求进行分流; - 在
4
的前提下, 设置nginx_main
统一管理80/443
端口相关的流量转发.
思路框架
网站内部的nginx
对外暴露1180(no SSL)/11443(SSL)
端口, 由nginx_main
进行端口转发:80->1180, 443->11443
.
typecho/docker-compose.yml:
network_mode: bridge
nginx: 对外开放1180, 11443
php
mysql
nginx_main/docker-compose.yml:
network_mode: host
nginx: 端口转发: 1180l转80, 11443转443
目录结构
typecho
├── asset
├── docker-compose.yml
├── generate_ssl_cert.sh
├── logs
│ └── nginx
├── mysql
│ └── backup_20250724.sql
├── nginx
│ ├── conf.d
│ ├── nginx.conf
│ └── ssl
├── openssl-san.cnf
└── web
nginx_main
├── asset
│ ├── acme-install.sh
│ ├── ssl-grassyiyi.com.sh
├── docker-compose.yml
├── logs
│ └── nginx
├── nginx
│ ├── conf.d
│ ├── nginx.conf
│ └── ssl
├── restart.sh
└── web
└── 404.html
主要步骤
docker环境配置
安装配置docker
的过程见他处.
另外还需要安装以下的包, 否则会有docker
容器无法启动的问题 (我在其他系统不需要下面这步操作, 原因不明).
yum install -y epel-release
yum install -y libseccomp libseccomp-devel
配置typecho/docker-compose.yml
构建 old_php:5.6-fpm
该博客用到了2014年发布的php5.6
及相关的extensions
. 今年(2025)已新发布了php8.5
. 没试过我博客能否在新版php
上正常运行.
编辑Dockerfile
FROM php:5.6-fpm
# 更新软件源为 Debian Jessie 归档源,并忽略签名验证
RUN echo "deb [trusted=yes] http://archive.debian.org/debian/ jessie main" > /etc/apt/sources.list \
&& echo "deb [trusted=yes] http://archive.debian.org/debian-security/ jessie/updates main" >> /etc/apt/sources.list \
&& apt-get update \
&& apt-get install -y --allow-downgrades zlib1g=1:1.2.8.dfsg-2+deb8u1 \
&& apt-get install -y \
zlib1g-dev \
libpng12-dev \
libjpeg62-turbo-dev \
&& docker-php-ext-configure gd --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install gd mysqli
# && apt-get clean && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --allow-downgrades \
libzip-dev \
libmcrypt-dev \
libicu-dev \
libxslt1-dev \
# libxml2-dev \
libxml2=2.9.1+dfsg1-5+deb8u8 \
libxml2-dev=2.9.1+dfsg1-5+deb8u8 \
libssl-dev \
libjpeg-dev \
libpng-dev \
libmysqlclient-dev \
libgettextpo-dev \
unzip \
&& docker-php-ext-install \
xmlrpc \
shmop \
zip \
mcrypt \
pcntl \
intl \
xsl \
sockets \
sysvsem \
bcmath \
soap \
gettext \
pdo_mysql \
mysql \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# 复制本地 php.ini
COPY ./php.ini /usr/local/etc/php/php.ini
# 复制 Zend Guard Loader(如果需要)
COPY ./ZendGuardLoader.so /usr/local/lib/php/extensions/no-debug-non-zts-20131226/
RUN echo "zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20131226/ZendGuardLoader.so" >> /usr/local/etc/php/php.ini
RUN echo "zend_loader.enable=1" >> /usr/local/etc/php/php.ini
# 设置工作目录
WORKDIR /usr/share/nginx/html
数据库迁移
# 导出
mysqldump -u 用户名 -p密码 数据库名 > typecho_backup.sql
# 导入(后续操作)
docker exec -it mysql_container bash
mysql -u 用户名 --password=密码 数据库名 < typecho_backup.sql
配置网站内部的nginx
编辑typecho/nginx/conf.d/default.conf
server {
listen 80;
listen 443 ssl;
server_name _ ;
root /usr/share/nginx/html;
index index.php index.html index.htm;
ssl_certificate /etc/nginx/ssl/grassyiyi.com/grassyiyi.com.cer;
ssl_certificate_key /etc/nginx/ssl/grassyiyi.com/grassyiyi.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000; # 指向 PHP-FPM 容器
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
}
location ~ /.well-known {
allow all;
}
location ~ /\. {
deny all;
}
# 设置静态文件的缓存(可选,优化性能)
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg|eot)$ {
expires 30d;
access_log off;
}
access_log /var/log/nginx/typecho-access.log main;
error_log /var/log/nginx/typecho-error.log warn;
}
这里的ssl
证书是通过openssl
的生成自签证书. 这个证书的作用是确保网站在https
访问时功能正常. 证书生成脚本如下:
编辑typecho/generate_ssl_cert.sh
:
#!/bin/bash
# Script function: Generate a self-signed SSL certificate supporting multiple wildcard and main domains using a fixed openssl-san.cnf
# Does not modify the system OpenSSL configuration
# Output: domain.com.cer (certificate file) and domain.com.key (private key file)
# Configuration parameters
# CERT_FILE="/path/to/ssl/domain.com/domain.com.cer"
# KEY_FILE="/path/to/ssl/domain.com/domain.com.key"
CERT_FILE="./nginx/ssl/grassyiyi.com/grassyiyi.com.cer"
KEY_FILE="./nginx/ssl/grassyiyi.com/grassyiyi.com.key"
DAYS=10950 # 30-year validity (30 * 365)
CONFIG_FILE="openssl-san.cnf"
# Check if OpenSSL is installed
if ! command -v openssl &>/dev/null; then
echo "Error: OpenSSL is not installed. Please install OpenSSL first."
exit 1
fi
# Check if the configuration file exists
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "Error: Configuration file $CONFIG_FILE does not exist. Ensure openssl-san.cnf is in the current directory."
exit 1
fi
# Create output directories (if they don't exist)
CERT_DIR=$(dirname "$CERT_FILE")
KEY_DIR=$(dirname "$KEY_FILE")
sudo mkdir -p "$CERT_DIR" "$KEY_DIR"
# Generate private key and self-signed certificate
echo "Generating self-signed certificate (valid for $DAYS days)..."
if ! sudo openssl req -x509 -newkey rsa:2048 -nodes -days "$DAYS" \
-keyout "$KEY_FILE" -out "$CERT_FILE" \
-config "$CONFIG_FILE" -extensions req_ext; then
echo "Error: Certificate generation failed."
exit 1
fi
# Set file permissions
sudo chmod 644 "$CERT_FILE"
sudo chmod 600 "$KEY_FILE"
# Verify the certificate
echo "Certificate generation complete, verifying..."
if openssl x509 -in "$CERT_FILE" -text -noout | grep -A 4 "Subject Alternative Name" | grep -q "DNS:*.grassyiyi.com"; then
echo "Certificate verification successful! Certificate details:"
echo "Certificate path: $CERT_FILE"
echo "Private key path: $KEY_FILE"
openssl x509 -in "$CERT_FILE" -noout -subject -issuer -dates
echo "SAN included domains:"
openssl x509 -in "$CERT_FILE" -text -noout | grep -A 4 "Subject Alternative Name"
else
echo "Error: Certificate verification failed. Please check the $CONFIG_FILE configuration file."
exit 1
fi
echo "Certificate generated and stored at $CERT_FILE and $KEY_FILE"
echo "Use the following paths in your Nginx configuration:"
echo "ssl_certificate $CERT_FILE;"
echo "ssl_certificate_key $KEY_FILE;"
编辑typecho/openssl-san.cnf
:
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[dn]
C = CN
ST = Beijing
L = Beijing
O = grassyiyi
OU = IT
CN = grassyiyi.com
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.grassyiyi.com
DNS.2 = grassyiyi.com
DNS.3 = *.m2e.me
DNS.4 = m2e.me
启动网站
编辑typecho/docker-compose.yml
services:
nginx:
# image: nginx:1.16.1
image: nginx:1.28.0 # 换成新版的 nginx
container_name: typecho-nginx
ports:
- 1180:80
- 11443:443
volumes:
- /etc/localtime:/etc/localtime:ro # 时区与宿主机一致
- ./asset:/asset
- ./web:/usr/share/nginx/html
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl
- ./logs/nginx:/var/log/nginx
depends_on:
- php
restart: unless-stopped
networks:
- network
php:
image: old_php:5.6-fpm # 这里用了刚刚构建的镜像
container_name: typecho-old_php
volumes:
- /etc/localtime:/etc/localtime:ro
- ./web:/usr/share/nginx/html
depends_on:
- mysql
restart: unless-stopped
networks:
- network
mysql:
image: mysql:5.7
container_name: typecho-mysql
environment:
MYSQL_ROOT_PASSWORD: ChJ_1415926535
MYSQL_DATABASE: typecho
MYSQL_USER: typecho
MYSQL_PASSWORD: 123456 # 对内密码, 不必太复杂
volumes:
- /etc/localtime:/etc/localtime:ro
- ./mysql:/var/lib/mysql
restart: unless-stopped
networks:
- network
networks:
network:
driver: bridge
后续操作:
- 把网站文件移动到
./typecho/web/
- 数据库导入
docker-compose up -d
启动网站
此时通过1180/11443
能看到网站页面了. 要注意这里的11443
端口需要通过https
访问.
配置nginx_main/docker-compose.yml
nginx_main
所起的作用是端口转发(80->1180, 443->11443
), 这里的443
端口需要加合法的SSL
证书.
编辑nginx_main/docker-compose.yml
version: '3.0'
services:
nginx:
image: nginx:1.28.0
container_name: nginx-main
network_mode: host
volumes:
- /etc/localtime:/etc/localtime:ro
- ./asset:/asset
- ./web:/usr/share/nginx/html
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl
- ./logs/nginx:/var/log/nginx
# - /media/apps/typecho/web/:/usr/share/nginx/html/typecho
# - ./acme.sh:/root/.acme.sh
restart: unless-stopped
编辑nginx_main/nginx/conf.d/typecho.conf
server {
listen 80;
server_name grassyiyi.com www.grassyiyi.com m2e.me www.m2e.me;
root /usr/share/nginx/html/ ;
location ~ /.well-known {
allow all;
}
location / {
proxy_pass http://127.0.0.1:1180; # 无SSL端口转发
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;
proxy_set_header X-Forwarded-Port $server_port;
}
access_log /var/log/nginx/typecho-access.log main;
error_log /var/log/nginx/typecho-error.log warn;
}
# !!! 无ssl证书时需要先注释掉以下部分
server {
listen 443 ssl;
server_name grassyiyi.com www.grassyiyi.com m2e.me www.m2e.me;
ssl_certificate /etc/nginx/ssl/grassyiyi.com/fullchain.cer; # 证书文件路径
ssl_certificate_key /etc/nginx/ssl/grassyiyi.com/grassyiyi.com.key; # 私钥文件路径
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root /usr/share/nginx/html/ ;
location ~ /.well-known {
allow all;
}
location / {
proxy_pass https://127.0.0.1:11443; # 带SSL端口转发
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;
proxy_set_header X-Forwarded-Port $server_port;
proxy_ssl_verify off;
}
location /for-study-purposes-only {
proxy_pass https://127.0.0.1:11443/for-study-purposes-only;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_ssl_verify off;
}
access_log /var/log/nginx/typecho-access.log main;
error_log /var/log/nginx/typecho-error.log warn;
}
此时docker-compose up -d
, 可以通过curl http://grassyiyi.com/
看到页面内容了.
安装Let's Encrypt
证书
接下来还需要进到容器内部安装合法的SSL
证书.
docker exec -it nginx_main bash
# 容器内部执行
apt-get update
apt-get install -y curl wget cron socat
curl https://get.acme.sh | sh
echo "alias acme.sh='/root/.acme.sh/acme.sh'" >> /root/.bashrc
编辑nginx_main/asset/ssl-grassyiyi.com.sh
#!/bin/bash
mkdir -p /etc/nginx/ssl/grassyiyi.com/
/root/.acme.sh/acme.sh --issue \
-d grassyiyi.com \
-d www.grassyiyi.com \
-d m2e.me \
-d www.m2e.me \
--webroot /usr/share/nginx/html \
--cert-file /etc/nginx/ssl/grassyiyi.com/grassyiyi.com.cer \
--key-file /etc/nginx/ssl/grassyiyi.com/grassyiyi.com.key \
--fullchain-file /etc/nginx/ssl/grassyiyi.com/fullchain.cer \
--reloadcmd "nginx -s reload"
并在容器内部执行该脚本. 若出现success
说明证书配置成功.
此时如果可以通过curl https://grassyiyi.com/
看到页面内容, 说明配置成功.
后记
失败尝试
几天探索式的配置过来, 这里的坑其实挺多的. 就提其中的一点吧.
我最开始打算让typecho
只对外暴露一个1180
端口, 让对外的nginx_main
的80/443
同时指向内部的1180
. 这样配置其实更简单. 但最终发现这样配置不可靠. 个中的原因在于typecho
的php
程序无法准确识别出来自加了SSL
证书的请求, 这会在https
环境下生成了http
的请求链接, 进而引发了mixed-content
的请求错误, 网站功能不正常.
优化空间
nginx
暂时不配置强制的http->https
, 以后看情况;- 把
mysql
换成sqlite
; - 在对外
nginx
的基础上, 配置trojan
.
更改日志
- 2025-08-04: 删去
v2ray
相关内容
评论已关闭