基础概念

  1. 容器(Container):
    定义:镜像是一个只读的模板,包含了运行应用所需的所有内容:代码,运行时,库文件,环境变量和配置文件
    特点:
    分层存储:镜像由多个层组成,每一层代表一次修改
    只读性:镜像本身是可读的,不能直接修改
    可复用:同一个镜像可以创建多个容器
    版本管理:通过标签(tag)进行版本管理
    轻量化的运行实例,包含应用代码,运行时环境和依赖库.基于镜像创建,与其他容器隔离,共享主机操作系统内核(比虚拟机更高效)
  2. 镜像(Image):
    定义:容器是镜像的运行实例,是一个轻量级,可移植的执行环境
    制度模板,定义了容器的运行环境(如操作系统,软件配置等).通过分层存储(Layer)优化空间和构建速度
  3. Dockerfile:文本文件,描述如何自动构建镜像(例如指定基础镜像,安装软件,复制文件等)

镜像和容器的关系:

1. 当我们利用docker安装应用时,docker会自动搜索并下载应用的镜像(image).镜像不仅包含应用本身,还包含应用运行所需要的环境,配置,系统函数库.Docker会在运行镜像时创建一个隔离环境,称为容器(container)
2. 镜像类似于一个模具,容器就是制作出来的糕点
  • 镜像仓库:存储和管理镜像的平台,Docker官方维护了一个公共仓库:Docker Hub
    客户端Client执行docker run命令,Docker Server的docker daemon守护进程监听到之后,就去镜像仓库拉取镜像images,运行的时候创建一个隔离的环境container

# 从镜像仓库拉取镜像到本地
docker pull

# 查看所有本地镜像
docker images

# 删除镜像(remove image)
docker rmi

# 自定义镜像(打包)
docker build
# 将demo包下的内容构建成一个镜像 demo包下包括:docker-demo.jar和Dockerfile
docker build -t docker-demo .

# 拉取别人的镜像方法1
# 将打包的镜像保存到本地,变成一个压缩文件
docker save

# -o是起名字 将最新版本的nginx镜像压缩成名为nginx的压缩包
docker save -o nginx.tar nginx:latest


# 将压缩文件加载到镜像中-->解压
docker load
docker load -i jdk.tar

#方法2 推到镜像仓库
docker push

# 创建并运行容器,一条龙服务,此时容器处于运行中状态
docker run

# 停止容器-->就是停止容器中某个进程
docker stop

# 重新启动容器-->将容器内部停掉的进程重新启动
docker start

# 查看当前容器的运行状态(process status)
docker ps
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"

# 查看所有的容器(包括运行完被停掉的)
docker ps -a 

# 删除容器
docker rm

# 查看容器日志
docker logs
#查看nginx的日志
docker logs nginx
# -f持续输入
docker -f logs nginx

# 进入到容器内部(execute)
docker exec

# attach也可以,但是进入再退出之后,容器也会停止运行
docker attach

# -it添加一个可输入的终端,在里面可以使用ls命令
docker exec -it nginx bash

# 查看某个镜像的历史
docker history 镜像名

Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成.这种层级的文件系统UnionFs

系统的启动需要引导加载 黑屏—开机

bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs (root file system),在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版,bootf基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs.

分层理解

比如拉取一个tomcat镜像的时候,有的显示Already exists;有的是pull complete
在这里插入图片描述通过 docker image inspect tomcat查看分层情况
在这里插入图片描述所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层。
在这里插入图片描述
这种情况下,上层镜像层中的层堆栈,并保证多镜像层对外展示为统一的文件系统。Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。Docker 在 Windows 上仅支持 windowsfilter一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW。
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
在这里插入图片描述
Docker的镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部
这一层就是我们通常说的容器层,容器之下的都叫镜像层
在这里插入图片描述

如何提交自己的镜像

commit镜像

# docker commit 提交容器成为一个新的副本  提交到本地

# 命令和git原理类似
docker commit -m="提交的信息" -a="作者" 容器id 目标镜像名:[TAG]

实战测试

# 1.启动了一个默认的tomcat
#2.发现这个默认的tomcat是没有webapps应用,镜像的原因,官方的镜像默认 webapps下面是没有文件的!
#3.我自己拷贝进去了基本的文件
#4.将我们操作过的容器通过commit提交为一个镜像!我们以后就使用我们修改过的镜像

如果想要保存当前容器的状态,就可以通过commit来提交,获得一个镜像
就好比之前学习VM的快照

数据卷

数据如果都存放在容器中,那么我们容器删除,数据就会丢失—>需求:数据可以持久化
Mysql,容器删了,删库跑路—>需求:Mysql数据可以存储在本地
Docker容器中产生的数据,同步到本地
多个容器用同一个目录,可以实现容器间的数据共享
数据卷(volume) 是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁;让数据在多个容器和宿主机之间共享,管理,备份
在这里插入图片描述

# 创建数据卷
docker volume create
# 查看所有数据卷
docker volume ls
# 查看某个数据卷的详情-->可用于查看数据卷在宿主机的位置
docker volume inspect
# 删除指定数据卷
docker volume rm
# 清除数据卷
docker volume prune
# 查看某个容器的数据卷详情
docker inspect

注意区分

# 某个数据卷的详情
docker volume inspect

# 某个容器的详情
docker inspect

创建容器并挂在数据卷(数据卷挂载只能在创建容器的时候进行)
当创建容器时,如果数据卷不存在,会自动创建数据卷(没有指定名字,称为匿名卷);
也可以自己手动创建数据卷(docker volume create),然后在创建容器的时候使用这个已经存在的数据卷

# 创建容器并挂在数据卷
docker run -v 数据卷名字:容器内目录

本地目录挂载


docker run -v 本地目录:容器内目录
# 本地目录必须以"/"或者"./"开头,如果直接以名称开头,可能会被识别为数据卷而非本地目录
# -v mysql:/var/lib/mysql 会被识别为一个数据卷叫mysql
# -v ./mysql:/var/lib/mysql 会被识别为当前目录下的mysql目录

使用数据卷

方式一:直接使用命令来挂载
docker run -it -v 主机目录,容器内目录
# 启动后通过Docker inspect 容器名 来查看挂载情况

在这里插入图片描述
具名挂载和匿名挂载

# 匿名挂载 
# -v 只有容器内路径!
docker run -d -p --name nginx01 -v /etc/nginx nginx

# 查看所有的volume 的情况
docker volume ls
[user@bogon data]$ docker volume ls
DRIVERVOLUME NAME
local        8844a5014041e7d3d780e314c8581df937ab9bcc4812764cd3510adb244c9a8a   ---->匿名挂载
local        html     --------->具名挂载
# 具名挂载
通过inspect查看卷的位置

所有的docker容器内的卷,没有制定目录的情况下都是在/var/lib/dokcer/volumes/xxx/data

# 如何区分是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径::容器内路径 #指定路径挂载

拓展:

#通过 -v 容器内路径:rorw改变读写权限
ro  readonly  # 只读
rw  readwrite  #可读可写

# 一旦这个设置了容器权限,容器对我们挂在出来的内容就有限定了
docker run -d -p --name nginx02 -v juming-nginx:/etc/nginx:ro  nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw  nginx

# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法

# 方式二 使用dockerfile 挂载

#创建Dockerfile文件
#指令(大写)  参数

FROM centos
VOLUME ["volume01","volume02"]
CMD echo "___end___"
CMD /bin/bash
# 这里的每一个参数,都是镜像的一层

数据卷容器

多个容器同步数据

docker run -it --name docker02 --volumes-from docker01 镜像名

在这里插入图片描述

数据卷容器和容器数据卷

  • 容器数据卷:一个容器挂在了一个卷docker run -v mydata:/app/data nginx
    • 使用 -v 挂载
    • 或者在dockerfile 中写VOLUME ["volume01","volume02"]挂载
  • 数据卷容器:多个容器共享一个卷docker create -v /app/data --name data-coontainer nginx,其他的容器通过--volumes-from来共享它的数据docker run --volume-from data-container ubuntu

Dockerfile语法

dockerfile就是用来构建docker镜像的文件
通过这个脚本可以生成一个镜像,镜像是一层一层的,脚本是一个一个的命令,每个命令都是一层

自定义镜像

镜像就是包含了应用程序,程序运行的系统函数库,运行配置等文件的文件包.构建镜像的过程其实就是把上述文件打包的过程

层(Layer)
添加安装包,依赖,配置等,每次操作都形成新的一层

基础镜像(BaseImage)
应用依赖的系统函数库,环境,配置,文件等

入口(Entrypoint)
镜像运行入口,一般是程序启动的脚本和参数

Dockerfile就是一个文本文件,其中包含一个个的指令,用指令来说说明要执行什么操作来构建镜像.将来dockerfile可以根据Dockerfile帮我们构建镜像,常见指令如

FROM 镜像名:tag                              #基础镜像
MAINTAINER ff <ff@example.com>              #镜像是谁写的,姓名+邮箱,该写法已经弃用
LABEL maintainer="ff <ff@example.com>"      #镜像是谁写的,姓名+邮箱
RUN                                         #镜像构建的时候需要运行的命令
ADD <源路径> <目标路径>                       #步骤,添加其他东西
WORKDIR app                                 #镜像的工作目录,用户进入容器之后的位置
VOLUME  ["/app/data","/app/logs"]           #挂载的目录;将app下的data和logs匿名挂载
EXPOSE  80                                  #暴露端口;告诉docker,这个容器会在80端口提供服务,只是声明,不会真的开放端口
CMD     ["python3","app-py"]                #指定容器启动的时候要运行的命令,只有最后一个会生效,可以被替代;
ENTRYPOINT                                  #指定容器启动的时候要运行的命令,可以追加命令
ONBUILD                                     #当构建一个被继承Dockfile这个时候会运行ONBUILD 的指令
COPY <源路径> <目标路径>                      #类似ADD,将我们的文件拷贝到镜像中
ENV  PATH=/usr/local/bin:$PATH              #构建的时候设置环境变量

注意:

  • CMD和ENTRYPOINT
    • 比如在docker run ls -l,CMD写ls -a,最后会按照ls -l执行------如果写多个CMD会执行最后一个
    • 但是ENTRYPOINT会按照ls -al执行
  • ADD和COPY:
    • COPY纯复制文件
    • ADD:复制+解压+网络下载
  • 每个保留关键字(指令)都必须是大写字母
  • 执行从上到下顺序执行
  • #表示注释
  • 每一个指令都会创建一个新的镜像层
  • dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件

基于ubuntu基础镜像,利用dockerfile描述镜像结构


#指定基础镜像
FROM ubuntu:16.04
#配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
#拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
#安装JDK
RUN cd $JAVA_DIR I && tar -xf ./jdk8.tar.gz
&& mv ./jdk1.8.0_144 ./java8
#配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENVPATH=$PATH:$JAVA_HOME/bin
#入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

在这里插入图片描述

更多推荐