容器化技术不是模拟的一个完整的操作系统,容器内的应用直接运行在宿主机内核,自身没有自己的内核,也没有去虚拟硬件,每个容器间是互相隔离的。
当 docker 新建一个容器时,不需要像虚拟机一样重新加载一个操作系统内核,它直接用宿主机操作系统内核。
linux 基本命令
1 | whereis [FILENAME] # 查找文件 |
组成
- 镜像(image):创建容器的模板
- 容器(container):docker 利用容器技术独立运行的一个或一组应用,是服务
- 仓库(repository):存放镜像的地方
安装卸载
安装
参考官方文档,这里只做部分注解
1 | # 安装前最好更新下软件包 |
docker-ce:社区版 docker-ee:企业版
启动测试镜像
docker 执行 run 命令时,会本地不存在该镜像,会前去镜像仓库查找并拉取
1 | docker run hello-world |
卸载
1 | # 卸载依赖 |
命令
帮助命令
1 | docker version # docker 版本信息 |
镜像命令
docker images 查看本地主机上的镜像
1 | [root@marry ~]# docker images |
docker search 搜索镜像
1 | [root@marry ~]# docker search redis |
docker pull 下载镜像
默认下载的是剔除了所有不必要文件的最小镜像,保证最小的可运行环境。
1 | [root@marry ~]# docker pull redis |
docker rmi 删除镜像
1 | # options |
查看镜像元数据
1 | docker inspect [IMAGE ID] |
查看镜像构建过程
1 | docker history [IMAGE ID] |
docker save 镜像导出
1 | docker save [OPTIONS] IMAGE |
docker load 镜像导入
1 | docker load [OPTIONS] |
容器命令
有了镜像才能创建容器
docker run 新建容器并启动
1 | docker run [OPTIONS] IMAGE [COMMAND] |
示例:创建并进入容器
交互需要获取控制台,linux 的控制台一般在 bin 目录下,这里是使用 bin 目录下的 bash 命令的意思
1 | # 可以看到执行后进入了一个新的控制台页面 |
退出容器
1 | exit # 退出即停止容器(exec方式进入容器的话退出不会停止) |
当启动没有前台应用的容器时,如 centos ,若要使得容器启动后保持运行,且支持被
start
和restart
启动容器,必须携带-it
参数并指定交互式控制台,即/bin/bash
,否则启动即停止。
这是因为 Docker 容器若后台运行,就必须有一个前台进程(即控制台)。前台进程结束,容器就会退出。若未给容器指定前台进程且容器并未自带前台进程,容器会发现自己没有提供前台服务,就会立刻自行终止。
若想要创建后台运行的容器且不进入容器内部,再加一个-d
参数即可。
docker ps 列举容器
1 | # options |
docker rm 删除容器
1 | # options |
进入正在运行的容器
1 | # 进入容器后开启一个新的命令行 |
启动停止和重启容器
1 | docker start [CONTAINER ID] # 启动停止的容器 |
从容器内拷贝文件至主机
1 | docker cp [CONTAINER ID]:容器文件路径 本机路径 |
查看容器中的进程
1 | docker top [CONTAINER ID] |
其他常用命令
docker logs 查看日志
1 | docker logs [OPTIONS] [CONTAINER ID] |
docker commit 提交容器成为一个新副本
1 | docker commit -m 提交的描述信息 -a 作者 [CONTAINER ID] 目标镜像名:[TAG] # 和git类似 |
并未做出影响大小的改动,但最终大小却多了 4MB,这就牵扯到了镜像分层原理
容器数据卷
如果数据在容器里,容器删除时数据也会丢失。现在希望数据可以保存在本机内,不会跟随着容器消失。 数据卷会将 docker 内产生的数据同步到本地,即将容器内的目录挂载到本机上,且支持容器间数据共享,即多个容器共用一个目录。
可以理解成双向绑定
命令挂载 -v
1 | docker run -it -v 主机目录:容器内目录 # 可以挂载多个,目录不存在会直接创建 |
docker volume 查看本地数据卷信息
1 | # options |
匿名挂载
-v 只跟一个容器内路径,主机路径也不指定
1 | [root@marry ~]# docker run -itd -v /data --name test-redis redis /bin/bash |
具名挂载
-v 卷名:容器内路径
1 | # 命名为 test-name-redis |
所有 dockers 容器内未指定主机路径的数据卷,都是在
/var/lib/docker/volumes/${卷名}/_data
目录下
大多数情况下都使用具名挂载,不建议使用匿名挂载
容器挂载权限
-v xxx:xxx:ro
ro:只读 rw:读写
1 | # 创建只读数据卷 |
对于只读数据卷,只允许在主机这边修改其内容,容器内将无法修改卷内容
Dockerfile
即用来构建 docker 镜像的构建文件,可以理解成命令脚本,执行即会创建一个镜像。
Dockerfile 由多个命令组成,每个命令都是一层。
由于官方镜像大多都是安装了少数命令的基础包,因此常需要通过 Dockerfile 搭建自己的镜像。
Dockerfile 内容
格式:指令(大写) 参数
1 | FROM # 这个镜像基于哪个基础镜像,最基础镜像:scratch |
docker build 构建
1 | docker build -f [Dockerfile路径] -t [新镜像名称(小写)] [放置目录] |
若 Dockerfile 文件名为
Dockerfile
,则不需要加-f
,docker 会自动寻找当前目录下该文件名的文件
使用构建出来的镜像创建容器时,要么使用 镜像 id 构建,要么必须使用 名字:tag(如果有 tag) 来构建
–volume-from 数据卷共享
一般用于多个 mysql 或 redis 实现数据共享
1 | # 使用上面的 Dockerfile 创建的镜像构建容器 |
镜像发布
登录/退出登录 DockerHub
1 | [root@marry ~]# docker login [options] [value] |
发布镜像
1 | docker push [author]/[ImageName]:[version] |
为了防止重名,最好以 作者名/镜像名 的方式构建镜像并上传
若未注明 version,则会以 latest 标签上传
阿里云容器镜像服务
- 创建命名空间
- 创建仓库,按教程推送
- 务必设置镜像为教程中指定的格式,否则阿里云会拒绝 push
原理
镜像文件系统
linux 实际由一层一层文件系统组成。最底层的有 bootfs
(引导系统+内核)和 rootfs
(操作系统发行版)。
rootfs 即各种 linux 操作系统的发行版,包含了典型 linux 系统中的 /bin /etc /dev
等文件夹。对于一个精简的 os,rootfs 可以非常精简,只包含最基本的命令和工具。
而 docker 的镜像因为底层直接使用本机的内核,不需要携带最大的 bootfs
,仅携带精简版的 rootfs
即可。因此安装一个 docker 的 centos
镜像只有 200+MB
大小。
镜像分层原理
镜像都是只读的,当容器启动时,一个新的可写层会被加到镜像顶部。这一层就是通常说的容器层,容器层之下的为只读的镜像层。
新做的所有改动都是在容器层实现的,而改动之后重新打为一个新的镜像发布时,会把容器层和镜像层打包为一个新的整体的镜像。
docker 网络
实践操作
ip addr
查看网卡,得到结果如下图:
图中有三个网卡,分别是:
- lo: 本机地址
- eth0: 阿里云地址
- docker0: docker 地址
尝试 docker 和容器的连接
新运行一个容器,在内部执行 ip addr
查看:
存在两个网卡,分别是 lo
和 eth0@if315
,其中 eth0@if315
就是 docker 分配给容器的网络(@后的数字不固定)
若提示
ip: command not found
则需执行yum install iproute iproute-doc
尝试在主机 ping 一下这个地址,可以 ping 通:
1 | [root@marry ~]# ping 172.17.0.3 |
可以看到,本机中 docker 的 ip 地址为 127.17.0.1
,分配的容器的 ip 地址为 127.17.0.3
。可以看出,docker 类似一个路由器,docker 和 它创建的容器均处于同一个网段。
此时在主机下再次执行 ip addr
,发现多了一个 315: veth3742c50@if314
与容器内的 314: eth0@if315
相对应:
尝试容器间的连接
再次用相同镜像运行一个容器,使用 ip addr
查看发现其 ip 为 127.17.0.4
,网卡为 316: eth0@if317
,紧接上一个容器的 314: eth0@if315
:
尝试用该容器去 ping 上一个容器,成功 ping 通:
1 | [root@2ffe04cbda1c marry-api]# ping 172.17.0.3 |
大白话理解
安装 docker 就是在家(主机)里装了个路由器,docker 就是那个路由器
docker 创建的每个容器就是家里连接路由器的设备
那么一个局域网内的两台设备能够连接不是很正常的事情
原理
- 每启动一个 docker 容器,docker 都会为其分配一个 ip
- 只要安装了 docker,就会有一个网卡
docker0
- 使用桥接模式,evth-pair 技术,即 一对虚拟设备接口,一端连协议,一端互相连接,类似桥梁的作用
- 所有容器在不指定网络时,都是使用的 docker0 网卡,docker 会给容器分配一个默认的可用 ip
- docker 中所有的网络接口都是虚拟的,因为虚拟的转发效率高
补充:127.17.0.1/16 释义
这里后面的 8/16/20
为子网掩码的缩写形式,意思是位数,计算机中 8 为 1 位,而 ip 地址 255.255.255.255
的二进制为 00000000.00000000.00000000.00000000
因此 /8
指的该网段(即的不可分配的部分)为前 8 位,即 后面三个 255 均可以分配出去,有 256 ^ 3
种分配方式 同理 /16
为前 16 位,后面两个 255 为可分配部分,有 256 ^ 2
种分配方式 而 /20
为前 20 位,可分配部分为 第三个 255 中的 16(2 ^ 4) 个网段 加上最后一个 255,有 16 * 256
种分配方式
在 docker0 的 ip 这里的子网掩码为 /16,因此说明 docker 最多可分配 256 ^ 2 即 65536
个网络接口。
自定义网络
使用 docker network ls
查看所有网络
1 | [root@iZ8vbe901lz5iyyekmlg7kZ adachi]# docker network ls |
网络模式:
- bridge: 桥接模式,即在 docker 上搭桥,让新容器 0.2 0.3 通过 0.1 的 docker 访问
- none: 不配置网络
- host: 主机模式,和宿主机共享网络
- container: 容器内网络连通(了解即可)
自定义网络一般使用 bridge 桥接模式
在此之前,直接
docker run
启动的容器,其实默认带着一个--net bridge
的 option,即 docker0。
但 docker0 是不能使用容器名访问的,只能使用 ip,因此才有了 自定义网络
创建网络
1 | docker network create [options] [name] |
多个使用 自定义网络 创建的容器,互相之间可以直接使用 容器名 访问
通过自定义网络的方式,可以让各个集群各自使用不同的网络
查看网络元信息
1 | docker network inspect [network name] |
网络连通
不同网段是无法相互连接的,但容器可以和网络打通,现需要让 网络a的容器a 连接到网络b上
1 | docker network connect [OPTIONS] NETWORK CONTAINER |
发现结果是 marrynet 的 container 属性中直接被追加了容器 docker0-container,即一个容器两个 ip 地址
公网 ip 和私网 ip 也是同理其实
这样处理过后,就可以实现网络 a 的容器 a ping 通网络 b 的容器 a了
docker-compose
docker-compose 是 docker 官方的开源项目,用于定义、运行多个容器,根据编写的配置文件来实现批量容器编排。
安装 docker-compose
在 windows 和 macos 上,安装 docker desktop
会自动附带 docker-compose
,无需手动安装,这里讲 linux 的安装方法
前往 https://github.com/docker/compose/releases 查找指定版本 release 包,现在最新版为 v2.5.0
方式一
直接执行以下指令,缺点,网速可能极慢(
1 | sudo curl -L "https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose |
方式二
手动下载对应 release 包,自己系统环境可以通过 uname -s
和 uname -m
来查看,我这里为 linux-x86_64
。
下载后通过 ftp 工具上传至服务器 usr/local/bin/
目录下,执行命令
1 | # 设置可执行权限 |
使用步骤
- 需要一个
Dockerfile
来保证项目在任何地方都可以运行 - 需要一个
docker-compose.yml
来定义服务 - 使用
docker-compose up
运行服务
docker-compose.yml
docker-compose.yml 分为三层
1 | version: # 第一层:指定 docker-compose 版本 |
部分配置说明
1 | # services |
docker-compose
在不指定容器名的情况下启动的默认容器名为:文件夹名-服务名-num