0

在 32MB Docker 容器中搭建 Typecho 博客

5 Dec 2024

今天收到了Secure Dragon LLC的邮件,明年OVZ服务器全线涨价:

OpenVZ 128MB plans are increasing to $24.99/year and the new pricing will take effect on the next invoice of 2025.

比locvps的特价香港机都贵了,只好搬到自己服务器上用 Docker 搭建了。

制作 Docker 构建配置

系统选型

  1. 肯定是 alpine 了。
  2. 因为 Typecho 1.2.1 对最新的 PHP 8.3 有一点兼容问题,而最新的 apline 3.20 默认安装的是 PHP 8.3,我偷懒直接选择 alpine 3.18 作为基准镜像,这样 apk 命令安装的 PHP 就是 8.1 版了。

环境还原

  1. 原版的 OVZ 主机是安装了 dropbear,容器内也会装上
  2. php-fpm 也是少了的
  3. 安装 typecho 依赖的 php 扩展

AI 生成配置

这年头不会全自己写了,先丢给 GPT,然后自己检查了一般,最终得到下边的Dockerfile

# 使用 Alpine 作为基础镜像
FROM alpine:3.18

# 设置时区为上海
RUN apk add --no-cache tzdata && \
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone && \
    apk del tzdata

# 安装必要的工具和依赖
RUN apk add --no-cache curl unzip dropbear lighttpd php php-ctype php-curl php-dom php-fpm php-iconv php-gd php-json php-openssl php-pdo php-pdo_sqlite php-sqlite3 php-xml php-xmlreader php-phar php-posix php-ftp php-session php-bcmath php-sockets php-mbstring php-tokenizer bc

# 动态获取 php.ini 路径
RUN PHP_INI_PATH=$(ls /etc/php*/php.ini | head -n 1) && \
    sed -i "s|;*cgi.fix_pathinfo=.*|cgi.fix_pathinfo=1|i" $PHP_INI_PATH && \
    sed -i 's@^;date.timezone.*@date.timezone = Asia/Shanghai@' $PHP_INI_PATH && \
    sed -i "s@^memory_limit.*@memory_limit = 12M@" $PHP_INI_PATH && \
    PHPFPM_CONF_PATH=$(ls /etc/php*/php-fpm.d/www.conf | head -n 1) && \
    sed -i "s@^user = nobody@user = lighttpd@" $PHPFPM_CONF_PATH && \
    sed -i "s@^group = nobody@group = lighttpd@" $PHPFPM_CONF_PATH

# 创建 lighttpd 的日志目录
RUN mkdir -p /var/log/lighttpd && \
    chown -R lighttpd:lighttpd /var/log/lighttpd

# 设置工作目录
WORKDIR /srv

# 暴露 lighttpd 端口
EXPOSE 80 443

# 暴露 dropbear 端口(使用参数设定)
ARG DROPBEAR_PORT=22
ENV DROPBEAR_PORT=${DROPBEAR_PORT}
EXPOSE ${DROPBEAR_PORT}

# 启动脚本
COPY start.sh /start.sh
RUN chmod +x /start.sh

# 启动 dropbear 和 lighttpd
CMD ["/start.sh"]

以及start.sh

#!/bin/sh

# 检查 /srv/index.php 是否存在
if [ ! -f /srv/index.php ]; then
    # 下载并解压最新的 Typecho 版本
    curl -L -o typecho.zip $(curl -s https://api.github.com/repos/typecho/typecho/releases/latest | grep 'browser_download_url' | cut -d '"' -f 4)
    unzip typecho.zip -d /srv
    rm typecho.zip
fi

# 设置权限
chown -R lighttpd:lighttpd /srv

# 检查并创建 /config/dropbear 目录
if [ ! -d /config/dropbear ]; then
    mkdir -p /config/dropbear
fi

# 生成 dropbear 主机密钥到 /config/dropbear 目录
if [ ! -f /config/dropbear/dropbear_rsa_host_key ]; then
    dropbearkey -t rsa -f /config/dropbear/dropbear_rsa_host_key
fi
if [ ! -f /config/dropbear/dropbear_ecdsa_host_key ]; then
    dropbearkey -t ecdsa -f /config/dropbear/dropbear_ecdsa_host_key
fi
if [ ! -f /config/dropbear/dropbear_ed25519_host_key ]; then
    dropbearkey -t ed25519 -f /config/dropbear/dropbear_ed25519_host_key
fi

# 检查并创建 /config/lighttpd 目录
if [ ! -d /config/lighttpd ]; then
    mkdir -p /config/lighttpd
fi

# 检查 /config/lighttpd/lighttpd.conf 是否存在,如果不存在则复制 /etc/lighttpd 目录下的所有文件到 /config/lighttpd 目录下
if [ ! -f /config/lighttpd/lighttpd.conf ]; then
    cp -r /etc/lighttpd/* /config/lighttpd/
fi

# 运行 PHP
/usr/sbin/php-fpm81 -c /etc/php81/php-fpm.conf

# 检查并运行 /srv/boot.sh 如果存在
if [ -f /srv/boot.sh ]; then
    chmod +x /srv/boot.sh
    /srv/boot.sh &  # 在后台运行
fi

# 启动 dropbear,指定密钥文件路径
/usr/sbin/dropbear -E -p ${DROPBEAR_PORT} -r /config/dropbear/dropbear_rsa_host_key -r /config/dropbear/dropbear_ecdsa_host_key -r /config/dropbear/dropbear_ed25519_host_key &

# 启动 lighttpd,指定配置文件路径
/usr/sbin/lighttpd -D -f /config/lighttpd/lighttpd.conf

构建并运行容器

把上边两个文件放在/data/32mb下,然后进入这个目录执行命令

docker build -t alpine-typecho .

接着是运行容器,这个容器必须限制到32MB内存以及32MB交换分区(SWAP)

docker run -d --memory 32m --memory-swap 64m -p 80:80 -p 443:443 -p 22:22 -v ~/.ssh:/root/.ssh -v /data/32mb/config:/config -v /data/32mb/www:/srv --name typecho-container --restart always alpine-typecho
# -v /data/32mb/config:/config 是映射配置目录
# -v /data/32mb/www:/srv 是映射 typecho 目录

启动容器后会自动安装Typecho到/srv目录,也就是VPS上的/data/32mb/www目录,lighttpd的配置文件也会生成到/data/32mb/config/lighttpd/lighttpd.conf

修改配置

  1. 直接执行下边的命令启动相关模块

    sed -i 's/#   include "mod_fastcgi_fpm.conf"/   include "mod_fastcgi_fpm.conf\ninclude "mod_ssl.conf""/g' /data/32mb/config/lighttpd/lighttpd.conf
    sed -i 's/#    "mod_rewrite","/    "mod_rewrite","/g' /data/32mb/config/lighttpd/lighttpd.conf
    sed -i 's/#    "mod_redirect","/    "mod_redirect","/g' /data/32mb/config/lighttpd/lighttpd.conf
    cat > /data/32mb/config/lighttpd/mod_ssl.conf << EOF
    server.modules += ("mod_openssl")
    server.modules += ("mod_setenv")
    $HTTP["scheme"] == "https" {
     setenv.add-response-header  = ( "Strict-Transport-Security" => "max-age=63072000; includeSubdomains; ")
    }
    EOF
  2. 这里不单独说明 SSL 证书申请了网上都有
  3. 新增虚拟主机,修改/data/32mb/config/lighttpd/lighttpd.conf,在最后一行之前加入

    # virtual host
    $HTTP["host"] =~ "^(www.)?32mb.cc" {
     server.document-root = "/srv"
     accesslog.filename = "/config/logs/32mb.cc.log"
     $HTTP["scheme"] == "http" {
         # redirect to https, port 443:
         url.redirect = (".*" => "https://%0$0")
     }
    }
    $SERVER["socket"] == ":443" {
     ssl.engine = "enable"
     ssl.pemfile = "/config/32mb.cc.pem"
     $HTTP["host"] =~ "(^|www\.)32mb.cc" {
         ssl.pemfile = "/config/32mb.cc.pem"
         # 这是 typecho 的伪静态规则,其他程序自行修改
         url.rewrite-if-not-file = (
             "^/(admin|usr)/(.*)" => "/$1/$2",
             "^/(.*)$" => "/index.php/$1"
         )
     }
    }

    然后重启容器访问绑定好的域名就行了

    docker restart alpine-typecho

    题外话

    因为使用Docker跑web服务,无需SSH就能执行命令了,甚至可以关闭dropbear把内存压缩到16M,12M的样子,不过没测过。

    分类:默认分类 ; 标签:docker , alpine , lighttpd