dockerfile 镜像构建


docker镜像的构建:commit与dockerfile

构建镜像
docker容器与镜像
当创建一个新容器时,docker会构建出一个镜像栈,每个只读镜像层都是只读的,并且以后永远不会变化,最后在栈的最顶端添加一个读写层。
docker commit(不推荐) 或者 dockerfile + docker build

1 docker commit方式构建镜像
本质上是以新建的容器来构建镜像。
1)创建账号 https://hub.docker.com/
mazhen11
1805354On****
2)docker login登陆
#docker logout 登出
3)创建一个新容器 docker run -i -t ubuntu /bin/bash
4)容器中安装apache
apt-get -yqq update
apt-get -y install apache2
目前:我们启动了一个容器,并且安装了apache。为了不必每次都创建一个新的容器并在此在里面安装apache,所以想要保存该容器的当前状态。
5)执行exit 退出该容器
6)docker ps -l -q 获取刚创建容器的ID
7)docker commit 容器id mazhen11/apache2
说明:docker commit仅仅提交了创建容器镜像于容器当前状态之间有差异的部分,该更新非常轻量。
8)docker images mazhen11/apache2 查看新创建的镜像

也可以再提交镜像是指定更多的数据,如
docker commit -m"a new custom image" -a"mazhen" 43e83b7ccbf5 mazhen11/apache2:webservew
-m"a new custom image"  :提交信息
-a"mazhen" :作者信息
43e83b7ccbf5 镜像id
mazhen11/apache2:webservew :用户名/仓库名 webservew标签

docker inspect mazhen11/apache2:webservew查看新镜像的详细信息

dockerfile方式构建镜像
1)宿主机中创建 构建上下文
mkdir static_web
cd static_web
touch Dockerfile
2)向dockerfile中写内容
# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER mazhen11 "mz@example.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'hi, i am in your container' > /usr/share/nginx/html/index.html
EXPOSE 80

说明:MAINTAINER指令,该镜像的作者以及电子邮件
RUN指令默认会在shell里面使用命令包装器/bin/sh -c来执行,如果在不支持shell的平台或不希望在shell中运行,可以使用exec格式的RUN指令,如:
RUN ["apt-get", " install", " -y", " nginx"]
EXPOSE:告诉docker该容器内的应用程序将会使用容器的指定端口,但docker并不会自动打开该端口,需要在run容器是指定打开的端口。

基本语法:
1)每条指令都必须为大写字母,如FROM,且后面要跟随一个参数,如ubuntu:14.04
2)每个dockerfile的第一条指令必须是FROM。FROM指定一个已存在的镜像作为基础镜像,后续指令基于该镜像执行

dockerfile中指令整体上顺序执行,内部执行逻辑为:
1)docker从基础镜像运行一个容器
2)执行一条指令,对容器做出修改
3)执行类似docker commit的操作,提交一个新的镜像层
4)基于刚提交的镜像运行一个新的容器
5)执行dockerfile中的下一条指令,修改容器;直到所有指令执行完毕。

基于dockerfile构建新镜像
在Dockerfile文件所在路径下执行:docker build -t "mazhen11/static_web:v1" .
说明:后面的.代表当前路径下的Dockerfile。
-t设置镜像的仓库和名称,仓库为mazhen11,镜像名为static_web,镜像标签为v1
如果没有指定标签,docker会自动为镜像设置标签为latest。

使用URL github.com/creack/docker-firefox 的 Dockerfile 创建镜像
docker build github.com/creack/docker-firefox

也可以通过 -f Dockerfile 文件的位置:
$ docker build -f /path/to/a/Dockerfile .

dockerfile指令失败与调试
如果上面Dockerfile中将nginx错写成ngin,看看此时构建会有什么问题。
其中一段错误日志如下:
 => CACHED [1/3] FROM docker.io/library/ubuntu:14.04@sha256:63fce984528cec8714c365919882f8fb64c8a3edf23fdfa0b218a  0.0s
 => ERROR [2/3] RUN apt-get update && apt-get install -y ngin    
 生成的中间镜像是63fce984528,在执行apt-get update && apt-get install -y ngin命令的时候出错了。


dockerfile和构建中的缓存
由于每一步的构建过程都会将结果提交为镜像,所以在docker镜像的构建过程中,其会将之前的镜像看做缓存。
假设Dockerfile的内容如下:
FROM ubuntu:14.04
MAINTAINER mazhen11 "mz@example.com"
RUN apt-get update && apt-get install -y ngin
RUN echo 'hi, i am in your container' > /usr/share/nginx/html/index.html
EXPOSE 80
那么在前三步中不会做任何修改,因此docker会将之前构建的镜像当做缓存,并作为新的开始点。
当再次构建时,docker会直接从第四步开始,能节省大量的时间。

但是有些场景,如apt-get update命令,我们需要获取最新的版本,此时不能缓存,可以使用的指令如下:
docker build --no-cache -t "mazhen11/static_web:v1" .

基于构建缓存的Dockerfile模板
为了充分使用构建缓存特性,我们可以使用简单的Dockerfile模板,这样每次通过Dockerfile构建时,都能命中缓存镜像,大大提升效率。

下面给出一个简单的Ubuntu相关镜像Dockerfile模板:
FROM ubuntu:14.04
MAINTAINER mzhen "mz@example.com"
ENV REFRESHED_AT 2020-12-31
RUN apt-get --qq update
该模板通过ENV指令设置了名为REFRESHED_AT的环境变量,该环境变量用来表明该镜像模板最后的更新时间。
RUN apt-get --qq update用来数显apt包的缓存,用来确保我们将要安装的每个软件都更新到最新版本。
如果需要更新,修改REFRESHED_AT对应的时间即可,会更新缓存中的镜像。

docker history 
展示docker的完整构建过程

启动一个新容器
docker run -d -p 80 --name static_web mazhen11/static_web:v1 nginx -g "daemon off;"
-d:以detached(分离)的方式在后台运行,该方式适合运行类似Nginx守护进程这样的需要长时间运行的进程。
nginx -g "daemon off;" 以前台的方式启动nginx,作为我们的web容器
-p:控制Docker在运行时应该公开哪些网络端口给外部

Docker有两种方法在宿主机上分配端口。
1)随机:docker在32768-61000中随机选择一个较大的端口号映射到容器中的80端口,docker run即是这种方式。
2)绑端口:指定宿主机一个具体的端口映射到容器的80端口 如 -p 8080:80 会将容器内的80端口绑定到本地宿主机的8080端口
3)绑ip:如:-p 127.0.0.1
4)绑ip和端口:如:-p 127.0.0.1:80:80,即把容器的80端口绑定到宿主机127.0.0.1这个ip的80端口上

-P :对外公开Dockerfile中通过EXPOSE指令公开的所有端口

查看端口的分配情况
1)docker ps     如:结果包含0.0.0.0:32768->80/tcp 容器中的80 端口映射到宿主机的32768端口上
2)docker port 容器名id 结果:80/tcp -> 0.0.0.0:32768

dockerfile中的指令
包括CMD ENTRYPOINT ADD COPY VOLUME WORKDIR USER ONBUILD LABEL STOPSIGNAL ARG ENV
1 CMD 指定容器启动时要运行的命令。
1)与RUN区别:二者运行的时间点不同;CMD 在docker run(即容器启动)时运行;RUN在docker build(即构建镜像时)运行;
2)可以被 docker run指令和自身覆盖
a docker run命令可以覆盖CMD指令,如果在docker run命令行中有指令,且dockerfile中中也有CMD指令,那么此时CMD会被覆盖而失效。
b dockerfile中只能指定一条CMD指令。如果指定了多条CMD指令,则仅有最后一条生效。

CMD语法,CMD + 命令数组,如:
CMD [ "/bin/bash" ]
CMD ["/bin/bash", "-1"] 


2 ENTRYPOINT
含义同CMD,区别
1)CMD后面的指令会被docker run后的指令覆盖,但ENTRYPOINT不会,而是将dcoker run后的指令放在ENTRYPOINT指令之后
2)如果确实想让docker run后的指令覆盖ENTRYPOINT,可以在docker run之后增加 --entrypoint标志

实例 如某dockerfile中包含如下语句:
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]

docker builder -t "mazhen11/static_web" . 使用上面dockerfile构建镜像mazhen11/static_web
docker run -t -i mazhen11/static_web -g "daemon off;" 根据镜像启动容器
那么最终ENTRYPOIN执行的命令是 /usr/sbin/nginx/ -g "daemon off;"

如果docker run中没有命令,那么ENTRYPOINT最终执行的命令是:  /usr/sbin/nginx/ -h

3 WORKDIR
创建新容器时,在容器内部设置一个工作目录。CMD和ENTRYPOINT指定的命令可以在该目录下执行。
如下dockfile代码:
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT ["rackup"]

如果docker run是不想让WORKDIR生效,可以采用 -w参数
如:docker run -ti -w /var/log ubuntu pwd


4 ENV
设置环境变量,如
ENV RVM_PATH /home/rvm
这个新的环境变量可以在后续任何RUN指令中使用,如同在命令前面指定了环境变量前缀一样,如
RUN gem install unicorn,那么实际该命令执行如下:
RVM_PATH=/home/rvm gem install unicorn

在别的指令中使用环境变量,如
WORKDIR $RVM_PATH

设置多个环境变量:
ENV RVM_PATH=/home/rvm RVM_AR="-arch i386"

上面设置的环境变量会被持久保存到从我们的镜像创建的任何容器,在容器中运行如下指令,则可以查看所有环境变量
env

也可以使用 docker run -e来传递环境变量,
如 docker run -ti -e "WEB_PORT=8080" ubuntu env
结果中会包含WEB_PORT=8080


5 USER
指定镜像运行的用户
ru:USER nginx:基于该镜像启动的容器会以nginx用户的身份来运行,还可以指定用户名、uid、组、组id
如:
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
也可以再dcoker run命令中 通过-u来覆盖USER指令
说明:如果未指定用户则默认为root


6 VOLUME
向基于该镜像创建的容器中添加卷。
卷:一个特定的目录,该目录可以绕过联合文件系统,并提供数据共享或持久化的功能。
具体包括:
卷可以在容器间共享和重用
对卷的修改立刻生效
对卷的修改不会对镜像产生任何影响
卷会一直存在,直到没有任何容器使用

卷可以让我们将数据、数据库、源代码等添加到镜像而不必提交给镜像,且允许多个容器间共享这些内容。
VOLUME ["/opt/project"]
那基于该镜像创建的任何容器都创建一个名为/opt/project的挂载点。

指定多个卷
VOLUME ["/opt/project", "/data"]

7 ADD
将构建环境中的文件和目录复制到镜像中。如:
ADD software.lic /opt/application/software.lic
即将构建目录下的software.lic指令复制到镜像中的/opt/application/software.lic
其中源文件的位置参数可以是URL 或 构建上下文的文件名或者目录
Docker通过目的地址参数末尾的字符来判断源文件是目录还是文件,如果目的地址以/结束,docker认为源位置指向一个目录。
否则为文件。

如果源文件为tar文件,则目的文件则会自动解压缩
如 ADD latest.tar.gz /var/www/wordpress/,那么命令会将latest.tar.gz解压到/var/www/wordpress/


8 COPY
类似ADD,不同点:
1)文件源路径必须是一个与当前构建环境相对的文件或目录,本地文件都放到和Dockerfile同一目录
2)不会对tar文件自动解压缩
docker copy指令理解
COPY --from=0 /build/server /
COPY 指令的 --from=0 参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,0代表第一个阶段。除了使用数字,我们还可以给阶段命名,比如:

# 编译阶段 命名为 builder
FROM golang:1.10.3 as builder
# ... 省略
# 运行阶段
FROM scratch
# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=builder /build/server /
更为强大的是,COPY --from 不但可以从前置阶段中拷贝,还可以直接从一个已经存在的镜像中拷贝。比如,
FROM ubuntu:16.04
COPY --from=quay.io/coreos/etcd:v3.3.9 /usr/local/bin/etcd /usr/local/bin/
我们直接将etcd镜像中的程序拷贝到了我们的镜像中,这样,在生成我们的程序镜像时,就不需要源码编译etcd了,直接将官方编译好的程序文件拿过来就行了。


9 LABEL
添加元数据,元数据以键值对的形式展现。如:
LABEL type="date cneter" role="web server"


10 STOPSIGNAL
设置停止容器时,发送什么系统调用信号给容器。如9 SIGKILL

11 ARG
定义可以在docker build命令运行时传递的变量。标志为--build-arg
如dockerfile中包含如下内容:
ARG build
ARG webapp_user=user

docker build --build-arg build=1224 -t mazhen11/webapp .
那么在构建mazhen11/webap镜像时,build会被设置为1224,webapp_user变量则会集成设置的默认值user。

docker预定义了一些ARG变零,包括HTTP_PROXY http_proxy HTTPS_PROXT FTP_PROXY NO_PROXY


12 ONBUILD
为镜像添加触发器,当一个镜像被用作别的镜像的基础镜像,该镜像中的触发器将会被执行。
如某dockerfile包含:
ONBUILD ADD . /app/src
上面dockerfile执行build构建出来的镜像(名为mazhen11/apache2)就会有触发器。
docker inspect能查看镜像中的触发器。

以上面mazhen11/apache2为基础镜像,创建新的dockerfile,内容如下:
FROM mazhen11/apache2
MATAINER mazhen "a@example.com"
ENV APPLICATION_NAME webapp

那么在依据上面dockerfile构建镜像时,会在FROM后执行ADD . /app/src指令。

ONBUILD触发器会按照在父镜像中的顺序执行,并且只能被继承一次,不会在孙子镜像中执行。

将镜像推送到docker hub
docker push mazhen11/static_web
说明 mazhen11是仓库名称,如果省略了仓库名称,即直接执行:docker push static_web
那么docker是任务你想要推送到root仓库,而root仓库是由docker公司的团队管理的,所以会被拒绝和失败。

镜像删除
docker rmi mazhen11/static_web
说明:该操作只能删除本地的镜像。如果想删除一个docker hub上的镜像仓库,需要在登陆docker hub后使用delete repository连接来删除。
rmi删除多个镜像的语法:
docker rmi mazhen11/apache2 mazhen11/puppetmaster
docker rmi `docker images -a -q`

运行自己的docker registry
有时存在不想公开的信息或数据的镜像,此时有两种选择:
1)使用docker hub上的私有仓库
2)在防火墙后面运行自己的registry

从容器运行registry
docker run -p 5000:5000 registry:2
该命令会启动一个运行registry2.0版本的容器,并将5000端口绑定到本宿主机。

使用新的registry方法如下:
1)获取镜像id
docker images
这里取镜像id:257e8cbe4448
2)打标签
docker tag 257e8cbe4448 docker.example.com:50000/mazhen11/static_web 
说明:为了制定新的registry目的地址,需要在镜像名前加上主机名和端口前缀。
3)推送到新的registry
docker push docker.example.com:50000/mazhen11/static_web 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值