Docker¶
知识的反刍是适当且必要的 🐮
本篇内容完全借鉴于:
这篇笔记重点在于描述docker的 提出背景、设计原理、常见指令的cheat sheet
提出背景¶
环境配置的痛点
软件开发最大的麻烦事之一, 就是环境配置。用户计算机的环境都不相同, 怎么知道自家的软件, 能在别人的机器跑起来?
用户必须保证两件事:操作系统的设置, 各种库和组件的安装。只有它们都正确, 软件才能运行。举例来说, 安装一个 Python 应用, 计算机必须有 Python 引擎, 还必须有各种依赖, 可能还要配置环境变量。
如果某些老旧的模块与当前环境不兼容, 那就麻烦了。开发者常常会说:"它在我的机器可以跑了"(It works on my machine), 言下之意就是, 其他机器很可能跑不了。
环境配置很麻烦, 换一台机器, 就要重来一次。很多人想:我们能不能从根本上解决问题, 软件可以带环境安装?也就是说, 安装的时候, 把原始环境一模一样地复制过来。
带环境配置软件的类型
基于上述的想法, 我们有以下两种设计思路:
(1) 使用虚拟机 (virtual machine)
- 使用 virtual machine:
- 可以在一种操作系统里面运行另一种操作系统, 如: 在 Windows 系统里面运行 Linux 系统
- 应用程序对此毫无感知, virtual machine 对上层应用完全透明
- 弊端:
- 资源占用量太高: 每次配置都是“操作系统level”的
- 启动速度慢: 每次启动都是“启动OS”, 因此很耗时
(2) 使用容器 (container):
由于虚拟机存在这些缺点, Linux 发展出了另一种虚拟化技术: Linux 容器(Linux Containers, 缩写为 LXC)
Linux 容器不是模拟一个完整的操作系统, 而是对进程进行隔离。或者说, 在正常进程的外面套了一个保护层
对于容器里面的进程来说, 它接触到的各种资源都是虚拟的, 从而实现与底层系统的隔离
由于容器是进程级别的, 相比虚拟机有很多优势:
- 启动速度快:
- 进程-level vs. OS-level
- 容器里面的应用, 本质上就是底层系统的一个进程, 而不是虚拟机内部的进程。所以, 启动容器相当于启动本机的一个进程, 而不是启动一个操作系统, 速度就快很多
- 资源占用少:
- 容器只占用需要的资源, 不占用那些没有用到的资源;虚拟机由于是完整的操作系统, 不可避免要占用所有资源
- 共享性好:
- 鉴于容器是 进程-level, 所以多个容器之间可以共享资源, 虚拟机只能独享资源
设计原理¶
Docker是什么
对比之下, 我们发现使用容器技术带来了更多的增益, Docker 就在这样的背景下应运而生
Docker 属于 Linux 容器的一种封装, 提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案
- Docker 将应用程序与该程序的依赖, 打包在一个文件里面
- 运行这个文件, 就会生成一个虚拟容器
- 程序在这个虚拟容器里运行, 就好像在真实的物理机上运行一样, 因此不用担心环境问题
Tip
Docker 的接口相当简单, 用户可以方便地创建和使用容器, 把自己的应用放入容器
容器还可以进行版本管理、复制、分享、修改, 就像管理普通的代码一样
Docker的常见服务
- 提供一次性的运行环境:
- 常见的是: 将别人的程序带环境拉下来, 在本机运行
- 提供elastic的云服务:
- 常见的是: 将kubernetes和docker一起做
- 原因: Docker 容器可以随开随关, 很适合动态扩容, 扩展性很强
- 组建微服务架构:
- 常见的是在一台机器上运行多个docker, 组建相关服务
- 原因: Docker 是进程级别的封装, 一台机器上可以运行多个进程-level的docker, 它们之间进行串口通信
入门操作¶
这里不建议直接上手,这更像是笔者自己的cheat sheet。入门学习的话,更建议看给的参考文档:
安装docker¶
以 MacOS 为例:
Bash | |
---|---|
1 |
|
安装完验证:
Bash | |
---|---|
1 2 |
|
sudo 权限问题
本质: Docker 需要用户具有 sudo
权限
在 Linux 上,安装之后要手动运行 sudo usermod -aG docker $USER
将 当前用户(CLI中的) 加入 Docker 用户组
但是在 MacOS 上,安装时就要你输入密码,因此安装时已经自动将 sudo
权限赋予了,因此不用管 🚀
几组概念的解析¶
image 文件¶
Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器
- image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例
- 同一个 image 文件,可以生成多个同时运行的容器实例
image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成
举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image:
Bash | |
---|---|
1 2 3 4 5 |
|
image 文件是通用的,一台机器的 image 文件拷贝到另一台机器,照样可以使用。一般来说,为了节省时间,我们应该尽量使用别人制作好的 image 文件,而不是自己制作。即使要定制,也应该基于别人的 image 文件进行加工,而不是从零开始制作
为了方便共享,image 文件制作完成后,可以上传到网上的仓库。Docker 的官方仓库 Docker Hub 是最重要、最常用的 image 仓库
容器文件¶
image 文件生成的容器实例,本身也是一个文件,称为容器文件
也就是说,一旦容器生成,就会同时存在两个文件:image 文件和容器文件。而且关闭容器并不会删除容器文件,只是容器停止运行而已
Bash | |
---|---|
1 2 3 4 5 |
|
上面命令的输出结果之中,包括容器的 ID。很多地方都需要提供这个 ID,比如上一节终止容器运行的 docker container kill
命令
终止运行的容器文件,依然会占据硬盘空间,可以使用 docker container rm
命令删除
Bash | |
---|---|
1 |
|
运行上面的命令之后,再使用 docker container ls --all
命令,就会发现被删除的容器文件已经消失了
Dockerfile文件¶
学会使用 image 文件以后,接下来的问题就是,如何可以生成 image 文件?如果你要推广自己的软件,势必要自己制作 image 文件
这就需要用到 Dockerfile 文件。它是一个文本文件,用来配置 image。Docker 根据 该文件生成二进制的 image 文件
dockerfile 实战解析: 看看 阮一峰docker中的“十”
TL;DR¶
梳理一下,按照逻辑关系,不同文件及其对应的含义应该是:
- Dockerfile: 用户手写
- Dockerfile 作为配置文件,构建相应的 image 文件
- Image: 二进制文件
- Image 作为可执行文件,用于生成 容器文件
- 容器文件: 可执行文件,生成实例
- 容器文件,可以在CLI中用相应的指令交互
- 生成 运行实例
cheat sheet¶
image
Bash | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
官方库拉取?
docker image pull
是抓取 image 文件的命令
library/hello-world
是 image 文件在仓库里面的位置,其中 library
是 image 文件所在的组,hello-world是 image 文件的名字
由于 Docker 官方提供的 image 文件,都放在library组里面,所以它的是默认组,可以省略。因此,上面pull的指令中, library/[imageName]
也可以简写成 [imageName]
Dockerfile的格式?
Text Only | |
---|---|
1 2 3 4 5 6 |
|
-p
参数: 容器的 3000 端口映射到本机的 8000 端口-it
参数: 容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器koa-demo:0.0.1
: image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)/bin/bash
: 容器启动后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 ShellCMD node demos/01.js
: 容器启动后自动执行node demos/01.js
指令
RUN
和 CMD
的区别与使用
RUN
命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;CMD
命令则是在容器启动后执行- 一个 Dockerfile 可以包含多个RUN命令,但是只能有一个
CMD
命令 - 指定
CMD
命令以后,docker container run
命令就不能附加命令了
container
一般查询:
Bash | |
---|---|
1 2 3 4 |
|
关闭/资源释放:
Bash | |
---|---|
1 2 3 4 5 |
|
运行 (单次/交互):
Bash | |
---|---|
1 2 3 4 5 |
|
Danger
- 对于
docker container run hello-world
:- 运行完自动关闭
- 对于
docker container run -it ubuntu bash
:- 运行完需要调用
docker container kill [continID]
关闭
- 运行完需要调用
一条常见运行指令的写法
我们以ubuntu交互式运行的shell为例:
Bash | |
---|---|
1 |
|
--rm
代表: 容器停止运行(退出)之后,会被立刻删除- 等价于:
docker container rm [containID]
- 等价于:
--name
代表: 给容器命名,如果没有加这个参数,那么 docker 会给容器随机起一个名字-it
可交互 Shell 所必须的参数-i
会将容器的 init(主进程,这里是 /bin/bash)的标准输入与 docker 这个程序的标准输入相连接-t
会告知主进程输入为终端(TTY)设备
ubuntu:20.04
: 指定镜像是ubuntu,版本是20.04- 不写版本, 则默认是
latest
- 不写版本, 则默认是
- 默认的指令是:
/bin/bash
- 明确写出
bash
也可以
- 明确写出
在执行以上命令之后,你会获得一个 Ubuntu 20.04 的容器环境,退出 Shell 之后容器就会被销毁
Bash | |
---|---|
1 |
|
获得一个 最新版本(可能是24.04) 的Ubuntu容器环境, 立马进入shell, 退出后容器不会被销毁