分類
發燒車訊

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

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

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

分類
發燒車訊

一起玩轉微服務(13)——AOP

一、什麼是AOP編程

AOP: Aspect Oriented Programming 面向切面編程。   面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP),是目前軟件開發中的一個熱點。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。   AOP是OOP的延續,是(Aspect Oriented Programming)的縮寫,意思是面向切面(方面)編程。   主要的功能是:日誌記錄,性能統計,安全控制,事務處理,異常處理等等。   主要的意圖是:將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改 變這些行為的時候不影響業務邏輯的代碼。

 

 注意:AOP不是一種技術,實際上是編程思想。凡是符合AOP思想的技術,都可以看成是AOP的實現。

二、AOP編程思想

功能: 讓關注點代碼與業務代碼分離!

  • 關注點
    關注點,重複代碼就叫做關注點;
  • 切面
    關注點形成的類,就叫切面(類)!
    面向切面編程,就是指 對很多功能都有的重複的代碼抽取,再在運行的時候網業務方法上動態植入“切面類代碼”。
  • 切入點
    執行目標對象方法,動態植入切面代碼。
    可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。

三、AOP原理剖析

在軟件業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。

 

 

AOP把軟件系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,它們經常發生在核心關注點的多處,而各處都基本相似,比如權限認證、日誌、事務處理。AOP 的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。
AOP中的一些名詞如下:

    • 切面(Aspect):一個關注點的模塊化,這個關注點可能會橫切多個對象。事務管理是J2EE應用中一個關於橫切關注點的很好的例子。在Spring AOP中,切面可以使用基於模式或者基於@Aspect註解的方式來實現。
    • 連接點(Joinpoint):在程序執行過程中某個特定的點,比如某方法調用的時候或者處理異常的時候。在Spring AOP中,一個連接點總是表示一個方法的執行。
    • 通知(Advice):在切面的某個特定的連接點上執行的動作。其中包括了“around”、“before”和“after”等不同類型的通知(通知的類型將在後面部分進行討論)。許多AOP框架(包括Spring)都是以攔截器做通知模型,並維護一個以連接點為中心的攔截器鏈。
    • 切入點(Pointcut):匹配連接點的斷言。通知和一個切入點表達式關聯,並在滿足這個切入點的連接點上運行(例如,當執行某個特定名稱的方法時)。切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法進行匹配。
    • 引入(Introduction):用來給一個類型聲明額外的方法或屬性(也被稱為連接類型聲明(inter-type declaration))。Spring允許引入新的接口(以及一個對應的實現)到任何被代理的對象。例如,你可以使用引入來使一個bean實現IsModified接口,以便簡化緩存機制。
    • 目標對象(Target Object):被一個或者多個切面所通知的對象。也被稱做被通知(advised)對象。既然Spring AOP是通過運行時代理實現的,這個對象永遠是一個被代理(proxied)對象。
    • AOP代理(AOP Proxy):AOP框架創建的對象,用來實現切面契約(例如通知方法執行等等)。在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。
    • 織入(Weaving):把切面連接到其它的應用程序類型或者對象上,並創建一個被通知的對象。這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。

四、AOP編程使用

註解版本實現AOP

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>  開啟事物註解權限
@Aspect                         指定一個類為切面類       
@Pointcut("execution(* com.service.UserService.add(..))")  指定切入點表達式
@Before("pointCut_()")              前置通知: 目標方法之前執行
@After("pointCut_()")               後置通知:目標方法之後執行(始終執行)
@AfterReturning("pointCut_()")       返回后通知: 執行方法結束前執行(異常不執行)
@AfterThrowing("pointCut_()")           異常通知:  出現異常時候執行
@Around("pointCut_()")              環繞通知: 環繞目標方法執行
​
​
@Component
@Aspect
public class AopLog {
​
    // 前置通知
    @Before("execution(* com.service.UserService.add(..))")
    public void begin() {
        System.out.println("前置通知");
    }
​
​
    // 後置通知
    @After("execution(* com.service.UserService.add(..))")
    public void commit() {
        System.out.println("後置通知");
    }
​
    // 運行通知
    @AfterReturning("execution(* com.service.UserService.add(..))")
    public void returning() {
        System.out.println("運行通知");
    }
​
    // 異常通知
    @AfterThrowing("execution(* com.service.UserService.add(..))")
    public void afterThrowing() {
        System.out.println("異常通知");
    }
​
    // 環繞通知
    @Around("execution(* com.service.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("環繞通知開始");
        proceedingJoinPoint.proceed();
        System.out.println("環繞通知結束");
    }
}

 

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

【其他文章推薦】

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

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

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

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

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

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

分類
發燒車訊

Day11-微信小程序實戰-交友小程序-附近的人(地圖的形式)及位置獲取

回顧:在下面的tabbar中,我們已經實現了首頁 消息 我的,就剩下”附近“頁面了

 

”附近“的頁面主要是用地圖來進行展示的(可以显示我的位置,也可以显示周圍附近的人的位置)

 

 (在地圖裡面點擊它的頭像的話,就可以看到詳情頁了,然後也可以知道它的位置)

1、首先要利用 地圖 組件-這個是小程序給我們提供的一個組件:

https://developers.weixin.qq.com/miniprogram/dev/component/map.html

地圖中的scale就是級別的意思,這個級別越大,在地圖裡面显示的就越詳細了

<map id="map" longitude="113.324520" latitude="23.099994" scale="14" controls="{{controls}}" bindcontroltap="controltap" markers="{{markers}}" bindmarkertap="markertap" polyline="{{polyline}}" bindregionchange="regionchange" show-location style="width: 100%; height: 300px;"></map>

”附近“這個頁面,我們在near文件裏面進行操作的

最簡單的實現就是,通過代碼:

<!--miniprogram/pages/near/near.wxml-->
<view class="map">
  <map id="map" longitude="113.324520" latitude="23.099994" scale="14"></map>
</view>

 

之後通過隊wxss的樣式設置

/* miniprogram/pages/near/near.wxss */
.map{
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}
.map map{
   width: 100%;
  height: 100%; 
}

效果圖:

之後就要變成我當前的地理位置了

那個地圖屬性 scale 就是詳細的程度:

上面是等於14的情況,如果是==2的時候

詳細程度就更低了

 

 

2、之後就要把經緯度改成我們自己的了,就要在js文件中定義,經緯度的變量

之後還要定義一個getlocation的函數,這個函數在一開始的時候就要被調用了,也就是在onload的時候被調用了

並且下一次切換到地圖的時候,可能也要再次的觸發,所以在onshow裏面也要執行這個函數了

3、獲取用戶的經緯度,微信提供了API,可以直接調用的

https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.getLocation.html

 

 

 可以看到,它是想要進行授權的,我們是通過全局的app.json添加上permission來實現的

https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.getLocation.html

"permission": {
    "scope.userLocation": {
      "desc": "你的位置信息將用於小程序位置接口的效果展示" 
    }
  }

 注意在json文件裏面的話不能有註釋的

寫好之後,就可以調用我們的getLocation函數了

 

wx.getLocation({
 type: 'wgs84',
 success (res) {
   const latitude = res.latitude
   const longitude = res.longitude
   const speed = res.speed
   const accuracy = res.accuracy
 }
})

上面的代碼中 默認的是用 wgs84,但是在開發者文檔的下面有一句話:

 

 

 但是,直接用這個函數的話:

 getLocation(){
    wx.getLocation({
      type: 'gcj02',
      success(res) {
        const latitude = res.latitude
        const longitude = res.longitude
        this.setData({
          longitude,
          latitude
        });
      }
    })
    
  }

會報錯,因為這裏的this指向不對,success要用一個箭頭函數才行的,設置為如下的代碼才行:

 getLocation(){
    wx.getLocation({
      type: 'gcj02',
      success:(res)=> {
        const latitude = res.latitude
        const longitude = res.longitude
        this.setData({
          longitude,
          latitude
        });
      }
    })
    
  }

得到的效果就是,可以直接定位到我的位置了:

 

 

 但是我們並不知道我們具體在地圖上面的哪一塊–其實微信也棒我們想好了,就是可以直接在map這個標籤裏面,添加一個 show-location 屬性

<view class="map">
  <map show-location id="map" longitude="{{ longitude }}" latitude="{{ latitude }}" scale="14"></map>
</view>

效果圖:

 

(注意:其實這個效果在真機上显示的效果會更好,可以直接掃碼在真機上面進行測試的

4、獲取我自己的位置之後,接下來就是,怎麼獲取到周圍附近的人的信息呢,然後還要把用戶的頭像显示出來了

===這個微信也提供了,就是可以直接用map標籤的屬性 markers,也就是可以添加標記點,包括圖片和圖標這種的

 

  ===然後因為我們是要通過用戶的經緯度來獲得用戶的位置的,整體的邏輯,就是我們獲得了用戶的經緯度,和用戶的頭像,還有id等等信息,然後在地圖中標記出來,所以users數據庫中就要添加兩個字段了,分別是經緯度

5、在user.js中創立數據庫字段的時候,就通過:

longitude : this.longitude, latitude : this.latitude   然後在合格js文件裏面,定義一個方法來給data中的經緯度賦值。這個方法和我們在near.js裏面定義的方法是很像的 所以在user.js裏面就可以這樣定義了

 getLocation(){
    wx.getLocation({
      type: 'gcj02',
      success: (res) => {
        this.latitude = res.latitude
        this.longitude = res.longitude
       
      }
    })
  }

效果圖:我們把users我的數據刪掉之後,重新登陸微信,可以看到數據庫頁進行了更新

 

 之後就是把這個經緯度讀取出來,然後渲染到我們的地圖上面即可了(然後出現了一個問題就是,我們要讀取的是附近的人,如果把數據庫中去用戶的經緯度都讀取出來,然後渲染了,這個是沒有意義的,我們就是要看到哪一塊,那一塊的用戶就显示出來這樣的,就是有一個範圍的  

====其實小程序中給了我們這樣的功能

文檔-》雲開發->command

https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.getLocation.html

 

並且有一個要求:

這樣的索引,其實和我們在數據庫中創立字段是差不多的

再次查看文檔中:

https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/database/geo/Geo.Point.html

 

 

 我們在user.js的 bindGetUserInfo(ev) 函數中,設置:

location: db.Geo.Point(this.longitude, this.latitude)

這個location設置完之後,就可以設置數據庫裏面的索引值了

因為在數據庫添加了location這個字段,所以我們要把users裏面的數據再次刪除掉,之後重新的微信用戶登陸,再進入數據庫中

(就可以看到location:

 

 之後就可以在數據可以中打開-》索引管理了

 

點擊添加索引

命名為_location 然後設置為”非唯一“也就是不僅僅可以得到自己的,還可以得到其他用戶的location,索引的字段就是我們剛剛在數據庫中建立的要映射的location了,並且選擇”地理位置“

 

 

 (其實這個地理索引的設置 是為了 提高性能的,沒有也是可以的)

再回到near.js文件中

 在後面添加一個方法 getNearUsers

 然後在獲取了經緯度之後,就可以調用這個方法了 

這個函數方法的書寫,可以參考

微信文檔裏面的demo

const _ = db.command
db.collection('restaurants').where({
  location: _.geoNear({
    geometry: db.Geo.Point(113.323809, 23.097732),
    minDistance: 1000,
    maxDistance: 5000,
  })
}).get()

所以還要在near.js文件開頭的時候 獲取一下 db.command下劃線

  getNearUsers(){
    db.collection('users').where({
      location: _.geoNear({
        geometry: db.Geo.Point(this.data.longitude, this.data.latitude),
        minDistance: 1000,
        maxDistance: 5000
        //這1000和5000的單位是米
      })
    });
  }

就寫好了

並且還要注意一個點,我們在”個人中心“-》”編輯個人信息“裏面設置了一個共享位置的打開和關閉

 

通過isLocation字段來設置的,就是是否開啟共享位置,所以除了找到附近的人以外,還要看這個用戶有沒有開啟”共享位置“

所以獲取到的用戶,既要在我們的範圍以內,又要是”開啟了共享位置“的

所以就還要添加一個這樣的條件才可以的

 

 

getNearUsers(){
    db.collection('users').where({
      location: _.geoNear({
        geometry: db.Geo.Point(this.data.longitude, this.data.latitude),
        minDistance: 1000,
        maxDistance: 5000
        //這1000和5000的單位是米
      }),
      islocation : true
    }).field({
      longitude : true,
      latitude : true ,
      userPhoto : true
    }).get().then((res)=>{
      console.log(res.data);
    });
  }

然後編譯之後,可以看到,返回的res.data是空的

 

主要就是因為我們周圍是沒人的,才返回了空的字段了

然後我們設置的也有問題,就是1000~5000,就是距離我1000米到5000米以內的人,就不包括自己了,所以先把minDistance設置為0

看看能不能把自己打印出來咯

下面我們就來奧marker的圖片標註了

 

 要在map標籤裏面添加一個 markers 這樣的標籤了

然後還要在near.js裏面給markers定義一個初始值 為一個空數組

 

因為在示例代碼中,看到的就是數組來的

 

getNearUsers(){
    db.collection('users').where({
      location: _.geoNear({
        geometry: db.Geo.Point(this.data.longitude, this.data.latitude),
        minDistance: 0,
        maxDistance: 1000
        //這1000和5000的單位是米
      }),
      islocation : true
    }).field({
      longitude : true,
      latitude : true ,
      userPhoto : true
    }).get().then((res)=>{
      console.log(res.data);
      let data = res.data;
      let result = [];
      if(data.length){
        for(let i=0;i<data.length;i++){
          result.push({
            iconPath: data[i].userPhoto,
            id: data[i]._id,
            latitude: data[i].latitude,
            longitude: data[i].longitude,
            width: 30,
            height: 30
          });
        }
        this.setData({
          markers : result
        });
      }
    });
  }

 

效果:

目前微信小程序還不支持把那個地圖裡面的圖片變成是圓形的

目前是只能通過,對width和height進行矩形的渲染了

通過多賬號進行一下測試:

 

 因為測試號和我的主號都是在同一個地方的,所以我們手動的把後面的88改成是89,就可以得到效果:

 

 

 

 

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

【其他文章推薦】

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

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

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

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

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

分類
發燒車訊

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

1.1 定義

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

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

                             

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

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

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

1.3 簡單工廠的實現一

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

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

1.3.1 產品抽象類

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

}

 

1.3.2  具體實現類

public class LenovoComputer extends Computer {

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

1.3.3  工廠類

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

1.3.4  UML類圖

                          

1.4 優缺點

 優點:

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

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

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

缺點:

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

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

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

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

1.5 適用場景

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

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

1.6 模式應用

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

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

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

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

1.7 開閉原則

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

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

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

 

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

【其他文章推薦】

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

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

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

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

※超省錢租車方案

分類
發燒車訊

03 . 二進制部署kubernetes1.18.4

簡介

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

kubeadm

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

二進制包

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

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

二進制部署K8s

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

初始化環境

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

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

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

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

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


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

部署etcd集群

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

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

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

準備cfssl證書生成工具

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

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

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

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

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

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

創建證書申請文件

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

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

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

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

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

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

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


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

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

安裝docker

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

部署Master Node

生成kube-apiserver證書

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

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

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

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

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

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

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


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

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

TLS bootstraping 工作流程

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

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

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

token也可自行生成替換

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

部署kube-controller-manager

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

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

部署kube-scheduler

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

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

部署worker node

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

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

在所有worker node創建工作目錄

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

從master節點拷貝

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

部署kubelet

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


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

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

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

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

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

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

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

部署kube-proxy

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

生成kube-proxy證書

# 切換工作目錄
cd TLS/k8s

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

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

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

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

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

部署CNI網絡

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

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

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

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

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

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

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

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

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

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

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

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

kubectl certificate approve node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro

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

部署Dashboard

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

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

kubectl apply -f recommended.yaml

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

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

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

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

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

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

部署CoreDNS

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

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

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

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

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

分類
發燒車訊

成大氫能電動機車 預估續航力可達 160 公里

  台灣機車密度是世界之最,造成嚴重的污染問題,電動、綠能機車的開發一直是學者致力的目標。國立成功大學近日整合關鍵技術,研發出以新型氫能燃料與鋰電池做為混合動力的新電動機車「飛馬一號」,首航測試即輕易完成 80 多公里路程,估計實際續航力可達 160 公里。   由成大航太工程學系賴維祥教授帶領的研究團隊,一年半前在經濟部能源局「能源學界科技專案」補助下,開始與銓寶工業、福桑聯合企業進行產學合作,共同研發代號「飛馬一號」氫能電動機車。   成大指出,「飛馬一號」是從銓寶公司提供的電動車改裝而來,除了原有的鋰電池外,研究團隊另外在車後加裝了 3,000 瓦級的燃料電池,以及 2 支容積達 6.8 公升、能儲存 300 大氣壓的儲氫瓶,原理大致與汽車大廠 TOYOTA 年底即將量產的氫氣電動車相同。   「飛馬一號」的動力系統是以新高壓儲存技術填充氫氣,可達傳統方式的 4 倍密度,利用氫能直接啟動燃料電池,再讓燃料電池把電傳給鋰電池,鋰電池的電力則驅動馬達運轉,此一模式可讓鋰電池在行進間同時充電,延長使用壽命。     (Source:)

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

【其他文章推薦】

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

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

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

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

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

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

分類
發燒車訊

步伐密集 易事特再建江蘇充電樁專案

易事特今日發佈公告稱,與江蘇高淳經濟開發區開發總公司就新能源汽車充電樁設備研發製造、IDC整體機房研發製造、智慧微電網等項目投資達成一致意見並簽署協議書。易事特承諾新辦企業固定資產投資總額為6億元人民幣(約新臺幣30億),畝均稅收不低於20萬元(約新臺幣101萬),首期建築面積不低於30000平方米。高淳經濟開發區將供地約120畝,使用年限為50年。   易事特佈局充電樁可謂步伐密集。今年8月13日,易事特與馬鞍山經濟技術開發區管理委員會簽訂了《投資框架協議書》,擬在對方區域內投資建設資料中心、分散式發電設備與系統集成和新能源汽車充電站(樁)項目。

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

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

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

※回頭車貨運收費標準

分類
發燒車訊

Zookeeper分佈式過程協同技術 – 概念及基礎

Zookeeper分佈式過程協同技術 – 概念及基礎

Zookeeper是什麼?

  • Zookeeper是一種分佈式過程協同技術,其所提供的客戶端API功能強大,其中包括:
  • 保障強一致性。有序性和持久性。
  • 實現通用的同步原語的能力。
  • 在實際分佈式系統中,併發往往導致不正確的行為。Zookeeper提供了一種簡單的併發處理機制。

關於Zookeeper名字的來源

Zookeeper由雅虎研究院開發,開發團隊原來想使用動物命名項目,在討論時大家覺得分佈式系統就像一個動物園,胡亂且難以管理,而Zookeeper就是將這一切變得可控。遂起名為Zookeeper,意為動物園管理員。

Zookeeper使用場景

  • 命名服務

命名服務是指通過路徑名稱來獲取服務的地址。首先zookeeper創建一個全局路徑,服務方在全局路徑下寫入服務的地址(或者名稱),服務消費方在啟動后watch全局路徑的數據和變化。注意,這裏的節點均為臨時節點,在服務連接中斷或者宕機時,通過臨時節點消失能夠通知訂閱方感知變化。

  • 配置管理

服務分佈部署在不同的機器上,將全局的配置信息存放於zookeeper的節點下,客戶端通過訂閱節點的數據變化獲取到配置信息。當節點中的配置信息發生變化時,客戶端可以通過watch節點發送的消息感知,從而修改服務本地的配置管理信息。

  • 集群管理

當有新機器加入集群時,由新機器的客戶端在指定的目錄下新建臨時節點,服務註冊管理端通過訂閱指定目錄的變化,收到了新增節點的通知后,將新機器加入集群服務的列表。當機器退出集群或者宕機時,臨時節點將會被刪除,同樣可以通知服務註冊管理端,有機器退出了集群。

  • 分佈式鎖

zookeeper的分佈式鎖可以分為2種,保持獨佔或者控制時序。

對於保持獨佔來說,可以把一個節點看做是一把鎖,通過創建節點的方式來搶佔鎖資源。如果創建節點成功,客戶端也就持有了這把鎖,如果創建失敗,則訂閱節點的變化,如果收到節點刪除消息,再次嘗試創建節點來搶佔鎖資源。

對於控制時序來說,通過在指定目錄下創建臨時有序節點,編號最小的節點首先持有鎖資源,每個後續節點都訂閱前一個節點。擁有鎖資源的客戶端在操作完畢后,刪除自己創建的節點。後續節點收到前置節點的刪除通知后,即獲取了鎖資源,可以進行業務操作。

Zookeeper基礎

Zookeeper通過節點(znode)來實現原語的功能,並不直接暴露原語。Znode指Zookeeper操作和維護一個小型的數據節點,類似於文件系統的層級樹狀結構進行管理。

    圖:Zookeeper數據結構示例

·  /作為跟目錄節點,根節點下有/workers和/tasks兩個字節點。

· /workers節點作為父節點,其下的每一個znode子節點用於保存系統中的可用worker,節點中保存着worker子節點的元數據信息。

· /tasks節點作為父節點,其下每個znode子節點用於保存一個代執行的任務,節點中保存着任務的詳細數據。

API概念

ZookeeperAPI提供了以下方法來實現地znode的控制。

create /path data

  創建一個名為/path的znode節點,並包含數據data。

delete /path

  刪除名為/path的znode。

exists /path

  檢查是否存在名為/path的節點。

setData /path data

  設置名為/path的znode節點的數據為data。

getData /path

  返回名為/path節點的數據信息。

getChildren /path

  返回所有/path節點的所有子節點列表。

Znode類型

在新建znode時,需要指定該節點類型,不同的類型決定了znode節點的行為方式,Zookeeper提供了4種不同的znode類型。

持久節點和臨時節點

znode節點可以是持久(persistent)節點,還可以是臨時(ephemeral)節點。持久的znode,如/path,只能通過delete來進行刪除。臨時的znode與之相反,當創建的客戶端崩潰或者關閉了與Zookeeper的連接時,這個節點就會被刪除。

持久znode是一種非常有用的znode,可以通過持久類型znode為應用保存一些數據,即使znode的創建者不再屬於應用系統時,數據也可以保存下來而不丟失。例如,在主-從模式中,需要保存從節點的任務分配情況,即使分配任務的主節點已經崩潰了。

一個臨時znode,在以下兩種情況會被刪除:

  1. 當創建該znode的客戶端的會話因超時或主動關閉而中止時。
  2. 當某個客戶端(不一定是創建者)主動刪除該節點時。

注:因為臨時的znode在其創建者的會話過期時被刪除,所以我們現在不允許臨時節點擁有子節點。

有序節點

一個有序znode節點被分配唯一一個單調遞增的整數。當創建有序節點時,一個序號會被追加到路徑之後。例如,客戶端創建一個有序znode節點,其路徑為/tasks/task-,那麼該znode節點為/tasks/task-1。有序znode通過提供了創建具有唯一名稱的znode的簡單方式。同時也通過這種方式可以直觀地查看znode的創建順序。

Znode一共有四種:持久節點、臨時節點、持久有序節點,臨時有序節點。

監視與通知

Zookeeper提供基於通知的機制提供對節點的監視能力。客戶端向Zookeeper註冊需要接收通知的znode,通過對znode的設置監視點(watch)來接收通知。監視點是一個單次觸發的操作,每次設置只會觸發一次。為了接收多個通知,客戶端必須在每次通知後設置新的監視點。在設置新監視點前客戶端會先讀取znode中的數據,所以不同擔心會錯過狀態的變化。

Zookeeper可以定義不同類型的通知,如監控znode的數據變化、監控znode子節點的變化、監控znode的創建或刪除。

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

【其他文章推薦】

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

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

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

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

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

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

分類
發燒車訊

Jmeter系列(34)- 詳解 Counter 計數器

如果你想從頭學習Jmeter,可以看看這個系列的文章哦

https://www.cnblogs.com/poloyy/category/1746599.html

 

簡單介紹

  • 計數器的作用:循環遞增生成数字
  • 計數器使用 long 來存儲值,因此取值範圍是 -2 ^ 63 2 ^ 63-1 
  • 可以在線程組任意地方添加計數器

 

計數器

 

計數器界面介紹

 

字段介紹

字段 含義
Starting value 初始值,long 整型,默認 0
Increment 每次迭代的遞增值,默認 0,表示不增加
Maximum value 最大值,包含此值
Number format 数字可選格式
Exported Variable Name 引用名稱
Track counter independently for rach user 每個用戶都有一個獨立的計數器
Reset counter on each Thread Group Iteration 每次線程組迭代時計數器將重置為初始值

 

最基礎的栗子

只有計數器的情況下的栗子

線程組結構樹

 

線程組屬性

共有 15 個線程,模擬 15 個用戶

 

計數器

計數器最多循環計數 10 次

 

運行結果

可以看到,因為有 15 個用戶,但計數器最多循環計數 10 次,所以第一輪循環結束後會重頭開始

 

計數器 + 循環控制器的栗子

線程組結構樹

 

線程組屬性

共有 5 個線程,模擬 5 個用戶

 

循環控制器

每個線程運行 3 次

 

計數器

計數器最多循環計數 30 次

 

未勾選【與每用戶獨立的跟蹤計數器】的運行結果

可以看到

  • 因為有 5 個線程,每個線程循環 3 次,一共 15 個請求,所以計數器是循環了 15 次
  • 此時計數器是對所有線程共享的,屬於線程組全局計數器,所以計數器是累計循環了 15 次

 

勾選【與每用戶獨立的跟蹤計數器】的運行結果

可以看到

  • 每個線程運行時,計數器都是從初始值算起的
  • 此時計數器是每個線程獨享的,不再是公共計數器,所以每次有新的線程運行時,都是新的計數器開始循環計數

 

計數器的一些注意事項

使用計數器生成的變量,值的類型為 string,所以有比較之類的操作時,需要帶 “” 操作

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

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

※回頭車貨運收費標準

分類
發燒車訊

我家也有太陽能:屋頂型太陽能對鳥兒的致命吸引力

文:宋瑞文(加州能源特約撰述)

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

【其他文章推薦】

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

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

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

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

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

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