Anguiao

使用 Docker 搭建 Typecho 个人博客
前几篇文章写的都是一些 Docker 相关的内容,这一篇也不例外。其实在第一篇有关博客搭建的博文写完之后,就看了一...
扫描右侧二维码阅读全文
11
2019/02

使用 Docker 搭建 Typecho 个人博客

前几篇文章写的都是一些 Docker 相关的内容,这一篇也不例外。其实在第一篇有关博客搭建的博文写完之后,就看了一些 Docker 相关的内容,体会到了 Docker 的强大和易用,因此萌生了将博客搭建在 Docker 上的想法。折腾完之后又模拟了更换服务器的过程,博客在 Docker 化之后,迁移起来确实非常方便。

从搭建完成到现在已经一个多月了,运行得还算稳定(虽然并没有更新几篇博客,233)。整理一下搭建的全部过程,希望能帮助到有同样需求的人。

本人目前使用的 Linux 发行版是 Debian 9,其他发行版可能需要对相关命令进行调整。

Docker 相关知识的入门,可以参考 yeasy 大大的 Docker — 从入门到实践

预备工作

本文让 Nginx、PHP、MySQL 各自运行在不同的容器中,并使用 Docker Compose 对各个容器进行管理,因此需要先安装 Docker 和 Docker Compose。

安装 Docker

推荐使用 Docker 官方的脚本进行安装,一条命令即可完成安装。如需了解官方一键安装脚本对发行版的支持情况,可以在脚本中的 SUPPORT_MAP 变量中找到脚本目前支持的发行版。

可通过如下命令安装 Docker:

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

如果是国内的服务器,可以添加 --mirror Aliyun 加速下载。

等待安装完成后,运行如下命令,验证安装是否成功。

sudo docker version

可将当前用户加入 docker 组,这样以后执行 Docker 相关命令时就不需要加 sudo 了。

sudo usermod -aG docker anguiao

安装 Docker Compose

参照官方文档进行安装:

sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

发文时的稳定版本为 1.23.2, 请使用当前的最新稳定版本。

验证是否安装成功:

docker-compose version

构建镜像

官方的 Docker Hub 中已经提供了大量现成的镜像,可以直接使用。但是现成的镜像有时候并不能满足我们的需求,所以需要自行构建镜像。为了缩减镜像体积,这里都会使用 Alpine Linux 作为基础镜像。

下文中的 Dockerfile 可能不能及时更新,最新的更新会放在 GitHub 上。

构建 Nginx 镜像

发文时 Alpine Linux 已升级至 3.9,可以直接安装 OpenSSL 1.1.1 以上版本,所以除非需要启用 3DES 等不够安全的加密算法(为了兼容 IE8),已经不需要自行编译 OpenSSL,即可启用 TLS 1.3。目前 Nginx 的官方 Docker 镜像已经基于 Alpine Linux 3.9,所以如果只需要启用 TLS 1.3,可以直接使用官方镜像,不需要自行构建镜像了。

由于需要启用 Brotli,这里还是选择自行构建镜像。Nginx 官方提供了 Dockerfile,在其之上稍作修改,即可为 Nginx 启用 Brotli 压缩算法。

FROM alpine:3.9

ENV TZ=Asia/Shanghai

ENV NGINX_VERSION 1.15.8

RUN NGINX_GPG_KEYS=B0F4253373F8F6F510D42178520A9993A1C052F8 \
    && CONFIG="\
        --prefix=/etc/nginx \
        --sbin-path=/usr/sbin/nginx \
        --modules-path=/usr/lib/nginx/modules \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --http-log-path=/var/log/nginx/access.log \
        --pid-path=/var/run/nginx.pid \
        --lock-path=/var/run/nginx.lock \
        --http-client-body-temp-path=/var/cache/nginx/client_temp \
        --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
        --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
        --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
        --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
        --user=www-data \
        --group=www-data \
        --with-http_ssl_module \
        --with-http_realip_module \
        --with-http_addition_module \
        --with-http_sub_module \
        --with-http_dav_module \
        --with-http_flv_module \
        --with-http_mp4_module \
        --with-http_gunzip_module \
        --with-http_gzip_static_module \
        --with-http_random_index_module \
        --with-http_secure_link_module \
        --with-http_stub_status_module \
        --with-http_auth_request_module \
        --with-http_xslt_module=dynamic \
        --with-http_image_filter_module=dynamic \
        --with-http_geoip_module=dynamic \
        --with-threads \
        --with-stream \
        --with-stream_ssl_module \
        --with-stream_ssl_preread_module \
        --with-stream_realip_module \
        --with-stream_geoip_module=dynamic \
        --with-http_slice_module \
        --with-mail \
        --with-mail_ssl_module \
        --with-compat \
        --with-file-aio \
        --with-http_v2_module \
        --add-module=/usr/src/ngx_brotli \
    " \
    && addgroup -g 82 -S www-data \
    && adduser -u 82 -D -S -h /var/cache/nginx -s /sbin/nologin -G www-data www-data \
    \
    && apk update \
    \
    && apk add --virtual .build-deps \
        gcc \
        libc-dev \
        make \
        openssl-dev \
        pcre-dev \
        zlib-dev \
        linux-headers \
        curl \
        gnupg1 \
        libxslt-dev \
        gd-dev \
        geoip-dev \
        git \
    && curl -fSL https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz -o nginx.tar.gz \
    && curl -fSL https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz.asc  -o nginx.tar.gz.asc \
    && export GNUPGHOME="$(mktemp -d)" \
    && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$NGINX_GPG_KEYS" \
    && gpg --batch --verify nginx.tar.gz.asc nginx.tar.gz \
    && rm -rf "$GNUPGHOME" nginx.tar.gz.asc \
    && mkdir -p /usr/src \
    && tar -zxC /usr/src -f nginx.tar.gz \
    && rm nginx.tar.gz \
    && git clone https://github.com/google/ngx_brotli.git /usr/src/ngx_brotli \
    && cd /usr/src/ngx_brotli \
    && git submodule update --init \
    && cd /usr/src/nginx-$NGINX_VERSION \
    && ./configure $CONFIG --with-debug \
    && make -j$(getconf _NPROCESSORS_ONLN) \
    && mv objs/nginx objs/nginx-debug \
    && mv objs/ngx_http_xslt_filter_module.so objs/ngx_http_xslt_filter_module-debug.so \
    && mv objs/ngx_http_image_filter_module.so objs/ngx_http_image_filter_module-debug.so \
    && mv objs/ngx_http_geoip_module.so objs/ngx_http_geoip_module-debug.so \
    && mv objs/ngx_stream_geoip_module.so objs/ngx_stream_geoip_module-debug.so \
    && ./configure $CONFIG \
    && make -j$(getconf _NPROCESSORS_ONLN) \
    && make install \
    && rm -rf /etc/nginx/html/ \
    && mkdir /etc/nginx/conf.d/ \
    && mkdir -p /usr/share/nginx/html/ \
    && install -m644 html/index.html /usr/share/nginx/html/ \
    && install -m644 html/50x.html /usr/share/nginx/html/ \
    && install -m755 objs/nginx-debug /usr/sbin/nginx-debug \
    && install -m755 objs/ngx_http_xslt_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_xslt_filter_module-debug.so \
    && install -m755 objs/ngx_http_image_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_image_filter_module-debug.so \
    && install -m755 objs/ngx_http_geoip_module-debug.so /usr/lib/nginx/modules/ngx_http_geoip_module-debug.so \
    && install -m755 objs/ngx_stream_geoip_module-debug.so /usr/lib/nginx/modules/ngx_stream_geoip_module-debug.so \
    && ln -s ../../usr/lib/nginx/modules /etc/nginx/modules \
    && strip /usr/sbin/nginx* \
    && strip /usr/lib/nginx/modules/*.so \
    && rm -rf /usr/src/nginx-$NGINX_VERSION /usr/src/ngx_brotli \
    \
    # Bring in gettext so we can get `envsubst`, then throw
    # the rest away. To do this, we need to install `gettext`
    # then move `envsubst` out of the way so `gettext` can
    # be deleted completely, then move `envsubst` back.
    && apk add --virtual .gettext gettext \
    && mv /usr/bin/envsubst /tmp/ \
    \
    && runDeps="$( \
        scanelf --needed --nobanner --format '%n#p' /usr/sbin/nginx /usr/lib/nginx/modules/*.so /tmp/envsubst \
            | tr ',' '\n' \
            | sort -u \
            | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
    )" \
    && apk add --virtual .nginx-rundeps $runDeps \
    && apk del .build-deps \
    && apk del .gettext \
    && mv /tmp/envsubst /usr/local/bin/ \
    \
    # Bring in tzdata so users could set the timezones through the environment
    # variables
    && apk add tzdata \
    && echo "${TZ}" > /etc/timezone \
    && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
    \
    # forward request and error logs to docker log collector
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
    \
    && rm /var/cache/apk/*

COPY nginx.conf /etc/nginx/nginx.conf
COPY nginx.vh.default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80 443

STOPSIGNAL SIGTERM

CMD ["nginx", "-g", "daemon off;"]

Dockerfile 末尾复制进镜像的配置文件,也都来自 Nginx 的官方镜像

准备好 Dockerfile 和配置文件之后,就可以进行构建了:

docker build -t anguiao/nginx-brotli .

如果不想自行构建,也可以使用我已经构建好的镜像 anguiao/nginx-brotli

构建 PHP 镜像

由于官方提供的 PHP 镜像中缺少一些扩展,因此同样需要自行构建镜像。与 Nginx 不同的是,这里只需将官方镜像作为基础镜像,然后安装所需扩展即可,不需要从头构建镜像。

这里添加了 PDO_MYSQL(如果使用 MySQL 作为 Typecho 的数据库,则需安装此扩展)和 GD(部分主题或插件需要,比如我使用的 handsome 主题)两个扩展:

FROM php:7.3.2-fpm-alpine

ENV TZ=Asia/Shanghai

RUN apk update \
    && apk add tzdata \
    && echo "${TZ}" > /etc/timezone \
    && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
    \
    && docker-php-ext-install -j$(nproc) pdo_mysql \
    \
    && apk add \
        freetype \
        freetype-dev \
        libpng \
        libpng-dev \
        libjpeg-turbo \
        libjpeg-turbo-dev \
    && docker-php-ext-configure gd \
        --with-freetype-dir=/usr/include/ \
        --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
    && apk del \
        freetype-dev \
        libpng-dev \
        libjpeg-turbo-dev \
    \
    && rm /var/cache/apk/*

COPY ./php.ini /usr/local/etc/php/
COPY ./www.conf /usr/local/etc/php-fpm.d/

同样也可从官方镜像获取配置文件,然后进行构建:

docker build -t anguiao/php-fpm .

和 Nginx 一样,我也将自己构建的镜像发布到了 Docker Hub,可以直接使用我构建的镜像 anguiao/php-fpm

配置与安装

目录结构

大致的目录结构如下:

.
├── conf                    配置文件
├── db                      数据库持久化目录
├── docker-compose.yml      Docker Compose 配置
├── log                     日志
├── ssl                     SSL 证书
└── www                     站点根目录

Nginx 相关配置

Nginx 全局配置

这里根据我自己的需要,对 Nginx 的全局配置文件进行了修改,主要是为了启用 Brotli 压缩算法。


user  www-data www-data;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include            mime.types;
    default_type       application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    charset            UTF-8;

    sendfile           on;
    tcp_nopush         on;
    tcp_nodelay        on;

    keepalive_timeout  60;

    gzip               on;
    gzip_vary          on;

    gzip_comp_level    6;
    gzip_buffers       16 8k;

    gzip_min_length    1000;
    gzip_proxied       any;
    gzip_disable       "msie6";

    gzip_http_version  1.0;

    gzip_types         text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

    brotli             on;
    brotli_comp_level  6;
    brotli_types       text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

    include            /etc/nginx/conf.d/*.conf;
}

站点配置

在 Nginx 全局配置文件的最后,包含了 conf.d 目录下的配置文件,这使得我们可以将站点的相关配置写在一个独立的配置文件当中。

server {
    listen 80;
    listen [::]:80;
    server_name test.anguiao.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2 fastopen=3 reuseport;
    listen [::]:443 ssl http2 fastopen=3 reuseport;
    server_name test.anguiao.com;
    index index.php index.html index.htm index.nginx-debian.html;
    root /var/www/html;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    ssl_certificate /var/www/ssl/fullchain.cer;
    ssl_certificate_key /var/www/ssl/anguiao.com.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    ssl_dhparam /var/www/ssl/dhparam.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security max-age=15768000;

    ssl_stapling on;

    ssl_early_data on;

    resolver 114.114.114.114 valid=300s;
    resolver_timeout 10s;

    if (!-e $request_filename) {
            rewrite ^(.*)$ /index.php$1 last;
    }

    location ~ .*\.php(\/.*)*$ {
            fastcgi_pass fpm:9000;
            fastcgi_index index.php;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    }
}

这里主要做了 HTTPS 和 Typecho 伪静态化相关的配置。

编写 docker-compose.yml

Docker Compose 默认使用当前目录下的 docker-compose.yml 来编排容器。

version: '3'

services:
  nginx:
    image: anguiao/nginx-brotli
    container_name: blog_nginx
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./www/:/var/www/html/:rw
      - ./ssl/:/var/www/ssl/:ro
      - ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./conf/nginx/test.anguiao.com.conf:/etc/nginx/conf.d/test.anguiao.com.conf:ro
    restart: always
    depends_on:
      - fpm
      - mysql

  fpm:
    image: anguiao/php-fpm
    container_name: blog_fpm
    expose:
      - 9000
    volumes:
      - ./www/:/var/www/html/:rw
      - ./log/php-fpm/:/var/log/php-fpm/:rw
    restart: always

  mysql:
    image: mysql:5.7
    container_name: blog_db
    expose:
      - 3306
    volumes:
      - ./db:/var/lib/mysql/:rw
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=typecho
      - MYSQL_USER=typecho
      - MYSQL_PASSWORD=123456

MySQL 相关的环境变量可以自行设置。

安装 Typecho

下载 Typecho 最新版本,并将文件移至站点根目录:

wget http://typecho.org/downloads/1.1-17.10.30-release.tar.gz
tar xvf 1.1-17.10.30-release.tar.gz

mv build www

rm 1.1-17.10.30-release.tar.gz

为了让容器获得站点相关文件的写入权限,需要更改相关文件的所有者。通过查看 PHP 镜像的 Dockerfile,得知其 www-data 用户的 UID 和 GID 均为 82,因此可以这样设置:

sudo chown -R 82:82 www

至此配置完成,可以启动了:

docker-compose up -d

在浏览器中输入 URL,完成最后的安装步骤即可。

需要注意的是,数据库配置中的“数据库地址”应填写“mysql”(与 docker-compose.yml 相对应),其他配置与环境变量保持一致即可。

其他配置可照常设置,整个安装过程就完成了。

博客迁移

在将博客 Docker 化之后,博客的迁移就变得十分方便了,只需将整个目录打包传输至新服务器,然后运行 docker-compose up -d 即可。Docker 会自动拉取镜像并启动容器,博客的迁移就完成了。

最后修改:2019 年 02 月 28 日 12 : 39 PM
如果觉得我的文章对你有用,请随意赞赏

4 条评论

  1. 牛牛

    欸 好欸 這評論自帶表情!! |´・ω・)ノ

  2. Azure

    CQHTTP观光团~

    写的还行OωO

    1. 牛牛
      @Azure

      CQHTTP观光团+1 ∠( ᐛ 」∠)_

      1. 子不语
        @牛牛

        CQHTTP观光团+2 ( ๑´•ω•) "(ㆆᴗㆆ)

发表评论