docker入门

01【熟悉】docker简介

1,什么是docker

        Docker是一个开源项目,诞生于2013年初,最初是dotCloud公司内部的一个业余项目。它基于Google公司推出的Go语言实现。项目后来加入了Linux基金会,遵从了Apache2.0协议,项目代码在GitHub上进行维护。Docker自开源后受到广范的关注和讨论,以至于dotCloud公司后来都改名为Docker Inc。RedHat已经在其RHEL6.5中集中支持Docker;Google也在其PaaS产品中广泛应用。Docker的目标是实现经量级的操作系统虚拟化解决方案。Docker的基础是Linux容器(LXC)等技术。在LXC的基础上Docker进行了进一步的封装,让用户不需要关心容器的管理,使得操作更加简单。用户操作Docker的容器就像操作一个快速轻量级的虚拟机一样简单。

        下图比较了Docker和传统虚拟化方式的不同之处,可见容器是在操作系统层面上实现的虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层现实现

[if !vml]

[endif]

2,docker的设计思想

        Docker的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,可以把货物规整的摆放起来。并且各种各样的货物被集装箱标准化了,集装箱和集装箱之间不会互相影响。那么我就不需要专门运送水果的船和专门运送化学品的船了。只要这些货物在集装箱里封装的好好的,那我就可以用一艘大船把他们都运走。docker就是类似的理念。现在都流行云计算了,云计算就好比大货轮。docker就是集装箱。

1.不同的应用程序可能会有不同的应用环境,比如.net开发的网站和php开发的网站依赖的软件就不一样,如果把他们依赖的软件都安装在一个服务器上就要调试很久,而且很麻烦,还会造成一些冲突。比如IIS和Apache访问端口冲突。这个时候你就要隔离.net开发的网站和php开发的网站。常规来讲,我们可以在服务器上创建不同的虚拟机在不同的虚拟机上放置不同的应用,但是虚拟机开销比较高。docker可以实现虚拟机隔离应用环境的功能,并且开销比虚拟机小,小就意味着省钱了。

2.你开发软件的时候用的是Ubuntu,但是运维管理的都是centos,运维在把你的软件从开发环境转移到生产环境的时候就会遇到一些Ubuntu转centos的问题,比如:有个特殊版本的数据库,只有Ubuntu支持,centos不支持,在转移的过程当中运维就得想办法解决这样的问题。这时候要是有docker你就可以把开发环境直接封装转移给运维,运维直接部署你给他的docker就可以了。而且部署速度快。

3.在服务器负载方面,如果你单独开一个虚拟机,那么虚拟机会占用空闲内存的,docker部署的话,这些内存就会利用起来。

总之docker就是集装箱原理。


3,为什么要使用docker

        作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。其次,Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个Docker 容器。

容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。

传统虚拟机方式运行10 个不同的应用就要起10 个虚拟机,而Docker 只需要启动10 个隔离的应用即可。具体说来,Docker 在如下几个方面具有较大的优势。

1,更快速的交付和部署

对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间.

2,更轻松的迁移和扩展

Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器

等。这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

3,更简单的管理

使用Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。对比虚拟机

[if !vml]

[endif]

3docker的局限性

[if !supportLists]1.   [endif]Docker是基于Linux 64bit的,无法在32bit的linux/Windows/unix环境下使用

[if !supportLists]2.   [endif]LXC是基于cgroup等linux kernel功能的,因此container的guest系统只能是linux base的

[if !supportLists]3.   [endif]隔离性相比KVM之类的虚拟化方案还是有些欠缺,所有container公用一部分的运行库

[if !supportLists]4.   [endif]网络管理相对简单,主要是基于namespace隔离

[if !supportLists]5.   [endif]cgroup的cpu和cpuset提供的cpu功能相比KVM的等虚拟化方案相比难以度量(所以dotcloud主要是按内存收费)

[if !supportLists]6.   [endif]Docker对disk的管理比较有限

[if !supportLists]7.   [endif]container随着用户进程的停止而销毁,container中的log等用户数据不便收集

4docker在开发、测试、部署中的定位

1,尝试新软件

对开发者而言,每天会催生出的各式各样的新技术都需要尝试,然而开发者却不太可能为他们一一搭建好环境并进行测试。时间非常宝贵,正是得益于 Docker,让我们有

可能在一条或者几条命令内就搭建完环境。Docker 有一个傻瓜化的获取软件的方法,Docker 后台会自动获得环境镜像并且运行环境。

并不仅仅是新技术环境搭建用得到 Docker。如果你想快速在你的笔记本上运行一个 MySQL 数据库,或者一个 Redis 消息队列,那么使用 Docker 便可以非常容易地做到。例如 Docker 只需要一条命令便可以运行 MySQL 数据库:docker run -d -p 3306:3306 tutum/mysql。

2,进行演示

工作中自己开发的成果对客户或者别人做一两个演示。搭建演示环境的过程非常麻烦。Docker是演示这些工具的最合理的方式。同时,对于客户来说,可以直接将 Docker 镜像提供给他们,而不必去做任何环境配置的工作,工作的效果也会和在他们演示中所看到的一模一样,同时不必担心他们的环境配置会导致我们的产品无法运行。

3,避免“我机器上可以运行”

无论是上一篇介绍的企业部署 Docker 还是本文的个人 Docker 用例,都提到了这个情况。因为环境配置不同,很多人在开发中也会遇到这个情况,甚至开发的软件到了测试人员的机器上便不能运行。但这都不是重点。重点是,如果我们有一个可靠的、可分发的标准开发环境,那么我们的开发将不会像现在这么痛苦。Docker 便可以解决这个问题。Docker 镜像并不会因为环境的变化而不能运行,也不会在不同的电脑上有不同的运行结果。可以给测试人员提交含有应用的 Docker 镜像,这样便不再会发生“在我机器上是可以运行的”这种事情,很大程度上减轻了开发人员测试人员互相检查机器环境设置带来的时间成本。

4,更好地利用资源

虚拟机的粒度是“虚拟出的机器”,而 Docker 的粒度则是“被限制的应用”,相比较而言 Docker 的内存占用更少,更加轻量级。对我来说这是 Docker 的一个优势:因为在如果在电脑中运行多个 Docker 应用,使用 Docker 比使用虚拟机更加简单,方便,粒度更细,也能持续地跟踪容器状态。

5,为微服务定制

我们一直在讲“微服务(Microservices)”的概念。Docker 可以很好地和微服务结合起来。从概念上来说,一个微服务便是一个提供一整套应用程序的部分功能,Docker 便可以在开发、测试和部署过程中一直充当微服务的容器。甚至生产环境也可以在 Docker 中部署微服务。

6,在云服务提供商之间移植

大多数的云主机提供商已经全面支持 Docker。对于开发人员来说,这表示你可以很方便地切换云服务提供商,当然也可以很方便地将你本地的开发环境移动到云主机上,不需要本地上配置一次运行环境、在云主机上还配置一次运行环境。全面部署 Docker (Docker here and Docker there) 作为标准运行环境可以极大地减轻应用上线时的工作量和产生 BUG。

7,技术的创新

Docker 正在快速发展,工具也在不断更新,没有人能预见到未来 Docker 会是什么样子的。你在复杂的系统中 Docker 使用的越多,越是可能会发现技术上的空白和未来技术发展的方向。现在还处在 Docker 的发展期,任何你使用 Docker 创建的工具都有可能成为社区关注的热点。这是 Docker 的机会,也是成就你自己的机会。



02【掌握】docker和虚拟技术比较

1,之前的虚拟技术

虚拟机(virtual machine)就是带环境安装的一种解决方案。

它可以在一种操作系统里面运行另一种操作系统,比如在Windows 系统里面运行Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。

[if !vml]

[endif]

虚拟机的缺点:

1 资源占用多 2 冗余步骤多 3 启动慢

2,docker容器化技术

    由于前面虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。


Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。

[if !vml]

[endif]

比较了 Docker 和传统虚拟化方式的不同之处:

*传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;

*而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

* 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。


3,开发和运维的春天

容器技术特点:一次构建,到处运行[雷哥教你再也不用纠结环境问题啦]

1,更快的应用交付和部署。传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。

2,更快的和扩缩容。随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个Docker容器将变成一块“积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级。

3,更简单的系统运维。应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。

4,更高效的计算资源利用。 Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。


4,企业级应用

新浪

[if !vml]

[endif][if !vml]

[endif][if !vml]

[endif][if !vml]

[endif]

美团

[if !vml]

[endif]

等等………………




03【掌握】docker下载

1,Docker 下载

1,官网http://www.docker.com

2,中文官网https://docker-cn.com

3,仓库https://hub.docker.com

2,Docker 安装前提说明

2.1 CentOS Docker 安装

Docker支持以下的CentOS版本:

CentOS 7 (64-bit)

CentOS 6.5 (64-bit) 或更高的版本

2.2 前提条件

目前,CentOS 仅发行版本中的内核支持 Docker。

Docker 运行在 CentOS 7上,要求系统为64位、系统内核版本为 3.10 以上。[我们基于7以上的版本来讲的哦]

Docker 运行在CentOS-6.5 或更高的版本的 CentOS 上,要求系统为64位、系统内核版本为 2.6.32-431 或者更高版本[。

2.3 查看自己的内核

uname命令用于打印当前系统相关信息(内核版本号、硬件架构、主机名称和操作系统类型等)。


uname-r


版本OK木有问题

注意在centos6.5下面也可以安装。但是有点麻烦。想装的可以自己去查资源


3,Docker 的基本组成

[if !vml]

[endif]



04【掌握】docker安装

1,centos 下安装Docker 文档

https://docs.docker.com/install/linux/docker-ce/centos/

2,Docker 安装

2.1,确定是centos7的版本


2.2,yum安装gcc相关

[if !supportLineBreakNewLine]

[endif]


1,确定centos7能上外网

2,yum-yinstallgcc

3,yum-yinstallgcc-c++

2.3,卸载旧版本

[if !supportLineBreakNewLine]

[endif]


yumremovedocker\

                  docker-client\

                  docker-client-latest\

                  docker-common\

                  docker-latest\

                  docker-latest-logrotate\

                  docker-logrotate\

                  docker-engine

2.4,安装方法


yuminstalldocker

2.5,启动docker


systemctlstartdocker

systemctlenabledocker开机自启

2.6,查看版本


dockerversion

2.7,测试运行 hello-world


dockerrunhello-world

由于本地没有hello-world这个镜像,所以会下载一个hello-world的镜像,并在容器内运行。

2.8,测试运行nginx


docker run-p8080:80-ddocker.io/nginx   //将80端口映射为8080,或者80:80还是原先的80端口,不可以不写。

测试

http://192.168.15.134:8080/

是不是很爽呀

2.9,卸载docker


systemctlstopdocker

yum-yremover docker

rm-rf/var/lib/docker


05【掌握】docker加速配置

为什么要加速

因为docker的运行需要在本地有相关的镜像,如果本地没有,就会去镜像仓库里面下载到本地,再运行,有时候下载比较慢,所以要配置镜像加速

阿里云的加速

找到加速地址

https://homenew.console.aliyun.com/

[if !vml]

[endif] [if !vml]

[endif]

编辑

vim /etc/docker/daemon.json

修改配置

{

 "registry-mirrors":  ["https://32xw0apq.mirror.aliyuncs.com"]

}

刷新重启



其它的加速

https://www.daocloud.io/mirror#accelerator-doc

[if !vml]

[endif]



vim /etc/docker/daemon.json

[if !vml]

[endif]


刷新重启

[if !vml]

[endif]






06【掌握】docker底层原理


1,Docker是如何工作的

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们前面说到的集装箱。

[if !vml]

[endif]

2,docker run了啥

[if !vml]

[endif]


07【掌握】docker命令[帮助+镜像]


1,帮助命令

docker version 查看docker版本

docker info 显示全系统信息

docker --help 显示docker相关的所有命令及功能说明



2,镜像命令



2.1 docker images  列表本机上的镜像

[if !vml]

[endif]

REPOSITORY:表示镜像的仓库源

TAG:镜像的标签

IMAGE ID:镜像ID

CREATED:镜像创建时间

SIZE:镜像大小


可选项

-a 列表本地的所有镜像及子镜像

-q 只显示镜像ID

--digests 显示镜像的摘要信息

--no-trunc 显示完整的镜像信息



2.2 docker search 镜像搜索命令

搜索网站:https://hub.docker.com

语法

docker search 镜像名称


docker search 镜像名称  -[options]   说明


-s 列出收藏数不少于指定值的镜像

[if !vml]

[endif]




2.3 docker pull 镜像下载命令

docker pull 镜像名称:[TAG]

例如:docker pull tomcat:8.5  下载8.5的镜像版本

    dokcer pull tomcat默认下载最新的tomcat镜像版本 【latest】


2.4 docker rim 镜像删除命令

根据镜像ID删除

[if !vml]

[endif]

根据镜像仓库删除

[if !vml]

[endif]

批量删除镜像

[if !vml]

[endif]


全部删除镜像


[if !vml]

[endif]



08【掌握】docker命令[容器]

1、什么是容器 

 容器镜像是一个软件的轻量级独立可执行软件包,包含运行它所需的一切:代码,运行时,系统工具,系统库,设置。不管环境如何,集装箱化软件都可以运行相同的Linux和Windows应用程序。容器将软件与其周围环境隔离开来,例如开发环境和生产环境之间的差异,并有助于减少在同一基础架构上运行不同软件的团队之间的冲突。

[if !vml]

[endif]

1.轻量级

在一台机器上运行的Docker容器共享该机器的操作系统内核; 他们立即开始并使用更少的计算和内存。图像由文件系统层构建并共享公用文件。这最大限度地减少了磁盘使用量,图像下载速度更快。

2.标准

Docker容器基于开放标准,可在所有主要Linux发行版,Microsoft Windows以及任何基础架构(包括虚拟机,裸机和云中)上运行。

3.安全

Docker容器将应用程序彼此隔离并从底层基础架构中分离出来。Docker提供了最强大的默认隔离功能,可以将应用程序问题限制在一个容器中,而不是整个机器上。




2,演示前提

    有镜像才能创建容器,这个是根本要求(下载centos镜像来演示)


docker pull centos


[if !vml]

[endif]


3,创建并启动容器

Run

3.1语法

docker run [options] images

  [command][args] /镜像ID


3.2options说明

有些是一个减号,有些是两个减号

--name="容器新名字": 为容器指定一个名称;

-d: 后台运行容器,并返回容器ID,也即启动守护式容器;

-i:以交互模式运行容器,通常与 -t 同时使用;

-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;

-P: 随机端口映射;

-p: 指定端口映射,有以下四种格式

ip:hostPort:containerPort

ip::containerPort

      hostPort:containerPort

containerPort


3.3交互式运行

docker run -it centos /bin/bash

[if !vml]

[endif]

直接进入了容器

5,退出容器

exit  退出容器并关闭容器

ctrl+p+q 退出不关闭容器



6,列表所容器

语法

docker ps [options ]


默认只列出正在运行的容器

options 的参数说明

-a  显示所有运行和没有运行的容器

-l :显示最近创建的容器。

-n:显示最近n个创建的容器。

-q :静默模式,只显示容器编号。

--no-trunc :不截断输出。


7,启动重启关闭容器

Docker restart 容器ID或容器名称

[if !vml]

[endif]


Docker start 容器ID或容器名称



Docker stop 容器ID或容器名称




8,强制停止容器

docker kill 容器ID或容器名称



9,删除已停止的容器

#单个删除

docker rm 容器ID

#多个删除

docker rm -f $(docker ps -aq)





10,其它重点内容


[if !supportLists]1, [endif]启用守护式容器

docker run -d centos

问题:docker ps -a 进行查看, 会发现容器已经退出

很重要的要说明的一点: Docker容器后台运行,就必须有一个前台进程.



2,查看容器日志

如果是后台运行的程序,那么如何查看运行的日志呢?


启动一个tomcat

docker 

  run -d -p 8080:8080 tomcat:8.5


使用日志命令查看tomcat的输出日志

docker 

  logs  容器ID


*  

  -t是加入时间戳

*  

  -f跟随最新的日志打印

*  

  --tail数字 显示最后多少条



[if !supportLists]2,[endif]查看容器运行进程


docker top 容器ID



4,查询容器内部细节

docker inspect 容器ID


[if !vml]

[endif]


5,进入正在进行的容器并以命令行交互


#方式1

docker exec -it 容器ID/bin/bash   重新打开一个新的终端 如果以这种方式进入容器,可以使用exit退出。而不会关闭容器



#方式2

docker attach 容器ID

#两种方式的区别

exec 在容器中打开新的终端 并且可以启动新的进程


attach 直接进行容器终端,不会启动新的进程

这种方式里面使用exit退出会关闭容器 如果不想关闭容器必须使用ctrl+p+q

6,进入容器内拷贝文件到主机

docker cp 容器ID:容器内的路径  主机目录

[if !vml]

[endif]





09【掌握】docker镜像详解

1,什么是镜像

1.1    UnionFS(联合文件系统)

UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

[if !vml]

[endif]

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录


1.2   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等等。 

[if !vml]

[endif]

 平时我们安装进虚拟机的CentOS都是好几个G,为什么docker这里才200M??

[if !vml]

[endif]

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

bootfs基本是一致的, rootfs会有差别, 因此不同的发行版可以公用bootfs。

1.3   分层的镜像

以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载

[if !vml]

[endif]

1.4 为什么docker要使用上面的这种分层结构

最大的一个好处就是 - 共享资源

比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,

同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

2,镜像的特点

Docker镜像都是只读的

当容器启动时,一个新的可写的镜像被加载到镜像层的顶部。

这一层通常被叫做容器层,容器层之下的都叫镜像层

3,镜像的commit操作

3.1 作用

当镜像运行之后可以修改容器里面的内容,再提交成一个新的镜像

3.2 命令语法


docker commit-m='新的镜像的描述信息'-a='作者' 容器ID 要创建的目标镜像名:[标签名]

3.3 案例演示

1,从hub上拉一下tomcat镜像运行


docker run-it-p8080:8080 tomcat

p 主机端口:容器端口

P 随机分配端口

i 交互

t 终端

[if !vml]

[endif]

[if !vml]

[endif]

[if !vml]

[endif]

从上面可以看出docs是可以访问的哦

2,删除tomcat里面webapps 里面的docs项目

[if !vml]

[endif]

刷新页面docs不存在了哦

[if !vml]

[endif]

3,把当前运行的这个没有docs的容器生成一个新的镜像

语法


docker commit-a='作者'-m='镜像描述'  容器ID  新的镜像名/名称:版本

案列


docker commit-a='laolei'-m='del tomcat docs'dbebc1893880   laolei/tomcatnodocs:1.0

[if !vml]

[endif]

4,启动自己创建的镜像和之前的对比

1 ,删除所有容器


dockerrm-f$(docker ps -aq)

2,启动之前的镜像


docker run-d-p8888:8080镜像ID或仓库ID+版本

3,启动自己的镜像


docker run-d-p9999:8080镜像ID





10【掌握】docker命令总结

[if !vml]

[endif]



11【掌握】docker容器数据卷

1,什么是容器数据卷

先来看看Docker的理念:

* 将应用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的

* 容器之间希望有可能共享数据

Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来,

那么当容器删除后,数据自然也就没有了。

为了能保存数据在docker中我们使用卷。


2,容器数据卷能做什么

1,容器数据的持久化

2,容器之间继承和共享数据


3,添加数据卷的方式


3.1直接使用命令添加

①语法

docker run -it -v /宿主机目录:/容器内目录 centos /bin/bash

[if !vml]

[endif]


②查看容器卷是否挂载成功


[if !vml]

[endif]


异常处理

[if !vml]

[endif]

--privileged=true




3.2使用DockerFile添加

在root下创建一个docker文件夹

[if !vml]

[endif]

进入docker文件夹创建文件名为Dockerfile的文件

[if !vml]

[endif]

编写Dockerfile的文件

#基于tomcat:8.5构造一个镜像

FROM tomcat:8.5

#加入容器卷

VOLUME

  ["/usr/local/tomcat/webapps"]

CMD echo "finished,--------success1"

CMD /bin/bash


使用Dockerfile生成自定义镜像

[if !vml]

[endif]

生成一个镜像 再运行

可以使用docker inspect 容器ID查看容器卷

[if !vml]

[endif]


4,数据卷容器

4.1,作用:实现容器之间的数据共享

4.2,操作思路

生成一个自定义的centos

创建Dockerfile

[if !vml]

[endif]

使用dockerfile构造镜像

[if !vml]

[endif]

使用自己创建的镜像启动一个容器

docker run -it –name=os1 mycentos01:1.0 /bin/bash


使用自己创建的镜像启动第二个容器继承第一个


docker run -it –name=os2 --volumes-from os1

mycentos01:1.0 /bin/bash


使用自己创建的镜像启动第三个容器继承第二个


docker run -it –name=os3 --volumes-from os2

mycentos01:1.0 /bin/bash



测试方法


进入os1在mydata里面创建个os1.txt


进入os2查看mydata里面也有os1.txt

在os2的mydata里面创建os2.txt


进入os3查看mydata里面的数据

可以找到os1.txt和os2.txt


删除os1之后情况

Os2 os3的mydata里面的数据不会发生变化



原因

[if !vml]

[endif]


12【掌握】Dockerfile详解【1】

1,什么是Dockerfile

1,Dockerfile是用来构建Docker镜像的构建文件,是由一系列的命令和参数构成的脚本

2,Dokcerfile的构建步骤

        编写Dokcerfile文件

        docker build 生成新的镜像

        docker run 运行镜像

3,以centos的镜像为例来说明 https://hub.docker.com/_/centos

[if !vml]

[endif]

[if !vml]

[endif]

2,DockerFile构建过程解析

2.1基础知识


1,每条保留字指令都必须为大写字母后面要跟随至少一个参数

2,指令从上到下顺序执行

3,#表示注释

4,每条指令都会创建一个新的镜像层,并对镜像进行提交

2.2大致流程

1,docker从基础镜像运行一个容器

2,执行一条指令并对容器进行修改

3,执行类似于dockercommit的操作提交一个新的镜像

4,docker再基于刚提交的新的镜像运行一个新的容器

5,执行dockerfile的下一个指令再从执行第2点直到没有指令

2.3总结


从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,

*  Dockerfile是软件的原材料

*  Docker镜像是软件的交付品

*  Docker容器则可以认为是软件的运行态。

Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。

1Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。

    Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、

    服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;

2Docker镜像,在用Dockerfile定义一个文件之后,dockerbuild时会产生一个Docker镜像,

    当运行Docker镜像时,会真正开始提供服务;

3Docker容器,容器是直接提供服务的。

[if !vml]

[endif]


3,DockerFile体系结构(关键字---重点啊)

FROM 基础镜像,当前新镜像是基于哪个镜像的

MAINTAINER  镜像维护者的姓名和邮箱地址

RUN  容器构建时需要运行的命令

EXPOSE 当前容器对外暴露的端口

WORKDIR 指定在创建容器后,终端默认登陆进来的工作目录

ENV 用来在构建镜像过程中设置环境变量

ADD 将宿主机目录下的文件拷贝进镜像并且ADD命令会自动处理URL和解压tar包

COPY 类似ADD,拷贝文件和目录到镜像中 ,语法COPY src dest  COPY[''src","dest"]

VOLUME 容器数据卷,用于数据保存和持久化工作

CMD 指定一个容器启动时要运行的命令格式

        shell:  CMD  <命令>

        exec  CMD ['可执行文件',"参数1","参数2"]

        DockerFile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换

ENTEYPONT  指定一个容器启动时要运行的命令

            ENTRYPOINT的目地和CMD一样,都是在指定容器启动程序及参数

OBBUILD 当构建一个被继承的Dockerfile时运行命令,父镜像在被子镜像继承后触发父镜像的onbuild

4,总结

[if !vml]

[endif]


13【掌握】Dockerfile详解【2】

1,Base镜像的说明

docker Hub上99%的镜像都是通过base镜像中安装和配置需要的软件构建出来的

[if !vml]

[endif]


2,自定义mycentos镜像

2.1目地

 登陆后的默认路径

 vim编辑器

 查看网络配置ifconfig支持

[if !vml]

[endif]

2.2进入mydocker目录创建DockerFile文件编写


cd  /mydocker

touchDockerfile

viDockerfile

向Dockerfile里面写入内容


#继承父镜像

FROMcentos

#作者和邮箱

MAINTAINERlaolei<78414842@qq.com>

#设置环境变量

ENVMYPATH/usr/local

WORKDIR$MYPATH

#安装vim 和网络工具

RUNyum-yinstallvim

RUNyum-yinstallnet-tools

#对外暴露的端口[提示作用]

EXPOSE80

#输出

CMDecho$MYPATH

CMDecho"success--------------ok"

CMD/bin/bash

2.3使用docker build命令构建镜像

docker build

-t mycentos:1.2 .

[if !vml]

[endif]

[if !vml]

[endif]

[if !vml]

[endif]

2.4使用docker run命令运行


dockerrun-itmycentos:1.2

[if !vml]

[endif]

可以看到里面可以使用ipconfig命令了哦

2.5列出镜像的变更历史


dockerhistory镜像名:TAG

[if !vml]

[endif]




14【掌握】Dockerfile详解【3】


1,CMD说明

Dockerfile 中可以有多个CMD的命令,但只有最后一个生效,CMD会被docker run之后的参数替换掉

可以使用tomcat的案例演示

2.1查看tomcat的Dockerfile

[if !vml]

[endif]

从上面的可以看出最后执行了一个catalina.sh的命令  相当于./bin/catalina.sh

运行如下命令,可以看出下图的效果


dockerrun-it-p9999:8080tomcatls-lh

[if !vml]

[endif]

发现这个tomcat的容器启动之后就退出了

那是因为在启动容器的时候后面加了ls -lh那么会覆盖Dockerfile里面的CMD ["catalina.sh", "run"]

2,ENTRYPOINT说明

使用ENTRYPOINT来运行命令,在run运行的参数会追加到新的命令后面

3,自定义ls镜像来说明CMD和ENTRYPOINT的区别

目地:讲解CMD和ENTRYOINT的区别

编写Dockerfile


FROMcentos

CMD["ls","-a"]

构建镜像


dockerbuild-tmyls:1.2.

启动镜像


dockerrun-itmyls:1.2-lh

[if !vml]

[endif]

出现错误,这是因为Dockerfile里面使用的是CMD ["ls", "-a"]运行是传一个-lh过去会覆盖


修改Dockerfile


FROMcentos

ENTRYPOINT["ls","-a"]

构建镜像


dockerbuild-tmyls:1.3.

运行镜像


dockerrun-itmyls:1.3-l

[if !vml]

[endif]

发现 【 -l 】被追加到 ls 后面了哦



[if !vml]

[endif]

15【掌握】Dockerfile详解【4】做一个tomcat

方案选择

基于一个openjdk 不用配置java环境变量


基于一个centos  要配置java的环境变量




基于一个centos创建一个tomcat镜像


准备工作

创建目录

[if !vml]

[endif]

下载jdk

下载tomcat


[if !vml]

[endif]




创建并编写Dockerfile文件

[if !vml]

[endif]

[if !vml]

[endif]


#基于centos镜像

FROM centos

#设置作者和邮箱

MAINTAINER laolei<78414842@qq.com>

#声明变量

ENV MYPATH /usr/local/tomcat

#创建tomcat的目录

RUN mkdir -p /usr/local/java

#把tomcat和JDKcopy到/root/

ADD jdk-8u181-linux-x64.tar.gz /root/

ADD apache-tomcat-8.5.43.tar.gz /root/

#列出root目录下的所有内容

RUN ls -lh /root/

#把apache-tomcat-8.5.43修改成tomcat

RUN mv /root/apache-tomcat-8.5.43  /root/tomcat

#把tomcat 放到/usr/local/

RUN mv /root/tomcat /usr/local/

#把jdk1.8.0_181移动到/usr/local/java

RUN mv /root/jdk1.8.0_181   /usr/local/java

#设置工作目录

WORKDIR $MYPATH

#配置JAVA和tomcat环境变量

ENV JAVA_HOME

  /usr/local/java/jdk1.8.0_181

ENV CLASSPATH

  $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

ENV CATALINA_HOME /usr/local/tomcat

ENV CATALINA_BASE /usr/local/tomcat

ENV PATH

  $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

#安装VIM   -y代表所有的确认的地方全部yes

#RUN yum -y install vim

#设置对名暴露的端口  纯提示作用

EXPOSE 8080 

#打开一个终端

CMD bin/startup.sh && tail -F

  logs/catalina.out





构造镜像

[if !vml]

[endif]


运行镜像测试

[if !vml]

[endif]


[if !vml]

[endif]



16【掌握】Dockerfile详解【5】

使用jar包做项目镜像

准备jar包

在linux里面创建一个目录

mkdir /root/dockerfile/myjarproject

[if !vml]

[endif]

上传jar包到linux的目录

[if !vml]

[endif]

创建并编写Dockerfile文件

[if !vml]

[endif]

构造镜像

[if !vml]

[endif]

[if !vml]

[endif]

使用镜像运行容器测试

[if !vml]

[endif]


[if !vml]

[endif]

使用war包做项目镜像

准备war包

[if !vml]

[endif]

在linux里面创建一个目录

[if !vml]

[endif]


mkdir /root/dockerfile/mywarproject

上传war包到linux的目录

[if !vml]

[endif]

创建并编写Dockerfile文件

[if !vml]

[endif]

构造镜像

[if !vml]

[endif]

使用镜像运行容器测试

[if !vml]

[endif]


[if !vml]

[endif]


17【熟悉】本地镜像发布到阿里云

1,镜像的生成方法

1,docker commit

2,Dockerfile

2,本地镜像推送到阿里云

2.1以修改的mywar的image为例来说明

[if !vml]

[endif]

2.2登陆阿里开发平台

https://dev.aliyun.com/search.html完成登陆

打开https://cr.console.aliyun.com/cn-hangzhou/instances/repositories

[if !vml]

[endif]


2.3创建镜像仓库

[if !vml]

[endif]

[if !vml]

[endif]

[if !vml]

[endif]

2.4设置镜像仓库的访问密码

[if !vml]

[endif]

2.5 打开阿像页面

[if !vml]

[endif]

按里面的操作来就行了


$ sudodocker login--username=78414842@qq.com registry.cn-hangzhou.aliyuncs.com

$ sudo docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/laolei/mywar:[镜像版本号]

$ sudo docker push registry.cn-hangzhou.aliyuncs.com/laolei/mywar:[镜像版本号]

[if !vml]

[endif]

2.6  的阿里的公有仓库里面查询试试

[if !vml]

[endif]

[if !vml]

[endif]

4,把阿里云的镜像下载到本地

4.1语法


docker pull registry.cn-hangzhou.aliyuncs.com/laolei/mywar:[镜像版本号]

4.2案例

[if !vml]

[endif]

4.3运行测试

[if !vml]

[endif]

[if !vml]

[endif]











18【掌握】docker常用安装

1,安装步骤梳理

搜索镜像    docker search mysql

拉取镜像     docker pull 

查看镜像     docker images 

启动镜像      docker run 

停止容器      docker stop 容器ID

移除容器     docker rm -f 容器ID

2,安装tomcat

docker hub好查找tomcat镜像  docker search tomcat


从docker hub上拉取tomcat镜像到本地  docker pull tomcat


使用docker images查看是否有拉取到tomcat

使用tomcat镜像运行镜像成为容器 

    docker run -it -p 8080:8080

tomcat 


    -p 主机端口:容器端口


    -P 随机分配端口


    -i  交互


    -t 终端  

3,安装mysql

docker hub上查找mysql镜像  docker search mysql

[if !vml]

[endif]

docker hub上拉取mysql镜像到本地  docker pull mysql:5.7

[if !vml]

[endif]


使用mysql5.7镜像创建容器运行存在问题

[if !vml]

[endif]

这是因为mysql镜像一启动中在加设置用户名和密码

修改成下面的命令


docker run--namemysql-p3306:3306-eMYSQL_ROOT_PASSWORD=123456-dmysql:5.7

[if !vml]

[endif]

发现启动起来了

4,使用navcat连接一下

[if !vml]

[endif]

如果是5.7的数据库不会报错

发现报错,这是因为docker 的mysql里面的localhost  -root 的密码是启动时设置的是123456现%-root的没有设置

下面是解决方法


1, 进入mysql的docker 容器

docker exec-itmysql /bin/bash

2,登陆mysql

mysql-uroot-p

输入密码

3,使用mysql数据库

use mysql

4,执行修改密码的命令

update usersetpassword=password(123456) wherehost='%'

5,重启mysql的docker 容器

exit退出

exit退出mysql容器

dockerrestartmysql

再就可以进行远程连接了哦

[if !vml]

[endif]

如果出现使用宿主机IP无法访问的情况 在宿主机里面执行如下命令

请顺序运行以下命令:


nmcli connection modify docker0

connection.zone trusted

systemctl stop NetworkManager.service

firewall-cmd --permanent --zone=trusted

--change-interface=docker0

systemctl start NetworkManager.service

nmcli connection modify docker0

connection.zone trusted

systemctl restart docker.service

再重启mysql和tomcat的容器

5,相关文件地址配置 

可以在运行容器时设置 :

docker run -p 3306:3306 --name mysql -v

$PWD/conf/my.cnf:/etc/mysql/my.cnf -v $PWD/logs:/logs -v $PWD/data:/mysql_data

-e MYSQL_ROOT_PASSWORD=123456 -d mysql

但要求对应路径对应文件已存在,才能成功挂载相内容到对应位置。

命令说明:

-p 3306:3306:将容器的3306端口映射到主机的3306端口

-v $PWD/conf/my.cnf:/etc/mysql/my.cnf:将主机当前目录下的conf/my.cnf挂载到容器的/etc/mysql/my.cnf

-v $PWD/logs:/logs:将主机当前目录下的logs目录挂载到容器的/logs

-v $PWD/data:/mysql_data:将主机当前目录下的data目录挂载到容器的/mysql_data

-e MYSQL_ROOT_PASSWORD=123456:初始化root用户的密码






19【掌握】Docker的四种网络模式

1,概述

docker run创建Docker容器时,可以用–net选项指定容器的网络模式,Docker有以下4种网络模式:

bridge模式:使--net =bridge指定,默认设置;

host模式:使--net =host指定;

none模式:使--net =none指定;

container模式:使--net =container:NAMEorID指定。

可以使用docker network ls来查看

[if !vml]

[endif]

下面我们分别来讲解docker的四种网络模式:

2,bridge模式

bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将并将一个主机上的Docker容器连接到一个虚拟网桥上。当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ip addr命令是可以看到docker0的,可以认为它是网桥的管理端口,在宿主机上作为一块虚拟网卡使用)。

具体操作:

启动容器:(由于是默认设置,这里没指定网络–net

=bridge,可以看到容器内创建了eth0)

[if !vml]

[endif]

使用ping命令连接Host网络发现,容器与Host网络是连通的:

[if !vml]

[endif]

eth0实际上是veth pair的一端,另一端(veth945c)连接在docker0网桥上:

[if !vml]

[endif]

3,host模式

如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network

Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。

使用host模式启动容器:

[if !vml]

[endif]

使用host模式启动容器后可以发现,使用ip addr查看网络环境时,看到的都是宿主机上的信息。这种方式创建出来的容器,可以看到host上的所有网络设备。

容器中,对这些设备有全部的访问权限。因此docker提示我们,这种方式是不安全的。如果在隔离良好的环境中(比如租户的虚拟机中)使用这种方式,问题不大。

4,none模式

在none模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。

使用--net =none模式启动容器:

[if !vml]

[endif]

5,container模式

这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。

使用--net =container模式启动容器:

[if !vml]

[endif]

通过该例子可以看出来,两者的网络完全相同。







20【了解】docker搭建GitLab


1,安装Centos

注意点:

      1,如果是VM里面安装要选择桥接模式,要不然局域网无法访问的哦

[if !vml]

[endif]

      2,处理器和内存选择两核4G的

[if !vml]

[endif]

2,修改Linux的22端口

以后只能使用修改之后的端口登陆了

[if !vml]

[endif]

vim /etc/ssh/sshd_config

[if !vml]

[endif]

让配置生效  并重启Linux


semanage port -a -t ssh_port_t -p tcp 10086

如果不能执行先安装一下

yum  install policycoreutils-python

// 把新的Port口告诉SELinux

# semanage port -a -t ssh_port_t -p tcp 10086

// 重启ssh

# systemctl restart sshd.service

// 查看ssh状态

# service sshd status

// 关闭防火墙

# systemctl stop firewalld.service

3,安装docker

https://docs.docker.com/install/linux/docker-ce/centos/

如果之前有docker的版本,请先删除原来的版本

按官网的步骤来

4,运行镜像

https://hub.docker.com/r/twang2218/gitlab-ce-zh

时间可能比较长


docker run-d\

    --hostnamewww.code.com \

    -p80:80 \

    -p443:443 \

    -p22:22 \

    --namegitlab \

    --restartunless-stopped \

    -vgitlab-config:/etc/gitlab \

    -vgitlab-logs:/var/log/gitlab \

    -vgitlab-data:/var/opt/gitlab \

    twang2218/gitlab-ce-zh

注意上面的hostnamewww.code.com 需要在使用者的电脑的host文件里面配置

5,异常处理

重启docker 

重启容器

6,基本使用

注册。登陆

[if !vml]

[endif]


感谢雷哥的视频(第一次写请大佬们不要喷我这个菜逼)https://ke.qq.com/webcourse/index.html#cid=469242&term_id=100561719&taid=3997484976646394&vid=5285890796224454021

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270