Docker 部署 Hexo

起因:由于云服务器经常改变,导致云服务器上需要不断部署,为了不重复工作,这里使用 Docker 部署 Hexo 以后可以不用重复部署,只需要将数据转移即可

技术栈&准备工作

所用到的技术:
  • Docker
    • docker compose
  • git
  • nginx
  • twikoo
准备工作:

这里的工作主要是需要购买一台云服务器,域名,服务器上需要有DockervimDocker Compose

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── data # 存储数据,注意备份和转移
│ ├── blog_files
│ │ └── hexo
│ ├── conf.d
│ ├── ssl
│ │ └── certs
│ └── twikoo_data
├── docker-compose.yml # 组合配置文件
├── nginx_dockerfile
│ ├── Dockerfile
│ ├── hexo.conf
│ ├── init_https.sh
│ ├── init_pwd.sh
│ ├── start.sh
│ └── twikoo.conf
└── test.sh # 启动之后的命令,手动配置 https

docker-compose.yml

容器编排配置

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
version: '3'
services:
nginx:
build: ./nginx_dockerfile
ports:
- "80:80"
- "443:443"
- "2333:22"
volumes:
- "./data/blog_files/hexo:/var/www/hexo"
- "./data/ssl/certs:/etc/letsencrypt/"
- "./data/conf.d:/etc/nginx/conf.d"
environment:
- PASSWD={your_password}
- EMAIL={your_email}
- DOMAIN={your_domain}
container_name: "nginx"
twikoo:
image: imaegoo/twikoo
container_name: "twikoo"
restart: unless-stopped
ports:
- "8099:8080"
environment:
TWIKOO_THROTTLE: 1000
volumes:
- "./data/twikoo_data:/app/data"
  • nginx 中,构建文件夹是./nginx_dockerfile

  • 使用了三个端口映射 80 端口接收 http 请求,443 端口处理 https ,这里将宿主机2333 映射到 容器的22 端口解决 ssh 连接问题

  • 将博客源文件、SSL证书、nginx的配置文件挂载到数据盘,以实现数据的持久化,以及容器的初始时的无状态

    注意这里的 volums 如果是将空的宿主机的目录挂载到容器内部的一个不为空的目录,则容器内部数据会被覆盖,也就是覆盖为空

  • 并且设置 PASSWD EMAIL DOMAIN 三个环境变量,来初始化容器内部的 root 初始密码,邮箱(申请https需要的),域名

到这一步需要做的是将宿主机的 2333 和 8099 端口暴露出来,并且在 之后的 ssh 连接以及指定 ssh key 的时候需要指定端口名

-p 2333

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
FROM nginx:latest

# change source
RUN sed -i "s@http://deb.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list.d/debian.sources \
&& rm -Rf /var/lib/apt/lists/* \
&& apt-get update

# Configure git and nginx and sshd
RUN apt-get install -y git certbot python3-certbot-nginx expect openssh-server net-tools \
&& mkdir /var/repo/ \
&& chmod -R 755 /var/repo/ \
&& git init --bare /var/repo/resinceBlog.git \
&& echo "git --work-tree=/var/www/hexo --git-dir=/var/repo/resinceBlog.git checkout -f" > /var/repo/resinceBlog.git/hooks/post-receive \
&& chmod +x /var/repo/resinceBlog.git/hooks/post-receive \
&& mkdir -p /var/www/hexo \
&& chmod -R 755 /var/www/hexo \
&& mkdir -p ~/.ssh \
&& touch ~/.ssh/authorized_keys \
&& chmod 700 ~/.ssh/ \
&& chmod 600 ~/.ssh/authorized_keys \
&& echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config \
&& echo "PermitRootLogin yes" >> /etc/ssh/sshd_config \
&& echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config \
&& echo "AuthorizedKeysFile .ssh/authorized_keys" >>/etc/ssh/sshd_config \
&& mkdir /run/sshd

# init nginx config
COPY hexo.conf /root/init_file/hexo.conf
COPY twikoo.conf /root/init_file/twikoo.conf

# entrypoint file
COPY start.sh /docker-entrypoint.d/start.sh
COPY init_pwd.sh /root/init_file/init_pwd.sh
COPY init_https.sh /root/init_file/init_https.sh
RUN chmod +x /docker-entrypoint.d/start.sh \
&& apt-get install -y expect \
&& apt-get install -y tcl

EXPOSE 80
EXPOSE 443
EXPOSE 22
  • 其中 Configure git and nginx 的大部分配置来自原本部署服务器端的命令,具体可以看我之前的博客:博客搭建(一) | Resince之后的配置 sshd 文件是将宿主机的部分选项打开,比如允许 root 用户登录,允许使用 ssh key 登录

    注意:

    在这份镜像中,apt的配置文件是在 /etc/apt/sources.list.d/debian.sources 中,而不是在 /etc/apt/sources.list/文件中

  • 初始化 nginx 配置这一步是为了防止直接转移到 /etc/nginx/conf.d 文件夹后,被后来的数据盘覆盖,为了将数据放在配置文件夹中,我们在 start.sh 入口文件中,将配置文件转移到conf.d 文件夹中

    关于覆盖问题具体可以看 dockerfile使用COPY覆盖父镜像定义的volume

  • entrypoint file 中有两个关键点

    • 首先为了容器数据的初始化,需要 .sh脚本 以及expert 来处理比如密码初始化,以及https的申请等,这个在 dockerfile 中不支持实现
    • 其次为什么不直接将 start.sh 设置为 ENTERYPOINT 呢,原因是由于这样会直接覆盖之前的入口点配置,导致功能不完整,而在 nginx 的这份镜像中,就有设置有入口配置文件夹 /docker-entrypoint.d 所以只需要将初始化文件转移到对应文件夹即可

nginx config

hexo.conf

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name resince.fun;

location / {
root /var/www/hexo;
index index.html index.htm;
}
}

twikoo.conf

1
2
3
4
5
6
7
8
9
10
11
12
upstream twi {
server resince.fun:8099;
}

server {
listen 80;
server_name twikoo.resince.fun;

location / {
proxy_pass http://twi;
}
}

以上是简单的nginx文件配置

entrypoint file

start.sh

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
#!/bin/sh
# vim:sw=4:ts=4:et

# 启动 sshd 服务
/usr/sbin/sshd
# 检查数据盘中的conf.d是否有数据,如果有就不初始化
if [ -z "$(ls -A /etc/nginx/conf.d)"]
then
mv /root/init_file/twikoo.conf /etc/nginx/conf.d/twikoo.conf
mv /root/init_file/hexo.conf /etc/nginx/conf.d/hexo.conf
sed -i "s@resince.fun@$DOMAIN@g" /etc/nginx/conf.d/hexo.conf
sed -i "s@resince.fun@$DOMAIN@g" /etc/nginx/conf.d/twikoo.conf
else
echo "conf.d is empty"
fi

# 初始化密码
if [ $PASSWD ]
then
chmod +x /root/init_file/init_pwd.sh
./root/init_file/init_pwd.sh
unset PASSWD
else
echo "you need init password by yourself"
fi

#给予权限
chmod +x /root/init_file/init_https.sh
./root/init_file/init_https.sh


exit 0

init_pwd.sh

1
2
3
4
5
6
7
8
#!/usr/bin/expect -f
set PASSWD $env(PASSWD)
spawn passwd
expect "New password:"
send "$PASSWD\r"
expect "Retype new password:"
send "$PASSWD\r"
expect eof

init_https.sh

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/expect -f
set EMAIL $env(EMAIL)
set DOMAIN $env(DOMAIN)
spawn certbot --nginx
expect {
"Enter email address" { send "$EMAIL\n";exp_continue}
"Please read the Terms of Service" {send "y\n";exp_continue}
"Would you be willing, once your first certificate is successfully issued" {send "N\n";exp_continue}
"Select the appropriate numbers separated by commas and/or spaces" {send "\n"}
eof
}

以上是使用 expect 来对交互式命令进行自动化处理

需要注意的是这里是使用 certbot 申请 https 证书,这里需要你将 DNS 解析配置好,并且证书的申请每周有固定次数(不要想我一样,重复删除建立容器导致达到次数上限)

启动

在对应文件夹下输入

1
docker compose up 

再进入容器,按照test.sh 的命令输入一遍即可

test.sh的命令就是简单的生成 https

还需要你本地的电脑生成ssh key 并发送到对应的容器中

1
ssh-copy-id -i ~/.ssh/id_rsa -p 2333 username@ip # 这里要指定端口

最后直接 hexo d 即可

注意这里没有写 certbot 的自动重新申请证书,大概三个月需要自己进入容器,执行

1
certbot renew

转移

之后有新的云主机,只需要复制一份 docker-compose.yml ,并把原来的 data 文件转移即可

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
version: '3'
services:
nginx:
image: resince/hexo-server
ports:
- "80:80"
- "443:443"
- "2333:22"
volumes:
- "./data/blog_files/hexo:/var/www/hexo"
- "./data/ssl/certs:/etc/letsencrypt/"
- "./data/conf.d:/etc/nginx/conf.d"
environment:
- PASSWD={your_password}
- EMAIL={your_email}
- DOMAIN={your_domain}
container_name: "nginx"
twikoo:
image: imaegoo/twikoo
container_name: "twikoo"
restart: unless-stopped
ports:
- "8099:8080"
environment:
TWIKOO_THROTTLE: 1000
volumes:
- "./data/twikoo_data:/app/data"

注意打包时需要使用 sudo ssl 的所有文件均打包