一、文檔簡介
作者:lanjiaxuan
郵箱:lanheader@163.com
博客地址:https://www.cnblogs.com/lanheader/
更新時間:2021-07-09
Docker 簡介
Docker 是一種運行於 Linux 和 Windows 上的軟件,用於創建、管理和編排容器。
Docker的基本組成
-
Docker Client客戶端
Docker是C/S架構的程序,docker的客戶端向服務器端(也就是Docker的守護進程)發出請求,守護進程處理完所有的工作並返回結果;docker客戶端向服務器端的訪問既可以在本地也可以通過遠程來訪問;
-
Docker Daemon 守護進程
-
Docker Image 鏡像
鏡像是docker容器的基石,容器基於鏡像啟動和運行,鏡像好比容器的源代碼,保存了啟動容器的各種條件
-
Docker Container 容器
容器通過鏡像來啟動,docker的容器是docker執行單元,容器中可以運行客戶的一個或者多個進程,如果說鏡像是docker生命周期中的構建和打包階段,那么容器就是啟動和執行階段;
-
Docker Registry 倉庫
docker用倉庫來保存用戶構建的對象,分為公有和私有,Docker公司自己提供了一個公有的,叫Docker Hub
Docker 安裝
linux 安裝
腳本安裝
$ wget -qO- https://get.docker.com/ | sh
yum安裝
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum list docker-ce --showduplicates | sort -r
$ sudo yum install docker-ce
Docker命令
Docker容器生命周期管理
docker run :創建一個新的容器並運行一個命令
語法
$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS說明:
-a stdin: 指定標准輸入輸出內容類型,可選 STDIN/STDOUT/STDERR 三項;
- -d: 后台運行容器,並返回容器ID;
- -i: 以交互模式運行容器,通常與 -t 同時使用;
- -P: 隨機端口映射,容器內部端口隨機映射到主機的端口
- -p: 指定端口映射,格式為:主機(宿主)端口:容器端口
- -t: 為容器重新分配一個偽輸入終端,通常與 -i 同時使用;
- --name="nginx-lb": 為容器指定一個名稱;
- --dns 8.8.8.8: 指定容器使用的DNS服務器,默認和宿主一致;
- --dns-search example.com: 指定容器DNS搜索域名,默認和宿主一致;
- -h "mars": 指定容器的hostname;
- -e username="ritchie": 設置環境變量;
- --env-file=[]: 從指定文件讀入環境變量;
- --cpuset="0-2" or --cpuset="0,1,2": 綁定容器到指定CPU運行;
- -m :設置容器使用內存最大值;
- --net="bridge": 指定容器的網絡連接類型,支持 bridge/host/none/container: 四種類型;
- --link=[]: 添加鏈接到另一個容器;
- --expose=[]: 開放一個端口或一組端口;
- --volume , -v: 綁定一個卷
示例
# 使用Docker鏡像nginx:latest以后台模式啟動一個容器,並將容器命名為mynginx。
$ docker run --name mynginx -d nginx:latest
# 使用鏡像nginx:latest以后台模式啟動一個容器,並將容器的80端口映射到主機隨機端口。(這里是P是大寫)
$ docker run -P -d nginx:latest
# 使用鏡像 nginx:latest,以后台模式啟動一個容器,將容器的 80 端口映射到主機的 80 端口,主機的目錄 /data 映射到容器的 /data。
$ docker run -p 80:80 -v /data/docker_data:/data -d nginx:latest
# 綁定容器的 8080 端口,並將其映射到本地主機 127.0.0.1 的 80 端口上。
$ docker run -p 127.0.0.1:80:8080/tcp ubuntu bash
# 使用鏡像nginx:latest以交互模式啟動一個容器,在容器內執行/bin/bash命令。
$ docker run -it nginx:latest /bin/bash
docker start/stop/restart
docker start :啟動一個或多個已經被停止的容器
docker stop :停止一個運行中的容器
docker restart :重啟容器
語法
$ docker start [OPTIONS] CONTAINER [CONTAINER...]
$ docker stop [OPTIONS] CONTAINER [CONTAINER...]
$ docker restart [OPTIONS] CONTAINER [CONTAINER...]
示例
# 啟動已被停止的容器
$ docker start mynginx
# 停止運行中的容器myrunoob
$ docker stop mynginx
# 重啟容器myrunoob
$ docker restart mynginx
docker kill :殺掉一個運行中的容器。
語法
$ docker kill [OPTIONS] CONTAINER [CONTAINER...]
OPTIONS說明:
- -s :向容器發送一個信號
示例
# 殺掉運行中的容器mynginx
$ docker kill -s KILL mynginx
mynginx
docker rm :刪除一個或多個容器。
語法
$ docker rm [OPTIONS] CONTAINER [CONTAINER...]
OPTIONS說明:
- -f :通過 SIGKILL 信號強制刪除一個運行中的容器。
- -l :移除容器間的網絡連接,而非容器本身。
- -v :刪除與容器關聯的卷。
示例
# 強制刪除容器 mynginx:
$ docker rm -f mynginx nginxdemo
# 刪除容器 nginx01, 並刪除容器掛載的數據卷:
$ docker rm -v nginx01
# 刪除所有已經停止的容器:
$ docker rm $(docker ps -a -q)
docker pause/unpause :
docker pause : 暫停容器中所有的進程。
docker unpause :恢復容器中所有的進程。
語法
$ docker pause CONTAINER [CONTAINER...]
$ docker unpause CONTAINER [CONTAINER...]
示例
# 暫停數據庫容器db01提供服務。
$ docker pause mynginx
# 恢復數據庫容器 db01 提供服務。
$ docker unpause mynginx
docker create :創建一個新的容器但不啟動它
用法同 docker run
語法
$ docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
語法同 docker run
示例
# 使用docker鏡像nginx:latest創建一個容器,並將容器命名為myrunoob
$ docker create --name nginxdemo3 nginx:latest
docker exec :在運行的容器中執行命令
語法
$ docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
OPTIONS說明:
- -d :分離模式: 在后台運行
- -i :即使沒有附加也保持STDIN 打開
- -t :分配一個偽終端
示例
# 在容器 mynginx 中開啟一個交互模式的終端:
$ docker exec -i -t mynginx /bin/bash
容器操作
docker ps : 列出容器
語法
$ docker ps [OPTIONS]
OPTIONS說明:
-
-a :顯示所有的容器,包括未運行的。
-
-f :根據條件過濾顯示的內容。
-
--format :指定返回值的模板文件。
-
-l :顯示最近創建的容器。
-
-n :列出最近創建的n個容器。
-
--no-trunc :不截斷輸出。
-
-q :靜默模式,只顯示容器編號。
-
-s :顯示總的文件大小。
示例
列出所有在運行的容器信息。
$ docker ps
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
09b93464c2f7 nginx:latest "nginx -g 'daemon off" ... 80/tcp, 443/tcp myrunoob
96f7f14e99ab mysql:5.6 "docker-entrypoint.sh" ... 0.0.0.0:3306->3306/tcp mymysql
輸出詳情介紹:
CONTAINER ID: 容器 ID。
IMAGE: 使用的鏡像。
COMMAND: 啟動容器時運行的命令。
CREATED: 容器的創建時間。
STATUS: 容器狀態。
狀態有7種:
- created(已創建)
- restarting(重啟中)
- running(運行中)
- removing(遷移中)
- paused(暫停)
- exited(停止)
- dead(死亡)
PORTS: 容器的端口信息和使用的連接類型(tcp\udp)。
NAMES: 自動分配的容器名稱。
示例
# 列出最近創建的5個容器信息。
$ docker ps -n 5
CONTAINER ID IMAGE COMMAND CREATED
09b93464c2f7 nginx:latest "nginx -g 'daemon off" 2 days ago ...
b8573233d675 nginx:latest "/bin/bash" 2 days ago ...
b1a0703e41e7 nginx:latest "nginx -g 'daemon off" 2 days ago ...
f46fb1dec520 5c6e1090e771 "/bin/sh -c 'set -x \t" 2 days ago ...
a63b4a5597de 860c279d2fec "bash" 2 days ago ...
# 列出所有創建的容器ID。
$ docker ps -a -q
09b93464c2f7
b8573233d675
b1a0703e41e7
f46fb1dec520
a63b4a5597de
6a4aa42e947b
de7bb36e7968
43a432b73776
664a8ab1a585
ba52eb632bbd
...
docker inspect : 獲取容器/鏡像的元數據。
語法
$ docker inspect [OPTIONS] NAME|ID [NAME|ID...]
OPTIONS說明:
- -f :指定返回值的模板文件。
- -s :顯示總的文件大小。
- --type :為指定類型返回JSON。
示例
獲取鏡像mysql:5.6的元信息。
$ docker inspect mysql:5.6
[
{
"Id": "sha256:2c0964ec182ae9a045f866bbc2553087f6e42bfc16074a74fb820af235f070ec",
"RepoTags": [
"mysql:5.6"
],
"RepoDigests": [],
"Parent": "",
"Comment": "",
"Created": "2016-05-24T04:01:41.168371815Z",
"Container": "e0924bc460ff97787f34610115e9363e6363b30b8efa406e28eb495ab199ca54",
"ContainerConfig": {
"Hostname": "b0cf605c7757",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"3306/tcp": {}
},
...
獲取正在運行的容器mymysql的 IP。
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mymysql172.17.0.3
docker top :查看容器中運行的進程信息,支持 ps 命令參數。
語法
$ docker top [OPTIONS] CONTAINER [ps OPTIONS]
容器運行時不一定有/bin/bash終端來交互執行top命令,而且容器還不一定有top命令,可以使用docker top來實現查看container中正在運行的進程。
示例
# 查看容器mymysql的進程信息。$ docker top mymysqlUID PID PPID C STIME TTY TIME CMD999 40347 40331 18 00:58 ? 00:00:02 mysqld# 查看所有運行容器的進程信息。$ for i in `docker ps |grep Up|awk '{print $1}'`;do echo \ &&docker top $i; done
docker attach :連接到正在運行中的容器。
語法
$ docker attach [OPTIONS] CONTAINER
要attach上去的容器必須正在運行,可以同時連接上同一個container來共享屏幕(與screen命令的attach類似)。
官方文檔中說attach后可以通過CTRL-C來detach,但實際上經過我的測試,如果container當前在運行bash,CTRL-C自然是當前行的輸入,沒有退出;如果container當前正在前台運行進程,如輸出nginx的access.log日志,CTRL-C不僅會導致退出容器,而且還stop了。這不是我們想要的,detach的意思按理應該是脫離容器終端,但容器依然運行。好在attach是可以帶上--sig-proxy=false來確保CTRL-D或CTRL-C不會關閉容器。
示例
容器mynginx將訪問日志指到標准輸出,連接到容器查看訪問信息。
$ docker attach --sig-proxy=false mynginx192.168.239.1 - - [10/Jul/2016:16:54:26 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36" "-"
docker events : 從服務器獲取實時事件
語法
$ docker events [OPTIONS]
OPTIONS說明:
- -f :根據條件過濾事件;
- --since :從指定的時間戳后顯示所有事件;
- --until :流水時間顯示到指定的時間為止;
示例
# 顯示docker 2021年3月20日后的所有事件。$ docker events --since="1616233483"# 顯示docker 2021年3月20日后的所有事件.$ docker events -f "image"="mysql" --since="1616233483"
如果指定的時間是到秒級的,需要將時間轉成時間戳。如果時間為日期的話,可以直接使用,如--since="2021-03-20"。
docker logs : 獲取容器的日志
語法
$ docker logs [OPTIONS] CONTAINER
OPTIONS說明:
- -f : 跟蹤日志輸出
- --since :顯示某個開始時間的所有日志
- -t : 顯示時間戳
- --tail :僅列出最新N條容器日志
示例
# 跟蹤查看容器mynginx的日志輸出。$ docker logs -f mynginx192.168.239.1 - - [10/Jul/2016:16:53:33 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36" "-"2016/07/10 16:53:33 [error] 5#5: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 192.168.239.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.239.130", referrer: "http://192.168.239.130/"192.168.239.1 - - [10/Jul/2016:16:53:33 +0000] "GET /favicon.ico HTTP/1.1" 404 571 "http://192.168.239.130/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36" "-"192.168.239.1 - - [10/Jul/2016:16:53:59 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36" "-"...
查看容器mynginx從2021-03-20后的最新10條日志。
$ docker logs --since="2021-03-20" --tail=10 mynginx
docker wait : 阻塞運行直到容器停止,然后打印出它的退出代碼。
語法
$ docker wait [OPTIONS] CONTAINER [CONTAINER...]
示例
$ docker wait CONTAINER
docker export :將文件系統作為一個tar歸檔文件導出到STDOUT。
語法
$ docker export [OPTIONS] CONTAINER
OPTIONS說明:
- -o :將輸入內容寫到文件。
示例
# 將id為46162045d607的容器按日期保存為tar文件。$ docker export -o mysql-`date +%Y%m%d`.tar 46162045d607$ ls mysql-`date +%Y%m%d`.tarmysql-20210320.tar
docker port :列出指定的容器的端口映射,或者查找將PRIVATE_PORT NAT到面向公眾的端口。
語法
$ docker port [OPTIONS] CONTAINER [PRIVATE_PORT[/PROTO]]
示例
# 查看容器mynginx的端口映射情況。$ docker port mymysql3306/tcp -> 0.0.0.0:3306
容器rootfs命令
docker commit :從容器創建一個新的鏡像。
語法
$ docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
OPTIONS說明:
-
-a :提交的鏡像作者;
-
-c :使用Dockerfile指令來創建鏡像;
-
-m :提交時的說明文字;
-
-p :在commit時,將容器暫停。
示例
# 將容器46162045d607 保存為新的鏡像,並添加提交人信息和說明信息。$ docker commit -a "e6gps" -m "my apache" 46162045d607 mymysql:v1 sha256:37af1236adef1544e8886be23010b66577647a40bc02c0885a6600b33ee28057$ docker images mymysql:v1REPOSITORY TAG IMAGE ID CREATED SIZEmymysql v1 37af1236adef 15 seconds ago 329 MB
docker cp :用於容器與主機之間的數據拷貝。
語法
$ docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-$ docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
OPTIONS說明:
- -L :保持源目標中的鏈接
示例
# 將主機/www/e6目錄拷貝到容器96f7f14e99ab的/www目錄下。$ docker cp /www/e6 96f7f14e99ab:/etc/nginx/conf.d# 將主機/www/e6目錄拷貝到容器96f7f14e99ab中,目錄重命名為www。$ docker cp /www/e6 96f7f14e99ab:/www# 將容器96f7f14e99ab的/www目錄拷貝到主機的/tmp目錄中。$ docker cp 96f7f14e99ab:/www /tmp/
docker diff : 檢查容器里文件結構的更改。
語法
$ docker diff [OPTIONS] CONTAINER
示例
# 查看容器mymysql的文件結構更改。$ docker diff mymysqlA /logsA /mysql_dataC /runC /run/mysqldA /run/mysqld/mysqld.pidA /run/mysqld/mysqld.sockC /tmp
鏡像倉庫
docker login/logout
docker login : 登陸到一個Docker鏡像倉庫,如果未指定鏡像倉庫地址,默認為官方倉庫 Docker Hub
docker logout : 登出一個Docker鏡像倉庫,如果未指定鏡像倉庫地址,默認為官方倉庫 Docker Hub
語法
$ docker login [OPTIONS] [SERVER]$ docker logout [OPTIONS] [SERVER]
OPTIONS說明:
- -u :登陸的用戶名
- -p :登陸的密碼
示例
# 登陸到阿里雲$ docker login --username=lanjiaxuan@e6yun registry.cn-shenzhen.aliyuncs.com# 登出# 登出Docker Hub$ docker logout
docker pull : 從鏡像倉庫中拉取或者更新指定鏡像
語法
$ docker pull [OPTIONS] NAME[:TAG|@DIGEST]
OPTIONS說明:
-
-a :拉取所有 tagged 鏡像
-
--disable-content-trust :忽略鏡像的校驗,默認開啟
示例
# 從Docker Hub下載java最新版鏡像。$ docker pull registry.cn-shenzhen.aliyuncs.com/base_e6yw/nginxdemo:v1# 從Docker Hub下載REPOSITORY為java的所有鏡像。$ docker pull -a java
docker push : 將本地的鏡像上傳到鏡像倉庫,要先登陸到鏡像倉庫
語法
$ docker push [OPTIONS] NAME[:TAG]
OPTIONS說明:
- --disable-content-trust :忽略鏡像的校驗,默認開啟
示例
# 上傳本地鏡像myapache:v1到鏡像倉庫中。$ docker tag 159d74e6cbce registry.cn-shenzhen.aliyuncs.com/base_e6yw/mysqldemo:v1$ docker push registry.cn-shenzhen.aliyuncs.com/base_e6yw/mysqldemo:v1
docker search : 從Docker Hub查找鏡像
語法
$ docker search [OPTIONS] TERM
OPTIONS說明:
- --automated :只列出 automated build類型的鏡像;
- --no-trunc :顯示完整的鏡像描述;
- -f <過濾條件>:列出收藏數不小於指定值的鏡像。
示例
# 從 Docker Hub 查找所有鏡像名包含 java,並且收藏數大於 10 的鏡像$ docker search -f stars=10 javaNAME DESCRIPTION STARS OFFICIAL AUTOMATEDnode Node.js is a JavaScript-based platform for s… 9857 [OK] tomcat Apache Tomcat is an open source implementati… 2980 [OK] openjdk OpenJDK is an open-source implementation of … 2664 [OK] java Java is a concurrent, class-based, and objec… 1976 [OK] ghost Ghost is a free and open source blogging pla… 1338 [OK] couchdb CouchDB is a database that uses JSON for doc… 393 [OK] jetty Jetty provides a Web server and javax.servle… 356 [OK] groovy Apache Groovy is a multi-faceted language fo… 105 [OK] lwieske/java-8 Oracle Java 8 Container - Full + Slim - Base… 49 [OK]nimmis/java-centos This is docker images of CentOS 7 with diffe… 42 [OK]fabric8/java-jboss-openjdk8-jdk Fabric8 Java Base Image (JBoss, OpenJDK 8) 29 [OK]cloudbees/java-build-tools Docker image with commonly used tools to bui… 15 [OK]frekele/java docker run --rm --name java frekele/java 12 [OK]
參數說明:
NAME: 鏡像倉庫源的名稱
DESCRIPTION: 鏡像的描述
OFFICIAL: 是否 docker 官方發布
stars: 類似 Github 里面的 star,表示點贊、喜歡的意思。
AUTOMATED: 自動構建。
本地鏡像管理
docker images : 列出本地鏡像。
語法
$ docker images [OPTIONS] [REPOSITORY[:TAG]]
OPTIONS說明:
-
-a :列出本地所有的鏡像(含中間映像層,默認情況下,過濾掉中間映像層);
-
--digests :顯示鏡像的摘要信息;
-
-f :顯示滿足條件的鏡像;
-
--format :指定返回值的模板文件;
-
--no-trunc :顯示完整的鏡像信息;
-
-q :只顯示鏡像ID。
示例
# 查看本地鏡像列表。$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEmymysql v1 37af1236adef 5 minutes ago 329 MBe6/ubuntu v4 1c06aa18edee 2 days ago 142.1 MB<none> <none> 5c6e1090e771 2 days ago 165.9 MBhttpd latest ed38aaffef30 11 days ago 195.1 MBalpine latest 4e38e38c8ce0 2 weeks ago 4.799 MBmongo 3.2 282fd552add6 3 weeks ago 336.1 MBredis latest 4465e4bcad80 3 weeks ago 185.7 MBphp 5.6-fpm 025041cd3aa5 3 weeks ago 456.3 MBpython 3.5 045767ddf24a 3 weeks ago 684.1 MB...# 列出本地鏡像中REPOSITORY為nginx的鏡像列表。$ docker images nginx
docker rmi : 刪除本地一個或多少鏡像。
語法
$ docker rmi [OPTIONS] IMAGE [IMAGE...]
OPTIONS說明:
-
-f :強制刪除;
-
--no-prune :不移除該鏡像的過程鏡像,默認移除;
示例
# 強制刪除本地鏡像 e6/ubuntu:v4。$ docker rmi -f e6/ubuntu:v4Untagged: e6/ubuntu:v4Deleted: sha256:1c06aa18edee44230f93a90a7d88139235de12cd4c089d41eed8419b503072beDeleted: sha256:85feb446e89a28d58ee7d80ea5ce367eebb7cec70f0ec18aa4faa874cbd97c73
docker tag : 標記本地鏡像,將其歸入某一倉庫。
語法
$ docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
示例
# 將鏡像ubuntu:15.10標記為 e6/ubuntu:v3 鏡像。$ docker tag ubuntu:15.10 e6/ubuntu:v3$ docker images e6/ubuntu:v3REPOSITORY TAG IMAGE ID CREATED SIZEe6/ubuntu v3 4e3b13c8a266 3 months ago 136.3 MB
docker build 命令用於使用 Dockerfile 創建鏡像。
語法
$ docker build [OPTIONS] PATH | URL | -
OPTIONS說明:
- --build-arg=[] :設置鏡像創建時的變量;
- --cpu-shares :設置 cpu 使用權重;
- --cpu-period :限制 CPU CFS周期;
- --cpu-quota :限制 CPU CFS配額;
- --cpuset-cpus :指定使用的CPU id;
- --cpuset-mems :指定使用的內存 id;
- --disable-content-trust :忽略校驗,默認開啟;
- -f :指定要使用的Dockerfile路徑;
- --force-rm :設置鏡像過程中刪除中間容器;
- --isolation :使用容器隔離技術;
- --label=[] :設置鏡像使用的元數據;
- -m :設置內存最大值;
- --memory-swap :設置Swap的最大值為內存+swap,"-1"表示不限swap;
- --no-cache :創建鏡像的過程不使用緩存;
- --pull :嘗試去更新鏡像的新版本;
- --quiet, -q :安靜模式,成功后只輸出鏡像 ID;
- --rm :設置鏡像成功后刪除中間容器;
- --shm-size :設置/dev/shm的大小,默認值是64M;
- --ulimit :Ulimit配置。
- --squash :將 Dockerfile 中所有的操作壓縮為一層。
- --tag, -t: 鏡像的名字及標簽,通常 name:tag 或者 name 格式;可以在一次構建中為一個鏡像設置多個標簽。
- --network: 默認 default。在構建期間設置RUN指令的網絡模式
示例
# 使用當前目錄的 Dockerfile 創建鏡像,標簽為 e6/ubuntu:v1。$ docker build -t e6/ubuntu:v1 . # 也可以通過 -f Dockerfile 文件的位置:$ docker build -f /path/to/a/Dockerfile .# 在 Docker 守護進程執行 Dockerfile 中的指令前,首先會對 Dockerfile 進行語法檢查,有語法錯誤時會返回:$ docker build -t test/myapp .Sending build context to Docker daemon 2.048 kBError response from daemon: Unknown instruction: RUNCMD
docker history : 查看指定鏡像的創建歷史。
語法
$ docker history [OPTIONS] IMAGE
OPTIONS說明:
-
-H :以可讀的格式打印鏡像大小和日期,默認為true;
-
--no-trunc :顯示完整的提交記錄;
-
-q :僅列出提交記錄ID。
示例
查看本地鏡像e6/ubuntu:v3的創建歷史。
$ docker history e6/ubuntu:v1IMAGE CREATED CREATED BY SIZE COMMENTe76ebd704270 2 days ago /bin/sh -c #(nop) CMD ["curl" "-s" "http://… 0B 643a0fed86a9 2 days ago /bin/sh -c apt-get update && apt-get install… 16.2MB 9499db781771 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 4 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B <missing> 4 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B <missing> 4 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B <missing> 4 months ago /bin/sh -c #(nop) ADD file:8eef54430e581236e… 131MB
docker save : 將指定鏡像保存成 tar 歸檔文件。
語法
$ docker save [OPTIONS] IMAGE [IMAGE...]
OPTIONS 說明:
- -o :輸出到的文件。
示例
將鏡像 e6/ubuntu:v3 生成 my_ubuntu_v3.tar 文檔
$ docker save -o my_ubuntu_v1.tar e6/ubuntu:v1$ ll my_ubuntu_v1.tar-rw------- 1 e6 e6 142102016 Jul 11 01:37 my_ubuntu_v1.tar
docker load : 導入使用 docker save 命令導出的鏡像。
語法
$ docker load [OPTIONS]
OPTIONS 說明:
-
--input , -i : 指定導入的文件,代替 STDIN。
-
--quiet , -q : 精簡輸出信息。
示例
導入鏡像:
$ docker image lsREPOSITORY TAG IMAGE ID CREATED SIZE$ docker load < my_ubuntu_v1.tarLoaded image: e6/ubuntu:v1$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEmymysql v1 159d74e6cbce 12 minutes ago 302MBregistry.cn-shenzhen.aliyuncs.com/base_e6yw/mysqldemo v1 159d74e6cbce 12 minutes ago 302MB<none> <none> 17c51e0230b6 2 days ago 302MBe6/ubuntu v1 e76ebd704270 2 days ago 147MBmyip latest e76ebd704270 2 days ago 147MBnginx latest f6d0b4767a6c 2 months ago 133MBmysql 5.6 6e68afc1976f 2 months ago 302MBubuntu 16.04 9499db781771 4 months ago 131MB$ docker load --input my_ubuntu_v1.tarLoaded image: e6/ubuntu:v1$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEmymysql v1 159d74e6cbce 13 minutes ago 302MBregistry.cn-shenzhen.aliyuncs.com/base_e6yw/mysqldemo v1 159d74e6cbce 13 minutes ago 302MB<none> <none> 17c51e0230b6 2 days ago 302MBe6/ubuntu v1 e76ebd704270 2 days ago 147MBmyip latest e76ebd704270 2 days ago 147MBnginx latest f6d0b4767a6c 2 months ago 133MBmysql 5.6 6e68afc1976f 2 months ago 302MBubuntu 16.04 9499db781771 4 months ago 131MB
docker import : 從歸檔文件中創建鏡像。
語法
$ docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
OPTIONS說明:
-
-c :應用docker 指令創建鏡像;
-
-m :提交時的說明文字;
示例
# 從鏡像歸檔文件my_ubuntu_v3.tar創建鏡像,命名為e6/ubuntu:v4$ docker import my_ubuntu_v1.tar e6/ubuntu:v2 sha256:7e6f3bd25c59e8c18206457cc403544278eb9049641e894a7a8576220fdcf398$ docker images e6/ubuntu:v2REPOSITORY TAG IMAGE ID CREATED SIZEe6/ubuntu v2 7e6f3bd25c59 25 seconds ago 152MB
info|version
docker info : 顯示 Docker 系統信息,包括鏡像和容器數。。
語法
$ docker info
示例
# 查看docker系統信息。$ docker infoClient: Context: default Debug Mode: false Plugins: app: Docker App (Docker Inc., v0.9.1-beta3) buildx: Build with BuildKit (Docker Inc., v0.5.1-docker)Server: Containers: 5 Running: 3 Paused: 0 Stopped: 2 Images: 7 Server Version: 20.10.5 Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: 1 Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog Swarm: inactive Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc Default Runtime: runc Init Binary: docker-init containerd version: 05f951a3781f4f2c1911b05e61c160e9c30eaa8e runc version: 12644e614e25b05da6fd08a38ffa0cfe1903fdec init version: de40ad0 Security Options: seccomp Profile: default Kernel Version: 3.10.0-1160.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 CPUs: 4 Total Memory: 15.19GiB Name: localhost.localdomain ID: 5PCC:SY6I:UAAH:CLPH:FDXT:A2EN:KWEA:7S3W:4HEA:WO3B:2HJR:3PN7 Docker Root Dir: /var/lib/docker Debug Mode: false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Registry Mirrors: https://7xatqpwu.mirror.aliyuncs.com/ Live Restore Enabled: false
docker version :顯示 Docker 版本信息。
語法
$ docker version [OPTIONS]
OPTIONS說明:
- -f :指定返回值的模板文件。
實例
# 顯示 Docker 版本信息。$ docker versionClient: Docker Engine - Community Version: 20.10.5 API version: 1.41 Go version: go1.13.15 Git commit: 55c4c88 Built: Tue Mar 2 20:33:55 2021 OS/Arch: linux/amd64 Context: default Experimental: trueServer: Docker Engine - Community Engine: Version: 20.10.5 API version: 1.41 (minimum version 1.12) Go version: go1.13.15 Git commit: 363e9a8 Built: Tue Mar 2 20:32:17 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.4.4 GitCommit: 05f951a3781f4f2c1911b05e61c160e9c30eaa8e runc: Version: 1.0.0-rc93 GitCommit: 12644e614e25b05da6fd08a38ffa0cfe1903fdec docker-init: Version: 0.19.0 GitCommit: de40ad0
kubernetes
什么是kubernetes
Kubernetes 是 Google 團隊發起的一個開源項目,它的目標是管理跨多個主機的容器,用於自動部署、擴展和管理容器化的應用程序,主要實現語言為 Go 語言。
kubernetes 的組成
-
Master:Master 節點是 Kubernetes 集群的控制節點,負責整個集群的管理和控制。Master 節點上包含以下組件:
-
kube-apiserver:集群控制的入口,提供 HTTP REST 服務
-
kube-controller-manager:Kubernetes 集群中所有資源對象的自動化控制中心
-
kube-scheduler:負責 Pod 的調度
-
Node:Node 節點是 Kubernetes 集群中的工作節點,Node 上的工作負載由 Master 節點分配,工作負載主要是運行容器應用。Node 節點上包含以下組件:
- kubelet:負責 Pod 的創建、啟動、監控、重啟、銷毀等工作,同時與 Master 節點協作,實現集群管理的基本功能。
- kube-proxy:實現 Kubernetes Service 的通信和負載均衡
- 運行容器化(Pod)應用
-
Pod: Pod 是 Kubernetes 最基本的部署調度單元。每個 Pod 可以由一個或多個業務容器和一個根容器(Pause 容器)組成。一個 Pod 表示某個應用的一個實例
-
ReplicaSet:是 Pod 副本的抽象,用於解決 Pod 的擴容和伸縮
-
Deployment:Deployment 表示部署,在內部使用ReplicaSet 來實現。可以通過 Deployment 來生成相應的 ReplicaSet 完成 Pod 副本的創建
-
Service:Service 是 Kubernetes 最重要的資源對象。Kubernetes 中的 Service 對象可以對應微服務架構中的微服務。Service 定義了服務的訪問入口,服務的調用者通過這個地址訪問 Service 后端的 Pod 副本示例。Service 通過 Label Selector 同后端的 Pod 副本建立關系,Deployment 保證后端Pod 副本的數量,也就是保證服務的伸縮性。
kubectl管理工具以及命令
基礎命令
kubectl create 創建
通過配置文件名或stdin創建一個集群資源對象。
支持JSON和YAML格式的文件。
語法
$ kubectl create -f FILENAME
示例
# 通過pod.json文件創建一個pod。$ kubectl create -f ./pod.json# 通過stdin的JSON創建一個pod。$ cat pod.json | kubectl create -f -
kubectl apply 創建/更新
通過配置文件名或stdin創建一個集群資源對象。
支持JSON和YAML格式的文件。
語法
$ kubectl apply -f FILENAME
示例
# 通過pod.json文件創建一個pod。$ kubectl apply -f ./pod.json# 通過stdin的JSON創建一個pod。$ cat pod.json | kubectl apply -f -
注意
kubectl create命令可創建新資源。 因此,如果再次運行該命令,則會拋出錯誤,因為資源名稱在名稱空間中應該是唯一的。
kubectl apply命令將配置應用於資源。 如果資源不在那里,那么它將被創建。 kubectl apply命令可以第二次運行,如果資源在那里,那么它將配置應用於現有資源
簡單來說,如果在單個文件上運行操作以創建資源,則create和apply基本相同。 但是, apply允許您在目錄下的多個文件上同時創建和修補。
kubectl delete 刪除
通過配置文件名、stdin、資源名稱或label選擇器來刪除資源。
語法
$ kubectl delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])
示例
# 使用 pod.json中指定的資源類型和名稱刪除pod。$ kubectl delete -f ./pod.json# 根據傳入stdin的JSON所指定的類型和名稱刪除pod。$ cat pod.json | kubectl delete -f -# 刪除名為“baz”和“foo”的Pod和Service。$ kubectl delete pod,service baz foo# 刪除 Label name = myLabel的pod和Service。$ kubectl delete pods,services -l name=myLabel# 強制刪除dead node上的pod$ kubectl delete pod foo --grace-period=0 --force# 刪除所有pod$ kubectl delete pods --all
kubectl get獲取資源信息
獲取列出一個或多個資源的信息。
可以使用的資源包括:
- all
- certificatesigningrequests (aka 'csr')
- clusterrolebindings
- clusterroles
- clusters (valid only for federation apiservers)
- componentstatuses (aka 'cs')
- configmaps (aka 'cm')
- controllerrevisions
- cronjobs
- daemonsets (aka 'ds')
- deployments (aka 'deploy')
- endpoints (aka 'ep')
- events (aka 'ev')
- horizontalpodautoscalers (aka 'hpa')
- ingresses (aka 'ing')
- jobs
- limitranges (aka 'limits')
- namespaces (aka 'ns')
- networkpolicies (aka 'netpol')
- nodes (aka 'no')
- persistentvolumeclaims (aka 'pvc')
- persistentvolumes (aka 'pv')
- poddisruptionbudgets (aka 'pdb')
- podpreset
- pods (aka 'po')
- podsecuritypolicies (aka 'psp')
- podtemplates
- replicasets (aka 'rs')
- replicationcontrollers (aka 'rc')
- resourcequotas (aka 'quota')
- rolebindings
- roles
- secrets
- serviceaccounts (aka 'sa')
- services (aka 'svc')
- statefulsets
- storageclasses
- thirdpartyresources
語法
$ kubectl get resource名稱
OPTIONS說明:
-
-o wide/yaml/json 用不同的格式查看
-
-l key=value 看指定標簽的pods,支持’=’, ‘==’, and ‘!=’操作符
-
-n 命名空間 查看指定的命名空間
示例
# 查看Master狀態$ kubectl get componentstatuses# 查看所有命名空間$ kubectl get namespace# 列出所有的pods$ kubectl get pods# 顯示更多的pods列表信息(例如 pod的ip和所處的node)$ kubectl get pods -o wide# 列出名字為web的rc$ kubectl get replicationcontroller web# 獲取名字為web-pod-13je7的pod的信息,並以json格式輸出$ kubectl get -o json pod web-pod-13je7# 根據pod文件查找pod,並以json格式輸出$ kubectl get -f pod.yaml -o json# 獲取pod容器的狀態$ kubectl get -o template pod/kube-dns-795f5f6f9c-ldxxs --template {{.status.phase}}# 同時獲取所有的rc和service$ kubectl get rc,services# 獲取符合條件的所有rc,svc,pod$ kubectl get rc/web service/frontend pods/web-pod-13je7# 獲取所有resource$ kubectl get all
kubectl run創建並運行
- 創建並運行一個或多個容器鏡像。
- 創建一個deployment 或job 來管理容器。
語法
$ kubectl run NAME --image=image [--env="key=value"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]
示例:
# 啟動nginx實例。$ kubectl run nginx --image=nginx# hazelcast實例,暴露容器端口 5701。$ kubectl run hazelcast --image=hazelcast --port=5701# 啟動hazelcast實例,在容器中設置環境變量“DNS_DOMAIN = cluster”和“POD_NAMESPACE = default”。$ kubectl run hazelcast --image=hazelcast --env="DNS_DOMAIN=cluster" --env="POD_NAMESPACE=default"# 啟動nginx實例,設置副本數5。$ kubectl run nginx --image=nginx --replicas=5# Dry 打印相應的API對象而不創建它們。$ kubectl run nginx --image=nginx --dry-run
kubectl expose 創建資源的service
將資源暴露為新的Kubernetes Service。
指定deployment、service、replica set、replication controller或pod ,並使用該資源的選擇器作為指定端口上新服務的選擇器。deployment 或 replica set只有當其選擇器可轉換為service支持的選擇器時,即當選擇器僅包含matchLabels組件時才會作為暴露新的Service。
資源包括(不區分大小寫):
pod(po),service(svc),replication controller(rc),deployment(deploy),replica set(rs)
語法
$ kubectl expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]
示例
# 為RC的deployment創建service,並通過Service的80端口轉發至宿主機端口上。$ kubectl expose deployment centos7-test2 --port=22 --type=NodePort
kubectl set配置應用資源
配置應用資源。
使用這些命令能幫你更改現有應用資源一些信息。
語法
$ kubectl set SUBCOMMAND
子命令
- image
- resources
- selector
- subject
kubectl edit 使用默認編輯器 編輯服務器上定義的資源。
使用命令行工具獲取的任何資源都可以使用edit命令編輯。edit命令會打開使用KUBE_EDITOR,GIT_EDITOR 或者EDITOR環境變量定義的編輯器,可以同時編輯多個資源,但所編輯過的資源只會一次性提交。edit除命令參數外還接受文件名形式。
文件默認輸出格式為YAML。要以JSON格式編輯,請指定“-o json”選項。
如果在更新資源時報錯,將會在磁盤上創建一個臨時文件來記錄。在更新資源時最常見的錯誤是幾個用戶同時使用編輯器更改服務器上資源,發生這種情況,你需要將你的更改應用到最新版本的資源上,或者更新保存的臨時副本。
語法
$ kubectl edit (RESOURCE/NAME | -f FILENAME)
示例
# 編輯名為'centos7-test2'的service:$ kubectl edit svc centos7-test2 -n test
故障排查和調試命令
kubtctl describe輸出指定的一個/多個資源的詳細信息
輸出指定的一個/多個資源的詳細信息。
此命令組合調用多條API,輸出指定的一個或者一組資源的詳細描述。
語法
$ kubectl describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)
示例
# 描述一個node$ kubectl describe nodes k8s-master# 描述一個pod$ kubectl describe pods/nginx# 描述deployment_centos7.yaml中的資源類型和名稱指定的pod$ kubectl describe -f deployment_centos7.yaml -n test# 描述所有的pod$ kubectl describe pods -n test
kubectl logs日志查看
查看容器日志
語法
$ kubectl logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER] [options]
示例
# 輸出一個單容器pod my-pod的日志到標准輸出$ kubectl logs pod/centos7-test2-5dbbc849f4-f6w77 -n test# 輸出多容器pod中的某個nginx容器的日志$ kubectl logs nginx-78f5d695bd-czm8z -c nginx# 輸出所有包含app-nginx標簽的pod日志$ kubectl logs -l app=nginx# 加上-f參數跟蹤日志,類似tail -f$ kubectl logs -f my-pod # 輸出該pod的上一個退出的容器實例日志。在pod容器異常退出時很有用$ kubectl logs my-pod -p# 指定時間戳輸出日志 $ kubectl logs my-pod --since-time=2018-11-01T15:00:00Z# 指定時間段輸出日志,單位s/m/h$ kubectl logs my-pod --since=1h
kubectl exec 執行容器的命令
exec主要作用是在容器內部執行命令(一般為查看容器內部日志
語法
$ kubectl exec -it podName -c containerName -n namespace -- shell comand
示例
# 進入centos 的pod $ kubectl exec -it pod/centos7-test2-5dbbc849f4-f6w77 /bin/bash -n test
kubectl cp文件傳輸
文件傳輸
語法:
$ kubectl cp <file-spec-src> <file-spec-dest> [options]
示例
# 拷貝宿主機本地文件夾到pod$ kubectl cp /tmp/foo_dir <some-pod>:/tmp/bar_dir # 指定namespace的拷貝pod文件到宿主機本地目錄$ kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar # 對於多容器pod,用-c指定容器名$ kubectl cp /tmp/foo <some-pod>:/tmp/bar -c <specific-container>
kubectl 部署命令
kubectl rollout對資源進行管理
可用資源包括:
- deployments
- daemonsets
語法
$ kubectl rollout SUBCOMMAND
示例
# 回滾到之前的deployment$ $ kubectl rollout undo deployment/abc# 查看daemonet的狀態$ kubectl rollout status daemonset/foo
kubectl rolling-update執行指定ReplicationController的滾動更新。
該命令創建了一個新的RC, 然后一次更新一個pod方式逐步使用新的PodTemplate,最終實現Pod滾動更新,new-controller.json需要與之前RC在相同的namespace下。
語法:
$ kubectl rolling-update OLD_CONTROLLER_NAME ([NEW_CONTROLLER_NAME] --image=NEW_CONTAINER_IMAGE | -f NEW_CONTROLLER_SPEC)
示例
# 使用frontend-v2.json中的新RC數據更新frontend-v1的pod$ kubectl rolling-update frontend-v1 -f frontend-v2.json# 使用JSON數據更新frontend-v1的pod。$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -
kubectl scale擴容或縮容 Deployment、ReplicaSet、Replication Controller或 Job 中Pod數量。
scale也可以指定多個前提條件,如:當前副本數量或 --resource-version ,進行伸縮比例設置前,系統會先驗證前提條件是否成立。
語法
$ kubectl scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)
示例
# 將名為centos7-test2的pod副本數設置為3$ kubectl scale --replicas=3 deployment/centos7-test2 -n test
kubectl autoscale使用 autoscaler 自動設置在kubernetes集群中運行的pod數量(水平自動伸縮)
指定Deployment、ReplicaSet或ReplicationController,並創建已經定義好資源的自動伸縮器。使用自動伸縮器可以根據需要自動增加或減少系統中部署的pod數量。
語法
$ kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [flags]
示例
# 使用 Deployment “centos7-test2”設定,使用默認的自動伸縮策略,指定目標CPU使用率,使其Pod數量在2到10之間。$ kubectl autoscale deployment centos7-test2 --min=2 --max=10
Dockerfile 定制鏡像
鏡像的定制實際上就是定制每一層所添加的配置、文件。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入一個腳本,用這個腳本來構建、定制鏡像,那么無法重復的問題、鏡像構建透明性的問題、體積的問題就都會解決。這個腳本就是 Dockerfile
。
Dockerfile
是一個文本文件,其內包含了一條條的指令
(Instruction),每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。
FROM nginxRUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
Dockerfile 指令詳解
FROM 指定基礎鏡像
所謂定制鏡像,那一定是以一個鏡像為基礎,在其上進行定制。而 FROM 就是指定基礎鏡像,因此一個 Dockerfile
中 FROM
是必備的指令,並且必須是第一條指令
。
在 Docker Store 上有非常多的高質量的官方鏡像,有可以直接拿來使用的服務類的鏡像,如nginx 、 redis 、 mongo 、mysql 等;也有一些方便開發、構建、運行各種語言應用的鏡像,如 node 、 openjdk 、 python 等。可以在其中尋找一個最符合我們最終目標的鏡像為基礎鏡像進行定制。
如果沒有找到對應服務的鏡像,官方鏡像中還提供了一些更為基礎的操作系統鏡像,如ubuntu 、 debian 、 centos 等,這些操作系統的軟件庫為我們提供了更廣闊的擴展空間。
除了選擇現有鏡像為基礎鏡像外,Docker 還存在一個特殊的鏡像,名為 scratch 。這個鏡像是虛擬的概念,並不實際存在,它表示一個空白的鏡像。
FROM scratch...
如果你以 scratch 為基礎鏡像的話,意味着你不以任何鏡像為基礎,接下來所寫的指令將作為鏡像第一層開始存在。
不以任何系統為基礎,直接將可執行文件復制進鏡像的做法並不罕見,比如 swarm 、 coreos/etcd 。對於 Linux 下靜態編譯的程序來說,並不需要有操作系統提供運行時支持,所需的一切庫都已經在可執行文件里了,因此直接 FROM scratch 會讓鏡像體積更加小巧。使用 Go 語言 開發的應用很多會使用這種方式來制作鏡像,這也是為什么有人認為 Go是特別適合容器微服務架構的語言的原因之一。
RUN 執行命令
語法:
RUN
指令是用來執行命令行命令的。由於命令行的強大能力, RUN 指令在定制鏡像時是最常用的指令之一。其格式有兩種:
- shell 格式:
RUN <命令>
,就像直接在命令行中輸入的命令一樣。剛才寫的 Dockerfile 中的 RUN 指令就是這種格式。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
- exec 格式:
RUN ["可執行文件", "參數1", "參數2"]
,這更像是函數調用中的格式。
注意:
既然 RUN 就像 Shell 腳本一樣可以執行命令,那么我們是否就可以像 Shell 腳本一樣把每個命令對應一個 RUN 呢?比如這樣:
FROM debian:jessieRUN apt-get updateRUN apt-get install -y gcc libc6-dev makeRUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"RUN mkdir -p /usr/src/redisRUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1RUN make -C /usr/src/redisRUN make -C /usr/src/redis install
之前說過,Dockerfile 中每一個指令都會建立一層, RUN 也不例外。每一個 RUN 的行為,就和剛才我們手工建立鏡像的過程一樣:新建立一層,在其上執行這些命令,執行結束后, commit 這一層的修改,構成新的鏡像。
而上面的這種寫法,創建了 7 層鏡像。這是完全沒有意義的,而且很多運行時不需要的東西,都被裝進了鏡像里,比如編譯環境、更新的軟件包等等。結果就是產生非常臃腫、非常多層的鏡像,不僅僅增加了構建部署的時間,也很容易出錯。 這是很多初學 Docker 的人常犯的一個錯誤。
Union FS (聯合文件系統Union File System)是有最大層數限制的,比如 AUFS(早期docker的默認文件系統),曾經是最大不得超過 42 層,現在是不得超過127 層。
上面的 Dockerfile 正確的寫法應該是這樣:
FROM debian:jessieRUN buildDeps='gcc libc6-dev make' \ && apt-get update \ && apt-get install -y $buildDeps \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \ && mkdir -p /usr/src/redis \ && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ && make -C /usr/src/redis \ && make -C /usr/src/redis install \ && rm -rf /var/lib/apt/lists/* \ && rm redis.tar.gz \ && rm -r /usr/src/redis \ && apt-get purge -y --auto-remove $buildDeps
示例
在 Dockerfile 文件所在目錄執行:
$ docker build -t nginx:v3 .Sending build context to Docker daemon 2.048 kBStep 1 : FROM nginx---> e43d811ce2f4Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html---> Running in 9cdc27646c7b---> 44aa4490ce2cRemoving intermediate container 9cdc27646c7bSuccessfully built 44aa4490ce2c$ docker run -d -P --name nginxdemo nignx:v3
從命令的輸出結果中,我們可以清晰的看到鏡像的構建過程。在 Step 2 中,如同我們之前所說的那樣, RUN 指令啟動了一個容器 9cdc27646c7b ,執行了所要求的命令,並最后提交了這一層 44aa4490ce2c ,隨后刪除了所用到的這個容器 9cdc27646c7b 。
在這里我們指定了最終鏡像的名稱 -t nginx:v3 ,構建成功后,我們可以直接運行這個鏡像,其結果就是我們的主頁被改變成了Hello, Docker!。
COPY 復制文件
語法:
-
COPY <源路徑>... <目標路徑>COPY ["<源路徑1>",... "<目標路徑>"]
注意:
-
<源路徑> 可以是多個,甚至可以是通配符,其通配符規則要滿足 Go 的 filepath.Match 規則
COPY hom* /mydir/COPY hom?.txt /mydir/
-
<目標路徑> 可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目標路徑不需要事先創建,如果目錄不存在會在復制文件前先行創建缺失目錄。
-
和 RUN 指令一樣,也有兩種格式,一種類似於命令行,一種類似於函數調用。COPY 指令將從構建上下文目錄中 <
源路徑
> 的文件/目錄復制到新的一層的鏡像內的 <目標路徑
> 位置。比如:COPY package.json /usr/src/app/
-
使用 COPY 指令,源文件的各種元數據都會保留。比如讀、寫、執行權限、文件變更時間等。這個特性對於鏡像定制很有用。特別是構建相關文件都在使用 Git進行管理的時候
ADD 更高級的復制文件
ADD 指令和 COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。比如 <源路徑> 可以是一個 URL ,這種情況下,Docker 引擎會試圖去下載這個鏈接的文件放到 <目標路徑> 去。下載后的文件權限自動設置為 600 ,如果這並不是想要的權限,那么還需要增加額外的一層 RUN 進行權限調整,另外,如果下載的是個壓縮包,需要解壓縮,也一樣還需要額外的一層 RUN 指令進行解壓縮。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下載,處理權限、解壓縮、然后清理無用文件更合理。因此,這個功能其實並不實用,而且不推薦使用。
如果 <源路徑> 為一個 tar 壓縮文件的話,壓縮格式為 gzip , bzip2 以及 xz 的情況下, ADD 指令將會自動解壓縮這個壓縮文件到 <目標路徑> 去。
在某些情況下,這個自動解壓縮的功能非常有用,比如官方鏡像 ubuntu 中:
FROM scratchADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /...
但在某些情況下,如果我們真的是希望復制個壓縮文件進去,而不解壓縮,這時就不可以使用 ADD 命令了。
但在某些情況下,如果我們真的是希望復制個壓縮文件進去,而不解壓縮,這時就不可以使用 ADD 命令了。
在 Docker 官方的 Dockerfile 最佳實踐文檔 中要求,盡可能的使用 COPY ,因為 COPY 的語義很明確,就是復制文件而已,而 ADD 則包含了更復雜的功能,其行為也不一定很清晰。最適合使用 ADD 的場合,就是所提及的需要自動解壓縮的場合。
另外需要注意的是, ADD 指令會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。
因此在 COPY 和 ADD 指令中選擇的時候,可以遵循這樣的原則,所有的文件復制均使用 COPY 指令,僅在需要自動解壓縮的場合使用 ADD 。
CMD 容器啟動命令
語法
CMD 指令的格式和 RUN 相似,也是兩種格式:
# shell 格式: CMD <命令># exec 格式: CMD ["可執行文件", "參數1", "參數2"...]# 參數列表格式(在指定了 ENTRYPOINT 指令后,用 CMD 指定具體的參數)CMD ["參數1", "參數2"...]
注意
1.容器就是進程。既然是進程,那么在啟動容器的時候,需要指定所運行的程序及參數。 CMD 指令就是用於指定默認的容器主進程的啟動命令的。
2.在運行時可以指定新的命令來替代鏡像設置中的這個默認命令,比如, ubuntu 鏡像默認的CMD 是 /bin/bash ,如果我們直接 docker run -it ubuntu 的話,會直接進入 bash 。我們也可以在運行時指定運行別的命令,如 docker run -it ubuntu cat /etc/os-release 。這就是用 cat /etc/os-release 命令替換了默認的 /bin/bash 命令了,輸出了系統版本信息。
3.在指令格式上,一般推薦使用 exec
格式,這類格式在解析時會被解析為 JSON 數組
,因此一定要使用雙引號 "
,而不要使用單引號。
4.如果使用 shell 格式的話,實際的命令會被包裝為 sh -c
的參數的形式進行執行。比如:
CMD echo $HOME
在實際執行中,會將其變更為:
CMD [ "sh", "-c", "echo $HOME" ]
ENTRYPOINT 入口點
ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式和 shell 格式。
ENTRYPOINT 的目的和 CMD 一樣,都是在指定容器啟動程序及參數。ENTRYPOINT 在運行時也可以替代,不過比 CMD 要略顯繁瑣,需要通過 docker run 的參數 –entrypoint 來指定。
當指定了 ENTRYPOINT 后, CMD 的含義就發生了改變,不再是直接的運行其命令,而是將CMD 的內容作為參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變為:
<ENTRYPOINT> "<CMD>"
ENV 設置環境變量
語法:
格式有兩種:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
示例:
ENV VERSION=1.0 DEBUG=on \ NAME="Happy Feet"
注意:
下列指令可以支持環境變量展開:
**ADD 、 COPY 、 ENV 、 EXPOSE 、 LABEL 、 USER 、 WORKDIR 、 VOLUME 、 STOPSIGNAL 、 ONBUILD **
VOLUME 定義匿名卷
格式為:
VOLUME ["<路徑1>", "<路徑2>"...]VOLUME <路徑>
之前我們說過,容器運行時應該盡量保持容器存儲層不發生寫操作,對於數據庫類需要保存動態數據的應用,其數據庫文件應該保存於卷(volume)中。為了防止運行時用戶忘記將動態文件所保存目錄掛載為卷,在 Dockerfile 中,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器存儲層寫入大量數據。
VOLUME /data
這里的 /data 目錄就會在運行時自動掛載為匿名卷,任何向 /data 中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。當然,運行時可以覆蓋這個掛載設置。比如:
$ docker run -d -v mydata:/data xxxx
在這行命令中,就使用了 mydata 這個命名卷掛載到了 /data 這個位置,替代了 Dockerfile 中定義的匿名卷的掛載配置。
EXPOSE 聲明端口
格式為
EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是聲明運行時容器提供服務端口,這只是一個聲明,在運行時並不會因為這個聲明應用就會開啟這個端口的服務。在 Dockerfile 中寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;另一個用處則是在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。
此外,在早期 Docker 版本中還有一個特殊的用處。以前所有容器都運行於默認橋接網絡中,因此所有容器互相之間都可以直接訪問,這樣存在一定的安全性問題。於是有了一個 Docker 引擎參數 --icc=false ,當指定該參數后,容器間將默認無法互訪,除非互相間使用了 --links 參數的容器才可以互通,並且只有鏡像中 EXPOSE 所聲明的端口才可以被訪問。這個 --icc=false 的用法,在引入了 docker network 后已經基本不用了,通過自定義網絡可以很輕松的實現容器間的互聯與隔離。
要將 EXPOSE 和在運行時使用 -p <宿主端口>:<容器端口> 區分開來。 -p ,是映射宿主端口和容器端口,換句話說,就是將容器的對應端口服務公開給外界訪問,而 EXPOSE 僅僅是聲明容器打算使用什么端口而已,並不會自動在宿主進行端口映射。
WORKDIR 指定工作目錄
格式為
WORKDIR <工作目錄路徑> 。
使用 WORKDIR 指令可以來指定工作目錄(或者稱為當前目錄),以后各層的當前目錄就被改為指定的目錄,如該目錄不存在, WORKDIR 會幫你建立目錄。
之前提到一些初學者常犯的錯誤是把 Dockerfile 等同於 Shell 腳本來書寫,這種錯誤的理解還可能會導致出現下面這樣的錯誤:
RUN cd /appRUN echo "hello" > world.txt
如果將這個 Dockerfile 進行構建鏡像運行后,會發現找不到 /app/world.txt 文件,或者其內容不是 hello 。原因其實很簡單,在 Shell 中,連續兩行是同一個進程執行環境,因此前一個命令修改的內存狀態,會直接影響后一個命令;而在 Dockerfile 中,這兩行 RUN 命令的執行環境根本不同,是兩個完全不同的容器。這就是對 Dockerfile 構建分層存儲的概念不了解所導致的錯誤。
如果將這個 Dockerfile 進行構建鏡像運行后,會發現找不到 /app/world.txt 文件,或者其內容不是 hello 。原因其實很簡單,在 Shell 中,連續兩行是同一個進程執行環境,因此前一個命令修改的內存狀態,會直接影響后一個命令;而在 Dockerfile 中,這兩行 RUN 命令的執行環境根本不同,是兩個完全不同的容器。這就是對 Dockerfile 構建分層存儲的概念不了解所導致的錯誤。
之前說過每一個 RUN 都是啟動一個容器、執行命令、然后提交存儲層文件變更。第一層 RUNcd /app 的執行僅僅是當前進程的工作目錄變更,一個內存上的變化而已,其結果不會造成任何文件變更。而到第二層的時候,啟動的是一個全新的容器,跟第一層的容器更完全沒關系,自然不可能繼承前一層構建過程中的內存變化。
因此如果需要改變以后各層的工作目錄的位置,那么應該使用 WORKDIR 指令。
USER 指定當前用戶
格式: USER <用戶名>
USER 指令和 WORKDIR 相似,都是改變環境狀態並影響以后的層。 WORKDIR 是改變工作目錄, USER 則是改變之后層的執行 RUN , CMD 以及 ENTRYPOINT 這類命令的身份。當然,和 WORKDIR 一樣, USER 只是幫助你切換到指定用戶而已,這個用戶必須是事先建立好的,否則無法切換。
RUN groupadd -r redis && useradd -r -g redis redisUSER redisRUN [ "redis-server" ]
如果以 root 執行的腳本,在執行期間希望改變身份,比如希望以某個已經建立好的用戶來運行某個服務進程,不要使用 su 或者 sudo ,這些都需要比較麻煩的配置,而且在 TTY
缺失的環境下經常出錯。建議使用 gosu
# 建立 redis 用戶,並使用 gosu 換另一個用戶執行命令RUN groupadd -r redis && useradd -r -g redis redis# 下載 gosuRUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \ && chmod +x /usr/local/bin/gosu \ && gosu nobody true# 設置 CMD,並以另外的用戶執行CMD [ "exec", "gosu", "redis", "redis-server" ]
練習
FROM openjdk:8-jdk-alpine# Add Maintainer InfoMAINTAINER honux <e6@e6yun.com># 設置localeENV LANG en_US.UTF-8ENV LANGUAGE en_US:enENV LC_ALL en_US.UTF-8ENV TZ=Asia/ShanghaiRUN mkdir /app \ mkdir /opt/settings && echo "env=DEV" > /opt/settings/server.propertiesWORKDIR /appCOPY e6-ms-gateway-2.0.0-SNAPSHOT-exec.jar /app/demo.jarCOPY config/* /app/config/EXPOSE 8080ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dserver.port=8080", "-jar","/app/demo.jar"]