typecho / Dockerfile

Docker Image packaging for Typecho
https://hub.docker.com/r/joyqi/typecho
GNU General Public License v2.0
86 stars 9 forks source link

关于使用 docker-compose 启动容器无法实现自动安装数据库的问题 #24

Closed zurica2013 closed 2 years ago

zurica2013 commented 2 years ago

最近在研究容器化使用 typecho,经常反复删除和重建容器,于是想到开启环境变量里自动安装的功能,但是尝试多次发现都没有效果。看到有朋友也遇到了一样的问题 #21。一番摸索后找到了原因,分享给大家供参考。

遇到问题: 开启自动安装需要在 docker-compose.yml 中设置环境变量 TYPECHO_INSTALL: 1,但我发现即便是启用了,首次登录时还是会出现安装数据库的初始化页面,之后一切使用如常。初始配置文件 config.inc.php 也没有自动生成,但手动配置后会生成(如果你的问题是根本连不上数据库或者打不开页面什么的,应该是其他原因)。所以我考虑不是程序出错,而是配置不到位。

原 docker-compose 配置文件:

version: "3.9"

services:
  nginx:
    container_name: nginx
    image: cym1102/nginxwebui
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - $PWD/typecho:/var/lib/nginx/html
      - nginx:/home/nginxWebUI
    depends_on:
      - typecho

  typecho:
    container_name: typecho
    image: joyqi/typecho:nightly-php8.0-fpm-alpine
    restart: unless-stopped
    environment:
      TIMEZONE: Asia/Shanghai
      TYPECHO_INSTALL: 1
      TYPECHO_DB_HOST: mysql
      TYPECHO_DB_USER: zurica
      TYPECHO_DB_PASSWORD: 123456
      TYPECHO_DB_DATABASE: typecho
      TYPECHO_SITE_URL: http://my.web.site
      TYPECHO_USER_NAME: zurica
      TYPECHO_USER_PASSWORD: password
      TYPECHO_USER_MAIL: my@email.com
    volumes:
      - $PWD/typecho:/app
    depends_on:
      - mysql

  mysql:
    container_name: mysql
    image: mysql
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: typecho
      MYSQL_USER: zurica
      MYSQL_PASSWORD: 123456

volumes:
  nginx:
    name: nginx

networks:
  default:
    name: blog

探究原因: 查看 docker-compose 的日志会发现以下内容:

typecho  | Typecho 1.2.0
typecho  | PHP 8.0.22
typecho  | 对不起, 无法连接数据库, 请先检查数据库配置再继续进行安装
typecho  | [23-Aug-2022 06:04:36] NOTICE: fpm is running, pid 33
typecho  | [23-Aug-2022 06:04:36] NOTICE: ready to handle connections
......
mysql    | 2022-08-23 14:04:38+08:00 [Note] [Entrypoint]: Creating database typecho
mysql    | 2022-08-23 14:04:38+08:00 [Note] [Entrypoint]: Creating user zurica
mysql    | 2022-08-23 14:04:38+08:00 [Note] [Entrypoint]: Giving user zurica access to schema typecho
......

已知 typecho 在访问 Mysql 时如果不存在相应数据库,会显示无法连接;而这里 Mysql 明明已经按照预设环境变量成功创建了 typecho 数据库。其实秘密在时间戳上:Mysql 创建 typecho 数据库的时间,比 typecho 尝试访问它的时间晚了2秒,typecho 自然访问了个寂寞 :sweat:

解决方法: 那么问题又来了,我在 docker-compose 的 typecho 段落里已经写了

depends_on:
      - mysql

不起作用吗? 答案是确实不起作用(果然不能乱抄配置),因为按照官方文档的解释,depends_on 只是让被配置的容器比被依赖的容器后启动,并不是等被依赖的容器完全准备好了才启动。但是这个参数也是有作用的,就是通过长语法再补充一些细节:

service_healthy
service_completed_successfully

你可能以为要选第二个?错了 :sweat_smile:,因为名称已经写的很清楚了,这个服务不但要成功,还要完成(exits with 0 code)。如果只是毫无差错地处于待命状态,依赖它的容器就只能一直等下去也不能启动~~ 使用 service_healthy 的话,就到要对容器进行健康检查,关于如何写判断条件我也查了很多:

  1. 检查数据库的目录是否已经生成 test: ["CMD", "test -f var/lib/mysql/typecho"]
  2. 检查运行的 mysql 版本 test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. 检查容器是否能连接 test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

实践证明以上三种都不管用,具体原因我也不清楚(尤其是第一种看似最可行的条件)

最后找到一个亲测可用的配置: Docker-compose with MySQL/MariaDB and healthcheck 这个命令在 Mysql 容器里直接运行的结果:

bash-4.4# mysql --user=zurica --password=123456 --execute "SHOW DATABASES;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| typecho            |
+--------------------+

其实也就是去判断目标数据库有没有建好

修改后的 docker-compose.yml

version: "3.9"

services:
  nginx:
    container_name: nginx
    image: cym1102/nginxwebui
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - $PWD/typecho:/var/lib/nginx/html
      - nginx:/home/nginxWebUI
    depends_on:
      - typecho

  typecho:
    container_name: typecho
    image: joyqi/typecho:nightly-php8.0-fpm-alpine
    restart: unless-stopped
    environment:
      TIMEZONE: Asia/Shanghai
      TYPECHO_INSTALL: 1
      TYPECHO_DB_HOST: mysql
      TYPECHO_DB_USER: zurica
      TYPECHO_DB_PASSWORD: 123456
      TYPECHO_DB_DATABASE: typecho
      TYPECHO_SITE_URL: http://my.web.site
      TYPECHO_USER_NAME: zurica
      TYPECHO_USER_PASSWORD: password
      TYPECHO_USER_MAIL: my@email.com
    volumes:
      - $PWD/typecho:/app
    depends_on:
      mysql:
        condition: service_healthy

  mysql:
    container_name: mysql
    image: mysql
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: typecho
      MYSQL_USER: zurica
      MYSQL_PASSWORD: 123456
    healthcheck:
      test: "mysql --user=zurica --password=123456 --execute \"SHOW DATABASES;\""
      interval: 2s
      timeout: 1s
      retries: 10

volumes:
  nginx:
    name: nginx

networks:
  default:
    name: blog

有几点要注意:

  1. depends_on 的长语法只对 2.x 和 3.9 的 docker-compose 配置文件有效,常规短语法没有限制;
  2. 关于健康检查的间隔和频次,可以酌情设定。我观察到从 Mysql 容器启动到创建 typecho 数据库需要15秒时间,那么设定的 Interval*retries 至少要能完全覆盖这段时间,不然健康检查会失败;
  3. 健康检查的条件不能写成 test: ["CMD“, ”XX", "XX"] 这种格式,也不能用 "-u" "-p" 等短参数。倒是没有报错,但也不会生效。

容器成功启动后的日志:

typecho  | Typecho 1.2.0
typecho  | PHP 8.0.22
typecho  | 安装成功
typecho  | 您的用户名是 zurica
typecho  | 您的密码是 password
typecho  | [23-Aug-2022 07:23:55] NOTICE: fpm is running, pid 32
typecho  | [23-Aug-2022 07:23:55] NOTICE: ready to handle connections
......
mysql    | 2022-08-23 15:23:49+08:00 [Note] [Entrypoint]: Creating database typecho
mysql    | 2022-08-23 15:23:49+08:00 [Note] [Entrypoint]: Creating user zurica
mysql    | 2022-08-23 15:23:49+08:00 [Note] [Entrypoint]: Giving user zurica access to schema typecho
......

数据库比访问请求早了6秒创建,自然就没有问题了

最后说一句,手动初始配置比这个省事多了,自动安装反而折腾 :rofl: