標籤: 網頁設計

  • Zookeeper分佈式過程協同技術 – 群首選舉

    Zookeeper分佈式過程協同技術 – 群首選舉

    Zookeeper分佈式過程協同技術 – 群首選舉

    群首概念

    群首為集群中服務器選擇出來的一個服務器,並被集群認可。設置群首目的在與對客戶端所發起的狀態變更請求進行排序,包括:create、setData、delete操作。群首將每一個請求轉換為一個事務並將事務發送給追隨者,確保集群按照群首確定的順序接受並處理這些事務。

    Zookeeper事務

    Zookeeper服務器會在本地處理只讀請求(例如:exists、getData、getChildren)。如果一台服務器接收到客戶端的getData請求,服務器讀取該狀態信息,並將這些信息返回客戶端。由於服務器在本地處理讀請求,所以在處理以只讀請求為主要負載時,性能會比較高。

    那些會改變狀態的客戶端請求(create、delete、setData)將會被轉發給群首,由群首執行相應的請求,完成狀態的更新,這就是Zookeeper的事務。

    選舉過程

    每個服務器啟動後進入LOOKING狀態,服務器之間進行通信來選舉一個群首,通過信息交換對群首選舉達成共識。在本次選舉中勝出的服務器將進入LEADING狀態,而集群中其他服務器將進入FOLLOWING狀態。

    群首選舉消息(leader election notifications)或簡單的稱為通知。當一個服務器進入LOOKING狀態就會向集群中的每一個服務器發送一個通知消息。消息中包含該服務器的投票信息。

    投票信息包含服務器標識符(sid)和最近執行的事務的zxid信息,例如投票信息(1,5)代表機器ID為1,最近執行的事務zxid為5。

    zxid為一個long型(64位)整數,分為2部分:時間戳部分和計數器部分,每個部分為32位。

     當一個服務器收到一個投票信息,該服務器將會根據以下規則修改自己的投票信息:

    1. 將接受的voteId和voteZxid作為一個標識符,並獲取接收方當前的投票中的zxid,用myZxid和mySid表示接收方服務器自己的值。
    2. 如果(voteZxid > myZxid)或者(voteZxid = myZxid 且 voteId > mySid),保留當前的投票信息。
    3. 否則,修改自己的投票信息,將voteZxid賦值給myZxid,將voteId賦值給mySid。

    當一個服務器收到的仲裁數量的服務器發來的投票信息都一樣時,就表示群首選舉成功,如果被選舉的群首為某個服務器自己,該服務器將會開始行使群首角色,否則就會成為一個追隨者並嘗試連接被選舉的群首服務器。一旦連接成功,追隨者和群首之間將會進行狀態同步,在同步完成后,追隨者才可以處理新的請求。 

     

     

    通過例子來演示選舉過程,三台服務器在分別發送出不同的選舉投票信息,其投票值包含服務器的標識符和最新的zxid。每個服務器都會收到另外兩個服務器發送的投票信息,在第一輪之後,服務器S2和S3將會改變其投票信息為(1,6),之後服務器S2和S3在改變投票信息之後會發送新的通知消息,S1服務器在接收到仲裁數量的通知消息擁有一樣的投票信息,最後S1被選舉出為集群的群首。

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

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

    ※推薦評價好的iphone維修中心

  • Python 簡明教程 — 20,Python 類中的屬性與方法

    Python 簡明教程 — 20,Python 類中的屬性與方法

    微信公眾號:碼農充電站pro
    個人主頁:https://codeshellme.github.io

    與客戶保持良好的關係可以使生產率加倍。
    —— Larry Bernstain

    目錄

    類中的變量稱為屬性,類中的函數稱為方法

    類中的屬性分為:

    • 實例屬性:對象所有,互不干擾
    • 類屬性:類所有,所有對象共享

    類中的方法分為:

    • 實例方法:定義中有self 參數
    • 類方法:定義中有cls 參數,使用@classmethod 裝飾器
    • 靜態方法:定義中即沒有self 參數,也沒有cls 參數,使用@staticmethod 裝飾器

    1,實例屬性與類屬性

    類的對象,就是類的一個實例。類的實例屬性被對象所有,包含在每個對象之中,不同的對象之間,互不干擾。類的類屬性被類所有,被包含在類中,是所有的類對象共享。

    一般情況下,實例屬性會在__init__ 方法中聲明並初始化,並且使用self 來綁定。而類屬性是在類作用域中被聲明,並且不使用self 來綁定。

    例如下面代碼中,country 為類屬性,__name 為實例屬性:

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    

    訪問實例屬性時使用對象.屬性名的格式,實例屬性屬於對象各自的,互不影響:

    >>> p1 = People('小明')
    >>> p2 = People('小美')
    >>> 
    >>> p1.get_name()
    '小明'
    >>> p2.get_name()
    '小美'
    

    類屬性被所有對象共有,一旦被改變,所有對象獲取到的值都會被改變。訪問類屬性時使用類名.屬性名的格式,也可以使用對象.屬性名的格式來訪問:

    >>> People.country              # 用`類名.屬性名`的格式訪問
    'CHINA'
    >>> p1.country                  # 用`對象.屬性名`的格式訪問
    'china'
    >>> p2.country
    'china'
    >>> 
    >>> People.country = 'CHINA'    # 類屬性的值被改變
    >>> p1.country                  # 每個對象獲取到的值也會被改變
    'CHINA'
    >>> p2.country
    'CHINA'
    

    注意,在使用對象.屬性名的格式訪問對象時,不要以這種格式對類屬性進行賦值,否則結果可能不會像你想象的一樣:

    >>> p1 = People('小明')
    >>> p2 = People('小美')
    >>> People.country
    'china'
    >>> p1.country
    'china'
    >>> p2.country
    'china'
    >>> p1.country = '中國'   # 使用`對象.屬性名`的格式對類對象進行賦值
    >>> p1.country           # 只有 p1.country 被改變
    '中國'
    >>> p2.country           # p2.country 沒有被改變
    'china'
    >>> People.country       # People.country 也沒有被改變
    'china'
    

    從上面代碼中可以看到,在Python 中以對象.屬性名格式對類屬性進行賦值時,只有p1.country 的值被改變了,p2.countryPeople.country 的值都沒有被改變。

    實際上,這種情況下,類屬性的值並沒有被改變,而是對象p1 中多了一個country 實例屬性,此後,p1.country 訪問的是p1 的實例屬性,p1.country對屬性country的訪問屏蔽了類中的country屬性,而p2.countryPeople.country 訪問的依然是原來的類屬性

    所以,類名.屬性名對象.屬性名的格式都可以訪問類屬性的值,但盡量避免使用對象.屬性名的格式對類屬性的值進行賦值,否則可能會發生混亂。

    建議:

    不管是訪問還是改變類屬性的值,都只用類名.屬性名的格式

    2,實例方法,類方法,靜態方法

    Python 類中有三種方法:

    • 實例方法
    • 類方法
    • 靜態方法

    實例方法屬於對象,方法中都有一個self 參數(代表對象本身)。實例方法只能由對象調用,不能通過類名訪問。實例方法中可以訪問實例屬性和類屬性。

    類方法屬於類,方法中都有一個cls 參數(代表類本身)。類方法即可以通過類名訪問,也可以通過對象訪問,類方法中只能訪問類屬性,不能訪問實例屬性。

    注意:

    Python 解釋器在構造類與對象時,是先於對象產生的。

    因此,類屬性與類方法是先於實例屬性與實例方法 產生的。

    所以當類方法產生時,還沒有實例屬性,因此,類方法中不能訪問實例屬性。

    靜態方法中,沒有self 參數,也沒有cls 參數。因此,在靜態方法中,即不能訪問類屬性,也不能訪問實例屬性。靜態方法可以使用類名訪問,也可以使用對象訪問。

    在Python 中,定義類方法需要用到裝飾器@classmethod,定義靜態方法需要用到裝飾器@staticmethod

    實例方法演示

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    
        # 實例方法中有self 參數
        def instance_method_test(self):
            # 在實例方法中訪問了實例屬性和類屬性
            print('name:%s country:%s' % (self.__name, People.country))
    

    使用:

    >>> p = People('小明')
    >>> p.instance_method_test()
    name:小明 country:china
    

    在實例方法中訪問了實例屬性__name和類屬性country,均可以被訪問。

    類方法演示

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    
        # 類方法中都有cls 參數
        @classmethod
        def class_method_test1(cls):
            print('在類方法中訪問類屬性country:%s' % cls.country)
    
        @classmethod
        def class_method_test2(cls):
            print('在類方法中訪問實例屬性__name:%s' % self.__name)
    

    使用:

    >>> p = People('小明')
    >>> p.class_method_test1()         # 在類方法中訪問類屬性,可以
    在類方法中訪問類屬性country:china
    >>>
    >>> p.class_method_test2()         # 在類方法中訪問實例屬性,出現異常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/wp/to_beijing/People.py", line 18, in class_method_test2
        print('在類方法中訪問實例屬性__name:%s' % self.__name)
    NameError: name 'self' is not defined
    

    從上面代碼中可以看到,在類方法中可以訪問類屬性,但在類方法中訪問實例屬性,會出現異常。

    靜態方法演示

    #! /usr/bin/env python3
    
    class People:
    
        country = 'china'
    
        def __init__(self, name):
            self.__name = name
    
        # 靜態方法中即沒有self 參數也不沒有cls 參數
        @staticmethod
        def static_method_test1():
            print('在靜態方法中訪問類屬性country:%s' % cls.country)
    
        @staticmethod
        def static_method_test2():
            print('在靜態方法中訪問實例屬性__name:%s' % self.__name)
    

    使用:

    >>> p = People('小明')
    >>> p.static_method_test1()      # 在靜態方法中訪問類屬性,出現異常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/wp/to_beijing/People.py", line 14, in static_method_test1
        print('在靜態方法中訪問類屬性country:%s' % cls.country)
    NameError: name 'cls' is not defined
    >>>
    >>> p.static_method_test2()     # 在靜態方法中訪問實例屬性,出現異常
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/wp/to_beijing/People.py", line 18, in static_method_test2
        print('在靜態方法中訪問實例屬性__name:%s' % self.__name)
    NameError: name 'self' is not defined
    

    從上面代碼中可以看到,在靜態方法中無論訪問實例方法還是類方法,都會出現異常。

    3,專有方法

    我們之前講到過的魔法方法,即以雙下劃線__開頭且結尾的方法__xxx__,就是專有方法。這些方法都被Python 賦予了特殊的含義,用戶可以根據需要,來實現這些方法。

    下面我們介紹一些 Python 中常見的專有方法。

    __len__ 方法

    實現該方法的類的對象,可以用len() 函數計算其長度。

    __str__ 方法

    實現該方法的類的對象,可以轉化為字符串。

    __call__ 方法

    實現該方法的類的對象,可以像函數一樣調用。

    __iter__ 方法

    實現該方法的類的對象,是可迭代的。

    __setitem__ 方法

    實現該方法的類的對象,可以用索引的方式進行賦值。

    __getitem__ 方法

    實現該方法的類的對象,可以用索引的方式進行訪問。

    __contains__ 方法

    實現該方法的類的對象,可以進行in 運算。

    __add__ 方法

    實現該方法的類的對象,可以進行加+運算。

    __sub__ 方法

    實現該方法的類的對象,可以進行減-運算。

    __mul__ 方法

    實現該方法的類的對象,可以進行乘*運算。

    __div__ 方法

    實現該方法的類的對象,可以進行除/運算。

    __pow__ 方法

    實現該方法的類的對象,可以進行乘方運算。

    __mod__ 方法

    實現該方法的類的對象,可以進行取模運算。

    __eq__ 方法

    實現該方法的類的對象,可以進行相等==比較。

    __ne__ 方法

    實現該方法的類的對象,可以進行不等於!=比較。

    __gt__ 方法

    實現該方法的類的對象,可以進行大於>比較。

    __ge__ 方法

    實現該方法的類的對象,可以進行大於等於>=比較。

    __lt__ 方法

    實現該方法的類的對象,可以進行小於<比較。

    __le__ 方法

    實現該方法的類的對象,可以進行小於等於<=比較。

    (完。)

    推薦閱讀:

    Python 簡明教程 — 15,Python 函數

    Python 簡明教程 — 16,Python 高階函數

    Python 簡明教程 — 17,Python 模塊與包

    Python 簡明教程 — 18,Python 面向對象

    Python 簡明教程 — 19,Python 類與對象

    歡迎關注作者公眾號,獲取更多技術乾貨。

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 關於技術文章“標題黨”一事我想說兩句

    關於技術文章“標題黨”一事我想說兩句

    閱讀本文大概需要 1.8 分鐘。

    前天發表的一篇文章,標題是:“面試官:你剛說你喜歡研究新技術,那麼請說說你對 Blazor 的了解”。確實,這篇文章有標題黨的味道,如果因此給部分童鞋帶來不適,我在這先真誠地給大家道個歉!

    這篇文章同步發表到博客園后,評論熱鬧了,其中“懟”文章是標題黨的不在少數。我先把與標題黨相關的評論截圖貼出來大家看看。

    上面只截了與“標題黨”相關的部分,一些討論技術的沒有包含在截圖裡。截止目前,評論大概有近 20 條,感興趣的可以去博客園看看那些有想法的優質評論。害,平時寫的技術文章很少見有這麼熱鬧的評論。

    評論中的 1 樓,@memmon 表達了博客園標題黨現象的看法。由於支持的人比較多,我再次引用一下:

    作為一個每天都會瀏覽博客園的用戶,不知道是不是我心態變了,現在看博客園 cnblog 幾乎看不下去了,一進去幾乎就不想看,全是標題黨,我搞不清楚這些人怎麼想的,在專業領域需要標題黨嗎,好的不學,全是網紅那一套,這些流量有什麼意義,標題幾乎是對話式的,百度貼吧的既視感,本來應該是知乎寫文章的態度,進去全是百度貼吧式的文章標題,我知道寫文章很悶,苦中作樂,自我調侃都是方式,不過觀感真的不太好。

    說的很中肯,語氣委婉客氣,並沒有多少“懟”的意思。當然他說的不是特別針對我這一篇文章,而是表達對博客園這種現象的“看不下去”。我理解他的心情,我也反感標題黨。

    我看到評論后回復了 @memmon:

    謝謝點評,接受一切批評指導,並會努力提升文章質量。本文確實有標題黨味道,但行文並沒有離題,確實是從群里大家最近聊面試的話題中產生寫本文的 Idea,目的是幫助那些正在面試的人了解一下 Blazor。也有想過把文章標題改成:“Blazor 介紹”,但又覺得有點太教科書了,技術文章本就枯燥,偶爾苦中作樂調劑一下我覺得挺好。這不,這麼一調劑,大家討論的熱情就上來了。

    我接着說兩句。

    標題黨在前兩三年很盛行,人人痛恨。但近一兩年見得越來越少了(只要你的興趣不和長輩們一致應該是很少看到標題黨了),說明自媒體人都意識到“標題黨”的危害。真正的“標題黨”文章雖然能在短時間內獲得一些點擊量,但稍微長遠一點來看,肯定引起讀者的反感,反而造成一大波真正的內容閱讀者取消關注。我,能不明白這個道理嗎。

    我也幾乎每天都會看看博客園,博客園首頁的文章我並沒有發現什麼標題黨。惟一讓我印象深刻的是下面這類的所謂的“標題黨”文章:

    • 標題:震驚!Windows Service 服務和定時任務框架 quartz 之間原來是這種關係 ……
      地址:cnblogs.com/xiongze520/p/13031944.html
    • 標題:CPU 瞞着內存竟干出這種事
      地址:cnblogs.com/xuanyuan/p/12894711.html
    • 標題:完了!CPU 一味求快出事兒了!
      地址:cnblogs.com/xuanyuan/p/12856598.html
    • … 類似的還有很多。

    我真心覺得這類標題挺有意思的,很贊,也很高級。如果是乾巴巴的教科書式的標題,我對文章一點興趣都沒有,不大可能會點進去看。但遇到這種標題,我極大可能會點進去看看,而且大多時候會認真地看完。對我來說,能讓我產生好奇或興趣、能讓我這顆浮躁的心不經意間讀完一篇技術文章,我就很感謝作者了。而且,作者深知要和短視頻、美女圖、搞笑動圖這樣能給人帶來既視快感的作品搶讀者的注意力是非常非常難的。為了能吸引我們一點點的注意力,為了能促使我們去閱讀一點更有營養的內容,技術文章“標題黨”作者也是想盡了辦法,難道我們不應該為作者的努力點個贊嗎?

    技術文章的目標讀者又不是小學畢業沒文化,幾乎都是 Geek 好嗎。為了能激起 Geek 們的熱情,能讓他們多表達一個句話,你知道有多難嗎。你去公眾號和頭條對比一下那些技術文章的評論量和那些生活情感類文章的評論量,一篇技術文章能有 20 幾條評論那就相當熱鬧了。Geek 們豈是你能用標題吸引他們加關注的,只是帶來短暫的點擊量罷了。內容沒有營養,再標題黨也不會讓 Geek 們買賬,他們反而分分鐘取關你!你看,作為原創作者,我們也不容易吧。​ 偶爾“標題黨”調劑一下,能激起大家討論的興趣,能讓氛圍熱鬧一點,我覺得是好事情啊。

    本就枯燥的技術文章,太在意標題,你就輸了。我是說,你真的把文章閱讀完了嗎?把你浮躁的心收起來吧,認真閱讀完文章后,在內容相關話題上表達自己的觀點和想法或提出意見,才是一個技術人應該有的態度,真正有收穫才算贏了自己,不是嗎。

    最後,本號一定努力提高文章質量,堅持創作優質內容。一路走來,謝謝各位陪伴與支持!沒有你們,我也沒有持續寫作的動力。​

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

    【其他文章推薦】

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

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

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

  • 【原】二進制部署 k8s 1.18.3

    【原】二進制部署 k8s 1.18.3

    二進制部署 k8s 1.18.3

    插播一條:ansible 一鍵部署:https://github.com/liyongjian5179/k8s-ansible

    1、相關前置信息

    1.1 版本信息

    kube_version: v1.18.3

    etcd_version: v3.4.9

    flannel: v0.12.0

    coredns: v1.6.7

    cni-plugins: v0.8.6

    pod 網段:10.244.0.0/16

    service 網段:10.96.0.0/12

    kubernetes 內部地址:10.96.0.1

    coredns 地址: 10.96.0.10

    apiserver 域名:lb.5179.top

    1.2 機器安排

    主機名 IP 角色及組件 k8s 相關組件
    centos7-nginx 10.10.10.127 nginx 四層代理 nginx
    centos7-a 10.10.10.128 master,node,etcd,flannel kube-apiserver kube-controller-manager kube-scheduler kubelet kube-proxy
    centos7-b 10.10.10.129 master,node,etcd,flannel kube-apiserver kube-controller-manager kube-scheduler kubelet kube-proxy
    centos7-c 10.10.10.130 master,node,etcd,flannel kube-apiserver kube-controller-manager kube-scheduler kubelet kube-proxy
    centos7-d 10.10.10.131 node,flannel kubelet kube-proxy
    centos7-e 10.10.10.132 node,flannel kubelet kube-proxy

    2、部署前環境準備

    centos7-nginx 當主控機對其他機器做免密

    2.1、 安裝ansible用於批量操作

    安裝過程略

    [root@centos7-nginx ~]# cat /etc/ansible/hosts
    [masters]
    10.10.10.128
    10.10.10.129
    10.10.10.130
    
    [nodes]
    10.10.10.131
    10.10.10.132
    
    [k8s]
    10.10.10.[128:132]
    

    推送宿主機 hosts 文件

    cat /etc/hosts
    127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
    10.10.10.127 centos7-nginx lb.5179.top
    10.10.10.128 centos7-a
    10.10.10.129 centos7-b
    10.10.10.130 centos7-c
    10.10.10.131 centos7-d
    10.10.10.132 centos7-e
    
    ansible k8s -m shell -a "mv /etc/hosts /etc/hosts.bak"
    ansible k8s -m copy -a "src=/etc/hosts dest=/etc/hosts"
    

    2.2 關閉防火牆及SELINUX

    # 關閉防火牆
    ansible k8s -m shell -a "systemctl stop firewalld &&  systemctl disable firewalld"
    # 關閉 selinux
    ansible k8s -m shell -a "setenforce  0  && sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/sysconfig/selinux && sed -i "s/^SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config "
    

    2.3 關閉 swap 分區

    ansible k8s -m shell -a "swapoff -a && sed -i 's/.*swap.*/#&/' /etc/fstab"
    

    2.4 安裝 docker及加速器

    vim ./install_docker.sh
    #!/bin/bash
    #
    yum install -y 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-19.03.11-19.03.11
    systemctl enable docker
    systemctl start docker
    docker version
    
    # 安裝加速器
    tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://ajpb7tdn.mirror.aliyuncs.com"],
      "log-opts": {"max-size":"100m", "max-file":"5"}
    }
    EOF
    systemctl daemon-reload
    systemctl restart docker
    

    然後使用 ansible 批量執行

    ansible k8s -m script -a "./install_docker.sh"
    

    2.5 修改內核參數

    vim 99-k8s.conf
    #sysctls for k8s node config
    net.ipv4.ip_forward=1
    net.ipv4.tcp_slow_start_after_idle=0
    net.core.rmem_max=16777216
    fs.inotify.max_user_watches=524288
    kernel.softlockup_all_cpu_backtrace=1
    kernel.softlockup_panic=1
    fs.file-max=2097152
    fs.inotify.max_user_instances=8192
    fs.inotify.max_queued_events=16384
    vm.max_map_count=262144
    vm.swappiness=0
    vm.overcommit_memory=1
    vm.panic_on_oom=0
    fs.may_detach_mounts=1
    net.core.netdev_max_backlog=16384
    net.ipv4.tcp_wmem=4096 12582912 16777216
    net.core.wmem_max=16777216
    net.core.somaxconn=32768
    net.ipv4.ip_forward=1
    net.ipv4.tcp_max_syn_backlog=8096
    net.bridge.bridge-nf-call-iptables=1
    net.bridge.bridge-nf-call-ip6tables=1
    net.ipv4.tcp_rmem=4096 12582912 16777216
    

    拷貝至遠程

    ansible k8s -m copy -a "src=./99-k8s.conf dest=/etc/sysctl.d/"
    ansible k8s -m shell -a "cd /etc/sysctl.d/ && sysctl --system"
    

    2.6 創建對應的目錄

    master 用

    vim mkdir_k8s_master.sh
    #!/bin/bash
    mkdir /opt/etcd/{bin,data,cfg,ssl} -p
    mkdir /opt/kubernetes/{bin,cfg,ssl,logs}  -p
    mkdir /opt/kubernetes/logs/{kubelet,kube-proxy,kube-scheduler,kube-apiserver,kube-controller-manager} -p
    
    echo 'export PATH=$PATH:/opt/kubernetes/bin' >> /etc/profile
    echo 'export PATH=$PATH:/opt/etcd/bin' >> /etc/profile
    source /etc/profile
    

    node 用

    vim mkdir_k8s_node.sh
    #!/bin/bash
    mkdir /opt/kubernetes/{bin,cfg,ssl,logs}  -p
    mkdir /opt/kubernetes/logs/{kubelet,kube-proxy} -p
    
    echo 'export PATH=$PATH:/opt/kubernetes/bin' >> /etc/profile
    source /etc/profile
    

    調用 ansible 執行

    ansible masters -m script -a "./mkdir_k8s_master.sh"
    ansible nodes -m script -a "./mkdir_k8s_node.sh"
    

    2.7 準備 LB

    為三台master提供高可用,可以選用雲廠商的 slb,也可以用 兩台 nginx + keepalived 實現。

    此處,為實驗環境,用單台 nginx 坐四層代理實現

    # 安裝 nginx
    [root@centos7-nginx ~]# yum install -y nginx
    # 創建子配置文件
    [root@centos7-nginx ~]# cd /etc/nginx/conf.d/
    [root@centos7-nginx conf.d]# vim lb.tcp
    stream {
        upstream master {
            hash $remote_addr consistent;
            server 10.10.10.128:6443 max_fails=3 fail_timeout=30;
            server 10.10.10.129:6443 max_fails=3 fail_timeout=30;
            server 10.10.10.130:6443 max_fails=3 fail_timeout=30;
        }
    
        server {
            listen 6443;
            proxy_pass master;
        }
    }
    # 在主配置文件中引入該文件
    [root@centos7-nginx ~]# cd /etc/nginx/
    [root@centos7-nginx nginx]# vim nginx.conf
    ...
    include /etc/nginx/conf.d/*.tcp;
    ...
    # 加入開機自啟,並啟動 nginx
    [root@centos7-nginx nginx]# systemctl enable nginx
    Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
    [root@centos7-nginx nginx]# systemctl start nginx
    

    3、部署

    3.1 生成證書

    執行腳本

    [root@centos7-nginx ~]# mkdir ssl && cd ssl
    [root@centos7-nginx ssl]# vim ./k8s-certificate.sh
    [root@centos7-nginx ssl]# ./k8s-certificate.sh 10.10.10.127,10.10.10.128,10.10.10.129,10.10.10.130,lb.5179.top,10.96.0.1
    

    IP 說明:

    • 10.10.10.127|lb.5179.top: nginx

    • 10.10.10.128|129|130: masters

    • 10.96.0.1: kubernetes(service 網段的第一個 IP)

    腳本內容如下

    #!/bin/bash
    # 二進制部署,生成 k8s 證書文件
    
    if [ $# -ne 1 ];then
        echo "please user in: `basename $0` MASTERS[10.10.10.127,10.10.10.128,10.10.10.129,10.10.10.130,lb.5179.top,10.96.0.1]"
        exit 1
    fi
    MASTERS=$1
    
    KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local
    
    
    
    for i in `echo $MASTERS | tr ',' ' '`;do
       if [ -z $IPS ];then
            IPS=\"$i\",
       else
            IPS=$IPS\"$i\",
       fi
    done
    
    
    command_exists() {
        command -v "$@" > /dev/null 2>&1
    }
    
    if command_exists cfssl; then
        echo "命令已存在"
    else
        # 下載生成證書命令
        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
    
        # 移動到 /usr/local/bin 目錄下
        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
    fi
    
    
    # 默認簽 10 年
    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 -
    
    #-----------------------
    
    cat > server-csr.json <<EOF
    {
        "CN": "kubernetes",
        "hosts": [
          ${IPS}
          "127.0.0.1",
          "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"
            }
        ]
    }
    EOF
    
    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes server-csr.json | cfssljson -bare server
    
    # 或者
    
    #cat > server-csr.json <<EOF
    #{
    #    "CN": "kubernetes",
    #    "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 \
    #  -hostname=${MASTERS},127.0.0.1,${KUBERNETES_HOSTNAMES} \
    #  -profile=kubernetes \
    #  server-csr.json | cfssljson -bare server
    
    
    
    
    #-----------------------
    
    cat > admin-csr.json <<EOF
    {
      "CN": "admin",
      "hosts": [],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
          "C": "CN",
          "L": "BeiJing",
          "ST": "BeiJing",
          "O": "system:masters",
          "OU": "System"
        }
      ]
    }
    EOF
    
    cfssl gencert \
      -ca=ca.pem \
      -ca-key=ca-key.pem \
      -config=ca-config.json \
      -profile=kubernetes \
      admin-csr.json | cfssljson -bare admin
    
    #-----------------------
    
    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
    
    
    # 注意: "CN": "system:metrics-server" 一定是這個,因為後面授權時用到這個名稱,否則會報禁止匿名訪問
    cat > metrics-server-csr.json <<EOF
    {
      "CN": "system:metrics-server",
      "hosts": [],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
          "C": "CN",
          "ST": "BeiJing",
          "L": "BeiJing",
          "O": "k8s",
          "OU": "system"
        }
      ]
    }
    EOF
    
    cfssl gencert \
      -ca=ca.pem \
      -ca-key=ca-key.pem \
      -config=ca-config.json \
      -profile=kubernetes \
      metrics-server-csr.json | cfssljson -bare metrics-server
    
    
    for item in $(ls *.pem |grep -v key) ;do echo ======================$item===================;openssl x509 -in $item -text -noout| grep Not;done
    
    #[root@aliyun k8s]# for item in $(ls *.pem |grep -v key) ;do echo ======================$item===================;openssl x509 -in $item -text -noout| grep Not;done
    #======================admin.pem====================
    #            Not Before: Jun 18 14:32:00 2020 GMT
    #            Not After : Jun 16 14:32:00 2030 GMT
    #======================ca.pem=======================
    #            Not Before: Jun 18 14:32:00 2020 GMT
    #            Not After : Jun 17 14:32:00 2025 GMT
    #======================kube-proxy.pem===============
    #            Not Before: Jun 18 14:32:00 2020 GMT
    #            Not After : Jun 16 14:32:00 2030 GMT
    #======================metrics-server.pem===========
    #            Not Before: Jun 18 14:32:00 2020 GMT
    #            Not After : Jun 16 14:32:00 2030 GMT
    #======================server.pem===================
    #            Not Before: Jun 18 14:32:00 2020 GMT
    #            Not After : Jun 16 14:32:00 2030 GMT
    

    注意:cfssl產生的ca證書固定5年有效期

    https://github.com/cloudflare/cfssl/blob/793fa93522ffd9a66d743ce4fa0958b6662ac619/initca/initca.go#L224

    // CAPolicy contains the CA issuing policy as default policy.
    var CAPolicy = func() *config.Signing {
    	return &config.Signing{
    		Default: &config.SigningProfile{
    			Usage:        []string{"cert sign", "crl sign"},
    			ExpiryString: "43800h",
    			Expiry:       5 * helpers.OneYear,
    			CAConstraint: config.CAConstraint{IsCA: true},
    		},
    	}
    }
    

    可以通過修改源碼方式重新編譯更改 ca 過期時間,或者在ca-csr.json添加如下

    "ca": {
          "expiry": "438000h"   #---> 50年
        }
    

    3.2 拷貝證書

    3.2.1 拷貝 etcd 集群使用的證書

    [root@centos7-nginx ~]# cd ssl
    [root@centos7-nginx ssl]#
    [root@centos7-nginx ssl]# ansible masters -m copy -a "src=./ca.pem dest=/opt/etcd/ssl"
    [root@centos7-nginx ssl]# ansible masters -m copy -a "src=./server.pem dest=/opt/etcd/ssl"
    [root@centos7-nginx ssl]# ansible masters -m copy -a "src=./server-key.pem dest=/opt/etcd/ssl"
    

    3.2.2 拷貝 k8s 集群使用的證書

    [root@centos7-nginx ~]# cd ssl
    [root@centos7-nginx ssl]#
    [root@centos7-nginx ssl]# scp *.pem  root@10.10.10.128:/opt/kubernetes/ssl/
    [root@centos7-nginx ssl]# scp *.pem  root@10.10.10.129:/opt/kubernetes/ssl/
    [root@centos7-nginx ssl]# scp *.pem  root@10.10.10.130:/opt/kubernetes/ssl/
    [root@centos7-nginx ssl]# scp *.pem  root@10.10.10.131:/opt/kubernetes/ssl/
    [root@centos7-nginx ssl]# scp *.pem  root@10.10.10.132:/opt/kubernetes/ssl/
    

    3.3 安裝 ETCD 集群

    下載二進制etcd包,並把執行文件推到各 master節點的 /opt/etcd/bin/ 目錄下

    [root@centos7-nginx ~]# mkdir ./etcd && cd ./etcd
    [root@centos7-nginx etcd]# wget https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz
    [root@centos7-nginx etcd]# tar zxvf etcd-v3.3.12-linux-amd64.tar.gz
    [root@centos7-nginx etcd]# cd etcd-v3.4.9-linux-amd64
    [root@centos7-nginx etcd-v3.4.9-linux-amd64]# ll
    總用量 40540
    drwxr-xr-x. 14 630384594 600260513     4096 5月  22 03:54 Documentation
    -rwxr-xr-x.  1 630384594 600260513 23827424 5月  22 03:54 etcd
    -rwxr-xr-x.  1 630384594 600260513 17612384 5月  22 03:54 etcdctl
    -rw-r--r--.  1 630384594 600260513    43094 5月  22 03:54 README-etcdctl.md
    -rw-r--r--.  1 630384594 600260513     8431 5月  22 03:54 README.md
    -rw-r--r--.  1 630384594 600260513     7855 5月  22 03:54 READMEv2-etcdctl.md
    
    [root@centos7-nginx etcd-v3.4.9-linux-amd64]# ansible masters -m copy -a "src=./etcd dest=/opt/etcd/bin mode=755"
    [root@centos7-nginx etcd-v3.4.9-linux-amd64]# ansible masters -m copy -a "src=./etcdctl dest=/opt/etcd/bin mode=755"
    

    編寫 etcd 配置文件腳本

    #!/bin/bash
    # 使用說明
    #./etcd.sh etcd01 10.10.10.128 etcd01=https://10.10.10.128:2380,etcd02=https://10.10.10.129:2380,etcd03=https://10.10.10.130:2380
    #./etcd.sh etcd02 10.10.10.129 etcd01=https://10.10.10.128:2380,etcd02=https://10.10.10.129:2380,etcd03=https://10.10.10.130:2380
    #./etcd.sh etcd03 10.10.10.130 etcd01=https://10.10.10.128:2380,etcd02=https://10.10.10.129:2380,etcd03=https://10.10.10.130:2380
    
    ETCD_NAME=${1:-"etcd01"}
    ETCD_IP=${2:-"127.0.0.1"}
    ETCD_CLUSTER=${3:-"etcd01=https://127.0.0.1:2379"}
    
    # ETCD 版本選擇[3.3,3.4]
    # 要用 3.3.14 以上版本:https://kubernetes.io/zh/docs/tasks/administer-cluster/configure-upgrade-etcd/#%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E5%85%B7%E6%9C%89%E5%AE%89%E5%85%A8%E7%AB%AF%E7%82%B9%E7%9A%84-etcd-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%9D%87%E8%A1%A1%E5%99%A8
    
    ETCD_VERSION=3.4.9
    
    if [ ${ETCD_VERSION%.*} == "3.4" ] ;then
    
    cat <<EOF >/opt/etcd/cfg/etcd.yml
    #etcd ${ETCD_VERSION}
    name: ${ETCD_NAME}
    data-dir: /opt/etcd/data
    listen-peer-urls: https://${ETCD_IP}:2380
    listen-client-urls: https://${ETCD_IP}:2379,https://127.0.0.1:2379
    
    advertise-client-urls: https://${ETCD_IP}:2379
    initial-advertise-peer-urls: https://${ETCD_IP}:2380
    initial-cluster: ${ETCD_CLUSTER}
    initial-cluster-token: etcd-cluster
    initial-cluster-state: new
    enable-v2: true
    
    client-transport-security:
      cert-file: /opt/etcd/ssl/server.pem
      key-file: /opt/etcd/ssl/server-key.pem
      client-cert-auth: false
      trusted-ca-file: /opt/etcd/ssl/ca.pem
      auto-tls: false
    
    peer-transport-security:
      cert-file: /opt/etcd/ssl/server.pem
      key-file: /opt/etcd/ssl/server-key.pem
      client-cert-auth: false
      trusted-ca-file: /opt/etcd/ssl/ca.pem
      auto-tls: false
    
    debug: false
    logger: zap
    log-outputs: [stderr]
    EOF
    
    else
    cat <<EOF >/opt/etcd/cfg/etcd.yml
    #etcd ${ETCD_VERSION}
    name: ${ETCD_NAME}
    data-dir: /opt/etcd/data
    listen-peer-urls: https://${ETCD_IP}:2380
    listen-client-urls: https://${ETCD_IP}:2379,https://127.0.0.1:2379
    
    advertise-client-urls: https://${ETCD_IP}:2379
    initial-advertise-peer-urls: https://${ETCD_IP}:2380
    initial-cluster: ${ETCD_CLUSTER}
    initial-cluster-token: etcd-cluster
    initial-cluster-state: new
    
    client-transport-security:
      cert-file: /opt/etcd/ssl/server.pem
      key-file: /opt/etcd/ssl/server-key.pem
      client-cert-auth: false
      trusted-ca-file: /opt/etcd/ssl/ca.pem
      auto-tls: false
    
    peer-transport-security:
      cert-file: /opt/etcd/ssl/server.pem
      key-file: /opt/etcd/ssl/server-key.pem
      peer-client-cert-auth: false
      trusted-ca-file: /opt/etcd/ssl/ca.pem
      auto-tls: false
    
    debug: false
    log-package-levels: etcdmain=CRITICAL,etcdserver=DEBUG
    log-outputs: default
    EOF
    
    fi
    
    cat <<EOF >/usr/lib/systemd/system/etcd.service
    [Unit]
    Description=Etcd Server
    Documentation=https://github.com/etcd-io/etcd
    Conflicts=etcd.service
    After=network.target
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=notify
    LimitNOFILE=65536
    Restart=on-failure
    RestartSec=5s
    TimeoutStartSec=0
    ExecStart=/opt/etcd/bin/etcd --config-file=/opt/etcd/cfg/etcd.yml
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable etcd
    systemctl restart etcd
    

    推送到 masters 機器上

    ansible masters -m copy -a "src=./etcd.sh dest=/opt/etcd/bin mode=755"
    

    分別登陸到三台機器上執行腳本文件

    [root@centos7-a bin]# ./etcd.sh etcd01 10.10.10.128 etcd01=https://10.10.10.128:2380,etcd02=https://10.10.10.129:2380,etcd03=https://10.10.10.130:2380
    [root@centos7-b bin]# ./etcd.sh etcd02 10.10.10.129 etcd01=https://10.10.10.128:2380,etcd02=https://10.10.10.129:2380,etcd03=https://10.10.10.130:2380
    [root@centos7-c bin]# ./etcd.sh etcd03 10.10.10.130 etcd01=https://10.10.10.128:2380,etcd02=https://10.10.10.129:2380,etcd03=https://10.10.10.130:2380
    

    驗證集群是否是健康的

    ### 3.4.9
    [root@centos7-a ~]# ETCDCTL_API=3 etcdctl --write-out="table" --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints=https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379 endpoint health
    +---------------------------+--------+-------------+-------+
    |         ENDPOINT          | HEALTH |    TOOK     | ERROR |
    +---------------------------+--------+-------------+-------+
    | https://10.10.10.128:2379 |   true | 31.126223ms |       |
    | https://10.10.10.129:2379 |   true | 28.698669ms |       |
    | https://10.10.10.130:2379 |   true | 32.508681ms |       |
    +---------------------------+--------+-------------+-------+
    

    查看集群成員

    [root@centos7-a ~]# ETCDCTL_API=3 etcdctl --write-out="table" --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints=https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379 member list
    +------------------+---------+--------+---------------------------+---------------------------+------------+
    |        ID        | STATUS  |  NAME  |        PEER ADDRS         |       CLIENT ADDRS        | IS LEARNER |
    +------------------+---------+--------+---------------------------+---------------------------+------------+
    | 2cec243d35ad0881 | started | etcd02 | https://10.10.10.129:2380 | https://10.10.10.129:2379 |      false |
    | c6e694d272df93e8 | started | etcd03 | https://10.10.10.130:2380 | https://10.10.10.130:2379 |      false |
    | e9b57a5a8276394a | started | etcd01 | https://10.10.10.128:2380 | https://10.10.10.128:2379 |      false |
    +------------------+---------+--------+---------------------------+---------------------------+------------+
    

    etcdctl創建別名,三台機器分別執行

    vim .bashrc
    alias etcdctl2="ETCDCTL_API=2 etcdctl --ca-file=/opt/etcd/ssl/ca.pem --cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem --endpoints=https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379"
    alias etcdctl3="ETCDCTL_API=3 etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints=https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379"
    
    source .bashrc
    

    3.3 安裝 k8s 相關組件

    3.3.1 下載二進制安裝包

    [root@centos7-nginx ~]# mkdir k8s-1.18.3 && cd k8s-1.18.3/
    [root@centos7-nginx k8s-1.18.3]# wget https://dl.k8s.io/v1.18.3/kubernetes-server-linux-amd64.tar.gz
    [root@centos7-nginx k8s-1.18.3]# tar xf kubernetes-server-linux-amd64.tar.gz
    [root@centos7-nginx k8s-1.18.3]# cd kubernetes
    [root@centos7-nginx kubernetes]# ll
    總用量 33092
    drwxr-xr-x. 2 root root        6 5月  20 21:32 addons
    -rw-r--r--. 1 root root 32587733 5月  20 21:32 kubernetes-src.tar.gz
    -rw-r--r--. 1 root root  1297746 5月  20 21:32 LICENSES
    drwxr-xr-x. 3 root root       17 5月  20 21:27 server
    [root@centos7-nginx kubernetes]# cd server/bin/
    [root@centos7-nginx bin]# ll
    總用量 1087376
    -rwxr-xr-x. 1 root root  48128000 5月  20 21:32 apiextensions-apiserver
    -rwxr-xr-x. 1 root root  39813120 5月  20 21:32 kubeadm
    -rwxr-xr-x. 1 root root 120668160 5月  20 21:32 kube-apiserver
    -rw-r--r--. 1 root root         8 5月  20 21:27 kube-apiserver.docker_tag
    -rw-------. 1 root root 174558720 5月  20 21:27 kube-apiserver.tar
    -rwxr-xr-x. 1 root root 110059520 5月  20 21:32 kube-controller-manager
    -rw-r--r--. 1 root root         8 5月  20 21:27 kube-controller-manager.docker_tag
    -rw-------. 1 root root 163950080 5月  20 21:27 kube-controller-manager.tar
    -rwxr-xr-x. 1 root root  44032000 5月  20 21:32 kubectl
    -rwxr-xr-x. 1 root root 113283800 5月  20 21:32 kubelet
    -rwxr-xr-x. 1 root root  38379520 5月  20 21:32 kube-proxy
    -rw-r--r--. 1 root root         8 5月  20 21:28 kube-proxy.docker_tag
    -rw-------. 1 root root 119099392 5月  20 21:28 kube-proxy.tar
    -rwxr-xr-x. 1 root root  42950656 5月  20 21:32 kube-scheduler
    -rw-r--r--. 1 root root         8 5月  20 21:27 kube-scheduler.docker_tag
    -rw-------. 1 root root  96841216 5月  20 21:27 kube-scheduler.tar
    -rwxr-xr-x. 1 root root   1687552 5月  20 21:32 mounter
    

    將對應文件拷貝到目標機器上

    # masters
    [root@centos7-nginx bin]# scp kube-apiserver kube-controller-manager kube-scheduler kubectl kubelet kube-proxy root@10.10.10.128:/opt/kubernetes/bin/
    [root@centos7-nginx bin]# scp kube-apiserver kube-controller-manager kube-scheduler kubectl kubelet kube-proxy root@10.10.10.129:/opt/kubernetes/bin/
    [root@centos7-nginx bin]# scp kube-apiserver kube-controller-manager kube-scheduler kubectl kubelet kube-proxy root@10.10.10.130:/opt/kubernetes/bin/
    
    # nodes
    [root@centos7-nginx bin]# scp kubelet kube-proxy root@10.10.10.131:/opt/kubernetes/bin/
    [root@centos7-nginx bin]# scp kubelet kube-proxy root@10.10.10.131:/opt/kubernetes/bin/
    
    # 本機
    [root@centos7-nginx bin]# cp kubectl /usr/local/bin/
    

    3.3.2 創建Node節點kubeconfig文件

    • 創建TLS Bootstrapping Token
    • 創建kubelet kubeconfig
    • 創建kube-proxy kubeconfig
    • 創建admin kubeconfig
    [root@centos7-nginx ~]# cd ~/ssl/
    [root@centos7-nginx ssl]# vim kubeconfig.sh # 修改第10行 KUBE_APISERVER 地址
    [root@centos7-nginx ssl]# bash ./kubeconfig.sh
    Cluster "kubernetes" set.
    User "kubelet-bootstrap" set.
    Context "default" created.
    Switched to context "default".
    Cluster "kubernetes" set.
    User "kube-proxy" set.
    Context "default" created.
    Switched to context "default".
    Cluster "kubernetes" set.
    User "admin" set.
    Context "default" created.
    Switched to context "default".
    

    腳本內容如下:

    # 創建 TLS Bootstrapping Token
    export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')
    cat > token.csv <<EOF
    ${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
    EOF
    
    #----------------------
    
    # 創建kubelet bootstrapping kubeconfig
    export KUBE_APISERVER="https://lb.5179.top:6443"
    
    # 設置集群參數
    kubectl config set-cluster kubernetes \
      --certificate-authority=./ca.pem \
      --embed-certs=true \
      --server=${KUBE_APISERVER} \
      --kubeconfig=bootstrap.kubeconfig
    
    # 設置客戶端認證參數
    kubectl config set-credentials kubelet-bootstrap \
      --token=${BOOTSTRAP_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
    
    #----------------------
    
    # 創建kube-proxy kubeconfig文件
    
    kubectl config set-cluster kubernetes \
      --certificate-authority=./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
    
    #----------------------
    
    # 創建 admin kubeconfig文件
    
      kubectl config set-cluster kubernetes \
        --certificate-authority=./ca.pem \
        --embed-certs=true \
        --server=${KUBE_APISERVER} \
        --kubeconfig=admin.kubeconfig
    
      kubectl config set-credentials admin \
        --client-certificate=./admin.pem \
        --client-key=./admin-key.pem \
        --embed-certs=true \
        --kubeconfig=admin.kubeconfig
    
      kubectl config set-context default \
        --cluster=kubernetes \
        --user=admin \
        --kubeconfig=admin.kubeconfig
    
      kubectl config use-context default --kubeconfig=admin.kubeconfig
    
    

    將文件拷貝至對應位置

    ansible k8s -m copy -a "src=./bootstrap.kubeconfig dest=/opt/kubernetes/cfg"
    ansible k8s -m copy -a "src=./kube-proxy.kubeconfig dest=/opt/kubernetes/cfg"
    ansible k8s -m copy -a "src=./token.csv dest=/opt/kubernetes/cfg"
    

    3.4 安裝 kube-apiserver

    Masters 節點安裝

    此處可以使用 tmux 打開三個終端窗口進行,并行輸入

    也可以在三台機器上分開執行

    [root@centos7-a ~]# mkdir k8s-scripts
    [root@centos7-a k8s-scripts]# vim install-apiserver.sh
    [root@centos7-a k8s-scripts]# IP=`ip addr | grep ens33 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'|head -1` && echo $IP
    10.10.10.128
    [root@centos7-a k8s-scripts]# bash install-apiserver.sh $IP https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379
    

    腳本內容如下:

    #!/bin/bash
    # MASTER_ADDRESS 寫本機
    MASTER_ADDRESS=${1:-"10.10.10.128"}
    ETCD_SERVERS=${2:-"http://127.0.0.1:2379"}
    
    cat <<EOF >/opt/kubernetes/cfg/kube-apiserver
    KUBE_APISERVER_OPTS="--logtostderr=false \\
    --v=2 \\
    --log-dir=/opt/kubernetes/logs/kube-apiserver \\
    --etcd-servers=${ETCD_SERVERS} \\
    --bind-address=0.0.0.0 \\
    --secure-port=6443 \\
    --advertise-address=${MASTER_ADDRESS} \\
    --allow-privileged=true \\
    --service-cluster-ip-range=10.96.0.0/12 \\
    --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,NodeRestriction \\
    --authorization-mode=RBAC,Node \\
    --kubelet-https=true \\
    --enable-bootstrap-token-auth=true \\
    --token-auth-file=/opt/kubernetes/cfg/token.csv \\
    --service-node-port-range=30000-50000 \\
    --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/kubernetes/ssl/ca.pem \\
    --etcd-certfile=/opt/kubernetes/ssl/server.pem \\
    --etcd-keyfile=/opt/kubernetes/ssl/server-key.pem \\
    --requestheader-client-ca-file=/opt/kubernetes/ssl/ca.pem \\
    --requestheader-extra-headers-prefix=X-Remote-Extra- \\
    --requestheader-group-headers=X-Remote-Group \\
    --requestheader-username-headers=X-Remote-User \\
    --proxy-client-cert-file=/opt/kubernetes/ssl/metrics-server.pem \\
    --proxy-client-key-file=/opt/kubernetes/ssl/metrics-server-key.pem \\
    --runtime-config=api/all=true \\
    --audit-log-maxage=30 \\
    --audit-log-maxbackup=3 \\
    --audit-log-maxsize=100 \\
    --audit-log-truncate-enabled=true \\
    --audit-log-path=/opt/kubernetes/logs/k8s-audit.log"
    EOF
    
    cat <<EOF >/usr/lib/systemd/system/kube-apiserver.service
    [Unit]
    Description=Kubernetes API Server
    Documentation=https://github.com/kubernetes/kubernetes
    
    [Service]
    EnvironmentFile=-/opt/kubernetes/cfg/kube-apiserver
    ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable kube-apiserver
    systemctl restart kube-apiserver
    

    3.5 安裝 kube-scheduler

    Masters 節點安裝

    此處可以使用 tmux 打開三個終端窗口進行,并行輸入,也可以在三台機器上分開執行

    [root@centos7-a ~]# cd k8s-scripts
    [root@centos7-a k8s-scripts]# vim install-scheduler.sh
    [root@centos7-a k8s-scripts]# bash install-scheduler.sh 127.0.0.1
    

    腳本內容如下

    #!/bin/bash
    
    MASTER_ADDRESS=${1:-"127.0.0.1"}
    
    cat <<EOF >/opt/kubernetes/cfg/kube-scheduler
    KUBE_SCHEDULER_OPTS="--logtostderr=false \\
    --v=2 \\
    --log-dir=/opt/kubernetes/logs/kube-scheduler \\
    --master=${MASTER_ADDRESS}:8080 \\
    --address=0.0.0.0 \\
    --leader-elect"
    EOF
    
    cat <<EOF >/usr/lib/systemd/system/kube-scheduler.service
    [Unit]
    Description=Kubernetes Scheduler
    Documentation=https://github.com/kubernetes/kubernetes
    
    [Service]
    EnvironmentFile=-/opt/kubernetes/cfg/kube-scheduler
    ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable kube-scheduler
    systemctl restart kube-scheduler
    

    3.6 安裝 kube-controller-manager

    Masters 節點安裝

    此處可以使用 tmux 打開三個終端窗口進行,并行輸入,也可以在三台機器上分開執行

    [root@centos7-a ~]# cd k8s-scripts
    [root@centos7-a k8s-scripts]# vim install-controller-manager.sh
    [root@centos7-a k8s-scripts]# bash install-controller-manager.sh 127.0.0.1
    

    腳本內容如下

    #!/bin/bash
    
    MASTER_ADDRESS=${1:-"127.0.0.1"}
    
    cat <<EOF >/opt/kubernetes/cfg/kube-controller-manager
    KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \\
    --v=2 \\
    --log-dir=/opt/kubernetes/logs/kube-controller-manager \\
    --master=${MASTER_ADDRESS}:8080 \\
    --leader-elect=true \\
    --bind-address=0.0.0.0 \\
    --service-cluster-ip-range=10.96.0.0/12 \\
    --cluster-name=kubernetes \\
    --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\
    --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \\
    --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\
    --experimental-cluster-signing-duration=87600h0m0s \\
    --feature-gates=RotateKubeletServerCertificate=true \\
    --feature-gates=RotateKubeletClientCertificate=true \\
    --allocate-node-cidrs=true \\
    --cluster-cidr=10.244.0.0/16 \\
    --root-ca-file=/opt/kubernetes/ssl/ca.pem"
    EOF
    
    cat <<EOF >/usr/lib/systemd/system/kube-controller-manager.service
    [Unit]
    Description=Kubernetes Controller Manager
    Documentation=https://github.com/kubernetes/kubernetes
    
    [Service]
    EnvironmentFile=-/opt/kubernetes/cfg/kube-controller-manager
    ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable kube-controller-manager
    systemctl restart kube-controller-manager
    

    3.7 查看組件狀態

    在三台機器上任意一台執行kubectl get cs

    [root@centos7-a k8s-scripts]# kubectl get cs
    NAME                 STATUS    MESSAGE             ERROR
    etcd-1               Healthy   {"health":"true"}
    etcd-2               Healthy   {"health":"true"}
    etcd-0               Healthy   {"health":"true"}
    controller-manager   Healthy   ok
    scheduler            Healthy   ok
    

    3.8 配置kubelet證書自動申請 CSR、審核及自動續期

    3.8.1 節點自動創建 CSR 請求

    節點 kubelet 啟動時自動創建 CSR 請求,將kubelet-bootstrap用戶綁定到系統集群角色 ,這個是為了頒發證書用的權限

    # Bind kubelet-bootstrap user to system cluster roles.
    kubectl create clusterrolebinding kubelet-bootstrap \
      --clusterrole=system:node-bootstrapper \
      --user=kubelet-bootstrap
    
    

    3.8.2 證書審批及自動續期

    1)手動審批腳本(啟動 node 節點 kubelet 之後操作)

    vim k8s-csr-approve.sh
    #!/bin/bash
    CSRS=$(kubectl get csr | awk '{if(NR>1) print $1}')
    for csr in $CSRS; do
    	kubectl certificate approve $csr;
    done
    
    1. 自動審批及續期

    創建自動批准相關 CSR 請求的 ClusterRole

    [root@centos7-a ~]# mkdir yaml
    [root@centos7-a ~]# cd yaml/
    [root@centos7-a yaml]# vim tls-instructs-csr.yaml
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver
    rules:
    - apiGroups: ["certificates.k8s.io"]
      resources: ["certificatesigningrequests/selfnodeserver"]
      verbs: ["create"]
    
    [root@centos7-a yaml]# kubectl apply -f tls-instructs-csr.yaml
    

    自動批准 kubelet-bootstrap 用戶 TLS bootstrapping 首次申請證書的 CSR 請求

    kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --user=kubelet-bootstrap
    

    自動批准 system:nodes 組用戶更新 kubelet 自身與 apiserver 通訊證書的 CSR 請求

    kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes
    

    自動批准 system:nodes 組用戶更新 kubelet 10250 api 端口證書的 CSR 請求

    kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeserver --group=system:nodes
    

    自動獲簽后的狀態如下:

    [root@centos7-a kubelet]# kubectl get csr
    NAME        AGE     SIGNERNAME                                    REQUESTOR           CONDITION
    csr-44lt8   4m10s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Approved,Issued
    csr-45njg   0s      kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Approved,Issued
    csr-nsbc9   4m9s    kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Approved,Issued
    csr-vk64f   4m9s    kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Approved,Issued
    csr-wftvq   59s     kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Approved,Issued
    

    3.9 安裝 kube-proxy

    拷貝對應包至所有節點

    [root@centos7-nginx ~]# cd k8s-1.18.3/kubernetes/server/bin/
    [root@centos7-nginx bin]# ansible k8s -m copy -a "src=./kube-proxy dest=/opt/kubernetes/bin mode=755"
    

    此處可以使用 tmux 打開五個終端窗口進行,并行輸入,也可以在五台機器上分開執行

    [root@centos7-a ~]# cd k8s-scripts
    [root@centos7-a k8s-scripts]# vim install-proxy.sh
    [root@centos7-a k8s-scripts]# bash install-proxy.sh ${HOSTNAME}
    

    腳本內容如下

    #!/bin/bash
    
    HOSTNAME=${1:-"`hostname`"}
    
    cat <<EOF >/opt/kubernetes/cfg/kube-proxy.conf
    KUBE_PROXY_OPTS="--logtostderr=false \\
    --v=2 \\
    --log-dir=/opt/kubernetes/logs/kube-proxy \\
    --config=/opt/kubernetes/cfg/kube-proxy-config.yml"
    EOF
    
    cat <<EOF >/opt/kubernetes/cfg/kube-proxy-config.yml
    kind: KubeProxyConfiguration
    apiVersion: kubeproxy.config.k8s.io/v1alpha1
    address: 0.0.0.0 # 監聽地址
    metricsBindAddress: 0.0.0.0:10249 # 監控指標地址,監控獲取相關信息 就從這裏獲取
    clientConnection:
      kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig # 讀取配置文件
    hostnameOverride: ${HOSTNAME} # 註冊到k8s的節點名稱唯一
    clusterCIDR: 10.244.0.0/16
    mode: iptables # 使用iptables模式
    
    # 使用 ipvs 模式
    #mode: ipvs # ipvs 模式
    #ipvs:
    #  scheduler: "rr"
    #iptables:
    #  masqueradeAll: true
    EOF
    
    cat <<EOF >/usr/lib/systemd/system/kube-proxy.service
    [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
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable kube-proxy
    systemctl restart kube-proxy
    

    3.10 安裝 kubelet

    拷貝對應包至所有節點

    [root@centos7-nginx ~]# cd k8s-1.18.3/kubernetes/server/bin/
    [root@centos7-nginx bin]# ansible k8s -m copy -a "src=./kubelet dest=/opt/kubernetes/bin mode=755"
    

    此處可以使用 tmux 打開五個終端窗口進行,并行輸入,也可以在五台機器上分開執行

    [root@centos7-a ~]# cd k8s-scripts
    [root@centos7-a k8s-scripts]# vim install-kubelet.sh
    [root@centos7-a k8s-scripts]# bash install-kubelet.sh 10.96.0.10 ${HOSTNAME} cluster.local
    

    腳本內容如下

    #!/bin/bash
    
    DNS_SERVER_IP=${1:-"10.96.0.10"}
    HOSTNAME=${2:-"`hostname`"}
    CLUETERDOMAIN=${3:-"cluster.local"}
    
    cat <<EOF >/opt/kubernetes/cfg/kubelet.conf
    KUBELET_OPTS="--logtostderr=false \\
    --v=2 \\
    --log-dir=/opt/kubernetes/logs/kubelet \\
    --hostname-override=${HOSTNAME} \\
    --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 \\
    --network-plugin=cni \\
    --cni-conf-dir=/etc/cni/net.d \\
    --cni-bin-dir=/opt/cni/bin \\
    --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 \\
    --system-reserved=memory=300Mi \\
    --kube-reserved=memory=400Mi"
    EOF
    
    cat <<EOF >/opt/kubernetes/cfg/kubelet-config.yml
    kind: KubeletConfiguration # 使用對象
    apiVersion: kubelet.config.k8s.io/v1beta1 # api版本
    address: 0.0.0.0 # 監聽地址
    port: 10250 # 當前kubelet的端口
    readOnlyPort: 10255 # kubelet暴露的端口
    cgroupDriver: cgroupfs # 驅動,要與docker info显示的驅動一致
    clusterDNS:
      - ${DNS_SERVER_IP}
    clusterDomain: ${CLUETERDOMAIN}  # 集群域
    failSwapOn: false # 關閉swap
    
    # 身份驗證
    authentication:
      anonymous:
        enabled: false
      webhook:
        cacheTTL: 2m0s
        enabled: true
      x509:
        clientCAFile: /opt/kubernetes/ssl/ca.pem
    
    # 授權
    authorization:
      mode: Webhook
      webhook:
        cacheAuthorizedTTL: 5m0s
        cacheUnauthorizedTTL: 30s
    
    # Node 資源保留
    evictionHard:
      imagefs.available: 15%
      memory.available: 300Mi
      nodefs.available: 10%
      nodefs.inodesFree: 5%
    evictionPressureTransitionPeriod: 5m0s
    
    # 鏡像刪除策略
    imageGCHighThresholdPercent: 85
    imageGCLowThresholdPercent: 80
    imageMinimumGCAge: 2m0s
    
    # 旋轉證書
    rotateCertificates: true # 旋轉kubelet client 證書
    featureGates:
      RotateKubeletServerCertificate: true
      RotateKubeletClientCertificate: true
    
    maxOpenFiles: 1000000
    maxPods: 110
    EOF
    
    cat <<EOF >/usr/lib/systemd/system/kubelet.service
    [Unit]
    Description=Kubernetes Kubelet
    After=docker.service
    Requires=docker.service
    
    [Service]
    EnvironmentFile=-/opt/kubernetes/cfg/kubelet.conf
    ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS
    Restart=on-failure
    KillMode=process
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable kubelet
    systemctl restart kubelet
    

    3.11 查看節點個數

    等待一段時間后出現

    [root@centos7-a ~]# kubectl get nodes
    NAME        STATUS   ROLES    AGE     VERSION
    centos7-a   NotReady    <none>   7m   v1.18.3
    centos7-b   NotReady    <none>   6m   v1.18.3
    centos7-c   NotReady    <none>   6m   v1.18.3
    centos7-d   NotReady    <none>   6m   v1.18.3
    centos7-e   NotReady    <none>   5m   v1.18.3
    

    3.12 安裝網絡插件

    3.12.1 安裝 flannel

    [root@centos7-nginx ~]# mkdir flannel
    [root@centos7-nginx flannel]# wget https://github.com/coreos/flannel/releases/download/v0.12.0/flannel-v0.12.0-linux-amd64.tar.gz
    [root@centos7-nginx flannel]# tar xf flannel-v0.12.0-linux-amd64.tar.gz
    [root@centos7-nginx flannel]# ll
    總用量 43792
    -rwxr-xr-x. 1 lyj  lyj  35253112 3月  13 08:01 flanneld
    -rw-r--r--. 1 root root  9565406 6月  16 19:41 flannel-v0.12.0-linux-amd64.tar.gz
    -rwxr-xr-x. 1 lyj  lyj      2139 5月  29 2019 mk-docker-opts.sh
    -rw-r--r--. 1 lyj  lyj      4300 5月  29 2019 README.md
    [root@centos7-nginx flannel]# vim remove-docker0.sh
    #!/bin/bash
    
    # Copyright 2014 The Kubernetes Authors All rights reserved.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    # Delete default docker bridge, so that docker can start with flannel network.
    
    # exit on any error
    set -e
    
    rc=0
    ip link show docker0 >/dev/null 2>&1 || rc="$?"
    if [[ "$rc" -eq "0" ]]; then
      ip link set dev docker0 down
      ip link delete docker0
    fi
    

    將包拷貝至所有主機對應位置

    [root@centos7-nginx flannel]# ansible k8s -m copy -a "src=./flanneld dest=/opt/kubernetes/bin mode=755"
    [root@centos7-nginx flannel]# ansible k8s -m copy -a "src=./mk-docker-opts.sh dest=/opt/kubernetes/bin mode=755"
    [root@centos7-nginx flannel]# ansible k8s -m copy -a "src=./remove-docker0.sh dest=/opt/kubernetes/bin mode=755"
    

    準備啟動腳本

    [root@centos7-nginx scripts]# vim install-flannel.sh
    [root@centos7-nginx scripts]# bash install-flannel.sh 
    [root@centos7-nginx scripts]# ansible k8s -m script -a "./install-flannel.sh https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379"
    

    腳本內容如下:

    #!/bin/bash
    ETCD_ENDPOINTS=${1:-'https://127.0.0.1:2379'}
    
    cat >/opt/kubernetes/cfg/flanneld <<EOF
    FLANNEL_OPTIONS="--etcd-endpoints=${ETCD_ENDPOINTS} \
    -etcd-cafile=/opt/kubernetes/ssl/ca.pem \
    -etcd-certfile=/opt/kubernetes/ssl/server.pem \
    -etcd-keyfile=/opt/kubernetes/ssl/server-key.pem"
    EOF
    
    cat >/usr/lib/systemd/system/flanneld.service <<EOF
    [Unit]
    Description=Flanneld Overlay address etcd agent
    After=network-online.target network.target
    Before=docker.service
    
    [Service]
    Type=notify
    EnvironmentFile=/opt/kubernetes/cfg/flanneld
    #ExecStartPre=/opt/kubernetes/bin/remove-docker0.sh
    ExecStart=/opt/kubernetes/bin/flanneld --ip-masq \$FLANNEL_OPTIONS
    #ExecStartPost=/opt/kubernetes/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/subnet.env
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    systemctl daemon-reload
    systemctl enable flanneld
    systemctl restart flanneld
    

    將 pod 網段信息寫入 etcd 中

    登陸到任意一台 master 節點上

    [root@centos7-a ~]# cd k8s-scripts/
    [root@centos7-a k8s-scripts]# vim install-flannel-to-etcd.sh
    [root@centos7-a k8s-scripts]# bash install-flannel-to-etcd.sh https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379 10.244.0.0/16 vxlan
    

    腳本內容如下

    #!/bin/bash
    # bash install-flannel-to-etcd.sh https://10.10.10.128:2379,https://10.10.10.129:2379,https://10.10.10.130:2379 10.244.0.0/16 vxlan
    
    ETCD_ENDPOINTS=${1:-'https://127.0.0.1:2379'}
    NETWORK=${2:-'10.244.0.0/16'}
    NETWORK_MODE=${3:-'vxlan'}
    
    ETCDCTL_API=2 etcdctl --ca-file=/opt/etcd/ssl/ca.pem --cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem --endpoints=${ETCD_ENDPOINTS} set /coreos.com/network/config   '{"Network": '\"$NETWORK\"', "Backend": {"Type": '\"${NETWORK_MODE}\"'}}'
    
    #ETCDCTL_API=3 etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints=${ETCD_ENDPOINTS} put /coreos.com/network/config -- '{"Network": "10.244.0.0/16", "Backend": {"Type": "vxlan"}}'
    

    由於flannel 使用的是v2版本的 etcd,所以此處 etcdctl 使用 v2 的 API

    3.12.2 安裝 cni-plugin

    下載 cni 插件

    [root@centos7-nginx ~]# mkdir cni
    [root@centos7-nginx ~]# cd cni
    [root@centos7-nginx cni]# wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz
    [root@centos7-nginx cni]# tar xf cni-plugins-linux-amd64-v0.8.6.tgz
    [root@centos7-nginx cni]# ll
    總用量 106512
    -rwxr-xr-x. 1 root root  4159518 5月  14 03:50 bandwidth
    -rwxr-xr-x. 1 root root  4671647 5月  14 03:50 bridge
    -rw-r--r--. 1 root root 36878412 6月  17 20:07 cni-plugins-linux-amd64-v0.8.6.tgz
    -rwxr-xr-x. 1 root root 12124326 5月  14 03:50 dhcp
    -rwxr-xr-x. 1 root root  5945760 5月  14 03:50 firewall
    -rwxr-xr-x. 1 root root  3069556 5月  14 03:50 flannel
    -rwxr-xr-x. 1 root root  4174394 5月  14 03:50 host-device
    -rwxr-xr-x. 1 root root  3614480 5月  14 03:50 host-local
    -rwxr-xr-x. 1 root root  4314598 5月  14 03:50 ipvlan
    -rwxr-xr-x. 1 root root  3209463 5月  14 03:50 loopback
    -rwxr-xr-x. 1 root root  4389622 5月  14 03:50 macvlan
    -rwxr-xr-x. 1 root root  3939867 5月  14 03:50 portmap
    -rwxr-xr-x. 1 root root  4590277 5月  14 03:50 ptp
    -rwxr-xr-x. 1 root root  3392826 5月  14 03:50 sbr
    -rwxr-xr-x. 1 root root  2885430 5月  14 03:50 static
    -rwxr-xr-x. 1 root root  3356587 5月  14 03:50 tuning
    -rwxr-xr-x. 1 root root  4314446 5月  14 03:50 vlan
    [root@centos7-nginx cni]# cd ..
    [root@centos7-nginx ~]# ansible k8s -m copy -a "src=./cni/ dest=/opt/cni/bin mode=755"
    

    創建 cni 配置文件

    [root@centos7-nginx scripts]# vim install-cni.sh
    [root@centos7-nginx scripts]# ansible k8s -m script -a "./install-cni.sh"
    

    腳本內容如下:

    #!/bin/bash
    mkdir /etc/cni/net.d/ -pv
    cat <<EOF > /etc/cni/net.d/10-flannel.conflist
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
    EOF
    

    3.13 查看 node 狀態

    [root@centos7-c cfg]# kubectl get nodes
    NAME           STATUS   ROLES    AGE   VERSION
    10.10.10.128   Ready    <none>   1h   v1.18.3
    10.10.10.129   Ready    <none>   1h   v1.18.3
    10.10.10.130   Ready    <none>   1h   v1.18.3
    10.10.10.131   Ready    <none>   1h   v1.18.3
    10.10.10.132   Ready    <none>   1h   v1.18.3
    

    3.14 安裝 coredns

    注意:k8s 與 coredns 的版本對應關係

    https://github.com/coredns/deployment/blob/master/kubernetes/CoreDNS-k8s_version.md

    安裝 dns 插件

    kubectl apply -f coredns.yaml
    

    文件內容如下

    cat coredns.yaml # 注意修改clusterIP 和 鏡像版本1.6.7

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: coredns
      namespace: kube-system
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      labels:
        kubernetes.io/bootstrapping: rbac-defaults
      name: system:coredns
    rules:
    - apiGroups:
      - ""
      resources:
      - endpoints
      - services
      - pods
      - namespaces
      verbs:
      - list
      - watch
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      annotations:
        rbac.authorization.kubernetes.io/autoupdate: "true"
      labels:
        kubernetes.io/bootstrapping: rbac-defaults
      name: system:coredns
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: system:coredns
    subjects:
    - kind: ServiceAccount
      name: coredns
      namespace: kube-system
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: coredns
      namespace: kube-system
    data:
      Corefile: |
        .:53 {
            errors
            health {
              lameduck 5s
            }
            ready
            kubernetes cluster.local in-addr.arpa ip6.arpa {
              fallthrough in-addr.arpa ip6.arpa
            }
            prometheus :9153
            forward . /etc/resolv.conf
            cache 30
            loop
            reload
            loadbalance
        }
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: coredns
      namespace: kube-system
      labels:
        k8s-app: kube-dns
        kubernetes.io/name: "CoreDNS"
    spec:
      # replicas: not specified here:
      # 1. Default is 1.
      # 2. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
      replicas: 2
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 1
      selector:
        matchLabels:
          k8s-app: kube-dns
      template:
        metadata:
          labels:
            k8s-app: kube-dns
        spec:
          priorityClassName: system-cluster-critical
          serviceAccountName: coredns
          tolerations:
            - key: "CriticalAddonsOnly"
              operator: "Exists"
          nodeSelector:
            kubernetes.io/os: linux
          affinity:
             podAntiAffinity:
               preferredDuringSchedulingIgnoredDuringExecution:
               - weight: 100
                 podAffinityTerm:
                   labelSelector:
                     matchExpressions:
                       - key: k8s-app
                         operator: In
                         values: ["kube-dns"]
                   topologyKey: kubernetes.io/hostname
          containers:
          - name: coredns
            image: coredns/coredns:1.6.7
            imagePullPolicy: IfNotPresent
            resources:
              limits:
                memory: 170Mi
              requests:
                cpu: 100m
                memory: 70Mi
            args: [ "-conf", "/etc/coredns/Corefile" ]
            volumeMounts:
            - name: config-volume
              mountPath: /etc/coredns
              readOnly: true
            ports:
            - containerPort: 53
              name: dns
              protocol: UDP
            - containerPort: 53
              name: dns-tcp
              protocol: TCP
            - containerPort: 9153
              name: metrics
              protocol: TCP
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                add:
                - NET_BIND_SERVICE
                drop:
                - all
              readOnlyRootFilesystem: true
            livenessProbe:
              httpGet:
                path: /health
                port: 8080
                scheme: HTTP
              initialDelaySeconds: 60
              timeoutSeconds: 5
              successThreshold: 1
              failureThreshold: 5
            readinessProbe:
              httpGet:
                path: /ready
                port: 8181
                scheme: HTTP
          dnsPolicy: Default
          volumes:
            - name: config-volume
              configMap:
                name: coredns
                items:
                - key: Corefile
                  path: Corefile
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: kube-dns
      namespace: kube-system
      annotations:
        prometheus.io/port: "9153"
        prometheus.io/scrape: "true"
      labels:
        k8s-app: kube-dns
        kubernetes.io/cluster-service: "true"
        kubernetes.io/name: "CoreDNS"
    spec:
      selector:
        k8s-app: kube-dns
      clusterIP: 10.96.0.10
      ports:
      - name: dns
        port: 53
        protocol: UDP
      - name: dns-tcp
        port: 53
        protocol: TCP
      - name: metrics
        port: 9153
        protocol: TCP
    

    驗證是否可以正常運行

    # 先創建一個 busybox 容器作為客戶端
    [root@centos7-nginx ~]# kubectl create -f https://k8s.io/examples/admin/dns/busybox.yaml
    # 解析 kubernetes
    [root@centos7-nginx ~]# kubectl exec -it busybox -- nslookup kubernetes
    Server:    10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      kubernetes
    Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
    [root@centos7-nginx ~]#
    

    3.15 安裝 metrics-server

    項目地址:https://github.com/kubernetes-sigs/metrics-server

    按照說明執行如下命令即可,需要根據自身集群狀態進行修改,比如,鏡像地址、資源限制…

    kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml
    

    將文件下載到本地

    [root@centos7-nginx scripts]# wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml
    

    修改內容:修改鏡像地址,添加資源限制和相關命令

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: metrics-server
    spec:
      template:
        spec:
          containers:
          - name: metrics-server
            image: registry.cn-beijing.aliyuncs.com/liyongjian5179/metrics-server-amd64:v0.3.6
            imagePullPolicy: IfNotPresent
            resources:
              limits:
                cpu: 400m
                memory: 512Mi
              requests:
                cpu: 50m
                memory: 50Mi
            command:
            - /metrics-server
            - --kubelet-insecure-tls
            - --kubelet-preferred-address-types=InternalIP
    

    根據您的群集設置,您可能還需要更改傳遞給Metrics Server容器的標誌。最有用的標誌:

    • --kubelet-preferred-address-types -確定連接到特定節點的地址時使用的節點地址類型的優先級(default [Hostname,InternalDNS,InternalIP,ExternalDNS,ExternalIP])
    • --kubelet-insecure-tls-不要驗證Kubelets提供的服務證書的CA。僅用於測試目的。
    • --requestheader-client-ca-file -指定根證書捆綁包,以驗證傳入請求上的客戶端證書。

    執行該文件

    [root@centos7-nginx scripts]# kubectl apply -f components.yaml
    clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
    clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
    rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
    apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
    serviceaccount/metrics-server created
    deployment.apps/metrics-server created
    service/metrics-server created
    clusterrole.rbac.authorization.k8s.io/system:metrics-server created
    clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
    

    等待一段時間即可查看效果

    [root@centos7-nginx scripts]# kubectl top nodes
    NAME        CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
    centos7-a   159m         15%    1069Mi          62%
    centos7-b   158m         15%    1101Mi          64%
    centos7-c   168m         16%    1153Mi          67%
    centos7-d   48m          4%     657Mi           38%
    centos7-e   45m          4%     440Mi           50%
    [root@centos7-nginx scripts]# kubectl top pods -A
    NAMESPACE     NAME                              CPU(cores)   MEMORY(bytes)
    kube-system   coredns-6fdfb45d56-79jhl          5m           12Mi
    kube-system   coredns-6fdfb45d56-pvnzt          3m           13Mi
    kube-system   metrics-server-5f8fdf59b9-8chz8   1m           11Mi
    kube-system   tiller-deploy-6b75d7dccd-r6sz2    2m           6Mi
    

    完整文件內容如下

    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: system:aggregated-metrics-reader
      labels:
        rbac.authorization.k8s.io/aggregate-to-view: "true"
        rbac.authorization.k8s.io/aggregate-to-edit: "true"
        rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rules:
    - apiGroups: ["metrics.k8s.io"]
      resources: ["pods", "nodes"]
      verbs: ["get", "list", "watch"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: metrics-server:system:auth-delegator
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: system:auth-delegator
    subjects:
    - kind: ServiceAccount
      name: metrics-server
      namespace: kube-system
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: metrics-server-auth-reader
      namespace: kube-system
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: extension-apiserver-authentication-reader
    subjects:
    - kind: ServiceAccount
      name: metrics-server
      namespace: kube-system
    ---
    apiVersion: apiregistration.k8s.io/v1beta1
    kind: APIService
    metadata:
      name: v1beta1.metrics.k8s.io
    spec:
      service:
        name: metrics-server
        namespace: kube-system
      group: metrics.k8s.io
      version: v1beta1
      insecureSkipTLSVerify: true
      groupPriorityMinimum: 100
      versionPriority: 100
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: metrics-server
      namespace: kube-system
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: metrics-server
      namespace: kube-system
      labels:
        k8s-app: metrics-server
    spec:
      selector:
        matchLabels:
          k8s-app: metrics-server
      template:
        metadata:
          name: metrics-server
          labels:
            k8s-app: metrics-server
        spec:
          serviceAccountName: metrics-server
          volumes:
          # mount in tmp so we can safely use from-scratch images and/or read-only containers
          - name: tmp-dir
            emptyDir: {}
          containers:
          - name: metrics-server
            image: registry.cn-beijing.aliyuncs.com/liyongjian5179/metrics-server-amd64:v0.3.6
            imagePullPolicy: IfNotPresent
            resources:
              limits:
                cpu: 400m
                memory: 512Mi
              requests:
                cpu: 50m
                memory: 50Mi
            command:
            - /metrics-server
            - --kubelet-insecure-tls
            - --kubelet-preferred-address-types=InternalIP
            args:
              - --cert-dir=/tmp
              - --secure-port=4443
            ports:
            - name: main-port
              containerPort: 4443
              protocol: TCP
            securityContext:
              readOnlyRootFilesystem: true
              runAsNonRoot: true
              runAsUser: 1000
            volumeMounts:
            - name: tmp-dir
              mountPath: /tmp
          nodeSelector:
            kubernetes.io/os: linux
            kubernetes.io/arch: "amd64"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: metrics-server
      namespace: kube-system
      labels:
        kubernetes.io/name: "Metrics-server"
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        k8s-app: metrics-server
      ports:
      - port: 443
        protocol: TCP
        targetPort: main-port
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: system:metrics-server
    rules:
    - apiGroups:
      - ""
      resources:
      - pods
      - nodes
      - nodes/stats
      - namespaces
      - configmaps
      verbs:
      - get
      - list
      - watch
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: system:metrics-server
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: system:metrics-server
    subjects:
    - kind: ServiceAccount
      name: metrics-server
      namespace: kube-system
    

    3.16 安裝 ingress

    3.16.1 LB 方案

    採用裸金屬服務器的方案:https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal

    可選NodePort或者LoadBalancer,默認是 NodePort 的方案

    在雲上的環境可以使用現成的 LB的方案:

    比如阿里雲Internal load balancer示例,可以通過註解的方式

    [...]
    metadata:
      annotations:  
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
    [...]
    

    裸金屬服務器上可選方案:

    1)純軟件解決方案:MetalLB(https://metallb.universe.tf/)

    該項目發佈於 2017 年底,當前處於 Beta 階段。

    MetalLB支持兩種聲明模式:

    • Layer 2模式:ARP/NDP
    • BGP模式

    Layer 2 模式

    Layer 2模式下,每個service會有集群中的一個node來負責。當服務客戶端發起ARP解析的時候,對應的node會響應該ARP請求,之後,該service的流量都會指向該node(看上去該node上有多個地址)。

    Layer 2模式並不是真正的負載均衡,因為流量都會先經過1個node后,再通過kube-proxy轉給多個end points。如果該node故障,MetalLB會遷移 IP到另一個node,並重新發送免費ARP告知客戶端遷移。現代操作系統基本都能正確處理免費ARP,因此failover不會產生太大問題。

    Layer 2模式更為通用,不需要用戶有額外的設備;但由於Layer 2模式使用ARP/ND,地址池分配需要跟客戶端在同一子網,地址分配略為繁瑣。

    BGP模式

    BGP模式下,集群中所有node都會跟上聯路由器建立BGP連接,並且會告知路由器應該如何轉發service的流量。

    BGP模式是真正的LoadBalancer。

    2)通過NodePort

    使用`NodePort`有一些局限性
    
    • Source IP address

    默認情況下,NodePort類型的服務執行源地址轉換。這意味着HTTP請求的源IP始終是從NGINX側接收到該請求的Kubernetes節點的IP地址。

    建議在NodePort設置中保留源IP的方法是將ingress-nginxServicespecexternalTrafficPolicy字段的值設置為Local,如下面的例子:

    kind: Service
    apiVersion: v1
    metadata:
      name: ingress-nginx
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        # by default the type is elb (classic load balancer).
        service.beta.kubernetes.io/aws-load-balancer-type: nlb
    spec:
      # this setting is t make sure the source IP address is preserved.
      externalTrafficPolicy: Local
      type: LoadBalancer
      selector:
        app.kubernetes.io/name: ingress-nginx
      ports:
      - name: http
        port: 80
        targetPort: http
      - name: https
        port: 443
        targetPort: https
    

    注意:此設置有效地丟棄了發送到未運行NGINX Ingress控制器任何實例的Kubernetes節點的數據包。考慮將NGINX Pod分配給特定節點,以控制應調度或不調度NGINX Ingress控制器的節點,可以通過nodeSelector實現。如果有三台機器,但是只有兩個 nginx 的 replica,分別部署在 node-2和 node-3,那麼當請求到 node-1 時,會因為在這台機器上沒有運行 nginx 的 replica 而被丟棄。

    給對應節點打標籤

    [root@centos7-nginx ~]# kubectl label nodes centos7-d lb-type=nginx
    node/centos7-d labeled
    [root@centos7-nginx ~]# kubectl label nodes centos7-e lb-type=nginx
    node/centos7-e labeled
    

    3.16.2 安裝

    本次實驗採用默認的方式:

    kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml
    

    如果需要進行修改,先下載到本地

    [root@centos7-nginx yaml]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/deploy.yaml
    [root@centos7-nginx yaml]# vim deploy.yaml
    [root@centos7-nginx yaml]# kubectl apply -f deploy.yaml
    namespace/ingress-nginx created
    serviceaccount/ingress-nginx created
    configmap/ingress-nginx-controller created
    clusterrole.rbac.authorization.k8s.io/ingress-nginx created
    clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
    role.rbac.authorization.k8s.io/ingress-nginx created
    rolebinding.rbac.authorization.k8s.io/ingress-nginx created
    service/ingress-nginx-controller-admission created
    service/ingress-nginx-controller created
    deployment.apps/ingress-nginx-controller created
    validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
    clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
    clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
    job.batch/ingress-nginx-admission-create created
    job.batch/ingress-nginx-admission-patch created
    role.rbac.authorization.k8s.io/ingress-nginx-admission created
    rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
    serviceaccount/ingress-nginx-admission created
    

    也可以先跑起來,在修改

    [root@centos7-nginx ~]# kubectl edit deploy ingress-nginx-controller -n ingress-nginx
    ...
    spec:
      progressDeadlineSeconds: 600
      replicas: 2  #----> 修改為 2 實現高可用
    ...
      template:
    ...
        spec:
          nodeSelector:  #----> 增加節點選擇器
            lb-type: nginx #----> 匹配標籤
    

    或者使用

    [root@centos7-nginx yaml]# kubectl -n ingress-nginx patch deployment ingress-nginx-controller -p '{"spec": {"template": {"spec": {"nodeSelector": {"lb-type": "nginx"}}}}}'
    deployment.apps/ingress-nginx-controller patched
    
    [root@centos7-nginx yaml]# kubectl -n ingress-nginx scale --replicas=2 deployment/ingress-nginx-controller
    deployment.apps/ingress-nginx-controller scaled
    

    查看 svc 狀態可以看到端口已經分配

    [root@centos7-nginx ~]# kubectl get svc -n ingress-nginx
    NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
    ingress-nginx-controller             NodePort    10.101.121.120   <none>        80:36459/TCP,443:33171/TCP   43m
    ingress-nginx-controller-admission   ClusterIP   10.111.108.89    <none>        443/TCP                      43m
    

    所有機器上的端口也已經開啟,為了防止請求被丟棄,建議將代理后的節點 ip 固定在已經打了lb-type=nginx的節點

    [root@centos7-a ~]# netstat -ntpl |grep proxy
    tcp        0      0 0.0.0.0:36459           0.0.0.0:*               LISTEN      69169/kube-proxy
    tcp        0      0 0.0.0.0:33171           0.0.0.0:*               LISTEN      69169/kube-proxy
    ...
    [root@centos7-d ~]# netstat -ntpl |grep proxy
    tcp        0      0 0.0.0.0:36459           0.0.0.0:*               LISTEN      84181/kube-proxy
    tcp        0      0 0.0.0.0:33171           0.0.0.0:*               LISTEN      84181/kube-proxy
    [root@centos7-e ~]# netstat -ntpl |grep proxy
    tcp        0      0 0.0.0.0:36459           0.0.0.0:*               LISTEN      74881/kube-proxy
    tcp        0      0 0.0.0.0:33171           0.0.0.0:*               LISTEN      74881/kube-proxy
    

    3.16.3 驗證

    # 創建一個應用
    [root@centos7-nginx ~]# kubectl create deployment nginx-dns --image=nginx
    deployment.apps/nginx-dns created
    # 創建 svc
    [root@centos7-nginx ~]# kubectl expose deployment nginx-dns --port=80
    service/nginx-dns exposed
    [root@centos7-nginx ~]# kubectl get pods
    NAME                         READY   STATUS    RESTARTS   AGE
    busybox                      1/1     Running   29         29h
    nginx-dns-5c6b6b99df-qvnjh   1/1     Running   0          13s
    [root@centos7-nginx ~]# kubectl get svc
    NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP   3d5h
    nginx-dns    ClusterIP   10.108.88.75   <none>        80/TCP    10s
    # 創建 ingress 文件並執行
    [root@centos7-nginx yaml]# vim ingress.yaml
    [root@centos7-nginx yaml]# cat ingress.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-nginx-dns
      namespace: default
      annotations:
        kubernetes.io/ingress.class: "nginx"
    spec:
      rules:
      - host: ng.5179.top
        http:
          paths:
          - path: /
            backend:
              serviceName: nginx-dns
              servicePort: 80
    [root@centos7-nginx yaml]# kubectl apply -f ingress.yaml
    ingress.extensions/ingress-nginx-dns created
    [root@centos7-nginx yaml]# kubectl get ingress
    NAME                CLASS    HOSTS         ADDRESS   PORTS   AGE
    ingress-nginx-dns   <none>   ng.5179.top             80      9s
    

    先將日誌刷起來

    [root@centos7-nginx yaml]# kubectl get pods
    NAME                         READY   STATUS    RESTARTS   AGE
    busybox                      1/1     Running   30         30h
    nginx-dns-5c6b6b99df-qvnjh   1/1     Running   0          28m
    [root@centos7-nginx yaml]# kubectl logs -f nginx-dns-5c6b6b99df-qvnjh
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    10.244.3.123 - - [20/Jun/2020:12:58:20 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "10.244.4.0"
    

    後端 Pod 中 nginx 的日誌格式為

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    

    另起一個終端進行訪問

    [root@centos7-a ~]# curl -H 'Host:ng.5179.top' http://10.10.10.132:36459 -I
    HTTP/1.1 200 OK
    Server: nginx/1.19.0
    Date: Sat, 20 Jun 2020 12:58:27 GMT
    Content-Type: text/html
    Content-Length: 612
    Connection: keep-alive
    Vary: Accept-Encoding
    Last-Modified: Tue, 26 May 2020 15:00:20 GMT
    ETag: "5ecd2f04-264"
    Accept-Ranges: bytes
    

    可以看到日誌10.244.3.123 - - [20/Jun/2020:12:58:20 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "10.244.4.0"

    然後我們可以配置前端的 LB

    [root@centos7-nginx conf.d]# vim ng.conf
    [root@centos7-nginx conf.d]# cat ng.conf
    upstream nginx-dns{
            ip_hash;
            server 10.10.10.131:36459 ;
            server 10.10.10.132:36459;
       }
    
    server {
        listen       80;
        server_name  ng.5179.top;
    
        #access_log  logs/host.access.log  main;
    
        location / {
            root   html;
            proxy_pass http://nginx-dns;
    	    proxy_set_header Host $host;
    	    proxy_set_header X-Forwarded-For $remote_addr;
            index  index.html index.htm;
        }
    }
    # 添加內部解析
    [root@centos7-nginx conf.d]# vim /etc/hosts
    [root@centos7-nginx conf.d]# cat /etc/hosts
    127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
    10.10.10.127 centos7-nginx lb.5179.top ng.5179.top
    10.10.10.128 centos7-a
    10.10.10.129 centos7-b
    10.10.10.130 centos7-c
    10.10.10.131 centos7-d
    10.10.10.132 centos7-e
    # 重啟 nginx
    [root@centos7-nginx conf.d]# nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    [root@centos7-nginx conf.d]# nginx -s reload
    

    訪問該域名

    [root@centos7-nginx conf.d]# curl http://ng.5179.top -I
    HTTP/1.1 200 OK
    Server: nginx/1.16.1
    Date: Sat, 20 Jun 2020 13:07:38 GMT
    Content-Type: text/html
    Content-Length: 612
    Connection: keep-alive
    Vary: Accept-Encoding
    Last-Modified: Tue, 26 May 2020 15:00:20 GMT
    ETag: "5ecd2f04-264"
    Accept-Ranges: bytes
    

    後端也能正常收到日誌

    10.244.4.17 - - [20/Jun/2020:13:22:11 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.29.0" "10.244.4.1

    $remote_addr —> 10.244.4.17:為某一台 ingress-nginx 的 nginx_IP

    $http_x_forwarded_for —> 10.244.4.1:為節點上的 cni0 網卡 IP

    [root@centos7-nginx conf.d]# kubectl get pods -n ingress-nginx -o wide
    NAME                                        READY   STATUS      RESTARTS   AGE    IP             NODE        NOMINATED NODE   READINESS GATES
    ingress-nginx-admission-create-tqp5w        0/1     Completed   0          112m   10.244.3.119   centos7-d   <none>           <none>
    ingress-nginx-admission-patch-78jmf         0/1     Completed   0          112m   10.244.3.120   centos7-d   <none>           <none>
    ingress-nginx-controller-5946fd499c-6cx4x   1/1     Running     0          11m    10.244.3.125   centos7-d   <none>           <none>
    ingress-nginx-controller-5946fd499c-khjdn   1/1     Running     0          11m    10.244.4.17    centos7-e   <none>           <none>
    

    修改 ingress-nginx-controller 的 svc

    [root@centos7-nginx conf.d]# kubectl get svc -n ingress-nginx
    NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
    ingress-nginx-controller             NodePort    10.101.121.120   <none>        80:36459/TCP,443:33171/TCP   97m
    ingress-nginx-controller-admission   ClusterIP   10.111.108.89    <none>        443/TCP                      97m
    [root@centos7-nginx conf.d]# kubectl edit svc ingress-nginx-controller -n ingress-nginx
    ...
    spec:
      clusterIP: 10.101.121.120
      externalTrafficPolicy: Cluster  #---> 修改為 Local
    ...  
      service/ingress-nginx-controller edited
    
    
    

    再次訪問

    [root@centos7-nginx conf.d]# curl http://ng.5179.top -I
    HTTP/1.1 200 OK
    Server: nginx/1.16.1
    Date: Sat, 20 Jun 2020 13:28:05 GMT
    Content-Type: text/html
    Content-Length: 612
    Connection: keep-alive
    Vary: Accept-Encoding
    Last-Modified: Tue, 26 May 2020 15:00:20 GMT
    ETag: "5ecd2f04-264"
    Accept-Ranges: bytes
    # 查看本機網卡 IP
    [root@centos7-nginx conf.d]# ip addr show ens33
    2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether 00:0c:29:38:d4:e3 brd ff:ff:ff:ff:ff:ff
        inet 10.10.10.127/24 brd 10.10.10.255 scope global ens33
           valid_lft forever preferred_lft forever
        inet6 fe80::20c:29ff:fe38:d4e3/64 scope link
           valid_lft forever preferred_lft forever
    

    nginx的日誌($http_x_forwarded_for)已經記錄了客戶端的真實IP

    10.244.4.17 - - [20/Jun/2020:13:28:05 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.29.0" "10.10.10.127"
    

    3.16.4 運行多個 ingress

    注意:如果要運行多個 ingress ,一個服務於公共流量,一個服務於“內部”流量。為此,必須將選項–ingress-class更改為控制器定義內群集的唯一值。

    spec:
      template:
         spec:
           containers:
             - name: nginx-ingress-internal-controller
               args:
                 - /nginx-ingress-controller
                 - '--election-id=ingress-controller-leader-internal'
                 - '--ingress-class=nginx-internal'
                 - '--configmap=ingress/nginx-ingress-internal-controller'
    

    需要創建單獨的ConfigmapServiceDeployment的文件,其他與默認安裝的 ingress 共用即可

    ---
    # Source: ingress-nginx/templates/controller-configmap.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      labels:
        helm.sh/chart: ingress-nginx-2.4.0
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/version: 0.33.0
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/component: controller
      name: ingress-nginx-internal-controller   # 修改名字
      namespace: ingress-nginx
    data:
    ---
    # Source: ingress-nginx/templates/controller-service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        helm.sh/chart: ingress-nginx-2.4.0
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/version: 0.33.0
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/component: controller
      name: ingress-nginx-internal-controller   # 修改名字
      namespace: ingress-nginx
    spec:
      type: NodePort
      ports:
        - name: http
          port: 80
          protocol: TCP
          targetPort: http
        - name: https
          port: 443
          protocol: TCP
          targetPort: https
      selector:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    ---
    # Source: ingress-nginx/templates/controller-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        helm.sh/chart: ingress-nginx-2.4.0
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/version: 0.33.0
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/component: controller
      name: ingress-nginx-internal-controller      # 修改名字
      namespace: ingress-nginx
    spec:
      selector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx
          app.kubernetes.io/instance: ingress-nginx
          app.kubernetes.io/component: controller
      revisionHistoryLimit: 10
      minReadySeconds: 0
      template:
        metadata:
          labels:
            app.kubernetes.io/name: ingress-nginx
            app.kubernetes.io/instance: ingress-nginx
            app.kubernetes.io/component: controller
        spec:
          dnsPolicy: ClusterFirst
          containers:
            - name: controller
              #image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0
              image: registry.cn-beijing.aliyuncs.com/liyongjian5179/nginx-ingress-controller:0.33.0
              imagePullPolicy: IfNotPresent
              lifecycle:
                preStop:
                  exec:
                    command:
                      - /wait-shutdown
              args:
                - /nginx-ingress-controller
                - --election-id=ingress-controller-leader-internal    
                - --ingress-class=nginx-internal
                - --configmap=ingress-nginx/ingress-nginx-internal-controller
                - --validating-webhook=:8443
                - --validating-webhook-certificate=/usr/local/certificates/cert
                - --validating-webhook-key=/usr/local/certificates/key
              securityContext:
                capabilities:
                  drop:
                    - ALL
                  add:
                    - NET_BIND_SERVICE
                runAsUser: 101
                allowPrivilegeEscalation: true
              env:
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
              livenessProbe:
                httpGet:
                  path: /healthz
                  port: 10254
                  scheme: HTTP
                initialDelaySeconds: 10
                periodSeconds: 10
                timeoutSeconds: 1
                successThreshold: 1
                failureThreshold: 3
              readinessProbe:
                httpGet:
                  path: /healthz
                  port: 10254
                  scheme: HTTP
                initialDelaySeconds: 10
                periodSeconds: 10
                timeoutSeconds: 1
                successThreshold: 1
                failureThreshold: 3
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
                - name: https
                  containerPort: 443
                  protocol: TCP
                - name: webhook
                  containerPort: 8443
                  protocol: TCP
              volumeMounts:
                - name: webhook-cert
                  mountPath: /usr/local/certificates/
                  readOnly: true
              resources:
                requests:
                  cpu: 100m
                  memory: 90Mi
          serviceAccountName: ingress-nginx
          terminationGracePeriodSeconds: 300
          volumes:
            - name: webhook-cert
              secret:
                secretName: ingress-nginx-admission
    

    然後執行即可,然後還需要在原配置文件中的 Role中添加一行信息

    # Source: ingress-nginx/templates/controller-role.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
    ...
      name: ingress-nginx
      namespace: ingress-nginx
    rules:
    ...
      - apiGroups:
          - ''
        resources:
          - configmaps
        resourceNames:
          # Defaults to "<election-id>-<ingress-class>"
          # Here: "<ingress-controller-leader>-<nginx>"
          # This has to be adapted if you change either parameter
          # when launching the nginx-ingress-controller.
          - ingress-controller-leader-nginx
          - ingress-controller-leader-internal-nginx-internal #此處要增加一行,如果不加,會出現下面的報錯
        verbs:
          - get
          - update
    

    上述所說,如果不添加,ingress-controller 的 nginx 會出現這個報錯信息

    E0621 08:25:07.531202       6 leaderelection.go:356] Failed to update lock: configmaps "ingress-controller-leader-internal-nginx-internal" is forbidden: User "system:serviceaccount:ingress-nginx:ingress-nginx" cannot update resource "configmaps" in API group "" in the namespace "ingress-nginx"
    

    然後修改 ingress 文件

    
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: nginx
      annotations:
        # 注意這裏要設置為您前面配置的 INGRESS_CLASS,比如:nginx-internal
        kubernetes.io/ingress.class: "<YOUR_INGRESS_CLASS>"
    

    示例:

    [root@centos7-nginx yaml]# kubectl apply -f ingress-internal.yaml
    ingress.extensions/ingress-nginx-dns-internal created
    [root@centos7-nginx yaml]# cat ingress-internal.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-nginx-dns-internal
    #  namespace: default
      annotations:
        kubernetes.io/ingress.class: "nginx-internal"
    spec:
      rules:
      - host: ng-inter.5179.top
        http:
          paths:
          - path: /
            backend:
              serviceName: nginx-dns
              servicePort: 80
    [root@centos7-nginx yaml]# kubectl get ingress
    NAME                         CLASS    HOSTS               ADDRESS                     PORTS   AGE
    ingress-nginx-dns            <none>   ng.5179.top         10.10.10.131                80      47m
    ingress-nginx-dns-internal   <none>   ng-inter.5179.top   10.10.10.131,10.10.10.132   80      32s
    

    在 nginx 的配置文件中增加

    [root@centos7-nginx yaml]# cat /etc/nginx/conf.d/ng.conf
    upstream nginx-dns{
            ip_hash;
            server 10.10.10.131:31511;
            server 10.10.10.132:31511;
       }
    upstream nginx-dns-inter{
            ip_hash;
            server 10.10.10.131:40377;
            server 10.10.10.132:40377;
       }
    
    server {
        listen       80;
        server_name  ng.5179.top;
    
        #access_log  logs/host.access.log  main;
    
        location / {
            root   html;
            proxy_pass http://nginx-dns;
    	proxy_set_header Host $host;
    	proxy_set_header X-Forwarded-For $remote_addr;
            index  index.html index.htm;
        }
    }
    server {
        listen       80;
        server_name  ng-inter.5179.top;
    
        #access_log  logs/host.access.log  main;
    
        location / {
            root   html;
            proxy_pass http://nginx-dns-inter/;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
            index  index.html index.htm;
        }
    }
    

    重啟后添加本地解析,然後訪問即可

    [root@centos7-nginx yaml]# curl http://ng-inter.5179.top -I
    HTTP/1.1 200 OK
    Server: nginx/1.16.1
    Date: Sun, 21 Jun 2020 09:07:12 GMT
    Content-Type: text/html
    Content-Length: 612
    Connection: keep-alive
    Vary: Accept-Encoding
    Last-Modified: Tue, 26 May 2020 15:00:20 GMT
    ETag: "5ecd2f04-264"
    Accept-Ranges: bytes
    
    [root@centos7-nginx yaml]# curl http://ng.5179.top -I
    HTTP/1.1 200 OK
    Server: nginx/1.16.1
    Date: Sun, 21 Jun 2020 09:07:17 GMT
    Content-Type: text/html
    Content-Length: 612
    Connection: keep-alive
    Vary: Accept-Encoding
    Last-Modified: Tue, 26 May 2020 15:00:20 GMT
    ETag: "5ecd2f04-264"
    Accept-Ranges: bytes
    

    3.17 安裝 prometheus-operator

    3.17.1 下載安裝

    使用 prometheus-operator 進行安裝.

    地址如下https://github.com/coreos/kube-prometheus

    根據 Readme.md 進行版本的選擇,本次 k8s 安裝的是 1.18 ,所以 prometheus 選的分支為 release-0.5

    git clone https://github.com/coreos/kube-prometheus.git -b release-0.5
    cd kube-prometheus
    # Create the namespace and CRDs, and then wait for them to be availble before creating the remaining resources
    kubectl create -f manifests/setup
    until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done
    kubectl create -f manifests/
    

    為了遠程訪問方便,創建了 ingress

    [root@centos7-a ingress]# cat ingress-grafana.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-grafana
      namespace: monitoring
      annotations:
        kubernetes.io/ingress.class: "nginx"
    spec:
      rules:
      - host: grafana.5179.top
        http:
          paths:
          - path: /
            backend:
              serviceName: grafana
              servicePort: 3000
              
    [root@centos7-a ingress]# cat ingress-prometheus.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-prometheus
      namespace: monitoring
      annotations:
        kubernetes.io/ingress.class: "nginx"
    spec:
      rules:
      - host: prometheus.5179.top
        http:
          paths:
          - path: /
            backend:
              serviceName: prometheus-k8s
              servicePort: 9090
    
    [root@centos7-a ingress]# cat ingress-alertmanager.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-alertmanager
      namespace: monitoring
      annotations:
        kubernetes.io/ingress.class: "nginx"
    spec:
      rules:
      - host: alertmanager.5179.top
        http:
          paths:
          - path: /
            backend:
              serviceName: alertmanager-main
              servicePort: 9093
    

    查看 ingress

    [root@centos7-a ingress]# kubectl get ingress -A
    NAMESPACE    NAME                   CLASS    HOSTS                   ADDRESS         PORTS   AGE
    monitoring   ingress-alertmanager   <none>   alertmanager.5179.top   10.10.10.129   80      3m6s
    monitoring   ingress-grafana        <none>   grafana.5179.top        10.10.10.129   80      3m6s
    monitoring   ingress-prometheus     <none>   prometheus.5179.top     10.10.10.129   80      3m6s
    

    3.17.2 遇到的坑

    1) kube-schedulerkube-controller-manager 的target 為 0/0

    二進制部署k8s管理組件和新版本 kubeadm 部署的都會發現在prometheus status 下的 target 頁面上發現kube-controller-managerkube-scheduler的 target 為0/0。因為 serviceMonitor是根據 label 去選取 svc的,可以查看對應的serviceMonitor是選取的ns範圍是kube-system

    解決辦法:

    查看endpoint 兩者的endpoint為 none

    [root@centos7-a kube-prometheus]# kubectl get endpoints -n kube-system
    NAME                      ENDPOINTS                                                               AGE
    kube-controller-manager   <none>                                                                  7m35s
    kube-dns                  10.244.43.2:53,10.244.62.2:53,10.244.43.2:9153 + 3 more...              4m10s
    kube-scheduler            <none>                                                                  7m31s
    kubelet                   10.10.10.129:4194,10.10.10.132:4194,10.10.10.128:4194 + 12 more...   22s
    

    查看兩者的端口

    [root@centos7-a kube-prometheus]# ss -tnlp| grep scheduler
    LISTEN     0      32768       :::10251                   :::*                   users:(("kube-scheduler",pid=60128,fd=5))
    LISTEN     0      32768       :::10259                   :::*                   users:(("kube-scheduler",pid=60128,fd=7))
    [root@centos7-a kube-prometheus]# ss -tnlp| grep contro
    LISTEN     0      32768       :::10252                   :::*                   users:(("kube-controller",pid=59695,fd=6))
    LISTEN     0      32768       :::10257                   :::*                   users:(("kube-controller",pid=59695,fd=7))
    

    創建文件並執行

    [root@centos7-a yaml]# cat schedulerandcontroller-ep-svc.yaml
    # cat kube-scheduer-service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        k8s-app: kube-scheduler
      name: kube-scheduler
      namespace: kube-system
    spec:
      clusterIP: None
      ports:
      - name: https-metrics
        port: 10259
        protocol: TCP
        targetPort: 10259
      - name: http-metrics
        port: 10251
        protocol: TCP
        targetPort: 10251
      type: ClusterIP
    
    ---
    # cat kube-controller-manager-service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        k8s-app: kube-controller-manager
      name: kube-controller-manager
      namespace: kube-system
    spec:
      clusterIP: None
      ports:
      - name: https-metrics
        port: 10257
        protocol: TCP
        targetPort: 10257
      - name: http-metrics
        port: 10252
        protocol: TCP
        targetPort: 10252
      type: ClusterIP
    
    ---
    # cat ep-controller-manager.yaml
    apiVersion: v1
    kind: Endpoints
    metadata:
      labels:
        k8s-app: kube-controller-manager
      name: kube-controller-manager
      namespace: kube-system
      annotations:
        prometheus.io/scrape: 'true'
    subsets:
    - addresses:
      - ip: 10.10.10.128
        targetRef:
          kind: Node
          name: 10.10.10.128
      - ip: 10.10.10.129
        targetRef:
          kind: Node
          name: 10.10.10.129
      - ip: 10.10.10.130
        targetRef:
          kind: Node
          name: 10.10.10.130
      ports:
      - name: http-metrics
        port: 10252
        protocol: TCP
      - name: https-metrics
        port: 10257
        protocol: TCP
    ---
    # cat ep-scheduler.yaml
    apiVersion: v1
    kind: Endpoints
    metadata:
      labels:
        k8s-app: kube-scheduler
      name: kube-scheduler
      namespace: kube-system
      annotations:
        prometheus.io/scrape: 'true'
    subsets:
    - addresses:
      - ip: 10.10.10.128
        targetRef:
          kind: Node
          name: 10.10.10.128
      - ip: 10.10.10.129
        targetRef:
          kind: Node
          name: 10.10.10.129
      - ip: 10.10.10.130
        targetRef:
          kind: Node
          name: 10.10.10.130
      ports:
      - name: http-metrics
        port: 10251
        protocol: TCP
      - name: https-metrics
        port: 10259
        protocol: TCP
    
    2) node-exporter的 target 显示(3/5)

    有兩個有問題的 Node,同時查看 kubectl top node 也發現問題,節點數據看不到

    [root@centos7--a kube-prometheus]# kubectl top nodes
    NAME            CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
    10.10.10.129   110m         5%     1360Mi          36%
    10.10.10.128   114m         5%     1569Mi          42%
    10.10.10.130   101m         5%     1342Mi          36%
    10.10.10.132   <unknown>                           <unknown>               <unknown>               <unknown>
    10.10.10.131    <unknown>                           <unknown>               <unknown>               <unknown>
    

    解決辦法:

    查看問題節點所對應的 Pod

    [root@centos7--a kube-prometheus]# kubectl get pods  -o custom-columns='NAME:metadata.name,NODE:spec.nodeName'  -n monitoring |grep node
    node-exporter-2fqt5                    10.10.10.130
    node-exporter-fxqxb                    10.10.10.129
    node-exporter-pbq28                    10.10.10.132
    node-exporter-tvw5j                    10.10.10.128
    node-exporter-znp6k                    10.10.10.131
    

    查看日誌

    [root@centos7--a kube-prometheus]# kubectl logs -f node-exporter-znp6k -n monitoring -c kube-rbac-proxy
    I0627 02:58:01.947861   53400 main.go:213] Generating self signed cert as no cert is provided
    I0627 02:58:44.246733   53400 main.go:243] Starting TCP socket on [10.10.10.131]:9100
    I0627 02:58:44.346251   53400 main.go:250] Listening securely on [10.10.10.131]:9100
    E0627 02:59:27.246742   53400 webhook.go:106] Failed to make webhook authenticator request: Post https://10.96.0.1:443/apis/authentication.k8s.io/v1beta1/tokenreviews: dial tcp 10.96.0.1:443: i/o timeout
    E0627 02:59:27.247585   53400 proxy.go:67] Unable to authenticate the request due to an error: Post https://10.96.0.1:443/apis/authentication.k8s.io/v1beta1/tokenreviews: dial tcp 10.96.0.1:443: i/o timeout
    E0627 02:59:42.160199   53400 webhook.go:106] Failed to make webhook authenticator request: Post https://10.96.0.1:443/apis/authentication.k8s.io/v1beta1/tokenreviews: dial tcp 10.96.0.1:443: i/o timeout
    

    一直在報連接 10.96.0.1:443 超時,像是 kubernetes 在回包的時候,無法建立連接,

    兩種解決辦法:

    1. 在問題節點加入一條防火牆命令(不推薦)
    iptables -t nat -I POSTROUTING -s  10.96.0.0/12 -j MASQUERADE
    
    1. 修改 kube-proxy 配置文件,改成正確的 cluster-CIDR (推薦)

    再次查看已經正常了

    [root@centos7--a kube-prometheus]# kubectl top nodes
    NAME            CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
    10.10.10.129   109m         5%     1362Mi          36%
    10.10.10.132   65m          3%     1118Mi          30%
    10.10.10.128   175m         8%     1581Mi          42%
    10.10.10.130   118m         5%     1344Mi          36%
    10.10.10.131    60m          3%     829Mi           22%
    

    實際排查后發現,是 kube-proxy 的 clusterCIDR寫成了 service 的網段。

    clusterCIDR: kube-proxy 根據 –cluster-cidr 判斷集群內部和外部流量,指定 –cluster-cidr 或 –masquerade-all選項后 kube-proxy 才會對訪問 Service IP 的請求做 SNAT;

    3.17.3 監控 etcd

    除了 Kubernetes 集群中的一些資源對象、節點以及組件需要監控,有的時候我們可能還需要根據實際的業務需求去添加自定義的監控項,添加一個自定義監控的步驟如下:

    • 第一步建立一個 ServiceMonitor 對象,用於 Prometheus 添加監控項
    • 第二步為 ServiceMonitor 對象關聯 metrics 數據接口的一個 Service 對象
    • 第三步確保 Service 對象可以正確獲取到 metrics 數據

    對於 etcd 集群一般情況下,為了安全都會開啟 https 證書認證的方式,所以要想讓 Prometheus 訪問到 etcd 集群的監控數據,就需要提供相應的證書校驗。

    首先我們將需要使用到的證書通過 secret 對象保存到集群中去:(在 etcd 運行的節點)

    [root@centos7--a ssl]# pwd
    /opt/etcd/ssl
    [root@centos7--a ssl]# kubectl -n monitoring create secret generic etcd-certs --from-file=/opt/kubernetes/ssl/server.pem --from-file=/opt/kubernetes/ssl/server-key.pem --from-file=/opt/kubernetes/ssl/ca.pem
    secret/etcd-certs created
    

    Prometheus配置文件,將上面創建的 etcd-certs 對象配置到 prometheus 資源對象中

    [root@centos7--a manifests]# pwd
    /root/kube-prometheus/manifests
    [root@centos7--a manifests]# vim prometheus-prometheus.yaml
      replicas: 2
      secrets:
        - etcd-certs
    [root@centos7--a manifests]# kubectl apply -f prometheus-prometheus.yaml
    prometheus.monitoring.coreos.com/k8s configured
    

    進入 pod 內查看證書是否存在

    #等到pod重啟后,進入pod查看是否可以看到證書
    [root@centos7--a kube-prometheus]# kubectl exec -it prometheus-k8s-0  -n monitoring  -- sh
    Defaulting container name to prometheus.
    Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
    /prometheus $ ls /etc/prometheus/secrets/
    etcd-certs
    /prometheus $ ls /etc/prometheus/secrets/ -l
    total 0
    drwxrwsrwt    3 root     2000           140 Jun 27 04:59 etcd-certs
    /prometheus $ ls /etc/prometheus/secrets/etcd-certs/ -l
    total 0
    lrwxrwxrwx    1 root     root            13 Jun 27 04:59 ca.pem -> ..data/ca.pem
    lrwxrwxrwx    1 root     root            21 Jun 27 04:59 server-key.pem -> ..data/server-key.pem
    lrwxrwxrwx    1 root     root            17 Jun 27 04:59 server.pem -> ..data/server.pem
    

    創建 ServiceMonitor

    現在 Prometheus 訪問 etcd 集群的證書已經準備好了,接下來創建 ServiceMonitor 對象即可(prometheus-serviceMonitorEtcd.yaml)

    $ vim prometheus-serviceMonitorEtcd.yaml
    apiVersion: monitoring.coreos.com/v1
    kind: ServiceMonitor
    metadata:
      name: etcd-k8s
      namespace: monitoring
      labels:
        k8s-app: etcd-k8s
    spec:
      jobLabel: k8s-app
      endpoints:
      - port: port
        interval: 30s
        scheme: https
        tlsConfig:
          caFile: /etc/prometheus/secrets/etcd-certs/ca.pem
          certFile: /etc/prometheus/secrets/etcd-certs/server.pem
          keyFile: /etc/prometheus/secrets/etcd-certs/server-key.pem
          insecureSkipVerify: true
      selector:
        matchLabels:
          k8s-app: etcd
      namespaceSelector:
        matchNames:
        - kube-system
    
    $ kubectl apply -f prometheus-serviceMonitorEtcd.yaml    
    
    

    上面我們在 monitoring 命名空間下面創建了名為 etcd-k8s 的 ServiceMonitor
    對象,匹配 kube-system 這個命名空間下面的具有 k8s-app=etcd 這個 label 標籤的
    Service,jobLabel 表示用於檢索 job 任務名稱的標籤,和前面不太一樣的地方是 endpoints 屬性的寫法,配置上訪問
    etcd 的相關證書,endpoints 屬性下面可以配置很多抓取的參數,比如 relabel、proxyUrl,tlsConfig
    表示用於配置抓取監控數據端點的 tls 認證,由於證書 serverName 和 etcd 中籤發的可能不匹配,所以加上了
    insecureSkipVerify=true

    創建 Service

    ServiceMonitor 創建完成了,但是現在還沒有關聯的對應的 Service 對象,所以需要我們去手動創建一個 Service 對象(prometheus-etcdService.yaml):

    $ vim prometheus-etcdService.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: etcd-k8s
      namespace: kube-system
      labels:
        k8s-app: etcd
    spec:
      type: ClusterIP
      clusterIP: None
      ports:
      - name: port
        port: 2379
        protocol: TCP
    
    ---
    apiVersion: v1
    kind: Endpoints
    metadata:
      name: etcd-k8s
      namespace: kube-system
      labels:
        k8s-app: etcd
    subsets:
    - addresses:
      - ip: 10.10.10.128
      - ip: 10.10.10.129
      - ip: 10.10.10.130    
      ports:
      - name: port
        port: 2379
        protocol: TCP
    
    $ kubectl apply -f prometheus-etcdService.yaml
    
    

    等待一會兒就可以看到 target 已經包含了

    數據採集到后,可以在 grafana 中導入編號為3070的 dashboard,獲取到 etcd 的監控圖表。

    3.18 為遠端 kubectl 準備管理員證書

    [root@centos7-nginx scripts]# cd ssl/
    [root@centos7-nginx ssl]# cat admin.kubeconfig > ~/.kube/config
    [root@centos7-nginx ssl]# vim ~/.kube/config
    [root@centos7-nginx ssl]# kubectl get cs
    NAME                 STATUS    MESSAGE             ERROR
    scheduler            Healthy   ok
    controller-manager   Healthy   ok
    etcd-2               Healthy   {"health":"true"}
    etcd-0               Healthy   {"health":"true"}
    etcd-1               Healthy   {"health":"true"}
    

    3.19 給節點打上角色標籤

    默認裝完在角色這列显示 <none>

    [root@centos7-nginx ~]# kubectl get nodes
    NAME        STATUS   ROLES    AGE   VERSION
    centos7-a   Ready    <none>   32h   v1.18.3
    centos7-b   Ready    <none>   32h   v1.18.3
    centos7-c   Ready    <none>   32h   v1.18.3
    centos7-d   Ready    <none>   21m   v1.18.3
    centos7-e   Ready    <none>   20m   v1.18.3
    

    執行如下命令即可:

    [root@centos7-nginx ~]# kubectl label nodes centos7-a node-role.kubernetes.io/master=
    node/centos7-a labeled
    [root@centos7-nginx ~]# kubectl label nodes centos7-b node-role.kubernetes.io/master=
    node/centos7-b labeled
    [root@centos7-nginx ~]# kubectl label nodes centos7-c node-role.kubernetes.io/master=
    node/centos7-c labeled
    [root@centos7-nginx ~]# kubectl label nodes centos7-d node-role.kubernetes.io/node=
    node/centos7-d labeled
    [root@centos7-nginx ~]# kubectl label nodes centos7-e node-role.kubernetes.io/node=
    node/centos7-e labeled
    

    再次查看

    [root@centos7-nginx ~]# kubectl get nodes
    NAME        STATUS   ROLES    AGE   VERSION
    centos7-a   Ready    master   32h   v1.18.3
    centos7-b   Ready    master   32h   v1.18.3
    centos7-c   Ready    master   32h   v1.18.3
    centos7-d   Ready    node     23m   v1.18.3
    centos7-e   Ready    node     22m   v1.18.3
    

    3.20 測試在節點上執行維護工作

    驅逐並使節點不可調度

    [root@centos7-nginx scripts]# kubectl drain centos7-d --ignore-daemonsets=true --delete-local-data=true --force=true
    node/centos7-d cordoned
    evicting pod kube-system/coredns-6fdfb45d56-pvnzt
    pod/coredns-6fdfb45d56-pvnzt evicted
    node/centos7-d evicted
    [root@centos7-nginx scripts]# kubectl get nodes
    NAME        STATUS                     ROLES    AGE   VERSION
    centos7-a   Ready                      master   47h   v1.18.3
    centos7-b   Ready                      master   47h   v1.18.3
    centos7-c   Ready                      master   47h   v1.18.3
    centos7-d   Ready,SchedulingDisabled   node     15h   v1.18.3
    centos7-e   Ready                      node     15h   v1.18.3
    

    重新使節點可調度:

    [root@centos7-nginx scripts]# kubectl uncordon centos7-d
    node/centos7-d uncordoned
    [root@centos7-nginx scripts]# kubectl get nodes
    NAME        STATUS   ROLES    AGE   VERSION
    centos7-a   Ready    master   47h   v1.18.3
    centos7-b   Ready    master   47h   v1.18.3
    centos7-c   Ready    master   47h   v1.18.3
    centos7-d   Ready    node     15h   v1.18.3
    centos7-e   Ready    node     15h   v1.18.3
    

    3.21 使 master 節點不運行pod

    master節點最好是不要作為node使用,也不推薦做node節點,

    在該集群中需要打下標籤node-role.kubernetes.io/master=:NoSchedule才能實現

    [root@centos7-nginx scripts]# kubectl taint nodes centos7-a  node-role.kubernetes.io/master=:NoSchedule
    node/centos7-a tainted
    [root@centos7-nginx scripts]# kubectl taint nodes centos7-b  node-role.kubernetes.io/master=:NoSchedule
    node/centos7-b tainted
    [root@centos7-nginx scripts]# kubectl taint nodes centos7-c  node-role.kubernetes.io/master=:NoSchedule
    node/centos7-c tainted
    

    部署一個 nginx 的 deploy 進行驗證

    # 創建一個 deployment
    [root@centos7-nginx scripts]# kubectl create deployment nginx-dns --image=nginx
    deployment.apps/nginx-dns created
    # 修改副本數為 3
    [root@centos7-nginx scripts]# kubectl patch deployment nginx-dns -p '{"spec":{"replicas":3}}'
    deployment.apps/nginx-dns patched
    # 查看位置分佈
    [root@centos7-nginx scripts]# kubectl get pods -o wide
    NAME                         READY   STATUS              RESTARTS   AGE    IP             NODE        NOMINATED NODE   READINESS GATES
    busybox                      1/1     Running             0          14m    10.244.3.113   centos7-d   <none>           <none>
    nginx-dns-5c6b6b99df-6k4qv   1/1     Running             0          2m8s   10.244.3.116   centos7-d   <none>           <none>
    nginx-dns-5c6b6b99df-88lcr   0/1     ContainerCreating   0          6s     <none>         centos7-d   <none>           <none>
    nginx-dns-5c6b6b99df-c2nnc   0/1     ContainerCreating   0          6s     <none>         centos7-e   <none>           <none>
    

    如果需要把master當node:

    kubectl taint nodes centos7-a node-role.kubernetes.io/master-
    

    4 FAQ

    4.1 解決無法查詢pods日誌問題

    [root@centos7-b cfg]# kubectl exec -it nginx -- bash
    error: unable to upgrade connection: Forbidden (user=kubernetes, verb=create, resource=nodes, subresource=proxy)
    [root@centos7-b cfg]# kubectl logs -f nginx
    Error from server (Forbidden): Forbidden (user=kubernetes, verb=get, resource=nodes, subresource=proxy) ( pods/log nginx)
    
    $ vim ~/yaml/apiserver-to-kubelet-rbac.yml
    
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: kubelet-api-admin
    subjects:
    - kind: User
      name: kubernetes
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: ClusterRole
      name: system:kubelet-api-admin
      apiGroup: rbac.authorization.k8s.io
    
    # 應用
    $ kubectl apply -f ~/yaml/apiserver-to-kubelet-rbac.yml
    
    [root@centos7-a ~]# kubectl logs -f nginx
    /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
    10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
    /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    /docker-entrypoint.sh: Configuration complete; ready for start up
    10.244.2.1 - - [17/Jun/2020:02:45:59 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
    10.244.2.1 - - [17/Jun/2020:02:46:09 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
    10.244.2.1 - - [17/Jun/2020:02:46:12 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
    10.244.2.1 - - [17/Jun/2020:02:46:13 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
    

    5 參考

    Prometheus Operator 監控 etcd 集群: https://www.qikqiak.com/post/prometheus-operator-monitor-etcd/

    Kubernetes v1.18.2 二進制高可用部署: https://www.yp14.cn/2020/05/19/Kubernetes-v1-18-2-二進制高可用部署/

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

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※幫你省時又省力,新北清潔一流服務好口碑

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

  • 空污太嚴重 印度最高法院諭令禁焚農作物殘梗

    摘錄自2019年11月5日中央社外電報導

    有鑑於空汙嚴重,印度最高法院今天(5 日)下令首都新德里周邊地區全面停止焚燒農作物殘梗。法院指出,新德里居民持續因為空汙喪失寶貴生命歲月,「這根本不該發生在文明國家」。倘若禁令未貫徹執行,整個行政和警察體系都會被追究責任。

    儘管焚燒農作物殘梗已屬非法,許多經濟困窘農民表示,他們別無其他選擇。印度政府曾試圖杜絕農民這個習慣,例如補助購買排除焚燒農作物殘梗必要性的設備,但效果不彰。此外,農民也可將殘梗轉化為生質能源團塊等較具實用價值物品,但成本偏高。

    氣候政策研究員、「印度大霧霾」」(The Great Smog of India,暫譯)一書作者辛赫(Siddharth Singh)表示,政府應採取作為,賦予農作殘餘物價值,例如出資採購並以乾淨方式焚燒。

    不過,印度智庫「政策研究中心」(Centre for Policy Research)研究員哈里希(Santosh Harish)認為,焚燒農作物對空汙的「貢獻」被高估了。

    哈里希指出:「過去幾週,殘梗焚燒確實(對空汙形成)扮演重要角色,占比約40%,但我認為新德里市長高估農民能耐,對新德里境內空汙來源卻輕描淡寫…若新德里全面推卸責任,他們就搞錯重點了。」

    根據哈里希的說法,新德里空汙危機的主要成因是交通運輸工具、工業設施和發電廠所排放廢氣,另外還有施工粉塵。此外,嚴重空汙範圍已超越首都區,涵蓋印度北部多數地區。

    根據環保組織「綠色和平」(Greenpeace)3月發布的報告,全球汙染程度最高的30座城市中,印度就包辦其中22座。新德里市長克里瓦爾(Arvind Kejriwal)上週表示,新德里已成「毒氣室」,罪魁禍首就是「焚燒農作物產生的煙霧」。

    新德里的空汙情形在3日急遽惡化,細懸浮微粒(PM2.5)濃度達近3年新高,接近每立方公尺1000微克,而世界衛生組織(WHO)建議的每日最高安全上限為每立方公尺25微克。空汙嚴重甚至導致飛機航班被迫延誤或改變起降地點,各級學校也關閉;經濟條件佳的居民多選擇待在有空氣清淨機運轉的住家裡。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

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

    ※推薦評價好的iphone維修中心

  • 在 Spring Boot 中使用 HikariCP 連接池

    在 Spring Boot 中使用 HikariCP 連接池

    上次幫小王解決了如何在 Spring Boot 中使用 JDBC 連接 MySQL 后,我就一直在等,等他問我第三個問題,比如說如何在 Spring Boot 中使用 HikariCP 連接池。但我等了四天也沒有等到任何音訊,似乎他從我的世界里消失了,而我卻仍然沉醉在他拍我馬屁的美妙感覺里。

    突然感覺,沒有小王的日子里,好空虛。怎麼辦呢?想來想去還是寫文章度日吧,积極創作的過程中,也許能夠擺脫對小王的苦苦思念。寫什麼好呢?

    想來想去,就寫如何在 Spring Boot 中使用 HikariCP 連接池吧。畢竟實戰項目當中,肯定不能使用 JDBC,連接池是必須的。而 HikariCP 據說非常的快,快到 Spring Boot 2 默認的數據庫連接池也從 Tomcat 切換到了 HikariCP(喜新厭舊的臭毛病能不能改改)。

    HikariCP 的 GitHub 地址如下:

    https://github.com/brettwooldridge/HikariCP

    目前星標 12K,被使用次數更是達到了 43.1K。再來看看它的自我介紹。

    牛逼的不能行啊,原來 Hikari 來源於日語,“光”的意思,這意味着快得像光速一樣嗎?講真,看簡介的感覺就好像在和我的女神“湯唯”握手一樣刺激和震撼。

    既然 Spring Boot 2 已經默認使用了 HikariCP,那麼使用起來也相當的輕鬆愜意,只需要簡單幾個步驟。

    01、初始化 MySQL 數據庫

    既然要連接 MySQL,那麼就需要先在電腦上安裝 MySQL 服務(本文暫且跳過),並且創建數據庫和表。

    CREATE DATABASE `springbootdemo`;
    DROP TABLE IF EXISTS `mysql_datasource`;
    CREATE TABLE `mysql_datasource` (
      `id` varchar(64NOT NULL,
      PRIMARY KEY (`id`)
    ENGINE=InnoDB DEFAULT CHARSET=utf8;

    02、使用 Spring Initlallzr 創建 Spring Boot 項目

    創建一個 Spring Boot 項目非常簡單,通過 Spring Initlallzr(https://start.spring.io/)就可以了。

    勾選 Web、JDBC、MySQL Driver 等三個依賴。

    1)Web 表明該項目是一個 Web 項目,便於我們直接通過 URL 來實操。

    3)MySQL Driver:連接 MySQL 服務器的驅動器。

    5)JDBC:Spring Boot 2 默認使用了 HikariCP,所以 HikariCP 會默認在 spring-boot-starter-jdbc 中附加依賴,因此不需要主動添加 HikariCP 的依賴。

    PS:怎麼證明這一點呢?項目導入成功后,在 pom.xml 文件中,按住鼠標左鍵 + Ctrl 鍵訪問 spring-boot-starter-jdbc 依賴節點,可在 spring-boot-starter-jdbc.pom 文件中查看到 HikariCP 的依賴信息。

    選項選擇完后,就可以點擊【Generate】按鈕生成一個初始化的 Spring Boot 項目了。生成的是一個壓縮包,導入到 IDE 的時候需要先解壓。

    03、編輯 application.properties 文件

    項目導入成功后,等待 Maven 下載依賴,完成后編輯 application.properties 文件,配置 MySQL 數據源信息。

    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/springbootdemo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    spring.datasource.username=root
    spring.datasource.password=123456

    是不是有一種似曾相識的感覺(和[上一篇]()中的數據源配置一模一樣)?為什麼呢?答案已經告訴過大家了——默認、默認、默認,重要的事情說三遍,Spring Boot 2 默認使用了 HikariCP 連接池。

    04、編輯 Spring Boot 項目

    為了便於我們查看 HikariCP 的連接信息,我們對 SpringBootMysqlApplication 類進行編輯,增加以下內容。

    @SpringBootApplication
    public class HikariCpDemoApplication implements CommandLineRunner {
        @Autowired
        private DataSource dataSource;

        public static void main(String[] args) {
            SpringApplication.run(HikariCpDemoApplication.class, args);
        }

        @Override
        public void run(String... args) throws Exception {
            Connection conn = dataSource.getConnection();
            conn.close();
        }
    }

    HikariCpDemoApplication 實現了 CommandLineRunner 接口,該接口允許我們在項目啟動的時候加載一些數據或者做一些事情,比如說我們嘗試通過 DataSource 對象與數據源建立連接,這樣就可以在日誌信息中看到 HikariCP 的連接信息。CommandLineRunner 接口有一個方法需要實現,就是我們看到的 run() 方法。

    通過 debug 的方式,我們可以看到,在項目運行的過程中,dataSource 這個 Bean 的類型為 HikariDataSource。

    05、運行 Spring Boot 項目

    接下來,我們直接運行 HikariCpDemoApplication 類,這樣一個 Spring Boot 項目就啟動成功了。

    HikariDataSource 對象的連接信息會被打印出來。也就是說,HikariCP 連接池的配置啟用了。快給自己點個贊。

    06、為什麼 Spring Boot 2.0 選擇 HikariCP 作為默認數據庫連接池

    有幾種基準測試結果可用來比較HikariCP和其他連接池框架(例如c3p0dbcp2tomcatvibur)的性能。例如,HikariCP團隊發布了以下基準(可在此處獲得原始結果):

    HikariCP 團隊為了證明自己性能最佳,特意找了幾個背景對比了下。不幸充當背景的有 c3p0、dbcp2、tomcat 等傳統的連接池。

    從上圖中,我們能感受出背景的尷尬,HikariCP 鶴立雞群了。HikariCP 製作以如此優秀,原因大致有下面這些:

    1)字節碼級別上的優化:要求編譯后的字節碼最少,這樣 CPU 緩存就可以加載更多的程序代碼。

    HikariCP 優化前的代碼片段:

    public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
    {
        return PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
    }

    HikariCP 優化后的代碼片段:

    public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
    {
        return ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
    }

    以上兩段代碼的差別只有一處,就是 ProxyFactory 替代了 PROXY_FACTORY,這個改動后的字節碼比優化前減少了 3 行指令。具體的分析參照 HikariCP 的 Wiki 文檔。

    2)使用自定義的列表(FastStatementList)代替 ArrayList,可以避免 get() 的時候進行範圍檢查,remove() 的時候從頭到尾的掃描。

    07、鳴謝

    好了,各位讀者朋友們,答應小王的文章終於寫完了。能看到這裏的都是最優秀的程序員,升職加薪就是你了。如果覺得不過癮,還想看到更多,可以 star 二哥的 GitHub【itwanger.github.io】,本文已收錄。

    PS:本文配套的源碼已上傳至 GitHub 【SpringBootDemo.SpringBootMysql】。

    原創不易,如果覺得有點用的話,請不要吝嗇你手中點贊的權力;如果想要第一時間看到二哥更新的文章,請掃描下方的二維碼,關注沉默王二公眾號。我們下篇文章見!

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

    【其他文章推薦】

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

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

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

  • STM32內存受限情況下攝像頭驅動方式與圖像裁剪的選擇

    STM32內存受限情況下攝像頭驅動方式與圖像裁剪的選擇

    1、STM32圖像接收接口

    使用stm32芯片,128kB RAM,512kB Rom,資源有限,接攝像頭採集圖像,這種情況下,內存利用制約程序設計。

    STM32使用DCMI接口讀取攝像頭,協議如下。行同步信號指示了一行數據完成,場同步信號指示了一幀圖像傳輸完成。所以出現了兩種典型的數據接收方式,按照行信號一行一行處理,按照場信號一次接收一副圖像。

     

    2、按行讀取

    以網絡上流行的野火的demo為例,使用行中斷,用DMA來讀取一行數據。

    //記錄傳輸了多少行
    static uint16_t line_num =0;
    //DMA傳輸完成中斷服務函數
    void DMA2_Stream1_IRQHandler(void)
    {
      if ( DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1) == SET )
      {
       /*行計數*/
      line_num++;
      if (line_num==img_height)
      {
      /*傳輸完一幀,計數複位*/
      line_num=0;
      }
      /*DMA 一行一行傳輸*/
      OV2640_DMA_Config(FSMC_LCD_ADDRESS+(lcd_width*2*(lcd_height-line_num-1)),img_width*2/4);
      DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TCIF1);
      }
    }
    
     //幀中斷服務函數,使用幀中斷重置line_num,可防止有時掉數據的時候DMA傳送行數出現偏移
    void DCMI_IRQHandler(void)
    {
      if ( DCMI_GetITStatus (DCMI_IT_FRAME) == SET )
      {
      /*傳輸完一幀,計數複位*/
      line_num=0;
      DCMI_ClearITPendingBit(DCMI_IT_FRAME);
      }
    }

    DMA中斷服務函數中主要是使用了一個靜態變量line_num來記錄已傳輸了多少行數據,每進一次DMA中斷時自加1,由於進入一次中斷就代表傳輸完一行數據,所以line_num的值等於lcd_height時(攝像頭輸出的數據行數),表示傳輸完一幀圖像,line_num複位為0,開始另一幀數據的傳輸。line_num計數完畢后利用前面定義的OV2640_DMA_Config函數配置新的一行DMA數據傳輸,它利用line_num變量計算顯存地址的行偏移,控制DCMI數據被傳送到正確的位置,每次傳輸的都是一行像素的數據量。

    當DCMI接口檢測到攝像頭傳輸的幀同步信號時,會進入DCMI_IRQHandler中斷服務函數,在這個函數中不管line_num原來的值是什麼,它都把line_num直接複位為0,這樣下次再進入DMA中斷服務函數的時候,它會開始新一幀數據的傳輸。這樣可以利用DCMI的硬件同步信號,而不只是依靠DMA自己的傳輸計數,這樣可以避免有時STM32內部DMA傳輸受到阻塞而跟不上外部攝像頭信號導致的數據錯誤。

    圖像按幀讀取比按行讀取效率更高,那麼為什麼要按行讀取呢?上面的例子是把圖像送到LCD,如果是送到內存,按幀讀取就需要芯片有很大的內存空間。以752*480的分辨率為例,需要360kB的RAM空間,遠遠超出了芯片RAM的大小。部分應用不需要攝像頭全尺寸的圖像,只需要中心區域,比如為了避免畸變影響一般只用圖像中間的部分,那麼按行讀取就有一個好處,讀到一行后,可以把不需要的丟棄,只保留中間部分的圖像像素。

    那麼問題來了?為什麼不直接配置攝像頭的屬性,來實現只讀取圖像的中間部分呢,全部讀取出來然後在arm的內存中裁剪丟棄不要的像素,第一浪費了讀取時間,第二浪費了讀取的空間。更優的做法是直接配置攝像頭sensor,使用sensor的裁剪功能輸出需要的像素區域。

     

    3、圖像裁剪–使用STM32 crop功能裁剪

    STM32F4系列的DCMI接口支持裁剪功能,對攝像頭輸出的像素點進行截取,不需要的像素部分不被DCMI傳入內存,從硬件接口一側就丟棄了。

    HAL_DCMI_EnableCrop(&hdcmi);
    HAL_DCMI_ConfigCrop(&hdcmi, CAM_ROW_OFFSET, CAM_COL_OFFSET, IMG_ROW-1, IMG_COL-1);

    裁剪的本質如下所述,從接收到的數據里選擇需要的矩形區域。所以STM32 DCMI裁剪功能可以完成節約內存,只選取需要的圖像存入內存的作用。

    此方法相比於一次讀一行,然後丟棄首尾部分后把需要的區域圖像像素存入buffer后再讀下一行,避免了時序錯誤,代碼簡潔了,DCMI硬件計數丟掉不要的像素,也提高了程序可靠性、可讀性。

    成也蕭何敗也蕭何,如上面所述,STM32的crop完成了選取特定區域圖像的功能,那麼也要付出代價,它是從接收到的圖像數據里進行選擇的,這意味着那些不需要的數據依然會傳輸到MCU一側,只不過MCU的DCMI對數據進行計數是忽略了它而已,那麼問題就來了,哪些不需要的數據的傳輸會帶來什麼問題呢?

    有圖為證,下圖是使用了STM32 crop裁剪的時序圖,通道1啟動採集IO置高,frame中斷里拉低,由於使用dma傳輸,那麼被crop裁剪后dma計數的數據量變少,所以DCMI frame中斷能在行數據傳輸完成前到達,通道1高電平部分就代表一有效分辨率的幀的採集時間。通道2 曝光信號管腳,通道3是行掃描信號。其中通道1下降沿到通道3下降沿4.5ms。代表單片機已經收到crop指定尺寸的圖像,採集有效區域(crop區域)的圖像完成,但是line信號沒有結束還有很多行沒傳輸,即CMOS和DCMI接口要傳輸752*480圖像還沒完成。

     舉例說明,如果使用752*480分辨率採集圖像,你只取中間的360*360視野,有效分辨率是360*360,但是總線上的數據依然是752*480,所以幀率無法提高,多餘的數據按說就不應該傳輸出來,如何破解,問題追到這裏,STM32芯片已經無能為力了,接下來需要在CMOS一側發力了。

     

    4、圖像裁剪–配置CMOS寄存器裁剪

    下圖是MT9V034 攝像頭芯片的寄存器手冊,Reg1–4配置CMOS的行列起點和寬度高度。

    修改寄存器后,攝像頭CMOS就不再向外傳輸多餘的數據,被裁剪丟棄的數據也不會反應在接口上,所以STM32 DCMI接收到的數據都是需要保留的有效區數據,極大地減少了數據輸出,提高了傳輸效率。本人也在STM324芯片上,實現了220*220分辨率120幀的連續採集。

    下面是序圖,通道1高電平代表開始採集和一幀結束,不同於使用STM32 的crop裁剪,使用CMOS寄存器裁剪有效窗口,使得幀結束時行信號也同時結束,後續沒有任何需要傳輸的行數據。

     

    5、一幀數據一次性傳輸

    一幀數據一次全部讀入到MCU的方式,其實是最簡單的驅動編寫方式,缺點就是太占內存,但是對於沒有壓縮功能的cmos芯片來說,一般都無力實現。對部分有jpg壓縮功能的cmos芯片而言,比如OV2640可以使用這種方式,一次性讀出一幀圖像。

    __align(4) u32 jpeg_buf[jpeg_buf_size];    //JPEG buffer
    //JPEG 格式
    const u16 jpeg_img_size_tbl[][2]=
    {
        176,144,    //QCIF
        160,120,    //QQVGA
        352,288,    //CIF
        320,240,    //QVGA
        640,480,    //VGA
        800,600,    //SVGA
        1024,768,    //XGA
        1280,1024,    //SXGA
        1600,1200,    //UXGA
    }; 

    //DCMI 接收數據
    void DCMI_IRQHandler(void)
    {
      if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)// 一幀數據
      {
        jpeg_data_process();  
        DCMI_ClearITPendingBit(DCMI_IT_FRAME); 
      }
    }

     

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

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

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

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※幫你省時又省力,新北清潔一流服務好口碑

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

  • 程序員必備基礎:Git 命令全方位學習

    程序員必備基礎:Git 命令全方位學習

    前言

    掌握Git命令是每位程序員必備的基礎,之前一直是用smartGit工具,直到看到大佬們都是在用Git命令操作的,回想一下,發現有些Git命令我都忘記了,於是寫了這篇博文,複習一下~

    https://github.com/whx123/JavaHome

    公眾號:撿田螺的小男孩

    文章目錄

    • Git是什麼?
    • Git的相關理論基礎
    • 日常開發中,Git的基本常用命令
    • Git進階之分支處理
    • Git進階之處理衝突
    • Git進階之撤銷與回退
    • Git進階之標籤tag
    • Git其他一些經典命令

    Git是什麼

    在回憶Git是什麼的話,我們先來複習這幾個概念哈~

    什麼是版本控制?

    百度百科定義是醬紫的~

    版本控制是指對軟件開發過程中各種程序代碼、配置文件及說明文檔等文件變更的管理,是軟件配置管理的核心思想之一。

    那些年,我們的畢業論文,其實就是版本變更的真實寫照…腦洞一下,版本控制就是這些論文變更的管理~

    什麼是集中化的版本控制系統?

    那麼,集中化的版本控制系統又是什麼呢,說白了,就是有一個集中管理的中央服務器,保存着所有文件的修改歷史版本,而協同開發者通過客戶端連接到這台服務器,從服務器上同步更新或上傳自己的修改。

    什麼是分佈式版本控制系統?

    分佈式版本控制系統,就是遠程倉庫同步所有版本信息到本地的每個用戶。嘻嘻,這裏分三點闡述吧:

    • 用戶在本地就可以查看所有的歷史版本信息,但是偶爾要從遠程更新一下,因為可能別的用戶有文件修改提交到遠程哦。
    • 用戶即使離線也可以本地提交,push推送到遠程服務器才需要聯網。
    • 每個用戶都保存了歷史版本,所以只要有一個用戶設備沒問題,就可以恢複數據啦~

    什麼是Git?

    Git是免費、開源的分佈式版本控制系統,可以有效、高速地處理從很小到非常大的項目版本管理。

    Git的相關理論基礎

    • Git的四大工作區域
    • Git的工作流程
    • Git文件的四種狀態
    • 一張圖解釋Git的工作原理

    Git的四大工作區域

    先複習Git的幾個工作區域哈:

    • Workspace:你電腦本地看到的文件和目錄,在Git的版本控制下,構成了工作區。
    • Index/Stage:暫存區,一般存放在 .git目錄下,即.git/index,它又叫待提交更新區,用於臨時存放你未提交的改動。比如,你執行git add,這些改動就添加到這個區域啦。
    • Repository:本地倉庫,你執行git clone 地址,就是把遠程倉庫克隆到本地倉庫。它是一個存放在本地的版本庫,其中HEAD指向最新放入倉庫的版本。當你執行git commit,文件改動就到本地倉庫來了~
    • Remote:遠程倉庫,就是類似github,碼雲等網站所提供的倉庫,可以理解為遠程數據交換的倉庫~

    Git的工作流程

    上一小節介紹完Git的四大工作區域,這一小節呢,介紹Git的工作流程咯,把git的操作命令和幾個工作區域結合起來,個人覺得更容易理解一些吧,哈哈,看圖:

    git 的正向工作流程一般就這樣:

    • 從遠程倉庫拉取文件代碼回來;
    • 在工作目錄,增刪改查文件;
    • 把改動的文件放入暫存區;
    • 將暫存區的文件提交本地倉庫;
    • 將本地倉庫的文件推送到遠程倉庫;

    Git文件的四種狀態

    根據一個文件是否已加入版本控制,可以把文件狀態分為:Tracked(已跟蹤)和Untracked(未跟蹤),而tracked(已跟蹤)又包括三種工作狀態:Unmodified,Modified,Staged

    • Untracked: 文件還沒有加入到git庫,還沒參与版本控制,即未跟蹤狀態。這時候的文件,通過git add 狀態,可以變為Staged狀態
    • Unmodified:文件已經加入git庫, 但是呢,還沒修改, 就是說版本庫中的文件快照內容與文件夾中還完全一致。 Unmodified的文件如果被修改, 就會變為Modified. 如果使用git remove移出版本庫, 則成為Untracked文件。
    • Modified:文件被修改了,就進入modified狀態啦,文件這個狀態通過stage命令可以進入staged狀態
    • staged:暫存狀態. 執行git commit則將修改同步到庫中, 這時庫中的文件和本地文件又變為一致, 文件為Unmodified狀態.

    一張圖解釋Git的工作原理

    日常開發中,Git的基本常用命令

    • git clone
    • git checkout -b dev
    • git add
    • git commit
    • git log
    • git diff
    • git status
    • git pull/git fetch
    • git push

    這個圖只是模擬一下git基本命令使用的大概流程哈~

    git clone

    當我們要進行開發,第一步就是克隆遠程版本庫到本地呢

    git clone url  克隆遠程版本庫
    

    git checkout -b dev

    克隆完之後呢,開發新需求的話,我們需要新建一個開發分支,比如新建開發分支dev

    創建分支:

    git checkout -b dev   創建開發分支dev,並切換到該分支下
    

    git add

    git add的使用格式:

    git add .	添加當前目錄的所有文件到暫存區
    git add [dir]	添加指定目錄到暫存區,包括子目錄
    git add [file1]	添加指定文件到暫存區
    

    有了開發分支dev之後,我們就可以開始開發啦,假設我們開發完HelloWorld.java,可以把它加到暫存區,命令如下

    git add Hello.java  把HelloWorld.java文件添加到暫存區去
    

    git commit

    git commit的使用格式:

    git commit -m [message] 提交暫存區到倉庫區,message為說明信息
    git commit [file1] -m [message] 提交暫存區的指定文件到本地倉庫
    git commit --amend -m [message] 使用一次新的commit,替代上一次提交
    

    把HelloWorld.java文件加到暫存區后,我們接着可以提交到本地倉庫啦~

    git commit -m 'helloworld開發'
    

    git status

    git status,表示查看工作區狀態,使用命令格式:

    git status  查看當前工作區暫存區變動
    git status -s  查看當前工作區暫存區變動,概要信息
    git status  --show-stash 查詢工作區中是否有stash(暫存的文件)
    

    當你忘記是否已把代碼文件添加到暫存區或者是否提交到本地倉庫,都可以用git status看看哦~

    git log

    git log,這個命令用得應該比較多,表示查看提交歷史/提交日誌~

    git log  查看提交歷史
    git log --oneline 以精簡模式显示查看提交歷史
    git log -p <file> 查看指定文件的提交歷史
    git blame <file> 一列表方式查看指定文件的提交歷史
    

    嘻嘻,看看dev分支上的提交歷史吧要回滾代碼就經常用它喵喵提交歷史

    git diff

    git diff 显示暫存區和工作區的差異
    git diff filepath   filepath路徑文件中,工作區與暫存區的比較差異
    git diff HEAD filepath 工作區與HEAD ( 當前工作分支)的比較差異
    git diff branchName filepath 當前分支的文件與branchName分支的文件的比較差異
    git diff commitId filepath 與某一次提交的比較差異
    

    如果你想對比一下你改了哪些內容,可以用git diff對比一下文件修改差異哦

    git pull/git fetch

    git pull  拉取遠程倉庫所有分支更新併合併到本地分支。
    git pull origin master 將遠程master分支合併到當前本地master分支
    git pull origin master:master 將遠程master分支合併到當前本地master分支,冒號後面表示本地分支
    
    git fetch --all  拉取所有遠端的最新代碼
    git fetch origin master 拉取遠程最新master分支代碼
    

    我們一般都會用git pull拉取最新代碼看看的,解決一下衝突,再推送代碼到遠程倉庫的。

    有些夥伴可能對使用git pull還是git fetch有點疑惑,其實
    git pull = git fetch+ git merge。pull的話,拉取遠程分支並與本地分支合併,fetch只是拉遠程分支,怎麼合併,可以自己再做選擇。

    git push

    git push 可以推送本地分支、標籤到遠程倉庫,也可以刪除遠程分支哦。

    git push origin master 將本地分支的更新全部推送到遠程倉庫master分支。
    git push origin -d <branchname>   刪除遠程branchname分支
    git push --tags 推送所有標籤
    

    如果我們在dev開發完,或者就想把文件推送到遠程倉庫,給別的夥伴看看,就可以使用git push origin dev~

    Git進階之分支處理

    Git一般都是存在多個分支的,開發分支,回歸測試分支以及主幹分支等,所以Git分支處理的命令也需要很熟悉的呀~

    • git branch
    • git checkout
    • git merge

    git branch

    git branch用處多多呢,比如新建分支、查看分支、刪除分支等等

    新建分支:

    git checkout -b dev2  新建一個分支,並且切換到新的分支dev2
    git branch dev2 新建一個分支,但是仍停留在原來分支
    

    查看分支:

    git branch    查看本地所有的分支
    git branch -r  查看所有遠程的分支
    git branch -a  查看所有遠程分支和本地分支
    

    刪除分支:

    git branch -D <branchname>  刪除本地branchname分支
    

    git checkout

    切換分支:

    git checkout master 切換到master分支
    

    git merge

    我們在開發分支dev開發、測試完成在發布之前,我們一般需要把開發分支dev代碼合併到master,所以git merge也是程序員必備的一個命令。

    git merge master  在當前分支上合併master分支過來
    git merge --no-ff origin/dev  在當前分支上合併遠程分支dev
    git merge --abort 終止本次merge,並回到merge前的狀態
    

    比如,你開發完需求后,發版全需要把代碼合到主幹master分支,如下:

    Git進階之處理衝突

    Git版本控制,還是多個人一起搞的,多個分支並存的,這就難免會有衝突出現~

    Git合併分支,衝突出現

    同一個文件,在合併分支的時候,如果同一行被多個分支或者不同人都修改了,合併的時候就會出現衝突。

    舉個粟子吧,我們現在在dev分支,修改HelloWorld.java文件,假設修改了第三行,並且commit提交到本地倉庫,修改內容如下:

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello,撿田螺的小男孩!");
        }
    }
    

    我們切回到master分支,也修改HelloWorld.java同一位置內容,如下:

    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello,jay!!");
        }
    }
    

    再然後呢,我們提交一下master分支的這個改動,並把dev分支合併過下,就出現衝突啦,如圖所示:

    Git解決衝突

    Git 解決衝突步驟如下:

    • 查看衝突文件內容
    • 確定衝突內容保留哪些部分,修改文件
    • 重新提交,done

    1.查看衝突文件內容

    git merge提示衝突后,我們切換到對應文件,看看衝突內容哈,,如下:

    2.確定衝突內容保留哪些部分,修改文件

    • Git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容,
    • <<<<<<<HEAD是指主分支修改的內容,>>>>>>> dev是指dev分支上修改的內容

    所以呢,我們確定到底保留哪個分支內容,還是兩個分支內容都保留呢,然後再去修改文件衝突內容~

    3.修改完衝突文件內容,我們重新提交,衝突done

    Git進階之撤銷與回退

    Git的撤銷與回退,在日常工作中使用的比較頻繁。比如我們想將某個修改后的文件撤銷到上一個版本,或者想撤銷某次多餘的提交,都要用到git的撤銷和回退操作。

    代碼在Git的每個工作區域都是用哪些命令撤銷或者回退的呢,如下圖所示:

    有關於Git的撤銷與回退,一般就以下幾個核心命令

    • git checkout
    • git reset
    • git revert

    git checkout

    如果文件還在工作區,還沒添加到暫存區,可以使用git checkout撤銷

    git checkout [file]  丟棄某個文件file
    git checkout .  丟棄所有文件
    

    以下demo,使用git checkout — test.txt 撤銷了暫存區test.txt的修改

    git reset

    git reset的理解

    git reset的作用是修改HEAD的位置,即將HEAD指向的位置改變為之前存在的某個版本.

    為了更好地理解git reset,我們來回顧一下,Git的版本管理及HEAD的理解

    Git的所有提交,會連成一條時間軸線,這就是分支。如果當前分支是master,HEAD指針一般指向當前分支,如下:

    假設執行git reset,回退到版本二之後,版本三不見了哦,如下:

    git reset的使用

    Git Reset的幾種使用模式

    git reset HEAD --file
    回退暫存區里的某個文件,回退到當前版本工作區狀態
    git reset –-soft 目標版本號 可以把版本庫上的提交回退到暫存區,修改記錄保留
    git reset –-mixed 目標版本號 可以把版本庫上的提交回退到工作區,修改記錄保留
    git reset –-hard  可以把版本庫上的提交徹底回退,修改的記錄全部revert。
    

    先看一個粟子demo吧,代碼git add到暫存區,並未commit提交,就以下醬紫回退,如下:

    git reset HEAD file 取消暫存
    git checkout file 撤銷修改
    

    再看另外一個粟子吧,代碼已經git commit了,但是還沒有push:

    git log  獲取到想要回退的commit_id
    git reset --hard commit_id  想回到過去,回到過去的commit_id
    

    如果代碼已經push到遠程倉庫了呢,也可以使用reset回滾哦(這裏大家可以自己操作實踐一下哦)~

    git log
    git reset --hard commit_id
    git push origin HEAD --force
    

    git revert

    與git reset不同的是,revert複製了那個想要回退到的歷史版本,將它加在當前分支的最前端。

    revert之前:

    revert 之後:

    當然,如果代碼已經推送到遠程的話,還可以考慮revert回滾呢

    git log  得到你需要回退一次提交的commit id
    git revert -n <commit_id>  撤銷指定的版本,撤銷也會作為一次提交進行保存
    

    Git進階之標籤tag

    打tag就是對發布的版本標註一個版本號,如果版本發布有問題,就把該版本拉取出來,修復bug,再合回去。

    git tag  列出所有tag
    git tag [tag] 新建一個tag在當前commit
    git tag [tag] [commit] 新建一個tag在指定commit
    git tag -d [tag] 刪除本地tag
    git push origin [tag] 推送tag到遠程
    git show [tag] 查看tag
    git checkout -b [branch] [tag] 新建一個分支,指向某個tag
    

    Git其他一些經典命令

    git rebase

    rebase又稱為衍合,是合併的另外一種選擇。

    假設有兩個分支master和test

          D---E test
          /
     A---B---C---F--- master
    

    執行 git merge test得到的結果

           D--------E
          /          \
     A---B---C---F----G---   test, master
    

    執行git rebase test,得到的結果

    A---B---D---E---C‘---F‘---   test, master
    

    rebase好處是: 獲得更優雅的提交樹,可以線性的看到每一次提交,並且沒有增加提交節點。所以很多時候,看到有些夥伴都是這個命令拉代碼:git pull –rebase

    git stash

    stash命令可用於臨時保存和恢復修改

    git stash  把當前的工作隱藏起來 等以後恢復現場後繼續工作
    git stash list 显示保存的工作進度列表
    git stash pop stash@{num} 恢復工作進度到工作區
    git stash show :显示做了哪些改動
    git stash drop stash@{num} :刪除一條保存的工作進度
    git stash clear 刪除所有緩存的stash。
    

    git reflog

    显示當前分支的最近幾次提交

    git blame filepath

    git blame 記錄了某個文件的更改歷史和更改人,可以查看背鍋人,哈哈

    git remote

    git remote   查看關聯的遠程倉庫的名稱
    git remote add url   添加一個遠程倉庫
    git remote show [remote] 显示某個遠程倉庫的信息
    

    參考與感謝

    感謝各位前輩的文章:

    • 一個小時學會Git
    • 【Git】(1)—工作區、暫存區、版本庫、遠程倉庫
    • Git Reset 三種模式
    • Git恢復之前版本的兩種方法reset、revert(圖文詳解)
    • Git撤銷&回滾操作(git reset 和 get revert)
    • 為什麼要使用git pull –rebase?

    公眾號

    • 歡迎關注我個人公眾號,交個朋友,一起學習哈~
    • 如果文章有錯誤,歡迎指出哈,感激不盡~

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

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

    ※推薦評價好的iphone維修中心

  • 【思考】URI和URL的區別?以及URL的結構

    【思考】URI和URL的區別?以及URL的結構

    URI = Universal Resource Identifier
    URL = Universal Resource Locator

    在學習中,我們難免會遇到 URI 和 URL,有時候都傻傻分不清,為啥這邊是 URI 那邊又是 URL,這兩者到底有什麼區別呢?

    我們從名字上看

    • 統一資源標識符(Uniform Resource Identifier, URI):是一個用於標識某一互聯網資源名稱的字符串。
    • 統一資源定位符(Uniform Resource Locator, URL):是一個用於標識和定位某一互聯網資源名稱的字符串。

    可能大家就比較困惑了,這倆好像是一樣的啊?那我們就類比一下我們現實生活中的情況:
    我們要找一個人——張三,我們可以通過他的唯一的標識來找,比如說身份證,那麼這個身份證就唯一的標識了一個人,這個身份證就是一個 URI
    而要找到張三,我們不一定要用身份證去找,我們還可以根據地址去找,如 在清華大學18號宿舍樓的404房間第一個床鋪的張三,我們也可以唯一確定一個張三,
    動物住址協議://地球/中國/北京市/清華大學/18號宿舍樓/404號寢/張三.人。而這個地址就是我們用於標識和定位的 URL
    我們從上面可以很明顯的看出,URI 通過任何方法標識一個人即可,而 URL 雖然也可以標識一個人,但是它主要是通過定位地址的方法標識一個人,所以 URL 其實是 URI 的一個子集,即 URL 是靠標識定位地址的一個 URI

    Url 的構成

    URL(Uniform Resource Locator,統一資源定位符),用於定位網絡上的資源,每一個信息資源都有統一的且在網上唯一的地址。

    Url一般有以下部分組成
    scheme://host:port/path?query#fragment

    Scheme: 通信協議,一般為http、https等;
    Host: 服務器的域名主機名或ip地址;
    Port: 端口號,此項為可選項,默認為80;
    Path: 目錄,由“/”隔開的字符串,表示的是主機上的目錄或文件地址;
    Query: 查詢,此項為可選項,可以給動態網頁傳遞參數,用“&”隔開,每個參數的名和值用“=”隔開;
    Fragment: 信息片段,字符串,用於指定網絡資源中的某片斷;

    其實,把 URL 說成是網址其實是很不嚴謹的說法,因為 URL 有很嚴格的結構,表示也很靈活、有彈性。
    在 RFC 3986: Uniform Resource Identifier (URI): Generic Syntax 的 Syntax Components 把 URL 描述為如下圖:

    如圖所示,把 URL 分成幾個部分,這樣便可以了解URL的構成。 在 URI scheme – Wikipedia 頁面中對 URL 的描述更為詳細,如下圖:

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

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

    ※推薦評價好的iphone維修中心

  • 學寫PEP,參与Python語言的設計

    學寫PEP,參与Python語言的設計

    如果你為Python寫了一篇PEP,這篇PEP成功的被Python指導委員會接受了,那麼以後你在吹牛皮的時候你就可以說我主導了Python語言某個特性的設計工作.

                                  					-- 跬蟒
    

    我就問你主導Python語言特性設計牛不牛皮,今天我就寫一篇文章告訴大家如何去為Python設計一篇PEP,並且整個PEP從一個想法到Python語言去實現它的這一套流程:

    假設你已經是一個Python高手了,在使用Python給過程中你覺得Python語言在某方面還不夠完善,你有一個不錯的想法可以去改善Python這方面的不足,你打算把你的想法加入到Python語言裏面,所以你打算寫一篇PEP,為Python的發展獻言建策,那首先需要做什麼呢?

    1. 首先你要確保你的想法是個新的想法是個比較大的想法,是一個由必要去建立一個PEP的想法,也許你發現了Python的一些小問題,但是這些小問題如果提交一個小補丁就可以解決了,那就沒必要提PEP
    2. 當你確定自己的想法很牛B之後,你也不是馬上就要提PEP,你首先要做的事情是引發社區的討論,看看其他人怎麼看,然後自己去實現一下這個想法看是否是可行的,並且發帖到 python-list@python.org mailing list或者到 python-ideas@python.org mailing list 進行進一步的確定,看看大家對你的想法是否認同,如果你能讓大多數人都認同,那你就有戲,在你發帖之前最好準備一份高質量的PEP草稿,這樣的話才會更容易的被接受
    3. 總之就是先討論,得到大家的認可,避免後期不必要的撕逼,然受自己也要做好準備,最好有個簡單的實現,然後還有個高質量的PEP草稿

    寫PEP你不得不知道的幾個Python社區角色

    PEP champion : PEP擁護者 也就是PEP的發起人,也就是跟大家說我有個非常XXX的想法的人

    PEP author: PEP作者 就是寫PEP的人,PEP從一個想法到一篇PEP草稿,再到一篇擁有官方PEP編號的PEP文檔,到後面PEP審核通過,PEP複審出現改動,PEP被接受這個過程中維護PEP文檔的人就是PEP的作者,大部分PEP作者就是PEP擁護者本人

    PEP reviewer: 這個角色不是單指某一個人,一個PEP從想法到實現需要經過很多此review, 每一次參与review的人都可以被稱作 PEP reviewer

    PEP editor: PEP編輯者 就是對PEP進行初步審核的人,審核通過的PEP進入到github上面的PEP倉庫的master分支,進行下一輪的評審

    Python Core Developers: Python核心開發人員 就是開發Cpython解釋器的那群人,都是大佬,都是大佬

    Python’s Steering Council: Python指導委員會 大佬中的大佬,從Python核心開發人員中選擇出來的指導Python語言開發工作的一群人,對於PEP是否接受有着最終發言權

    PEP的工作流程是這樣的:

    1. PEP champion 先有一個高質量的idear(經過討論分析和理性驗證)
    2. 你去github上面去fork PEP倉庫
    3. 在倉庫中創建一個 pep-9999.rst的文件去把你的PEP草稿粘貼進去
    4. 確定你的PEP的類型,PEP的狀態設為草稿,PEP頭部按照模板寫一波
    5. 把你的pep-9999.rst push到PEP倉庫
    6. 然後PEP editors 會去審核你的提交
    7. 如果審核通過,這個本來是草稿的PEP會拿到一個正規的PEP編號,如果沒有審核通過那PEP editors 會打回去讓 PEP author 去修改
    8. 如果PEP審核通過拿到了PEP編號 PEP editor 會把這個新提交的PEP合併到PEP倉庫的 master 分支
    9. 如果你的PEP的類型是Standards Track類,那你提交的PEP還會被發送給Python-dev list 成員進行再次review, 確保你的新PEP沒有坑
    10. 有些聽起很不錯的PEP在實現的時候其實是非常蛋疼的,沒做的時候想的挺好,真正去實現的時候才知道是否靠譜,最好的情況時你在提交PEP的時候你手裡就已經有一個這個PEP的原型實現了,所以如果你的PEP類型是Standards Track類型那你就不僅需要準備設計文檔,你還需要準備一個參考實現,以此來避免一些不切實際的想法

    當然凡事都有例外,有些Python的核心開發者是不會走這個流程的因為他們本身的權限比較大,他們有直接push內容到PEP倉庫的權限,所以有時候他們會直接給自己的PEP分配一個PEP編號push進入PEP倉庫的master分支,當然這並不意味着這個PEP就被接受了,他只是繞過了PEP editor的審批而已,PEP被接受和PEP通過審批是完全兩碼事兒,只有通過Python指導委員會的同意,PEP計劃實現,才能叫做PEP被接受.

    如果我寫的PEP無法審核通過被拒怎麼辦?

    PEP被拒絕是很正常的事情,不要灰心,只要能夠堅信自己的PEP是真正對Python有用的東西,真正好的idear,修改一下繼續上就行了,但是被拒肯定是有原因的,最主要的原因就是下面幾條:

    1. 該特性已經存在了
    2. 技術上不合理
    3. Python不需要去實現這樣的特性,也就是說偽需求
    4. 無法進行後向兼容
    5. 不符合Python的設計哲學(Python設計哲學可以在Python交互解釋器中輸入import this獲取)其實在PEP的審批階段可以拿着自己的PEP idear去諮詢Python指導委員會,因為PEP最終會不會被接受其實是由Python指導委員會所決定的,所以如果真的想要自己的PEP被接受,做好提前的溝通還是非常有必要的
    6. 奧對了還有一個蛋疼的要求,就是你的PEP草稿必須帶着至少一名Python核心開發人員一起寫,或者有一個Python核心開發人員指導你寫,或者有一個經過Python指導委員會批準的非Python核心開發人員一起寫,反正就是需要有一個能夠被Python指導委員會所信任的人參與了你的PEP設計,如果沒能滿足這個條件 PEP editor有權直接駁回你的PEP草稿

    PEP的複審和決定機制

    一篇PEP是否最終被接受並且決定去實現是需要經過層層複審的,反正要經過很麻煩了一個流程,下面有個Python官方畫的簡單流程圖:

    但是實際情況比較複雜,有時候不會按照這個流程圖來,但是這個流程圖給人們提供了一個比較清晰的PEP工作流的概覽

    PEP格式和模板

    這年頭寫啥文檔沒個模板真不行,PEP也是文檔,所以模板搞起來:

    1. 首先PEP是UTF-8編碼的rst文件,首先你需要去指導rst文件的格式,如果rst的語法格式你已經會了,那你就可以閱讀官方的PEP 12--Sample reStructuredText PEP Template,沒錯PEP12是介紹rst格式PEP模板的PEP(有點繞),為什麼要用rst格式?官方給出的解釋是 容易轉成html進行在線發布和閱讀
    2. 每一篇PEP必須有一個標準的PEP頭部,如下所示,帶* 號是可寫可不寫的,不帶* 號的是必須要寫的,記住寫PEP頭的時候,頭的各個字段的順序,必須按照下圖的內容去寫,先後順序不能亂

    寫道這裏就講的差不多了,但是其實PEP的書寫還有很多的內容比如:

    1. 如何判斷PEP是不是一個成功的PEP
    2. PEP提交之後發現內容有bug怎麼解決
    3. PEP所有權以及所有權轉移問題
    4. PEP editor的詳細職責和工作流
    5. 等等問題,我就不寫了,寫不動了…..

    想寫PEP的可以先根據上面流程走一波,
    然後等到遇到問題的時候再去查資料吧.

    如果感覺本篇內容還不錯,微信的朋友請點個在看,其他平台的朋友可以(近距離)掃描下方的二維碼關注我的公眾號 早睡蟒更多優質原創無廣告內容等你來看.

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案