|
之前 博客 经常因为云厂商服务器优惠到期,不得不更换云服务器,而整个博客系统依赖很多,导致迁移部署的成本很大。

通过容器服务编排技术让整个系统上 Docker 后,再更换云服务器厂商时,就可以做到快速平滑迁移了。
Docker 环境
云服务器
服务器的操作系统是 CentOS,我们选用 Docker Compose 作为容器编排工具。
安装 Docker
# 1.删除旧的Docker版本sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine# 2.添加Docker源sudo yum install -y yum-utilssudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo# 3.安装Dockersudo yum install docker-ce docker-ce-cli containerd.iosudo yum install docker-ce-20.10.1 docker-ce-cli-20.10.1 containerd.io# 4.启动Dockersudo systemctl start docker# 5.查看Docker版本docker -vDocker version 20.10.1, build 831ebea | 安装 Docker Compose
# 1.获取docker-compose脚本sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose# 2.增加可执行权限sudo chmod +x /usr/local/bin/docker-composesudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose# 3.查看版本docker-compose --versiondocker-compose version 1.27.4, build 40524192 | 项目改造
调整目录结构
之前的目录结构较为单一,需调整项目目录结构。调整后的目录结构如下:
├── _config.yml # Hexo配置文件├── disqus # Disqus评论├── themes # Hexo主题│ └── yilia # hexo-theme-yilia主题├── source # 文章.md文件├── public # Hexo发布后的静态资源文件├── dockerfiles # Dockerfile文件│ ├── nginx│ ├── nodejs│ └── php├── docker-compose.yml # 容器编排配置├── docker.env # Docker环境变量文件├── docker.example.env # Docker环境变量示例文件├── network-override.yml # 容器编排配置(特殊网络)└── package.json # Hexo依赖包 | 其中,disqus 和 yilia 目录分别对应 disqus-php-api 和 hexo-theme-yilia 这 2 个子项目,并采用 submodule 模式管理这些源代码。
在 submodule 模式下,clone 和 pull 命令会有一些变化,分别为 git clone --recursive https://github.com/fan-haobai/blog.git
和 git pull && git submodule foreach git pull origin master。 编排容器
本博客系统,主要依赖 NodeJS、PHP、Nginx 环境,因此分别构建 3 个容器。
配置 docker-compose.yml
Docker Compose 会根据 docker-compose.yml 配置文件,来自动编排容器。配置如下:
version: '3'services: nginx: restart: always build: ./dockerfiles/nginx ports: - "80:80" - "443:443" volumes: # 博客源代码 - "/var/www/blog:/var/www/blog" # HTTPS证书 - "/var/www/ssl/certs:/var/www/ssl/certs" # Nginx配置 - "$PWD/dockerfiles/nginx/conf.d:/etc/nginx/conf.d" command: /bin/bash /start.sh env_file: - docker.env extra_hosts: - "raw.githubusercontent.com:199.232.96.133" container_name: "nginx" nodejs: build: ./dockerfiles/nodejs ports: - "4000:4000" volumes: - "/var/www/blog:/var/www/blog" container_name: "nodejs" php: restart: always build: ./dockerfiles/php expose: - "9000" volumes: - "/var/www/blog:/var/www/blog" container_name: "php" | 其中,services 下为需要编排的 nodejs、php、nginx 容器服务。每个容器服务都可以灵活配置,常见的配置参数如下:
- restart:容器退出时,是否重启
- build:构建容器 Dockerfile 文件所在的目录
- ports:映射端口
- volumes:挂载目录
- command:容器启动后执行的命令
- env_file:环境变量文件
- extra_hosts:域名IP映射
- container_name:容器名称
Docker Compose 支持多配置文件,且为覆盖关系。因此将 ssl-override.yml 作为获取 HTTPS 证书时启动容器的配置文件。 配置 docker.env
环境变量统一配置在 docker.env 文件中,并增加示例环境文件 docker.example.env。环境变量目前较少,如下:
# 是否启用HTTPS证书ENABLE_SSL=true# 支持HTTPS协议的域名SSL_DOMAINS=fanhaobai.com,http://www.fanhaobai.com | 构建 Dockerfile
Dockerfile 文件统一放在 dockerfiles 目录下,并分别建立 nodejs、php、nginx 文件夹。
NODEJS
该容器下需要安装 git、npm。Dockerfile 文件如下:
FROM node:12-alpineRUN echo "Asia/Shanghai" > /etc/timezone \ && echo "https://mirrors.ustc.edu.cn/alpine/v3.9/main/" > /etc/apk/repositories \ && npm config set registry https://registry.npm.taobao.org \ && apk add --no-cache git \ && npm install hexo-cli -gADD *.sh /RUN chmod 777 /*.shEXPOSE 4000ENTRYPOINT ["sh", "/start.sh"] | 其中,start.sh 为容器的启动脚本,主要作用为生成静态资源文件。内容如下:
#!/bin/bashcd /var/www/blog# 更新代码git pull && git submodule foreach git pull origin master# 生成静态资源npm install --force# hexo cleanhexo ghexo s | PHP
该容器基于官方的基础镜像,安装一些必要的扩展。Dockerfile 文件如下:
FROM php:7.3.7-fpm-alpine3.9RUN echo 'https://mirrors.aliyun.com/alpine/v3.9/main/' > /etc/apk/repositories && \ echo 'https://mirrors.aliyun.com/alpine/v3.9/community/' >> /etc/apk/repositories# 安装扩展RUN apk add --no-cache $PHPIZE_DEPS \ && apk add --no-cache libstdc++ libzip-dev vim\ && apk update \ && apk del $PHPIZE_DEPSRUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev \ && apk update \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/ \ && docker-php-ext-install -j$(nproc) gd \ && docker-php-ext-install -j$(nproc) opcache \ && docker-php-ext-install -j$(nproc) bcmath# 配置文件RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"ADD conf.d/* $PHP_INI_DIR/conf.d/ | 其中,conf.d 下为 php 的配置文件。
NGINX
该容器基于官方的基础镜像,并安装 cron、wget、python。Dockerfile 文件如下:
FROM nginx:latest# 安装cron等RUN sed -i s@/http://deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list \ && apt-get update && apt-get install -y cron wget python# 启动脚本和配置ADD *.sh /ADD nginx.conf /etc/nginx/nginx.conf# HTTPS证书生成脚本ADD ssl/* /var/www/ssl/RUN chmod +x /var/www/ssl/*.shRUN chmod 777 -R /var/log/nginx | 其中,conf.d 下为 nginx 的配置文件,ssl 下为 HTTPS 证书的生成脚本。
ssl 下的 init_ssl.sh 为首次获取 HTTPS 证书脚本,refresh_cert.sh 为更新 HTTPS 证书脚本。
其中,init_ssl.sh 脚本内容如下:
#!/bin/bashecho "### Stoping nginx ..."docker-compose down# 启动容器echo "### Starting nginx ..."docker-compose -f docker-compose.yml -f ssl-override.yml up --force-recreate --build -d# 是否启动完成until [ "`docker inspect -f {{.State.Running}} nginx`"=="true" ]; do echo "### Wait nginx docker start ..." sleep 0.1;done;# 生成HTTPS证书echo "### Gen nginx ssl ..."docker exec nginx /bin/bash /var/www/ssl/refresh_cert.sh# 重启nginxecho "### Restart nginx ..."docker exec nginx nginx -s reload |
ssl-override.yml 会覆盖 docker-compose.yml 中的环境变量,因此会将环境变量 ENABLE_SSL 设置为 false,并将 php 解析到 127.0.0.1,以确保 nginx 容器在首次能成功启动。 而 refresh_cert.sh 脚本内容如下:
#!/bin/bashdir=&#39;/var/www/ssl&#39;certs_dir=&#34;$dir/certs&#34;mkdir -p $certs_dircd $certs_dirif [ -z &#34;$SSL_DOMAINS&#34; ]; then echo &#34;### Domains is empty&#34; exit 1fiecho &#34;### Starting ssl ...&#34;openssl genrsa 4096 > account.keyopenssl genrsa 4096 > domain.keydomains=`echo &#34;DNS:$SSL_DOMAINS&#34; | sed &#39;s/,/&DNS:/g&#39;`echo &#34;### Gen domain key, domains: $domains ...&#34;openssl req -new -sha256 -key domain.key -subj &#34;/&#34; -reqexts SAN -config \ <(cat /etc/ssl/openssl.cnf <(printf &#34;[SAN]\nsubjectAltName=$domains&#34;)) > domain.csrecho &#34;### Download acme_tiny script ...&#34;wget https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py -O acme_tiny.pyecho &#34;### Gen chained cert ...&#34;python acme_tiny.py --account-key account.key --csr domain.csr --acme-dir $dir/challenges/ > signed.crt || exitopenssl dhparam -out dhparams.pem 2048wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pemcat signed.crt intermediate.pem > chained.pemecho &#34;### End ssl ...&#34; | 其中,SSL_DOMAINS 为环境变量文件 docker.env 中配置需要支持 HTTPS 的域名。
在该容器启动后,会执行 start.sh 脚本。其内容如下:
#!/bin/bashdir=&#34;/var/www/ssl&#34;mkdir -p &#34;$dir/challenges&#34;# 是否启用HTTPSif [ &#34;$ENABLE_SSL&#34; = &#34;false&#34; ]; then # 修改nginx配置, 不启用HTTPS sed -i &#39;/https/d&#39; /etc/nginx/nginx.confelse # 每2个月更新一次, 并重启nginx容器 ssl_cron=&#34;0 0 1 */2 * $dir/refresh_cert.sh && nginx -s reload 2>> /var/log/acme_tiny.log&#34; crontab -l | { cat; echo &#34;$ssl_cron&#34;; } | crontab -fi# 前台启动nginx -g &#34;daemon off;&#34; | 其中需要注意,当不启用 HTTPS 协议时,需要将 Nginx 配置修改为不启用 HTTPS;而启用时,会添加每 2 个月重新生成证书的定时任务。nginx 也需要改为前台启动模式,否则容器会因没有前台程序而自动退出。
部署
前面的一切都准备就绪后,部署就异常简单了,后续再迁移时,也只需要简单做部署这一步就好了。
cp docker.example.env docker.env |
/bin/bash dockerfiles/nginx/ssl/init_ssl.sh |
注意:如果无需支持HTTPS协议,则跳过此步骤,并将环境变量 ENABLE_SSL 修改为 false。
docker-compose up --force-recreate --build -d | 如果一切顺利,那么运行 docker ps -a 命令就能看到已成功启动的容器,如下:
docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESb0307bac08d7 blog_nodejs &#34;sh /start.sh&#34; 2 days ago Up 2 days 0.0.0.0:4000->4000/tcp nodejse8ef7a1e9271 blog_nginx &#34;/docker-entrypoint.…&#34; 2 days ago Up 2 days 0.0.0.0:80->80/tcp nginxaf7baad788c5 blog_php &#34;docker-php-entrypoi…&#34; 2 days ago Up 2 days 9000/tcp php | 通过 http://www.fanhaobai.com 域名也可以直接访问了。 |
|