目录
前言
为什么会出现docker?
背景
docker理念
容器和虚拟机比较
容器发展简史
容器虚拟化技术
Why Docker
docker的基本组成
镜像(image)
容器(container)
仓库(repository)
总结
第一个docker镜像——hello-world
run干了什么
docker为什么比虚拟机快
帮助启动类命令
帮助命令
镜像命令
docker images 查看所有本地主机上的镜像
docker search 搜索镜像
docker pull 下载镜像
查看镜像/容器/数据卷所占的空间
删除镜像
容器命令
新建容器并启动
列出运行中的程序
退出容器
删除容器
启动和停止容器操作
后台启动容器
前台启动和后台启动区别
Detached (-d)
Foregroud(不写-d)
其他常用命令(重点)
查看日志
查看容器中的进程信息
查看镜像的元数据
进入当前正在运行的容器
方式一
方式二
attach与exec对比:
从容器拷贝文件到主机上
导入和导出容器
Docker镜像
镜像是什么?
镜像是分层的
UnionFS(联合文件系统)
Docker镜像加载原理
为什么 Docker 镜像要采用这种分层结构呢?
重点理解
Docker容器数据卷
数据卷是什么?
数据卷能干什么?
容器和宿主机之间数据共享
查看数据卷是否挂载成功
读写规则限制说明
卷的继承和共享
容器1完成和宿主机的映射
容器2继承容器1的卷规则
继承举例
思考
总结一波当前学的命令:
练习:下载nginx
端口暴露
查找ngnix的位置 whereis
作业: docker来装一个tomcat
前言
大家好,我是躺平哥,这是我的docker学习笔记基础篇,用来记录自己的学历历程,我感觉学习docker主要是多敲多练多看。我也是个小白,如果有写的不好的地方也请大家多多指点!
为什么会出现docker?
背景
在公司开发过程中,开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。
于是——
docker诞生了
Docker的出现使得Docker得以打破过去「程序即应用」的观念。
透过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
大白话来讲:就是我把环境连带程序都给你一块打包起来了!
docker理念
Docker是基于Go语言实现的云开源项目。
只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
一句话:
解决了运行环境和配置问题的软件容器, 方便做持续集成并有助于整体发布的容器虚拟化技术。
在了解docker之前我们先聊聊虚拟机,虚拟机也是一个伟大的发明!
容器和虚拟机比较
容器发展简史
传统虚拟机的缺点
- 资源占用多,因为它要模拟出一整套操作系统
- 冗余步骤多,虚拟机创建出一个操作系统往往要有很多步骤
- 启动慢,往往好几分钟。
容器虚拟化技术
由于前面虚拟机存在某些缺点,Linux发展出了另一种虚拟化技术:
Linux容器(Linux Containers,缩写为 LXC)
Linux 容器不是模拟一个完整的操作系统而是对进程进行隔离。
有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。
容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。
*容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
例如下图:
那话又说回来了——
Why Docker
●更轻量: 基于容器的虚拟化,仅包含业务运行所需的runtime环境,CentOS/Ubuntu基础镜像仅170M;宿主机可部署100~ 1000个容器
●更高效:无操作系统虚拟化开销
●计算:轻量,无极外开销
#存储:系统盘aufs/dmloverlayts; 数据盘volume
●网络:宿主机网络,NS隔离
.更敏捷、更灵活:
●分层的存储和包管理,devops理念
●支持多种网络配置
docker的基本组成
下面的图可以很清楚的看出docker各个组成之间的转换关系,以及对应命令:
镜像(image)
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以建很多容器。
它也相当于是一个root文件系统。比如官方镜像 centos:7 就包含了完整的一套 centos:7 最小系统的 root 文件系统。
相当于容器的“源代码”,docker镜像文件类似于Java的类模板,而docker容器实例类似于java中new出来的实例对象。
容器(container)
下面我们从以下两个角度来介绍容器 :
1 从面向对象角度
Docker 利用容器(Container)独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台
2 从镜像容器角度
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
仓库(repository)
仓库(Repository)是集中存放镜像文件的场所。
最大的公开仓库是 DockerHub(Docker Hub)
tar与dockerfile以后会说
总结
镜像文件
- image 文件生成的容器实例,本身也是一个文件,称为镜像文件。
容器实例
- 一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器
仓库
- 就是放一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候再从仓库中拉下来就可以了。
第一个docker镜像——hello-world
run干了什么
docker为什么比虚拟机快
(1)docker有着比虚拟机更少的抽象层:
(2)docker利用的是linux的内核,而不需要加载操作系统OS内核
帮助启动类命令
- 启动docker : systemctl start docker
- 停止docker : systemctl stop docker
- 重启docker : systemctl restart docker
- 查看docker状态: systemctl status docker
- 开机启动 : systemctl enable docker
- 查看docker概要信息: docker info
- 查看docker 总体帮助文档: docker --help
- 查看docker命令帮助文档: docker 具体命令 --help
帮助命令
docker version //显示docker 的版本信息
docker info //显示docker 的系统信息 包括镜像和容器数量
docker 命令 -- help
镜像命令
docker images 查看所有本地主机上的镜像
REPOSITORY 仓库的镜像
TAG 镜像的标签,也就是镜像版本号,不写默认最新版
IMAGE ID 镜像的id
CREATE 镜像的创建时间
SIZE 镜像的大小
可选项
- -a --all 列出所有镜像
- -q --quiet 只列出镜像的id
docker search 搜索镜像
可选项,通过搜索来过滤
[root@ecs-287241 ~]# docker search mysql --filter=stars=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 13384 [OK]
mariadb MariaDB Server is a high performing open sou… 5109 [OK]
[root@ecs-287241 ~]#
筛选starts大于3000的
docker pull 下载镜像
下载镜像 docker pull 镜像名 [: tag版本]
- docker pull mysql
- 如果不写tag,默认就是latest
- 分层下载,这是docker images 的核心,联合文件信息
查看镜像/容器/数据卷所占的空间
docker system df
删除镜像
docker rmi id
[root@ecs-287241 ~]# docker rmi -f feb5d9fea6a5
Untagged: hello-world:latest
Untagged: hello-world@sha256:18a657d0cc1c7d0678a3fbea8b7eb4918bba25968d3e1b0adebfa71caddbc346
Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
[root@ecs-287241 ~]# docker images
面试题:谈谈docker虚悬镜像是什么?
· 是什么
· 仓库名、标签都是<none>的镜像,俗称虚悬镜像dangling image
· 长什么样
· 后续Dockerfile章节再介绍
容器命令
说明:我们有了镜像才可以创建容器,linux,下载一个centos来学习
docker pull centos (centos小型的服务器)
新建容器并启动
docker run [可选参数] image
#参数说明(最常用参数)
--name="Name" 容器名字 tomcat1 tomcat2 用来区分容器
-d 后台方式运行
-i:以交互模式运行容器,通常与 -t 同时使用;
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;
也即启动交互式容器(前台有伪终端,等待交互),就是等待你输入命令!
-it 使用交互方式运行,进入容器查看内容
为什么要写 -it?
不写-it你会发现是这样的
我们想要一个终端进行交互!输入命令,所以必须写!
后面往往还要写上/bin/bash: 是指此脚本使用/bin/bash来解释执行。/bin/bash:放在镜像名后的shell命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。
#使用镜像centos:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it centos /bin/bash
或者
docker run -it centos bash 也可以!即使你没写/bin/bash 他也会默认在你后面带一个shell脚本
-p 指定容器的端口 -p 8080:8080
-p IP:主机端口:容器端口
-p 主机端口:容器端口 (常用)
-p 容器端口
-P: 随机端口映射,大写P
-p: 指定端口映射,小写p
一般我们用小p
如下图:
左边是宿主机暴露的端口,右边是docker访问redis暴露的端口
启动 并进入容器
[root@ecs-287241 ~]# docker run -it centos /bin/bash
查看容器内的centos,基础镜像都是不完善的
[root@882655ac322c /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
从容器中退到主机
exit
列出运行中的程序
docker ps 命令
-a 当前正在运行,带出历史运行过的容器。
-l:显示最近创建的容器。
-n=? 最近创建的容器。
-q 静默模式,只显示容器的编号。
退出容器
exit 直接容器停止并退出
ctrl +p +q 容器不停止退出
删除容器
docker rm 容器id 删除指定的容器,不能删除正在运行的容器,如果要强制删除 rm -f
可以先docker stop 停止容器 再docker rm
docker rm -f $(docker ps -aq) 删除所有的容器
docker ps -a -qlxargs docker rm
启动和停止容器操作
docker satrt 容器id 启动容器
docker restart 容器id 重启容器
docker stop 容器id 停止当前正在运行的容器
docker kill 容器id 强制停止当前容器
后台启动容器
- 命令 docker run -d 镜像名!
- docker run -d centos
- 问题docker ps,发现了 centos停止了
- docker run 之后一定要docker ps查看容器是否运行!
- 常见的坑,docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止 。
- ngnix ,容器启动后,发现自己没有提供服务,就会立即停止,就是没有程序了
- Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器
前台启动和后台启动区别
- 前台交互式启动
- docker run -it redis:6.0.8
- 后台守护式启动
- docker run -d redis:6.0.8
Detached (-d)
如果在docker run后面追加-d=true或者-d,那么容器将会运行在后台模式。此时所有I/O数据只能通过网络资源或者共享卷组来进行交互。因为容器不再监听你执行docker run的这个终端命令行窗口。但你可以通过执行docker attach来重新附着到该容器的会话中。需要注意的是,容器运行在后台模式下,是不能使用--rm选项的。
Foregroud(不写-d)
在前台模式下(不指定-d参数即可),Docker会在容器中启动进程,同时将当前的命令行窗口附着到容器的标准输入、标准输出和标准错误中。也就是说容器中所有的输出都可以在当前窗口中看到。甚至它都可以虚拟出一个TTY窗口,来执行信号中断。
我们要在在容器中干活!
以redis容器为例,在里面干活
两个开发方式对比,想必你更能体会到容器的威力!
其他常用命令(重点)
查看日志
查看容器日志:docker logs 容器ID
docker logs -f -t --tail 容器,没有日志
显示日志
-tf 显示日志
--tail number 显示日志条数
查看容器中的进程信息
查看容器内运行的进程
命令: docker top 容器id
查看镜像的元数据
命令:
查看容器内部细节
docker inspect 容器 id
进入当前正在运行的容器
我们通常容器都是使用后台放式运行的,需要进入容器,修改一些配置
方式一
#命令
docker exec -it 容器id bashShell
[root@ecs-287241 /]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e5cf171dca0 centos "/bin/bash" 10 minutes ago Up 10 minutes serene_goodall [root@ecs-287241 /]# docker exec -it 6e5cf171dca0 /bin/bash [root@6e5cf171dca0 /]# ls bin etc lib lost+found mnt proc run srv tmp var dev home lib64 media opt root sbin sys usr [root@6e5cf171dca0 /]# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 13:42 pts/0 00:00:00 /bin/bash root 15 0 0 13:54 pts/1 00:00:00 /bin/bash root 30 15 0 13:55 pts/1 00:00:00 ps -ef [root@6e5cf171dca0 /]#
方式二
docker attach 容器id
进入后会发现正在执行当前代码!
attach与exec对比:
attach 直接进入容器启动命令的终端,不会启动新的进程 用exit退出,会导致容器的停止。
exec 是在容器中打开新的终端,并且可以启动新的进程 用exit退出,不会导致容器的停止。
docker exec 进入容器后开启一个新的终端,可以在里面操作(常用)
docker attach 进入容器正在执行的终端,不会启动新的进程
从容器拷贝文件到主机上
为了防止别人或者自己误删!所以要做备份
docker cp 容器id :容器内路径 目的主机路径
[root@ecs-287241 home]# docker cp 6e5cf171dca0:/home/test.java /home [root@ecs-287241 home]# ls one1 test1 test.java wei wei.java www [root@ecs-287241 home]#
拷贝是一个手动过程,未来我们使用 -v卷的技术,可以实现,自动同步
即容器内的home目录和主机内的home目录连通,打通
导入和导出容器
比拷贝docker cp 更加强大,这是拷贝整个容器!
· export 导出容器的内容留作为一个tar归档文件[对应import命令]
· docker export 容器ID > 文件名.tar
· import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
· cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
删除之后,利用import将abcd.tar变成一个镜像,然后在通过run运行成为一个容器。
下面我们来深入探究一下docker运行原理!
Docker镜像
镜像是什么?
镜像
是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。
只有通过这个镜像文件才能生成Docker容器实例!
镜像是分层的
以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载
一层一层的下载! 因此镜像是分层的。
UnionFS(联合文件系统)
UnionFS(联合文件系统):
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。
Union文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
Docker镜像加载原理
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等等。
说白了我们只要bootfs和rootfs!
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M??
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用宿主机的linux内核(Host的kernel),自己只需要提供 rootfs 就行了。
由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。
就像这样:
那么话说回来了——
为什么 Docker 镜像要采用这种分层结构呢?
其实就是像乐高积木一样,想用哪块拿来组装就行了!
镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。
比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;
同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
一句话来讲分层的意义:
加载简单,资源复用
重点理解
· Docker镜像层都是只读的,容器层是可写的当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动——无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
Docker容器数据卷
坑:容器卷记得加入
--privileged=true
why?
Docker挂载主机目录访问如果出现cannotopen directory .: Permission denied
解决办法:在挂载目录后多加一个--privileged=true参数即可
如果是CentOS7安全模块会比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为,
在SELinux里面挂载目录被禁止掉了额,如果要开启,我们一般使用--privileged=true命令,扩大容器的权限解决挂载目录没有权限的问题,也即使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。一个容器就相当于是一个小的linux内核。
数据卷是什么?
卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
· 一句话:有点类似我们Redis里面的rdb和aof文件
· 将docker容器内的数据保存进宿主机的磁盘中
· 运行一个带有容器卷存储功能的容器实例
· docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
映射我们用“ :”,即冒号表示。
数据卷能干什么?
将运用与运行的环境打包镜像,run后形成容器实例运行 ,但是我们对数据的要求希望是久化的。
Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没了。
为了能保存数据在docker中我们使用卷。
特点:
1:数据卷可在容器之间共享或重用数据。
2:卷中的更改可以直接实时生效,爽。
3:数据卷中的更改不会包含在镜像的更新中。
4:数据卷的生命周期一直持续到没有容器使用它为止。
容器和宿主机之间数据共享
docker:
host宿主机
查看数据卷是否挂载成功
docker inspect 容器ID
1 docker修改,主机同步获得
2 主机修改,docker同步获得
3 docker容器stop,主机修改,docker容器重启数据依旧同步。
因此即使是docker容器停止了也能够同步数据
读写规则限制说明
只限制容器,没有限制主机
rw = read + write
· docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
· 默认同上案例,默认就是rw
· 只读
· 容器实例内部被限制,只能读取不能写
/容器目录:ro镜像名 就能完成功能,此时容器自己只能读取不能写
ro = read only
此时如果宿主机写入内容,可以同步给容器内,容器可以读取到。
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
操作一下:
容器
宿主机
卷的继承和共享
容器1完成和宿主机的映射
docker run -it --privileged=true -v /mydocker/u:/tmp --name u1ubuntu
容器2继承容器1的卷规则
· docker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu
继承举例
u3继承u1
思考
u1stop掉了,u3和宿主机之间同步数据,u1start之后会同步数据吗?
会的!
总结一波当前学的命令:
1.容器上拷贝到linux上?cp
2.讲一个image启动成一个container容器?run
3.运行了容器怎么停止?kill/stop
4.怎么启动容器?start 怎么暂停?pause
5.logs:查看日志 inspect是干什么的?查看源数据
6.attach 进入已经启动的重端,而exec是启动一个新的终端
7.^p^q是以运行的方式退出
8.port 控制端口
9.ps可以看到当前所有的进程信息
10,top查看进程信息
port # 查看映射端口对应的容器内部源端口
pause # 暂停容器
ps # 猎户容器列表
pull # 从docker镜像源服务器拉取指定镜像或者库镜像
push # 推送指定镜像或者库镜像至docker源服务器
restart # 重启运行的容器
rm # 移除一个或多个容器
rmi # 移除一个或多个镜像 (无容器使用该镜像才可删除,否则需要删除相关容器才可继续或 -f 强制删除)
run # 创建一个新的容器并运行一个命令
save # 保存一个镜像为一个 tar 包【对应 load】
search # 在 docker hub 中搜索镜像
start # 启动容器
stop # 停止容器
tag # 给源中镜像打标签
top # 查看容器中运行的进程信息
unpause # 取消暂停容器
version # 查看 docker版本号
wait # 截取容器停止时的退出状态值
练习:下载nginx
端口暴露
[root@ecs-287241 home]# docker run -d --name ngimx01 -p 3344:80 nginx 303bb4275798c811290a237bde71456194ed41fe82fdc9b03fb72df87edb6fbc [root@ecs-287241 home]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 303bb4275798 nginx "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:3344->80/tcp, :::3344->80/tcp ngimx01 [root@ecs-287241 home]# curl localhost:3344 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> [root@ecs-287241 home]#
查找ngnix的位置 whereis
root@303bb4275798:/# whereis ngnix
ngnix:地址
root@303bb4275798:/#
思考问题:我们每次改动nginx配置文件.都需要进入容器内部?
十分的麻烦,我要是可以在容器外部提供一个映射路径 ,达到在容器修改文件名,容器内部就可以自动修改? -v 数据卷
作业: docker来装一个tomcat
#官方的使用
docker run -it --rm tomcat:9.0
#我们之前的启动都是后台,停止了容器之后,容器还是可以查到
docker run -it --rm. 一般用来测试,用完就删除
感谢大家观看!