使用 nginx 和 certbot 为 wordpress 开启 https

前段时间 wordpress 搭建完成,但是没有 https 总感觉少了点什么。由于 docker 还不熟悉,nginx 也不懂,前后折腾许久踩了很多坑,但是好在找到了一篇文章,很详细的描述了如何使用 docker-compose 来搭建 https 的 wordpress。

参考:https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-docker-compose

首先是 wordpress 的搭建,可以参考另一篇

创建 nginx 文件夹和配置文件

创建的文件夹最好和 docker-compose.yml 在同一个文件夹下,方便编写配置和管理。
这个文件夹将用来绑定 nginx 容器的 volume

~/www/wordpress$ mkdir nginx-conf
~/www/wordpress$ vi nginx-conf/nginx.conf

写入:

server {
    listen 80;
    server_name linyqiang.com www.mingof.top;

    index index.php index.html index.htm;

    root /var/www/html;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

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

    location ~ \.php$ {
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }

}

我对 nginx 不是很了解,只是 Ctrl+C, Ctrl+V。知道大概有什么用处就好了。注意这一块:

location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

这一块代码的作用就是让 certbot 在访问 linyqiang.com/.well-known/acme-challenge 时能通过。certbot 申请证书的时候需要。

改写 docker-compose.yml

原来的 compose file 是这样的:

version: '3.7'

    services:
        wordpress:
            image: wordpress
            depends_on:
                - mysql
            volumes:
                - wordpress:/var/www/html
            environment:
                WORDPRESS_DB_HOST: mysql:3306
                WORDPRESS_DB_USER: wordpress
                WORDPRESS_DB_PASSWORD: 12345
                WORDPRESS_DB_NAME: wordpress
            # docker-compose 不支持 deploy,在使用 docker stack deploy 时可以用到
            deploy:
                resources:
                    limits:
                        cpus: "0.5"
            ports:
                - 4000:80
        mysql:
            image: mysql
            volumes: 
                - mysql:/var/lib/mysql
            environment:
                MYSQL_DATABASE: wordpress
                MYSQL_USER: wordpress
                MYSQL_PASSWORD: 12345
                MYSQL_RANDOM_ROOT_PASSWORD: '1'
            ports:
                - 3306:3306

volumes:
    wordpress:
    mysql:

这里需要添加两个 service,一个是 nginx,一个是 certbot

version: '3.7'

services:

    wordpress:
        image: wordpress:5.2.3-fpm-alpine
        depends_on:
            - mysql
        volumes:
            - wordpress:/var/www/html
        environment:
            WORDPRESS_DB_HOST: mysql:3306
            WORDPRESS_DB_USER: your_wordpress_dbuser
            WORDPRESS_DB_PASSWORD: your_secret
            WORDPRESS_DB_NAME: wordpress
        # 使用 docker-compose 会自动忽略 deploy
        deploy:
            replicas: 5
            resources:
                limits:
                    cpus: "0.5"
            restart_policy:
                condition: on-failure

    mysql:
        image: mysql:5.7
        deploy:
            restart_policy:
                condition: on-failure
        ports:
            - 3306:3306
        volumes:
            - mysql:/var/lib/mysql
        environment:
            MYSQL_DATABASE: wordpress
            MYSQL_USER: your_wordpress_dbuser
            MYSQL_PASSWORD: your_secret
            MYSQL_RANDOM_ROOT_PASSWORD: "1"

    # nginx 依赖 wordpress        
    nginx:
        depends_on:
            - wordpress
        image: nginx:1.17.4-alpine
        restart: unless-stopped
        ports:
            - "80:80"
            - "443:443"
        volumes:
            - wordpress:/var/www/html
            - ./nginx-conf:/etc/nginx/conf.d
            - certbot:/etc/letsencrypt

    # certbot 依赖 nginx
    certbot:
        depends_on:
            - nginx
        image: certbot/certbot
        container_name: certbot
        volumes:
            - certbot:/etc/letsencrypt
            - wordpress:/var/www/html
        command: certonly --webroot --webroot-path=/var/www/html --email [email protected] --agree-tos --no-eff-email --force-renewal -d linyqiang.com -d www.mingof.top --server https://acme-v02.api.letsencrypt.org/directory    
volumes:
    certbot:
    wordpress:
    mysql:

把上面的 your_wordpress_dbuser 和 you_secret 换成你自定义的 mysql 用户和密码,域名换成自己域名就好。上面的镜像的依赖关系大概就是这个样子。

certbot -> nginx <- wordpress <- mysql 

certbot 服务中指定了 ‘command’, 这个 command 后面的命令会在服务建立后自动运行。这个命令会帮助我们申请到证书。

使用 docker-compose 创建容器并自动获取证书

使用 docker-compose up 来试运行一下,成功后就可以使用 https 访问了。此时使用 docker volume ls 可以看到3个 volume。mysql 和 wordpress 的 volume 就不用说了,这里还多了一个 volume 叫做 wordpress_certbot。上面的 docker-compose.yml 可以看到这个 volume 同时绑定了 nginx 容器的 /etc/lesencrypt 文件夹 和 certbot 容器的 /etc/letsencrypt 文件夹。这样在 nginx 容器中就可以访问 certbot 容器下创建的 letsencrypt 证书文件了。

DRIVER              VOLUME NAME
local               wordpress_certbot
local               wordpress_mysql
local               wordpress_wordpress

现在还不可以使用 https 访问,要开启 https 还有改写一下 nginx.conf。让 nignx 监听 443 端口,并且加入证书配置。

改写 nignx.conf 让其支持 https

~/www/wordpress$ vi nginx-conf/nginx.conf

改写为:

server {
    listen 80;
    server_name linyqiang.com www.mingof.top;
    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }
    location / {
        rewrite ^ https://$host$request_uri? permanent;
    }
}
server {
    listen 443 ssl http2;
    #listen 80;
    server_name linyqiang.com www.mingof.top;

    index index.php index.html index.htm;

    root /var/www/html;

    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/linyqiang.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/linyqiang.com/privkey.pem;

    include /etc/nginx/conf.d/options-ssl-nginx.conf;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;

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

    location ~ \.php$ {
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /favicon.ico {
        log_not_found off; access_log off;
    }
    location = /robots.txt {
        log_not_found off; access_log off; allow all;
    }
    location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
        expires max;
        log_not_found off;
    }

}

再次使用 docker-compose 试运行

docker-compose up

现在应该可以使用 https 访问了。

使用 docker stack deploy 代替 docker-compose

docker stack deploy 毕竟是 docker 官方的,能用官方推荐就用官方推荐吧。

docker stack deploy -c docker-compose.yml wordpress

docker stack deploy 和 docker-compose 还是有一点区别的。

Docker stack 会忽略一些指令,如 build 指令。 无法使用stack命令构建新镜像。 它是需要镜像是预先已经构建好的。

Docker stack 运行在 swarm mode 下。

Docker stack 还支持 deploy 指令

还有一个重要的一点就是我在使用 docker-compose 的时候会把 volume 的用户改为 root ,这个改动容易导致一些权限问题。