Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于Google 公司推出的 Go 语言实现。项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码
在GitHub 上进行维护。
Docker 自开源后受到广泛的关注和讨论,以至于 dotCloud 公司后来都改名为 Docker Inc,Redhat 已经在其 RHEL6.5 中集中支持 Docker;Google 也在其 PaaS 产品中广泛应用。
Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案,Docker 的基础是 Linux 容器(LXC)等技术。
Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。
LXC为Linux Container的简写,可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性,相当于C++中的NameSpace,容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。
与传统虚拟化技术相比,它的优势在于:
Linux Container提供了在单一可控主机节点上支持多个相互隔离的server container同时执行的机制。Linux Container有点像chroot,提供了一个拥有自己进程和网络空间的虚拟环境,但又有别于虚拟机,因为lxc是一种操作系统层次上的资源的虚拟化。
docker并不是LXC替代品,docker底层使用了LXC来实现,LXC将linux进程沙盒化,使得进程之间
相互隔离,在LXC的基础之上,docker提供了一系列更强大的功能。
容器化越来越受欢迎,因为容器是:
作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。
首先,Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多。其次,Docker 对
系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。
具体说来,Docker 在如下几个方面具有较大的优势。
更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker对系统资源的利用率更高,无论是应用执行速度,内存消耗以及文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而Docker容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级,甚至毫秒级的启动时间,大大的节约了开发,测试,部署的时间。
一致的运行环境
开发过程中一个常见的问题是环境一致性问题,由于开发环境,测试环境,生产环境不一致,导致有些bug并未在开发过程中被发现,而Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性。
持续交付和部署
对于开发和运维人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用Docker可以通过定制应用镜像来实现持续集成,持续交付,部署。开发人员可以通过Dockerfile来进行镜像构建,并结合持续集成系统进行集成测试,而运维人员则可以在生产环境中快速部署该镜像,甚至结合持续部署系统进行自动部署
更轻松的迁移
由于Docker确保了执行环境的一致性,使得应用的迁移更加容易,Docker可以在很多平台上运行,无论是物理机,虚拟机,公有云,私有云,其运行结果是一致的,因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
隔离性
基于hypervisor的虚机技术,在隔离性上比容器技术要更好,它们的系统硬件资源完全是虚拟化的,当一台虚机出现系统级别的问题,往往不会蔓延到同一宿主机上的其他虚机。但是容器就不一样了,容器之间共享同一个操作系统内核以及其他组件,所以在收到攻击之类的情况发生时,更容易通过底层操作系统影响到其他容器
性能
不管是虚机还是容器,都是运用不同的技术,对应用本身进行了一定程度的封装和隔离,在降低应用和应用之间以及应用和环境之间的耦合性上做了很多努力,但是随即而来的,就会产生更多的网络连接转发以及数据交互,这在低并发系统上表现不会太明显,而且往往不会成为一个应用的瓶颈(可能会分散于不同的虚机或者服务器上),但是当同一虚机或者服务器下面的容器需要更高并发量支撑的时候,也就是并发问题成为应用瓶颈的时候,容器会将这个问题放大,所以,并不是所有的应用场景都是适用于容器技术的。
存储方案
容器的诞生并不是为OS抽象服务的,这是它和虚机最大的区别,这样的基因意味着容器天生是为应
用环境做更多的努力,容器的伸缩也是基于容器的这一disposable特性,而与之相对的,需要持久化存
储方案恰恰相反。
这一点docker容器提供的解决方案是利用volume接口形成数据的映射和转移,以实现数据持久化的目的。但是这样同样也会造成一部分资源的浪费和更多交互的发生,不管是映射到宿主机上还是到网络磁盘,都是退而求其次的解决方案。
随着Docker的不断流行与发展,docker公司(或称为组织)也开启了商业化之路,Docker 从17.03版本之后分为 CE(Community Edition) 和 EE(Enterprise Edition)。我们来看看他们之间的区别与联系。
版本区别
Docker EE
Docker EE由公司支持,可在经过认证的操作系统和云提供商中使用,并可运行来自Docker Store
的、经过认证的容器和插件。
Docker EE提供三个服务层次:
https://www.docker.com/pricing
Docker CE
Docker CE是免费的Docker产品的新名称,Docker CE包含了完整的Docker平台,非常适合开发人员和运维团队构建容器APP。
Docker公司认为,Docker CE和EE版本的推出为Docker的生命周期、可维护性以及可升级性带来了巨大的改进。
版本说明
在此之前docker的最新版本更新到docker1.13,而在1.13的基础之上,在2017年的3月1号开始,版本的格式变为如下
小结
较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关
的依赖项。
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库,之后,您可以从
仓库安装和更新 Docker。
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
设置 Docker 安装地址
在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库,之后,您可以从
仓库安装和更新 Docker,使用以下命令来设置稳定的仓库(阿里云)
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装 Docker Engine-Community
安装最新版本的 Docker Engine-Community 和 containerd
sudo yum install -y docker-ce docker-ce-cli containerd.io
如果提示您接受 GPG 密钥,请选是。
Docker 安装完默认未启动,并且已经创建好 docker 用户组,但该用户组下没有用户。
Docker 镜像加速
阿里云镜像获取地址:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors,登陆
后,左侧菜单选中镜像加速器就可以看到你的专属地址了:
配置daemon.json
您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://ffunzkst.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
https://www.baiyp.ren/Docker%E5%AE%89%E8%A3%85.html
systemctl start docker
验证docker服务
通过运行 hello-world 镜像来验证是否正确安装了 Docker Engine-Community 。
docker run hello-world
运行该代码后首先会从仓库拉取 hello-world 镜像,然后运行该镜像,运行后会输出一个 Hello from Docker!
使用docker时无法自动补全镜像名和其他参数,这样使用效率大大降低,下面是解决方法
bash-complete
yum install -y bash-completion
刷新文件
source /usr/share/bash-completion/completions/docker
source /usr/share/bash-completion/bash_completion
测试
输入 docker p 后docker会将可选项列出来
docker p
Docker总体架构为c/s架构,模块之间松耦合,包含了Client, Daemon, Registry, Graph, Driver,
Libcontainer以及Docker Container
Docker 包括三个基本概念:
镜像
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
分层存储
镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,
由多层文件系统联合组成。
Union FS
联合文件系统(Union FS)是linux的存储技术,也是Docker镜像的存储方式, 它是分层的文件系统,将不同目录拉到同一个虚拟目录下,下图展示了Docker用Union FS 搭建的分层镜像:(比如最下层是操作系统的引导,上一层是Linux操作系统,再上一层是Tomcat,jdk,再上一层是应用代码)
这些层是只读的,加载完后这些文件会被看成是同一个目录,相当于只有一个文件系统。
我们可以通过docker info查看镜像的一些信息,可以看到存储驱动用的并不是Union FS 而是
overlay2,overlay也是一个联合文件系统
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。(比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。)
容器
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜
像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学Docker 时常常会混淆容器和虚拟机。
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器
存储层的信息都会随容器删除而丢失。
仓库
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜
像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签
(Tag);每个标签对应一个镜像。
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们
可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以latest作为默认标签。(以 Ubuntu 镜像为例,ubuntu是仓库的名字,其内包含有不同的版本标签,如,14.04,16.04。我们可以通过ubuntu:14.04,或者ubuntu:16.04来具体指定所需哪个版本的镜像。如果忽略了标签,比如ubuntu,那将视为ubuntu:latest。)
仓库名经常以三段式路径形式出现,比如 heima.com/nginx-proxy:tag ,前者往往意味着Docker
Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体Docker Registry 的软件或服务
关于进程相关的命令
systemctl start docker
systemctl stop docker
systemctl restart docker
systemctl status docker
systemctl enable docker
docker images
docker images –q # 查看所用镜像的id
TAG:镜像标签
IMAGE ID:镜像ID
CREATED:镜像的创建日期
SIZE:镜像大小
docker search 镜像名称
NAME:镜像名称
DESCRIPTION:镜像描述
STARS:用户评价,反应一个镜像的受欢迎程度
OFFICIAL:是否官方
AUTOMATED:自动构建,表示该镜像由Docker Hub自动构建流程创建的
从Docker仓库下载镜像到本地,镜像名称格式为 名称:版本号,如果版本号不指定则是最新的版本。 如果不知道镜像版本,可以去docker hub 搜索对应镜像查看。
docker pull 镜像名称
删除本地镜像
docker rmi 镜像id # 删除指定本地镜像
docker rmi `docker images -q` # 删除所有本地镜像
关于docker更多命令, 大家可以菜鸟教程查看, 网址:
https://www.runoob.com/docker/docker-command-manual.html
docker ps # 查看正在运行的容器
docker ps –a # 查看所有容器,包括正在运行和停止的容器
docker run 参数
参数说明:
• -i:保持容器运行。通常与 -t 同时使用。加入it这两个参数后,容器创建后自动进入容器中,退出容器后,容器自动关闭。
• -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用。
• -d:以守护(后台)模式运行容器。创建一个容器在后台运行,需要使用docker exec 进入容器。退出后,容器不会关闭。
• -it 创建的容器一般称为交互式容器,-id 创建的容器一般称为守护式容器
• --name:为创建的容器命名。
1)交互式容器
以交互式方式创建并启动容器,启动完成后,直接进入当前容器。使用exit命令退出容器。需要注意的是以此种方式 启动容器,如果退出容器,则容器会进入停止状态。
# 先拉取一个镜像;这一步不是每次启动容器都要做的,而是因为前面我们删除了镜像,无镜像可用所以才再拉取一个
docker pull centos:7
#创建并启动名称为 mycentos7 的交互式容器;下面指令中的镜像名称 centos:7 也可以使用镜像id
docker run -it --name=mycentos7 centos:7 /bin/bash
2)守护式容器
创建一个守护式容器;如果对于一个需要长期运行的容器来说,我们可以创建一个守护式容器。命令如下(容器名称 不能重复):
#创建并启动守护式容器
docker run -di --name=mycentos2 centos:7
#登录进入容器命令为:docker exec -it container_name (或者 container_id) /bin/bash(exit退出 时,容器不会停止)
docker exec -it mycentos2 /bin/bash
docker exec 参数 # 退出容器,容器不会关闭
eg:
docker exec -it mycentos2 /bin/bash
docker stop 容器名称或者容器id
docker start 容器名称或者容器id
#删除指定容器
docker rm 容器名称或者容器id
# 删除所有容器:
docker rm `docker ps -a -q`
注意:如果容器是运行状态则删除失败,需要停止容器才能删除
docker inspect 容器名称或者容器id
说明:容器之间在一个局域网内,linux宿主机器可以与容器进行通信;但是外部的物理机笔记本是不能与容器直接通信的,如果需要则需要通过宿主机器端口的代理。
思考:
• Docker 容器删除后,在容器中产生的数据也会随之销毁
• Docker 容器和外部机器可以直接交换文件吗?
• 容器之间想要进行数据交互?
数据卷
“卷”是容器上的一个或多个“目录”,此类目录可绕过联合文件系统,与宿主机上的某个目录“绑定(关联)”;
Docker镜像由多个只读层叠加而成,启动容器时,docker会加载只读镜像层并在镜像栈顶部加一个读写层;
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即**“写时复制(COW)”机 制**。
为了避免这种情况,构建Dockerfile的时候应该加入一个存储卷
增加存储卷
编写Dockerfile增加存储卷,增加日志存储卷 /logs ,这会是一个匿名存储卷
FROM openjdk:8-jdk-alpine
VOLUME /tmp /logs
ADD learn-docker-storage-1.0-SNAPSHOT.jar app.jar
EXPOSE 8003
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
构建镜像
因为我们原来已经构建了镜像,这次使用版本号是 0.0.2
#构建镜像
docker build -t learn-docker-storage:0.0.2 .
# 查看镜像列表
docker images
# 运行容器
docker run -d -p 8003:8003 learn-docker-storage:0.0.2
# 查看运行中的容器
docker ps
查看存储卷
通过 docker volume ls 可以看到存储卷
docker volume ls
查看容器信息
我们发现都是匿名的存储卷,如何来确定是哪个呢,可以通过 docker inspect 容器ID 来查看存储信息
[root@linux30 docker-learn]# docker inspect 2ff69575338d |grep Mounts -A 20
通过这个命令可以看到数据卷的名称以及宿主机的存储路径,我们可以直接到宿主机打印日志
# 进入文件挂载目录
[root@linux30 docker-learn]# ls
/var/lib/docker/volumes/a343b517882f9e248c6f7e9f7e43deb27bb0207cae1f63d0f0b05689 be16a397/_data
# 输出日志
[root@linux30 docker-learn]# tail -f
/var/lib/docker/volumes/a343b517882f9e248c6f7e9f7e43deb27bb0207cae1f63d0f0b05689 be16a397/_data/learn-docker-storage.log
这样就看到了我们的日志文件
验证存储卷
删除容器检查存储卷释放消失
# 查看运行的容器列表
docker ps
#删除容器
[root@linux30 docker-learn]# docker rm -f 2ff69575338d
#查看所有的容器列表(运行+停止)
docker ps -a
docker volume ls
# 查看存储卷列表
docker volume ls
# 查看存储卷详情
docker inspect 296ccc64d919e86bb8329bf6b08447c2ea6a118458d3fcb86d5c7c9a3177dfe0
# 运行容器
[root@linux30 docker-learn]# docker run -d -p 8080:8080 81c753a43b3c
# 查看运行中的容器
docker ps
# 查看存储卷列表
docker volume ls
我们发现又创建了两个存储卷,查看容器详情
[root@linux30 docker-learn]# docker inspect d8f2f27682dd|grep Mounts -A20
什么是bind
Bind mounts模式和Volumes非常相似,不同点在于Bind mounts模式是将宿主机上的任意文件或文件夹挂载到容器,而Volumes本质上是将Docker服务管理的一块区域(默认是/var/lib/docker/volumes下的文件夹)挂载到容器。
共享存储
经过上面的测试,我们发现每一个容器都是单独用一个存储卷,用于临时文件没有问题的,但是如果要让容器都用同一个存储路径怎么办呢,这个时候就用到了 bind挂载了,可以使用 -v 进行挂载刚才的存储卷。
# 级联创建文件夹
mkdir -p /opt/docker/logs
# 运行容器,指定挂载路径
docker run -d -v /opt/docker/logs:/logs \
-p 8003:8003 --name learn-docker-storage \
learn-docker-storage:0.0.2
这里面 --name 是指定docker容器的名称,我们操作容器就可以使用名称进行操作了
然后使用 docker inspect 命令来检查容器详情
[root@linux30 logs]# docker inspect learn-docker-storage|grep Mounts -A20
我们发现挂载日志的挂载方式已经变了,由原来的volume变为了bind,并且挂载路径变为了我们自己定义的路径,进入目录查看
# 进入目录并浏览目录文件
[root@linux30 logs]# ll /opt/docker/logs
# 打印日志详情
[root@linux30 logs]# tail -f /opt/docker/logs/learn-docker-storage.log
验证共享存储
我们也按照上面步骤验证下bind方式挂载的存储,先删除容器,检查日志文件是否存在
# 停止并删除容器
docker rm -f learn-docker-storage
# 查看容器已经被删除了
docker ps -a
# 进入日志挂载路径查看日志是否存在
[root@linux30 logs]# ll /opt/docker/logs/
# 运行容器,指定挂载路径
docker run -d -v /opt/docker/logs:/logs \
-p 8003:8003 --name learn-docker-storage \
learn-docker-storage:0.0.2
# 查看日志文件
cat learn-docker-storage.log
volume和bind的区别
对于多个容器需要共享访问同一数据目录,或者需要持久化容器内数据(如数据库)时,我们都是采用挂载目录形式(bind mounts),将宿主机的某一目录挂载到容器内的指定目录,这种方式能解决问题,但这种方式也一直有一些缺点
bind mount和volume其实都是利用宿主机的文件系统,不同之处在于volume是docker自身管理的目录中的子目录,所以不存在权限引发的挂载的问题,并且目录路径是docker自身管理的,所以也不需要在不同的服务器上指定不同的路径,你不需要关心路径。
清理volume挂载
volume挂载方式,会生成很多匿名的目录,我们可以找到对应的没有使用的volume进行删除
docker volume ls
通过查看我们发现里面,有很多的volume,我们可以找到没有用的删除
docker volume rm volume_name
还可以通过命令将没有引用的全部volume清除掉,但是这个命令很危险
docker volume prune
创建启动容器时,使用 –v 参数 设置数据卷
docker run ... –v 宿主机目录(文件):容器内目录(文件) ...
注意事项:
目录必须是绝对路径
如果目录不存在,会自动创建
一个容器可以挂载多个数据卷
一个数据卷也可以被多个容器挂载
两个容器可以挂载同一个容器
在宿主机中实现与容器目录的挂载:
在c1容器中的root目录下就可以看到data_container文件夹:如下图
同时我们我们回到宿主机中,可以在data文件夹下创建一个文件itcast.txt ,可以发现在容器中也会生成itcast.txt文件:如下图
当我们把c1容器删除后,宿主机中的数据卷依然存在。 所以当我们重新创建一个容器的同时依然可以挂载宿主机中的data文件夹,对应data里的数据依然同步到容器中。
3. 多个容器可以挂载同一个数据卷
分别创建两个容器,两个容器都挂载宿主机中data目录,当 修改c3容器的data目录c4会实现同步。
从而实现两个容器之间的交互。
多容器进行数据交换
多个容器挂载同一个数据卷
数据卷容器
1.创建启动c3数据卷容器,使用 –v 参数 设置数据卷
docker run –it --name=c3 –v /volume centos:7 /bin/bash
docker run –it --name=c1 --volumes-from c3 centos:7 /bin/bash
docker run –it --name=c2 --volumes-from c3 centos:7 /bin/bash
数据卷概念
•宿主机的一个目录或文件
数据卷作用
• 容器数据持久化
• 客户端和容器数据交换
• 容器间数据交换
数据卷容器
• 创建一个容器,挂载一个目录,让其他容器继承自该容器( --volume-from )。
• 通过简单方式实现数据卷配置
docker search mysql
docker pull mysql
下载指定版本的镜像
我们上面下载的镜像不符合我们的预期,我们需要Mysql5.7的镜像
查找指定镜像
可以登录 https://hub.docker.com 地址搜索镜像
输入需要搜索的镜像名称,并选择对应镜像名称
选择 tag 标签,这个就是版本的信息
找到符合的版本 的mysql镜像,这里我们选择 5.7.35
下载指定版本镜像
刚才我们已经找到了 5.7.35 版本的Mysql的镜像,我们使用 docker pull 命令下载,镜像后面跟
的是tag也就是版本名称
docker pull mysql:5.7.35
我们发现,我们的新拉取的mysql镜像共用了 部分分层
查看镜像
安装完镜像后,我们需要看一下我们的本地镜像,使用 docker images 可用查看本地镜像
docker images
# 创建MySQL配置的文件夹
mkdir -p /tmp/etc/mysql
# 编辑my.cnf配置文件
vi /tmp/etc/mysql/my.cnf
配置MySQL忽略大小写,在我们创建的MySQL配置文件挂载的目录的my.cnf文件加入如下内容
[mysqld]
lower_case_table_names=1
创建MySQL数据目录
因为默认MySQL启动后他的文件是在容器中的,如果我们删除容器,数据也将会消失,我们需要
将数据挂载出来。
#创建mysql存储的目录
mkdir -p /tmp/data/mysql
启动MySql
使用 docker run 启动容器
docker run -d -p 3306:3306 -v /tmp/etc/mysql:/etc/mysql/mysql.conf.d/ -v /tmp/data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql mysql:5.7.34
检查MySQL运行状态
现在并不能确认MySQL的运行状态,我们需要去看下
查看容器运行状态
使用 docker ps 可以看到运行中的容器
docker ps
查看MySQL运行日志
现在MySQL容器起来了,并不代表MySQL已经成功启动,我们需要使用 docker logs 命令查看
MySQL的日志
docker logs -f mysql
客户端连接MySQL
因为我们已经暴漏端口了,可以使用客户端工具连接MySQL
SHOW VARIABLES LIKE '%case_table%';
查看容器挂载的配置文件
可以通过 docker exec -ti mysql /bin/bash 命令登录容器,检查挂载的配置文件情况
# 登录容器
docker exec -ti mysql /bin/bash
我们可以看到我们修改的配置文件已经被挂载到了docker内部,这里面用到了exec命令,主要是
在deocker容器中运行命令,下面我们介绍下
命令格式
主要是在deocker容器中运行命令
docker exec [options] container command [arg...]
查看挂载的数据文件
可以看下挂载的数据文件是否存在
cd /tmp/data/mysql/ && ll
MySql需要导入一些数据用来操作,我们用MySQL官方提供的数据库来操作
下载并导入数据
下载MySQL测试数据库
到测试数据官网 下载数据库文件
直接导入无法导入,需要编辑 employees.sql 文件的一些地方
.....
set default_storage_engine = InnoDB;
-- set storage_engine = MyISAM;
-- set storage_engine = Falcon;
-- set storage_engine = PBXT;
-- set storage_engine = Maria;
select CONCAT('storage engine: ',@@default_storage_engine) as INFO;
.....
导入测试数据
下载解压后,在本地执行命令导入到mysql服务器
root@906e80348596:/var/lib/mysql# mysql -uroot -p < employees.sql
Enter password:
INFO
CREATING DATABASE STRUCTURE
INFO
storage engine: InnoDB
INFO
LOADING departments
INFO
LOADING employees
INFO
LOADING dept_emp
INFO
LOADING dept_manager
INFO
LOADING titles
INFO
LOADING salaries
root@906e80348596:/var/lib/mysql#
1)将employees_db目录下的sql文件上传到/opt/docker/data/mysql/挂载目录下,
2)进入docker容器,
[root@linux30 mysql]# docker exec -it mysql /bin/bash
3)在/var/lib/mysql目录下执行导入命令
root@24d27c3d7ae0:/var/lib/mysql# mysql -uroot -p123456 < employees.sql
docker search tomcat
docker pull tomcat
# 在/root目录下创建tomcat目录用于存储tomcat数据信息
mkdir ~/tomcat
cd ~/tomcat
docker run -id --name=c_tomcat \
-p 8080:8080 \
-v $PWD:/usr/local/tomcat/webapps \
tomcat
参数说明:
**-p 8080:8080:**将容器的8080端口映射到主机的8080端口
**-v $PWD:/usr/local/tomcat/webapps:**将主机中当前目录挂载到容器的webapps
docker search nginx
docker pull nginx
# 在/root目录下创建nginx目录用于存储nginx数据信息
mkdir ~/nginx
cd ~/nginx
mkdir conf
cd conf
# 在~/nginx/conf/下创建nginx.conf文件,粘贴下面内容
vim nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
docker run -id --name=c_nginx \
-p 80:80 \
-v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf \
-v $PWD/logs:/var/log/nginx \
-v $PWD/html:/usr/share/nginx/html \
nginx
docker search redis
docker pull redis:5.0
docker run -id --name=c_redis -p 6379:6379 redis:5.0
./redis-cli.exe -h 192.168.149.135 -p 6379
Nacos是阿里巴巴开源的一款支持服务注册与发现,配置管理以及微服务管理的组件。用来取代以
前常用的注册中心(zookeeper , eureka等等),以及配置中心(spring cloud config等等)。Nacos是集成了注册中心和配置中心的功能,做到了二合一。
直接运行服务
可以直接用docker 启动服务,镜像不存在会自动拉取
docker run -d -p 8848:8848 --name nacos --env MODE=standalone nacos/nacos-server
运行容器后可以稍等下,等待nacos启动成功,受环境限制启动可能有些慢
登录页面测试
可以登录服务器测试一下
用户名:nacos 密码:nacos
场景介绍
我们使用Docker完成一个微服务的搭建过程
整体架构如下
使用多个微服务进行项目部署测试
整体服务说明
我们总共涉及到三个微服务以及两个中间件
配置文件提取
因为我们在开发中需要频繁修改application.yml 文件,我们将配置项配置到 pom 文件中打包时自动打到配置文件,这样可以用一个 pom 文件控制多个不同的服务的配置文件项的修改
pom文件定义属性
我们需要在总 pom 文件定义全局配置,例如 nacos 、 mysql 等配置
<properties>
<mysql.addr>192.168.10.30:3306mysql.addr>
<nacos.addr>192.168.10.30:8848nacos.addr>
properties>
配置编译选项
在子项目的pom文件的 build 构建配置中使用 true 配置,这样就可以将我们的总pom中的配置编译进配置文件了
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
resource>
resources>
build>
配置文件配置
在子项目的 application.yml 配置文件中注意使用 @xxx@ 占位符来配置编译占位配置,下面的配
置就使用了 @@ 占位符,编译后会将pom中的配置编译到配置文件中
server:
port: @project.server.port@
spring:
application:
name: learn-docker-storage
######################### 数据源连接池的配置信息 #################
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://@mysql.addr@/employees?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
initialSize: 10
minIdle: 20
maxActive: 100
maxWait: 60000
#### nacos 配置#######
cloud:
nacos:
server-addr: @nacos.addr@
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.heima.module.po
logging:
file:
name: ./logs/${spring.application.name}.log
编译测试
配置完编译项目后,可以进入target目录下查看编译后的配置文件
我们看到maven已经帮我们将配置编译进了配置文件中了
我们准备将一个微服务打包成Docker镜像,在各种服务器中进行运行,改为服务支持进行查询用
户信息
提前说明
因为我们刚开始进行学习Docker,先从单个微服务开始学习,我们就先部署 learn-docker- storage 服务,后面随着课程的深入再慢慢增加其他的微服务
访问测试
在idea中运行 learn-docker-storage ,
我们配置的端口是8003,我们可以直接访问
http://localhost:8003/storage/employe/findByID/10001
打包上传微服务
将 learn-docker-storage 服务打包后上传到服务器
注意配置项
这里需要检查下maven配置的编译属性是否正确
<mysql.addr>192.168.10.30:3306mysql.addr> <nacos.addr>192.168.10.30:8848nacos.addr>
我们发现是我们容器中启动的地址
上传打包后的微服务
将target目录中打包完成的微服务上传到服务器
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
创建一个Dockerfile文件
vi Dockerfile
具体内容如下
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD learn-docker-storage-1.0-SNAPSHOT.jar app.jar
EXPOSE 8003
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
命令解释
写好DockerFile后就需要用 docker build 命令来构建我们的镜像了,这样就可以将我们的微服务
打包成一个镜像了
构建命令格式
构建一个镜像需要使用以下命令
docker bulid -t 仓库名/镜像名:tag .
参数解释
仓库名:一般是私服或者dockerhub等地址,如果忽略默认就是dockerhub的地址docker.io.library/
镜像名称:就是我们的自己的服务名称,可以随意命名
tag:就是我们的版本号
该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
实战操作
一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下,如果该目录下没有所
需文件,那么应该把所需文件复制一份过来。
一般大家习惯性的会使用默认的文件名 Dockerfile ,以及会将其置于镜像构建上下文目录中。
当前目录的结构如下
构建镜像
进入Dockerfile的目录,通过如下命令构建镜像
docker build -t learn-docker-storage:0.0.1 .
构建完成如果出现 Successfully 说明已经构建成功了
查看我们构建的镜像
使用 docker images 命令查看我们构建完成的镜像
docker images
我们可以看到我们的镜像就在第一个位置
运行镜像
刚才已经打包完成了镜像,现在我们运行我们自己的镜像
# 运行容器
docker run -d -p 8003:8003 learn-docker-storage:0.0.1
# 查看运行中的容器
docker ps
参数最后的 learn-docker-storage:0.0.1 是镜像的名称,启动容器也可以使用镜像ID
参数解释
查看启动日志
使用 docker logs 容器ID 来查看启动日志
[root@linux30 docker-learn]# docker logs -f d8b384678684
curl http://192.168.10.30:8003/storage/employe/findByID/10001 | python -m json.tool
我们发现服务调用成功了,我们基本实现了微服务改造为docker方式并运行
删除容器
要删除一个容器首先需要将一个容器停止掉
停止容器
我们要把刚才运行的容器停止掉,使用 docker stop 容器ID 停止一个容器
[root@linux30 docker-learn]# docker stop d8b384678684
停止容器后,我们再通过进程查看是看不到容器的,但是容器还是存在我们的服务中
查看全部容器
通过 docker ps 只能看到运行中的容器,但是我们停止的容器是看不到的,可以加上 -a 查看所有的容器
docker ps -a
我们可以看到 加上 -a 参数可以看到刚才已经停止掉的容器
启动停止的容器
想要启动一个停止的容器可以使用 docker start 容器ID
[root@linux30 docker-learn]# docker start d8b384678684
这样就把刚才已经停止的容器启动了
删除容器
已经停止的容器可以使用 docker rm 容器ID 删除容器,但是对于运行中的容器可以加上 -f 参数
强制删除
[root@linux30 docker-learn]# docker rm -f d8b384678684
这样可以将一个运行的容器强制删除,而停止的容器可以通过 docker rm 删除
[root@linux30 docker-learn]# docker rm 7de77a6f9c22
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性好。
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以
通过 lo 网卡设备通信。
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
容器在两个跨主机进行通信的时候,是使用overlay network这个网络模式进行通信,如果使用host也可以实现跨主机进行通信,直接使用这个物理的ip地址就可以进行通信。overlay它会虚拟出一个网络比如10.0.9.3这个ip地址,在这个overlay网络模式里面,有一个类似于服务网关的地址,然后把这个包转发到物理服务器这个地址,最终通过路由和交换,到达另一个服务器的ip地址。
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用 iptables -t nat -vnL查看。
bridge模式如下图所示
下图是我们自己的网络结构,我们是通过宿主机访问Mysql容器的,刚才我们学过,默认Docker已经接入了一个名字叫 bridge 的桥接网络
我们可以让我们的网络直接接入桥接网络,例如下图
查看网络列表
可以通过 docker network ls 命令查看网络列表
# 查看网络列表
docker network ls
创建一个桥接网络
默认容器启动会自动默认接入 bridge 的桥接网络,为了区分我们的服务也防止各种网络问题,我们创建一个专用网络,可以通过 docker network create 网络名称 来创建一个默认的桥接网络
# 创建一个桥接网络
docker network create learn-docker-network
# 查看网络列表
docker network ls
服务接入网络
停止并删除原有容器
停止和删除我们的微服务以及mysql服务
# 删除当前运行中的容器
[root@linux30 docker]# docker rm -f learn-docker-storage nacos mysql intelligent_cray
创建MySQL
因为我们的微服务依赖MySQL先启动MySQL并接入网络,因为MySQL不需要通过宿主机访问,所有也不需要映射端口了,–network 是配置接入哪一个网络
docker run -d \
-v /opt/docker/etc/mysql:/etc/mysql/mysql.conf.d/ \
-v /opt/docker/data/mysql:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mysql --network=learn-docker-network \
mysql:5.7.35
这样我们就把我们的MySQL服务启动起来了并且加入了learn-docker-network的网络
创建Nacos
我们的nacos是需要暴漏端口的,因为我们需要外部能够看到nacos页面,但是我们也需要我们的nacos连接到当前网络
docker run -d -p 8848:8848 \
--network=learn-docker-network \
--name nacos --env MODE=standalone \
nacos/nacos-server
查看网络详情
可以通过 docker network inspect 网络名称 可以查看当前的网络的详细信息
docker network inspect learn-docker-network|grep Containers -A 20
修改微服务配置
因为需要使用自定义网络访问mysql容器以及nacos容器,需要修改微服务数据库连接地址,
docker 网络访问 可以通过IP或者通过服务名称都是可以的,这里我们通过服务名称访问,因为我们使用了maven打包的方式,我们只需要将pom文件修改就可以
<properties>
<mysql.addr>mysql:3306mysql.addr>
<nacos.addr>nacos:8848nacos.addr>
properties>
修改完成后进行编译项目
这里将数据库连接地址改为mysql容器的服务名称 mysql ,nacos的连接地址变为了 nacos
重新打包服务
将打包的文件上传服务器后按照上面步骤同上面打包,打包版本为 0.0.3
docker build -t learn-docker-storage:0.0.3 .
创建微服务
下面就按部就班的创建微服务就可以,只是注意需要加入网络,这里这个端口需要映射外网访问
docker run -d \
-v /opt/docker/logs:/logs \
-p 8003:8003 \
--name learn-docker-storage \
--network=learn-docker-network \
learn-docker-storage:0.0.3
curl http://192.168.10.30:8003/storage/employe/findByID/10001 | python -m json.tool
访问测试数据没有问题,到现在我们服务已经搭建完成,并且使用网络进行了优化
前面的课程中已经知道了,要获得镜像,可以从Docker仓库中进行下载。那如果我们想自己开发一个镜像,那该如 何做呢?答案是:Dockerfifile
Dockerfifile其实就是一个文本文件,由一系列命令和参数构成,Docker可以读取Dockerfifile文件并根据Dockerfifile文 件的描述来构建镜像。
思考:
Docker 镜像本质是什么?
是一个分层文件系统
Docker 中一个centos镜像为什么只有200MB,而一个centos操作系统的iso文件要几个个G? ?
Centos的iso镜像文件包含bootfs和rootfs,而docker的centos镜像复用操作系统的bootfs,只有rootfs和其他镜像层
Docker 中一个tomcat镜像为什么有500MB,而一个tomcat安装包只有70多MB? ?
由于docker中镜像是分层的,tomcat虽然只有70多MB,但他需要依赖于父镜像和基础镜像,所有整个对外暴露的
tomcat镜像大小500多MB
命令形式:docker commit 容器名称 镜像名称
eg:
docker commit 381827f60f70 itheima_tomcat:1.0
命令形式:docker save –o tar文件名 镜像名
docker save -o itheima_tomcat:1.0.tar itheima_tomcat:1.0
命令形式:docker load -i tar文件名
# 加载恢复镜像
docker load -i itheima_tomcat.tar
# 在镜像恢复之后,基于该镜像再次创建启动容器
docker run -di --name=new_tomcat -p 8080:8080 itheima_tomcat:1.0
注意:新的镜像制作后,原本容器中挂载的目录将失效, 索引一般在恢复镜像创建容器容器的时候需要重新挂载。
Dochub网址:https://hub.docker.com
关键字 | 作用 | 备注 |
---|---|---|
FROM | 指定父镜像 | 指定dockerfile基于哪个image构建 |
MAINTAINER | 作者信息 | 用来标明这个dockerfile谁写的 |
LABEL | 标签 | 用来标明dockerfile的标签 可以使用Label代替Maintainer 最终都是在docker image基本信息中可以查看 |
RUN | 执行命令 | 执行一段命令 默认是/bin/sh 格式: RUN command 或者 RUN [“command” , “param1”,“param2”] |
CMD | 容器启动命令 | 提供启动容器时候的默认命令 和ENTRYPOINT配合使用.格式 CMD command param1 param2 或者 CMD [“command” , “param1”,“param2”] |
ENTRYPOINT | 入口 | 一般在制作一些执行就关闭的容器中会使用 |
COPY | 复制文件 | build的时候复制文件到image中 |
ADD | 添加文件 | build的时候添加文件到image中 不仅仅局限于当前build上下文 可以来源于远程服务 |
ENV | 环境变量 | 指定build时候的环境变量 可以在启动的容器的时候 通过-e覆盖 格式ENV name=value |
ARG | 构建参数 | 构建参数 只在构建的时候使用的参数 如果有ENV 那么ENV的相同名字的值始终覆盖arg的参数 |
VOLUME | 定义外部可以挂载的数据卷 | 指定build的image那些目录可以启动的时候挂载到文件系统中 启动容器的时候使用 -v 绑定 格式 VOLUME [“目录”] |
EXPOSE | 暴露端口 | 定义容器运行的时候监听的端口 启动容器的使用-p来绑定暴露端口 格式: EXPOSE 8080 或者 EXPOSE 8080/udp |
WORKDIR | 工作目录 | 指定容器内部的工作目录 如果没有创建则自动创建 如果指定/ 使用的是绝对地址 如果不是/开头那么是在上一条workdir的路径的相对路径 |
USER | 指定执行用户 | 指定build或者启动的时候 用户 在RUN CMD ENTRYPONT执行的时候的用户 |
HEALTHCHECK | 健康检查 | 指定监测当前容器的健康监测的命令 基本上没用 因为很多时候 应用本身有健康监测机制 |
ONBUILD | 触发器 | 当存在ONBUILD关键字的镜像作为基础镜像的时候 当执行FROM完成之后 会执行 ONBUILD的命令 但是不影响当前镜像 用处也不怎么大 |
STOPSIGNAL | 发送信号量到宿主机 | 该STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。 |
SHELL | 指定执行脚本的shell | 指定RUN CMD ENTRYPOINT 执行命令的时候 使用的shell |
自定义centos7镜像。要求:
实现步骤:
① 定义父镜像:FROM centos:7
② 定义作者信息:MAINTAINER itheima [email protected]
③ 执行安装vim命令: RUN yum install -y vim
④ 定义默认的工作目录:WORKDIR /usr
⑤ 定义容器启动执行的命令:CMD /bin/bash
⑥ 通过dockerfile构建镜像:docker bulid –f dockerfile文件路径 –t 镜像名称:版本
docker build -f ./centos_dockerfile -t itheima_centos:1 .
定义dockerfile,发布springboot项目
实现步骤
① 定义父镜像:FROM java:8
② 定义作者信息:MAINTAINER itheima [email protected]
③ 将jar包添加到容器: ADD springboot.jar app.jar
④ 定义容器启动执行的命令:CMD java–jar app.jar
⑤ 通过dockerfile构建镜像:docker bulid –f dockerfile文件路径 –t 镜像名称:版本
文件编写完成执行命令构建:
docker build -f ./springboot_dockerfile -t app .
微服务架构的应用系统中一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,维护的工作量会很大。
Docker Compose是一个编排多容器分布式部署的工具,提供命令集管理容器化应用的完整开发周期,包括服务构建 ,启动和停止。使用步骤:
# Compose目前已经完全支持Linux、Mac OS和Windows,在我们安装Compose之前,需要先安装Docker。下面我 们以编译好的二进制包方式安装在Linux系统中。
curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 设置文件可执行权限
chmod +x /usr/local/bin/docker-compose
# 查看版本信息
docker-compose -version
# 二进制包方式安装的,删除二进制文件即可
rm /usr/local/bin/docker-compose
mkdir ~/docker-compose
cd ~/docker-compose
version: '3'
services:
nginx:
image: nginx
ports:
- 80:80
links:
- app
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
app:
image: app
expose:
- "8080"
mkdir -p ./nginx/conf.d
server {
listen 80;
access_log off;
location / {
proxy_pass http://app:8080;
}
}
docker-compose up
http://192.168.149.135/hello
Docker官方的Docker hub(https://hub.docker.com)是一个用于管理公共镜像的仓库,我们可以从上面拉取镜像 到本地,也可以把我们自己的镜像推送上去。但是,有时候我们的服务器无法访问互联网,或者你不希望将自己的镜 像放到公网当中,那么我们就需要搭建自己的私有仓库来存储和管理自己的镜像。
搭建参考《docker 私有仓库.md》