-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.json
1 lines (1 loc) · 169 KB
/
index.json
1
[{"categories":["AWS"],"content":"前言 最近遇到客戶有上傳至 S3 的應用,因為客戶希望使用同一個域名,依據用戶所在國家,自動路由到最接近的 region 的 S3 bucket 進行上傳,以增進用戶上傳速度體驗。 由於 S3 的機制,有一些限制導致無法直接使用 Rotue53 加上 Geolocation routing,實際做起來會比想像中複雜。 而且客戶因為在中國也有業務,也有使用中國的 S3 bucket,因此無法直接使用 S3 multiregion access point。 本文主要紀錄實現步驟,以滿足這項需求。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:1:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"建立 S3 bucket 首先建立兩個 S3 bucket,這邊以 us-east-1 以及 ap-east-1 (香港) 作為示範,因為香港的 S3 剛好不支援 multiregion access point。 bucket name: o3r-us region: US East (N. Virginia) us-east-1 bucket name: o3r-hk region: Asia Pacific (Hong Kong) ap-east-1 接著各上傳一個 index.html 做為測試,其實稍後會使用 CloudFront signed URL 進行上傳。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:2:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"建立 Cloudfront distribution 建立一個 Cloudfront distribution,Origin domain 可以隨便選一個 bucket,因為接下來我們會透過 Lambda@Edge 依據用戶地理位置選擇適當的 S3 bucket 作為 origin。 CloudFront 設定: Origin Origin domain: o3r-us.s3.us-east-1.amazonaws.com Origin path: 空 Origin access: Legacy access identities 點選 Legacy access identities 下的 Create OAI,建立完成後在 Origin access identity 選擇剛才創建的 OAI,Bucket policy 下選擇 Yes, update the bucket policy。 Default cache behavior Viewer protocol policy: HTTP and HTTPS 或 Redirect HTTP to HTTPS 皆可,建議選擇後者 Allowed HTTP methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE Cache key and origin requests 為了簡單化步驟,選擇 Legacy cache settings Headers 選擇 Include the following headers,勾選 CloudFront-Viewer-Country 拉到下方的 Settings 區塊 Alternate domain name (CNAME) 選擇 Add item,輸入您想提供用戶存取 S3 的域名,這邊以 s3domain.o3r.moe 做示範 Custom SSL certificate 有在 ACM 申請的話,也一起加入。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:3:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"添加另一個 S3 bucket 作為第二個 S3 Origin 在 CloudFront 的 Origin 設定中,新增一個 Origin。 Origin domain: o3r-hk.s3.ap-east-1.amazonaws.com Origin path: 空 Origin access: Legacy access identities (設定步驟請參考上述第一個 S3 origin 的設定,並選擇相同的 OAI) ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:3:1","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"修改兩個 S3 bucket 權限,允許 s3:PutObject 行為 前往 S3 頁面,編輯兩個 bucket 的 policy,添加 s3:PutObject 如下: { \"Version\": \"2008-10-17\", \"Id\": \"PolicyForCloudFrontPrivateContent\", \"Statement\": [ { \"Sid\": \"1\", \"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E34XXXXXXX9QA\" }, \"Action\": [\"s3:GetObject\",\"s3:PutObject\"], \"Resource\": \"arn:aws:s3:::bucket-name/*\" } ] } ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:3:2","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"透過 CloudFront Signel URL 讓只有允許的用戶存取 S3 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:4:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"建立 Cloudfront Key Pair 提供 CloudFront signed URL 使用 CloudFront signed URL 是使用我們自行生成的 RSA 金鑰對,public key 會放在 CloudFront Key Group 中,private key 會放在 AWS Secrets Manager 中,我們會透過撰寫 Lambda 來獲得 Secrets Manager 中的 private key 產生 CloudFront Signed URL,用戶獲得這個 URL 便可透過 CloudFront 上傳或下載背後的 S3。 首先在 Linux 執行以下指令: openssl genrsa -out cf_private_key.pem 2048 openssl rsa -pubout -in cf_private_key.pem -out cf_public_key.pem 接著複製 cf_public_key.pem 的內容 (請連同 BEGIN PUBLIC KEY 一起複製) $ cat cf_public_key.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8hOjjASNcMJYPgoLTb89 (中間省略) fxTZtxWf73lV5wcWbnBiQfYn7Up+glu05IzOK43D4vVjtAgYEg2XlpzoMKybkOCL CQIDAQAB -----END PUBLIC KEY----- 前往 CloudFront Public Key 頁面,點選 Create publie key,將以上內容貼在下方 Key 中。 請記下 Public Key 的 ID (例如 K2XXXXXXXXXXXX),稍後建立 Lambda 會使用到。 創建完成後,前往 Key Groups,選擇 Create Key Group,Public Keys 選擇剛才創建的 public key。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:4:1","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"將 private key 上傳至 Secrets Manager 前往 AWS Secrets Manager. 選擇一個區域 (us-east-1) 選擇 Store a new secret. Select secret type 選擇 Other type of secrets. Specify the key/value pairs to be stored in this secret 選擇 Plaintext. 將之前產生的 cf_private_key.pem 內容貼上 接著下一步,為密鑰命名 其餘設定先不管他,我們直接下一步直到點選 Store 記下 Secret 的名稱 以及 ARN,接下來會用到。如果沒看到東西,請重整頁面 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:4:2","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"建立生成 CloudFront Signed URL 的 Lambda 這是第一個 Lambda function,請參考以下步驟: 在 Lambda 主控台選擇和剛才 AWS Secrets Manager 相同的區域 選擇 Create function. 選擇 Author from scratch. 為 Function 命名 Runtime 為 Node.js 12.x. 在 Change default execution role 下方選擇 Create a new role with basic Lambda permissions. 選擇 Create Function,並將以下程式碼覆蓋貼上: const AWS = require('aws-sdk'); const secretsManager = new AWS.SecretsManager({region: process.env.awsRegion}); const crypto = require('crypto'); const replacementChars = {'+':'-', '=':'_', '/':'~'} const getKeyFromSecretsManager = () =\u003e { return new Promise((resolve, reject) =\u003e { secretsManager.getSecretValue({SecretId: process.env.awsSecretsManagerSecretName}, (err, data) =\u003e { if (err) { console.log (\"Get Secret Error\", err); return reject(err) } console.log(\"Private key retrieved\"); return resolve(data.SecretString); }); }); } exports.handler = async (event, data, callback) =\u003e { let expiration = new Date(event.queryStringParameters.expiration)/1000|0; let baseUrl = event.queryStringParameters.baseUrl; let cannedPolicy = { \"Statement\":[ { \"Resource\": baseUrl, \"Condition\":{ \"DateLessThan\":{ \"AWS:EpochTime\": expiration } } } ] }; cannedPolicy = JSON.stringify(cannedPolicy); let encodedPolicy = new Buffer.from(cannedPolicy).toString(\"base64\"); encodedPolicy = encodedPolicy.replace(/[+=/]/g, m =\u003e replacementChars[m]); const signer = crypto.createSign('RSA-SHA1'); signer.update(cannedPolicy); let signedPolicy = signer.sign(await getKeyFromSecretsManager(), 'base64'); signedPolicy = signedPolicy.replace(/[+=/]/g, m =\u003e replacementChars[m]); const paramDelimiter = (baseUrl.indexOf('?') === -1) ? '?' : '\u0026'; const cfSignedUrl = `${baseUrl}${paramDelimiter}Expires=${expiration}\u0026Signature=${signedPolicy}\u0026Key-Pair-Id=${process.env.amazonCloudFrontKeyPairId}`; callback(null,cfSignedUrl); } 在 Lambda environment variables 中新增以下環境變數 : awsRegion: “us-east-1” // 區域名稱 amazonCloudFrontKeyPairId: “K2XXXXXXXXXXXX” // CloudFront 上的 Public Key ID awsSecretsManagerSecretName: “your_secret_name” // secrets manager 的密鑰名稱 儲存 (File \u003e Save) 並 deploy 修改剛才建立 Lambda 過程中建立的 IAM Role policy,新增以下的內容讓 Lambda 有權限讀取 secrets manager 中的密鑰,請記得將 ARN 替換為您剛才建立的 secrets manager 密鑰 ARN。 { \"Effect\": \"Allow\", \"Action\": \"secretsmanager:GetSecretValue\", \"Resource\": \"arn:aws:secretsmanager:us-east-1:8xxxxxxxxxx6:secret:your_secret_name\" } 接著在 Lambda 進入 Configuration 頁籤,選擇 Function URL,點選 create function URL,Auth type 選擇 NONE。您會獲得一個 Function URL。 使用以下指令測試: curl https://7xegi64psvsqbxr3dgljq6hlve0vnoki.lambda-url.us-east-1.on.aws/?baseUrl=https%3A%2F%2Fs3domain.o3r.moe%2Findex.html\u0026expiration=12%2F12%2F2022%2012%3A30%3A30%20EST 這樣用戶/APP 可以透過這個 URL 發送請求來獲得 CloudFront 的 signed URL,讓用戶只能透過 APP 獲得連接 CloudFront 的權限。 我們透過兩個 query string 提供資訊給 Lambda function URL,baseUrl 是允許用戶訪問的 URL 路徑,expiration 則是指定 signed URL 的時效,query string 格式如下: 名稱:baseUrl,值: https://xxxx.cloudfront.net/path1/.../pathN 名稱:expiration,值: 例如 12/12/2022 12:30:30 EST,只要 JavaScript Date.parse() 可以辨識的格式都可以,詳細可參考 Date.parse() 的說明。 注意: 以上 query string 的\"值\"必須經過 URL encode 的字串處理。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:4:3","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"建立 Lambda@Edge function Lambda@Edge 就是一透過 Lambda function 的程式碼客製化 CloudFront 的行為,並佈署到全球的 CloudFront 節點上實現功能。 這會是第二個 Lambda function, 請注意,Lambda 必須使用 us-east-1 區域,才能佈署到 Cloudfront 的 Lambda@Edge。 參考 [3] 新增 Lambda function,runtime 選擇 Node.js 16.x,Change default execution role 選擇 Create a new role from AWS policy templates,在 Policy templates 選擇 Basic Lambda@Edge permissions (for CloudFront trigger)。 名稱命名為 s3-bucket-country-based-routing。 建立完成後,將以下程式碼覆蓋到 Lambda 程式碼編輯視窗內。 'use strict'; exports.handler = (event, context, callback) =\u003e { const request = event.Records[0].cf.request; const headers = request.headers; const origin = request.origin; //Setup the two different origins const usBucket = \"o3r-us.s3.us-east-1.amazonaws.com\"; const hkBucket = \"o3r-hk.s3.ap-east-1.amazonaws.com\"; if (headers['cloudfront-viewer-country']) { for (let i = 0; i \u003c headers['cloudfront-viewer-country'].length; i++) { if (headers['cloudfront-viewer-country'][i].value == 'US') { headers['host'] = [{key: 'host', value: usBucket}]; origin.s3.domainName = usBucket; origin.s3.region = 'us-east-1'; break; } else if (headers['cloudfront-viewer-country'][i].value == 'CN') { headers['host'] = [{key: 'host', value: hkBucket}]; origin.s3.domainName = hkBucket; origin.s3.region = 'ap-east-1'; break; } } } callback(null, request); }; 接著點選 Test 旁的 Deploy 佈署新版本,並切換到 Versions 頁籤,點選 deploy new version。 接著頁面會導向到 Lambda 版本頁面,請複製 ARN,ARN 是有版本區別的,請注意結尾的數字就是版本。例如: arn:aws:lambda:us-east-1:123456789012:function:s3-bucket-country-based-routing:1 接著回到 Cloudfront 頁面,在 Bahavior 編輯 Default(*) behavior,在最下方 Function associations 的 Origin request 選擇 Lambda@edge,並貼上剛才複製的 ARN。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:5:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"Route53 在 Route53 新增一筆紀錄,對應到剛才新建的 Cloudfront。 如果 CloudFront 和 Route53 域名是位於同一個帳戶,可以使用 A record type 加上開啟 Alias,並且輸入 CloudFront 的域名。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:6:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"測試結果 我在北京 AWS 的 EC2 透過 curl 測試,CloudFront 確實按照請求來源國家,路由到指定對應的 bucket: 測試下載檔案: curl https://s3domain.o3r.moe/index.html\\?Expires\\=1670866230\\\u0026Signature\\=iv7CdZyY3P9rYk....90xijA__\\\u0026Key-Pair-Id\\=KXXXXXXXX6 輸出為一個我之前放在香港 bucket 的識別用網頁: \u003ch1\u003e Bucket in HK \u003c/h1\u003e 測試上傳檔案 (PUT 請求): curl -sv --upload-file ‘test.txt’ ‘https://s3domain.o3r.moe/path/test?Expires=1670866230\u0026Signature=DO-THpe.....zrOQ__\u0026Key-Pair-Id=KXXXXXXX6’ 到對應的 bucket 檢查,確實檔案上傳至 CloudFront signed URL 指定的路徑。 另外在 us-east-1 測試路由效果: 測試下載檔案: curl https://s3domain.o3r.moe/index.html\\?Expires\\=1670866230\\\u0026Signature\\=iv7CdZyY3P9rYk....90xijA__\\\u0026Key-Pair-Id\\=KXXXXXXXX6 輸出為一個我之前放在 us-east-1 bucket 的識別用網頁: \u003ch1\u003e Bucket in US \u003c/h1\u003e 測試上傳檔案,檔案上傳到 us-east-1 bucket。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:7:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"結語 建置過程較複雜,因為服務上有許多限制,導致必須 coding 並使用 Lambda@Edge,加上 CloudFront Signed URL 導致架構變得複雜。 以下是一些 Q\u0026A 以及限制的說明: Q: 不能透過 Route 53 Geolocation Routing 實現嗎? A: 想得太簡單,依據 RFC 7230 規定: A client MUST send a Host header field in all HTTP/1.1 request messages 所以 client 會依據 Route 53 的 domain 作為 Host header,然而 S3 有一個限制,如果要使用 Route53 + S3,bucket 名稱必須就是 domain name。 例如: Route53 example.com CNAME –\u003e S3 bucket example.com.s3.amazonaws.com。 S3 是依據 client 發送的 Host header 來判斷請求要路由至哪個 bucket,因為不同 bucket 會共用相同的 S3 伺服器(i.e. 共用 IP pool),因此 S3 也必須透過這個方式在多租戶環境下進行區別。 所以不可能建立第二個相同名稱的 bucket,這是 s3 的限制。 Q: S3 multiregion access point 呢? A: 客戶情境是有些 bucket 在不支援 multiregion access point 的 region 中。 Q: 那 Route 53 Geolocation Routing 加上一層 CloudFront 再串 S3 呢? A: CloudFront 自己沒有 Geolocation Routing 的功能,照這樣變成要使用兩個 CloudFront 並提供給 Route53 兩個 Geolocation Routing 紀錄使用。 但是 CloudFront 也是透過 Host header 判斷路由至哪個 distribution,所以最後問題會和 Route53 + 純 S3 一樣不可行。 ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:8:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["AWS"],"content":"參考 [1] Dynamically Route Viewer Requests to Any Origin Using Lambda@Edge [2] Authenticating Requests: Using Query Parameters (AWS Signature Version 4) [3] Create your function [4] Example: Amazon CloudFront Signed URLs using Lambda and Secrets Manager ","date":"2022.09.01","objectID":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/:9:0","tags":["aws","s3","cloudfront","route53","l@e"],"title":"透過相同域名,將用戶依據地理位置路由至對應 S3 bucket","uri":"/using-single-domain-routing-to-s3-buckets-in-different-regions-based-on-client-location/"},{"categories":["Keyboards"],"content":"前言 第一次使用機械式鍵盤大約是在大學唸資工系時,因為寫程式需要爽感才寫得下去,因此入手一個雜牌青軸。 一直到研究所時,陸續換了 Ducky One 2 櫻桃紅軸和 FILCO Majestouch 2 HAKUA 茶軸,大約接觸機械鍵盤四年了,總覺的不是自己喜歡的手感。 在某次聽 Kana 提到她有在玩 Customized Keyboard (客製化鍵盤),她使用的正是 AARU TKL,當下我宛如發現新天地:原來機械鍵盤中還有比 Filco 厲害的存在 (價格上XD),而且更好看、手感也更好。 當時查了 AARU TKL 真的對他的價格印象深刻,不過當時在台灣我找不太到資訊 (大概一年前),但這把 AARU TKL 就不斷放我腦子裡,心想總有一天我要入坑。 這篇紀錄我第一次組裝客製化鍵盤的過程,包含潤軸、搞衛星軸的過程分享,希望供新手入坑參考,若過程有錯誤也歡迎留言指正 \u003c(_ _)\u003e ","date":"2022.04.18","objectID":"/aaru-tkl/:1:0","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"Helix Lab 與 硬派 AARU TKL 是 Helix Lab 設計的第一款鍵盤,創辦人佛屠在網站上簡單說明他設計這款鍵盤的靈感來源,其中主要是來自刺客教條:起源 (Assassin’s Creed Original, ACO)中的古埃及意象和視覺感受,因此才想設計一款帶有聖書體(古埃及文字)設計的鍵盤: Inspiration of this design comes from an amalgamation of many traits, among which the most direct one being the game Assassin’s Creed Origin (ACO) I recently played. I was thoroughly amazed by the views and abundance of details of ancient Egypt that ACO has recreated, and thought to myself wouldn’t it be nice to have a keyboard that’s embossed with hieroglyphs like on walls or steles or papyri. Helix Lab 官方網站 Helix Lab 官方 Discord 直到 2022 三月,突然看到硬派精璽開了團購而且有現貨,因此決定著手組裝我的第一把客製化鍵盤。 硬派 Discord 在台灣玩客製化鍵盤,真的推硬派,他們什麼零件/工具都有,潤軸 (Lube) 的潤滑油 (Grease)、軸間紙 (Film) 和吸音棉等等,以及拔軸器/開軸器等等應有盡有,讓你不用跑遍蝦X還不一定找的到貨,也不用逛敝司的微笑A電商網站從國外買。 絕對不是頁配,這部落格沒什麼人看也不會有人掏錢請我寫這個,單純分享。 ","date":"2022.04.18","objectID":"/aaru-tkl/:2:0","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"開箱 AARU 的外盒就充滿古埃及的設計,但說實在左下那個法老王的表情也太可愛了吧哈哈哈,像是 OVO 的樣子 AARU TKL 外盒\" AARU TKL 外盒 內容包含: Top \u0026 Bottom Frame (鋁合金上下蓋) Plate + PCB (1.5mm 鋁定位板和1.6mm RGB熱插拔PCB板,原廠另外有焊接板,不過硬派GB是熱差拔版) Underglow diffuser (底光擴散器) Back Bar + TypeC badge (後條和Typc-C接口) 六角螺絲(2.5) SD卡 (說明書、QMK 工具程式、VIA和漂亮桌布) 1, 3, 4 已經鎖在一起了,因此組裝時需要再將上下蓋拆開。 這些內容等等會在分享組裝過程中一起放上照片。 ","date":"2022.04.18","objectID":"/aaru-tkl/:3:0","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"開始組裝 因為我偏好線性軸體,因此住次挑了櫻桃紅軸,而且我想潤軸並加上軸間紙,因此買了以下的工具和零件 (如果是新手第一次玩,建議參考以下清單以免漏買零件QQ): Cherry MX red * 100 顆,雖然 TKL 只需要 87 鍵,建議多買一些以免把軸體拆開時弄壞,也可以解決良率問題。 Krytox 205g0 潤滑油 軸間紙 (我選擇 0.13mm,手感會比 0.15mm 軟) 開軸器 拔軸器 潤軸筆 夾軸器 TYPE-C USB捲線 (這是玩客製化鍵盤必備,一定要用卷線!) 鍵帽 靜音棉 (墊在PCB板下面吸音用,Optional 不一定要買) 衛星軸 (須支援1.6mm pcb,2U*4 + 6.25U *1) 六角螺絲起子組 ","date":"2022.04.18","objectID":"/aaru-tkl/:4:0","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"潤軸 首先將軸體用開軸器拆開,Cherry 軸旁邊有四個溝槽,對到開軸器 4 個角壓下去就可以打開了。 打開後將四個部份(軸的上蓋、下蓋、軸心和彈簧)分開,需要家刷上潤滑油的地方主要是軸的下蓋和軸心。 軸心因為很小,因此用夾軸器夾住十字的部份,需要刷上潤滑油的地方就是組件之間會接觸摩擦的地方,所以我是潤軸心的四個面: 紅軸的軸心\" 紅軸的軸心 尤其是其中兩側的凸起部份,這會和軸體下蓋的凹槽產生摩擦: 紅軸的軸心 - 凸起部份\" 紅軸的軸心 - 凸起部份 注意潤滑油的量不要太多 (Less is more!),原則就是不要刷上去還看得到白色的油脂,如果看到白色油脂代表潤軸筆沾太多油了 潤軸中\" 潤軸中 接著潤軸體的下蓋,我潤的地方是下圖黃色圓圈部份的凹槽,以及底部用筆刷轉一圈潤一下: 潤軸中\" 潤軸中 接著加上軸間紙,軸間紙主要是減少軸心(紅色部份)的晃動 (to reduce wobbling),會稍微讓軸變得比較緊,手感會紮時一些 加上軸間紙\" 加上軸間紙 加上軸間紙\" 加上軸間紙 網路上有些人會潤彈簧,但是我懶。 放好軸間紙之後,將彈簧、軸心依序放到下蓋上,就可以將軸體組回去: 把軸體組回去\" 把軸體組回去 這樣的動作要重複 87 次潤好 87 顆軸,我剛好是一邊看 Kana 打 Overwatch 一邊用,輕鬆悠閒 (離題了)。 ","date":"2022.04.18","objectID":"/aaru-tkl/:4:1","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"PCB板測試 這個是熱差拔版本的 PCB,可以看見每個 switch 都有自己的插槽,而且每個按鍵都已經焊上 LED 了。 附帶一題, AARU 的 firmware 有分「熱差拔 PCB」 以及 「焊接版 PCB」 兩種,更新 firmware 時才要注意。 PCB板 正面\" PCB板 正面 PCB 背面可看見印上金色字體 “PCB designed by helix lab” 以及 logo PCB板 背面\" PCB板 背面 PCB板 背面,可見熱差拔插槽\" PCB板 背面,可見熱差拔插槽 建議先測試 PCB 板,避免整個組裝好後發現某個按鍵沒反應,才懷疑是 PCB 的問題還是軸體的問題。 推薦使用 Switch Hitter 這個軟體進行測試,可以拿金屬鑷子之類的工具,輕輕接觸熱差拔插槽兩端金屬,形成通路來測試。 PCB板 測試\" PCB板 測試 Switch Hitter\" Switch Hitter ","date":"2022.04.18","objectID":"/aaru-tkl/:4:2","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"衛星軸安裝 好再來衛星軸\" 好再來衛星軸 我這次使用的是 好再來衛星軸,他的好處是出廠就已經調直鋼絲,我使用絕對平坦表面(手機螢幕)測試,真的很直了,不用再特別調直。 真的很直\" 真的很直 衛星軸當然也是要潤軸一下 接著是衛星軸的組裝,我找到下面這個網站有很詳細的說明和示意圖,圖片引用自: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 好再來衛星軸的零件與下圖類似,潤軸的部份是 Housings 內側與 Stems 外側,以及鋼絲 (Wire) 插入 housing and stem 的地方。 source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6\" source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 衛星軸潤軸 source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6\" 衛星軸潤軸 source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 潤軸完畢,將 Stem 插入 Housing,注意紅色圈圈的地方,Stem 上面有一個缺口,插入 housing 要對應到橘色圈圈 (有鎖螺絲的地方),要在同一側。 source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6\" source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 將鋼絲插入 stem 下方 的洞口,如圖: source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6\" source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 接著將鋼絲稍微用力卡進 housing 的卡扣裡面固定: source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6\" source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 接著因為好再來應該是支援 1.6mm 的 PCB,不過他有附墊片,AARU 其實是 1.5mm 的,因此先將衛星軸附的墊片黏上PCB 黏上墊片\" 黏上墊片 接著拿出 PCB,找到安裝衛星軸的洞口:先將 Housing 突出的地方卡入較大的洞口(圖中橘色標示的洞口),接著在圖片中紅色標示的洞口處鎖上螺絲 source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6\" source: https://www.keyboard.university/guides/using-screw-in-stabilizers-7nxj6 完成 衛星軸裝好惹\" 衛星軸裝好惹 ","date":"2022.04.18","objectID":"/aaru-tkl/:4:3","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"安裝軸體 先在 PCB 上面鋪上減震橡膠墊,再放上定位板,注意洞口要對齊 在四個角落的地方先插上軸體,以固定 PCB/減震墊及定位板。 減震墊\" 減震墊 放上定位板\" 放上定位板 接著插上所有軸體 Cherry Reds On PCB\" Cherry Reds On PCB 注意安裝軸體/更換軸體時,一定要將 PCB 拿出外殼才插上軸體,我看到有國外朋友 PCB 先裝在鍵盤外殼內才插上軸體,結果熱差拔插槽脫落的悲劇發生… ","date":"2022.04.18","objectID":"/aaru-tkl/:4:4","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"外殼組裝 鋁合金外殼\" 鋁合金外殼 包裝內的外殼上下蓋原本是裝在一起的,需要分離,要準備六腳螺絲起子拆開。 上蓋的部份: 鋁合金外殼 上蓋\" 鋁合金外殼 上蓋 將 PCB 鎖到上蓋 PCB 鎖到鋁合金上蓋上面\" PCB 鎖到鋁合金上蓋上面 接下來是下蓋,AARU 最帥氣的金色荷魯斯之眼,需要鎖在下蓋 荷魯斯之眼\" 荷魯斯之眼 荷魯斯之眼完美鑲嵌在下蓋\" 荷魯斯之眼完美鑲嵌在下蓋 下蓋有 Designed By Helix Lab 字樣,十分精緻 雖然看不到 Designed By Helix Lab\" Designed By Helix Lab 在下蓋鋪上靜音棉,注意靜音棉的尺寸不要擋到 LED 底光擴散器,才能讓 PCB 上的 LED 亮光從鍵盤背部擴散開來。 最後將上下蓋鎖上,這樣只剩安裝鍵帽了。 本體組裝完成\" 本體組裝完成 ","date":"2022.04.18","objectID":"/aaru-tkl/:4:5","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"鍵帽 鍵帽的選擇對我而言十分困難,因為個人這次想要的鍵帽是:白色、PBT、兩色成型 (不要熱昇華或雷雕)、英文、不透光、非Ducky製以及非淘寶。 剛好硬派有出 108鍵 黑白 PBT二色鍵帽組,符合上條件 (雖然不是全白),但是價格划算,因此先暫時使用這款。 之後來等等看 Mass Drop 出我喜歡的款式及 profile 吧。 硬派 黑白PBT二色鍵帽組\" 硬派 黑白PBT二色鍵帽組 ","date":"2022.04.18","objectID":"/aaru-tkl/:4:6","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"晒圖 組裝完成就可以拍照晒圖 AARU TKL\" AARU TKL 外殼作工精細,可以看見埃及象形文字的浮雕 AARU TKL\" AARU TKL 底光擴散器將 PCB 最上排的燈光從背部擴散出來,有光害、才有厲害! AARU TKL\" AARU TKL AARU 的 LED 燈光可以用鍵盤 menu 鍵調整亮度、模式及彩度,我超喜歡用漸層模式搭配低彩度,可以當作非常有格調的氣氛燈。 AARU 附贈的 SD 卡內有說明書,有寫到燈光設定的方式以及韌體更新以及 Keymap 等等。 AARU SD卡\" AARU SD卡 ","date":"2022.04.18","objectID":"/aaru-tkl/:4:7","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Keyboards"],"content":"結語 我的感想是…好爽喔!終於入坑了哈哈。 AARU 的手感真的很棒,絕對勝過那種 5000 塊的鍵盤。因為鋁定位板和鋁合金外殼的關係,整體的塑膠感也降低,非常沉穩,當然本體也重了不少。 我認為 AARU 已經是藝術品般的存在,但是作為重要的生財器具我覺得非常值得, 在接觸機械鍵盤的旅程中,AARU 開啟了我對客製化鍵盤的視野,它將會陪伴我一同開創事業上班賺錢許久,不過,我相信這只是一個開端,日後我還會嘗試不同線性軸體以及 65% 客製化鍵盤,但是 AARU 在心中已經是不能被取代的地位。 ","date":"2022.04.18","objectID":"/aaru-tkl/:5:0","tags":["Peripherals"],"title":"Helix Lab AARU TKL 客製化鍵盤,第一次組裝就上手","uri":"/aaru-tkl/"},{"categories":["Thoughts"],"content":"在 2020 年,剛好是我在臺北科技大學唸研究所的時候,經過重重關卡,很幸運能進入 AWS 做 Cloud Support Intern。 但是寫這篇感想文時,已經是 2022 了 (也已轉為正職),所以透過回顧的方式,將記憶中能撈出來的寫下來。 文筆沒有很好還請見諒。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:0:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"第一次接觸 這是更早以前的事,發生在2019的一個颱風天週末。 當時參加AWS為學生舉辦的「雲端技術支援工程師體驗營」,為期兩天的活動,由AWS的工程師帶大家進行一連串 Hands-on labs,活動第二天下午則是刺激的 Troubleshooting 實戰考試 (對,他們說這攸關面試錄取機會)。 當時了解到 雲端技術支援工程師Cloud Support Engineer 的工作內容,也覺得蠻適合「不想做軟體開發(SDE)工作」的我。 不過礙於還有前一份打工的合約,只好再等等,順便繼續點技能樹。 關於 Cloud Support Engineer 的工作內容,我打算在另一篇文章分享。 Disclaimer: 這篇分享文屬於個人觀點,不能代表公司言論,且招聘情況/政策會隨時變化。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:1:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"面試 2020年正好研究所課都差不多修完,剛好 amazon.jobs 也有台北 Cloud Support Intern 的缺,就投了。 雖然實習生不用協助解決客戶的技術問題,但是 Cloud Support Intern 未來是要成為 Cloud Support Associate,因此你必須具備足夠的技術知識。 Cloud Support Associate 或 Engineer 會依據你專精的領域,分為不同的 Profile,面試前須選擇一個 Profile ,recruiter 會安排相同 profile 的面試官與你面試。 之前對網路比較有接觸,所以我是選了 Networking,其餘 profile 可以參考 JD。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:2:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"面試準備小建議 面試前大概準備了兩週,有幾項需要準備: profile 涵蓋的基礎知識: 以 networking 為例,可以著重在複習 Layer 3 以上的東西 (IP, TCP/UDP, NAT, DHCP, HTTP/S, TLS, DNS…etc.可能還有很多) 盡可能回想,曾經遇過的幾個 困難的 Troubleshooting 經驗,以及除錯過程的細節,如果面試官問到,就可以拿出來討論。面試官會依據你提及的相關技術,進一步 Dive deep。 Leadership Principles: 收集一些你過去實習/分組專案等等的故事,展現你符合 Leadership Principles 內的特質吧,這個非常重要哦! ","date":"2022.02.21","objectID":"/aws-internship-experience/:2:1","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"面試過程 一開始先做 Online Assessment,這個可以在規定的期限內,挑一個方便的時間完成。過關的話,會進入一連串的面試。我的情況是大約 2-3 位工程師面試,加上一位主管進行面試,每次一小時。當時分兩天進行,因為武漢肺炎的關係改採 Chime 線上面試。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:2:2","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"Day One Day one @AWS (那個鍵盤不是我的)\" Day one @AWS (那個鍵盤不是我的) 第一天我就發現,公司有非常多內部系統,而且開發得非常完善,當然我必須趕緊熟悉這些工具、以及各種簡稱/縮寫。 可以學習的資源非常多,但是我馬上了解到:資源不會主動找上你,你必須善用內部平台去找尋,或是詢問主管/同事或其他實習生。尤其當時大家已經開始 WFH,無法和大家面對面的接觸,反而更需要善用通訊軟體和大家聯繫 當網友。 Day One 除了指 onboarding 的第一天,在 Amazon,Day One 具有特殊的意含。 我所理解的「Day One 精神」代表每天你需要持有接觸全新事物的心態、勇於嘗試、勇於接納嘗試後的失敗。每天都可能出現你以前沒處理過的問題類型,而不是只停留在自己知識所涵蓋的範圍舒適圈內、也不能只做著一成不變的工作。這意味著,有時我們需要做出快速的決策,而不是害怕出錯導致花了很久的時間去survey。 Elements of Amazon’s Day 1 Culture One-way and Two-way Door Decisions Quote Day 2 is stasis. Followed by irrelevance. Followed by excruciating, painful decline. Followed by death. And that is why it is always Day 1. – Jeff Bezos ","date":"2022.02.21","objectID":"/aws-internship-experience/:3:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"工作內容/模式 實習生的工作主要是協助開發和維護內部工具,來協助其他工程師或是主管。專案中除了程式開發以外,我們也負責將程式碼Deploy、以及維護自己開發的服務。因此專案進行過程中有可能需要整合 AWS 的服務,也需要熟悉內部的 CI/CD 流程。如果自己開發的服務突然掛掉,需要自己去搶修,也可能當天沒班,回來發現神之同事已經修好了。我對內部的東西比較熟悉之後,看到其他實習生的服務掛掉,也會幫忙去修。在這裡不會有人說 “It’s not my job”,這就是 Ownership。 專案的需求通常是正職工程師或主管提出 initiative,包含流程自動化、收集metric進行統計等等,主要是協助工程師/主管的工作流程,可以間接提升對客戶的服務品質。實習生可以用任何方式實做,只要夠有效率、且程式/架構的維護成本不高,有很大的空間可以發揮。我做的第一個專案甚至需要想出不同團隊合作的流程,畢竟這會影響到程式/UI要如何實做。對於不熟悉內部流程的實習生來說,其實這是熟悉正職工程師們工作流程的好方式,對於轉為正職後可以大大減低上手的時間。 另外我覺得這裡是文件導向的公司,大家會透過共享文件作為溝通,因此你可以查看過去實習生使用內部工具開發的細節,減少很多踩坑的機率。 透過文件撰寫,也可以減少很多開會的時間,大家在開會前先閱讀主講者的文件,增加溝通效率。 2017 Letter to Shareholders - Six-Page Narratives 我們需要為每個專案撰寫詳細的文件,這樣對交接給後來的實習生非常有幫助,一個專案甚至可以交接了3-4位實習生繼續開發。 在我實習期間,總共進行2個專案,另外又接手維護5個前人開發的專案。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:4:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"環境 內部的學習資源非常多,多到看不完,無論是教學影片、過去的 broadcast 錄影或是內部 Wiki等等,這些資源需要你主動挖掘,而且實習生有非常多時間可以看這些資源。 另外每週會有幾名工程師和實習生開會,一同討論當週進度以及專案遇到的瓶頸,任何人只要有想法都可以提出解答。每一、兩週也會與主管進行一對一的會議,可以提出任何 concern 或是問題。 因為疫情的關係都是線上會議,難免會覺得有疏離感,或是覺得自己被孤立。但是如果積極的聯繫/溝通,大家都非常樂意提供幫助。工程師明明超級忙,很感謝他們願意抽個半小時、一小時的時間討論專案的事情。因此 主動的reach out 非常重要。 中秋禮盒\" 中秋禮盒 中秋禮盒居然是威士忌杯冰石木合組!! \u003c3\" 中秋禮盒居然是威士忌杯冰石木合組!! \u003c3 ","date":"2022.02.21","objectID":"/aws-internship-experience/:5:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"我的失衡 這份實習剛好是我碩二的時候,所以同時要寫論文、做實驗和準備口試。之前因為時間分配不均,讓公司的事情佔用大部分時間導致論文沒什麼進度。聽起來像藉口,不過如果你念過研究所應該知道這個壓力在哪裡。 如果沒有如期畢業,意味著會影響到在AWS轉正職後的工作,可能延後、甚至是沒了offer都有可能。建議大家,前萬不要在碩二的時候找一個一年期的實習,會很累!! 我要非常感謝我的主管,在口試前的最後幾個月允許我減少上班的天數 (當然$$也少拿了…QQ) ,讓我專心趕論文。 因為北科大對論文的標準…不會太高,不會要求你用英文寫,也不用投國外期刊,所以我大概只花了三個月的時間,從零開始做程式撰寫、實驗測試和論文撰寫。 能夠三個月完成是因為,實驗方向和架構已經在我腦袋轉了大約半年,因此只需要花三個月克服實作和理想上的差距。 經過這密集的三個月,幸好最後完成了,也通過口試委員的認可得以準時畢業。 但是口試完馬上就是轉正職的面試,完全沒有歇息的機會。經過這兩個考驗之後,我的聽覺出現了異常,類似耳鳴,導致其中一耳的低頻喪失,且一耳聽到的頻率和另一耳差了大約一個半音semitone。 主要是因為肌肉過度緊繃導致,看完醫生吃了整整一週的肌肉放鬆藥物才緩解。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:6:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"結語 在 AWS 的實習真的是很難得的經驗,可以藉這個機會一窺大型外商內部的運作方式,尤其 Amazon 是一個很特別的地方,雖然看到層層組織架構和長久維護的龐大資訊系統,但是縮小範圍觀察每個小團隊、每個部門的運作,又像是新創一樣靈活變動。 也可能是因為台灣的團隊相比其他國家來說不是很大的關係,但是這一年團隊成長的速度真的很快。 實習結束後我讓自己放了一個月的長假。當所有階段任務都落定後的輕鬆感,實在非常可貴。 雖然在疫情荼毒之下,我還是去台東玩玩 放鬆心情,而且打了幾週的 Dark Souls 3,當個難得的短暫廢人。 「回到 AWS 之後,準備面對更多意想不到的困難挑戰吧」,我當時是這麼想的。 ","date":"2022.02.21","objectID":"/aws-internship-experience/:7:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":["Thoughts"],"content":"同時來看看我同事/前輩們的AWS實習心得吧~ 以下是 曾經和我一起當實習生、剛好也有寫 Blog 的同學們心得分享! Jack - AWS 雲端技術支援工程師體驗營心得 Wilson - AWS Support 實習心得 (2020) 另外這是我的學長 Eason 的實習心得。他是最早的台北 AWS 實習生,目前是 Cloud Support Engineer,是實力非常強的前輩! 我是如何在還沒畢業就錄取並進入到 Amazon 工作 ","date":"2022.02.21","objectID":"/aws-internship-experience/:8:0","tags":["AWS","Experiences","Interview"],"title":"AWS Taiwan 實習感想 2020-2021","uri":"/aws-internship-experience/"},{"categories":null,"content":"Hello~ 可以叫我 Oscar,這個部落格的名稱 O3R 就是 OscarO3R 的簡寫。 我會在這裡寫一些技術文,主要是網路/雲端相關,也會寫一些與技術無關的感想或其他主題。 大學唸資工系游手好閒時,偶然看了奮鬥吧!系統工程師なれる! SE的小說之後,突然想當系統工程師、研究網路相關技術,也找了一些實習機會 (反正就是不想做軟體工程)。 結果誤打誤撞,工作內容從地端變成雲端了: Amazon Web Services Cloud Support Engineer 2022 function non0plural(number, name) { if (number == 0) { return \"\" } if (number 1) { return number + \" \" + name + \"s\" } return number + \" \" +name } el = document.querySelector(\"#moment\"); function refresh() { start = dayjs(\"2022-08-09\") now = dayjs() total_months = now.diff(start, \"M\", true) months = total_months % 12 years = Math.floor((total_months) / 12) el.innerHTML = non0plural(years,\"year\")+\" \"+non0plural(months.toFixed(8),\"month\") } window.setInterval(refresh, 100); Amazon Web Services 10 months Cloud Support Associate 2022 Amazon Web Services 1 year Cloud Support Intern 2021 National Taipei University of Technology 2 years M.S. Student in Wireless and Broadband Networks Lab, Master in Computer Science 2021 Nextlink Technology 4 months Intern 2020 Zero One Technology Co. Ltd. 1 year Intern 2019 eASPNet Taiwan Inc. 2 months Summer Intern 2018 ","date":"2022.02.20","objectID":"/about/:0:0","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"沒什麼用的證照 AWS Solutions Architect Associate Cisco Certified Network Associate AWS Certified Cloud Practitioner ","date":"2022.02.20","objectID":"/about/:1:0","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"其他東西 ","date":"2022.02.20","objectID":"/about/:2:0","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"pc spec gpu: gtx 1080ti cpu: R9 5900x with Kraken Z53 water cooling ram: 4 * 16GB DDR4 Kingston 3200 MHz CL22 motherboard: Gigabyte Aorus X570S master monitor: EIZO FlexScan EV2457 (white) audio interface: Audient iD4 2in/2out speakers: krk rp5g3 peripherals keyboard: AARU TKL + cherry reds lubed with Krytox 205g0 and filmed with 0.13mm films keyboard2: Filco Majestouch Hakua with cherry browns mouse: a cheap razer deathadder essential ","date":"2022.02.20","objectID":"/about/:2:1","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"音樂製作 在高三時接觸電子音樂編曲,透過原文書、英文教學和論壇,學習音樂、編曲和 Sound Design。曾擔任過社團教學和編曲家教。目前專攻 Hardstyle 曲風,以 Extern 的名稱發佈作品。 ","date":"2022.02.20","objectID":"/about/:2:2","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"精神支柱 2019年我認識了住在美東的 VtuberVirtual Youtuber – 神狐かなKamiko Kana(Kamiko Kana),從此,只要認識我的都知道在眾多 Vtuber 裡面我只單推 Kana。在她開台 的時候,無論是看她遊戲實況或是雜談,身上任何壓力和痛苦都減輕許多,和她互動完全是一個治癒的過程。 在2021年我撰寫論文的最後階段,我無法想樣如果沒有 Kana 的陪伴,是要怎麼撐過去的?因此,我大概是…全台、或至少全校,把一位 VTuber 的名稱寫進我論文的致謝中,雖然我們位於地球的兩端,但是她對我的影響卻很深遠。 如果眾人支持Kana 我就是其中一個 如果眾人離Kana而去 我會是唯一守護她的人 如果世界與Kana為敵 我會與世界為敵 如果世界上沒有人守護Kana了 代表我已經不在這個世界上了 ","date":"2022.02.20","objectID":"/about/:2:3","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"橫幅背景 (About the Banner Image) 橫幅背景是我委託花咲-san所精心繪製的可愛 Kana,請尊重花咲ちゆ的著作權。 繪師(Artist): 花咲ちゆ 角色(Character): Kamiko Kana 委託證明(proof of Commission): https://skeb.jp/@hanasakichu/works/5 ","date":"2022.02.20","objectID":"/about/:2:4","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"大頭貼 (Profile Pic) 繪師(Artist): 中森 煙 角色(Character): Kamiko Kana 委託證明(proof of Commission): https://twitter.com/foxsc4r/status/1517045435117965312 ","date":"2022.02.20","objectID":"/about/:2:5","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"⚠️ 授權條款 License ⚠️ 以下授權不包含本部落格上方橫幅圖片,橫幅圖片著作權及版權屬於原作者。 The below license does not include the banner image on the top of this blog. The original artist owns the copyright in this artwork. ","date":"2022.02.20","objectID":"/about/:3:0","tags":null,"title":"About meee","uri":"/about/"},{"categories":null,"content":"CC BY-NC 4.0 (Attribution-NonCommercial 4.0 International) 姓名標示 (Attribution) 你必須給予適當表彰、提供指向本授權條款的連結,以及指出(本作品的原始版本)是否已被變更。你可以任何合理方式為前述表彰,但不得以任何方式暗示授權人為你或你的使用方式背書。 You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 非商業性 (NonCommercial) 你不得將本素材進行商業目的之使用。 You may not use the material for commercial purposes. 不得增加額外限制 (No additional restrictions) 你不能增設法律條款或科技措施,來限制別人依授權條款本已許可的作為。 You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 你可自由 You are free to 分享 — 以任何媒介或格式重製及散布本素材 Share — copy and redistribute the material in any medium or format 修改 — 重混、轉換本素材、及依本素材建立新素材 Adapt — remix, transform, and build upon the material ","date":"2022.02.20","objectID":"/about/:3:1","tags":null,"title":"About meee","uri":"/about/"},{"categories":["AWS"],"content":"目前我實習的公司使用 Mattermost 作為內部通訊軟體,且 Mattermost 支援 Webhook 和 Slash Command。為了方便隨時隨地可以快速開啟/關閉 EC2,因此想寫一個下 Slash Command 指令的工具,直接呼叫 API 來控制和查看 EC2,免去登入 console 的麻煩,一定會方便許多。我使用 Go 寫了一個程式處理 Mattermost 傳入的資料,並且透過 aws-sdk-go 對 EC2 進行操作。程式會在 Lambda 上執行,原始碼請參考我的 Github:https://github.com/LYTzeng/ec2ctl,日後考慮用 CloudFormation 讓需要的人快速佈署。這篇主要會介紹 API Gateway、Lambda 和部份 SDK 的使用。","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"目前我實習的公司使用 Mattermost 作為內部通訊軟體,且 Mattermost 支援 Webhook 和 Slash Command。為了方便隨時隨地可以快速開啟/關閉 EC2,因此想寫一個下 Slash Command 指令的工具,直接呼叫 API 來控制和查看 EC2,免去登入 console 的麻煩,一定會方便許多。整體架構如上圖 我使用 Go 寫了一個程式處理 Mattermost 傳入的資料,並且透過 aws-sdk-go 對 EC2 進行操作。程式會在 Lambda 上執行,原始碼請參考我的 Github:https://github.com/LYTzeng/ec2ctl,日後考慮用 CloudFormation 讓需要的人快速佈署。這篇主要會介紹 API Gateway、Lambda 和部份 SDK 的使用。 先展示成果: ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:0:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"API Gateway ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:1:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"IAM Role 在建立一個 API 之前,我們先創一個 IAM Policy 並 Attach 到 Role。API Gateway 需要 Invoke Lambda 以及寫入 CLoudWatch 的權限。 進入 IAM 管理介面,首先 Create 一個 Policy 使其允許 Invoke Lambda,我們使用 JSON 來建立 Policy: { \"Version\": \"2012-10-17\", \"Statement\": [ { \"Sid\": \"VisualEditor0\", \"Effect\": \"Allow\", \"Action\": [ \"lambda:InvokeFunction\", \"lambda:PutFunctionEventInvokeConfig\" ], \"Resource\": \"*\" } ] } 接著建立一個 Role 並 Attach policies。除了 attach 剛才建立的 policy,還需要 attach 一個 AmazonAPIGatewayPushToCloudWatchLogs 的 policy,可以直接搜尋,順便將 Role ARN複製下來,之後會使用到。接下來就可以建立一個 API。 ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:1:1","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"建立 API Gateway 進入 API Gateway 介面,選擇 Create API,找到 REST API 選擇 Build,Choose the protocol 選 REST,Create new API 選擇 New PIA。API Name輸入 API 名稱,Endpoint Type 保留預設 Regional 即可。 Create API 後進入 API 設定的頁面。選擇左側選單最下面的 Settings,將之前複製的 Role ARN 貼上,Role ARN 就是一個 IAM Role 的 ID,這可以提供 API Gateway 寫入 CloudWatch 的權限。 API Gateway 幾個組成元素大概有 Resource、Method、Stages 和 Resource Policy,這幾點設置完畢就能有一個可以運作的簡單 API 了。先簡單說明這幾個項目: Resources 一個 Resource 可以接收多種的 HTTP request method,例如 GET/POST/OPTION 等等。 Resource 的名稱會包含在 URL 裡面。 Methods HTTP request 的單一種類,並且決定接收到這個 method 後下一步該做什麼,以及如何做出 response。例如:收到 HTTP POST 時 觸發 Lambda function,並且將 body 傳送至 Lambda,隨後將 Lambda 回傳的 response (如:402 Unauthorized) 回應給 Client。流程會顯示在設定頁面中。 Stages Stage 代表這個 API 的開發階段,例如 dev, prod, beta, v2等,要產生一個可以被呼叫的 API,一定要 Deploy 到一個 Stage,Stage 的名稱會包含在 URL 中。 Resource Policy 提供 API 存取控制的機制,使用 JSON 格式來設定,條件可以使用 IAM,或是單一主機 IP等,可用來決定 被允許/禁止的動作,避免被不明人士存取到 API。 API Gateway 的 REST API 的 URL 的格式為: https://{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}/{resource} 假設我建立一個名為 ec2ctl 的 resource,stage 為 dev,API 位於 us-west-2 的 region,則我的 API URL 會是 https://{restapi-id}.execute-api.us-west-2.amazonaws.com/dev/ec2ctl 。 建立 Resource 左側選單選擇 Resources,會看到只有/這個符號,代表我們位於 base url,點擊 Action \u003e Create Resource 建立 Resource,並決定 resource name 與 resource path。 建立 Method 選擇 Action \u003e Create Method,並選擇 POST。 Resource Policy 我們需要允許 API Gateway invoke Lambda funciton,而且只有 Mattermost server 和我們的測試主機的 SourceIp 是被允許的,使用 JSON 格式輸入設定。 { \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Principal\": { \"AWS\": \"*\" }, \"Action\": \"execute-api:Invoke\", \"Resource\": \"execute-api:/*/*/ec2ctl\", \"Condition\": { \"IpAddress\": { \"aws:SourceIp\": [ \"192.0.2.1/32\", \"192.0.2.2/32\" ] } } } ] } Deploy API 到指定 Stage 點選左側選單 Resources,Action \u003e Deploy API,[New Stage] 後輸入 stage name,deploy 後即可使用這個 API。 有一點需要特別注意的是,對 API 設定做任何更動後,請記得要重新 Deploy 才會生效。 接著我們需要使用 Lambda 來和 API Gateway 串接。 ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:1:2","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"Lambda ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:2:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"IAM Role 我們會使用 Lambda 達成幾項功能:讀取所有 region 的 EC2,並回傳相關資訊,以及開啟/關閉指定的 EC2。要進行這些操作,我們需要賦予 Lambda function 權限,所以須建立 IAM Role。先新增所需的 Policy,JSON 設定請複製這裡:https://gist.github.com/LYTzeng/f935732d160c8fa72e90a18deeed9ae4 ,再將這個 Policy Attach 至新增的 Role。 ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:2:1","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"建立 Function 到 Lambda 界面,選擇 Create Function,Runtime 選擇 Go 1.x,Permissions 選擇 Use an existing role 找到剛才建立的 Role 並套用。 因為這次使用 Go 撰寫程式,必須先 compile 成能夠讓 Linux 執行的 binary (Lambda 底層 OS 使用 Amazon Linux) 並上傳,而且只能寫在 package main 裡面。你可以直接下載 compile 過的執行檔,透過 console 上傳 Function Package,Handler 設定為 main.upx。 接著找到 Environment Variables 區塊新增環境變數,Key 請輸入 MATTERMOST_TOKEN,Value 為你的 Mattermost server 的 Slash command token。 接著新增一個 Trigger: 選擇 API Gateway,API 就是剛才建立的 API,Deployment Stage 是剛才 Deploy 時命名的名稱 (如:dev),Security 選 Open,我們已經用 Token 做驗證,加上用 Resource Policy 白名單過濾,不必擔心這個 Open 設定。 接著回到 API Gateway 頁面,選擇 /ec2ctl 底下的 POST,設定 Integration Request Integration type 為 Lambda Function Use Lambda Proxy integration 務必勾選,此選項可透過 event 將 Request body 傳送至 Lambda。 Lambda Function 即剛才建立的 function 儲存後可以看到整個 Method Execution 流程。 這樣系統已經建制完畢,接下來可以使用 Postman 進行測試,或是直接在 Mattermost 開發環境測試。 ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:2:2","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"Mattermost 架設與設定 本篇不會詳述這部份操作,請參考 Mattermost 的官方文件: 開發環境架設 https://developers.mattermost.com/contribute/server/developer-setup/ Slash Command 設定 https://docs.mattermost.com/developer/slash-commands.html https://developers.mattermost.com/integrate/slash-commands/ Slash Command 設定可以參考這裡的範例: Title: ec2ctl Description: Start/stop/list EC2 instances from Mattermost. Command Trigger Word: ec2ctl Request URL: https://{restapi-id}.execute-api.{region}.amazonaws.com/dev/ec2ctl Request Method: POST Response Username: 留空白 Response Icon: 留空白 Autocomplete: ✔ Autocomplete Hint: (version | (start | stop) -i \u003cinstance id\u003e | ls [-r \u003cregion\u003e]) Autocomplete Description: Start/stop/list EC2 instances from Mattermost. 設定完畢請複製 token 並貼上至 Lambda 的 Environment Variable MATTERMOST_TOKEN 中。 ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:3:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"使用 Mattermost 測試 此程式 ec2ctl 的 Slash command 規則如下: Usage: ec2ctl version ec2ctl (stop | start) -i \u003cid\u003e ec2ctl ls [-r \u003cregion\u003e] Options: -i \u003cid\u003e Specify an instance ID. -r \u003cregion\u003e Specify a region. 測試指令是否正常,/ec2ctl ls -r us-west-2輸入後可以看到輸出如下: ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:4:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"使用 Postman 測試 使用 Postman 測試時需要模仿 Mattermost 的 HTTP Request 格式,否則程式會出錯。圖中 Postman 的設置都是必要的參數,不過只須設定 Headers 以及 Body: Content-Type:application/x-www-from-urlencoded token 為 Mattermost slash command token text 欄位就是指令的內容 (不含 slash command 的 trigger /ec2ctl),例如ls -r us-west-2 或 stop -i i-134134fwifi。 user_name 請隨意輸入 若看到 Status: 200 OK 和一些內容,代表成功了。 ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:5:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["AWS"],"content":"References IAM JSON Policy Reference Deploying a REST API in Amazon API Gateway ","date":"2020.04.21","objectID":"/lambda-apigateway-mattermost-go-ec2/:6:0","tags":["go","aws lambda","aws ec2","aws api gateway"],"title":"透過 AWS Lambda、API Gateway 和 AWS Go SDK,從 Mattermost 查看/開關 EC2 Instances","uri":"/lambda-apigateway-mattermost-go-ec2/"},{"categories":["Infra"],"content":"Kibana 能夠將資料視覺化成各種圖表並進行分析,同時提供 ELK Stack 的管理介面,架設完成的 ELK Stack 之後只需進入 Kibana,就可以透過這個 portal 瀏覽一切 Elastic 的服務。之前我將 pfSense 的 pfBlockerNG 防火牆阻擋紀錄透過 syslog 送給 Logstash,並且使用 Elasticsearch 接收已結構化的資料來提供全文搜尋服務,最後一步就是架設 Kibana 進行視覺化。","date":"2020.03.14","objectID":"/monitoring-pfsense-via-kibana/","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (三):Kibana Dashboard","uri":"/monitoring-pfsense-via-kibana/"},{"categories":["Infra"],"content":"Kibana 能夠將資料視覺化成各種圖表並進行分析,同時提供 ELK Stack 的管理介面,架設完成的 ELK Stack 之後只需進入 Kibana,就可以透過這個 portal 瀏覽一切 Elastic 的服務。之前我將 pfSense 的 pfBlockerNG 防火牆阻擋紀錄透過 syslog 送給 Logstash,並且使用 Elasticsearch 接收已結構化的資料來提供全文搜尋服務,最後一步就是架設 Kibana 進行視覺化。 整個 pfSense 與 ELK Stack 的架構如下面這張圖,架設過程中只要注意一下 Port 的對應其他都沒有太大的問題。 ","date":"2020.03.14","objectID":"/monitoring-pfsense-via-kibana/:0:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (三):Kibana Dashboard","uri":"/monitoring-pfsense-via-kibana/"},{"categories":["Infra"],"content":"ELK Stack 整合 pfSense 系列文 ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash ELK Stack 整合 pfSense (二):Elasticsearch [本篇] ELK Stack 整合 pfSense (三):Kibana Dashboard ","date":"2020.03.14","objectID":"/monitoring-pfsense-via-kibana/:1:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (三):Kibana Dashboard","uri":"/monitoring-pfsense-via-kibana/"},{"categories":["Infra"],"content":"安裝 Kibana 我在 Ubuntu 18.04 的環境上安裝,只需輸入下面指令。 wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add - sudo apt-get install apt-transport-https echo \"deb https://artifacts.elastic.co/packages/7.x/apt stable main\" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list sudo apt-get update \u0026\u0026 sudo apt-get install kibana 接著要對 Kibana 進行設定 sudo vim /etc/kibana/kibana.yml 在 kibana.yml 找到 server.host 並且改成主機的 IP,如果沒有改這部分,Kibana 不會接收外部的連接。找到 elasticsearch.hosts 並把參數改為 Elasticsearch 的 IP,Port 請對應到 9200。如果你把 ELK 架在同一台主機上,則不需要進行這段設定。 server.host: \"172.30.0.6\" elasticsearch.hosts: [\"http://172.30.0.5:9200\"] 接著啟動 Kibana sudo systemctl start kibana.service 然後我們可以透過http://YOURDOMAIN.com:5601直接進入他的 portal。 接著需要與 Elasticsearch 建立連接。 ","date":"2020.03.14","objectID":"/monitoring-pfsense-via-kibana/:2:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (三):Kibana Dashboard","uri":"/monitoring-pfsense-via-kibana/"},{"categories":["Infra"],"content":"連接 Elasticsearch 點選齒輪圖示,選擇 Index Patterns \u003e Create Index Pattern 接著輸入 logstash-pfsense-*,其實這就是我們在 Logstash Output 自訂的 Index,有對應的 Index 會看到下面有跳出來對應的 Index。接著 Next step 後 接著選擇 @timestamp,然後 Next step 接著會看到所有結構化的資料,可以準備建立圖表。 ","date":"2020.03.14","objectID":"/monitoring-pfsense-via-kibana/:3:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (三):Kibana Dashboard","uri":"/monitoring-pfsense-via-kibana/"},{"categories":["Infra"],"content":"建立圖表和 Dashboard 建立圖表要先從 Visualize 建立單一圖表,然後再從 Dashboard 加入圖表。因為圖表有很多種,可以思考哪些欄位適合用哪種圖表呈現。 如果要建立地圖熱點圖,需使用 Coordinate Map,但是 7.6 版預設不會讓你建立,所以需要增加一行設定到kibana.yml。 echo 'xpack.maps.showMapVisualizationTypes: true' \u003e\u003e /etc/kibana/kibana.yml 接著在 Visualization 就會出現 Coordinate Map 的選項。 想知道 pfSense 阻擋的連接都是來自哪些國家,所以先新增一個 Bucket,應該會自動偵測到 geoip.location 欄位,也就是經緯度。 設定完畢按下播放(?)鍵預覽結果 我們還可以新增 Filter,action.keyword等於block代表 pfBlocker 做出阻擋的紀錄。 結束時記得儲存 然後新增 Dashboard,選擇左上角 Add 來新增剛才製作的 Visualization,最後一樣記得 Save。 還可以統計所有可疑連線的目的 port 圓餅圖 這樣基本的儀表板便完成了,可以依需求和情境新增其他圖表,我自己做完的結果大概像這個樣子,搭配 Dark Theme 實在是非常有看頭。 ","date":"2020.03.14","objectID":"/monitoring-pfsense-via-kibana/:4:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (三):Kibana Dashboard","uri":"/monitoring-pfsense-via-kibana/"},{"categories":["Infra"],"content":"Logstash 的功能像是一個接收器,支援從許多種 Protocol 接收 Log,如 Syslog、Netflow等,並且透過 Parser 將非結構化資料轉換成半結構化資料。Parser 方便的是使用 Grok Pattern,可以避免自行撰寫複雜的 Regex,不過他也支援 Regex 讓我們可以自訂 Pattern,因此 logstash 的 parsing 是很彈性的。本系列文章介紹 pfSense 與 ELK Stack (7.6 版) 的整合,藉此分析與收集阻擋的連接紀錄。","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"Logstash 的功能像是一個接收器,支援從許多種 Protocol 接收 Log,如 Syslog、Netflow等,並且透過 Parser 將非結構化資料轉換成半結構化資料。Parser 方便的是使用 Grok Pattern,可以避免自行撰寫複雜的 Regex,不過他也支援 Regex 讓我們可以自訂 Pattern,因此 Logstash 的 parsing 是很彈性的。本系列文章介紹 pfSense 與 ELK Stack (7.6 版) 的整合,藉此分析與收集阻擋的連接紀錄。 整個 pfSense 與 ELK Stack 的架構如下面這張圖,架設過程中只要注意一下 Port 的對應其他都沒有太大的問題。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:0:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"ELK Stack 整合 pfSense 系列文 [本篇] ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash ELK Stack 整合 pfSense (二):Elasticsearch ELK Stack 整合 pfSense (三):Kibana Dashboard ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:1:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"安裝 Logstash 裝 logstash 前需要先安裝 Java,注意一下 Logstash 和 Java 的相容性。 # 安裝 JRE sudo apt-get update \u0026\u0026 sudo apt-get install default-jre -y java -version # 安裝 logstash curl -L -O https://artifacts.elastic.co/downloads/logstash/logstash-7.6.1.deb sudo dpkg -i logstash-7.6.1.deb ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:2:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"Logstash 的檔案結構 請見文件中有詳情,這邊主要是了解 Logstash 各種設定放在哪裡,以及他們有不同的作用。 /etc/logstash/ 底下有各種 yml 檔,都是 logstash 的設定檔。下面說明設定檔的功能。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:3:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"pipeline.yml /etc/logstash/pipelines.yml 決定 logstash pipeline config 的路徑,預設在 /etc/logstash/conf.d/*.conf 下,這邊使用預設即可。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:3:1","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"logstash.yml logstash.yml 用來設定 logstash 執行時的選項,logstash.yml 中大部分的設定也可以在執行指令用 Command-line Flags 帶入。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:3:2","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"Config File 這邊是 Logstash 最重要的地方,所有 config 預設都要放在 /etc/logstash/conf.d 底下。我們只要在這裡建立一個 .conf 結尾的檔案就可以寫設定了。如果有多個 config file,則 Logstash 會自行整合。 Config File 的結構分為 3 個部分,input、filter、output。Logstash 有需要的話要安裝 plugin,plugin 有四種:input、filter、output、codec。要查看已安裝的 plugin 可以透過指令 bin/logstash-plugin list,bin的路徑會因OS而不同,Logstash 的檔案結構文件文件有寫。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:3:3","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"bin 檔 放在 /usr/share/logstash/bin。執行 Logstash、安裝其他 Plugin 等會需要用到這裡的 binary。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:3:4","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"Custom Grok Patterns 有時我們需要自行定義 Grok Pattern,並且命名之,這可以透過.grok file 進行定義,然後再從 config file 中指定 pattern 的路徑。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:3:5","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"在 pfSense 設定 Syslog 的 Remote log server 在 pfSense 的選單中進入 Status \u003e System Logs。 接著切換到 Settings 標籤: 輸入 Logstash 的 IP 和 Port: ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:4:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"建立 Config file 和 Grok Patttern 我找到 Github 上已經有勇者寫好 pfSense 的 Grok pattern 了,所以直接拿來使用,把整個 Repo 給clone下來吧。 如果要新增更多自己的 Pattern,很多人會使用 Grok Debugger 進行除錯。 最上面就是輸入原始的 Log;中間的欄位輸入 Grok Pattern;如果有額外定義的 custom pattern (像 logstash-pfSense/patterns 裡的內容就是 custom pattern),請將 Add custom patterns 打勾就會可以輸入了。最下方就是預覽 Parsed 後的結果。 logstash-pfSense/conf.d 裡的檔案複製到/etc/logstash/conf.d,logstash-pfSense/patterns複製到 etc/logstash/patterns。記得修改01-inputs.conf的 port 和10-syslog.conf的 IP,須對應到 pfSense 的 syslog 設定。還有30-outputs.conf的 Elasticsearch host IP。 01-inputs.conf 例子使用 Port 6514 #tcp syslog stream via 5140 input { tcp { type =\u003e \"syslog\" port =\u003e 6514 } } #udp syslogs stream via 5140 input { udp { type =\u003e \"syslog\" port =\u003e 6514 } } 10-syslog.conf 前半段 filter { if [type] == \"syslog\" { #change to pfSense ip address if [host] =~ /172\\.30\\.0\\.1/ { # 這邊要修改 mutate { add_tag =\u003e [\"pfSense\", \"Ready\"] } } if \"Ready\" not in [tags] { mutate { add_tag =\u003e [ \"syslog\" ] } } } } 架 ELK 需要特別注意服務之間溝通的 Port,預設 Elasticsearch 會使用 Port 9200 來接收 Logstash 的資料,因此30-outputs.conf的設定請參考下面: 30-outputs.conf output { if [type] == \"syslog\" { elasticsearch { hosts =\u003e \"172.30.0.5:9200\" index =\u003e \"logstash-pfSense-%{+YYYY.MM.dd}\" } stdout { codec =\u003e rubydebug } } } 接著啟動 Logstash。 sudo service logstash start 如何確定 Logstash 有接收到 Syslog? 確認 OS 有 Listen 我設定的 Port 6514 後,我是使用 TCPDump 查看,因為目前還沒架起 Elasticsearch,所以用很原始的方式看。 要驗證 Log 被 parse 的結果,可以從 Wireshark 複製 log 內容,到 Grok Debugger 試試看套用 pattern。接著下一篇就是架 Elasticsearch。 ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:5:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"References Structure of a Config File | Logstash Reference [7.6] | Elastic System Monitoring — Remote Logging with Syslog | pfSense Documentation patrickjennings/logstash-pfSense: Logstash configuration for pfSense syslog events. ","date":"2020.03.13","objectID":"/sending-logs-from-pfsense-2-logstash/:6:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash","uri":"/sending-logs-from-pfsense-2-logstash/"},{"categories":["Infra"],"content":"本系列文章介紹 pfSense 與 ELK Stack (7.6 版) 的整合,藉此分析與收集阻擋的連接紀錄。Elasticsearch 是個全文搜尋引擎,透過 Inverted Index 的資料結構來提供即時的搜尋和分析功能。整個 ELK Stack 讀取和分析的 Log 就是儲存在 Elasticsearch,因此 Elasticsearch 需要足夠的儲存空間,而且它專門接收 JSON 型態的資料,所以除了和 Logstash 整合,只要能輸出 JSON 格式的工具都能與 Elasticsearch 整合。","date":"2020.03.12","objectID":"/elasticsearch-receives-data-from-logstash/","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (二):Elasticsearch","uri":"/elasticsearch-receives-data-from-logstash/"},{"categories":["Infra"],"content":"本系列文章介紹 pfSense 與 ELK Stack (7.6 版) 的整合,藉此分析與收集阻擋的連接紀錄。Elasticsearch 是個全文搜尋引擎,透過 Inverted Index 的資料結構來提供即時的搜尋和分析功能。整個 ELK Stack 讀取和分析的 Log 就是儲存在 Elasticsearch,因此 Elasticsearch 需要足夠的儲存空間,而且它專門接收 JSON 型態的資料,所以除了和 Logstash 整合,只要能輸出 JSON 格式的工具都能與 Elasticsearch 整合。在查詢時皆透過 REST API,文件中有提到可使用 Query String 或是 Query DSL 在 GET 的 request body 塞 JSON 查詢指令,雖然目前沒有定義 GET method 的 body 的用途,關於是否可以這樣使用也存在不少爭議,不過看起來 Elastic 他們就自行使用了。 整個 pfSense 與 ELK Stack 的架構如下面這張圖,架設過程中只要注意一下 Port 的對應其他都沒有太大的問題。 ","date":"2020.03.12","objectID":"/elasticsearch-receives-data-from-logstash/:0:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (二):Elasticsearch","uri":"/elasticsearch-receives-data-from-logstash/"},{"categories":["Infra"],"content":"ELK Stack 整合 pfSense 系列文 ELK Stack 整合 pfSense (一):將 pfSense 防火牆阻擋紀錄傳送到 Logstash [本篇] ELK Stack 整合 pfSense (二):Elasticsearch ELK Stack 整合 pfSense (三):Kibana Dashboard ","date":"2020.03.12","objectID":"/elasticsearch-receives-data-from-logstash/:1:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (二):Elasticsearch","uri":"/elasticsearch-receives-data-from-logstash/"},{"categories":["Infra"],"content":"安裝 Elasticsearch 依照文件步驟安裝,一樣要先裝 Java,再裝 Elasticsearch。 sudo apt-get update \u0026\u0026 sudo apt-get install default-jre -y wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add - sudo apt-get install -y apt-transport-https echo \"deb https://artifacts.elastic.co/packages/7.x/apt stable main\" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list sudo apt-get update \u0026\u0026 sudo apt-get install -y elasticsearch Elasticsearch 的設定檔放在/etc/elasticsearch/elasticsearch.yml,elasticsearch.yml有幾個地方需要特別注意。 elasticsearch.yml # 因為我另外掛載大容量的 NFS 來當作儲存空間,所以需要修改預設的路徑。# 儲存 data 的地放path.data:/boot/lib/elasticsearch# 儲存 log 的地方path.log:/boot/log/elasticserch# 如果 Kibana 和 Elasticsearch 不在同一台,一定要修改 IP。# 預設是 Listen 127.0.0.1 的,不修改的話無法讓其他主機連接network.host:172.30.0.5# 由於 network.host 非 loopback address# Elasticsearch 會認為這是 Production 環境# 所以必須修改 discovery.seed_hostsdiscovery.seed_hosts:[\"172.30.0.5\"] 接著就可以啟動 Elasticsearch,Elasticsearch 會透過 Port 9200 接收 Logstash 的資料和接收查詢指令,然後透過 Port 9300 和 Kibana 進行 Keepalive。 sudo systemctl start elasticsearch.service 然後檢查 Port 狀態: netstat -nlt 注意 9200 與 9300: tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp6 0 0 172.30.0.5:9200 :::* LISTEN tcp6 0 0 172.30.0.5:9300 :::* LISTEN tcp6 0 0 :::22 :::* LISTEN 如果 Logstash 已經在運作,我們會看得到 Elasticsearch 和 Logstash 建立的 connection: root@elasticsearch:/etc/elasticsearch# netstat -net | grep 9200 tcp6 0 0 172.30.0.5:9200 172.30.0.4:51242 ESTABLISHED 111 253749 接著等待 Logstash 傳送一些資料之後,可以透過 API 查詢看看是否收到資料。之前在 Logstash 的 output config 有定義 index 名稱為 logstash-pfSense-%{+YYYY.MM.dd},所以查詢所有資料可以在 URL 用logstash-pfSense-\\*,_search就是查詢所有資料,?pretty=true是為了讓輸出結果方便人類閱讀。 curl http://172.30.0.5:9200/logstash-pfSense-\\*/_search\\?pretty=true 如果有看到一堆 JSON 格式的資料,就代表成功了,這邊沒有要特別研究查詢語法,因為接下來的工作就是讓 Kibana 查詢並製作視覺化 Dashboard。 ","date":"2020.03.12","objectID":"/elasticsearch-receives-data-from-logstash/:2:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (二):Elasticsearch","uri":"/elasticsearch-receives-data-from-logstash/"},{"categories":["Infra"],"content":"References Install Elasticsearch with Debian Package | Elasticsearch Reference [7.6] | Elastic URI Search | Elasticsearch Reference [7.6] | Elastic ","date":"2020.03.12","objectID":"/elasticsearch-receives-data-from-logstash/:3:0","tags":["ELK","pfSense"],"title":"ELK Stack 整合 pfSense (二):Elasticsearch","uri":"/elasticsearch-receives-data-from-logstash/"},{"categories":["Infra"],"content":"蘋果真高價,何不自己架? 如果覺得 Mac Pro 很貴,或是說需要更進一步客製化硬體,何不考慮自己在伺服器上架設一台 Mac 呢?要多少 Storage 和 Memory 就有多少。總之,能省錢又不犧牲效能,就是爽!網路上有一些關於 ESXi 安裝 macOS 的教學,但是大多是給 VMware Workstation,不然就是版本不夠新,這裡提供我親自嘗試過、可行的且最新的 ESXi 搭配 macOS 的安裝方式。","date":"2020.01.27","objectID":"/installing-macos-on-esxi/","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":" 蘋果真高價,何不自己架? — me 如果覺得蘋果的刨絲器 Mac Pro 很貴,或是說需要更進一步客製化硬體,何不考慮自己在伺服器上架設一台 Mac 呢?要多少 Storage 和 Memory 就有多少。總之,能省錢又不犧牲效能,就是爽!可以參考 Linus Tech Tips 對 Mac Pro 的評論。網路上有一些關於 ESXi 安裝 macOS 的教學,但是大多是給 VMware Workstation,不然就是版本不夠新,這裡提供我親自嘗試過、可行的且最新的 ESXi 搭配 macOS 的安裝方式。 2019 Mac Pro 選好一點的規格,就跟買車一樣了。 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:0:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"更新 ESXi 6.7 至 Update 3 首先請更新 ESXi Server 到 Update 3,才能支援 MacOS 10.15。我們必須依序從 Update 1 更新到 Update 3。以下過程需要進行三遍,分別安裝三次 Update 的更新。 到這裡 找到對應版本,下載 zip 檔。Update 1 的檔名為 update-from-esxi6.7-6.7_update01.zip。 讓 Server 進入維護模式。 zip 上傳至 Datastore,SSH 進入 ESXi,執行指令,注意檔名和路徑要改: esxcli software vib install -d /vmfs/volumes/datastore1/updates/update-from-esxi6.7-6.7_update01.zip 重新開機 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:1:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"安裝 Unlocker ESXi 6.7 必須使用 Unlocker 3.0 版才能讓 MacOS 成功開機,舊版本都會進入無限自動重開循環。如果你的 Server 安裝過 Unlocker,更新 ESXi 後需重新再安裝 Unlocker。 下載 ESXi Unlocker 3.0 (二擇一): Unlocker 作者的載點,需登入會員 我的 Google Drive 載點 將 esxi-unlocker-300.tgz 上傳至 ESXi,在自己電腦執行: scp /你的下載路徑/esxi-unlocker-300.tgz root@xxx.xxx.xxx.xxx:/vmfs/volumes/datastore1/ cd /vmfs/volumes/datastore1/ tar xvf esxi-unlocker-300.tgz 然後執行 esxi-install.sh,結束後重開機: chomod 777 esxi-install.sh ./esxi-install.sh reboot -f Unlocker 安裝完畢後,可以執行esxi-smctest.sh 檢查 Unlocker 是否成功安裝。成功安裝的話會有以下 Output: /bin/vmx smcPresent = true custom.vgz false 38930360 B ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:2:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"macOS 的 VMDK 接著我們會使用比較不同的安裝方式。有別於一般使用映像檔安裝,我們會先建立一個 VM,再將 macOS 的 VMDK 新增到該 VM。這樣開機時該虛擬硬碟就會被讀取。和 ISO 安裝相比,這是我目前嘗試過成功機率最高的方法。安裝的 macOS 版本是 10.15,請特別留意文件中有註明 ESXi 所支援的 macOS 版本。 VMDK 載點 (二擇一): Geekrar Google Drive 載點 我的 Google Drive 載點 把 VMDK 上傳到 ESXi,可以使用任何 UI 或 SCP。接著先使用 VMDK 來登錄虛體機器。 接著我們要對 VMDK 動一點手腳,不然會開不起來,因此要使用vmkfstools轉換 VMDK。 cd /vmfs/volumes/[datastore 名稱] vmkfs -i macOS_Catalina_Final.vmdk -d thin macOS_Catalina_Final_new.vmdk 接著開始建立新的虛擬機器。 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:3:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"建立新的虛擬機器 OS 請選擇 Apple macOS 10.14 即可。 其實對於資源大小沒有別的要求,只要不要太小即可。接著在下面的畫面中,砍掉預設的硬碟,我們要在這裡新增剛剛轉換好的 VMDK 硬碟。 選擇「新增磁碟」\u003e 「新增現有磁碟」,如圖: 接著選擇剛才轉換好的 VMDK macOS_Catalina_Final_new.vmdk 接著「下一步」、「完成」,成功建立虛擬機器。 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:4:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"修改 VMX 設定 接著我們要修改 VMX 設定,SSH 進入 ESXi,cd到 VM 的位置。[datastore 名稱]請換成你的 datastore 名稱,[虛擬機器名稱]請換成你的 VM 名稱。 cd /vmfs/volumes/[datastore 名稱]/[虛擬機器名稱] echo 'smc.version = \"0\"' \u003e\u003e [虛擬機器名稱].vmx 接著就可以開機了,開機成功的話會進入各種設定,之後就只要照流程走完即可。 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:5:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"在 macOS 內安裝 VMware tools 最後就剩安裝 VMware tools 的部分了,VMware Tools 提供 Guest OS 和 ESXi 的相容度,讓我們可以調整螢幕解析度、。到這個官方連結下載 VMware tools,解壓縮後把 darwin.iso 檔上傳到 ESXi。 接著掛載darwin.iso,macOS 會自動讀取光碟。 直接點選 “Install VMware Tools” 並使用「下一步安裝法」。 中間需要從設定那邊 Allow 一下,點選 Allow 即可。 安裝完後重開機,即可使用。成功安裝就可以看見 ESXi 偵測到 VMware Tools: 如果發現螢幕解析度沒有隨著調整視窗尺寸而改變,請試著提升虛擬顯示卡記憶體。 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:6:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"顯卡與 USB 裝置透通 (Passthrough) 因為用遠端或是 Remote Console 操作體驗感覺不順暢,接著我想嘗試讓顯卡直接 Passthrough 給 Guest OS,也就是 macOS。 首先在管理\u003e硬體下面找到想 Passthrough 的裝置,開啟它的 Passthrough 功能。 因為 Passthrough 後會無法從 ESXi 操控,所以我們也要 Passthrough USB 裝置用來連接滑鼠鍵盤,才能直接操控 macOS。因為無法 Passthrough 單一 USB 裝置給 Guest OS,我們必須 Passthrough 一整個 USB Controller。 接著在 macOS VM 設定裡,將記憶體的 “Reserve all guest memory” 打勾, 接著在「新增其他裝置」(Add other device),選擇新增 PCI 裝置,並選擇顯卡與 USB Conroller。 儲存後就可以開機了,開機後便無法從 ESXi 操控了,如果開機後發現螢幕只顯示桌布,可以直接試著輸入密碼按 Enter,猜測這是因為 macOS 誤以為有多螢幕所造成的,登入後一切會恢復正常。或是也可以從 macOS 裡面的顯示設定去設定。 恭喜你,終於擁有一台性能好的桌上型 Mac,媲美 MAC Pro 而硬體想怎樣搞都可以。對於 macOS 大家應該都如此,又愛又恨,甚至踩雷無數,但只要不由愛轉恨都好。最後附上一張 Unlocker 沒裝好導致開機 Kernel Panic 出現的 Dont_Steal_MacOS.cpp 截圖,我 Troubleshooting 時從未看過給得這麼爽快的線索XD。 欲知詳情,以及為何安裝 Unlocker 可以成功,請在 macOS 中開啟 Terminal,看看這個檔案: cat '/System/Library/Extensions/Dont Steal MacOS X.kext/Contents/MacOS/Dont Steal Mac OS X' 裡面有一段文字這麼寫道: Copyright (c) 2006-2019 Apple Inc. All rights reserved. The purpose of this Apple software is to protect Apple copyrighted materials from unauthorized copying and use. You may not copy, modify, reverse engineer, publicly display, publicly perform, sublicense, transfer or redistribute this file, in whole or in part. If you have obtained a copy of this Apple software and do not have a valid license from Apple to use it, please immediately destroy or delete it from your computer. Dont_Steal_Mac_OS_X AppleSMC \"DSMOS: SMC read error K0: %d\"@/BuildRoot/Library/Caches/ com.apple.xbs/Sources/DontStealMacOS/DontStealMacOS-30.0.1/Dont_Steal_MacOS.cpp:191 \"DSMOS: SMC read error K1: %d\"@/BuildRoot/Library/Caches/com.apple.xbs/Sources/ DontStealMacOS/DontStealMacOS-30.0.1/Dont_Steal_MacOS.cpp:198 %02x DSMOS: SMC returned incorrect key: %s 什麼意思呢,嘿嘿,就自行解讀囉。 ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:7:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["Infra"],"content":"References How to patch your VMware ESXi 6.7 server - StevenAnnett.co.uk 頭城國小資訊組 | VMware ESXi - 安裝 macOS Sierra 10.12.4 【VMware】失败 - “scsi0:0”的磁盘类型 2 不受支持或无效。请确保磁盘已导入_永无止境的Pasu_新浪博客 Install macOS Mojave on VMware on Windows PC - Geekrar macOS Unlocker V3.0 for VMware ESXi - Miscellaneous - InsanelyMac Forum ","date":"2020.01.27","objectID":"/installing-macos-on-esxi/:8:0","tags":["VMware","vSphere","MacOS"],"title":"想擁有一台伺服器等級 Mac? macOS 10.15 Catalina on VMware ESXi 6.7","uri":"/installing-macos-on-esxi/"},{"categories":["SDN"],"content":"Mininet 是一個虛擬網路的模擬器,可以在單一主機、VM 或 Container 中執行。一開始我想用 Container 的環境玩玩看 Mininet,主要目的是學習 OpenFlow。為了產出研究所課程的期末加分報告,自訂題目中我選擇 OpenFlow,也是想藉由這個機會學習 OpenFlow,而這一系列文章同時作為學習筆記與報告題材。","date":"2020.01.23","objectID":"/introduction-to-mininet/","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"Mininet 是一個虛擬網路的模擬器,可以在單一主機、VM 或 Container 中執行。一開始我想用 Container 的環境玩玩看 Mininet,主要目的是學習 OpenFlow。為了產出研究所課程的期末加分報告,自訂題目中我選擇 OpenFlow,也是想藉由這個機會學習 OpenFlow,而這一系列文章同時作為學習筆記與報告題材。本篇主要以 Mininet 官方的 openflow-tutorial 為主軸,唯一不同是我使用 Container 環境取代官方的 OVF VM 範本。 ","date":"2020.01.23","objectID":"/introduction-to-mininet/:0:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"SDN 與 OpenFlow 簡易來說,Controller 負責決定封包如何轉發,即負責 Control Plane 的部分。Controller 透過南向介面與 Switch 溝通,並調整 Switch 的 Flow Table,更動 Flow Table 中的 Entry。 而 Switch 只會依照 Flow Entry 的規則進行轉發,也就是負責 Data Plane 的部分。OpenFlow 就是 Controller 和交換器間的溝通協定。 基本上 Cotroller 和 Switch 的溝通是透過南向介面 (Southbound Interfaces),包含 OpenFlow、NETCONF、SNMP 等。而北向介面 (Northbound Interfaces) 是各種服務與 Controller 溝通的介面,例如許多廠牌如 Cisco ACI 解決方案,具備讓使用者透過圖形介面操作 Controller 的功能,則北向介面使用 RESTful API 居多。北向介面也包含 gRPC、RESTCONF、YANG Data Model 等。接著使用 Mininet 操作 SDN 拓樸,我會把實際做過的 Commands 列出,並搭配解釋說明。 ","date":"2020.01.23","objectID":"/introduction-to-mininet/:1:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"安裝 Docker 因為常常要在各種 VM 安裝 Docker,所以乾脆偷懶把安裝指另一次寫成腳本執行。這個 Script 主要會安裝最新版本的 Docker CE,如果要安裝特定版本,請不要使用此 Script。 註: 我使用的環境是 Ubuntu 18.04 請先用過一次sudo,再直接執行: bash \u003c(curl https://gist.githubusercontent.com/LYTzeng/bc67f4cb051f92ce7206a4a585229e25/raw/e403038cfa5c8ff2f4900425882213b57f31d0bb/install_docker_ubuntu.sh) 原始 Script 如下,參考自官方步驟。 # Install using the repository sudo apt-get update sudo apt-get install -y \\ apt-transport-https \\ ca-certificates \\ curl \\ gnupg-agent \\ software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo apt-key fingerprint 0EBFCD88 sudo add-apt-repository \\ \"deb [arch=amd64] https://download.docker.com/linux/ubuntu \\ $(lsb_release -cs)\\ stable\" sudo apt-get update # Install the latest version of Docker Engine - Community and containerd sudo apt-get install -y docker-ce docker-ce-cli containerd.io # Disable swap sudo swapoff -a sudo sed -i '/ swap / s/^\\(.*\\)$/#\\1/g' /etc/fstab ","date":"2020.01.23","objectID":"/introduction-to-mininet/:2:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"把 Mininet Image 拉下來並執行 docker pull iwaseyusuke/mininet docker run -it --rm --privileged -e DISPLAY \\ -v /tmp/.X11-unix:/tmp/.X11-unix \\ -v /lib/modules:/lib/modules \\ iwaseyusuke/mininet 這時注意 Command line 應該已經進入到容器裡面了。 ","date":"2020.01.23","objectID":"/introduction-to-mininet/:3:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"建立簡易拓樸 一個 OpenFlow 簡易拓樸可以包含一個 OpenFlow Controller、OpenFlow Switch 和三個 Host。執行以下指令建立網路。 mn --topo single,3 --mac --switch ovsk --controller remote --topo single,3 告訴 Mininet 產生 Single-Switch Topology,包含 3 個 Hosts --mac 讓 Hosts 的 MAC 等同於 IP Address --switch ovsk 指定交換器類型為 OVSK --controller remote 讓 OpenFlow Switch 指到 Remote 的 Controller,預設是 localhost 這個網路架構會長這樣,參考自官方 Tutorial 觀察指令輸出可以看到一些細節。 root@8987721c841b:~# mn --topo single,3 --mac --switch ovsk --controller remote *** Error setting resource limits. Mininet's performance may be affected. *** Creating network *** Adding controller Unable to contact the remote controller at 127.0.0.1:6653 Unable to contact the remote controller at 127.0.0.1:6633 Setting remote controller to 127.0.0.1:6653 *** Adding hosts: h1 h2 h3 *** Adding switches: s1 *** Adding links: (h1, s1) (h2, s1) (h3, s1) *** Configuring hosts h1 h2 h3 *** Starting controller c0 *** Starting 1 switches s1 ... *** Starting CLI: mininet\u003e ","date":"2020.01.23","objectID":"/introduction-to-mininet/:4:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"Mininet 指令 在 mininet\u003e 下可以執行很多指令,包含查看節點、在節點直營特定指令等等。 查看所有節點: mininet\u003e nodes available nodes are: c0 h1 h2 h3 s1 在 h1 節點執行指令: mininet\u003e h1 ifconfig h1-eth0: flags=4163\u003cUP,BROADCAST,RUNNING,MULTICAST\u003e mtu 1500 inet 10.0.0.1 netmask 255.0.0.0 broadcast 10.255.255.255 inet6 fe80::200:ff:fe00:1 prefixlen 64 scopeid 0x20\u003clink\u003e ether 00:00:00:00:00:01 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 16 bytes 1316 (1.3 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73\u003cUP,LOOPBACK,RUNNING\u003e mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10\u003chost\u003e loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 我覺得有一個指令也很方便,可以查看 Host 連接到 Switch 的第幾 Port: mininet\u003e net h1 h1-eth0:s1-eth1 h2 h2-eth0:s1-eth2 h3 h3-eth0:s1-eth3 s1 lo: s1-eth1:h1-eth0 s1-eth2:h2-eth0 s1-eth3:h3-eth0 c0 為了之後的操作,這時候需要開多個 CLI 到同一個 Container 裡面,可以開多個視窗SSH進入VM,先用docker ps查看容器名稱,再執行exec打開多個 bash。 root@ubuntu:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ba8cee17af88 iwaseyusuke/mininet \"/ENTRYPOINT.sh\" 9 minutes ago Up 9 minutes 6633/tcp, 6640/tcp, 6653/tcp confident_wilson root@ubuntu:~# docker exec -it ba8cee17af88 /bin/bash root@ba8cee17af88:~# ","date":"2020.01.23","objectID":"/introduction-to-mininet/:5:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"ovs-ofctl 查看 Switch 的 Port 狀態和其他資訊,在另一個 CLI 輸入 ovs-ofctl show s1 輸出如下: root@ba8cee17af88:~# ovs-ofctl show s1 OFPT_FEATURES_REPLY (xid=0x2): dpid:0000000000000001 n_tables:254, n_buffers:0 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(s1-eth1): addr:f2:38:c2:6b:c5:b2 config: 0 state: 0 current: 10GB-FD COPPER speed: 10000 Mbps now, 0 Mbps max 2(s1-eth2): addr:e6:0d:27:7a:72:bd config: 0 state: 0 current: 10GB-FD COPPER speed: 10000 Mbps now, 0 Mbps max 3(s1-eth3): addr:1e:a6:3b:48:61:41 config: 0 state: 0 current: 10GB-FD COPPER speed: 10000 Mbps now, 0 Mbps max LOCAL(s1): addr:16:10:2d:1b:8a:46 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 輸出 Flow table,目前Controller 沒有任何 Flow,所以是空的: ovs-ofctl dump-flows s1 這時候 Hosts 之間還不能溝通,因為 Switch 無法得知進來的封包要往哪送,因此要在 Controller 加入 Flow。先加入 h1 和 h2 之間的 Flow。 ovs-ofctl add-flow s1 in_port=1,actions=output:2 ovs-ofctl add-flow s1 in_port=2,actions=output:1 會發現 h1 可以 ping h2 了 mininet\u003e h1 ping -c4 h2 PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data. 64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.072 ms 64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.082 ms 再 Dump 一次 Flow table,有東西了: root@ba8cee17af88:~# ovs-ofctl dump-flows s1 cookie=0x0, duration=314.375s, table=0, n_packets=25, n_bytes=1694, in_port=\"s1-eth1\" actions=output:\"s1-eth2\" cookie=0x0, duration=285.479s, table=0, n_packets=16, n_bytes=1316, in_port=\"s1-eth2\" actions=output:\"s1-eth1\" 恭喜!這時候你已經成功在 SDN 環境讓兩台電腦溝通了!有沒有回想到第一天學CCNA時的興奮呢哈 ","date":"2020.01.23","objectID":"/introduction-to-mininet/:6:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"觀察 Controller 啟動時和 Switch 的交涉訊息 我們會透過 tcpdump 監聽封包,並輸出檔案至 Wireshark 讀取。因為容器中的檔案系統會隨著容器生命週期存亡,因此要透過 Volume 使容器中的檔案系統和本機的某個資料夾建立連結。砍掉剛才的容器,先建立一個存放tcpdump 輸出 pcap 檔的資料夾,用下面的指令建立一個新的: mkdir ~/tcpdump docker run -it --rm --privileged -e DISPLAY \\ -v /tmp/.X11-unix:/tmp/.X11-unix \\ -v /lib/modules:/lib/modules \\ -v ~/tcpdump:/tcpdump \\ iwaseyusuke/mininet 接著我們需要至少3個 CLI 視窗,方便操作。我是使用 Hyper Terminal 操作,分割4個視窗很方便。其中3個都exec到 Container 裡面。 首先啟動 Controller,讓控制器在 localhost 的 6633 port 執行: root@13559bb74f97:~# ovs-testcontroller ptcp:6633 \u0026 [1] 89 root@13559bb74f97:~# 然後另一個視窗開 tcpdump: root@13559bb74f97:~# tcpdump -i lo -w /tcpdump/take1.pcap tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes 接著啟動 Mininet: root@13559bb74f97:~# mn --topo single,3 --mac --switch ovsk --controller remote 這時候 ping 看看,可以通: mininet\u003e h1 ping -c4 h2 結束 tcpdump,將檔案放進 Wireshark 研究封包 ","date":"2020.01.23","objectID":"/introduction-to-mininet/:7:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"Startup 訊息 從 Wireshark 中觀察到,整個 Mininet 啟動時,Switch 和 Controller 進行了許多來往。分別如下: Type 傳送方向 描述 OFPT_HELLO Controller-\u003eSwitch Controller 傳送 Hello 訊息,裡面包含 Openlow 版本,例如版本 1.4 就是 0x05 OFPT_HELLO Switch-\u003eController Switch 回傳支援的版本 OFPT_FEATURES_REQUEST Controller-\u003eSwitch Controller 向 Switch 要求支援的功能 OFPT_SET_CONFIG Controller-\u003eSwitch 裡面有一個 Flags 欄位,我看起來應該是對 Switch 進行設定的部分 OFPT_FEATURES_REPLY Switch-\u003eController Switch 將支援的功能回應給 Controller,如圖 OFPT_PORT_STATUS Switch-\u003eController 通知 Controller ,交換器上 port 的狀態 接著是一些常見的封包, OFPT_PACKET_IN 和 OFPT_PACKET_OUT ,可能還會有 OFPT_FLOW_MOD。 Type 傳送方向 描述 OFPT_PACKET_IN Switch-\u003eController 當 Switch 不知道要把封包往哪送,則送給 Controller 處理 OFPT_PACKET_OUT Controller-\u003eSwitch Controller 送給 Switch 的封包 OFPT_FLOW_MOD Controller-\u003eSwitch 指示 Switch 新增 Flow table 欄位 OFPT_FLOW_EXPIRED Switch-\u003eController Flow 過期 下一篇,也是這次期末報告的下半段,會介紹 Ryu Controller 的簡易操作和程式碼。 ","date":"2020.01.23","objectID":"/introduction-to-mininet/:7:1","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"References Install Docker Engine - Community iwaseyusuke/mininet - Docker Hub Home · mininet/openflow-tutorial Wiki Mount volumes into a running container - Koki - Medium ovs-testcontroller - simple OpenFlow controller for testing Docker 實戰系列(三):使用 Volume 保存容器內的數據 - Larry・Blog ","date":"2020.01.23","objectID":"/introduction-to-mininet/:8:0","tags":["Mininet","OpenFlow"],"title":"OpenFlow 1.5 從入門到交報告 (一) - Mininet 基本概念","uri":"/introduction-to-mininet/"},{"categories":["SDN"],"content":"支援 OpenFlow 的 Controller 有很多種,Ryu 就是其中一員。Ryu 是一個開源的 OpenFlow Controller 開發框架,主要語言是 Python,由日本 NTT 的實驗室創造。截至目前為止,Ryu 支援到 OpenFlow 1.5。","date":"2020.01.04","objectID":"/introduction-to-ryu/","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"支援 OpenFlow 的 Controller 有很多種,Ryu 就是其中一員。Ryu 是一個開源的 OpenFlow Controller 開發框架,主要語言是 Python,由日本 NTT 的實驗室創造。截至目前為止,Ryu 支援到 OpenFlow 1.5。本篇同時是 2019 研究所課程報告下半部,上一篇介紹了 Mininnet 基本概念,有興去可以參照。 ","date":"2020.01.04","objectID":"/introduction-to-ryu/:0:0","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"安裝 Ryu pip install ryu Ryu 部分功能有依賴其他套件,可以一併安裝。 pip install -r \u003c(curl https://raw.githubusercontent.com/osrg/ryu/master/tools/optional-requires) 還有一些是必要的 Dependencies: apt-get install -y gcc python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev zlib1g-dev ","date":"2020.01.04","objectID":"/introduction-to-ryu/:1:0","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"或是,使用 Ryu Container 我很喜歡 Container 超好用,環境單純,不需要煩惱架設環境所踩到的雷,不過要想清楚的是 Container 間的網路怎麼連,目前我想讓 Mininet 容器能夠和 Ryu Controller 容器可以互連,因此使用預設的 bridge mode 即可。為了讓 Ryu 容器可以吃到我之後寫的 Python,所以要 Mount 一個 Volume 對應到主機的某資料夾。 mkdir -p ~/ryu_test ~/tcpdump # 開 Ryu docker pull osrg/ryu docker run -it --name ryu \\ -v ~/ryu_test:/ryu_test \\ --hostname ryu \\ osrg/ryu 接著到另一個視窗開 Mininet docker run -it --rm --privileged -e DISPLAY \\ --name mininet \\ -v /tmp/.X11-unix:/tmp/.X11-unix \\ -v /lib/modules:/lib/modules \\ -v ~/tcpdump:/tcpdump \\ --hostname mininet \\ iwaseyusuke/mininet 查看 Ryu 的 IP,是172.17.0.2: root@ryu:~# ip a 1: lo: \u003cLOOPBACK,UP,LOWER_UP\u003e mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 20: eth0@if21: \u003cBROADCAST,MULTICAST,UP,LOWER_UP\u003e mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever ","date":"2020.01.04","objectID":"/introduction-to-ryu/:2:0","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"執行 Simple Switch 運行 Ryu 的 Simple Switch: ryu-manager ryu.app.simple_switch_15, 測試 Controller 是否可以被 Mininet 連接。 root@ryu:~# ryu-manager ryu.app.simple_switch_15 loading app ryu.app.simple_switch_15 loading app ryu.controller.ofp_handler instantiating app ryu.app.simple_switch_15 of SimpleSwitch15 instantiating app ryu.controller.ofp_handler of OFPHandler 接著啟動 Mininet,控制器指定到 Ryu 的 IP: root@mininet:~# mn --topo single,3 --mac \\ --switch ovsk,protocols=OpenFlow15 \\ --controller remote,ip=172.17.0.2 ... mininet\u003e 你會看見 Ryu 輸出一堆封包訊息: packet in 1 00:00:00:00:00:01 33:33:ff:00:00:01 1 packet in 1 00:00:00:00:00:03 33:33:00:00:00:16 3 packet in 1 00:00:00:00:00:02 33:33:00:00:00:16 2 packet in 1 00:00:00:00:00:01 33:33:00:00:00:16 1 ... 最後 Pingall 測試 Hosts 之間可以互通。 mininet\u003e pingall *** Ping: testing ping reachability h1 -\u003e h2 h3 h2 -\u003e h1 h3 h3 -\u003e h1 h2 *** Results: 0% dropped (6/6 received) ","date":"2020.01.04","objectID":"/introduction-to-ryu/:3:0","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"Ryu 開發框架結構 下面這個程式還沒有任何功能,不過已經是一個最基本可以運行的 Ryu 程式。ryu.base.app_manager.RyuApp(*_args, **_kwargs) 這個 Class 是 Ryu 的 Base class,我們自己寫的 Class 必須繼承這個 Class。文件中規定,Constructor 中必須呼叫 Parent 的 Constructor,換句話說就是 __init__ 中必須呼叫 RyuApp.__init__,也就是super.__init__。 from ryu.base import app_manager class L2SW(app_manager.RyuApp): def __init__(self, *args, **kwargs): super(L2SW, self).__init__(*args, **kwargs) 接下來觀察 simple_switch 原始碼,藉此了解如何開發 Ryu Controller。 Code 在官方 Repo 中 ","date":"2020.01.04","objectID":"/introduction-to-ryu/:4:0","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"指定 OpenFlow 版本 這個例子指定了 OpenFlow 1.5 版本 from ryu.ofproto import ofproto_v1_5 ... OFP_VERSIONS = [ofproto_v1_5.OFP_VERSION] ","date":"2020.01.04","objectID":"/introduction-to-ryu/:4:1","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"Decorator @set_ev_cls(ev_cls, dispatchers=None) Ryu 讓我們透過 @set_ev_cls 這個 Decorated Method 來定義 Event Handeler 的角色,當該定義的 Event 發生時,就會執行 Decorator 下方的函式。這個 Decorated Method 接收兩個參數: ev_cls 和 dispatcher。ev_cls 這個 Event Class 參數要放入 OpenFlow 的 Messages and Structures,依照 OpenFlow 標準分為 Controller-to-Switch、Asynchronous 與 Symmetric,以 OpenFlow 1.5 為例,文件中就有列出所有的種類以及他們的作用。 dispatcher 參數用來決定要處理哪個 Negotiation Phase (Switch 與 Controller 交涉的階段),總共有四個階段,取決於你放的 ev_cls 為何。 Negotiation phase Description ryu.controller.handler.HANDSHAKE_DISPATCHER Sending and waiting for hello message ryu.controller.handler.CONFIG_DISPATCHER Version negotiated and sent features-request message ryu.controller.handler.MAIN_DISPATCHER Switch-features message received and sent set-config message ryu.controller.handler.DEAD_DISPATCHER Disconnect from the peer. Or disconnecting due to some unrecoverable errors. 例如 simple_switch_15.py 中,就包含兩個 Decorated Method。以下這段程式碼,ofp_event.EventOFPSwitchFeatures代表函式會在 Switch 回應 Feature Reply 時觸發,對應到的 Negotiation phase 即為 CONFIG_DISPATCHER。 @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): ofp_event.EventOFPPacketIn 是 Packet-In Message,意即當 Switch 收到進入的封包時,所發送給 Controller 的訊息。當 Switch 收到封包時觸發,對應到的 Negotiation phase 為 MAIN_DISPATCHER。 @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): ","date":"2020.01.04","objectID":"/introduction-to-ryu/:4:2","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"Add Flow 再來看看 Function 裡面的變數是如何使用的。 @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] self.add_flow(datapath, 0, match, actions) ev.msg.datapath 代表 Switch 物件 datapath.ofproto 與 datapath.ofproto_parser 代表著 OpenFlow protocol 物件 OFPMatch 用來指定 Flow Match,共40種。這裡沒有定義何 Match。 OFPActionOutput 用來決定封包要往哪個連接埠送出,主要參數是兩個 Flag,port 就是 Output port,max_len 指送給 Controller 的最大長度。OFPP_CONTROLLER 就是送到 Controller,OFPCML_NO_BUFFER 代表封包完整地給 Controller,這些值都是一個 Flag,可以在原始碼查看。 def add_flow(self, datapath, priority, match, actions): ofproto = datapath.ofproto parser = datapath.ofproto_parser inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) OFPInstructionActions 就是 OpenFlow 的 Instruction,種類由第一個參數設定,三選一如下,第二個參數放入 OFPInstructionActions。 OFPIT_WRITE_ACTIONS OFPIT_APPLY_ACTIONS OFPIT_CLEAR_ACTIONS OFPFlowMod 用來讓 Controller 改變 Flow Table。 table_id 如果有要指定 Table 來放入 Entry 可使用 instructions 中用來傳入 OFPInstructionActions 最後透過 datapath.send_msg(mod) 將 OpenFlow 訊息送到交換器 ","date":"2020.01.04","objectID":"/introduction-to-ryu/:4:3","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["SDN"],"content":"References Getting Started — Ryu 4.30 documentation osrg/ryu - Docker Hub Install Mininet and Ryu Controller osrg/ryu: Ryu component-based software defined networking framework openflow-switch-v1.5.1.pdf 張衛峰,深度解析SDN:利益、戰略、技術、實踐,台灣:碁峰,2014,第 68 至 83 頁。 ","date":"2020.01.04","objectID":"/introduction-to-ryu/:5:0","tags":["Ryu","OpenFlow","Python"],"title":"OpenFlow 1.5 從入門到交報告 (二) - Ryu","uri":"/introduction-to-ryu/"},{"categories":["Infra"],"content":"自前陣子學校電腦教室汰換之電腦,有幾台送給實驗室利用,我藉這個機會使用那些資源架設一台 ESXi。架設完成後,接下來考量到一些需求與環境因素,開始設計網路的架構。這篇會使用 pfSense、OpenVPN、ESXi 伺服器和一台 Cisco 2950 Switch,進行網路架構設計。","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"前陣子學校電腦教室汰換之電腦,有幾台送給實驗室利用,我藉這個機會使用那些資源架設一台 ESXi。架設完成後,接下來考量到一些需求與環境因素,開始設計網路的架構。這篇會使用 pfSense、OpenVPN、ESXi 伺服器和一台 Cisco 2950 Switch,進行網路架構設計。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:0:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"需求與現有環境 對於 Lab 環境的幾項需求,分別如下: 所有 VM 皆可連接外網,因為如 EVE-NG 在安裝時便需要連接外部下載 Package 對於 VM 的 DHCP,如果伺服器提供讓一些同學使用,在上面佈建的 VM 有 DHCP 較方便 能夠從校內與校外網路 VPN 進入 Lab 環境 不能直接從 WAN 連到 ESXi 的管理介面 可以透過 VPN 間接連到管理介面 如果使用實驗室裡面的電腦存取伺服器,無須繞過學校的網段,可透過自己的 LAN 直連 必要的話始可使用 Port Forwarding 現有環境有一些限制,不過不成問題: 學校的網管系統,綁定學校 Public IP 與 MAC 要進行遠端,只能使用 RDP,Team Viewer 與 Google Remote Desktop 皆會被擋 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:1:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"架構圖 規劃時,我畫了一張架構圖,方便日後管理與交接。 由於 Server 的主機板原本就帶有兩個網路介面,因此可以使用 pfSense,將一個 NIC 作為 WAN,另一個做為 LAN。pfSense 在架構中擔任 Firewall 與 Router 的腳色,提供包含 NAT、Routing、VPN 和 DHCP Sever的功能。因為我只跟學校要到一個 IP,不想買一台 Firewall,所以 pfSense 就是首選。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:2:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"Cisco Switch 設定 Switch 的設定非常簡單,我透過 VLAN 拆分成邏輯上的兩台不同的交換器,一個負責 LAN,同時是交換器的管理 VLAN;另一個連接學網對外(Uplink)與 Server 規劃的 WAN 端。 # int ra fa 0/1 - 12 (config-if)# sw mode access (config-if)# sw access vl 10 # int ra fa 0/13 - 24 (config-if)# sw mode access 但是第24 port 要接到學校的 Swich,我怕對端有設定BPDU Guard,因此特定對該port開啟 BPDU filter,阻止我的交換器發出 BPDU。 # int fa0/24 (config-if)# spanning-tree bpdufilter en 同時將 VTP 的模式切換成 Transparent,總之,避免一切私接 Switch 對學校網路的影響。 # vtp mode transparent ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:3:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"安裝 pfSense 安裝 pfSense 的過程參考自官方文件,這邊順便翻譯以內化學習。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:4:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"vSphere 網路設定 在建立 VM 前,需要在 ESXi 上新增2個 vSwitch 和兩個連接埠群組(Port Group),一個做為 WAN,另一個作為 LAN。 在左側選擇「網路」,切換至「虛擬交換器」標籤,「新增虛擬交換器」。 圖中是把 vSwitch0 作為 LAN,兩個 vSwitch 分別連接到不同的實體介面。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:4:1","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"建立連接埠群組 切換到「連接埠群組」標籤,新增兩個群組,分別連接到 LAN 和 WAN 兩個 vSwitch。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:4:2","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"建立 pfSense VM 到這裡下載映像檔,64位元系統請選「AMD64」,下面選「CD Image (ISO) Installer」。其餘步驟和一般架設 VM 是一樣的。注意 VM 的網卡,種類選 VMXNET 3 的效能最好,但是第一次啟動時要手動選擇 LAN/WAN 介面。 接著開啟 VM,看到以下畫面 當看到下面畫面時,可以先輸入 2 設定 IP 了。 接著連入 pfSense 的 Web UI,預設登入帳密是 admin,密碼pfsense,安全考量,請一定要更改預設密碼。接著就可以安裝 VMware tools。 不過我的環境是學網,DNS 必須使用學校所規定的,能夠成功解析地址才能讓 Package Manager 連得上。 安裝 VMware tools。切換到 Available Packages 標籤,搜尋 Open-vm-tools ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:4:3","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"設定 OpenVPN 這裡一樣是翻譯自文件。 這邊使用的是「最基本」的設定,使用 local user 的帳密進行驗證。pfSense 的 VPN \u003e OpenVPN 底下,有 Wizard 的選項,是比較簡易設置 VPN 的方式。第一步選擇 Local User Access,User 可以到 System \u003e User Manager 新增。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"新增 CA 憑證 接著新增一個自簽憑證,設定可以參考下面 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:1","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"新增 Server 憑證 資料和 CA 的類似 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:2","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"OpenVPN Server 設定 TLS Authentication 請打勾 Tunnel Network 請輸入一個網段,該網段不能存在於現有網路和路由表中。 Local Network 就是 VPN 進來後可以存取的網段,依照上面的架構圖就是 172.30.0.0/24。 接著下一步。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:3","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"Firewall Rules 底下兩個選項都打勾,否則不會通。 接著設定便完成了。到 Firewall \u003e rules 確認防火牆規則有沒有新增一條規則:Source any 到 destination WAN,port 則是剛才設定中的 OpneVPN port,預設是1194。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:4","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"新增 User System \u003e User Manager 底下,選+號新增 user ,CA 請選擇剛才建立的。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:5","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"OpenVPN Client Export Package 這個工具非常好用,它可以針對特定使用者,輸出一個安裝檔(帶有使用者和這個VPN的相關設定),所以使用者只要安裝完即可使用。 到 System \u003e Packages, Available Packages 標籤,搜尋 openvpn,安裝 OpenVPN Client Export Package。 接著到 VPN \u003e OpenVPN, Client Export標籤底下,會發現剛才建立的使用者。 如果是 Windows,下載Current Windows Installer底下的選項,給 User 安裝即可。千萬不要單獨下載設定檔,另外安裝 OpenVPN Client 然後輸入設定,我嘗試過這樣 會失敗。盡量使用 Installer。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:5:6","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"結語 目前完成了 Lab 的架構,所有網路需求也達成了。我可以從任何地方 VPN 到我的 LAN,登入 ESXi 介面,甚至可以對實體的 Cisco Switch 進行設定。如果需要上傳龐大的 Image,我可以遠端到我的 Lab PC,作為跳板機,從該 PC 透過 LAN 上傳檔案至 Server 而不會消耗 WAN 流量。而日後任何新的 VM 被建立,也都可以自動取得 IP 和指定的 DNS resolver,已達成更新與安裝 Packages。 ","date":"2019.12.18","objectID":"/esxi-server-in-lab-structure/:6:0","tags":["VMware","vSphere","pfSense","OpenVPN"],"title":"實驗室自架 Lab 環境 — 網路架構設計","uri":"/esxi-server-in-lab-structure/"},{"categories":["Infra"],"content":"自從離開前一間實習的公司,我就缺乏一個可以做 Lab 的地方,深刻感受到「伺服器戒斷症」。想當初公司是天天有 Server 可以使用,有 Cisco UCS、Hyperflex 和 Supermicro 的等等,離開後變成只能偶爾用 AWS Educate 帳號開個限制滿滿的 EC2。這次學校電腦教室有汰換下來的幾台電腦,聽說當初買一台要價 10 萬元,不過也是好幾年前了。規格不會太差,因此開始動起歪腦筋,抱了一台到座位旁開始研究。","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"自從離開前一間實習的公司,我就缺乏一個可以做 Lab 的地方,深刻感受到「伺服器戒斷症」。想當初公司是天天有 Server 可以使用,有 Cisco UCS、Hyperflex 和 Supermicro 的等等,離開後變成只能偶爾用 AWS Educate 帳號開個限制滿滿的 EC2。這次學校電腦教室有汰換下來的幾台電腦,聽說當初買一台要價 10 萬元,不過也是好幾年前了。規格不會太差,因此開始動起歪腦筋,抱了一台到座位旁開始研究。 ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:0:0","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"規格 規格還不差: CPU: Intel Xeon E5 2620 MB: ASUS Z9PE-D8 WS 支援雙CPU、四通道、單科 CPU 支援到 64GB RAM、自帶兩個 1G 網路介面 RAM: 原本很寒酸的差了一條 8GB (PC3-12800),我打算怒擴到 32GB 顯卡:NVIDIA GeForce GTX 780, 其實我裝 ESXi 根本不太需要顯卡 SSD: 200GB HDD: 1TB 噢易還原卡 (畢竟電腦教室的電腦,正常) ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:1:0","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"前置作業 首先當然是拔掉還原卡,然後格式化硬碟,製作開機 USB 來安裝 ESXi。到 VMware 官網下載了 VMware vSphere Hypervisor 6.7 (ESXi),接著使用 rufus 做成開機片。 附帶一提,ESXi 是可以免費使用的,灌完記得去剛才的下載頁面,上面會有免費版序號,再從 ESXi web GUI 貼上即可,沒有期限,但不能搭配 vCenter。 回想以前灌 ESXi 時,安裝過程會做格式化,所以我就懶的手動自己格式化一遍。 :::danger 結果, 這才是麻煩的開始。 ::: ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:2:0","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"踩雷 安裝 ESXi 時,看進度條看很爽,結果重開機後出現: 這簡直被澆冷水! Google 之後發現和 Windows 有關,這個提問有人回答。由於這台電腦的硬碟是安裝 Win 10,而 Win 10 會複寫 MBR分割表,使其有數值而非為0,所以造成 ESXi 錯誤,如下圖。 好,所以這時候只得乖乖格式化硬碟了,而且經過查詢,格式化不見得會清除 MBR 的部分,為了保險起見,MBR 那 512 Bytes 最好全部變成\"0\"。但是作業系統已經被我弄掉了,要格式化除非拔下來,不然就是製作 Linux Live 的開機片,使用指令操作格式化。因為該硬碟看起來不好拔,我決定使用後者的方法。(幸好今天一口氣帶了3個隨身碟) ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:3:0","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"救星:GParted GParted 是一個 partition editor,有圖形介面和 CLI,可以安裝到現有OS,也可以做成開機片 (正是我想要的!)。我到這裡下載 iso 映像檔,加上LinuxLive USB Creator,把 USB 做成開機片。從 BIOS 設定 USB 開機後出現畫面了。 第一次進到 GParted 選擇第一個選項就被雷了,馬上沒有畫面訊號。後來我選了第三個選項 Other modes of GParted。 接著選擇 GParted Live Safe graphics settings,如果你發現你的電腦也不能開啟 GParted,請嘗試這個選項。 成功進入了,可以敲 command 了 首先列出所有硬碟: ls /dev/[sh]d* /dev/sda /dev/sdb ... 接著猜猜看,我覺得目標是 /dev/sda/ fdisk -l /dev/sda ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:4:0","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"處理MBR 看容量,果然是 sda,我先把他的 MBR 抹成 0。 dd if=/dev/zero of=/dev/sda bs=512 count=1 #然後再確認一次 fdisk -l /dev/sda 你會發現 GPT Table is corrupted 的錯誤訊息消失了。 ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:4:1","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"切磁區 我想用 GPT,所以接著使用 parted工具進行磁區分割,這目的是為了讓 ESXi installer 更有可能認出這顆硬碟。所以我只切一個磁區,指定範圍從 1 到 240GB,它會自動掠過 MBR 和 GPT的部分,所以START可以放心打上\"1\"。 也可以用下面這行指令代替: parted /dev/sda mklabel gpt mkpart P1 fat32 1 240GB :::warning 注意: 使用這些指令請格外小心! ::: ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:4:2","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Infra"],"content":"格式化 接著使用 mkfs 格式化,保險起見使用 FAT32,不要用 ext2 或 ext3,文件有提到這可能會讓 ESXi 安裝失敗,至少我使用 FAT32 最後成功安裝了。VMware 會把硬碟格式化成 VMFS,但是安裝前也要注意格式,減少踩雷機率。 mkfs.vfat -F 32 /dev/sda1 重新安裝,成功。看到熟悉的黃灰畫面。 折騰半天,終於成功在 LAB 擁有自己的 Server 了 不用花自己的電費和硬體$$ ","date":"2019.12.13","objectID":"/self-hosted-esxi-server-in-lab/:4:3","tags":["VMware","vSphere"],"title":"在實驗室自架 VMware ESXi — 使用電腦教室汰換之電腦","uri":"/self-hosted-esxi-server-in-lab/"},{"categories":["Frontend"],"content":"VuePress 原本是提供官方文件網站一個產生靜態頁面的框架,因此許多部落格應有的功能都是缺乏的。例如標籤、分類和留言等都需要自己做,因為我想掌控排版和背後的邏輯,所以就寫起來囉。這篇會討論 Components 的應用、標籤(Tags)和分類(Categories)的實做部分。範例程式碼就是直接從這個部落格的原始碼節錄的。","date":"2019.12.11","objectID":"/vuepress-blog-3/","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"「使用 VuePress 架設部落格以及被折騰」的系列文,是我架設本部落格的過程筆記,希望日後對有需要使用 VuePress 架設 Blog 的朋友有一點點幫助。內文有誤的部分,敬請指正。 VuePress 原本是提供官方文件網站一個產生靜態頁面的框架,因此許多部落格應有的功能都是缺乏的。例如標籤、分類和留言等都需要自己做,因為我想掌控排版和背後的邏輯,所以就寫起來囉。這篇會討論 Components 的應用、標籤(Tags)和分類(Categories) 的實做部分。範例程式碼就是直接從這個部落格的原始碼節錄的。 ","date":"2019.12.11","objectID":"/vuepress-blog-3/:0:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"Global Computed 與 Frontmatter ","date":"2019.12.11","objectID":"/vuepress-blog-3/:1:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"Frontmatter 我在本系列文第一篇提過 Frontmatter,就是在每篇 Markdown 最前面的 YAML 區塊,而 Frontmatter 除了 YAML,也可以使用 JSON 或 TOML。下面是本篇文章的 Frontmatter: ---lang:zh-TWdescription:VuePress 原本是提供官方文件網站一個產生靜態頁面的框架,因此許多部落格應有的功能都是缺乏的。例如標籤、分類和留言等都需要自己做,因為我想掌控排版和背後的邏輯,所以就寫起來囉。這篇會討論 Components 的應用、標籤(Tags)和分類(Categories)的實做部分。範例程式碼就是直接從這個部落格的原始碼節錄的。sidebar:autotags:[\"VuePress\"]category:Frontend--- Frontmatter 一部分是 VuePress 預設的,而其餘可以自行新增資料。VuePress 預設的幾項設定如下: title 型態:string 預設值為 h1_title || siteConfig.title 例如本篇標題「VuePress 部落格架設與折騰 (三)」,本站名稱(定義於.vuepress/config.js)為「Oscar’s Pathways」,就會呈現 「VuePress 部落格架設與折騰 (三) | Oscar’s Pathways」。 lang 型態:string 預設值為 en-US 設定網站語系,這是讓瀏覽器去讀取的,和SEO比較有關。中文的話就是zh-Hant-TW,注意大小寫與 Dash 的部分,為什麼不是zh-TW呢?我一開始也以為是zh-TW,這部分請看 IETF,錯不了的:RFC 4646。 description 型態:string 預設值為 siteConfig.descripton 我把這個變數當作文章大綱和首頁預覽功能使用。 其他官方定義的變數可以到這裡查看。 ","date":"2019.12.11","objectID":"/vuepress-blog-3/:1:1","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"Global Computed Global Computed 是一系列以 $ 開頭為名的變數,都是 Object,皆可從任何頁面讀取這些變數。下面列出我接下來寫標籤與分類會用到的變數。 $site 這個變數是「全站」的所有變數,如果有100篇文章,\"pages\"就會是一個包含100個元素的 Array。 { \"title\": \"VuePress\", \"description\": \"Vue-powered static site generator\", \"base\": \"/\", \"pages\": [ { \"lastUpdated\": 1524027677000, \"path\": \"/\", \"title\": \"VuePress\", \"frontmatter\": {} }, ... ] } $page 這個變數是「本頁」的變數,也就是你所在的頁面的相關參數都在裡面,包含自己定義的 Frontmatter。 { \"title\": \"Global Computed\", \"frontmatter\": {}, \"regularPath\": \"/guide/global-computed.html\", \"key\": \"v-d4cbeb69eff3d\", \"path\": \"/guide/global-computed.html\", \"headers\": [ { \"level\": 2, \"title\": \"$site\", \"slug\": \"site\" }, { \"level\": 2, \"title\": \"$page\", \"slug\": \"$page\" }, ... ] } $frontmatter 等同$page.frontmatter。 我當初在嘗試時經常在 Vue 元件的Script區塊中export default底下使用console.log(this.$page)查看變數。如果要查看$site就是console.log(this.$site),以此類推。 xxx.vue 某元件內 \u003cscript\u003e export default { computed: { category () { console.log(this.$page); return this.$page.frontmatter.category }, } } \u003c/script\u003e ","date":"2019.12.11","objectID":"/vuepress-blog-3/:1:2","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"標籤 接下來開始製作標籤功能。參考其他部落格(像是用 Hexo 製作的那種),一篇文章包含多個標籤,而我可以點選標籤查看具有該標籤的所有文章,同時可以顯示該標籤所包含的文章總數。 首先來定義 Frontmatter,因為可能有多個標籤,所以用陣列。 ---...tags:[\"Tag1\",\"Tag2\"]...--- 接著我希望在文章、側邊欄和首頁最新文章都可以看到每篇文章包含的標籤,因此需要製作 Components。 首先是文章頂端的標籤,結果如下(藍色方塊的部分): 撰寫元素: TagLinks.vue \u003ctemplate lang=\"html\"\u003e \u003cdiv class=\"taglinks\"\u003e \u003crouter-link v-for=\"tag in $page.frontmatter.tags\" :key=\"tag\" :to=\"{ path: `/tags/${tag}`}\"\u003e \u003cBadge :text=\"tag\"/\u003e \u003c/router-link\u003e \u003c/div\u003e \u003c/template\u003e \u003cscript\u003e export default { name: \"TagLinks\", } \u003c/script\u003e \u003cstyle lang=\"stylus\"\u003e .taglinks \u003e a padding-right 5px font-family 'Noto Sans TC Medium' \u003c/style\u003e template裡面,router-link標籤是類似 HTML 的\u003ca\u003e\u003c/a\u003e提供超連結的功能,目的地則是定義在to=,如果要使用變數只要在前面加上冒號即可::to=。注意例子中:to=\"{ path … }\",path 提供相對路徑的效果,一定要用大括號包起來,後面是相對路徑,這裡使用到tag變數,要用${tag}的形式嵌入。假設我的網站 Root 為 https://lytzeng.github.io,這個連結就會變成https://lytzeng.github.io/tags/tagXXX。 在來是 v-for,它讓這個標籤可以跑 for loop,每跑一次就產生一個router-link,邏輯就是有 n 個標籤在陣列 $page.frontmatter.tags 中,就產生 n 個藍色標籤方塊。key 則是讓 Vue 內部演算法可以增加物件的重複使用率,這個不一定要設置。 接著是側邊欄顯示所有標籤的部分,同時每個標籤都顯示包含的文章總數,完成後如下圖。 由於這裡是顯示全站的標籤,故使用$site而不是$page。我們先建立一個元件叫Tags.vue,先把整段程式碼放上來,方便討論。 Tags.vue \u003ctemplate\u003e \u003cdiv class=\"tags\"\u003e \u003cspan v-for=\"tag in Object.keys(tags)\"\u003e \u003cBadge :id=\"tag\"\u003e \u003crouter-link :to=\"{ path: `/tags/${tag}`}\"\u003e \u003cdiv\u003e{{tag}} \u003cdiv class=\"tagcount\"\u003e{{tags[tag].length}}\u003c/div\u003e\u003c/div\u003e \u003c/router-link\u003e \u003c/Badge\u003e \u003c/span\u003e \u003c/div\u003e \u003c/template\u003e \u003cscript\u003e export default { computed: { tags() { let tags = {}; for (let page of this.$site.pages) { for (let index in page.frontmatter.tags) { const tag = page.frontmatter.tags[index]; if (tag in tags) { tags[tag].push(page); } else { tags[tag] = [page]; } } } return tags; } } }; \u003c/script\u003e 這裡開始用到一個作法:「從 template 的 HTML 中呼叫 Scripts 區塊中的函式」。 Scripts 區塊的寫法也是被定義的,接下來開始說明。 ","date":"2019.12.11","objectID":"/vuepress-blog-3/:2:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"Scripts script 中一定要使用 export default ,function 可以放在幾個地方:computed, methods, data, mounted 等等,這部分可以直接參考 Vue 的文件,下面簡單說明一下他們的差別。 Computed :::v-pre 上段程式碼的寫法屬於 Getter,目的在於避免 template 存在複雜的邏輯,以免未來看 code 會看不懂。注意在 template 區塊中使用 global computed 時可以直接使用像 {{$page.frontmatter}} 的寫法,但在script 中必須使用 this.$page.frontmatter 這種方法,注意this的差別。 ::: 而 computed 有 Getter 和 Setter 的寫法,需要搭配 v-model,如下面是文件範例: // ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ... data data 的部分可以直接用來放定義的變數,但是注意 data 內的寫法,一定要使用 function,再把 Object 丟出去。 data() { return { message: 'about page' } } methods 其實上面提到,computed 下的 function 其實也可以改放在 methods 裡,它們的行為是一樣的。差別在於:computed 會 cache,除非裡面的參數有改變(比如透過 setter ),不然每次call computed 內的 function 所回傳的值都會是第一次的運算結果。而methods則是每次呼叫、每次重新計算。使用方法如下: // in component methods: { reverseMessage: function () { return this.message.split('').reverse().join('') } } :::v-pre 在 template 使用時插入 {{ reversedMessage() }} 即可。 \u003cp\u003eReversed message: \"{{ reverseMessage() }}\"\u003c/p\u003e ::: mounted 這個通常用在頁面載入後,對 DOM 進行操作。如果要在頁面載入前對 DOM 操作,使用beforeMount。只能在這兩個底下對 DOM 進行操作。 ","date":"2019.12.11","objectID":"/vuepress-blog-3/:2:1","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"應用 回到剛才的 Tags.vue ,這時元件已經完成,我們可以嵌入到 Sidebar.vue 中。 Sidebar.vue //... \u003ctemplate\u003e \u003cTags/\u003e \u003c/template\u003e \u003cscript\u003e //... import Tags from \"@theme/components/Tags.vue\" //... export default { name: \"Sidebar\", // ... components: { \"....\", Tags }, //... }; \u003c/script\u003e 為了讓每個標籤都有文章數量顯示,Tags.vue 這段程式碼可以計算每個標籤的文章數量。 computed: { tags() { let tags = {}; for (let page of this.$site.pages) { for (let index in page.frontmatter.tags) { const tag = page.frontmatter.tags[index]; if (tag in tags) { tags[tag].push(page); } else { tags[tag] = [page]; } } } return tags; } } 接著router-link讓我們點擊標籤時可以到達標籤頁面,顯示相同標籤文章。 \u003crouter-link :to=\"{ path: `/tags/${tag}`}\"\u003e \u003c!-- ... --\u003e \u003c/router-link\u003e 所以要製作標籤頁面,先新增元件 GetPagesByTag.vue,這個元件將嵌入到 Markdown 中,我會為每個標籤建立一個 Markdown,在裡面加入 \u003cGetPagesByTag/\u003e,透過標題決定顯示哪一種標籤。 :::v-pre GetPagesByTag.vue \u003ctemplate\u003e \u003cdiv class=\"tag-posts\"\u003e \u003cul\u003e \u003cli v-for=\"page in thisTag\"\u003e \u003crouter-link :to=\"{ path: page.path}\"\u003e \u003ch1\u003e{{page.title}}\u003c/h1\u003e \u003cp\u003e{{page.frontmatter.description}}\u003c/p\u003e \u003c/router-link\u003e \u003c/li\u003e \u003c/ul\u003e \u003c/div\u003e \u003c/template\u003e \u003cscript\u003e export default { name: \"GetPagesByTag\", computed: { thisTag() { let tags = {}; for (let page of this.$site.pages) { for (let index in page.frontmatter.tags) { const tag = page.frontmatter.tags[index]; if (tag in tags) { tags[tag].push(page); } else { tags[tag] = [page]; } } } return tags[this.$page.title]; } } }; \u003c/script\u003e ::: ","date":"2019.12.11","objectID":"/vuepress-blog-3/:2:2","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"類別 實作類別功能時,我只想使用單層的類別,有些部落格可以有像檔案系統般的類別,但我只想做單層。先端上結果: 就是那個資料夾圖案,做法和標籤是一樣的,只是一篇文章只會有一個類別,所以不用跑迴圈。因為我想讓類別和標籤顯示在同一行,所以寫在同一個元件比較方便,一樣寫在 TagLinks.vue 中。 我在 Template 中新增這段 :::v-pre \u003crouter-link :key=\"category\" :to=\"{ path: `/categories/#${category}` }\"\u003e \u003cdiv style=\"display: inline;\"\u003e \u003csvg fill=\"#14cdef\" focusable=\"false\" preserveAspectRatio=\"xMidYMid meet\" style=\"display: inline-block; vertical-align: middle; will-change: transform;\" xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 32 32\" aria-hidden=\"true\"\u003e\u003cpath d=\"M11.17 6l3.42 3.41.58.59H28v16H4V6h7.17m0-2H4a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2H16l-3.41-3.41A2 2 0 0 0 11.17 4z\"\u003e\u003c/path\u003e\u003ctitle\u003eFolder\u003c/title\u003e\u003c/svg\u003e \u003c/div\u003e {{ category }} \u003c/router-link\u003e ::: 附帶一提,中間那段是 SVG 圖示(資料夾小圖示),網頁中的 icon 最好盡量用 SVG ,載入速度較圖片快,也支援無線的縮放不失真。 Scripts 中新增這段: export default { name: \"TagLinks\", computed: { category () { return this.$page.frontmatter.category }, } } 再來是製作類別總覽的頁面,我想使用比較特別的做法,整個頁面會顯示所有類別以及底下所有文章,在其他頁面點選特定類別時,跳到類別總覽的特定類別位置 (可以點這裡先看結果)。怎麼做到呢?在目的地網址加入#element-id 就可,element-id 是你想要捲動到該物件的 id。類別總覽元件:Categories.vue,注意有深色標示的程式碼部分。因為要跑過所有文章與類別,這裡使用一層 for loop。 \u003ctemplate\u003e \u003cdiv class=\"categories\" v-if=\"categories\"\u003e \u003cdiv class=\"tilesWrap\"\u003e \u003cdiv v-for=\"cat in Object.keys(categories)\" :id=\"cat\" class=\"category-card-container\"\u003e \u003cdiv class=\"category-card\"\u003e \u003cdiv class=\"category-name\"\u003e{{ cat }}\u003c/div\u003e \u003crouter-link v-for=\"page of categories[cat]\" :to=\"{ path: `${page.path}`}\"\u003e \u003cdiv class=\"cat-page-title\"\u003e{{ page.title }}\u003c/div\u003e \u003cp\u003e{{ page.frontmatter.description }}\u003c/p\u003e \u003c/router-link\u003e \u003c/div\u003e \u003c/div\u003e \u003c/div\u003e \u003c/div\u003e \u003c/template\u003e \u003cscript\u003e export default { name: \"Categories\", computed: { categories() { let categories = {}; let posts = this.$site.pages.filter(p =\u003e { return p.path.indexOf(\"/posts/\") \u003e= 0; }); this.$page[\"headers\"] = []; for (let post of posts) { const cat = post.frontmatter.category; let postArr = [post]; if (cat in categories) categories[cat].push(post); else { categories[cat] = postArr; this.$page[\"headers\"].push({ level: 2, slug: cat, title: cat }) } } console.log(this.$page) return categories; } } }; \u003c/script\u003e 終於到這篇結尾,目前我都省略 CSS 樣式的部分,也就是\u003cstyle\u003e裡面的內容不提,之後會單獨一篇討論如何客製化 VuePress 的顏色設計等等,以及 Stylus 的部分。 這篇東西比較多,如果之後發現有遺漏的部分,我會回來補上。 ","date":"2019.12.11","objectID":"/vuepress-blog-3/:3:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (三):Global Computed、標籤與分類功能、Components 的應用","uri":"/vuepress-blog-3/"},{"categories":["Frontend"],"content":"本篇先介紹 VuePress 的 Components (元件)。因為我接觸 VuePress 時還沒開始學 Vue,在架部落格同時想透過 VuePress 間接摸索 Vue.js,這裡簡單介紹元件的組成以及如何在 VuePress 頁面使用自己寫的元件。","date":"2019.12.10","objectID":"/vuepress-blog-2/","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (二):Components","uri":"/vuepress-blog-2/"},{"categories":["Frontend"],"content":"「使用 VuePress 架設部落格以及被折騰」的系列文,是我架設本部落格的過程筆記,希望日後對有需要使用 VuePress 架設 Blog 的朋友有一點點幫助。內文有誤的部分,敬請指正。 本篇先介紹 VuePress 的 Components (元件)。因為我接觸 VuePress 時還沒開始學 Vue,在架部落格同時想透過 VuePress 間接摸索 Vue.js,這裡簡單介紹元件的組成以及如何在 VuePress 頁面使用自己寫的元件。 ","date":"2019.12.10","objectID":"/vuepress-blog-2/:0:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (二):Components","uri":"/vuepress-blog-2/"},{"categories":["Frontend"],"content":"Components 概念 VuePress 的 Vue 元件存放在.vuepress/theme/components 下,使用的方式有二:直接在 Markdown 文件嵌入,或在其他 component 再嵌入另一個 component,會依情境而有不同選擇。 .vuepress/theme/components 底下的每個 .vue 檔都是使用 Vue 的 Static File Component,其分為三個部分:template、script和style。範例如下: \u003ctemplate\u003e \u003cmain class=\"page\"\u003e \u003cslot name=\"top\" /\u003e \u003cContent class=\"theme-default-content\" /\u003e \u003cPageNav v-bind=\"{ sidebarItems }\" /\u003e \u003cslot name=\"bottom\" /\u003e \u003c/main\u003e \u003c/template\u003e \u003cscript\u003e import PageEdit from '@theme/components/PageEdit.vue' import PageNav from '@theme/components/PageNav.vue' export default { components: { PageEdit, PageNav }, props: ['sidebarItems'] } \u003c/script\u003e \u003cstyle lang=\"stylus\"\u003e $page-max-width = 900px .page-edit margin 0 padding 0 max-width $page-max-width @require '../styles/wrapper.styl' .page padding-bottom 2rem display block @media (min-width: $MQNarrow) .theme-default-content:not(.custom) max-width $page-max-width !important padding 2rem 5% 2rem 10% \u003c/style\u003e 元素裡面可以只有 template,也可以沒有script或style。 \u003ctemplates\u003e標籤內放的就是 HTML,當然也支援 pug。注意這裡的 HTML 雖然不是完整頁面的 HTML,但是建議撰寫之前,一切元素都使用\u003cdiv\u003e\u003cdiv/\u003e標籤包起來,以免出現\"Component template should contain exactly one root element. \" 的錯誤。 \u003cscript\u003e標籤內放的是 Javascript 或 TypeScript,頁面邏輯運算的部分。這裡的格式是被規定的,一定要使用 export default,下一篇會更詳細介紹這裡面一些 Object 的功能。 \u003cstyle\u003e就是頁面的樣式,可以是 CSS 或 Stylus,Vue 比較推薦用 Stylus。如果使用 Stylus,可以去 @require 其他 .styl 檔。 VuePress 預設的樣式放在 .vuepress/theme/styles 底下。 接著是使用元件的兩種方法。 ","date":"2019.12.10","objectID":"/vuepress-blog-2/:1:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (二):Components","uri":"/vuepress-blog-2/"},{"categories":["Frontend"],"content":"使用元件 以下兩種方法需要視需求決定使用其中一種,第一種方法適用於大量頁面都需要使用同一個元件,第二種適合用於多個小元件組合成單一大元件的情況。 ","date":"2019.12.10","objectID":"/vuepress-blog-2/:2:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (二):Components","uri":"/vuepress-blog-2/"},{"categories":["Frontend"],"content":"直接將元件嵌入到 Markdown .vuepress/theme/components裡面有一些是預設的 Components,可以去更動它,但千萬別移除。如果有額外自訂的元件(.vue檔),要在.vuepress/enhanceApp.js import,如果enhanceApp.js不存在,可以自行新增。這裡的例子我加入了兩個自己寫的元件:TagLinks 和 PageEdit。 enhanceApp.js import TagLinks from './theme/components/TagLinks.vue' import PageEdit from './theme/components/PageEdit.vue' export default ({ Vue, // the version of Vue being used in the VuePress app options, // the options for the root Vue instance router, // the router instance for the app siteData // site metadata }) =\u003e { Vue.component('TagLinks', TagLinks) Vue.component('PageEdit', PageEdit) } enhenceApp.js的原件所有頁面都可以存取,有全域的概念。在 Markdown 只要加入元件名稱的 HTML 標籤即可。 \u003cTagLinks/\u003e \u003cPageEdit/\u003e ","date":"2019.12.10","objectID":"/vuepress-blog-2/:2:1","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (二):Components","uri":"/vuepress-blog-2/"},{"categories":["Frontend"],"content":"在其他元件再嵌入另一個元件 參考以下元件的script區塊,這個元件嵌入了SidebarGroup 和 SidebarLink 兩個元件: \u003cscript\u003e import SidebarGroup from '@theme/components/SidebarGroup.vue' import SidebarLink from '@theme/components/SidebarLink.vue' import { isActive } from '../util' export default { name: 'SidebarLinks', components: { SidebarGroup, SidebarLink }, props: [ 'items', 'depth', // depth of current sidebar links 'sidebarDepth' // depth of headers to be extracted ], data () { return { openGroupIndex: 0 } }, } \u003c/script\u003e 這樣只有這個元件可以使用SidebarGroup 和 SidebarLink,接著在template區塊使用元件,也是加入元件名稱的 HTML 標籤,這裡只須看\u003cSidebarGroup/\u003e 和 \u003cSidebarLink/\u003e。 \u003ctemplate\u003e \u003cul class=\"sidebar-links\" v-if=\"items.length\" \u003e \u003cli v-for=\"(item, i) in items\" :key=\"i\"\u003e \u003cSidebarGroup v-if=\"item.type === 'group'\" :item=\"item\" :open=\"i === openGroupIndex\" :collapsable=\"item.collapsable || item.collapsible\" :depth=\"depth\" @toggle=\"toggleGroup(i)\" /\u003e \u003cSidebarLink v-else :sidebarDepth=\"sidebarDepth\" :item=\"item\" /\u003e \u003c/li\u003e \u003c/ul\u003e \u003c/template\u003e 中間有一些使用變數和 Template Syntax 的方法,下一篇將透過撰寫標籤功能以及分類功能示範如何使用 Vue 元件進行參數傳遞。 ","date":"2019.12.10","objectID":"/vuepress-blog-2/:2:2","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (二):Components","uri":"/vuepress-blog-2/"},{"categories":["Frontend"],"content":"這個部落格決定使用 VuePress 架設,過程中遇到種種問題以及官方文件寫不清楚的地方,因此這一系列 VuePress 筆記同時也希望幫助到剛接觸的人。會想使用 VuePress,一方面是因為想用 Vue 來寫,另一方面則是需要高度的 Customization。使用其他框架雖然很多功能都已經實現,但是前端很多東西如顏色、Navbar、Sidebar等需要更細的調整,我個人也不想看到整個 Theme 和其他網站「撞衫」,所以比較偏好大部分自己來。","date":"2019.12.09","objectID":"/vuepress-blog-1/","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"這個部落格決定使用 VuePress 架設,過程中遇到種種問題以及官方文件寫不清楚的地方,因此這一系列 VuePress 筆記同時也希望幫助到剛接觸的人。會想使用 VuePress,一方面是因為想用 Vue 來寫,另一方面則是需要高度的 Customization。使用其他框架雖然很多功能都已經實現,但是前端很多東西如顏色、Navbar、Sidebar等需要更細的調整,我個人也不想看到整個 Theme 和其他網站「撞衫」,所以比較偏好大部分自己來。 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:0:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"安裝 Yarn 和 VuePress VuePress 官方建議使用 yarn ,我的環境已經安裝 npm,因此使用npm安裝yarn。 npm install -g yarn 接著 cd 到你的專案路徑地底下,初始化 vuepress yarn add -D vuepress 新增docs資料夾,在底下建立README.md,這將會作為首頁的頁面。 VuePress 預設將 docs 當成網頁的根目錄,首頁就是docs/README.md 接著在package.json新增 scripts 段落: \"scripts\": { \"docs:dev\": \"vuepress dev docs\", \"docs:build\": \"vuepress build docs\" } 對 yarn 來說package.json 的 scripts 作用 請見這裡 這樣編寫網頁時使用 yarn docs:dev 開啟 localhost:8080 開發環境, 編寫完成要 Deploy 時使用yarn docs:build 產生靜態網頁檔案,便可以 push 到 github pages。 整個專案架構會像這樣: . ├── docs │ ├── .vuepress (Optional) │ │ ├── components (Optional) │ │ ├── dist (build 好的靜態檔案都在這裡,只要把這裡推上github pages即可) │ │ ├── theme (Optional) │ │ │ └── Layout.vue │ │ ├── public (Optional) │ │ ├── styles (Optional) │ │ │ ├── index.styl │ │ │ └── palette.styl │ │ ├── templates (Optional, Danger Zone) │ │ │ ├── dev.html │ │ │ └── ssr.html │ │ ├── config.js (Optional) │ │ └── enhanceApp.js (Optional) │ │ │ ├── README.md │ ├── guide │ │ └── README.md │ └── config.md │ └── package.json ","date":"2019.12.09","objectID":"/vuepress-blog-1/:1:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"使用佈景主題 目前示範使用 Vue 的預設佈景主題 Default Theme,安裝 Theme 就像安裝 Vue Plugin 一樣,需要在docs/config.js增加以下內容: module.exports = { extend: '@vuepress/theme-default', } @vuepress/開頭的主題名稱為 Vue 官方的主題。 接著就可以在 markdown 文件加入設定值 (此方法只適用於 Default theme),這段設定其實是 YAML 格式,一定要放在 markdown 文件的第一行開始。這個之後會稱作 Frontmatter。 ---home:trueheroImage:/hero.pngheroText:Hero Titletagline:Hero subtitleactionText:Get Started →actionLink:/guide/features:- title:Simplicity Firstdetails:Minimal setup with markdown-centered project structure helps you focus on writing.- title:Vue-Powereddetails:Enjoy the dev experience of Vue + webpack, use Vue components in markdown, and develop custom themes with Vue.- title:Performantdetails:VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.footer:MIT Licensed | Copyright © 2018-present Evan You--- YAML frontmatter 會使用 gray-matter 處理以上的設定。 其實你也可以使用 json 或 TOML。 如果想要複寫主題的樣式,可以參考這個網站。 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:2:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"基本頁面元素 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:3:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"Navbar 導覽列區塊 Logo: .vuepress/config.js module.exports = { themeConfig: { logo: '/assets/img/logo.png', } } 疑,這裡有圖片相對路徑,所以要設定 Base URL,假設你等等要放在 yourname.github.io/blogs,那麼你的 Base URL 就是 /blogs/,設定如下: module.exports = { base: '/blogs/' } 在 md 裡面就要使用 HTML 標籤帶入圖片,$withBase讓我們日後更動路徑不需要每個地方改: \u003cimg :src=\"$withBase('/test.jpg')\" alt=\"foo\"\u003e 當然如果是要直接部署在yourname.github.io,那麼 base 就是 '/'。 超連結 : .vuepress/config.js module.exports = { themeConfig: { nav: [ { text: 'Home', link: '/' }, { text: 'Guide', link: '/guide/' }, { text: 'External', link: 'https://google.com' } ] } } 如果想使用巢狀結構也是可以的: module.exports = { themeConfig: { nav: [ { text: 'Categories', link: '/categories/' }, { text: 'Tags', link: '/tags/' }, { text: 'About', link: '/about/' }, { text: 'Frontend', items: [ { text: 'Vue', link: '/posts/frontend/Vue/' }, { text: '###', link: '#' }, ], }, ], } } 附帶一提,Vuepress 會對連外的連結加上 target=\"_blank\" rel=\"noopener noreferrer\",就是開啟新分頁,而rel=\"noopener noreferrer\"和資安考量有關,詳細請見這裡。 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:3:1","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"側邊欄 .vuepress/config.js module.exports = { themeConfig: { sidebar: [ '/', '/page-a', ['/page-b', 'Explicit link text'] ] } } 這裡要注意一點,官方文件範例如上,但是這會導致文章段落導覽的功能失效!所以我建議不要在這裡進行 Sidebar 的設定(不要將 sidebar[] 放入 themeConfig{} 底下)。文章段落導覽功能,只要在每篇文章的 Frontmatter 放入 sidebar: auto 即可。 要做額外的客製化,以及文章段落導覽的小小 Hack,後續文章會再說明。 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:3:2","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"全站搜尋功能 VuePress 自帶搜尋功能真是太開心了,只要使用內建 plugin 即可。 .vuepress/config.js module.exports = { plugins: [ // 在這裡使用 plugin ['@vuepress/search', { searchMaxSuggestions: 10 }], ] } ","date":"2019.12.09","objectID":"/vuepress-blog-1/:3:3","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"首頁 VuePress 其實原本是設計給 Documentation 網頁使用,而首頁就是讓你放大大的 Logo。不過如果是部落格,比較常見是放置最新文章等內容,如何整個改掉的部分後續文章再說明,這裡先按照官方文件。 依照剛才的檔案架構,docs 是網站的 Root,Root 的 README.md 裡面為 Frontmatter 加上 home:true 它就會認定這是首頁囉。其他參數如下: ---home:trueheroImage:/hero.pngheroText:Hero Titletagline:Hero subtitleactionText:Get Started →actionLink:/guide/features:- title:Simplicity Firstdetails:Minimal setup with markdown-centered project structure helps you focus on writing.- title:Vue-Powereddetails:Enjoy the dev experience of Vue + webpack, use Vue components in markdown, and develop custom themes with Vue.- title:Performantdetails:VuePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.footer:MIT Licensed | Copyright © 2018-present Evan You--- 這樣的頁面會長的和 VuePress 官方網站一模一樣。 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:3:4","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Frontend"],"content":"部署到 Github Pages 確認已經跑過 yarn docs:build 或 vuepress build docs。 在 docs/.vuepress/dist 底下初始化一個 Git repo: git init git add -A git commit -m 'deploy' 接著就可以 git remote add origin git@github.com:username/repo.git 並 push 上去。如果不熟 Git 和 Github 看這裡。 確認是否成功 deploy 到 Github pages: Environment 點進去查看,像下面這樣就表示成功了: 之後會發更多 VuePress 客製化過程,包含這個部落格使用的、自行開發的 Tags 功能、分類功能等,因為VuePress 原本都沒有這些功能。 ","date":"2019.12.09","objectID":"/vuepress-blog-1/:4:0","tags":["VuePress"],"title":"VuePress 部落格架設與折騰 (一):Themes、頁面元素、部署至 Github","uri":"/vuepress-blog-1/"},{"categories":["Misc"],"content":"為什麼決定要架 Blog 寫技術文章? 其實很早以前就有這個想法,而遲遲沒有執行。當下和周遭的人表達這個想法時,通常都是「寫部落格,太累了吧!」 當然我知道做這個事其實需要時間跟精力的,但是在孤獨的學習路上,走久了只會覺得疲乏。同時也發現「自己讀」和「對別人講解一遍」的巨大落差,時常落入「自己覺得會了,其實還不會」的情境。","date":"2019.12.08","objectID":"/why-i-launch-this-blog/","tags":["心得想法"],"title":"開端","uri":"/why-i-launch-this-blog/"},{"categories":["Misc"],"content":"為什麼決定要架 Blog 寫技術文章? 其實很早以前就有這個想法,而遲遲沒有執行。當下和周遭的人表達這個想法時,通常都是「寫部落格,太累了吧!」 當然我知道做這個事其實需要時間跟精力的,但是在孤獨的學習路上,走久了只會覺得疲乏。同時也發現「自己讀」和「對別人講解一遍」的巨大落差,時常落入「自己覺得會了,其實還不會」的情境。 這一切動機源自一場面試 19 年 11 月,我踏入信義區某棟商辦,那是一間大家都聽過的外商公司。面試的過程我對自己的評價是不太順利的,某些網路的概念只要詳細問便卡住了,尤其是在白板畫圖,細節是藏不住的,當然最後獲得了人生第一張無聲卡。經過患得患失的幾天,我自知學習的過程是有問題的,因此開始思考改變學習的方式,也尋找了一些答案。 引用 SRE 社團的前輩大神 Rick 撰寫的 學習法則: 人類的學習必須 有進、有出,進就是 Input,方法像是閱讀、聽講、練習 … 等;出就是 Output、產出,形式可以是寫文章、做出成品、演講、教人 … etc。 想想幾種能產生 Output 的途徑:演講(機會不多,目前實力不敢報 Meetup,而研究室 Group Meeting 總不能請教授多讓我講幾次?)、寫文章(這就是想寫 Blog 的動機)、教人(我自學電子音樂編曲的過程有體會到)。在技術這塊,又多又雜,因此我覺得初期透過寫文章是最快速的回饋方式。 ","date":"2019.12.08","objectID":"/why-i-launch-this-blog/:0:0","tags":["心得想法"],"title":"開端","uri":"/why-i-launch-this-blog/"},{"categories":["Misc"],"content":"So Let’s Get Started. ","date":"2019.12.08","objectID":"/why-i-launch-this-blog/:1:0","tags":["心得想法"],"title":"開端","uri":"/why-i-launch-this-blog/"},{"categories":null,"content":".figure-photo img {width: 25rem;height: auto;} 有些放公司(因為溫濕度條件比家裡好),請勿觸摸;如果您是我同事,歡迎到我座位欣賞 :3 ","date":"0001.01.01","objectID":"/myfigures/:0:0","tags":null,"title":"O3R 的模型收藏","uri":"/myfigures/"},{"categories":null,"content":"GK 小魔女諾貝塔 1/6 GK雕像\" 小魔女諾貝塔 1/6 GK雕像 已預訂名稱:小魔女諾貝塔 1/6 GK雕像作品:小魔女諾貝塔製作:BS STUDIO發售:2022 Q2/Q3Link ","date":"0001.01.01","objectID":"/myfigures/:1:0","tags":null,"title":"O3R 的模型收藏","uri":"/myfigures/"},{"categories":null,"content":"比例模型 POP UP PARADE 白 狙擊手 Ver.\" POP UP PARADE 白 狙擊手 Ver. 名稱:POP UP PARADE 白 狙擊手 Ver.作品:NO GAME NO LIFE 遊戲人生製作:Good Smile Company發售:2020/05Link ハチロク 水着Ver.\" ハチロク 水着Ver. 名稱:ハチロク 水着Ver.作品:まいてつ pure station製作:ALTER發售:2022/01Link 白 夏季Ver.\" 白 夏季Ver. 已預訂名稱:白 夏季Ver.作品:NO GAME NO LIFE 遊戲人生製作:Good Smile Company發售:2022/05Link ","date":"0001.01.01","objectID":"/myfigures/:2:0","tags":null,"title":"O3R 的模型收藏","uri":"/myfigures/"},{"categories":null,"content":"黏土人 黏土人 仙狐\" 黏土人 仙狐 名稱:黏土人 仙狐作品:請讓我撒嬌,仙狐大人!製作:Good Smile Company發售:2020/08Link ","date":"0001.01.01","objectID":"/myfigures/:3:0","tags":null,"title":"O3R 的模型收藏","uri":"/myfigures/"}]