learn-tech/专栏/Jenkins持续交付和持续部署/06.Jenkins部署之Docker要点.md
2024-10-16 06:37:41 +08:00

36 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        06.Jenkins部署之Docker要点
                        docker 基础操作

在前面的实践章节介绍了将服务部署到容器中有些读者对于docker可能并不熟悉而且在后面的文章中基本上所有的实践案例都是围绕着容器展开的所以介绍后面的实践内容之前有必要先学习一下docker的基础操作。当然如果你对docker的操作比较熟悉也可以略过此节的内容。

Docker的三大核心组件镜像、容器和镜像仓库。使用docker服务的操作基本上也是围绕这三大核心组件开展的在前面的”基础工具安装“章节对于企业级的镜像仓库已经做了介绍本节主要来介绍一下镜像操作和容器操作。如果你时一个docker老司机那么你也可以略过此章节。

镜像基本操作

本小节主要涉及到的内容如下:

获取镜像 列出镜像 删除镜像 构建镜像

获取镜像

从Docker 官方Registry获取镜像的命令是docker pull。其命令格式为

docker pull [option] [Docker Registry地址]/<仓库名>:<标签>

具体的参数选项可以通过docker pull --help命令查看这里说一下镜像地址名称的格式。

Docker Registry地址的格式一般是

<域名/IP>[:端口号]

默认地址是Docker Hub的地址。

<仓库名>:如上面语法所示,这里的仓库名是两段式名称,即

<用户名>/<镜像名>

对于 Docker Hub如果不给出用户名则默认为library也就是官方镜像。比如

$ docker pull ubuntu:14.04 14.04:Pulling from library/ubuntu ...... Digest:sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe Status:Downloaded newer image for ubuntu:14.04

上面的镜像地址没有给出默认使用docker hub下library用户下镜像获取官方library/ubuntu仓库中标签为14.04的镜像。

如果从其他镜像仓库获取,例如:阿里云

docker pull registry.cn-beijing.aliyuncs.com/daimler-jenkins/jenkins-slave-java

如果想要查询官方的某个镜像怎么办?使用

docker search <关键字>

命令可以根据关键字搜索镜像比如我要搜索jenkins镜像就会列出存在的jenkins镜像如下所示

[root@glusterfs-160 ~]# docker search jenkins NAME DESCRIPTION STARS OFFICIAL AUTOMATED jenkins Official Jenkins Docker image 4642 [OK]
jenkins/jenkins The leading open source automation server 1904
jenkinsci/blueocean https://jenkins.io/projects/blueocean 490
......

也可以通过在浏览器访问hub.docker.com官方界面通过输入关键字搜素想要获取的镜像这里就不在演示了有兴趣大家可以试一下

列出镜像

要想列出已经下载下来的镜像,可以使用 docker images 命令。

$ docker images REPOSITORYTAG IMAGE ID CREATED SIZE redis latest 5f515359c7f8 5 days ago 183MB nginx latest 05a60462f8ba 5 days ago 181MB

上面列表包含了仓库名、标签、镜像ID、创建时间以及所占用的空间。镜像ID是镜像的唯一标识一个镜像可以对应多个标签。

如果仔细观察会注意到这里标识的所占用空间和在Docker Hub上看到的镜像大小不同。

比如openjdk:latest 镜像大小拉取下来为495 MB但是在 Docker Hub显示的却是 244 MB。这是因为Docker Hub中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。

而 docker images命令显示的是镜像下载到本地后展开的大小准确说是展开后的各层所占空间的总和因为镜像到本地后查看空间的时候更关心的是本地磁盘空间占用的大小。

另外一个需要注意的问题是docker images 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS相同的层只需要保存一份即可因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

虚悬镜像

如果在images列出的这中镜像既没有仓库名也没有标签并且均为”<none>“的镜像,比如

00285df0df87 5 days ago 342 MB

这类镜像为虚悬镜像虚悬镜像的产生大多是因为镜像仓库里的镜像更新并重新拉取后旧镜像的标签转移到新镜像上来导致的另一种情况是通过build操作构建镜像时构建失败导致的。一般来说虚悬镜像已经失去了存在的价值是可以随意删除的。

可通过如下命令列出所有的虚悬镜像

$ docker images -f dangling=true

可以用下面的命令删除

$ docker rmi $(docker images -q -f dangling=true)

中间层镜像

为了加速镜像构建、重复利用资源Docker会利用中间层镜像。所以在使用一段时间后可能会看到一些依赖的中间层镜像。

与之前的虚悬镜像不同,有些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。实际上,如果这些镜像在使用中,默认也是不能删除的。这些镜像也没必要删除,因为相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。

只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。

默认的docker images列表中只会显示顶层镜像如果希望显示包括中间层镜像在内的所有镜像的话需要加-a参数。

$ docker images -a

列出部分镜像

不加任何参数的情况下docker images命令会列出所有顶级镜像但是有时候我们只希望列出部分镜像。docker images命令有多个参数可以帮助做到这个事情。

#根据仓库名列出镜像

$ docker images Ubuntu

#列出特定的某个镜像,也就是说指定仓库名和标签

$ docker images ubuntu:16.04

根据指定条件列出镜像

比如根据REPOSITORY和Tag列出匹配的镜像

$ docker images --filter=reference='busy*:*libc'

REPOSITORY TAG IMAGE ID CREATED SIZE busybox uclibc e02e811dd08f 5 weeks ago 1.09 MB busybox glibc 21c16b6787c6 5 weeks ago 4.19 MB

也可以使用多个filter

$ docker images --filter=reference='busy*:uclibc' --filter=reference='busy*:glibc'

REPOSITORY TAG IMAGE ID CREATED SIZE busybox uclibc e02e811dd08f 5 weeks ago 1.09 MB busybox glibc 21c16b6787c6 5 weeks ago 4.19 MB

--filter支持的关键字如下

dangling (boolean - true or false),列出虚悬镜像 label (label= or label==) 根据指定lable列出镜像 before ([:], or) - 过滤出在给定的image id之前创建的镜像 since ([:], or) - 过滤出引用此image id创建的镜像 reference 根据给出的匹配条件过滤出镜像

以特定格式显示

默认情况下docker images 会输出一个完整的表格但是我们并非所有时候都会需要这些内容。比如刚才删除虚悬镜像的时候我们需要利用docker images 把所有的虚悬镜像的ID列出来然后才可以交给docker rmi命令作为参数来删除指定的这些镜像这个时候可以使用-q 参数用于列出镜像的id

$ docker images -q 5f515359c7f8

如果希望自己组织列获取指定的解析结果可以用Go的模板语法。

比如下面的命令会直接列出镜像结果并且只包含镜像ID和仓库名

$ docker images --format "{{.ID}}:{{.Repository}}“ 5f515359c7f8: redis

或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:

$ docker images --format "table{{.ID}}\t{{.Repository}}\t{{.Tag}}" IMAGE ID REPOSITORY TAG 5f515359c7f8 redis latest

format支持如下关键字

.ID

Image ID

.Repository

镜像仓库名称

.Tag

镜像标签

.Digest

镜像 摘要

.CreatedSince

镜像创建后经过多长时间

.CreatedAt

镜像创建时间

.Size

镜像占用空间大小

删除镜像

如果要删除本地的镜像可以使用docker image rm命令其格式为

$ docker image rm [选项] <镜像1> [<镜像2>...] 或者使用 $ docker rmi [OPTIONS] IMAGE [IMAGE...]

可以通过 docker rmi --help 查看更多帮助

#用docker images 命令来配合

可以使用 docker images -q 来配合使用docker rmi这样可以成批的删除希望删除的镜像。删除虚悬镜像的指令是

$ docker rmi $(docker images -q -f dangling=true)

#比如我们需要删除所有仓库名为redis的镜像

$ docker rmi $(docker images -q redis)

#默认情况下,正在运行使用的镜像是不能被删除的,需要先停止容器在进行删除操作,也可以使用 -f 参数强制删除

构建镜像

使用 Dockerfile 定制镜像

要构建镜像就要制定构建的指令而Dockerfile就是包含一些自定义的指令和格式、用来构建镜像的文本文件。

Dockerfile 提供了一系列统一的资源配置语法,用户可以根据这些语法命令自定义配置,构建自定义镜像。

在学习构建镜像之前先学习一下Dockerfile的一些基本介绍以及基本指令

此次Dockerfile主要介绍如下内容:

Docker build 构建过程 Dockerfile 基础指令 使用 Dockerfile 定制镜像

构建过程大致如下:

Docker Client 端收到用户指令解析命令行参数并发送给Docker ServerDocker Server 端收到http请求后

首先创建一个临时目录将context指定的文件系统解压到该目录下 读取并解析Dockerfile 根据解析出的Dockerfile遍历其中所有指令并分发到不同的模块去执行 解析器指令parser为每一个指令创建一个临时容器并在里面执行当前命令然后通过commit使此容器生成一个镜像层。所有层的集合就是build后的结果。最后一次commit的镜像id就会作为此镜像的最终Id

Dockerfile 基础指令

COPY

复制文件,格式如下

COPY <源路径>...<目标路径>COPY ["<源路径1>",..."<目标路径>"]

COPY 指令有两种格式,一种类似于命令行,一种类似于函数调用。

COPY 指令将从构建上下文目录中<源路径> 的文件/目录复制到新的一层的镜像内的<目标路径> 位置。

比如:

COPY package.json /usr/src/app/

<源路径> 可以是多个甚至可以是通配符其通配符规则要满足Go的 filepath.Match规则

COPY hom* /mydir/COPY hom?.txt /mydir/

说明:

<目标路径> 可以是容器内的绝对路径也可以是相对于工作目录的相对路径工作目录可以用WORKDIR指令来指定。 <目标路径>不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。

此外还需要注意一点使用COPY指令源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用Git进行管理的时候。

ADD 高级的复制文件

ADD 指令和COPY的格式和性质基本一致。但是在COPY基础上增加了一些功能

比如 <源路径> 可以是一个URL这种情况下Docker引擎会试图去下载这个链接的文件放到<目标路径> 下下载后的文件权限自动设置为600如果这并不是想要的权限那么还需要增加额外的一层RUN进行权限调整。

另外如果下载的是个压缩包需要解压缩也一样还需要额外的一层RUN指令进行解压缩。所以不如直接使用RUN指令然后使用wget或者curl工具下载处理权限、解压缩、然后清理无用文件更合理。因此这个功能其实并不实用而且不推荐使用。

如果 <源路径> 为一个tar压缩文件的话压缩格式为gzipbzip2以及xz的情况下ADD指令将会自动解压缩这个压缩文件到<目标路径> 去。

在某些情况下这个自动解压缩的功能非常有用比如官方镜像ubuntu中

FROM scratch ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /...

但在某些情况下如果我们真的是希望复制个压缩文件进去而不解压缩这时就不可以使用ADD命令了。最适合使用ADD的场合就是所提及的需要自动解压缩的场合。

因此在COPY和ADD指令中选择的时候可以遵循这样的原则所有的文件复制均使用COPY指令仅在需要自动解压缩的场合使用ADD。

CMD 容器启动命令

Docker 容器实质就是进程。既然是进程那么在启动容器的时候需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。

在指令格式上一般推荐使用exec格式这类格式在解析时会被解析为JSON数组因此一定要使用双引号”而不要使用单引号。

提到CMD就不得不提容器中应用在前台执行和后台执行的问题。

Docker 不是虚拟机容器中的应用都应该以前台执行而不是像虚拟机、物理机里面那样用upstart/systemd去启动后台服务容器内没有后台服务的概念。

CMD service nginx start 会被理解为 CMD [ "sh","-c", "service nginx start"]因此主进程实际上是sh。那么当 service nginx start命令结束后sh也就结束了sh作为主进程退出了自然就会令容器退出。

正确的做法是直接执行nginx可执行文件并且要求以前台形式运行。比如

CMD ["nginx","-g", "daemon off"]

ENTRYPOINT

ENTRYPOINT 的格式和RUN指令格式一样分为exec格式和 shell 格式。ENTRYPOINT 的目的和CMD一样都是在指定容器启动程序及参数。

  当指定了ENTRYPOINT 后CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT指令即实际执行时将变为

""

说通俗了就是相当于在容器内部或者dockerfileCMD执行的命令列表里加上了新传入的参数并且执行

示例如下

首先看一下CMD命令

$ cat Dockerfile FROM nginx CMD ["echo","hello"]

$ docker build -t nginx:test .

$ docker run -it nginx:test

效果如下所示:

说明

重新构建的镜像加入了CMD命令此命令会覆盖之前存在镜像里的nginx daeomon off命令所以运行时会打印出新加入的命令并且运行后会自动关闭容器因为命令执行完毕了

在运行一个容器,并且传入命令

docker run -it nginx:test world

说明

新传入的命令 world 会覆盖cmd里的echo hello命令由于此命令不存在所以会报错

修改DockerfileCMD改成ENTRYPOINT

$ cat dockerfile FROM nginx ENTRYPOINT ["echo","hello"]

$ docker build -t nginx:ent .

$ docker run -it nginx:ent

$ docker run -it nginx:ent world

从执行结果可以看到world 被当做参数传去了

WORKDIR 指定工作目录

格式:

WORKDIR <工作目录路径>

使用WORKDIR指令可以来指定工作目录或者称为当前目录以后各层的当前目录就被改为指定的目录如该目录不存在WORKDIR 会帮你建立目录。

  在 Shell中连续两行是同一个进程执行环境因此前一个命令修改的内存状态会直接影响后一个命令而在Dockerfile中这两行RUN命令的执行环境根本不同是两个完全不同的容器。

因此如果需要改变以后各层的工作目录的位置那么应该使用WORKDIR指令。

示例

FROM nginx WORKDIR /home RUN pwd ENTRYPOINT ["pwd"]

效果如下所示:

ENV 设置环境变量

格式:

ENV ENV = =...

这个指令是设置环境变量无论是后面的其它指令如RUN还是运行时的应用都可以直接使用这里定义的环境变量。

ENV NODE_VERSION 7.2.0

RUN touch $NODE_VERSION-txt CMD ["ls"]

下列指令可以支持环境变量展开:

ADD、COPY、ENV、EXPOSE、LABEL、USER、WORKDIR、VOLUME、STOPSIGNAL、ONBUILD。

ARG 构建参数

格式:

ARG <参数名>[=<默认值>]

构建参数和ENV的效果一样都是设置环境变量。所不同的是ARG所设置的构建环境的环境变量在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用ARG 保存密码之类的信息因为docker history 还是可以看到所有值的。

Dockerfile中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。

在1.13 之前的版本,要求--build-arg中的参数名必须在Dockerfile中用ARG定义过才能用。换句话说就是--build-arg指定的参数必须在Dockerfile中使用了。如果对应参数没有被使用则会报错退出构建。

从1.13 开始这种严格的限制被放开不再报错退出而是显示警告信息并继续构建。这对于使用CI系统用同样的构建流程构建不同的Dockerfile的时候比较有帮助避免构建命令必须根据每个Dockerfile的内容修改。

如下示例:

$ cat Dockerfile ARG full_name ENV JAVA_APP_JAR $full_name ENV AB_OFF true ADD$JAVA_APP_JAR /deployments/

#构建 $ docker build -t image_name --build-arg full_name=full_name .

EXPOSE 声明端口

格式:

EXPOSE <端口1> [<端口2>...]

EXPOSE指令是声明运行时容器提供服务端口这只是一个声明在运行时并不会因为这个声明应用就会开启这个端口的服务。

在Dockerfile中写入这样的声明有两个好处

帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射; 在运行时使用随机端口映射时也就是docker run -P时会自动随机映射 EXPOSE的端口。

要将EXPOSE和在运行时使用 -p 宿主端口:容器端口 区分开来。-p是映射宿主端口和容器端口。换句话说就是将容器的对应端口服务公开给外界访问而EXPOSE仅仅是声明容器打算使用什么端口而已并不会自动在宿主进行端口映射。

VOLUME 定义匿名卷

格式:

VOLUME ["<路径1>","<路径2>"...] VOLUME <路径>

VOLUME /data

  这里的/data目录就会在运行时自动挂载为匿名卷任何向/data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置,比如:

docker run -d -v /mydata:/data xxxx

在这行命令中就使用了mydata这个命名卷挂载到了/data 这个位置替代了Dockerfile中定义的匿名卷的挂载配置。

示例

$ cat dockerfile FROM nginx WORKDIR /home RUN pwd volume ["/data"]

$ docker build -t nginx:volume .

$ docker run -itd nginx:volume

通过docker inspect docker_id 可查看此volume 映射到本地的位置,创建一个文件,写入测试的内容,然后退出容器,可发现文件在本地还在

USER 指定当前用户

USER 指令和 WORKDIR 相似都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录USER 则是改变之后层的执行RUNCMD 以及ENTRYPOINT 这类命令的身份。

当然,和 WORKDIR 一样USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换

RUN groupadd -r redis && useradd -r -g redisredis

USER redis

RUN ["redis-server"]

如果以root 执行的脚本在执行期间希望改变身份比如希望以某个已经建立好的用户来运行某个服务进程不要使用su或者sudo这些都需要比较麻烦的配置而且在TTY 缺失的环境下经常出错。

建立redis用户并使用gosu换另一个用户执行命令

RUN groupadd -r redis && useradd -r -g redis redis

设置CMD并以另外的用户执行

CMD ["exec", "gosu","redis","redis-server"]

到此常用的dockerfile的指令就介绍的差不多了熟悉dockerfile的各个指令以后可以帮助我们更加快速的编写dockerfile文件

使用 Dockerdile 定制镜像

了解了Dockerfile的常用指令后编写好Dockerfile就可以通过docker build命令构建镜像了。

首先看一下构建语法

$ docker build --help $ docker build [OPTIONS] PATH | URL | -

PATH或URL指向的文件为context上下文路径此路径包含build过程中的Dockerfile文件以及其它需要构建到镜像中的资源文件。

使用Dockerfile 定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像下面 redis镜像的容器对其进行修改一样基础镜像是必须指定的。而FROM就是指定基础镜像因此一个Dockerfile中FROM是必备的指令并且必须是第一条指令。

先看一个初学者示例(只做演示用,非最终版):

$ cat Dockerfile FROM debian:jessie RUN apt-get update RUN apt-get install -y gcclibc6-dev make RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" RUN mkdir -p /usr/src/redis RUN tar -xzf redis.tar.gz -C /usr/src/redis--strip-components=1 RUN make -C /usr/src/redis RUN make -C /usr/src/redis install

说明:

FROM 指定一个基础镜像,必须存在 RUN 指令用来执行命令行命令,最常用的指令之一

Dockerfile 中每一个指令都会建立一层RUN也不例外。每一个RUN的行为就和刚才我们手工建立镜像的过程一样新建立一层在其上执行这些命令执行结束后commit这一层的修改构成新的镜像。

而上面的这种写法创建了7层镜像。这是完全没有意义的而且很多运行时不需要的东西都被装进了镜像里比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像不仅仅增加了构建部署的时间也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。Union FS 是有最大层数限制的,比如 AUFS曾经是最大不得超过 42 层,现在是不得超过 127 层。

所以修改上面dockerfile 如下所示:

FROM debian:jessie RUN apt-get update
&& apt-get install -y gcclibc6-dev make
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
&& mkdir -p /usr/src/redis
&& tar -xzf redis.tar.gz -C /usr/src/redis--strip-components=1
&& make -C /usr/src/redis
&& make -C /usr/src/redis install

另一点需要注意的是Dockerfile的文件名并不要求必须Dockerfile。也可以使用-f参数指定其他Dockerfile名称此时后面的工作目录需要指定Dockerfile的上下文目录。

比如我的Dockerfile在/root/docker/目录下此目录下还有一个aa.json文件Dockerfile如下

FROM debian:jessie RUN build Deps='gcc libc6-dev make wget'\
&& mkdir -p /usr/src/redis COPY ./aa.json /usr/src/redis

那么构建的命令为

docker build -t redis:v1 -f /root/docker/Dockerfile /root/docker/

如果目录下有些东西确实不希望构建时传给Docker引擎那么可以用.gitignore一样的语法写一个.dockerignore该文件是用于剔除不需要作为上下文传递给Docker引擎的。

echo ".git" > .dockerignore

在Dockerfile文件所在目录根据上面的docker build命令进行镜像构建

$ docker build -t redis:v3 .

说明:

”.” 表示当前目录可以理解为Dockerfile目录而dockerfile就在当前目录也是在指定的上下文的目录

有关镜像操作的内容就介绍到这里,下面看一下容器的操作

容器操作

本小节主要介绍以下内容:

Docker命令分类 启动容器 后台运行容器 查看容器 进入容器 导入导出容器 删除容器

Docker 命令分类

在进行容器实操之前,先看看容器的一些命令分类

Docker 环境信息 info、version

容器生命周期管理 Create、exec、kill、pause、restart、rm、run、start、stop、unpause

镜像仓库命令 login、logout、pull、push、search

镜像管理 build、images、import、load、rmi、save、tag、commit

容器运维操作 attach、export、inspect、port、ps、rename、stats、top、wait、cp、diff、update

容器资源管理 volume、network

系统信息日志 events、history、logs #events打印容器的实时系统事件 history 打印出指定镜像的历史版本信息logs打印容器中进程的运行日志

更多命令可参考官网

启动容器

启动容器有两种方式一种是基于镜像新建一个容器并启动另外一个是将在终止状态stopped的容器重新启动。

命令语法

$ docker run --help Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

新建并启动,所需要的命令主要为 docker run。例如下面的命令输出一个 “Hello World”之后终止容器。

$ docker run ubuntu:14.04 /bin/echo 'Hello world' Hello world

注意:容器启动后只是输出 hello world输出后容器关闭退出

下面的命令则启动一个 bash终端允许用户进行交互。

$ sudo docker run -t -i -m 2G --cpu-shares 1536 ubuntu:14.04 /bin/bash root@af8bae53bdd3:/#

其中:

-t 参数让Docker分配一个伪终端pseudo-tty并绑定到容器的标准输入上 -i 参数则让容器的标准输入保持打开。 -c 参数用于给运行的容器分配cpu的shares值 -m 参数用于限制为容器的内存信息,以 B、K、M、G 为单位 -v 参数用于挂载一个volume可以用多个-v参数同时挂载多个volume -p 参数用于将容器的端口暴露给宿主机端口 格式hostportcontainerport

需要注意的是,如果没加-d后台运行参数从容器bash终端退出后此容器会终止运行

启动和终止容器

可以使用 docker start/stop/restart 命令,直接将一个已经存在的容器启动/关闭/重启。容器的核心为所执行的应用程序所需要的资源都是应用程序运行所必需的。除此之外并没有其它的资源。可以在伪终端中利用ps或top命令来查看进程信息。

$ docker start/stop/restart <container_Id/containerName>

后台运行容器

更多的时候需要让Docker在后台运行而不是直接把执行命令的结果输出在当前宿主机下。可以通过添加 -d 参数来实现。

注意:

容器是否会长久运行是和docker run指定的命令有关和 -d 参数无关。docker 容器后台运行必须加 -it参数使用-d参数启动后会返回一个唯一的 id也可以通过 docker ps 命令来查看容器信息。

要获取容器的输出信息,可以通过如下命令获取。

docker logs -f [container ID or container NAMES]

终止后台运行的容器

可以使用docker container stop来终止一个运行中的容器当Docker容器中指定的应用结束时容器也自动终止。 只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。

终止状态的容器可以用docker container ls -a或者docker ps -a 命令看到。

例如:

$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton 98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike

处于终止状态的容器可以通过docker start命令来重新启动(如果不是意外终止的容器)。docker restart命令会将一个运行态的容器终止然后再重新启动它。

查看容器

容器启动了那么该如何查看呢通过docker ps命令docker ps命令不加参数情况下用来列出正在运行的容器。

首先看一下ps命令的参数

$ docker ps -h Flag shorthand -h has been deprecated, please use --help

Usage: docker ps [OPTIONS]

List containers

Options: -a, --all Show all containers (default shows just running) -f, --filter filter Filter output based on conditions provided --format string Pretty-print containers using a Go template -n, --last int Show n last created containers (includes all states) (default -1) -l, --latest Show the latest created container (includes all states) --no-trunc Don't truncate output -q, --quiet 只显示容器id -s, --size Display total file sizes

-a 参数用来显示所有的容器,无论状态是运行的还是退出的

$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 336dded76f59 gitlab/gitlab-ee "/assets/wrapper" 2 months ago Up 2 months (healthy) 0.0.0.0:80->80/tcp, 0.0.0.0:2222->22/tcp, 0.0.0.0:2443->443/tcp gitlab 854e0ae79353 goharbor/harbor-jobservice:v1.9.1 "/harbor/harbor_jobs…" 4 months ago Exited (128) 4 months ago harbor-jobservice .....

-f 参数用来根据关键字过滤出想要列出的容器,比如

根据name获取指定的容器

$ docker ps --filter "name=nostalgic"

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 715ebfcee040 busybox "top" 3 seconds ago Up 1 second i_am_nostalgic 9b6247364a03 busybox "top" 7 minutes ago Up 7 minutes nostalgic_stallman

根据指定退出码列出容器

$ docker ps -a --filter 'exited=0'

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ea09c3c82f6e registry:latest /srv/run.sh 2 weeks ago Exited (0) 2 weeks ago 127.0.0.1:5000->5000/tcp desperate_leakey

$ docker ps -a --filter 'exited=137'

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b3e1c0ed5bfe ubuntu:latest "sleep 1000" 12 seconds ago Exited (137) 5 seconds ago grave_kowalevski

根据容器状态列出容器status

可选参数有 created, restarting, running, removing, paused, exited and dead.

$ docker ps --filter status=running

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 715ebfcee040 busybox "top" 16 minutes ago Up 16 minutes i_am_nostalgic

$ docker ps --filter status=paused

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 673394ef1d4c busybox "top" About an hour ago Up About an hour (Paused)

使用-f参数还可以使用format关键字将获取到的内容格式化或者获取想要的指定的容器信息

例如要获取容器的容器id和容器名称

$ docker ps -a --filter 'exited=0' --format "table {{.ID}}\t{{.Names}}" CONTAINER ID NAMES 0f15dba71e5c runner-Zm2_mVvw-project-2-concurrent-0-cache-c33bcaa1fd2c77edfc3893b41966cea8 5e3cff545d1f runner-Zm2_mVvw-project-2-concurrent-0-cache-3c3f060a0374fc8bc39395164f415a70

format参数支持的关键字如下

.ID

Container ID

.Image

Image ID

.Command

command 命令

.CreatedAt

创建时间

.RunningFor

运行时间

.Ports

expose的端口

.Status

容器状态

.Size

容器占用的disk大小

.Names

容器名称

.Label

特殊的容器标签

有关容器的查看操作就简单介绍到这里

进入容器

在使用-d参数时容器启动后会进入后台。某些时候需要进入容器进行操作有很多种方法包括使用docker attach/exec 命令或 nsenter 工具等。

exec 命令

sudo docker exec -it 775c7c9ee1e1 /bin/bash

更简单的,下载 .bashrc_docker并将内容放到 .bashrc 中

$ cat .bashrc_docker

alias docker-pid="sudo docker inspect --format '{{.State.Pid}}'" alias docker-ip="sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}'"

#the implementation refs from https://github.com/jpetazzo/nsenter/blob/master/docker-enter function docker-enter() { #if [ -e $(dirname "$0")/nsenter ]; then #Change for centos bash running if [ -e $(dirname '$0')/nsenter ]; then # with boot2docker, nsenter is not in the PATH but it is in the same folder NSENTER=$(dirname "$0")/nsenter else # if nsenter has already been installed with path notified, here will be clarified NSENTER=$(which nsenter) #NSENTER=nsenter fi [ -z "$NSENTER" ] && echo "WARN Cannot find nsenter" && return

if [ -z "$1" ]; then
    echo "Usage: `basename "$0"` CONTAINER [COMMAND [ARG]...]"
    echo ""
    echo "Enters the Docker CONTAINER and executes the specified COMMAND."
    echo "If COMMAND is not specified, runs an interactive shell in CONTAINER."
else
    PID=$(sudo docker inspect --format "{{.State.Pid}}" "$1")
    if [ -z "$PID" ]; then
        echo "WARN Cannot find the given container"
        return
    fi
    shift

    OPTS="--target $PID --mount --uts --ipc --net --pid"

    if [ -z "$1" ]; then
        # No command given.
        # Use su to clear all host environment variables except for TERM,
        # initialize the environment variables HOME, SHELL, USER, LOGNAME, PATH,
        # and start a login shell.
        #sudo $NSENTER "$OPTS" su - root
        sudo $NSENTER --target $PID --mount --uts --ipc --net --pid su - root
    else
        # Use env to clear all host environment variables.
        sudo $NSENTER --target $PID --mount --uts --ipc --net --pid env -i $@
    fi
fi

}

将以上内容放到 .bashrc文件下

$ source ~/.bashrc

这个文件中定义了很多方便使用Docker的命令例如docker-pid可以获取某个容器的PID而docker-enter可以进入容器或直接在容器内执行命令。

$ docker-enter c0c00b21f8f8 root@c0c00b21f8f8:# ls root@c0c00b21f8f8:# pwd /root root@c0c00b21f8f8:~# exit logout

$ docker-pid c0c00b21f8f8 11975

删除容器

可以使用 docker rm 来删除一个处于终止状态的容器。 例如

$sudo docker rm trusting_newton(<container_name>) trusting_newton

如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。

清理所有处于终止状态的容器

用 docker ps -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用 docker rm $(docker ps -a -q) 可以全部清理掉。

注意:这个命令其实会试图删除所有的包括还在运行中的容器,不过就像上面提过的 docker rm 默认并不会删除运行中的容器。

删除所有终止的容器

$ docker container prune    #删除所有停止的容器, -f 强制删除, --filter "until=24h" 删除停止超过24小时的容器

有关docker容器的内容就简单介绍到这里