Docker基础
概述
为什么会有docker出现?
假定您在开发一个尚硅谷的谷粒商城,您使用的是一台笔记本电脑而且您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。此外,您的企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列支持文件。您希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销。请问?您要如何确保应用能够在这些环境中运行和通过质量检测?并且在部署过程中不出现令人头疼的版本、配置问题,也无需重新编写代码和进行故障修复?
答案就是使用容器。Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案——-系统平滑移植,容器虚拟化技术。也就是说,安装的时候,把原始环境一模一样地复制过来。开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题。
Docker的出现使得Docker得以打破过去「程序即应用」的观念。透过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由下而上打包,达到应用程式跨平台间的无缝接轨运作。
理念
Docker是基于Go语言实现的云开源项目。解决了运行环境和配置问题的软件容器, 方便做持续集成并有助于整体发布的容器虚拟化技术。
Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次镜像,处处运行”。Linux容器技术的出现就解决了这样一个问题,而 Docker 就是在它的基础上发展过来的。将应用打成镜像,通过镜像成为运行在Docker容器上面的实例,而 Docker容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
容器与虚拟机比较
- 传统虚拟机技术
虚拟机(virtual machine)就是带环境安装的一种解决方案。虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。
缺点:1、资源占用多(不需要的也要加载)2、冗余步骤多 3、启动慢 - 容器虚拟化技术
Linux容器(Linux Containers,缩写为 LXC)。Linux容器是与系统其他部分隔离开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件。容器提供的镜像包含了应用的所有依赖项,因而在从开发到测试再到生产的整个过程中,它都具有可移植性和一致性。Linux 容器不是模拟一个完整的操作系统而是对进程进行隔离。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。 - 对比:
- 传统虚拟技术虚拟出硬件后,运行整套操作系统,在运行所需应用,而容器内应用直接运行于宿主内核,容器内没有自己的内核且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
- 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。
能干嘛?
官网
Docker官网:http://www.docker.com
Docker Hub官网: https://hub.docker.com/ (容器仓库)
Docker安装和卸载
前提条件
目前,CentOS 仅发行版本中的内核支持 Docker。Docker 运行在CentOS 7 (64-bit)上,要求系统为64位、Linux系统内核版本为 3.8以上,这里选用Centos7.x。
查看自己的内核:
Docker基本组成
镜像
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。它也相当于是一个root文件系统。比如官方镜像 centos:7 就包含了完整的一套 centos:7 最小系统的 root 文件系统。相当于容器的“源代码”,docker镜像文件类似于Java的类模板,而docker容器实例类似于java中new出来的实例对象。
容器
Docker 利用容器(Container)独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。类似于java中的对象。
仓库
仓库(Repository)是集中存放镜像文件的场所。类似于Maven仓库,存放各种jar包的地方;github仓库,存放各种git项目的地方;
Docker公司提供的官方registry被称为Docker Hub,存放各种镜像模板的地方。仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云 、网易云等。
三者关系
Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。类似于Maven,在创建容器实例时会先在本地查找镜像,没有才会在仓库中拉取到本地。
- 镜像文件
image 文件生成的容器实例,本身也是一个文件,称为镜像文件。 - 容器实例
一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器 - 仓库
是放一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候再从仓库中拉下来就可以了。
安装步骤
- 安装gcc环境
yum -y install gcc
yum -y install gcc-c++ - 安装所需要的软件包
yum install -y yum-utils - 设置stable仓库(注意使用阿里云镜像,外网太慢)
yum-config-manager —add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo - 更新yum软件包索引
yum makecache fast - 安装docker
yum -y install docker-ce docker-ce-cli containerd.io - 启动docker
systemctl start docker - 查看docker版本
docker version - hello world 程序测试
docker run hello-world
配置阿里云镜像加速
阿里云🔗
在容器镜像服务中创建个人服务,点击镜像加速器。
此时参照官网的操作步骤。第二步需要进入文件目录操作。
卸载
- systemctl stop docker
- yum remove docker-ce docker-ce-cli containerd.io
- rm -rf /var/lib/docker
- rm -rf /var/lib/containerd
常用命令
帮助启动类
- 启动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 images(列出本地主机上的镜像)
各选项说明:
REPOSITORY:表示镜像的仓库源
TAG:镜像的标签版本号
IMAGE ID:镜像ID
CREATED:镜像创建时间
SIZE:镜像大小
注意:不指定版本将自动安装最新版latest
OPTIONS说明:
- -a :列出本地所有的镜像(含历史映像层)。
- -q :只显示镜像ID。(也可以-aq一起使用)
docker search 某个XXX镜像名字
格式:docker search [OPTIONS] 镜像名字
各选项说明
OPTIONS说明:
- —limit : 只列出N个镜像,默认25个
例如:
docker pull 某个XXX镜像名字
格式:docker pull 镜像名字[:TAG](没有TAG就是最新版)
docker system df 查看镜像/容器/数据卷所占的空间
docker rmi 某个XXX镜像名字ID
id和名字都可以删除,但id不会重复。
- 删除单个
docker rmi -f 镜像ID - 删除多个
docker rmi -f 镜像名1:TAG 镜像名2:TAG - 删除全部
docker rmi -f $(docker images -qa)
面试题:谈谈docker虚悬镜像是什么?
仓库名、标签都是的镜像,俗称虚悬镜像dangling image。
可能是弃用版本,需要删除。
容器命令
新建+启动容器
格式:docker run [OPTIONS] IMAGE [COMMAND] [ARG…]
OPTIONS说明(常用):
- 有些是一个减号,有些是两个减号 —name=”容器新名字” 为容器指定一个名称;
- -d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
- -i:以交互模式运行容器,通常与 -t 同时使用;
- -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;也即启动交互式容器(前台有伪终端,等待交互);
- -P: 随机端口映射,大写P
- -p: 指定端口映射,小写p
演示启动ubuntu容器
- 有镜像才能创建容器, 这是根本前提(下载一个CentOS或者ubuntu镜像演示)
- docker pull ubuntu 先下载镜像文件。
- 启动交互式容器(前台命令行)
使用镜像centos:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it ubuntu /bin/bash
参数说明:
-i: 交互式操作。
-t: 终端。
ubuntu : ubuntu镜像。
/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。
要退出终端,直接输入 exit:
后台运行容器
在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的后台运行模式。Docker容器后台运行,就必须有一个前台进程.容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
以redis为例:
- 先pull镜像
- docker run -d 容器名
此时后台在运行,而使用 docker run -it redis 即为前台交互式启动。 - 而有些 -d 会无法启动,例如:ubuntu
Docker容器后台运行,就必须有一个前台进程.容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。
进入正在运行的容器并以命令行交互
- docker exec -it 容器ID bashShell (推荐)
exec 是在容器中打开新的终端,并且可以启动新的进程,用exit退出,不会导致容器的停止。
例如:此时redis在后台运行,使用 docker exec -it 容器ID redis-cli 进入
此时发现exit退出后,容器还在运行。 - 重新进入docker attach 容器ID
attach 直接进入容器启动命令的终端,不会启动新的进程,用exit退出,会导致容器的停止。
列出当前所有正在运行的容器
格式:docker ps [OPTIONS]
OPTIONS说明(常用):
- -a :列出当前所有正在运行的容器+历史上运行过的。
- -l :显示最近创建的容器。
- -n:显示最近n个创建的容器。
- -q :静默模式,只显示容器编号。
退出容器
- run进去容器,exit退出,容器停止
- run进去容器,ctrl+p+q退出,容器不停止
启动已停止运行的容器
docker start 容器ID或者容器名
重启容器
docker restart 容器ID或者容器名
将容器设置为docker启动后自动启动
docker update 容器ID —restart=always
停止容器
docker stop 容器ID或者容器名
强制停止容器
docker kill 容器ID或容器名
删除已停止的容器
- 删除单个
docker rm 容器ID - 一次性删除多个容器实例
docker rm -f $(docker ps -a -q) 或者 docker ps -a -q | xargs docker rm
查看容器日志
docker logs 容器ID
查看容器内运行的进程
docker top 容器ID
查看容器内部细节
docker inspect 容器ID
从容器内拷贝文件到主机上
容器→主机:docker cp 容器ID:容器内路径 目的主机路径
导入和导出Docker容器
- 导出
docker export 容器ID > 文件名.tar
export 导出容器的内容留作为一个tar归档文件[对应import命令] - 导入
cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
import 从tar包中的内容创建一个新的文件系统再导入为镜像[对应export]
Docker镜像
概述
是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。
镜像的分层
UnionFS(联合文件系统)
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 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等等。
平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M??
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供 rootfs 就行了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。
为什么 Docker 镜像要采用这种分层结构呢?
镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。
Docker的容器层
Docker镜像层都是只读的,容器层是可写的 当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
因此我们操作的都是容器层。
Commit操作实例
docker commit提交容器副本使之成为一个新的镜像
格式:
docker commit -m=”提交的描述信息” -a=”作者” 容器ID 要创建的目标镜像名:[标签名]
实战练习:
- 在启动ubuntu容器,安装vim
apt-get update
apt-get -y install vim - 退出容器后,注意不关闭容器,安装完成后,commit我们自己的新镜像
此时可以发现镜像多了100MB,因为下载了 vim 。
结论:
Docker中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似Java继承于一个Base基础类,自己再按需扩展。新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。
本地镜像发布到阿里云
流程图:
- 先创建仓库
创建仓库后,在仓库基本信息可以查看相关仓库的操作。 - 基于当前容器创建一个新的镜像,新功能增强 docker commit [OPTIONS] 容器ID [REPOSITORY[:TAG]]
OPTIONS说明:-a :提交的镜像作者;-m :提交时的说明文字;后面的DockerFile章节,第2种方法。 登录阿里云
docker login --username=刘家琳 registry.cn-chengdu.aliyuncs.com
docker login —username=… registry.cn-hangzhou.aliyuncs.com将镜像推送到阿里云
docker login --username=刘家琳 registry.cn-chengdu.aliyuncs.com
docker tag [ImageId] registry.cn-chengdu.aliyuncs.com/peanut325/myubuntu:[镜像版本号]
docker push registry.cn-chengdu.aliyuncs.com/peanut325/myubuntu:[镜像版本号]
将阿里云上的镜像下载到本地
docker pull registry.cn-chengdu.aliyuncs.com/peanut325/myubuntu:[镜像版本号]
删除镜像后,重新拉取,发现ubuntu还是176MB(带有vim编辑器)
本地镜像发布到私有库
可以参考链接🔗
Dockerhub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供给团队使用,基于公司内部项目构建镜像。Docker Registry是官方提供的工具,可以用于构建私有镜像仓库。
流程图:
案例演示:将刚才带有vim的ubuntu上传到私服。
- 下载镜像docker pull registry
- 运行私有库Registry,相当于本地有个私有Docker hub
sudo docker run -d -p 5000:5000 registry 其他参数参照官网
默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便于宿主机联调 - curl验证私服库上有什么镜像
curl -XGET http://192.168.241.130:5000/v2/_catalog
此时镜像为空。 - 将新镜像ubuntu:1.3修改符合私服规范的Tag
docker tag 镜像:Tag Host:Port/Repository:Tag
docker tag ba6acccedd29 192.168.241.130:5000/ubuntu:latest - 修改配置文件使之支持http
{
自己的阿里镜像,
“insecure-registries”: [“192.168.241.130:5000”]
}
注意逗号
上述理由:docker默认不允许http方式推送镜像,通过配置选项来取消这个限制。====> 修改完后如果不生效,建议重启docker - push推送到私服库
docker push 192.168.241.130:5000/ubuntu
重启后push报错,connect: connection refused,可能是没有开启仓库服务。
此时查看仓库,可以发现ubuntu - 查看私服的镜像tag
curl -XGET http://registry:5000/v2/镜像名字/tags/list - pull到本地
docker pull 192.168.241.130:5000/ubuntu
此时清空镜像 pull 一波
Docker容器卷
概述
卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。(有点类似我们Redis里面的rdb和aof文件)
使用
运行一个带有容器卷存储功能的容器实例
格式:docker run -it —privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
在使用时如果报错出现cannot open directory .: Permission denied
在挂载目录后多加一个—privileged=true参数即可
将运用与运行的环境打包镜像,run后形成容器实例运行 ,但是我们对数据的要求希望是持久化的Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。为了能保存数据在docker中我们使用卷。
特点:
1:数据卷可在容器之间共享或重用数据
2:卷中的更改可以直接实时生效
3:数据卷中的更改不会包含在镜像的更新中
4:数据卷的生命周期一直持续到没有容器使用它为止
实例
- 容器运行ubuntu,挂载到宿主机的 /tmp/u1 目录
- 查看是否挂载成功
docker inspect 容器ID
容器和宿主机之间数据共享
- docker修改,主机同步获得
docker中创建文件,并追加内容
宿主机中看到有关修改啊 - 主机修改,docker同步获得
- docker容器stop,主机修改,docker容器重启看数据是否同步。
关闭重启后还存在
读写规则映射添加说明
- 读写(默认)
格式:docker run -it —privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
- 只读
容器实例内部被限制,只能读取不能写
格式:docker run -it —privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
此时如果宿主机写入内容,可以同步给容器内,容器可以读取到。
卷的继承和共享
docker run -it —privileged=true -v /tmp/u1:/tmp/u1 —name u1 ubuntu
容器1完成和宿主机的映射
此时开启另一个容器,可以完成和宿主机的映射,可以直接使用挂载的文件启动docker容器
此时有2个容器
容器2继承容器1的卷规则
格式: docker run -it —privileged=true —volumes-from 父类 —name u2 ubuntu
此时继承刚才的u1容器启动u2容器,可以发现数据不变
此时u1容器关闭对u2容器无影响。
常用开发工具的安装
Tomcat
- 拉取镜像
docker pull tomcat:8.0.53 - 后台运行镜像
docker run -d -p 8080:8080 tomcat:8.0.53 - 即可访问,使用自己的主机ip
注意:使用pull拉取最新版的tomcat时,需要自己修改一下配置(tomcat修改了webapps文件)
Mysql
注意在安装mysql启动时,如果虚拟机中有mysql服务,需要关闭。
- 拉取镜像
docker pull mysql:5.7 - 由于docker容器中的mysql和可视化工具中字符集不对应,所以需要使用容器卷配置字符集,还有需要使用容器卷进行mysql备份,防止数据丢失
docker run -d -p 3306:3306 —privileged=true
-v /peanut/mysql/log:/var/log/mysql
-v /peanut/mysql/data:/var/lib/mysql
-v /peanut/mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456 —name mysql mysql:5.7
(需要注意虚拟机中是否有mysql服务,防止占用3306端口) 创建配置文件,修改配置
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
此时重启容器,登录mysql查看字符集
docker restarat 容器ID
show variables like ‘character%’;- 此时在docker上的mysql创建数据,使用navicat连接可以查看到数据
- 由于我们使用了容器卷,此时关闭容器,重启,可以发现数据还存在
Redis
因为需要修改配置文件保存数据,所以使用容器卷。
- 拉取镜像
docker pull redis - 启动redis
docker run -p 6379:6379 —name myr3 —privileged=true
-v /peanut/redis/redis.conf:/etc/redis/redis.conf
-v /peanut/redis/data:/data -d redis:6.0.8
redis-server /etc/redis/redis.conf - 自己可以创建配置文件
此时我将以前的配置文件cp过来
做修改:- 开启redis验证 可选
requirepass 123 - 允许redis外地连接 必须
- 开启redis验证 可选
注释掉 # bind 127.0.0.1
- daemonize no
将daemonize yes注释起来或者 daemonize no设置,因为该配置和docker run中-d参数冲突,会导致容器一直启动失败- 修改数据库数,来测试是否使用到我们修改的配置文件
重新启动一个容器实例
进入客户端 redis-cli
可以发现数据库只有10个,证明使用的容器卷的配置文件
- 修改数据库数,来测试是否使用到我们修改的配置文件
Redis和MySQL设置启动docker自动启动
sudo docker update redis —restart=always
sudo docker update mysql —restart=always