最近把收藏多年的域名又续费了10年,如果不用的话又显得有些浪费,于是就打算在搞一个小网站打发打发时间,首先想起了马上要过期的SugarHosts,可惜发生了很多不愉快的事,导致我再也不会选择这个主机上。想起我还有一个闲置的RackNerd的VPS,所以就搜了下LNMP一键安装包,发现比较热门的都停止提供服务,只能自己安装,比起繁琐的安装步骤,我打算使用Docker来部署LNMP这一套服务,所有记此一文。
1.环境要求
- 操作系统:我用的是debian11,或者Ubuntu 18.04以上即可
- 安装好Docker、Docker Compose
- 做好域名解析,确认@的A记录和WWW的A记录指向宿主机IP,并且DNS都已经更新(会出现你本地生效,但服务器没生效的情况,所以改完大约1-2小时应该就全部同步了)。
- 本篇域名假设为 ABC.COM(顶级域名)
2.使用Docker Compose安装WordPress
2.1 先规整下目录
个人多年运维习惯,统一规整,比较直观的能找到你需要的路径。 目前我是将所有的配置文件、docker-compose.yml文件等统一放在/data/wordpress中。所以先创建/data/wordpress
mkdir -p /data/wordpress
在/data/wordpress中创建nginx配置文件
mkdir /data/wordpress/nginx-conf
2.2 创建nginx.conf
vim /data/wordpress/nginx-conf/nginx.conf
输入如下内容:
server {
listen 80;
listen [::]:80;
server_name abc.com www.abc.com;
index index.php index.html index.htm;
root /var/www/html;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
2.3 定义环境变量
虽然是个个人网站,但也最好将一些敏感信息稍微的处理下。这里主要指的是数据库的认证信息。
在/data/wordpress目录中创建一个.env的文件
vim /data/wordpress/.env
定义如下三个变量
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password
MYSQL_ROOT_PASSWORD指的是MySQL的root用户的密码。
MYSQL_USER 指的是MySQL的某个用户名,这个名字随意,例如wp_user
MYSQL_PASSWORD 指的是MYSQL_USER用户对应的密码
2.4 编写docker-compose文件
在这个文件中需要定义四个服务,分别是wordpress、mysql、nginx、cerbot(ssl证书签发),在/data/wordpress中创建docker-compose.yml文件,其完整内容如下:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:6.6.1-php8.3-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.27.1-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email your@email.com --agree-tos --staging -d abc.com -d www.abc.com
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
有对docker-compose不熟悉的可以去学习一下,上手还是非常快的。
2.5 获取SSL证书
在/data/wordpress中确认有docker-compose.yml文件,然后进入该目录,执行如下命令即可启动容器实例。
cd /data/wordpress
docker-compose up -d
此时,控制台会打印如下内容:
WARN[0000] /data/wordpress/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 6/6
✔ Network wordpress_default Created 0.1s
✔ Network wordpress_app-network Created 0.1s
✔ Container db Started 0.7s
✔ Container wordpress Started 1.4s
✔ Container webserver Started 2.1s
✔ Container certbot Started
如此即表示容器实例都启动成功,这里面只有certbot这个实例会出现问题,主要就是DNS解析的问题,所以在第一章就要求了提前确认DNS的解析是正确的。由于certbot执行完就会退出,所以我们可以通过查看日志来确认该实例执行成功
docker-compose logs certbot
如果执行成功的话会打印如下内容:
WARN[0000] /data/wordpress/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot | Requesting a certificate for abc.com www.abc.com
certbot |
certbot | Successfully received certificate.
certbot | Certificate is saved at: /etc/letsencrypt/live/abc.com/fullchain.pem
certbot | Key is saved at: /etc/letsencrypt/live/abc.com/privkey.pem
certbot | This certificate expires on 2024-11-20.
certbot | These files will be updated when the certificate renews.
certbot | NEXT STEPS:
certbot | - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
举一反三,如果要查看 mysql、nginx等服务器,只需要将cerbot修改为对应的容器名称
docker-compose logs service_name
虽然日志提示执行成功,但还需要再次验证是否挂载到webserver容器中了
docker-compose exec webserver ls -la /etc/letsencrypt/live
如果可以看到证书文件就说明执行成功了。此时,将docker-compose.yml中的certbot服务的command修改一下,打开docker-compose.yml
vim /data/wordpress/docker-compose.yml
找到certbot,修改为如下内容(只需要修改command的值即可):
...
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email your@email.com --agree-tos --no-eff-email --force-renewal -d abc.com -d www.abc.com
...
修改完成后,重新创建certbot
docker-compose up --force-recreate --no-deps certbot
这个命令执行成功会打印如下内容:
WARN[0000] /data/wordpress/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 1/1
✔ Container certbot Recreated 0.1s
Attaching to certbot
certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot | Account registered.
certbot | Renewing an existing certificate for abc.com
certbot |
certbot | Successfully received certificate.
certbot | Certificate is saved at: /etc/letsencrypt/live/abc.com/fullchain.pem
certbot | Key is saved at: /etc/letsencrypt/live/abc.com/privkey.pem
certbot | This certificate expires on 2024-11-20.
certbot | These files will be updated when the certificate renews.
certbot | NEXT STEPS:
certbot | - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in the background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal-setup for instructions.
certbot |
certbot | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
certbot | If you like Certbot, please consider supporting our work by:
certbot | * Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
certbot | * Donating to EFF: https://eff.org/donate-le
certbot | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
certbot exited with code 0
2.6 修改nginx的配置文件,使其支持ssl
在 Nginx 配置中启用 SSL 将涉及添加到 HTTPS 的 HTTP 重定向、指定 SSL 证书和密钥位置,以及添加安全参数和标头。因为要修改配置文件,所以先停止webserver
docker-compose stop webserver
获取一份ssl-nginx.conf的模板,可以在/data/wordpress目录中执行如下命令获取:
curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
打开/data/wordpress/nginx-conf/nginx.conf配置文件,删除所有内容。
vim /data/wordpress/nginx-conf/nginx.conf
删除所有内容后,将如下内容拷贝进去
server {
listen 80;
listen [::]:80;
server_name abc.com www.abc.com;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name abc.com www.abc.com;
index index.php index.html index.htm;
root /var/www/html;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/abc.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/abc.com/privkey.pem;
include /etc/nginx/conf.d/options-ssl-nginx.conf;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off; access_log off;
}
location = /robots.txt {
log_not_found off; access_log off; allow all;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
注意,将abc.com替换为你自己的域名。修改完配置文件后,再打开docker-compose.yml文件,添加443端口映射
vim /data/wordpress/docker-compose.yml
找到webserver,将posts修改为如下:
...
webserver:
depends_on:
- wordpress
image: nginx:1.27.1-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
保存即可,下面贴一份最终完全版的docker-compose.yml内容
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:6.6.1-php8.3-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.27.1-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email iat@outlook.com --agree-tos --no-eff-email --force-renewal -d abc.com -d www.abc.com
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
然后重新创建webserver
docker-compose up -d --force-recreate --no-deps webserver
查看容器实例进程
docker-compose ps
WARN[0000] /data/wordpress/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
db mysql:8.0 "docker-entrypoint.s…" db 5 hours ago Up 5 hours 3306/tcp, 33060/tcp
webserver nginx:1.27.1-alpine "/docker-entrypoint.…" webserver 5 hours ago Up 5 hours 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp
wordpress wordpress:6.6.1-php8.3-fpm-alpine "docker-entrypoint.s…" wordpress 5 hours ago Up 5 hours 9000/tcp
确认都运行正常,打开https://abc.com,根据提示安装WordPress即可。
当然,可能会有人有疑问,如果重启会不会数据就没了,其实数据都映射到宿主机了,在docker-compose.yml中,有个volumes定义了三个卷,分别是certbot-etc、wordpress、dbdata,可执行如下命令查看
docker volume ls |grep wordpress
local wordpress_certbot-etc
local wordpress_dbdata
local wordpress_wordpress
查看wordpress的实际路径
docker volume inspect wordpress_wordpress
[
{
"CreatedAt": "2024-08-21T22:10:09-04:00",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "wordpress",
"com.docker.compose.version": "2.29.2",
"com.docker.compose.volume": "wordpress"
},
"Mountpoint": "/var/lib/docker/volumes/wordpress_wordpress/_data",
"Name": "wordpress_wordpress",
"Options": null,
"Scope": "local"
}
]
在宿主机的/var/lib/docker/volumes/wordpress_wordpress/_data目录就是容器中wordpress的路径
ls /var/lib/docker/volumes/wordpress_wordpress/_data
index.php readme.html wp-admin wp-comments-post.php wp-config.php wp-content wp-includes wp-load.php wp-mail.php wp-signup.php xmlrpc.php
license.txt wp-activate.php wp-blog-header.php wp-config-docker.php wp-config-sample.php wp-cron.php wp-links-opml.php wp-login.php wp-settings.php wp-trackback.php
不过,为了避免不必要的损失,建议还是在wordpress中安装备份插件,定期备份数据。
2.7 续签证书
Let’s Encrypt 证书的有效期为 90 天,所以需要每三个月重新续签一份证书,方法如下:
创建一个续签的脚本 ssl_renew.sh
vim ~/ssl_renew.sh
将如下代码添加到该文件中
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /data/wordpress/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
注意检查路径,然后添加到corntab中
0 1 1 * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
每月1日的凌晨1点执行。
3. 注意事项
- 注意将所有的abc.com替换为你自己的域名
- 严格按照顺序执行
- 对nginx配置文件、docker-compose文件有不懂的,建议简单学习一下。