一、概念
1、基於容器生成鏡像
通過 docker commit
命令將現有的容器提交來生成新的鏡像。
原理:容器啟動后的修改都保存在可寫層,通過對可寫層的修改生成新的鏡像。
[root@hqs docker-hello]# docker commit --help
Usage: docker commit [OPTIONS選項] CONTAINER容器 [REPOSITORY倉庫名[:TAG標簽]]
Create a new image from a containers changes
Options:
-a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>") # 指定作者
-c, --change list Apply Dockerfile instruction to the created image # 允許使用dockerfile的指令
-m, --message string Commit message # 提交信息
-p, --pause Pause container during commit (default true) # 創建鏡像過程中容器暫停(掛起)
# 操作流程:運行容器——》修改容器——》容器保存為新的鏡像
# 案例:
# 1.運行容器
[hqs-docker@hecs-hqs-01 tester03]$ docker run -ti centos /bin/bash
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
a1d0c7532777: Pull complete
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
# 2.修改容器
[root@69b728ed9a9b yum.repos.d]# rm -rf Cent*
[root@69b728ed9a9b yum.repos.d]# vi nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
[root@69b728ed9a9b yum.repos.d]# yum install -y nginx
[root@69b728ed9a9b yum.repos.d]# exit
# 3.保存為新的鏡像
[hqs-docker@hecs-hqs-01 tester03]$ docker commit 69b728ed9a9b centos-with-nginx
sha256:192cee469ee07eee42d653d65239eff8474d34f359b9431221e341f1e704587d
# 4.啟動新容器
[hqs-docker@hecs-hqs-01 tester03]$ docker run -dti centos-with-nginx
0b76bde1cb0f2bbc363541b2f85765d45ae1b0b343ca81af31552fce711fe5b7
[hqs-docker@hecs-hqs-01 tester03]$ docker exec -ti 0b76bde /bin/bash
[root@0b76bde1cb0f /]# find / -name nginx
/usr/share/nginx
/usr/sbin/nginx
/usr/libexec/initscripts/legacy-actions/nginx
/usr/lib64/nginx
/etc/logrotate.d/nginx
/etc/nginx
/var/log/nginx
/var/cache/nginx
基於容器生成鏡像無法重復、構建缺乏透明性和體積偏大的問題,因此不推薦使用這種方式構建鏡像。
2、Dockerfile 構建鏡像
Dockerfile是由一系列指令和參數構成的腳本,每一條指令構建一層
,因此每一條指令的內容就是描述該層應當如何構建,一個Dockerfile包含了構建鏡像的完整指令。
Dockerfile就是一個腳本來構建和定制鏡像,把每一層的修改、安裝、構建、操作都寫入腳本。以此來解決體積、鏡像構建透明等問題。
Dockerfile是一個文本文件,包含一條條指令(Instruction),每一條指令構建一層,每一條指令的內容,就是描述該層應當如何構建。
3、docker build命令
基於 dockerfile 構建鏡像使用 docker build
命令。命令是通過 Dockerfile文件
和 構建上下文(Build Context)
來構建鏡像的。
注意:
- 不要把多余的文件放到構建上下文中————一般就要創建一個空目錄作為構建上下文
- 一般將 Dockerfile直接命名為
Dockerfile
,並放在上下文根目錄,否則構建找不到(可以用-f指定) - Dockerfile的每個指令都被獨立執行並創建一個新鏡像。
# 語法
[root@localhost docker]# docker build --help
Usage: docker build [OPTIONS選項] PATH路徑 | URL | -
Build an image from a Dockerfile
Options:
--add-host list Add a custom host-to-IP mapping (host:ip) # 添加映射
--build-arg list Set build-time variables # 設置鏡像創建時的變量
--cache-from strings Images to consider as cache sources
--cgroup-parent string Optional parent cgroup for the container
--compress Compress the build context using gzip # 壓縮構建的內容
--cpu-period int Limit the CPU CFS (Completely Fair Scheduler) period # 限制 CPU CFS周期
--cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota # 限制 CPU CFS配額
-c, --cpu-shares int CPU shares (relative weight) # 設置cpu使用權重
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1) # 指定使用的CPU id
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1) # 指定使用的內存 id
--disable-content-trust Skip image verification (default true) # 忽略校驗,默認開啟
-f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile') # 設置Dockerfile名字
--force-rm Always remove intermediate containers # 總是刪除中間容器
--iidfile string Write the image ID to the file
--isolation string Container isolation technology # 使用容器隔離技術
--label list Set metadata for an image # 設置鏡像使用的元數據
-m, --memory bytes Memory limit # 內存限制
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap # 設置Swap的最大值為內存+swap,"-1"表示不限swap
--network string Set the networking mode for the RUN instructions during build (default "default") # 在構建期間設置RUN指令網絡模式
--no-cache Do not use cache when building the image # 構建鏡像不使用緩存
--pull Always attempt to pull a newer version of the image # 總是嘗試拉取新版本鏡像
-q, --quiet Suppress the build output and print image ID on success # 安靜模式,成功后只打印鏡像ID
--rm Remove intermediate containers after a successful build (default true) # 構建成功后刪除中間容器
--security-opt strings Security options # 安全選項
--shm-size bytes Size of /dev/shm # /dev/shm大小
-t, --tag list Name and optionally a tag in the 'name:tag' format # 鏡像的名字及標簽
--target string Set the target build stage to build. # 目標構建階段設為build
--ulimit ulimit Ulimit options (default []) # ?
# 最簡案例
docker build .
4、Dockerfile格式
指令:
- 指令不區分大小寫,建議大寫。
- 指令可以指定若干參數。
- Docker按順序執行指令。
- Dockerfile文件必須以
FROM
指令開頭。
注釋:
#
號開頭的行都將被視為注釋。解析器指令除外。- 行中其他位置的
#
視為參數的一部分(不視為注釋)。
解析器指令:
- 不添加鏡像,也不會出現在構建步驟。
- 語法:
# 指令 = 值
,e.g:escape=\
。 - 一旦注釋、空行、解析器指令被處理,不再搜尋解析器指令,全格式化為注釋。————》解析器指令必須在Dockerfile的首部。
5、.dockerignore文件
可以添加.dockerignore
文件到構建上下文中來定義要排除的文件和目錄。
用途:構建鏡像時,在將構建上下文傳給docker daemon進程時,命令行接口就修改了上下文以排除匹配的文件或目錄。有助於避免發送大型文件或敏感文件。
使用要點:
- 解釋為換行符分隔的模式列表(一行一行讀取)
- 支持特殊通配符
**
,匹配任意數量的目錄。
二、Dockerfile的指令
1、FROM指令
FROM
指令為后續指令設置基礎鏡像。
指令寫法:
# 不帶標簽和摘要值,默認選擇latest標簽的鏡像
FROM <鏡像>
# 帶標簽,指定倉庫中對應標簽版本的鏡像
FROM <鏡像>:<標簽>
# 帶摘要值,指定倉庫中對應的鏡像
FROM <鏡像>@<摘要值>
# AS語法指定名稱,用處:在后期可以引用此階段構建的鏡像
FROM <鏡像> AS <名稱>
2、RUN指令
RUN
指令在當前鏡像頂部創建新的層,執行定義的命令並提交結果。結果產生的鏡像由Dockerfile進一步處理。
RUN 指令是用來執行命令行命令的。Run指令在定制鏡像時是最常用的指令之一。
注意:
- 多少個RUN就構建多少層鏡像,多個RUN會造成鏡像臃腫,盡量將要執行的命令都寫在一個RUN里面。
- 多條命令執行可以使用
&&
,還可以用\
來換行 - 執行到最后還需要將不需要的文件和目錄刪除
- Union FS有最大層數限制,如AUFS之前最大不得超過42層,現在是不得超過127層。
(1)shell格式
像直接在命令行輸入命令一樣。
命令在shell環境中運行:
- 在Linux系統中默認為
/bin/sh -c
命令; - 在Windows系統中默認為
cmd /S/C
命令。
# 語法格式
RUN <命令>
# 案例
RUN echo '<h1>Hello,Docker!</h1>' > /usr/share/nginx/html/index.html
# 可以用反斜杠將單個RUN指令換行
RUN /bin/bash -c 'source $HOME/.bashrc;\
echo $HOME'
(2)exec格式
更類似函數調用的格式。
# 語法格式
RUN ["可執行文件(程序)", "參數1", "參數2"]
# 案例
RUN ["/bin/bash", "-c", "echo hello"]
3、CMD指令
CMD
指令一般是整個 Dockerfile
的最后一行,主要作用是:為運行中的容器提供默認值。
當Dockerfile 完成所有的環境安裝和配置后,CMD指示容器啟動時要執行的命令。
注意:Dockerfile中只能有一條CMD命令,如果寫了多條則最后一條生效。
(1)exec格式
# 語法
CMD ["可執行文件(程序)", "參數1", "參數2"]
# 案例
CMD ["sh", "-c", "echo $HOME"]
CMD ["/bin/bash"]
(2)ENTRYPOINT指令默認參數
# 語法
CMD ["參數1", "參數2"...]
# 案例??
CMD ["/usr/bin/wc","--help"]
(3)shell格式
# 語法
CMD <命令> 參數1 參數2
# 案例
CMD echo "this is test" | wc -
4、LABEL指令
LABEL
指令向鏡像添加標記。即:以鍵值對的形式給鏡像添加一些元數據(metadata)。
其中包含空格,應該使用引號和反斜杠。
一個鏡像可以定義多個標記,每個LABEL指令都會產生一層鏡像,因此盡量合並在一個LABLE指令里。
# 語法
LABEL <鍵>=<值> <鍵>=<值> <鍵>=<值>...
# 案例
LABEL version="1.0"
# 換行案例
LABEL description="asdasdsad \
標記使用多行"
# 合並標記
LABEL org.opencontainers.image.authors="runoob" version="1.2" description="test"
# 案例
[root@localhost my-centos]# docker inspect --format='{{json .ContainerConfig.Labels}}' my-centos:v2-env
{"com.example.vender":"ACME","datetime":"2023/3/22","description":"聲明容器 運行時偵聽的網絡端口",
"org.label-schema.build-date":"20210915","org.label-schema.license":"GPLv2","org.label-schema.name":"CentOS Base Image",
"org.label-schema.schema-version":"1.0","org.label-schema.vendor":"CentOS",
"version":"1.13"}
5、EXPOSE指令
EXPOSE
指令通知(聲明)容器在運行時監聽指定的網絡端口。
可以指定TCP或UDP端口,默認是TCP端口。
注意:EXPOSE
指令不會發布端口,只能聲明端口,發布端口,要在運行容器時用 -p
或-P
選項發布。
# 語法
EXPOSE <端口>[<端口>....]
# 案例
EXPOSE 8080
EXPOSE 8080 8081 8082
# 用`-P`暴露端口
[root@localhost my-centos]# docker run -tid -P my-centos:v1-net-plus
[root@localhost my-centos]# docker ps -a
CONTAINER ID IMAGE COMMAND PORTS NAMES adoring_vaughan
cbc965feaff1 my-centos:v1-net-plus "/bin/bash" 0.0.0.0:49156->8080/tcp, :::49156->8080/tcp, pedantic_dijkstra
0.0.0.0:49155->8081/tcp, :::49155->8081/tcp,
0.0.0.0:49154->8082/tcp, :::49154->8082/tcp
6、ENV指令
ENV
指令以鍵值對的形式定義環境變量。該值會存在於構建鏡像階段后續指令環境中。
# 第一種寫法(只支持單個變量設置)
ENV <鍵> <值>
# 案例:
# 注意空格,第一個空格后的整個字符串都被視為值,因此無法設置多個變量
ENV GOROOT /usr/local/go
# 第二種寫法(支持單個、多個變量設置)
ENV <鍵>=<值> ....
# 單個案例
ENV GOPATH=/Users/hqs/GolangProjects
ENV GOBIN=/Users/hqs/GolangProjects/bin
# 多個案例
ENV GOROOT=/usr/local/go GOPATH=/Users/hqs/GolangProjects \
GOBIN=/Users/hqs/GolangProjects/bin
# 運行容器檢查環境變量
[root@localhost my-centos]# docker run -ti my-centos:v2-env
[root@c308dc947b33 /]# echo $GOROOT
/usr/local/go
[root@c308dc947b33 /]# echo $GOPATH
/Users/hqs/GolangProjects
[root@c308dc947b33 /]# echo $GOBIN
/Users/hqs/GolangProjects/bin
7、COPY指令
COPY
指令將指定源路徑的文件或目錄復制到容器文件系統指定的目的路徑中。
# 語法
COPY [--chown=<user>:<group>] <源路徑1>... <目標路徑>
COPY [--chown=<user>:<group>] ["<源路徑1>",... "<目標路徑>"]
# 選項--chown:改變復制到容器內文件的用戶和用戶組(默認是UID和GID都為0的用戶和組)
# 選項--from=<name|index>:將源位置改為之前構建階段產生的鏡像,可以用名字或數字索引來標識
# <源路徑>:源文件或者源目錄,這里可以是通配符表達式,其通配符規則要滿足 Go 的 filepath.Match 規則。
# <目標路徑>:容器內的指定路徑,該路徑不用事先建好,路徑不存在的話,會自動創建。
# 通配符案例:
COPY hom* /mydir/ # 添加所有hom開頭文件
COPY hom?.txt /mydir/ # ?替換任意單個字符
# 絕對路徑案例:
COPY test /root/absoluteDir/
# 相對路徑案例:
COPY test relativeDir/
示例:
# 下載centos8的源
[root@localhost my-centos]# wget http://mirrors.aliyun.com/repo/Centos-8.repo
# Dockerfile文件如下:
FROM centos:latest
LABEL "com.example.vender"="ACME" version="1.13" description="聲明容器 \
運行時偵聽的網絡端口" "datetime"="2023/3/22"
ENV GOROOT=/usr/local/go GOPATH=/Users/hqs/GolangProjects \
GOBIN=/Users/hqs/GolangProjects/bin
RUN echo 'nameserver 8.8.8.8' > /etc/resolve.conf && rm -rf /etc/yum.repos.d/* && \
mkdir -p /home/hqs
COPY *.repo /home/
COPY Centos-8.repo /etc/yum.repos.d/
EXPOSE 8080 8081 8082
CMD ["/bin/bash"]
# 構建鏡像
[root@localhost my-centos]# docker build -t my-centos:v1-copy .
# 創建容器
[root@localhost my-centos]# docker run -ti cc5b86cb7cb0
[root@7b6c9c6d53f3 /]# yum install -y httpd
COPY指令遵守的復制規則:
- 源路徑必須位於構建上下文中,不能使用指令COPY ../something/something,因為docker build命令的第1步是發送上下文目錄及其子目錄到Docker守護進程中。
- 如果復制的源是目錄,則復制目錄的整個內容,包括文件系統元數據。
- 如果復制源是任何其他類型的文件,則它會與其元數據被分別復制。在這種情形下,如果目的路徑以斜杠(/)結尾,則它將被認為是一個目錄,源內容將被寫到“<目的>/base(<源>)”路徑中。
- 如果直接指定多個源,或者源中使用了通配符,則目的路徑必須是目錄,並且必須以斜杠(/)結尾。
- 如果目的路徑不以斜杠結尾,則它將被視為常規文件,源內容將被寫入目錄路徑。
- 如果目的路徑不存在,則會與其路徑中所有缺少的目錄一起被創建。
8、ADD指令
ADD
指令和 COPY
指令類似,ADD指令的功能是將主機構建環境(上下文)目錄中的文件和目錄、或URL標記的文件拷貝到鏡像中。
ADD
指令和 COPY
指令區別:
ADD
指令可以使用URL地址指定。ADD
指令的歸檔文件在復制過程中能被自動解壓縮。
# 語法
ADD [--chown=<user>:<group>] <源路徑1>... <目標路徑>
ADD [--chown=<user>:<group>] ["<源路徑1>",... "<目標路徑>"]
# 案例
# ADD指令-三種寫法
ADD Centos-8.repo /etc/yum.repos.d/
ADD http://mirrors.aliyun.com/repo/Centos-8.repo /etc/yum.repos.d/
ADD bridge-utils-1.6.tar.xz /home/hqs
# 登錄容器后可查看到bridge-utils-1.6.tar.xz已解壓
[root@ce2e0b1bf97e hqs]# tree
.
|-- bridge-utils-1.6
| |-- AUTHORS
| |-- COPYING
| |-- ChangeLog
| |-- Makefile.in
| |-- README
| |-- THANKS
| |-- TODO
| |-- brctl
| | |-- Makefile.in
| | |-- brctl.c
注意:
- 源是遠程URL地址時,復制產生的文件為600權限(
-rw-------
),即只有所有者有讀寫權限,其他用戶無法訪問。 - 源是遠程URL地址且不以反斜杠結尾,則下載文件並復制到目的路徑。
- 源是遠程URL地址且以反斜杠結尾,解析出文件名,並下載到"<源>/<文件名>"路徑中。
- 源是可識別的壓縮格式(gzip、bzip2等)的本地tar文件,解壓為目錄。
- 源是遠程URL地址,資源不會被解壓縮。
9、ENTRYPOINT指令
ENTRYPOINT
指令配置容器的默認入口點,也是配置容器運行的可執行文件。
類似於 CMD
指令,但其不會被 docker run
的命令行參數指定的指令所覆蓋,而且這些命令行參數會被當作參數送給 ENTRYPOINT
指令指定的程序。
(1)exec格式
docker run
的命令行參數將附加在exec格式ENTRYPOINT指令后,覆蓋CMD指令。
exec格式ENTRYPOINT可以與CMD搭配使用:exec格式的ENTRYPOINT指令設置默認命令和參數,使用CMD來設置更容易修改的其他默認值。
# 語法
ENTRYPOINT ["可執行文件(程序)", "參數1", "參數2"]
# exec格式ENTRYPOINT與CMD搭配使用示例:
FROM centos
RUN rm -rf /etc/yum.repos.d/*
ADD http://mirrors.aliyun.com/repo/Centos-8.repo /etc/yum.repos.d/
# 1.如果容器啟動默認是執行某個命令用cmd即可
#CMD ["/bin/bash"]
# 2.如果容器啟動默認是執行某個腳本程序則用entrypoint
#ENTRYPOINT ["/usr/local/sbin/test.py"]
# 3.如果執行的腳本可以有參數的話:
#ENTRYPOINT ["/usr/local/sbin/test.py", "-f", "/usr/local/conf/conf.json"]
# 4.如果執行的腳本參數有變換需求的話entrypoint和cmd一起用
ENTRYPOINT ["/usr/local/sbin/test.py", "-f"]
CMD ["/usr/local/conf/conf.json"]
# 1、不傳參運行
$ docker run -tid test:v3 # 容器內會默認運行命令,啟動主進程。
# 2、傳參運行
$ docker run -tid test:v3 /home/hqs/conf.json # 容器內會默認運行命令,啟動主進程
[root@localhost my-centos]# docker ps -a --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7fa2cd3f1f4cc test:v3 "/usr/local/sbin/test.py -f /home/hqs/conf.json" 2 seconds ago Created competent_proskuriakova
bd9b3b310d002 test:v3 "/usr/local/sbin/test.py -f /usr/local/conf/conf.json" 35 seconds ago Created suspicious_burnell
在 docker run
時,可以使用 --entrypoint
選項覆蓋ENTRYPOINT指令:
$ docker run -tid --entrypoint /bin/bash/ls test:v3 -lrt
[root@localhost my-centos]# docker ps -a --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5466ed9a4 test:v3 "ls -lrt" 3 seconds ago Exited (0) 3 seconds ago wizardly_feistel
(2)shell格式
這種方式會在/bin/sh -c中執行,會忽略任何CMD或者docker run命令行選項。
缺點:ENTRYPOINT
指令將作為 /bin/sh -c
的子命令啟動,不傳任何其他信息,不會接受系統信號,也不會接受docker stop
中止信號。
為了確保 docker stop
能夠停止長時間運行ENTRYPOINT的容器,推薦使用exec格式。
# 語法
ENTRYPOINT 命令 參數1 參數2
# shell格式ENTRYPOINT示例:
FROM ubuntu
ENTRYPOINT top -b
# 構建鏡像
[root@localhost my-ubuntu]# docker build -t test_shell_entry .
Sending build context to Docker daemon 2.56kB
Step 1/2 : FROM ubuntu
latest: Pulling from library/ubuntu
4d32b49e2995: Pull complete
Digest: sha256:bea6d19168bbfd6af8d77c2cc3c572114eb5d113e6f422573c93cb605a0e2ffb
Status: Downloaded newer image for ubuntu:latest
---> ff0fea8310f3
Step 2/2 : ENTRYPOINT top -b
---> Running in 09805224b170
Removing intermediate container 09805224b170
---> e84ae9ca3b7e
Successfully built e84ae9ca3b7e
Successfully tagged test_shell_entry:latest
# 查看鏡像
[root@localhost my-ubuntu]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test_shell_entry latest e84ae9ca3b7e 44 seconds ago 72.8MB
ubuntu latest ff0fea8310f3 12 days ago 72.8MB
# 啟動容器
[root@localhost my-ubuntu]# docker run -ti --name test test_shell_entry
top - 19:39:21 up 3:27, 0 users, load average: 0.01, 0.06, 0.06
Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 1982.6 total, 500.1 free, 646.7 used, 835.7 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 1172.9 avail Mem
# docker stop 等了很久停止容器成功???
# 加入exec命令示例:
[root@localhost my-ubuntu]# cat Dockerfile
FROM ubuntu
ENTRYPOINT exec top -b
[root@localhost my-ubuntu]# docker build -t top .
[root@localhost my-ubuntu]# docker run -it --name test_exec_entry top
top - 19:45:01 up 3:33, 0 users, load average: 0.00, 0.02, 0.05
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 1982.6 total, 498.3 free, 648.3 used, 836.0 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 1171.1 avail Mem
# docker stop 迅速停止容器成功
注意:
- 如果
Dockerfile
中如果存在多個ENTRYPOINT
指令,僅最后一個生效。 - 當指定了
ENTRYPOINT
后,CMD
的含義就發生了改變,不再是直接的運行其命令,而是將CMD
的內容作為參數傳給ENTRYPOINT
指令。 - 要確保
docker stop
能正確終止一直執行的ENTRYPOINT
,需要使用exec
來啟動命令。
10、VOLUME指令
創建一個可以從本地主機或其他容器訪問的掛載點。
作用:
- 避免重要的數據,因容器重啟而丟失,這是非常致命的。(數據保存在主機)
- 避免容器不斷變大。(數據不在容器,不影響容器大小)
# 語法
VOLUME ["<路徑1>", "<路徑2>"...]
VOLUME <路徑>
# 案例
VOLUME ["/opt/log/"]
# 分別啟動兩個容器,實現文件共享
[root@localhost my-centos]# docker run -tid -v "$(pwd)"/target:/opt/log/ my-centos:v7-vol
[root@localhost my-centos]# docker run -tid -v "$(pwd)"/target:/app my-centos:v7-vol
# 雖然傳遞了文件到共享目錄,但是容器可寫層大小為0
[root@localhost target]# docker ps -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
3292f3b32b35 my-centos:vol "/bin/bash" 6 minutes ago Up 6 minutes youthful_banzai 0B (virtual 231MB)
ea772648df98 my-centos:vol "/bin/bash" 6 minutes ago Up 6 minutes focused_johnson 0B (virtual 231MB)
啟動容器 docker run 的時候,我們可以通過 -v 參數修改掛載點。
11、WORKDIR指令
指定工作目錄。用 WORKDIR
指定的工作目錄,會在構建鏡像的每一層中都存在(每一層都能訪問)。
# 語法
WORKDIR <工作目錄路徑>
# 案例
WORKDIR /home/hqs
WORKDIR apache
WORKDIR conf
# 登錄后路徑為:
[root@localhost my-centos]# docker exec -ti 6425fa66b9ec /bin/bash
[root@6425fa66b9ec conf]# pwd
/home/hqs/apache/conf
# 可與搭配ENV指令使用
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
# 最終路徑則為 /path/$DIRNAME。
注意:
- 一個
Dockerfile
可以多次使用WORKDIR
指令。 - 若提供相對路徑,該路徑將相對於前面的
WORKDIR
指令的路徑。 WORKDIR
指令可以在ENV
設置變量之后調用環境變量
12、其他指令(USER\ARG\SHELL)
USER
:設置運行鏡像時使用的用戶名(或UID)和可選的用戶組(或GID),Dockerfile中的任何RUN、CMD和ENTRYPOINT指令也會使用這個指定的身份。
ARG
:定義一個變量,用戶可以在使用--build-arg
SHELL
:用於指定shell格式以覆蓋默認的shell。
三、Dockerfile指令使用要點
1、exec和shell格式
RUN
、CMD
、ENTRYPOINT
指令都會用到 exec 和 shell 格式。
一般應該首選 exec
格式,這樣的指令可讀性更強,更容易理解。RUN
指令兩種格式均可。
如使用 CMD
指令為 ENTRYPOINT
指令提供默認參數,則兩個指令都應以 JSON 數組格式指定。
(1)exec格式指令直接調用命令,環境變量不會被shell解析
直接調用的案例:
# 創建上下文環境目錄和Dockerfile
[hqs-docker@hecs-hqs-01 tester01]$ cat Dockerfile
FROM ubuntu
ENV name Tester
ENTRYPOINT ["/bin/echo", "Hello,$name"]
# 構建鏡像
[hqs-docker@hecs-hqs-01 tester01]$ docker build -t tester01 .
# 運行容器
[hqs-docker@hecs-hqs-01 tester01]$ docker run tester01
Hello,$name 《——————參數中的環境變量沒有被shell解析
加入/bin/sh -c
可以讓環境變量被shell解析
# 創建上下文環境目錄和Dockerfile
[hqs-docker@hecs-hqs-01 ~]$ cp -r tester01/ tester02/
[hqs-docker@hecs-hqs-01 tester02]$ cat Dockerfile
FROM ubuntu
ENV name Tester02
ENTRYPOINT ["/bin/sh", "-c", "echo Hello,$name"]
# 構建鏡像
[hqs-docker@hecs-hqs-01 tester02]$ docker build -t tester02 .
# 運行容器
[hqs-docker@hecs-hqs-01 tester02]$ docker run tester02
Hello,Tester02 《——————參數中的環境變量已經被shell解析
(2)shell格式底層會默認調用/bin/sh -c執行命令
# 創建上下文環境目錄和Dockerfile
[hqs-docker@hecs-hqs-01 tester03]$ cat Dockerfile
FROM ubuntu
ENV name Tester03
ENTRYPOINT echo "Hello,$name!"
# 構建鏡像
[hqs-docker@hecs-hqs-01 tester03]$ docker build -t tester03-shell .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM ubuntu
---> ff0fea8310f3
Step 2/3 : ENV name Tester03
---> Running in d2b492215377
Removing intermediate container d2b492215377
---> 21ca9abdb792
Step 3/3 : ENTRYPOINT echo "Hello,$name!"
---> Running in 8793060549fa
Removing intermediate container 8793060549fa
---> 98d0dde74246
Successfully built 98d0dde74246
Successfully tagged tester03-shell:latest
# 運行容器
[hqs-docker@hecs-hqs-01 tester03]$ docker run tester03-shell
Hello,Tester03! 《————環境變量已經被shell解析
(3)exec格式沒有運行bash和sh的開銷
可以在沒有bash和sh的鏡像中運行,如scratch
# 案例1:
FROM scratch
ADD centos-7-docker.tar.xz /
CMD ["/bin/bash"]
# exec案例:
# 構建上下文和dockerfile
[hqs-docker@hecs-hqs-01 hello01]$ cat Dockerfile
FROM scratch
COPY hello /
CMD ["/hello"]
# 編譯出hello文件
[hqs-docker@hecs-hqs-01 hello01]$ cat hello.c
#include<stdio.h>
void main (){
printf("hello docker\n");
}
[hqs-docker@hecs-hqs-01 hello01]$ gcc --static hello.c -o hello
# 構建鏡像
[hqs-docker@hecs-hqs-01 hello01]$ docker build -t hello01 .
# 容器運行
[hqs-docker@hecs-hqs-01 hello01]$ docker run hello01
hello docker
# shell案例:
# 構建上下文和dockerfile
[hqs-docker@hecs-hqs-01 ~]$ cp -r hello01/ hello02/
[hqs-docker@hecs-hqs-01 hello02]$ cat Dockerfile
FROM scratch
COPY hello /
CMD /hello
# 構建鏡像
[hqs-docker@hecs-hqs-01 hello02]$ docker build -t hello02 .
# 容器運行
[hqs-docker@hecs-hqs-01 hello02]$ docker run hello02
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/bin/sh": stat /bin/sh: no such file or directory: unknown.
2、RUN、CMD和ENTRYPOINT指令的區別和聯系
- 執行時間不一樣:
RUN
指令執行命令並創建新的鏡像層,RUN
先於CMD
或ENTRYPOINT
指令在構建鏡像時執行;而CMD
和ENTRYPOINT
指令在每次啟動容器時才執行。 - 功能不一樣:
RUN
經常用於安裝應用程序和軟件包,並被固化在所生成的鏡像中。CMD
指令的主要目的是為運行容器提供默認值,即默認執行的命令及其參數。 CMD
和ENTRYPOINT
執行區別:ENTRYPOINT
指令中的參數始終會被docker run
命令使用,不可改變;而CMD
指令中的額外參數可以在執行docker run
命令啟動容器時被動態替換掉,會被docker run
命令所覆蓋。CMD
和ENTRYPOINT
一起使用情況:ENTRYPOINT
指令作為可執行文件,而CMD
指令則為ENTRYPOINT
指令提供默認參數。CMD
和ENTRYPOINT
搭配使用要求:如果CMD指令省略可執行文件,必須指定ENTRYPOINT指令;- 必須使用
ENTRYPOINT
的情況:當容器作為可執行文件時,應該定義ENTRYPOINT
指令。
3、組合使用CMD和ENTRYPOINT指令
CMD
和 ENTRYPOINT
指令都可以定義運行容器時要執行的命令。組合使用規則:
Dockerfile
中應該至少定義一個CMD
或ENTRYPOINT
指令。(至少要有一個)- 將整個容器作為一個可執行文件時應當定義
ENTRYPOINT
指令。(必須定義ENTRYPOINT的情況) CMD
指令應為ENTRYPOINT
指令提供默認參數,或者用於容器中執行臨時命令。(兩個都有的情況)- 當使用替代參數運行容器時,CMD指令的定義將會被覆蓋。(docker run時指定參數的情況)
如果CMD指令從基礎鏡像定義,那么ENTRYPOINT指令的定義會將CMD指令重置為空值。在這種情形下,必須在當前鏡像中為CMD指令指定一個實際的值。(不太好理解)
四、Dockerfile構建案例
1、centos-nginx鏡像案例
在centos鏡像的基礎上安裝nginx服務器軟件構建出新的鏡像。
(1)顯示nginx首頁原本信息
# 1.構建上下文和所需的文件
[root@localhost ~]# mkdir dockerfile-test
[root@localhost ~]# cd dockerfile-test/
[root@localhost dockerfile-test]# touch nginx.repo
[root@localhost dockerfile-test]# touch Dockerfile
# 2.編輯Dockerfile
[root@localhost dockerfile-test]# cat Dockerfile
# 引入基礎鏡像
FROM centos
# 設置作者
LABEL maintainer='hqs'
# 清空repo文件
RUN rm -rf /etc/yum.repos.d/*
# 復制nginx.repo
COPY ./nginx.repo /etc/yum.repos.d
# 更新緩存
RUN yum makecache
# 安裝nginx
RUN yum install -y nginx
# 改NGINX首頁
# 聲明對外暴露的端口
EXPOSE 80
# 不以守護進程啟動nginx的命令
CMD ["nginx", "-g", "daemon off;"]
# 3.編輯的nginx.repo
[root@localhost dockerfile-test]# cat nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
# 4.鏡像構建時網絡問題處理(非必須)
# 報錯:WARNING: IPv4 forwarding is disabled. Networking will not work.
解決辦法:
vi /etc/sysctl.conf
net.ipv4.ip_forward=1 #添加這段代碼
# 重啟network服務
systemctl restart network
# 查看是否修改成功 (備注:返回1,就是成功)
[root@studyCMachine aaaaaaa]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
# 5.構建鏡像
[root@localhost dockerfile-test]# docker build -t centos-nginx-hqs:1.0 .
# 6.啟動容器
[root@localhost dockerfile-test]# docker run --rm -d -p 8000:80 --name my-nginx centos-nginx-hqs:1.0
e5be77c444922817b69acc15810d186634765d27ec0d8ea8e7b345acffc610fa
# 7.訪問驗證
[root@localhost dockerfile-test]# curl 127.0.0.1:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
或
在瀏覽器上訪問http://192.168.100.111:8000/
(2)修改nginx首頁信息
# 1.復制生成新的上下文環境和相關文件
[root@localhost ~]# cp -r dockerfile-test/ dockerfile-test2/
[root@localhost ~]# cd dockerfile-test2/
# 2.修改Dockerfile文件
[root@localhost dockerfile-test2]# vi Dockerfile
# 引入基礎鏡像
FROM centos
# 設置作者
LABEL maintainer='hqs'
# 清空repo文件
RUN rm -rf /etc/yum.repos.d/*
# 復制nginx.repo
COPY ./nginx.repo /etc/yum.repos.d
# 更新緩存
RUN yum makecache
# 安裝nginx
RUN yum install -y nginx
# 改NGINX首頁
RUN echo "Hello! Please test the nginx server " > /usr/share/nginx/html/index.html
# 聲明對外暴露的端口
EXPOSE 80
# 不以守護進程啟動nginx的命令
CMD ["nginx", "-g", "daemon off;"]
# 3.構建鏡像
[root@localhost dockerfile-test2]# docker build -t centos-nginx-hqs:2.0 .
# 4.啟動容器
[root@localhost dockerfile-test2]# docker run -d -p 8080:80 --name my-nginx-2 centos-nginx-hqs:2.0
749444ad2183c322978c7bf4d58c3f6a1ae617b8f49737d4a79d7ba2b2e76fea
# 5.驗證
[root@localhost dockerfile-test2]# curl 127.0.0.1:8080
Hello! Please test the nginx server
或
瀏覽器訪問:http://192.168.100.111:8080/
2、httpd服務構建
執行以下操作前,先創建上下文環境,拷貝repo文件
mkdir http1.0
cd http1.0
cp /etc/yum.repos.d/CentOS-Base.repo ./local.repo
(1)准備Dockerfile
FROM centos:centos7
LABEL maintainer='zrl'
RUN rm -rf /etc/yum.repos.d/*
COPY ./local.repo /etc/yum.repos.d
RUN yum makecache
RUN yum install -y httpd
EXPOSE 80
CMD ["-D", "FOREGROUND"]
ENTRYPOINT ["/usr/sbin/httpd"]
(2)構建鏡像和啟動容器
# 構建鏡像
[root@localhost http:v1.0]# docker build -t http:v1.0 .
# 啟動容器
[root@localhost http:v1.0]# docker run -tid --name web -p 8000:80 http:v1.0
18121bb92601e6a85d0c53b8bfe08338e5031f75a5e3a36078ae0ca898e89280
[root@localhost http:v1.0]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
18121bb92601 http:v1.0 "/usr/sbin/httpd -D …" 4 seconds ago Up 3 seconds 0.0.0.0:8000->80/tcp, :::8000->80/tcp web
# 訪問測試
http://10.10.10.111:8000/
3、httpd服務構建進階
centos7鏡像編寫dockerfile文件,構建http服務,上傳index.html文件到鏡像/var/www/html目錄,內容為"hello apache"
鏡像暴露80端口,且修改參數HOSTNAME為www.example.com,設置httpd服務前台啟動,容器將8080端口映射到容器內部80端口。
[root@localhost ~]# mkdir http2.0
[root@localhost yum.repos.d]# cp CentOS-Base.repo /root/http2.0/local.repo
# 編輯index.html文件
[root@localhost http2.0]# vi index.html
hello apache
# 編輯Dockerfile
[root@localhost http2.0]# vi Dockerfile
FROM centos:centos7
LABEL maintainer='zrl'
RUN rm -rf /etc/yum.repos.d/*
COPY ./local.repo /etc/yum.repos.d
RUN yum makecache
RUN yum install -y httpd
COPY ./index.html /var/www/html
EXPOSE 80
CMD ["-D", "FOREGROUND"]
ENTRYPOINT ["/usr/sbin/httpd"]
# 鏡像構建
[root@localhost http2.0]# docker build -t http:v2.0 .
# 啟動容器
[root@localhost http2.0]# docker run -tid --name http-1 -p 8080:80 -h www.example.com http:v2.0
784ce9055d90f41b4d5329783d29780d049a31cc0729e07ac5b45a8912d1158f
[root@localhost http2.0]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
784ce9055d90 http:v2.0 "/usr/sbin/httpd -D …" 15 seconds ago Up 14 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp http-1
# 查看hostname
[root@localhost http2.0]# docker inspect --format="{{.Config.Hostname}}" http-1
www.example.com
# 訪問網站
http://192.168.100.111:8080/
4、本地registry倉庫
(1)啟動registry私有倉庫容器
在本地docker宿主機上加載registry的tar包為registry:latest鏡像,並啟動為私有倉庫容器,將宿主機5000端口映射到容器的5000端口,倉庫命名為registry,設置為自啟動狀態。
# 上傳registry_latest.tar包
# 上傳完查看包的大小
[root@localhost ~]# du -sh registry_latest.tar
26M registry_latest.tar
# 加載鏡像
[root@localhost ~]# docker load -i registry_latest.tar
d9ff549177a9: Loading layer 4.671MB/4.671MB
f641ef7a37ad: Loading layer 1.587MB/1.587MB
d5974ddb5a45: Loading layer 20.08MB/20.08MB
5bbc5831d696: Loading layer 3.584kB/3.584kB
73d61bf022fd: Loading layer 2.048kB/2.048kB
Loaded image: registry:latest
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry latest f32a97de94e1 3 years ago 25.8MB
# 啟動容器
[root@localhost ~]# docker run -d -p 5000:5000 --restart=always --name myregistry -v /opt/data/registry:/var/lib/registry registry
e6384c131de47bdb2720c7a917fb5b1aea040f16f49d9c01c1bea54dc454e184
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e6384c131de4 registry "/entrypoint.sh /etc…" 5 seconds ago Up 4 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp myregistry
(2)上傳鏡像到本地registry倉庫
下載官方的centos:7鏡像,將該鏡像上傳到registry倉庫。
# 下載官方的centos:7鏡像
[root@localhost ~]# docker pull centos:7
7: Pulling from library/centos
Digest: sha256:9d4bcbbb213dfd745b58be38b13b996ebb5ac315fe75711bd618426a630e0987
Status: Downloaded newer image for centos:7
docker.io/library/centos:7
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos 7 eeb6ee3f44bd 9 months ago 204MB
# 給已有的鏡像打標簽符合自建注冊中心格式
[root@localhost ~]# docker tag centos:7 127.0.0.1:5000/centos:7
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
http v2.0 0af11e06a37c 33 minutes ago 768MB
127.0.0.1:5000/centos 7 eeb6ee3f44bd 9 months ago 204MB
centos 7 eeb6ee3f44bd 9 months ago 204MB
# 執行鏡像上傳
[root@localhost ~]# docker push 127.0.0.1:5000/centos:7
The push refers to repository [127.0.0.1:5000/centos]
174f56854903: Pushed
7: digest: sha256:dead07b4d8ed7e29e98de0f4504d87e8880d4347859d839686a31da35a3b532f size: 529
# 執行查看測試
[root@localhost ~]# curl http://127.0.0.1:5000/v2/_catalog
{"repositories":["centos"]}
(3)遠程上傳鏡像到本地registry倉庫
在遠程docker主機上下載registry的centos:7鏡像,將該鏡像運行為一個容器,命名為centos-7,在容器安裝net-tools工具。
將容器打包封裝為新的鏡像,命名為centos:net,重新上傳到registry。
# 1.克隆一個新的虛擬機(要先把原主機關閉,建議先創建克隆再做其他考試操作)
# 啟動兩台虛擬機
# 2.修改第二台虛擬機網卡,給一個新的IP,並重啟網絡
[root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens33
IPADDR=192.168.100.118
[root@localhost ~]# systemctl restart network
# 3.第二台主機清理環境(如果需要再做)
[root@localhost ~]# docker rm -f $(docker ps -qa)
[root@localhost ~]# docker rmi 127.0.0.1:5000/centos:7
[root@localhost ~]# docker rmi 192.168.100.111:5000/centos:7
[root@localhost ~]# docker rmi centos:7
[root@localhost ~]# docker images
# 4.檢查第一台虛擬機倉庫(保證是up狀態)
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
decca5395c07 registry "/entrypoint.sh /etc…" 9 minutes ago Up 3 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp myregistry
# 5.第二台主機配置注冊中心地址,並重啟
[root@localhost ~]# vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://nxwgbmaq.mirror.aliyuncs.com"],
"insecure-registries":["192.168.100.111:5000"]
}
[root@localhost ~]# systemctl restart docker
# 6.第二台主機上拉取遠程主機倉庫中鏡像
[root@localhost ~]# docker pull 192.168.100.111:5000/centos:7
7: Pulling from centos
2d473b07cdd5: Pull complete
Digest: sha256:dead07b4d8ed7e29e98de0f4504d87e8880d4347859d839686a31da35a3b532f
Status: Downloaded newer image for 192.168.100.111:5000/centos:7
192.168.100.111:5000/centos:7
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.100.111:5000/centos 7 eeb6ee3f44bd 9 months ago 204MB
# 7.第二台主機上拉起新容器
[root@localhost ~]# docker run -tid --name centos-7 192.168.100.111:5000/centos:7 /bin/bash
e22ff1d463a0bf8f8e4de027b9e046d71d34f7726197f15b85d01ec9e801ad0f
# 8.容器中安裝net-tools工具
[root@localhost ~]# docker exec -ti centos-7 /bin/bash
[root@e22ff1d463a0 /]# yum install -y net-tools
# 9.將容器打包封裝為新的鏡像
[root@localhost ~]# docker commit centos-7 centos:net
sha256:8836665003fa0d465aa3bce1b28951ae60d2bbf6e2bed606e0ef8ac04b275ed8
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos net 8836665003fa 6 seconds ago 375MB
192.168.100.111:5000/centos 7 eeb6ee3f44bd 9 months ago 204MB
# 10.重新上傳到registry
# 先打標簽
[root@localhost ~]# docker tag centos:net 192.168.100.111:5000/centos:net
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos net 8836665003fa About a minute ago 375MB
192.168.100.111:5000/cent
5、ubuntu 運行nginx鏡像
(1)創建上下文環境目錄和Dockerfile
[root@localhost ~]# mkdir ubuntu-nginx
[root@localhost ubuntu-nginx]# vi Dockerfile
FROM ubuntu
LABEL maintainer='hqs'
ADD sources.list /etc/apt/
RUN apt-get -y update
RUN apt-get install -y nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
(2)創建配置sources.list文件
在編寫sources.list文件時,需要先確認當前ubuntu系統版本:
[root@localhost ~]# docker run -ti ubuntu /bin/bash
root@cd939f604f1a:/# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS"
由此得知,當前鏡像的系統版本為 20.04
。
編寫sources.list文件如下:
[root@localhost ubuntu-nginx]# vi sources.list
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
(3)構建鏡像,啟動容器
# 構建鏡像
[root@localhost ubuntu-nginx]# docker build -t my-ubuntu/nginx:v1 .
[root@localhost ubuntu-nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-ubuntu/nginx v1 9e93b415f52a 8 minutes ago 201MB
# 啟動容器
[root@localhost ubuntu-nginx]# docker run -d -p 8080:80 my-ubuntu/nginx:v1
# 訪問網頁:http://192.168.200.103:8080/ ,即可訪問網站首頁。