穩當國內新能源車銷售冠軍 比亞迪今年前10月售逾13000輛

10日,比亞迪官方公佈了新能源汽車的銷量情況。比亞迪秦10月銷售1702輛,1-10月累計11175輛。e6電動車銷售199輛,1-10月累計2203輛。今年1-10比亞迪新能源乘用車累計銷量達13378輛,穩居國內新能源汽車銷量冠軍寶座。   受免購置稅政策和電池產能釋放的雙重效應,比亞迪秦9月銷量環比增加29%,其中在上海的上牌量達1212台,上海已成為比亞迪秦在全國最大的市場。10月銷量增長速度放緩,可見目前其電池產能仍不能滿足新能源汽車發展的需求。比亞迪副總經理李雲飛透露,第二個電池廠9月建成後,能逐步消化以前累計近萬輛的秦訂單和新增訂單要求。   面向公共領域的純電動車型e6每月銷量保持在200輛左右。根據比亞迪總裁王傳福公佈的戰略計畫,比亞迪將在e6的基礎上新增E3/E5兩款產品,並推出e6的行政版,主打私家車市場。另外,比亞迪唐插電式混動SUV車型將於12月上市。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

太魯閣與日月潭分別推出電動公車與電動船

知名的台灣景點太魯閣國家公園與日月潭,為了進一步推動綠能、低碳意識,分別推出電動公車以及遊憩電動船,將先後於本月投入營運。電動交通工具除了具有環保功能,其安靜、清潔的特質,也有助維護風景區環境品質。

日月潭國家風景區於14、15日分別展開兩艘電動船「娜魯灣號」與「希望之星」的啟航儀式。娜魯灣號是日月潭休閒育樂公司所有,採取低阻力雙胴體船型設計,提升電池的續航力。而希望之星則隸屬於湛岸沙蓮遊船公司,是日月潭第一艘20噸以上的大型電動客船,最多可容納85名乘客。在這兩艘電動船投入使用後,日月潭風景區已有七艘電動船隻。

另一方面,由花蓮縣政府與太魯閣客運公司合作推出的國家公園綠能電動公車則標榜零排放、零污染、低噪音的綠能精神。花蓮縣政府已於今年三月推出「東華大學線」的綠能公車,並將於本月底啟動「太魯閣線」試營運,往返新城火車站與天祥,服務前往太魯閣的遊客。太魯閣客運總經理林志晟表示,太魯閣線公車係全電動車,採用西門子馬達系統,還有低平台、輪椅升降設備等無障礙設計。太魯閣線未來的目標是投入100輛電動公車,以取代遊覽車與自小客車。

(照片:新城~天祥電動公車路線啟用。照片來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

特斯拉大舉安裝機器人 「X 戰警」投入產能將拉高 50%

  美國豪華電動車製造商特斯拉 (Tesla) 宣佈,位於加州費利蒙 (Fremont) 的廠房最近進行了一次大改造、大舉採納機器人,預期到了 2015 年底年產能可望從今年的 35,000 台進一步攀升 50%。   特斯拉 Fremont 組裝廠最近一度暫停生產 2 週、以便升級組裝設備,而最大的變化就在組裝線改採先進的機器人,可把整輛車以最精確的方式舉高,還能節省空間。這些最新的機器人應該很快就能替汽車安裝電池,讓人類不必再負擔如此吃重的工作,同時還能將安裝的時間從原本的 4 分鐘縮短至 2 分鐘。   特斯拉並以電影「X 戰警」的角色為這些機器人命名:查爾斯 (Xavier) 是組裝線入口處的舉重機,任務是把汽車從電軌挪至地板;金鋼狼 (Wolverine)、猛獸 (Beast) 是舉重力量更為強大的機器人;暴風女 (Storm)、鋼人 (Colossus) 位在底盤組裝線的最末端;火神 (Vulcan)、法官 (Havok) 則組隊把車送回電軌。   除了機器人之外,特斯拉也把組裝線改造的更為流暢與自動化,現在一週已能生產約 1,000 輛車,未來有機會透過小幅度的修正把產能進一步拉高。特斯拉本次在 2 週的改造期間內總計安裝了 10 台全球最大的機器人。
在動力系統 (powertrain) 的製作方面,特斯拉也增添了高階機器人,可將電池的日處理量從原本的 80 萬顆拉升至 100 萬顆。     (Source:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

Model X 鷹翼門問題多 恐延至 2017 年問世

  美國豪華電動車製造商特斯拉 (Tesla ) 為何要將電動休旅車 Model X  的發表時間點從原先的 2015 年第二季延後到第三季?大摩(Morgan Stanley) 認為,這可能是與 Model X 的鷹翼門 (falcon doors) 有關。   MarketWatch、ValueWalk 18 日報導,大摩分析師 Adam Jonas 17 日發表研究報告指出,鷹翼門的設計與機械原理頗為複雜,它擁有 2 個鏈接點,而傳統的鷗翼門 (gull-wing doors) 則只有一個鏈接點。   使用鷹翼門的目的是要讓 Model X 在狹窄的空間 (例如停車場與車庫) 也能順利開門。不過,大摩認為,鷹翼門有 2 個鏈接點,開啟後所需的空間恐怕會比鷗翼門還要多。   大摩猜測,特斯拉可能會再度推延 Model X 的發表時程,也許會遞延至 2017 年初。不過,該證券認為特斯拉不會輕易放棄鷗翼門,因為這會是 Model X 的一大賣點。     (Source:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

第五屆新能源汽車峰會暨展覽會2014完美落幕好評如潮

第五屆新能源汽車峰會暨展覽會2014於11月12-14日在中國北京海航大廈萬豪酒店召開。大會由中國汽車工業協會和決策者會議聯合主辦,交通大學汽車工程研究院協辦。主題為新增長局勢下的中國新能源汽車產業動態聚焦,來自國家環保部、國家發改委、北京市科委、中國汽車工業協會、全國乘用車市場訊息聯席會、中國一汽、北汽、吉利、重慶長安新能源、奇瑞新能源汽車、海堅電機、三菱電機、海科新能源等政府協會整車廠商和核心零部件公司、科研機構和媒體機構等集聚一堂,探討新能源整車商專案、戰略規劃以及對核心設備的需求、對於新能源汽車電氣以及動力系統、電控系統、智慧汽車創新、動力電池沖換電基建建設系統的案例分析以及核心技術。

第五屆新能源汽車峰會暨展覽會2014自確定舉辦以來,一直受到了業內廣泛關注。它的成長與輝煌離不開您的大力支持。對此,我們要特別感謝所有的支持機構,使大會更具專業性;所有的嘉賓,在大會上與所有行業人士分享最新資訊與觀點;所有的贊助商,對於大會工作的理解與支持;所有的媒體朋友們,在大會前夕所做的宣傳與付出的努力。

三天的會議期間,我們聆聽到了眾多演講嘉賓精彩紛呈的演說。

中國汽車工業協會副秘書長葉盛基上臺致大會歡迎詞,他對本屆峰會召開表示熱烈的祝賀,對各位的到來和深度的參與表示衷心的感謝,同時對長期以來支持和關注中國汽車工業發展,以及中國新能源汽車發展的各界人士表示誠摯的謝意。
國家環境保護部落實大氣污染防治司大氣處副處長汪濤為大家分享了落實大氣污染防治行動,加強機動車環保達標監管的這方面課題。
國家發展和改革委員會能源研究所清潔發展機制項目管理中心康豔兵給我們作關於生態文明背景下我國新能源汽車產業發展的主題分享。
北京市科學技術委員會新能源與新材料處處長許心超發言的題目是北京新能源汽車產業發展實踐。
中國第一汽車集團技術中心電動車部的部長劉明輝博士做了一汽新能源汽車技術創新與展望的報告。
重慶長安新能源汽車有限公司副總經理周安健的演講題目是長安新能源電控系統的自主研發。
奇瑞新能源汽車技術有限公司副總經理孫衛健演講的題目是電商時代-新能源汽車行銷模式之探索。
吉利電子傳動有限公司技術部部長于海生的演講主題是CHS混合動力系統平臺開發。
上海中科深江電動車輛有限公司總經理、中科院電動汽車研發中心副主任孫江明演講主題是新能源汽車動力總成系統的集成創新。
北京汽車集團有限公司新能源汽車管理部高級工程師王智文分享了新能源發力元年的北汽集團純電動汽車產業佈局。
上海中科力帆電動汽車有限公司副總經理夏先明分享了力帆新能源汽車產業模式與創新技術—換電。
全國乘用車市場訊息聯席會秘書長助理孫木子的演講是關於從市場角度談新能源汽車乘用車的熱門話題。
山東省汽車行業協會常務副會長兼秘書長魏學勤就山東省新能源汽車產業發展現狀與趨勢進行演講。
中國電源工業協會常務副理事長方英民分享的主題是新能源汽車與鋰電池成組管理。
Kopis Group總裁Tim WEAVER的演講主題是政府刺激和政策導向在新能源汽車推廣中的作用。
北京理工大學機械與車輛學院車輛工程系副主任何洪文的演講主題是汽車混合動力系統控制及能量管理。
高工產業研究院(GGII)院長張小飛分享了新能源汽車巡迴成果報告。
國家863電動車重大專項動力電池測試中心主任王子冬圍繞電動車的商業模式開發動力電池系統進行主題分享。
清華大學汽車安全與節能國家重點實驗室副主任陳全世探討了中國新能源汽車技術及產業發展的幾個問題。
李爾集團高功率電子開發工程經理狄新輝的演講主題是車載充電機——技術領先成本優化的新能源技術。
海堅電機首席執行官Jae Hak KIM的演講主題是非稀土磁鐵永磁電機在電動汽車上的應用。
常州海科新能源技術有限公司董事長兼總經理廖越峰的演講主題是飛輪動力技術的研發現狀及發展方向。
安徽巨一自動化裝備有限公司M&C事業部總經理王淑旺分享了ISO26262 在電驅動產品開發中的應用。
富華德電子(東莞)有限公司總工程師王樹曉分享了超級電容在汽車上的應用
微宏動力系統(湖州)有限公司技術中心主任鄭卓群博士的演講主題是快速充電電池技術在電動汽車上的應用。
上海國際汽車城(集團)有限公司副總經理曹光宇分享了上海電動汽車國際示範區商業模式探索。
電車匯創始人辛蒂嘉就新能源汽車與互聯網展開討論。
三菱電機機電(上海)有限公司汽車電源模組事業部副經理及設計部部長Khalid HUSSEIN先生的演講主題是應用於電動汽車的先進功率模組。

大會現場,我們收到了許多人的贊許:

中國汽車工業協會副秘書長葉盛基說到:”我注意到峰會的內容,我覺得既有戰略方面的論壇和研討,也有關於技術的一個應用和融合,我認為非常好,一個論壇應該涉及到不同的層面,而且不同的廣泛程度和深度去研討整個產業發展過程中的重點事件、焦點事件和關鍵事件以及我們產業發展所共鳴的事件,我認為是非常有意義的,對新能源汽車產業的發展有積極的促進作用。”

中國第一汽車股份有限公司技術中心電動車部劉明輝部長:”這個會我感覺辦的還比較專業,參加會議的都是新能源汽車方面的一些主要的參與者,這裡有整車廠也有一些重要的零部件供應商,說的比較專業的話就是都是一些落實新能源汽車產業化的主要的參與者在這裡,這樣可以讓大家在一個比較好的環境下溝通交流,從我自身感覺通過參加這個會跟很多整車廠有一個很好的交流,同時跟這些零件供應商也有很好的交流。”

Hofer GmbH & Co. KG陳原:”這是一個交流的很好的場合,我看到有很多的主機廠都在這邊,所有的主機廠都有他們新能源的項目在開展,不僅僅是Hofer公司,當然對Hofer公司來講可以很好的介紹公司的經驗給所以的主機廠,那麼對於產業來講也是一個很好的機會促進溝通,包括一些政府法規的頒佈都是一個非常好的場合。”

上海鷹峰電子徐颯:”本次峰會,客戶對來的人的專業度還是不錯的,對於我們一個新興的產品來說,關注度和互動還是頗有成效的,我們會考慮明年會繼續參加這個會議的。

全國乘用車市場訊息聯席會孫木子讚歎道:“非常榮幸參加在北京舉辦的新能源汽車盛會!我們相信隨著用戶接受度的逐步提升,新能源汽車在中國發展將越來越好!”

Jochem KUEHNLE from DANA said that “Very well organized conference! Excellent presenters and a handed by various technical experts and decision makers of the automobile industry.Different with conference participants would be of interest for the next years’ event! Thanks again to CDMC and everybody else envolved in organizing this great event!”

上海中科力帆電動汽車有限公司夏先明贊許道:”非常高興參加新能源汽車峰會。受益很大,希望下次再會!”

吉利電子傳動有限公司於海生表示:”非常榮幸參加此次會議,感謝主辦方的辛勤勞動,對本次會議總體感覺較好。但需要加強對內容和演講嘉賓的要求,對模組進行規劃和梳理。祝大會越辦越好!”

亞普汽車部件有限公司王傳寶說:“非常專業,對新能源汽車的發展趨勢很有用。”

重慶長安新能源汽車有限公司萬豔寬表示:“各位領導演講的內容很多,資訊很全,演講人都是本領域的專家、領導,給新能源汽車的發展指明了方向。”

上海先鋒電聲器材有限公司吳銘亮讚歎道:“非常好的交流平臺,對國內新能源車市友有了全面認識。”

第一電動網薑永傑贊許道:“大會很國際化,乾貨十足。”

李爾公司薑慧表示:“Quite deep dive from technology side。”

除此以外,媒體也是我們大會的一大主角。

大會合作了共60家行業媒體如:第一電動網、中國汽車要聞、電車匯、蓋世汽車網、AI《汽車製造業》、新華信汽車、節能與新能源汽車網、高工鋰電、中國電動汽車時代網、新能源汽車網、電動汽車網、電動汽車資源網、輕量化線上、汽車材料網、汽車供應商網、汽車製造網、集邦能源、華人電池網、電池中國、車典網、電車之家、中國新能源網、電動汽車獵頭網、中華汽配網、環球汽車網、北極星電力網、赤浪綠色新能源網、全球節能環保網、前瞻網、企業網景、中商情報網、中國商業活動網、價值中國等媒體的支持。

作為會議主辦方,此次大會飽含著CDMC整個團隊對行業的熱情並全力付出。同時,我們也對行業未來的蓬勃發展寄予殷切的希望!最後,我們想誠懇地邀請各位,在參加大會的同時,不吝為我們提出寶貴的意見,以助我們能在未來新能源汽車峰會上為大家奉獻更多精彩。

我們也熱切地期盼著,新能源汽車峰會的每一次盛放,都能看見您的身影。

相約2015,老友再相會、新朋共參與!明年,我們將舉辦從規格到規模、從內容到形式、從資訊到社交更加高大上的新能源汽車峰會暨展覽會。

第六屆新能源汽車峰會暨展覽會2015大會聯繫:
邱小姐(Elva Qiu)
電話:021 63931899轉2041
手機:18930215786
QQ:1147789586
e-mail:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

設計模式系列之一:簡單工廠模式

1.1 定義

 簡單工廠模式屬於創建型模式,又稱為靜態工廠方法模式,在簡單工廠模式中,可以根據參數的不同,來返回不同類的實例,簡單工廠模式專門定義一個類來負責創建子類的實例,被創建的類通常有一個共同的父類

1.2 簡單工廠模式結構圖(簡版)

                             

 Factory:工廠類,簡單工廠模式的核心,它負責實現創建所有實例的內部邏輯。工廠類的創建產品類的方法可以被外界直接調用,創建所需的產品對象

 IProduct:抽象產品類,簡單工廠模式所創建的所有對象的父類,它負責描述所有實例所共有的公共接口

 Product:具體產品類,是簡單工廠模式的目標類

1.3 簡單工廠的實現一

 假設有一個電腦的代工生產商,它目前已經可以代工生產聯想電腦了,隨着業務的拓展,這個代工生產商還要生產惠普和mac電腦;

 這樣我們就需要用一個單獨的類來專門生產電腦,這就用到了簡單工廠模式,同時用到了繼承、封裝、多態等面向對象編程的思想,下面我們來實現簡單工廠模式

1.3.1 產品抽象類

public abstract class Computer {
    /**
     * 產品的抽象方法,由具體的產品類去實現
     */
    public abstract void start();

}

 

1.3.2  具體實現類

public class LenovoComputer extends Computer {

    @Override
    public void start() {
        System.out.println("lenovo computer run");
    }
}
public class MacComputer extends Computer {
    @Override
    public void start() {
        System.out.println("Mac computer run");
    }
}
public class HpComputer extends Computer {
    @Override
    public void start() {
        System.out.println("hp computer run");
    }
}

1.3.3  工廠類

public class ComputerFactory {
    public static Computer createComputer(String type) {
        Computer computer = null;
        switch (type) {
            case "lenovo":
                computer = new LenovoComputer();
                break;
            case "hp":
                computer = new HpComputer();
                break;
            case "Mac":
                computer = new MacComputer();
                break;
            default:
                break;
        }
        return computer;
    }
}

1.3.4  UML類圖

                          

1.4 優缺點

 優點:

 1. 工廠類含有必要的判斷邏輯,可以決定在什麼時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而僅僅“消費”產品;實現了對責任的分割,它提供了專門的工廠類用於創建對象

 2. 客戶端無須知道所創建的具體產品類的類名,只需要知道具體產品類所對應的參數即可,對於一些複雜的類名,通過簡單工廠模式可以減少使用者的記憶量。

 3. 通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。

缺點:

 1. 由於工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。

 2. 使用簡單工廠模式將會增加系統中類的個數,在一定程序上增加了系統的複雜度和理解難度。

 3. 系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,同樣破壞了“開閉原則”;在產品類型較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴展和維護

 4. 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構

1.5 適用場景

 1. 工廠類負責創建的對象比較少:由於創建的對象較少,不會造成工廠方法中的業務邏輯太過複雜。

 2. 客戶端只知道傳入工廠類的參數,對於如何創建對象不關心:客戶端既不需要關心創建細節,甚至連類名都不需要記住,只需要知道類型所對應的參數

1.6 模式應用

 1. JDK類庫中廣泛使用了簡單工廠模式,如工具類java.text.DateFormat,它用於格式化一個本地日期或者時間

public final static DateFormat getDateInstance(); 
public final static DateFormat getDateInstance(int style); 
public final static DateFormat getDateInstance(int style,Locale locale);

 2. 獲取不同加密算法的密鑰生成器

KeyGenerator keyGen=KeyGenerator.getInstance("DESede");

1.7 開閉原則

 對於上面兩種簡單工廠模式的實現方法,如果我們要添加新的 parser,那勢必要改動到 RuleConfigParserFactory 的代碼,那這是不是違反開閉原則呢?

 實際上,如果不是需要頻繁地添加新的 parser,只是偶爾修改一下 RuleConfigParserFactory 代碼,稍微不符合開閉原則,也是完全可以接受的

 儘管簡單工廠模式的代碼實現中,有多處 if 分支判斷邏輯,違背開閉原則,但權衡擴展性和可讀性,這樣的代碼實現在大多數情況下(比如,不需要頻繁地添加 parser,也沒有太多的 parser)是沒有問題的

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

03 . 二進制部署kubernetes1.18.4

簡介

目前生產部署kubernetes集群主要兩種方式

kubeadm

Kubeadm是一個K8s部署工具,提供kubeadm init和kubeadm join,用於快速部署Kubernetes集群。

二進制包

從github下載發行版的二進制包,手動部署每個組件,組成Kubernetes集群。

Kubeadm降低部署門檻,但屏蔽了很多細節,遇到問題很難排查。如果想更容易可控,推薦使用二進制包部署Kubernetes集群,雖然手動部署麻煩點,期間可以學習很多工作原理,也利於後期維護。

二進制部署K8s

List
CentOS7.3
cni-plugins-linux-amd64-v0.8.6.tgz
etcd-v3.4.9-linux-amd64.tar.gz
kube-flannel.yml
kubernetes-server-linux-amd64.tar.gz
角色 IP 組件
master 192.168.31.71 kube-apiserver,kube-controller-manager,kube-scheduler,etcd
Node1 192.168.31.74 kube-apiserver,kube-controller-manager,kube-scheduler
Node2 192.168.31.72 kubelet,kube-proxy,docker etcd

初始化環境

# 初始化
init_security() {
systemctl stop firewalld
systemctl disable firewalld &>/dev/null
setenforce 0
sed -i '/^SELINUX=/ s/enforcing/disabled/'  /etc/selinux/config
sed -i '/^GSSAPIAu/ s/yes/no/' /etc/ssh/sshd_config
sed -i '/^#UseDNS/ {s/^#//;s/yes/no/}' /etc/ssh/sshd_config
systemctl enable sshd crond &> /dev/null
rpm -e postfix --nodeps
echo -e "\033[32m [安全配置] ==> OK \033[0m"
}
init_security

init_yumsource() {
if [ ! -d /etc/yum.repos.d/backup ];then
    mkdir /etc/yum.repos.d/backup
fi
mv /etc/yum.repos.d/* /etc/yum.repos.d/backup 2>/dev/null
if ! ping -c2 www.baidu.com &>/dev/null    
then
    echo "您無法上外網,不能配置yum源"
    exit    
fi
    curl -o /etc/yum.repos.d/163.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo &>/dev/null
    curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo &>/dev/null
    yum clean all
    timedatectl set-timezone Asia/Shanghai
    echo "nameserver 114.114.114.114" > /etc/resolv.conf
    echo "nameserver 8.8.8.8" >> /etc/resolv.conf
    chattr +i /etc/resolv.conf
    yum -y install ntpdate
    ntpdate -b  ntp1.aliyun.com        # 對時很重要
    echo -e "\033[32m [YUM Source] ==> OK \033[0m"
}
init_yumsource

# 關掉swap分區
swapoff -a
# 如果想永久關掉swap分區,打開如下文件註釋掉swap哪一行即可.
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab #永久

# 配置主機名解析
tail -3 /etc/hosts
192.168.0.121 master
192.168.0.123 node1
192.168.0.124 node2

# 將橋接的IPv4流量傳遞到iptables的鏈
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system  # 生效


# 升級內核(非必須,只是性能更好)
wget https://cbs.centos.org/kojifiles/packages/kernel/4.9.220/37.el7/x86_64/kernel-4.9.220-37.el7.x86_64.rpm
  
rpm -ivh kernel-4.9.220-37.el7.x86_64.rpm
reboot

部署etcd集群

Etcd 是一個分佈式鍵值存儲系統,Kubernetes使用Etcd進行數據存儲,所以先準備一個Etcd數據庫,為解決Etcd單點故障,應採用集群方式部署,這裏使用3台組建集群,可容忍1台機器故障,當然,你也可以使用5台組建集群,可容忍2台機器故障。

節點名稱 IP
etcd-1 192.168.31.71
etcd-2 192.168.31.72
etcd-3 192.168.31.73

注:為了節省機器,這裏與K8s節點機器復用。也可以獨立於k8s集群之外部署,只要apiserver能連接到就行。

準備cfssl證書生成工具

cfssl是一個開源的證書管理工具,使用json文件生成證書,相比openssl更方便使用。

找任意一台服務器操作,這裏用Master節點。

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 cfssl-certinfo_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo
生成etcd證書
創建工作目錄
mkdir -p ~/TLS/{etcd,k8s}

cd TLS/etcd
自簽CA
cat > ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "www": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

cat > ca-csr.json << EOF
{
    "CN": "etcd CA",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing"
        }
    ]
}
EOF
生成證書
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

ls *pem
ca-key.pem  ca.pem
使用自簽CA簽發etcd https證書

創建證書申請文件

cat > server-csr.json << EOF
{
    "CN": "etcd",
    "hosts": [
    "192.168.0.121",
    "192.168.0.123",
    "192.168.0.124"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "BeiJing",
            "ST": "BeiJing"
        }
    ]
}
EOF

注:上述文件hosts字段中IP為所有etcd節點的集群內部通信IP,一個都不能少!為了方便後期擴容可以多寫幾個預留的IP。

生成證書
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server

ls server*pem
server-key.pem  server.pem
下載etcd二進制文件
wget https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz
創建工作目錄並解壓二進制包
mkdir /opt/etcd/{bin,cfg,ssl} -p
tar zxvf etcd-v3.4.9-linux-amd64.tar.gz
mv etcd-v3.4.9-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/

# 配置etcd
cat /opt/etcd/cfg/etcd.conf 
#[Member]
ETCD_NAME="etcd-1"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.0.121:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.0.121:2379"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.0.121:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.0.121:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.0.121:2380,etcd-2=https://192.168.0.123:2380,etcd-3=https://192.168.0.124:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

# ETCD_NAME:節點名稱,集群中唯一
# ETCD_DATA_DIR:數據目錄
# ETCD_LISTEN_PEER_URLS:集群通信監聽地址
# ETCD_LISTEN_CLIENT_URLS:客戶端訪問監聽地址
# ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址
# ETCD_ADVERTISE_CLIENT_URLS:客戶端通告地址
# ETCD_INITIAL_CLUSTER:集群節點地址
# ETCD_INITIAL_CLUSTER_TOKEN:集群Token
# ETCD_INITIAL_CLUSTER_STATE:加入集群的當前狀態,new是新集群,existing表示加入已有集群
systemd管理etcd
cat > /usr/lib/systemd/system/etcd.service << EOF
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
EnvironmentFile=/opt/etcd/cfg/etcd.conf
ExecStart=/opt/etcd/bin/etcd \
--cert-file=/opt/etcd/ssl/server.pem \
--key-file=/opt/etcd/ssl/server-key.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem \
--peer-key-file=/opt/etcd/ssl/server-key.pem \
--trusted-ca-file=/opt/etcd/ssl/ca.pem \
--peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \
--logger=zap
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
拷貝剛生成證書及生成的文件拷貝到節點2,節點3
cp ~/TLS/etcd/ca*pem ~/TLS/etcd/server*pem /opt/etcd/ssl/

scp -r /opt/etcd/ node1:/opt/
scp -r /opt/etcd/ node2:/opt/
scp /usr/lib/systemd/system/etcd.service node1:/usr/lib/systemd/system/
scp /usr/lib/systemd/system/etcd.service node2:/usr/lib/systemd/system/
修改節點2和節點3etcd.conf配置文件
node-1
cat /opt/etcd/cfg/etcd.conf 
#[Member]
ETCD_NAME="etcd-2"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.0.123:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.0.123:2379"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.0.123:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.0.123:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.0.121:2380,etcd-2=https://192.168.0.123:2380,etcd-3=https://192.168.0.124:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"


# node-2
#[Member]
ETCD_NAME="etcd-3"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.0.124:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.0.124:2379"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.0.124:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.0.124:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.0.121:2380,etcd-2=https://192.168.0.123:2380,etcd-3=https://192.168.0.124:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

# 啟動服務並設置開機自啟
systemctl daemon-reload
systemctl start etcd
systemctl enable etcd
驗證etcd集群狀態
 /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.0.121:2379,https://192.168.0.123:2379,https://192.168.0.124:2379" endpoint healthhttps://192.168.0.124:2379 is healthy: successfully committed proposal: took = 13.213712ms
https://192.168.0.121:2379 is healthy: successfully committed proposal: took = 12.907787ms
https://192.168.0.123:2379 is healthy: successfully committed proposal: took = 12.168703ms
        
# 如果輸出上面信息,就說明集群部署成功。如果有問題第一步先看日誌:/var/log/message 或 journalctl -u etcd

安裝docker

下載安裝docker
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y install docker-ce-19.03.9-3.el7
配置docker鏡像源
mkdir /etc/docker
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"]
}
EOF
啟動並設置開機自啟
systemctl daemon-reload
systemctl start docker
systemctl enable docker

部署Master Node

生成kube-apiserver證書

1. 自簽證書頒發機構(CA)

cat > ca-config.json << EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF
cat > ca-csr.json << EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing",
            "O": "k8s",
            "OU": "System"
        }
    ]
}
EOF

# 生成證書
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

ls *pem
ca-key.pem  ca.pem
使用自簽CA簽發kube-apiserver https 證書
cat /root/TLS/k8s/server-csr.json 
{
    "CN": "kubernetes",
    "hosts": [
      "10.0.0.1",
      "127.0.0.1",
      "192.168.0.121",
      "192.168.0.123",
      "192.168.0.124",
      "192.168.0.125",
      "192.168.0.100",
      "kubernetes",
      "kubernetes.default",
      "kubernetes.default.svc",
      "kubernetes.default.svc.cluster",
      "kubernetes.default.svc.cluster.local"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "BeiJing",
            "ST": "BeiJing",
            "O": "k8s",
            "OU": "System"
        }
    ]
}

# 上述文件hosts字段中IP為所有Master/LB/VIP IP,一個都不能少!為了方便後期擴容可以多寫幾個預留的IP。

# 生成證書
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes server-csr.json | cfssljson -bare server

ls server*pem
server-key.pem  server.pem
下載解壓二進制包
wget https://dl.k8s.io/v1.18.4/kubernetes-server-linux-amd64.tar.gz
mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs} 
tar zxvf kubernetes-server-linux-amd64.tar.gz
cd kubernetes/server/bin
cp kube-apiserver kube-scheduler kube-controller-manager /opt/kubernetes/bin
cp kubectl /usr/bin/
部署kube-apiserver
cat /opt/kubernetes/cfg/kube-apiserver.conf 
KUBE_APISERVER_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/opt/kubernetes/logs \
--etcd-servers=https://192.168.0.121:2379,https://192.168.0.123:2379,https://192.168.0.124:2379 \
--bind-address=192.168.0.121 \
--secure-port=6443 \
--advertise-address=192.168.0.121 \
--allow-privileged=true \
--service-cluster-ip-range=10.0.0.0/24 \
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
--authorization-mode=RBAC,Node \
--enable-bootstrap-token-auth=true \
--token-auth-file=/opt/kubernetes/cfg/token.csv \
--service-node-port-range=30000-32767 \
--kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \
--kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \
--tls-cert-file=/opt/kubernetes/ssl/server.pem  \
--tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \
--client-ca-file=/opt/kubernetes/ssl/ca.pem \
--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \
--etcd-cafile=/opt/etcd/ssl/ca.pem \
--etcd-certfile=/opt/etcd/ssl/server.pem \
--etcd-keyfile=/opt/etcd/ssl/server-key.pem \
--audit-log-maxage=30 \
--audit-log-maxbackup=3 \
--audit-log-maxsize=100 \
--audit-log-path=/opt/kubernetes/logs/k8s-audit.log"


# –logtostderr:啟用日誌
# —v:日誌等級
# –log-dir:日誌目錄
# –etcd-servers:etcd集群地址
# –bind-address:監聽地址
# –secure-port:https安全端口
# –advertise-address:集群通告地址
# –allow-privileged:啟用授權
# –service-cluster-ip-range:Service虛擬IP地址段
# –enable-admission-plugins:准入控制模塊
# –authorization-mode:認證授權,啟用RBAC授權和節點自管理
# –enable-bootstrap-token-auth:啟用TLS bootstrap機制
# –token-auth-file:bootstrap token文件
# –service-node-port-range:Service nodeport類型默認分配端口範圍
# –kubelet-client-xxx:apiserver訪問kubelet客戶端證書
# –tls-xxx-file:apiserver https證書
# –etcd-xxxfile:連接Etcd集群證書
# –audit-log-xxx:審計日誌
拷貝剛生成證書
cp ~/TLS/k8s/ca*pem ~/TLS/k8s/server*pem /opt/kubernetes/ssl/
啟用TLS Bootstrapping機制

TLS Bootstraping:Master apiserver啟用TLS認證后,Node節點kubelet和kube-proxy要與kube-apiserver進行通信,必須使用CA簽發的有效證書才可以,當Node節點很多時,這種客戶端證書頒發需要大量工作,同樣也會增加集群擴展複雜度。為了簡化流程,Kubernetes引入了TLS bootstraping機制來自動頒發客戶端證書,kubelet會以一個低權限用戶自動向apiserver申請證書,kubelet的證書由apiserver動態簽署。所以強烈建議在Node上使用這種方式,目前主要用於kubelet,kube-proxy還是由我們統一頒發一個證書

TLS bootstraping 工作流程

創建上述配置文件中token文件

cat > /opt/kubernetes/cfg/token.csv << EOF
c47ffb939f5ca36231d9e3121a252940,kubelet-bootstrap,10001,"system:node-bootstrapper"
EOF

# 格式:token,用戶名,UID,用戶組

token也可自行生成替換

head -c 16 /dev/urandom | od -An -t x | tr -d ' '
systemd管理apiserver
cat > /usr/lib/systemd/system/kube-apiserver.service << EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf
ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
啟動設置開機啟動
systemctl daemon-reload
systemctl start kube-apiserver
systemctl enable kube-apiserver
授權kubelet-bootstrap用戶允許請求證書
kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap

部署kube-controller-manager

創建配置文件
cat /opt/kubernetes/cfg/kube-controller-manager.conf 
KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/opt/kubernetes/logs \
--leader-elect=true \
--master=127.0.0.1:8080 \
--bind-address=127.0.0.1 \
--allocate-node-cidrs=true \
--cluster-cidr=10.244.0.0/16 \
--service-cluster-ip-range=10.0.0.0/24 \
--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \
--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \
--root-ca-file=/opt/kubernetes/ssl/ca.pem \
--service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \
--experimental-cluster-signing-duration=87600h0m0s"

# –master:通過本地非安全本地端口8080連接apiserver。
# –leader-elect:當該組件啟動多個時,自動選舉(HA)
# –cluster-signing-cert-file/–cluster-signing-key-file:自動為kubelet頒發證書的CA,與apiserver保持一致
systemd管理controller-manager
cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf
ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
啟動設置開機啟動
systemctl daemon-reload
systemctl start kube-controller-manager
systemctl enable kube-controller-manager

部署kube-scheduler

創建配置文件
cat /opt/kubernetes/cfg/kube-scheduler.conf 
KUBE_SCHEDULER_OPTS="--logtostderr=false --v=2 --log-dir=/opt/kubernetes/logs --leader-elect --master=127.0.0.1:8080 --bind-address=127.0.0.1"

# –master:通過本地非安全本地端口8080連接apiserver。
# –leader-elect:當該組件啟動多個時,自動選舉(HA)
systemd管理scheduler
cat > /usr/lib/systemd/system/kube-scheduler.service << EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf
ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
啟動並設置開機自啟動
systemctl daemon-reload
systemctl start kube-scheduler
systemctl enable kube-scheduler
查看集群狀態
# 所有組件都已經啟動成功,通過kubectl工具查看當前集群組件狀態:
kubectl get cs
NAME                 STATUS    MESSAGE             ERROR
scheduler            Healthy   ok                  
controller-manager   Healthy   ok                  
etcd-2               Healthy   {"health":"true"}   
etcd-1               Healthy   {"health":"true"}   
etcd-0               Healthy   {"health":"true"} 

部署worker node

下面還是在Master Node上操作,即同時作為Worker Node

創建工作目錄並拷貝二進制文件

在所有worker node創建工作目錄

mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs} 

從master節點拷貝

cd kubernetes/server/bin
cp kubelet kube-proxy /opt/kubernetes/bin   # 本地拷貝,註釋這裏操作還是master節點,

部署kubelet

創建配置文件
cat /opt/kubernetes/cfg/kubelet.conf 
KUBELET_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/opt/kubernetes/logs \
--hostname-override=master \
--network-plugin=cni \
--kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \
--bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \
--config=/opt/kubernetes/cfg/kubelet-config.yml \
--cert-dir=/opt/kubernetes/ssl \
--pod-infra-container-image=lizhenliang/pause-amd64:3.0"


# –hostname-override:显示名稱,集群中唯一
# –network-plugin:啟用CNI
# –kubeconfig:空路徑,會自動生成,後面用於連接apiserver
# –bootstrap-kubeconfig:首次啟動向apiserver申請證書
# –config:配置參數文件
# –cert-dir:kubelet證書生成目錄
# –pod-infra-container-image:管理Pod網絡容器的鏡像
配置參數文件
cat > /opt/kubernetes/cfg/kubelet-config.yml << EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
cgroupDriver: cgroupfs
clusterDNS:
- 10.0.0.2
clusterDomain: cluster.local 
failSwapOn: false
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /opt/kubernetes/ssl/ca.pem 
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
evictionHard:
  imagefs.available: 15%
  memory.available: 100Mi
  nodefs.available: 10%
  nodefs.inodesFree: 5%
maxOpenFiles: 1000000
maxPods: 110
EOF
生成bootstrap.kubeconfig文件
KUBE_APISERVER="https://192.168.0.121:6443" # apiserver IP:PORT

TOKEN="c47ffb939f5ca36231d9e3121a252940" # 與token.csv里保持一致

# 生成 kubelet bootstrap kubeconfig 配置文件
kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=bootstrap.kubeconfig
  
  
kubectl config set-credentials "kubelet-bootstrap" \
  --token=${TOKEN} \
  --kubeconfig=bootstrap.kubeconfig
  
  
kubectl config set-context default \
  --cluster=kubernetes \
  --user="kubelet-bootstrap" \
  --kubeconfig=bootstrap.kubeconfig
  
  
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

# 拷貝到配置文件路徑
cp bootstrap.kubeconfig /opt/kubernetes/cfg
systemd管理kubelet
cat > /usr/lib/systemd/system/kubelet.service << EOF
[Unit]
Description=Kubernetes Kubelet
After=docker.service
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf
ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
啟動並設置開機自啟
systemctl daemon-reload
systemctl start kubelet
systemctl enable kubelet
批准kubelet證書並加入集群
# 查看kubelet證書請求
kubectl get csr
NAME                                                   AGE    SIGNERNAME                                    REQUESTOR           CONDITION
node-csr-uCEGPOIiDdlLODKts8J658HrFq9CZ--K6M4G7bjhk8A   6m3s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pending

# 批准申請
kubectl certificate approve node-csr-uCEGPOIiDdlLODKts8J658HrFq9CZ--K6M4G7bjhk8A

# 查看節點
kubectl get node
NAME     STATUS   ROLES    AGE    VERSION
master   Ready    <none>   123m   v1.18.4

# 由於網絡插件還沒有部署,節點會沒有準備就緒 NotReady

部署kube-proxy

創建配置文件
cat > /opt/kubernetes/cfg/kube-proxy.conf << EOF
KUBE_PROXY_OPTS="--logtostderr=false \\
--v=2 \\
--log-dir=/opt/kubernetes/logs \\
--config=/opt/kubernetes/cfg/kube-proxy-config.yml"
EOF
創建參數文件
cat > /opt/kubernetes/cfg/kube-proxy-config.yml << EOF
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
metricsBindAddress: 0.0.0.0:10249
clientConnection:
  kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig
hostnameOverride: k8s-master
clusterCIDR: 10.0.0.0/24
EOF
生成kube-proxy.kubeconfig文件

生成kube-proxy證書

# 切換工作目錄
cd TLS/k8s

# 創建證書請求文件
cat > kube-proxy-csr.json << EOF
{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
EOF

# 生成證書
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy

ls kube-proxy*pem
kube-proxy-key.pem  kube-proxy.pem
生成kubeconfig文件
KUBE_APISERVER="https://192.168.0.121:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/opt/kubernetes/ssl/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-proxy.kubeconfig
  
kubectl config set-credentials kube-proxy \
  --client-certificate=./kube-proxy.pem \
  --client-key=./kube-proxy-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-proxy.kubeconfig
  
kubectl config set-context default \
  --cluster=kubernetes \
  --user=kube-proxy \
  --kubeconfig=kube-proxy.kubeconfig
  
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

# 拷貝配置文件到指定路徑
cp kube-proxy.kubeconfig /opt/kubernetes/cfg/
systemd管理kube-proxy
cat > /usr/lib/systemd/system/kube-proxy.service << EOF
[Unit]
Description=Kubernetes Proxy
After=network.target
[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf
ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
啟動並設置開機啟動
systemctl daemon-reload
systemctl start kube-proxy
systemctl enable kube-proxy

部署CNI網絡

準備二進制文件
wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz
  
# 解壓二進制文件並移動到默認工作目錄
mkdir /opt/cni/bin
tar zxvf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin

# 部署cni網絡
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
sed -i -r "s#quay.io/coreos/flannel:.*-amd64#lizhenliang/flannel:v0.12.0-amd64#g" kube-flannel.yml

# 默認鏡像地址無法訪問,修改為docker hub鏡像倉庫。
kubectl apply -f kube-flannel.yml

kubectl get pods -n kube-system
NAME                          READY   STATUS    RESTARTS   AGE
kube-flannel-ds-amd64-2pc95   1/1     Running   0          72s

kubectl get node
NAME         STATUS   ROLES    AGE   VERSION
master   Ready    <none>   41m   v1.18.4
# 部署好網絡插件,Node準備就緒
授權apiserver訪問kubelet
cat > apiserver-to-kubelet-rbac.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
      - pods/log
    verbs:
      - "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes
EOF

kubectl apply -f apiserver-to-kubelet-rbac.yaml
新增加Worker Node

拷貝已部署好的Node相關文件到新節點

scp -r /opt/kubernetes/ node1:/opt/
scp -r /usr/lib/systemd/system/{kubelet,kube-proxy}.service node1:/usr/lib/systemd/system

scp -r /opt/cni/ node1:/opt/
scp /opt/kubernetes/ssl/ca.pem node1:/opt/kubernetes/ssl
刪除kubelet證書和kubeconfig文件
rm -f /opt/kubernetes/cfg/kubelet.kubeconfig 
rm -f /opt/kubernetes/ssl/kubelet*

# 這幾個文件是證書申請審批后自動生成的,每個Node不同,必須刪除重新生成。
修改主機名並設置開機自啟動
vi /opt/kubernetes/cfg/kubelet.conf
--hostname-override=node1

vi /opt/kubernetes/cfg/kube-proxy-config.yml
hostnameOverride: node1

# 啟動並設置開機啟動
systemctl daemon-reload
systemctl start kubelet
systemctl enable kubelet
systemctl start kube-proxy
systemctl enable kube-proxy
再master上批准Node kubelet證書申請
kubectl get csr
NAME                                                   AGE   SIGNERNAME                                    REQUESTOR           CONDITION
node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro   89s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pending

kubectl certificate approve node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro

kubectl get node
NAME     STATUS   ROLES    AGE    VERSION
master   Ready    <none>   138m   v1.18.4
node1    Ready    <none>   120m   v1.18.4
node2    Ready    <none>   112m   v1.18.4

部署Dashboard

下載dashboard.yaml文件
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
修改yaml配置文件使其端口暴露外部訪問
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
  
# 默認Dashboard只能集群內部訪問,修改Service為NodePort類型,暴露到外部:

vi recommended.yaml
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30001  # 修改這裏
  type: NodePort   # 修改這裏
  selector:
    k8s-app: kubernetes-dashboard

kubectl apply -f recommended.yaml

kubectl get pods,svc -n kubernetes-dashboard
NAME                                             READY   STATUS    RESTARTS   AGE
pod/dashboard-metrics-scraper-694557449d-69x7g   1/1     Running   0          111m
pod/kubernetes-dashboard-9774cc786-kwgkt         1/1     Running   0          111m

NAME                                TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
service/dashboard-metrics-scraper   ClusterIP   10.0.0.3     <none>        8000/TCP        111m
service/kubernetes-dashboard        NodePort    10.0.0.122   <none>        443:30001/TCP   111m
創建service account並綁定默認cluster-admin管理員集群角色
kubectl create serviceaccount dashboard-admin -n kube-system
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')

# 接下來訪問https://node ip:30001
# 然後將上面過濾出來的token複製上面即可訪問dashboard

# 我們可以部署個Nginx測試下集群可用性
kubectl run --generator=run-pod/v1 nginx-test2 --image=daocloud.io/library/nginx --port=80 --replicas=1

kubectl get pods -o wide
kubectl get pods -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
nginx-test2   1/1     Running   0          89m   10.244.2.2   node2   <none>           <none>

# 我們去相應的節點訪問指定IP即可訪問
[root@node1 ~]# curl -I -s  10.244.2.2 |grep 200
HTTP/1.1 200 OK

部署CoreDNS

CoreDNS用於集群內部Service名稱解析

kubectl apply -f coredns.yaml 
serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns 
configmap/coredns created
deployment.apps/coredns created
DNS解析測試
kubectl run -it --rm dns-test --image=busybox:1.28.4 sh
If you don't see a command prompt, try pressing enter.
/ # nslookup kubernetes
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local

此篇文章借鑒於公眾號DevOps技術棧 ,作者阿良

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

jvm入門及理解(六)——垃圾回收與算法

一、jvm垃圾回收要做的事情

  • 哪些內存需要回收
  • 什麼時候回收
  • 怎麼回收

二、如何判斷對象已經死亡,或者說確定為垃圾

  1. 引用計數法:

    給對象中添加一個引用計數器,每當有一個地方引用它時,計數器的值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的對象就是不可能再被使用的。這也就是需要回收的對象,簡單地說,即一個對象如果沒有任何與之關聯的引用,即他們的引用計數都為 0,則說明對象不太可能再被用到,那麼這個對象就是可回收的對象。

    引用計數法是對象記錄自己被多少程序引用,引用計數為零的對象將被清除。

    計數器表示的是有多少程序引用了這個對象(被引用數)。計數器是無符號的整數。

  2. 根搜索算法:

    通過一系列成為GC roots的點作為起點,向下搜索,當一個對象到任何GC Roots時沒有引用鏈相連,則說明對象已經死亡。

    如果在GC roots和一個對象之間沒有可達路徑(引用鏈),則稱該對象是不可達的。要注意的是,不可達對象不等價於可回收對象,不可達對象變為可回收對象至少要經過兩次標記 過程。兩次標記后仍然是可回收對象,則將面臨回收。

    jvm會將以下的對象定義為GC Roots:

 

  • Java虛擬機棧中引用的對象:比如方法裏面定義這種局部變量 User user= new User();
  • 方法區中的靜態屬性引用的對象:比如 private static User user = new User();
  • 常量引用的對象:比如 private static final  User user = new User();
  • 本地方法棧(JNI)中引用的對象

三、垃圾回收算法

  1. 標記清除算法(Mark-Sweep)

    是最基礎的垃圾回收算法,分為兩個階段,標註和清除。標記階段標記出所有需要回收的對象,清除階段回收被標記的對象所佔用的空間。首先從根開始將可能被引用的對象用遞歸的方式進行標記,然後將沒有標記到的對象作為垃圾進行回收。

 

 

從圖中我們就可以發現,該算法最大的問題是內存碎片化嚴重,後續可能發生大對象不能找到可利用空間的問題。

 

 

  2. 複製算法(copying)

   為了解決 Mark-Sweep 算法內存碎片化的缺陷而被提出的算法。按內存容量將內存劃分為等大小 的兩塊。每次只使用其中一塊,當這一塊內存滿后將尚存活的對象複製到另一塊上去,把已使用 的內存清掉,如圖:

 

 

 

 

 

 這種算法雖然實現簡單,內存效率高,不易產生碎片,但是最大的問題是可用內存被壓縮到了原 本的一半。且存活對象增多的話,Copying 算法的效率會大大降低。

  

  3.標記整理算法(Mark-Compact)

   結合了以上兩個算法,為了避免缺陷而提出。標記階段和 Mark-Sweep 算法相同,標記后不是清 理對象,而是將存活對象移向內存的一端。然後清除端邊界外的對象。如圖:

 

 

 

四、分代收集算法

  介紹:分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據對象存活的不同生命周期將內存 劃分為不同的域,一般情況下將 GC 堆劃分為老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特點是每次垃圾回收時只有少量對象需要被回收,新生代的特點是每次垃圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的算法。

  1. 新生代與複製算法

    目前大部分 JVM 的 GC 對於新生代都採取 Copying 算法,因為新生代中每次垃圾回收都要 回收大部分對象,即要複製的操作比較少,但通常並不是按照 1:1 來劃分新生代。一般將新生代 劃分為一塊較大的 Eden 空間和兩個較小的 Surviror 空間(From Space, To Space),每次使用 Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象複製到另 一塊 Survivor 空間中。

  

 

 

 假設from space為s0,to space 為s1

算法過程:

  1. Eden+S0可分配新生對象;
  2. 對Eden+S0進行垃圾收集,存活對象複製到S1清理Eden+S0。一次新生代GC結束。
  3. Eden+S1可分配新生對象;
  4. 對Eden+S1進行垃圾收集,存活對象複製到S0清理Eden+S1。二次新生代GC結束。
  5. 循環1。

  2.老年代與標記整理算法

老年代因為每次只回收少量對象,因而採用 Mark-Compact 算法。

1. JAVA 虛擬機提到過的處於方法區的永生代(Permanet Generation),它用來存儲 class 類, 常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。

2. 對象的內存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目 前存放對象的那一塊),少數情況會直接分配到老生代。

3. 當新生代的 Eden Space 和 From Space 空間不足時就會發生一次 GC,進行 GC 后,Eden Space 和 From Space 區的存活對象會被挪到 To Space,然後將 Eden Space 和 From Space 進行清理。

4. 如果 To Space 無法足夠存儲某個對象,則將這個對象存儲到老生代。

5. 在進行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反覆循環。

6. 當對象在 Survivor 區躲過一次 GC 后,其年齡就會+1。默認情況下年齡到達 15 的對象會被 移到老生代中。

 

五、分區收集算法

分區算法則將整個堆空間劃分為連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的 好處是可以控制一次回收多少個小區間 , 根據目標停頓時間, 每次合理地回收若干個小區間(而不是 整個堆), 從而減少一次 GC 所產生的停頓。

 

六、java中的四種引用

  • 強引用

在 Java 中最常見的就是強引用,把一個對象賦給一個引用變量,這個引用變量就是一個強引 用。當一個對象被強引用變量引用時,它處於可達狀態,它是不可能被垃圾回收機制回收的,即 使該對象以後永遠都不會被用到 JVM 也不會回收。因此強引用是造成 Java 內存泄漏的主要原因之一,直接new的對象就是強引用的。

  • 軟引用

軟引用需要用 SoftReference 類來實現,對於只有軟引用的對象來說,當系統內存足夠時它 不會被回收,當系統內存空間不足時它會被回收。軟引用通常用在對內存敏感的程序中。

  • 弱引用

弱引用需要用 WeakReference 類來實現,它比軟引用的生存期更短,對於只有弱引用的對象 來說,只要垃圾回收機制一運行,不管 JVM 的內存空間是否足夠,總會回收該對象佔用的內存。

  • 虛引用

虛引用需要 PhantomReference 類來實現,它不能單獨使用,必須和引用隊列聯合使用。虛 引用的主要作用是跟蹤對象被垃圾回收的狀態。

 

七、GC 垃圾收集器

Java 堆內存被劃分為新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收算法; 年老代主要使用標記-整理垃圾回收算法,因此 java 虛擬中針對新生代和年老代分別提供了多種不 同的垃圾收集器,JDK1.6 中 Sun HotSpot 虛擬機的垃圾收集器如下:

 

新生代:

  1.  Serial 垃圾收集器(單線程、複製算法)

Serial(英文連續)是最基本垃圾收集器,使用複製算法,曾經是JDK1.3.1 之前新生代唯一的垃圾 收集器。Serial 是一個單線程的收集器,它不但只會使用一個 CPU 或一條線程去完成垃圾收集工 作,並且在進行垃圾收集的同時,必須暫停其他所有的工作線程,直到垃圾收集結束。 Serial 垃圾收集器雖然在收集垃圾過程中需要暫停所有其他的工作線程,但是它簡單高效,對於限 定單個 CPU 環境來說,沒有線程交互的開銷,可以獲得最高的單線程垃圾收集效率,因此 Serial 垃圾收集器依然是 java 虛擬機運行在 Client 模式下默認的新生代垃圾收集器。

   2.ParNew 垃圾收集器(Serial+多線程)

ParNew 垃圾收集器其實是 Serial 收集器的多線程版本,也使用複製算法,除了使用多線程進行垃 圾收集之外,其餘的行為和 Serial 收集器完全一樣,ParNew 垃圾收集器在垃圾收集過程中同樣也 要暫停所有其他的工作線程。 ParNew 收集器默認開啟和 CPU 數目相同的線程數,可以通過-XX:ParallelGCThreads 參數來限 制垃圾收集器的線程數。ParNew雖然是除了多線程外和Serial 收集器幾乎完全一樣,但是ParNew垃圾收集器是很多 java 虛擬機運行在 Server 模式下新生代的默認垃圾收集器。

  3.Parallel Scavenge 收集器(多線程複製算法、高效)

Parallel Scavenge 收集器也是一個新生代垃圾收集器,同樣使用複製算法,也是一個多線程的垃 圾收集器,它重點關注的是程序達到一個可控制的吞吐量(Thoughput,CPU 用於運行用戶代碼 的時間/CPU 總消耗時間,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)), 高吞吐量可以最高效率地利用 CPU 時間,儘快地完成程序的運算任務,主要適用於在後台運算而 不需要太多交互的任務。自適應調節策略也是 ParallelScavenge 收集器與 ParNew 收集器的一個 重要區別。

 

老年代:

  4.Serial Old 收集器(單線程標記整理算法 )

Serial Old 是 Serial 垃圾收集器年老代版本,它同樣是個單線程的收集器,使用標記-整理算法, 這個收集器也主要是運行在 Client 默認的 java 虛擬機默認的年老代垃圾收集器。 在 Server 模式下,主要有兩個用途:

    1. 在 JDK1.5 之前版本中與新生代的 Parallel Scavenge 收集器搭配使用。

    2. 作為年老代中使用 CMS 收集器的後備垃圾收集方案。

新生代 Parallel Scavenge 收集器與 ParNew 收集器工作原理類似,都是多線程的收集器,都使 用的是複製算法,在垃圾收集過程中都需要暫停所有的工作線程。

  

  5.Parallel Old 收集器(多線程標記整理算法)

Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多線程的標記-整理算法,在 JDK1.6 才開始提供。 在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保證新生代的吞吐量優先,無法保證整體的吞吐量,Parallel Old 正是為了在年老代同樣提供吞 吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,可以優先考慮新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。

 

  6.Parallel Old 收集器(多線程標記整理算法)

Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多線程的標記-整理算法,在 JDK1.6 才開始提供。 在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保證新生代的吞吐量優先,無法保證整體的吞吐量,Parallel Old 正是為了在年老代同樣提供吞 吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,可以優先考慮新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。

  

  7.CMS 收集器(多線程標記清除算法)

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾 回收停頓時間,和其他年老代使用標記-整理算法不同,它使用多線程的標記-清除算法。 最短的垃圾收集停頓時間可以為交互比較高的程序提高用戶體驗。

 

 

  8.G1 收集器

Garbage first 垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與 CMS 收集器,G1 收 集器兩個最突出的改進是:

    1. 基於標記-整理算法,不產生內存碎片。

    2. 可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。 G1 收集器避免全區域垃圾收集,它把堆內存劃分為大小固定的幾個獨立區域,並且跟蹤這些區域 的垃圾收集進度,同時在後台維護一個優先級列表,每次根據所允許的收集時間,優先回收垃圾 最多的區域。區域劃分和優先級區域回收機制,確保 G1 收集器可以在有限時間獲得最高的垃圾收 集效率。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

【Spring】@Transactional 閑聊

菜瓜:上次的AOP理論知識看完收穫挺多的,雖然有一個自定義註解的demo,但還是覺得差點東西

水稻:我也覺得沒有跟一遍源碼還是差點意思,這次結合@Transactional註解深入源碼看一下

菜瓜:事務註解,這個平時用的挺多的

水稻:是嗎?來看看你的基礎咋樣

  1. 要保證一個方法中多個數據庫操作的原子性,要共用一個數據庫連接,但是coding時我們不用显示傳遞連接對象,這是咋弄的?
  2. 如果一個方法裏面只有查詢操作,是否不用開啟事務?
  3. 如何解決非事務方法調用本地事務方法失效的?
  4. 註解常用的傳播屬性,你知道他們的區別嗎

菜瓜:雖然沒看過源碼,我大膽猜測一下

  1. 隱式傳遞連接對象可以將其封裝到線程中,一般一次請求操作都是在一個線程中完成。使用ThreadLocal將連接和線程綁定
  2. 查詢操作也得看業務場景,如果多次查詢相同的數據要避免不可重複讀問題,可開啟只讀事務 (readOnly = true)
  3. 結合AOP的知識,這裏其實要解決調用事務方法的對象不是代理對象的問題。用代理對象調本地事務方法即可(注入自己)
    • /**
       * @author QuCheng on 2020/6/24.
       */
      @Service
      public class ItemServiceImpl implements ItemService {
      
          @Resource
          private IcbcItemMapper itemMapper;
      
          @Resource
          private ItemService itemService;
      
          @Override
          public void changeNameById(Long itemId) {
              // changeItemById(itemId);
              itemService.changeItemById(itemId);
          }
      
          @Transactional(rollbackFor = RuntimeException.class)
          @Override
          public void changeItemById(Long itemId) {
              itemMapper.updateNameById(itemId, "name4");
              int a = 10 / 0;
              itemMapper.updatePriceById(itemId, 100L);
          }
      }
  4. 傳播屬性這個沒了解過啊,數據庫事務裏面么得這個概念

水稻:可以啊,平時的代碼沒白寫

菜瓜:coding這種事情,easy啦!

水稻:這就飄了?來看這個問題

  • 如果我想在A事務方法中調用B事務方法,B方法如果回滾了,不能影響A事務繼續執行,但是A事務如果執行出問題了,B也要回滾,怎麼弄?

菜瓜:。。。這不就是大事務嵌套小事務嘛。。。我不會

水稻:不扯了,來看源碼吧,這個問題等解釋了傳播屬性你就知道了

  • 上回我們說到,@Transactional是AOP的典型應用,bean被實例化之後要創建代理(參考自定義註解),就少不了切面類Advisor對象。那麼它是誰,它在哪,它在干什麼?
  • 回到夢開始的地方,事務功能開啟的註解@EnableTransactionManagement
    • 沒錯,它肯定會有一個Import註解引入TransactionManagementConfigurationSelector類,它又引入了切面類
    • public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
       
         @Override
         protected String[] selectImports(AdviceMode adviceMode) {
            switch (adviceMode) {
               case PROXY:
                  return new String[] {AutoProxyRegistrar.class.getName(),
                        // 看這裏
                        ProxyTransactionManagementConfiguration.class.getName()};
               case ASPECTJ:
                  return new String[] {determineTransactionAspectClass()};
               default:
                  return null;
            }
         }
      。。。
      
      }
      
      
      @Configuration
      public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
      
         @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
         @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
         public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
            BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
            advisor.setTransactionAttributeSource(transactionAttributeSource());
            advisor.setAdvice(transactionInterceptor());
            if (this.enableTx != null) {
               advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
            }
            return advisor;
         }
      
         @Bean
         @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
         public TransactionAttributeSource transactionAttributeSource() {
            return new AnnotationTransactionAttributeSource();
         }
      
         @Bean
         @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
         public TransactionInterceptor transactionInterceptor() {
      // 增強 TransactionInterceptor interceptor
      = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } } 
    • 切面類對象設置了事務的掃描器,也set了增強類TransactionInterceptor
    • public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
      。。。
      @Override
          @Nullable
          public Object invoke(MethodInvocation invocation) throws Throwable {
              // Work out the target class: may be {@code null}.
              // The TransactionAttributeSource should be passed the target class
              // as well as the method, which may be from an interface.
              Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
      
              // Adapt to TransactionAspectSupport's invokeWithinTransaction...
              return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
          }
      }
      
      public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
        
      。。。  
      @Nullable
      protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {
         。。。
      // ①創建事務,數據庫連接處理也在這 TransactionInfo txInfo
      = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try {
      // 調用目標方法 retVal
      = invocation.proceedWithInvocation(); } catch (Throwable ex) {
      // 異常後事務處理 completeTransactionAfterThrowing(txInfo, ex);
      throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } 。。。 } 

菜瓜:懂,接下來的代碼邏輯就是在增強類TransactionInterceptor的invoke方法里

水稻:對

  • 先看數據庫連接的處理 – 驗證ThreadLocal
  • protected void doBegin(Object transaction, TransactionDefinition definition) {
       。。。
    // 如果連接是新的,就進行綁定
    if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder()); } 。。。 } class TransactionSynchronizationManager public static void bindResource(Object key, Object value) throws IllegalStateException { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Assert.notNull(value, "Value must not be null"); Map<Object, Object> map = resources.get(); // set ThreadLocal Map if none found if (map == null) { map = new HashMap<>(); resources.set(map); } Object oldValue = map.put(actualKey, value); // Transparently suppress a ResourceHolder that was marked as void... if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) { oldValue = null; } if (oldValue != null) { throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } if (logger.isTraceEnabled()) { logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]"); } }
  • 回過頭來看AB方法調用的回滾問題,直接給出答案(突然發現這個問題要講清楚篇幅會很大,就。。挺突然的。。挺突然der)
    • 在B方法上設置傳播屬性為NESTED即可,然後在A中catch住B的異常
    • 你肯定會問我不加NESTED去catch不行嗎?不行,非NESTED的方法拋出的異常是無法回滾的。
    • 不信你看
    • @Transactional(rollbackFor = RuntimeException.class)
          @Override
          public void changeNameById(Long itemId) {
              itemMapper.updateNameById(itemId, "A");
              try {
                  itemService.changeItemById(itemId);
      //            itemService.changeItemByIdNested(itemId);
              } catch (Exception e) {
                  System.out.println("我想繼續執行,不影響修改A");
              }
              itemMapper.updatePriceById(itemId, 1L);
          }
      
          @Transactional(rollbackFor = RuntimeException.class)
          @Override
          public void changeItemById(Long itemId) {
              itemMapper.updateNameById(itemId, "B+REQUIRED");
              itemMapper.updatePriceById(itemId, 10L);
              int a = 10 / 0;
          }
      
          @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.NESTED)
          @Override
          public void changeItemByIdNested(Long itemId) {
              itemMapper.updateNameById(itemId, "B+NESTED");
              itemMapper.updatePriceById(itemId, 100L);
              int a = 10 / 0;
          }
      
      
      -- 測試結果
      //①  itemService.changeItemById(itemId);  數據庫所有數據都不會改變
      我想繼續執行,不影響修改A
      org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
      
      // ② itemService.changeItemByIdNested(itemId); 第一個方法的修改會生效
      我想繼續執行,不影響修改A

菜瓜:這就是傳播屬性NESTED?默認的是REQUIRED,還有一個常用的REQUIRES_NEW呢?

水稻:搞清楚這個其實從數據庫連接入手其實就很清楚

  • REQUIRED修飾的方法和A使用同一個連接,A和B是掛一起的,誰回滾都會影響對方,且B方法的異常會被事務管理器標記為必須回滾
  • NESTED修飾的方法和A使用同一個連接,但是用到了數據庫的savePoint特性,它可以回滾到指定的點,如果是有回滾點的操作,拋出的異常可以被處理
  • REQUIRES_NEW修飾的方法和A使用的就不是一個連接了,回不回滾都不會影響對方,當然,要捕捉異常

菜瓜:傳播屬性了解。回滾的問題還得再看看,篇幅很大是很複雜嗎?

水稻:其實不複雜,就是要跟蹤源碼斷點調試。。。截圖搞來搞去,篇幅就很長,你自己去調的話其實很快

菜瓜:那我下去康康

 

總結

  • 這裏提到Transactional註解其實是為了鞏固AOP的,當然提到了一些注意點。譬如本地調用,譬如ThreadLocal的應用,還譬如傳播屬性
  • 傳播屬性其實用的少,但是聊起來就比較多了,可以聊事務的隔離級別,聊ACID的實現,聊MySQL的鎖

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

中國政策放開電動車准入要求 小米或入局電動車

11月26日,國家發改委網站掛出《新建純電動乘用車生企業投資項目和生產準入管理的暫行規定(徵求意見稿)》(以下簡稱《規定》),計劃為純電動車生準入放開口子。   《規定》要求的三條基礎能力中,最核心的是:(申報企業)有3年以上純電動乘用車的研發基礎,具有專業研發團隊和整車正向研發能力,掌握整車控制系統、動力電池系統、整車整合和整車輕量化方面的核心技術以及相應的試驗驗證能力,擁有純電動乘用車自主智慧財產權和已授權的相關發明專利。另外還要求新建企業只能生產純電動轎車和純電動其他乘用車(包括增程式電動乘用車),插電式混合動力不在準入範圍內。   面對新一批電動車開放名單,包括小米、樂視與萬向等企業集團都展現出高度興趣,爭取成為入圍名單。其中小米的動向最受矚目。小米創辦人之一黎萬強早在10月底就悄悄飛往矽谷展開暖身動作,小米董事長雷軍更在拜會特斯拉後就對電動車念念不忘。事實上,小米電動車的諜照早就在網路上曝光,只是細節外界所知不多。不過,這次開放的限制很嚴,小米能否如願以償擠進電動車窄門,備受關注。   (照片來源: )

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案