最近著手改進部落格架構,才注意到 Google Cloud Platform (GCP) 已經開始提供 Compute Engine 的免費方案,前提是位置必須指定到美國 United States,以及選用最低階的硬體配置(f1-micro + 0.6 GB memory)。

之前因為貪戀伺服器連接速度以及極小的 ping值 Delay ,而將伺服器指定到東亞地區的位置(看起來應該在台灣)。雖然是 f1-micro + 0.6 GB 記憶體配置,但是一個月流量加 GCP 評估的運算需求量,平均要花6-7美元。在台灣用得很開心,網站速度基本上都是秒開。

後來看到有人分享極低價格的 Shared-host 主機,提到 Cloudflare, 這才想起這個名聞遐邇的 CDN 服務。CDN 分散式架構目的之一,是設法將全球各地不同地區的網路延遲優化。於是我使用 VPN 模擬從不同國家地區連到本站的情形,發現從歐洲連線延遲很嚴重,網站載人體驗很差,於是興起了搬遷主機到美國,重新規劃網站架構的念頭。

至於之前從 Wordpress 轉換到 Ghost 的經過,請參考這一篇

架構簡圖

Website-structure

GCP 配置

主機

Ubuntu 本身做什麼特別的設定,只是開了 BBR TCP/IP 演算法,以及基本的資安設定。

modprobe tcp_bbr 
echo "tcp_bbr" >> /etc/modules-load.d/modules.conf 

echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf

sysctl -p

sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control

//如果以上兩行都輸出含有 BBR 的結果則代表內核已載入 BBR

lsmod | grep bbr //如果看到有 tcp_bbr 則代表已成功啟用

開啟 BBR 前請先確認內核版本是否 >= 4.9

網站 Ghost blog 設置

以下是我在 Ghost Blog 的 config.json.production

{
  "url": "https://example.com",
  "preloadHeaders": 100,
  "server": {
    "port": 2368,
    "host": "127.0.0.1"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "localhost",
      "user": "example",
      "password": "example",
      "database": "example"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/www/ghost/content"
  },
  "caching": {
    "301": {
      "maxAge": 31536000
    },
    "frontend": {
      "maxAge": 31536000
    },
    "customRedirects": {
      "maxAge": 31536000
    },
    "favicon": {
      "maxAge": 86400
    },
    "sitemap": {
      "maxAge": 3600
    },
    "robotstxt": {
      "maxAge": 3600000
    }
  }
}

設定檔除了原本 Ghost 正常運作需要的資訊外,還增加了:

  1. preloadHeaders: 讓 Ghost 輸出可以 http2-push 的資源標記
  2. Caching 一區設定各個元素在 Header 輸出的快取壽命。

Ghost 設定檔請參考官方文件

Nginx 設置

Ubuntu 16.04 官方預設提供的 nginx 版本比較舊,如果需要追求一些最新的功能,需要自行下載較新的版本。

我需要的 Edge 功能有:

  1. 支援 http2 server push
  2. 額外的 Google pagespeed module
  3. Brotli compression 壓縮演算法支持
  4. Cloudflare TLS Dynamic Records Resizing Patch

因爲要安裝支援以上功能的 nginx, 乾脆到 Github 找了一個還不錯的安裝腳本,依照作者說明指示安裝完畢。

我的 nginx 設定檔如下:

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
        worker_connections 4096;
        multi_accept on;
        use epoll;
}

http {
        aio threads;
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 30;
        types_hash_max_size 2048;
        server_tokens off;

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

        ##
        # Logging Settings
        ##

        #access_log /var/log/nginx/access.log;
        #error_log /var/log/nginx/error.log;

        ##
        # Gzip and brotli Settings
        ##

        gzip on;
        gzip_disable "msie6";

        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_types
           application/atom+xml
           application/javascript
           application/json
           application/rss+xml
           application/vnd.ms-fontobject
           application/x-font-ttf
           application/x-web-app-manifest+json
           application/xhtml+xml
           application/xml
           font/opentype
           image/svg+xml
           image/x-icon
           text/css
           text/plain
           text/x-component
           text/xml
           text/javascript;
        
       brotli on;
       brotli_comp_level 6;
       brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
       brotli_static off;
       brotli_buffers 16 8k;
       brotli_window 512k;
       brotli_min_length 20;

      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;
}

這部分主要設定 nginx 通用的設定,Pagespeed 以及每個網站的設定另外獨立。重點是開啟了 multi_accept, epoll, aio, brotli

本部落格設定檔如下:

server {
    listen 443 ssl http2 fastopen=3 reuseport;
    listen [::]:443 ssl http2 fastopen=3 reuseport;

    server_name example.com;
    root /var/www/example-site/;

    ssl_certificate /etc/letsencrypt/example.com/fullchain.cer;
    ssl_certificate_key /etc/letsencrypt/example.com.key;
    include /etc/nginx/snippets/ssl-params.conf;
    
    pagespeed on; 
    location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
     add_header "" "";
    }
    location ~ "^/pagespeed_static/" { }
    location ~ "^/ngx_pagespeed_beacon$" { }

    location / {
     proxy_set_header   X-Real-IP $remote_addr;
     proxy_set_header   Host      $http_host;
     proxy_pass         http://127.0.0.1:2368;
     http2_push_preload on;
     add_header Set-Cookie "session=1";
     add_header Link $resources;
    }

    location ~ /.well-known {
        allow all;
    }
}
map $http_cookie $resources {
    "~*session=1" "";
    default "</style.css>; as=style; rel=preload";
}

主要是除了 Ghost Blog 的基本設定以外,再額外啟用 http2 server push, Pagespeed. 另外由於本站也有啟用 Let’s encrypt, 所以也有參雜一些相關設定在裡面。

為了避免伺服器重複主動推送資源,所以特別設定 Cookie

另外 SSL 的部分也另外做了一些修改

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets on; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload';
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/nginx/snippets/dhparam.pem;

Pagespeed module 設定檔

pagespeed RewriteLevel PassThrough;
pagespeed FileCachePath /tmp/ngx_pagespeed_cache;
pagespeed DisableFilters convert_jpeg_to_webp;
pagespeed EnableFilters collapse_whitespace;
pagespeed EnableFilters inline_images,dedup_inlined_images;
pagespeed EnableFilters insert_dns_prefetch;
pagespeed EnableFilters remove_comments;
pagespeed EnableFilters prioritize_critical_css;
pagespeed EnableFilters inline_css;
pagespeed EnableFilters defer_javascript;
pagespeed AllowVaryOn None;
pagespeed ServeRewrittenWebpUrlsToAnyAgent off;\\

Pagespeed 我沒有開啟預設值全部的設定,而且經過多次測試後關掉了 Webp 相關的設定。Pagespeed 一經啟用,會在傳送的網站資料內插入 beacon 來搜集瀏覽器資料以最佳化網站。

SSL 優化參考

Brotli 是 Google 在 2015 年開發出來的新壓縮演算法,比起 Gzip 又進一步增加資料壓縮密度,可以節省更多傳輸流量,詳情可參考 Wiki (https://zh.wikipedia.org/zh-tw/Brotli)


之前因為沒有啟用 Cloudflare ,為了增加網站回應速度,曾經參考教學設定了 nginx cache
然而,啟用了 Cloudflare 以後,在 Ghost blog 平台動態生成頁面以後,Cloudflare 就可以扮演起生成靜態頁面快取,減少伺服器主機負荷的角色了。
在原本主機在設定一層靜態快取,我想只是徒增設計開發上的困擾。所以取消 nginx cache ,可以簡化整個架構,降低網站開發除錯的成本。

Cloudflare 配置

儘管 Cloudflare 在台北建立了 CDN 主機,但是以前曾經聽說 Cloudflare 只有專業版用戶才會從台北線路傳送網站資料。不過這一次重新設定以後很驚訝地發現很快的 Chrome 開發者工具顯示的 Header 資訊是來自於台北。

學會看 Chrome 的開發者工具,可以判斷一些 Cloudflare 的快取資訊,如前面提過的快取提供主機來源地,以及快取有沒有命中等等。

Chrome-Header

不過一開始 Header 顯示的訊息頁面都沒有被 Cache 到,上網查詢了一下資料才發現如果沒有設定 Page Rules 的話,Cloudflare 不會主動 Cache HTML 。

目前 Cloudflare 除了基本的 DNS Record 設定以外,還額外設定了以下項目:

  1. Page Rules
  2. HSTS
  3. Brotli
  4. 停用 Minify / Rocket Loader (因為已經啟用 Pagespeed 了)

Page Rules 的設定目的,在於將後台編輯器排除快取,以及將前端設定最大的頁面快取。

Cloudflare-pagerule

cloudflare-cli - 指令快速開啟 Development mode 以及重設清除快取

在啟用 Cloudflare 後,因故要更新某一篇文章,或是要修改整個網站的 CSS 設定,都要登入到 Cloudflare 後台去做設定,覺得很不方便,靈機一動,上網搜尋有沒有 commandline 指令行工具,還真的有。

cloudflare-cli npm 套件連結

npm install -g cloudflare-cli

安裝以後,可以在個人家目錄新增一個 ~/.cfcli.yml

defaults:
    token: <cloudflare-token>
    email: <[email protected]>
    domain: <default-cloudflare-domain>

token 資訊可以在 Cloudflare 帳戶資訊裡面找到,填完以後,就可以很方便的從終端機裡面下指令了:

cfcli purge <url> //清除某個頁面的*Cloudflare*快取

cfcli devmode on|off //開關cloudflare development mode