Docker 指南
Docker 指南
本文的目的是解释与 Docker 相关的重要概念,以便能够有效的使用 Docker 开发应用程序。
什么是 Docker?
Docker 是一个能够在独立的环境中运行应用程序的的工具。
它可以确保应用程序能在独立的环境下运行,应用程序能够按预期的那样在不同的机器/服务器上运行。这样可以确保在本地开发应用程序,部署到服务器环境上也可以运行。对于开发人员来说,Docker 能够在任何计算机上按照预期快速的启动和运行程序,与其他开发人员协作时也不需要关心他们的电脑配置如何。
在不同的电脑上运行应用程序之所以这么困难,是因为必须先正确的安装应用程序所依赖的软件版本。例如,假如在一台安装了Node.js 12.8 版本的电脑上可以正常运行一个由 Node.js 构建的API项目,换到一台安装了 Node.js 10.18 版本的电脑上可能不一定就能运行起来,因为使用的API可能不一样。其他的应用程序可能也会有同样的问题,比如 python, ruby, php, typescript, mysql 等等。Docker 的出现使得构建容器化的应用程序成为了可能,这些应用程序都自带了依赖项的正确版本,可以在不同电脑上运行。
Docker 有4种类型的“对象”来创建这些隔离的环境:images(镜像), containers(容器),volumes(存储卷) 和 network(网络)。我们使用最多的还是 Docker images 和 Docker containers。
Docker images(镜像)理解为一个包含了操作系统和应用的对象,Docker containers(容器)是用来运行Docker镜像中代码的环境。Docker 镜像可以保存到Docker仓库中心,供其他人下载使用。目前最流行的Docker 仓库是 Docker Hub。
Docker volumes(存储卷) 用于保存容器所产生的数据,将数据保存到 Docker 存储卷中后,即使 Docker 容器被删除、重新创建、重新启动,数据也不会丢失。Docker volumen支持2个容器共享访问相同的数据,只需要将容器的的存储卷指向相同的位置就可以。
Docker network(网络) 可以用来隔离容器,可以允许容器之间彼此通信。
那么虚拟机呢 virtual machines?
经常出现虚拟机(vm)与 Docker 相关的话题,因为它们都用来创建隔离的环境。
使用 Docker后,应用程序将在容器的独立环境下运行,这些容器中的每一个都共享同一个电脑上的操作系统内核。另一方面,在虚拟机上运行的应用程序运行在自己的操作系统上,不共享底层内核,虚拟机需要在 hypervisor 帮助下运行和管理要运行的操作系统。
使用Docker相对于虚拟机来说有压倒性的优势,Docker容器可以在几秒钟到几分钟运行起来,而且是轻量级的(MB 相对于GB的大小),容易配置,并且只使用少量的资源。也许使用虚拟机而不是Docker的唯一原因是,由于担心 Docker 容器在主机操作系统上使用共享内核会产生安全漏洞,因此需要更高级别的隔离。
Docker 镜像对于虚拟机区别
特性 | 容器 | 虚拟机 |
---|---|---|
启动 | 秒级 | 分钟级 |
硬盘使用 | 一般为 MB |
一般为 GB |
性能 | 接近原生 | 弱于 |
系统支持量 | 单机支持上千个容器 | 一般几十个 |
Docker engine
安装 Docker
目前安装 Docker 相关依赖的最简单的方法是安装 Docker Desktop。 Docker Desktop 附带了几个与 Docker 相关的工具,包括 Docker Engine、Docker CLI 和 Docker Compose。
对于 Mac 和 Windows 用户可以通过下面的链接进行下载安装
- Mac https://docs.docker.com/docker-for-mac/install/
- Windows https://docs.docker.com/docker-for-windows/install/
安装以后,请确保 Docker Desktop 正在运行。如果 Docker Desktop 正在运行,则意味着 Docker Engine 已启动,并且本文提到的 Docker CLI 命令也能执行。
对于 Linux 用户来说并没有 Docker Desktop软件,所以每个组件必须单独安装
在 Linux 系统当中,需要通过下面的命令启动 Docker 的守护进程
1 |
|
上面的命令应该可以正常运行,因为 systemctl
已经在大多数 Linux 发行版中集成了,如果无法运行那么请使用 sudo service docker start
命令。这是另一种可以在 Linux 开机时自动启动 Docker。
Dockerfile
DockerFile
是一个如何构建镜像的说明文件。这个文件通常首先会指定一个基础的 Docker 镜像,例如,如何需要构建一个基于 python 开发的 API 程序 ,那么就可以使用一个安装了 python 环境的 Linux 操作系统作为 Docker 基础镜像,指定这个基础镜像之后使用其他指令来构建 Docker 镜像。
1 |
|
下面列出了常用的指令说明,完整的列表请查看 Docker 官方文档
Instruction | Description |
---|---|
FROM |
Defines a base image 定义一个基础的镜像 |
RUN |
Executes command in a new image layer 在镜像上执行命令 |
CMD |
Command to be executed when running a container 容器运行时执行的命令 |
EXPOSE |
Documents which ports are exposed (not used for anything other than documentation) 容器对外暴露的端口 |
ENV |
Sets environment variables 设置环境变量 |
COPY |
Copies files/directories into the image 把文件复制到镜像中 |
ADD |
A more feature-rich version of the COPY instruction. COPY is preferred over ADD . 功能更强大的COPY指令,但是COPY指令优于ADD |
ENTRYPOINT |
Define a container’s executable. See the difference between the CMD instruction here. 入口点 |
VOLUME |
Defines which directory in an image should be treated as a volume. The volume will be given a random name which can be found using docker inspect command.定义镜像中应该被视为卷的目录。该卷将被赋予一个随机名称,可以使用 docker inspect 命令找到它。 |
WORKDIR |
Defines the working directory for subsequent instructions in the Dockerfile 指定工作目录 |
ARG |
Defines variables that can be passed by the docker build --build-arg command and used within the Dockerfile .定义可以通过docker build --build-arg 命令传递并在Dockerfile中使用的变量。 |
如果有一些文件不需要复制到 Docker 镜像当中,那么可是在 Dockerfile 同级目录下面添加一个 .dockerignore
文件,这样使用 COPY
或 ADD
指令的时候将不会把.dockerignore
中指定文件复制到Docker镜像当中。更多关于.dockerignore
语法问题请参考这个链接。
Docker images
Docker镜像由多个层组成(类似于洋葱),每层叠加之后,从外部看就如同一个独立的对象。镜像内部是一个精简的操作系统,同时还包含应用运行所必须的文件和依赖包。因为容器的设计初衷就是快速和小巧,所以镜像通常比较小。
构建镜像和给镜像打标签
使用 docker build
命令来创建镜像,当构建镜像时会提供一个 --tag
选项给镜像打标签,这样就知道从镜像仓库中使用哪个镜像,或者在运行容器时使用哪个镜像了。
1 |
|
分解上面的命令
docker build
指定正在创建Docker镜像--tag my-app:1.0
指定给镜像命名为 my-app 并且标记为1.0 版本.
最后一个点表示 Docker 镜像是由当前目录的Dockerfile 构建的。
使用 docker images
命令可以查看所有的本地的镜像,这样就可以验证镜像刚刚已经创建成功
1 |
|
如果构建镜像时只有 --tag
并没有指定版本的话,那么默认使用的版本就是 latest
。
1 |
|
除了使用 docker build --tag
命令给镜像打标签之外,还可以使用 docker tag
命令。因为同一个镜像可以有多个标签,使用docker tag
可以给通过docker build --tag
构建的镜像打上新的标签。
注意:现在my-app有多个镜像版本,它们的 Image ID 都相同但 Repository Name 却不同,但实际上它们都同一个镜像,Repository Name 与使用 docker push
将镜像推送到Docker镜像仓库的名称有关,在此处不详细展开讲。
注意镜像的标签是可变的
假设镜像golftrack:1.5存在一个已知的Bug。因此可以拉取该镜像后修复它,并使用相同的标签将更新的镜像重新推送回仓库。一起来思考下刚才发生了什么。镜像golftrack:1.5存在Bug,这个镜像已经应用于生产环境。如果创建一个新版本的镜像,并修复了这个Bug。那么问题来了,构建新镜像并将其推送回仓库时使用了与问题镜像相同的标签!原镜像被覆盖,但在生产环境中遗留了大量运行中的容器,没有什么好办法区分正在使用的镜像版本是修复前还是修复后的,因为两个镜像的标签是相同的!
这个时候就不要通过镜像的摘要进行获取镜像了
docker image pull nginx@sha256:c3dcdb92d7432d56604d….
查看镜像
使用docker images
或 docker images ls
命令可以列出当前本地可用的Docker 镜像
1 |
|
拉取和推送镜像
Docker 镜像可以保存在Docker镜像注册中心,默认的中心是 Docker Hub 。从Docker Hub拉取镜像使用docker pull
命令:
1 |
|
上面的命令将从Docker Hub 拉取官方的 1.18.0 版本的 nginx 镜像。
如果不指定nginx 的版本,默认会拉取标记为latest
最新的版本。
上面使用nginx 镜像都是来自Docker Hub的官方镜像,官方的镜像一般都是经过Docker Hub正式批准的镜像,并且这些镜像会定期进行安全漏洞测试。
任何人都可以在Docker Hub上创建自己的账号和仓库,并箱仓库中推送镜像。把镜像推送到Docker Hub意味着镜像被保存在Docker Hub 中。docker push 命令的形式类似下面的命令
1 |
|
删除镜像
使用 docker rmi
或 docker image rm
命令可以删除镜像
如果镜像已经被容器使用,那么需要先删除容器然后再删除镜像,或者使用docker rmi
命令加上--focus
选项
1 |
|
这里有两个命令,可以一次性清除所有镜像
1 |
|
保存和加载镜像
在某些情况下,需要将镜像保存到一个文件中,然后重新加载到Docker主机当中。使用docker save
命令保存镜像
1 |
|
上面命令是将1.0版本的my-app 镜像保存成 tar 文件,然后我们可以使用docker load
命令将新保存的 tar 文件加载到Docker 主机当中
1 |
|
Docker container
我们可以把容器看做是镜像的运行时实例。
运行容器
通过 docker run
命令启动容器
1 |
|
上面的命令将会使用 my-app 1.0 的镜像创建容器,执行 docker run
命令后将会启动容器,还会执行在 Dockerfile 中指定的 CMD 命令。使用 docker ps
命令可以列出当前所有正在运行的容器。
容器创建成功后,Docker会随机生成一个 ContainerID 和 Container Name给容器,我们也可以通过添加 --name
参数给容器取个好记的名字
1 |
|
通过上面的命令将运行一个叫my-app
名字的容器
查看容器日志
默认情况下使用docker run
命令运行容器时会将容器中进程的执行日志实时的输出到当前启动的控制台,然而我们可以通过使用 -d
参数在让容器在后台下运行容器,这样就可以继续在控制台中执行命令
[配图说明区别]
1 |
|
如果使用 -d
参数在后台运行容器,那么我们可以使用docker logs
命令查下容器的日志
1 |
|
暴露端口
如果容器自身提供一个端口(例如3000),但是在局域网中访问 http://localhost:3000
是访问不到的,还需要使用下面命令将容器的3000端口映射给 Docker主机的 4000 端口,这个时候在局域网就能正常访问 http://localhost:4000
了。
1 |
|
停止和删除容器
查看容器
在容器中执行命令
要在正在运行的容器中运行命令,请使用 docker exec
命令
1 |
|
如果想进入容器里面执行命令,需要使用一下命令
1 |
|
查看容器详情
1 |
|
Docker Volumes
Docker networks
其他
使用 Dockerfile 和 save 打包镜像的区别
Dockerfile 可以查看详细的历史
使用 docker save 的方式就无法查看打包的历史了,外人不知道作者给镜像做了什么具体的操作