<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

         摘要: from:https://zhangge.net/4703.html昨天,同事告訴我發現一個詭異的問題,grep無法搜索shell中的變量,著實很驚訝。到他所說的服務器上試了下,還真是不行!大概就是這樣一個要求:①、有個文本為userid.txt,里面每一行一個用戶id,類似如下:Shell1234500010003000500070009②、另外還有一個文本為record...  閱讀全文
    posted @ 2017-03-01 16:59 小馬歌 閱讀(746) | 評論 (0)編輯 收藏
     

    來自: http://tonybai.com/2016/02/26/deploy-a-private-docker-registry/

    安裝部署一個私有的Docker Registry是引入、學習和使用 Docker 這門技術的必經之路之一。尤其是當Docker被所在組織接受,更多人、項目和產品開始接觸和使用Docker時,存儲和分發自制的Docker image便成了剛需。Docker Registry一如既往的繼承了“Docker坑多”的特點,為此這里將自己搭建”各類”Registry過程中執行的步驟、遇到的問題記錄下來,為己備忘,為他參考。

    Docker在2015年推出了 distribution 項目,即Docker Registry 2。相比于 old registry ,Registry 2使用Go實現,在安全性、性能方面均有大幅改進。Registry設計了全新的Rest API,并且在image存儲格式等方面不再兼容于old Registry。去年8月份,docker官方hub使用Registriy 2.1替代了原先的old Registry。如果你要與Registry2交互,你的Docker版本至少要是Docker 1.6。

    Docker的開發者也一直在致力于改善Registry安裝和使用的體驗,通過提供 官方Registry Image以及 Docker Compose工具 等來簡化Registry的配置。不過在本文中,我們只是利用Docker以及Registry的官方Image來部署Registry,這樣更便于全面了解Registry的部署配置細節。

    Registry2在鏡像存儲方面不僅支持本地盤,還支持諸多主流第三方存儲方案。通過分布式存儲系統你還可以實現一個分布式Docker Registry服務。這里僅以本地盤以及single node registry2為例。

    一、環境

    這里還是復用以往文章中的Docker環境:

    Docker Registry Server: 10.10.105.71 Ubuntu 14.04 3.16.0-57-generic;docker 1.9.1
    
    其他兩個工作Server:
    10.10.105.72 Ubuntu 14.04 3.19.0-25-generic; docker 1.9.1
    10.10.126.101 Ubuntu 12.04 3.16.7-013607-generic; docker 1.9.1

    本次Registry使用當前最新stable版本:Registry 2.3.0。由于鏡像采用本地磁盤存儲,root分區較小,需要映射使用其他volume。

    二、初次搭建

    本以為Docker Registry的搭建是何其簡單的,甚至簡單到通過一行命令就可以完成的。比如我們在Registry Server上執行:

    在~/dockerregistry下,執行:
    
    $sudo docker run -d -p 5000:5000 -v `pwd`/data:/var/lib/registry --restart=always --name registry registry:2
    Unable to find image 'registry:2' locally
    2: Pulling from library/registry
    f32095d4ba8a: Pull complete
    9b607719a62a: Pull complete
    973de4038269: Pull complete
    2867140211c1: Pull complete
    8da16446f5ca: Pull complete
    fd8c38b8b68d: Pull complete
    136640b01f02: Pull complete
    e039ba1c0008: Pull complete
    c457c689c328: Pull complete
    Digest: sha256:339d702cf9a4b0aa665269cc36255ee7ce424412d56bee9ad8a247afe8c49ef1
    Status: Downloaded newer image for registry:2
    e9088ef901cb00546c59f89defa4625230f4b36b0a44b3713f38ab3d2a5a2b44
    
    $ docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    registry            2                   c457c689c328        9 days ago          165.7 MB
    
    $ docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                    NAMES
    e9088ef901cb        registry:2          "/bin/registry /etc/d"   About a minute ago   Up About a minute   0.0.0.0:5000->5000/tcp   registry

    Registry container已經跑起來了,其啟動日志可以通過:docker logs registry查看。

    我們在71本地給busybox:latest打一個tag,并嘗試將新tag下的image push到Registry中去:

    $ docker tag busybox:latest 10.10.105.71:5000/tonybai/busybox:latest
    $ docker images
    REPOSITORY                          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    registry                            2                   c457c689c328        9 days ago          165.7 MB
    busybox                             latest              65e4158d9625        9 days ago          1.114 MB
    10.10.105.71:5000/tonybai/busybox   latest              65e4158d9625        9 days ago          1.114 MB
    ... ...

    push到Registry中:

    $ docker push 10.10.105.71:5000/tonybai/busybox
    The push refers to a repository [10.10.105.71:5000/tonybai/busybox] (len: 1)
    unable to ping registry endpoint https://10.10.105.71:5000/v0/
    v2 ping attempt failed with error: Get https://10.10.105.71:5000/v2/: Tunnel or SSL Forbidden
     v1 ping attempt failed with error: Get https://10.10.105.71:5000/v1/_ping: Tunnel or SSL Forbidden

    出錯了!簡單分析了一下,可能是71上docker daemon配置中加了http代理的緣故,導致無法ping通registry endpoint。于是在/etc/default/docker中注釋掉export http_proxy=”xxx”的設置,并重啟docker daemon。

    再次嘗試push:

    $ docker push 10.10.105.71:5000/tonybai/busybox
    The push refers to a repository [10.10.105.71:5000/tonybai/busybox] (len: 1)
    unable to ping registry endpoint https://10.10.105.71:5000/v0/
    v2 ping attempt failed with error: Get https://10.10.105.71:5000/v2/: tls: oversized record received with length 20527
     v1 ping attempt failed with error: Get https://10.10.105.71:5000/v1/_ping: tls: oversized record received with length 20527

    雖然還是失敗,但錯誤信息已有所不同了。這次看來連接是可以建立的,但client端通過https訪問server端,似乎想tls通信,但這一過程并未完成。

    在其他機器上嘗試push image到registry也遇到了同樣的錯誤輸出,如下:

    10.10.105.72:
    
    $ docker push 10.10.105.71:5000/tonybai/ubuntu
    The push refers to a repository [10.10.105.71:5000/tonybai/ubuntu] (len: 1)
    unable to ping registry endpoint https://10.10.105.71:5000/v0/
    v2 ping attempt failed with error: Get https://10.10.105.71:5000/v2/: tls: oversized record received with length 20527
     v1 ping attempt failed with error: Get https://10.10.105.71:5000/v1/_ping: tls: oversized record received with length 20527

    從錯誤信息來看,client與Registry交互,默認將采用https訪問,但我們在install Registry時并未配置指定任何tls相關的key和crt文件,https訪問定然失敗。要想弄清這個問題,只能查看 Registry Manual 。

    三、Insecure Registry

    Registry的文檔還是相對詳盡的。在文檔中,我們找到了 Insecure Registry ,即接收plain http訪問的Registry的配置和使用方法,雖然這不是官方推薦的。

    實際上對于我們內部網絡而言,Insecure Registry基本能滿足需求,部署過程也避免了secure registry的那些繁瑣步驟,比如制作和部署證書等。

    為了搭建一個Insecure Registry,我們需要先清理一下上面已經啟動的Registry容器。

    $ docker stop registry
    registry
    $ docker rm registry
    registry

    修改Registry server上的Docker daemon的配置,為DOCKER_OPTS增加–insecure-registry:

    DOCKER_OPTS="--insecure-registry 10.10.105.71:5000 ....

    重啟Docker Daemon,啟動Registry容器:

    $ sudo service docker restart
    docker stop/waiting
    docker start/running, process 6712
    $ sudo docker run -d -p 5000:5000 -v `pwd`/data:/var/lib/registry --restart=always --name registry registry:2
    5966e92fce9c34705050e19368d19574e021a272ede1575385ef35ecf5cea019

    嘗試再次Push image:

    $ docker push 10.10.105.71:5000/tonybai/busybox
    The push refers to a repository [10.10.105.71:5000/tonybai/busybox] (len: 1)
    65e4158d9625: Pushed
    5506dda26018: Pushed
    latest: digest: sha256:800f2d4558acd67f52262fbe170c9fc2e67efaa6f230a74b41b555e6fcca2892 size: 2739

    這回push ok!

    我們將本地的tag做untag處理,再從Registry pull相關image:

    $ docker images
    REPOSITORY                          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    registry                            2                   c457c689c328        9 days ago          165.7 MB
    10.10.105.71:5000/tonybai/busybox   latest              65e4158d9625        9 days ago          1.114 MB
    busybox                             latest              65e4158d9625        9 days ago          1.114 MB
    ubuntu                              14.04               6cc0fc2a5ee3        5 weeks ago         187.9 MB
    
    $ docker rmi 10.10.105.71:5000/tonybai/busybox
    Untagged: 10.10.105.71:5000/tonybai/busybox:latest
    
    $ docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    registry            2                   c457c689c328        9 days ago          165.7 MB
    busybox             latest              65e4158d9625        9 days ago          1.114 MB
    ubuntu              14.04               6cc0fc2a5ee3        5 weeks ago         187.9 MB
    
    $ docker pull 10.10.105.71:5000/tonybai/busybox
    Using default tag: latest
    latest: Pulling from tonybai/busybox
    Digest: sha256:800f2d4558acd67f52262fbe170c9fc2e67efaa6f230a74b41b555e6fcca2892
    Status: Downloaded newer image for 10.10.105.71:5000/tonybai/busybox:latest
    
    $ docker images
    REPOSITORY                          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    registry                            2                   c457c689c328        9 days ago          165.7 MB
    10.10.105.71:5000/tonybai/busybox   latest              65e4158d9625        9 days ago          1.114 MB
    busybox                             latest              65e4158d9625        9 days ago          1.114 MB
    ubuntu                              14.04               6cc0fc2a5ee3        5 weeks ago         187.9 MB

    可以看到:Pull過程也很順利。

    在Private Registry2中查看或檢索Repository或images, 將不能用docker search :

    $ docker search 10.10.105.71:5000/tonybai/busybox/
    Error response from daemon: Unexpected status code 404

    但通過v2版本的API,我們可以實現相同目的:

    $curl  http://10.10.105.71:5000/v2/_catalog
    {"repositories":["tonybai/busybox"]}
    
    $ curl  http://10.10.105.71:5000/v2/tonybai/busybox/tags/list
    {"name":"tonybai/busybox","tags":["latest"]}

    在其他主機上,我們嘗試pull busybox:

    10.10.105.72:
    
    $docker pull 10.10.105.71:5000/tonybai/busybox
    Using default tag: latest
    Error response from daemon: unable to ping registry endpoint https://10.10.105.71:5000/v0/
    v2 ping attempt failed with error: Get https://10.10.105.71:5000/v2/: tls: oversized record received with length 20527
     v1 ping attempt failed with error: Get https://10.10.105.71:5000/v1/_ping: tls: oversized record received with length 20527

    我們發現依舊不能pull和push!在Registry手冊中講到,如果采用insecure registry的模式,那么所有與Registry交互的主機上的Docker Daemon都要配置:–insecure-registry選項。

    我們按照上面的配置方法,修改105.72上的/etc/default/docker,重啟Docker daemon,再執行pull/push就會得到正確的結果:

    $ sudo vi /etc/default/docker
    $ sudo service docker restart
    docker stop/waiting
    docker start/running, process 10614
    $ docker pull 10.10.105.71:5000/tonybai/busybox
    Using default tag: latest
    latest: Pulling from tonybai/busybox
    5506dda26018: Pull complete
    65e4158d9625: Pull complete
    Digest: sha256:800f2d4558acd67f52262fbe170c9fc2e67efaa6f230a74b41b555e6fcca2892
    Status: Downloaded newer image for 10.10.105.71:5000/tonybai/busybox:latest
    
    $ docker images
    REPOSITORY                          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    ubuntu                              14.04               36248ae4a9ac        8 days ago          187.9 MB
    10.10.105.71:5000/tonybai/ubuntu    14.04               36248ae4a9ac        8 days ago          187.9 MB
    10.10.105.71:5000/tonybai/busybox   latest              65e4158d9625        9 days ago          1.114 MB
    
    $ docker push 10.10.105.71:5000/tonybai/ubuntu
    The push refers to a repository [10.10.105.71:5000/tonybai/ubuntu] (len: 1)
    36248ae4a9ac: Pushed
    8ea5373bf5a6: Pushed
    2e0188208e83: Pushed
    e3c70beaa378: Pushed
    14.04: digest: sha256:72e56686cb9fb38438f0fd68fecf02ef592ce2ef7069bbf97802d959d568c5cc size: 6781

    四、Secure Registry

    Docker官方是推薦你采用Secure Registry的工作模式的,即transport采用tls。這樣我們就需要為Registry配置tls所需的key和crt文件了。

    我們首先清理一下環境,將上面的Insecure Registry停掉并rm掉;將各臺主機上Docker Daemon的DOCKER_OPTS配置中的–insecure-registry去掉,并重啟Docker Daemon。

    如果你擁有一個域名,域名下主機提供Registry服務,并且你擁有某知名CA簽署的證書文件,那么你可以建立起一個Secure Registry。不過我這里沒有現成的證書,只能使用自簽署的證書。嚴格來講,使用自簽署的證書在Docker官方眼中依舊屬于Insecure,不過這里只是借助自簽署的證書來說明一下Secure Registry的部署步驟罷了。

    1、制作自簽署證書

    如果你有知名CA簽署的證書,那么這步可直接忽略。

    $ openssl req -newkey rsa:2048 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt
    Generating a 2048 bit RSA private key
    ..............+++
    ............................................+++
    writing new private key to 'certs/domain.key'
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:CN
    State or Province Name (full name) [Some-State]:Liaoning
    Locality Name (eg, city) []:shenyang
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:foo
    Organizational Unit Name (eg, section) []:bar
    Common Name (e.g. server FQDN or YOUR name) []:mydockerhub.com
    Email Address []:bigwhite.cn@gmail.com

    2、啟動Secure Registry

    啟動帶證書的Registry:

    $ docker run -d -p 5000:5000 --restart=always --name registry \
      -v `pwd`/data:/var/lib/registry \
      -v `pwd`/certs:/certs \
      -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
      -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
      registry:2
    35e8ce77dd455f2bd50854e4581cd52be8a137f4aaea717239b6d676c5ea5777

    由于證書的CN是mydockerhub.com,我們需要修改一下/etc/hosts文件:

    10.10.105.71 mydockerhub.com

    重新為busybox制作一個tag:

    $docker tag busybox:latest mydockerhub.com:5000/tonybai/busybox:latest

    Push到Registry:

    $ docker push mydockerhub.com:5000/tonybai/busybox
    The push refers to a repository [mydockerhub.com:5000/tonybai/busybox] (len: 1)
    unable to ping registry endpoint https://mydockerhub.com:5000/v0/
    v2 ping attempt failed with error: Get https://mydockerhub.com:5000/v2/: x509: certificate signed by unknown authority
     v1 ping attempt failed with error: Get https://mydockerhub.com:5000/v1/_ping: x509: certificate signed by unknown authority

    push失敗了!從錯誤日志來看,docker client認為server傳輸過來的證書的簽署方是一個unknown authority(未知的CA),因此驗證失敗。我們需要讓docker client安裝我們的CA證書:

    $ sudo mkdir -p /etc/docker/certs.d/mydockerhub.com:5000
    $ sudo cp certs/domain.crt /etc/docker/certs.d/mydockerhub.com:5000/ca.crt
    $ sudo service docker restart //安裝證書后,重啟Docker Daemon

    再執行Push,我們看到了成功的輸出日志。由于data目錄下之前已經被push了tonybai/busybox repository,因此提示“已存在”:

    $docker push mydockerhub.com:5000/tonybai/busybox
    The push refers to a repository [mydockerhub.com:5000/tonybai/busybox] (len: 1)
    65e4158d9625: Image already exists
    5506dda26018: Image already exists
    latest: digest: sha256:800f2d4558acd67f52262fbe170c9fc2e67efaa6f230a74b41b555e6fcca2892 size: 2739

    3、外部訪問Registry

    我們換其他機器試試訪問這個secure registry。根據之前的要求,我們照貓畫虎的修改一下hosts文件,安裝ca.cert,去除–insecure-registry選項,并重啟Docker daemon。之后嘗試從registry pull image:

    $ docker pull mydockerhub.com:5000/tonybai/busybox
    Using default tag: latest
    latest: Pulling from tonybai/busybox
    
    Digest: sha256:800f2d4558acd67f52262fbe170c9fc2e67efaa6f230a74b41b555e6fcca2892
    Status: Downloaded newer image for mydockerhub.com:5000/tonybai/busybox:latest
    
    $ docker images
    REPOSITORY                             TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    10.10.105.71:5000/tonybai/ubuntu       14.04               36248ae4a9ac        9 days ago          187.9 MB
    ubuntu                                 14.04               36248ae4a9ac        9 days ago          187.9 MB
    10.10.105.71:5000/tonybai/busybox      latest              65e4158d9625        9 days ago          1.114 MB
    mydockerhub.com:5000/tonybai/busybox   latest              65e4158d9625        9 days ago          1.114 MB

    這樣來看,如果使用自簽署的證書,那么所有要與Registry交互的Docker主機都需要安裝mydockerhub.com的ca.crt(domain.crt)。但如果你使用知名CA,這一步也就可以忽略。

    五、Registry的鑒權管理

    Registry提供了一種基礎的鑒權方式。我們通過下面步驟即可為Registry加上基礎鑒權:

    在Register server上,為Registry增加foo用戶,密碼foo123:(之前需要停掉已有的Registry,并刪除之)

    //生成鑒權密碼文件
    $ mkdir auth
    $ docker run --entrypoint htpasswd registry:2 -Bbn foo foo123  > auth/htpasswd
    $ ls auth
    htpasswd
    
    //啟動帶鑒權功能的Registry:
    $ docker run -d -p 5000:5000 --restart=always --name registry \
       -v `pwd`/auth:/auth \
       -e "REGISTRY_AUTH=htpasswd" \
       -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
       -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
       -v `pwd`/data:/var/lib/registry \
       -v `pwd`/certs:/certs \
       -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
       -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
       registry:2
    199ad0b3591fb9613b21b1c96f017267f3c39661a7025d30df636c6805e7ab50

    在105.72上,我們嘗試push image到Registry:

    $ docker push mydockerhub.com:5000/tonybai/busybox
    The push refers to a repository [mydockerhub.com:5000/tonybai/busybox] (len: 1)
    65e4158d9625: Image push failed
    Head https://mydockerhub.com:5000/v2/tonybai/busybox/blobs/sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4: no basic auth credentials

    錯誤信息提示:鑒權失敗。

    在72上執行docker login:

    $docker login mydockerhub.com:5000
    Username: foo
    Password:
    Email: bigwhite.cn@gmail.com
    WARNING: login credentials saved in /home/baiming/.docker/config.json
    Login Succeeded

    login成功后,再行Push:

    $ docker push mydockerhub.com:5000/tonybai/busybox
    The push refers to a repository [mydockerhub.com:5000/tonybai/busybox] (len: 1)
    65e4158d9625: Image already exists
    5506dda26018: Image already exists
    latest: digest: sha256:800f2d4558acd67f52262fbe170c9fc2e67efaa6f230a74b41b555e6fcca2892 size: 2739

    Push ok!

    六、Registry中images的管理

    前面提到過,通過V2版Rest API可以查詢Repository和images:

    $ curl --cacert domain.crt  --basic --user foo:foo123 https://mydockerhub.com:5000/v2/_catalog
    {"repositories":["tonybai/busybox","tonybai/ubuntu"]}

    但如果要刪除Registry中的Repository或某個tag的Image,目前v2還不支持,原因見 Registry的roadmap中的說明 。

    不過如果你的Registry的存儲引擎使用的是本地盤,倒是有一些第三方腳本可供使用,比如:delete-docker-registry-image 。

    七、小結

    Registry2發布不到1年,目前還有許多問題待解決,就比如delete image的問題,相信在2.4以及后續版本這些問題會被逐個解決掉或能找到一個相對理想的方案。

    posted @ 2016-10-25 14:24 小馬歌 閱讀(386) | 評論 (0)編輯 收藏
     
    from:http://www.infoq.com/cn/articles/centos7-practical-kubernetes-deployment

    . 前言

    上一節我們闡述了Kubernetes的系統架構,讓大家對Kubernetes有一定的初步了解,但是就如何使用Kubernetes, 也許大家還不知如何下手。本文作者將帶領大家如何在本地部署、配置Kubernetes集群網絡環境以及通過實例演示跨機器服務間的通信,主要包括如下內容:

    • 部署環境介紹
    • Kubernetes集群邏輯架構
    • 部署Open vSwitch、Kubernetes、Etcd組件
    • 演示Kubernetes管理容器

    2. 部署環境

    • VMware Workstation:10.0.3
    • VMware Workstation網絡模式:NAT
    • 操作系統信息:CentOS 7 64位
    • Open vSwitch版本信息:2.3.0
    • Kubernetes版本信息:0.5.2
    • Etcd版本信息:0.4.6
    • Docker版本信息:1.3.1
    • 服務器信息:

              | Role      | Hostname   | IP Address  | 	|:---------:|:----------:|:----------: | 	|APIServer  |kubernetes  |192.168.230.3| 	|Minion     | minion1    |192.168.230.4| 	|Minion     | minion2    |192.168.230.5|

    3. Kubernetes集群邏輯架構

    在詳細介紹部署Kubernetes集群前,先給大家展示下集群的邏輯架構。從下圖可知,整個系統分為兩部分,第一部分是Kubernetes APIServer,是整個系統的核心,承擔集群中所有容器的管理工作;第二部分是minion,運行Container Daemon,是所有容器棲息之地,同時在minion上運行Open vSwitch程序,通過GRE Tunnel負責minion之間Pod的網絡通信工作。

    4. 部署Open vSwitch、Kubernetes、Etcd組件

    4.1 安裝Open vSwitch及配置GRE

    為了解決跨minion之間Pod的通信問題,我們在每個minion上安裝Open vSwtich,并使用GRE或者VxLAN使得跨機器之間Pod能相互通信,本文使用GRE,而VxLAN通常用在需要隔離的大規模網絡中。對于Open vSwitch的具體安裝步驟,可參考這篇博客,我們在這里就不再詳細介紹安裝步驟了。安裝完Open vSwitch后,接下來便建立minion1和minion2之間的隧道。首先在minion1和minion2上建立OVS Bridge,

    [root@minion1 ~]# ovs-vsctl add-br obr0 

    接下來建立gre,并將新建的gre0添加到obr0,在minion1上執行如下命令,

    [root@minion1 ~]# ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=192.168.230.5 

    在minion2上執行,

    [root@minion2 ~]# ovs-vsctl add-port obr0 gre0 -- set Interface gre0 type=gre options:remote_ip=192.168.230.4 

    至此,minion1和minion2之間的隧道已經建立。然后我們在minion1和minion2上創建Linux網橋kbr0替代Docker默認的docker0(我們假設minion1和minion2都已安裝Docker),設置minion1的kbr0的地址為172.17.1.1/24, minion2的kbr0的地址為172.17.2.1/24,并添加obr0為kbr0的接口,以下命令在minion1和minion2上執行。

    [root@minion1 ~]# brctl addbr kbr0               //創建linux bridge [root@minion1 ~]# brctl addif kbr0 obr0          //添加obr0為kbr0的接口 [root@minion1 ~]# ip link set dev docker0 down   //設置docker0為down狀態 [root@minion1 ~]# ip link del dev docker0        //刪除docker0 

    為了使新建的kbr0在每次系統重啟后任然有效,我們在/etc/sysconfig/network-scripts/目錄下新建minion1的ifcfg-kbr0如下:

    DEVICE=kbr0 ONBOOT=yes BOOTPROTO=static IPADDR=172.17.1.1 NETMASK=255.255.255.0 GATEWAY=172.17.1.0 USERCTL=no TYPE=Bridge IPV6INIT=no 

    同樣在minion2上新建ifcfg-kbr0,只需修改ipaddr為172.17.2.1和gateway為172.17.2.0即可,然后執行systemctl restart network重啟系統網絡服務,你能在minion1和minion2上發現kbr0都設置了相應的IP地址。為了驗證我們創建的隧道是否能通信,我們在minion1和minion2上相互ping對方kbr0的IP地址,從下面的結果發現是不通的,經查找這是因為在minion1和minion2上缺少訪問172.17.1.1和172.17.2.1的路由,因此我們需要添加路由保證彼此之間能通信。

    [root@minion1 network-scripts]# ping 172.17.2.1 PING 172.17.2.1 (172.17.2.1) 56(84) bytes of data. ^C --- 172.17.2.1 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1000ms  [root@minion2 ~]#  ping 172.17.1.1 PING 172.17.1.1 (172.17.1.1) 56(84) bytes of data. ^C --- 172.17.1.1 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1000ms 

    由于通過ip route add添加的路由會在下次系統重啟后失效,為此我們在/etc/sysconfig/network-scripts目錄下新建一個文件route-eth0存儲路由,這里需要注意的是route-eth0和ifcfg-eth0的黑體部分必須保持一致,否則不能工作,這樣添加的路由在下次重啟后不會失效。為了保證兩臺minion的kbr0能相互通信,我們在minion1的route-eth0里添加路由172.17.2.0/24 via 192.168.230.5 dev eno16777736,eno16777736是minion1的網卡,同樣在minion2的route-eth0里添加路由172.17.1.0/24 via 192.168.230.4 dev eno16777736。重啟網絡服務后再次驗證,彼此kbr0的地址可以ping通,如:

    [root@minion2 network-scripts]# ping 172.17.1.1 PING 172.17.1.1 (172.17.1.1) 56(84) bytes of data. 64 bytes from 172.17.1.1: icmp_seq=1 ttl=64 time=2.49 ms 64 bytes from 172.17.1.1: icmp_seq=2 ttl=64 time=0.512 ms ^C --- 172.17.1.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 0.512/1.505/2.498/0.993 ms 

    到現在我們已經建立了兩minion之間的隧道,而且能正確的工作。下面我們將介紹如何安裝Kubernetes APIServer及kubelet、proxy等服務。

    4.2 安裝Kubernetes APIServer

    在安裝APIServer之前,我們先下載Kubernetes及Etcd,做一些準備工作。在kubernetes上的具體操作如下:

    [root@kubernetes ~]# mkdir /tmp/kubernetes [root@kubernetes ~]# cd /tmp/kubernetes/ [root@kubernetes kubernetes]# wget https://github.com/GoogleCloudPlatform/kubernetes/releases/download/v0.5.2/kubernetes.tar.gz [root@kubernetes kubernetes]# wget https://github.com/coreos/etcd/releases/download/v0.4.6/etcd-v0.4.6-linux-amd64.tar.gz 

    然后解壓下載的kubernetes和etcd包,并在kubernetes、minion1、minion2上創建目錄/opt/kubernetes/bin,

    [root@kubernetes kubernetes]# mkdir -p /opt/kubernetes/bin [root@kubernetes kubernetes]# tar xf kubernetes.tar.gz [root@kubernetes kubernetes]# tar xf etcd-v0.4.6-linux-amd64.tar.gz [root@kubernetes kubernetes]# cd ~/kubernetes/server [root@kubernetes server]# tar xf kubernetes-server-linux-amd64.tar.gz [root@kubernetes kubernetes]# /tmp/kubernetes/kubernetes/server/kubernetes/server/bin 

    復制kube-apiserver,kube-controller-manager,kube-scheduler,kubecfg到kubernetes的/opt/kubernetes/bin目錄下,而kubelet,kube-proxy則復制到minion1和minion2的/opt/kubernetes/bin,并確保都是可執行的。

    [root@kubernetes amd64]# cp kube-apiserver kube-controller-manager kubecfg kube-scheduler /opt/kubernetes/bin [root@kubernetes amd64]# scp kube-proxy kubelet root@192.168.230.4:/opt/kubernetes/bin [root@kubernetes amd64]# scp kube-proxy kubelet root@192.168.230.5:/opt/kubernetes/bin 

    為了簡單我們只部署一臺etcd服務器,如果需要部署etcd的集群,請參考官方文檔,在本文中將其跟Kubernetes APIServer部署同一臺機器上,而且將etcd放置在/opt/kubernetes/bin下,etcdctl跟ectd同一目錄。

    [root@kubernetes kubernetes]# cd /tmp/kubernetes/etcd-v0.4.6-linux-amd64 [root@kubernetes etcd-v0.4.6-linux-amd64]# cp etcd etcdctl /opt/kubernetes/bin 

    需注意的是kubernetes和minion上/opt/kubernetes/bin目錄下的文件都必須是可執行的。到目前,我們準備工作已經差不多,現在開始給apiserver,controller-manager,scheduler,etcd配置unit文件。首先我們用如下腳本etcd.sh配置etcd的unit文件,

    #!/bin/sh  ETCD_PEER_ADDR=192.168.230.3:7001 ETCD_ADDR=192.168.230.3:4001 ETCD_DATA_DIR=/var/lib/etcd ETCD_NAME=kubernetes  ! test -d $ETCD_DATA_DIR && mkdir -p $ETCD_DATA_DIR cat <<EOF >/usr/lib/systemd/system/etcd.service [Unit] Description=Etcd Server  [Service] ExecStart=/opt/kubernetes/bin/etcd \\     -peer-addr=$ETCD_PEER_ADDR \\     -addr=$ETCD_ADDR \\     -data-dir=$ETCD_DATA_DIR \\     -name=$ETCD_NAME \\     -bind-addr=0.0.0.0  [Install] WantedBy=multi-user.target EOF  systemctl daemon-reload systemctl enable etcd systemctl start etcd 

    對剩下的apiserver,controller-manager,scheduler的unit文件配置的腳本,可以在github 上GetStartingKubernetes找到,在此就不一一列舉。運行相應的腳本后,在APIServer上etcd, apiserver, controller-manager, scheduler服務就能正常運行。

    4.3 安裝Kubernetes Kubelet及Proxy

    根據Kubernetes的設計架構,需要在minion上部署docker, kubelet, kube-proxy,在4.2節部署APIServer時,我們已經將kubelet和kube-proxy已經分發到兩minion上,所以只需配置docker,kubelet,proxy的unit文件,然后啟動服務就即可,具體配置見GetStartingKubernetes

    5. 演示Kubernetes管理容器

    為了方便,我們使用Kubernetes提供的例子Guestbook來演示Kubernetes管理跨機器運行的容器,下面我們根據Guestbook的步驟創建容器及服務。在下面的過程中如果是第一次操作,可能會有一定的等待時間,狀態處于pending,這是因為第一次下載images需要一段時間。

    5.1 創建redis-master Pod和redis-master服務

    [root@kubernetes ~]# cd /tmp/kubernetes/kubernetes/examples/guestbook [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 -c redis-master.json create pods [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 -c redis-master-service.json create services 

    完成上面的操作后,我們可以看到如下redis-master Pod被調度到192.168.230.4。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list pods Name                                   Image(s)                   Host                Labels                                       Status ----------                             ----------                 ----------          ----------                                   ---------- redis-master                           dockerfile/redis           192.168.230.4/      name=redis-master                            Running 

    但除了發現redis-master的服務之外,還有兩個Kubernetes系統默認的服務kubernetes-ro和kubernetes。而且我們可以看到每個服務都有一個服務IP及相應的端口,對于服務IP,是一個虛擬地址,根據apiserver的portal_net選項設置的CIDR表示的IP地址段來選取,在我們的集群中設置為10.10.10.0/24。為此每新創建一個服務,apiserver都會在這個地址段中隨機選擇一個IP作為該服務的IP地址,而端口是事先確定的。對redis-master服務,其服務地址為10.10.10.206,端口為6379。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list services Name                Labels              Selector                                  IP                  Port ----------          ----------          ----------                                ----------          ---------- kubernetes-ro                           component=apiserver,provider=kubernetes   10.10.10.207        80 redis-master        name=redis-master   name=redis-master                         10.10.10.206        6379 kubernetes                              component=apiserver,provider=kubernetes   10.10.10.161        443 

    5.2 創建redis-slave Pod和redis-slave服務

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 -c redis-slave-controller.json create replicationControllers [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 -c redis-slave-service.json create services 

    然后通過list命令可知新建的redis-slave Pod根據調度算法調度到兩臺minion上,服務IP為10.10.10.92,端口為6379

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list pods Name                                   Image(s)                   Host                Labels                                       Status ----------                             ----------                 ----------          ----------                                   ---------- redis-master                           dockerfile/redis           192.168.230.4/      name=redis-master                            Running 8c0ddbda-728c-11e4-8233-000c297db206   brendanburns/redis-slave   192.168.230.5/      name=redisslave,uses=redis-master            Running 8c0e1430-728c-11e4-8233-000c297db206   brendanburns/redis-slave   192.168.230.4/      name=redisslave,uses=redis-master            Running  [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list services Name                Labels              Selector                                  IP                  Port ----------          ----------          ----------                                ----------          ---------- redisslave          name=redisslave     name=redisslave                           10.10.10.92         6379 kubernetes                              component=apiserver,provider=kubernetes   10.10.10.161        443 kubernetes-ro                           component=apiserver,provider=kubernetes   10.10.10.207        80 redis-master        name=redis-master   name=redis-master                         10.10.10.206        6379 

    5.3 創建Frontend Pod和Frontend服務

    在創建之前修改frontend-controller.json的Replicas數量為2,這是因為我們的集群中只有2臺minion,如果按照frontend-controller.json的Replicas默認值3,那會導致有2個Pod會調度到同一臺minion上,產生端口沖突,有一個Pod會一直處于pending狀態,不能被調度。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 -c frontend-controller.json create replicationControllers [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 -c frontend-service.json create services 

    通過查看可知Frontend Pod也被調度到兩臺minion,服務IP為10.10.10.220,端口是80。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list pods Name                                   Image(s)                   Host                Labels                                       Status ----------                             ----------                 ----------          ----------                                   ---------- redis-master                           dockerfile/redis           192.168.230.4/      name=redis-master                            Running 8c0ddbda-728c-11e4-8233-000c297db206   brendanburns/redis-slave   192.168.230.5/      name=redisslave,uses=redis-master            Running 8c0e1430-728c-11e4-8233-000c297db206   brendanburns/redis-slave   192.168.230.4/      name=redisslave,uses=redis-master            Running a880b119-7295-11e4-8233-000c297db206   brendanburns/php-redis     192.168.230.4/      name=frontend,uses=redisslave,redis-master   Running a881674d-7295-11e4-8233-000c297db206   brendanburns/php-redis     192.168.230.5/      name=frontend,uses=redisslave,redis-master   Running  [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list services Name                Labels              Selector                                  IP                  Port ----------          ----------          ----------                                ----------          ---------- kubernetes-ro                           component=apiserver,provider=kubernetes   10.10.10.207        80 redis-master        name=redis-master   name=redis-master                         10.10.10.206        6379 redisslave          name=redisslave     name=redisslave                           10.10.10.92         6379 frontend            name=frontend       name=frontend                             10.10.10.220        80 kubernetes                              component=apiserver,provider=kubernetes   10.10.10.161        443 

    除此之外,你可以刪除Pod、Service及更新ReplicationController的Replicas數量等操作,如刪除Frontend服務:

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 delete services/frontend Status ---------- Success 

    還可以更新ReplicationController的Replicas的數量,下面是更新Replicas之前ReplicationController的信息。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list replicationControllers Name                   Image(s)                   Selector            Replicas ----------             ----------                 ----------          ---------- redisSlaveController   brendanburns/redis-slave   name=redisslave     2 frontendController     brendanburns/php-redis     name=frontend       2 

    現在我們想把frontendController的Replicas更新為1,則這行如下命令,然后再通過上面的命令查看frontendController信息,發現Replicas已變為1。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 resize frontendController 1  [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list replicationControllers Name                   Image(s)                   Selector            Replicas ----------             ----------                 ----------          ---------- redisSlaveController   brendanburns/redis-slave   name=redisslave     2 frontendController     brendanburns/php-redis     name=frontend       1 

    5.4 演示跨機器服務通信

    完成上面的操作后,我們來看當前Kubernetes集群中運行著的Pod信息。

    [root@kubernetes guestbook]# kubecfg -h http://192.168.230.3:8080 list pods Name                                   Image(s)                   Host                Labels                                       Status ----------                             ----------                 ----------          ----------                                   ---------- a881674d-7295-11e4-8233-000c297db206   brendanburns/php-redis     192.168.230.5/      name=frontend,uses=redisslave,redis-master   Running redis-master                           dockerfile/redis           192.168.230.4/      name=redis-master                            Running 8c0ddbda-728c-11e4-8233-000c297db206   brendanburns/redis-slave   192.168.230.5/      name=redisslave,uses=redis-master            Running 8c0e1430-728c-11e4-8233-000c297db206   brendanburns/redis-slave   192.168.230.4/      name=redisslave,uses=redis-master            Running 

    通過上面的結果可知當前提供前端服務的PHP和提供數據存儲的后端服務Redis master的Pod分別運行在192.168.230.5和192.168.230.4上,即容器運行在不同主機上,還有Redis slave也運行在兩臺不同的主機上,它會從Redis master同步前端寫入Redis master的數據。下面我們從兩方面驗證Kubernetes能提供跨機器間容器的通信:

    • 在瀏覽器打開http://${IPAddress}:8000,IPAddress為PHP容器運行的minion的IP地址,其暴漏的端口為8000,這里IP_Address為192.168.230.5。打開瀏覽器會顯示如下信息:

      你可以輸入信息并提交,如"Hello Kubernetes"、"Container",然后Submit按鈕下方會顯示你輸入的信息。

      由于前端PHP容器和后端Redis master容器分別在兩臺minion上,因此PHP在訪問Redis master服務時一定得跨機器通信,可見Kubernetes的實現方式避免了用link只能在同一主機上實現容器間通信的缺陷,對于Kubernetes跨機器通信的實現方法,以后我會詳細介紹。

       

    • 從上面的結果,可得知已經實現了跨機器的通信,現在我們從后端數據層驗證不同機器容器間的通信。根據上面的輸出結果發現Redis slave和Redis master分別調度到兩臺不同的minion上,在192.168.230.4主機上執行docker exec -ti c41711cc8971 /bin/sh,c41711cc8971是Redis master的容器ID,進入容器后通過redis-cli命令查看從瀏覽器輸入的信息如下:

      如果我們在192.168.230.5上運行的Redis slave容器里查到跟Redis master容器里相同的信息,那說明Redis master和Redis slave之間的數據同步正常工作,下面是從192.168.230.5上運行的Redis slave容器查詢到的信息:

      由此可見Redis master和Redis slave之間數據同步正常,OVS GRE隧道技術使得跨機器間容器正常通信。

    6. 結論

    本文主要介紹如何在本地環境部署Kubernetes集群和演示如何通過Kubernetes管理集群中運行的容器,并通過OVS管理集群不同minion的Pod之間的網絡通信。接下來會對Kubernetes各個組件源碼進行詳細分析,闡述Kubernetes的工作原理。

    7. 個人簡介

    楊章顯,現就職于Cisco,主要從事WebEx SaaS服務運維,系統性能分析等工作。特別關注云計算,自動化運維,部署等技術,尤其是Go、OpenvSwitch、Docker及其生態圈技術,如Kubernetes、Flocker等Docker相關開源項目。Email: yangzhangxian@gmail.com

    8. 參考資料

    1. https://n40lab.wordpress.com/2014/09/04/openvswitch-2-3-0-lts-and-centos-7/
    2. https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook

    感謝郭蕾對本文的策劃和審校。

    posted @ 2016-10-25 14:23 小馬歌 閱讀(221) | 評論 (0)編輯 收藏
     
    from:http://blog.csdn.net/linuxgo/article/details/52121125

    加快Kubernetes編譯速度

    除了Linux/amd64,默認還會為其他平臺做交叉編譯。為了減少編譯時間,可以修改hack/lib/golang.sh,把KUBE_SERVER_PLATFORMS, KUBE_CLIENT_PLATFORMS和KUBE_TEST_PLATFORMS中除linux/amd64以外的其他平臺注釋掉

    gcr.io無法訪問

    Kubernetes在創建Pod的時候,需要從gcr.io下載一個helper鏡像(目前是 gcr.io/google_containers/pause-amd64:3.0 )。

    但是目前國內無法訪問gcr.io,這個問題會導致無法下載該鏡像,然后Pod一直處于ContainerCreating狀態。

    解決辦法

    1) 在可以訪問gcr.io的地方

    docker pull gcr.io/google_containers/pause-amd64:3.0

    傳到私有docker registry

    docker tag gcr.io/google_containers/pause-amd64:3.0 k8s-docker.mydomain.com/google_containers/pause-amd64:3.0

    docker push k8s-docker.mydomain.com/google_containers/pause-amd64:3.0

    2) 在所有的k8s節點

    docker pull k8s-docker.mydomain.com/google_containers/pause-amd64:3.0
    docker tag k8s-docker.mydomain.com/google_containers/pause-amd64:3.0 gcr.io/google_containers/pause-amd64:3.0

    Note

    不通過私有registry中轉,而是使用Docker save/load應該也可以,只是要把save導出的文件復制到所有節點.

    如何從集群外訪問Service和Pod

    這里說的集群外是指K8s集群以外的主機,比如使用nginx/HAProxy搭建的負載均衡主機。這些主機跟K8s集群部署在一起,到K8s網絡可達。

    對于不是部署在GCE以及AWS等云平臺的K8s,我們一般需要自己搭建負載均衡,然后分發請求到到Service。

    使用NodePort方式發布服務,那么負載均衡主機上不需要額外配置;使用ClusterIP方式,為了能夠訪問Service的ClusterIP, 需要在這些主機上安裝Flanneld和kube-proxy

    posted @ 2016-10-25 14:23 小馬歌 閱讀(1716) | 評論 (0)編輯 收藏
     
    優化CMS(concurrent garbage collection)
       使用CMS,old代的垃圾回收執行線程會和應用程序的線程最大程度的并發執行。這個提供了一個機會來減少最壞延遲的頻率和最壞延遲的時間消耗。CMS沒有執行壓縮,所以可以避免old代空間的stop-the-world壓縮(會讓整個應用暫停運行)。

       優化CMS的目標就是避開stop-the-world壓縮垃圾回收,然而,這個說比做起來容易。在一些的部署情況下,這個是不可避免的,尤其是當內存分配受限的時候。

       在一些特殊的情況下,CMS比其他類型的垃圾回收需要更多優化,更需要優化young代的空間,以及潛在的優化該什么時候初始化old代的垃圾回收循環。

       當從吞吐量垃圾回收器(Throughput)遷移到CMS的時候,有可能會獲得更慢的MinorGC,由于對象從young代轉移到old會更慢 ,由于CMS在old代里面分配的內存是一個不連續的列表,相反,吞吐量垃圾回收器只是在本地線程的分配緩存里面指定一個指針。另外,由于old代的垃圾回收線程和應用的線程是盡可能的并發運行的,所以吞吐量會更小一些。然而,最壞的延遲的頻率會少很多,由于在old代的不可獲取的對象能夠在應用運行的過程被垃圾回收,這樣可以避免old代的空間溢出。

       使用CMS,如果old代能夠使用的空間有限,單線程的stop-the-world壓縮垃圾回收會執行。這種情況下,FullGC的時間會比吞吐量垃圾回收器的FullGC時間還要長,導致的結果是,CMS的絕對最差延遲會比吞吐量垃圾回收器的最差延遲嚴重很多。old代的空間溢出以及運行了stop-the-world垃圾回收必須被應用負責人重視,由于在響應上會有更長的中斷。因此,不要讓old代運行得溢出就非常重要了。對于從吞吐量垃圾回收器遷移到CMS的一個比較重要的建議就是提升old代20%到30%的容量。

       在優化CMS的時候有幾個注意點,首先,對象從young代轉移到old代的轉移率。其次,CMS重新分配內存的概率。再次,CMS回收對象時候產生的old代的分隔,這個會在可獲得的對象中間產生一些空隙,從而導致了分隔空間。

       碎片可以被下面的幾種方法尋址。第一辦法是壓縮old代,壓縮old代空間是通過stop-the-world垃圾回收壓縮完成的,就像前面所說的那樣,stop-the-world垃圾回收會執行很長時間,會嚴重影響應用的響應時間,應該避開。第二種辦法是,對碎片編址,提高old代的空間,這個辦法不能完全解決碎片的問題的,但是可以延遲old代壓縮的時間。通常來講,old代越多內存,由于碎片導致需要執行的壓縮的時間久越長。努力把old的空間增大的目標是在應用的生命周期中,避免堆碎片導致stop-the-world壓縮垃圾回收,換句話說,應用GC最大內存原則。另外一種處理碎片的辦法是減少對象從young代移動到old的概率,就是減少MinorGC,應用MinorGC回收原則。

       任期閥值(tenuring threshold)控制了對象該什么時候從young代移動到old代。任期閥值會在后面詳細的介紹,它是HotSpot VM基于young代的占用空間來計算的,尤其是survivor(幸存者)空間的占用量。下面詳細介紹一下survivor空間以及討論任期閥值。

    survivor空間

       survivor空間是young代的一部分,如下圖所示。young代被分成了一個eden區域和兩個survivor空間。
       
       兩個survivor空間的中一個被標記為“from”,另外一個標記為“to”。新的Java對象被分配到Eden空間。比如說,下面的一條語句:
       
    [java] view plain copy
    1. <span style="font-size:14px;">   Map<String,String> map = new HashMap<String,String>();</span>  

       一個新的HashMap對象會被放到eden空間,當eden空間滿了的時候,MinorGC就會執行,任何存活的對象,都從eden空間復制到“to” survivor空間,任何在“from” survivor空間里面的存活對象也會被復制到“to” survivor。MinorGC結束的時候,eden空間和“from” survivor空間都是空的,“to” survivor空間里面存儲存活的對象,然后,在下次MinorGC的時候,兩個survivor空間交換他們的標簽,現在是空的“from” survivor標記成為“to”,“to” survivor標記為“from”。因此,在MinorGC結束的時候,eden空間是空的,兩個survivor空間中的一個是空的。

       在MinorGC過程,如果“to” survivor空間不夠大,不能夠存儲所有的從eden空間和from suvivor空間復制過來活動對象,溢出的對象會被復制到old代。溢出遷移到old代,會導致old代的空間快速增長,會導致stop-the-world壓縮垃圾回收,所以,這里要使用MinorGC回收原則。

       避免survivor空間溢出可以通過指定survivor空間的大小來實現,以使得survivor有足夠的空間來讓對象存活足夠的歲數。高效的歲數控制會導致只有長時間存活的對象轉移到old代空間。

       歲數控制是指一個對象保持在young代里面直到無法獲取,所以讓old代只是存儲長時間保存的對象。

       survivor的空間可以大小設置可以用HotSpot命令行參數:-XX:SurvivorRatio=<ratio>

       <ratio>必須是以一個大于0的值,-XX:SurvivorRatio=<ratio>表示了每一個survivor的空間和eden空間的比值。下面這個公式可以用來計算survivor空間的大小

       
    [html] view plain copy
    1. survivor spave size = -Xmn<value>/(-XX:SurvivorRatio=<ratio>+2)  

       這里有一個+2的理由是有兩個survivor空間,是一個調節參數。ratio設置的越大,survivor的空間越小。為了說明這個問題,假設young代的大小是-Xmn512m而且-XX:SurvivorRatio=6.那么,young代有兩個survivor空間且空間大小是64M,那么eden空間的大小是384M。

       同樣假如young代的大小是512M,但是修改-XX:SurvivorRatio=2,這樣的配置會使得每一個survivor空間的大小是128m而eden空間的大小是256M。

       對于一個給定大小young代空間大小,減小ratio參數增加survivor空間的大小而且減少eden空間的大小。反之,增加ratio會導致survivor空間減少而且eden空間增大。減少eden空間會導致MinorGC更加頻繁,相反,增加eden空間的大小會導致更小的MinorGC,越多的MinorGC,對象的歲數增長得越快。

       為了更好的優化survivor空間的大小和完善young代空間的大小,需要監控任期閥值,任期閥值決定了對象會再young代保存多久。怎么樣來監控和優化任期閥值將在下一節中介紹。
       
    任期閥值

       “任期”是轉移的代名詞,換句話說,任期閥值意味著對象移動到old代空間里面。HotSpot VM每次MinorGC的時候都會計算任期,以決定對象是否需要移動到old代去。任期閥值就是對象的歲數。對象的歲數是指他存活過的MinorGC次數。當一個對象被分配的時候,它的歲數是0。在下次MinorGC的時候之后,如果對象還是存活在young代里面,它的歲數就是1。如果再經歷過一次MinorGC,它的歲數變成2,依此類推。在young代里面的歲數超過HotSpot VM指定閥值的對象會被移動到old代里面。換句話說,任期閥值決定對象在young代里面保存多久。

       任期閥值的計算依賴于young代里面能夠存放的對象數以及MinorGC之后,“to” servivor的空間占用。HotSpot VM有一個選項-XX:MaxTenuringThreshold=<n>,可以用來指定當時對象的歲數超過<n>的時候,HotSpot VM會把對象移動到old代去。內部計算的任期閥值一定不會超過指定的最大任期閥值。最大任期閥值在可以被設定為0-15,不過在Java 5 update 5之前可以設置為1-31。

       不推薦把最大任期閥值設定成0或者超過15,這樣會導致GC的低效率。

       如果HotSpot VM它無法保持目標survivor 空間的占用量,它會使用一個小于最大值的任期閥值來維持目標survivor空間的占用量,任何比這個任期閥值的大的對象都會被移動到old代。話句話說,當存活對象的量大于目標survivor空間能夠接受的量的時候,溢出發生了,溢出會導致對象快速的移動到old代,導致不期望的FullGC。甚至會導致更頻繁的stop-the-world壓縮垃圾回收。哪些對象會被移動到old代是根據評估對象的歲數和任期閥值來確定的。因此,很有必要監控任期閥值以避免survivor空間溢出,接下來詳細討論。

    監控任期閥值

       為了不被內部計算的任期閥值迷惑,我們可以使用命令選項-XX:MaxTenuringThreshod=<n>來指定最大的任期閥值。為了決定出最大的任期閥值,需要監控任期閥值的分布和對象歲數的分布,通過使用下面的選項實現

     
    [html] view plain copy
    1. -XX:+PrintTenuringDistribution  

       -XX:+PrintTenuringDistribution的輸出顯示在survivor空間里面有效的對象的歲數情況。閱讀-XX:+PrintTenuringDistribution輸出的方式是觀察在每一個歲數上面,對象的存活的數量,以及其增減情況,以及HotSpot VM計算的任期閥值是不是等于或者近似于設定的最大任期閥值。

       -XX:+PrintTenuringDistribution在MinorGC的時候產生任期分布信息。它可以同其他選項一同使用,比如-XX:+PrintGCDateStamps,-XX:+PrintGCTimeStamps以及-XX:+PringGCDetails。當調整survivor空間大小以獲得有效的對象歲數分布,你應該使用-XX:+PrintTenuringDistribution。在生產環境中,它同樣非常有用,可以用來判斷stop-the-world的垃圾回收是否發生。

       下面是一個輸出的例子:

       Desired survivor size 8388608 bytes, new threshold 1 (max 15) 
       - age 1: 16690480 bytes, 16690480 total

       在這里例子中,最大任期閥值被設置為15,(通過max 15表示)。內部計算出來的任期閥值是1,通過threshold 1表示。Desired survivor size 8388608 bytes表示一個survivor的空間大小。目標survivor的占有率是指目標survivor和兩個survivor空間總和的比值。怎么樣指定期望的survivor空間大小在后面會詳細介紹。在第一行下面,會列出一個對象的歲數列表。每行會列出每一個歲數的字節數,在這個例子中,歲數是1的對象有16690480字節,而且每行后面有一個總的字節數,如果有多行輸出的話,總字節數是前面的每行的累加數。后面舉例說明。

       在前面的例子中,由于期望的survivor大小(8388608)比實際總共survivor字節數(16690480)小,也就是說,survivor空間溢出了,這次MinorGC會有一些對象移動到old代。這個就意味著survivor的空間太小了。另外,設定的最大任期閥值是15,但是實際上JVM使用的是1,也表明了survivor的空間太小了。

       如果發現survivor區域太小,就增大survivor的空間,下面詳細介紹如何操作。
       
    設定survivor空間

       當修改survivor空間的大小的時候,有一點需要記住。當修改survivor空間大小的時候,如果young代的大小不改變,那么eden空間會減小,進一步會導致更頻繁的MinorGC。因此,增加survivor空間的時候,如果young代的空間大小違背了MinorGC頻率的需求,eden空間的大小同需要需要增加。換句話說,當survivor空間增加的時候,young代的大小需要增加。

       如果有空間來增加MinorGC的頻率,有兩種選擇,一是拿一些eden空間來增加survivor的空間,二是讓young的空間更大一些。常規來講,更好的選擇是如果有可以使用的內存,增加young代的空間會比減少eden的空間更好一些。讓eden空間大小保持恒定,MinorGC的頻率不會改變,即使調整survivor空間的大小。

       使用-XX:+PrintTenuringDistribution選項,對象的總字節數和目標survivor空間占用可以用來計算survivor空間的大小。重復前面的例子:
       Desired survivor size 8388608 bytes, new threshold 1 (max 15) 
       - age 1: 16690480 bytes, 16690480 total

       存活對象的總字節數是1669048,這個并發垃圾回收器(CMS)的目標survivor默認使用50%的survivor空間。通過這個信息,我們可以知道survivor空間至少應該是33380960字節,大概是32M。這個計算讓我們知道對survivor空間的預估值需要計算對象的歲數更高效以及防止溢出。為了更好的預估survivor的可用空間,你應該監控應用穩定運行情況下的任期分布,并且使用所有的額外總存活對象的字節數來作為survivor空間的大小。

       在這個例子,為了讓應用計算歲數更加有效,survivor空間需要至少提升32M。前面使用的選項是:

     
    [html] view plain copy
    1. -Xmx1536m -Xms1536m -Xmn512m -XX:SurvivorRatio=30  

       那么為了保持MinorGC的頻率不發生變化,然后增加survivor空間的大小到32M,那么修改后的選項如下:

     
    [html] view plain copy
    1. -Xmx1568m -Xms1568m -Xmn544m -XX:SurvivvorRatio=15  

       當時young代空間增加了,eden空間的大小保持大概相同,且survivor的空間大小增減了。需要注意的時候,-Xmx、-Xms、-Xmn都增加了32m。另外,-XX:SurvivvorRatio=15讓每一個survivor空間的大小都是32m (544/(15+2) = 32)。

       如果存在不能增加young代空間大小的限制,那么增加survivor空間大小需要以減少eden空間的大小為代價。下面是一個增加survivor空間大小,每一個survivor空間從16m增減加到32m,那么會見減少eden的空間,從480m減少到448m(512-32-32=448,512-16-16=480)。

       
    [html] view plain copy
    1. -Xms1536m -Xms1536m -Xmn1512m -XX:SurvivorRatio=14  

       再次強調,減少eden空間大小會增加MinorGC的頻率。但是,對象會在young代里面保持更長的時間,由于提升survivor的空間。

       假如運行同樣的應用,我們保持eden的空間不變,增加survivor空間的大小,如下面選項:

     
    [html] view plain copy
    1. <span style="font-size:14px;"> -Xmx1568m -Xms1568m -Xmn544m -XX:SurvivorRatio=15</span>  

       可以產生如下的任期分布:
       Desired survivor size 16777216 bytes, new threshold 15 (max 15)
    - age 1: 6115072 bytes, 6115072 total
    - age 2: 286672 bytes, 6401744 total
    - age 3: 115704 bytes, 6517448 total
    - age 4: 95932 bytes, 6613380 total
    - age 5: 89465 bytes, 6702845 total
    - age 6: 88322 bytes, 6791167 total
    - age 7: 88201 bytes, 6879368 total
    - age 8: 88176 bytes, 6967544 total
    - age 9: 88176 bytes, 7055720 total
    - age 10: 88176 bytes, 7143896 total
    - age 11: 88176 bytes, 7232072 total
    - age 12: 88176 bytes, 7320248 total

       從任期分布的情況來看,survivor空間沒有溢出,由于存活的總大小是7320248,但是預期的survivor空間大小是16777216以及任期閥值和最大任期閥值是相等的。這個表明,對象的老化速度是高效的,而且survivor空間沒有溢出。

       在這個例子中,由于歲數超過3的對象很少,你可能像把最大任期閥值設置為3來測試一下,即設置選項-XX:MaxTenuringThreshhold=3,那么整個選項可以設置為:

     
    [html] view plain copy
    1. -Xmx1568m -Xms1658m -Xmn544m -XX:SurvivorRatio=15 -XX:MaxTenuringThreshold=3  

       這個選項設置和之前的選項設置的權衡是,后面這個選擇可以避免在MinorGC的時候不必要地把對象從“from” survivor復制到“to” survivor。在應用運行在穩定狀態的情況下,觀察多次MinorGC任期分布情況,看是否有對象最終移動到old代或者顯示的結果還是和前面的結果類似。如果你觀察得到和前面的任期分布情況相同,基本沒有對象的歲數達到15,也沒有survivor的空間溢出,你應該自己設置最大任期閥值以代替JVM默認的15。在這個例子中,沒有長時間存活的對象,由于在他們的歲數沒有到達15的時候就被垃圾回收了。這些對象在MinorGC中被回收了,而不是移動到old代里面。使用并發垃圾回收(CMS)的時候,對象從young代移動到old代最終會導致old的碎片增加,有可能導致stop-the-world壓縮垃圾回收,這些都是不希望出現的。寧可選擇讓對象在“from” survivor和“to” survivor中復制,也不要太快的移動到old代。

       你可能需要重復數次監控任期分布、修改survivor空間大小或者重新配置young代的空間大小直到你對應用由于MinorGC引起的延遲滿意為止。如果你發現MinorGC的時間太長,你可以通過減少young代的大小直到你滿意為止。盡管,減少young代的大小,會導致更快地移動對象到old代,可能導致更多的碎片,如果CMS的并發垃圾回收能夠跟上對象的轉移率,這種情況就比不能滿足應用的延遲需求更好。如果這步不能滿足應用的MinorGC的延遲和頻率需求,這個時候就有必要重新審視需求以及修改應用程序了。

       如果滿足對MinorGC延遲的需求,包括延遲時間和延遲頻率,你可以進入下一步,優化CMS垃圾回收周期的啟動,下節詳細介紹。
    posted @ 2016-10-18 20:24 小馬歌 閱讀(324) | 評論 (0)編輯 收藏
     
         摘要: from:http://blog.csdn.net/fenglibing/article/details/6321453這是我公司同事的GC學習筆記,寫得蠻詳細的,由淺入深,循序漸進,讓人一看就懂,特轉到這里。一、GC特性以及各種GC的選擇1、垃圾回收器的特性2、對垃圾回收器的選擇2.1 連續 VS. 并行2.2 并發 VS. stop-...  閱讀全文
    posted @ 2016-10-18 15:37 小馬歌 閱讀(281) | 評論 (0)編輯 收藏
     
         摘要: from:http://www.tuicool.com/articles/RNjUfa原文  http://286.iteye.com/blog/1924947主題 JVM        -XX 參數被稱為不穩定參數,之所以這么叫是因為此類參數的設置很容易引起JVM 性能上的差異,使JVM 存在極大...  閱讀全文
    posted @ 2016-10-18 11:18 小馬歌 閱讀(397) | 評論 (0)編輯 收藏
     
         摘要: from:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html不管是YGC還是Full GC,GC過程中都會對導致程序運行中中斷,正確的選擇不同的GC策略,調整JVM、GC的參數,可以極大的減少由于GC工作,而導致的程序運行中斷方面的問題,進而適當的提高Java程序的工作效率。但是調整GC是以個極為復雜的過程,由于各個程序具...  閱讀全文
    posted @ 2016-10-14 15:06 小馬歌 閱讀(281) | 評論 (0)編輯 收藏
     
    from:http://www.oschina.net/news/74775/open-source-database-situation

    數據庫作為業務的核心,是整個基礎軟件棧非常重要的一環。近幾年的開源社區,新的思想和方案層出不窮,我將總結一下近幾年一些主流的開源數據庫方案,及其背后的設計思想以及適用場景。本人才疏學淺如有遺漏或者錯誤請見諒。本次分享聚焦于數據庫即結構化數據存儲 OLTP 及 NoSQL 領域,不會涉及 OLAP、對象存儲以及分布式文件系統。

    開源 RDBMS 與互聯網的崛起

    很長時間以來,關系型數據庫一直是大公司的專利,市場被 Oracle / DB2 等企業數據庫牢牢把持。但是隨著互聯網的崛起和開源社區的發展,上世紀九十年代 MySQL 1.0 的發布,標志著在關系型數據庫的領域,社區終于有了可選擇的方案。

    MySQL

    第一個介紹的單機 RDBMS 就是 MySQL。相信大多數朋友都已經對 MySQL 非常熟悉,基本上 MySQL 的成長史就是互聯網的成長史。我接觸的第一個 MySQL 版本是 MySQL 4.0,后來的 MySQL 5.5 更是經典——基本上所有的互聯網公司都在使用。MySQL 也普及了「可插拔」引擎這一概念,即針對不同的業務場景選用不同的存儲引擎,這也是 MySQL tuning 的一個重要方式。比如對于有事務需求的場景使用 InnoDB;對于并發讀取的場景 MyISAM 可能比較合適;但是現在我推薦絕大多數情況還是使用 InnoDB,畢竟 MySQL 5.6 后它已經成為了官方的默認引擎。MySQL適用于幾乎所有需要持久化結構化數據的場景, 大多數朋友應該都知道,我就不贅述了。

    另外值得一提的是 MySQL 5.6 中引入了多線程復制和 GTID,使得故障恢復和主從的運維變得比較方便。另外,MySQL 5.7(目前處于 GA 版本) 發布了一個重大更新,主要是在讀寫性能和復制性能上有了長足的進步:在5.6版本中實現了 SCHEMA 級別的并行復制。不過意義不大,倒是 MariaDB 的多線程并行復制大放異彩,有不少人因為這個特性選擇 MariaDB。另外,MySQL 5.7 MTS 支持兩種模式,一種是和5.6一樣,另一種則是基于 binlog group commit 實現的多線程復制,也就是MASTER上同時提交的 binlog 在 SLAVE 端也可以同時被 apply,實現并行復制。如果有單機數據庫技術選型的朋友,基本上只需要考慮 MySQL 5.7 或者 MariaDB 就好了,而且 MySQL 5.6和5.7 由 Oracle 接手后,性能和穩定性都有了明顯的提升。

    PostgreSQL

    PostgreSQL 的歷史也非常悠久,其前身是 UCB 的 Ingres,主持這個項目的 Michael Stronebraker 于 2015 年獲得圖靈獎。后來該項目更名為 Post-Ingres,基于 BSD license 下開源。 1995 年幾個 UCB 的學生為 Post-Ingres 開發了 SQL 的接口,正式發布了 PostgreSQL95,隨后一步步在開源社區中成長起來。和 MySQL 一樣,PostgreSQL 也是一個單機的關系型數據庫,但是與 MySQL 方便用戶過度擴展的 SQL 文法不一樣的是,PostgreSQL 的 SQL 支持非常強大,不管是內置類型、JSON 支持、GIS 類型以及對于復雜查詢的支持,PL/SQL 等都比 MySQL 強大得多,而且從代碼質量上來看,PostgreSQL 的代碼質量是優于 MySQL 的。另外,相對于MySQL 5.7以前的版本, PostgreSQL 的 SQL 優化器比 MySQL 強大很多,幾乎所有稍微復雜的查詢PostgreSQL 的表現都優于 MySQL。

    從近幾年的趨勢上來看,PostgreSQL 的勢頭也很強勁,我認為 PostgreSQL 的不足之處在于沒有 MySQL 那樣強大的社區和群眾基礎。MySQL 經過那么多年的發展,積累了很多的運維工具和最佳實踐,但是 PostgreSQL 作為后起之秀,擁有更優秀的設計和更豐富的功能。PostgreSQL 9 以后的版本也足夠穩定,在做新項目技術選型的時候,是一個很好的選擇。另外也有很多新的數據庫項目是基于 PostgreSQL 源碼的基礎上進行二次開發,比如 Greenplum 等。

    我認為,單機數據庫的時代很快就會過去。摩爾定律帶來的硬件紅利總是有上限的,現代業務的數據規模、流量以及現代的數據科學對于數據庫的要求,單機已經很難滿足。比如,網卡磁盤 IO 和 CPU 總有瓶頸,線上敏感的業務系統可能還得承擔 SPOF(單點故障) 的風險,主從復制模型在主掛掉時到底切還是不切?切了以后數據如何恢復?如果只是出現主從機器網絡分區問題呢?甚至是監控環境出現網絡分區問題呢?這些都是單機數據庫面臨的巨大挑戰。所以我的觀點是,無論單機性能多棒(很多令人乍舌的評測數據都是針對特定場景的優化,另外甚至有些都是本機不走網絡,而大多數情況數據庫出現的第一個瓶頸其實是網卡和并發連接……),隨著互聯網的蓬勃發展和移動互聯網的出現,數據庫系統迎來了第一次分布式的洗禮。

    分布式時代:NoSQL 的復興和模型簡化的力量

    在介紹 NoSQL 之前,我想提兩個公司,一個是 Google,另一個是 Amazon。

    Google

    Google 應該是第一個將分布式存儲技術應用到大規模生產環境的公司,同時也是在分布式系統上積累最深的公司,可以說目前工業界的分布式系統的工程實踐及思想大都來源于 Google。比如 2003 年的 GFS 開創了分布式文件系統,2006 年的 Bigtable 論文開創了分布式鍵值系統,直接催生的就是 Hadoop 的生態;至于 2012 年發表論文的 Spanner 和 F1更是一個指明未來關系型數據庫發展方向的里程碑式的項目,這個我們后續會說。

    Amazon

    另一個公司是 Amazon。2007 年發表的 Dynamo的論文 嘗試引入了最終一致性的概念, WRN 的模型及向量時鐘的應用,同時將一致性 HASH、merkle tree 等當時一些很新潮的技術整合起來,正式標志著 NoSQL 的誕生。NoSQL——對后來業界的影響非常也是很大,包括后來的 Cassandra、RiakDB、Voldemort 等數據庫都是基于 Dynamo 的設計發展起來的。

    新思潮

    另外這個時期(2006 年前后持續至今)一個比較重要的思潮就是數據庫(持久化)和緩存開始有明確的分離——我覺得這個趨勢是從 memcached 開始的。隨著業務的并發越來越高,對于低延遲的要求也越來越高;另外一個原因是隨著內存越來越便宜,基于內存的存儲方案漸漸開始普及。當然內存緩存方案也經歷了一個從單機到分布式的過程,但是這個過程相比關系型數據庫的進化要快得多。這是因為 NoSQL 的另外一個重要的標志——數據模型的變化——大多 NoSQL 都拋棄了關系模型,選擇更簡單的鍵值或者文檔類型進行存儲。數據結構和查詢接口都相對簡單,沒有了 SQL 的包袱,實現的難度會降低很多。另外 NoSQL 的設計幾乎都選擇犧牲掉復雜 SQL 的支持及 ACID 事務換取彈性擴展能力,也是從當時互聯網的實際情況出發:業務模型簡單、爆發性增長帶來的海量并發及數據總量爆炸、歷史包袱小、工程師強悍,等等。其中最重要的還是業務模型相對簡單。

    嵌入式存儲引擎

    在開始介紹具體的開源的完整方案前,我想介紹一下嵌入式存儲引擎們。

    隨著 NoSQL 的發展,不僅僅緩存和持久化存儲開始細分,再往后的存儲引擎也開始分化并走上前臺。之前很難想象一個存儲引擎獨立于數據庫直接對外提供服務,就像你不會直接拿著 InnoDB 或者 MyISAM甚至一個 B-tree 出來用一樣(當然,bdb 這樣鼎鼎大名的除外)。人們基于這些開源的存儲引擎進行進一步的封裝,比如加上網絡協議層、加上復制機制等等,一步步構建出完整的風格各異的 NoSQL 產品。

    這里我挑選幾個比較著名的存儲引擎介紹一下。

    TC

    我最早接觸的是 Tokyo Cabinet(TC)。TC 相信很多人也都聽說過,TC 是由日本最大的社交網站 Mixi 開發并開源的一個混合 Key-Value 存儲引擎,其中包括 HASH Table 和 B+ Tree 的實現。但是這個引擎的一個缺陷是隨著數據量的膨脹,性能的下降會非常明顯,而且現在也基本不怎么維護了,所以入坑請慎重。與于 TC 配合使用的 Tokyo Tyrant(TT) 是一個網絡庫,為 TC 提供網絡的接口使其變成一個數據庫服務,TT + TC 應該是比較早的 NoSQL 的一個嘗試。

    LevelDB

    在 2011 年,Google 開源了 Bigtable 的底層存儲引擎: LevelDB。LevelDB 是一個使用 C++ 開發的嵌入式的 Key-Value 存儲引擎,數據結構采用了 LSM-Tree,具體 LSM-Tree 的算法分析可以很容易在網上搜索到,我就不贅述了。其特點是,對于寫入極其友好,LSM 的設計避免了大量的隨機寫入;對于特定的讀也能達到不錯的性能(熱數據在內存中);另外 LSM-Tree 和 B-tree 一樣是支持有序 Scan 的;而且 LevelDB 是出自 Jeff Dean 之手,他的事跡做分布式系統的朋友一定都知道,不知道的可以去 Google 搜一下。

    LevelDB 擁有極好的寫性能,線程安全,BatcTCh Write 和 Snapshot 等特性,使其很容易的在上層構建 MVCC 系統或者事務模型,這對于數據庫來說非常重要。另外值得一說的是,Facebook 維護了一個活躍的 LevelDB 的分支,名為 RocksDB。RocksDB 在 LevelDB 上做了很多的改進,比如多線程 Compactor、分層自定義壓縮、多 MemTable 等。另外 RocksDB 對外暴露了很多 ConfigurationConfigration ,可以根據不同業務的形態進行調優;同時 Facebook 在內部正在用 RocksDB 來實現一個全新的 MySQL 存儲引擎:MyRocks,值得關注。RocksDB 的社區響應速度很快也很友好,實際上 PingCAP 也是 RocksDB 的社區貢獻者。我建議新的項目如果在 LevelDB 和 RocksDB 之間糾結的話,請果斷選擇 RocksDB。

    B-tree 家族

    當然,除了 LSM-Tree 外,B-tree 的家族也還是有很多不錯的引擎。首先大多數傳統的單機數據庫的存儲引擎都選擇了B+Tree,B+Tree 對磁盤的讀比較友好,第三方存儲引擎比較著名的純 B+Tree 實現是 LMDB。首先 LMDB 選擇在內存映像文件 (mmap) 實現 B+Tree,而且同時使用了 Copy-On-Write 實現了 MVCC 實現并發事務無鎖讀的能力,對于高并發讀的場景比較友好;同時因為使用的是 mmap 所以擁有跨進程讀取的能力。不過因為我并沒有在生產環境中使用過 LMDB ,所以并不能給出 LMDB 的一些缺陷,見諒。

    混合引擎

    還有一部分的存儲引擎選擇了多種引擎混合,比如最著名的應該是 WiredTiger,大概是2014年去年被 MongoDB 收購,現在成為了 MongoDB 的默認存儲引擎。WiredTiger 內部有 LSM-Tree 和 B-tree 兩種實現,對外提供相同的一套接口,根據業務的情況可自由選擇。另外一些特殊數據結構的存儲引擎在某些特殊場合下非常搶眼,比如極高壓縮比 TokuDB,采用了名為分形樹的數據結構,在維持一個可接受的讀寫壓力的情況下,能擁有 10 倍以上的壓縮率。

    NoSQL

    說完了幾個比較著名的存儲引擎,我們來講講比較著名的 NoSQL。在我的定義中,NoSQL 是 Not Only SQL 的縮寫,所以可能包含的范圍有內存數據庫,持久化數據庫等。總之就是和單機的關系型數據庫不一樣的結構化數據存儲系統。

    我們先從緩存開始。

    memcached

    前面提到了 memcached 應該是第一個大規模在業界使用的緩存數據庫,memcached 的實現極其簡單,相當于將內存用作大的 HASH Table,只能在上面進行 get/set/ 計數器等操作,在此之上用 libevent 封裝了一層網絡層和文本協議(也有簡單的二進制協議),雖然支持一些 CAS 的操作,但是總體上來看,還是非常簡單的。但是 memcached 的內存利用率并不太高,這是這個因為 memcached 為了避免頻繁申請內存導致的內存碎片的問題,采用了自己實現的 slab allocator 的方式。即內存的分配都是一塊一塊的,最終存儲在固定長度的 chunk 上,內存最小的分配單元是 chunk,另外 libevent 的性能也并沒有優化到極致。但是這些缺點并不妨礙 memcached 成為當時的開源緩存事實標準。(另外,八卦一下,memcached 的作者 Brad Fitzpatrick 現在在 Google,大家如果用 Golang 的話,Go 的官方 HTTP 包就是這哥們寫的,是個很高產的工程師)。

    Redis

    如果我沒記錯的話,在 2009 年前后,一位意大利的工程師 Antirez ,開源了 Redis。從此徹底顛覆了緩存的市場,到現在大多數緩存的業務都已用上 Redis,memcached 基本退出了歷史舞臺。Redis 最大的特點是擁有豐富的數據結構支持,不僅僅是簡單的 Key-Value,還包括隊列、集合、Sorted Set 等等,提供了非常豐富的表達力,而且 Redis 還提供 sub/pub 等超出數據庫范疇的便捷功能,使得幾乎一夜之間大家紛紛投入 Redis 的懷抱。

    Twemproxy

    但是隨著 Redis 漸漸的普及,而且越用越狠,另外內存也越來越便宜,人們開始尋求擴展單機 Redis 的方案,最早的嘗試是 twitter 開源的 twemproxy。twemproxy 是一個 Redis 中間件,基本只有最簡單的數據路由功能,并沒有動態的伸縮能力,但是還是受到了很多公司的追捧,因為確實沒其他替代方案。隨后的 Redis Cluster 也是難產了好久,時隔好幾年,中間出了 7 個RC 版本,最后才發布;2014 年底,我們開源了 Codis,解決了 Redis 中間件的數據彈性伸縮問題,目前廣泛應用于國內各大互聯網公司中,這個在網上也有很多文章介紹,我也就不展開了。所以在緩存上面,開源社區現在倒是非常統一,就是 Redis 及其極其周邊的擴展方案。

    MongoDB

    在 NoSQL 的大家庭中,MongoDB 其實是一個異類,大多 NoSQL 舍棄掉 SQL 是為了追求更極致的性能和可擴展能力,而 MongoDB 主動選擇了文檔作為對外的接口,非常像 JSON 的格式。Schema-less 的特性對于很多輕量級業務和快速變更的了互聯網業務意義很大,而且 MongoDB 的易用性很好,基本做到了開箱即用,開發者不需要費心研究數據的表結構,只需要往里存就好了,這確實籠絡了一大批開發者。

    盡管 MongoDB 早期的版本各種不穩定,性能也不太好(早期的 Mongo 并沒有存儲引擎,直接使用了 mmap 文件),集群模式還全是問題(比如至今還未解決的 Cluster 同步帶寬占用過多的問題),但是因為確實太方便了,在早期的項目快速迭代中,Mongo 是一個不錯的選擇。但是這也正是它的問題,我不止一次聽到當項目變得龐大或者「嚴肅」的時候,團隊最后還是回歸了關系型數據庫。Anyway,在 2014 年底 MongoDB 收購了 WiredTiger 后,在 2.8 版本中正式亮相,同時 3.0 版本后更是作為默認存儲引擎提供,性能和穩定性有了非常大的提升。

    但是,從另一方面講,Schema-less 到底對軟件工程是好事還是壞事這個問題還是有待商榷。我個人是站在 Schema 這邊的,不過在一些小項目或者需要快速開發的項目中使用 Mongo 確實能提升很多的開發效率,這是毋庸置疑的。

    ?HBase

    說到 NoSQL 不得不提的是 HBase,HBase 作為 Hadoop 旗下的重要產品,Google Bigtable 的正統開源實現,是不是有一種欽定的感覺 :)。提到 HBase 就不得不提一下 Bigtable, Bigtable 是 Google 內部廣泛使用的分布式數據庫,接口也不是簡單的Key-Value,按照論文的說法叫:multi-dimensional sorted map,也就是 Value 是按照列劃分的。Bigtable 構建在 GFS 之上,彌補了分布式文件系統對于海量、小的、結構化數據的插入、更新以及、隨機讀請求的缺陷。

    HBase 就是這么一個系統的實現,底層依賴 HDFS。HBase 本身并不實際存儲數據,持久化的日志和 SST file (HBase 也是 LSM-Tree 的結構) 直接存儲在 HDFS 上,Region Server (RS) 維護了 MemTable 以提供快速的查詢,寫入都是寫日志,后臺進行 Compact,避免了直接隨機讀寫 HDFS。數據通過 Region 在邏輯上進行分割,負載均衡通過調節各個 Region Server 負責的 Region 區間實現。當某 Region 太大時,這個 Region 會分裂,后續可能由不同的 RS 負責,但是前面提到了,HBase 本身并不存儲數據,這里的 Region 僅是邏輯上的,數據還是以文件的形式存儲在 HDFS 上,所以 HBase 并不關心 Replication 、水平擴展和數據的分布,統統交給 HDFS 解決。

    和 Bigtable 一樣,HBase 提供行級的一致性,嚴格來說在 CAP 理論中它是一個 CP 的系統,但遺憾的是并沒有更進一步提供 ACID 的跨行事務。HBase 的好處就不用說了,顯而易見,通過擴展 RS 可以幾乎線性提升系統的吞吐,及 HDFS 本身就具有的水平擴展能力。

    但是缺點仍然是有的。首先,Hadoop 的軟件棧是 Java,JVM 的 GC Tuning 是一個非常煩人的事情,即使已經調得很好了,平均延遲也得幾十毫秒;另外在架構設計上,HBase 本身并不存儲數據,所以可能造成客戶端請求的 RS 并不知道數據到底存在哪臺 HDFS DataNode 上,憑空多了一次 RPC;第三,HBase 和 Bigtable 一樣,并不支持跨行事務,在 Google 內部不停的有團隊基于 Bigtable 來做分布式事務的支持,比如 MegaStore、Percolator。后來 Jeff Dean 有次接受采訪也提到非常后悔沒有在 Bigtable 中加入跨行事務,不過還好這個遺憾在 Spanner 中得到了彌補,這個一會兒說。總體來說,HBase 還是一個非常健壯且久經考驗的系統,但是需要你有對于 Java 和 Hadoop 比較深入的了解后,才能玩轉,這也是 Hadoop 生態的一個問題,易用性真是不是太好,而且社區演進速度相對緩慢,也是因為歷史包袱過重的緣故吧。

    Cassandra

    提到 Cassandra (C*),雖然也是 Dynamo 的開源實現,但就沒有這種欽定的感覺了。C* 確實命途多舛,最早 2008 由 Facebook 開發并開源,早期的 C* 幾乎全是 bug,Facebook 后來索性也不再維護轉過頭搞 HBase 去了,一個爛攤子直接丟給社區。還好 DataStax 把這個項目撿起來商業化,搞了兩年,終于漸漸開始流行起來。

    C* 不能簡單的歸納為讀快寫慢,或者讀慢寫快,因為采用了 qourm 的模型,調整復制的副本數以及讀的數量,可以達到不同的效果,對于一致性不是特別高的場景,可以選擇只從一個節點讀取數據,達到最高的讀性能。另外 C* 并不依賴分布式文件系統,數據直接存儲在磁盤上,各個存儲節點之間自己維護復制關系,減少了一層 RPC 調用,延遲上對比 HBase 還是有一定優勢的。

    不過即使使用 qourm 的模型也并不代表 C* 是一個強一致的系統。C* 并不幫你解決沖突,即使你 W(寫的副本數) + R(讀請求的副本數) > N(節點總數),C* 也沒辦法幫你決定哪些副本擁有更新的版本,因為每個數據的版本是一個 NTP 的時間戳或者客戶端自行提供,每臺機器可能都有誤差,所以有可能并不準確,這也就是為什么 C* 是一個 AP 的系統。不過 C* 一個比較友好的地方是提供了 CQL,一個簡單的 SQL 方言,比起 HBase 在易用性上有明顯優勢。

    即使作為一個 AP 系統,C* 已經挺快了,但是人們追求更高性能的腳步還是不會停止。應該是今年年初,ScyllaDB 的發布就是典型的證明,ScyllaDB 是一個兼容 C* 的 NoSQL 數據庫,不一樣的是,ScyllaDB 完全用 C++ 開發,同時使用了類似 DPDK 這樣的黑科技,具體我就不展開了,有興趣可以到 Scylla 的官網去看看。BTW, 國內的蘑菇街第一時間使用了 ScyllaDB,同時在 Scylla 的官網上 share 了他們的方案,性能還是很不錯的。

    中間件與分庫分表

    NoSQL 就先介紹到這里,接下來我想說的是一些在基于單機關系型數據庫之上的中間件和分庫分表方案。

    這些技術確實歷史悠久,而且也是沒有辦法的選擇。關系型數據庫不比 Redis,并不是簡單的寫一個類似 Twemproxy 的中間件就搞定了。數據庫的中間件需要考慮很多,比如解析 SQL,解析出 sharding key,然后根據 sharding key 分發請求,再合并;另外數據庫有事務,在中間件這層還需要維護 Session 及事務狀態,而且大多數方案并沒有辦法支持跨 shard 的事務。這就不可避免的導致了業務使用起來會比較麻煩,需要重寫代碼,而且會增加邏輯的復雜度,更別提動態的擴容縮容和自動的故障恢復了。在集群規模越來越大的情況下,運維和 DDL 的復雜度是指數級上升的。

    中間件項目盤點

    數據庫中間件最早的項目大概是 MySQL Proxy,用于實現讀寫分離。后來國人在這個領域有過很多著名項目,比如阿里的 Cobar 和 TDDL(并未完全開源);后來社區基于 Cobar 改進的 MyCAT、360 開源的 Atlas 等,都屬于這一類中間件產品;在中間件這個方案上基本走到頭的開源項目應該是 Youtube 的 Vitess。Vitess 基本上是一個集大成的中間件產品,內置了熱數據緩存、水平動態分片、讀寫分離等等,但是代價也是整個項目非常復雜,另外文檔也不太好。大概1年多以前,我們嘗試搭建起完整的 Vitess 集群,但是并未成功,可見其復雜度。

    另外一個值得一提的是 Postgres-XC 這個項目,Postgres-XC 的野心還是很大的,整體的架構有點像早期版本的 OceanBase,由一個中央節點來處理協調分布式事務 / 解決沖突,數據分散在各個存儲節點上,應該是目前 PostgreSQL 社區最好的分布式擴展方案。其他的就不提了。

    未來在哪里?NewSQL!

    一句話,NewSQL 就是未來。

    2012 年 Google 在 OSDI 上發表了 Spanner 的論文,2013 年在 SIGMOD 發表了 F1 的論文。這兩篇論文讓業界第一次看到了關系模型和 NoSQL 的擴展性在超龐大集群規模上融合的可能性。在此之前,大家普遍認為這個是不可能的,即使是 Google 也經歷了 Megastore 這樣的失敗。

    Spanner綜述

    但是 Spanner 的創新之處在于通過硬件(GPS時鐘+原子鐘)來解決時鐘同步的問題。在分布式系統里,時鐘是最讓人頭痛的問題,剛才提到了 C* 為什么不是一個強 C 的系統,正是因為時鐘的問題。而 Spanner 的厲害之處在于即使兩個數據中心隔得非常遠,不需要有通信(因為通信的代價太大,最快也就是光速)就能保證 TrueTime API的時鐘誤差在一個很小的范圍內(10ms)。另外 Spanner 沿用了很多 Bigtable 的設計,比如 Tablet / Directory 等,同時在 Replica 這層使用 Paxos 復制,并未完全依賴底層的分布式文件系統。但是 Spanner 的設計底層仍然沿用了 Colossus,不過論文里也說是可以未來改進的點。

    Google 的內部的數據庫存儲業務,大多是 3~5 副本,重要一點的 7 副本,遍布全球各大洲的數據中心,由于普遍使用了 Paxos,延遲是可以縮短到一個可以接受的范圍(Google 的風格一向是追求吞吐的水平擴展而不是低延遲,從悲觀鎖的選擇也能看得出來,因為跨數據中心復制是必選的,延遲不可能低,對于低延遲的場景,業務層自己解決或者依賴緩存)。另外由 Paxos 帶來的 Auto-Failover 能力,更是能讓整個集群即使數據中心癱瘓,業務層都是透明無感知的。另外 F1 構建在 Spanner 之上,對外提供了更豐富的 SQL 語法支持,F1 更像一個分布式 MPP SQL——F1 本身并不存儲數據,而是將客戶端的 SQL 翻譯成類似 MapReduce 的任務,調用 Spanner 來完成請求。

    其實 Spanner 和 F1 除了 TrueTime 整個系統并沒有用什么全新的算法,其意義在于這是近些年來第一個 NewSQL 在生產環境中提供服務的分布式系統技術。

    Spanner 和 F1 有以下幾個重點:

    1. 完整的 SQL 支持,ACID 事務;2. 彈性伸縮能力;

    3. 自動的故障轉移和故障恢復,多機房異地災備。

    NewSQL 特性確實非常誘人,在 Google 內部,大量的業務已經從原來的 Bigtable 切換到 Spanner 之上。我相信未來幾年,整個業界的趨勢也是如此,就像當年的 Hadoop 一樣,Google 的基礎軟件的技術趨勢是走在社區前面的。

    社區反應

    Spanner 的論文發表之后,當然也有社區的追隨者開始實現(比如我們 :D ),第一個團隊是在紐約的 CockroachDB。CockroachDB 的團隊的組成還是非常豪華的,早期團隊由是 Google 的分布式文件系統 Colossus 團隊的成員組成;技術上來說,Cockroach 的設計和 Spanner 很像,不一樣的地方是沒有選擇 TrueTime而是 HLC (Hybrid logical clock),也就是 NTP +邏輯時鐘來代替 TrueTime 時間戳;另外 Cockroach 選用了 Raft 代替 Paxos 實現復制和自動容災,底層存儲依賴 RocksDB 實現,整個項目使用 Go 語言開發,對外接口選用 PostgreSQL 的 SQL 子集。

    TiDB

    目前從全球范圍來看,另一個朝著 Spanner / F1 的開源實現這個目標上走的產品是 TiDB(終于談到我們的產品了)。TiDB 本質上是一個更加正統的 Spanner 和 F1 實現,并不像 CockroachDB 那樣選擇將 SQL 和 Key-Value 融合,而是像 Spanner 和 F1 一樣選擇分離,這樣分層的思想也是貫穿整個 TiDB 項目始終的。對于測試、滾動升級以及各層的復雜度控制會比較有優勢;另外 TiDB 選擇了 MySQL 協議和語法的兼容,MySQL 社區的 ORM 框架和運維工具,直接可以應用在 TiDB 上。

    和 F1 一樣,TiDB 是一個無狀態的 MPP SQL Layer,整個系統的底層是依賴 TiKV 來提供分布式存儲和分布式事務的支持。TiKV 的分布式事務模型采用的是 Google Percolator 的模型,但是在此之上做了很多優化。Percolator 的優點是去中心化程度非常高,整個集群不需要一個獨立的事務管理模塊,事務提交狀態這些信息其實是均勻分散在系統的各個 Key 的 meta 中,整個模型唯一依賴的是一個授時服務器。在我們的系統上,極限情況這個授時服務器每秒能分配 400w 以上個單調遞增的時間戳,大多數情況基本夠用了(畢竟有 Google 量級的場景并不多見);同時在 TiKV中,這個授時服務本身是高可用的,也不存在單點故障的問題。

    TiKV 和 CockroachDB 一樣也是選擇了 Raft 作為整個數據庫的基礎;不一樣的是,TiKV 整體采用 Rust 語言開發,作為一個沒有 GC 和 Runtime 的語言,在性能上可以挖掘的潛力會更大。

    關于未來

    我覺得未來的數據庫會有幾個趨勢,也是 TiDB 項目追求的目標:

    ●數據庫會隨著業務云化,未來一切的業務都會跑在云端,不管是私有云、公有云還是混合云,運維團隊接觸的可能再也不是真實的物理機,而是一個個隔離的容器或者「計算資源」。這對數據庫也是一個挑戰,因為數據庫天生就是有狀態的,數據總是要存儲在物理的磁盤上,而移動數據的代價比移動容器的代價可能大很多。

    ●多租戶技術會成為標配,一個大數據庫承載一切的業務,數據在底層打通,上層通過權限,容器等技術進行隔離;但是數據的打通和擴展會變得異常簡單,結合第一點提到的云化,業務層可以再也不用關心物理機的容量和拓撲,只需要認為底層是一個無窮大的數據庫平臺即可,不用再擔心單機容量和負載均衡等問題。

    ●OLAP 和 OLTP 會進一步細分,底層存儲也許會共享一套,但是SQL優化器這層的實現一定是千差萬別的。對于用戶而言,如果能使用同一套標準的語法和規則來進行數據的讀寫和分析,會有更好的體驗。

    ●在未來分布式數據庫系統上,主從日志同步這樣落后的備份方式會被 Multi-Paxos / Raft 這樣更強的分布式一致性算法替代,人工的數據庫運維在管理大規模數據庫集群時是不可能的,所有的故障恢復和高可用都會是高度自動化的。


    Q&A

    問:HANA等內存數據庫怎么保證系統掉電而處理結果不丟?傳統數據庫也用緩存,可是HANA用的內存太大。

    黃東旭:沒用過 HANA,但是直觀感覺這類內存數據庫的可用性可能通過集中方式保證:●寫入會先寫 WAL;

    > - 寫入可能會通過主從或者paxos 之類的算法做同步和冗余復制還有 HANA 本身就是內存數據庫,會盡可能把數據放到內存里,這樣查詢才能快呀。 

    問:對于傳統創業公司如何彌補NoSQL的技術短板?快速的引入NoSQL提高效率?

    黃東旭:選用 NoSQL 主要注意兩點:1.做好業務的調研,估計并發量,數據量,數據的結構看看適不適合;2.對各種 NoSQL 擅長和不擅長的地方都盡可能了解。

    不要盲目相信關系型數據庫,也不要盲目相信 NoSQL,沒有銀彈的。

    問:有多個條件  比如年齡20到30或年齡35到40 并且加入購物車或下單  這種數據怎么存儲?

    黃東旭:購物車這種場景是典型的 OLTP 的場景,可以選用關系型數據庫 MySQL PostgreSQL 什么的,如果對于擴展性的數據跨機房有要求的話,可以調研一下 NewSQL,比如我們的 TiDB。

    問:多緯度查詢應該選擇哪種數據庫?

    黃東旭:多緯度查詢可以說是一個 OLAP 的場景,可以選用 Greenplum 或者 Vertica 之類的分析性數據庫。

    問:想知道為什么需要這些開源的數據庫,既然已經有了MySQL、DB2、Oracle這些成熟的數據庫,成本考慮,還是傳統數據庫滿足不了需求?

    黃東旭:對,傳統數據庫的擴展性是有問題的,在海量并發和數據量的場景下很難支持業務。所以可以看到比較大的互聯網公司基本都有自己的分布式數據庫方案。

    問:未來可能不再需要數據倉庫嗎?

    黃東旭:大家可以想想數據倉庫的定義,如果是還需要離線的從線上庫倒騰數據到數據倉庫上,這樣很難做到實時查詢,而且空間的利用率也低,我認為是目前并沒有太好的方案的情況下的折衷……如果有一個更好的數據庫能解決數據倉庫的場景,為什么還需要一個獨立的數據倉庫?


    關于作者

    黃東旭,PingCAP 聯合創始人兼 CTO。PingCAP 是一家專注于研發下一代的開源的分布式數據庫的公司,主要作品是 TiDB / TiKV,是 Google Spanner 及 F1 的開源實現。

    相關鏈接

      想通過手機客戶端(支持 Android、iPhone 和 Windows Phone)訪問開源中國:請點這里

      posted @ 2016-09-08 15:05 小馬歌 閱讀(306) | 評論 (0)編輯 收藏
       
      from:https://linux.cn/article-2871-1.html

      如果你想在命令行界面監控網絡吞吐量,nload 應用程序是個不錯的選擇。它是一個實時監控網絡流量和帶寬使用的控制臺應用程序,使用兩個圖表可視化地展示接收和發送的流量,并提供諸如數據交換總量、最小/最大網絡帶寬使用量等附加信息。

      安裝

      在 CentOS/RHEL/Red Hat/Fedora Linux 上安裝 nload

      首先在 CentOS 或者基于 RHEL 的操作系統上啟用 EPEL 倉庫,然后鍵入 yum 命令安裝 nload:

      1. # yum install nload

      在 Debian 或者 Ubuntu Linux 上安裝 nload

      鍵入 apt-get 命令

      1. $ sudo apt-get install nload

      在 FreeBSD 操作系統上安裝 nload

      通過 port 安裝 nload,鍵入:

      1. # cd /usr/ports/net/nload/ &amp;&amp; make install clean

      或者添加包

      1. # pkg install net/nload

      在 OpenBSD 操作系統上安裝 nload

      鍵入下列命令:

      1. $ sudo pkg_add -i nload

      在類 Unix 操作系統上從源代碼安裝 nload

      首先,使用 wget 或者 curl 命令獲取源代碼:

      1. $ cd /tmp
      2. $ wget http://www.roland-riegel.de/nload/nload-0.7.4.tar.gz

      使用 tar 命令解壓縮名為 nload-0.7.4.tar.gz 的 tar 包,鍵入:

      1. $ tar xvf nload-0.7.4.tar.gz

      使用 cd 命令進入 nload 源代碼所在目錄:

      1. $ cd nload*

      然后鍵入 ./configure 為你的操作系統配置安裝包:

      1. $ sh ./configure

      或者

      1. $ ./configure

      運行 configure 命令需要一點時間。完成后,使用 make 命令編譯 nload:

      1. $ make

      最后,鍵入 make install 命令以 root 用戶身份安裝 nload 應用程序和相關文件:

      1. $ sudo make install

      或者

      1. # make install

      使用

      如何使用 nload 顯示當前網絡使用量呢?

      基本語法是:

      1. nload
      2. nload device
      3. nload [options] device1 device2

      鍵入下列命令:

      1. $ nload
      2. $ nload eth0
      3. $ nload em0 em2

      會得到輸出:

      圖01: 使用 nload 命令

      圖01: 使用 nload 命令

      操控 nload 應用程序

      nload 命令一旦執行就會開始監控網絡設備,你可以使用下列快捷鍵操控 nload 應用程序。

      1. 你可以按鍵盤上的 ← → 或者 Enter/Tab 鍵在設備間切換。
      2. 按 F2 顯示選項窗口。
      3. 按 F5 將當前設置保存到用戶配置文件。
      4. 按 F6 從配置文件重新加載設置。
      5. 按 q 或者 Ctrl+C 退出 nload。

      設置顯示刷新間隔

      默認每 100 毫秒刷新一次顯示數值,下面的例子將時間間隔設置成 500 毫秒:

      1. $ nload -t {interval_number_in_millisec}
      2. $ nload -t 500

      輸出:

      Animated gif 01 - nload command in action

      Animated gif 01 - nload command in action

      GIF 動畫 01 - 使用 nload 命令

      設置流量數值顯示的單位

      語法如下:

      1. $ nload -u h|H|b|B|k|K|m|M|g|G
      2. $ nload -U h|H|b|B|k|K|m|M|g|G
      3. $ nload -u h
      4. $ nload -u G
      5. $ nload -U G

      釋義:

      • 小寫選項 -u: h 意為自動格式化為人類易讀的單位,b 意為 Bit/s,k 意為 kBit/s,m 意為 MBit/s,g 意為 GBit/s。大寫字母意為使用 Byte 替代 Bit。默認為 k。
      • 大寫選項 -U 與小寫選項 -u 非常相似,不同之處在于它展示的是數據量,比如 Bit, kByte, GBit 等等。(沒有 "/s")。默認值是 M。

      結論

      我覺得 nload 是一個穩定可靠的應用程序,如果你喜歡 nload,你可能也想試試 Linux 和其他類 Unix 操作系統環境下的 vnstat 與 iftop 工具。


      譯自: http://www.cyberciti.biz/networking/nload-linux-command-to-monitor-network-traffic-bandwidth-usage/

      posted @ 2016-09-08 15:04 小馬歌 閱讀(1524) | 評論 (0)編輯 收藏
      僅列出標題
      共95頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 Last 
       
      主站蜘蛛池模板: a色毛片免费视频| 亚洲GV天堂无码男同在线观看 | 91亚洲国产成人精品下载| 国产黄色片免费看| 久久精品夜色噜噜亚洲A∨| 特级做a爰片毛片免费看| 亚洲精品无码你懂的网站| 美女黄频a美女大全免费皮| 亚洲av高清在线观看一区二区| 青春禁区视频在线观看直播免费| 久久av无码专区亚洲av桃花岛| 91制片厂制作传媒免费版樱花| 中文字幕亚洲色图| 国产无人区码卡二卡三卡免费 | 日韩亚洲AV无码一区二区不卡| 国产免费爽爽视频在线观看| 亚洲高清国产AV拍精品青青草原| 最好免费观看高清在线| 337p日本欧洲亚洲大胆精品555588 | 激情综合亚洲色婷婷五月| 成年午夜视频免费观看视频 | 亚洲Av无码一区二区二三区| 无码一区二区三区免费视频 | 亚洲国产精品无码久久| 亚洲第一页综合图片自拍| 日韩av无码免费播放| 亚洲最大福利视频网站| 成人毛片免费视频| 一区二区三区免费看| 亚洲国产高清人在线| 午夜神器成在线人成在线人免费| 激情吃奶吻胸免费视频xxxx| 久久青青草原亚洲AV无码麻豆| 在线看片无码永久免费视频| 在线亚洲精品视频| 亚洲Av综合色区无码专区桃色| 亚洲免费福利在线视频| 香港经典a毛片免费观看看| 亚洲天堂视频在线观看| 大香人蕉免费视频75| 深夜免费在线视频|