2024年最全Docker与Dockerfile极简入门文档_docker 构建dockerfile

镜像(Image)

通过docker images命令可以看到本地已有的镜像:

REPOSITORY                              TAG                 IMAGE ID            CREATED             SIZE
ddfddf/tutorial                         latest              48a0196af3c3        3 hours ago         140MB

每个镜像都有一个IMAGE ID作为唯一标识,可以看出这个镜像的IMAGE ID为48a0196af3c3,使用镜像的id可以将它删除,命令如下:

docker rmi 镜像id

这里先不要着急把刚刚拉下来的镜像删掉,待会实验还要用。

容器(Container)

然后使用docker run来运行这个镜像(运行之后镜像就变成一个容器):

docker run ddfddf/tutorial apt-get install -y ping

这个命令会在docker容器中执行"apt-get install -y ping",也就是安装一个ping命令,运行完之后容器就自动退出了。之后使用docker ps命令可以查看所有当前正在运行的容器,输出如下:

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

空的,没有任何一个容器正在运行,之所以会这样是因为刚刚容器执行完命令后就退出了。使用docker ps -a命令可以查看到所有容器,不管它正在运行还是已经退出,输出如下:

CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS                      PORTS               NAMES
0299878039f0        ddfddf/tutorial       "apt-get install -y …"   1 minutes ago      Exited (0) 11 minutes ago                       peaceful_wozniak

每个容器也会有一个ID作为唯一标识,从上面的输出中可以看出CONTAINER ID0299878039f0.

我们尝试一下如下的命令:

docker run ddfddf/tutorial ping www.baidu.com

这个命令会报如下的错误:

docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"ping\": executable file not found in $PATH": unknown.

"ping": executable file not found in $PATH这句话中可以看出是因为ping命令没有装导致的,刚刚明明装过ping命令了,为什么没有呢?这是因为在容器中的修改并不会影响镜像,通过docker commit命令可以将这个容器提交成一个新的镜像。命令如下:

docker commit 0299878039f0 ddfddf/ping

命令中0299878039f0是之前用docker ps -a查询出来的容器ID。

试一下我们新提交的镜像learn/ping:

docker run ddfddf/ping ping www.baidu.com

输出如下:

PING www.a.shifen.com (111.13.100.92) 56(84) bytes of data.
64 bytes from promote.cache-dns.local (111.13.100.92): icmp_req=1 ttl=127 time=60.3 ms
64 bytes from promote.cache-dns.local (111.13.100.92): icmp_req=2 ttl=127 time=61.8 ms
......(以下省略)

可见ping命令可以使用了。在上面这个现象的背后,容器其实只是在镜像上面添加一个可写层,每当对这个容器进行修改都会在可写层标明与原本镜像的不同之处,当你使用docker commit命令时,只是提交了一个可写层,将它变成一个不可写的镜像层,而这个新的镜像和原本的镜像共享原本镜像的所有层,这就是所谓的docker分层机制,其实每个docker镜像都是由好多层构成的,这个机制能极大地缩小镜像占用的硬盘空间,如下图:

docker分层机制

刚刚我们运行ping命令的时候都是在前台运行的,在它运行的时候我们命令行做不了别的事情,只能眼睁睁地看它输出,使用如下命令可以让它后台运行:

docker run -d ddfddf/ping ping www.baidu.com

这个时候在控制台就看不到它的输出,使用docker ps可以看到这个容器的id,它是目前唯一正在运行的容器,如果想看它的输出的话可以使用docker logs 容器ID的方式查看。

使用docker stop命令可以停止这个后台运行的容器:

docker stop 2f371d67d92d

2f371d67d92d为这个容器在我的电脑上的ID

如果你想继续让这个容器运行,可以使用docker start命令:

docker start 2f371d67d92d

这个容器又会从刚才停止的地方重新运行,在初学的时候总是会搞混docker startdocker run命令,其实在理解了容器与镜像的区别之后,就很容易理解了,docker run使用来启动镜像的,而docker start是用来重新启动被停止的容器的。
只要镜像被执行了一次啊,都会生成一个新的容器,所以此时用docker -a会看到好多废弃的容器,可以使用docker rm 容器ID的方式将他们删除掉,也可以使用如下的小技巧一次性删除所有容器:

docker rm $(docker ps -a -q)

不用担心,这里只是删除所有容器而已,镜像还完好无损的保留在那里。

这里把容器和镜像容易混淆的命令总结在了下表中:

删除启动
镜像docker rmidocker run
容器docker rmdocker start

上文中一直使用容器ID来标明容器,其实在启动的时候可以通过–name选项来指定一个别名,之后就可以使用这个别名来代替容器ID使用,注意这个别名必须在本台机器中唯一,示例如下:

docker run -d --name=test ddfddf/ping ping www.baidu.com
docker stop test
docker rm test

上面的命令中给容器起了一个叫做test的别名,然后使用它的别名将其停止与删除。

如果有些情况下不得已要进入容器内部进行操作的话,可以使用如下命令进入容器内部的shell:

docker run -d --name=test ddfddf/tutorial ping www.baidu.com
docker exec -ti test /bin/bash

docker exec用于在正在运行的容器中执行命令,-ti选项表示分配一个虚拟终端。注意docker exec只能在“正在运行”的容器中执行命令,所以在docker run的时候执行ping就是为了让这个容器一直运行,而不是立即退出。

再聊聊仓库(Registry)

之前讲过,Docker Hub Registry是Docker的官方仓库,其实这是一个有点类似于Github的地方,任何人都可以在上面提交与下载镜像,我们可以先去上面注册一个账户(地址:https://hub.docker.com/),注册账户时会有谷歌的人机认证系统,所以需要一些科学上网技巧。
注册完成后可以用如下命令在shell中登录:

docker login -u 用户名 -p 密码

在将镜像push到自己新建的账户之前,要用docker tag重命名一下,将镜像命名你的用户名/镜像名这种形式,不然会push认证不通过,代码如下:

docker tag ddfddf/tutorial 你的用户名/tutorial 

docker tag并没有干太多的事情,只是创建了一个到ddfddf/tutorial镜像的引用。之后就可以使用docker push命令将自己的镜像推送到账户中去了,方便自己和别人的使用:

docker push 你的用户名/tutorial 

搞定之后就可以使用以下命令登出了:

docker logout

登录上Docker Hub,你将能看到刚刚你push上去的镜像。

总结

最后我再总结一下Docker的三个核心概念间的关系:

docker核心概念及其关联

Dockerfile


如果你想要从一个基础镜像开始建立一个自定义镜像,可以选择一步一步进行构建,也可以选择写一个配置文件,然后一条命令(docker build)完成构建,显然配置文件的方式可以更好地应对需求的变更,这个配置文件就是Dockerfile。
学习Dockerfile的最好方式就是阅读别人写的Dockerfile,遇到不会的指令就查一查Dockerfile的文档,文档地址如下:
https://docs.docker.com/engine/reference/builder/
大家遇到不知道的指令多去里面翻一翻,下面我带着大家读几个开源Dockerfile,在读的过程中学习相关知识。

第一个Dockerfile以阿里中间件大赛给的debian-jdk8镜像为例,Dockerfile文件如下:

FROM debian:stretch

ARG DEBIAN_FRONTEND=noninteractive
ARG JAVA_VERSION=8
ARG JAVA_UPDATE=172
ARG JAVA_BUILD=11
ARG JAVA_PACKAGE=jdk
ARG JAVA_HASH=a58eab1ec242421181065cdc37240b08

ENV LANG C.UTF-8
ENV JAVA_HOME=/opt/jdk
ENV PATH=${PATH}:${JAVA_HOME}/bin

RUN set -ex \
 && apt-get update \
 && apt-get -y install ca-certificates wget unzip \
 && wget -q --header "Cookie: oraclelicense=accept-securebackup-cookie" \
         -O /tmp/java.tar.gz \
         http://download.oracle.com/otn-pub/java/jdk/${JAVA_VERSION}u${JAVA_UPDATE}-b${JAVA_BUILD}/${JAVA_HASH}/${JAVA_PACKAGE}-${JAVA_VERSION}u${JAVA_UPDATE}-linux-x64.tar.gz \
 && CHECKSUM=$(wget -q -O - https://www.oracle.com/webfolder/s/digest/${JAVA_VERSION}u${JAVA_UPDATE}checksum.html | grep -E "${JAVA_PACKAGE}-${JAVA_VERSION}u${JAVA_UPDATE}-linux-x64\.tar\.gz" | grep -Eo '(sha256: )[^<]+' | cut -d: -f2 | xargs) \
 && echo "${CHECKSUM}  /tmp/java.tar.gz" > /tmp/java.tar.gz.sha256 \
 && sha256sum -c /tmp/java.tar.gz.sha256 \
 && mkdir ${JAVA_HOME} \
 && tar -xzf /tmp/java.tar.gz -C ${JAVA_HOME} --strip-components=1 \
 && wget -q --header "Cookie: oraclelicense=accept-securebackup-cookie;" \
         -O /tmp/jce_policy.zip \
         http://download.oracle.com/otn-pub/java/jce/${JAVA_VERSION}/jce_policy-${JAVA_VERSION}.zip \
 && unzip -jo -d ${JAVA_HOME}/jre/lib/security /tmp/jce_policy.zip \
 && rm -rf ${JAVA_HOME}/jar/lib/security/README.txt \
       /var/lib/apt/lists/* \
       /tmp/* \
       /root/.wget-hsts

在解释含义之前,我们先体验一下如何用Dockerfile打包一个镜像,新建一个空目录,假设就是~/debian-jdk8吧,cd进这个目录,新建一个Dockerfile,然后把上面的内容copy进去,然后执行下面的命令:

docker build -t debian-jdk8:v1.0 .

其中-t debian-jdk8:v1.0表示打包的镜像名为debian-jdk,tag为v1.0(前面说过,tag是可以任意命名的,不一定要是这种格式),注意命令的最后有一个.,这个表示打包的上下文(其实就是Dockerfile所在目录)是在当前目录,然后目录下的Dockerfile就会被编译执行。

执行完毕后运行docker images就会发现多了一个debian-jdk8镜像。

下面来解释一下Dockerfile的结构,那些字母全部大写的每行第一个单词都是Dockerfile的指令,可以看出这个Dockefile中包括的指令有FROMARGENVRUN,下面的表格中我对其含义进行了解释:

指令含义解释
FROMFROM debian:stretch表示以debian:stretch作为基础镜像进行构建
RUN可以看出RUN后面跟的其实就是一些shell命令,通过&&将这些脚本连接在了一行执行,这么做的原因是为了减少镜像的层数,每多一行RUN都会给镜像增加一层,所以这里选择将所有命令联结在一起执行以减少层数
ARG特地将这个指令放在RUN之后讲解,这个指令可以进行一些宏定义,比如我定义ENV JAVA_HOME=/opt/jdk,之后RUN后面的shell命令中的${JAVA_HOME}都会被/opt/jdk代替
ENV可以看出这个指令的作用是在shell中设置一些环境变量(其实就是export)

多阶段构建(multi-stage build)

这个功能是在Docker17.05版本及以上才出现的,主要用于解决docker镜像构建的中间冗余文件的处理,这里只做简要介绍,更详细的解释请看官方文档:
https://docs.docker.com/develop/develop-images/multistage-build/

有的时候会看见一些古怪,里面会有多个FROM指令或者FROM...AS...指令(这都是多阶段构建的标志),比如如下的dockerfile(来源于阿里中间件大赛的agent-demp,完整项目请见:https://github.com/DQinYuan/Agent-demo):

# Builder container
FROM registry.cn-hangzhou.aliyuncs.com/aliware2018/services AS builder

COPY . /root/workspace/agent
WORKDIR /root/workspace/agent
RUN set -ex && mvn clean package




### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/43fd002e65ee1d6074837cd76ad44454.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/2ef15c673f13220ba10dc88eca29984b.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
g-kSIJzkO6-1714495390565)]

给大家整理的电子书资料:

  

[外链图片转存中...(img-39fedJsO-1714495390566)]



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 30
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值