最近通过Halo搭建了个人的博客站,在这里做一下记录。Halo官网其实已经提供了非常全的部署方案,这里我采用的docker 的安装方式(不使用宝塔面板),使用Nginx容器去代理Halo容器,cloudflare去托管解析域名,acme自动更新证书。

为什么要用这些:

  • 不使用宝塔面板,主要是宝塔本身就是一个比较大的软件,在一些小型的服务器上比较吃力。
  • cloudflare可以有效的隐藏我们的真实IP,一定程度防止被攻击
  • acme可以自动更新证书,不用手动更新,这样一次搭建后无需再关注服务器端
  • 使用Nginx容器去代理Halo容器,这里的Nginx容器其实不止可以代理Halo的容器,后续所有容器都可以代理,实现https的访问(需要注意的是,cloudflare免费提供了https证书,但是,这是用户到cloudflare的连接是才用https方式,如果服务端没有使用https,那cloudflare到源服务器就不是https的方式连接了。相当于cloudflare去代理了用户的请求,然后再转发给源服务器的)

服务器的连接与配置

在开始之前,我们需要准备一下环境:

  • 一台初始化的centos7.8系统服务器(避免软件安装冲突)
  • 一个已经备案的域名(国内服务器域名需要备案)

修改默认端口:

连接服务器:

1
ssh root@you ip

修改端口,编辑sshd_config文件:

1
vi /etc/ssh/sshd_config

输入 i 编辑,把#去掉,把22改为想要的端口,最后:wq保存:

1
Port 10024

然后需要执行这一句命令:(端口是刚才修改的端口)

1
semanage port -a -t ssh_port_t -p tcp 10024

可能会得到一个错误,可以执行一下下面这句:

1
yum whatprovides semanage
1
yum install -y  policycoreutils-python

最后再重新执行:

1
semanage port -a -t ssh_port_t -p tcp 10024

查看SSH运行的端口:(不是必须执行)

1
semanage port -l | grep ssh

删除SSH端口:(不是必须执行)

1
semanage port -d -t ssh_port_t -p tcp 22 

重启SSH

1
service sshd restart

重新连接服务器:(端口是刚才的端口)

1
ssh -p 10024 root@your ip

密钥方式连接服务器:

在本地电脑生成SSH Key,一直回车就可以:

1
ssh-keygen

在本地路径C:\Users\你的用户\.ssh下创建config文件:

1
2
3
4
5
6
Host Test
Port #端口号
HostName #IP地址
User root
IdentityFile #本地id_rsa文件
IdentitiesOnly yes

服务器cd ~/.ssh/目录,(一定要CD到这个目录,切勿直接在根目录直接使用下面命令,会导致这个文件不生效)编辑文件,到本地C:\Users\你的用户名\.ssh 找到公钥文件,把本地电脑的公钥文件id_rsa.pub复制进去,:wq保存:

1
vi authorized_keys

如果之前连接过服务器,格式化后再连接可能报错,可以先删除本地电脑这两个文件,这两个文件是历史连接的记录

最后我们就可以使用别名的方式登录服务器了:

1
ssh Test

至此,完成服务器的端口修改和使用密钥登录的操作

Docker和dockercompose的安装

Docker在今年(2024年)6月初的时候,大批量无法拉取镜像,docker的安装也有些无法正常安装,这里提供两种方法:一种是国内服务器安装,另外一种是国外服务器的安装,两者区别在于安装的速度上。

国内服务器安装方式

  1. 运行以下命令,下载docker-ceyum源。

    1
    sudo wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  2. 运行以下命令,安装Docker。

    1
    sudo yum -y install docker-ce
  3. 执行以下命令,检查Docker是否安装成功。

    1
    docker -v
  4. 如下图回显信息所示,表示Docker已安装成功。

  5. 执行以下命令,启动Docker服务,并设置开机自启动。

    1
    2
    sudo systemctl start docker 
    sudo systemctl enable docker
  6. 执行以下命令,查看Docker是否启动。

    1
    sudo systemctl status docker

安装docker-compose:

1
2
3
4
5
6
7
8
9
10
# 下载docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

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

# 测试命令
docker-compose --version
#显示版本号为正常
docker-compose version 1.23.2, build 1110ad01

这一步如果出错,可以把dockercompose文件下载到本地,然后上传到服务器解压,再给予执行权限就可以了。

关于docker在其他系统的安装可以参考阿里云这篇文章:https://help.aliyun.com/zh/ecs/use-cases/install-and-use-docker-on-a-linux-ecs-instance

国外服务器安装方式

  1. 先删除旧的版本(如果没有可以跳过):

    1
    2
    3
    4
    5
    6
    7
    8
    sudo yum remove docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-engine
  2. 安装必须的依赖:

    1
    2
    3
    sudo yum install -y yum-utils \
    device-mapper-persistent-data \
    lvm2

    添加stable的Docker-ce的源:

    1
    2
    3
    sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
  3. 安装docker-ce:

    1
    sudo yum install docker-ce docker-ce-cli containerd.io
  4. 启动服务:

    1
    2
    3
    4
    5
    #启动docker
    sudo systemctl start docker
    # 开机自动启动
    sudo systemctl enable docker

docker-compose安装:

Docker-Compose工具是一个批量工具,用于运行与管理多个docker容器。官方文档:Install Docker Compose

1
2
3
4
5
6
7
8
9
10
# 下载docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

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

# 测试命令
docker-compose --version
#显示版本号为正常
docker-compose version 1.23.2, build 1110ad01

解决docker镜像拉取问题

使用国内服务器拉取镜像会出现失败的情况,在这里提供一种解决的方式:

原理:使用cloudflare代理Docker镜像站点。

cloudflare官网:https://dash.cloudflare.com/

开源仓库:https://github.com/cmliu/CF-Workers-docker.io

步骤:

  1. 注册登录cloudflare,选择Workers 和 Pages选项,点击创建;

  2. Pages方式:连接github仓库,选择github仓库(Fork过来的开源仓库),直接开始默认设置就可以

  3. Workers :复制 _worker.js 代码,保存并部署即可

  4. 修改文件 /etc/docker/daemon.json(如果不存在则创建)

    使用vi /etc/docker/daemon.json

    1
    2
    3
    {
    "registry-mirrors": ["https://docker.fxxk.dedyn.io"] # 请替换为您自己的Worker自定义域名
    }

    重启更新一下docker

    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker

注意点:

  • 部署后会一定时间的延迟,需要等一会才可以进行访问;

  • 部署测试第一点是看域名能不能正常访问,第二是镜像名称前面加域名能不能正常pull,比如格式:

    域名/镜像名

    1
    docker pull cf-workers-docker-io-XXX.XXX.dev/mongo
  • 注意格式引号,一定要英文状态下的引号;否则重启docker 的时候会报错;

  • 最好不要公开自己的域名,因为cloudflare是有免费的请求次数限制;

  • 如果用一段时间后无法访问,这是因为cloudflare容易被墙,最好的办法是自定义域名

Nginx反代和HTTPS证书

上面是服务器连接配置和docker 的安装,接下来是搭建的重要环节,也是这个搭建的灵魂所在。

域名托管到cloudflare

把域名服务商的DNS服务器改为cloudflare就可以被cloudflare托管;

cloudflare有两个配置需要设置一下,否则在部署后会出现问题:

  • SSL连接:选择完全模式,这是要源服务器配置了证书的情况下设置,这是因为cloudflare代理所有用户请求,cloudflare提供了证书,但是这是用户与cloudflare之间的连接,cloudflare与源服务器之间应该也要走https协议,具体看下面这图就可以知道了。

  • 这里的缓存需要设置一下成标准,halo是带有参数的缓存请求,所以这里不能没有或者忽略。

安装acme

acme安装如果是国内服务器可能会出现安装非常缓慢的情况或者失败,这里一样提供两种安装方式:一种是国内服务器安装,另外一种是国外服务器的安装,两者区别在于安装的速度上。

国内服务器:

直接浏览器访问:https://get.acme.sh,会出现这样的字眼:

访问这个链接就可以得到我们想要的安装方式:

1
2
3
git clone https://gitee.com/neilpang/acme.sh.git
cd acme.sh
./acme.sh --install -m [email protected]

这里两个问题:

  • 安装git命令:sudo yum install -y git

  • ./acme.sh不是我们想要的,需要直接使用acme命令就可以使用:

    1
    2
    3
    cd /root/.acme.sh/
    alias acme.sh=~/.acme.sh/acme.sh
    acme.sh -h

国外服务器:

使用一句安装命令:(这里一定要带上自己的邮箱,否则后面会报错)

1
curl https://get.acme.sh | sh -s email="[email protected]"

这个命令安装acme并且创建了一个 cronjob, 每天 0:00 点自动检测所有的证书, 如果快过期了, 需要更新, 则会自动更新证书.

注意:安装好之后建议重新连接服务期,如果这个时候直接使用acme.sh可能有问题

acme生成证书

这里使用的acme的DNSApi的方式生成证书(这里演示的是cloudflare,不同的DNS服务商是不同命令的,具体参考:https://github.com/acmesh-official/acme.sh/wiki/dnsapi)

Cloudflare api:

1
2
3
4
5
6
export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"

export CF_Email="[email protected]"

acme.sh --issue -d example.com --dns dns_cf
acme.sh --issue --dns dns_cf -d example.com -d *.example.com

这样,我们就完成了域名和子域名下的证书签发。

安装Nginx容器

安装Nginx之前,还需要做一下准备工作:

  • 创建文件夹:mkdir /home/keys,存放Https证书文件

  • 创建随机的https证书密钥:

    1
    openssl dhparam -out /home/keys/dhparam.pem 2048
  • 创建容器的局域网段:

1
2
3
4
5
docker network create https
#这是查询network有哪些网段
docker network ls
#这是查询network里面https网段里面有哪些容器加入进来
docker network inspect https
  • 创建文件夹:

    1
    2
    mkdir /home/nginx
    mkdir /home/nginx/conf.d

    home/nginx 是用来存放nginx容器的文件

    home/nginx/conf.d是用来存放带来其他容器的nginx conf.d文件

  • 配置nginx.conf文件,并上传到服务器home/nginx文件夹下,这是一个通用的文件配置,正常无需修改

    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
    user nginx;
    worker_processes auto;
    pid /run/nginx.pid;
    worker_rlimit_nofile 65535;
    events {
    # 设置事件驱动模型,是内核2.6以上支持
    use epoll;
    worker_connections 65535;
    accept_mutex off;
    multi_accept off;
    }

    http {
    # Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    send_timeout 120;
    keepalive_timeout 300;
    client_body_timeout 300;
    client_header_timeout 120;

    proxy_read_timeout 300;
    proxy_send_timeout 300;
    #tcp_nopush on;
    types_hash_max_size 4096;
    client_header_buffer_size 16m;
    client_max_body_size 4096m;

    include /etc/nginx/mime.types;
    include /etc/nginx/conf.d/*.conf;
    # include /usr/share/nginx/modules/*.conf;

    default_type application/octet-stream;
    # Logging Settings
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    # 开启gzip
    gzip on;
    # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    gzip_min_length 1k;
    # gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
    gzip_comp_level 2;
    # 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁用IE 6 gzip
    gzip_disable "MSIE [1-6]\.";
    }
  • 接下来是Nginx容器安装,编写docker-compose文件:

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:
web:
image: nginx:latest
container_name: "some-nginx"
restart: always
volumes:
- /home/nginx/nginx.conf:/etc/nginx/nginx.conf
- /home/nginx/conf.d:/etc/nginx/conf.d
- /home/keys:/home/keys
# blog
- /home/blog:/var/www
ports:
- "80:80"
- "443:443"

# docker network create https
networks:
default:
external:
name: https

解释:创建了一个some-nginx的容器,把容器文件nginx.confconf.d和keys文件夹挂载出来。暴露端口是80和443;把容器加入到https的网段里面,让这个网段里面的容器可以被nginx容器代理到。

编写好后就可以直接使用一下命令安装:

1
docker-compose up -d

最后我们就可以运行之前acem的安装命令:

1
2
3
4
acme.sh --install-cert -d example.top \
--key-file /home/keys/key.pem \
--fullchain-file /home/keys/cert.pem \
--reloadcmd "docker resatrt some-nginx"

正常的话,这里运行着一个nginx容器了,这里的步骤稍微复杂但是细心一点还是没有难度的,特别需要注意的是域名、文件夹和文件名这些内容不要写错。

TIP:这里在使用docker-compos安装前,需要把Nginx的文件夹先创建好,给目录777权限,nginx.conf是文件,conf.d是文件夹

安装Halo

由于小型的服务器可能承受不住Halo和mysql的压力,这里演示的是H2数据库:

这是官方提供的docker-compose文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3"

services:
halo:
image: registry.fit2cloud.com/halo/halo:2.17
restart: on-failure:3
volumes:
- ./halo2:/root/.halo2
ports:
- "8090:8090"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health/readiness"]
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
command:
# 外部访问地址,请根据实际需要修改
- --halo.external-url=http://localhost:8090/

但是我们安装这个文件安装,可能就会报错,这里我们需要优化调整一下:

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
version: "3"

services:
halo:
image: registry.fit2cloud.com/halo/halo:2.17.2
restart: on-failure:3
container_name: test #容器名称
networks:
https:
volumes:
- /home/halo2/halo2:/root/.halo2
ports:
- "8090:8090"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health/readiness"]
interval: 30s
timeout: 5s
retries: 5
command:
# 外部访问地址,请根据实际需要修改
- --halo.external-url=https://test.test.top
networks:
https:
external:
name: https

增加了container_name(自定义域名,方便接下来的Nginx容器访问),剔除了start_period(主要报错原因),增加了networks(加入https网段,和Nginx容器同个网段,可以之间通讯访问)

Nginx代理Halo容器

安装好Halo容器,最后就需要使用Nginx来代理这个容器,我们可以编写一个conf文件,存放到home/nginx/conf.d目录下,这样就可以使Nginx容器代理Halo容器了

Halo.conf

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
upstream halo-server {
server test:8090 fail_timeout=0;
}
# listen on HTTP2/SSL
server {
listen 443 ssl http2;
server_name test.test.top;

# ssl certs from letsencrypt
ssl_certificate /home/keys/cert.pem;
ssl_certificate_key /home/keys/key.pem;
# dhparam.pem
ssl_dhparam /home/keys/dhparam.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4';
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://land-server;
}
}
# redirect HTTP and handle let's encrypt requests
server {
listen 80;
server_name test.test.top;
# send everything else to HTTPS
location / {
return 302 https://test.test.top;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://halo-server;
}
}


需要修改地方:

  • proxy_pass http://halo-server 是 和 halo-server对应的
  • return 302 https://test.test.top 填写自己的域名
  • server_name test.test.top; 填写自己的域名
  • server test:8090 fail_timeout=0; 填写自己容器名称

总结

这样搭建的halo博客,不用关心https证书更新问题,也不用担心暴漏IP风险,少了宝塔,也减轻了服务器的负担,nginx容器可以带来其他容器,为以后其他项目做了一个兼容扩展性。