『壹』 生產環境,測試環境中,Docker 可以做什麼
上次有人說不敢在生產環境中用 Docker,所以 SegmentFault 社區組織翻譯 8 個你可能不知道的 Docker 知識 這篇文章,和大家介紹一下生產環境中的 Docker 用例。
自從上世紀 90 年代硬體虛擬化被主流的技術廣泛普及之後,對數據中心而言,發生的最大的變革莫過於容器和容器管理工具,例如:Docker。在過去的一年內,Docker 技術已經逐漸走向成熟,並且推動了大型初創公司例如 Twitter 和 Airbnb 的發展,甚至在銀行、連鎖超市、甚至 NASA 的數據中心都贏得了一席之地。當我幾年前第一次直到 Docker 的時候,我還對 Docker 的未來持懷疑的態度,我認為他們是把以前的 Linux 容器的概念拿出來包裝了一番推向市場。但是使用 Docker 成功進行了幾個項目 例如 Spantree 之後,我改變了我的看法:Docker 幫助我們節省了大量的時間和經歷,並且已經成為我們技術團隊中不可或缺的工具。
GitHub 上面每天都會催生出各式各樣的工具、形態各異的語言和千奇百怪的概念。如果你和我一樣,沒有時間去把他們全部都測試一遍,甚至沒有時間去親自測試 Docker,那麼你可以看一下我的這篇文章:我將會用我們在 Docker 中總結的經驗來告訴你什麼是 Docker、為什麼 Docker 會這么火。
Docker 是容器管理工具
Docker 是一個輕量級、攜帶型、與外界隔離的容器,也是一個可以在容器中很方便地構建、傳輸、運行應用的引擎。和傳統的虛擬化技術不同的是,Docker 引擎並不虛擬出一台虛擬機,而是直接使用宿主機的內核和硬體,直接在宿主機上運行容器內應用。也正是得益於此,Docker 容器內運行的應用和宿主機上運行的應用性能差距幾乎可以忽略不計。
但是 Docker 本身並不是一個容器系統,而是一個基於原有的容器化工具 LXC 用來創建虛擬環境的工具。類似 LXC 的工具已經在生產環境中使用多年,Docker 則基於此提供了更加友好的鏡像管理工具和部署工具。
Docker 不是虛擬化引擎
Docker 第一次發布的時候,很多人都拿 Docker 和虛擬機 VMware、KVM 和 VirtualBox 比較。盡管從功能上看,Docker 和虛擬化技術致力於解決的問題都差不多,但是 Docker 卻是採取了另一種非常不同的方式。虛擬機是虛擬出一套硬體,虛擬機的系統進行的磁碟操作,其實都是在對虛擬出來的磁碟進行操作。當運行 CPU 密集型的任務時,是虛擬機把虛擬系統里的 CPU 指令「翻譯」成宿主機的CPU指令並進行執行。兩個磁碟層,兩個處理器調度器,兩個操作系統消耗的內存,所有虛擬出的這些都會帶來相當多的性能損失,一台虛擬機所消耗的硬體資源和對應的硬體相當,一台主機上跑太多的虛擬機之後就會過載。而 Docker 就沒有這種顧慮。Docker 運行應用採取的是「容器」的解決方案:使用 namespace 和 CGroup 進行資源限制,和宿主機共享內核,不虛擬磁碟,所有的容器磁碟操作其實都是對 /var/lib/docker/ 的操作。簡言之,Docker 其實只是在宿主機中運行了一個受到限制的應用程序。
從上面不難看出,容器和虛擬機的概念並不相同,容器也並不能取代虛擬機。在容器力所不能及的地方,虛擬機可以大顯身手。例如:宿主機是 Linux,只能通過虛擬機運行 Windows,Docker 便無法做到。再例如,宿主機是 Windows,Windows 並不能直接運行 Docker,Windows上的 Docker 其實是運行在 VirtualBox 虛擬機里的。
Docker 使用層級的文件系統
前面提到過,Docker 和現有容器技術 LXC 等相比,優勢之一就是 Docker 提供了鏡像管理。對於 Docker 而言,鏡像是一個靜態的、只讀的容器文件系統的快照。然而不僅如此,Docker 中所有的磁碟操作都是對特定的Copy-On-Write文件系統進行的。下面通過一個例子解釋一下這個問題。
例如我們要建立一個容器運行 JAVA Web 應用,那麼我們應該使用一個已經安裝了 JAVA 的鏡像。在 Dockerfile(一個用於生成鏡像的指令文件)中,應該指明「基於 JAVA 鏡像」,這樣 Docker 就會去 Docker Hub Registry 上下載提前構建好的 JAVA 鏡像。然後再 Dockerfile 中指明下載並解壓 Apache Tomcat 軟體到 /opt/tomcat 文件夾中。這條命令並不會對原有的 JAVA 鏡像產生任何影響,而僅僅是在原有鏡像上面添加了一個改動層。當一個容器啟動時,容器內的所有改動層都會啟動,容器會從第一層中運行 /usr/bin/java 命令,並且調用另外一層中的 /opt/tomcat/bin 命令。實際上,Dockerfile 中每一條指令都會產生一個新的改動層,即便只有一個文件被改動。如果用過 Git 就能更清楚地認識這一點,每條指令就像是每次 commit,都會留下記錄。但是對於 Docker 來說,這種文件系統提供了更大的靈活性,也可以更方便地管理應用程序。
我們Spantree的團隊有一個自己維護的含有 Tomcat 的鏡像。發布新版本也非常簡單:使用 Dockerfile 將新版本拷貝進鏡像從而創建一個新鏡像,然後給新鏡像貼上版本的標簽。不同版本的鏡像的不同之處僅僅是一個 90 MB 大小的 WAR 文件,他們所基於的主鏡像都是相同的。如果使用虛擬機去維護這些不同的版本的話,還要消耗掉很多不同的磁碟去存儲相同的系統,而使用 Docker 就只需要很小的磁碟空間。即便我們同時運行這個鏡像的很多實例,我們也只需要一個基礎的 JAVA / TOMCAT 鏡像。
Docker 可以節約時間
很多年前我在為一個連鎖餐廳開發軟體時,僅僅是為了描述如何搭建環境都需要寫一個 12 頁的 Word 文檔。例如本地 Oracle 資料庫,特定版本的 JAVA,以及其他七七八八的系統工具和共享庫、軟體包。整個搭建過程浪費掉了我們團隊每個人幾乎一天的時間,如果用金錢衡量的話,花掉了我們上萬美金的時間成本。雖然客戶已經對這種事情習以為常,甚至認為這是引入新成員、讓成員適應環境、讓自己的員工適應我們的軟體所必須的成本,但是相比較起來,我們寧願把更多的時間花在為客戶構建可以增進業務的功能上面。
如果當時有 Docker,那麼構建環境就會像使用自動化搭建工具 Puppet / Chef / Salt / Ansible 一樣簡單,我們也可以把整個搭建時間周期從一天縮短為幾分鍾。但是和這些工具不同的地方在於,Docker 可以不僅僅可以搭建整個環境,還可以將整個環境保存成磁碟文件,然後復制到別的地方。需要從源碼編譯 Node.js 嗎?Docker 做得到。Docker 不僅僅可以構建一個 Node.js 環境,還可以將整個環境做成鏡像,然後保存到任何地方。當然,由於 Docker 是一個容器,所以不用擔心容器內執行的東西會對宿主機產生任何的影響。
現在新加入我們團隊的人只需要運行 docker-compose up 命令,便可以喝杯咖啡,然後開始工作了。
Docker 可以節省開銷
當然,時間就是金錢。除了時間外,Docker 還可以節省在基礎設施硬體上的開銷。高德納和麥肯錫的研究表明,數據中心的利用率在 6% - 12% 左右。不僅如此,如果採用虛擬機的話,你還需要被動地監控和設置每台虛擬機的 CPU 硬碟和內存的使用率,因為採用了靜態分區(static partitioning)所以資源並不能完全被利用。。而容器可以解決這個問題:容器可以在實例之間進行內存和磁碟共享。你可以在同一台主機上運行多個服務、可以不用去限制容器所消耗的資源、可以去限制資源、可以在不需要的時候停止容器,也不用擔心啟動已經停止的程序時會帶來過多的資源消耗。凌晨三點的時候只有很少的人會去訪問你的網站,同時你需要比較多的資源執行夜間的批處理任務,那麼可以很簡單的便實現資源的交換。
虛擬機所消耗的內存、硬碟、CPU 都是固定的,一般動態調整都需要重啟虛擬機。而用 Docker 的話,你可以進行資源限制,得益於 CGroup,可以很方便動態調整資源限制,讓然也可以不進行資源限制。Docker 容器內的應用對宿主機而言只是兩個隔離的應用程序,並不是兩個虛擬機,所以宿主機也可以自行去分配資源。
『貳』 常見的容器安全威脅有哪些
以Docker 容器的安全問題為例
(1) Docker 自身安全
Docker 作為一款容器引擎,本身也會存在一些安全漏洞,CVE 目前已經記錄了多項與 Docker 相關的安全漏洞,主要有許可權提升、信息泄露等幾類安全問題。
(2) 鏡像安全
由於Docker 容器是基於鏡像創建並啟動,因此鏡像的安全直接影響到容器的安全。具體影響鏡像安全的總結如下。
鏡像軟體存在安全漏洞:由於容器需要安裝基礎的軟體包,如果軟體包存在漏洞,則可能會被不法分子利用並且侵入容器,影響其他容器或主機安全。
倉庫漏洞:無論是Docker 官方的鏡像倉庫還是我們私有的鏡像倉庫,都有可能被攻擊,然後篡改鏡像,當我們使用鏡像時,就可能成為攻擊者的目標對象。
用戶程序漏洞:用戶自己構建的軟體包可能存在漏洞或者被植入惡意腳本,這樣會導致運行時提權影響其他容器或主機安全。
(3) Linux 內核隔離性不夠
盡管目前Namespace 已經提供了非常多的資源隔離類型,但是仍有部分關鍵內容沒有被完全隔離,其中包括一些系統的關鍵性目錄(如 /sys、/proc 等),這些關鍵性的目錄可能會泄露主機上一些關鍵性的信息,讓攻擊者利用這些信息對整個主機甚至雲計算中心發起攻擊。
而且僅僅依靠Namespace 的隔離是遠遠不夠的,因為一旦內核的 Namespace 被突破,使用者就有可能直接提權獲取到主機的超級許可權,從而影響主機安全。
(4) 所有容器共享主機內核
由於同一宿主機上所有容器共享主機內核,所以攻擊者可以利用一些特殊手段導致內核崩潰,進而導致主機宕機影響主機上其他服務。
既然容器有這么多安全上的問題,那麼我們應該如何做才能夠既享受到容器的便利性同時也可以保障容器安全呢?下面我帶你來逐步了解下如何解決容器的安全問題。
如何解決容器的安全問題?
(1) Docker 自身安全性改進
事實上,Docker 從 2013 年誕生到現在,在安全性上面已經做了非常多的努力。目前 Docker 在默認配置和默認行為下是足夠安全的。
Docker 自身是基於 Linux 的多種 Namespace 實現的,其中有一個很重要的 Namespace 叫作 User Namespace,User Namespace 主要是用來做容器內用戶和主機的用戶隔離的。在過去容器里的 root 用戶就是主機上的 root 用戶,如果容器受到攻擊,或者容器本身含有惡意程序,在容器內就可以直接獲取到主機 root 許可權。Docker 從 1.10 版本開始,使用 User Namespace 做用戶隔離,實現了容器中的 root 用戶映射到主機上的非 root 用戶,從而大大減輕了容器被突破的風險。
因此,我們盡可能地使用Docker 最新版本就可以得到更好的安全保障。
(2) 保障鏡像安全
為保障鏡像安全,我們可以在私有鏡像倉庫安裝鏡像安全掃描組件,對上傳的鏡像進行檢查,通過與CVE 資料庫對比,一旦發現有漏洞的鏡像及時通知用戶或阻止非安全鏡像繼續構建和分發。同時為了確保我們使用的鏡像足夠安全,在拉取鏡像時,要確保只從受信任的鏡像倉庫拉取,並且與鏡像倉庫通信一定要使用 HTTPS 協議。
(3) 加強內核安全和管理
由於僅僅依賴內核的隔離可能會引發安全問題,因此我們對於內核的安全應該更加重視。可以從以下幾個方面進行加強。
宿主機及時升級內核漏洞
宿主機內核應該盡量安裝最新補丁,因為更新的內核補丁往往有著更好的安全性和穩定性。
使用Capabilities 劃分許可權
Capabilities 是 Linux 內核的概念,Linux 將系統許可權分為了多個 Capabilities,它們都可以單獨地開啟或關閉,Capabilities 實現了系統更細粒度的訪問控制。
容器和虛擬機在許可權控制上還是有一些區別的,在虛擬機內我們可以賦予用戶所有的許可權,例如設置cron 定時任務、操作內核模塊、配置網路等許可權。而容器則需要針對每一項 Capabilities 更細粒度的去控制許可權,例如:
cron 定時任務可以在容器內運行,設置定時任務的許可權也僅限於容器內部;
由於容器是共享主機內核的,因此在容器內部一般不允許直接操作主機內核;
容器的網路管理在容器外部,這就意味著一般情況下,我們在容器內部是不需要執行ifconfig、route等命令的 。
由於容器可以按照需求逐項添加Capabilities 許可權,因此在大多數情況下,容器並不需要主機的 root 許可權,Docker 默認情況下也是不開啟額外特權的。
最後,在執行docker run命令啟動容器時,如非特殊可控情況,–privileged 參數不允許設置為 true,其他特殊許可權可以使用 --cap-add 參數,根據使用場景適當添加相應的許可權。
使用安全加固組件
Linux 的 SELinux、AppArmor、GRSecurity 組件都是 Docker 官方推薦的安全加固組件。下面我對這三個組件做簡單介紹。
SELinux (Secure Enhanced Linux): 是 Linux 的一個內核安全模塊,提供了安全訪問的策略機制,通過設置 SELinux 策略可以實現某些進程允許訪問某些文件。
AppArmor: 類似於 SELinux,也是一個 Linux 的內核安全模塊,普通的訪問控制僅能控制到用戶的訪問許可權,而 AppArmor 可以控制到用戶程序的訪問許可權。
GRSecurity: 是一個對內核的安全擴展,可通過智能訪問控制,提供內存破壞防禦,文件系統增強等多種防禦形式。
這三個組件可以限制一個容器對主機的內核或其他資源的訪問控制。目前,容器報告的一些安全漏洞中,很多都是通過對內核進行加強訪問和隔離來實現的。
資源限制
在生產環境中,建議每個容器都添加相應的資源限制。下面給出一些執行docker run命令啟動容器時可以傳遞的資源限制參數:
1--cpus 限制 CPU 配額
2-m, --memory 限制內存配額
3--pids-limit 限制容器的 PID 個數
例如我想要啟動一個1 核 2G 的容器,並且限制在容器內最多隻能創建 1000 個 PID,啟動命令如下:
1 $ docker run -it --cpus=1 -m=2048m --pids-limit=1000 busybox sh
推薦在生產環境中限制CPU、內存、PID 等資源,這樣即便應用程序有漏洞,也不會導致主機的資源完全耗盡,最大限度降低安全風險。
(4) 使用安全容器
容器有著輕便快速啟動的優點,虛擬機有著安全隔離的優點,有沒有一種技術可以兼顧兩者的優點,做到既輕量又安全呢?
答案是有,那就是安全容器。安全容器是相較於普通容器的,安全容器與普通容器的主要區別在於,安全容器中的每個容器都運行在一個單獨的微型虛擬機中,擁有獨立的操作系統和內核,並且有虛擬化層的安全隔離。
安全容器目前推薦的技術方案是Kata Containers,Kata Container 並不包含一個完整的操作系統,只有一個精簡版的 Guest Kernel 運行著容器本身的應用,並且通過減少不必要的內存,盡量共享可以共享的內存來進一步減少內存的開銷。另外,Kata Container 實現了 OCI 規范,可以直接使用 Docker 的鏡像啟動 Kata 容器,具有開銷更小、秒級啟動、安全隔離等許多優點。
『叄』 怎麼限制cpu
現在很多高端筆記本游戲本都有一個通病,那就是在玩大型游戲和渲染輸出視頻的時候,CPU會滿載運行以至於溫度飆升,下面就教大家如何限制cpu頻率吧。
1、首先找到控制面板,打開控制面板,打開這個電源選項。
2、這里有三種模式,我這里以高性能模式為例(其他兩種模式也步驟一樣),點擊更改計劃設置。
3、然後點擊更改高級電源設置。
4、這里選擇處理器,依次展開,打開最大處理器狀態,限制我們限制的就是這個數值。
5、我們分別將這兩個數據都設置為85%,以我的本子為例,處理器最高睿頻為3.4Ghz,限制最高性能在85%後最高睿頻為2Ghz左右,此時玩游戲是感覺不到任何區別的,但是溫度卻降下來了,對於那些散熱不給力的本本,這個方法可以有效降低CPU高溫問題。
拓展資料:
中央處理器(CPU,central processing unit)作為計算機系統的運算和控制核心,是信息處理、程序運行的最終執行單元。CPU 自產生以來,在邏輯結構、運行效率以及功能外延上取得了巨大發展。
中央處理器主要包括兩個部分,即控制器、運算器,其中還包括高速緩沖存儲器及實現它們之間聯系的數據、控制的匯流排。電子計算機三大核心部件就是CPU、內部存儲器、輸入/輸出設備。中央處理器的功效主要為處理指令、執行操作、控制時間、處理數據。
在計算機體系結構中,CPU 是對計算機的所有硬體資源(如存儲器、輸入輸出單元) 進行控制調配、執行通用運算的核心硬體單元。CPU 是計算機的運算和控制核心。計算機系統中所有軟體層的操作,最終都將通過指令集映射為CPU的操作。
『肆』 如何實現自己的linux container
這幾個flag 可以在調用clone時候作為參數傳入,從而實現namespace的隔離,
從這個角度來說,container跟主要是進程角度的隔離,而不是傳統的虛擬機,
因為它底層用用的同一個內核來調度。
cgroup 是linux 內核的另外一個控制和隔離進程的特性,他分為cpu ,memory,net,io等幾個子系統,從而實現對進程cpu,內存,磁碟,網路等資源使用的控制。
製作自己容器,需要一個image ,可以從網上下一個,也可以自己製作,製作很簡單,新裝一個操作系統,安裝一些需要用到的軟體包,然後用tar 製作 / 目錄下的壓縮包,去掉一些虛擬文件系統的文件,本文用的是自己製作的centos 6.5 的image。
容器實現過程可以歸納為
1, 用clone系統調用 創建子進程,傳入namespace的那幾個參數,實現namespace的隔離
2, 父進程中創建veth pair ,一個veth在自己的namespace,將另一個設置為子進程的namespace,實現container和宿主機的網路通信
3, 父進程創建cgroup memory和cpuset子系統,將子進程attach到cgroup子系統上,實現container 的資源限制和隔離
4, 子進程在自己的namespace里,設置主機名,mount proc虛擬文件系統,設置veth ip,chroot到centos 6鏡像的位置, 最終將進程鏡像替換成/bin/bash
5, 父進程調用waitpid 等待子進程退出
『伍』 如何限制容器內存大小
container中的free命令,看的是Host上的內存。 要查看container的內存限制用這個: cat /cgroup/memory/lxc/{full_container_id}/memory.limit_in_bytes /cgroup/下還可以查看其它資源的限制: ll /cgroup/
『陸』 如何分配docker容器的系統資源
最近在和阿里的一些同事談起使用Docker部署Java應用的場景,其中一個大家普遍關心的問題就是如何設置容器中JVM的內存限制。 如果使用官方的Java鏡像,或者基於Java鏡像構建的Docker鏡像,都可以通過傳遞 JAVA_OPTS 環境變數來輕松地設置JVM的內存參數。比如,對於官方Tomcat 鏡像,我們可以執行下面命令來啟動一個最大內存為512M的tomcat實例 docker run --rm -e JAVA_OPTS='-Xmx512m' tomcat:8 在日誌中,我們可以清楚地發現設置已經生效 「Command line argument: -Xmx512m」 02-Apr-2016 12:46:26/denverdino/tomcat:8-autoheap ,其源代碼可以從Github 獲得。它基於Docker官方Tomcat鏡像創建,它的啟動腳本會檢查CGroup中內存限置,並計算JVM最大Heap size來傳遞給Tomcat。其代碼如下 #!/bin/bash limit_in_bytes=$(cat /sys/fs/cgroup/memory/memory/denverdino/tomcat:8-autoheap 通過下列命令,從日誌中我們可以檢測到相應的JVM參數已經被設置成 448MB (512-64) docker logs test ... 02-Apr-2016 14:18:09.870 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx448m ... 我們也可以方便的調整Java應用的內存. Docker 1.10提供了對容器資源限制的動態修改能力。但是由於JVM無法感知容器資源修改,我們依然需要重啟tomcat來變更JVM的內存設置,例如,我們可以通過下面命令把容器內存限制調整到1GB docker update -m 1024m test docker restart test 再次檢查日誌,相應的JVM Heap Size最大值已被設置為960MB docker logs test ... 02-Apr-2016 14:21:07.644 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx960m ...
『柒』 docker可以控制很多資源,目前還不能對如下哪些資源進行隔離
不能對硬碟I/O讀寫進行隔離。
硬碟I/O是指硬碟的輸入和輸出(Input和Output的縮寫)。就是發指令,從磁碟讀取某段扇區的內容。指令一般是通知磁碟開始扇區位置,然後給出需要從這個初始扇區往後讀取的連續扇區個數,同時給出動作是讀,還是寫。
對於磁碟I/O資源來說,考量的參數是容量和讀寫速度,因此對容器的磁碟限制也應該從這兩個維度出發。目前Docker 支持對磁碟的讀寫速度進行限制,但是並沒有方法能限制容器能使用的磁碟容量(一旦磁碟 mount 到容器里,容器就能夠使用磁碟的所有容量)。
(7)容器如何做資源限制擴展閱讀
引擎局限性:
1、Docker是基於Linux 64bit的,無法在32bit的linux/Windows/unix環境下使用。
2、LXC是基於cgroup等linux kernel功能的,因此container的guest系統只能是linux base的。
3、隔離性相比KVM之類的虛擬化方案還是有些欠缺,所有container公用一部分的運行庫。
4、網路管理相對簡單,主要是基於namespace隔離。
5、cgroup的cpu和cpuset提供的cpu功能相比KVM的等虛擬化方案相比難以度量(所以dotcloud主要是按內存收費)。
6、Docker對disk的管理比較有限。
7、container隨著用戶進程的停止而銷毀,container中的log等用戶數據不便收集。
『捌』 如何使用 Docker 來限制 CPU,內存和 IO等資源
Docker 作為容器的管理者,自然提供了控制容器資源的功能。正如使用內核的 namespace 來做容器之間的隔離,Docker 也是通過內核的 cgroups 來做容器的資源限制。
『玖』 如何實時查看Docker容器佔用的CPU,內存狀態
Docker
作為容器的管理者,自然提供了控制容器資源的功能。
正如使用內核的
namespace
來做容器之間的隔離,Docker
也是通過內核的
cgroups
來做容器的資源限制。
『拾』 如何設置Docker容器中Java應用的內存限制
最近在和阿里的一些同事談起使用Docker部署Java應用的場景,其中一個大家普遍關心的問題就是如何設置容器中JVM的內存限制。
如果使用官方的Java鏡像,或者基於Java鏡像構建的Docker鏡像,都可以通過傳遞 JAVA_OPTS 環境變數來輕松地設置JVM的內存參數。比如,對於官方Tomcat 鏡像,我們可以執行下面命令來啟動一個最大內存為512M的tomcat實例
docker run --rm -e JAVA_OPTS='-Xmx512m' tomcat:8
在日誌中,我們可以清楚地發現設置已經生效 「Command line argument: -Xmx512m」
02-Apr-2016 12:46:26.970 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/8.0.32
02-Apr-2016 12:46:26.974 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Feb 2 2016 19:34:53 UTC
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number: 8.0.32.0
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 4.1.19-boot2docker
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64
02-Apr-2016 12:46:26.975 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /usr/lib/jvm/java-7-openjdk-amd64/jre
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 1.7.0_95-b00
02-Apr-2016 12:46:26.976 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Oracle Corporation
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
02-Apr-2016 12:46:26.977 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
02-Apr-2016 12:46:26.978 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx512m
...
然而在Docker集群上部署運行Java容器應用的時候,僅僅對JVM的heap參數設置是不夠的,我們還需要對Docker容器的內存資源進行限制:
1. 限制容器使用的內存的最大量,防止對系統或其他應用造成傷害
2. 能夠將Docker容器調度到擁有足夠空餘的內存的節點,從而保證應用的所需運行資源
關於容器的資源分配約束,Docker提供了相應的啟動參數
對內存而言,最基本的就是通過 -m參數來約束容器使用內存的大小
-m, --memory=""
Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M.
那麼問題就來了,為了正確設置Docker容器內存的大小,難道我們需要同時傳遞容器的內存限制和JAVA_OPTS環境變數嗎? 如下所示:
docker run --rm -m 512m -e JAVA_OPTS='-Xmx512m' tomcat:8
這個方法有兩個問題
1. 需要管理員保證容器內存和JVM內存設置匹配,否則可能引發錯誤
2. 當對容器內存限制調整時,環境變數也需要重新設定,這就需要重建一個新的容器
是否有一個方法,可以讓容器內部的JVM自動適配容器的內存限制?這樣可以採用更加統一的方法來進行資源管理,簡化配置工作。
大家知道Docker是通過CGroup來實現資源約束的,自從1.7版本之後,Docker把容器的local cgroups以只讀方式掛載到容器內部的文件系統上,這樣我們就可以在容器內部,通過cgroups信息來獲取系統對當前容器的資源限制了。
我創建了一個示例鏡像 registry.aliyuncs.com/denverdino/tomcat:8-autoheap
,其源代碼可以從Github 獲得。它基於Docker官方Tomcat鏡像創建,它的啟動腳本會檢查CGroup中內存限置,並計算JVM最大Heap size來傳遞給Tomcat。其代碼如下
#!/bin/bash
limit_in_bytes=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
# If not default limit_in_bytes in cgroup
if [ "$limit_in_bytes" -ne "9223372036854771712" ]
then
limit_in_megabytes=$(expr $limit_in_bytes \/ 1048576)
heap_size=$(expr $limit_in_megabytes - $RESERVED_MEGABYTES)
export JAVA_OPTS="-Xmx${heap_size}m $JAVA_OPTS"
echo JAVA_OPTS=$JAVA_OPTS
fi
exec catalina.sh run
說明:
為了監控,故障排查等場景,我們預留了部分內存(預設64M),其餘容器內存我們都分配給JVM的堆。
這里沒有對邊界情況做進一步處理。在生產系統中需要根據情況做相應的設定,比如最大的堆大小等等。
現在我們啟動一個tomcat運行在512兆的容器中
docker run -d --name test -m 512m registry.aliyuncs.com/denverdino/tomcat:8-autoheap
通過下列命令,從日誌中我們可以檢測到相應的JVM參數已經被設置成 448MB (512-64)
docker logs test
...
02-Apr-2016 14:18:09.870 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx448m
...
我們也可以方便的調整Java應用的內存.
Docker 1.10提供了對容器資源限制的動態修改能力。但是由於JVM無法感知容器資源修改,我們依然需要重啟tomcat來變更JVM的內存設置,例如,我們可以通過下面命令把容器內存限制調整到1GB
docker update -m 1024m test
docker restart test
再次檢查日誌,相應的JVM Heap Size最大值已被設置為960MB
docker logs test
...
02-Apr-2016 14:21:07.644 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Xmx960m
...