1. 镜像相关命令
2. 容器管理命令
3. 进入容器管理
一. 镜像相关命令
1、获取:docker pull centos获取镜像 #从官方仓库注册服务器下载镜像
docker pull dl.dockerpool.com:5000/ubuntu:12.04 指定完整的仓库注册服务器地址下载镜像。
docker search centos #搜索官方仓库注册服务器下的centos镜像
2、列出本地镜像: docker images :REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
REPOSITORY TAG IMAGE ID CREATED SIZE java8 latest 98da345fd9e5 32 hours ago 557.4 MB docker.io/centos latest 36540f359ca3 11 days ago 192.5 MB
3、提交镜像到本地:docker commit -m "message" ImageID tag 提交更新镜像后的副本 4、导出镜像:
docker save -o cenos7.3.tar centos #导出镜像到本地文件 cenos7.3.tar
docker save <myimage>:<tag> | gzip > <myimage>_<tag>.tar.gz
5、导入镜像:
docker load --input cenos7.3.tar #docker load -i cenos7.3.tar
#或者 docker load < centos7.3.tar
# gunzip -c <myimage>_<tag>.tar.gz | docker load
8、移除本地docker rmi <image_id>
*注意: docker rm 命令是移除容器。
*注意:在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器。
二. 容器管理命令
1、运行镜像:
docker run centos /bin/echo 'Hello world': 输出一个 “Hello World”, 之后终止容器。跟在本地直接执行 echo 'hello world' 几乎感觉不出任何区别。
docker run -t -i centos /bin/bash:启动一个bash 终端, 允许用户进行交互:
-t 选项让Docker分配一个伪终端( pseudo-tty) 并绑定到容器的标准输入上,
-i 则让容器的标准输入保持打开。
在交互模式下, 用户可以通过所创建的终端来输入命令。
当利用 docker run 来创建容器时, Docker 在后台运行的标准操作包括:
1)检查本地是否存在指定的镜像, 不存在就从公有仓库下载
2)利用镜像创建并启动一个容器
3)分配一个文件系统, 并在只读的镜像层外面挂载一层可读写层
4)从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
5)从地址池配置一个 ip 地址给容器
6)执行用户指定的应用程序
7)执行完毕后容器被终止
2、守护运行:docker run centos -d /bin/echo 'Hello world'
运行状态:docker ps -a
3、端口映射: docker run centos -d -p 80:8080 springboot :本地0.0.0.0:80端口映射到容器的0.0.0.0:8080端口
映射指定ip:docker run -d -p 127.0.0.1:80:8080 training/webapp python app.py
4、自定义容器名称:启动容器的时候, 系统默认会分配一个名字,使用 --name 标记可以为容器自定义命名。 docker run -d -P --name web training/webapp python app.py 注意:容器的名称是唯一的。 如果已经命名了一个叫 web 的容器, 当你要再次使用 web 这个名称的时候,需要先用 docker rm 来删除之前创建的同名容器。 6、docker inspect <container name> 显示更底层的容器或image信息
7、查看容器日志:docker logs centos
8、启动容器:docker start centos , 直接将一个已经终止的容器centos启动运行。
9、终止容器:docker stop centos 来终止一个运行中的容器。 注意:当Docker容器中指定的应用终结时, 容器也自动终止,终止状态的容器可以用 docker ps -a 命令看到。
1) 例如只启动了一个终端的容器:docker run -t -i centos /bin/bash .用户通过 exit 命令或 Ctrl d 来退出终端时, 所创建的容器立刻终止。
2) 另外系统镜像默认启动是bash,如果没有衔接输入流,本身就会马上结束。
例如Dockerfile最后的命令后面带有&
代码语言:javascript复制#基础镜像:仓库是java,标签用java8
FROM java8
#当前镜像的维护者和联系方式
MAINTAINER guisu guisu@example.com
#将打包好的spring程序拷贝到容器中的指定位置
ADD CityData-0.0.1-SNAPSHOT.jar /opt/CityData-0.0.1-SNAPSHOT.jar
#容器对外暴露8080端口
EXPOSE 8080
#容器启动后需要执行的命令
CMD $JAVA_HOME/bin/java -jar /opt/CityData-0.0.1-SNAPSHOT.jar &
启动容器后就会退出。
10、重启容器:docker restar<tag> :将一个运行态的容器终止, 然后再重新启动它。
11、导出容器快照:docker export <CONTAINER id > centos.tar
12、导入容器快照:cat centos.tar | sudo docker import - test/centos:v1.0
可以通过指定 URL 或者某个目录来导入, 例如 $docker import http://example.com/exampleimage.tgz example/imagerepo
*注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库, 也可以使用 docker import 来导入一个容器快照到本地镜像库。 这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息( 即仅保存容器当时的快照状态) , 而镜像存储文件将保存完整记录, 体积也要大。 此外, 从容器快照文件导入时可以重新指定标签等元数据信息。
13、删除容器:docker rm centos #来删除一个处于终止状态的容器centos。 docker rm -f centos 强制删除容器,不管是运行还是终止状态容器
3. 进入容器管理
进入容器的方法主要有以下几种: 1. 使用ssh登陆进容器 2. 使用nsenter、nsinit等第三方工具 3. 使用docker本身提供的工具
方法1 :需要在容器中启动sshd,存在开销和攻击面增大的问题。同时也违反了Docker所倡导 的一个容器一个进程的原则。 方法2:需要额外学习使用第三方工具。 所以大多数情况最好还是使用Docker原生方法,Docker目前主要提供了Docker exec和 Docker attach两个命令。
docker attach < container id > 或者 Docker attach <container name >
Docker attach可以attach到一个已经运行的容器的stdin,然后进行命令执行的动作。
**注意:如果从这个标准输入stdin中exit或者Ctrl C,会导致容器的停止。
代码语言:javascript复制[root@iZ235fz06auZ java8]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3473c923443d nlp-chitchat-data-api "/bin/sh -c '$JAVA_HO" 7 seconds ago Up 6 seconds 8080/tcp serene_bartik bb2
[root@localhost temp]# docker attach serene_bartik
/ # pwd
/
/ #
docker exec -it <container name > /bin/bash
或者docker exec -it <container name > /bin/sh
关于-i、-t参数
可以看出只用-i时,由于没有分配伪终端,看起来像pipe执行一样。但是执行结果、命令 返回值都可以正确获取。
[root@iZ235fz06auZ java8]# docker exec -i 0638203e2f89 /bin/bash date Mon Jul 17 03:25:51 UTC 2017
使用-it时,则和我们平常操作console界面类似。而且也不会像attach方式因为退出,导致 整个容器退出。 这种方式可以替代ssh或者nsenter、nsinit方式,在容器内进行操作。
代码语言:javascript复制[root@localhost temp]# docker exec -it 0638203e2f89 /bin/sh
/ # pwd
/
/ # echo $?
0
/ # dir
/bin/sh: dir: not found
/ # echo $?
127
如果只使用-t参数,则可以看到一个console窗口,但是执行命令会发现由于没有获得stdin 的输出,无法看到命令执行情况。
docker exec执行后,会命令执行返回值。(备注Docker1.3似乎有Bug,不能正确返回命令执行结果)
代码语言:javascript复制[root@localhost temp]# docker exec -it bb cat /a.sh
echo "running a.sh"
exit 10
[root@localhost temp]# docker exec -t bb /a.sh
running a.sh
[root@localhost temp]# echo $?
10
[root@localhost temp]# docker exec -it bb /a.sh
running a.sh
[root@localhost temp]# echo $?
10
[root@localhost temp]# docker exec -i bb /a.sh
running a.sh
[root@localhost temp]# echo $?
10
关于-d参数
在后台执行一个进程。可以看出,如果一个命令需要长时间进程,使用-d参数会很快返回。 程序在后台运行。
代码语言:javascript复制[root@localhost temp]# docker exec -d bb2 /a.sh
[root@localhost temp]# echo $?
0
如果不使用-d参数,由于命令需要长时间执行,docker exec会卡住,一直等命令执行完成 才返回。
代码语言:javascript复制# docker exec bb2 /a.sh
#
# docker exec -it bb2 /a.sh
#
# docker exec -i bb2 /a.sh
# docker exec -t bb2 /a.sh
#
四. Dockerfile详解
Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile
中的指令自动生成映像。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。 可以用一张图来说明:Docker镜像,Dockerfile ,容器之间的关系
1、Dockerfile的基本结构
Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动时执行指令.
代码语言:javascript复制## Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
RUN apt-get update && apt-get install -y nginx
# 4、容器启动执行指令
CMD /usr/sbin/nginx
2、Dockerfile命令详细说明
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#
字符开头则被视为注释。可以在Docker文件中使用RUN
,CMD
,FROM
,EXPOSE
,ENV
等指令。
在这里列出了一些常用的指令。
FROM:指定基础镜像
所有Dockerfile都必须以FROM
命令开始。 FROM
命令会指定镜像基于哪个基础镜像创建,接下来的命令也会基于这个基础镜像(译者注:CentOS和Ubuntu有些命令可是不一样的)。FROM
命令可以多次使用,表示会创建多个镜像。
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>
#三种写法,其中<tag>和<digest> 是可选项,如果没有选择,那么默认值为latest
MAINTAINER: 维护者信息
代码语言:javascript复制格式:
MAINTAINER <author name>
示例:
MAINTAINER Jasper Xu
MAINTAINER sorex@163.com
MAINTAINER Jasper Xu <sorex@163.com>
RUN:构建镜像时执行的命令
代码语言:javascript复制RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行
格式:
RUN <command>
exec执行
格式:
RUN ["executable", "param1", "param2"]
第二种是类似于函数调用。可将executable理解成为可执行文件,后面就是两个参数。
示例:
RUN ["executable", "param1", "param2"]
RUN apk update
RUN ["/etc/execfile", "arg1", "arg1"]
注:
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
ADD:将本地文件添加到容器中
tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget
复制文件指令。它有两个参数<source>和<destination>。destination是容器内的路径。source可以是URL或者是启动配置上下文中的一个文件。
代码语言:javascript复制格式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
示例:
ADD hom* /mydir/ # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类超酷的功能:
- 解压压缩文件并把它们添加到镜像中
- 从 url 拷贝文件到镜像中
当然,这些功能也让 ADD 命令用起来复杂一些,不如 COPY 命令那么直观。
1)解压压缩文件并把它们添加到镜像中 如果我们有一个压缩文件包,并且需要把这个压缩包中的文件添加到镜像中。需不需要先解开压缩包然后执行 COPY 命令呢?当然不需要!我们可以通过 ADD 命令一次搞定:
WORKDIR /app ADD nickdir.tar.gz . 这应该是 ADD 命令的最佳使用场景了!
2)从 url 拷贝文件到镜像中 这是一个更加酷炫的用法!但是在 docker 官方文档的最佳实践中却强烈建议不要这么用!!docker 官方建议我们当需要从远程复制文件时,最好使用 curl 或 wget 命令来代替 ADD 命令。原因是,当使用 ADD 命令时,会创建更多的镜像层,当然镜像的 size 也会更大(下面的两段代码来自 docker 官方文档):
ADD http://example.com/big.tar.xz /usr/src/things/ RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things RUN make -C /usr/src/things all 如果使用下面的命令,不仅镜像的层数减少,而且镜像中也不包含 big.tar.xz 文件:
RUN mkdir -p /usr/src/things && curl -SL http://example.com/big.tar.xz | tar -xJC /usr/src/things && make -C /usr/src/things all 好吧,看起来只有在解压压缩文件并把它们添加到镜像中时才需要 ADD 命令!
COPY:功能类似ADD
但是是不会自动解压文件,也不能访问网络资源:
除了指定完整的文件名外,COPY 命令还支持 Go 风格的通配符,比如:
COPY check* /testdir/ # 拷贝所有 check 开头的文件 COPY check?.log /testdir/ # ? 是单个字符的占位符,比如匹配文件 check1.log 对于目录而言,COPY 和 ADD 命令具有相同的特点:只复制目录中的内容而不包含目录自身。
比如我们在 Dockerfile 中添加下面的命令:
在制作 docker 镜像时,有复制某一个路径下所有文件和文件夹到镜像的需求,写下了如下 dockerfile:
代码语言:javascript复制FROM alpine
WORKDIR /root/test_docker_proj
COPY * ./
原始目录结构是这样的:
代码语言:javascript复制/projects/test_docker_proj
├── Dockerfile
├── dir1
│ ├── dir11
│ │ └── file11
│ └── file1
└── file2
然而复制到 docker 镜像里的目录结构变成了这样:
代码语言:javascript复制/root/test_docker_proj
├── Dockerfile
├── dir11
│ └── file11
├── file1
└── file2
可以看到 dir1 这个文件夹并没有被复制到镜像里,但是 dir1 中的子文件夹和文件都被复制进来了,和 dir1 同级的文件也被复制了。也就是说,在 COPY 执行的过程中,第一层文件夹被「解包」了。
CMD:构建容器后调用,也就是在容器启动时才进行调用。
代码语言:javascript复制格式:
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)
示例:
CMD echo "This is a test." | wc -
CMD ["/usr/bin/wc","--help"]
注:
CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
ENTRYPOINT:配置容器,使其可执行化。
配合CMD可省去"application",只使用参数。
代码语言:javascript复制格式:
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
ENTRYPOINT command param1 param2 (shell内部命令)
示例:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
注:
ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。
LABEL:用于为镜像添加元数据
代码语言:javascript复制格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
注:
使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。
ENV:设置环境变量
代码语言:javascript复制格式:
ENV <key> <value> #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ... #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
示例:
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy
EXPOSE:指定于外界交互的端口
代码语言:javascript复制格式:
EXPOSE <port> [<port>...]
示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
VOLUME:用于指定持久化目录
格式:
代码语言:javascript复制 VOLUME ["/path/to/dir"]
示例:
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"
注:
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
代码语言:javascript复制1 卷可以容器间共享和重用
2 容器并不一定要和其它容器共享卷
3 修改卷后会立即生效
4 对卷的修改不会对镜像产生影响
5 卷会一直存在,直到没有任何容器在使用它
WORKDIR:工作目录,类似于cd命令
代码语言:javascript复制格式:
WORKDIR /path/to/workdir
示例:
WORKDIR /a (这时工作目录为/a)
WORKDIR b (这时工作目录为/a/b)
WORKDIR c (这时工作目录为/a/b/c)
注:
通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。
USER:指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户
格式: USER user USER user:group USER uid USER uid:gid USER user:gid USER uid:group
示例: USER www
注:
使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run
运行容器时,可以通过-u参数来覆盖所指定的用户。
ARG:用于指定传递给构建运行时的变量
构建参数,作用于ENV相同,不同的是ARG的参数只在构建镜像的时候起作用,也就是docker build的时候。
Dockerfile中的ARG指令用以定义构建时需要的参数,使用格式如下:
代码语言:javascript复制格式:
ARG <name>[=<default value>]
示例:
ARG site
ARG build_user=www
ARG指令定义的参数,在docker build命令中以--build-arg a_name=a_value形式赋值。
如果docker build命令传递的参数,在Dockerfile中没有对应的参数,将抛出如下警告:
代码语言:javascript复制[Warning] One or more build-args [foo] were not consumed.
如果在Dockerfile中,ARG指令定义参数之前,就有其他指令引用了参数,则参数值为空字符串。
ARG指令定义参数在CMD指令为空。
不建议在构建的过程中,以参数的形式传递保密信息,如key, password等。
Docker自带的如下ARG参数,可以在其他指令中直接引用:
- HTTP_PROXY
- http_proxy
- HTTPS_PROXY
- https_proxy
- FTP_PROXY
- ftp_proxy
- NO_PROXY
- no_proxy
ONBUILD:用于设置镜像触发器
代码语言:javascript复制格式:
ONBUILD [INSTRUCTION]
示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
注:
当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发
以下是一个小例子:
代码语言:javascript复制# This my first nginx Dockerfile
# Version 1.0
# Base images 基础镜像
FROM centos
#MAINTAINER 维护者信息
MAINTAINER tianfeiyu
#ENV 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH
#ADD 文件放在当前目录下,拷过去会自动解压
ADD nginx-1.8.0.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/
#RUN 执行以下命令
RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm
RUN yum install -y wget lftp gcc gcc-c make openssl-devel pcre-devel pcre && yum clean all
RUN useradd -s /sbin/nologin -M www
#WORKDIR 相当于cd
WORKDIR /usr/local/nginx-1.8.0
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install
RUN echo "daemon off;" >> /etc/nginx.conf
#EXPOSE 映射端口
EXPOSE 80
#CMD 运行以下命令
CMD ["nginx"]