# 負載平衡和高度可用性 * NCNU OpenSource LSA 課程共筆 [Book mode](https://hackmd.io/@ncnu-opensource/book) * Load Balancing and High Availability @ 1092 LSA * 負載平衡和高度可用性 從網路層的 HA / LoadBalance、SLA、資源調配。 [TOC] ## 故事時間 ![](https://i.imgur.com/g2MwYQb.png) > 網友對於[上週二某 cdn 故障](https://www.ithome.com.tw/news/144911)所作的反應,轉載自 [Twitter](https://twitter.com/ninja_writer21/status/1402231025715933187) > (不過他們只花 [38 分鐘](https://status.fastly.com/incidents/vpk0ssybt3bj)就修好了) * 如果你架的服務很常壞掉,而且一壞掉要修很久才修好 * 使用者覺得你不可靠 * 不跟你買服務,不給你錢 * 你只好吃土 * 想辦法降低使用者不能用你服務的時間,就是本章要探討的問題 ## 什麼是可用性? * 一個系統處在可工作狀態的時間的比例 ### 計算方法 $Availability = \frac{E[\mathrm{uptime}]}{E[\mathrm{uptime}]+E[\mathrm{downtime}]} =\frac{MTBF}{MTBF + MTTR}$ :::info * Uptime 正常運行時間 * Downtime 當機時間;停機時間 * MTBF (Mean time between failures) 故障平均間隔時間,平均故障時間 * MTTR (Mean time to repair) 平均修復時間 ::: * 例子: * 我們的機器的平均每 81.5 年會發生故障 * MTBF = 81.5 x 365 x 24 = 713940 小時 * 每次故障平均要花 1 個小時來修好 * MTTR = 1 小時 * Avalability = 713940 / (713940+1) = 713940 / 713941 = 99.999860% * 4 個 9,5 個 9 ... 對應到能容許的停機時間, 來自[維基上的轉換表](https://en.wikipedia.org/wiki/High_availability#Percentage_calculation) | | 90% | 99% | 99.9% | 99.99% | 99.999% | | -------------- | -------- | - | - | - | - | | 每個月的停機時間 | 73.05 小時 | 7.31 小時 | 43.83 分鐘 | 4.38 分鐘 | 26.30 秒 | | 俗稱 | 1 個 9 | 2 個 9 | 3 個 9 | 4 個 9 | 5 個 9 | ### SLA * Service Level Agreement 服務層級協議 * 服務供應商與客戶之間訂定的協議或契約 * 服務層級目標(Service Level Objective,簡稱 SLO) * 可用性 * 客服在線上回應你問題的時段,例如 7x24x365 技術支援 * 違約時的賠償政策 * 給抵免額度 credit,該服務的餘額 * 排除之例外 * 排定停機時間 * 測試版功能 * 不可抗力因素 * 客戶違反使用協議 * 例子: * [臺灣 AI 雲端服務層級協議 (SLA) | zh - HackMD](https://man.twcc.ai/@twccdocs/terms-sla-zh) * [Amazon Compute Service Level Agreement](https://aws.amazon.com/compute/sla/) ## 高可用性 * 讓服務有很高機率的時間在線上,減少服務無法使用的時間 ### 誰需要 * 使用者數量較多的網路服務 * 例如:Google、LINE、YouTube、Cloudflare、AWS... * 企業的資料庫 * 例如 7-Eleven 平常都用收銀 POS 機來結帳,要是哪天不能用了,只能改用手寫來開收據,大大降低效率 * 股票交易系統,尤其在交易期間 * 延伸閱讀:[台灣證券交易所 - 不停頓與容錯的交易系統](http://sun.csim.scu.edu.tw/~epaper/newspaper/0028/includes/content_news9.php) * 重要基礎設施,水、電、航空交通管制... ### 需要考慮的環節 * 硬體:跳電、各種硬體(網路卡、硬碟)故障... * 有備用零件、雙電源、RAID (Redundant Array of Independent Disks) * 軟體:系統預期外的重啟、遇到 exception 不讓全部卡住 * 網路:線路的多元性 * 環境:火災、淹水、地震、停電、冷氣故障 * 把伺服器放在不同地理位置 * cold site / warm site / hot site [寫在之前備份主題共筆](https://hackmd.io/qh3eBiEtQj--Zp_piczxgQ?view#Backup-site--DR-site) * 人:操作失誤,ID10T error [梗](https://youtu.be/3klMcY8amOY) <!-- 例如 sudo rm -rf / --> ![](https://i.imgur.com/BZgs1zi.jpg) * 資料:沒有同步,不一致 ### 幾個大原則 * 監控錯誤的發生 (Monitoring) * 容錯移轉 (Failover) * 冗餘備援 (Redundancy) ### 監控錯誤的發生 Monitoring * 定期監控伺服器健康狀況,讓出問題時能趕快啟動救援機制 * 也叫做 Heartbeat / Health Checks * 自動偵測資源還能不能用 #### 不同層次的監控 * L3 網路層 * 發 ICMP 來看有沒有回覆 * L4 傳輸層 * 看 port 能不能 access * L7 應用層 * 看能不能存取到特定的 URL * 發送特定內容的請求,看回應內容是否符合預期結果 * 用自訂 shell / Python 腳本來測應用程式是否正常 #### 應用程式 health check 例子 * 所有 Linux 本機程式都通用 * `pgrep <PATTERN>` 來找 Process name 符合那個 pattern 的 Process ID * 如果找得到,會回傳 Process ID,exit status code 會回傳 0 * 如果 Process 不存在,exit status code 會回傳 1 * 例如:`pgrep nginx` ``` 1410 1411 ``` * 在 shell 看 exit status code 用 `echo $?` `0` * NGINX * Passive Health Checks * 在把 nginx 當作 reverse proxy 的情形下 * 當 nginx 存取 upstream server 失敗時,會自動把那台認為是 unhealthy,並一段時間內停止往它送請求 * 我們可以自訂一些參數 `sudo vim /etc/nginx/sites-available/default ` ```=nginx upstream stream_backend { server backend1.example.com:12345; server backend2.example.com:12345 max_fails=2 fail_timeout=30s; server backend3.example.com:12346; } ``` * `max_fails` 幾次失敗就認為不健康,預設為 1 * `fail_timeout` 在多久時間內失敗 max_fails 次,就認為不健康;和持續認為不健康多久,預設為 10 秒。(兩個共用同一參數) * 例如這裡讓 backend2 30 秒內試失敗 2 次的話,就停止往它送請求 30 秒 * MySQL * 用個低權限的使用者,來看是否能連線進資料庫 * 環境設定 * `sudo mysql` 進入 MySQL 命令列 * `mysql> CREATE DATABASE testdb;` 建立測試用資料庫 * 建立測試用表格 ``` mysql> CREATE TABLE example_table ( -> example_column varchar(30) -> ); ``` * `CREATE USER 'testuser' IDENTIFIED BY 'testuser';` 建立測試用使用者 * `GRANT SELECT ON testdb TO 'testuser';` 給予測試使用者權限 * 用 testuser 連進我們建的 testdb ```bash mysql -u testuser -ptestuser -e 'SELECT * FROM testdb.example_table' ``` * 若成功會回傳 exit status code 0,不成功則是 0 以外數值 * 在 shell 中執行 `echo $?`,會回傳上個指令的 exit status code * Docker (尚未經測試) * 在 Dockerfile 裡 ```=dockerfile FROM nginx RUN apt-get update && apt-get install -y curl HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1 ``` * 增加 `HEALTHCHECK` 指示,讓 Docker 能執行 `CMD` 裡寫的指令,以確認 container 的健康狀況 * 執行 `CMD` 後回傳的 exit status code 若為 * 0: 代表成功,container 是健康的 * 1: 代表不健康 * 2: 內部保留值,別用 * `--interval`: 兩次健康檢查之間的時間間隔,預設為 30 秒。 * `--timeout`: 當健康檢查運行超過這時間,則本次檢查視為失敗。 * `--retries`: 當健康檢查連續失敗次數多於 retries 時,則視為 unhealthy。 * `docker build -t mynginx .` 來 build 成一個 image * `docker images` 查看是否有 build 成功 ``` REPOSITORY TAG IMAGE ID CREATED SIZE mynginx latest 991d5f1e0725 13 seconds ago 151MB ``` * `docker run mynginx` 把 image 執行起來 * 在 `docker ps` 能在 STATUS 那列看得到 `(healthy)` ``` CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 94b77e6d91da mynginx "/docker-entrypoint.…" 24 seconds ago Up 22 seconds (healthy) 80/tcp vigilant_newton ``` * 用 `docker inspect mynginx` 可以查看 log 檔,也會記載 healthcheck 運行紀錄 > 參考資料:[Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#healthcheck) ### 容錯移轉 Failover * 自動從災難中復原(Disaster Recovery)的能力 * 災害復原計畫(Disaster Recovery Plan, DRP) * 回復時間目標(Recovery Time Objective, RTO) * 災難發生後,恢復需要花的的時間長短 * 資料回復點目標(Recovery Point Objective, RPO) * 前一次儲存的時間點 * 會受到資料備份頻率影響 * 如果不能即時複製,備份資料會落後生產環境一段時間,你能忍受有多少資料遺失 ![](https://i.imgur.com/RhaB2mw.png) > 圖片來源:[File:RPO RTO example converted.png - Wikimedia Commons](https://commons.wikimedia.org/wiki/File:RPO_RTO_example_converted.png), [CC BY 4.0](https://creativecommons.org/licenses/by/4.0) * Backup Site * cold site / warm site / hot site [寫在之前備份主題共筆](https://hackmd.io/qh3eBiEtQj--Zp_piczxgQ?view#Backup-site--DR-site) ### 冗餘 Redundancy * 有多台設備在運行同樣的任務 * 避免單點故障 (single point of failure) * 在多個環節避免單點故障 :::info **單點故障 (single point of failure)** 指系統中一旦失效,就會讓整個系統無法運作的部件。 換句話說,單點故障,全部故障。 ![](https://i.imgur.com/1hqhiJU.png) > [圖片設計原始檔](https://gist.github.com/jiazheng0609/2803c14351060061cf369ce33b2377bc),網路架構參考自:[Cisco Virtualized Multi-Tenant Data Center Framework - Cisco](https://www.cisco.com/c/en/us/td/docs/solutions/Enterprise/Data_Center/VMDC/2-2/vmdcframework.html) ::: * 藉由設立叢集(Cluster)來達成 ::: info **叢集(Cluster)** 一組鬆散或緊密連接在一起工作的電腦,且每個節點設定為執行相同的任務,由軟體控制和排程。 ![](https://i.imgur.com/ototzqd.png) > 圖片來源:[File:Beowulf.png - Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Beowulf.png) ::: #### 叢集的架構種類 * Active/Standby(Passive) Cluster ![](https://i.imgur.com/pqpaAae.png) * Hot standby * Active 那台在提供服務 * Standby 時時刻刻和 Active 保持一致,但不提供服務 * 等到 Active 掛掉的時候,Standby 能馬上跳出來做事 (Failover 機制) * Warm standby * Active 那台在提供服務 * Standby 定期複製 Active 的資料,且軟體還沒啟動 * 等到 Active 掛掉的時候,Standby 開始啟動服務 (Failover 機制) * Active/Active Cluster ![](https://i.imgur.com/XIjwTLD.png) > 以上兩張圖片來源:[Active-Active vs. Active-Passive High-Availability Clustering | JSCAPE](https://www.jscape.com/blog/active-active-vs-active-passive-high-availability-cluster) * 兩台 Active 同時有提供服務 * 靠 load balancer 來平均工作量 * Shared-Nothing vs. Shared-Disk Clusters * Shared-Disk Clusters ![](https://i.imgur.com/NmWXdXF.jpg) > 圖片來源:[File:Shared Disk Architecture.jpg - Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Shared_Disk_Architecture.jpg) * 多個前端 server 依靠單一資料庫 * 優點:節省成本 * 缺點:有單點故障危機 * Shared-Nothing ![](https://i.imgur.com/p7StjpO.jpg) > 圖片來源:[File:Shared Nothing Architecture.jpg - Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Shared_Nothing_Architecture.jpg) * 分成多個資料庫伺服器 * 優點:沒單點故障 * 缺點:資料要做 replication,很麻煩;要開多台機器,成本高 #### 資料複製 Replication * 叢集裡不同節點要同步資料,術語是複製 Replication ::: info **Replication 跟 Backup 有啥差別?** | | Replication 複製| Backup 備份| | -------- | -------- | -------- | | 執行時機 | 每次資料變更 | 固定間隔時間 | | 災難恢復時間 | 較短 | 較長 | ::: * 常見的複製方法 * 分散式檔案系統 * 一個經由網路同步的檔案系統 * 最主要設計目標是「通透性(Transparent)」,讓使用者感覺他只是一個本機的檔案系統 * 通透性:使用者完全不需要知道他是個分散式檔案系統,不需要去煩惱各種分散式系統可能帶來的問題,就能正常使用這服務 #### 資料庫叢集 * 這裡介紹最基礎 MySQL Replication 的作法 * source-replica 架構 | 主要角色 | 備用角色 | 註解 | | -------- | -------- | -------- | | Source | Replica | MySQL 8 後官方用詞 | | Master | Slave | 2020 年以前較多人用詞 | | Active | Standby | 參照本文「叢集的架構種類」段落 | ::: info **附註 為何有那麼多叫法?** 其實在 MySQL 8 之前,官方用詞是 master-slave,但自從「黑人的命也是命」(Black Lives Matter,BLM)運動受到重視後,許多帶有歧視眼光的技術字彙從 2020 年開始被慢慢改掉。 延伸閱讀:[iThome 新聞](https://www.ithome.com.tw/news/138616)、[官方說明](https://mysqlhighavailability.com/mysql-terminology-updates/)。 ::: * 原理 * binary log * source 會把所有資料變更動作寫進 binary log * replica 先去 source 把 binary log 逐行抓下來,存成 relay log * replica 再把 relay log 裡的資料變更動作套用在自己身上 * relay log * 記錄了檔案複製的進度,下一個 event 從什麼位置開始,由 mysql 負責更新 * binary log file position-based replication * 因為每次 replica 每次都是抓整個 binary log 下來 * 要讓 replica 知道從哪個 log 檔,從哪個位置開始要做重做,不然會一直做重複的事 * 一旦設定好,replica 會自己開始記複製到哪了 * 不同格式的 binary log * statement-based replication (SBR) * 把 SQL 指令記起來 * row-based replication (RBR) * 把表格裡每行變更記起來 * 例如 `sudo mysqlbinlog /var/log/mysql/mysql-bin.000002 -vv` 來看 binlog,`#` 開頭的都是工具幫我們做 base64 decode 的結果 ```=sql # at 373 #210606 8:01:12 server id 1 end_log_pos 427 CRC32 0x2e5e0aa4 Write_rows: table id 88 flags: STMT_END_F BINLOG ' yIC8YBMBAAAAPwAAAHUBAAAAAFgAAAAAAAEABGhhZGIADWV4YW1wbGVfdGFibGUAAQ8CeAABAgP8 /wCmxJQT yIC8YB4BAAAANgAAAKsBAAAAAFgAAAAAAAEAAgAB/wARZm91cnRoIGFmdGVyIEdUSUSkCl4u '/*!*/; ### INSERT INTO hadb.example_table ### SET ### @1='fourth after GTID' /* VARSTRING(120) meta=120 nullable=1 is_null=0 */ ``` * 光是只有冗餘的資料是不夠的!要成為一個高可用系統,尚未達成的要素: * Load balance - 透過 HAProxy VIP,實作於[期末專案](https://github.com/NCNU-OpenSource/hanetdb) * Monitor - 建個沒權限的使用者讓人戳,介紹於「[監控錯誤的發生](#監控錯誤的發生-Monitoring)」段落 * Failover - Group Replication * source 死掉時,其中一個 replica 要變更角色成為 source <!--* STONITH(Shoot The Other Node In The Head):當 source 死掉再復活時,他的資料已經落後生產環境了 (split-brain),不能直接上線,先擺進隔離區 (fencing) --> ::: info BlueT 補充:**CAP 定理(CAP theorem)** 在分散式系統中,不可能同時滿足下面三點: * 一致性 (Consistency):每次的讀取請求,都能取得最新資料。等同於所有節點都有同一份最新的資料 * 可用性 (Availability):每次請求資料時,都能得到非錯誤回應 * 分區容錯性 (Partition tolerance):就算網路出現問題導致有些資料遺失,整個系統仍然要可以繼續運作 這是個背後有許多討論空間的理論,在此僅略提起有此理論存在。 ::: ::: info BlueT 補充:**Raft Consensus Algorithm** 是一種共識演算法。在本章節所要介紹的 database replication 情境中,能解決「當 source 掛掉時怎麼選出新的 source」的這個問題。實際運作方式可參閱[此互動式網站](http://thesecretlivesofdata.com/raft/)。 ::: #### MySQL 叢集 DEMO * 以下用 Ubuntu 20.04 上的 MySQL 8.0.25 進行示範 * 檢查版本號碼 `mysql -V` * 若你的 MySQL 版本號碼小於 MySQL 8.0.22(或大約在 2020 年 7 月之前安裝) * 把所有大小寫 source 換成 master * 把所有大小寫 replica 換成 slave * 有兩台安裝完成的 MySQL Server * **source** 外界能對他**讀/寫** * **replica** 外界只能**讀** * 以下指令會因角色有所區分 1. 若 source 有防火牆,把他設為允許 replica 連入 * 在 **source** 上 `sudo ufw allow from replica-server-ip to any port 3306` 2. 在 **source** 上修改設定檔,預設有在裡面的可把 `#` 刪掉修改 * 在 **source** 上 `sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf` ```= bind-address = 192.168.57.7 server-id = 1 log_bin = /var/log/mysql/mysql-bin.log binlog_do_db = hadb ``` * 參數意思 * `bind-address` 填上你 source server 的 IP (可以用`ip a`看)。預設是 `127.0.0.1` 的話他只會接受來自本機的請求,所以要改才能讓 replica 進來 * `server-id` 只要和其他 replica 不重複就好 * `binlog_do_db` 填你想複製的 db 名字 * 如果需要複製更多 db,就多加幾行,像是 ```= binlog_do_db = db binlog_do_db = db_1 binlog_do_db = db_2 ``` * 接著重開 mysql `sudo systemctl restart mysql.service` 3. 建立要進來做複製的使用者,和給他權限 * 在 **source** 上,用 `sudo mysql` 或 `mysql -u <username> -p` 開啟 mysql 命令列 * mysql 命令列內 ```=sql mysql> CREATE USER 'replica'@'replica-server-ip' IDENTIFIED BY 'replica-password'; ``` * 參數意思 * `replica`、`replica-server-ip`、`replica-password` 等等設定 replica 會用到,他能從啥帳密從哪裡登進來 * 給予權限 ```=sql mysql> GRANT REPLICATION SLAVE ON *.* TO 'replica'@'replica-server-ip'; mysql> FLUSH PRIVILEGES; ``` 4. 取得 source 的 binary log coordinates * 在 **source** 上,mysql 命令列內 * 將整個表格鎖定,讓其他連線無法讀取及異動,直到資料處理完畢為止 ```=mysql mysql> FLUSH TABLES WITH READ LOCK; ``` * 取得目前 binary log 檔案名稱和座標位置,等一下在 replica 會用到 ```=sql mysql> SHOW MASTER STATUS; -------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 73 | hadb | | +------------------+----------+--------------+------------------+ ``` 5. 這裡假設 source 資料庫裡還沒有 hadb,建立一個全新的 :::warning 若在執行本教學這些步驟之前 source 裡已經有資料,且想要複製到 replica 的,可參考 [之前的自動化共筆](https://hackmd.io/LqiMw8qRS2usU_IpQBe-Yg#%E8%87%AA%E5%8B%95%E5%8C%96%E4%BE%8B%E5%AD%90%E5%AF%A6%E4%BD%9C) 進行 mysqldump。 > 為啥就算 binlog 有從頭記還是建議 dbdump?因為 replica 包含所有歷史更動,太多。dbdump 只有最終結果。 ::: * 在 **source**,mysql 命令列內 * 剛剛鎖住了,先解鎖才能修改 ```=sql mysql> UNLOCK TABLES; Query OK, 0 rows affected (0.00 sec) ``` * 建立一個全新的 hadb ```=sql mysql> CREATE DATABASE hadb; Query OK, 1 row affected (0.01 sec) ``` 6. 開始設定 replica * 在 **replica** 上,修改設定檔 * `sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf` ```= bind-address = 192.168.57.8 server-id = 2 log_bin = /var/log/mysql/mysql-bin.log binlog_do_db = hadb relay-log = /var/log/mysql/mysql-relay-bin.log ``` * 參數意思 * `bind-address` 填上你 replica server 的 IP (可以用`ip a`看) * `server-id` 只要不跟 source 和其他 replica 重複就好 * `binlog_do_db` 填你想複製的 db 名字,跟 source 的一樣 * 接著重開 mysql `sudo systemctl restart mysql.service` 7. 開始複製 * 在 **replica**,進入 mysql 命令列 `sudo mysql` * 在 **replica**,mysql 命令列內 ```=sql mysql> CHANGE REPLICATION SOURCE TO mysql> SOURCE_HOST='source_server_ip', mysql> SOURCE_USER='replica', mysql> SOURCE_PASSWORD='replica-password', mysql> SOURCE_LOG_FILE='mysql-bin.000001', mysql> SOURCE_LOG_POS=73; ``` * 參數意思 * `SOURCE_USER` 和 `SOURCE_PASSWORD` 是要填在 2. 給權限的那組帳密 * `SOURCE_LOG_FILE` 和 `SOURCE_LOG_POS` 填在 3. 拿到的資訊 * 在 **replica**,mysql 命令列內 `mysql> START REPLICA;` 8. 測試複製是否有在運作 * 在 **source**,mysql 命令列內 * 切換到有在複製的 DB 裡 ```=sql mysql> USE hadb; ``` * 建立新 TABLE ```=sql mysql> CREATE TABLE example_table ( mysql> example_column varchar(30) mysql> ); Query OK, 0 rows affected (0.07 sec) ``` * 加入一些資料 ```=sql mysql> INSERT INTO example_table VALUES mysql> ('This is the first row'), mysql> ('This is the second row'), mysql> ('This is the third row'); Query OK, 3 rows affected (0.02 sec) Records: 3 Duplicates: 0 Warnings: 0 ``` * 在 **replica**,mysql 命令列內 * 切換到有在複製的 DB 裡 ```=sql mysql> USE hadb; ``` * 顯示資料 ```=sql mysql> SHOW TABLES; +----------------+ | Tables_in_hadb | +----------------+ | example_table | +----------------+ 1 row in set (0.00 sec) mysql> SELECT * FROM example_table; +-------------------+ | example_column | +-------------------+ | this first row | | this second row | | this third row | +-------------------+ 3 rows in set (0.00 sec) ``` > 資料來源: [How To Set Up Replication in MySQL | DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-set-up-replication-in-mysql) ## 負載平衡 Load balancing * 把工作(load)分配下去給多台設備,讓每一部設備工作量維持平均,避免其中某一台過載 * 工作可以是外部 traffic / request / API call ,也能來自內部其他服務(例如前端 call 後端) * 如果 Cluster 其中一台伺服器掛了,Load Balancer 要負責把流量都導到健康的其他台上面 * 也能作為提供可擴縮性(Scalability)手段(這裡先不做討論) * 這裡討論由 Web Service 提供的 Load Balancer 幫我們做到的 Load Balancing ### L4 與 L7 的 Load balancing :::info **OSI Layer 示意圖**,但這裡主要介紹的還是只有 L4 與 L7 層的軟體 Load Balancer ![](https://i.imgur.com/FpFiebP.jpg) > 圖片來源:[Load balancing in Layer 4 vs Layer 7 with HAPROXY Examples - YouTube](https://www.youtube.com/watch?v=aKMLgFVxZYk&t=180) ::: * Layer 4 傳輸層資訊的分配機制 * 以實體的對外 IP 或 Virtual IP(VIP) 接收請求,再由 Load Balancer 做負載平衡 * 看 Source IP / port 或 Destination IP / port * Source: IP Hash(下面會介紹)、Destionation: 轉傳至特定機器 :::info 延伸閱讀: [九個硬體負載平衡器優於軟體負載平衡器的理由](https://blog.avinetworks.com/f5-vs-avi-networks) ::: * Layer 7 應用層資訊的分配機制 * 在 L4 的基礎之上 * 看請求內容 (URL, Cookie, Header) 來做 Load Balancer * 例如:在 Nginx 設定反向代理 (Reverse Proxy),讓 `http://example.com/app1 -> http://10.0.0.1:8080` 和 `http://example.com/app2 -> http://10.0.0.2:8080` :::info * **Layer 7** * 某些會有能力進行 SSL 加解密 * 除了負載平衡,某些也常被當作網路防火牆 (Web Application Firewall),阻絕外人直接存取內網資源 > BT 補充:有公司會額外買處理 SSL 的機器 (SSL 加速器) * **Proxy** * 外網是在 Proxy 與 Server 之間 * 內網是在 Proxy 與 Client 之間 * Caching * 假設內網的使用者請求的頁面在 Proxy 有暫存,可以直接回給使用者 * 好處: 減少外部流量,節省頻寬,加快回應速度 ![](https://i.imgur.com/IGUWvXD.png) * **Reverse proxy** * 外網是在 Proxy 與 Client 之間 * 內網是在 Proxy 與 Server 之間 * Caching * 假設外網的使用者請求的頁面在 Proxy 有暫存,可以直接回給使用者 * 好處: 減少 Server 負擔 * 但本章節的 Reverse Proxy 只有做到代理,而無暫存 * Client 只需與 Reverse Proxy 溝通,再由 Reverse Proxy 決定如何分配給 Server * 若 Server 調整設定後只需通知 Proxy,而使用者則不需知道任何更動 * Ex. 更換內網 IP or Port ![](https://i.imgur.com/uO7cFxP.png) > BT 補充:有個軟體叫 Varnish ,是很強的 reverse cache proxy ::: * 使用者端 * 給一個 server list 讓使用者端決定要連哪個伺服器 * DNS-based Load balancing * 根據來源 IP 的不同,發給你不同的 IP 產生 Load Balancing * 或一次回給你很多 IP,依照實作方式的不同(亂數或以第一個為主),達成 Load Balancing :::info 用 `host google.com` 看到的 IP 會根據不同來源位置,而產生不一樣的結果 ::: ### 在 L4 實作 Load Balance 方法 > 本節圖片來源:[Linux伺服器叢集系統(三)--LVS叢集中的IP負載平衡技術 | linux-vs.org (簡體中文)](http://www.linuxvirtualserver.org/zh/lvs3.html) :::info **Virtual IP** Virtual IP 為 Virtual Router Master 負責持有的IP ::: * NAT 模式 * Load balancer 收到 Client 的要求後,修改封包 Header * Load balancer,再將要求轉送給後端的其他伺服器處理 * 後端處理完要求後回給 Load Balancer * Load Balancer 收到後端的封包,修改封包 Header 後,再轉送給 Client * 缺點:容易造成 Load Balancer 過載 ![](https://i.imgur.com/kH4NMEB.jpg) * 封包修改範例 1. 連入封包 `SOURCE 202.100.1.2:3456 DEST 202.103.106.5:80` 2. Load balancer 改完轉傳給 real server `SOURCE 202.100.1.2:3456 DEST 172.16.0.3:8000` 3. 後端伺服器回覆給 Load balancer `SOURCE 172.16.0.3:8000 DEST 202.100.1.2:3456` 4. Load balancer 再回給使用者 `SOURCE 202.103.106.5:80 DEST 202.100.1.2:3456` * IP 隧道模式 (IP Tunneling) > IP 隧道經常用於連接兩個不是用路由直接連結的 IP 網路 Load Balancer 與 Real Server 的 IP 都必須是 Public IP Load Balancer 與 Real Server 都擁有相同的 VIP * Load balancer 先收到 Client 的要求,再根據演算法選出適合的後端伺服器 * Load balancer 將請求封包封裝 (encapsulation) 在另一個 IP 封包中,再將封裝後的 IP 封包轉送給選出的後端伺服器 * Real Server 處理完要求後再將回應封包直接返回給 Client ![](https://i.imgur.com/oy81Ook.gif) ![](https://i.imgur.com/mzxTdXQ.jpg) * Direct Routing Mode (DR) > Load Balancer 與 Real Server 位於相同的區域網路底下 * Load balancer 收到 Client 的要求後 * 將資料封包的 MAC 地址改為選出後端伺服器的 MAC 地址,再將修改後的資料封包傳給後端伺服器組成的區域網路 * 該負責的伺服器直接回應 Client 而不經過主伺服器。這樣效能比較不會卡在主伺服器上 :::info Real server 與 Load balancer 同時擁有相同的 VIP。但只有 Load balancer 設定的 VIP 地址是對外的;並且 Real Server 把 ARP 關閉,這裡的 VIP 對外部是不可見的,只是用於處理目標地址為該 VIP 的要求。 ::: ![](https://i.imgur.com/879KbED.gif) ![](https://i.imgur.com/kaCjRwx.jpg) | | NAT | TUNNEL | DR | | - | - | - | - | | Server | any | Tunneling | Non-arp device | | Server Network | private | LAN/WAN | LAN | | Server Connection | low (10~20) | High (100) | High (100) | | Server Gateway | load balancer | own router | own router | ### 常見的排程演算法 / 規則 [Comparing Load Balancing Algorithms - YouTube](https://youtu.be/iqOTT7_7qXY?t=54) 以下圖片來自這很棒的影片動畫圖解 * 隨機 * 循環法 (round-robin) * 大家公平輪流 ![](https://i.imgur.com/0LnTipd.jpg) * 加權循環法 (weighted round-robin) * 有其中一個人能力大於其他人時,能者多勞 * 例如有時候備用伺服器用弱一點的機器 ![](https://i.imgur.com/Ch26tYN.jpg) * 最少連線數 (least connections) * 當 Server 2 的使用者待在線上特別久,而 Server 1 已經沒人用了 * 再繼續輪流塞人的話,Server 2 會超忙,Server 1 超閒 ![](https://i.imgur.com/PmLZelq.jpg) ![](https://i.imgur.com/kp6ADju.jpg) * 基於局部性 (locality-based) * 最少連線數(Locality-Based Least Connections) * 找出該 IP 最近使用的伺服器 * 伺服器可用就直接發送請求 * 若不存在或超載,就以「最少連線數」的原則,選出可用的伺服器 * 備援式的最少連線數(Locality-Based Least Connections with Replication) * 找出該 IP 最近使用的伺服器組 * 以「最少連線數」的原則從伺服器組中選出伺服器 * 若伺服器組中的伺服器皆超載,則從其他未加入組的伺服器中,選出伺服器,並加入到伺服器組中 * 若經過一段時間後,組內有伺服器閒置,則該伺服器會被移出,用以提高叢集效率 > 參考資料:http://kb.linuxvirtualserver.org/wiki/IPVS * 各種基於雜湊的 (Hash-based) * 可能會看的東西(取決於是第幾層) * 使用者請求的 URL * Protocol 種類 * 來源位址 * 目的地位址 ### sticky / persistent session > sticky session 必要性:1. stateful 2. 且 session 存在 server,沒在任何 share database * 例如:登入狀態 * 同一個 session 盡量導到同一台機器上 * 用 source address / cookie 決定要分給哪台 * 那台機器中途死掉了怎麼辦 * 架構設計成讓其他資料庫 (ex: redis, memcache) 記住 session,再同步給 server * 或把資訊存在 client * 改成 stateless 的方式,降低對 sticky session 的依賴 ## 常見的 Load Balancer 軟體(或硬體) ### 硬體 #### F5 Big-IP #### Citrix Netscaler #### Radware ### 軟體 #### Linux Virtual Server * 運作在 TCP/IP 架構中的第 4 層(傳輸層) * 能設定的參數比較少,但因為沒有可太多設定的東西,可以減少人為出錯的機率 * 除了對 Web Server 做負載平衡外,也可以對 MySQL 做負載平衡 * 實作於[期末專題報告](https://github.com/NCNU-OpenSource/hanetdb) #### Nginx * 運作在 TCP/IP 架構中的第 7 層(應用層),可以針對 HTTP 應用做一些分流的策略,比如針對域名、目錄結構 * 安裝與設定較為簡單易懂,測試起來較為方便 * 僅支援 HTTP, HTTPS, E-mail,應用範圍較小 * 預設有三種 Load Balance 方法,Round Robin、weight 以及 ip_hash #### HAProxy * 做為 TCP 和 HTTP 應用程式的 high availability load balancer 和 proxy server * 可以實作 L4 或 L7 load balancer * HAProxy 可以對 MySQL 進行負載平衡,對後端的資料庫進行檢測和負載平衡。 <!-- > FIXME: 擴張啥 rephrasing > 他有啥特性,他超強 C100K > L4 能用什麼模式,L7 支援什麼應用 > session 量 > throughput 量 --> ## 常見的 High Availability 軟體 #### Keepalived * 提供 Load Balancing 的故障隔離與故障轉移 * 部署和使用非常的簡單,所有設定只需要一個設定檔即可以完成 * 用 Linux Virtual Server IPVS kernel module 來實作的 * 實作了 VRRP 協定 #### Heartbeat * Heartbeat 2.1.4 後拆分成 3 個子專案,安裝與使用比較複雜 <!-- 功能更強大,配套工具更全,適合做大型叢集管理 (編按: Citation needed) --> ##### VRRP 協定 ![](https://i.imgur.com/JJHnbCp.png) > 圖片來源: [Red Hat Enterprise Linux 7 Load Balancer 管理 | Red Hat](https://access.redhat.com/documentation/zh-tw/red_hat_enterprise_linux/7/pdf/load_balancer_administration/Red_Hat_Enterprise_Linux-7-Load_Balancer_Administration-zh-TW.pdf), [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/ "Creative Commons — Attribution-ShareAlike 3.0 Unported — CC BY-SA 3.0") * Virtual Router Redundancy Protocol,虛擬路由器備援協定 * RFC2338, 3768, [5798](https://datatracker.ietf.org/doc/html/rfc5798) * 虛擬路由器: 一個抽象的物件,有一個辨識號碼(VRID)和一組相關聯的 IP 位址(Virual IP, VIP) * 一個虛擬路由器底下有很多 VRRP 路由器,同 VRID 的成為同一群組 * VRRP 路由器可以分為:主控制路由器(master)與備援路由器(backup) * 會根據 priority 選其中一個 VRRP 路由器來作為 master,剩下是 backup * 對外有一個虛擬 IP (VIP),跟 master 產生關聯 (associate) * 只有 master 會回覆想問 VIP 是誰的 ARP 請求 * 若是 master 掛掉了,則從 backup 中依優先權再選出一個成為 master,是個 **Failover** 動作 * master 會定期 multicast 發送 ADVERTISEMENT * 若 backup 一段時間沒聽到 ADVERTISEMENT,就知 master 掛了 <!-- > 步驟這麼多,做個流程圖 UML --> ![](https://i.imgur.com/cb3jX2N.png) > 圖片來源: [Best Practices for Floating IP Addresses | Compute Engine 說明文件](https://cloud.google.com/solutions/best-practices-floating-ip-addresses#example_use_case_for_migration), [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0) ### DEMO * 主要架構 ![](https://i.imgur.com/z6gqR2o.png =400x) * 摘要 * ![DigitalOcean Referral Badge](https://web-platforms.sfo2.digitaloceanspaces.com/WWW/Badge%202.svg) [BT 的 Digital Ocean 邀請連結,你能夠獲得 100 美元的額度,而 BT 能夠獲得 25 美元的額度!還不快點加入!](https://m.do.co/c/7a14e0e84052) (需要信用卡才能註冊) * 這裡我們用 Keepalived 軟體,以監控叢集成員與叢集服務,並使用 Nginx 作為軟體的 Load Balancer * 本次 DEMO 利用 DigitalOcean 提供的 Float IP API 作為分配 Virtual IP 的手段 * 附註: 在 AWS 也有 Elastic IP 能使用 * 本次 DEMO 總共用到四台虛擬機,兩台前端用來做 Load Balance 以及 Failover,兩台後端用來做 Application Server * 建立虛擬機 * 先建立 droplet 這邊選擇 Ubuntu 18.04 ![](https://i.imgur.com/KXAnHJs.png =1020x) * 選最便宜的 ![](https://i.imgur.com/xiodt4D.png) * 選亞洲伺服器這樣延遲比較低 ![](https://i.imgur.com/tDJwnYP.png) * 選擇登入時的驗證方式,這邊自由隨意~~但是他密碼的要求有夠多~~ ![](https://i.imgur.com/zPjp5V0.png) * 這邊可以直接建立四台虛擬機 ![](https://i.imgur.com/Kps5urk.png) * 接下來直接透過 ssh 連入虛擬機 * 後端 * nginx ```=shell sudo apt-get update sudo apt-get install nginx ``` * 修改首頁方便我們辨識 ```=shell sudo vim /var/www/html/index.nginx-debian.html ``` * 全部刪掉然後打上 * 第一台 ```=html5 <h1>Primary</h1> ``` * 第二台 ```=html5 <h1>Secondary</h1> ``` * 前端 * keepalived * 首先安裝 nginx ```=shell sudo apt-get update sudo apt-get install nginx ``` * 直接修改預設設定 ```=shell sudo vim /etc/nginx/sites-available/default ``` * 新增 load balance 的設定 ```=shell upstream backend { server first_web_server_ip:80; server second_web_server_ip:80; } server { location / { proxy_pass http://backend.example.com; } } ``` :::info ``first_web_server_ip``,``second_web_server_ip``,``http://backend.example.com`` 都是要修改的地方 ::: * 安裝 keepalived ```=shell sudo apt install keepalived ``` * 接下來開始建立設定檔 ```=shell sudo vim /etc/keepalived/keepalived.conf ``` * on Primary :::info ``MASTER_private_IP``,``BACKUP_private_IP``,``password`` 都是要修改的地方 ::: ```=shell vrrp_script chk_nginx { script "pgrep nginx" # 檢查 nginx 是否有在行程 (Process) 中 interval 2 } vrrp_instance VI_1 { interface eth1 # 選擇網路介面卡 state MASTER # 宣告身分 (MASTER OR BACKUP) priority 200 # 優先權 virtual_router_id 33 # Keepalived 用來識別是否為同一個群組 unicast_src_ip MASTER_private_IP # 用來多播 (multicast) 的來源 IP unicast_peer { BACKUP_private_IP # 接收來自哪裡多播 (multicast) 的IP } authentication { auth_type PASS # 驗證方式 auth_pass password # 驗證密碼 } track_script { # 追蹤服務是否正常 (health check) chk_nginx } notify_master /etc/keepalived/master.sh # 當前節點成為 master 時,執行腳本 } ``` * on Secondary * Secondary 的地方也要喔,注意兩邊的 IP 互換 ```=shell vrrp_script chk_nginx { script "pgrep nginx" interval 2 } vrrp_instance VI_1 { interface eth1 state BACKUP priority 100 virtual_router_id 33 unicast_src_ip BACKUP_private_IP unicast_peer { MASTER_private_IP } authentication { auth_type PASS auth_pass password } track_script { chk_nginx } notify_master /etc/keepalived/master.sh } ``` * 接下來下載 DigitalOcean 提供的 Python script ```=shell cd /usr/local/bin sudo curl -LO http://do.co/assign-ip ``` * 建立 Float IP * 在 DigitalOcean 的設定頁面選擇 Networking -> Floating IPs </br>選擇剛剛建立的 Primary 後按下 Assign Float IP :::info 申請 Float IP 的用意,是讓使用者透過這個另外申請、能變更綁定 Server 的 Virtual IP 來存取服務,而不是直接去造訪兩台的 Real IP ::: * 建立 DigitalOcean API Token * 在 DigitalOcean 的設定頁面選擇 API , 按下 Generate New Token * 取個名字,然後就可以建立了 * 建立完的 Token 只會出現這一次,所以我們先複製起來 * 回到虛擬機,建立當主機掛掉時需要執行的腳本 * `DO_TOKEN` 變數要改成上一步拿到的 API Token * `IP` 變數要改成上一步拿到的 Floating IP ```=shell sudo vim /etc/keepalived/master.sh ``` :::info 這個腳本由 DigitalOcean 提供,用意是當某一台 Real Server 變成 Master state 的時候,會去檢查自身是否擁有 Floating IP,如果沒有就去跟 DO 要求將 Floating IP 導向自身 ::: ```=shell #!/bin/bash export DO_TOKEN='digitalocean_api_token' IP='floating_ip_addr' ID=$(curl -s http://169.254.169.254/metadata/v1/id) HAS_FLOATING_IP=$(curl -s http://169.254.169.254/metadata/v1/floating_ip/ipv4/active) if [ $HAS_FLOATING_IP = "false" ]; then n=0 while [ $n -lt 10 ] do python /usr/local/bin/assign-ip $IP $ID && break n=$((n+1)) sleep 3 done fi ``` ::: warning 先確定你的機器裡有安裝 Python 了,以及 Python requests module,因為這個 script 需要用到 `sudo apt install python python-requests` ::: * 接下來讓 ``master.sh`` 可以被 keepalived 執行 ```=shell sudo chmod +x /etc/keepalived/master.sh ``` * 可以來啟動我們的 keepalived 了 ```=shell sudo systemctl start keepalived ``` * 測試當 Nginx 故障 (Testing Nginx Failure) * 在 Primary 上輸入 ```=shell sudo service nginx stop ``` * 在數秒後 keepalived 將會把 Float IP 交給 Secondary * 在 Primary 上輸入 ```=shell sudo service nginx start ``` * 在數秒後 keepalived 將會把 Float IP 交給 Primary * 測試當伺服器故障時 (Testing Server Failure) * 在 Primary 上輸入 ```=shell sudo reboot ``` * 在數秒後 keepalived 將會把 Float IP 交給 Secondary * 在 Primary 開機完成數秒後 keepalived 將會把 Float IP 交給 Primary ## References [High-Availability Computer System in 1991](https://jimgray.azurewebsites.net/papers/ieee_HA_Swieorick.pdf) [Introduction to High Availability | Linode](https://www.linode.com/docs/guides/introduction-to-high-availability/) [High Availability tutorials, questions and resources | DigitalOcean](https://www.digitalocean.com/community/tags/high-availability) [Introduction to High Availability | Fusion Middleware High Availability Guide](https://docs.oracle.com/cd/A91202_01/901_doc/rac.901/a89867/pshavdtl.htm) [Overview of High Availability ](https://docs.oracle.com/database/121/HAOVW/overview.htm#HAOVW113) [Load Balancing | Administration Guide | SUSE Linux Enterprise High Availability Extension 15](https://documentation.suse.com/sle-ha/15-GA/html/SLE-HA-all/cha-ha-lb.html) [Synology High Availability White Paper](https://global.download.synology.com/download/Document/Software/WhitePaper/Package/HighAvailability/All/enu/Synology_SHA_White_Paper.pdf) [SLA服務可用性4個9是什麼意思?如何保證服務的高可用性 HA(High Availability)?_Kotlin 開發者社群 - MdEditor](https://www.gushiciku.cn/pl/pLMh/zh-tw) [高有效性 (High Availability) 初論 30 講 :: 2011 iT 邦幫忙鐵人賽](https://ithelp.ithome.com.tw/users/20000065/ironman/279) [HAProxy document](http://cbonte.github.io/haproxy-dconv/2.3/intro.html#3) [雲端服務品質不卡住?連 Google、微軟都在用的 SLA 你不能不懂! | TechOrange](https://buzzorange.com/techorange/2013/05/21/service-level-agreement-sla/) [之前 LSA 講如何建立 Cluster - Corosync and Pacemaker](https://hackmd.io/G0DdQLAiSemZv_sMbDagQg#The-way-to-create-the-cluster-Computing) [之前 LSA 講如何建立 Cluster - HAProxy](https://hackmd.io/@ncnu-opensource/book/https%3A%2F%2Fhackmd.io%2FxhFa9y3TQLCsYrL2WMOK5A) [MySQL 高可用方案及成功案例](https://www.slideshare.net/fanndywang/mysql-28141817) [章 2. Keepalived 總覽 Red Hat Enterprise Linux 7 | Red Hat Customer Portal](https://access.redhat.com/documentation/zh-tw/red_hat_enterprise_linux/7/html/load_balancer_administration/ch-keepalived-overview-vsa) https://www.keepalived.org/ [External HTTP(S) Load Balancing overview  |  Google Cloud](https://cloud.google.com/load-balancing/docs/https) 好多漂亮的圖解可以拿來用 [High Availability Cluster: Concepts and Architecture | NetApp](https://cloud.netapp.com/blog/cvo-blg-high-availability-cluster-concepts-and-architecture) 參考 Cluster 架構來自這 [Server Load Balancing – 阿喵就像家](https://mlwmlw.org/2011/04/server-load-balancing/) [國家教育研究院雙語詞彙、學術名詞暨辭書資訊網](https://terms.naer.edu.tw/) 避免使用支語,有英文轉中文的專有名詞都來查一下 [搜尋和下載國際詞彙 - Microsoft | 語言入口網站](https://www.microsoft.com/zh-tw/language/) 查台灣軟體技術用語的另一個好東西,雖然是 M 開頭,BT 別打我 OAO [make 簡單介紹](http://linux.vbird.org/linux_basic/0520source_code_and_tarball.php#intro_make) [make 簡單介紹2](https://www.cnblogs.com/tinywan/p/7230039.html) [libssl-dev](https://blog.csdn.net/WANG__RONGWEI/article/details/54898410) [build-essential](https://www.ubuntu-tw.org/modules/newbb/viewtopic.php?post_id=37689) [Module ngx_stream_upstream_module](https://nginx.org/en/docs/stream/ngx_stream_upstream_module.html#server) [Docker 容器的健康检查 - 张志敏的技术专栏](https://beginor.github.io/2018/03/11/healthy-check-instruction-of-docker.html) [Important Health Checks for your MySQL Master-Slave Servers](https://scalegrid.io/blog/important-health-checks-for-your-mysql-master-slave-servers/) [叢集檔案系統 - 維基百科,自由的百科全書](https://zh.wikipedia.org/wiki/%E9%9B%86%E7%BE%A4%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F) [proxy & reverse proxy](https://www.jyt0532.com/2019/11/18/proxy-reverse-proxy/) [三種 LVS 的模式:LVS-NAT、LVS-TUN、LVS-DR](https://blog.maxkit.com.tw/2016/05/lvs-lvs-natlvs-tunlvs-dr.html) [How To Set Up Highly Available HAProxy Servers with Keepalived and Floating IPs on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-highly-available-haproxy-servers-with-keepalived-and-floating-ips-on-ubuntu-14-04) #### 延伸閱讀 * L2 load balance: Link Aggregation * Cloudflare Anycast 原理概述於[期末專題報告](https://github.com/NCNU-OpenSource/hanetdb) * router multipath * [Introduction to modern network load balancing and proxying | by Matt Klein | Envoy Proxy](https://blog.envoyproxy.io/introduction-to-modern-network-load-balancing-and-proxying-a57f6ff80236) 介紹近代(文章寫作於 2018 年)的負載平衡,[中文翻譯 by Neil's Note](https://qazwsxedccsqzse.blogspot.com/2018/06/blog-post.html) * 裡面提到了 Google, Facebook, GitHub 各自有 open source 自己開發的 L4 Load balancer,我還好奇另外去查到 [LINE 自己開發的](https://speakerdeck.com/line_devday2019/software-engineering-that-supports-line-original-lbaas) * [binhnguyennus/awesome-scalability | GitHub](https://github.com/binhnguyennus/awesome-scalability#availability) 的 Availability 大標 * [分散式資料庫系統上的問題探討 (Web Archive)](https://web.archive.org/web/20220818072702/http://120.105.184.250/lwcheng/Kid51/kidpps/Kid51_CHAP14.pdf) [(原址已失效)](http://120.105.184.250/lwcheng/Kid51/kidpps/Kid51_CHAP14.pdf) by [Frank S.C. Tseng ](http://www2.nkfust.edu.tw/~imfrank) * [Google - Site Reliability Engineering](https://sre.google/books/) * [Improving load balancing with a new consistent-hashing algorithm | by arodland | Vimeo Engineering Blog | Medium](https://medium.com/vimeo-engineering-blog/improving-load-balancing-with-a-new-consistent-hashing-algorithm-9f1bd75709ed) * DNS-based Load Balancing - [EDNS Client Subnet](https://web.archive.org/web/20140702174224/http://www.afasterinternet.com/howitworks.htm)