Docker提供了兩個版本:社區版(CE)和企業版(EE)。html
Docker社區版(CE)是開發人員和小型團隊開始使用Docker並嘗試使用基於容器的應用的理想之選。Docker CE有兩個更新渠道,即stable和edge:python
Docker企業版(EE)專爲負責在生產環境中大規模構建、交付和運行業務關鍵型應用程序的企業開發和 IT 團隊設計mysql
docker-ce安裝linux
配置yum源nginx
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 localinstall或rpm -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鏈接方式
Docker爲C/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=client,client爲客戶端主機的主機名或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個指令
1、FROM #必要指令,一般是第一條(引用變量及多階段構建例外),可有多條(多階段構建)
FROM <image> [AS <name>]
或
FROM <image>[:<tag>] [AS <name>]
或
FROM <image>[@<digest>] [AS <name>]
scratch鏡像:
FROM scratch
ADD hello /
CMD ["/hello"]
說明:
1、可經過添加AS name來爲該構建階段命名。該名稱可用於後續FROM和COPY --from=<name|index>指令,以引用此階段構建的鏡像。
2、tag或digest值是可選的。若是省略其中任何一個,則構建器默認採用latest標記
3、FROM指令支持變量,變量在第一個FROM指令以前使用ARG聲明
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
2、MAINTAINER 鏡像維護者信息
MAINTAINER username@domain.com
說明:指定該鏡像維護者信息,儘可能使用LABEL替代
docker inspect <Container ID> |grep -A 6 Labels
3、RUN 執行命令
用來執行命令,有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選項
4、CMD 爲執行容器提供默認值 #只能有一條,若是列出多個只有最後一條生效
有三種形式:
說明:
1)exec形式不支持變量擴展,shell形式支持
2)exec形式被解析爲JSON數組,這意味着必須使用雙引號(")來包括單詞而不是單引號(')
3)在JSON形式中,必須轉義反斜槓\(windows中常常遇到)
注意:不要混淆RUN和CMD。RUN實際上運行一條命令並提交結果,CMD在構建過程當中不執行任何操做,而是做爲鏡像的默認指令(即便用該鏡像啓動容器時默認執行的指令)。
5、LABEL 將元數據添加到鏡像
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"
6、EXPOSE 容器運行時偵聽其服務端口
EXPOSE <port1> [<port2>/<protocol>...] #多個端口之間使用空格間隔
EXPOSE指令通知Docker容器在運行時偵聽指定的網絡端口。
能夠指定端口是偵聽TCP仍是UDP,若是未指定協議,則默認爲TCP
7、ENV 設置環境變量
有兩種形式:
形式一:
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>在構建容器時更改變量的值。
2、Dockerfile中可以擴展變量的指令:
FROM/ADD/COPY/ENV/EXPOSE/LABEL/STOPSIGNAL/USER/VOLUME/WORKDIR/ONBUILD
8、ARG #設置構建過程當中的環境變量
格式:
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
4、Dockerfile能夠包括一個或多個ARG指令。
例如,如下是有效的Dockerfile:
FROM busybox
ARG user1
ARG buildno
...
警告:
不要使用構建時變量來傳遞密碼,如github密鑰,用戶憑據等,構建時變量值對於任何用戶都是可見的
5、ARG生效範圍
ARG變量定義從Dockerfile中定義的行開始生效,直到其所在構建階段結束
示例:
FROM busybox
USER ${user:-some_user}
ARG user
USER $user
...
經過如下指令構建:
$ docker build --build-arg user=what_user .
第2行USER使用some_user做爲user變量的值。
第4行USER使用what_user做爲user變量的值,且what_user在命令行上傳遞的值。
在經過ARG指令定義以前,對變量的任何使用都會致使空字符串
6、使用ARG變量
可使用ARG或ENV指令指定在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,
9、COPY 複製上下文中的文件或目錄到容器中
COPY有兩種形式:
--chown功能僅在用於構建Linux容器
注意:
1、源路徑,必定是相對於build指令中指定的上下文的相對路徑
如COPY package.json /usr/src/app/
2、目標路徑,能夠是容器內的絕對路徑,也能夠是相對於工做目錄的相對路徑(工做目錄可使用WORKDIR指令來指定)
3、目標路徑不須要事先建立,若是目錄不存在,在複製文件以前會自動建立
4、使用COPY指令時,源文件的各類元數據都會被保留,如讀寫執行權限,文件的相關時間屬性等
5、源路徑能夠是多個,甚至能夠含有通配符,通配符要符合Go的filepath.Match規則:
COPY hom* /mydir
COPY hom?.txt /mydir
6、若是使用--chown選項,最好使用數字形式的GID和UID(若是使用用戶名和組名,在容器文件系統中不存在/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引用,若是先前構建階段是第一階段,index爲0,以此類推
若是找不到具備指定名稱的構建階段,則嘗試使用具備相同名稱的鏡像
COPY遵照如下規則:
10、ADD 複製本地文件到上下文中
從<src>中複製文件、目錄或遠程文件URL,並將它們添加到路徑上鏡像的文件系統<dest>中
ADD有兩種形式:
ADD指令的全部使用注意事項及遵照規則與COPY相同
與COPY不一樣的是:
源路徑:
1、能夠是URL,Docker引擎會嘗試下載這個連接文件,放入到<dest>中,下載後文件的權限爲600,如果想更改文件的權限,還須要增長一層RUN指令
2、若是下載的是壓縮包,還須要增長一層RUN指令進行解壓
3、若是<src>是一個上下文中的tar壓縮文件(gzip、bzip2、xz),ADD指令會自動解壓放入到<dest>中,若是不但願壓縮文件解壓,則使用COPY(這是ADD指令最大的用處)
4、僅在須要自動解壓時,纔會選擇使用ADD指令,不然複製文件儘量使用COPY
5、ADD指令會令鏡像構建緩存失效,從而可能使鏡像構建過程很是緩慢(所以多階段構建時,儘量不要使用)
11、ENTRYPOINT 入口點 #只能有一條,如有多條僅最後一條生效
ENTRYPOINT有兩種形式:
exec形式:
docker run <image>的命令行參數將附加在exec形式的ENTRYPOINT的全部元素以後,做爲ENTRYPOINT指令的參數,並將覆蓋全部使用CMD指定的元素。
可使用docker run --entrypoint標誌覆蓋exec形式的ENTRYPOINT指令
shell形式:
shell形式防止任何被CMD或run使用的命令行參數,缺點是ENTRYPOINT將被做爲/bin/sh -c的一個子命令,且不傳遞信號。這意味着可執行文件將不是容器中PID爲1的進程,而且不會收到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 "$@"
配置應用爲容器中PID爲1進程
此腳本使用的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"]
注意:
1、exec形式被解析爲JSON數組,這意味着必須使用雙引號(")來引用而不是單引號(')。
2、與shell形式不一樣,exec形式不會調用命令shell,所以不能擴展變量。例如,ENTRYPOINT [ "echo", "$HOME" ]不會對變量進行替換$HOME。
3、若是想要shell處理,那麼要麼使用shell形式,要麼直接執行shell,例如:ENTRYPOINT [ "sh", "-c", "echo $HOME" ]
Shell形式的ENTRYPOINT示例
能夠爲ENTRYPOINT指定一個純字符串指令,將在/bin/sh -c中執行。該形式支持環境變量擴展,並將忽略任何CMD或docker 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)
CMD和ENTRYPOINT如何相互做用:
下表顯示了針對不一樣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 |
12、VOLUME
格式:
VOLUME ["/data"]
VOLUME ["/var/log/","/data "]
VOLUME /var/log
VOLUME /var/log /var/db
VOLUME指令建立具備指定名稱的掛載點,並將其標記爲從本機或其餘容器保存外部掛載的卷。該值能夠是JSON數組,VOLUME ["/var/log/"]或具備多個參數的普通字符串,如VOLUME /var/log或VOLUME /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參數,必須在建立或運行容器時指定掛載點
13、USER
USER <user>[:<group>] 或
USER <UID>[:<GID>]
當構建鏡像時,在Dockerfile文件中有RUN、CMD、ENTRYPOINT指令時,USER指令用於設置執行這些指令的用戶(UID),及可選的用戶組(GID),用戶或組必須事先存在
警告:當用戶沒有主組時,鏡像(或下一個指令)將使用root組運行
14、WORKDIR
WORKDIR /path/to/workdir
WORKDIR指令用於在Dockerfile中設置RUN,CMD,ENTRYPOINT,COPY和ADD指令的工做目錄。若是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
15、ONBUILD
ONBUILD [INSTRUCTION]
當鏡像用做另外一個構建的基礎時,該ONBUILD指令向鏡像添加將在稍後執行的觸發指令。觸發器將在下游構建的上下文中執行,就好像它在下游Dockerfile中FROM指令以後當即插入同樣。
任何構建指令均可以註冊爲觸發器
16、HEALTHCHECK #只能有一條,若是列出多個,則只有最後一個生效
該HEALTHCHECK指令有兩種形式:
當容器指定了運行情況檢查時,除了正常狀態外,它還具備運行情況:
這個狀態最初是starting
每當健康檢查經過時,它就會變成healthy
通過必定數量的連續失敗後,它就變成了unhealthy
OPTIONS:
CMD關鍵字後面的命令能夠是shell命令(如HEALTHCHECK CMD /bin/check-running)或exec形式
命令的退出狀態指示容器的運行情況。可能的值是:
示例:
要檢查每五分鐘左右網絡服務器可以在三秒鐘內爲網站的主頁面提供服務:
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
17、SHELL
SHELL ["executable", "parameters"]
該指令容許覆蓋用於shell形式的命令的默認shell。
Linux上默認shell是["/bin/sh", "-c"]
Windows上默認shell是["cmd", "/S", "/C"]。該SHELL指令必須以JSON格式寫入Dockerfile
Windows上常用
18、STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL指令設置將發送到容器的系統調用信號以退出。此信號能夠是與內核的系統調用表中的位置匹配的有效無符號數,例如9,或SIGNAME格式的信號名,例如SIGKILL
默認的stop signal是SIGTERM,在docker stop的時候會給容器內PID爲1的進程發送這個signal,經過--stop-signal能夠設置本身須要的signal,主要的目的是爲了讓容器內的應用程序在接收到signal以後能夠先作一些事情,實現容器的平滑退出,若是不作任何處理,容器將在一段時間以後強制退出,會形成業務的強制中斷,這個時間默認是10s
Dockerfile說明
Docker鏡像由只讀層組成,每一個層都表明一個Dockerfile指令。這些層是堆疊的,每一層都是前一層變化的增量。
Dockerfile:
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
每條指令建立一個層:
運行鏡像並生成容器時,能夠在基礎層的頂部添加新的可寫層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 .
移動Dockerfile和hello文件到單獨的目錄
構建映像的第二個版本(不依賴於上一個版本的緩存)
使用-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使用本地或遠程構建上下文進行管道來構建映像的功能。
在早期版本中,使用來自stdin的Dockerfile構建映像,不支持發送構建上下文
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文件
注意:這些文件仍然發送到守護程序,由於它須要它們來完成它的工做。可是ADD和COPY指令不會將它們複製到鏡像中
使用多階段構建:
多階段構建(在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 .
一些可能的場景:
使用外部鏡像做爲「stage」
使用多階段構建時,不只能夠從先前在Dockerfile中的構建階段進行復制。還可使用該COPY --from指令從單獨的鏡像進行復制,使用本地鏡像名稱,本地或Docker倉庫中可用的標記或標記ID。
若有必要,Docker客戶端會提取鏡像,並從鏡像中複製工件。語法是:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
管理鏡像
使本身的鏡像可供組織內部或外部的其餘人使用的最簡單方法是使用Docker registry,例如Docker Hub,Docker Trusted Registry,或運行本身的私有註冊服務器。
Docker Hub
註冊及使用(演示使用過程)
Docker註冊服務器
Docker Registry是Docker生態系統的一個組件。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 爲容器中的目錄,將會被掛載到宿主機
src和dst均不須要事先建立
src和dst也但是文件
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的網絡子系統是可插拔的,使用驅動程序。默認狀況下存在多個驅動程序,並提供核心網絡功能:
1、在同一個Docker宿主機上多個容器進行通訊時,用戶定義的橋接網絡(即自定義bridge)是最佳選擇
2、不一樣Docker宿主機上運行的容器進行通訊時,或者當多個應用程序使用swarm服務協同工做時,overlay網絡是最佳選擇
3、從VM設置遷移或須要容器看起來像網絡上的物理主機(每一個都具備惟一的MAC地址)時,Macvlan網絡是最佳選擇
bridge網絡
橋接網絡適用於在同一個Docker守護程序宿主機上運行的容器之間通訊。
對於在不一樣Docker守護程序宿主機上運行的容器之間的通訊,能夠在操做系統級別管理路由,也可使用overlay網絡
啓動Docker時,會自動建立默認橋接網絡(也稱爲bridge),而且除非另行指定,不然新啓動的容器將鏈接到該橋接網絡。還能夠建立用戶自定義網橋,用戶定義的網橋優於默認bridge網橋
用戶定義的網橋與默認網橋之間的差別:
鏈接到同一個用戶自定義的網橋的容器會自動將全部端口相互暴露,而不會向外界顯示任何端口
默認網橋上的容器只能經過IP地址相互訪問,除非使用--link選項。在用戶定義的橋接網絡上,容器能夠經過主機名相互解析。
在容器的生命週期中,能夠動態地將其與用戶自定義的網絡鏈接或斷開。而從默認橋接網絡中刪除容器,則須要中止容器並使用不一樣的網絡選項從新建立容器
若是容器使用默認橋接網絡,則能夠對其進行配置,但全部容器都使用相同的設置,如MTU和iptables規則。此外,配置默認橋接網絡發生在Docker服務以外,而且須要從新啓動Docker。
最初,在兩個容器之間共享環境變量的惟一方法是使用--link選項連接,用戶定義的網絡沒法實現這種類型的變量共享。但有更好的方法來共享環境變量。
一些想法:
使用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使用特定於操做系統的工具來管理底層網絡基礎結構(例如iptables在Linux上添加或刪除網橋設備或配置規則)
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主機的內核。
$ sysctl net.ipv4.conf.all.forwarding=1
$ 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的相關選項,進行資源限制
1、CPU相關限制選項
--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 0、cpu 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
表示容器中的進程只能使用內存節點1和3上的內存。
$ docker run -it --cpuset-mems="0-2" ubuntu:14.04 /bin/bash
表示容器中的進程只能使用內存節點 0、1、2 上的內存。
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 經過CFS(Completely 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 必須大於 a,b-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,有三種可能限制核心內存的方式:
1)U != 0,不限制核心內存。這是默認的標準設置方式
2)K < U,核心內存是用戶內存的子集。這種設置在部署時,每一個 cgroup 的內存總量被過分使用。
過分使用核心內存限制是毫不推薦的,由於系統仍是會用完不能回收的內存。
在這種狀況下,能夠設置 K,這樣 groups 的總數就不會超過總內存了。而後,根據系統服務的質量自由地設置 U。
3)K > 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 daemon即docker守護程序也有該選項,對全部容器生效),像這樣:
$ docker run --storage-opt dm.basesize=5G
此(size)將容許在建立時將容器的rootfs大小設置爲5G。此選項僅適用於devicemapper,btrfs,overlay2,windowsfilter和zfs存儲驅動。對於devicemapper,btrfs,windowsfilter和zfs存儲驅動,用戶沒法傳遞小於Default BaseFS Size的大小(即10G)。對於overlay2存儲驅動,size選項僅在文件系統爲xfs並使用pquota選項掛載時可用,在這種條件下,用戶能夠傳遞任何大小。