阿绪的建站手册

写在开始之前

经过了历时三天的努力, “ 阿绪的小站”建了起来。以后就有地方可以自由的写一些文字了。

因为阿绪也是第一次建站,关于这方面的知识懂得也不多,如果有什么意见或者建议的欢迎留言。

本次建站所用到的技术大致为以下几个:

  • Docker
  • Nginx
  • WordPress

在本手册里,阿绪会把建站的过程按顺序贴出来,并贴上相应的代码和资料,方便大家查阅。

Docker

脚本安装

推荐无内网镜像源的服务器使用,如国外的一些VPS

这引用一下《Docker — 从入门到实践》的做法:

在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Debian 系统上可以使用这套脚本安装,另外可以通过 --mirror 选项使用国内源进行安装:

$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun

为了方便起见,我把这两句命令合并起来运行了,记得用 root 权限。

bash <(curl -fsSL get.docker.com)

这样 docker 就装好了,可以 docker info 查看一下版本号,然后转到下面的 step 5 处。

命令安装

推荐有内网镜像源的服务器使用,如阿里云ECS

step 1: 安装必要的一些系统工具

sudo apt update
sudo apt -y install apt-transport-https ca-certificates curl software-properties-common

step 2: 安装GPG证书(ubuntu记得把Debian换成ubuntu)

curl -fsSL http://mirrors.cloud.aliyuncs.com/docker-ce/linux/debian/gpg | sudo apt-key add -

step 3: 写入软件源信息(使用阿里云内网镜像源)

sudo add-apt-repository "deb [arch=amd64] http://mirrors.cloud.aliyuncs.com/docker-ce/linux/debian $(lsb_release -cs) stable"

step 4: 更新并安装 Docker-CE

sudo apt -y update
sudo apt -y install docker-ce

step 5: 开启 Docker 服务

sudo systemctl start docker
sudo systemctl enable docker

step 6: 添加用户组

sudo usermod -aG docker $USER

简单的说明一下:

  1. 如果是非Debian系统的需要在第二步的时候修改源地址。
  2. 如果是非阿里云服务器的可以使用阿里云公网源或者其他镜像源,如果是阿里云服务器建议使用阿里云内网源,这样安装和更新的时候不使用公网带宽和公网流量。
  3. 第六步是将用户添加到docker用户组,这样可以直接使用docker命令。

Docker设置加速源

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://阿里云分配的地址.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

阿绪使用的是阿里云的加速源,如果使用其它的加速源可以将地址换成其他的,这里放一下几个常见的源地址。

  • docker中国 https://registry.docker-cn.com
  • 网易 http://hub-mirror.c.163.com
  • 中科大 http://docker.mirrors.ustc.edu.cn

这样docker就装好并配置好加速器了,可以进行我们的下一步,安装博客了。

docker-compose

有关模板文件的编写的可以查看《Docker — 从入门到实践》或者官网文档,这边不过多的赘述了。

docker-compose up -d 后台运行容器

docker-compose down 关闭并清理容器

docker-compose start/stop/restart 启动、停止、重启容器

docker-compose logs/ps 查看日志、进程(logs后面可以加容器名称,查看那一个容器的日志)

watchtower

Watchtower 会监视正在运行的容器并查看这些容器的镜像有没有变动。当一个镜像发生时,它会使用新镜像自动重新启动相应的容器。

docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
--restart=unless-stopped containrrr/watchtower

docker-gc

docker-gc 通过删除不需要的容器和镜像来帮你清理 Docker 主机。它会删除存在超过一个小时的所有容器。同时,它还会删除不属于任何留置容器的镜像。

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock spotify/docker-gc

WordPress

建立文件夹

为了工程目标的简洁明了,需要建立相应的文件夹来存放配置文件。

这是这个博客的工程目录,其中,logs是用来存放日志的,nginx是存放nginx的配置文件的, wordpress是存放站点的,最重要的是就是docker-compose.yml文件,用来存放docker配置。

使用阿里云RDS

先看 docker-compose.yml 文件。

version: '3.1'

services:

    wordpress:
        restart: always
        container_name: wordpress
        image: wordpress:php7.4-fpm-alpine
        networks:
            - wp_net
        environment:
            WORDPRESS_DB_HOST: 服务器地址
            WORDPRESS_DB_USER: 服务器用户名
            WORDPRESS_DB_PASSWORD: 服务器用户密码
            WORDPRESS_DB_NAME: 数据库名
        volumes:
            - ./wordpress:/var/www/html

    nginx:
        restart: always
        container_name: nginx
        image: nginx:stable-alpine
        networks:
            - wp_net
        ports:
            - 80:80
            - 443:443
        volumes:
            - ./nginx/certs:/etc/nginx/certs
            - ./nginx/conf.d:/etc/nginx/conf.d
            - ./nginx/nginx.conf:/etc/nginx/nginx.conf
            - ./logs:/var/log/nginx
            - ./wordpress:/usr/share/nginx/html
            - ./nginx/rotatelog.sh:/var/log/nginx/rotatelog.sh

networks:
    wp_net:

这是删除一些隐私数据之后的 docker-compose.yml 文件,其中各个字段是啥意思就不赘述了。特别讲明一下,使用网络的话nginx可以不依赖于wordpress,但是为了防止wordpress还没有起来的时候就有访问进来,导致nginx报错,所以我添加了这个依赖。

因为阿绪使用的是阿里云的RDS服务(双十一活动低价买来的),所以不需要mysql的镜像,WORDPRESS_DB_xxx那边直接填写RDS服务的参数即可。如果你没有RDS服务的话,可以使用下面这边配置。

使用本地mysql容器

version: '2'

services:
    wordpress:
        restart: always
        container_name: wordpress
        image: wordpress:php7.4-fpm-alpine
        networks:
            - wp_net
        environment:
            WORDPRESS_DB_HOST: mariadb
            WORDPRESS_DB_USER: wordpress
            WORDPRESS_DB_PASSWORD: wordpress
            WORDPRESS_DB_NAME: wordpress
        volumes:
            - ./wordpress:/var/www/html

    nginx:
        restart: always
        container_name: nginx
        image: nginx:stable-alpine
        networks:
            - wp_net
        ports:
            - 80:80
            - 443:443
        volumes:
            - ./nginx/certs:/etc/nginx/certs
            - ./nginx/conf.d:/etc/nginx/conf.d
            - ./nginx/nginx.conf:/etc/nginx/nginx.conf
            - ./logs:/var/log/nginx
            - ./wordpress:/usr/share/nginx/html
            - ./nginx/rotatelog.sh:/var/log/nginx/rotatelog.sh

    mariadb:
        restart: always
        container_name: mariadb
        image: mariadb:latest
        networks:
            - wp_net
        environment:
            MYSQL_DATABASE: wordpress
            MYSQL_USER: wordpress
            MYSQL_PASSWORD: wordpress
            MYSQL_ROOT_PASSWORD: "设置一个root密码"
        volumes:
            - ./db:/var/lib/mysql

networks:
    wp_net:

这个配置文件比起第一个来说就只是添加了一个数据库的配置文件(还是从官方那边拷贝过来的,如果不能运行的话记得和阿绪说一下,阿绪去研究一下)。为了方便备份数据,阿绪把官网的挂载数据卷改成了挂载本地目录。

现在,工程目录已经建好了,但是还缺少一点灵魂。

配置nginx

这是nginx文件夹里面的文件,其中certs文件夹里存放证书文件,conf.d文件夹里存放网站的配置文件,nginx.conf是整个nginx的配置文件,rotatelog.sh是日志轮转文件。我们将从上到下依次讲解。

certs文件夹

这个文件夹将被挂载到容器的/etc/nginx/certs目录里。

其他的啥好说的,你把证书下载下来丢进去就好了。到时候配置里指定一下目标,让nginx能找到就行。一个私钥一个公钥,但不一定非要是这两个格式的。

conf.d文件夹

这个文件夹将被挂载到容器的/etc/nginx/conf.d目标里。

server {
    listen       443 ssl;
    server_name  域名;

    ssl_certificate      /etc/nginx/certs/域名.pem;
    ssl_certificate_key  /etc/nginx/certs/域名.key;

    location / {
        root      /usr/share/nginx/html;
        index     index.php index.html index.htm;
    }

    location ~ .php$ {
        root           /var/www/html;
        fastcgi_pass   wordpress:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

server {
    listen       80;
    server_name  域名;
    return       301 https://$server_name$request_uri;
}

server {
    listen 80 default;
    server_name _;
    return 403;
}

server {
    listen 443 default;
    server_name _ ;

    ssl_certificate /etc/nginx/certs/域名.pem;
    ssl_certificate_key /etc/nginx/certs/域名.key;

    return 403;
}

这是一个除去敏感信息并简化后的web.conf文件,它大概就处理了三件事情

  1. 监听443端口,如果发现访问域名就交给wordpress处理。/和~ .php$里面root目录不一样是因为两个容器里面挂载目录不一样。(应该吧,关于这点阿绪也不是很确定,如果有清楚的欢迎指正)
  2. 监听80端口, 如果发现访问域名就发一个301重定向到443端口(http跳转https)。
  3. 同时监听80和443,如果发现非这两个域名的数据,返回一个403回去,来防止他人用未备案域名来绑定你的服务器,也防止有人用ip访问你的服务器。

nginx.conf文件

user  nginx;

worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    include conf.d/web.conf;
}

这里同样是一个去处敏感信息并简化后的nginx.conf文件,最重要的是最下面那句话,它把conf.d文件夹下面的web.conf文件给include了进来。其它的都是一些默认配置了。

rotatelog.sh文件

#!/bin/bash

getdatestring()
{
    TZ='Asia/Shanghai' date "+%Y%m%d%H%M"
}
datestring=$(getdatestring)

mv /var/log/nginx/access.log /var/log/nginx/access.${datestring}.log
mv /var/log/nginx/error.log /var/log/nginx/error.${datestring}.log

kill -USR1 `cat /var/run/nginx.pid`

关于日志轮转文件阿绪将在下一章里面详细说明,这边就只放一下文件。当然可以看阿绪在参考文献中列出的地址,基本上就是根据他的代码改的。

别忘了用 chmod +x rotatelog.sh 添加运行权限。

打开站点吧

好了,到现在我们的站点的基本配置已经完成了。让我们运行下面这条指令开启它吧。

docker-compose up -d

docker-compose会自己去寻找镜像、下载安装、运行镜像。如果不出意外的话,在一段时间的等待之后,访问你的域名就可以打开你的站点了。当然,在第一次打开的时候需要一些配置,阿绪相信那些简单的配置不会让你产生什么疑惑的,所以,恭喜你,你的站点已经建好了。

当然,在开始写博客并向全世界宣告你的博客开张之前,还需要一些小小的工作,比如nginx的配置、wordpress伪静态、SEO……阿绪将在下一章讲述这些。

nginx

web.conf

server {
    listen       443 ssl;
    server_name  域名;

    charset utf-8;

    ssl_certificate      /etc/nginx/certs/域名.pem;
    ssl_certificate_key  /etc/nginx/certs/域名.key;

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;

    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;

    location / {
        root      /usr/share/nginx/html;
        index     index.php index.html index.htm;
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        root           /var/www/html;
        fastcgi_pass   wordpress:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}


server {
    listen       80;
    server_name  域名;
    return       301 https://$server_name$request_uri;
}


server {
    listen 80 default;
    server_name _;
    return       301 https://域名;
}

server {
    listen 443 default;
    server_name _ ;

    ssl_certificate /etc/nginx/certs/域名.pem;
    ssl_certificate_key /etc/nginx/certs/域名.key;

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;

    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;

    return       301 https://域名;
}

是不是比之前的多了一些东西呢,大概的讲一下关键语句吧。

ssl的那一堆是设置ssl的(好像是废话),具体的话可以查一下

charset utf-8; 选择网页编码

try_files $uri $uri/ /index.php?$args; 设置伪静态

nginx.conf

user  nginx;

worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    use epoll;
    worker_connections  10240;
}


include conf.d/kms.conf;

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile           on;
    tcp_nopush         on;
    tcp_nodelay        on;

    client_header_timeout 60;
    client_body_timeout   60;
    client_max_body_size  50m;
    send_timeout          60;
    keepalive_timeout     60;

    gzip               on;
    gzip_vary          on;
    gzip_static        on;
    gzip_proxied       any;
    gzip_comp_level    9;
    gzip_min_length    1024;

    autoindex off;
    server_tokens off;

    map $http_x_forwarded_for  $clientRealIp {
        ""	$remote_addr;
        ~^(?P<firstAddr>[0-9\.]+),?.*$	$firstAddr;
    }

    limit_conn_zone $clientRealIp zone=TotalConnLimitZone:5m ;
    limit_conn TotalConnLimitZone 50;
    limit_conn_log_level notice;

    limit_req_zone $clientRealIp zone=ConnLimitZone:5m rate=50r/s;
    limit_req_log_level notice;
    limit_req zone=ConnLimitZone burst=10 nodelay;

    include conf.d/web.conf;
}

这是阿绪博客上正在跑的配置文件,至于为啥直接用ip而不用真实ip呢。

  1. 因为阿绪的博客目前到不了那么大的访问量
  2. 阿绪没有使用cdn(好贵)
  3. 阿绪比较懒。

一导致了三,三导致了二,二导致了这个结果,所以,等访问人数上去了阿绪就会改这个配置(遥遥无期)。

已支持CDN。

rotatelog.sh

因为nginx会产生巨多的日志,如果不进行轮转的话,定位甚至打开都会成为一个巨大的问题。

日志轮转的原理其实非常简单:

  • 先把旧的日志文件重命名
  • 然后给 nginx master 进程发送 USR1 信号

我们的脚本已经完成了这两步,接下来,我们只需要定期启动脚本就好啦。

先运行测试一下脚本是否能正常运行。 docker exec mynginx bash /var/log/nginx/rotatelog.sh

然后执行 crontab -e 添加定时任务

0 1 * * * docker exec mynginx bash /var/log/nginx/rotatelog.sh

这样日志转存就做完了。

最后的最后,博客都建完了,确定不加一个友链吗?

参考文献

https://yeasy.gitbooks.io/docker_practice/
https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html
https://blog.xebialabs.com/2017/05/18/5-docker-utilities-you-should-know/ 
https://docs.docker.com/compose/reference/overview/
http://www.ha97.com/5194.html
https://www.jqhtml.com/37068.html
https://hub.docker.com/_/wordpress/?tab=description
https://juejin.im/post/5c1616186fb9a049a42ef21d
https://www.cnblogs.com/sparkdev/p/9537520.html
https://carey.akhack.com/2017/05/04/CDN%E4%B8%8Bnginx%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E7%9C%9F%E5%AE%9Eip/
https://www.nginx.com/resources/wiki/start/
http://www.nginx.cn/4646.html
知识共享许可协议
本作品采用知识共享署名 4.0 国际许可协议进行许可。

2月 ago

发表评论