標籤: 網頁設計公司

  • 另類思考!電動車熱未必有害原油、說不定恰恰相反?

    另類思考!電動車熱未必有害原油、說不定恰恰相反?

    國際車廠爭相研發電動車,汽油車似乎被宣判死刑,原油也會跟著步上末日。不過有專家大膽推估,電動車發展會削減流入原油業的資金,油市將汰弱留強,油價會維持低檔,也許將阻礙電動車普及之路。

    加拿大能源私募基金龍頭ARC Financial Corp.首席能源經濟學家Peter Tertzakian,13日在OilPrice發文稱,上週Volvo宣布,2019年起只生產電動車、停產汽柴油車。法國政府也宣布,2040年起停售汽柴油車。與此同時,特斯拉創辦人馬斯克貼出平價電動車Model 3照片,讓油市陷入低迷。

    電動車發展迅速,不少人認為原油業末日倒數計時。但是Tertzakian不以為然,指出環保車買氣疲弱。2013~2014年中,油價處於每桶100美元的高點時,美國電動車銷售提高,可是之後油價崩盤,電動車動能隨之消散。能源有兩股相反力量,現在的消費趨勢是原油便宜、使用率高,相對的,電動車滲透率低迷。但是市場對未來的預期恰恰相反,斷言原油將亡。

    未來幾年究竟會如何發展,Tertzakian推測有兩種可能結果,一是資本緊縮,汰除沒有效率的生產商,剩餘業者將不斷創新壓低成本,在低油價時也能存活,油價會繼續低迷,消費者缺乏誘因改買電動車。第二種可能是資本投資緊縮,將使原油供給下降,帶動油價上揚,電動車需求因此大增。諷刺的是,厲害的油商在兩種情況下,都能有好表現。可以確定的是,未來投入原油業的資金勢必減少。

    原油期貨報價從去(2016)年12月開始一路往下盤跌,高盛全球原物料研究部主管Jeffrey Currie對這提出了一個簡單解釋:市場上的錢太多。

    CNBC報導,Currie 12日在接受專訪時指出,市場上的原油其實並不是太多,只是錢潮淹腳目、委實過多。舉例來說,人們對石油探鑽活動的投資,是墨西哥最近能發現十數億桶原油的主因。

    Currie說,石油市場正在試著尋找供需均衡點,而產油商的損益兩平點在跌破高盛2016年估計的50-55美元後,新的平衡點在哪,則變得愈來愈難預測。不過,在被問到油市有沒有可能崩盤時,Currie說雖然他希望看到市場能有些波動,但其實崩盤機率極低。

    (本文內容由授權使用。圖片出處:public domain CC0)

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

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 特斯拉啟用臺南奇美博物館超級充電站,下一站臺中七期將啟用

    特斯拉啟用臺南奇美博物館超級充電站,下一站臺中七期將啟用

    特斯拉(Tesla) 今日(7/20) 啟用臺灣第二組超級充電站,為全亞洲第一組設在博物館園區的超級充電站。而第三組超級充電站則會在下週於臺中七期啟用。

    此次啟用的台南超級充電站位於台南奇美博物館,是全亞洲第一組設立於博物館的超級充電設施,共有8 組超級充電設備,足以應付開車環島遊,以及跨縣市長途旅遊等的需要。這次的項目更獲得台南政府大力支持,鼓勵特斯拉對於環保理念的實踐。特斯拉在台的首座超級充電站設於台北花博園區,該充電站提供6 組超級充電設備。

    下週特斯拉將啟用位於臺中七期的第三組超級充電站,鄰近國道一號臺中交流,方便南來北往的特斯拉車主休息同時,快速充電再上路。

    特斯拉的超級充電站充電30 分鐘,就能供應行駛270 公里所需電力,為臺北臺南間七成路程。而設在百貨公司的目的地充電站,已經從臺灣頭的翡翠灣,到臺灣尾的墾丁,遍佈14 個縣市,超過80 個地點,250 支的目的地充電座。

    (合作媒體:。圖片出處:特斯拉提供)  

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

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Gogoro 雲嘉地區電池交換站正式啟用,暢騎基隆到屏東

    Gogoro 雲嘉地區電池交換站正式啟用,暢騎基隆到屏東

    Gogoro 車主們期待已久的雲嘉地區電池交換站21 日正式開通提供服務。未來,Gogoro 的車主將可以騎乘Smartscooter 智慧雙輪縱貫台灣西半部,一路從基隆暢騎到屏東。

    Gogoro 於21 日宣布,雲林嘉義地區首批5 座GoStation 電池交換站正式上線,其中兩站位於雲林斗六、虎尾;另三站則分布於嘉義市區。自從Gogoro 於2015 年在台北市設立首座電池交換站以來,短短兩年的時間,已經建置將近400 座電池交換站,廣布於基隆到屏東的各個縣市,在六都甚至不到一公里就設有一站,累積提供將近500 萬次的電池交換服務,是全世界最穩定的電池交換能源網路系統。

    Gogoro 台灣區行銷總監陳彥揚表示: 「雲嘉地區GoStation 電池交換站的開通有3 個劃時代的意義。首先,我們顛覆消費者對於電動機車續航力不足的刻板印象;兩年前,沒有人會相信,我們可以騎乘電動機車橫跨北高。其次,對Gogoro 車主而言,Gogoro 將不再僅是都會的通勤工具,而是更進一步深入他們的生活,成為跨縣市旅遊的另一種交通選擇;最後,我們則提供雲嘉地區民眾一個能夠改善當地空污,讓生活環境更環保、更健康的解決方案。」

    日前宣布Gogoro 2 系列預購超過13,000 台,震撼市場的Gogoro 在雲林嘉義地區提供電池交換站的服務後,搭配雲嘉地區相對較高的電動機車補助,將加速當地居民的購車意願。目前,雲林縣汰換二行程機車購買電動機車的總補助金額達34,000 元,嘉義市為31,000 元,嘉義縣則為21,000 元。Gogoro 也正規劃在不久的將來於雲嘉地區增設銷售及維修據點,希望很快可以讓雲嘉的居民享有Gogoro 完整的銷售、維修、電池交換等全方位服務。

      (合作媒體:。圖片出處:科技新報)

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

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

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

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

  • 日產傳退出EV電池生產業務、子公司AES賣中國GSR

    日產傳退出EV電池生產業務、子公司AES賣中國GSR

    朝日新聞22日報導,日產汽車(Nissan)將退出電動車(EV)用電池的生產業務,旗下車用電池生產子公司「Automotive Energy Supply(以下簡稱AES)」將出售給中國投資基金「GSR」,出售額預估為1,000億日圓,雙方預計會在今年夏天達成共識並對外發表。日產在退出EV電池生產業務後,會將資源集中在最先端電池的研發上。

    報導指出,鋰離子電池是決定EV競爭力的關鍵,而日系廠商原先是該領域的霸主,不過近年來,南韓、中國廠商呈現顯著成長,加上AES銷售通路有限,因此日產研判擁有EV電池生產業務不具備太多好處。

    AES為日產、NEC分別出資51%、49%於2007年設立的公司,主要生產日產電動車Leaf或油電混合車(HV)所需的鋰離子電池。

    根據調查公司Techno Systems Research指出,2014年Panasonic車用鋰離子電池全球市佔率高達47%,而2015年雖維持首位、但市佔率萎縮至34%;反觀「其他廠商」市佔率從14%飆增至33%,而在「其他廠商」中、比亞迪(BYD)等中國廠商佔了大多數,LG化學、三星SDI等南韓廠商也在後猛追。

    中國對電池市場虎視眈眈,成長最快的電池廠「寧德時代新能源科技股份」(簡稱寧德時代或CATL)已誓言要在2020年底前,使電池產量超越特斯拉(Tesla)、Panasonic合資的「Gigafactory」超大電池廠。

    英國金融時報3月5日報導,北京已呼籲企業在2020年底前將車用電池產能拉高一倍,還鼓勵業者前往海外投資。高盛預估,鋰電池將是未來10年的關鍵技術,估計到了2025年市場總值上看400億美元、且主要會由中國主導。

    寧德時代行銷主任Neil Yang預測,雖然超越日本、南韓業者並非易事,但預估未來10年內,全球只會剩下10家鋰電池製造商,其中60%的市場會由前三大業者拿下。他也透露寧德時代要將觸角探至全球的野心,聲稱該公司希望跟特斯拉合作,也一直在跟通用汽車(GM)接洽,而福斯(Volkswagen)、BMW則已都是他們的客戶。

    寧德時代預計要在2020年將鋰電池總產能拉升至50GWh,而中國整體的鋰電池產能則合計會達121GWh。相較之下,特斯拉則希望在明(2018)年將鋰電池產能拉升至35GWh。每1個GWh的電力,可供應40,000輛電動車、每部行駛100公里所需。中國已在2013年超越南韓成為全世界最大的鋰電池供應國。

    (本文內容由MoneyDJ授權使用。圖片出處:public domain CC0)

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

    【其他文章推薦】

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Gogoro 太陽能電池交換站,在新北八里驅動示範

    Gogoro 太陽能電池交換站,在新北八里驅動示範

    新創科技公司 Gogoro 睿能創意股份有限公司,28 日宣布其與新北市政府合作的首座太陽能電池交換站,正式在新北市八里區開始營運。隨著這個搭配太陽能面板的電池交換站的啟動,Gogoro 對永續能源的承諾邁出更關鍵的一步。   這座位於八里十三行博物館附近(新北市八里區文昌路與文昌一街交叉口)的 Gogoro 八里公兒四電池交換站配置 2.3kW 太陽能面板,依據天氣環境等因素,每天可以產生大約 6.21 kWH 的電力,是第一座以再生能源提供電力的 GoStation 電池交換站。太陽能電力不但可以提供電池交換站的 Gogoro 電池使用,降低發電時所產生的二氧化碳排放,更可以與全台電力網路連結,參與整體網路的電量調節。   Gogoro 執行長陸學森表示 :「Gogoro 致力發展潔淨的智慧能源,希望透過具備能源調度能力的智慧電網,成為城市的電力調節樞紐,以促成電力平衡。與新北市政府合作的太陽能八里電池交換示範站為我們上述的目標邁出了重要的一步。」   新北市政府則表示,市府一直在積極尋找降低溫室氣體排放、改善空氣污染的積極方法,與 Gogoro 合作成立全世界第一座太陽能電池交換站具有標竿式的意義。電動機車零排放的特性不但可以提升市區的空氣品質,以再生能源為來源的電力,更可以降低交通工具所帶來的碳排放。未來也將持續努力發展以再生能源補充電力的相關設施,做為新北市重要的基礎設施。   這座太陽能電池交換示範站,設有物聯網智慧平台,透過分析供電情況的螢幕,說明了包括減少碳排量,減少樹木砍伐面積,綠能總儲電量,城市電網 ,太陽能發電量等訊息,讓每名換電的民眾,清楚的知道,自己對環境的貢獻度,也具有相當程度的教育意義。   Gogoro 目前擁有 25,000 名車主,總共累計將近 500 萬次的電池交換,9,000 萬公里的總里程數,已經替地球減少 720 萬公斤的二氧化碳排放,隨著未來再生能源比例逐漸提升,Gogoro 的車主們將更對地球與環境產生更多的正面影響力。Gogoro 為鼓勵車主響應綠能電池交換站,也設計了特別的機制。凡是造訪八里公兒四站,並完成電池交換的車主,即可在 Gogoro App 上獲得「阿波羅的力量」徽章。   (合作媒體:。圖片出處:科技新報)  

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

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • MachO文件詳解–逆向開發

    MachO文件詳解–逆向開發

    今天是逆向開發的第5天內容–MachO文件(Mac 和 iOS 平台可執行的文件),在逆向開發中是比較重要的,下面我們着重講解一下MachO文件的基本內容和使用。

    一、MachO概述

    1. 概述

    Mach-O是Mach Object文件格式的縮寫,iOS以及Mac上可執行的文件格式,類似Window的exe格式,Linux上的elf格式。Mach-O是一個可執行文件、動態庫以及目標代碼的文件格式,是a.out格式的替代,提供了更高更強的擴展性。

    2.常見格式

    Mach-O常見格式如下:

    • 目標文件 .o
    • 庫文件
    1. .a
    2. .dylib
    3. .framework
    • 可執行文件
    • dyld
    • .dsym

      通過file文件路徑查看文件類型

    我們通過部分實例代碼來簡單研究一下。

    2.1目標文件.o

    通過test.c 文件,可以使用clang命令將其編譯成目標文件.o

    我們再通過file命令(如下)查看文件類型

    是個Mach-O文件。

    2.2 dylib

    通過cd /usr/lib命令查看dylib

    通過file命令查看文件類型

     

     2.3 .dsym

    下面是一個截圖來說明.dsym是也是Mach-O文件格式

     

    以上只是Mach-O常見格式的某一種,大家可以通過命令來嘗試。

    3. 通用二進制文件

    希望大家在了解App二進制架構的時候,可以先讀一下本人的另一篇博客關於armv7,armv7s以及arm64等的介紹。

    通用二進制文件是蘋果自身發明的,基本內容如下

    下面通過指令查看Macho文件來看下通用二進制文件

     

    然後通過file指令查看文件類型

     

    上面該MachO文件包含了3個架構分別是arm v7,arm v7s 以及arm 64 。

    針對該MachO文件我們做幾個操作,利用lipo命令拆分合併架構

    3.1 利用lipo-info查看MachO文件架構

    3.2 瘦身MachO文件,拆分

    利用lipo-thin瘦身架構

     

     查看一下結果如下,多出來一個新建的MachO_armv7

     

    3.3 增加架構,合併

    利用lipo -create 合併多種架構

    發現多出一種框架,合併成功多出Demo可執行文件。結果如下:

     

    整理出lipo命令如下:

     

    二、MachO文件

    2.1 文件結構

    下面是蘋果官方圖解釋MachO文件結構圖

    MachO文件的組成結構如上,看包括了三個部分

    • Header包含了該二進制文件的一般信息,信息如下:
    1. 字節順序、加載指令的數量以及架構類型
    2. 快速的確定一些信息,比如當前文件是32位或者64位,對應的文件類型和處理器是什麼
    • Load commands 包含很多內容的表
    1. 包括區域的位置、動態符號表以及符號表等
    • Data一般是對象文件的最大部分
    1. 一般包含Segement具體數據

    2.2 Header的數據結構

    在項目代碼中,按下Command+ 空格,然後輸入loader.h  

    然後查看loader.h文件,找到mach_header

    上面是mach_header,對應結構體的意義如下:

    通過MachOView查看Mach64 Header頭部信息

    2.3 LoadCommands

    LoadCommand包含了很多內容的表,通過MachOView查看LoadCommand的信息,圖如下:

     

    但是大家看的可能並不了解內容,下面有圖進行註解,可以看下主要的意思

    2.4 Data

    Data包含Segement,存儲具體數據,通過MachOView查看,地址映射內容

     

    三、DYLD

    3.1 dyld概述

    dyld(the dynamic link editor)是蘋果動態鏈接器,是蘋果系統一個重要的組成部分,系統內核做好準備工作之後,剩下的就會交給了dyld。

    3.2 dyld加載過程

    程序的入口一般都是在main函數中,但是比較少的人關心main()函數之前發生了什麼?這次我們先探索dyld的加載過程。(但是比在main函數之前,load方法就在main函數之前)

    3.2.1 新建項目,在main函數下斷

     

    main()之前有個libdyld.dylib start入口,但是不是我們想要的,根據dyld源碼找到__dyld_start函數

    3.2.2 dyld main()函數

    dyld main()函數是關鍵函數,下面是函數實現內容。(此時的main實現函數和程序App的main 函數是不一樣的,因為dyld也是一個可執行文件,也是具有main函數的

    //
    // Entry point for dyld.  The kernel loads dyld and jumps to __dyld_start which
    // sets up some registers and call this function.
    //
    // Returns address of main() in target program which __dyld_start jumps to
    //
    uintptr_t
    _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 
            int argc, const char* argv[], const char* envp[], const char* apple[], 
            uintptr_t* startGlue)
    {
        // Grab the cdHash of the main executable from the environment
        // 第一步,設置運行環境
        uint8_t mainExecutableCDHashBuffer[20];
        const uint8_t* mainExecutableCDHash = nullptr;
        if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) )
            // 獲取主程序的hash
            mainExecutableCDHash = mainExecutableCDHashBuffer;
    
        // Trace dyld's load
        notifyKernelAboutImage((macho_header*)&__dso_handle, _simple_getenv(apple, "dyld_file"));
    #if !TARGET_IPHONE_SIMULATOR
        // Trace the main executable's load
        notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file"));
    #endif
    
        uintptr_t result = 0;
        // 獲取主程序的macho_header結構
        sMainExecutableMachHeader = mainExecutableMH;
        // 獲取主程序的slide值
        sMainExecutableSlide = mainExecutableSlide;
    
        CRSetCrashLogMessage("dyld: launch started");
        // 設置上下文信息
        setContext(mainExecutableMH, argc, argv, envp, apple);
    
        // Pickup the pointer to the exec path.
        // 獲取主程序路徑
        sExecPath = _simple_getenv(apple, "executable_path");
    
        // <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
        if (!sExecPath) sExecPath = apple[0];
    
        if ( sExecPath[0] != '/' ) {
            // have relative path, use cwd to make absolute
            char cwdbuff[MAXPATHLEN];
            if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) {
                // maybe use static buffer to avoid calling malloc so early...
                char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2];
                strcpy(s, cwdbuff);
                strcat(s, "/");
                strcat(s, sExecPath);
                sExecPath = s;
            }
        }
    
        // Remember short name of process for later logging
        // 獲取進程名稱
        sExecShortName = ::strrchr(sExecPath, '/');
        if ( sExecShortName != NULL )
            ++sExecShortName;
        else
            sExecShortName = sExecPath;
        
        // 配置進程受限模式
        configureProcessRestrictions(mainExecutableMH);
    
    
        // 檢測環境變量
        checkEnvironmentVariables(envp);
        defaultUninitializedFallbackPaths(envp);
    
        // 如果設置了DYLD_PRINT_OPTS則調用printOptions()打印參數
        if ( sEnv.DYLD_PRINT_OPTS )
            printOptions(argv);
        // 如果設置了DYLD_PRINT_ENV則調用printEnvironmentVariables()打印環境變量
        if ( sEnv.DYLD_PRINT_ENV ) 
            printEnvironmentVariables(envp);
        // 獲取當前程序架構
        getHostInfo(mainExecutableMH, mainExecutableSlide);
        //-------------第一步結束-------------
        
        // load shared cache
        // 第二步,加載共享緩存
        // 檢查共享緩存是否開啟,iOS必須開啟
        checkSharedRegionDisable((mach_header*)mainExecutableMH);
        if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
            mapSharedCache();
        }
        ...
    
        try {
            // add dyld itself to UUID list
            addDyldImageToUUIDList();
    
            // instantiate ImageLoader for main executable
            // 第三步 實例化主程序
            sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
            gLinkContext.mainExecutable = sMainExecutable;
            gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
    
            // Now that shared cache is loaded, setup an versioned dylib overrides
        #if SUPPORT_VERSIONED_PATHS
            checkVersionedPaths();
        #endif
    
    
            // dyld_all_image_infos image list does not contain dyld
            // add it as dyldPath field in dyld_all_image_infos
            // for simulator, dyld_sim is in image list, need host dyld added
    #if TARGET_IPHONE_SIMULATOR
            // get path of host dyld from table of syscall vectors in host dyld
            void* addressInDyld = gSyscallHelpers;
    #else
            // get path of dyld itself
            void*  addressInDyld = (void*)&__dso_handle;
    #endif
            char dyldPathBuffer[MAXPATHLEN+1];
            int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN);
            if ( len > 0 ) {
                dyldPathBuffer[len] = '\0'; // proc_regionfilename() does not zero terminate returned string
                if ( strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0 )
                    gProcessInfo->dyldPath = strdup(dyldPathBuffer);
            }
    
            // load any inserted libraries
            // 第四步 加載插入的動態庫
            if  ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
                for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
                    loadInsertedDylib(*lib);
            }
            // record count of inserted libraries so that a flat search will look at 
            // inserted libraries, then main, then others.
            // 記錄插入的動態庫數量
            sInsertedDylibCount = sAllImages.size()-1;
    
            // link main executable
            // 第五步 鏈接主程序
            gLinkContext.linkingMainExecutable = true;
    #if SUPPORT_ACCELERATE_TABLES
            if ( mainExcutableAlreadyRebased ) {
                // previous link() on main executable has already adjusted its internal pointers for ASLR
                // work around that by rebasing by inverse amount
                sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
            }
    #endif
            link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
            sMainExecutable->setNeverUnloadRecursive();
            if ( sMainExecutable->forceFlat() ) {
                gLinkContext.bindFlat = true;
                gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
            }
    
            // link any inserted libraries
            // do this after linking main executable so that any dylibs pulled in by inserted 
            // dylibs (e.g. libSystem) will not be in front of dylibs the program uses
            // 第六步 鏈接插入的動態庫
            if ( sInsertedDylibCount > 0 ) {
                for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                    ImageLoader* image = sAllImages[i+1];
                    link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
                    image->setNeverUnloadRecursive();
                }
                // only INSERTED libraries can interpose
                // register interposing info after all inserted libraries are bound so chaining works
                for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                    ImageLoader* image = sAllImages[i+1];
                    image->registerInterposing();
                }
            }
    
            // <rdar://problem/19315404> dyld should support interposition even without DYLD_INSERT_LIBRARIES
            for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
                ImageLoader* image = sAllImages[i];
                if ( image->inSharedCache() )
                    continue;
                image->registerInterposing();
            }
            ...
    
            // apply interposing to initial set of images
            for(int i=0; i < sImageRoots.size(); ++i) {
                sImageRoots[i]->applyInterposing(gLinkContext);
            }
            gLinkContext.linkingMainExecutable = false;
            
            // <rdar://problem/12186933> do weak binding only after all inserted images linked
            // 第七步 執行弱符號綁定
            sMainExecutable->weakBind(gLinkContext);
    
            // If cache has branch island dylibs, tell debugger about them
            if ( (sSharedCacheLoadInfo.loadAddress != NULL) && (sSharedCacheLoadInfo.loadAddress->header.mappingOffset >= 0x78) && (sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset != 0) ) {
                uint32_t count = sSharedCacheLoadInfo.loadAddress->header.branchPoolsCount;
                dyld_image_info info[count];
                const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCacheLoadInfo.loadAddress + sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset);
                // <rdar://problem/20799203> empty branch pools can be in development cache
                if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) {
                    for (int poolIndex=0; poolIndex < count; ++poolIndex) {
                        uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheLoadInfo.slide;
                        info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr;
                        info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands";
                        info[poolIndex].imageFileModDate = 0;
                    }
                    // add to all_images list
                    addImagesToAllImages(count, info);
                    // tell gdb about new branch island images
                    gProcessInfo->notification(dyld_image_adding, count, info);
                }
            }
    
            CRSetCrashLogMessage("dyld: launch, running initializers");
            ...
            // run all initializers
            // 第八步 執行初始化方法
            initializeMainExecutable(); 
    
            // notify any montoring proccesses that this process is about to enter main()
            dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN_DYLD2, 0, 0);
            notifyMonitoringDyldMain();
    
            // find entry point for main executable
            // 第九步 查找入口點並返回
            result = (uintptr_t)sMainExecutable->getThreadPC();
            if ( result != 0 ) {
                // main executable uses LC_MAIN, needs to return to glue in libdyld.dylib
                if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
                    *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
                else
                    halt("libdyld.dylib support not present for LC_MAIN");
            }
            else {
                // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
                result = (uintptr_t)sMainExecutable->getMain();
                *startGlue = 0;
            }
        }
        catch(const char* message) {
            syncAllImages();
            halt(message);
        }
        catch(...) {
            dyld::log("dyld: launch failed\n");
        }
        ...
        
        return result;
    }

    View Code

    摺疊開dyld main函數,步驟總結如下

    對待dyld的講述,是非常不易的,因為本身過程是比較複雜的,上面僅僅是自身的抽出來的。下面再畫一張流程圖,幫助大家理解。

     

    四、總結

    MachO文件對於逆向開發是非常重要的,通過本次講解,希望對大家理解逆向開發有所幫助,也希望大家真正可以提高技術,應對iOS市場的大環境,下一篇我們將講述Hook原理–逆向開發。謝謝!!!

     

     

     

     

     

     

     

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

    ※帶您來了解什麼是 USB CONNECTOR  ?

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

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

  • .NET資源泄露與處理方案

    .NET雖然擁有強大易用的垃圾回收機制,但並不是因為這樣,你就可以對資源管理放任不管,其實在稍不注意的時候,可能就造成了資源泄露,甚至因此導致系統崩潰,到那時再來排查問題就已經是困難重重。

    ==========    ==========

    一、知識點簡單介紹

    常見的資源泄露有:

    • 內存泄漏:非託管資源沒有釋放、非靜態對象註冊了靜態實例。
    • GDI泄露:字體。
    • 句柄泄露:Socket或線程。
    • 用戶對象泄露:移除的對象未釋放。

    二、具體實例

    1. 內存泄漏

    很常見的現象是分不清哪些對象需要釋放,對於控件、Stream等一些非託管資源也只管新增,卻沒有釋放,功能是實現了,卻埋了顆不小的雷。

    private void button1_Click(object sender, EventArgs e)
    {
        for(int i=0;i<1000;i++)
            this.Controls.Add(new TabPage());
    }
    private void button1_Click(object sender, EventArgs e)
    {
        new Form2.ShowDialog();
    }

    如果你覺得寫這樣的代碼很Cool,很簡潔,你在項目中也有這麼寫代碼,那你就碰到大麻煩了,你試試在上面Form2中開個大一點的數組來檢查內存,然後運行,按幾下按鈕,你就會發現,內存一直增加,即使你調用了GC也無濟於事。所以,對於此類非託管資源要記住釋放,用完即廢可以採用using關鍵字。

    public Form2()
    {
        InitializeComponent();
        MyApp.FormChanged += FormChanged;
    }

    上面這個例子中,MyApp是一個靜態類,如果在實例對象中向這種類裏面註冊了事件,而又沒有取消註冊,這樣也會遇到大麻煩,即使在外部已經記得調用了Form2的Dispose也是沒用的。

    解決方案

    • 注意託管資源和非託管資源的釋放區別,非託管資源是需要手動釋放的。
    • ,如上面的ShowDialog問題。(using中還起到了try-catch的作用,避免由於異常未調用Dispose的情況)
    • 使用UnLoad事件或者析構函數,對註冊的全局事件進行取消註冊。
    • 特別注意自定義組件的穩定性更重要,發生問題時影響也更廣。

    2. GDI泄露

    一般會跟字體相關,例如我曾在Android上用Cocos2d做一個小遊戲時頻繁地切換字體、Dev控件的Font屬性賦值也會有這種現象。

    XXX.Font = new Font(...)

    解決方案

    • 這個問題我目前是採用字體池來解決,類似線程池的概念,相同Key值取同一個對象。若有更好方案歡迎留言討論

    3. 句柄泄露

    一般跟Socket和Thread(線程)有關

    for(int i=0;i<1000;i++){
        new Thread(()=>{
            Thread.Sleep(1000);
        }).Start();
    }

    解決方案

    • Socket的場景暫時沒遇到。
    • 線程問題採用線程池相關的輔助類能有效解決,例如ThreadPool、Task、Parallel。

    4. 用戶對象泄露

    一般跟移除的對象未釋放有關

    private void button1_Click(object sender, EventArgs e)
    {
        tab.Remove(tabPage);
    }

    三、最後特別奉送一個內存釋放的大招

    [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
    public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
    /// <summary>    
    /// 釋放內存    
    /// </summary>    
    public static void ClearMemory()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
        {
            SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
        }
    }

    調用以上API能讓你的內存一下爆減,是不是很給力,一調用內存就降下來了。But,先別高興太早,這其實是偽釋放,只是暫時解決內存大量泄漏導致系統崩潰的應急處理方案。具體原因參考:,關鍵信息:物理內存轉虛擬內存,涉及磁盤讀寫。好處壞處都貼出來了,是否需要使用請君自己斟酌。

    四、總結

    實際上由於各個開發人員的水平跟接觸面不同,又沒有經過統一的培訓(各個人對資源釋放的理解與關注度不同,或者寫代碼時就沒考慮內存未被釋放這種問題),發現問題的時候項目往往已經做到了一個階段,系統也比較龐大了,這種時候才發現內存泄露的問題確實是很頭疼的。

    • 資源泄露的場景往往是相互關聯的,發生最多的就是內存泄漏,而除了寫法可能有問題外,也可能是因為句柄泄露或用戶對象泄露引起的。

    五、參考資料

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • AutoCad 二次開發 文字鏡像

    AutoCad 二次開發 文字鏡像

    AutoCad 二次開發 文字鏡像

    參考: 在autocad中如果使用Mirror命令把塊參照給鏡像了(最終得到一個對稱的塊),塊裏面的文字包括DBText和MText以及標註上面的文字都會被對稱,變得不易閱讀。而在單個字體實體和標註實體鏡像的時候只要設置系統變量mirrtext為0鏡像后的文字就不會與原文字對稱變成我們未學習過的文字了。   所以我們在鏡像塊的時候就可以先把塊炸開是用快捷鍵X,或者輸入explode,然後在使用鏡像命令。之後在把對稱后的實體集合組成一個新的塊。不過這樣操作十分的繁瑣,我覺得其中這樣做的優勢是mirror時的jig操作可以很方便的預先知道我們想要的對稱后的結果。但如果用代碼實現這種jig操作,我覺得有點複雜,目前我還不知道怎麼實現。   我要講的主要就是用代碼來實現塊的鏡像。難點就在與文字的鏡像,和標註的鏡像。這篇文章先講文字的鏡像。文字鏡像的主要步驟分為: 1.找到鏡像前文字邊界的四個角,這四個角構成了一個矩形,我們要求得這個矩形的長和寬所代表的向量。 2.判斷文字鏡像后的方向,如果是偏向朝Y軸鏡像,那麼文字鏡像后的方向是沿着X軸翻轉的,如果是偏向朝X軸鏡像,那麼文字鏡像后的方向是沿着X軸翻轉的。這裏我以沿着Y軸鏡像為例子。 3.移動鏡像后切被翻轉后的文字,這裏也是根據鏡像軸的不同,需按不同的向量來移動。   詳細情況見圖: 圖中左邊是要鏡像的文字,文字上的藍色線,和黃色線是我調試的時候加入的,黃線左端是 pt1,右端是pt2,藍線左端是pt3,右端是pt4。 中間的豎線是Y軸鏡像線,右邊就是不同情況下鏡像后的文字。其中黃色部分表示正確的鏡像結果,紅色部分表示:鏡像后延第一個步驟移動后求得的向量移動了文字的position但是沒翻轉的結果。黑色部分表示:鏡像后翻轉了文字但文字的position沒有按向量移動的結果。 下面我就來仔細分析一下代碼: 要實現第一步驟,前提是要有一段P/Invoke的代碼: 其中 引入的acdb22.dll是 autocad2018中的版本,不同版本,這個dll後面的数字不一樣。我們可以到cad安裝目錄下查找acdb幾個字,找到後面帶数字的就是了,64位的安裝目錄默認位置:C:\Program Files\Autodesk\AutoCAD 2018。這兩個函數一個是32位,一個是64位,具體用哪個後面的代碼會自動判斷。這個函數作用我覺得主要是求 這個name。   這裏用到了accore.dll,有的cad版本沒有這個dll,就用acad.exe代替就可以了。上面的acdbEntGet主要是根據entity的名字求的entity實體的Intptr,下面的函數時求的文字邊界對角點,這裏注意,我把這個兩個點用直線打印在cad空間里,發現它時在原點,沒旋轉的線,但其實文字不的position不在原點,也帶有旋轉角度。後面要求的文字邊界向量就是根據這兩個點來的。 上面求得的pt1,pt2 經過:
    pt1 = pt1.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
    pt2 = pt2.TransformBy(rotMat).Add(dbText.Position.GetAsVector()); 這種操作就得到了第一幅圖中的黃線。 在經過這樣的操作,得到的pt3 和pt4就是第一幅圖的藍線。這其中的rotDir和linDir就是我們要求得的寬和長代表的向量了,然後在把它給鏡像了得到的mirRotDir和mirLinDir就是鏡像后的文字要移動的向量了,這裏第一步就結束了。 第二步,第三步:   大的話,就說明文字需要朝X軸翻轉,所以這裏的IsMirroredInX=true就代表需要朝X軸翻轉。 緊接着下面句,如果沒加mirLineDir這個向量,就會出現第一幅圖中的畫黑線的情況,如果不加IsMirrorInX就會出現畫紅線的情況。 到這裏就全部結束了。 下面給出所有代碼:

    public class MyMirror
        {
            Document Doc = Application.DocumentManager.MdiActiveDocument;
            Editor Ed = Application.DocumentManager.MdiActiveDocument.Editor;
            Database Db = Application.DocumentManager.MdiActiveDocument.Database;
    
            List<Entity> list = new List<Entity>();
            List<ObjectId> listOId = new List<ObjectId>();
    
            [CommandMethod("testM")]
    
            public void MirrorTextCmd()
    
            {
    
                Document doc = Application.DocumentManager.MdiActiveDocument;
    
                Database db = doc.Database;
    
                Editor ed = doc.Editor;
    
    
    
                //Entity selection
    
                PromptEntityOptions peo = new PromptEntityOptions(
    
                    "\nSelect a text entity:");
    
    
    
                peo.SetRejectMessage("\nMust be text entity...");
    
                peo.AddAllowedClass(typeof(DBText), true);
    
    
    
                PromptEntityResult perText = ed.GetEntity(peo);
    
    
    
                if (perText.Status != PromptStatus.OK)
    
                    return;
    
    
    
                peo = new PromptEntityOptions("\nSelect a mirror line:");
    
                peo.SetRejectMessage("\nMust be a line entity...");
    
                peo.AddAllowedClass(typeof(Line), true);
    
    
    
                PromptEntityResult perLine = ed.GetEntity(peo);
    
    
    
                if (perLine.Status != PromptStatus.OK)
    
                    return;
    
    
    
                using (Transaction tr = db.TransactionManager.StartTransaction())
    
                {
    
                    Line line = tr.GetObject(perLine.ObjectId, OpenMode.ForRead)
    
                        as Line;
    
    
    
                    Line3d mirrorLine = new Line3d(
    
                        line.StartPoint,
    
                        line.EndPoint);
    
    
    
                    MirrorText(perText.ObjectId, mirrorLine);
    
    
    
                    tr.Commit();
    
                }
    
            }
    
    
    
            void MirrorText(ObjectId oId, Line3d mirrorLine)
    
            {
    
                Database db = oId.Database;
    
    
    
                using (Transaction tr = db.TransactionManager.StartTransaction())
    
                {
    
                    // Get text entity
    
                    DBText dbText = tr.GetObject(oId, OpenMode.ForRead)
    
                        as DBText;
    
    
    
                    // Clone original entity
    
                    DBText mirroredTxt = dbText.Clone() as DBText;
    
    
    
                    // Create a mirror matrix
    
                    Matrix3d mirrorMatrix = Matrix3d.Mirroring(mirrorLine);
    
    
    
                    // Do a geometric mirror on the cloned text
    
                    mirroredTxt.TransformBy(mirrorMatrix);
    
    
    
                    // Get text bounding box
    
                    Point3d pt1, pt2, pt3, pt4;
    
                    GetTextBoxCorners(
    
                        dbText,
    
                        out pt1,
    
                        out pt2,
    
                        out pt3,
    
                        out pt4);
    
    
    
                    // Get the perpendicular direction to the original text
    
                    Vector3d rotDir =
    
                        pt4.Subtract(pt1.GetAsVector()).GetAsVector();
    
    
    
                    // Get the colinear direction to the original text
    
                    Vector3d linDir =
    
                        pt3.Subtract(pt1.GetAsVector()).GetAsVector();
    
    
    
                    // Compute mirrored directions
    
                    Vector3d mirRotDir = rotDir.TransformBy(mirrorMatrix);
    
                    Vector3d mirLinDir = linDir.TransformBy(mirrorMatrix);
    
    
    
                    //Check if we need to mirror in Y or in X
    
                    if (Math.Abs(mirrorLine.Direction.Y) >
    
                        Math.Abs(mirrorLine.Direction.X))
    
                    {
    
                        // Handle the case where text is mirrored twice
    
                        // instead of doing "oMirroredTxt.IsMirroredInX = true"
    
                        mirroredTxt.IsMirroredInX = !mirroredTxt.IsMirroredInX;
    
                        mirroredTxt.Position = mirroredTxt.Position + mirLinDir;
    
                    }
    
                    else
    
                    {
    
                        mirroredTxt.IsMirroredInY = !mirroredTxt.IsMirroredInY;
    
                        mirroredTxt.Position = mirroredTxt.Position + mirRotDir;
    
                    }
    
    
    
                    // Add mirrored text to database
    
                    //btr.AppendEntity(mirroredTxt);
    
                    //tr.AddNewlyCreatedDBObject(mirroredTxt, true);
    
                    //list.Add(mirroredTxt);
                    mirroredTxt.ToSpace();
                    tr.Commit();
    
                }
    
            }
            #region p/Invoke
    
    
            public struct ads_name
            {
    
                public IntPtr a;
    
                public IntPtr b;
    
            };
    
    
    
            // Exported function names valid only for R19
    
    
    
            [DllImport("acdb22.dll",
    
                CallingConvention = CallingConvention.Cdecl,
    
                EntryPoint = "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AAY01JVAcDbObjectId@@@Z")]
    
            public static extern int acdbGetAdsName32(
    
                ref ads_name name,
    
                ObjectId objId);
    
    
    
            [DllImport("acdb22.dll",
    
                CallingConvention = CallingConvention.Cdecl,
    
                EntryPoint = "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AEAY01_JVAcDbObjectId@@@Z")]
    
            public static extern int acdbGetAdsName64(
    
                ref ads_name name,
    
                ObjectId objId);
    
    
    
            public static int acdbGetAdsName(ref ads_name name, ObjectId objId)
    
            {
    
                if (Marshal.SizeOf(IntPtr.Zero) > 4)
    
                    return acdbGetAdsName64(ref name, objId);
    
    
    
                return acdbGetAdsName32(ref name, objId);
    
            }
    
    
    
            [DllImport("accore.dll",
    
                CharSet = CharSet.Unicode,
    
                CallingConvention = CallingConvention.Cdecl,
    
                EntryPoint = "acdbEntGet")]
    
            public static extern System.IntPtr acdbEntGet(
    
                ref ads_name ename);
    
    
    
            [DllImport("accore.dll",
    
                CharSet = CharSet.Unicode,
    
                CallingConvention = CallingConvention.Cdecl,
    
                EntryPoint = "acedTextBox")]
    
            public static extern System.IntPtr acedTextBox(
    
                IntPtr rb,
    
                double[] point1,
    
                double[] point2);
    
    
    
            void GetTextBoxCorners(DBText dbText, out Point3d pt1, out Point3d pt2, out Point3d pt3, out Point3d pt4)
    
            {
    
                ads_name name = new ads_name();
    
    
    
                int result = acdbGetAdsName(
    
                    ref name,
    
                    dbText.ObjectId);
    
    
    
                ResultBuffer rb = new ResultBuffer();
    
    
    
                Interop.AttachUnmanagedObject(
    
                    rb,
    
                    acdbEntGet(ref name), true);
    
    
    
                double[] point1 = new double[3];
    
                double[] point2 = new double[3];
    
    
    
                // Call imported arx function
    
                acedTextBox(rb.UnmanagedObject, point1, point2);
    
    
    
                pt1 = new Point3d(point1);
    
                pt2 = new Point3d(point2);
    
                var ptX = pt1 + Vector3d.XAxis * 40;
                var ptY = pt2 + Vector3d.YAxis * 50;
    
    
                var lX = new Line(pt1, ptX);
                var lY = new Line(pt2, ptY);
    
                lX.Color= Color.FromColor(System.Drawing.Color.Green);
                lY.Color= Color.FromColor(System.Drawing.Color.Orange);
    
    
                Line line = new Line(pt1, pt2);
    
                line.Color = Color.FromColor(System.Drawing.Color.Red);
    
                line.ToSpace();
                lX.ToSpace();
                lY.ToSpace();
    
                // Create rotation matrix
    
                Matrix3d rotMat = Matrix3d.Rotation(
    
                    dbText.Rotation,
    
                    dbText.Normal,
    
                    pt1);
    
    
    
                // The returned points from acedTextBox need
    
                // to be transformed as follow
    
                pt1 = pt1.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
    
                pt2 = pt2.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
    
                Line linetrans = new Line(pt1, pt2);
    
                linetrans.Color = Color.FromColor(System.Drawing.Color.Yellow) ;
    
                linetrans.ToSpace();
    
    
                Vector3d rotDir = new Vector3d(
    
                    -Math.Sin(dbText.Rotation),
    
                    Math.Cos(dbText.Rotation), 0);
    
    
                //求垂直於rotDir和normal的法向量
                Vector3d linDir = rotDir.CrossProduct(dbText.Normal);
    
    
    
                double actualWidth =
    
                    Math.Abs((pt2.GetAsVector() - pt1.GetAsVector())
    
                        .DotProduct(linDir));
    
    
    
                pt3 = pt1.Add(linDir * actualWidth);
    
                pt4 = pt2.Subtract(linDir * actualWidth);
    
                Line linetrans2 = new Line(pt3, pt4);
    
                linetrans2.Color = Color.FromColor(System.Drawing.Color.Blue);
    
                linetrans2.ToSpace();
            }
    
            #endregion
        }

     

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

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Docker基礎與實戰,看這一篇就夠了

    Docker基礎與實戰,看這一篇就夠了

    docker 基礎

    什麼是Docker

    Docker 使用 Google 公司推出的 Go 語言 進行開發實現,基於 Linux 內核的 cgroupnamespace,以及 AUFS 類的 Union FS 等技術,對進程進行封裝隔離,屬於 操作系統層面的虛擬化技術。由於隔離的進程獨立於宿主和其它的隔離的進程,因此也稱其為容器。

    Docker 在容器的基礎上,進行了進一步的封裝,從文件系統、網絡互聯到進程隔離等等,極大的簡化了容器的創建和維護。使得 Docker 技術比虛擬機技術更為輕便、快捷。

    記住最重要的一點,Dokcer實際是宿主機的一個普通的進程,這也是Dokcer與傳統虛擬化技術的最大不同。

    為什麼要使用Docker

    使用Docker最重要的一點就是Docker能保證運行環境的一致性,不會出現開發、測試、生產由於環境配置不一致導致的各種問題,一次配置多次運行。使用Docker,可更快地打包、測試以及部署應用程序,並可減少從編寫到部署運行代碼的周期。

    docker 安裝

    • Docker 要求 CentOS 系統的內核版本高於 3.10 ,查看本頁面的前提條件來驗證你的CentOS 版本是否支持 Docker 。
      uname -r

    • 更新yum,升級到最新版本
      yum update

    • 卸載老版本的docker(若有)
      yum remove docker docker-common docker-selinux docker-engine
      執行該命令只會卸載Docker本身,而不會刪除Docker存儲的文件,例如鏡像、容器、卷以及網絡文件等。這些文件保存在/var/lib/docker 目錄中,需要手動刪除。

    • 查看yum倉庫,查看是否有docker
      ll /etc/yum.repos.d/

      如果用的廠商的服務器(阿里雲、騰訊雲)一般都會有docker倉庫,如果用的是虛擬機或者公司的服務器基本會沒有。

    • 安裝軟件包, yum-util 提供yum-config-manager功能,另外兩個是devicemapper驅動依賴的
      yum install -y yum-utils device-mapper-persistent-data lvm2

    • 安裝倉庫
      yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

    • 查看docker版本
      yum list docker-ce --showduplicates | sort -r

    • 安裝docker
      yum install docker-ce
      以上語句是是安裝最新版本的Docker,你也可以通過yum install docker-ce-<VERSION> 安裝指定版本

    • 啟動docker
      systemctl start docker

    • 驗證安裝是否正確
      dokcer run hello-world

    docker 重要命令

    鏡像相關

    • 搜索鏡像docker search
      docker search nginx Docker就會在Docker Hub中搜索含有“nginx”這個關鍵詞的鏡像倉庫

    • 下載鏡像docker pull
      docker pull nginx Docker就會在Docker Hub中下載含有“nginx”最新版本的鏡像
      當然也可以使用docker pull reg.jianzh5.com/nginx:1.7.9 下載指定倉庫地址標籤的nginx鏡像

    • 列出鏡像docker images

    • 刪除鏡像docker rmi
      docker rmi hello-world刪除我們剛剛下載的hello-world鏡像

    • 構建鏡像docker build
      通過Dockerfile構建鏡像,這個我們等下再拿出來詳細說明。

    容器相關

    • 新建啟動鏡像docker run
      這個命令是我們最常用的命令,主要使用以下幾個選項
      ① -d選項:表示後台運行
      ② -P選項(大寫):隨機端口映射
      ③ -p選項(小寫):指定端口映射,前面是宿主機端口後面是容器端口,如docker run nginx -p 8080:80,將容器的80端口映射到宿主機的8080端口,然後使用localhost:8080就可以查看容器中nginx的歡迎頁了
      ④ -v選項:掛載宿主機目錄,前面是宿主機目錄,後面是容器目錄,如docker run -d -p 80:80 -v /dockerData/nginx/conf/nginx.conf:/etc/nginx/nginx.conf nginx 掛載宿主機的/dockerData/nginx/conf/nginx.conf的文件,這樣就可以在宿主機對nginx進行參數配置了,注意目錄需要用絕對路徑,不要使用相對路徑,如果宿主機目錄不存在則會自動創建。
      ⑤–rm : 停止容器後會直接刪除容器,這個參數在測試是很有用,如docker run -d -p 80:80 --rm nginx
      ⑥–name : 給容器起個名字,否則會出現一長串的自定義名稱如 docker run -name niginx -d -p 80:80 - nginx

    • 列出容器 docker ps
      這個命令可以列出當前運行的容器,使用-a參數后列出所有的容器(包括已停止的)

    • 停止容器docker stop
      docker stop 5d034c6ea010 後面跟的是容器ID,也可以使用容器名稱

    • 啟動停止的容器docker start
      docker run是新建容器並啟動,docker start 是啟動停止的容器,如docker start 5d034c6ea010

    • 重啟容器docker restart
      此命令執行的過程實際是先執行docker stop,然後再執行docker start,如docker restart 5d034c6ea010

    • 進入容器docker exec -it 容器id /bin/bash
      docker exec -it 5d034c6ea010 /bin/bash,就相當於進入了容器本身的操作系統

    • 刪除容器 docker rm
      docker rm 5d034c6ea010 後面跟的是容器ID,刪除容器之前需要先停止容器運行

    • 數據拷貝docker cp
      此命令用於容器與宿主機之間進行數據拷貝,如 docker cp 5d034c6ea010: /etc/nginx/nginx.conf /dockerData/nginx/conf/nginx.conf 將容器的目錄文件拷貝到宿主機指定位置,容器ID可以替換成容器名。

    命令實戰

    如果我們需要一個nginx容器,並且需要在宿主機上直接修改nginx的配置文件、默認主頁,在宿主機可以實時看到容器nginx的日誌。我們可以按照如下的方式一步一步完成。

    • 使用–rm參數啟動容器,方便刪除
      docker run -d -p 8081:80 --name nginx --rm nginx

    • 進入容器,查看容器中配置文件、項目文件、日誌文件的目錄地址
      docker exec -it 9123b67e428e /bin/bash

    • 導出容器的配置文件
      docker cp nginx:/etc/nginx/nginx.conf /dockerData/nginx/conf/nginx.conf導出配置文件 nginx.conf
      docker cp nginx:/etc/nginx/conf.d /dockerData/nginx/conf/conf.d導出配置目錄 conf.d

    • 停止容器docker stop 9123b67e428e,由於加了–rm參數,容器會自動刪除

    • 再以如下命令啟動容器,完成目錄掛載
      shell docker run -d -p 8081:80 --name nginx \ -v /dockerData/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ -v /dockerData/nginx/conf/conf.d:/etc/nginx/conf.d \ -v /dockerData/nginx/www:/usr/share/nginx/html \ -v /dockerData/nginx/logs:/var/log/nginx nginx
    • 訪問服務器地址http://192.168.136.129:8081/

      訪問報錯,這時候就進入宿主機的日誌目錄/dockerData/nginx/logs查看日誌
      2019/11/23 10:08:11 [error] 6#6: *1 directory index of “/usr/share/nginx/html/” is forbidden, client: 192.168.136.1, server: localhost, request: “GET / HTTP/1.1”, host: “192.168.136.129:8081”
      因為/usr/share/nginx/html/被掛載到了服務器上面的/dockerData/nginx/www目錄下,原來的歡迎頁面在dockerData/nginx/www是沒有的,所有就報錯了,這裏我們隨便建一個。

    • 建立默認主頁
      shell #打開項目文件 cd /dockerData/nginx/www #使用vim 創建並編輯文件 vi index.html #此時我們會進入vim界面,按 i 插入,然後輸入 <h1 align="center">Hello,Welcome to Docker World</h1> #輸入完后,按 esc,然後輸入 :wq
    • 再次訪問瀏覽器地址

    Dockerfile

    我們可以使用Dockfile構建一個鏡像,然後直接在docker中運行。Dockerfile文件為一個文本文件,裡面包含構建鏡像所需的所有的命令,首先我們來認識一下Dockerfile文件中幾個重要的指令。

    指令詳解

    • FROM
      選擇一個基礎鏡像,然後在基礎鏡像上進行修改,比如構建一個SpringBoot項目的鏡像,就需要選擇java這個基礎鏡像,FROM需要作為Dockerfile中的第一條指令
      如:FROM openjdk:8-jdk-alpine 基礎鏡像如果可以的話最好使用alpine版本的,採用alpline版本的基礎鏡像構建出來的鏡像會小很多。

    • RUN
      RUN指令用來執行命令行命令的。它有一下兩種格式:

      • shell 格式:RUN ,就像直接在命令行中輸入的命令一樣。 RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
      • exec 格式:RUN [“可執行文件”, “參數1”, “參數2”],這更像是函數調用中的格式。
    • CMD
      此指令就是用於指定默認的容器主進程的啟動命令的。
      CMD指令格式和RUN相似,也是兩種格式
      • shell 格式:CMD
      • exec 格式:CMD [“可執行文件”, “參數1”, “參數2″…]
      • 參數列表格式:CMD [“參數1”, “參數2″…]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具體的參數。
    • ENTRYPOINT
      ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式和 shell 格式。 ENTRYPOINT 的目的和 CMD 一樣,都是在指定容器啟動程序及參數。ENTRYPOINT 在運行時也可以替代,不過比 CMD 要略顯繁瑣,需要通過 docker run 的參數 --entrypoint 來指定。
      當指定了 ENTRYPOINT 后,CMD 的含義就發生了改變,不再是直接的運行其命令,而是將 CMD 的內容作為參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變為:
      <ENTRYPOINT> "<CMD>"

    • COPY & ADD
      這2個指令都是複製文件,它將從構建上下文目錄中   的文件/目錄 複製到新的一層的鏡像內的   位置。比如: COPY demo-test.jar app.jarADD demo-test.jar app.jar
      ADD指令比 COPY高級點,可以指定一個URL地址,這樣Docker引擎會去下載這個URL的文件,如果 ADD後面是一個 tar文件的話,Dokcer引擎還會去解壓縮。
      我們在構建鏡像時盡可能使用 COPY,因為 COPY 的語義很明確,就是複製文件而已,而 ADD 則包含了更複雜的功能,其行為也不一定很清晰。

    • EXPOSE
      聲明容器運行時的端口,這隻是一個聲明,在運行時並不會因為這個聲明應用就會開啟這個端口的服務。在 Dockerfile 中寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;另一個用處則是在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。
      要將 EXPOSE 和在運行時使用 -p <宿主端口>:<容器端口> 區分開來。-p,是映射宿主端口和容器端口,換句話說,就是將容器的對應端口服務公開給外界訪問,而 EXPOSE 僅僅是聲明容器打算使用什麼端口而已,並不會自動在宿主進行端口映射。

    • ENV
      這個指令很簡單,就是設置環境變量,無論是後面的其它指令,如 RUN,還是運行時的應用,都可以直接使用這裏定義的環境變量。它有如下兩種格式:
      • ENV <key> <value>
      • ENV <key1>=<value1> <key2>=<value2>...
    • VOLUME
      該指令使容器中的一個目錄具有持久化存儲的功能,該目錄可被容器本身使用,也可共享給其他容器。當容器中的應用有持久化數據的需求時可以在Dockerfile中使用該指令。如VOLUME /tmp
      這裏的 /tmp 目錄就會在運行時自動掛載為匿名卷,任何向 /tmp 中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。當然,運行時可以覆蓋這個掛載設置。比如:
      docker run -d -v mydata:/tmp xxxx

    • LABEL
      你可以為你的鏡像添加labels,用來組織鏡像,記錄版本描述,或者其他原因,對應每個label,增加以LABEL開頭的行,和一個或者多個鍵值對。如下所示:
      LABEL version="1.0" LABEL description="test"

    Dockerfile實戰

    我們以一個簡單的SpringBoot項目為例構建基於SpringBoot應用的鏡像。
    功能很簡單,只是對外提供了一個say接口,在進入這個方法的時候打印出一行日誌,並將日誌寫入日誌文件。

    @SpringBootApplication
    @RestController
    @Log4j2
    public class DockerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DockerApplication.class, args);
        }
    
        @GetMapping("/say")
        public String say(){
            log.info("get say request...");
            return "Hello,Java日知錄";
        }
        
    }

    我們使用maven將其打包成jar文件,放入一個單獨的文件夾,然後按照下面步驟一步步構建鏡像並執行

    • 在當前文件夾建立Dockerfile文件,文件內容如下:
      properties FROM openjdk:8-jdk-alpine #將容器中的/tmp目錄作為持久化目錄 VOLUME /tmp #暴露端口 EXPOSE 8080 #複製文件 COPY docker-demo.jar app.jar #配置容器啟動后執行的命令 ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
    • 使用如下命令構建鏡像
      docker built -t springboot:v1.0 .

      -t 指定鏡像的名稱及版本號,注意後面需要以 . 結尾。

    • 查看鏡像文件

    • 運行構建的鏡像
      docker run -v /app/docker/logs:/logs -p 8080:8080 --rm --name springboot springboot:v1.0

    • 瀏覽器訪問http://192.168.136.129:8080/say

    • 在宿主機上實時查看日誌
      tail -100f /app/docker/logs/docker-demo-info.log

      請關注個人公眾號:JAVA日知錄

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

    ※帶您來了解什麼是 USB CONNECTOR  ?

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

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

  • 023.掌握Pod-Pod擴容和縮容

    023.掌握Pod-Pod擴容和縮容

    一 Pod的擴容和縮容

    Kubernetes對Pod的擴縮容操作提供了手動和自動兩種模式,手動模式通過執行kubectl scale命令或通過RESTful API對一個Deployment/RC進行Pod副本數量的設置。自動模式則需要用戶根據某個性能指標或者自定義業務指標,並指定Pod副本數量的範圍,系統將自動在這個範圍內根據性能指標的變化進行調整。

    1.1 手動縮容和擴容

      1 [root@uk8s-m-01 study]# vi nginx-deployment.yaml
      2 apiVersion: apps/v1beta1
      3 kind: Deployment
      4 metadata:
      5   name: nginx-deployment
      6 spec:
      7   replicas: 3
      8   template:
      9     metadata:
     10       labels:
     11         app: nginx
     12     spec:
     13       containers:
     14       - name: nginx
     15         image: nginx:1.7.9
     16         ports:
     17         - containerPort: 80
      1 [root@uk8s-m-01 study]# kubectl create -f nginx-deployment.yaml
      2 [root@uk8s-m-01 study]# kubectl scale deployment nginx-deployment --replicas=5	#擴容至5個
      3 [root@uk8s-m-01 study]# kubectl get pods	                                	#查看擴容后的Pod

      1 [root@uk8s-m-01 study]# kubectl scale deployment nginx-deployment --replicas=2	#縮容至2個
      2 [root@uk8s-m-01 study]# kubectl get pods

    1.2 自動擴容機制

    Kubernetes使用Horizontal Pod Autoscaler(HPA)的控制器實現基於CPU使用率進行自動Pod擴縮容的功能。HPA控制器基於Master的kube-controller-manager服務啟動參數–horizontal-pod-autoscaler-sync-period定義的探測周期(默認值為15s),周期性地監測目標Pod的資源性能指標,並與HPA資源對象中的擴縮容條件進行對比,在滿足條件時對Pod副本數量進行調整。

    • HPA原理

    Kubernetes中的某個Metrics Server(Heapster或自定義MetricsServer)持續採集所有Pod副本的指標數據。HPA控制器通過Metrics Server的API(Heapster的API或聚合API)獲取這些數據,基於用戶定義的擴縮容規則進行計算,得到目標Pod副本數量。
    當目標Pod副本數量與當前副本數量不同時,HPA控制器就向Pod的副本控制器(Deployment、RC或ReplicaSet)發起scale操作,調整Pod的副本數量,完成擴縮容操作。

    • HPA指標類型

    Master的kube-controller-manager服務持續監測目標Pod的某種性能指標,以計算是否需要調整副本數量。目前Kubernetes支持的指標類型如下:
    Pod資源使用率:Pod級別的性能指標,通常是一個比率值,例如CPU使用率。
    Pod自定義指標:Pod級別的性能指標,通常是一個數值,例如接收的請求數量。
    Object自定義指標或外部自定義指標:通常是一個數值,需要容器應用以某種方式提供,例如通過HTTP URL“/metrics”提供,或者使用外部服務提供的指標採集URL。
    Metrics Server將採集到的Pod性能指標數據通過聚合API(Aggregated API) 如metrics.k8s.io、 custom.metrics.k8s.io和external.metrics.k8s.io提供給HPA控制器進行查詢。

    • 擴縮容算法

    Autoscaler控制器從聚合API獲取到Pod性能指標數據之後,基於下面的算法計算出目標Pod副本數量,與當前運行的Pod副本數量進行對比,決定是否需要進行擴縮容操作:
    desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

    即當前副本數 x(當前指標值/期望的指標值),將結果向上取整。
    釋義:以CPU請求數量為例,如果用戶設置的期望指標值為100m,當前實際使用的指標值為200m,則計算得到期望的Pod副本數量應為兩個(200/100=2)。如果設置的期望指標值為50m,計算結果為0.5,則向上取整值為1, 得到目標Pod副本數量應為1個。
    注意:當計算結果與1非常接近時,可以設置一個容忍度讓系統不做擴縮容操作。容忍度通過kube-controller-manager服務的啟動參數–horizontalpod-autoscaler-tolerance進行設置,默認值為0.1(即10%),表示基於上述算法得到的結果在[-10%-+10%]區間內,即[0.9-1.1],控制器都不會進行擴縮容操作
    也可以將期望指標值(desiredMetricValue)設置為指標的平均值類型,例如targetAverageValue或targetAverageUtilization,此時當前指標值(currentMetricValue) 的算法為所有Pod副本當前指標值的總和除以Pod副本數量得到的平均值。
    此外,存在幾種Pod異常的如下情況:

    • Pod正在被刪除(設置了刪除時間戳):將不會計入目標Pod副本數量。
    • Pod的當前指標值無法獲得:本次探測不會將這個Pod納入目標Pod副本數量,後續的探測會被重新納入計算範圍。
    • 如果指標類型是CPU使用率,則對於正在啟動但是還未達到Ready狀態的Pod,也暫時不會納入目標副本數量範圍。

    提示:可以通過kubecontroller-manager服務的啟動參數–horizontal-pod-autoscaler-initialreadiness-delay設置首次探測Pod是否Ready的延時時間,默認值為30s。

    另一個啟動參數–horizontal-pod-autoscaler-cpuinitialization-period設置首次採集Pod的CPU使用率的延時時間。
    當存在缺失指標的Pod時,系統將更保守地重新計算平均值。系統會假設這些Pod在需要縮容(Scale Down) 時消耗了期望指標值的100%,在需要擴容(Scale Up)時消耗了期望指標值的0%,這樣可以抑制潛在的擴縮容操作。
    此外,如果存在未達到Ready狀態的Pod,並且系統原本會在不考慮缺失指標或NotReady的Pod情況下進行擴展,則系統仍然會保守地假設這些Pod消耗期望指標值的0%,從而進一步抑制擴容操作。如果在HorizontalPodAutoscaler中設置了多個指標,系統就會對每個指標都執行上面的算法,在全部結果中以期望副本數的最大值為最終結果。如果這些指標中的任意一個都無法轉換為期望的副本數(例如無法獲取指標的值),系統就會跳過擴縮容操作。
    最後, 在HPA控制器執行擴縮容操作之前,系統會記錄擴縮容建議信息(Scale Recommendation)。控制器會在操作時間窗口(時間範圍可以配置)中考慮所有的建議信息,並從中選擇得分最高的建議。這個值可通過kube-controller-manager服務的啟動參數–horizontal-podautoscaler-downscale-stabilization-window進行配置,默認值為5min。這個配置可以讓系統更為平滑地進行縮容操作,從而消除短時間內指標值快速波動產生的影響。

    1.3 HorizontalPodAutoscaler

    Kubernetes將HorizontalPodAutoscaler資源對象提供給用戶來定義擴縮容的規則。
    HorizontalPodAutoscaler資源對象處於Kubernetes的API組“autoscaling”中, 目前包括v1和v2兩個版本。 其中autoscaling/v1僅支持基於CPU使用率的自動擴縮容, autoscaling/v2則用於支持基於任意指標的自動擴縮容配置, 包括基於資源使用率、 Pod指標、 其他指標等類型的指標數據。
    示例1:基於autoscaling/v1版本的HorizontalPodAutoscaler配置,僅可以設置CPU使用率。

      1 [root@uk8s-m-01 study]# vi php-apache-autoscaling-v1.yaml
      2 apiVersion: autoscaling/v1
      3 kind: HorizontalPodAutoscaler
      4 metadata:
      5   name: php-apache
      6 spec:
      7   scaleTargetRef:
      8     apiVersion: apps/v1
      9     kind: Deployment
     10     name: php-apache
     11   minReplicas: 1
     12   maxReplicas: 10
     13   targetCPUUtilizationPercentage: 50


    釋義:
    scaleTargetRef:目標作用對象,可以是Deployment、ReplicationController或ReplicaSet。
    targetCPUUtilizationPercentage:期望每個Pod的CPU使用率都為50%,該使用率基於Pod設置的CPU Request值進行計算,例如該值為200m,那麼系統將維持Pod的實際CPU使用值為100m。
    minReplicas和maxReplicas:Pod副本數量的最小值和最大值,系統將在這個範圍內進行自動擴縮容操作, 並維持每個Pod的CPU使用率為50%。
    為了使用autoscaling/v1版本的HorizontalPodAutoscaler,需要預先安裝Heapster組件或Metrics Server,用於採集Pod的CPU使用率。
    示例2:基於autoscaling/v2beta2的HorizontalPodAutoscaler配置。

      1 [root@uk8s-m-01 study]# vi php-apache-autoscaling-v2.yaml
      2 apiVersion: autoscaling/v2beta2
      3 kind: HorizontalPodAutoscaler
      4 metadata:
      5   name: php-apache
      6 spec:
      7   scaleTargetRef:
      8     apiVersion: apps/v1
      9     kind: Deployment
     10     name: php-apache
     11   minReplicas: 1
     12   maxReplicas: 10
     13   metrics:
     14   - type: Resource
     15     resource:
     16       name: cpu
     17       target:
     18         type: Utilization
     19         averageUtilization: 50


    釋義:
    scaleTargetRef:目標作用對象,可以是Deployment、ReplicationController或ReplicaSet。
    minReplicas和maxReplicas:Pod副本數量的最小值和最大值,系統將在這個範圍內進行自動擴縮容操作, 並維持每個Pod的CPU使用率為50%。
    metrics:目標指標值。在metrics中通過參數type定義指標的類型;通過參數target定義相應的指標目標值,系統將在指標數據達到目標值時(考慮容忍度的區間)觸發擴縮容操作。

    • metrics中的type(指標類型)設置為以下幾種:
      • Resource:基於資源的指標值,可以設置的資源為CPU和內存。
      • Pods:基於Pod的指標,系統將對全部Pod副本的指標值進行平均值計算。
      • Object:基於某種資源對象(如Ingress)的指標或應用系統的任意自定義指標。

    Resource類型的指標可以設置CPU和內存。對於CPU使用率,在target參數中設置averageUtilization定義目標平均CPU使用率。對於內存資源,在target參數中設置AverageValue定義目標平均內存使用值。指標數據可以通過API“metrics.k8s.io”進行查詢,要求預先啟動Metrics Server服務。
    Pods類型和Object類型都屬於自定義指標類型,指標的數據通常需要搭建自定義Metrics Server和監控工具進行採集和處理。指標數據可以通過API“custom.metrics.k8s.io”進行查詢,要求預先啟動自定義Metrics Server服務。
    類型為Pods的指標數據來源於Pod對象本身, 其target指標類型只能使用AverageValue,示例:

      1  metrics:
      2   - type: Pods
      3     pods:
      4       metrics:
      5         name: packets-per-second
      6       target:
      7         type: AverageValue
      8         averageValue: 1k

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!