Docker
Docker 基础概念、镜像与容器的工作方式,以及常用安装和使用记录。
一、Docker 简介
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
Docker 和传统虚拟化方式的不同之处: 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
Docker 的优势:
- 更高效利用系统资源
- 更快速的启动时间
- 一致的运行环境
- 持续交付和部署
- 更轻松的迁移
- 更轻松的维护和扩展
二、基本概念
- Dockerfile 用于定义如何构建镜像。
- 镜像是只读的模板,由 Dockerfile 构建或从注册表拉取。
- 容器是镜像的运行实例,具有可读写层。
1、镜像(Image)
Linux 内核启动后,会挂载 root 文件系统为其提供用户空间支持。
Docker 镜像(Image),就相当于是一个 root 文件系统。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
分层存储:
镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
2、容器(Container)
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。
每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为 容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者 绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
数据卷 的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
3、仓库(Registry)
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。
通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
Docker Registry 公开仓库
官方的 Docker Hub,需要换成国内镜像。
Docker Registry 私有仓库
三、安装
1、Centos
警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker。
需要 root 用户,不在 root 用户下需要使用 sudo + 命令。
准备工作
系统要求
Docker 支持 64 位版本 CentOS 7/8,并且要求内核版本不低于 3.10。CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。
卸载旧版本
旧版本的 Docker 称为 docker 或者 docker-engine,使用以下命令卸载旧版本:
# 卸载旧 Docker 版本
yum -y remove docker docker-common docker-selinux docker-engine
使用 yum 安装
执行以下命令安装依赖包:
yum install -y yum-utils
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 yum 软件源:
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 添加阿里云 Docker 源
sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo # 替换为阿里云镜像地址
# 官方源:
# sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
如果需要测试版本的 Docker 请执行以下命令:
sudo yum-config-manager --enable docker-ce-test # 启用 Docker 测试版本仓库
安装 Docker
更新 yum 软件源缓存,并安装 docker-ce。Docker 分为 CE 版和 EE 版,一般我们用 CE 版就够用了。
sudo yum install docker-ce docker-ce-cli containerd.io # 安装 Docker CE、CLI 和 containerd
启动 Docker
sudo systemctl enable docker # 设置 Docker 开机自启
sudo systemctl start docker # 启动 Docker 服务
建立 docker 用户组
默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。
建立 docker 组:
sudo groupadd docker # 创建 docker 用户组
将当前用户加入 docker 组:
sudo usermod -aG docker $USER # 把当前用户加入 docker 组
退出当前终端并重新登录,进行如下测试。
测试 Docker 是否安装正确
docker version # 查看 Docker 版本
docker run hello-world # 运行 hello-world 测试容器
若能正常输出以上信息,则说明安装成功。
卸载 Docker
yum remove docker-ce docker-ce-cli containerd.io # 卸载 Docker Engine、CLI 和 containerd
rm -rf /var/lib/docker # 删除 Docker 默认工作目录
镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以通过配置文件添加 Docker 国内镜像加速。
Docker 安装后默认没有 daemon.json 这个配置文件,需要进行手动创建。配置文件的默认路径:/etc/docker/daemon.json
四、使用镜像
获取镜像
从 Docker 镜像仓库获取镜像。
docker pull ubuntu:18.04 # 拉取指定版本 ubuntu 镜像
docker image pull alpine:latest # 使用 image 子命令拉取 alpine 镜像
docker pull mysql # 拉取 mysql latest 镜像
docker pull mysql:5.7 # 拉取指定版本 mysql 镜像
列出镜像
docker images # 查看本地镜像
docker image ls # 查看本地镜像
docker search nginx # 在仓库中搜索镜像
docker search --filter stars=100 nginx # 按过滤条件搜索镜像
docker search --filter is-official=true nginx # 搜索官方镜像
镜像体积说明:
- 在 Docker Hub 上是压缩状态,到本地后展开,所以本地显示比 Hub 上更大。
- Docker 镜像是多层存储结构,并且可以继承、复用;相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
删除镜像
指定名称删除镜像:
docker rmi java:8 # 删除指定镜像
docker rmi -f java:8 # 强制删除指定镜像
删除所有没有引用的镜像:
docker rmi `docker images | grep none | awk '{print $3}'` # 删除 none 镜像
docker rmi -f $(docker images -q) # 强制删除所有镜像
镜像删除的 Untagged 和 Deleted
- Untagged:一个镜像可以对应多个标签,当删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。
- Deleted:当一个镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。
虚悬镜像
新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none> 的镜像。
docker image ls -f dangling=true # 查看虚悬镜像
docker image prune # 删除虚悬镜像
其他命令
docker image ls -a # 查看所有镜像,包括中间层镜像
docker image ls ubuntu # 根据仓库名列出镜像
docker image ls ubuntu:18.04 # 准确列出某个镜像
docker image rm <镜像1> <镜像2> # 删除多个本地镜像
五、操作容器
当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
启动容器
- 通过镜像创建容器,使用
docker run
# docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]
# --name # 分配一个唯一的名字
# -d # 后台运行
# -it # 使用交互方式运行
# --rm # 退出容器后删除
# -p 8080:9090 # 将主机的 8080 端口映射到容器的 9090 端口
# --restart=always # 设置容器自启
docker run -d -P --name nginx01 -v /etc/nginx:ro nginx # 后台启动 nginx 并挂载目录
- 重新启动容器,使用
docker start
docker start <容器名称> # 使用名称启动容器
docker start a1b2c3d4e5f6 # 使用 ID 启动容器
前后台切换
docker attach + 容器名称
命令可以把后台运行的容器挂到前台来。如果容器运行时使用了 -it,那么可以使用 Ctrl + P Ctrl + Q 把容器重新放到后台,继续执行。如果没有使用 -it,那么就只能使用 Ctrl + C 杀死容器才能退出。
设置容器自启动
在运行 docker 容器时加如下参数来保证每次 docker 服务重启后容器也自动重启:
docker run --restart=always nginx # 创建时设置容器自动重启
docker update --restart=always <CONTAINER ID> # 更新已有容器的重启策略
启动一个 bash 终端,允许用户进行交互
docker run -t -i ubuntu:18.04 /bin/bash # 交互式启动 ubuntu 并进入 bash
后台运行容器
通过添加 -d 参数来实现,使用 -d 参数启动后会返回一个唯一的 id。
可以通过 docker container ls 命令来查看容器信息。
docker run -t -i -d ubuntu:18.04 /bin/bash # 后台启动 ubuntu 容器
docker container ls # 查看运行中的容器
注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。
查看容器
docker ps # 查看运行中容器
docker ps -a # 查看所有容器
docker commit my-stopped-container my-new-image:v1 # 将容器当前状态保存为新镜像
终止
docker container stop <id> # 终止一个运行中的容器
docker container ls -a # 查看所有容器
docker container start <id> # 启动终止状态的容器
docker container restart <id> # 重启容器
进入容器
在使用 -d 参数后,容器将在后台执行,有时需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令。
attach
docker attach [id] # 连接到容器主进程
如果使用这个命令,在完成操作后执行 exit,会导致容器停止。
exec
docker exec -i -t [id] [bash] # 在容器中启动一个新进程进入 bash
docker exec -it 50f bash # 交互式进入容器 bash
docker exec -it 50fe sh # 交互式进入容器 sh
如果使用这个命令,在完成操作后执行 exit,不会导致容器停止。
退出容器,不关闭容器
Ctrl + P + Q
导入导出
镜像和容器导出和导入的区别:
- 容器导出,是将当前容器变成一个新的镜像。
- 镜像导入,是复制的过程。
save 和 export 区别:
save保存镜像所有的信息,包含历史。export只导出当前的信息。
容器导出
docker container ls -a # 查看终止状态容器
docker export [id] > [xxx.tar] # 导出容器到当前目录
docker export [id] > [/xxx/xxx.tar] # 导出容器到指定目录
容器导入
docker import [xxx.tar] # 导入容器导出文件
docker image ls # 查看导入后的镜像
docker tag [id] [仓库名:标签] # 为镜像设置仓库和标签
镜像导出(保存)
docker save [id] > [xxx.tar] # 保存镜像到当前目录
docker save [id] > [/xxx/xxx.tar] # 保存镜像到指定目录
镜像导入
docker load < [xxx.tar] # 从当前目录加载镜像
docker load < [/xxx/xxx.tar] # 从指定目录加载镜像
docker image ls # 查看导入后的镜像
docker tag [id] [仓库名:标签] # 给镜像重新打标签
删除
删除容器
docker container ls # 查看运行状态容器
docker container rm [name] # 删除不在运行状态的容器
docker container ls -a # 查看所有容器
docker container rm -f [name] # 强制删除运行状态的容器
删除所有不在运行状态的容器
docker container prune # 删除所有已停止容器
六、使用 docker 部署项目
Docker 可视化系统
先拉取最新的镜像:
docker pull portainer/portainer:latest # 拉取 Portainer 镜像
为 Portainer 创建一个 volume:
docker volume create portainer_data # 创建 Portainer 数据卷
启动:
docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer # 启动 Portainer
注意 --restart=always 作用是 Docker 重启时,自动重启容器。
成功启动后,访问地址 http://localhost:9000/ 就可以了。
七、数据卷
数据卷是将容器内数据映射到主机文件系统中,即容器数据持久化。
挂载类型
两种方式:
volume(常用)
docker run -v /etc/nginx nginx # 匿名挂载
docker run -v nginxConfig:/etc/nginx nginx # 具名挂载
docker inspect 容器名 # 查看挂载信息
bind
docker run -v /home/test:/home centos # 绑定挂载宿主机目录
docker inspect 容器名 # 查看挂载信息
docker volume(卷管理命令)
docker volume create volume_name # 创建一个自定义容器卷
docker volume ls # 查看所有容器卷
docker volume inspect volume_name # 查看指定容器卷详情信息
docker volume rm volume_name # 删除自定义数据卷
数据卷容器
数据卷容器(Data Volume Containers):用专门的容器来挂载数据卷,其他容器通过挂载这个父容器来实现数据共享,这个专门挂载数据卷的容器就是数据卷容器。简单的总结就是有一个容器来专门管理数据的持久化和容器之间数据共享。
如果我们经常需要 多个容器之间进行数据共享,我们需要用到命令 --volumes-from。
具体实例:
-
我们从仓库拉一个 centos 的容器镜像
docker pull centos # 拉取 centos 镜像 -
然后运行这个镜像,创建 mycentos 容器,并创建一个数据卷挂载到
/mydatadocker run -it -v /mydata --name mycentos centos # 创建带数据卷的容器 -
再运行两个容器,在这两个容器中使用
--volumes-from来继承 mycentos 容器中的数据卷docker run -it --volumes-from mycentos --name soncentos1 centos # 继承 mycentos 的数据卷 docker run -it --volumes-from mycentos --name soncentos2 centos # 继承 mycentos 的数据卷
此时,容器 soncentos1 和 soncentos2 都挂载同一个数据卷到相同的 /mydata 目录。三个容器任何一方在该目录下的写入数据,其他容器都可以看到。
--volumes-from 后面的容器(mycentos)就是 数据卷容器。可以通过数据卷容器创建多个子容器(soncentos1,soncentos2),即便是删除了初始的数据卷容器 mycentos,或是删除了其它容器,但只要是有容器在使用该数据卷,那么它里面的数据就不会丢失(除非是没有容器在使用它们)。
八、Docker File
见 [[dockerFile]]
九、Docker Compose
见 [[Docker Compose]]
命令记录
docker logs --tail=100 CONTAINER_ID # 查看最后 100 行日志
docker logs -f -t --since="2018-02-08" --tail=100 CONTAINER_ID # 查看指定时间后的日志并持续输出
docker logs --since 30m CONTAINER_ID # 查看最近 30 分钟日志
docker logs -t --since="2018-02-08T13:23:37" CONTAINER_ID # 查看某时间之后的日志
docker logs -t --since="2018-02-08T13:23:37" --until "2018-02-09T12:23:37" CONTAINER_ID # 查看某时间段日志