本站使用的博客程序是WordPress, 目前采取的搭建方式是, 通过脚本LAMP部署环境, 再手动安装WordPress到Apache的/var/www/html
目录, 然后配置SSL. 说实话, 搭建的时间精力成本都十分地高, 光是LAMP脚本就要跑一小时左右. 如果哪天换了VPS服务商, 要再重新来一遍, 想想就头疼...
最近学习了一些docker的知识, 十分轻易地在本地跑起来一个WordPress镜像. 在不考虑https的情况下, 部署流程可以简化为:
- 安装
MySQL
/MariaDB
, 创建用户和数据库; - 启动WordPress镜像.
大体上两步就完事了... 耗时5分钟, 高下立判!
之前尝试过将docker版的WordPress用NGINX配置SSL证书, 但是失败了. 这两天有一些空闲时间, 终于搞定了这个问题. 所以编辑此文记录一下完整踩坑流程~
环境信息
-
Ubuntu 16.04
-
MySQL 5.7
-
Docker version 18.09.7
-
docker-compose version 1.11.1
-
Docker images
- wordpress:latest
- nginx:latest
部署过程
本文的部署流程参照了这篇文章, 并根据实际情况作了一些调整.
安装数据库
由于我使用的是Ubuntu, 所以直接用apt安装mysql-server-5.7
, 其他发行版可以用相应的包管理工具自行安装.
sudo apt-get install -y mysql-server-5.7
安装完成后, 使用root用户登录
sudo mysql -uroot -p
然后新建用户和数据库, 此处YOURPASSWORD
替换为你自己的密码.
CREATE USER 'wordpress'@'%' IDENTIFIED BY 'YOURPASSWORD';
GRANT ALL PRIVILEGES ON *.* TO 'wordpress'@'%' IDENTIFIED BY 'YOURPASSWORD';
FLUSH PRIVILEGES;
CREATE DATABASE wordpress;
这里我偷懒了, 直接创建了任何host都能登录的用户, 而且无脑给了all privileges... 如果不嫌麻烦, 可以自己优化🤣
经过上面的步骤, 我们创建好了名为wordpress
的用户, 名为wordpress
的数据库.
MySQL服务默认监听的是127.0.0.1:3306
, 而我们的wordpress之后会运行在docker内, 一般来说, ip将会是172.17.0.1
, 这样是访问不到宿主机的MySQL服务的. 这里解决办法有几个:
- 使用docker版本的
MySQL
/MariaDB
, 通过docker内部ip地址进行通信; - 在
docker run
wordpress镜像的时候, 通过--network=host
指定网络模式; [参考] - 将宿主机的MySQL配置调整为, 监听
0.0.0.0:3306
Emm, 我又选择了偷懒的第三种方式. 直接开放端口是不安全的! 但是配合云服务商的安全组, 或者将3306端口修改为其他非常用端口, 又或是用iptables
作限制, 还是可以保证安全性的. 至少对于我的弱鸡VPS加上无人访问的博客, 能挡住端口扫描暴力破解就够了...
开始部署之前, 先准备一个workspace, 假设它的目录是WORKSPACE
. 在该目录下新建两个目录wp
和nginx
, 用来存放相关的配置文件.
cd WORKSPACE; mkdir wp; mkdir nginx;
配置WordPress
首先在wp
目录下新建一个wp-app
文件夹用来存放映射的WordPress程序文件:
cd WORKSPACE/wp; mkdir wp-app;
然后在wp
目录新建一个docker-compose.yml
配置文件:
vim WORKSPACE/wp/docker-compose.yml
内容如下:
version: '3'
services:
wp:
image: wordpress:latest
ports:
- 9009:80
volumes:
- ./config/php.conf.uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
- ./wp-app:/var/www/html # Full wordpress project
#- ./plugin-name/trunk/:/var/www/html/wp-content/plugins/plugin-name # Plugin development
#- ./theme-name/trunk/:/var/www/html/wp-content/themes/theme-name # Theme development
environment:
WORDPRESS_DB_HOST: 172.17.0.1:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: YOURPASSWORD
配置中开放端口9009
映射容器的80
端口, 然后映射WordPress的程序文件到WORKSPACE
的wp-app
目录下.
接下来在以下路径创建一个php.conf.uploads.ini
文件, 配置php相关的一些参数.
vim WORKSPACE/wp/config/php.conf.uploads.ini
内容如下:
file_uploads = On
max_execution_time = 600
memory_limit = 512M
post_max_size = 32M
upload_max_filesize = 32M
最后用docker-compose
启动镜像:
cd WORKSPACE/wp; docker-compose up -d
这一部分就完成了!
可以使用docker ps -a
命令查看容器是否处在up状态, 如果是的话, 可以访问you_ip_address:9009
看看能否进入WordPress的安装界面. 如果失败, 可以尝试往安全组, 防火墙方向排查.
SSL 证书申请
关于SSL, 我使用了acme.sh
来申请Let's Encrypt的SSL证书.
首先确保你有个域名, 且已经添加了A记录指向你VPS的公网IP地址.
安装acme.sh
curl https://get.acme.sh | sh
我比较偏向于使用standalone模式签发证书, 使用之前需要先安装socat
:
sudo apt-get install -y socat
安装完成后, 使用这条命令签发证书. your_domain.com
替换为你的A记录域名地址.
alias acme.sh=~/.acme.sh/acme.sh
acme.sh --issue -d your_domain.com --standalone
不出意外的话, 申请成功的证书会放在~/.acme.sh/your_domain.com/
目录下.
配置NGINX
首先在nginx
目录下创建一个certs
文件夹用来存放证书文件. acme.sh
官方推荐使用--installcert
命令来copy/安装证书, 我为了(再次)偷懒, 直接把~/.acme.sh/your_domain.com/
这个文件夹cp
到了certs
文件夹内, 目前并无大碍, 但是并不推荐这么做.😂
复制完证书后, 开始准备NGINX的docker-compose
配置文件
vim WORKSPACE/nginx/docker-compose.yml
内容如下:
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
#- ./.htpasswd:/etc/nginx/.htpasswd
- ./certs:/etc/nginx/certs
ports:
- 80:80
- 443:443
配置里映射了nginx.conf
配置文件和certs
证书文件夹, 开放了80和443端口, 这些可以根据实际情况进行修改.
接下来准备NGINX本身的配置文件nginx.conf
:
worker_processes 2;
pid /var/run/nginx.pid;
worker_rlimit_nofile 65535;
# [ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx.error_log info;
events {
worker_connections 2000;
# use [ kqueue | epoll | /dev/poll | select | poll ];
# use kqueue;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$gzip_ratio"';
log_format download '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$http_range" "$sent_http_content_range"';
client_header_timeout 3m;
client_body_timeout 3m;
send_timeout 3m;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
gzip on;
gzip_min_length 1100;
gzip_buffers 4 8k;
gzip_types text/plain;
output_buffers 1 32k;
postpone_output 1460;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
send_lowat 12000;
keepalive_timeout 75 20;
#lingering_time 30;
#lingering_timeout 10;
#reset_timedout_connection on;
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/your_domain.com/your_domain.com.cer;
ssl_certificate_key /etc/nginx/certs/your_domain.com/your_domain.com.key;
server_name your_domain.com;
client_max_body_size 500M ;
location / {
proxy_pass http://172.17.0.1:9009;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
#proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection "upgrade";
#proxy_read_timeout 86400;
}
}
server {
listen 80;
server_name your_domain.com;
location / {
return 301 https://$host$request_uri;
}
}
}
和参考的那篇原文不同的是, 为了防止NGINX的循环重定向, 我没有选择修改wp-config.php
文件, 而是取消注释了下面这一行. 测试证明是有效的.
proxy_set_header X-Forwarded-Proto https;
另外这里还加入了强制https访问的配置, 80端口收到的请求统统重定向到https.
这样, NGINX这边的配置也都完成了.
在启动nginx
镜像前, 请先确保80和443端口没有被占用, 再确保WordPress在http协议下的ip:port的访问是否正常, 再再确保你的WordPress是全新的或做好了资料备份的. 一切OK的话, 前往WordPress的设置->常规中把站点地址和WordPress地址都改成https://your_domain.com
. 这一步有风险, 请不要在WordPress有资料且未备份的情况下执行操作!
然后启动nginx
镜像:
cd WORKSPACE/nginx; docker-compose up -d
就大功告成了!
正常情况下, 一切顺利的话, 到这一步就可以通过https://your_domain.com
来访问你的站点了.
但是如果, 我是说如果... 按照我的叙述进行操作, 很不幸地... 没有成功, 上述修改WordPress站点地址的操作应当会导致整个WordPress崩掉, 且无法登陆进去再把url修改回来🤪(危险操作+1)
当然假设这里部署的是一个船新的WordPress镜像, 里面没有任何资料, 那直接删掉镜像再重来一遍就好. 不过我这里还是给一个官方补救措施供参考.
已知的问题
众所周知, Let's Encrypt的证书签发方便快捷, 但是有效期很短, 需要定期去更新. acme.sh
很贴心地帮我们考虑到了这一点, 签发证书成功后, 会自动添加一条crontab, 每天检查证书是否需要更新, 如果有需要更新的证书还会自动帮你renew, 棒呆!
但是, 我上面是通过standalone模式申请的SSL证书, 它要求更新证书操作时, 主机的80端口不能被占用, 因为它的工作原理就是利用socat监听80端口来验证域名的, 而Let's Encrypt并不信任除80和443之外的端口, 我们也就无法指定其他端口来完成验证(ref).
也就是说, 如果网站一直跑着, 监听着80和443端口, 就无法使用acme.sh的自动证书更新功能. 针对这个问题我也想了几个办法:
- 弃用证书自动更新, 等发现网站提示证书失效时手动更新(误);
- 不使用standalone模式申请SSL证书, 考虑其他高级模式来申请;
- 写一个强制更新脚本, 每月执行一次强制更新, 更新前stop掉nginx容器, 更新完成后再start.
希望日后能看到这篇文章的朋友也帮忙想想办法~
时隔大半年诈尸更新了一篇, 往后要加油哇!