亞馬遜水壩重大設計缺陷 危害人身安全和生物多樣性

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

【其他文章推薦】

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

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

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

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

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

你不得不知的十大氣候科學事實 來自「未來地球」智庫的警示

文:詹詒絜(台達電子文教基金會高級專員)

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

【其他文章推薦】

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

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

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

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

暖冬雪下太少!東奧觀賽席「人工雪」恐難施行

摘錄自2020年2月27日民視新聞網報導

距離東京奧運開幕只剩下不到150天,日本近年夏天的高溫氣候恐為參賽選手和民眾帶來不小負擔。雖然東奧組委會先前想過,在觀賽席降下人工雪,或是從其他地區徵用積雪,製作雪包為民眾降溫,但這項計畫也受到今年日本暖冬天氣影響,恐怕難以施行。

日本氣象廳公布,今年國內的長期氣象預報,當中說到將舉辦奧運的6~8月,日本全國各地將會因太平洋高氣壓出現比往年夏天更熱的酷暑氣候,氣象廳也呼籲民眾要預先做好抗暑對策。

除了人工雪外,東奧組委會想出的另一個計畫,就是和有雪國稱號的東北新潟縣南魚沼市合作,在當地的儲雪場收集100噸雪備用,未來在賽事期間製成雪包提供給觀眾消暑。但南魚沼降雪量嚴重不足,消暑雪包計畫要執行難度恐怕很高。而暖冬也為冬季的滑雪觀光產業帶來嚴重衝擊。

面對來勢洶洶的酷暑,東奧組委會除了將部分賽事的舉辦時間提前,先前也決定將馬拉松挪至札幌舉辦,試圖降低高溫影響,但效果如何還有待觀察。

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

【其他文章推薦】

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

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

※超省錢租車方案

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

網頁設計最專業,超強功能平台可客製化

出席桃園眷村文化節 林右昌期許北北基桃共榮共好

已邁入第20屆的桃園眷村文化節,今年首度跨出桃園,與基隆市、台北市、新北市攜手串連,基隆市長林右昌6日與桃園市長鄭文燦、台北市長柯文哲及新北市長侯友宜一同出席活動記者會,期許4個城市攜手合作共榮共好。

林市長在啟動記者會中指出,不管大家是從大江南北來台北網頁設計到台灣,都是台灣的一分子,也是台灣生命共同體的一部分,眷村文化也是大家記憶中重要的一環,基隆市租車是一個港口城市,很多人都是在基隆港下船,對台灣的第一印象都是基隆,也因此基隆有很多眷村,加上基隆過去是要塞城市,有很多海軍跟陸軍的駐軍。

林右昌強調,這次北北基包裝行銷桃4個城市的市長一起參與眷村文化節,代表大家對於iphone維修眷村文化的重視,也是台灣人民向前行的重要力量,未來不管北北基如何寫文案桃都會繼續FB行銷把這個活動擴大辦理下去,大台北都會就是1千萬人口的生活圈,有非常多的跨域治理工作可以一起努力。

林右昌也不忘藉這個機會宣傳台北網頁設計即將在基隆舉辦的國慶晚會,他說今年的國慶口罩代表多元的聲音,設計來源是網頁設計公司聲波跳動,不同的顏色跟跳動的圖紋,代表台灣人民包容合作的精神,桃園市長鄭文燦舉辦這個活動把大家拉在一起,在此時此刻的台灣特別有意義,希望讓4個城市共好共榮。

桃園市文化局表示,桃園眷村文化銷售文案節今年邁入第20屆,是首次與台北市眷遊記(10/9-10/25)、新北市眷村貨運文化節(10/1-10/3)、基隆市攜手串連網頁設計,舉辦「集合啦!眷村守護軍團」線上集章活動,透過線上集章,邀請全國民眾一同到北北基桃眷村文化園區旅遊,見證眷村文化的保存成果

和通用競爭,Ford推首全電動車CUV

目前福特所販售的電動車皆為「合規車(compliance cars)」,也就是各大車廠為了符合美國州政府零排放規定而以一般汽車為基底改造而成的電動車。近日,福特技術長Raj Nair 表示他們將於2020 年推出首款全電動車,這款電動車將進行量產且定價相對低廉。

福特進入電動車市場的時間點明顯晚於其主要競爭對手—通用汽車。通用汽車已推出價格優惠又續航力充足的Chevy Bolt 電動車,而福特將「以量取勝」。Nair 在Business Insider 的訪問中強調,「這將是一款大眾化的電動車,我們必須同時達成高續航力與價格實惠的目標,否則這只會是一款瞄準小眾市場的奢侈品。」

這款交叉型(CUV)全電動車將於2020 年推出,Nair 保證福特的全電動車將以充電一次行駛超過480 公里的續航力以及交叉型車款的多功能特性來取得市場青睞。在交叉型與小型SUV 車款方面,Tesla 將於2019 至2020 年間推出Model Y。

除了這款全電動車之外,福特也承認他們正計畫推出其他類似的車款,但多半為油電混合車。這款電動車將於福特Flat Rock 工廠進行生產,並在2020 年於北美、歐洲與亞洲推出。

(合作媒體:。圖片出處:Ford Europe)

 

首圖來源:Ford Europe)

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

【其他文章推薦】

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

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

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

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

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

德國將蓋鋰電池廠制衡Tesla,鋰電池成本望下滑

特斯拉(Tesla)懷著超級工廠大夢,準備在電池領域一統江湖,歐洲強國也不甘示弱,德國總理梅克爾(Angela Merkel)參加德國戴姆勒汽車(Daimler)斥資5 億歐元興建的鋰電池工廠的動土典禮,這項歐洲大陸的超級電池工廠計畫,將挑戰Tesla 在綠色電力領域的領導地位。

彭博報導,德國電池工廠位於柏林南部130 公里處,突顯主要汽車製造商和電力公司進入儲能的行動,這項技術對推動下一代綠色車輛,以及在需要時儲存風力與太陽能電力上至關重要。報導認為,隨著汽車製造商與電力公司同步發展,電池成本可能會迅速下滑。

彭博分析師認為,電池成本下降與能源密度增加,預計2030 年就可以看到電動車比燃料汽車更便宜的情況。根據彭博新能源財經(BNEF)數據,全球電池製造產能將在2021 年翻倍,達到2.78 億度,現在約1.03 億度,屆時歐洲市場佔比將從現在的2.5% 提高一倍。

瑞典、匈牙利和波蘭計劃中的大型工廠,以及戴姆勒在德國的電池組裝廠,預計會為福斯與雷諾等汽車製造商提供需求,屆時鋰離子電池成本將降低43%,使電動汽車成為主流。

對於公用事業而言,便宜的電池可以降低儲存單元的成本,儲存單元可將變動型再生能源如風能與太陽能的電力傳送至電網。義大利國家電力公司Enel SpA 將電池與風力發電場配套使用,電網管理人員對電力輸出的預測準度因此提高30%。

1990 年代初期消費類電子產品如電腦和手機使用的鋰離子電池,已經跨界應用於運輸和電力行業,但礙於成本,鋰電池在電網和汽車上的應用才剛剛開始,電池的興起對於電動汽車的消費者來說最為明顯,大多數主要汽車製造商將充電式電動車訂為未來10 年的中期計劃。

目前電池業務仍由亞洲電子製造商所主導。根據BNEF,韓國LG 和三星SDI 是頂級供應商,亞洲有望繼續保持領先地位,中國另有8 個工廠正在興建。

汽車製造商採取行動確保電池供應源,戴姆勒的工廠將是歐洲最大的工廠,供應旗下汽車與梅賽德斯賓士,並與屋頂太陽能安裝商Vivint Solar 合資生產家庭能源儲存系統。

2017 年1 月Tesla 的工廠完成三分之一,完工後每年產能可達35 千兆瓦,足以支持每年生產50 萬輛電動車,這將使Tesla 成為繼LG 化學之後的第2 大供應商,特斯拉也在計畫興建更多超級工廠。

戴姆勒的投資規模較小,且尚未披露產能目標。福斯正在與電池廠商討論投資項目,並計劃在德國興建原型組裝廠以開發自己的技術。位於斯德哥爾摩的創業公司NorthVolt AB 宣布計劃在2023 年在瑞典設立一家40 億歐元的電池廠。鋰電池產量大增可望降低所有應用的電池成本,讓家庭和電網的儲存更加實惠。

未來10 年電動汽車價格可望能與汽油或柴油車競爭,由於電池組是充電電動車中最昂貴的部分,佔總成本的三分之一,預計到2021 年,鋰離子電池將便宜43%,從目前的每千瓦273 美元降至156 美元。

鋰電池普及是可以預見的事,但問題是儲存對消費者或大型公用事業而言是否有利可圖仍然是一個懸而未決的問題。即使如此,電動汽車廠商也正在尋找未來,根據BNEF,2030 年前充電式電動車可能佔新車銷售的五分之一,即2,100 萬台。梅克爾訪問戴姆勒工廠展現了德國政府計劃2030 年讓600 萬輛電動汽車上路的決心。

(合作媒體:。圖片出處:Tesla)

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

電動車發展腳步快,石油恐2040年前觸頂

Thomson Reuters報導,嘉能可(Glencore)董事長Tony Hayward 22日表示,電動車的快速進步意味著石油需求可能會在2040年以前觸頂,深海鑽油、加拿大油砂等高成本原油生產商恐將先被淘汰出局,擁有生產成本優勢的石油輸出國組織(OPEC)相對較不受衝擊。Hayward曾任英國石油公司(BP Plc)執行長。

嘉能可執行長Ivan Glasenberg表示,石油需求可能提前觸頂對嘉能可有利、因為旗下投資組合並沒有太多的原油。Glasenberg指出,如果電動車在2035年拿下90-95%的市占率,全球年度銅需求量可望較目前的2,300萬噸呈現倍增。德國總理梅克爾(Angela Merkel)22日指出,鋰電池技術已經進步到可以讓電動車擁有1千公里的續航力、遠高於目前的200-300公里,德國必須大舉投資以確保產業繼續保有優勢。

戴姆勒(Daimler AG)董事長Deiter Zetsche 22日表示,預估到2022年旗下將有超過10款的純電動轎車系列。戴姆勒旗下全資子公司ACCUMOTIVE 22日在德國卡門茨(Kamenz)為第二座電池工廠舉行奠基儀式、邀請梅克爾出席。這座工廠耗資5億歐元、預計在2018年年中正式營運。

英國金融時報去年8月報導,麥格理集團全球能源策略師Vikas Dwivedi指出,沙烏地阿拉伯對電動車的長期發展存有戒心,這可能就是它為何宣布將讓沙烏地阿拉伯國家石油公司(Saudi Aramco)初次公開發行(IPO)的原因之一。

通用汽車(General Motors)Chevrolet Bolt電動車續航力達238英里(383公里)、建議零售價37,495美元起(註:最多可取得7,500美元的聯邦折抵稅額、扣除後入手價相當於29,995美元)。美聯社報導,IHS Markit汽車業分析師Stephanie Brinley指出,Bolt續航力遠高於美國平均來回通勤距離(40英里),但有時人們回家後可能忘了或沒有足夠時間進行充電,這是電動車主得多加費心的地方。

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

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

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

網頁設計最專業,超強功能平台可客製化

打個總結:Web性能優化

前段時間優化一個公司歷史老項目的Web性能,卻引出了一系列的問題,讓我反思良多。
我通過Chrome的Lighthouse工具可以看出一些性能參數和問題反饋,我逐一對其進行優化。
根據資源請求的不同,大致可以分為前端資源性能和後端程序性能兩個方面。
先分析一下前端資源吧:

  1. Defer offscreen images。

Chrome給出的建議是:

Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. Learn more.

意思是可以考慮延遲加載一些圖片或者隱藏一些圖片在所有關鍵資源完成加載后再考慮加載,通過延遲加載來降低交互時間。
lazy-load的實現方法很多,開源框架推薦:lazysizes。
當然也可以使用npm方式安裝:

npm install lazysizes
使用方式很簡單,先引入lazysize到需要的頁面:

<script src="lazysizes.min.js" async=""></script>

然後給需要被lazyload的img標籤上加新的屬性,如下:

<img
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w" class="lazyload" />

特別要注意,有時候太多background方式加載的圖片也會影響性能,lazysizes也可以處理這樣的圖片。方法如下,使用data-bg屬性即可:

<div class="lazyload" data-bg="/path/to/image.jpg"></div>
  1. Reduce JavaScript execution time

解決這個問題方法很多,第一個想到的就是壓縮資源。
比如壓縮js和css文件,可以考慮使用webpack工具或者gulp來壓縮大資源文件,合併一些文件資源請求。
還可以通過預加載來提高響應速度,可以在最重要的js資源文件的引入上加入預加載,代碼如下:

<link rel="preload" as="style" href="css/style.css">

通過增加preload
最後還可以異步加載資源,異步不會阻塞主進程,代碼調整也很小:

<script src="xxx" async></script>

除此之外JavaScript的執行時間過長還有可能是有大量邏輯運算,有很多頁面把一些邏輯判斷和計算都交給前端去計算,這樣也不利於渲染,建議還是把複雜邏輯和計算盡可能交給後端去處理,讓前端渲染更加“輕量”。
3. Backend response
數據接口返回有時候也很拖累響應時間,因為一些前端結構需要根據返回的數據進行組裝新的頁面結構。
這裏可以考慮找到性能真正的瓶頸,到底是數據庫查詢導致的慢,還是業務邏輯不清晰導致的冗餘,或者其他原因。
我遇到的是因為老項目的數據庫操作類不是單例,會每次產生一個數據庫連接句柄,且該頁面複雜又多的sql查詢。
我勇敢地修改着10多年歷史的代碼,編寫了單例模式的操作類,然後增加了必要的緩存機制。
然而後面我遇到了問題,首先單例類在一些特殊情形下不滿足之前的代碼需求,導致奇特的數據庫報錯,而async屬性導致一些js文件無法同步加載到位,而有一些依賴這些資源的php文件執行報錯。
最終在同事的幫助下,恢復了服務,我也再一次體會到了任何一種性能提升都要謹慎,特別是對一個古老的項目。
前人不敢動的代碼,可能是深淵。

PS:我的公眾號 成都有娃兒會同步發布該文章,也可以關注哦!

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

【其他文章推薦】

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

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

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

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

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

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

容器技術之Docker-swarm

  前文我聊到了docker machine的簡單使用和基本原理的說明,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13160915.html;今天我們來聊一聊docker集群管理工具docker swarm;docker swarm是docker 官方的集群管理工具,它可以讓跨主機節點來創建,管理docker 集群;它的主要作用就是可以把多個節點主機的docker環境整合成一個大的docker資源池;docker swarm面向的就是這個大的docker 資源池在上面管理容器;在前面我們都只是在單台主機上的創建,管理容器,但是在生產環境中通常一台物理機上的容器實在是不能夠滿足當前業務的需求,所以docker swarm提供了一種集群解決方案,方便在多個節點上創建,管理容器;接下來我們來看看docker swarm集群的搭建過程吧;

  docker swarm 在我們安裝好docker時就已經安裝好了,我們可以使用docker info來查看

[root@node1 ~]# docker info
Client:
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
 Server Version: 19.03.11
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
 init version: fec3683
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 3.10.0-693.el7.x86_64
 Operating System: CentOS Linux 7 (Core)
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 3.686GiB
 Name: docker-node01
 ID: 4HXP:YJ5W:4SM5:NAPM:NXPZ:QFIU:ARVJ:BYDG:KVWU:5AAJ:77GC:X7GQ
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
  provider=generic
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

[root@node1 ~]# 

  提示:從上面的信息可以看到,swarm是處於非活躍狀態,這是因為我們還沒有初始化集群,所以對應的swarm選項的值是處於inactive狀態;

  初始化集群

[root@docker-node01 ~]# docker swarm init --advertise-addr 192.168.0.41
Swarm initialized: current node (ynz304mbltxx10v3i15ldkmj1) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

[root@docker-node01 ~]# 

  提示:從上面反饋的信息可以看到,集群初始化成功,並且告訴我們當前節點為管理節點,如果想要其他節點加入到該集群,可以在對應節點上運行docker swarm join –token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377 這個命令,就把對應節點當作work節點加入到該集群,如果想要以管理節點身份加入到集群,我們需要在當前終端運行docker swarm join-token manager命令

[root@docker-node01 ~]# docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-dqjeh8hp6cp99bksjc03b8yu3 192.168.0.41:2377

[root@docker-node01 ~]# 

  提示:我們執行docker swarm join-token manager命令,它返回了一個命令,並告訴我們添加一個管理節點,在對應節點上執行docker swarm join –token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-dqjeh8hp6cp99bksjc03b8yu3 192.168.0.41:2377命令即可;

  到此docker swarm集群就初始化完畢,接下來我們把其他節點加入到該集群

  把docker-node02以work節點身份加入集群

[root@node2 ~]# docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377
This node joined a swarm as a worker.
[root@node2 ~]# 

  提示:沒有報錯就表示加入集群成功;我們可以使用docker info來查看當前的docker 環境詳細信息

  提示:從上面的信息可以看到,在docker-node02這台主機上docker swarm 已經激活,並且可以看到管理節點的地址;除了以上方式可以確定docker-node02以及加入到集群;我們還可以在管理節點上運行docker node ls 查看集群節點信息;

  查看集群節點信息

  提示:在管理節點上運行docker node ls 就可以列出當前集群里有多少節點已經成功加入進來;

  把docker-node03以管理節點身份加入到集群

  提示:可以看到docker-node03已經是集群的管理節點,所以可以在docker-node03這個節點執行docker node ls 命令;到此docker swarm集群就搭建好了;接下來我們來說一說docker swarm集群的常用管理

  有關節點相關管理命令

  docker node ls :列出當前集群上的所有節點

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
aeo8j7zit9qkoeeft3j0q1h0z     docker-node03       Ready               Active              Reachable           19.03.11
[root@docker-node01 ~]# 

  提示:該命令只能在管理節點上執行;

  docker node inspect :查看指定節點的詳細信息;

[root@docker-node01 ~]# docker node inspect docker-node01
[
    {
        "ID": "ynz304mbltxx10v3i15ldkmj1",
        "Version": {
            "Index": 9
        },
        "CreatedAt": "2020-06-20T05:57:17.57684293Z",
        "UpdatedAt": "2020-06-20T05:57:18.18575648Z",
        "Spec": {
            "Labels": {},
            "Role": "manager",
            "Availability": "active"
        },
        "Description": {
            "Hostname": "docker-node01",
            "Platform": {
                "Architecture": "x86_64",
                "OS": "linux"
            },
            "Resources": {
                "NanoCPUs": 4000000000,
                "MemoryBytes": 3958075392
            },
            "Engine": {
                "EngineVersion": "19.03.11",
                "Labels": {
                    "provider": "generic"
                },
                "Plugins": [
                    {
                        "Type": "Log",
                        "Name": "awslogs"
                    },
                    {
                        "Type": "Log",
                        "Name": "fluentd"
                    },
                    {
                        "Type": "Log",
                        "Name": "gcplogs"
                    },
                    {
                        "Type": "Log",
                        "Name": "gelf"
                    },
                    {
                        "Type": "Log",
                        "Name": "journald"
                    },
                    {
                        "Type": "Log",
                        "Name": "json-file"
                    },
                    {
                        "Type": "Log",
                        "Name": "local"
                    },
                    {
                        "Type": "Log",
                        "Name": "logentries"
                    },
                    {
                        "Type": "Log",
                        "Name": "splunk"
                    },
                    {
                        "Type": "Log",
                        "Name": "syslog"
                    },
                    {
                        "Type": "Network",
                        "Name": "bridge"
                    },
                    {
                        "Type": "Network",
                        "Name": "host"
                    },
                    {
                        "Type": "Network",
                        "Name": "ipvlan"
                    },
                    {
                        "Type": "Network",
                        "Name": "macvlan"
                    },
                    {
                        "Type": "Network",
                        "Name": "null"
                    },
                    {
                        "Type": "Network",
                        "Name": "overlay"
                    },
                    {
                        "Type": "Volume",
                        "Name": "local"
                    }
                ]
            },
            "TLSInfo": {
                "TrustRoot": "-----BEGIN CERTIFICATE-----\nMIIBaTCCARCgAwIBAgIUeBd/eSZ7WaiyLby9o1yWpjps3gwwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMjAwNjIwMDU1MjAwWhcNNDAwNjE1MDU1\nMjAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABMsYxnGoPbM4gqb23E1TvOeQcLcY56XysLuF8tYKm56GuKpeD/SqXrUCYqKZ\nHV+WSqcM0fD1g+mgZwlUwFzNxhajQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBTV64kbvS83eRHyI6hdJeEIv3GmrTAKBggqhkjO\nPQQDAgNHADBEAiBBB4hLn0ijybJWH5j5rtMdAoj8l/6M3PXERnRSlhbcawIgLoby\newMHCnm8IIrUGe7s4CZ07iHG477punuPMKDgqJ0=\n-----END CERTIFICATE-----\n",
                "CertIssuerSubject": "MBMxETAPBgNVBAMTCHN3YXJtLWNh",
                "CertIssuerPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyxjGcag9sziCpvbcTVO855BwtxjnpfKwu4Xy1gqbnoa4ql4P9KpetQJiopkdX5ZKpwzR8PWD6aBnCVTAXM3GFg=="
            }
        },
        "Status": {
            "State": "ready",
            "Addr": "192.168.0.41"
        },
        "ManagerStatus": {
            "Leader": true,
            "Reachability": "reachable",
            "Addr": "192.168.0.41:2377"
        }
    }
]
[root@docker-node01 ~]#

  docker node ps :列出指定節點上運行容器的清單

[root@docker-node01 ~]# docker node ps 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE       ERROR               PORTS
[root@docker-node01 ~]# docker node ps docker-node01
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE       ERROR               PORTS
[root@docker-node01 ~]# 

  提示:類似docker ps 命令,我上面沒有運行容器,所以看不到對應信息;默認不指定節點名稱表示查看當前節點上的運行容器清單;

  docker node rm :刪除指定節點

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
aeo8j7zit9qkoeeft3j0q1h0z     docker-node03       Ready               Active              Reachable           19.03.11
[root@docker-node01 ~]# docker node rm docker-node03
Error response from daemon: rpc error: code = FailedPrecondition desc = node aeo8j7zit9qkoeeft3j0q1h0z is a cluster manager and is a member of the raft cluster. It must be demoted to worker before removal
[root@docker-node01 ~]# docker node rm docker-node02
Error response from daemon: rpc error: code = FailedPrecondition desc = node tzkm0ymzjdmc1r8d54snievf1 is not down and can't be removed
[root@docker-node01 ~]# 

  提示:刪除節點前必須滿足,被刪除的節點不是管理節點,其次就是要刪除的節點必須是down狀態;

  docker swarm leave:離開當前集群

[root@docker-node03 ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
e7958ffa16cd        nginx               "/docker-entrypoint.…"   28 seconds ago      Up 26 seconds       80/tcp              n1
[root@docker-node03 ~]# docker swarm leave 
Error response from daemon: You are attempting to leave the swarm on a node that is participating as a manager. Removing this node leaves 1 managers out of 2. Without a Raft quorum your swarm will be inaccessible. The only way to restore a swarm that has lost consensus is to reinitialize it with `--force-new-cluster`. Use `--force` to suppress this message.
[root@docker-node03 ~]# docker swarm leave -f
Node left the swarm.
[root@docker-node03 ~]# 

  提示:管理節點默認是不允許離開集群的,如果強制使用-f選項離開集群,會導致在其他管理節點無法正常管理集群;

[root@docker-node01 ~]# docker node ls
Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.
[root@docker-node01 ~]#

  提示:我們在docker-node01上現在就不能使用docker node ls 來查看集群節點列表了;解決辦法重新初始化集群;

[root@docker-node01 ~]# docker node ls 
Error response from daemon: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.
[root@docker-node01 ~]# docker swarm init --advertise-addr 192.168.0.41
Error response from daemon: This node is already part of a swarm. Use "docker swarm leave" to leave this swarm and join another one.
[root@docker-node01 ~]# docker swarm init --force-new-cluster 
Swarm initialized: current node (ynz304mbltxx10v3i15ldkmj1) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-6difxlq3wc8emlwxzuw95gp8rmvbz2oq62kux3as0e4rbyqhk3-2m9x12n102ca4qlyjpseobzik 192.168.0.41:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Unknown             Active                                  19.03.11
aeo8j7zit9qkoeeft3j0q1h0z     docker-node03       Down                Active                                  19.03.11
rm3j7cjvmoa35yy8ckuzoay46     docker-node03       Unknown             Active                                  19.03.11
[root@docker-node01 ~]# 

  提示:重新初始化集群不能使用docker swarm init –advertise-addr 192.168.0.41這種方式初始化,必須使用docker swarm init –force-new-cluster,該命令表示使用從當前狀態強制創建一個集群;現在我們就可以使用docker node rm 把down狀態的節點從集群刪除;

  刪除down狀態的節點

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
aeo8j7zit9qkoeeft3j0q1h0z     docker-node03       Down                Active                                  19.03.11
rm3j7cjvmoa35yy8ckuzoay46     docker-node03       Down                Active                                  19.03.11
[root@docker-node01 ~]# docker node rm aeo8j7zit9qkoeeft3j0q1h0z rm3j7cjvmoa35yy8ckuzoay46
aeo8j7zit9qkoeeft3j0q1h0z
rm3j7cjvmoa35yy8ckuzoay46
[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
[root@docker-node01 ~]# 

  docker node promote:把指定節點提升為管理節點

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
[root@docker-node01 ~]# docker node promote docker-node02
Node docker-node02 promoted to a manager in the swarm.
[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active              Reachable           19.03.11
[root@docker-node01 ~]# 

  docker node demote:把指定節點降級為work節點

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active              Reachable           19.03.11
[root@docker-node01 ~]# docker node demote docker-node02
Manager docker-node02 demoted in the swarm.
[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
[root@docker-node01 ~]# 

  docker node update:更新指定節點

[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Active              Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
[root@docker-node01 ~]# docker node update docker-node01 --availability drain 
docker-node01
[root@docker-node01 ~]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
ynz304mbltxx10v3i15ldkmj1 *   docker-node01       Ready               Drain               Leader              19.03.11
tzkm0ymzjdmc1r8d54snievf1     docker-node02       Ready               Active                                  19.03.11
[root@docker-node01 ~]# 

  提示:以上命令把docker-node01的availability屬性更改為drain,這樣更改后docker-node01的資源就不會被調度到用來運行容器;

  為docker swarm集群添加圖形界面

[root@docker-node01 docker]# docker run --name v1 -d -p 8888:8080 -e HOST=192.168.0.41 -e PORT=8080 -v /var/run/docker.sock:/var/run/docker.sock docker-registry.io/test/visualizer
Unable to find image 'docker-registry.io/test/visualizer:latest' locally
latest: Pulling from test/visualizer
cd784148e348: Pull complete 
f6268ae5d1d7: Pull complete 
97eb9028b14b: Pull complete 
9975a7a2a3d1: Pull complete 
ba903e5e6801: Pull complete 
7f034edb1086: Pull complete 
cd5dbf77b483: Pull complete 
5e7311667ddb: Pull complete 
687c1072bfcb: Pull complete 
aa18e5d3472c: Pull complete 
a3da1957bd6b: Pull complete 
e42dbf1c67c4: Pull complete 
5a18b01011d2: Pull complete 
Digest: sha256:54d65cbcbff52ee7d789cd285fbe68f07a46e3419c8fcded437af4c616915c85
Status: Downloaded newer image for docker-registry.io/test/visualizer:latest
3c15b186ff51848130393944e09a427bd40d2504c54614f93e28477a4961f8b6
[root@docker-node01 docker]# docker ps 
CONTAINER ID        IMAGE                                COMMAND             CREATED             STATUS                            PORTS                    NAMES
3c15b186ff51        docker-registry.io/test/visualizer   "npm start"         6 seconds ago       Up 5 seconds (health: starting)   0.0.0.0:8888->8080/tcp   v1
[root@docker-node01 docker]# 

  提示:我上面的命令是從私有倉庫中下載的鏡像,原因是互聯網下載太慢了,所以我提前下載好,放在私有倉庫中;有關私有倉庫的搭建使用,請參考https://www.cnblogs.com/qiuhom-1874/p/13061984.html或者https://www.cnblogs.com/qiuhom-1874/p/13058338.html;在管理節點上運行visualizer容器后,我們就可以直接訪問該管理節點地址的8888端口,就可以看到當前容器的情況;如下圖

  提示:從上面的信息可以看到當前集群有一個管理節點和兩個work節點;現目前集群里沒有運行任何容器;

  在docker swarm運行服務

[root@docker-node01 ~]# docker service create --name myweb docker-registry.io/test/nginx:latest
i0j6wvvtfe1360ibj04jxulmd
overall progress: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged 
[root@docker-node01 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                  PORTS
i0j6wvvtfe13        myweb               replicated          1/1                 docker-registry.io/test/nginx:latest   
[root@docker-node01 ~]# docker service ps myweb
ID                  NAME                IMAGE                                  NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
99y8towew77e        myweb.1             docker-registry.io/test/nginx:latest   docker-node03       Running             Running 1 minutes ago                       
[root@docker-node01 ~]#

  提示:docker service create 表示在當前swarm集群環境中創建一個服務;以上命令表示在swarm集群上創建一個名為myweb的服務,用docker-registry.io/test/nginx:latest鏡像;默認情況下只啟動一個副本;

  提示:可以看到當前集群中運行了一個myweb的容器,並且運行在docker-node03這台主機上;

  在swarm 集群上創建多個副本服務

[root@docker-node01 ~]# docker service create --replicas 3 --name web docker-registry.io/test/nginx:latest
mbiap412jyugfpi4a38mb5i1k
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
[root@docker-node01 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                  PORTS
i0j6wvvtfe13        myweb               replicated          1/1                 docker-registry.io/test/nginx:latest   
mbiap412jyug        web                 replicated          3/3                 docker-registry.io/test/nginx:latest   
[root@docker-node01 ~]#docker service ps web
ID                  NAME                IMAGE                                  NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
1rt0e7u4senz        web.1               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 28 seconds ago                       
31ll0zu7udld        web.2               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 28 seconds ago                       
l9jtbswl2x22        web.3               docker-registry.io/test/nginx:latest   docker-node03       Running             Running 32 seconds ago                       
[root@docker-node01 ~]# 

  提示:–replicas選項用來指定期望運行的副本數量,該選項會在集群上創建我們指定數量的副本,即便我們集群中有節點宕機,它始終會創建我們指定數量的容器在集群上運行着;

  測試:把docker-node03關機,看看我們運行的服務是否會遷移到節點2上呢?

  docker-node03關機前

  docker-node03關機后

  提示:從上面的截圖可以看到,當節點3宕機后,節點3上跑的所有容器,會全部遷移到節點2上來;這就是創建容器時用–replicas選項的作用;總結一點,創建服務使用副本模式,該服務所在節點故障,它會把對應節點上的服務遷移到其他節點上;這裏需要提醒一點的是,只要集群上的服務副本滿足我們指定的replicas的數量,即便故障的節點恢復了,它是不會把服務遷移回來的;

[root@docker-node01 ~]# docker service ps web
ID                  NAME                IMAGE                                  NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
1rt0e7u4senz        web.1               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 15 minutes ago                        
31ll0zu7udld        web.2               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 15 minutes ago                        
t3gjvsgtpuql        web.3               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 6 minutes ago                         
l9jtbswl2x22         \_ web.3           docker-registry.io/test/nginx:latest   docker-node03       Shutdown            Shutdown 23 seconds ago                       
[root@docker-node01 ~]# 

  提示:我們在管理節點查看服務列表,可以看到它遷移服務就是把對應節點上的副本停掉,然後在其他節點創建一個新的副本;

  服務伸縮

[root@docker-node01 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                  PORTS
i0j6wvvtfe13        myweb               replicated          1/1                 docker-registry.io/test/nginx:latest   
mbiap412jyug        web                 replicated          3/3                 docker-registry.io/test/nginx:latest   
[root@docker-node01 ~]# docker service scale myweb=3 web=5
myweb scaled to 3
web scaled to 5
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
overall progress: 5 out of 5 tasks 
1/5: running   [==================================================>] 
2/5: running   [==================================================>] 
3/5: running   [==================================================>] 
4/5: running   [==================================================>] 
5/5: running   [==================================================>] 
verify: Service converged 
[root@docker-node01 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                  PORTS
i0j6wvvtfe13        myweb               replicated          3/3                 docker-registry.io/test/nginx:latest   
mbiap412jyug        web                 replicated          5/5                 docker-registry.io/test/nginx:latest   
[root@docker-node01 ~]# docker service ps myweb web
ID                  NAME                IMAGE                                  NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
j7w490h2lons        myweb.1             docker-registry.io/test/nginx:latest   docker-node02       Running             Running 12 minutes ago                       
1rt0e7u4senz        web.1               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 21 minutes ago                       
99y8towew77e        myweb.1             docker-registry.io/test/nginx:latest   docker-node03       Shutdown            Shutdown 5 minutes ago                       
en5rk0jf09wu        myweb.2             docker-registry.io/test/nginx:latest   docker-node03       Running             Running 31 seconds ago                       
31ll0zu7udld        web.2               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 21 minutes ago                       
h1hze7h819ca        myweb.3             docker-registry.io/test/nginx:latest   docker-node03       Running             Running 30 seconds ago                       
t3gjvsgtpuql        web.3               docker-registry.io/test/nginx:latest   docker-node02       Running             Running 12 minutes ago                       
l9jtbswl2x22         \_ web.3           docker-registry.io/test/nginx:latest   docker-node03       Shutdown            Shutdown 5 minutes ago                       
od3ti2ixpsgc        web.4               docker-registry.io/test/nginx:latest   docker-node03       Running             Running 31 seconds ago                       
n1vur8wbmkgz        web.5               docker-registry.io/test/nginx:latest   docker-node03       Running             Running 31 seconds ago                       
[root@docker-node01 ~]# 

  提示:docker service scale 命令用來指定服務的副本數量,從而實現動態伸縮;

  服務暴露

[root@docker-node01 ~]# docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                                  PORTS
i0j6wvvtfe13        myweb               replicated          3/3                 docker-registry.io/test/nginx:latest   
mbiap412jyug        web                 replicated          5/5                 docker-registry.io/test/nginx:latest   
[root@docker-node01 ~]# docker service update  --publish-add 80:80 myweb 
myweb
overall progress: 3 out of 3 tasks 
1/3: running   [==================================================>] 
2/3: running   [==================================================>] 
3/3: running   [==================================================>] 
verify: Service converged 
[root@docker-node01 ~]#

  提示:docker swarm集群中的服務暴露和docker裏面的端口暴露原理是一樣的,都是通過iptables 規則表或LVS規則實現的;

  提示:我們可以在管理節點上看到對應80端口已經處於監聽狀態,並且在iptables規則表中多了一項訪問本機80端口都DNAT到172.18.0.2的80上了;其實不光是在管理節點,在work節點上相應的iptables規則也都發生了變化;如下

  提示:從上面的規則來看,我們訪問節點地址的80端口,都會DNAT到172.18.0.2的80;

  提示:從上面是显示結果看,我們不難得知在docker-node02運行myweb容器的內部地址是10.0.0.7,那為什麼我們訪問172.18.0.2是能夠訪問到容器內部的服務呢?

  測試:我們在docker-node02追蹤查看nginx容器的訪問日誌,看看到容器的IP地址是那個?

[root@docker-node02 ~]# docker ps
CONTAINER ID        IMAGE                                  COMMAND                  CREATED             STATUS              PORTS               NAMES
2134e1b2c689        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   24 minutes ago      Up 24 minutes       80/tcp              nginx.1.ych7y3ugxp6o592pbz5k2i412
[root@docker-node02 ~]# docker logs -f nginx.1.ych7y3ugxp6o592pbz5k2i412 
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
10.0.0.3 - - [21/Jun/2020:02:37:11 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
172.18.0.1 - - [21/Jun/2020:02:38:35 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.0.0.2 - - [21/Jun/2020:02:53:32 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.0.0.2 - - [21/Jun/2020:02:53:58 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
^C
[root@docker-node02 ~]# 

  提示:我們在管理節點上訪問172.18.0.2在node2節點上看到的日誌是10.0.0.2的ip訪問到nginx服務;這是為什麼呢?其實原因就是在每個節點上都有一個ingress-sbox容器,該容器的地址就10.0.0.2;不同節點上的ingress-sbox的地址都不同,所以我們訪問不同節點地址,在nginx上看到地址也就不同;如下圖所示

  提示:訪問不同的節點地址,在nginx日誌上記錄的IP各不相同

  提示:從上面的截圖可以了解到每個節點的ingress-sbox容器的地址各不相同,但他們都把網關指向10.0.0.1,這意味着各個節點容器通信就可以基於這個網關來進行,從而實現了swarm集群上的容器間通信能夠基於ingress網絡進行;現在還有一個問題就是172.18.0.0/18的網絡是怎麼和10.0.0.0/24的網絡通信的?

  提示:從上面的截圖可以看到,在管理節點上有兩個網絡名稱空間,一個id為0,而id為0的網絡名稱空間中有veth0和vxlan0這兩個網卡;而veth0和vxlan0都是橋接到br0上的,br0的地址就是10.0.0.1/24;vxlan的vlan id為4096;結合上面nginx的日誌,不難想到

我們訪問管理節點上的80,通過iptables規則把流量轉發給docker-gwbridge網絡上;現在我們還不清楚docker-gwbridge網絡上那個名稱空間的網絡,但是我們清楚知道在容器內部有兩張網卡,一張是eth0,一張是eth1,而eth1就是橋接到docker-gwbridge網絡上,這也就意味着容docker-gwbridge網絡的名稱空間和容器內部的eth1網絡名稱空間相同;

  提示:從上面的截圖看,1-u5mwgfq7rb這個名稱的網絡名稱空間有三張網卡,分別是eth0,eth1和vxlan0,它們都是橋接在br0這個網卡上;而上面管理節點也在1-u5mwgfq7rb這個網絡名稱空間,並且它們中的vxlan0的vlan id都是4096,這意味着管理節點上的vxlan0可以同node2上的vxlan0直接通信(相同網絡名稱空間中的相同VLAN id是可以直接通信的),而vxlan0又是直接橋接到br0這塊網卡,所以我們在nginx日誌中能夠看到ingress-sbox容器的地址在訪問nginx;這其中的原因是ingress-sbox的網關就是br0;其實node3也是相同邏輯,不同節點上的容器間通信都是走vxlan0,與外部通信走eth1—->然後通過SNAT走docker-gwbridge—->物理網卡出去;

  提示:一個容器上有兩個網絡,一個是eth0 ingress網絡,一個是eth1屬於docker-gwbridge網絡,兩者都屬於同一容器中的網絡名稱空間,所以我們訪問172.18.0.2就會通過ingress-sbox容器把源地址更改為docker-gwbridge上的ingress-sbox的地址,從而我們在看nginx日誌,就會看到10.0.0.2的地址;ingress-sbox容器作用我們可以理解為做SNAT的作用;

  測試:訪問管理節點的80服務看看是否能夠訪問到nginx提供的頁面呢?

[root@docker-node02 ~]# docker ps
CONTAINER ID        IMAGE                                  COMMAND                  CREATED             STATUS              PORTS               NAMES
b829991d6966        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   About an hour ago   Up About an hour    80/tcp              myweb.1.ilhkslrlnreyo6xx5j2h9isjb
8c2965fbdc27        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              web.2.pthe8da2n45i06oee4n7h4krd
b019d663e48e        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              web.3.w26gqpoyysgplm7qwhjbgisiv
a7c1afd76f1f        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              web.1.ho0d7u3wensl0kah0ioz1lpk5
[root@docker-node02 ~]# docker exec -it myweb.1.ilhkslrlnreyo6xx5j2h9isjb  bash
root@b829991d6966:/# cd /usr/share/nginx/html/
root@b829991d6966:/usr/share/nginx/html# ls
50x.html  index.html
root@b829991d6966:/usr/share/nginx/html# echo "this is docker-node02 index page" >index.html
root@b829991d6966:/usr/share/nginx/html# cat index.html
this is docker-node02 index page
root@b829991d6966:/usr/share/nginx/html# 

  提示:以上是在docker-node02節點上對運行的nginx容器的主頁進行了修改,接下我們訪問管理節點的80端口,看看是否能夠訪問得到work節點上的容器,它們會有什麼效果?是輪詢?還是一直訪問一個容器?

  提示:可以看到我們訪問管理節點的80端口,會輪詢的訪問到work節點上的容器;用瀏覽器測試可能存在緩存的問題,我們可以用curl命令測試比較準確;如下

[root@docker-node03 ~]# docker ps
CONTAINER ID        IMAGE                                  COMMAND                  CREATED             STATUS              PORTS               NAMES
f43fdb9ec7fc        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              myweb.3.pgdjutofb5thlk02aj7387oj0
4470785f3d00        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              myweb.2.uwxbe182qzq00qgfc7odcmx87
7493dcac95ba        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              web.4.rix50fhlmg6m9txw9urk66gvw
118880d300f4        docker-registry.io/test/nginx:latest   "/docker-entrypoint.…"   2 hours ago         Up 2 hours          80/tcp              web.5.vo7c7vjgpf92b0ryelb7eque0
[root@docker-node03 ~]# docker exec -it myweb.2.uwxbe182qzq00qgfc7odcmx87 bash
root@4470785f3d00:/# cd /usr/share/nginx/html/
root@4470785f3d00:/usr/share/nginx/html# echo "this is myweb.2 index page" > index.html 
root@4470785f3d00:/usr/share/nginx/html# cat index.html
this is myweb.2 index page
root@4470785f3d00:/usr/share/nginx/html# exit
exit
[root@docker-node03 ~]# docker exec -it myweb.3.pgdjutofb5thlk02aj7387oj0 bash
root@f43fdb9ec7fc:/# cd /usr/share/nginx/html/
root@f43fdb9ec7fc:/usr/share/nginx/html# echo "this is myweb.3 index page" >index.html 
root@f43fdb9ec7fc:/usr/share/nginx/html# cat index.html
this is myweb.3 index page
root@f43fdb9ec7fc:/usr/share/nginx/html# exit
exit
[root@docker-node03 ~]# 

  提示:為了訪問方便看得出效果,我們把myweb.2和myweb.3的主頁都更改了內容

[root@docker-node01 ~]# for i in {1..10} ; do curl 192.168.0.41; done
this is myweb.3 index page
this is docker-node02 index page
this is myweb.2 index page
this is myweb.3 index page
this is docker-node02 index page
this is myweb.2 index page
this is myweb.3 index page
this is docker-node02 index page
this is myweb.2 index page
this is myweb.3 index page
[root@docker-node01 ~]# 

  提示:通過上面的測試,我們在使用–publish-add 暴露服務時,就相當於在管理節點創建了一個load balance;

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

【Spring註解驅動開發】如何使用@Bean註解指定初始化和銷毀的方法?看這一篇就夠了!!

寫在前面

在【String註解驅動開發專題】中,前面的文章我們主要講了有關於如何向Spring容器中註冊bean的知識,大家可以到【String註解驅動開發專題】中系統學習。接下來,我們繼續肝Spring,只不過從本篇文章開始,我們就進入Spring容器中有關Bean的生命周期的學習。

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

Bean的生命周期

通常意義上講的bean的名稱周期,指的是bean從創建到初始化,經過一系列的流程,最終銷毀的過程。只不過,在Spring中,bean的生命周期是由Spring容器來管理的。在Spring中,我們可以自己來指定bean的初始化和銷毀的方法。當我們指定了bean的初始化和銷毀方法時,當容器在bean進行到當前生命周期的階段時,會自動調用我們自定義的初始化和銷毀方法。

如何定義初始化和銷毀方法?

我們已經知道了由Spring管理bean的生命周期時,我們可以指定bean的初始化和銷毀方法,那具體該如何定義這些初始化和銷毀方法呢?接下來,我們就介紹第一種定義初始化和銷毀方法的方式: 通過@Bean註解指定初始化和銷毀方法。

如果是使用XML文件的方式配置bean的話,可以在 標籤中指定bean的初始化和銷毀方法,如下所示。

<bean id = "person" class="io.mykit.spring.plugins.register.bean.Person" init-method="init" destroy-method="destroy">
    <property name="name" value="binghe"></property>
    <property name="age" value="18"></property>
</bean>

這裏,需要注意的是,在我們寫的Person類中,需要存在init()方法和destroy()方法。而且Spring中規定,這裏的init()方法和destroy()方法必須是無參方法,但可以拋異常。

如果我們使用註解的方式,該如何實現指定bean的初始化和銷毀方法呢?接下來,我們就一起來搞定它!!

首先,創建一個名稱為Student的類,這個類的實現比較簡單,如下所示。

package io.mykit.spring.plugins.register.bean;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試bean的初始化和銷毀方法
 */
public class Student {
    
    public Student(){
        System.out.println("Student類的構造方法");
    }

    public void init(){
        System.out.println("初始化Student對象");
    }

    public void destroy(){
        System.out.println("銷毀Student對象");
    }
}

接下來,我們將Student類對象通過註解的方式註冊到Spring容器中,具體的做法就是新建一個LifeCircleConfig類作為Spring的配置類,將Student類對象通過LifeCircleConfig類註冊到Spring容器中,LifeCircleConfig類的代碼如下所示。

package io.mykit.spring.plugins.register.config;

import io.mykit.spring.plugins.register.bean.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author binghe
 * @version 1.0.0
 * @description Bean的生命周期
 */
@Configuration
public class LifeCircleConfig {
    @Bean
    public Student student(){
        return new Student();
    }
}

接下來,我們就新建一個BeanLifeCircleTest類來測試容器中的Student對象,BeanLifeCircleTest類的部分代碼如下所示。

package io.mykit.spring.test;

import io.mykit.spring.plugins.register.config.LifeCircleConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試bean的生命周期
 */
public class BeanLifeCircleTest {

    @Test
    public void testBeanLifeCircle01(){
        //創建IOC容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
        System.out.println("容器創建完成...");
    }
}

在前面的文章中,我們說過:對於單實例bean對象來說,在Spring容器創建完成后,就會對單實例bean進行實例化。那麼,我們先來運行下BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

Student類的構造方法
容器創建完成...

可以看到,在Spring容器創建完成時,自動調用單實例bean的構造方法,對單實例bean進行了實例化操作。

總之:對於單實例bean來說,在Spring容器啟動的時候創建對象;對於多實例bean來說,在每次獲取bean的時候創建對象。

現在,我們在Student類中指定了init()方法和destroy()方法,那麼,如何讓Spring容器知道Student類中的init()方法是用來執行對象的初始化操作,而destroy()方法是用來執行對象的銷毀操作呢?如果是使用XML文件配置的話,我們可以使用如下配置來實現。

<bean id="student" class="io.mykit.spring.plugins.register.bean.Student" init-method="init" destroy-method="destroy"></bean>

如果我們在@Bean註解中該如何實現呢?其實就更簡單了,我們來看下@Bean註解的源碼,如下所示。

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

	@AliasFor("name")
	String[] value() default {};

	@AliasFor("value")
	String[] name() default {};

	@Deprecated
	Autowire autowire() default Autowire.NO;

	boolean autowireCandidate() default true;

	String initMethod() default "";

	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;

}

看到@Bean註解的源碼,相信小夥伴們會有種豁然開朗的感覺:沒錯,就是使用@Bean註解的initMethod屬性和destroyMethod屬性來指定bean的初始化方法和銷毀方法。

所以,我們在LifeCircleConfig類中的@Bean註解中指定initMethod屬性和destroyMethod屬性,如下所示。

@Bean(initMethod = "init", destroyMethod = "destroy")
public Student student(){
    return new Student();
}

此時,我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

Student類的構造方法
初始化Student對象
容器創建完成...

從輸出結果可以看出,在Spring容器中,先是調用了Student類的構造方法來創建Student對象,接下來調用了Student對象的init()方法來進行初始化。

那小夥伴們可能會問,運行上面的代碼沒有打印出bean的銷毀方法中的信息啊,那什麼時候執行bean的銷毀方法呢? 這個問題問的很好, bean的銷毀方法是在容器關閉的時候調用的。

接下來,我們在BeanLifeCircleTest類中的testBeanLifeCircle01()方法中,添加關閉容器的代碼,如下所示。

@Test
public void testBeanLifeCircle01(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
    System.out.println("容器創建完成...");
    context.close();
}

我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

Student類的構造方法
初始化Student對象
容器創建完成...
銷毀Student對象

可以看到,此時輸出了對象的銷毀方法中的信息,說明執行了對象的銷毀方法。

指定初始化和銷毀方法的使用場景

一個典型的使用場景就是對於數據源的管理。例如,在配置數據源時,在初始化的時候,對很多的數據源的屬性進行賦值操作;在銷毀的時候,我們需要對數據源的連接等信息進行關閉和清理。此時,我們就可以在自定義的初始化和銷毀方法中來做這些事情!

初始化和銷毀方法調用的時機

  • bean對象的初始化方法調用的時機:對象創建完成,如果對象中存在一些屬性,並且這些屬性也都賦值好之後,會調用bean的初始化方法。對於單實例bean來說,在Spring容器創建完成后,Spring容器會自動調用bean的初始化和銷毀方法;對於單實例bean來說,在每次獲取bean對象的時候,調用bean的初始化和銷毀方法。
  • bean對象的銷毀方法調用的時機:對於單實例bean來說,在容器關閉的時候,會調用bean的銷毀方法;對於多實例bean來說,Spring容器不會管理這個bean,也不會自動調用這個bean的銷毀方法。不過,小夥伴們可以手動調用多實例bean的銷毀方法。

前面,我們已經說了單實例bean的初始化和銷毀方法。接下來,我們來說下多實例bean的初始化和銷毀方法。我們將Student對象變成多實例bean來驗證下。接下來,我們在LifeCircleConfig類的student()方法上通過@Scope註解將Student對象設置成多實例bean,如下所示。

@Scope("prototype")
@Bean(initMethod = "init", destroyMethod = "destroy")
public Student student(){
    return new Student();
}

接下來,我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

容器創建完成...

可以看到,當我們將Student對象設置成多實例bean,並且沒有獲取bean實例對象時,Spring容器並沒有執行bean的構造方法、初始化方法和銷毀方法。

說到這,我們就在BeanLifeCircleTest類中的testBeanLifeCircle01()方法中添加一行獲取Student對象的代碼,如下所示。

@Test
public void testBeanLifeCircle01(){
    //創建IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LifeCircleConfig.class);
    System.out.println("容器創建完成...");
    context.getBean(Student.class);
    context.close();
}

此時,我們再來運行BeanLifeCircleTest類中的testBeanLifeCircle01()方法,輸出的結果信息如下所示。

容器創建完成...
Student類的構造方法
初始化Student對象

可以看到,此時,結果信息中輸出了構造方法和初始化方法中的信息。但是當容器關閉時,並沒有輸出bean的銷毀方法中的信息。

這是因為 將bean設置成多實例時,Spring不會自動調用bean對象的銷毀方法。至於多實例bean對象何時銷毀,那就是程序員自己的事情了!!Spring容器不再管理多實例bean。

好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回復“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。

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

【其他文章推薦】

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

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

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

網頁設計最專業,超強功能平台可客製化