Docker 建站小記

一,前言

Docker 建站小記,我使用了四個鏡像來搭建:nginx,certbot,mysql,gradle。歡迎訪問:https://www.zzk0.tophtml

這個網頁是從 github 上找的我的主頁,背景用的是 bing 的壁紙,中間有兩個連接,一個指向個人 github,一個指向博客園。後端項目目前只有一個接口,當有人訪問這個網站的時候,就會向後端項目發出一個請求,由 nginx 負責將對 api.zzk0.top 的請求轉發到後端項目。mysql

最理想的狀態是,我直接 docker-compose up,我就能夠將項目運行起來了。不過個人方法,須要本身手動配置一下才行,一個是初始化申請 SSL 的腳本,一個是定時分割 NGINX 日誌。以後就只須要 docker-compose up 就好了。nginx

這裏簡單講講這四個鏡像分別是幹什麼的吧。git

  • nginx,這個是 http 服務器,用來提供提供靜態資源的,這篇博客大部份內容都是在講如何配置 nginx。
  • certbot,這個用來申請 SSL 證書。設置了一個定時任務,每月刷新一下證書。
  • mysql,後端項目數據庫。
  • gradle,編譯並運行 springboot 項目。

二,NGINX

從新加載配置

修改了 NGINX 的配置,可是又不想從新啓動容器。咱們能夠經過向容器發送命令來加載新配置。github

# docker exec -it your-name nginx -s reload
2021/01/23 02:30:58 [notice] 23#23: signal process started

HTTPS 配置

首先咱們須要證書和密鑰,咱們可使用免費的 Let's Encrypt 來生成,不過三個月就會過時,因此須要刷新一下。web

證書的生成能夠選擇在本機上仍是在 Docker 裏,若是在本機上生成,咱們能夠選擇 standalone 模式,可是這樣的話,它須要短暫佔用一下 80 端口來驗證這個域名是不是你的。所以當你有一個正在運行的 NGINX 而且佔用了 80 端口的話,咱們須要去暫停它。若是在 Docker 中生成,而且你的網站有一個根目錄,那麼可使用 webroot 模式,它會在你的網站根目錄下生成一些文件,而後經過域名去訪問它來驗證這個域名是否是你的,使用 webroot 模式就不須要暫停 NGINX,所以對正在運行的網站影響會小一些。spring

選擇前者的好處是,不須要定製 Docker,可是須要短暫中止服務器;選擇後者的好處是,不須要中止服務器,可移植性更好一點,本機只須要有 Docker 就能跑,可是須要定製鏡像比較麻煩。不過,這種比較常見的需求,咱們上 docker hub 搜一搜,一找就有了:https://hub.docker.com/r/staticfloat/nginx-certbot/ 。再搜一搜,咱們會發現另外一個方案:爲何不搞一個 certbot 的容器呢?而後將 nginx 的容器和 certbot 的容器鏈接起來!好,那咱們就加多一個容器,來幫咱們生成 SSL 證書好了。sql

申請docker

這篇文章能夠參考看看 certbot 的操做。這篇文章提供了 certbot 和 nginx 的方案。shell

從文章配套的 Github 倉庫 中把它的腳本和配置文件複製粘貼過來,把全部的 example.org 域名改爲本身的域名,而後運行它的腳本。若是失敗了,建議檢查一下是否是 NGINX 沒有啓動。這個腳本不會提醒你 NGINX 沒有啓動,若是失敗了,頗有多是 NGINX 沒有配置好。

一開始跑這個腳本,我失敗了幾回。以後逐步執行腳本,而後啓動容器看看什麼問題。一看,才知道原來是 https 的推薦參數文件沒有下載好,因此手動下載了那幾個文件,而後修改腳本爲複製而不是下載。

重定向

nginx 將 http 重定向到 https,完整的配置文件能夠看附錄。

server {
    listen 80;
    server_name zzk0.top www.zzk0.top;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name zzk0.top www.zzk0.top;
    root /var/www/html;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

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

配置子域名

假設有一個域名 test.com,咱們須要將對 api.test.com 的請求轉發到 Tomcat,對 test.com 的請求則直接提供靜態網站。

這個需求比較簡單,在 server 下面設置好 server_name 就能夠了。另外,咱們還須要注意不要讓別人用 ip 訪問咱們的網站,網上的說法是,別人能夠惡意使用未備案的域名指向這個 ip,而後致使網站被封掉了。

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

server {
    listen 443 default_server;
    server_name _;
    ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    return 403;
}

訪問日誌

爲了記錄網站的訪問記錄,可使用 NGINX 的訪問日誌來作記錄,按天分割。NGINX 的鏡像裏面沒有 crontab,因此這裏就用主機來作。方法是天天定時將文件重命名,而後 reload NGINX。不過這個部分比較不智能,每次更換主機的時候,都須要手動去配置,還須要修改下面的路徑。

#!/bin/sh

LOGS_PATH=/root/home/data/nginx
TODAY=$(date -d 'today' +%Y-%m-%d)

mv ${LOGS_PATH}/error.log ${LOGS_PATH}/error_${TODAY}.log
mv ${LOGS_PATH}/access.log ${LOGS_PATH}/access_${TODAY}.log

docker exec -i home-nginx nginx -s reload

接下來,將如下內容加入到 /etc/crontab 中,這樣就能夠執行每日任務了。天天凌晨4點的時候,自動分割日誌。

0 4 * * * root /root/home/nginx/daily_log.sh >> /root/home/data/nginx/daily_log.log 2>&1

三,MySQL

有時候咱們須要進入數據庫的鏡像裏面去看看數據。

# 進入鏡像
docker exec -it your-name bash

# 登陸
mysql -uroot -p

# 羅列數據庫
show database

四,後端項目

後端項目使用 SpringBoot 來作。需求是:啓動 Docker 後構建源碼並運行。這個部分使用 gradle 的鏡像,啓動的時候,運行 gradle bootRun 就能夠了。

當咱們更新了後端項目以後,咱們只須要重啓這個容器,就能夠構建了。個人後端項目容器名字叫作 springboot,重啓一下就能夠將接口更新了,不太重啓過程當中的請求會失敗。

docker restart springboot

下面是 docker-compose.yml 中的配置。

web:
    container_name: springboot
    restart: always
    image: gradle:6.7.1-jdk8
    depends_on:
      - db
    volumes:
      - ./api:/home/gradle/project
    environment:
      TZ : 'Asia/Shanghai'
    command: bash -c "cd /home/gradle/project && gradle bootRun"

附錄

NGINX 配置

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;
	upstream tomcat {
		server web:8080;
	}

    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;

    limit_req_zone $binary_remote_addr zone=api_limit_req:10m rate=30r/m;

    sendfile        on;
    keepalive_timeout  65;
    gzip  on;
    include /etc/nginx/conf.d/*.conf;

    # forbid access via ip address
    server {
        listen 80 default_server;
        server_name _;
        return 403;
    }

    server {
        listen 443 default_server;
        server_name _;
        ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        return 403;
    }

    # configure zzk0.top, redirect to https and serve static files
    server {
        listen 80;
        server_name zzk0.top www.zzk0.top;
        server_tokens off;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        server_name zzk0.top www.zzk0.top;
        root /var/www/html;
        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

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

    # configure api.zzk0.top, backend api
    server {
        listen 80;
        server_name api.zzk0.top;
        server_tokens off;

        location /.well-known/acme-challenge/ {
            root /var/www/certbot;
        }

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        server_name api.zzk0.top;
        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/zzk0.top/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/zzk0.top/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

        location / {
            limit_req zone=api_limit_req;
            proxy_pass http://tomcat;
            proxy_set_header    Host                $http_host;
            proxy_set_header    X-Real-IP           $remote_addr;
            proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        }
    }

}

Docker 配置

version: '3'
services:
  certbot:
    container_name: home-certbot
    restart: always
    image: certbot/certbot
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 720h & wait $${!}; done;'"

  nginx:
    container_name: home-nginx
    restart: always
    image: nginx:1.18.0
    ports:
      - 80:80
      - 443:443
    depends_on:
      - web
    links:
      - web:web
    environment:
      TZ : 'Asia/Shanghai'
    volumes:
      - ./html:/var/www/html
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
      - ./data/nginx:/var/log/nginx
    command: "/bin/sh -c 'while :; do sleep 720h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

  db:
    container_name: home-db
    restart: always
    image: mysql:8.0.13
    ports:
      - 7706:3306
    volumes:
      - ./data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: backend_database
      MYSQL_USER: root
      MYSQL_PASSWORD: root

  web:
    container_name: springboot
    restart: always
    image: gradle:6.7.1-jdk8
    depends_on:
      - db
    volumes:
      - ./api:/home/gradle/project
    environment:
      TZ : 'Asia/Shanghai'
    command: bash -c "cd /home/gradle/project && gradle bootRun"
相關文章
相關標籤/搜索