一文了解Docker安装和入门

一文详解Docker

什么是Docker?

Docker 是一种开源的容器化平台,旨在简化应用程序的部署、运行和管理过程。它基于容器化技术,可以将应用程序及其依赖项打包到一个独立的、可移植的容器中,从而实现应用程序在不同环境中的一致性运行。

容器是一种轻量级、独立的执行单位,包含应用程序及其所有运行时所需的软件、库、环境变量和配置文件。通过使用 Docker,可以将应用程序和其依赖项打包成一个容器镜像,然后在任何支持 Docker 的环境中部署和运行这个容器镜像,而不需要担心底层环境的差异性。

Docker 提供了一套命令行工具和 API,用于创建、管理和运行容器。它使用了操作系统层的虚拟化技术,如 Linux 容器(LXC)或更近期的开源技术,如容器引擎(containerd)和容器运行时(container runtime),以实现高效的容器化。

使用 Docker,可以快速部署和扩展应用程序,提高开发和运维的效率。它提供了隔离性、可移植性和可复用性,使得应用程序更容易被打包、交付和管理。此外,Docker 还支持容器之间的网络通信和数据卷挂载,方便容器间的互联和数据共享。

总结来说,Docker 是一种容器化平台,提供了简化应用程序部署和管理的工具和环境,使应用程序能够在不同的环境中以一致的方式运行

Docker的应用场景有哪些?

Docker 的应用场景非常广泛,以下是一些常见的应用场景:

  1. 应用程序的打包和交付:Docker 可以将应用程序及其所有依赖项打包成一个容器镜像,确保应用程序在不同环境中的一致性运行。这使得应用程序的部署和交付变得更加简单和可靠。
  2. 应用程序的部署和扩展:使用 Docker,可以快速地部署和扩展应用程序。通过将应用程序打包成容器镜像,可以在任何支持 Docker 的环境中轻松地部署和运行应用程序。同时,可以根据需要快速地水平扩展应用程序,以满足不断增长的需求。
  3. 微服务架构:Docker 很适合构建和管理微服务架构。每个微服务可以打包成一个独立的容器,从而实现服务之间的解耦和独立部署。通过使用 Docker,可以更容易地管理和扩展微服务,以及进行微服务之间的通信和协作。
  4. 持续集成和持续部署(CI/CD):Docker 可以与持续集成和持续部署流程相结合,实现自动化的构建、测试和部署。通过使用 Docker 容器,可以创建一个一致的构建和测试环境,确保应用程序在不同阶段的一致性。
  5. 开发环境的隔离和一致性:Docker 可以帮助开发人员在本地创建轻量级、隔离的开发环境。开发人员可以使用容器来配置和管理应用程序的运行环境,而不会影响到主机系统。这使得开发环境的搭建和配置变得更加简单和可重复。
  6. 跨平台和混合云部署:由于 Docker 的跨平台性,可以将容器镜像在不同的操作系统和云平台中进行部署。这使得应用程序可以更加灵活地在不同的环境中迁移和扩展,同时也便于进行混合云部署和跨云平台的管理。

除了上述应用场景,Docker 还可以用于测试环境的创建、快速环境搭建和共享、资源隔离和安全等方面。它的灵活性和可移植性使得 Docker 在现代应用程序开发和部署中被广泛应用。

Docker基本概念

在理解 Docker 的基本概念之前,有几个核心术语需要了解:

  1. 镜像(Image):Docker 镜像是一个只读的模板,包含了运行容器所需的所有文件系统、软件环境、应用程序和依赖项。镜像是用于创建容器的基础,可以从 Docker Hub 或私有仓库获取现有的镜像,或者通过 Dockerfile 构建自定义镜像。
  2. 容器(Container):Docker 容器是镜像的可运行实例,是一个独立的、隔离的执行环境。容器包含了运行一个应用程序所需的所有文件、环境变量、库和配置。可以通过启动容器来运行应用程序,并可以对容器进行启动、停止、暂停、删除等操作。
    1. 镜像( Image )和容器( Container )的关系,就像是面向对象程序设计中的 类 和 实例一样,镜像是静态的定义,容器是镜像运行时的实体
  3. 仓库(Docker Registry):Docker Registry 提供了一个集中的、可访问的存储库,用于存储和分发 Docker 镜像,使用户能够方便地共享、下载和管理镜像。
    1. 镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
    2. 一个 Docker Registry 中可以包含多个仓库( Repository );每个仓库可以包含多个标签Tag;每个标签对应一个镜像
    3. 通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版
      本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像
    4. Docker 官方提供了一个公共的 Registry,称为 Docker Hub。在 Docker Hub 上,用户可以找到大量的公共镜像,可以直接使用或作为基础镜像来构建自己的镜像。
    5. 除了 Docker Hub,用户还可以搭建私有的 Docker Registry 来存储和管理自己的镜像,以实现更高的安全性和可控性。
  4. Dockerfile:Dockerfile 是一个文本文件,用于定义如何构建 Docker 镜像。Dockerfile 中包含了一系列的指令和配置,用于指导 Docker 引擎在构建过程中的操作,如基础镜像选择、软件安装、文件复制、环境变量配置等。通过 Dockerfile,可以实现自动化的镜像构建过程。
  5. Docker Compose:Docker Compose 是一个用于定义和运行多个 Docker 容器的工具。它使用一个 YAML 文件来配置多个容器之间的关系和依赖,并可以通过一个命令来启动、停止和管理整个容器应用。

这些基本概念构成了 Docker 的核心,通过理解它们,可以更好地使用和管理 Docker 容器化环境。

Docker安装

这里引用相关外部资料,列举下CenOS7环境下,Docker的安装:

  1. Vmvare详细安装教程:https://blog.csdn.net/SoulNone/article/details/126681722
  2. Vmvare虚拟机激活秘钥:https://www.bilibili.com/read/cv21151094
  3. CenterOS7镜像下载:https://blog.csdn.net/a350904150/article/details/129833998
  4. Vmvare安装CenterOS7:https://blog.csdn.net/StupidlyGrass/article/details/128646335
  5. CenterOS7环境下安装Docker:https://blog.csdn.net/Siebert_Angers/article/details/127315542

Docker镜像

Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。

获取镜像

从 Docker 镜像仓库获取镜像的命令是 docker pull 。其命令格式为:

$ docker pull [选项] [Docker Registry 地址[:端口号]] 仓库名[:标签]
  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号] 。默认地址是 DockerHub
  • 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名> 。对于 DockerHub,如果不给出用户名,则默认为 library ,也就是官方镜像。
[root@localhost snow]# docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete 
b51569e7c507: Pull complete 
da8ef40b9eca: Pull complete 
fb15d46c38dc: Pull complete 
Digest: sha256:1f1a2d56de1d604801a9671f301190704c25d604a416f59e03c04f5c6ffee0d6
Status: Downloaded newer image for ubuntu:16.04
docker.io/library/ubuntu:16.04
[root@localhost snow]# 

上面示例中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 ubuntu:16.04 ,因此将会获取官方镜像 library/ubuntu 仓库中标签为 16.04 的镜像.

从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。

运行

有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 ubuntu:16.04 为例,如果我们打算启动里面的 bash 并且进行交互式操作:

root@localhost snow]# docker run -it --rm ubuntu:16.04 bash
root@3f44186a6166:/# ls -la
total 4
drwxr-xr-x.   1 root root    6 May 27 18:29 .
drwxr-xr-x.   1 root root    6 May 27 18:29 ..
-rwxr-xr-x.   1 root root    0 May 27 18:29 .dockerenv
drwxr-xr-x.   2 root root 4096 Aug  4  2021 bin
drwxr-xr-x.   2 root root    6 Apr 12  2016 boot

docker run 就是运行容器的命令,详细命令格式我们会在 容器 一节进行讲解

列出镜像

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

root@3f44186a6166:/# exit
exit
[root@localhost snow]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    9c7a54a9a43c   3 weeks ago     13.3kB
ubuntu        16.04     b6f507652425   21 months ago   135MB

删除本地镜像

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

$ docker image rm [选项] <镜像1> [<镜像2> ...]

其中, <镜像> 可以是 镜像短 ID 、 镜像长 ID 、 镜像名 或者 镜像摘要 。

docker image ls 默认列出的就已经是短 ID 了,一般取前3个字符以上,只要足够区分于别的镜像就可以了

[root@localhost snow]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
redis         latest    0ec8ab59a35f   4 days ago      117MB
hello-world   latest    9c7a54a9a43c   3 weeks ago     13.3kB
ubuntu        16.04     b6f507652425   21 months ago   135MB
[root@localhost snow]# docker image rm 0ec
Untagged: redis:latest
Untagged: redis@sha256:f9724694a0b97288d2255ff2b69642dfba7f34c8e41aaf0a59d33d10d8a42687
Deleted: sha256:0ec8ab59a35faa3aaee416630128e11949d44ac82d15d43053f8af5d61182a5d
Deleted: sha256:f89720eafb298773509177d4b8b76a32adda4da1015ca28f52daa03fc6090499
Deleted: sha256:c3b5385e13627e14d553f09a098275d1f1ada0b6228cc30c92e095d669df799c
Deleted: sha256:b830b2806be6a052c6d857f927f72ef18a2539e69fdb6d51cf95d76d7e06c8f1
Deleted: sha256:8de1c0863fac10debb4e19de0cc27639ae97c1111eca0920649b21d97bc8dded
Deleted: sha256:80940c1d5550f3f56f5ab008aa79e899e4d3c9b6b41b9f76077f31dcfb2c482c
Deleted: sha256:8cbe4b54fa88d8fc0198ea0cc3a5432aea41573e6a0ee26eca8c79f9fbfa40e3
[root@localhost snow]# 

如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是Untagged ,另一类是 Deleted 。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。

使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除Deleted行为。

commit 理解镜像构成

注意: docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 docker commit 定制镜像,定制镜像应该使用 Dockerfile 来完成。如果你想要定制镜像请查看下一小节

现在让我们以定制一个 Web 服务器为例子,来讲解镜像是如何构建的:

[root@localhost snow]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
f03b40093957: Pull complete 
eed12bbd6494: Pull complete 
fa7eb8c8eee8: Pull complete 
7ff3b2b12318: Pull complete 
0f67c7de5f2c: Pull complete 
831f51541d38: Pull complete 
Digest: sha256:af296b188c7b7df99ba960ca614439c99cb7cf252ed7bbc23e90cfda59092305
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
[root@localhost snow]# docker run --name web1.0 -d -p 80:80 nginx
054d1cdcf5cf2de43116697f2a96e0c50a5138cd753580f55f1966364df155f1
[root@localhost snow]# 

这条命令会用 nginx 镜像启动一个容器,命名为 web1.0,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器;

如果是在 Linux 本机运行的 Docker,那么可以直接访问:http://localhost;如果使用的是虚拟机、云服务器上安装的 Docker,则需要将 localhost 换为虚拟机地址或者实际云服务器地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Licj8a72-1686898089872)(C:\Users\lixuewen\AppData\Roaming\Typora\typora-user-images\image-20230527191015639.png)]

假设我们不喜欢这个欢迎页面,我们希望改成其他文字,我们可以使用docker exec 命令进入容器,修改其内容

[root@localhost snow]# docker exec -it web1.0 bash
root@054d1cdcf5cf:/# echo '<h1>Hello,World!</h1>' > /usr/share/nginx/html/index.html
root@054d1cdcf5cf:/# exit
exit
[root@localhost snow]# 

我们以交互式终端方式进入 webserver 容器,并执行了 bash 命令,也就是获得一个可操作的 Shell;然后,我们用

Hello, World!

覆盖了 /usr/share/nginx/html/index.html 的内容;

刷新浏览器,页面显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N2yya1zd-1686898089873)(C:\Users\lixuewen\AppData\Roaming\Typora\typora-user-images\image-20230527193042587.png)]

我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff 命令看到具体的改动

[root@localhost snow]# docker diff web1.0
C /run
A /run/nginx.pid
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /root
A /root/.bash_history
C /etc
C /etc/nginx
C /etc/nginx/conf.d
C /etc/nginx/conf.d/default.conf
C /var
C /var/cache

Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化:

docker commit 的语法格式为:

$ docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
[root@localhost snow]# docker commit --author "snow" --message "edit index.html" web1.0 nginx:2.0
sha256:c06e3839c9eb09fee7e4409290e2d51ddeb214fe87a73d9806cd9479ddf2c9ca
[root@localhost snow]# docker image ls nginx
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
nginx        2.0       c06e3839c9eb   28 seconds ago   143MB
nginx        latest    f9c14fe76d50   2 days ago       143MB
[root@localhost snow]# 

–author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git版本控制相似;我们可以在 docker image ls 中看到这个新定制的镜像。

新的镜像定制好后,我们可以来运行这个镜像:

这里我们命名为新的服务为 web2.0 ,并且映射到 81 端口。我们重新直接访问 http://ip:81 看到结果,其内容应该和之前修改后的 web1.0一样。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UdbJXDID-1686898089874)(C:\Users\lixuewen\AppData\Roaming\Typora\typora-user-images\image-20230527194357988.png)]

至此,我们第一次完成了定制镜像,使用的是 docker commit 命令,手动操作给旧的镜像添加了新的一层,形成新的镜像

慎用docker commit

使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用

使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知

Dockerfile定制镜像

从docker commit 的学习中了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。

还以之前定制 nginx 镜像为例,这次我们使用 Dockerfile 来定制。

在一个空白目录中,建立一个文本文件,并命名为 Dockerfile :

[root@localhost snow]# mkdir nginxtest
[root@localhost snow]# cd nginxtest
[root@localhost nginxtest]# touch Dockerfile

通过Vim命令添加其内容为:

FROM nginx
RUN echo '<h1>Hello, EveryDay!</h1>' > /usr/share/nginx/html/index.html
  • FROM 指定基础镜像

    所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个
    nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 FROM 就是指定基础镜
    像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令

  • RUN执行命令

    RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最
    常用的指令之一。其格式有两种:

    • shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样
    RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
    
    • exec 格式: RUN [“可执行文件”, “参数1”, “参数2”] ,这更像是函数调用中的格式

既然 RUN 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个
命令对应一个 RUN 呢?比如这样:

FROM debian:jessie
RUN apt-get update
RUN apt-get install -y gcc libc6-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

上面的这种写法,创建了 7 层镜像。这是完全没有意义的;更好的写法如下:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& 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 \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps

这里没有使用很多个 RUN 对一一对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。将之前的 7 层,简化为了1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每
一层该如何构建。

构建镜像

现在我们明白了这个 Dockerfile的内容,那么让我们来构建这个镜像吧。在 Dockerfile 文件所在目录执行

[root@localhost nginxtest]# docker build -t nginx:3.0 .
[+] Building 0.9s (6/6) FINISHED                                                             
 => [internal] load build definition from Dockerfile                                         
 => => transferring dockerfile: 179B                                                         
 => [internal] load .dockerignore                                                           
 => => transferring context: 2B                                                             
 => [internal] load metadata for docker.io/library/nginx:latest                             
 => [1/2] FROM docker.io/library/nginx                                                       
 => [2/2] RUN echo '<h1>Hello, EveryDay!</h1>' > /usr/share/nginx/html/index.html           
 => exporting to image                                                                       
 => => exporting layers                                                                     
 => => writing image sha256:e899e0dd12df606efc99e3bb62b57d99935db376c9b1760b05ed7352a004c37a 
 => => naming to docker.io/library/nginx:3.0                                                            

(注意:上述构建命令结尾一定要加空格再加点,不然会报错)

镜像构建上下文(Context)

如果注意,会看到 docker build 命令最后有一个 . 。 . 表示当前目录,而 Dockerfile就在当前目录;上面的命令格式,实际是在指定上下文路径。那么什么是上下文呢?

首先要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 DockerRemote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。

当使用docker build 命令构建镜像,经常会需要将一些本地文件复制进镜像,而其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径, dockerbuild 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 COPY /opt/xxxx /app 不工作后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build 打包整个硬盘,这显然是使用错误。

一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore ,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。

Dockerfile 指令详解

  1. COPY 复制文件:COPY <源路径>… <目标路径>

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

      COPY package.json /usr/src/app/
      
    2. <源路径> 可以是多个,甚至可以是通配符

    3. <目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以
      用 WORKDIR 指令来指定)

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

  2. ADD 更高级的复制文件:指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能

    1. 比如 <源路径> 可以是一个 URL ,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。

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

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

  3. CMD 容器启动命令:

    shell 格式: CMD <命令>;

    exec 格式: CMD [“可执行文件”, “参数1”, “参数2”…]

    介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。 CMD 指令就是用于指定默认的容器主进程的启动命令的。

    在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如, ubuntu 镜像默认的CMD 是 /bin/bash ,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash 。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release 。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。

  4. ENTRYPOINT 入口点

    ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。 ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。

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

    有了 CMD 后,为什么还要有 ENTRYPOINT 呢?

    1. 让镜像变成像命令一样使用

    2. 应用运行前的准备工作

  5. ENV 设置环境变量:两种格式如下:

    ENV
    ENV = =…

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

    定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

  6. ARG 构建参数:格式: ARG <参数名>[=<默认值>]

    构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的

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

  7. VOLUME 定义匿名卷

    格式为:
    VOLUME [“<路径1>”, “<路径2>”…]
    VOLUME <路径>

    之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍Docker 卷的概念。

    为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

    VOLUME /data
    

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

  8. EXPOSE 声明端口

    格式为: EXPOSE <端口1> [<端口2>…]

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

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

    1. 一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;

    2. 另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

  9. WORKDIR 指定工作目录

    格式为: WORKDIR <工作目录路径>

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

    之前提到一些初学者常犯的错误是把 Dockerfile 等同于 Shell 脚本来书写,这种错误的理解
    还可能会导致出现下面这样的错误:

    RUN cd /app
    RUN echo "hello" > world.txt
    

    如果将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /app/world.txt 文件,或者其内容不是 hello 。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。这就是对 Dockerfile 构建分层存储的概念不了解所导致的错误。

    之前说过每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。

操作 Docker 容器

简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。

启动容器

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

新建并启动

所需要的命令主要为 docker run

例如,下面的命令输出一个 “Hello Docker”,之后终止容器:

[root@localhost snow]# docker run ubuntu:16.04 /bin/echo 'Hello Docker'
Hello Docker

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

[root@localhost snow]# docker run -t -i ubuntu:16.04 /bin/bash
root@d80afe7ea29f:/# ps
   PID TTY          TIME CMD
     1 pts/0    00:00:00 bash
    10 pts/0    00:00:00 ps
root@d80afe7ea29f:/# exit
exit

其中, -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i则让容器的标准输入保持打开。

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  1. 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  2. 利用镜像创建并启动一个容器
  3. 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  4. 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  5. 从地址池配置一个 ip 地址给容器
  6. 执行用户指定的应用程序
  7. 执行完毕后容器被终止
启动已终止容器

可以利用 docker container start 命令,直接将一个已经终止的容器启动运行。

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 ps 或 top 来查看进程信息。

后台运行

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

使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息。

[root@localhost snow]# docker container ls
CONTAINER ID   IMAGE       COMMAND                  CREATED      STATUS      PORTS                               NAMES
e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0

要获取容器的输出信息,可以通过 docker container logs 命令。

终止容器

可以使用 docker container stop 来终止一个运行中的容器。

此外,当 Docker 容器中指定的应用终结时,容器也自动终止。例如对于上一章节中只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。

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

[root@localhost snow]# docker container ls -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                        PORTS                               NAMES
d80afe7ea29f   ubuntu:16.04   "/bin/bash"              9 minutes ago    Exited (0) 9 minutes ago                                          bold_saha
626c2066188c   ubuntu:16.04   "/bin/bash"              10 minutes ago   Exited (127) 10 minutes ago                                       jolly_shockley
791d1f4d5eca   ubuntu:16.04   "/bin/bash"              10 minutes ago   Exited (127) 10 minutes ago                                       boring_margulis
1280e87e1cae   ubuntu:16.04   "/bin/echo 'Hello Do…"   11 minutes ago   Exited (0) 11 minutes ago                                         wonderful_saha

处于终止状态的容器,可以通过 docker container start 命令来重新启动。
此外, docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

进入容器

在使用 -d 参数时,容器启动后会进入后台。

某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐大家使用 docker exec 命令,原因会在下面说明。

  1. attach 命令

    docker attach 是 Docker 自带的命令。下面示例如何使用该命令:

    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                               NAMES
    5f6884f826e4   ubuntu      "/bin/bash"              23 seconds ago   Up 22 seconds                                       practical_payne
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago       Up 6 days       0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago       Up 6 days       0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    [root@localhost snow]# docker attach 5f6
    root@5f6884f826e4:/# exit
    exit
    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED      STATUS      PORTS                               NAMES
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago   Up 6 days   0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    

    注意: 如果从这个 stdin 中 exit,会导致容器的停止。

  2. exec 命令

    docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。

    只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执
    行结果仍然可以返回。

    当 -i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

    [root@localhost snow]# docker run -dit ubuntu
    3a7731e1a8a290b59263e9c73695f0a52e57e009505b4652be57c3fbe88a147e
    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                               NAMES
    3a7731e1a8a2   ubuntu      "/bin/bash"              3 seconds ago   Up 2 seconds                                       hardcore_ptolemy
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago      Up 6 days      0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago      Up 6 days      0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    [root@localhost snow]# docker exec -it 3a77 bash
    root@3a7731e1a8a2:/# exit
    exit
    [root@localhost snow]# docker container ls
    CONTAINER ID   IMAGE       COMMAND                  CREATED              STATUS              PORTS                               NAMES
    3a7731e1a8a2   ubuntu      "/bin/bash"              About a minute ago   Up About a minute                                       hardcore_ptolemy
    e015f7987776   nginx:2.0   "/docker-entrypoint.…"   6 days ago           Up 6 days           0.0.0.0:81->80/tcp, :::81->80/tcp   web2.0
    054d1cdcf5cf   nginx       "/docker-entrypoint.…"   6 days ago           Up 6 days           0.0.0.0:80->80/tcp, :::80->80/tcp   web1.0
    

    如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 docker exec 的原因。

    更多参数说明请使用 docker exec --help 查看

导出和导入容器

导出容器:

如果要导出本地某个容器,可以使用 docker export 命令。(这样将导出容器快照到本地文件)

[root@localhost snow]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                        PORTS                               NAMES
3a7731e1a8a2   ubuntu         "/bin/bash"              4 minutes ago    Up 4 minutes                                                      hardcore_ptolemy
[root@localhost snow]# docker export 3a77 > ubuntuBash.tar

导入容器:

可以使用 docker import 从容器快照文件中再导入为镜像:

[root@localhost snow]# cat ubuntuBash.tar | docker import - test/ubuntu:test2.0
sha256:1f2b949f67cade289f240a22612630363380d95b43d63bc60e032bbff69bc373
[root@localhost snow]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
test/ubuntu   test2.0   1f2b949f67ca   12 seconds ago   77.8MB
nginx         3.0       e899e0dd12df   3 hours ago      143MB

此外,也可以通过指定 URL 或者某个目录来导入。

注意:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 dockerimport 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。

此外,从容器快照文件导入时可以重新指定标签等元数据信息。

删除容器

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

[root@localhost snow]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED             STATUS                           PORTS                               NAMES
3a7731e1a8a2   ubuntu         "/bin/bash"              15 minutes ago      Up 15 minutes                                                        hardcore_ptolemy
5f6884f826e4   ubuntu         "/bin/bash"              19 minutes ago      Exited (0) 18 minutes ago                                            practical_payne
d80afe7ea29f   ubuntu:16.04   "/bin/bash"              About an hour ago   Exited (0) About an hour ago                                         bold_saha
[root@localhost snow]# docker container rm 5f68
5f68

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

清理所有终止状态的容器

用 docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。

$ docker container prune

访问仓库

仓库( Repository )是集中存放镜像的地方。

一个容易混淆的概念是注册服务器( Registry )。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 dl.dockerpool.com/ubuntu 来说, dl.dockerpool.com 是注册服务器地址, ubuntu 是仓库名。

Docker Hub

Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

  1. 注册:在 https://cloud.docker.com 免费注册一个 Docker 账号

  2. 登录:执行 docker login 进行登录,执行 docker logout 可退出登录

  3. 拉取镜像:通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它
    下载到本地;

    根据是否是官方提供,可将镜像资源分为两类

    1. 一种是类似 centos 这样的镜像,被称为基础镜像或根镜像,官方提供
    2. 另一种类型,比如 tianon/centos 镜像,它是由 Docker 的用户创建并维护的,往往带有
      用户名称前缀。
  4. 推送镜像:通过 docker push 命令来将自己的镜像推送到 Docker Hub

  5. 自动创建:对于需要经常升级镜像内程序来说,十分方便

    允许用户通过 Docker Hub 指定跟踪一个目标网站(目前支持 GitHub 或BitBucket)上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。

私有仓库

地址 dl.dockerpool.com/ubuntu 来说, dl.dockerpool.com 是注册服务器地址, ubuntu 是仓库名。

Docker Hub

Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。

  1. 注册:在 https://cloud.docker.com 免费注册一个 Docker 账号

  2. 登录:执行 docker login 进行登录,执行 docker logout 可退出登录

  3. 拉取镜像:通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它
    下载到本地;

    根据是否是官方提供,可将镜像资源分为两类

    1. 一种是类似 centos 这样的镜像,被称为基础镜像或根镜像,官方提供
    2. 另一种类型,比如 tianon/centos 镜像,它是由 Docker 的用户创建并维护的,往往带有
      用户名称前缀。
  4. 推送镜像:通过 docker push 命令来将自己的镜像推送到 Docker Hub

  5. 自动创建:对于需要经常升级镜像内程序来说,十分方便

    允许用户通过 Docker Hub 指定跟踪一个目标网站(目前支持 GitHub 或BitBucket)上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。

私有仓库

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值