前戏
镜像是容器的基础,每次执行docker run的时候都会指定哪个镜像作为容器运行的基础,我们之前的例子都是使用来自docker hub的镜像。直接使用这些镜像只能满足一定的需求,当镜像无法满足我们的需求时,就得自定制这些镜像。
镜像的定制就是定制每一层所添加的配置、文件。如果把每一层修改,安装,构建,操作的命令都写入到一个脚本,用脚本来构建、定制镜像,这个脚本就是Dockerfile。
Dockerfile是一个文本文件,其内部包含一条条指令, 每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建
Dockerfile的命令
FROM scratch #制作base image 基础镜像,尽量使用官方的image作为base image FROM centos # 使用base image FROM ubuntu:14.04 #带有tag的base image LABEL version=“1.0” #容器元信息,帮助信息,Metadata,类似于代码注释 LABEL maintainer=“zouzou0214" #对于复杂的RUN命令,避免无用的分层,多条命令用反斜线换行,合成一条命令! RUN yum update && yum install -y vim Python-dev #反斜线换行 RUN /bin/bash -c "source $HOME/.bashrc;echo $HOME” WORKDIR /root #相当于linux的cd命令,改变目录,尽量使用绝对路径!!!不要用RUN cd WORKDIR /test # 如果没有就自动创建 WORKDIR demo # 再进入demo文件夹 RUN pwd #打印结果是/test/demo ADD and COPY ADD hello / #把本地文件添加到镜像中(和Dockerfile同级),把本地的hello可执行文件拷贝到镜像的/目录 ADD test.tar.gz / #添加到根目录并解压 WORKDIR /root ADD hello test/ #进入/root/ 添加hello可执行命令到test目录下,也就是/root/test/hello 一个绝对路径 COPY hello test/ #等同于上述ADD效果 ''' ADD与COPY - 优先使用COPY命令 -ADD除了COPY功能还有解压功能 添加远程文件/目录使用curl或wget ''' ENV #环境变量,尽可能使用ENV增加可维护性 ENV MYSQL_VERSION 5.6 #设置一个mysql常量 RUN yum install -y mysql-server=“${MYSQL_VERSION}” ------这里需要稍微理解一下了-------中级知识--- VOLUME and EXPOSE 存储和网络 RUN and CMD and ENTRYPOINT RUN:执行命令并创建新的Image Layer CMD:设置容器启动后默认执行的命令和参数 ENTRYPOINT:设置容器启动时运行的命令 Shell格式和Exec格式 RUN yum install -y vim CMD echo ”hello docker” ENTRYPOINT echo “hello docker” Exec格式 RUN [“apt-get”,”install”,”-y”,”vim”] CMD [“/bin/echo”,”hello docker”] ENTRYPOINT [“/bin/echo”,”hello docker”] 通过shell格式去运行命令,会读取$name指令,而exec格式是仅仅的执行一个命令,而不是shell指令 cat Dockerfile FROM centos ENV name Docker ENTRYPOINT [“/bin/echo”,”hello $name”]#这个仅仅是执行echo命令,读取不了shell变量 ENTRYPOINT [“/bin/bash”,”-c”,”echo hello $name"] CMD 容器启动时默认执行的命令 如果docker run指定了其他命令(docker run -it [image] /bin/bash ),CMD命令被忽略 如果定义多个CMD,只有最后一个执行 ENTRYPOINT 让容器以应用程序或服务形式运行 不会被忽略,一定会执行 最佳实践:写一个shell脚本作为entrypoint COPY docker-entrypoint.sh /usr/local/bin ENTRYPOINT [“docker-entrypoint.sh] EXPOSE 27017 CMD [“mongod”] [root@master home]# more Dockerfile FROm centos ENV name Docker #CMD ["/bin/bash","-c","echo hello $name"] ENTRYPOINT ["/bin/bash","-c","echo hello $name”]
创建自己的容器镜像
1.先准备一段flask代码
myflask.py
from flask import Flask app=Flask(__name__) @app.route('/') def hello(): return "hello docker,i am is zouzou" if __name__=="__main__": app.run(host='0.0.0.0',port=8080)
2.准备传到docker容器里的文件
[root@HH tmp]# ls CentOS-Base.repo Dockerfile epel.repo myflask.py
3.制作Dockerfile
FROM centos LABEL maintainer="zouzou0214" ADD CentOS-Base.repo /etc/yum.repos.d/ ADD epel.repo /etc/yum.repos.d/ RUN yum clean all RUN yum install python-pip -y RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask COPY myflask.py /app/ WORKDIR /app/ EXPOSE 8081 CMD ["python","myflask.py"]
- 1.引入一个centos镜像,为基础镜像
- 2.作者注释信息
- 3.添加本地的两个yum文件,到容器的/etc/yum.repos.d/底下
- 4.清空yum缓存
- 5.yum安装pip工具
- 6.pip安装flask模块,指定清华源
- 7.拷贝本地的flask代码,到容器的/app/目录下
- 8.切换工作目录,到/app底下
- 9.暴露容器的8081端口,然后在运行镜像时候,加上-p参数,指定端口映射
- 10.执行命令,运行flask
4.构建docker镜像
docker build -t zouzu0214/my-docker-flask .
注意:最后面有个.(点)代表从当前目录build
构建当前目录的Dcokerfile,然后构建出一个名为yuchao163/s14-flask-docker 这个的镜像文件
-t tag参数,给镜像加上标记名
dockerhub账户名:zouzou0214,dockerhub账户名/镜像名 ,是为了后面讲docker镜像,推送到dockerhub
5.查看镜像是否构建成功:docker images
[root@HH tmp]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE zouzu0214/my-docker-flask latest 845652a3022e 12 seconds ago 346MB centos latest 67fa590cfc1c 5 weeks ago 202MB
我本地是没有centos镜像的,会去先下载一个centos镜像,然后基于该镜像构建
6.运行镜像
[root@HH tmp]# docker run -d -p 9000:8081 8456 bcd4e98ba419f5eaf3c9f43e64d9b1dd2558fddb2316be84b46fdf656318ab64
docker run -p 9000:8081 -d 8456
-p 映射9000端口到容器的8081
-d 后台运行
8456 镜像id
7.查看已经运行的docker实例
[root@HH tmp]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 44a53f62f533 76a7 "/sbin/tini -- /usr/…" 11 hours ago Up 11 hours 50000/tcp, 0.0.0.0:8000->8080/tcp MyJenkins
RUN、CMD、ENTRYPOINT之间的区别
这三个命令都是执行命令的,那它们之间有什么区别呢?先来熟悉两个概念
- shell命令格式:RUN yum install -y python
- exec命令格式:RUN [ "yum","install" ,"-y" ,"python"]
我们有一个centos:7的基础镜像
案例一:写一个dockerfile,内容如下
FROM centos:7 RUN echo "my is run..." CMD ["echo","my","is","cmd"] ENTRYPOINT ["echo","my","is","entrypoint"]
构建镜像
docker build -t centos:v1 .
启动这个镜像
[root@WC ~]# docker run centos:v1 my is entrypoint echo my is cmd
由上面的例子我们可以看出来,RUN 命令是作用于镜像的,而CMD和ENTRYPOINT是作用于容器的,在容器启动时执行的,并且先执行ENTRYPOINT在执行CMD
案例二:修改dockerfile,修改后的如下
FROM centos:7 CMD ["echo","my","is","cmd1"] CMD ["echo","my","is","cmd2"] ENTRYPOINT ["echo","my","is","entrypoint1"] ENTRYPOINT ["echo","my","is","entrypoint2"]
构建镜像
[root@WC ~]# docker build -t centos:v2 . Sending build context to Docker daemon 854kB Step 1/5 : FROM centos:7 ---> 5e35e350aded Step 2/5 : CMD ["echo","my","is","cmd1"] ---> Running in a5a50c279343 Removing intermediate container a5a50c279343 ---> 580f6c1ca1d8 Step 3/5 : CMD ["echo","my","is","cmd2"] ---> Running in bda45cc4de5f Removing intermediate container bda45cc4de5f ---> 98daa774b962 Step 4/5 : ENTRYPOINT ["echo","my","is","entrypoint1"] ---> Running in f181f49673e7 Removing intermediate container f181f49673e7 ---> f477e3841a4a Step 5/5 : ENTRYPOINT ["echo","my","is","entrypoint2"] ---> Running in cc4b61298119 Removing intermediate container cc4b61298119 ---> c7f3fbb0365a Successfully built c7f3fbb0365a Successfully tagged centos:v2
还是没有执行CMD和ENTRYPOINT,启动容器
[root@WC ~]# docker run centos:v2 my is entrypoint2 echo my is cmd2
结果:有多个CMD和ENTRYPOINT时,只执行最后的一个
案例三:修改dockerfile,修改后的如下
FROM centos:7 CMD ["-ef"] ENTRYPOINT ["ps"]
构建镜像
[root@WC ~]# docker build -t centos:v3 . Sending build context to Docker daemon 854.5kB Step 1/3 : FROM centos:7 ---> 5e35e350aded Step 2/3 : CMD ["-ef"] ---> Running in 3c10444a83a0 Removing intermediate container 3c10444a83a0 ---> 36d8da189295 Step 3/3 : ENTRYPOINT ["ps"] ---> Running in e7751f2f9c64 Removing intermediate container e7751f2f9c64 ---> ba96ea2aebf5 Successfully built ba96ea2aebf5 Successfully tagged centos:v3
启动镜像
可以看出会将ENTRYPOINT和CMD的执行结果显示出来,我们还可以在启动容器的时候加上参数,会替换掉CMD的参数
总结:
- RUN:在构建镜像的时候执行,作用于镜像层面
- ENTRYPOINT:在容器启动的时候执行,作用于容器层,dockerfile里有多条时只允许执行最后一条,先执行ENTRYPOINT在执行CMD
- CMD:在容器启动的时候执行,作用于容器层,dockerfile里有多条时只允许执行最后一条,容器启动后执行默认的命令或者参数,允许被修改
注意:如果ENTRYPOINT使用的是exec的格式,则会将CMD当做参数,如果是shell,则不会将CMD当做参数