前言

Typecho 1.3.0 之前跟 outline 一起安装好了,一直没时间去整理,并且安装过程中的问题也不少。最近有时间开始解决问题,也顺便把基本的 docker 容器部署的方案整理一下。

docker 项目配置文件和解决方案主要来自于 deepseek,个人主要调整一些配置,还有调通服务。

PS:不建议普通用户升级,尤其是主题和插件重度使用者。

docker 部署 Typecho 1.3.0

docker compose 配置文件 /root/typecho_project/docker-compose.yml

services:
  typecho:
    image: joyqi/typecho:nightly-php8.2-apache # 使用包含PHP 8.2和Apache的镜像[4](@ref)
    container_name: typecho-app
    restart: always
    depends_on:
      - typecho-db
    environment:
      - TIMEZONE=Asia/Shanghai
      - MAX_POST_BODY=64M
      - TYPECHO_DB_HOST=typecho-db
      - TYPECHO_DB_PORT=3306
      - TYPECHO_DB_NAME=typecho
      - TYPECHO_DB_USER=typecho
      - TYPECHO_DB_PASSWORD=xxxxxxxxx # 请修改为强密码[5](@ref)
      - TYPECHO_DB_DATABASE=typecho
      - TYPECHO_DB_ENGINE=InnoDB # 可选,指定数据库引擎
      - PHP_UPLOAD_MAX_FILESIZE=64M
      - PHP_POST_MAX_SIZE=64M
      - PHP_MEMORY_LIMIT=256M
        #    ports:
        #      - "8080:80" # 将宿主机的8080端口映射到容器的80端口[3,8](@refi)
    expose:
      - "80"
    volumes:
      - ./typecho-uploads:/app/usr/uploads # 挂载上传目录,便于持久化主题、插件和上传的文件[1,4](@ref)
      - ./typecho-themes:/app/usr/themes # 主题
      - ./typecho-plugins:/app/usr/plugins # 插件
      - ./typecho-config.inc.php:/app/config.inc.php # 可选,挂载自定义配置文件[1](@ref)
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - internal
      - shared

  typecho-db:
    image: mysql:8.0 # 使用MySQL 8.0镜像[5](@ref)
    container_name: typecho-mysql
    restart: always
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: xxxxxxxx # 请修改并妥善保管
      MYSQL_DATABASE: typecho
      MYSQL_USER: typecho
      MYSQL_PASSWORD: xxxxxxxx # 需与typecho服务中的配置一致[5](@ref)
    volumes:
      - typecho-mysql-data:/var/lib/mysql # 使用命名卷持久化数据库数据[5,7](@ref)
      - ./mysql/conf.d:/etc/mysql/conf.d:ro
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - internal
      - shared

volumes:
  typecho-mysql-data: # 定义命名卷,用于数据库持久化[7](@ref)

networks:
  internal: # 创建自定义网络,便于容器间通信[3,5](@ref)
    driver: bridge
  shared:
    external: true
    name: shared-services

joyqi/typecho:nightly-php8.2-apache 是 deepseek 搜索到的,官方不知道在什么地方推荐过的 docker 镜像,内部包含了 php8.2apache 服务,并且提供了最新的源码 /app/*。服务启动需要数据库服务,在环境变量中数据库连接信息(账号密码)。这里做了多个需要的 volumes 共享文件夹,其实也可以整个 app 映射出来,方便手动修改源码。

数据库采用了 mysql:8.0,之前是 mysql 5.5.3。可以指定 root 账号密码,还有一个独立的连接用户和密码。给 typecho 和 tyecho-db 都加了 healthcheck,这样在容器启动后会自动检测服务是否可用。command: --default-authentication-plugin=mysql_native_password 是后面另外一个项目 [使用 docker 部署一个 laravel 5.2 的项目
][1],完整导入数据库时做的兼容处理,重新安装不需要修改。

networks 网络部分增加了一个共享网络 shared-services,是为了让 mysql 数据库可以被其他的服务复用。需要提供对外服务的,就在服务的网络部分增加一个共享网络。

配置 https-portal 访问

之前 outline 项目安装过 https-portal 的镜像,该镜像可以自动对配置的域名进行 SSL 证书申请,非常适合当前这种 3 个月就要重新搞的测试证书情况。

  https-portal:
    image: steveltn/https-portal:1
...
    environment:
      DOMAINS: 'xx.seasidecrab.com -> http://outline:3000, blog.seasidecrab.com -> http://typecho:80'

定义之后发现,博客的域名 blog 无法正常访问到。

通过指令确认 blog 域名没有生成相应的配置文件:docker exec outline-https ls -la /etc/nginx/conf.d/,查询环境变量 DOMAINS 也没有生成对应的映射关系:

docker inspect outline-https --format='{{range .Config.Env}}{{println .}}{{end}}' | grep DOMAINS
docker exec outline-https env | grep DOMAINS
DOMAINS=xx.seasidecrab.com -> http://outline:3000

最终发现是域名指向的服务地址有问题,http://typecho:80 中 typecho 是服务名或者容器名,端口使用 expose 端口即可(处于共享网络内)。

把域名指向 typecho 服务,blog.seasidecrab.com -> http://typecho:80,又报错:

nginx: [emerg] host not found in upstream "typecho" in /etc/nginx/conf.d/blog.seasidecrab.com.ssl.conf:36
[cont-init.d] 20-setup: exited 0.
[cont-init.d] 30-set-docker-gen-status: executing... 
[cont-init.d] 30-set-docker-gen-status: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
nginx: [emerg] host not found in upstream "typecho" in /etc/nginx/conf.d/blog.seasidecrab.com.ssl.conf:36
s6-svscanctl: fatal: unable to control /var/run/s6/services: supervisor not listening
[cont-finish.d] executing container finish scripts...
[cont-finish.d] done.
[s6-finish] waiting for services.
[s6-finish] sending all processes the TERM signal.

deepseek 的解析是:

https-portal 容器无法解析 Docker 网络内的主机名!
https-portal 容器没有正确连接到共享网络!
很可能的问题是:typecho 容器根本没有连接到 shared-services 网络!

测试确实不在共享网络里:

docker inspect outline --format='{{.NetworkSettings.Networks.shared-services.IPAddress}}' 2>/dev/null || echo "typecho 不在 shared-servic
es 网络中!"
typecho 不在 shared-services 网络中!
docker inspect typecho --format='{{.NetworkSettings.Networks.shared-services.IPAddress}}' 2>/dev/null || \
echo "typecho 不在 shared-services 网络中!"
typecho 不在 shared-services 网络中!

检查确定 typecho 服务下的 networks 没有添加 shared 共享网络(别称,项目配置 yml 最下面有定义)

    networks:
      - internal
      - shared

添加之后重启更新,OK!

连接数据库

此时 typecho 访问报错:Error establishing a database connection。检查发现是数据库连接配置的问题,因为使用的是镜像自带的最新的源码,所以默认配置需要修改成 typecho-db 定义的账号密码。之后得创建数据库,不然还是会连接报错。

到这里有两种备份方法,一种是原来的数据库全量保存备份到新的库里,另一种是从管理后台备份管理里导出一份。注意:两者是有很大区别的,全量备份可以保留一些系统配置、插件配置等等,而后台导出的备份仅是文章、独立页面、评论等数据。

使用的是 typecho 后台的一个备份功能,从 mysql 5.5 到 mysql 8.0,然后报错了:

恢复过程中遇到如下错误: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'title' at row 1
这是 MySQL 5.5 到 8.0 的兼容性问题!字符集编码差异导致的
根本原因: MySQL 5.5 默认字符集是 utf8(实际上是 utf8mb3,最多3字节),而 MySQL 8.0 的 utf8mb4(最多4字节)对某些特殊字符的处理不同。

使用了方案3:临时禁用严格模式(最快速):

cd /root/typecho_project

# 1. 导入前临时禁用严格模式
source .env

docker compose exec db mysql -u root -p"${MYSQL_ROOT_PASSWORD}" -e "
SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';
"

# 2. 恢复数据(现在应该能成功)
docker compose exec -T db mysql -u root -p"${MYSQL_ROOT_PASSWORD}" < your_backup.sql
# 这里从后台导入数据

# 3. 恢复严格模式(可选)
docker compose exec db mysql -u root -p"${MYSQL_ROOT_PASSWORD}" -e "
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
"
文章目录