前言

痛点

  • 宝塔等面板虽然方便,但你永远不知道什么时候曝出来什么后门、漏洞,而且占用高、备份也不太方便
  • 直接手撸 LDNMP 看起来很高级,但是耗时耗力,不方便备份

Docker 化就完美解决了这些问题

系统配置

默认现在处于 root

更新 packges

1
apt update -y  && apt upgrade -y

安装一些必须得软件包

1
apt install wget curl sudo vim git unzip -y

安装 Docker

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
# 安装 Dokcer
wget -qO- get.docker.com | bash

# 查看 Dokcer 版本
docker -v

# 设置开机自动启动
systemctl enable docker

# 开启容器的 IPv6 功能,以及限制日志文件大小,防止 Docker 日志塞满硬盘
cat > /etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "3"
},
"ipv6": true,
"fixed-cidr-v6": "fd00:dead:beef:c0::/80",
"experimental":true,
"ip6tables":true
}
EOF

# 重启 Docker 服务
systemctl restart docker

安装 Docker Compose

1
2
3
4
5
6
7
8
# 自行去 compose 的 GitHub 找最新版本号:https://github.com/docker/compose
curl -L "https://github.com/docker/compose/releases/download/v2.24.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 给执行权限
chmod +x /usr/local/bin/docker-compose

# 查看 docker-compose 版本
docker-compose -v

安全配置

新建普通用户

Linux 系统中的 root 至关重要,我们都知道基于 Linux 的安卓系统解锁之后拿到 Root 权限会有一堆软件报安全问题。root 就是整个系统中的神,一旦 root 账户出现安全问题,那整个系统就会成为俎上鱼肉

  1. 故首先要新增一个用户并设定登录密码:

    1
    adduser debian # 名字随意

    执行命令后,根据提示操作即可。请务必设置一个用户密码(别忘记设置密码时你时看不到 ****** 的)。之后系统会询问你一些用户的附加信息,这些就可以无视,一路回车即可。image-20240123045611791

  2. debian 拉入 sudo 名单里,让它获得暂时拥有 root 的权利的能力

    1
    visudo

    User Privilege Specification 下加入一行 debian ALL=(ALL) NOPASSWD: ALL 即可。image-20240123050155525

    此处特别说明的是 NOPASSWD 这个设置,它的意思是 debian 用户临时使用 root 权限时,不用额外输入密码。这与一般的安全建议相反。之所以如此推荐,是因为很多人不顾危险坚持使用 root 账号就是因为用 root 时不用重复输入密码、觉得轻松。“两害相权取其轻”,我认为【直接用 root 用户的风险】大于【使用 sudo 时不用输密码的风险】,所以做了以上的建议。

    如果你希望遵守传统习惯、每次使用 sudo 时需要输入密码,那么这一行改成 vpsadmin ALL=(ALL:ALL) ALL 即可。

设置私钥登录并禁止 root 和密码登录

本博客有过介绍,参考这篇

下列操作在本地终端执行

  1. 执行 ssh-keygen -o -a 256 -t ed25519,此处 ed25519 是加密算法的一种,不用理会。提示输入将私钥保存在哪里,默认保存在 C:\Users\{your user name}/.ssh/(mac 是保存在 /Users/{your user name}/.ssh),文件名 id_ed25519

    1
    2
    Generating public/private ed25519 key pair.
    Enter file in which to save the key (C:\Users\Hantong/.ssh/id_ed25519):
  2. 随后提示输入密钥密码,最好设置一下,输入密码后回车,能保证私钥不泄露也可以直接回车留空。输入密码时,不会有显示:

    1
    Enter passphrase (empty for no passphrase):
  3. 提示再输入一遍,再输入一遍密码就行,前面留空这儿也留空

    1
    Enter same passphrase again:
  4. 生成完毕,提示公钥私钥的保存位置。如下例子, C:\Users\Hantong/.ssh/id_ed25519.pub 就是接下来将保存到服务器的公钥, C:\Users\Hantong/.ssh/id_ed25519 就是登陆用的私钥

    1
    2
    3
    4
    5
    6
    Your 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:
    ***
  5. 用 Notepad 打开公钥文件 C:\Users\Hantong/.ssh/id_ed25519.pub 并复制内容到剪贴板备用

下列操作在服务器执行

debian 登录服务器:

切勿用 root 登录,不然要改权限比较麻烦

1
2
3
4
cd ~/.ssh # 文件夹不存在就 mkdir ~/.ssh
nano authorized_keys # 编辑 authorized_keys, 将刚刚复制的公钥文件内容粘贴进去, 保存即可
chmod 600 authorized_keys # 配置文件权限
sudo systemctl restart sshd # 重启 SSH 服务
  1. 尝试用私钥登陆,登陆成功证明前面的操作没问题

    终端执行 ssh -i "{/entire/path/to/your/privkey/with/filename}" {user}@{server ip}, 按提示来就行,提示保存到 known_host 就 yes,前面给私钥设置了密码提示输密码就输密码

  2. 配置 sshd_config

    此处命令配置 sshd_config,新机器无脑照搬就行,有自定义设置的就别直接执行,cat <<'TEXT' > /etc/ssh/sshd_configTEXT 前的内容为 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
    58
    sudo -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
2
3
4
5
6
7
8
9
10
11
12
# 安装 ufw
sudo apt install ufw -y

# 配置 ufw
sudo ufw default deny incoming # 默认阻止入站
sudo ufw default allow outgoing # 默认允许出站
sudo ufw allow 22 # SSH 端口, 根据实际情况填写端口
sudo ufw allow 80 # HTTP
sudo ufw allow 443 # HTTPS

# 立即启用 ufw, 提示可能中断当前 SSH 连接, 按 y 继续即可
sudo ufw enable

正式部署 LNMP

默认处于 debian 用户下

所有的服务都交给 Nginx Proxy Manager (以下简称 NPM)反代出去,只开放宿主机的 ssh、http、https 端口

  1. 先创建个文件夹来存储 docker 数据

    1
    mkdir docker
  2. 再创建个网桥备用

    1
    sudo docker network create reverse-network

部署 Nginx Proxy Manager

  1. 先去域名商那把域名解析到服务器上

    我的域名挂在 Cloudflare 上,假设我的服务器 IP 为 1.1.1.1,这里添加一个 A 记录,名称填你随意,IPV4 地址填 1.1.1.1,这样就把 blog.liqiye.com 解析到 1.1.1.1image-20240123075314633

  2. 创建目录

    1
    mkdir -p ~/docker/npm && cd ~/docker/npm
  3. 创建 docker-compose.yml 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    version: '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
  4. 执行 sudo docker-compose up -d

  5. 访问 NPM

  6. 进去先编辑下信息image-20240123081253115image-20240123081342705

  7. 添加一个反代,把 NPM 的 81 端口反代到 443

    PS:这里的图放错了,HostnamenpmPort81image-20240123081458423image-20240123082623743

  8. 申请 SSL 证书并打开强制 HTTPS 访问image-20240123082111592

  9. 删除 NPM

    1
    2
    3
    4
    5
    # 停止容器
    sudo docker stop npm

    # 删除容器
    sudo docker rm npm
  10. 重新编辑 NPM 的 docker-compose.yml,删去 81 端口映射

    1
    2
    cd ~/docker/npm
    nano docker-compose.yml

    更改为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    version: '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
  11. 执行 sudo docker-compose up -d,即可通过 blog.liqiye.com 访问 NPM

部署 Nginx

  1. 创建挂载目录

    1
    2
    3
    mkdir -p ~/docker/nginx/nginx
    mkdir -p ~/docker/nginx/log
    mkdir -p ~/docker/nginx/html
  2. 将容器中的 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
  3. 进入 Docker Nginx 的数据文件夹

    1
    cd ~/docker/nginx
  4. 创建 docker-compose.yml 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    version: '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
  5. 执行 sudo docker-compose up -d

部署 PHP

  1. 创建目录

    1
    mkdir -p ~/docker/php && cd ~/docker/php
  2. 创建 docker-compose.yml 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    version: '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
  3. 执行 sudo docker-compose up -d

  4. 安装 PHP 扩展

    1
    2
    3
    4
    5
    6
    7
    sudo 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'
  5. 重启 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.1image-20240123075314633

小云朵最好还是打开吧,不打开怕被打,打开了又怕访问太慢,哈哈

不过可以找个线路好的机器来反代

创建目录

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
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name blog.liqiye.com;

root /usr/share/nginx/html/blog.liqiye.com; # 指定网站根目录

index index.html;

location / {
try_files $uri $uri/ =404;
}
}

利用 NPM 反代并申请 SSL 证书强制 HTTPS 访问image-20240123082623743image-20240123082111592

动态网站

以开源版的彩虹易支付为例:https://github.com/Blokura/Epay

先去域名商那把域名解析到服务器上

我的域名挂在 Cloudflare 上,假设我的服务器 IP 为 1.1.1.1,这里添加一个 A 记录,名称填你随意,IPV4 地址填 1.1.1.1,这样就把 blog.liqiye.com 解析到 1.1.1.1image-20240123075314633

小云朵最好还是打开吧,不打开怕被打,打开了又怕访问太慢,哈哈

不过可以找个线路好的机器来反代

拉源码

1
2
cd ~/docker/nginx/html
git clone https://github.com/Blokura/Easy_pay && mv Easy_pay blog.liqiye.com

创建 Nginx 配置

1
sudo nano ~/docker/nginx/nginx/conf.d/blog.liqiye.com.conf

Nginx 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 80;
server_name blog.liqiye.com;
root /usr/share/nginx/html/blog.liqiye.com;
index index.php index.html index.htm;

location / {
try_files $uri $uri/ /index.php?$args;
}

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

部署 Mysql

  1. 创建目录

    1
    mkdir -p ~/docker/blog_mysql && cd ~/docker/blog_mysql
  2. 创建 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
      22
      version: '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: true
    • ARM 机器:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      version: '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
    1. 执行 sudo docker-compose up -d

    2. 修改 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
      <?php
      /*数据库配置*/
      $dbconfig=array(
      'host' => 'blog_mysql', //数据库服务器
      'port' => 3306, //数据库端口
      'user' => 'my-user', //数据库用户名
      'pwd' => 'my-password', //数据库密码
      'dbname' => 'my-database' //数据库名
      );
    3. 导入数据库

      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
    4. 重启 Nginx、PHP、Mysql

      1
      sudo docker restart nginx php blog_mysql

利用 NPM 反代并申请 SSL 证书强制 HTTPS 访问

访问 blog.liqiye.com/admin 登录image-20240123082623743image-20240123082111592

备份

思路

  1. 定时压缩整个 docker 文件夹放在~/docker_backup 目录下
  2. 通过 alist 挂载 ~/docker_backup 目录
  3. 外部服务器通过 WebDAV 连接 alist 下载 alist 挂载的备份

实现

定时压缩

1
2
3
4
5
# 创建文件夹
mkdir ~/docker_backup && cd ~/docker_backup

# 创建定制备份脚本
nano backup_script.sh

脚本内容如下:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

# 设置备份路径和文件名
backup_path="/home/debian/docker_backup"
backup_file="docker_$(date +%Y%m%d).tar.gz"

# 执行备份
tar -czvf "$backup_path/$backup_file" /home/debian/docker

# 删除前两周的备份文件
find "$backup_path" -name 'docker_*' -type f -mtime +14 -exec rm {} \;

部署 alist

  1. 先去域名商那把域名解析到服务器上

    我的域名挂在 Cloudflare 上,假设我的服务器 IP 为 1.1.1.1,这里添加一个 A 记录,名称填你随意,IPV4 地址填 1.1.1.1,这样就把 blog.liqiye.com 解析到 1.1.1.1image-20240123075314633

  2. 创建目录

    1
    mkdir -p ~/docker/alist && cd ~/docker/alist
  3. 创建 docker-compose.yml 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    version: '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
  4. 执行 sudo docker-compose up -d

  5. 访问 blog.liqiye.com ,输入账号密码进行登录image-20240123113418933

    1
    sudo docker logs alist
  6. 先进设置页面改一下用户名和密码image-20240123113525419image-20240123113545049

  7. 挂载备份目录image-20240123113844986

与第三方同步

以群晖为例:

  1. 安装 Cloud Sync 套件image-20240123114107672
  2. 云供应商选 WebDAVimage-20240123114207856
  3. 填入服务器信息image-20240123114348563
  4. 同步方向选:仅下载远程更改image-20240123114715351