秒懂 Docker 镜像:实战技巧让你快速上手
1 | 作者:李晓辉 |
你是不是也和我一样,刚学 Docker 的时候看到“镜像”两个字脑子里全是问号?什么是镜像?为什么每次 docker run
都要“拉一下”?这些镜像到底藏在哪里?能不能自己整一个?
别急,这篇文章就是来帮你一一解答这些问题的!我们会从镜像的概念讲起,再带你看看怎么拉取、查看、制作、导出和导入镜像,还会顺手写个 Dockerfile 玩一下。全程没有废话,只有实战,包你看完后立刻上手不迷路!
准备好了?那就一起搞懂镜像这回事吧~
镜像的概念
当你听到“容器镜像”这几个字时,可能会想:“这到底是个啥?怎么感觉又神秘又复杂?”其实,容器镜像就像是容器的“照片”或者“模板”,它保存了一个应用程序所需的所有东西,简单来说,它就是用来跑容器的“食谱”!
1. 镜像是不可变的
镜像的特别之处在于,它是不可变的。一旦镜像创建好了,它的内容就定型了,不会改变。所以你每次运行一个容器,都是从这个固定的镜像创建出来的。而容器的运行时状态是可以变动的,比如运行中的数据、文件等,反正不会影响到镜像本身。
2. 镜像是分层的
镜像不像一个大文件那样堆在一起,而是分成了好几层!每一层都代表了镜像做的一个更改或增加的内容。例如,你可以从一个空白的操作系统镜像开始,然后安装一些工具,再安装你的应用。这种分层的方式让镜像更加高效,省空间,也让更新更容易(只需要更新特定的层而不是整个镜像)。
3. 怎么构建镜像?
镜像的创建通常是通过一个叫做 Dockerfile
的文件来完成的。你可以把它看成是一个“食谱”,告诉 Docker 该如何从零开始构建镜像。你在 Dockerfile
中写下基础操作系统、需要安装的软件、配置等内容,然后 Docker 会按照这些指令一步步构建出镜像。构建好之后,你就可以拿这个镜像跑容器了!
4. 镜像仓库
镜像可不是一口气都丢进本地硬盘的,它们会被存储在一个叫做“镜像仓库”的地方。最常用的仓库是 Docker Hub,这就像一个大型的应用商店,提供各种常用的镜像。而如果你不想让别人随便拿你的镜像,也可以设置私有仓库。
5. 拉取和推送镜像
通常,我们都从远程仓库(像 Docker Hub)拉取镜像,也就是下载到本地。而当你创建了自己的镜像后,就可以把它推送到仓库,分享给其他人。操作很简单:
拉取镜像:
docker pull <镜像名>
推送镜像:
docker push <镜像名>
6. 镜像优化小技巧
镜像虽然方便,但有时候会有点大。尤其在网络慢或者存储有限的情况下,镜像太大可能会让你抓狂。所以,这里有几个小技巧能帮你让镜像更轻便:
用更小的基础镜像,比如
alpine
镜像。合并一些镜像创建步骤,减少层数。
删除一些没必要的文件,尤其是缓存文件。
用
.dockerignore
文件排除不需要的文件。
这样一来,你的镜像既轻巧又高效,构建和下载也能更快。
容器和镜像有啥区别和联系?
简单来说,镜像就像是容器的“蓝图”或“模板”,它是静态的、不可变的。每次你需要启动容器时,Docker 会根据这个镜像生成一个可运行的实例,这个实例就是容器。
镜像是我们构建和分发应用的基础,可以保存在本地或者远程仓库。镜像本身不会在运行时改变,而容器则是根据镜像创建并启动的一个动态实体。容器是运行在主机上的进程,启动后可以在其中执行命令、修改文件或保存数据等。容器的生命周期是从启动到运行,再到停止或销毁。
这两者的关系可以理解为:镜像是容器的父本,容器是镜像的子本。每个容器都是基于某个镜像创建出来的,但容器的状态是可以变化的,而镜像则始终保持不变。
概念 | 容器镜像(Image) | 容器(Container) |
---|---|---|
是什么 | 运行容器的“模板”,类似应用的快照 | 运行时的实例,基于镜像启动的进程 |
是否可变 | 不可变,构建后内容固定 | 可变,运行时可以修改内部状态 |
存储位置 | 本地或远程仓库 | 运行在宿主机上的进程 |
生命周期 | 构建 -> 拉取 -> 用来生成容器 | 启动 -> 运行 -> 停止/销毁 |
镜像从哪儿来?
获取镜像其实并不复杂,Docker 给我们提供了几种非常方便的方式来获取和管理镜像。你可以从官方镜像库、第三方镜像仓库,甚至是自己构建镜像。下面,我会给你介绍几种常见的镜像获取方式。
1. 从 Docker Hub 拉取镜像
最常见的方式就是从 Docker Hub 拉取镜像。Docker Hub 就像是一个应用商店,里面有成千上万的开源镜像,涵盖了各种各样的操作系统、应用程序和工具。你只需要知道镜像的名字,使用 docker pull
命令就能下载到本地,像这样:
1 | docker pull ubuntu |
其实这条命令全称是:
1 | docker pull docker.io/library/ubuntu:latest |
但是由于docker和dockerhub都是一家公司的,它隐藏了全路径,但是我们要知道这个格式:
1 | docker pull 可解析的域名或IP/命名空间/镜像名称:标签 |
没指定标签的时候,默认会自动使用latest标签
上面这个命令会从 Docker Hub 下载最新的 Ubuntu 镜像。你也可以指定版本号或者标签来拉取不同版本的镜像:
1 | docker pull ubuntu:20.04 |
如果你镜像从加速器拉取镜像,又不想在pull镜像的时候每次加前缀,那可以把容器镜像加速器放到docker daemon中,每次pull的时候,docker就会自动从特定的加速器拉取镜像,而不是在中国无法访问的dockerhub拉取了哦
1 | sudo mkdir -p /etc/docker |
2. 从第三方镜像仓库拉取
除了 Docker Hub,网络上还有很多其他的镜像仓库,像 阿里云、腾讯云、华为云 等也提供了 Docker 镜像服务。如果你身处国内,可能访问 Docker Hub 速度比较慢或者无法,那么你可以选择使用这些国内镜像仓库或者从你公司自己的仓库里下载,获得更快的下载速度。
比如我就在互联网上提供了容器镜像仓库:registry.myk8s.cn
,如果你从我的仓库下载,那就这样加上前缀即可
1 | docker login registry.myk8s.cn -u USERNAME -p PASSWORD |
1 | docker pull registry.myk8s.cn/library/ubuntu |
如果你也想在公司里搭建一个容器镜像仓库给内网用,那就看看我的这篇文章吧,一步一步的教你搭建harbor容器镜像仓库,点此打开Harbor仓库搭建教程
构建自己的镜像
构建容器镜像有两个主流方法:
Docker commit容器
Dockerfile(最推荐,最主流)
Docker commit
docker commit
是 Docker 提供的一个命令,用来创建一个新的镜像,它是基于现有的容器状态来生成的。简单来说,它允许你将正在运行的容器的变化(例如文件的修改、包的安装、配置的改变等)保存为一个新的镜像。
通常情况下,docker commit
适用于你手动操作容器后,希望将这些操作结果保存下来,生成一个新的镜像。比如,你进入容器内部做了一些修改,然后想要把这些修改保存成镜像以备后续使用,或者分享给其他人。
假设你有一个运行中的容器(比如 container_id
是 abc123
),你在里面做了一些修改(比如安装了软件、修改了配置文件等),现在你希望把这些修改保存到一个新镜像中,可以运行:
1 | docker commit abc123 my-new-image:latest |
这条命令会将 abc123
这个容器的当前状态保存为一个新的镜像,镜像名为 my-new-image:latest
。
docker commit
通常用于一些临时的修改,或者你在测试一个容器时,做了一些想要保存的改变。不过,如果你要长期管理和复用镜像,最好还是写一个 Dockerfile
来描述镜像的构建过程,这样更清晰,也方便版本控制。
注意事项
docker commit
并不会保存容器的日志、网络设置等,它只保存容器的文件系统(包括文件和已安装的包等)。频繁使用
docker commit
可能会导致镜像管理变得混乱,因为每次提交都会生成一个新的镜像,可能不容易追踪和管理。
Dockerfile
Dockerfile
是一个文本文件,包含了一系列指令,用于自动化地构建 Docker 镜像。它就像是镜像的“配方”,你通过编写 Dockerfile
指定镜像的基础环境、所需的依赖、配置、执行命令等,Docker 根据这些指令来一步一步地构建出一个新的镜像。
简单来说,Dockerfile
就是用来告诉 Docker,怎么从零开始构建一个镜像。这个过程包括选择基础镜像、安装软件、拷贝文件、设置环境变量、运行程序等。
一个 Dockerfile
由多个指令(每个指令一行)组成,Docker 会按顺序执行这些指令来构建镜像。每个指令都有特定的功能。
1 | # 使用官方 nginx 镜像作为基础镜像 |
除了上面的之外,还有一些别的常见指令,都可以看看哈
- FROM: 指定镜像的基础镜像,用于构建新镜像。
- LABEL: 添加元数据(如作者信息或版本号)到镜像中。
- RUN: 在镜像构建过程中执行命令,通常用于安装软件。
- COPY: 将本地文件或目录复制到容器中的指定路径。
- ADD: 类似于
COPY
,但支持从 URL 下载文件,并自动解压.tar
文件。 - WORKDIR: 设置容器内的工作目录,后续的指令会在此目录下执行。
- EXPOSE: 声明容器监听的端口,但不会实际开放端口。
- CMD: 设置容器启动时执行的默认命令。
- ENTRYPOINT: 设置容器启动时执行的主命令,不能被覆盖。
- ENV: 设置环境变量,在容器内有效。
- ARG: 定义构建时使用的变量,可以在
docker build
时传入。 - VOLUME: 创建一个挂载点,用于容器与宿主机之间的数据共享。
- USER: 设置在容器内运行命令时使用的用户。
- WORKDIR: 设置工作目录,指定后续操作路径。
写好了文件之后,用下面的命令来构建
我们要拷贝文件进去,所以先复制过来
1 | cp /etc/fstab . |
构建出lxh:v1这个镜像
1 | docker build -t lxh:v1 . |
输出:
1 | [+] Building 8.3s (9/9) FINISHED docker:default |
我们注意最后的输出,直接把我们的镜像生成了docker.io/library/lxh:v1
,我们看看本地有哪些镜像
1 | docker images |
对于docker来说,没有域名前缀和命名空间,默认就是docker.io/library
1 | REPOSITORY TAG IMAGE ID CREATED SIZE |
Dockerfile可以作为源代码,直接上传到Git仓库中做版本控制哦,这样每次变化都可以追踪和回溯。
推送镜像到仓库
如果对名称不满意,或者需要改成自己的前缀,方便推送到自己的仓库,就用docker tag
给容器镜像改名
1 | docker tag lxh:v1 registry.myk8s.cn/library/lxh:v1 |
改名后,用docker push
推送到远程仓库,在推送时,会自动解析你镜像前的域名,自动像那个地方推送
1 | docker tag lxh:v1 registry.myk8s.cn/library/lxh:v1 |
输出
1 | The push refers to repository [registry.myk8s.cn/library/lxh] |