docker入門

Docker提供了兩個版本:社區版(CE)企業版(EE)html

Docker社區版(CE)是開發人員和小型團隊開始使用Docker並嘗試使用基於容器的應用的理想之選。Docker CE有兩個更新渠道,即stableedgepython

  • Stable每一個季度爲您提供可靠更新
  • Edge每月爲您提供新功能

Docker企業版(EE)專爲負責在生產環境中大規模構建、交付和運行業務關鍵型應用程序的企業開發和 IT 團隊設計mysql

docker-ce安裝linux

配置yumnginx

wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repogit

wget -O /etc/yum.repos.d/epel.repo  http://mirrors.aliyun.com/repo/epel-7.repogithub

wget -O /etc/yum.repos.d/docker-ce.repo  https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repogolang

yum  list  docker-ce.x86_64  --showduplicates | sort -rredis

yum  install  docker-ce  -ysql

systemctl start docker

systemctl enable docker

systemctl status docker

 

離線安裝,可事先下載rpm(https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/7/x86_64/stable/Packages/),使用yum localinstallrpm -ivh安裝

 

宿主機開啓ip轉發(docker安裝完成後直接啓動docker程序不會自動開啓,重啓一次docker便可自動開啓,docker守護程序默認啓用--ip-forward選項)

 

dockerd設置NDS服務器/開啓IPv6支持(linux支持)/加速器配置:

cat >>/etc/docker/daemon.json<<-end

{

"dns": ["1.1.1.1","10.0.91.7"],

"ipv6":"true",

"registry-mirrors": ["https://registry.docker-cn.com"]

}

end

systemctl  restart  docker

docker  --version

docker   info

docker  run  hello-world

Docker Daemon鏈接方式

DockerC/S架構,服務端爲docker daemon,客戶端爲docker.service,支持本地unix socket域套接字通訊與遠程socket通訊。

默認爲本地unix socket通訊,要支持遠程客戶端訪問須要作以下設置(不安全,僅用於測試),

1. UNIX域套接字

默認就是這種方式,會生成一個/var/run/docker.sock文件,UNIX域套接字用於本地進程之間的通信, 這種方式相比於網絡套接字效率更高,但侷限性就是隻能被本地的客戶端訪問。

 

2. TCP端口監聽

服務端開啓端口監聽 dockerd  -H|--host  IP:PORT , 客戶端經過指定IP和端口訪問服務端 docker -H IP:PORT

經過這種方式,任何人只要知道暴露的ip和端口就能隨意訪問docker服務(由於docker的權限很高,一旦被突破就可以取得服務端宿主機的最高權限)

 

3. 啓動docker守護進程時能夠同時監聽多個socket

dockerd  -H unix:///var/run/docker.sock  -H tcp://127.0.0.1:2376  -H tcp://127.0.0.1:2377

...

INFO[0004] API listen on 127.0.0.1:2377

INFO[0004] API listen on /var/run/docker.sock

INFO[0004] API listen on 127.0.0.1:2376

 

啓用TLS安全鏈接

上述的http方式遠程鏈接很不安全,解決的辦法是啓用TLS證書實現客戶端和服務端的雙向認證, 以此來保證安全性。

建立TLS證書(根證書、服務端證書、客戶端證書)

 

cat  gencert.sh

#/bin/bash

#

if [ $# != 1 ] ; then

echo  "USAGE: $0  [HOST_IP]"

exit  1;

fi

 

COUNTRY=CN

PROVINCE=Jiangsu

CITY=Nanjing

ORGANIZATION=Hiynn

GROUP=Quality

HOST=$1

EMAIL=haiyun@hiynn.com

SUBJ="/C=$COUNTRY/ST=$PROVINCE/L=$CITY/O=$ORGANIZATION/OU=$GROUP/CN=$HOST/emailAddress=$EMAIL "

echo "the host(IP) is: $1"

 

# 1.生成證書RSA私鑰,PASSWORD做爲私鑰文件的密碼(這裏一旦設定密碼,後面在使用該私鑰時,必須提供密碼)

openssl  genrsa  -aes256  -out ca-key.pem  4096

 

# 2.用根證書RSA私鑰生成自簽名的根證書

openssl  req  -new -x509 -days 3650  -key ca-key.pem  -sha256  -out ca.pem  -subj $SUBJ

 

# 3.生成服務端私鑰

openssl   genrsa   -out server-key.pem  4096

 

# 4.生成服務端證書請求文件

openssl  req  -new  -sha256  -key server-key.pem    -out server.csr   -subj "/CN=$HOST"

 

# 5.使tls鏈接能經過ip地址方式,綁定IP

echo  subjectAltName = IP:127.0.0.1,IP:$HOST  >  extfile.cnf

 

# 6.使用根證書籤發服務端證書

openssl  x509  -req -days 3650  -sha256  -in server.csr  -CA ca.pem   -CAkey ca-key.pem  -out server-cert.pem  -CAcreateserial  -extfile extfile.cnf

 

 

# 7.生成客戶端私鑰

openssl genrsa -out key.pem 4096

 

# 8.生成客戶端證書請求文件(這裏這裏的/CN=clientclient爲客戶端主機的主機名或IP地址)

openssl  req  -subj '/CN=client'  -new -key  key.pem  -out client.csr

 

# 9.客戶端證書配置文件

echo  extendedKeyUsage = clientAuth  >  extfile.cnf

 

# 10.使用根證書籤發客戶端證書

openssl  x509  -req  -days 3650  -sha256  -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem   -extfile extfile.cnf

 

# 刪除中間文件(可選)

rm  -fm  client.csr  server.csr  ca.srl  extfile.cnf

 

# 移動文件

mkdir  client  server

cp {ca,cert,key}.pem    client

cp {ca,server-cert,server-key}.pem    server

rm {cert,key,server-cert,server-key}.pem

 

 

# 設置私鑰權限爲只讀

chmod  -f  0400    ca-key.pem   server/server-key.pem    client/key.pem

 

執行服務端配置

給腳本添加運行權限

chmod  +x  gencert.sh

 

HOST_IP=127.0.0.1

./gencert.sh  $HOST_IP

 

# 客戶端須要的證書保存在client目錄下, 服務端須要的證書保存在server目錄下

cp   server/ca*    /etc/docker

 

# 修改配置

cat  >>  /etc/default/docker<<end

DOCKER_OPTS="\

--selinux-enabled \

--tlsverify \

--tlscacert=/etc/docker/ca.pem \

--tlscert=/etc/docker/server-cert.pem \

--tlskey=/etc/docker/server-key.pem \

-H=unix:///var/run/docker.sock \

-H=0.0.0.0:2375"

end

 

vi  /lib/systemd/system/docker.service   [Service]中添加以下一行

EnvironmentFile=/etc/default/docker

systemctl  daemon-reload

systemctl  restart  docker

 

systemd服務文件介紹:

http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html

 

若是不想進行上述配置,可將配置文件寫入到/etc/docker/daemon.json文件中

cat  >>/etc/docker/daemon.json<<end

--selinux-enabled \

--tlsverify \

--tlscacert=/etc/docker/ca.pem \

--tlscert=/etc/docker/server-cert.pem \

--tlskey=/etc/docker/server-key.pem \

-H=unix:///var/run/docker.sock \

-H=0.0.0.0:2375"

end

 

接着按以前的方式鏈接服務端就出錯了

docker  -H tcp://127.0.0.1:2375    version

 

API訪問一樣報錯:

Get http://127.0.0.1:2375/v1.29/version: malformed HTTP response "\x15\x03\x01\x00\x02\x02".

* Are you trying to connect to a TLS-enabled daemon without TLS?

 

正確的訪問方式:

# 客戶端加tls參數訪問

docker --tlsverify --tlscacert=client/ca.pem --tlscert=client/cert.pem --tlskey=client/key.pem -H tcp://127.0.0.1:2375 version

 

# Docker API方式訪問

curl  https://127.0.0.1:2375/images/json  --cert client/cert.pem  --key client/key.pem  --cacert client/ca.pem

 

# 簡化客戶端調用參數配置

cp    client/*     ~/.docker

 

# 追加環境變量

echo -e "export DOCKER_HOST=tcp://$HOST_IP:2375  DOCKER_TLS_VERIFY=1"  >>  ~/.bashrc

docker  version

 

 

docker三大組件

鏡像、容器、倉庫

1、鏡像

docker  images

-q    僅顯示容器的ID

-a    顯示全部鏡像(中間層鏡像也會顯示出來),可能會看到不少無標籤的鏡像,這些鏡像是其它鏡像的依賴鏡像,不可刪除

-f   過濾器: dangling=true過濾出虛懸鏡像;

                            before|since=mysql:5.6 過濾出mysql:5.6以前或以後創建的全部鏡像

                            若是構建鏡像時指定了label,也可經過label過濾(docker  images -f  label=...

--format   按照go模板顯示

示例:

docker images --format "{{.ID}}: {{.Repository}}"

docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"

 

docker  rmi  <Images ID>                                     #刪除鏡像

docker  inspect  <Images ID>                                #顯示鏡像的詳細信息

docker  image  prune                                            #要刪除懸空(dangling)圖像:

docker  image  prune  -a                                      #刪除現有容器未使用的全部圖像,請使用如下-a標誌

 

使用帶有--filter標誌的過濾表達式限制修剪哪些圖像。例如,要僅考慮超過24小時前建立的鏡像:

docker  image  prune  -a  --filter "until=24h"

 

docker  search  centos

docker  pull  centos

docker  image  ls

docker  save  centos  > /opt/centos.tar.gz               #保存鏡像

docker  save  -o  centos.tar.gz  centos                  #同上

docker  load  <  /opt/centos.tar.gz                         #加載鏡像

docker  load  --input  /opt/centos.tar.gz                  #同上

 

docker  run -itd  --name  test  centos  [/bin/bash]

docker  attach

nsenter

 

docker  login

docker  pull

docker  push

 

構建鏡像

yum install python2-pip

配置pip國內源

mkdir  -p  /root/.pip

cat >> /root/.pip/pip.conf<<end

[global]
trusted-host=mirrors.aliyun.com
index-url=http://mirrors.aliyun.com/pypi/simple/

end

 

構建鏡像指令:docker  build   docker  image  build

 

Dockerfile初識:

cat >>Dockerfile<<end

FROM  python:2.7-slim

WORKDIR  /app

ADD  .  /app

RUN  pip  install  -r  requirements.txt

EXPOSE  80

ENV  NAME  World

CMD  ["python", "app.py"]

end

 

cat >>requirements.txt<<end

Flask

Redis

end

 

cat  >> app.py<<end

from  flask   import  Flask

from  redis   import  Redis, RedisError

import os

import socket

# Connect to Redis

redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

 

app = Flask(__name__)

 

@app.route("/")

def hello():

    try:

        visits = redis.incr("counter")

    except RedisError:

        visits = "<i>cannot connect to Redis, counter disabled</i>"

 

    html = "<h3>Hello {name}!</h3>" \

           "<b>Hostname:</b> {hostname}<br/>" \

           "<b>Visits:</b> {visits}"

    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

 

if __name__ == "__main__":

    app.run(host='0.0.0.0', port=80)

end

 

docker  build  -t friendlyhello  .

 

 

Dockerfile詳解:

格式:

# Comment

INSTRUCTION  arguments

注意事項:

1)、若是一行太長,使用反斜線 \ 續行

2)、在行首使用#符號,表示註釋(註釋中不支持 \續行)

3)、INSTRUCTION不區分大小寫。慣例是讓它們成爲大寫的,以便更容易地將它們與參數區分開來

18個指令

 

1FROM                          #必要指令,一般是第一條(引用變量及多階段構建例外),可有多條(多階段構建)

FROM <image> [AS <name>]

FROM <image>[:<tag>] [AS <name>]

FROM <image>[@<digest>] [AS <name>]

 

scratch鏡像:

FROM  scratch

ADD  hello  /

CMD  ["/hello"]

 

說明:

1、可經過添加AS name爲該構建階段命名該名稱可用於後續FROMCOPY --from=<name|index>指令,以引用此階段構建的鏡像

2tagdigest值是可選的。若是省略其中任何一個,則構建器默認採用latest標記

3FROM指令支持變量,變量在第一個FROM指令以前使用ARG聲明
ARG  CODE_VERSION=latest
FROM  base:${CODE_VERSION}
CMD  /code/run-app

 

2MAINTAINER  鏡像維護者信息

MAINTAINER  username@domain.com

說明:指定該鏡像維護者信息,儘可能使用LABEL替代

docker  inspect  <Container ID>   |grep -A 6  Labels

 

3RUN  執行命令

用來執行命令,有2種格式

格式一:

shell格式:         RUN  <命令>

                如:RUN  echo  '<h1>Hello, Docker!</h1>'   >  /usr/share/nginx/html/index.html

說明:

1、這種格式在shell中運行,會轉換爲/bin/sh  -c  <命令>執行

2、可使用SHELL命令更改默認的shell,或使用exec形式

3、要使用除「/bin/sh」以外的其餘shell,請使用exec形式。例如,RUN ["/bin/bash", "-c", "echo hello"]

4、命令中支持變量擴展

示例:

RUN  /bin/bash -c  source $HOME/.bashrc; \

echo  $HOME'

 

格式二:

exec格式:      RUN  ["executable", "param1", "param2"]

說明:

一、  這種格式不會被轉換

二、  不支持變量擴展

三、  exec形式被解析爲JSON數組,這意味着必須使用雙引號(")來包括單詞而不是單引號('

四、  JSON形式中,必須轉義反斜槓\windows中做爲路徑分隔符常常遇到)

 

注意:

1、可以使用反斜槓(續行符)分隔的多行上拆分長或複雜語句,使Dockerfile更具可讀性,可理解性和可維護性

示例:

    RUN  yum update && yum install -y \

      package-bar \

      package-baz \

      package-foo=1.3.*

2、使用管道

某些RUN命令依賴於使用管道符(|)將一個命令的輸出傳遞到另外一個命令的能力,以下例所示:

RUN  wget -O - https://some.site  |  wc -l  > /number

Docker使用/bin/sh -c解釋器執行這些命令,解釋器僅評估管道中最後一個操做的退出代碼以肯定成功。

在上面的示例中,只要wc -l命令成功,即便wget命令失敗,此構建步驟也會成功並生成新映像。

若是但願命令因管道中任何階段的錯誤而失敗,預先使用set -o pipefail 設置bash環境變量,肯定意外錯誤可防止構建無心中成功。

例如:

RUN  set -o pipefail && wget -O - https://some.site | wc -l > /number

注:並不是全部shell都支持該-o pipefail選項

 

4CMD 爲執行容器提供默認值            #只能有一條,若是列出多個只有最後一條生效

有三種形式:

  • CMD  ["executable","param1","param2"]exec形式,這是首選形式)
  • CMD  command  param1  param2shell形式)
  • CMD  ["param1","param2"](必須配合ENTRYPOINT使用,做爲其默認參數,單獨使用無效果

說明:

1exec形式不支持變量擴展,shell形式支持

2exec形式被解析爲JSON數組,這意味着必須使用雙引號(")來包括單詞而不是單引號('

3)在JSON形式中,必須轉義反斜槓\windows中常常遇到)

注意:不要混淆RUNCMDRUN實際上運行一條命令並提交結果,CMD在構建過程當中不執行任何操做,而是做爲鏡像的默認指令(即便用該鏡像啓動容器時默認執行的指令)。

 

5LABEL  將元數據添加到鏡像

LABEL  <key>=<value>  <key>=<value>  <key>=<value> ...

LABEL是鍵值對,要在LABEL值中包含空格,請使用()引號或反斜槓轉義,就像在命令行解析中同樣

示例:

LABEL  "com.example.vendor"="ACME Incorporated"

LABEL  com.example.label-with-value="foo"

LABEL  version="1.0"

LABEL  description="This text illustrates \                             #續行符

that label-values can span multiple lines."

鏡像能夠有多個標籤,能夠在一行中指定多個標籤

兩種示例:

LABEL  multi.label1="value1"  multi.label2="value2"  other="value3"

LABEL  multi.label1="value1" \

      multi.label2="value2" \

      other="value3"

 

6EXPOSE  容器運行時偵聽其服務端口

EXPOSE  <port1>  [<port2>/<protocol>...]                                        #多個端口之間使用空格間隔

EXPOSE指令通知Docker容器在運行時偵聽指定的網絡端口。

能夠指定端口是偵聽TCP仍是UDP,若是未指定協議,則默認爲TCP

 

7ENV  設置環境變量

有兩種形式:

形式一:

ENV <key> <value>                           #第二個空格後的整個字符串將被視爲<value>,包括空格字符

形式二:

ENV  <key>=<value> ...                   # ENV指令將環境變量<key>的值設置爲<value>

該環境變量在構建階段全部後續指令的環境中生效,而且使用ENV設定的環境變量會在鏡像中一直存在

示例:

ENV  myName="John Doe"  myDog=Rex\ The\ Dog \

    myCat=fluffy

ENV myName John Doe

ENV myDog Rex The Dog

ENV myCat fluffy                       #效果相同

 

ENV指令中可以使用變量:

ENV  abc=hello

ENV  abc=bye  def=$abc

ENV  ghi=$abc

示例:

ENV  PG_MAJOR  9.3

ENV  PG_VERSION  9.3.4

RUN  curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress &&…

ENV  PATH  /usr/local/postgres-$PG_MAJOR/bin:$PATH

 

注意:

1、可使用docker inspect查看變量值,使用docker run --env <key>=<value>構建容器時更改變量的值。

2Dockerfile中可以擴展變量的指令:

FROM/ADD/COPY/ENV/EXPOSE/LABEL/STOPSIGNAL/USER/VOLUME/WORKDIR/ONBUILD

 

8ARG                                                  #設置構建過程當中的環境變量

格式:

ARG  <參數名>[=<默認值>]                  

ARG指令能夠可選地包括一個默認值:

示例:

FROM  busybox

ARG  user1=someuser                       #指定了默認值

ARG  buildno=1                               #指定了默認值

...

說明:

若是ARG指令具備默認值,而且在構建時沒有傳遞值,將使用默認值,
若是構建時傳遞值,將不使用默認值,而是用傳遞的值

 

注意:

1、效果和ENV同樣,都是設置環境變量,不一樣的是:ARG設置的環境變量,在未來容器運行時,再也不存在,僅在構建鏡像時存在

2、可在構建命令docker build使用--build-arg <參數名>[=<>]選項覆蓋Dockerfile中設定的默認值,參數名必須在Dockerfile中已經使用ARG指令構建,不然會報錯或警告

3、一個ARG指令生效範圍,在其以後,且在其構建階段內。若在多階段構建中的每一個階段均使用ARG,每一個階段都必須包含ARG指令。

示例:多階段構建

FROM  busybox

ARG  SETTINGS

RUN  ./run/setup $SETTINGS

 

FROM  busybox

ARG  SETTINGS

RUN  ./run/other   $SETTINGS

4Dockerfile能夠包括一個或多個ARG指令。

例如,如下是有效的Dockerfile

FROM  busybox

ARG  user1

ARG  buildno

...

警告:

不要使用構建時變量來傳遞密碼,如github密鑰,用戶憑據等,構建時變量值對於任何用戶都是可見的

 

5ARG生效範圍

ARG變量定義從Dockerfile中定義的行開始生效,直到其所在構建階段結束

示例:

FROM  busybox

USER  ${user:-some_user}

ARG  user

USER  $user

...

經過如下指令構建:

$ docker  build  --build-arg  user=what_user  .

2USER使用some_user做爲user變量的值。

4USER使用what_user做爲user變量的值,且what_user在命令行上傳遞的值。

在經過ARG指令定義以前,對變量的任何使用都會致使空字符串

 

6、使用ARG變量

可使用ARGENV指令指定在RUN指令中可用的變量。ENV指令定義的環境變量始終覆蓋ARG定義的同名變量。

示例:

 FROM  ubuntu

 ARG  CONT_IMG_VER

 ENV  CONT_IMG_VER  v1.0.0

 RUN  echo  $CONT_IMG_VER

而後,假設使用此命令構建此映像:

$ docker  build  --build-arg  CONT_IMG_VER=v2.0.1  .

在這種狀況下,RUN指令使用v1.0.0而不是ARG用戶傳遞的設置:v2.0.1

 

 

9COPY  複製上下文中的文件或目錄到容器中

COPY有兩種形式:

  • COPY [--from=<name|index>] [--chown=<user>:<group>] <src>... <dest>
  • COPY [--from=<name|index>] [--chown=<user>:<group>] ["<src>",... "<dest>"]包含空格的路徑使用此形式

--chown功能僅在用於構建Linux容器

 

注意:

1、源路徑,必定是相對於build指令中指定的上下文的相對路徑
COPY  package.json   /usr/src/app/

2、目標路徑,能夠是容器內的絕對路徑,也能夠是相對於工做目錄的相對路徑(工做目錄可使用WORKDIR指令來指定)

3目標路徑不須要事先建立,若是目錄不存在,在複製文件以前會自動建立

4、使用COPY指令時,源文件的各類元數據都會被保留,如讀寫執行權限,文件的相關時間屬性等

5、源路徑能夠是多個,甚至能夠含有通配符,通配符要符合Gofilepath.Match規則:

COPY  hom*        /mydir

COPY  hom?.txt    /mydir

 

6、若是使用--chown選項,最好使用數字形式的GIDUID(若是使用用戶名和組名,在容器文件系統中不存在/etc/passwd/etc/group,將會致使構建失敗)

示例:

COPY   --chown=55:mygroup   files*   /somedir/

COPY   --chown=bin   files*   /somedir/                    #不指定組名,使用與用戶名同名的組

COPY   --chown=1    files*   /somedir/                     #GID也爲1

COPY   --chown=10:11   files*   /somedir/

7、(可選)COPY接受一個標誌--from=<name|index>
該選項將用於替代用戶發送先前構建階段的構建上下文

若是先前構建階段使用FROM .. AS  <name>,則--from可經過name引用

若是先前構建階段未使用FROM .. AS  <name>,則--from可經過索引值index引用,若是先前構建階段是第一階段,index0,以此類推
若是找不到具備指定名稱的構建階段,則嘗試使用具備相同名稱的鏡像

COPY遵照如下規則:

  • <src>路徑必須位於build構建時的context上下文中;不能COPY ../something /something,由於第一步docker build是將上下文目錄(和子目錄)發送到docker守護程序
  • 若是<src>是目錄,則複製目錄中的所有內容,包括文件系統元數據
    注意不復制目錄自己,只複製其內容
  • 若是<src>任何其餘類型的文件,則將其與元數據一塊兒單獨複製。在這種狀況下,若是<dest>以尾部斜槓結尾/,則將其視爲目錄,<src>並將寫入內容<dest>/base(<src>)
  • 若是<src>直接或因爲使用通配符指定了多個資源,則<dest>必須是目錄,而且必須以斜槓結尾/
  • 若是<dest>不以尾部斜槓結束,則將其視爲常規文件,並將<src>的內容寫入<dest>文件中。
  • 若是<dest>不存在,則會在其路徑中建立全部缺乏的目錄。

 

10ADD  複製本地文件到上下文中

<src>中複製文件、目錄或遠程文件URL,並將它們添加到路徑上鏡像的文件系統<dest>

ADD有兩種形式:

  • ADD  [--chown=<user>:<group>]  <src>...<dest>
  • ADD  [--chown=<user>:<group>]  ["<src>",..."<dest>"]包含空格的路徑須要使用這種形式

ADD指令的全部使用注意事項及遵照規則與COPY相同

COPY不一樣的是:

源路徑:

1、能夠是URLDocker引擎會嘗試下載這個連接文件,放入到<dest>中,下載後文件的權限爲600,如果想更改文件的權限,還須要增長一層RUN指令

2、若是下載的是壓縮包,還須要增長一層RUN指令進行解壓

3、若是<src>是一個上下文中的tar壓縮文件(gzipbzip2xz),ADD指令會自動解壓放入到<dest>中,若是不但願壓縮文件解壓,則使用COPY(這是ADD指令最大的用處)

4、僅在須要自動解壓時,纔會選擇使用ADD指令,不然複製文件儘量使用COPY

5ADD指令會令鏡像構建緩存失效,從而可能使鏡像構建過程很是緩慢(所以多階段構建時,儘量不要使用)

 

11ENTRYPOINT  入口點                  #只能有一條,如有多條僅最後一條生效

ENTRYPOINT有兩種形式:

  • ENTRYPOINT ["executable", "param1", "param2"]exec形式,首選
  • ENTRYPOINT command param1 param2shell形式,最好在command前使用exec指令,以使該進程做爲容器的PID 1進程)

exec形式:

docker run <image>的命令行參數將附加在exec形式的ENTRYPOINT的全部元素以後,做爲ENTRYPOINT指令的參數,並將覆蓋全部使用CMD指定的元素。

可使用docker run --entrypoint標誌覆蓋exec形式的ENTRYPOINT指令

shell形式:

shell形式防止任何被CMDrun使用的命令行參數,缺點是ENTRYPOINT將被做爲/bin/sh -c的一個子命令且不傳遞信號。這意味着可執行文件將不是容器中PID1的進程,而且不會收到Unix信號,所以可執行文件將不會收到來自docker  stop  <container>SIGTERM信號。

 

從命令行工具的鏡像示例:

cat >>Dockerfile<<end

FROM  centos

RUN   yum install  curl -y

CMD   ["curl", "-s", "http://ip.cn"]

end

 

docker build  -t  myip:1.0  .

docker  run  --name myip01  myip:1.0

 

docker  run  --name myip02  myip:1.0  -i                                                #此時會報錯

緣由:執行docker run myip -i   鏡像名稱myip後的手動指定的主進程將會替換構建鏡像時CMD指定的主進程,

這裏替換後至關於執行-i指令,但實際上沒有該指令,提示executable file not found in $PATH

 

正確用法:docker  run  myip  curl -s http://ip.cn  -i

 

使用ENTRYPOINT解決:

使用以下的dockerfile構建鏡像

cat >> Dockerfile<<end

FROM centos

RUN  yum  install  curl  -y

ENTRYPOINT  ["curl", "-s", "http://ip.cn"]

end

 

docker  build  -t  myip2:1.0  .

docker  run  --name myip201  myip2:1.0

docker  run  --name myip202  myip2:1.0  -i

這種方式頗有用,由於鏡像名稱能夠兼做二進制文件的引用

 

ENTRYPOINT指令還能夠與輔助腳本結合使用,使其可以以與上述命令相似的方式運行,即便啓動該工具可能須要多個步驟

例如,Postgres官方鏡像使用如下腳本做爲其ENTRYPOINT

#!/bin/bash

set -e

if [ "$1" = 'postgres' ]; then

    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then

        gosu postgres initdb

    fi

    exec gosu postgres "$@"

fi

exec "$@"

 

配置應用爲容器中PID1進程

此腳本使用的exec bash命令,以使最終運行的應用程序成爲容器的PID 1,這容許應用程序接收發送到容器的任何Unix信號

幫助程序腳本被複制到容器中並經過ENTRYPOINT容器啓動運行:

COPY  ./docker-entrypoint.sh  /

ENTRYPOINT  ["/docker-entrypoint.sh"]

CMD  ["postgres"]

該腳本容許用戶以多種方式與Postgres交互。

它能夠簡單地啓動Postgres

$ docker  run  postgres

或者,它可用於運行Postgres並將參數傳遞給服務器:

$ docker  run  postgres  postgres  --help

最後,它還能夠用來啓動一個徹底不一樣的工具,好比Bash

$ docker  run  --rm  -it  postgres  bash

 

Exec形式的ENTRYPOINT示例

可使用exec形式ENTRYPOINT設置默認命令和參數,而後使用任一形式CMD設置可更改的其餘默認值。

FROM  ubuntu

ENTRYPOINT  ["top", "-b"]

CMD  ["-c"]

構建鏡像,啓容器

 

檢查結果:

$ docker  exec  -it test  ps aux

 

 可使用docker stop優雅地請求top進程關閉

可再次使用docker start啓動

 

如下Dockerfile顯示使用ENTRYPOINT前臺運行Apache(即做爲容器的PID 1進程):

FROM  debian:stable

RUN  apt-get update  &&  apt-get install -y  --force-yes  apache2

EXPOSE  80  443

VOLUME  ["/var/www", "/var/log/apache2", "/etc/apache2"]

ENTRYPOINT  ["/usr/sbin/apache2ctl",  "-D",  "FOREGROUND"]

 

注意

1exec形式被解析爲JSON數組,這意味着必須使用雙引號(")來引用而不是單引號(')。

2shell形式不一樣,exec形式不會調用命令shell,所以不能擴展變量。例如,ENTRYPOINT [ "echo", "$HOME" ]不會對變量進行替換$HOME

3、若是想要shell處理,那麼要麼使用shell形式,要麼直接執行shell,例如:ENTRYPOINT [ "sh", "-c", "echo $HOME" ]

 

 

Shell形式的ENTRYPOINT示例

能夠爲ENTRYPOINT指定一個純字符串指令,將在/bin/sh -c中執行。該形式支持環境變量擴展,並將忽略任何CMDdocker run命令行參數爲了確保docker stop可以正確地發出任何信號給長時間運行的ENTRYPOINT可執行文件,須要記住使用exec指令執行相關指令

FROM ubuntu

ENTRYPOINT exec top -b

運行此鏡像時,將看到單個PID 1進程:

$ docker run -it --rm --name test top

 

執行docker stop關閉及docker start啓動容器

 

若是shell形式中未使用exec執行ENTRYPOINT中的指令:

FROM ubuntu

ENTRYPOINT  top  -b

CMD  -d 5

啓容器:

$ docker  run  -it  --name  test

 

從輸出中top看到指定ENTRYPOINT的不是PID 1

若是運行docker stop test,容器將不會乾淨地退出(最終容器也會推出,可是經過將信號發送給sh,而不是發送給應用程序指令top

CMDENTRYPOINT如何相互做用:

  1. Dockerfile應至少指定一個CMDENTRYPOINT命令
  2. ENTRYPOINT應該在將容器用做可執行文件時使用
  3. CMD應該用做定義ENTRYPOINT命令的默認參數
  4. CMD在使用替代參數運行容器時將被覆蓋

下表顯示了針對不一樣ENTRYPOINT/CMD組合執行的命令:

 

沒有ENTRYPOINT

ENTRYPOINT exec_entry  p1_entry

ENTRYPOINT [「exec_entry」「p1_entry」]

沒有CMD

錯誤,不容許

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry

CMD [「exec_cmd」「p1_cmd」]

exec_cmd  p1_cmd

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry  exec_cmd  p1_cmd

CMD [「p1_cmd」「p2_cmd」]

p1_cmd  p2_cmd

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry  p1_cmd p2_cmd

CMD  exec_cmd  p1_cmd

/bin/sh  -c  exec_cmd  p1_cmd

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry  /bin/sh -c exec_cmd p1_cmd

 

12VOLUME

格式:

VOLUME  ["/data"]

VOLUME  ["/var/log/","/data "]

VOLUME  /var/log

VOLUME  /var/log  /var/db

VOLUME指令建立具備指定名稱的掛載點,並將其標記爲從本機或其餘容器保存外部掛載的卷。該值能夠是JSON數組,VOLUME ["/var/log/"]或具備多個參數的普通字符串,如VOLUME /var/logVOLUME  /var/log  /var/db

示例:

FROM  centos

RUN  mkdir  /myvol

RUN  echo "hello world"  >  /myvol/greeting

VOLUME  ["/myvo","/data"]

CMD ["/bin/bash"]

 

docker volume ls

docker volume inspect

ll  /var/lib/docker/volumes/

注意:

宿主機目錄在容器運行時聲明:宿主機目錄(mountpoint)本質上是依賴於主機的。這是爲了保持鏡像的可移植性,由於不能保證給定的主機目錄在全部主機上均可用。所以,沒法從Dockerfile中安裝主機目錄。VOLUME指令不支持指定host-dir參數,必須在建立或運行容器時指定掛載點

 

13USER

USER  <user>[:<group>]

USER  <UID>[:<GID>]

當構建鏡像時,在Dockerfile文件中有RUNCMDENTRYPOINT指令時,USER指令用於設置執行這些指令的用戶(UID),及可選的用戶組(GID),用戶或組必須事先存在

警告當用戶沒有主組時,鏡像(或下一個指令)將使用root組運行

 

14WORKDIR

WORKDIR  /path/to/workdir

WORKDIR指令用於在Dockerfile設置RUNCMDENTRYPOINTCOPYADD指令的工做目錄。若是WORKDIR指定的目錄事先不存在,則會自動被建立。

Dockerfile中可屢次使用WORKDIR指令。若是提供了相對路徑,則它將相對於前一條WORKDIR指令的路徑。

例如:

WORKDIR  /a

WORKDIR  b

WORKDIR  c

RUN  pwd

最終pwd命令的輸出將是/a/b/c

WORKDIR指令能夠解析先前使用ENV設定的環境變量。只能使用Dockerfile文件中顯式設置的環境變量。

例如:

ENV  DIRPATH  /path

WORKDIR  $DIRPATH/$DIRNAME

RUN  pwd

最終pwd命令的輸出Dockerfile將是/path/$DIRNAME

 

15ONBUILD

ONBUILD  [INSTRUCTION]

當鏡像用做另外一個構建的基礎時,該ONBUILD指令向鏡像添加將在稍後執行的觸發指令。觸發器將在下游構建的上下文中執行,就好像它在下游DockerfileFROM指令以後當即插入同樣

任何構建指令均可以註冊爲觸發器

 

16HEALTHCHECK               #只能有一條,若是列出多個,則只有最後一個生效

HEALTHCHECK指令有兩種形式:

  • HEALTHCHECK  [OPTIONS]  CMD  command(經過在容器內運行命令來檢查容器運行情況
  • HEALTHCHECK  NONE禁用從基礎映像繼承的任何運行情況檢查

當容器指定了運行情況檢查時,除了正常狀態外,它還具備運行情況:

這個狀態最初是starting

每當健康檢查經過時,它就會變成healthy

通過必定數量的連續失敗後,它就變成了unhealthy

OPTIONS

  • --interval=DURATION(默認值:30s                    檢查時間間隔
  • --timeout=DURATION(默認值:30s                    單次檢查超時秒數,若超時視爲檢查失敗
  • --retries=N(默認值:3                           連續重試N次後,健康檢查均失敗,認爲容器爲unhealthy
  • --start-period=DURATION(默認值:0s                爲須要時間引導的容器提供初始化時間,在此期間探測失敗將不計入最大重試次數。但若是在啓動期間運行情況檢查成功,則會將容器視爲已啓動,而且全部連續失敗將計入最大重試次數

CMD關鍵字後面的命令能夠是shell命令(如HEALTHCHECK CMD /bin/check-running)或exec形式

命令的退出狀態指示容器的運行情況。可能的值是:

  • 0:成功 - 容器健康且隨時可用
  • 1:不健康 - 容器沒法正常工做
  • 2:保留 - 不要使用此退出代碼

示例:

要檢查每五分鐘左右網絡服務器可以在三秒鐘內爲網站的主頁面提供服務:

HEALTHCHECK  --interval=5m  --timeout=3s  CMD curl -f http://localhost/ || exit 1

 

17SHELL

SHELL  ["executable", "parameters"]

該指令容許覆蓋用於shell形式的命令的默認shell

Linux上默認shell["/bin/sh", "-c"]

Windows上默認shell["cmd", "/S", "/C"]SHELL指令必須JSON格式寫入Dockerfile

Windows上常用

 

18STOPSIGNAL

STOPSIGNAL  signal

STOPSIGNAL指令設置將發送到容器的系統調用信號以退出。此信號能夠是與內核的系統調用表中的位置匹配的有效無符號數,例如9,或SIGNAME格式的信號名,例如SIGKILL

默認的stop signalSIGTERM,在docker stop的時候會給容器內PID1的進程發送這個signal,經過--stop-signal能夠設置本身須要的signal,主要的目的是爲了讓容器內的應用程序在接收到signal以後能夠先作一些事情,實現容器的平滑退出,若是不作任何處理,容器將在一段時間以後強制退出,會形成業務的強制中斷,這個時間默認是10s

 


Dockerfile說明

Docker鏡像由只讀層組成,每一個層都表明一個Dockerfile指令這些層是堆疊的,每一層都是前一層變化的增量。

Dockerfile

FROM  ubuntu:15.04

COPY  .  /app

RUN  make  /app

CMD  python  /app/app.py

每條指令建立一個層

  • FROMubuntu:15.04Docker鏡像建立一個層。
  • COPYDocker客戶端的當前目錄添加文件。
  • RUN用你的應用程序構建make
  • CMD指定在容器中運行的命令。

運行鏡像並生成容器時,能夠在基礎層的頂部添加新的可寫層writable layer容器層)。對正在運行的容器所作的全部更改(例如寫入新文件,修改現有文件和刪除文件)都將寫入此可寫容器層

 

上下文的概念

執行docker build命令時,宿主機上的當前工做目錄稱爲構建上下文。默認狀況下,假定Dockerfile位於本地當前目錄,但可使用選項(-f)指定其餘位置不管Dockerfile實際存在的位置如何,當前目錄中的全部文件目錄的遞歸內容都將做爲構建上下文發送到Docker守護程序(構建由Dockerd守護程序運行,而不是由CLI運行)

Usage:  docker  build [OPTIONS]   PATH | URL | -

 

從構建上下文(.中構建映像:

mkdir  project && cd  project

echo  "hello" > hello

echo  -e "FROM  busybox\nCOPY  /hello /\nRUN  cat /hello"   >  Dockerfile

docker  build  -t helloapp:v1  .

移動Dockerfilehello文件到單獨的目錄

構建映像的第二個版本(不依賴於上一個版本的緩存)

使用-f指向Dockerfile並指定構建上下文的目錄:

mkdir  -p  dockerfiles  context

mv  Dockerfile  dockerfiles && mv  hello  context

docker  build  --no-cache  -t helloapp:v2  -f dockerfiles/Dockerfile  context

 

Docker 17.05增長了Dockerfile經過stdin使用本地遠程構建上下文進行管道來構建映像的功能。

在早期版本中,使用來自stdinDockerfile構建映像,不支持發送構建上下文

Docker 17.04及更低版本

docker  build  -t foo  -  <<EOF

FROM  busybox

RUN  echo "hello world"

EOF

Docker 17.05及更高版本(本地構建上下文)

docker  build  -t foo  .   -f -  <<EOF

FROM  busybox

RUN  echo "hello world"

COPY  . /my-copied-files

EOF

Docker 17.05及更高版本(遠程構建上下文)

docker  build  -t foo  https://github.com/thajeztah/pgadmin4-docker.git  -f -  <<EOF

FROM  busybox

COPY  LICENSE  config_local.py  /usr/local/lib/python2.7/site-packages/pgadmin4/

EOF

 

排除上下文中與構建無關的文件,使用上下文中的.dockerignore文件(每一個要排除的文件或目錄獨佔一行)

示例.dockerignore文件(支持通配):

# comment

*/temp*

*/*/temp*

temp?

**匹配任意數量目錄(包括零)的特殊通配符字符串

例如,**/*.go將排除.go在全部目錄中找到的以該結尾的全部文件,包括構建上下文的根

!(感嘆號)開頭的行可用於對排除項進行例外處理

如下是.dockerignore使用此機制的示例文件:

    *.md

    !README.md

README.md上下文外,全部以md爲擴展名的文件除外(也就是README.md文件不會被排除)

示例:

    *.md

    README-secret.md

    !README*.md

包含全部README文件。中間一行沒有效果,由於!README*.md匹配README-secret.md而且最後。

甚至可使用該.dockerignore文件來排除Dockerfile.dockerignore文件

注意:這些文件仍然發送到守護程序,由於它須要它們來完成它的工做。可是ADDCOPY指令不會將它們複製到鏡像中

 

 

使用多階段構建:

多階段構建(在Docker 17.05或更高版本中)可大幅減少最終鏡像的大小,而沒必要費力地減小中間層和文件的數量。

因爲鏡像是在構建過程的最後階段構建的,所以能夠經過利用構建緩存來最小化鏡像層

 

#第一階段

Dockerfile

FROM  golang:1.7.3

WORKDIR  /go/src/github.com/alexellis/href-counter/

RUN  go  get  -d  -v golang.org/x/net/html

COPY  app.go  .

RUN  CGO_ENABLED=0  GOOS=linux go build -a -installsuffix cgo -o app .

#第二階段

FROM  alpine:latest

RUN  apk  --no-cache  add  ca-certificates

WORKDIR  /root/

COPY  --from=0  /go/src/github.com/alexellis/href-counter/app  .

CMD  ["./app"]

只須要單個Dockerfile,也不須要單獨的構建腳本。只要運行docker build

$ docker  build  -t  alexellis2/href-counter:latest  .

 

默認狀況下,階段未命名,能夠經過整數來引用它們,第一條FROM指令從0開始。可是,能夠經過as <NAME>FROM指令中添加一個來命名您的階段

此示例經過命名階段並使用COPY指令中的名稱來改進前一個示例。這意味着即便Dockerfile中的指令稍後從新排序,COPY也不會中斷。

FROM  golang:1.7.3  as  builder

WORKDIR  /go/src/github.com/alexellis/href-counter/

RUN  go get -d -v golang.org/x/net/html

COPY  app.go  .

RUN  CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

 

FROM  alpine:latest

RUN  apk --no-cache add ca-certificates

WORKDIR  /root/

COPY  --from=builder  /go/src/github.com/alexellis/href-counter/app  .

CMD  ["./app"]

 

停在特定的構建階段

構建鏡像時,不必定須要構建整個Dockerfile,包括每一個階段。能夠指定目標構建階段

如下命令假定您使用的是前一個Dockerfile但在名爲builder的階段中止:

$ docker  build  --target builder  -t alexellis2/href-counter:latest .

一些可能的場景:

  • 調試特定的構建階段
  • 使用debug階段,該階段啓用了全部調試符號或工具,以及lean production階段
  • 使用testing階段,該階段應用程序填充測試數據,但使用實際數據的不一樣階段構建生產環境

使用外部鏡像做爲「stage」

使用多階段構建時,不只能夠從先前在Dockerfile中的構建階段進行復制還可使用該COPY --from指令從單獨的鏡像進行復制,使用本地鏡像名稱,本地或Docker倉庫中可用的標記或標記ID

若有必要,Docker客戶端會提取鏡像,並從鏡像中複製工件。語法是:

COPY  --from=nginx:latest  /etc/nginx/nginx.conf  /nginx.conf

 

管理鏡像

使本身的鏡像可供組織內部或外部的其餘人使用的最簡單方法是使用Docker registry,例如Docker HubDocker Trusted Registry,或運行本身的私有註冊服務器。

Docker Hub

註冊及使用(演示使用過程)

Docker註冊服務器

Docker RegistryDocker生態系統的一個組件。registry是一個存儲和內容傳送系統,包含命名的Docker映像,可使用不一樣的標記版本

docker  images

docker  run  -p 4000:80  friendlyhello                                 #啓容器

docker  tag  image  username/repository:tag

docker push                                                     #發佈鏡像

docker kill                                         #強制關閉指定的容器

docker rm

docker rm $(docker ps -a -q)

docker images -a

docker rmi <imagename>


容器中數據的簡單管理(基礎)

docker中數據有兩種:

1、數據卷
-v  /data                             #
卷,等同於Dockerfile中的VOLUME指令(生產環境使用
-v  src:dst:ro|rw                  #
綁定掛載,docker不能直接管理(開發及測試環境使用)

說明:

src 爲宿主機上的目錄

dst 爲容器中的目錄,將會被掛載到宿主機

srcdst均不須要事先建立

srcdst也但是文件

2、數據卷容器

--volumes-from

 

示例:

docker run -itd --name nginx-volume-test  -v /data  nginx

docker run -itd --name nginx-volume-test2  -v /data_nginx:/data2  nginx

docker inspect -f {{.Mounts}}   <Container ID >

docker run -itd -v /root/.bash_history:/root/.bash_history  nginx  /bin/bash

 

docker  run -itd  --name datasource  -v /root/.datasource:/data  nginx

docker  run -itd  --volumes-from datasource  centos

 

 


配置網絡

Docker的網絡子系統是可插拔的,使用驅動程序。默認狀況下存在多個驅動程序,並提供核心網絡功能:

  • bridgedocker默認的網絡驅動。若是未指定驅動程序,則這是須要建立的網絡類型。當應用程序在須要通訊的獨立容器中運行時,一般會使用橋接網絡。
  • host:對於獨立的容器,刪除容器和Docker主機之間的網絡隔離,並直接使用主機的網絡(若啓用特權容器,將能夠直接修改宿主機網絡設置)。host僅適用於Docker 17.06及更高版本上的swarm羣集服務
  • overlayOverlay networks將多個Docker守護程序鏈接在一塊兒,並使swarm羣集服務可以相互通訊。還可使用Overlay networks來促進swarm羣集服務和獨立容器之間的通訊,或者在不一樣Docker守護程序上的兩個獨立容器之間進行通訊。此策略無需在這些容器之間執行OS級別的路由。
  • macvlanMacvlan網絡容許爲容器分配MAC地址,使其顯示爲網絡上的物理設備Docker守護程序經過其MAC地址將流量路由到容器。macvlan在處理指望直接鏈接到物理網絡的傳統應用程序時,使用該驅動程序有時是最佳選擇,而不是經過Docker主機的網絡堆棧進行路由
  • none:對於此容器,禁用全部網絡。一般與自定義網絡驅動程序一塊兒使用,none不適用於swarm集羣服務。

 

1、在同一個Docker宿主機上多個容器進行通訊時,用戶定義的橋接網絡(即自定義bridge是最佳選擇

2、不一樣Docker宿主機上運行的容器進行通訊時,或者當多個應用程序使用swarm服務協同工做時,overlay網絡是最佳選擇

3、從VM設置遷移或須要容器看起來像網絡上的物理主機(每一個都具備惟一的MAC地址)時,Macvlan網絡是最佳選擇

bridge網絡

橋接網絡適用於在同一個Docker守護程序宿主機上運行的容器之間通訊。

對於在不一樣Docker守護程序宿主機上運行的容器之間的通訊,能夠在操做系統級別管理路由,也可使用overlay網絡

啓動Docker時,會自動建立默認橋接網絡(也稱爲bridge),而且除非另行指定,不然新啓動的容器將鏈接到該橋接網絡。還能夠建立用戶自定義網橋,用戶定義的網橋優於默認bridge網橋

 

用戶定義的網橋與默認網橋之間的差別:

  • 用戶定義的橋接器可在容器化應用程序之間提供更好的隔離和互操做性

鏈接到同一個用戶自定義的網橋的容器會自動將全部端口相互暴露,而不會向外界顯示任何端口

  • 用戶定義的橋接器在容器之間提供自動DNS解析

默認網橋上的容器只能經過IP地址相互訪問,除非使用--link選項。在用戶定義的橋接網絡上,容器能夠經過主機名相互解析。

  • 容器能夠在運行中與用戶自定義的網絡鏈接和分離

在容器的生命週期中,能夠動態地將其與用戶自定義的網絡鏈接或斷開。而從默認橋接網絡中刪除容器,則須要中止容器並使用不一樣的網絡選項從新建立容器

  • 每一個用戶定義的網絡都會建立一個可配置的網橋

若是容器使用默認橋接網絡,則能夠對其進行配置,但全部容器都使用相同的設置,如MTUiptables規則。此外,配置默認橋接網絡發生在Docker服務以外,而且須要從新啓動Docker

  • 默認橋接網絡上的相互連接容器(即便用--link選項互聯)共享環境變量

最初,在兩個容器之間共享環境變量的惟一方法是使用--link選項連接,用戶定義的網絡沒法實現這種類型的變量共享。但有更好的方法來共享環境變量。

一些想法:

    • 多個容器可使用Docker volume掛載包含共享信息的文件或目錄。
    • 可使用docker-compose一塊兒啓動多個容器,而且compose文件能夠定義共享變量。
    • 可使用swarm服務而不是獨立容器,並利用secretsconfigs共享。

 

使用docker network create建立和配置用戶定義的網橋。若是不一樣的應用程序組具備不一樣的網絡要求,則能夠在建立時單獨配置每一個用戶定義的網橋。

1、管理用戶自定義網絡

docker  network  create  \

--subnet  172.20.0.1/16 \

  --ip-range  172.20.0.1/24 \

  --gateway  172.20.0.1 \

my-net

能夠指定子網,IP地址範圍,網關和其餘選項

docker  network  rm  my-net                                       #刪除用戶定義的橋接網絡

當建立或刪除用戶定義的網橋或從用戶定義的網橋鏈接或斷開容器時,Docker使用特定於操做系統的工具來管理底層網絡基礎結構(例如iptablesLinux上添加或刪除網橋設備或配置規則)

 

2、將容器鏈接到用戶定義的橋

建立新容器時,能夠指定一個或多個--network標誌

此示例將Nginx容器鏈接到my-net網絡。它還將容器中的端口80發佈到Docker宿主機上的端口8080,所以外部客戶端能夠訪問該端口8080。鏈接到my-net網絡的任何其餘容器均可以訪問my-nginx容器上的全部端口,反之亦然。

$ docker  create  --name  my-nginx \

  --network  my-net \

--publish  8080:80 \

  nginx:latest

 

若要將正在運行的容器鏈接到現有的用戶定義的橋,使用docker network connect命令。

如下命令將已在運行的my-nginx容器鏈接到已存在的my-net網絡

$ docker  network  connect  my-net  my-nginx

 

要斷開正在運行的容器與用戶定義的橋接器的鏈接,使用docker network disconnect命令。

如下命令將my-nginx容器與my-net網絡斷開鏈接。

$ docker  network  disconnect  my-net  my-nginx

 

示例:

docker network inspect  bridge

docker  run  -itd  --name alpine1 alpine

docker  run  -itd  --name alpine2 alpine

docker network inspect  bridge

 

進入兩個容器中分別使用ip地址、容器名、主機名互ping

docker  container  stop  alpine1  alpine2

docker  container  rm  alpine1  alpine2

 

docker  run -dit  --name alpine1  --network my-net  alpine

docker  run -dit  --name alpine2  --network my-net  alpine

docker  run -dit  --name alpine3  alpine

docker  run -dit  --name alpine4  --network my-net  alpine

 

docker  network  connect  bridge  alpine4

 

docker  network  disconnect  my-net  alpine4

 

docker  container  stop  alpine1 alpine2 alpine3 alpine4

docker  container  rm    alpine1 alpine2 alpine3 alpine4

docker  network    rm  my-net

 

啓用從Docker容器轉發到外部網絡

默認狀況下,來自鏈接到默認網橋的容器的流量不會轉發到外部。要啓用轉發,須要更改兩個設置。這些不是Docker命令,它們會影響Docker主機的內核。

  1. 配置Linux內核以容許IP轉發。

$ sysctl  net.ipv4.conf.all.forwarding=1

  1. 將策略的iptables FORWARD策略更改DROPACCEPT

$ sudo iptables  -P FORWARD  ACCEPT

 

默認bridge網絡不建議用於生產用途

配置dockerd的默認網橋

要配置默認bridge網絡,請在daemon.json中指定選項。這是一個daemon.json指定了幾個選項的示例。僅指定須要自定義的設置。

{

  "bip": "192.168.1.5/24",

  "fixed-cidr": "192.168.1.5/25",

  "fixed-cidr-v6": "2001:db8::/64",

  "mtu": 1500,

  "default-gateway": "10.20.1.1",

  "default-gateway-v6": "2001:db8:abcd::89",

  "dns": ["10.20.1.2","10.20.1.3"]

  "ipv6":"true"

}

從新啓動Docker以使更改生效。


容器的資源限制

在啓動容器時可經過docker run的相關選項,進行資源限制

1CPU相關限制選項

--cpu-period int             限制 CPU CFS(徹底公平調度器)的週期,範圍從 1ms~1s,即[1000, 1000000],單位微秒

--cpu-quota int              限制 CPU CFS(徹底公平調度器)配額,必須不小於1ms,即 >= 1000,單位微秒

--cpu-rt-period int         Limit CPU real-time period in microseconds

--cpu-rt-runtime int       Limit CPU real-time runtime in microseconds

-c, --cpu-shares int         用於設置多個容器競爭 CPU 時,各個容器相對分配到的CPU時間比例

--cpus decimal              CPU個數

--cpuset-cpus string       用於設置容器可使用的vCPU核(即親核綁定),即容許使用的CPU集,值能夠爲0-3,0,1

--cpuset-mems string     容許在指定的內存節點(MEMs)上運行容器中的進程,只對NUMA系統有效(0-3, 0,1),不多使用

 

Docker的資源限制和隔離徹底基於 Linux cgroups

CPU資源的限制方式也和cgroups相同。

Docker提供的CPU 源限制選項能夠在多核系統上限制容器能利用哪些vCPU

而對容器最多能使用的CPU時間有兩種限制方式:

一是有多個CPU密集型的容器競爭CPU時,設置各個容器能使用的CPU時間相對比例

二是以絕對的方式設置容器在每一個調度週期內最多能使用的CPU時間

 

CPU

能夠設置容器能夠在哪些CPU核上運行。

示例:

$ docker run -it --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash

表示容器中的進程能夠在 cpu 1 cpu 3 上執行

 

$ docker run -it --cpuset-cpus="0-2" ubuntu:14.04 /bin/bash

表示容器中的進程能夠在 cpu 0cpu 1 cpu 2 上執行

 

NUMA系統上,能夠設置容器可使用的內存節點

內存節點概念參考:

https://blog.csdn.net/gatieme/article/details/52384075

https://www.cnblogs.com/youngerchina/p/5624516.html

示例:

$ docker run -it --cpuset-mems="1,3" ubuntu:14.04 /bin/bash

表示容器中的進程只能使用內存節點13上的內存。

 

$ docker run -it --cpuset-mems="0-2" ubuntu:14.04 /bin/bash

表示容器中的進程只能使用內存節點 012 上的內存。

CPU資源的相對限制

默認狀況下,全部的容器獲得同等比例的CPU週期。

在有多個容器競爭CPU時,能夠設置每一個容器能使用的CPU時間比例。這個比例稱爲共享權值,經過-c--cpu-shares設置。

Docker默認每一個容器的權值爲1024,不設置或將其設置爲0,都將使用這個默認值。

系統會根據每一個容器的共享權值和全部容器共享權值和比例來給容器分配CPU時間。

 

假設有三個正在運行的容器,這三個容器中的任務都是CPU密集型的。

第一個容器的cpu共享權值是1024,其它兩個容器的cpu共享權值是512

第一個容器將獲得50%CPU時間,而其它兩個容器就只能各獲得25%CPU時間了。

 

若是再添加第四個cpu共享值爲1024的容器,每一個容器獲得的CPU時間將從新計算。

第一個容器的CPU時間變爲33%,其它容器分得的 CPU 時間分別爲 16.5%16.5%33%

 

必須注意的是,這個比例只有在CPU密集型的任務執行時纔有用!!!!!!!

在四核的系統上,假設有四個單進程的容器,它們都能各自使用一個核的100% CPU時間,無論它們的cpu共享權值是多少。

 

在多核系統上,CPU時間權值是在全部CPU核上計算的。即便某個容器的CPU時間限制少於 100%,它也能使用各個CPU核的100%時間。

假設有一個不止三核的系統。用-c=512的選項啓動容器{C0},而且該容器只有一個進程,用-c=1024的啓動選項爲啓動容器C2,而且該容器有兩個進程。

CPU 權值的分佈多是這樣的:

 PID    container    CPU CPU share

100    {C0}     0   100% of CPU0

101    {C1}     1   100% of CPU1

102    {C1}     2   100% of CPU2

 

CPU 資源的絕對限制

Linux 經過CFSCompletely Fair Scheduler,徹底公平調度器)來調度各個進程對CPU的使用。CFS默認的調度週期是100ms

 

能夠設置每一個容器進程的調度週期,以及在這個週期內各個容器最多能使用多少CPU時間。

使用--cpu-period便可設置調度週期,使用--cpu-quota便可設置在每一個週期內容器能使用的CPU時間,二者通常配合使用。

例如:

$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash

CFS 調度的週期設爲 50000,將容器在每一個週期內的CPU配額設置爲25000,表示該容器每50ms能夠獲得50%CPU運行時間。

 

$ docker run -it --cpu-period=10000 --cpu-quota=20000 ubuntu:16.04 /bin/bash

將容器的 CPU 配額設置爲 CFS 週期的兩倍,CPU 使用時間怎麼會比周期大呢?

其實很好解釋,給容器分配兩個 vCPU 就能夠了。該配置表示容器能夠在每一個週期內使用兩個 vCPU 100% 時間。

 

CFS 週期的有效範圍是 1ms~1s,對應的--cpu-period的數值範圍是 1000~1000000

而容器的CPU配額必須不小於1ms,即--cpu-quota的值必須 >= 1000,這兩個選項的單位都是us

 

正確的理解「絕對」

注意前面咱們用--cpu-quota設置容器在一個調度週期內能使用的CPU時間時實際上設置的是一個上限,並非說容器必定會使用這麼長的CPU時間。

 

好比:先啓動一個容器,將其綁定到cpu 1上執行。給其--cpu-quota--cpu-period都設置爲 50000

 

$ docker run --rm --name test01 --cpu-cpus 1 --cpu-quota=50000 --cpu-period=50000 deadloop:busybox-1.25.1-glibc

調度週期爲50000,容器在每一個週期內最多能使用50000 cpu 時間。

 

再用docker stats test01能夠觀察到該容器對CPU的使用率在100%左右。

 

而後,再以一樣的參數啓動另外一個容器:

$ docker run --rm --name test02 --cpu-cpus 1 --cpu-quota=50000 --cpu-period=50000 deadloop:busybox-1.25.1-glibc

再用docker stats test01 test02能夠觀察到這兩個容器,每一個容器對 cpu 的使用率在 50% 左右。說明容器並無在每一個週期內使用 50000 cpu 時間。

 

使用docker stop test02命令結束第二個容器,再加一個參數-c 2048啓動它:

 

$ docker run --rm --name test02  --cpu-cpus 1  --cpu-quota=50000  --cpu-period=50000  -c 2048 deadloop:busybox-1.25.1-glibc

再用docker stats test01命令能夠觀察到:

第一個容器的 CPU 使用率在 33% 左右,

第二個容器的 CPU 使用率在 66% 左右。

由於第二個容器的共享權值是2048,第一個容器的默認共享權值是 1024,因此第二個容器在每一個週期內能使用的 CPU 時間是第一個容器的兩倍

 

2、內存相關限制

相關選項:

--kernel-memory bytes          核心內存限制。格式是數值加單位,單位能夠爲 b,k,m,g,最小爲 4M

-m, --memory bytes              內存限制,格式同上,單位能夠爲 b,k,m,g。最小爲 4M

--memory-swap bytes            內存+交換分區大小總限制,格式同上。必須比-m設置的大,'-1'表示表示不限制swap的使用,便可使用宿主機上的最大swap

--memory-reservation bytes          內存的軟性限制。格式同上

--memory-swappiness int             設置容器的虛擬內存控制行爲。值爲0~100之間的整數(默認值-1)

--oom-kill-disable                       是否阻止 OOM killer 殺死容器,默認未設置

--oom-score-adj int                      容器被 OOM killer 殺死的優先級,範圍是[-1000, 1000],默認爲 0

 

用戶內存限制

用戶內存限制就是對容器能使用的內存和交換分區的大小做出限制。

使用時要遵循兩條直觀的規則:

-m--memory選項的參數最小爲 4 M

--memory-swap不是交換分區,而是內存加交換分區的總大小,因此--memory-swap必須比-m,--memory大。

 

在這兩條規則下,通常有四種設置方式:

若是在進行內存限制的實驗時發現docker run命令報錯:

WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.

緣由是宿主機內核的相關功能沒有打開,按照下面的設置就行:

 

step1:編輯/etc/default/grub文件,將GRUB_CMDLINE_LINUX一行改成GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

step 2:更新GRUB,即執行sudo update-grub(Ubuntu系列)指令(紅帽系列使用grub2-mkconfig -o /boot/grub2/grub.cfg)

step 3: 重啓系統。

 

方式一:不設置

若是不設置-m,--memory--memory-swap,容器默承認以用完宿主機機的全部內存和swap 分區。

不過注意,若是容器佔用宿主機的全部內存和 swap 分區超過一段時間後,會被宿主機系統殺死(若是沒有設置--oom-kill-disable=true的話)。

方式二:設置-m,--memory,不設置--memory-swap

-m--memory設置一個不小於 4M 的值,假設爲 a,不設置--memory-swap,或將--memory-swap設置爲 0

這種狀況下,容器能使用的內存大小爲 a,能使用的交換分區大小也爲 a。由於 Docker 默認容器交換分區的大小和內存相同

若是在容器中運行一個一直不停申請內存的程序,會觀察到該程序最終能佔用的內存大小爲 2a

好比:

$ docker run -m 1G ubuntu:16.04

該容器能使用的內存大小爲 1G,能使用的 swap 分區大小也爲 1G

容器內的進程能申請到的總內存大小爲 2G

方式三:設置-m,--memory=a--memory-swap=b,且b > a

-m設置一個參數 a,給--memory-swap設置一個參數 b

a是容器能使用的物理內存大小,b是容器能使用的物理內存大小 + swap 分區大小。

因此 b 必須大於 ab-a 差值即爲容器能使用的 swap 分區大小

好比:

$ docker run -m 1G --memory-swap 3G ubuntu:16.04

該容器能使用的內存大小爲 1G,能使用的 swap 分區大小爲 2G。容器內的進程能申請到的總內存大小爲 3G

方式四:設置-m,--memory=a--memory-swap=-1

-m參數設置一個正常值,而給--memory-swap設置成 -1

這種狀況表示限制容器能使用的內存大小爲a,而不限制容器能使用的swap分區大小

這時候,容器內進程能申請到的內存大小爲  a + 宿主機的swap 大小。

 

Memory reservation

是一種軟性限制,用於節制(節約)容器內存使用。它不保證任什麼時候刻容器使用的內存不會超過--memory-reservation限定的值,它只是確保容器不會長時間佔用超過--memory-reservation限制的內存大小

--memory-reservation設置一個比-m小的值後,雖然容器最多可使用-m使用的內存大小,但在宿主機內存資源緊張時,在系統的下次內存回收時,系統會回收容器的部份內存頁,強迫容器的內存佔用回到--memory-reservation設置的值大小。

 

沒有設置時(默認狀況下)--memory-reservation的值和-m的限定的值相同。

將它設置爲 0 設置的比-m的參數大等同於沒有設置。

示例:

$ docker run -it  -m 500M  --memory-reservation 200M  ubuntu:16.04   /bin/bash

若是容器使用了大於 200M 但小於 500M 內存時,下次系統的內存回收會嘗試將容器的內存鎖緊到 200M 如下

 

$ docker run -it  --memory-reservation 1G  ubuntu:16.04 /bin/bash 

容器可使用盡量多的內存。--memory-reservation確保容器不會長時間佔用太多內存

 

虛擬內存控制行爲

默認狀況下,容器的內核能夠交換出必定比例的匿名頁,--memory-swappiness選項就是用來設置這個比例的,能夠設置值爲從 0 100,默認值-1表示表示不限制。

0 表示關閉匿名頁面交換;100表示全部的匿名頁均可以交換。

默認狀況下,若是不使用--memory-swappiness,則該值從父進程繼承而來。

示例:

$ docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash

--memory-swappiness設置爲 0 能夠保持容器的工做狀態,避免交換代理的性能損失。

 

 

 

核心內存和用戶內存不一樣的地方在於核心內存不能被交換出

不能交換出去的特性使得容器能夠經過消耗太多內存來堵塞一些系統服務。

核心內存包括:

stack pages(棧頁面)

slab pages

socket memory pressure

tcp memory pressure

能夠經過設置核心內存限制來約束這些內存。

例如,每一個進程都要消耗一些棧頁面,經過限制核心內存,能夠在覈心內存使用過多時阻止新進程被建立

核心內存和用戶內存並非獨立的,必須在用戶內存限制的上下文中限制核心內存!!!!!!

 

假設用戶內存的限制值爲U,核心內存的限制值爲K,有三種可能限制核心內存的方式:

1U != 0,不限制核心內存。這是默認的標準設置方式

2K < U,核心內存是用戶內存的子集。這種設置在部署時,每一個 cgroup 的內存總量被過分使用。

過分使用核心內存限制是毫不推薦的,由於系統仍是會用完不能回收的內存。

在這種狀況下,能夠設置 K,這樣 groups 的總數就不會超過總內存了。而後,根據系統服務的質量自由地設置 U

3K > U,由於核心內存的變化也會致使用戶計數器的變化,容器核心內存和用戶內存都會觸發回收行爲。

這種配置可讓管理員以一種統一的視圖看待內存,對想跟蹤核心內存使用狀況的用戶也是有用的。

 

示例:

$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash

容器中的進程最多能使用 500M 內存,在這 500M 中,最多隻有 50M 核心內存。

 

$ docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash

沒用設置用戶內存限制,因此容器中的進程可使用盡量多的內存,可是最多能使用 50M 核心內存。

 

 

默認狀況下,在出現 out-of-memory(OOM) 錯誤時,系統會殺死容器內的進程來獲取更多空閒內存。這個殺死進程來節省內存的進程,稱爲OOM killer

能夠經過設置--oom-kill-disable選項來禁止 OOM killer 殺死容器內進程。

必須確保只有在使用了-m/--memory選項時才使用--oom-kill-disable禁用OOM killer

若是沒有設置-m選項,卻禁用了 OOM-killer,可能會形成出現 out-of-memory 錯誤時,系統經過殺死宿主機進程或獲取更多內存。

下面的例子限制了容器的內存爲 100M 並禁止了 OOM killer

$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash                 #是正確的使用方法。

 

而下面這個容器沒設置內存限制,卻禁用了 OOM killer 是很是危險的:

$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash

容器沒用內存限制,可能會致使系統無內存可用,並嘗試時殺死系統進程來獲取更多可用內存。

 

通常一個容器只有一個進程,這個惟一進程被殺死,容器也就被殺死了。

能夠經過--oom-score-adj選項來設置在系統內存不夠時,容器被殺死的優先級。負值更不可能被殺死,而正值更有可能被殺死。

 

 

3、限制磁盤空間

相關選項

--storage-opt list                                 容器的存儲驅動選項

默認每一個容器有10GB的空間,有時候它太大了,有時候過小,不能知足全部的數據放在這裏,容器一旦被建立就不能改變。

惟一能作的事情就是改變新容器的默認值,

若是一些其餘的值(好比5GB)更適合本身的狀況,能夠經過指定docker run --storage-opt來實現(另外Docker daemondocker守護程序也有該選項,對全部容器生效),像這樣:

$ docker run --storage-opt dm.basesize=5G

此(size)將容許在建立時將容器的rootfs大小設置爲5G。此選項僅適用於devicemapperbtrfsoverlay2windowsfilterzfs存儲驅動。對於devicemapperbtrfswindowsfilterzfs存儲驅動,用戶沒法傳遞小於Default BaseFS Size的大小(即10G)。對於overlay2存儲驅動,size選項僅在文件系統爲xfs並使用pquota選項掛載時可用,在這種條件下,用戶能夠傳遞任何大小。

相關文章
相關標籤/搜索