用 Docker 搭建 LNMP 大型攻略
前言
痛点
- 宝塔等面板虽然方便,但你永远不知道什么时候曝出来什么后门、漏洞,而且占用高、备份也不太方便
- 直接手撸 LDNMP 看起来很高级,但是耗时耗力,不方便备份
Docker 化就完美解决了这些问题
系统配置
默认现在处于
root
下
更新 packges
1 | apt update -y && apt upgrade -y |
安装一些必须得软件包
1 | apt install wget curl sudo vim git unzip -y |
安装 Docker
1 | # 安装 Dokcer |
安装 Docker Compose
1 | # 自行去 compose 的 GitHub 找最新版本号:https://github.com/docker/compose |
安全配置
新建普通用户
Linux 系统中的 root
至关重要,我们都知道基于 Linux 的安卓系统解锁之后拿到 Root 权限会有一堆软件报安全问题。root
就是整个系统中的神,一旦 root
账户出现安全问题,那整个系统就会成为俎上鱼肉
故首先要新增一个用户并设定登录密码:
1
adduser debian # 名字随意
执行命令后,根据提示操作即可。请务必设置一个用户密码(别忘记设置密码时你时看不到
******
的)。之后系统会询问你一些用户的附加信息,这些就可以无视,一路回车即可。把
debian
拉入sudo
名单里,让它获得暂时拥有root
的权利的能力1
visudo
在
User Privilege Specification
下加入一行debian ALL=(ALL) NOPASSWD: ALL
即可。此处特别说明的是
NOPASSWD
这个设置,它的意思是debian
用户临时使用root
权限时,不用额外输入密码。这与一般的安全建议相反。之所以如此推荐,是因为很多人不顾危险坚持使用root
账号就是因为用root
时不用重复输入密码、觉得轻松。“两害相权取其轻”,我认为【直接用root
用户的风险】大于【使用sudo
时不用输密码的风险】,所以做了以上的建议。如果你希望遵守传统习惯、每次使用
sudo
时需要输入密码,那么这一行改成vpsadmin ALL=(ALL:ALL) ALL
即可。
设置私钥登录并禁止 root 和密码登录
本博客有过介绍,参考这篇
下列操作在本地终端执行
执行
ssh-keygen -o -a 256 -t ed25519
,此处 ed25519 是加密算法的一种,不用理会。提示输入将私钥保存在哪里,默认保存在C:\Users\{your user name}/.ssh/
(mac 是保存在/Users/{your user name}/.ssh
),文件名id_ed25519
1
2Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\Hantong/.ssh/id_ed25519):随后提示输入密钥密码,最好设置一下,输入密码后回车,能保证私钥不泄露也可以直接回车留空。输入密码时,不会有显示:
1
Enter passphrase (empty for no passphrase):
提示再输入一遍,再输入一遍密码就行,前面留空这儿也留空
1
Enter same passphrase again:
生成完毕,提示公钥私钥的保存位置。如下例子,
C:\Users\Hantong/.ssh/id_ed25519.pub
就是接下来将保存到服务器的公钥,C:\Users\Hantong/.ssh/id_ed25519
就是登陆用的私钥1
2
3
4
5
6Your identification has been saved in C:\Users\Hantong/.ssh/id_ed25519.
Your public key has been saved in C:\Users\Hantong/.ssh/id_ed25519.pub.
The key fingerprint is:
***
The key's randomart image is:
***用 Notepad 打开公钥文件
C:\Users\Hantong/.ssh/id_ed25519.pub
并复制内容到剪贴板备用
下列操作在服务器执行
用 debian
登录服务器:
切勿用
root
登录,不然要改权限比较麻烦
1 | cd ~/.ssh # 文件夹不存在就 mkdir ~/.ssh |
尝试用私钥登陆,登陆成功证明前面的操作没问题
终端执行
ssh -i "{/entire/path/to/your/privkey/with/filename}" {user}@{server ip}
, 按提示来就行,提示保存到 known_host 就 yes,前面给私钥设置了密码提示输密码就输密码配置 sshd_config
此处命令配置 sshd_config,新机器无脑照搬就行,有自定义设置的就别直接执行,
cat <<'TEXT' > /etc/ssh/sshd_config
后TEXT
前的内容为 sshd_config 的内容,自行手动编辑就行。编辑前建议备份一下原来的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58sudo -i
mv /etc/ssh/sshd_config /etc/ssh/sshd_config.b
cat <<'TEXT' > /etc/ssh/sshd_config
Include /etc/ssh/sshd_config.d/*.conf
# 端口, 默认22
#Port 22
# 监听地址相关, 不需修改
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
# Ciphers and keying
#RekeyLimit default none
# 日志
# 指定将日志消息通过哪个日志子系统(facility)发送
SyslogFacility AUTH
# 指定日志等级
LogLevel INFO
# 鉴权
# 限制用户必须在指定的时限(单位秒)内认证成功
LoginGraceTime 2m
# 允许root用户登录
PermitRootLogin no
# 指定是否要求sshd(8)在接受连接请求前对用户主目录和相关的配置文件进行宿主和权限检查
StrictModes yes
# 指定每个连接最大允许的认证次数
MaxAuthTries 6
# 最大允许保持多少个连接。默认值是 10
MaxSessions 16
# 是否开启公钥认证, 仅可以用于SSH-2. 默认值为"yes"
PubkeyAuthentication yes
# 是否允许密码验证
PasswordAuthentication no
# 是否允许空密码
PermitEmptyPasswords no
# 是否允许质疑-应答(challenge-response)认证
ChallengeResponseAuthentication no
# 是否通过PAM验证
UsePAM yes
# 是否允许X11转发
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
# 指定sshd是否在每一次交互式登录时打印 /etc/motd 文件的内容
PrintMotd no
# 指定sshd是否在每一次交互式登录时打印最后一位用户的登录时间
PrintLastLog yes
# 配置超时
TCPKeepAlive yes
ClientAliveInterval 120
ClientAliveCountMax 10
# 配置Pid
PidFile /var/run/sshd.pid
# Allow client to pass locale environment variables
AcceptEnv LANG LC_*
# override default of no subsystems
Subsystem sftp /usr/lib/openssh/sftp-server
TEXT
service sshd restart
至此为止配置私钥登录结束,可以参考本博客的另一篇文章,简易化登录操作
防火墙配置
甲骨文等大厂的服务器都带有安全组, 主机就基本不用另外安装防火墙了,其余商家默认认为没有安全组, 需要我们自己在服务器上配置防火墙,Debian 系列使用 ufw
1 | # 安装 ufw |
正式部署 LNMP
默认处于
debian
用户下
所有的服务都交给 Nginx Proxy Manager (以下简称 NPM)反代出去,只开放宿主机的 ssh、http、https 端口
先创建个文件夹来存储 docker 数据
1
mkdir docker
再创建个网桥备用
1
sudo docker network create reverse-network
部署 Nginx Proxy Manager
先去域名商那把域名解析到服务器上
我的域名挂在 Cloudflare 上,假设我的服务器 IP 为
1.1.1.1
,这里添加一个 A 记录,名称填你随意,IPV4 地址填1.1.1.1
,这样就把blog.liqiye.com
解析到1.1.1.1
了创建目录
1
mkdir -p ~/docker/npm && cd ~/docker/npm
创建
docker-compose.yml
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20version: '3'
services:
app:
image: jc21/nginx-proxy-manager:latest
container_name: npm
ports:
- '80:80'
- '81:81'
- '443:443'
networks:
- reverse-network
restart: unless-stopped
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
reverse-network:
external: true执行
sudo docker-compose up -d
访问 NPM
http://ip:81
访问- 账号:admin@example.com
- 密码:changeme
进去先编辑下信息
添加一个反代,把 NPM 的 81 端口反代到 443
PS:这里的图放错了,
Hostname
是npm
,Port
是81
申请 SSL 证书并打开强制 HTTPS 访问
删除 NPM
1
2
3
4
5# 停止容器
sudo docker stop npm
# 删除容器
sudo docker rm npm重新编辑 NPM 的
docker-compose.yml
,删去81
端口映射1
2cd ~/docker/npm
nano docker-compose.yml更改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19version: '3'
services:
app:
image: jc21/nginx-proxy-manager:latest
container_name: npm
ports:
- '80:80'
- '443:443'
networks:
- reverse-network
restart: unless-stopped
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
reverse-network:
external: true执行
sudo docker-compose up -d
,即可通过blog.liqiye.com
访问 NPM
部署 Nginx
创建挂载目录
1
2
3mkdir -p ~/docker/nginx/nginx
mkdir -p ~/docker/nginx/log
mkdir -p ~/docker/nginx/html将容器中的
nginx.conf
文件和conf.d
文件夹复制到宿主机1
2
3
4
5
6
7
8
9
10
11
12
13# 生成容器
sudo docker run --name nginx -d nginx
# 将容器 nginx 文件夹下内容复制到宿主机
sudo docker cp nginx:/etc/nginx ~/docker/nginx/
# 将容器中的 html 文件夹复制到宿主机
sudo docker cp nginx:/usr/share/nginx/html ~/docker/nginx/
# 停止容器
sudo docker stop nginx
# 删除容器
sudo docker rm nginx进入 Docker Nginx 的数据文件夹
1
cd ~/docker/nginx
创建
docker-compose.yml
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
networks:
- reverse-network
restart: unless-stopped
volumes:
- ./nginx:/etc/nginx
- ./html:/usr/share/nginx/html
- ./logs:/var/log/nginx
networks:
reverse-network:
external: true执行
sudo docker-compose up -d
部署 PHP
创建目录
1
mkdir -p ~/docker/php && cd ~/docker/php
创建
docker-compose.yml
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15version: '3'
services:
php:
image: php:7.4-fpm
container_name: php
networks:
- reverse-network
restart: unless-stopped
volumes:
- /home/debian/docker/nginx/html:/usr/share/nginx/html
networks:
reverse-network:
external: true执行
sudo docker-compose up -d
安装 PHP 扩展
1
2
3
4
5
6
7sudo docker exec php apt update && sudo docker exec php apt install -y libmariadb-dev-compat libmariadb-dev libzip-dev libmagickwand-dev imagemagick
sudo docker exec php docker-php-ext-install mysqli pdo_mysql zip exif gd intl bcmath opcache
sudo docker exec php pecl install imagick && sudo docker exec php sh -c 'echo "extension=imagick.so" > /usr/local/etc/php/conf.d/imagick.ini'
sudo docker exec -it php sh -c 'echo "upload_max_filesize=50M \n post_max_size=50M" > /usr/local/etc/php/conf.d/uploads.ini'重启 PHP
1
sudo docker restart php
部署 Mysql
数据库一般在实际部署站点等服务时才需要,在下面举实例时会展示
实例
静态站点
以这篇提到的 Hexo 博客为例
先去域名商那把域名解析到服务器上
我的域名挂在 Cloudflare 上,假设我的服务器 IP 为 1.1.1.1
,这里添加一个 A 记录,名称填你随意,IPV4 地址填 1.1.1.1
,这样就把 blog.liqiye.com
解析到 1.1.1.1
了
小云朵最好还是打开吧,不打开怕被打,打开了又怕访问太慢,哈哈
不过可以找个线路好的机器来反代
创建目录
1 | mkdir ~/docker/nginx/html/blog.liqiye.com |
拉源码
把 Hexo 的 public 文件夹里的内容放到 ~/docker/nginx/html/blog.liqiye.com
目录下
按照上篇博客中的设定,
remote_path
就是/home/debian/docker/nginx/html/blog.liqiye.com
只需要在根目录执行
./upload.sh
即可同步
创建 Nginx 配置
1 | nano ~/docker/nginx/nginx/conf.d/blog.liqiye.com.conf |
Nginx 配置如下:
1 | server { |
利用 NPM 反代并申请 SSL 证书强制 HTTPS 访问
动态网站
以开源版的彩虹易支付为例:https://github.com/Blokura/Epay
先去域名商那把域名解析到服务器上
我的域名挂在 Cloudflare 上,假设我的服务器 IP 为 1.1.1.1
,这里添加一个 A 记录,名称填你随意,IPV4 地址填 1.1.1.1
,这样就把 blog.liqiye.com
解析到 1.1.1.1
了
小云朵最好还是打开吧,不打开怕被打,打开了又怕访问太慢,哈哈
不过可以找个线路好的机器来反代
拉源码
1 | cd ~/docker/nginx/html |
创建 Nginx 配置
1 | sudo nano ~/docker/nginx/nginx/conf.d/blog.liqiye.com.conf |
Nginx 配置如下:
1 | server { |
部署 Mysql
创建目录
1
mkdir -p ~/docker/blog_mysql && cd ~/docker/blog_mysql
创建
docker-compose.yml
文件X86 机器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22version: '3'
services:
blog_mysql:
image: mysql:5.7.30
container_name: blog_mysql
networks:
- reverse-network
restart: unless-stopped
volumes:
- ./conf:/etc/mysql/conf.d
- ./logs:/logs
- ./data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=my-secret-pw # 自行设置密码
- MYSQL_DATABASE=my-database # 自行设置名称
- MYSQL_USER=my-user # 自行设置名称
- MYSQL_PASSWORD=my-password # 自行设置密码
networks:
reverse-network:
external: trueARM 机器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22version: '3'
services:
epay_mysql:
image: biarms/mysql:5.7.30-linux-arm64v8
container_name: epay_mysql
networks:
- reverse-network
restart: unless-stopped
volumes:
- ./conf:/etc/mysql/conf.d
- ./logs:/logs
- ./data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=my-secret-pw # 自行设置密码
- MYSQL_DATABASE=my-database # 自行设置名称
- MYSQL_USER=my-user # 自行设置名称
- MYSQL_PASSWORD=my-password # 自行设置密码
networks:
reverse-network:
external: true
执行
sudo docker-compose up -d
修改
config.php
1
2
3
4
5# 前往网站目标目录
cd ~/docker/nginx/html/blog.liqiye.com/includes
# 编辑 config.php
nano config.php修改如下:
1
2
3
4
5
6
7
8
9
/*数据库配置*/
$dbconfig=array(
'host' => 'blog_mysql', //数据库服务器
'port' => 3306, //数据库端口
'user' => 'my-user', //数据库用户名
'pwd' => 'my-password', //数据库密码
'dbname' => 'my-database' //数据库名
);导入数据库
1
2
3
4
5
6
7
8# 复制数据库
sudo cp ~/docker/nginx/html/blog.liqiye.com/install.sql ~/docker/blog_mysql/data
# 进入容器
sudo docker exec -it blog_mysql bash
## 导入数据库
mysql -u my-user -p my-password my-database < /var/lib/mysql/install.sql重启 Nginx、PHP、Mysql
1
sudo docker restart nginx php blog_mysql
利用 NPM 反代并申请 SSL 证书强制 HTTPS 访问
访问 blog.liqiye.com/admin
登录
备份
思路
- 定时压缩整个
docker
文件夹放在~/docker_backup
目录下 - 通过 alist 挂载
~/docker_backup
目录 - 外部服务器通过 WebDAV 连接 alist 下载 alist 挂载的备份
实现
定时压缩
1 | # 创建文件夹 |
脚本内容如下:
1 |
|
部署 alist
先去域名商那把域名解析到服务器上
我的域名挂在 Cloudflare 上,假设我的服务器 IP 为
1.1.1.1
,这里添加一个 A 记录,名称填你随意,IPV4 地址填1.1.1.1
,这样就把blog.liqiye.com
解析到1.1.1.1
了创建目录
1
mkdir -p ~/docker/alist && cd ~/docker/alist
创建
docker-compose.yml
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20version: '3.3'
services:
alist:
image: xhofe/alist:latest
container_name: alist
networks:
- reverse-network
restart: unless-stopped
volumes:
- ./:/opt/alist/data
- /home/debian/docker_backup:/docker_backup # 映射备份目录
environment:
- PUID=0
- PGID=0
- UMASK=022
networks:
reverse-network:
external: true执行
sudo docker-compose up -d
访问
blog.liqiye.com
,输入账号密码进行登录1
sudo docker logs alist
先进设置页面改一下用户名和密码
挂载备份目录
与第三方同步
以群晖为例:
- 安装 Cloud Sync 套件
- 云供应商选 WebDAV
- 填入服务器信息
- 同步方向选:仅下载远程更改