環境資訊中心綜合外電;黃鈺婷 編譯;林大利 審校
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※超省錢租車方案
※別再煩惱如何寫文案,掌握八大原則!
※回頭車貨運收費標準
※教你寫出一流的銷售文案?
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
勁客的優勢在於成熟可靠的動力總成,採用1。5L自然吸氣發動機+CVT無級變速器的搭配能夠帶來非常平順線性的輸出,油門調校靈敏,能夠很好地應付日常市區內行駛,並且也非常省油,可靠性較高。不過其檔次感與配置水平不如哈弗H2。
15萬左右比較推薦的是本田XR-V與豐田卡羅拉,這兩款車目前在市場上都有着不錯的銷量、保值率以及口碑,XR-V在小型SUV市場混得風生水起,靠的就是其成熟的動力系統和越級的空間表現,家用是個非常棒的選擇。而卡羅拉乃全球銷量神車,中庸就是其最大的武器,除了汽油版本外還有雙擎版本可選,擁有非常優秀的燃油經濟性,能夠滿足許許多多的消費者,除了隔音濾振差點,其它方面的表現都非常不錯,乃家用車的不二之選。
一輛是接近中型車的緊湊型車,一輛是標準的中型車,凌渡280DSG豪華版擁有更加豐富的配置,不過因為其比較扁平的設計所以頭部空間表現一般,而君威中型車的身份自然擁有更大的空間以及更高的檔次感,同時全系標配9AT也讓其競爭力進一步上升,採用的1.5T發動機也有着不錯的動力輸出,因此更推薦君威1.5T中配。
510採用的是6擋手動變速器和5擋AMT變速器,假設會開手動擋的話更加推薦手動車型,6個擋位在高速行駛時把轉速壓得更低,能帶來更好的燃油經濟性,同時擁有更高的傳動效率及可靠性,510的手動擋換擋手感不錯,有吸入感並且行程不長,離合器的力度也不沉,沒那麼容易疲勞。而AMT變速器雖然說省去了踩離合器的麻煩,但是其換擋邏輯不清晰,而且頓挫比較嚴重,尤其是在起步階段或者是在堵車狀況下,因此更加推薦510的手動版車型。
兩車都是定位小型SUV,哈弗H2擁有更加親民的售價,更加大氣上檔次的外觀內飾設計,同時配置更加豐富,採用的1.5T發動機+7擋雙離合的搭配,擁有不錯的爆發力,但是雙離合變速器的邏輯有待提高,並且油耗也會偏高。
勁客的優勢在於成熟可靠的動力總成,採用1.5L自然吸氣發動機+CVT無級變速器的搭配能夠帶來非常平順線性的輸出,油門調校靈敏,能夠很好地應付日常市區內行駛,並且也非常省油,可靠性較高。不過其檔次感與配置水平不如哈弗H2。
綜上,假如你預算充足的話,更加建議購買勁客的中配以上車型,而預算不是很足的話,那麼家用選擇H2是一個非常具有性價比的選擇。
2018款飛度新增了運動套件車型,不過在配置方面還是一如既往的寒酸,比較推薦指導價為8.88萬的1.5L CVT潮跑版,在配置上面擁有主/副駕駛座安全氣囊、運動外觀套件、行車電腦显示屏、前霧燈等,類似比較常用的电子車身穩定系統、駐車雷達要到頂配才配備,但是飛度的動力和空間還是非常不錯的,1.5L自吸發動機就能爆發出131匹馬力,日常市區駕駛毫無壓力,加上本田“MM”理念,讓它的空間能夠滿足大多數人的使用需求,市面上大量的改裝件也能讓每位飛度車主把愛車改成獨一無二的樣子。
以上就是本期網友問答欄目的全部內容,假如你也想上牆的話,點擊下方留言留下你的問題並且點個贊,就有機會在下期欄目看見你的身影!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計最專業,超強功能平台可客製化
這套前端監控系統用到的技術棧是:React+MongoDB+Node.js+Koa2。將性能和錯誤量化。因為自己平時喜歡吃菠蘿,所以就取名叫菠蘿系統。其實在很早以前就有這個想法,當時已經實現了前端的參數搜集,只是後台遲遲沒有動手,也就拖着。
目前完成的還只是個雛形,僅僅是搜集了錯誤和相關的性能參數。
後台樣式採用了封裝過的matrix。
分析功能還很薄弱,只是做了簡單的演示,並且各種基礎功能還有待完善。
後面打算強化數據分析,並且還要實現錯誤的回放機制,思路的話以前也調研過,參考之前的一篇文章。
現在的這個系統還只能算是個玩具,後期還需要雕琢雕琢。下面是這套系統的目錄結構。
├── pingapple --------------------------------- 菠蘿監控系統 │ ├── client -------------------------------- 系統的前端部分 │ ├── sdk ----------------------------------- 信息搜集代碼庫 │ ├── server -------------------------------- 系統的後端部分
1)primus.js
在之前的《前端頁面性能參數搜集》一文中,詳細記載了各類性能指標的計算規則,並整理到了primus.js中。
本次將在primus.js的基礎上做適當的修改,包括刪除代理、測速、資源信息等功能,改變部分性能指標的計算規則,例如從瀏覽器發起HTTP請求算起,忽略瀏覽器重定向的時間等。
2)錯誤處理
完善錯誤處理,將錯誤分成三類:runtime、load和Promise。在window的error事件中,處理前兩種錯誤。像img元素載入的圖片地址不存在,就會執行formatLoadError()函數;像變量未定義,就會執行formatRuntimerError()函數。
window.addEventListener("error", function (event) {
var errorTarget = event.target;
// 過濾 target 為 window 的異常
if (
errorTarget !== window &&
errorTarget.nodeName &&
LOAD_ERROR_TYPE[errorTarget.nodeName.toUpperCase()]
) {
handleError(formatLoadError(errorTarget));
} else {
handleError(
formatRuntimerError(
event.message,
event.filename,
event.lineno,
event.colno,
event.error
)
);
}
}, true
);
將window綁定unhandledrejection事件后,就會在Promise被拒絕且沒有reject的回調函數時觸發。
window.addEventListener( "unhandledrejection", function (event) { // console.log('Unhandled Rejection at:', event.promise, 'reason:', event.reason); handleError({ type: ERROR_PROMISE, desc: event.reason, stack: "no stack" }); }, true );
3)初始化
由於要計算白屏時間,DOM時間等,所以位置不能隨便放,得要放在head的最後面。
<head> <script> window.pineapple || (pineapple = {}); pineapple.param = { "token": "dsadasd2323dsad23dsada" }; </script> <script src="js/pineapple.js"></script> </head>
1)Koa
Koa是由Express原班人馬打造的Web輕量框架,通過組合各種中間件來避免繁瑣的回調函數嵌套,當前使用的版本是V2。
npm install --save koa
使用的Koa腳手架:koa-generator,創建項目的結構,並且在此基礎上做了調整(目錄如下所示)。暫時還不會用到靜態資源和視圖層。
npm install -g koa-generator
├── server --------------------------------- 服務端 │ ├── bin -------------------------------- 命令 │ ├── config ----------------------------- 配置目錄 │ ├── controllers ------------------------ MVC中的邏輯層 │ ├── db --------------------------------- MVC中的數據層 │ ├── public ----------------------------- 靜態資源 │ ├── routes ----------------------------- 路由 │ ├── utils ------------------------------ 工具庫 │ ├── views ------------------------------ MVC中的視圖層 │ ├── app.js ----------------------------- 入口文件
為了區分開發環境和生產環境,通過cross-env統一不同系統設置環境變量的方式。
npm install --save cross-env
package.json中的命令如下,添加了環境配置。
"scripts": { "start": "node bin/www", "dev": "cross-env NODE_ENV=development ./node_modules/.bin/nodemon bin/www", "prd": "cross-env NODE_ENV=production pm2 start bin/www" }
prd按字面意思應該是生產環境的命令,其中使用了pm2,默認沒有安裝。還沒部署過Node.js,還不清楚裏面有多少坑。
npm install --save pm2
2)MongoDB
MongoDB是一個開源的非關係型數據庫(圖1是下載界面),既沒有表、行等概念,也沒有固定的模式和結構,所有的數據以文檔(一個對象)的形式存儲。但其使用方式和關係型數據庫相似,並且還支持對數據建立索引,適用於高併發讀寫、海量數據存儲和實時分析等。
圖1
注意,在安裝時默認會下載MongoDB Compress(一個可視化的MongoDB工具),默認下載會非常慢,建議自行下載,該工具的界面還是蠻清爽的,如圖2所示。
圖2
在Mac上配置MongoDB比較麻煩,不像Windows那樣一件安裝,需要一些步驟,廢了點力氣才裝好,下面是執行的命令。
sudo mongod --dbpath=/Users/pw/data
3)Mongoose
Mongoose是MongoDB的一個ORM(Object-Document Mapper,對象文檔映射)工具,可在Node.js環境中執行,封裝了MongoDB操作文檔的常用方法,包括引入數據庫連接(connect),定義模型(model),聲明文檔結構(scheme),實例化模型等操作數據庫的方法。
npm install --save mongoose
借鑒了以前PHP數據分層的思想,單獨分離出數據庫的連接,並抽象通用的Model層(如下所示)。
const mongoose = require("./db");
class Mongodb {
constructor(name, schema) {
//聲明結構
const mySchema = new mongoose.Schema(schema, { typeKey: "$type" });
this.model = mongoose.model(name, mySchema);
}
//保存
save(obj) {
obj.created = Date.now(); //日期
const doc = new this.model(obj);
return new Promise((resolve, reject) => {
doc.save((err, row) => {
if (err) {
reject(err);
return;
}
resolve(row);
});
});
}
}
module.exports = {
model: Mongodb,
mongoose
};
4)路由
由於發送的地址是一張gif圖片,因此在處理路由時,返回本地的一張gif圖,如下所示,圖像地址得是絕對路徑,否則無法讀取。
router.get('/pa.gif', async (ctx, next) => {
const ctr = new indexController();
ctr.collect(ctx);
const url = path.resolve(__dirname, "../public/images/blank.gif");
ctx.body = fs.readFileSync(url); //空白gif圖
});
5)代理分析
在接收參數的時候分析代理所帶的信息,例如瀏覽器、操作系統、設備等。使用的是一個第三方庫:UAParser.js,四年前就關注過,當時GitHub上只有1K多個關注量,現在已經翻了4倍。
npm install --save ua-parser-js
6)假數據
製作一套合適的假數據,新增命令“npm run data”,初始化數據,便於展示。
1)UI
後台模板採用了之前封裝過的Matrix,但不會依賴Bootstrap框架。
將整個頁面分成五塊,分別是導航、側邊欄、麵包屑、底部欄以及主體。
安裝react-router的history,用於路由。
npm install --save history
期間也會安裝各類依賴包,例如不支持在類中直接聲明屬性等。
在使用的過程中,ESLint會不時的彈出各種錯誤和警告,期間就不停的修改問題或查找相關配置忽略部分限制。
後台的側邊欄和麵包屑等部分,會隨着URL的不同而發生狀態變化,本來想用多頁實現,但配置要改很多,就依然做成一個SPA,只是稍微做了些改動。
組件庫採用了流行的Ant Design,調用了按鈕、單選框、日期等組件。
npm install --save antd
圖表庫使用的是ECharts,目前只用到了折線圖和餅圖。在引用圖表時,為了優化構建,採取了按需引用的手段。
npm install --save echarts
2)項目管理
首先建立一個項目,然後才能分析該項目的性能和錯誤,如圖3所示。
圖3
用彈框的形式來創建項目,使用了Ant Design的Model、Form等組件,如圖4所示。
圖4
3)性能分析
在第一個折線圖標籤中的過濾條件包括項目、字段、日期等,性能指標按平均值呈現,可看到每個性能指標的趨勢,如圖5所示。
圖5
按分時日統計性能平均數,在MongoDB中計算。原先創建日期是以時間戳的形式存儲的,為了便於使用Aggregate,改成字符串形式。碰到一個坑,MongoDB中的Date類型採用的是格林尼治時間,而不是當前時區的時間,也就是說存在數據庫中的時間會比當前時間早8小時。
在第二個列表標籤中,可以詳細看到每條記錄的信息,包括代理、網絡等,便於在了解趨勢的前提下,獲悉更為細節的內容,如圖6所示。
圖6
點擊ajax那一列,可彈出具體的異步請求信息,如圖7所示。
圖7
4)錯誤分析
有三個標籤,第一個也是折線圖,描繪的是某個時間的錯誤個數;第二個是錯誤列表,會給出具體的錯誤信息,如圖8所示。
圖8
第三個是餅圖,餅圖主要體現的是發生錯誤的瀏覽器分佈情況(如圖9所示),點擊某一塊可查看瀏覽器的具體版本(如圖10所示)。
圖9
圖10
【參考資料】
PerformanceTiming
unhandledrejection 處理沒有顯式捕獲的 Promise 異常
狼書(卷2)
Node-區分環境
Koa從零搭建到Api實現—項目部署
koa如何連接MongoDB
Koa2進階學習筆記
如何計算首屏加載時間
Mongoose Schema Error: “Cast to string failed for value” when pushing object to empty array
Support for the experimental syntax ‘classProperties’ isn’t currently enabled
Template string failing with Cannot read property ‘range’ of null
Disallow JSX props spreading (react/jsx-props-no-spreading)
TypeError: Cannot read property ‘range’ of null from template-curly-spacing
echarts項目的優化
使用 happypack 提升 Webpack 項目構建速度
mac下的mongoDB的安裝和啟動
安裝MongoDB報錯 mkdir: /data/db: Read-only file system
$sum mongoose
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計最專業,超強功能平台可客製化
摘要:升職加薪,出任 CTO,迎娶白富美/高帥富,走向人生巔峰是很多人的夢想。在本期的熱點速覽中你將了解自由作者 Easy 如何優雅賺取零花錢的方法,以及定投改變命運 —— 讓時間陪你慢慢變富。說到程序員自我增值,除了優雅賺錢之外,還可以研究下各種生活中小工具的代碼實現,例如,收錄 20+ Web 小應用的 vanillawebprojects。將技術應用在生活中點滴,展現你的技術輔助日常“肝”口袋妖精,或者偶爾用技術給自己生活添加點小樂趣,用遺傳算法製作一個繪製圖像過程的小玩具。
以下內容摘錄自微博@HelloGitHub 的 GitHub Trending,選項標準:新發布 | 實用 | 有趣,根據項目 release 時間分類,發布時間不超過 7 day 的項目會標註 New,無該標誌則說明項目 release 超過一周。由於本文篇幅有限,還有部分項目未能在本文展示,望周知
本周 star 增長數:1200+
Newgenetic-drawing 作者在 2017 年做的模仿給定目標圖像的繪製過程的玩具項目,效果見下圖。項目受到互聯網上許多基因繪製示例的啟發,由於項目深受歡迎,作者便在近日將其開源。
GitHub 地址→https://github.com/anopara/genetic-drawing
本周 star 增長數:900+
SpaceX-API 是一個用於火箭、核心艙、太空艙、發射台和發射數據的開源 REST API。技術棧
GitHub 地址→https://github.com/r-spacex/SpaceX-API
本周 star 增長數:1000+
go-ast-book 是一個 Go 語法樹入門項目。讓我們語法樹這個維度重新審視 Go 語言程序,我們將得到創建Go語言本身的技術。本書簡單介紹語法樹相關包的使用。
GitHub 地址→https://github.com/chai2010/go-ast-book
本周 star 增長數:800+
New xgenecloud 是一個能即時生成任何數據庫上的 REST 和 GraphQL API 工具,它支持 MySQL、PostgreSQL、MsSQL、SQLite、MariaDB。特性:
GitHub 地址→https://github.com/xgenecloud/xgenecloud
本周 star 增長數:1100+
vanillawebprojects 收錄了用前端技術(Javascript、CSS、HTML5)開發的 20+ 款小應用,包括:表單驗證、匯率計算、打字遊戲、語音閱讀、新年倒計時等等。
GitHub 地址→https://github.com/bradtraversy/vanillawebprojects
本周 star 增長數:10900+
《統計學習方法》可以說是機器學習的入門寶典,許多機器學習培訓班、互聯網企業的面試、筆試題目,很多都參考這本書。本項目收錄了該書的所有代碼實現,特別是監督學習方法,包括感知機、k 近鄰法、樸素貝恭弘=叶 恭弘斯法、決策樹、邏輯斯諦回歸與支持向量機、提升方法、em 算法、隱馬爾可夫模型和條件隨機場等。
GitHub 地址→https://github.com/fengdu78/lihang-code
本周 star 增長數:500+
NewPokedex 使用基於 MVVM 架構的 Dagger Hilt、Motion、Coroutines、Jetpack 開發的 Poke(口袋妖精)輔助工具。這個項目專註實現依賴注入的新庫,支持從網絡獲取數據,並通過存儲庫模式集成數據庫中的持久化數據。
GitHub 地址→https://github.com/skydoves/Pokedex
本周 star 增長數:1300+
Fastapi 是一個基於 python 的框架,該框架鼓勵使用 Pydantic 和 OpenAPI 進行文檔編製,使用 Docker 進行快速開發和部署以及基於 Starlette 框架進行的簡單測試。特性:
GitHub 地址→https://github.com/tiangolo/fastapi
本周 star 增長數:800+
從基礎到高級,JavaScript Questions 收錄了 JS 相關的面試題及解法。
GitHub 地址→https://github.com/lydiahallie/javascript-questions
在本期主題模塊,小魚乾這裏選取了 3 個和增值相關的小工具,希望能提高你生活、工作的幸福值。
howto-make-more-money 是一個程序員@Easy 現身講述優雅的掙零花錢的項目,雖然是一個教你如何賺零花錢的項目,但是通過閱讀本賺零花錢小書你可理清自己的核心資源,以及如何創造資產。
GitHub 地址→https://github.com/easychen/howto-make-more-money
定投改變命運 —— 讓時間陪你慢慢變富。regular-investing-in-box 這本書要講的是普通人擺脫階層固化的路徑 —— 絕對可行,毫無水分,並且全靠你自己。這裏所說的普通人,不分國界、不分地域、不分種族、不分性別、不分年齡、不分高矮胖瘦美醜、不分何種性取向…… 關鍵在於,甚至壓根不分智商和學歷!換言之,這個解決方案,甚至對在北京跑腿送外賣的小哥都適用……
GitHub 地址→https://github.com/xiaolai/regular-investing-in-box
Coursera-ML-AndrewNg-Notes 是吳恩達老師的機器學習課程個人筆記,旨在提供了一個廣泛的介紹機器學習、數據挖掘、統計模式識別的課程。主題包括:
項目還將使用大量的案例研究,你可學習到如何運用學習算法構建智能機器人(感知,控制),文本的理解(Web 搜索,反垃圾郵件),計算機視覺,醫療信息,音頻,數據挖掘,和其他領域。
GitHub 地址→https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes
以上為 2020 年第 23 個工作周的 GitHub Trending 如果你 Pick 其他好玩、實用的 GitHub 項目,記得來 HelloGitHub issue 區和我們分享下喲
HelloGitHub 交流群現已全面開放,添加微信號:HelloGitHub 為好友入群,可同前端、Java、Go 等各界大佬談笑風生、切磋技術~
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※想知道最厲害的網頁設計公司"嚨底家"!
※別再煩惱如何寫文案,掌握八大原則!
※產品缺大量曝光嗎?你需要的是一流包裝設計!
上一篇完成了後台分類模塊的所有功能,本篇繼續將標籤模塊和友情鏈接模塊的增刪改查完成。
實現方式和之前的分類管理是一樣的,在Admin文件夾下面添加Tags.razor組件,設置路由@page "/admin/tags"。
同樣的內容也需要放在AdminLayout組件下面,添加幾個參數:彈窗狀態bool Open、新增或更新時標籤字段string tagName, displayName、更新時的標籤Idint id、API返回的標籤列表接收參數ServiceResult<IEnumerable<QueryTagForAdminDto>> tags。
/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;
/// <summary>
/// 新增或者更新時候的標籤字段值
/// </summary>
private string tagName, displayName;
/// <summary>
/// 更新標籤的Id值
/// </summary>
private int id;
/// <summary>
/// API返回的標籤列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryTagForAdminDto>> tags;
//QueryTagForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class QueryTagForAdminDto : QueryTagDto
{
/// <summary>
/// 主鍵
/// </summary>
public int Id { get; set; }
}
}
在初始化方法OnInitializedAsync()中獲取數據。
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
var token = await Common.GetStorageAsync("token");
Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
tags = await FetchData();
}
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> FetchData()
{
return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryTagForAdminDto>>>("/blog/admin/tags");
}
注意需要設置請求頭,進行授權訪問,然後頁面上綁定數據。
<AdminLayout>
@if (tags == null)
{
<Loading />
}
else
{
<div class="post-wrap tags">
<h2 class="post-title">- Tags -</h2>
@if (tags.Success && tags.Result.Any())
{
<div class="categories-card">
@foreach (var item in tags.Result)
{
<div class="card-item">
<div class="categories">
<NavLink title="刪除" @onclick="@(async () => await DeleteAsync(item.Id))"></NavLink>
<NavLink title="編輯" @onclick="@(() => ShowBox(item))"></NavLink>
<NavLink target="_blank" href="@($"/tag/{item.DisplayName}")">
<h3>@item.TagName</h3>
<small>(@item.Count)</small>
</NavLink>
</div>
</div>
}
<div class="card-item">
<div class="categories">
<NavLink><h3 @onclick="@(() => ShowBox())">~~~ 新增標籤 ~~~</h3></NavLink>
</div>
</div>
</div>
}
else
{
<ErrorTip />
}
</div>
<Box OnClickCallback="@SubmitAsync" Open="@Open">
<div class="box-item">
<b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" />
</div>
<div class="box-item">
<b>TagName:</b><input type="text" @bind="@tagName" @bind:event="oninput" />
</div>
</Box>
}
</AdminLayout>
tags沒獲取到數據的時候显示<Loading />組件內容,循環遍曆數據進行綁定,刪除按鈕綁定點擊事件調用DeleteAsync()方法。新增和編輯按鈕點擊事件調用ShowBox()方法显示彈窗。新增的時候不需要傳遞參數,編輯的時候需要將當前item即QueryTagForAdminDto傳遞進去。
<Box>組件中綁定了標籤的兩個參數,是否打開參數Opne和確認按鈕回調事件方法SubmitAsync()。
刪除標籤的方法DeleteAsync(...)如下:
// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n真的要幹掉這個該死的標籤嗎");
if (confirmed)
{
var response = await Http.DeleteAsync($"/blog/tag?id={id}");
var result = await response.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
tags = await FetchData();
}
}
刪除之前進行二次確認,避免誤傷,刪除成功重新加載一遍數據。
彈窗的方法ShowBox(...)如下:
/// <summary>
/// 显示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryTagForAdminDto dto = null)
{
Open = true;
id = 0;
// 新增
if (dto == null)
{
displayName = null;
tagName = null;
}
else // 更新
{
id = dto.Id;
displayName = dto.DisplayName;
tagName = dto.TagName;
}
}
最後在彈窗中確認按鈕的回調事件方法SubmitAsync()如下:
/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
var input = new EditTagInput()
{
DisplayName = displayName.Trim(),
TagName = tagName.Trim()
};
if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.TagName))
{
return;
}
var responseMessage = new HttpResponseMessage();
if (id > 0)
responseMessage = await Http.PutAsJsonAsync($"/blog/tag?id={id}", input);
else
responseMessage = await Http.PostAsJsonAsync("/blog/tag", input);
var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
tags = await FetchData();
Open = false;
}
}
輸入參數EditTagInput。
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class EditTagInput : TagDto
{
}
}
最終執行新增或者更新數據都在點擊事件中進行,將變量的值賦值給EditTagInput,根據id判斷走新增還是更新,成功后重新加載數據,關掉彈窗。
標籤管理頁面全部代碼如下:
點擊查看代碼
@page "/admin/categories"
<AdminLayout>
@if (categories == null)
{
<Loading />
}
else
{
<div class="post-wrap categories">
<h2 class="post-title">- Categories -</h2>
@if (categories.Success && categories.Result.Any())
{
<div class="categories-card">
@foreach (var item in categories.Result)
{
<div class="card-item">
<div class="categories">
<NavLink title="刪除" @onclick="@(async () => await DeleteAsync(item.Id))"></NavLink>
<NavLink title="編輯" @onclick="@(() => ShowBox(item))"></NavLink>
<NavLink target="_blank" href="@($"/category/{item.DisplayName}")">
<h3>@item.CategoryName</h3>
<small>(@item.Count)</small>
</NavLink>
</div>
</div>
}
<div class="card-item">
<div class="categories">
<NavLink><h3 @onclick="@(() => ShowBox())">~~~ 新增分類 ~~~</h3></NavLink>
</div>
</div>
</div>
}
else
{
<ErrorTip />
}
</div>
<Box OnClickCallback="@SubmitAsync" Open="@Open">
<div class="box-item">
<b>DisplayName:</b><input type="text" @bind="@displayName" @bind:event="oninput" />
</div>
<div class="box-item">
<b>CategoryName:</b><input type="text" @bind="@categoryName" @bind:event="oninput" />
</div>
</Box>
}
</AdminLayout>
@code {
/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;
/// <summary>
/// 新增或者更新時候的分類字段值
/// </summary>
private string categoryName, displayName;
/// <summary>
/// 更新分類的Id值
/// </summary>
private int id;
/// <summary>
/// API返回的分類列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryCategoryForAdminDto>> categories;
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
var token = await Common.GetStorageAsync("token");
Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
categories = await FetchData();
}
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> FetchData()
{
return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>>("/blog/admin/categories");
}
/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task DeleteAsync(int id)
{
Open = false;
// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n真的要幹掉這個該死的分類嗎");
if (confirmed)
{
var response = await Http.DeleteAsync($"/blog/category?id={id}");
var result = await response.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
categories = await FetchData();
}
}
}
/// <summary>
/// 显示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryCategoryForAdminDto dto = null)
{
Open = true;
id = 0;
// 新增
if (dto == null)
{
displayName = null;
categoryName = null;
}
else // 更新
{
id = dto.Id;
displayName = dto.DisplayName;
categoryName = dto.CategoryName;
}
}
/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
var input = new EditCategoryInput()
{
DisplayName = displayName.Trim(),
CategoryName = categoryName.Trim()
};
if (string.IsNullOrEmpty(input.DisplayName) || string.IsNullOrEmpty(input.CategoryName))
{
return;
}
var responseMessage = new HttpResponseMessage();
if (id > 0)
responseMessage = await Http.PutAsJsonAsync($"/blog/category?id={id}", input);
else
responseMessage = await Http.PostAsJsonAsync("/blog/category", input);
var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
categories = await FetchData();
Open = false;
}
}
}
實現方式都是一樣的,這個就不多說了,直接上代碼。
先將API返回的接收參數和新增編輯的輸入參數添加一下。
//QueryFriendLinkForAdminDto.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class QueryFriendLinkForAdminDto : FriendLinkDto
{
/// <summary>
/// 主鍵
/// </summary>
public int Id { get; set; }
}
}
//EditFriendLinkInput.cs
namespace Meowv.Blog.BlazorApp.Response.Blog
{
public class EditFriendLinkInput : FriendLinkDto
{
}
}
@page "/admin/friendlinks"
<AdminLayout>
@if (friendlinks == null)
{
<Loading />
}
else
{
<div class="post-wrap categories">
<h2 class="post-title">- FriendLinks -</h2>
@if (friendlinks.Success && friendlinks.Result.Any())
{
<div class="categories-card">
@foreach (var item in friendlinks.Result)
{
<div class="card-item">
<div class="categories">
<NavLink title="刪除" @onclick="@(async () => await DeleteAsync(item.Id))"></NavLink>
<NavLink title="編輯" @onclick="@(() => ShowBox(item))"></NavLink>
<NavLink target="_blank" href="@item.LinkUrl">
<h3>@item.Title</h3>
</NavLink>
</div>
</div>
}
<div class="card-item">
<div class="categories">
<NavLink><h3 @onclick="@(() => ShowBox())">~~~ 新增友鏈 ~~~</h3></NavLink>
</div>
</div>
</div>
}
else
{
<ErrorTip />
}
</div>
<Box OnClickCallback="@SubmitAsync" Open="@Open">
<div class="box-item">
<b>Title:</b><input type="text" @bind="@title" @bind:event="oninput" />
</div>
<div class="box-item">
<b>LinkUrl:</b><input type="text" @bind="@linkUrl" @bind:event="oninput" />
</div>
</Box>
}
</AdminLayout>
@code {
/// <summary>
/// 默認隱藏Box
/// </summary>
private bool Open { get; set; } = false;
/// <summary>
/// 新增或者更新時候的友鏈字段值
/// </summary>
private string title, linkUrl;
/// <summary>
/// 更新友鏈的Id值
/// </summary>
private int id;
/// <summary>
/// API返回的友鏈列表數據
/// </summary>
private ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>> friendlinks;
/// <summary>
/// 初始化
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
var token = await Common.GetStorageAsync("token");
Http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
friendlinks = await FetchData();
}
/// <summary>
/// 獲取數據
/// </summary>
/// <returns></returns>
private async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> FetchData()
{
return await Http.GetFromJsonAsync<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>>("/blog/admin/friendlinks");
}
/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task DeleteAsync(int id)
{
Open = false;
// 彈窗確認
bool confirmed = await Common.InvokeAsync<bool>("confirm", "\n真的要幹掉這個該死的分類嗎");
if (confirmed)
{
var response = await Http.DeleteAsync($"/blog/friendlink?id={id}");
var result = await response.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
friendlinks = await FetchData();
}
}
}
/// <summary>
/// 显示box,綁定字段
/// </summary>
/// <param name="dto"></param>
private void ShowBox(QueryFriendLinkForAdminDto dto = null)
{
Open = true;
id = 0;
// 新增
if (dto == null)
{
title = null;
linkUrl = null;
}
else // 更新
{
id = dto.Id;
title = dto.Title;
linkUrl = dto.LinkUrl;
}
}
/// <summary>
/// 確認按鈕點擊事件
/// </summary>
/// <returns></returns>
private async Task SubmitAsync()
{
var input = new EditFriendLinkInput()
{
Title = title.Trim(),
LinkUrl = linkUrl.Trim()
};
if (string.IsNullOrEmpty(input.Title) || string.IsNullOrEmpty(input.LinkUrl))
{
return;
}
var responseMessage = new HttpResponseMessage();
if (id > 0)
responseMessage = await Http.PutAsJsonAsync($"/blog/friendlink?id={id}", input);
else
responseMessage = await Http.PostAsJsonAsync("/blog/friendlink", input);
var result = await responseMessage.Content.ReadFromJsonAsync<ServiceResult>();
if (result.Success)
{
friendlinks = await FetchData();
Open = false;
}
}
}
截至目前為止,還剩下文章模塊的功能還沒做了,今天到這裏吧,明天繼續剛,未完待續…
開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
※教你寫出一流的銷售文案?
※超省錢租車方案
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※產品缺大量曝光嗎?你需要的是一流包裝設計!
閱讀本文大概需要 2 分鐘。
呼~~,每次過完一個周末,寫作就失去了動力,一两天才能緩過來。儘管如此,還是要堅持寫好每一篇文章的。寧缺毋濫嘛,寧願發文的頻率低一點,也要保證文章的質量,至少排版不能差,行文要流暢,錯別字不能有。
關於類型推導想必大家都很熟悉,它是在 var 關鍵字引入的時候引入 C# 的。
var i = 10;
var u = new User();
編譯器會通過右邊的字面量自動推導左邊變量的類型,這種推導方式可以歸納為:從上下文右邊推導出左邊的類型。我們不妨把它稱為源類型推導(Source-typed inferring,參考 Target-typed 自創的術語)。
相應的,有源類型推導就有目標類型推導 (Target-typed inferring),它是指從上下文左邊推導出右邊的類型。比如數組的初始化和 Lambda 表達式常常是目標類型推導的表達式。舉個例子:
// 沒有使用類型推導
string[] s = new string[] { "a", "b" };
// 目標類型推導(左推右)
string[] s = new { "a", "b" };
string[] s = new [] { "a", "b" };
// 沒有使用類型推導
Users.FirstOrDefault<User>(u => u.id = 123);
// 目標類型推導(左推右)
Users.FirstOrDefault(u => u.id = 123);
這次在 C# 9 中,增加了用戶定義類型 new 表達式的目標類型推導,即通過上下文左邊自動推導 new 表達式的類型,從而在使用 new 構造時省略類型的指定,請看示例:
// C# 9 之前
Point p = new Point(3, 5);
// C# 9
Point p = new (3, 5);
除此之外,C# 9 也增加了操作符 ?? 和 ?: 的目標類型推導支持。之前這兩個操作符必須要求兩邊的操作對象都是相同的類型,否則會編譯報錯。而在 C# 9 中,只要目標類型是操作對象共同的基類就不再會編譯報錯了,比如:
// Student 和 Customer 擁有共同的父類 Person
Person person = (Person)(student ?? customer); // C# 9 之前
Person person = student ?? customer; // C# 9
// 可空類型,0 和 null 都可以隱式轉換為 int? 類型
int? result = b ? 0 : (int?)null; // C# 9 之前
int? result = b ? 0 : null; // C# 9
其實本文的核心就一句代碼:
Point p = new (3, 5);
卻一不小心啰嗦了這麼一堆。但講真,學習新的知識不是要死記硬背,而要學會歸類推理,舉一反三,經常思考,最好能形成自己的一種思維習慣,這樣學習才會變成一件水到渠成的事。多看我的文章,希望你能學到的不僅僅是生硬的編程知識點,也希望我的行文風格和思維習慣對你有所啟發。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※超省錢租車方案
※教你寫出一流的銷售文案?
※網頁設計最專業,超強功能平台可客製化
※產品缺大量曝光嗎?你需要的是一流包裝設計!
Hi,好久不見,我是CPU一號車間的阿Q,不認識我的話,可以看看:完了!CPU一味求快出事兒了!
真的是好久不見了,人類有個說法叫天上一天,地上一年,而在我們的世界里,人類一天,我們不知要過多少年~~
在我所在的CPU這座工廠里,時間的概念有些不太一樣。工廠大門外的中央廣場上掛着一個大大的鐘錶,整個計算機世界里的居民能夠掐着時間過日子全都仰仗它,你們人類把它叫做晶振。
這個鐘錶每隔66000000分之一秒就會報一次時,比人類的鐘錶不知道快到哪裡去了。
早些年還是夠用的,不過隨着我們CPU工廠生產效率的不斷提升,我們多次向晶振提出提升報時的精度,想讓他報時報的的更快一些,不過都被拒絕了。給我們的理由是內存那傢伙聯合主板上其他單位帶頭反對,說他們受條件限制,沒辦法像我們這麼快。
靠人不如靠己,為此,咱們工廠專門設立了一個叫倍頻器的部門進一步把這個報時細分,達到了3600000000分之1秒,作為我們工廠內部工作作息的時鐘周期,這数字實在是太長了,人類為了好記,取了一個叫主頻的名字,表示1秒鐘報時的次數,就是3.6GHz。
一不小心扯遠了,這次想給大家說一件事兒······
我們這座工廠的任務就是不斷的執行人類編寫的程序指令,咱廠里有8個車間,大家開足了馬力,就能同時執行8個線程,那速度那叫一個快。
可是廠里的老闆還是嫌我們不夠快,那天居然告訴我們要每個車間執行兩個線程,實現八核十六線程,是要把我們的勞動力壓榨到極致!我們都滿肚子怨言······
事情的起因是這樣的~~
有一次,我們一號車間的四人組趁着工作的空當,又鬥起了地主,突然領導過來視察。
“你們怎麼又在玩?是工作量不飽和嗎?”,見我們幾個閑着,領導一下就不高興了。
我趕緊上前解釋到:“不好意思領導,咱們剛剛執行了一條指令,需要內存中的一塊數據,剛好又不在緩存中,所以找內存那傢伙要數據去了,這不您也知道那傢伙向來很慢,我們閑着也是閑着所以就稍微放鬆了一下······”
聽了我的話領導一下皺起了眉頭,“還給我狡辯,廠里現在不是用上了亂序執行技術嗎?有這閑功夫你們可以先執行後面的指令啊”
“這我們當然知道,這不您看,我們把後面那幾條指令也都處理了,現在遇到了一條沒法提前執行的指令才停下來的”
領導看了一下問到:“為啥那條不能提前執行?”
“那是一個加法指令,加數依賴於現在正在處理的指令的運算結果呢,所以內存那傢伙不來消息,我們只能擱置着了”,我繼續解釋到。
領導聽完,一臉不高興的離開了。
過了幾天,領導又來到咱們一號車間來了,也不知道怎麼回事,這明明有八個車間,領導怎麼老愛往我們這邊跑。
不過這一次,我們沒有斗地主,正在辛辛苦苦的工作着。
當時,我正在執行一個浮點數運算,領導過來一看,拍了拍我的肩膀說到:“喲,阿Q,忙着吶,這是在做什麼啊?”
我笑着說到:“領導好,我剛剛用浮點數運算電路單元做了一個浮點數乘法,正在等待計算結果呢”
領導點了點頭,往周邊巡視一圈,指着一堆設備問到:“這一堆是什麼?”
“哦,那是整數運算電路單元,這條指令用不到它”
領導再次點了點頭,若有所思的離開了。
又過了幾天,廠里召開了一次會議,八個車間都派了代表參會。
會上,領導發話了:“前段時間我到各個車間視察,發現現在咱們廠里資源浪費的情況很嚴重!”
二號車間的虎子一聽就坐不住了,“領導,咱們大傢伙工作都挺賣力的,哪裡有浪費啊?”
領導瞥了一眼,繼續說到:“一方面,廠里的計算資源——電路設備得不到充分利用,另一方面,又因為內存讀取緩慢、指令依賴等方面的原因,浪費大家太多時間花在等待上”
八號車間的代表向來愛拍馬屁,接着領導的話問到:“領導是有什麼指示?我們八號車間絕對支持!”
“我們幾個管理層經過討論,決定讓你們一個車間由現在執行一個線程,變成執行兩個線程!”
領導這話一出,會場竊竊私語此起彼伏。虎子偏頭小聲對我說到:“這資本家改不了剝削的本色,這壓榨的也太狠了!”
領導咳嗽了幾聲,會場再次安靜了下來。
我起身問到:“領導,這咱們一個車間怎麼能執行兩個線程呢,每個車間的寄存器只有一套,這用起來豈不是要亂掉?”
“這個你不用擔心,我們會給每個車間配兩套寄存器!”
五號車間的代表一聽說到:“要不再給我們添點人手吧,這樣效率肯定提升快!”
領導一聽笑着說到:“還添人手?要不要再給你們添點運算設備?那我不如再增加幾個車間,還開這會幹嘛?這次會議的主題就是如何讓我們現有的資源得到最大程度的利用,減少浪費現象!”
會場一度陷入了尷尬又緊張的氛圍。
還是虎子打破了安靜,“領導,這兩個線程的工作該怎麼開展,我們心底沒有數啊!”
領導滿意的笑了一下:“這才是你們該問的問題嘛!每個車間回去重新分配一下工作,劃分為兩套班子,各自維護一套寄存器,對外宣稱你們是兩個不同的物理核心,但各車間的緩存和計算資源還是只有一套。你們內部協調好,在執行代碼指令的時候,充分利用等待的時間執行另一個線程的指令,這樣也不用擔心指令依賴的問題。”
大家一邊聽一邊做着筆記。
“還有,如果遇到資源閑置的情況,也可以同時執行兩個線程的指令。比如一個線程是執行整數運算指令,一個線程是執行浮點數運算指令,就可以一起來,讓工廠的計算資源充分用起來,別閑置。”
看我們都認真的記着筆記,領導露出了滿意的笑容,“都記好了吧,我們給這項革命性的技術取了個特別酷的名字,叫超線程技術!”
散會後,大家都紛紛抱怨,把大家逼得這麼緊,以後上班看來是沒法摸魚了,這日子真是越來越難過了。
不過,抱怨歸抱怨,大家還是得按照新規來執行。
很快,廠里就落地了這項技術,咱們一個車間搖身一變,變成了倆,咱們原來八核八線程的CPU一下變成了八核十六線程。操作系統那幫人都被我們給騙了,還以為咱們是十六核的CPU呢!
不過畢竟計算資源還是只有一份,遇到兩個線程都要使用同樣的計算單元時,還是得要排隊,還要花時間在兩個線程之前的協調工作上,所以整體工作效率的根本沒有2倍,絕大多數時候能提升個20%-30%就不錯了。
不僅如此,車間改造后,增加了新的邏輯電路單元,咱這CPU工廠的功耗也更大了,工廠門口那座巨大的風扇也得加大馬力給我們降溫了。
廠子里對這項技術的反對聲音開始不絕於耳。
不過後來發生了一件事,讓人們不得不關閉這項技術。聽聞這個消息,我們都樂開了花,看來又可以繼續摸魚了······
每當有網絡數據包到來,網卡那傢伙就通過中斷告訴我們CPU去處理。
可咱明明有8個車間,它非得一個勁的只給我們車間發中斷,搞得我們都沒法好好工作。
終於,我忍不住了······
預知後事如何,請關注後續精彩······
說明:
超線程技術出現時間其實早於多核技術。本故事僅為敘述方便,不代表二者真實的發展順序。
真慘!連各大編程語言都擺起地攤了!
因為一個跨域請求,我差點丟了飯碗
完了!CPU一味求快出事兒了!
哈希表哪家強?幾大編程語言吵起來了!
一個HTTP數據包的奇幻之旅
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※教你寫出一流的銷售文案?
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※回頭車貨運收費標準
※別再煩惱如何寫文案,掌握八大原則!
※超省錢租車方案
※產品缺大量曝光嗎?你需要的是一流包裝設計!
參考《附003.Kubeadm部署Kubernetes》。
參考《附003.Kubeadm部署Kubernetes》。
| 節點主機名 | IP | 類型 | 運行服務 |
|---|---|---|---|
| master01 | 172.24.8.71 | Kubernetes master節點 | docker、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、 kubectl、kubelet、metrics、calico、HAProxy、KeepAlived |
| master02 | 172.24.8.72 | Kubernetes master節點 | docker、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、 kubectl、kubelet、metrics、calico、HAProxy、KeepAlived |
| master03 | 172.24.8.73 | Kubernetes master節點 | docker、etcd、kube-apiserver、kube-scheduler、kube-controller-manager、 kubectl、kubelet、metrics、calico、HAProxy、KeepAlived |
| worker01 | 172.24.8.74 | Kubernetes node節點1 | docker、kubelet、proxy、calico |
| worker02 | 172.24.8.75 | Kubernetes node節點2 | docker、kubelet、proxy、calico |
| worker03 | 172.24.8.76 | Kubernetes node節點3 | docker、kubelet、proxy、calico |
| VIP | 172.24.8.100 |
Kubernetes的高可用主要指的是控制平面的高可用,即指多套Master節點組件和Etcd組件,工作節點通過負載均衡連接到各Master。
Kubernetes高可用架構中etcd與Master節點組件混布方式特點:
提示:本實驗使用Keepalived+HAProxy架構實現Kubernetes的高可用。
[root@master01 ~]# hostnamectl set-hostname master01 #其他節點依次修改
[root@master01 ~]# cat >> /etc/hosts << EOF
172.24.8.71 master01
172.24.8.72 master02
172.24.8.73 master03
172.24.8.74 worker01
172.24.8.75 worker02
172.24.8.76 worker03
EOF
[root@master01 ~]# vi k8sinit.sh
# Initialize the machine. This needs to be executed on every machine.
# Install docker
useradd -m docker
yum -y install yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y install docker-ce
mkdir /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://dbzucv6w.mirror.aliyuncs.com"],
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
EOF
systemctl restart docker
systemctl enable docker
systemctl status docker
# Disable the SELinux.
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
# Turn off and disable the firewalld.
systemctl stop firewalld
systemctl disable firewalld
# Modify related kernel parameters & Disable the swap.
cat > /etc/sysctl.d/k8s.conf << EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.tcp_tw_recycle = 0
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
net.ipv6.conf.all.disable_ipv6 = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf >&/dev/null
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
modprobe br_netfilter
# Add ipvs modules
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
modprobe -- nf_conntrack
EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules
bash /etc/sysconfig/modules/ipvs.modules
# Install rpm
yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget
# Update kernel
rpm --import http://down.linuxsb.com:8888/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://down.linuxsb.com:8888/elrepo-release-7.0-4.el7.elrepo.noarch.rpm
yum --disablerepo="*" --enablerepo="elrepo-kernel" install -y kernel-ml
sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT=0/' /etc/default/grub
grub2-mkconfig -o /boot/grub2/grub.cfg
yum update -y
# Reboot the machine.
# reboot
提示:對於某些特性,可能需要升級內核,內核升級操作見《018.Linux升級內核》。
4.19版及以上內核nf_conntrack_ipv4已經改為nf_conntrack。
為了更方便遠程分發文件和執行命令,本實驗配置master01節點到其它節點的 ssh 信任關係。
[root@master01 ~]# ssh-keygen -f ~/.ssh/id_rsa -N ''
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master01
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master02
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master03
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@worker01
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@worker02
[root@master01 ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@worker03
提示:此操作僅需要在master節點操作。
[root@master01 ~]# vi environment.sh
#!/bin/sh
#****************************************************************#
# ScriptName: environment.sh
# Author: xhy
# Create Date: 2020-05-30 16:30
# Modify Author: xhy
# Modify Date: 2020-06-15 17:55
# Version:
#***************************************************************#
# 集群 MASTER 機器 IP 數組
export MASTER_IPS=(172.24.8.71 172.24.8.72 172.24.8.73)
# 集群 MASTER IP 對應的主機名數組
export MASTER_NAMES=(master01 master02 master03)
# 集群 NODE 機器 IP 數組
export NODE_IPS=(172.24.8.74 172.24.8.75 172.24.8.76)
# 集群 NODE IP 對應的主機名數組
export NODE_NAMES=(worker01 worker02 worker03)
# 集群所有機器 IP 數組
export ALL_IPS=(172.24.8.71 172.24.8.72 172.24.8.73 172.24.8.74 172.24.8.75 172.24.8.76)
# 集群所有IP 對應的主機名數組
export ALL_NAMES=(master01 master02 master03 worker01 worker02 worker03)
[root@master01 ~]# source environment.sh
[root@master01 ~]# chmod +x *.sh
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
do
echo ">>> ${all_ip}"
scp -rp /etc/hosts root@${all_ip}:/etc/hosts
scp -rp k8sinit.sh root@${all_ip}:/root/
ssh root@${all_ip} "bash /root/k8sinit.sh"
done
需要在每台機器上都安裝以下的軟件包:
kubeadm不能安裝或管理 kubelet 或 kubectl ,所以得保證他們滿足通過 kubeadm 安裝的 Kubernetes 控制層對版本的要求。如果版本沒有滿足要求,可能導致一些意外錯誤或問題。
具體相關組件安裝見《附001.kubectl介紹及使用》。
提示:Kubernetes 1.18版本所有兼容相應組件的版本參考:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.18.md。
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
do
echo ">>> ${all_ip}"
ssh root@${all_ip} "cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF"
ssh root@${all_ip} "yum install -y kubeadm-1.18.3-0.x86_64 kubelet-1.18.3-0.x86_64 kubectl-1.18.3-0.x86_64 --disableexcludes=kubernetes"
ssh root@${all_ip} "systemctl enable kubelet"
done
[root@master01 ~]# yum search -y kubelet --showduplicates #查看相應版本
提示:如上僅需Master01節點操作,從而實現所有節點自動化安裝,同時此時不需要啟動kubelet,初始化的過程中會自動啟動的,如果此時啟動了會出現報錯,忽略即可。
說明:同時安裝了cri-tools, kubernetes-cni, socat三個依賴:
socat:kubelet的依賴;
cri-tools:即CRI(Container Runtime Interface)容器運行時接口的命令行工具。
[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
do
echo ">>> ${master_ip}"
ssh root@${master_ip} "yum -y install gcc gcc-c++ make libnl libnl-devel libnfnetlink-devel openssl-devel wget openssh-clients systemd-devel zlib-devel pcre-devel libnl3-devel"
ssh root@${master_ip} "wget http://down.linuxsb.com:8888/software/haproxy-2.1.6.tar.gz"
ssh root@${master_ip} "tar -zxvf haproxy-2.1.6.tar.gz"
ssh root@${master_ip} "cd haproxy-2.1.6/ && make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 PREFIX=/usr/local/haprpxy && make install PREFIX=/usr/local/haproxy"
ssh root@${master_ip} "cp /usr/local/haproxy/sbin/haproxy /usr/sbin/"
ssh root@${master_ip} "useradd -r haproxy && usermod -G haproxy haproxy"
ssh root@${master_ip} "mkdir -p /etc/haproxy && cp -r /root/haproxy-2.1.6/examples/errorfiles/ /usr/local/haproxy/"
done
[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
do
echo ">>> ${master_ip}"
ssh root@${master_ip} "yum -y install gcc gcc-c++ make libnl libnl-devel libnfnetlink-devel openssl-devel"
ssh root@${master_ip} "wget http://down.linuxsb.com:8888/software/keepalived-2.0.20.tar.gz"
ssh root@${master_ip} "tar -zxvf keepalived-2.0.20.tar.gz"
ssh root@${master_ip} "cd keepalived-2.0.20/ && ./configure --sysconf=/etc --prefix=/usr/local/keepalived && make && make install"
done
提示:如上僅需Master01節點操作,從而實現所有節點自動化安裝。
[root@master01 ~]# wget http://down.linuxsb.com:8888/hakek8s.sh #拉取自動部署腳本
[root@master01 ~]# chmod u+x hakek8s.sh
[root@master01 ~]# vi hakek8s.sh
#!/bin/sh
#****************************************************************#
# ScriptName: hakek8s.sh
# Author: xhy
# Create Date: 2020-06-08 20:00
# Modify Author: xhy
# Modify Date: 2020-06-15 18:15
# Version: v2
#***************************************************************#
#######################################
# set variables below to create the config files, all files will create at ./config directory
#######################################
# master keepalived virtual ip address
export K8SHA_VIP=172.24.8.100
# master01 ip address
export K8SHA_IP1=172.24.8.71
# master02 ip address
export K8SHA_IP2=172.24.8.72
# master03 ip address
export K8SHA_IP3=172.24.8.73
# master01 hostname
export K8SHA_HOST1=master01
# master02 hostname
export K8SHA_HOST2=master02
# master03 hostname
export K8SHA_HOST3=master03
# master01 network interface name
export K8SHA_NETINF1=eth0
# master02 network interface name
export K8SHA_NETINF2=eth0
# master03 network interface name
export K8SHA_NETINF3=eth0
# keepalived auth_pass config
export K8SHA_KEEPALIVED_AUTH=412f7dc3bfed32194d1600c483e10ad1d
# kubernetes CIDR pod subnet
export K8SHA_PODCIDR=10.10.0.0
# kubernetes CIDR svc subnet
export K8SHA_SVCCIDR=10.20.0.0
[root@master01 ~]# ./hakek8s.sh
解釋:如上僅需Master01節點操作。執行hakek8s.sh腳本後會生產如下配置文件清單:
[root@master01 ~]# cat kubeadm-config.yaml #檢查集群初始化配置
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
networking:
serviceSubnet: "10.20.0.0/16" #設置svc網段
podSubnet: "10.10.0.0/16" #設置Pod網段
dnsDomain: "cluster.local"
kubernetesVersion: "v1.18.3" #設置安裝版本
controlPlaneEndpoint: "172.24.11.254:16443" #設置相關API VIP地址
apiServer:
certSANs:
- master01
- master02
- master03
- 127.0.0.1
- 192.168.2.11
- 192.168.2.12
- 192.168.2.13
- 192.168.2.200
timeoutForControlPlane: 4m0s
certificatesDir: "/etc/kubernetes/pki"
imageRepository: "k8s.gcr.io"
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
featureGates:
SupportIPVSProxyMode: true
mode: ipvs
提示:如上僅需Master01節點操作,更多config文件參考:https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2。
此kubeadm部署初始化配置更多參考:https://pkg.go.dev/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2?tab=doc。
[root@master01 ~]# cat /etc/keepalived/keepalived.conf
[root@master01 ~]# cat /etc/keepalived/check_apiserver.sh 確認Keepalived配置
[root@master01 ~]# for master_ip in ${MASTER_IPS[@]}
do
echo ">>> ${master_ip}"
ssh root@${master_ip} "systemctl start haproxy.service && systemctl enable haproxy.service"
ssh root@${master_ip} "systemctl start keepalived.service && systemctl enable keepalived.service"
ssh root@${master_ip} "systemctl status keepalived.service | grep Active"
ssh root@${master_ip} "systemctl status haproxy.service | grep Active"
done
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
do
echo ">>> ${all_ip}"
ssh root@${all_ip} "ping -c1 172.24.8.100"
done #等待30s執行檢查
提示:如上僅需Master01節點操作,從而實現所有節點自動啟動服務。
[root@master01 ~]# kubeadm --kubernetes-version=v1.18.3 config images list #列出所需鏡像
[root@master01 ~]# cat config/downimage.sh #確認版本
#!/bin/sh
#****************************************************************#
# ScriptName: downimage.sh
# Author: xhy
# Create Date: 2020-05-29 19:55
# Modify Author: xhy
# Modify Date: 2020-06-10 19:15
# Version: v2
#***************************************************************#
KUBE_VERSION=v1.18.3
CALICO_VERSION=v3.14.1
CALICO_URL=calico
KUBE_PAUSE_VERSION=3.2
ETCD_VERSION=3.4.3-0
CORE_DNS_VERSION=1.6.7
GCR_URL=k8s.gcr.io
METRICS_SERVER_VERSION=v0.3.6
INGRESS_VERSION=0.32.0
CSI_PROVISIONER_VERSION=v1.4.0
CSI_NODE_DRIVER_VERSION=v1.2.0
CSI_ATTACHER_VERSION=v2.0.0
CSI_RESIZER_VERSION=v0.3.0
ALIYUN_URL=registry.cn-hangzhou.aliyuncs.com/google_containers
UCLOUD_URL=uhub.service.ucloud.cn/uxhy
QUAY_URL=quay.io
kubeimages=(kube-proxy:${KUBE_VERSION}
kube-scheduler:${KUBE_VERSION}
kube-controller-manager:${KUBE_VERSION}
kube-apiserver:${KUBE_VERSION}
pause:${KUBE_PAUSE_VERSION}
etcd:${ETCD_VERSION}
coredns:${CORE_DNS_VERSION}
metrics-server-amd64:${METRICS_SERVER_VERSION}
)
for kubeimageName in ${kubeimages[@]} ; do
docker pull $UCLOUD_URL/$kubeimageName
docker tag $UCLOUD_URL/$kubeimageName $GCR_URL/$kubeimageName
docker rmi $UCLOUD_URL/$kubeimageName
done
calimages=(cni:${CALICO_VERSION}
pod2daemon-flexvol:${CALICO_VERSION}
node:${CALICO_VERSION}
kube-controllers:${CALICO_VERSION})
for calimageName in ${calimages[@]} ; do
docker pull $UCLOUD_URL/$calimageName
docker tag $UCLOUD_URL/$calimageName $CALICO_URL/$calimageName
docker rmi $UCLOUD_URL/$calimageName
done
ingressimages=(nginx-ingress-controller:${INGRESS_VERSION})
for ingressimageName in ${ingressimages[@]} ; do
docker pull $UCLOUD_URL/$ingressimageName
docker tag $UCLOUD_URL/$ingressimageName $QUAY_URL/kubernetes-ingress-controller/$ingressimageName
docker rmi $UCLOUD_URL/$ingressimageName
done
csiimages=(csi-provisioner:${CSI_PROVISIONER_VERSION}
csi-node-driver-registrar:${CSI_NODE_DRIVER_VERSION}
csi-attacher:${CSI_ATTACHER_VERSION}
csi-resizer:${CSI_RESIZER_VERSION}
)
for csiimageName in ${csiimages[@]} ; do
docker pull $UCLOUD_URL/$csiimageName
docker tag $UCLOUD_URL/$csiimageName $QUAY_URL/k8scsi/$csiimageName
docker rmi $UCLOUD_URL/$csiimageName
done
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
do
echo ">>> ${all_ip}"
scp -rp config/downimage.sh root@${all_ip}:/root/
ssh root@${all_ip} "bash downimage.sh &"
done
提示:如上僅需Master01節點操作,從而實現所有節點自動拉取鏡像。
[root@master01 ~]# docker images #確認驗證
[root@master01 ~]# kubeadm init --config=kubeadm-config.yaml --upload-certs
You can now join any number of the control-plane node running the following command on each as root:
kubeadm join 172.24.8.100:16443 --token xifg5c.3mvph3nwx1srdf7l \
--discovery-token-ca-cert-hash sha256:031a8758ddad5431be4132ecd6445f33b17c2192c11e010209705816a4a53afd \
--control-plane --certificate-key 560c926e508ed6011cd35fe120a5163d3ca32e16b745cf1877da970e3e0982f0
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 172.24.8.100:16443 --token xifg5c.3mvph3nwx1srdf7l \
--discovery-token-ca-cert-hash sha256:031a8758ddad5431be4132ecd6445f33b17c2192c11e010209705816a4a53afd
注意:如上token具有默認24小時的有效期,token和hash值可通過如下方式獲取:
kubeadm token list
如果 Token 過期以後,可以輸入以下命令,生成新的 Token:
kubeadm token create
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
[root@master01 ~]# mkdir -p $HOME/.kube
[root@master01 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master01 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
[root@master01 ~]# cat << EOF >> ~/.bashrc
export KUBECONFIG=$HOME/.kube/config
EOF #設置KUBECONFIG環境變量
[root@master01 ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master01 ~]# source ~/.bashrc
附加:初始化過程大致步驟如下:
提示:初始化僅需要在master01上執行,若初始化異常可通過kubeadm reset && rm -rf $HOME/.kube重置。
[root@master02 ~]# kubeadm join 172.24.8.100:16443 --token xifg5c.3mvph3nwx1srdf7l \
--discovery-token-ca-cert-hash sha256:031a8758ddad5431be4132ecd6445f33b17c2192c11e010209705816a4a53afd \
--control-plane --certificate-key 560c926e508ed6011cd35fe120a5163d3ca32e16b745cf1877da970e3e0982f0
[root@master02 ~]# mkdir -p $HOME/.kube
[root@master02 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master02 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
[root@master02 ~]# cat << EOF >> ~/.bashrc
export KUBECONFIG=$HOME/.kube/config
EOF #設置KUBECONFIG環境變量
[root@master02 ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master02 ~]# source ~/.bashrc
提示:master03也如上執行添加至集群的controlplane。
提示:若添加異常可通過kubeadm reset && rm -rf $HOME/.kube重置。
[root@master01 ~]# kubectl taint nodes --all node-role.kubernetes.io/master- #允許master部署應用
提示:部署完內部應用后可使用kubectl taint node master01 node-role.kubernetes.io/master=””:NoSchedule重新設置Master為Master Only 狀態。
[root@master01 ~]# cat config/calico/calico.yaml #檢查配置
……
- name: CALICO_IPV4POOL_CIDR
value: "10.10.0.0/16" #檢查Pod網段
……
- name: IP_AUTODETECTION_METHOD
value: "interface=eth.*" #檢查節點之間的網卡
# Auto-detect the BGP IP address.
- name: IP
value: "autodetect"
……
[root@master01 ~]# kubectl apply -f config/calico/calico.yaml
[root@master01 ~]# kubectl get pods --all-namespaces -o wide #查看部署
[root@master01 ~]# kubectl get nodes
[root@master01 ~]# vi /etc/kubernetes/manifests/kube-apiserver.yaml
……
- --service-node-port-range=1-65535
……
提示:如上僅需在所有Master節點操作。
[root@master01 ~]# for node_ip in ${NODE_IPS[@]}
do
echo ">>> ${node_ip}"
ssh root@${node_ip} "kubeadm join 172.24.8.100:16443 --token xifg5c.3mvph3nwx1srdf7l \
--discovery-token-ca-cert-hash sha256:031a8758ddad5431be4132ecd6445f33b17c2192c11e010209705816a4a53afd"
ssh root@${node_ip} "systemctl enable kubelet.service"
done
提示:如上僅需Master01節點操作,從而實現所有Worker節點添加至集群,若添加異常可通過如下方式重置:
[root@node01 ~]# kubeadm reset
[root@node01 ~]# ifconfig cni0 down
[root@node01 ~]# ip link delete cni0
[root@node01 ~]# ifconfig flannel.1 down
[root@node01 ~]# ip link delete flannel.1
[root@node01 ~]# rm -rf /var/lib/cni/
[root@master01 ~]# kubectl get nodes #節點狀態
[root@master01 ~]# kubectl get cs #組件狀態
[root@master01 ~]# kubectl get serviceaccount #服務賬戶
[root@master01 ~]# kubectl cluster-info #集群信息
[root@master01 ~]# kubectl get pod -n kube-system -o wide #所有服務狀態
提示:更多Kubetcl使用參考:https://kubernetes.io/docs/reference/kubectl/kubectl/
https://kubernetes.io/docs/reference/kubectl/overview/
更多kubeadm使用參考:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/
Kubernetes的早期版本依靠Heapster來實現完整的性能數據採集和監控功能,Kubernetes從1.8版本開始,性能數據開始以Metrics API的方式提供標準化接口,並且從1.10版本開始將Heapster替換為Metrics Server。在Kubernetes新的監控體系中,Metrics Server用於提供核心指標(Core Metrics),包括Node、Pod的CPU和內存使用指標。
對其他自定義指標(Custom Metrics)的監控則由Prometheus等組件來完成。
有關聚合層知識參考:https://blog.csdn.net/liukuan73/article/details/81352637
kubeadm方式部署默認已開啟。
[root@master01 ~]# mkdir metrics
[root@master01 ~]# cd metrics/
[root@master01 metrics]# wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml
[root@master01 metrics]# vi components.yaml
……
apiVersion: apps/v1
kind: Deployment
……
spec:
replicas: 3 #根據集群規模調整副本數
……
spec:
hostNetwork: true
……
- name: metrics-server
image: k8s.gcr.io/metrics-server-amd64:v0.3.6
imagePullPolicy: IfNotPresent
args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP #追加此args
……
[root@master01 metrics]# kubectl apply -f components.yaml
[root@master01 metrics]# kubectl -n kube-system get pods -l k8s-app=metrics-server
NAME READY STATUS RESTARTS AGE
metrics-server-7b97647899-8txt4 1/1 Running 0 53s
metrics-server-7b97647899-btdwp 1/1 Running 0 53s
metrics-server-7b97647899-kbr8b 1/1 Running 0 53s
[root@k8smaster01 ~]# kubectl top nodes
[root@master01 metrics]# kubectl top pods --all-namespaces
提示:Metrics Server提供的數據也可以供HPA控制器使用,以實現基於CPU使用率或內存使用值的Pod自動擴縮容功能。
部署參考:https://linux48.com/container/2019-11-13-metrics-server.html
有關metrics更多部署參考:
https://kubernetes.io/docs/tasks/debug-application-cluster/resource-metrics-pipeline/
開啟開啟API Aggregation參考:
https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/
API Aggregation介紹參考:
https://kubernetes.io/docs/tasks/access-kubernetes-api/configure-aggregation-layer/
參考《附020.Nginx-ingress部署及使用》
[root@master01 ~]# kubectl label nodes master01 dashboard=yes
[root@master01 ~]# kubectl label nodes master02 dashboard=yes
[root@master01 ~]# kubectl label nodes master03 dashboard=yes
本實驗已獲取免費一年的證書,免費證書獲取可參考:https://freessl.cn。
[root@master01 ~]# mkdir -p /root/dashboard/certs
[root@master01 ~]# cd /root/dashboard/certs
[root@master01 certs]# mv k8s.odocker.com tls.crt
[root@master01 certs]# mv k8s.odocker.com tls.crt
[root@master01 certs]# ll
total 8.0K
-rw-r--r-- 1 root root 1.9K Jun 8 11:46 tls.crt
-rw-r--r-- 1 root root 1.7K Jun 8 11:46 tls.ke
提示:也可手動如下操作創建自簽證書:
[root@master01 ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=ZheJiang/L=HangZhou/O=Xianghy/OU=Xianghy/CN=k8s.odocker.com"
[root@master01 ~]# kubectl create ns kubernetes-dashboard #v2版本dashboard獨立ns
[root@master01 ~]# kubectl create secret generic kubernetes-dashboard-certs --from-file=$HOME/dashboard/certs/ -n kubernetes-dashboard
[root@master01 ~]# kubectl get secret kubernetes-dashboard-certs -n kubernetes-dashboard -o yaml #查看新證書
NAME TYPE DATA AGE
kubernetes-dashboard-certs Opaque 2 4s
[root@master01 ~]# cd /root/dashboard
[root@master01 dashboard]# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.1/aio/deploy/recommended.yaml
[root@master01 dashboard]# vi recommended.yaml
……
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort #新增
ports:
- port: 443
targetPort: 8443
nodePort: 30001 #新增
selector:
k8s-app: kubernetes-dashboard
---
…… #如下全部註釋
#apiVersion: v1
#kind: Secret
#metadata:
# labels:
# k8s-app: kubernetes-dashboard
# name: kubernetes-dashboard-certs
# namespace: kubernetes-dashboard
#type: Opaque
……
kind: Deployment
……
replicas: 3 #適當調整為3副本
……
imagePullPolicy: IfNotPresent #修改鏡像下載策略
ports:
- containerPort: 8443
protocol: TCP
args:
- --auto-generate-certificates
- --namespace=kubernetes-dashboard
- --tls-key-file=tls.key
- --tls-cert-file=tls.crt
- --token-ttl=3600 #追加如上args
……
nodeSelector:
"beta.kubernetes.io/os": linux
"dashboard": "yes" #部署在master節點
……
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
type: NodePort #新增
ports:
- port: 8000
nodePort: 30000 #新增
targetPort: 8000
selector:
k8s-app: dashboard-metrics-scraper
……
replicas: 3 #適當調整為3副本
……
nodeSelector:
"beta.kubernetes.io/os": linux
"dashboard": "yes" #部署在master節點
……
[root@master01 dashboard]# kubectl apply -f recommended.yaml
[root@master01 dashboard]# kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard
[root@master01 dashboard]# kubectl get services -n kubernetes-dashboard
[root@master01 dashboard]# kubectl get pods -o wide -n kubernetes-dashboard
提示:master01 NodePort 30001/TCP映射到 dashboard pod 443 端口。
提示:dashboard v2版本默認沒有創建具有管理員權限的賬戶,可如下操作創建。
[root@master01 dashboard]# vi dashboard-admin.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
[root@master01 dashboard]# kubectl apply -f dashboard-admin.yaml
[root@master01 ~]# cd /root/dashboard/certs
[root@master01 certs]# kubectl -n kubernetes-dashboard create secret tls kubernetes-dashboard-tls --cert=tls.crt --key=tls.key
[root@master01 certs]# kubectl -n kubernetes-dashboard describe secrets kubernetes-dashboard-tls
[root@master01 ~]# cd /root/dashboard/
[root@master01 dashboard]# vi dashboard-ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: kubernetes-dashboard-ingress
namespace: kubernetes-dashboard
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
#nginx.ingress.kubernetes.io/secure-backends: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_ssl_session_reuse off;
spec:
rules:
- host: k8s.odocker.com
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 443
tls:
- hosts:
- k8s.odocker.com
secretName: kubernetes-dashboard-tls
[root@master01 dashboard]# kubectl apply -f dashboard-ingress.yaml
[root@master01 dashboard]# kubectl -n kubernetes-dashboard get ingress
將k8s.odocker.com導入瀏覽器,並設置為信任,導入操作略。
使用token相對複雜,可將token添加至kubeconfig文件中,使用KubeConfig文件訪問dashboard。
[root@master01 dashboard]# ADMIN_SECRET=$(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')
[root@master01 dashboard]# DASHBOARD_LOGIN_TOKEN=$(kubectl describe secret -n kubernetes-dashboard ${ADMIN_SECRET} | grep -E '^token' | awk '{print $2}')
[root@master01 dashboard]# kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=172.24.8.100:16443 \
--kubeconfig=local-hakek8s-dashboard-admin.kubeconfig # 設置集群參數
[root@master01 dashboard]# kubectl config set-credentials dashboard_user \
--token=${DASHBOARD_LOGIN_TOKEN} \
--kubeconfig=local-hakek8s-dashboard-admin.kubeconfig # 設置客戶端認證參數,使用上面創建的 Token
[root@master01 dashboard]# kubectl config set-context default \
--cluster=kubernetes \
--user=dashboard_user \
--kubeconfig=local-hakek8s-dashboard-admin.kubeconfig # 設置上下文參數
[root@master01 dashboard]# kubectl config use-context default --kubeconfig=local-hakek8s-dashboard-admin.kubeconfig # 設置默認上下文
本實驗採用ingress所暴露的域名:https://k8s.odocker.com 方式訪問。
使用local-hakek8s-dashboard-admin.kubeconfig文件訪問:
提示:更多dashboard訪問方式及認證可參考 《附004.Kubernetes Dashboard簡介及使用》。
dashboard登錄整個流程可參考:https://www.cnadn.net/post/2613.html
Longhorn是用於Kubernetes的開源分佈式塊存儲系統。
提示:更多介紹參考:https://github.com/longhorn/longhorn。
[root@master01 ~]# source environment.sh
[root@master01 ~]# for all_ip in ${ALL_IPS[@]}
do
echo ">>> ${all_ip}"
ssh root@${all_ip} "yum -y install iscsi-initiator-utils &"
done
提示:所有節點都需要安裝。
[root@master01 ~]# mkdir longhorn
[root@master01 ~]# cd longhorn/
[root@master01 longhorn]# wget \
https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
[root@master01 longhorn]# vi longhorn.yaml
#……
---
kind: Service
apiVersion: v1
metadata:
labels:
app: longhorn-ui
name: longhorn-frontend
namespace: longhorn-system
spec:
type: NodePort #修改為nodeport
selector:
app: longhorn-ui
ports:
- port: 80
targetPort: 8000
nodePort: 30002
---
……
kind: DaemonSet
……
imagePullPolicy: IfNotPresent
……
#……
[root@master01 longhorn]# kubectl apply -f longhorn.yaml
[root@master01 longhorn]# kubectl -n longhorn-system get pods -o wide
提示:若部署異常可刪除重建,若出現無法刪除namespace,可通過如下操作進行刪除:
wget https://github.com/longhorn/longhorn/blob/master/uninstall/uninstall.yaml
rm -rf /var/lib/longhorn/
kubectl apply -f uninstall.yaml
kubectl delete -f longhorn.yaml
提示:默認longhorn部署完成已創建一個sc,也可通過如下手動編寫yaml創建。
[root@master01 longhorn]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
……
longhorn driver.longhorn.io Delete Immediate true 15m
[root@master01 longhorn]# vi longhornsc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: longhornsc
provisioner: rancher.io/longhorn
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "30"
fromBackup: ""
[root@master01 longhorn]# kubectl create -f longhornsc.yaml
[root@master01 longhorn]# vi longhornpod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: longhorn-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
name: longhorn-pod
namespace: default
spec:
containers:
- name: volume-test
image: nginx:stable-alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- name: volv
mountPath: /data
ports:
- containerPort: 80
volumes:
- name: volv
persistentVolumeClaim:
claimName: longhorn-pvc
[root@master01 longhorn]# kubectl apply -f longhornpod.yaml
[root@master01 longhorn]# kubectl get pods
[root@master01 longhorn]# kubectl get pvc
[root@master01 longhorn]# kubectl get pv
[root@master01 longhorn]# yum -y install httpd-tools
[root@master01 longhorn]# htpasswd -c auth xhy #創建用戶名和密碼
提示:也可通過如下命令創建:USER=xhy; PASSWORD=x120952576; echo "${USER}:$(openssl passwd -stdin -apr1 <<< ${PASSWORD})" >> auth
[root@master01 longhorn]# kubectl -n longhorn-system create secret generic longhorn-basic-auth --from-file=auth [root@master01 longhorn]# vi longhorn-ingress.yaml #創建ingress規則
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: longhorn-ingress
namespace: longhorn-system
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: longhorn-basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required '
spec:
rules:
- host: longhorn.odocker.com
http:
paths:
- path: /
backend:
serviceName: longhorn-frontend
servicePort: 80
[root@master01 longhorn]# kubectl apply -f longhorn-ingress.yaml
瀏覽器訪問:longhorn.odocker.com,並輸入賬號和密碼。
參考《053.集群管理-Helm工具》。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※超省錢租車方案
※別再煩惱如何寫文案,掌握八大原則!
※回頭車貨運收費標準
※教你寫出一流的銷售文案?
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
摘錄自2020年10月30日中央社報導
巴西國家太空署(INPE)最新數據指出,10月中西部大沼澤再次創下自1998年太空署開始監測以來,當月起火點數目最多紀錄,10月1日至28日共記錄有2825個起火點。
根據巴西刑警和聯邦警察調查,跡象顯示中西部大沼澤的火災是人為過失引起,至少4處農牧場業主涉嫌蓄意放火,燒毀2萬5000公頃山林。
國家太空署指出,2020年1月至10月28日,大沼澤共記錄2萬1084個起火點,是大沼澤生物群系史上最嚴重的火災。大沼澤生物群系是世界上最大的淹水平原區,一旦不下雨,平原不會被淹沒,火勢容易擴散。大沼澤今年面臨47年來最嚴重乾旱,更有助於火勢蔓延。
除了大沼澤外,亞馬遜的火災數量今年也屢創歷史新高,1月至10月28日,亞馬遜生物群系共記錄9萬1873個起火點,此外,今年1月至9月在雨林累計的起火點也創下2010年以來新高。
根據聯邦政府本身的數據顯示,位於瑪瑙斯市和博阿維斯塔市兩市之間的羅賴諾波利斯(Rorainopolis),是今年亞馬遜地區發生最多森林火災的城市,如果加上鄰近兩個城市,累計燒毀面積超過2萬公頃。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計最專業,超強功能平台可客製化
摘錄自2020年10月29日新頭殼報導
蝙蝠生病也懂得保持社交距離?!美國一項研究發現,野生的吸血蝙蝠(Desmodus rotundus)在被注入會生病的物質後,與其他蝙蝠或動物的接觸明顯變少,到等到牠們恢復健康,才會恢復原有的社交活動。
《行為生態學》(Behavioural Ecology)期刊發表一項最新研究《持續追蹤野生吸向蝙蝠染病對社交的影響》。研究人員是在2018年4月下旬,於伯利茲(Belize)Lamanai的一棵空心樹上,捕獲31隻成年的雌性吸血蝙蝠,其中16隻被注射脂多醣,讓蝙蝠出現發炎等生病症狀,而另外15隻則注射生理食鹽水對照。結果發現,在注射藥劑6小時內,被注射會生病物質的吸血蝙蝠與同伴互動數量,比注射食鹽水的蝙蝠少了約4隻。
研究發現,對照蝙蝠與每個對照蝙蝠有49%的接觸,但與生病蝙蝠只有35%的接觸。雖然之前有科學家在實驗室裡就發現此現象,但這是第一次在野外對野生蝙蝠進行研究,研究者得出結論表示,牠們真的會在生病時保持社交距離。且每隻生病蝙蝠的伴侶,陪伴時間平均也少了25分鐘,而病人本身則是睡得更多、移動得更少。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※想知道最厲害的網頁設計公司"嚨底家"!
※別再煩惱如何寫文案,掌握八大原則!
※產品缺大量曝光嗎?你需要的是一流包裝設計!