镜像制作
说明:
Docker 镜像中有没有内核
- 镜像本身很小,最小只有几M,当镜像作为容器启动后,直接使用物理机的内核,而镜像本身只提供rootfs(保证系统基本运行的文件系统:/dev、proc、/bin、/etc等,容器中boot目录为空)
- 由于没有内核,不会直接调用物理硬件,所以也不会涉及到硬件驱动,因此也无需容器内拥有自已的内核和驱动。而如果使用虚拟机技术,对应每个虚拟机都有自已独立的内核
容器中必须启动前台运行的程序:
- 容器如果希望启动后能持续运行,就必须有一个能前台持续运行的进程,如果在容器中启动传统的服务,如:httpd,php-fpm等均为后台进程模式运行,就导致docker在前台没有运行的应用,这样的容器启动后会立即退出。所以一般会将服务程序以前台方式运行,对于有一些可能不知道怎么实现前台运行的程序,只需要在你启动的该程序之后添加类似于tail、top这种可以前台运行的程序即可.比较常用的方法,如 tail -f /etc/hosts
例:
httpd
ENTRYPOINT [ "/usr/sbin/apache2" ]
CMD ["-D", "FOREGROUND"]
nginx
ENTRYPOINT [ "/usr/sbin/nginx", "-g", "daemon off;" ]
用脚本运行容器,不能前台执行的程序,就手动创造一个前台
cat run_haproxy.sh
#!/bin/bash
haproxy -f /etc/haproxy/haproxy.cfg
tail -f /etc/hosts
tail -n1 Dockerfile
CMD ["run_haproxy.sh"]
镜像制作方法:
镜像制作类似于虚拟机的镜像(模版)制作,即按照公司的实际业务需求将需要安装的软件、相关配置等基础环境配置完成,然后将其做成镜像,最后再批量从镜像批量生成容器实例,这样可以极大的简化相同环境的部署工作
第一种:基于原有容器做修改(手动制作)
- 用网上已有的基础镜像,运行一个容器后,在这个容器里面做修改,修改完成后制作成新镜像,最后推的镜像仓库
- docker commit 通过修改现有容器,将之手动构建为镜像
第二种:写脚本构建新的镜像(自动制作,企业常用)
- 编写构建镜像的dokcerfile文件(类似脚本),构建完成后可以运行容器或推到镜像仓库
- docker build 通过Dockerfile文件,批量构建为镜像
手动构建镜像:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
选项:
-a string 镜像作者信息
-c 指令 添加一些dockerfile的指令
-m string 提交信息,一般记录有哪些修改
-p 提交镜像的时候,暂停容器,默认是暂停
说明:
- 制作镜像和容器状态无关,停止状态也可以制作镜像
- 如果没有指定[镜像名[:版本]],REPOSITORY和TAG都为<none>
- 提交的时候标记TAG号:
- 生产当中常用,后期可以根据TAG标记创建不同版本的镜像以及创建不同版本的容器
构建镜像大致步骤:
- 下载一个系统的官方基础镜像,如: CentOS 或 Ubuntu
- 基于基础镜像启动一个容器,并进入到容器
- 在容器里面做配置操作
- 安装基础命令
- 配置运行环境
- 安装服务和配置服务
- 放业务程序代码
- 提交为一个新镜像 docker commit
- 基于自己的的镜像创建容器并测试访问
基于busybox制作httpd镜像
bosybox自带一个httpd程序,可直接运行web
httpd -h
-i
-f 不以守护经常运行
-v[v] 显示访问日志
-p [ip:]端口 设置监听端口
-u user[:组] 程序运行身份
-r 开启basic认证
-h 目录 指定家目录,默认当前目录
-c 文件 配置文件路径,默认{/etc、home}/httpd.conf
-m string md5加密
-e string html
-d url
1)准备基础镜像并运行容器
docker run -it --name b1 busybox
mkdir -p /data/html
echo 'test website' > /data/html/index.html
2)制作v1版httpd
#-a说明作者,-c在容器运行时,执行httpd命令设置家目录并前台执行,暴露80端口,基于b1容器,制作成httpd-busybox:v1.0
docker commit -a "hj" -c 'CMD /bin/httpd -fv -h /data/html' -c "EXPOSE 80" b1 httpd-busybox:v1.0
3)制作v2版httpd
#制作时未暴露端口,运行时要-p,-P不能成功(因为必须在制作时指定暴露端口才能使用-P)
docker commit -a "hj" b1 httpd-busybox:v2.0
制作tomcat
1)运行官方tomcat
docker run -td --name t1 -p 8080:8080 tomcat
docker exec -it t1 bash
cp -a webapps.dist/* webapps/
exit
2)制作v1版
docker commit -m "hj's web app" -a 'www.hj.com' t1 hj-tomcat:v1.0
docker images
docker run -td --name hj-t1 -p 81:8080 hj-tomcat:v1.0
curl 2.2.2.42:81
基于Ubuntu制作nginx
1)运行Ubuntu并换源
docker run -it -p 80 -p 443 --name ngx_ub ubuntu
#由于docker安装的系统,都没有设置时区,所有系统都要设置时区
echo 'TZ="Asia/Shanghai"; export TZ' >> /etc/profile
. /etc/profile
或者:
rm -rf /etc/localtime
ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
2)nginx设置
docker exec -it ngx_ub bash
sed -i.bak 's@:.*/@://mirrors.aliyun.com/ubuntu/@g' /etc/apt/sources.list
apt update
apt install -y nginx 一般安装完后建议清除apt/yum缓冲,节约空间
#docker安装的nginx的家目录在/var/www/html
grep root /etc/nginx/sites-enabled/default
echo Nginx Website in Docker > /var/www/html/index.html
3)制作nginx镜像,并启动
docker commit -c "EXPOSE 80 443" ngx_ub hj-nginx:v1
#nginx的-g是设置配置文件,在配置文件中关闭守护进程
docker run -d -p 80 -p 443 --name hj-n1 hj-nginx:v1 nginx -g 'daemon off;'
centos编译安装nginx
1)运行centos
docker run -it --name ngx_ct centos bash
cd /etc/yum.repos.d/;rm -rf *
curl -G http://mirrors.aliyun.com/repo/Centos-8.repo > ctos8.repo
2)编译安装
docker exec -it ngx_ct bash
cd /opt
yum install -y gcc gcc-c++ pcre-devel zlib-devel openssl-devel vim wget make
wget http://nginx.org/download/nginx-1.20.1.tar.gz
tar xf nginx-1.20.1.tar.gz
cd nginx-1.20.1
./configure --prefix=/opt/nginx
make && make install
cd ..
rm -rf nginx-*
yum clean all
3)配置nginx
cd nginx
sed -i '3 a daemon off;' conf/nginx.conf
echo nginx web > html/index.html
4)制作镜像
docker commit -m "hj's src nginx" -c "CMD /opt/nginx/sbin/nginx" ngx_ct hj-src-nginx:v1
docker run -d -p 80:80 --name hj-n3 hj-src-nginx:v1
自动构建镜像:
介绍:
- DockerFile是一种被Docker程序解释执行的脚本,由一条条的命令组成的,每条命令对应linux下面的一条命令,Docker程序将这些DockerFile指令再翻译成真正的linux命令,其有自己的书写方式和支持的命令
- Docker守护程序Dockerfile逐一运行指令,如有必要,将每个指令的结果提交到新镜像,然后最终输出新镜像的ID。Docker守护程序将自动清理之前发送的上下文
- 每一条指令时独立运行的,并会导致创建新镜像,也就是说上一条指令与下一条指令不关联
- 在构建的过程中,会利用缓存。已经运行过的指令会加载到缓存中,当新指令来时,只会运行指令,原来有的直接使用缓存中的。用于加速docker build过程
镜像分层结构:
- 官方系统镜像
- 个性化修改
- 基础环境:nginx、php、jdk等
- 网页资源、代码、tomcat
例:
按照业务类型或系统类型等方式划分创建目录环境,方便后期镜像比较多的时候进行分类
mkdir /data/dockerfile/{web/{nginx,apache,tomcat,jdk},system/{centos,ubuntu,alpine,debian}} -p
Dockerfile 构建镜像过程:
- 从基础镜像运行一个容器
- 执行一条指令,对容器做出修改
- 执行类似docker commit 的操作,提交一个新的中间镜像层(可以利用中间层镜像创建容器进行调试和排错)
- 再基于刚提交的镜像运行一个新容器
- 执行Dockerfile中的下一条指令,直至所有指令执行完毕
Dockerfile 文件格式:
文件名就必须叫Dockerfile,且有语法格式,文件名大小写铭感
官方说明:https://docs.docker.com/engine/reference/builder/
系统帮助: man 5 dockerfile
文件说明:
- 每一行以Dockerfile的指令开头,指令不区分大小写,但是惯例使用大写
- 使用 # 开始作为注释
- 每一行只支持一条指令,每条指令可以携带多个参数
- 指令按文件的顺序从上至下进行执行
- 每个指令的执行会生成一个新的镜像层,为了减少分层和镜像大小,尽可能将多条指令合并成一条指令
制作镜像一般可能需要反复多次,每次执行dockfile都按顺序执行,从头开始,已经执行过的指令已经缓存,不需要再执行,如果后续有一行新的指令没执行过,其往后的指令将会重新执行,所以为加速镜像制作,将最常变化的内容放下dockerfile的文件的后面
docker build命令:
使用Dockerfile时,必须使用此命令进行自动构建成镜像
docker build [OPTIONS] PATH | URL | -
选项:
路径|url路径|- -表示从标准输入获取
-f 文件 不指定时,默认在当前目录下找Dockerfile,若指定,则在指定目录构建
--force-rm 总是删除中间层容器,创建镜像失败时,删除临时容器
--no-cache 不使用之前构建的镜像缓存
-q 静默模式构建,不输出内容
--rm=true 镜像创建成功后,删除临时容器
-t 设置构建的新镜像名:项目名/镜像名:版本
例:
#当前目录构建
docker build -t xxx:v1 .
#指定文件构建
docker build -f /opt/xxx -t xxx:v1 .
docker history命令:
用于查看镜像的构建过程
例:
#查看官方nginx镜像的构建过程
docker history nginx:latest
Dockerfile相关指令:
ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
FROM指令:
指定基础镜像,在基础镜像上进行定制
FROM就是指定基础镜像,此指令通常必需放在Dockerfile文件第一个非注释行。后续的指令都是运行于此基准镜像所提供的运行环境
基础镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,在其不存在时,则会从Docker Hub Registry 上拉取所需的镜像文件.如果找不到指定的镜像文件,docker build会返回一个错误信息
用法:
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
说明:
- --platform 指定镜像的平台,比如: linux/amd64, linux/arm64, or windows/amd64
- tag 和 digest是可选项,如果不指定,默认为latest
关于FROM scratsch镜像:
scratch是祖宗镜像,所有的镜像都源于此创建,也是一个空镜像,一般要从零开始构建镜像时,用此方法
参考链接:
- https://hub.docker.com/_/scratch?tab=description
- https://docs.docker.com/develop/develop-images/baseimages/
该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像
该镜像在构建基础镜像(如debian和busybox)或超最小镜像(仅包含一个二进制文件及其所需内容,如:hello-world)的上下文中最有用
用法:
FROM scratch 所有镜像的起源镜像,相当于Object类
FROM ubuntu
FROM ubuntu:bionic
FROM debian:buster-slim
LABEL指令:
指定镜像元数据:作者...,以键值对方式
一个镜像可以有多个label,可以写在一行中,即多标签写法,可以减少镜像的的大小,“\”符号换行
docker inspect查看到的label就是此处定义的
LABEL <key>=<value> <key>=<value> <key>=<value> ...
例:
LABEL author="hj" version="1.0"
或者:
LABEL auname="hj" \
version="1.0"
RUN指令:
在构建镜像阶段需要执行FROM,指定镜像所支持的shell命令,一般各种基础镜像都支持普通shell命令
注意:
- RUN可以写多个,每一个RUN指令都会建立一个镜像层,所以尽可能合并成一条指令,比如将多个shell命令通过“&&”符号连接一起成为在一条指令
- 每个RUN都是独立运行的,和前一个RUN无关
用法用法:
#shell格式: 支持环境变量
RUN <命令>
#exec格式:
RUN ["可执行文件", "参数1", "参数2"]
#exec格式指定其他shell:
RUN ["/bin/bash","-c","echo hello wang"]
#dockerfile:1.2加入,允许挂载文件、secrets、ssh-agent套接字、持久包管理缓存加快构建速度
RUN --mount=[type=<TYPE>][,选项1=参数,...]
类型:
bind #挂载文件系统
选项:
target #挂载目标路径
source #from中的源路径。默认为 from 的根目录
from #源路径来源,也就是上个镜像层的文件系统,from指定的镜像处得来
rw #读写,默认只读权限挂载
cache #挂载一个临时目录来缓存编译器和包管理器的目录
选项:
id #标识符,默认是target
target #挂载路径
ro #只读权限
sharing #参数为:shared 、private或locked之一。默认shared,缓存装载可由多个写入器同时使用。private在有多个写入器时创建新装载。 locked暂停第二个写入器,直到第一个写入器释放装载
from #构建阶段以用作缓存装载的基础。默认为空目录
source #要挂载的from中的子路径。默认为 from 的根目录
mode #目录权限,默认0755
uid、gid #用户id,默认0
secret #允许生成容器访问私钥等安全文件
选项:
id
target #默认/run/secrets/ + id
required #如果设置为 true ,则当密钥不可用时,指令会出错。默认为 false
mode、uid、gid
ssh #允许生成容器通过SSH代理访问SSH密钥,并支持密码
tmpfs #挂载 tmpfs文件系统
选项:
size #文件系统大小(使用的是内存,也就是内存使用大小)
target #挂载目标路径
说明:
- shell格式中,<command>通常是一个shell命令,且以 "/bin/sh -c” 来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号
- exec格式中的参数是一个JSON格式的数组,其中<executable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而,此种格式指定的命令不会以"/bin/sh -c"来发起,因此常见的shell操作如变量替换、通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可替换为类似下面的格式
RUN ["/bin/bash", "-c", "<executable>", "<param1>"]
例:
RUN echo "<h1>web 123</h1>" >/var/www/html/index.html
RUN ["/bin/bash","-c","echo 123456"]
RUN yum install -y epel-release \
&& yum install -y nginx \
&& echo "hj's web page" > /usr/share/nginx/html/index.html
例2: 缓存go包
FROM golang
RUN --mount=type=cache,target=/root/.cache/go-build \
go build ...
例3:缓存 apt 包
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt update && apt-get --no-install-recommends install -y gcc
例4:容器访问s3
vim Dockerfile
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws s3 cp s3://... ...
docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
例5:运行多行脚本(高级用法)
#方法1
FROM debian
RUN <<EOT bash
set -ex
apt-get update
apt-get install -y vim
EOT
#方法2
FROM debian
RUN <<EOT
mkdir -p foo/bar
EOT
#方法3
FROM python:3.6
RUN <<EOT
#!/usr/bin/env python
print("hello world")
EOT
#方法4: 创建脚本,并运行
FROM alpine
COPY <<-"EOT" /app/script.sh
echo hello ${FOO}
EOT
RUN FOO=abc ash /app/script.sh
ENV指令:
以定义环境变量和值,会被后续指令(如:ENV,ADD,COPY,RUN等)通过\(KEY或\){KEY}进行引用,并在容器运行时保持
用法:
格式1:
此格式只能对一个key赋值,<key>之后的所有内容均会被视作其<value>的内容
ENV <key> <value>
格式2:
此格式可以支持多个key赋值,定义多个变量建议使用,减少镜像层。还是 \ 位换行
ENV <key1>=<value1> <key2>=<value2> \
<key3>=<value3> ...
一次性变量:
RUN key=value cmd
高级变量赋值:
${key:-v1} key变量不存在,就赋值v1
${key:+v2} key变量存在,就赋值v2
引用变量:
RUN $key 或 RUN ${key}
例:
ENV user=hj pswd=123 \
xxx=123
env a1 123
ENV a2 qwe
COPY指令:
复制本地宿主机的到容器中
参考链接:https://golang.org/pkg/path/filepath/#Match
用法:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] 适用于路径有空白字符的情况
COPY --from=分层镜像名 <src> <dst>
说明:
- 可以是多个,可以使用通配符,通配符规则满足Go语言的filepath.Match规则
- 只能复制在Dockerfile文件同级目录下的文件,也就是以Dockerfile存放目录为根目录
- 如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制
- 如果指定了多个,或在中使用了通配符,则dest必须是一个目录,且必须以 / 结尾
- 可以是绝对路径或者是 WORKDIR 指定的相对路径
- 使用COPY指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。也就是说可以加速构建速度,一般可用此优化镜像构建
- 如果事先不存在,它将会被自动创建,这包括其父目录路径,即递归创建目录
例1:
COPY test.txt /root/ 本地文件到容器root目录
例2:
FROM node:18 AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
例3:java分层构建
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package
FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps
ADD指令:
增强版的COPY,不仅支持COPY,还支持自动解缩。可以将复制指定的到容器中的
用法:
#Dockerfile 1.3 开始支持 --chmod
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
#git克隆,无需在容器内执行git clone。--keep-git-dir配置是否创建.git
ADD [--keep-git-dir=<boolean>] git仓库 <dir>
说明:
- 可以是Dockerfile所在目录的一个相对路径;也可是一个 URL;还可是一个 tar 文件(自动解压)
- 可以是绝对路径或者是 WORKDIR 指定的相对路径
- 如果是目录,只复制目录中的内容,而非目录本身
- 如果是一个 URL ,下载后的文件权限自动设置为 600
- 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为,如果以“\”结尾,则文件名URL指定的文件将被直接下载并保存为/<filename>
- 如果是一个本地文件系统上的打包文件,如: gz, bz2 ,xz ,它将被解包 ,其行为类似于"tar -x"命令, 但是通过URL获取到的tar文件将不会自动展开
- 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则其被视作一个普通文件,的内容将被直接写入到
例:
ADD a.tgz /opt/ 文件传到容器的/opt/目录后自动解压
CMD指令:
容器中需要持续运行的进程一般只有一个,CMD用来指定启动容器时默认执行的一个命令,且其运行结束后,容器也会停止,所以一般CMD 指定的命令为持续运行且为前台命令
RUN是构建时运行的指令,CMD是容器运行时运行的指令
如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么开启容器时就会使用执行CMD指定的默认的命令
每个Dockerfile只能有一条CMD命令。如指定了多条,只有最后一条被执行
如果启动容器时用 docker run xxx cmd 指定运行的命令,则会覆盖 CMD 指定的命令
用法:
#exec格式执行:推荐使用,且不支持环境变量
CMD ["命令","param1","param2"]
#sh格式执行:交互式使用,支持环境变量
CMD command param1 param2
#给entrypoint命令的传递参数:
CMD ["param1","param2"]
例:
CMD ["nginx","-g","daemon off;"]
CMD ["curl","-s","https://ip.cn"] 同一文件中,只有此命令生效
ENTRYPOINT指令:
类似CMD,配置容器启动后执行的命令及参数
如果与cmd命令共存,则cmd的内容当做参数传递给entrypoint
docker run执行时,可手动传参,其优先级顺序:
-e > --env-file > ENV
用法:
#exc执行
ENTRYPOINT ["executable", "param1", "param2"]
#sh执行
ENTRYPOINT command param1 param2
说明:
- docker run -e/--env-file 不能覆盖ENTRYPOINT的参数,而是把传递的参数追加在ENTRYPOINT原有的参数后面
- 如果docker run 后面没有额外参数,但是dockerfile中的CMD里有(即上面CMD的第三种用法),即Dockerfile中即有CMD也有ENTRYPOINT,那么CMD的全部内容会作为ENTRYPOINT的参数
- 如果docker run 后面有额外参数,同时Dockerfile中即有CMD也有ENTRYPOINT,那么docker run 后面的参数覆盖掉CMD参数内容,最终作为ENTRYPOINT的参数
- 可以通过docker run --entrypoint string 参数在运行时替换,注意string不要加空格
- 使用CMD要在运行时重新写命令本身,然后在后面才能追加运行参数,ENTRYPOINT则可以运行时无需重写命令就可以直接接受新参数
- 每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效
例:
#CMD的内容做参数传给ENTRYPOINT的命令
CMD ["-g","daemon off;"]
ENTRYPOINT ["nginx"]
#命令行指定,优先级最高
docker run -d --entrypoint tail hj-nginx -f /etc/hosts
例:脚本指定环境变量动态生成配置文件内容(高级玩法)
#dockerfile
vim Dockerfile
FROM nginx:1.16-alpine
LABEL maintainer="hj.com"
ENV DOC_ROOT='/data/website/'
ADD index.html ${DOC_ROOT}
ADD entrypoint.sh /bin/
EXPOSE 80/tcp 8080
RUN apk nginx
CMD ["/usr/sbin/nginx","-g", "daemon off;"]
ENTRYPOINT [ "/bin/entrypoint.sh"]
#entrypoint脚本,动态配置nginx并启动
vim entrypoint.sh
#!/bin/sh
cat > /etc/nginx/conf.d/www.conf <<EOF
server {
server_name ${HOSTNAME};
listen ${IP:-0.0.0.0}:${PORT:-80};
root ${DOC_ROOT:-/usr/share/nginx/html};
}
EOF
exec "$@"
解析:
- CMD的内容变成参数,传给ENTRYPOINT,而后者执行entrypoint.sh脚本时,把CMD的内容当做变量传给正在执行的entrypoint.sh,此时“$@”变量获取的参数就是:/usr/sbin/nginx -g daemon off;
- entrypoint.sh脚本执行时,从上往下运行,基于shell的变量高级赋值,生成配置文件,最后exec执行有$@获取的参数,也就是:
exec /usr/sbin/nginx -g daemon off
ARG指令:
在build阶段指定变量,和ENV不同的是,容器运行时不会存在这些环境变量,只在构建时生效
用法:
ARG <name>[=<default value>]
docker build --build-arg <参数名>=<值>
说明:
- 如果变量名与ENV同名,则ENV覆盖ARG
- 支持做最开始的变量定义,也就是写在FROM之前
例:
vim base/Dockerfile
FROM busybox
ARG author=hj
docker build --build-arg author="hj-v1" -t 镜像
例:
vim xx/Dockerfile
ARG version=1.0
FROM base:${version}
VOLUME指令:
VOLUME实现的是匿名数据卷,无法指定宿主机路径和容器目录的挂载关系
通过docker rm -fv <容器ID> 可以删除容器的同时删除VOLUME指定的卷
宿主机目录为:
/var/lib/docker/volumes/<volume_id>/_data
指定容器内的挂载目录后,物理机自动在该路径下创建目录,卷id是随机生成的,_data就是容器内挂载的目录名,卷id以后就是挂载点根目录
当容器删除时,容器内在/data/下存的文件,还保留在物理机的/var/lib/docker/volumes/卷id/_data/
用法:
VOLUME <容器内路径>
VOLUME ["<容器内路径1>", "<容器内路径2>"...]
docker rm -v 容器 删除容器时,删除对应的匿名卷
例:
VOLUME ["/data","/data2"]
EXPOSE指令:
EXPOSE仅仅是声明容器打算使用什么端口而已,并不会真正暴露端口,即不会自动在宿主进行端口映射
启动容器是还是需要-p/-P来指明与物理机映射的端口才能真正分配端口
注意:
- 即使 Dockerfile没有EXPOSE 端口指令,也可以通过docker run -p 临时暴露容器内程序真正监听的端口,所以EXPOSE 相当于指定默认的暴露端口,可以通过docker run -P 进行真正暴露
用法:
EXPOSE <port>[/ <protocol>] [<port>[/ <protocol>] ..]
说明:
- 可为tcp或udp二者之一,默认为TCP协议
例:
EXPOSE 80 443/tcp
EXPOSE 3306
WORKDIR指令:
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,当容器运行后,进入容器内WORKDIR指定的默认目录
如果目录不存在,会自动创建
用法:
WORKDIR /path/to/workdir
说明:
- 可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径
例:
WORKDIR /web
RUN echo 123456 > ts.txt
WORKDIR /a
WORKDIR b 切换多级目录,/a/b
ONBUILD指令:
可以用来配置构建当前镜像的子镜像时,会自动触发执行的指令,但在当前镜像构建时,并不会执行,即延迟到子镜像构建时才执行
用法:
OPTIONS 指令 命令
例:
#A镜像为父镜像: 不会在父镜像执行
FROM ubuntu
ONBUILD RUN echo 123456 > 1.txt
ONBUILD RUN cat /etc/hosts
#B镜像为子镜像,引用B:
FROM A
#引用后,首先执行父镜像的ONBUILD指令的内容
RUN echo 123456 > 1.txt
RUN cat /etc/hosts
USER指令:
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户
用户必须是事先就有的,否则报错,没有指定时默认root
用法:
USER <user>[:<group>]
USER <UID>[:<GID>]
例:
RUN useradd -r mysql
USER mysql
HEALTHCHECKH指令:
容器健康检查,有错误是自动删除容器重新创建
用法:
#设置检查容器健康状况的命令
HEALTHCHECK [选项] CMD <命令>
选项:
--interval=间隔 两次检测时间间隔
--timeout=时长 超时时长,超过为失败,默认30s
--retries=次数 失败次数
--start-period=时间 检查开始的时间,默认0s(立即检查)
#如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK NONE
返回状态结果说明:
- 0: 健康状态
- 1: 非正常工作状态
- 2: 不使用此退出状态码
例:
FROM ubuntu
RUN apt install -y nginx
#容器健康检测,5秒一次,超时3秒,执行curl命令检查
HEALTHCHECK --interval 5s --timeout=3s CMD curl -fs http://127.0.0.1 ||exit 1
STOPSIGNALZ指令:
设置容器退出时的信号,支持系统所有信号
用法:
STOPSIGNAL 信号
SHELL指令:
更改容器的默认shell,一般默认,除非win系统时改成cmd或者powershell
linux的默认使用sh -c ,win默认使用cmd /s /c
SHELL指令可以出现多次。每个SHELL指令将覆盖所有先前的SHELL指令,并影响所有后续的指令
用法:
SHELL ["executable", "parameters"]
例:
FROM microsoft/windowsservercore
RUM echo default
RUN powershell -command Write-Host default
#powershell模式
SHELL ["powershell", "-command"]
RUN Write-Host hello
#cmd模式
SHELL ["cmd", "/S", "/C"]
RUN echo hello
#sh模式
SHELL ["sh","-c"]
RUN echo shell
.dockerignore文件:
当用copy和add复制文件时,通配符会把所有文件都复制,此时可以在.dockerignore文件中定义哪些文件不需要复制(此文件只能定义忽略的)
通配符规则基于go语言
官方文档:https://docs.docker.com/engine/reference/builder/#dockerignore-file
go语言通配符规则:https://golang.org/pkg/path/filepath/#Match
完整语法:
# | 以#开头的行为注释 |
* | 任意字符 |
? | 任意单个字符 |
\ | 表示 \转义 |
** | 取反通配符,**/*.go,表示任何目录下的.go结尾文件都不处理 |
! | 表示取反,可用于排除例外情况 |
例:
vim .dockerignore
! readme* 不要所有的readme开头的文件
*.txt #.txt文件都忽略