Docker学习笔记
Docker笔记
docker学习大纲
- docker概述
- docker安装
- docker命令
- 镜像命令
- 容器命令
- 操作命令
- …
- docker镜像
- 容器数据卷
- dockerfile
- docker网络原理
- IDEA整合docker
- docker compose
- docker swarm
Docker概述
docker为什么出现?
一款产品:开发到上线,是两套环境
开发… 运维。问题:现在在我的电脑上可以运行,而版本更新,导致服务不可用。对于运维来说很麻烦,每个机器都要部署环境(集群Redis,ES,Hadoop)
所以我们希望发布一个项目时,带上所有环境打包
传统:开发jar,运维部署
现在:开发,打包,部署一套
docker思想来源于集装箱
核心思想–隔离
Docker历史
- 2010年,几个搞IT的年轻人,就在美国成立了一家公司dotCloud,做一些 pass的云计算服务,LXC 有关的容器技术
- 他们将自己的技术 (容器化技术) 命名 就是 Docker
- Docker 刚刚诞生的时候,没有引起行业的注意, dotCloud,就活不下去
- 2013年,Docker开源
- Docker越来越多的人发现了docker的优点,火了,Docker每个月都会更新一个版本
- 2014年4月9日,Docker1.0发布
- Docker为什么这么火?十分的轻巧
- 在容器技术出来之前,我们都是使用虚拟机技术
- 虚拟机:在windows中装一个Vmware,通过这个软件我们可以虚拟出来一台或者多台电脑,笨重。
- 虚拟机也是属于虚拟化技术,Docker 容器技术,也是一种 虚拟化技术
1 | vm ;1inux centos原生镜像(一个电脑!) 隔离,需要开启多个虚拟机!几个G几分钟 |
聊聊docker
Docker是基于Go语言开发的
Docker仓库:https://hub.docker.com/
Docker能干嘛
之前的虚拟化技术
- 虚拟机技术缺点:
- 资源占用多
- 冗余步骤多
- 启动慢
容器化技术
- docker和虚拟机比较
- 传统虚拟机,虚拟出一套硬件,运行一个完整的操作系统,然后在这个系统上运行软件
- 容器内的应用直接运行在宿主机的内核上,没有自己单独的内核,也没有虚拟我们的硬件,轻便
- 每个容器间相互隔离,每个容器内都有一个自己的文件系统 ,互不影响
DevOps(开发、运维)
应用更快的交付部署
- 传统运维:一堆帮助文档,安装程序
- docker:打报镜像发布测试,一键运行
更便捷的升级和扩缩容
- 使用docker后,部署应用就和搭积木一样
- 项目打包为一个镜像,服务器A性能瓶颈,直接一键在服务器B上运行做负载均衡
更简单的系统运维
- 在容器化之后,我们的开发、测试环境都是高度一致的
更高效的计算资源利用
- docker是内核级别的虚拟化,可以在一个物理机上运行很多的容器,极致压榨服务器性能
Docker安装
Docker基本组成
docker架构图
镜像(image):
docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,如Tomcat镜像=》run=》tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务或者项目就是运行在容器中)
容器(container):
- docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建
- 启动、停止、删除等基本命令
- 可以理解为一个建议的Linux系统
仓库(repository):
- 存放镜像的地方
- 分为私有仓库和公有仓库
- dockerhub、阿里云…都有容器服务器
安装Docker
环境准备
- Linux操作基础
- CentOS 7
- Xshell远程连接
环境查看
1 | 系统内核3.10以上 |
1 | 查看系统版本 |
安装
查看官方帮助文档
- 卸载旧版本
1 | sudo yum remove docker \ |
- 需要的安装包
1 | sudo yum install -y yum-utils |
- 设置镜像仓库
1 | sudo yum-config-manager \ |
- 更新yum软件包索引
1 | [root@Mir etc]# yum makecache fast |
- 安装docker
1 | sudo yum install docker-ce docker-ce-cli containerd.io # ce为社区版 ee企业版 |
- 启动docker
1 | systemctl start docker |
- 使用docker version查看是否安装成功
1 | docker version |
- 测试docker引擎
1 | sudo docker run hello-world |
- 查看下载的hello-world镜像
1 | [root@Mir etc]# docker images |
- 卸载docker
1 | 卸载依赖 |
阿里云镜像加速
- 找到容器镜像服务
- 配置
1 | sudo mkdir -p /etc/docker |
- 回顾hello-world流程
底层原理
docker是怎么工作的?
- Docker是一个Client - Server结构的系统,Docker的守护进程运行在主机上,通过socket从客户端访问
- DockerServer 接收到 Docker-Client 的指令,就会执行这个命令
Docker为什么比虚拟机快?
- docker有着比虚拟机更少的抽象层
- docker利用的是宿主机的内核,VM需要的是Guest OS
- 所以说,新建一个容器的时候,docker不需要想虚拟机一样重新加载一个操作系统内核,避免引导
- 虚拟机是加载Guest OS,分钟级别的,而docker 是利用 宿主机的操作系统,省略了这个复杂的过程,秒级
Docker常用命令
帮助命令
1 | docker version # 显示docker版本信息 |
镜像命令
- docker images 查看所有本地主机上的镜像
1 | [root@Mir etc]# docker images |
- docker search 搜索镜像
1 | [root@Mir etc]# docker search mysql |
1 | 可选参数 |
- docker pull 下载镜像
1 | 下载镜像 docker pull 镜像名[:tag] |
- 上面的Already exists体现了联合文件系统的优势,检查到不同版本镜像之间有相同文件则只下载差异部分
- docker rmi删除镜像
1 | docker rmi -f 容器id # 删除指定id镜像 |
容器命令
说明:我们有了镜像才可以创建容器,这里我们使用centOS测试学习
1 | docker pull centos |
新建容器并启动
1 | docker run [args] iamge |
1 | 启动并进入容器 |
列出所有运行中的容器
1 | [root@Mir /]# docker ps # 列出当前正在运行的容器 |
退出容器
1 | exit # 直接停止容器并退出 |
删除容器
1 | docker rm 容器id # 删除指定容器,不能删除正在运行的容器,强制删除用rm -f |
启动、停止容器
1 | docker start 容器id # 启动容器 |
常用其他命令
后台启动容器
1 | docker run -d 镜像名 |
查看日志
1 | docker logs -f -t --tail n 容器id # n是输出日志条数,f是format,t显示时间戳 |
查看容器中的进程信息
1 | docker top 容器id |
查看镜像元数据
1 | docker inspect 容器id |
进入当前正在运行的容器
1 | 我们通常都是以后台方式运行容器,需要进入容器修改配置 |
从容器内拷贝文件到主机上
1 | docker cp 容器id:容器内路径 目的主机路径 |
小结
1 | attach # 当前shell下attach连接指定运行镜像 |
练习
Docker安装Nginx
1 | 1、搜索镜像 search 建议去dockerhub搜索,可以查看帮助文档 |
思考:
- 我们每次改动nginx配置文件,都需要进入容器内部,十分的麻烦,如果能在容器外部提供一个映射路径,达到在容器外部修改文件名,容器内部就可以自动修改? ==》-v 数据卷技术
Tomcat部署
1 | 官方使用命令 |
思考:
- 以后要部署项目,如果每次都要进入容器十分麻烦,希望在容器外部提供一个映射路径,webapps,我们在外部部署项目,然后自动同步到内部就好了
可视化
portainer(暂时先使用)
Rancher(CI/CD再用)
什么是portainer?
- Docker图形化管理工具,提供一个后台面板供我们操作
1 | 安装并运行 |
- 选择本地
- 进入之后的面板
平时一般不使用可视化面板,可测试玩玩
Docker镜像讲解
镜像是什么?
- 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件。
- 所有的应用,直接打包docker镜像,就可以直接跑起来!
- 如何得到镜像:
- 从远程仓库下载
- 朋友拷贝给你
- 自己制作一个镜像 DockerFile
Docker镜像加载原理
UnionFS (联合文件系统)
我们下载镜像的时候看到的就是这个,类似于Git多次提交修改叠加
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。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镜像要采用这种分层的结构呢?
- 最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
- 查看镜像分层的方式可以通过 docker image inspect 命令!
1 | [root@Mir /]# docker image inspect redis:latest |
理解
- 所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
- 举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
- 该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
- 在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,这一点很重要。下图中举了一个简单的例子,每个镜像层包含 3个文件,而镜像包含了来自两个镜像层的 6 个文件。
- 上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
- 下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。
- 这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
- Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
- Linux 上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
- Docker在Windows 上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW。
- 下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点
- Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像顶部
- 这一层就是我们通常说的容器层,容器之下的都叫镜像层
- 可以将操作后的容器层和原本的镜像层重新打包为新的镜像
Commit镜像
1 | docker commit 提交容器成为一个新的副本 |
实战测试:
1 | 1.启动一个官网下载的默认Tomcat |
可见提交后镜像文件变大
因此我们可以通过commit保存当前容器的状态,类似于VMware的快照
容器数据卷
什么是容器数据卷?
docker的理念回顾
将应用和环境打包成一个镜像!
数据?如果数据都在容器中,那么我们容器删除,数据就会丢失! 需求:数据可以持久化
MySQL,容器删了,删库跑路! 需求:MySQL数据可以存储在本地!
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Liux上面!
总结:容器的持久化和同步操作,同时容器间可以绑定到同一个目录,实现数据共享
使用数据卷:
方式一:直接使用命令来挂载 -v (volume)
1 | docker run -it -v 主机目录:容器内目录 |
、
测试文件同步
停止容器后,在宿主机修改文件,再启动容器,文件仍然同步
总结:以后只需在本地修改即可同步到容器
实战:安装MySQL
思考:mysql容器如何将数据持久化存储,不能因为删除容器而导致数据丢失
1 | 获取镜像 |
假设将容器删除,挂载到本地的数据卷依旧没有消失,实现了容器数据持久化存储功能
具名和匿名挂载
1 | 匿名挂载 |
所有的docker容器卷在没有指定主机路径的情况下都是在 /var/lib/docker/volumes/xxx/_data
通过具名挂载可以方便的找到我们存储的卷,建议使用具名挂载
1 | 如何确定是具名挂载,匿名挂载,还是指定路径挂载 |
拓展
1 | 通过-v 容器内路径:ro或者rw 改变读写权限 |
初识DockerFile
方式二:
dockerfile就是用来构建docker镜像的构建文件,命令脚本
通过这个脚本可以生成镜像,每一行命令就相当于镜像的一层
1 | 创建一个dockerfile文件,名字可以随机,建议Dockerfile |
1 | [root@Mir docker_test_volume]# docker build -f /home/docker_test_volume/dockerfile -t yyj_centos:1.0 . |
启动自己构建的镜像
1 | [root@Mir docker_test_volume]# docker run -it yyj_centos:1.0 /bin/bash |
这种方式较为常用,通常会自己构建镜像
假设构建镜像时没有挂载卷,则需要手动挂载
数据卷容器
实现多MySQL同步数据
1 | 通过刚刚自己构建的镜像启动3个容器 |
总结:类似根域名服务器与镜像服务器的关系
实战:多个MySQL实现数据共享
1 | [root@Mir home]# docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 |
结论:容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用为止,但是一旦持久化存储到本地,即-v,则本地数据不会受影响
DcockerFile
dockerfile是用来构建docker镜像的文件,命令参数脚本
构建步骤:
编写一个dockerfile文件
docker build 构建成为一个镜像
docker run 运行镜像
docker push 发布镜像(DockerHub,阿里云镜像仓库)
查看官方命令
选择一个版本进入
很多官方镜像都是基础包,缺少很多功能,通常需要自己构建
DockerFile构建过程
基础知识:
- 每个保留关键字(指令)都必须是大写字母
- 自上而下执行
- #表示注释
- 每个指令都会创建提交一个新的镜像层
DockerFile是面向开发的,以后要发布项目,做镜像,就需要编写dockerfile文件
Docker镜像逐渐成为企业交付的标准,很重要
DockerFile:构建文件,定义了构建的步骤,源代码
DockerImages:通过DockerFile构建生成的容器,最终发布和运行的产品
Docker容器:运行镜像的服务器
DockerFile 的指令
1 | FROM # 基础镜像,一切从这里开始构建 |
实战测试
DockerHub中绝大多数镜像都是从这个基础镜像开始构建的(FROM scratch),然后配置需要的软件
创建一个自己的centOS
1 | 构建命令 |
关于命令末尾的点号:
在官方文档的Dockerfile reference的章节中有以下重点内容:
- 生成过程的第一件事是将整个文件构建镜像上下文(递归)发送到守护进程。
- ps:不要使用根目录 ,因为它会将硬盘驱动器的全部内容传输到 Docker 守护进程。
- 一个个指令是在Docker守护进程中运行,然后将每个指令的结果提交到新镜像,最终输出新镜像的ID,Docker守护进程会自动清理发送的构建镜像上下文。”
- 通过上述内容可以大致知道 镜像的构建是在Docker引擎(Docker守护进程)中完成的,在执行docker build命令后,本机会将Dockerfile文件所在路径下的所有文件打包上传给Docker引擎,由Docker引擎完成镜像的构建。
结论:点号是指镜像构建时打包上传到Docker引擎中的文件的目录,而不是本机目录
1 | 编写dockerfile文件 |
1 | 通过这个文件构建镜像 |
报错原因:自2022年1月31日起,CentOS团队从官方镜像中移除CentOS 8的所有包,但软件包仍在官方镜像上保留一段时间。现在被转移到https://vault.centos.org。如需继续运行旧CentOS 8,可以在/etc/yum.repos中更新repos.d,使用vault.centos.org代替mirror.centos.org
1 | 此处通过修改dockerfile解决,改为FROM centos:7 |
1 | 再通过这个文件构建镜像 |
前后对比
1 | 官方原始的centos7,命令不全,比如缺少vim,ifconfig,并且默认工作目录为/ |
1 | 我们在官方原始的centos7基础上构建的镜像,新添了vim和ifconfig命令 |
可以通过history命令列出镜像变更历史记录
1 | [root@Mir my_centos]# docker history 97891acdbe87 |
平时拿到镜像,可以研究一下别人是怎么做的
CMD和ENTRYPOINT 区别
1 | CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代 |
测试CMD
1 | [root@Mir my_centos]# cat cmdtest |
测试ENTRYPOINT
1 | [root@Mir my_centos]# cat entrypointtest |
DockerFile中很多相似命令,注意区分
实战:Tomcat镜像
- 准备镜像文件Tomcat的压缩包,JDK压缩包(Tomcat依赖于Java)
- 编写dockerfile文件 ,建议使用官方命名 Dockerfile ,build的时候会自动寻找这个文件,不需要 -f 指定
1 | FROM centos:7 |
3.构建镜像
1 | [root@Mir DIYTomcat]# docker build -t diytomcat |
4.启动镜像
1 | [root@Mir DIYTomcat]# docker run -d -p 9090:8080 --name yyjdiytomcat -v /home/yyj/build/tomcat/test:/usr/local/apache-tomcat-9.0.62/webapps/test -v /home/yyj/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.62/logs diytomcat |
5.访问测试
用curl命令测试
1 | [root@Mir DIYTomcat]# curl localhost:9090 |
浏览器测试:47.99.154.107:9090
6.发布项目(由于做了卷挂载,直接在本地编写项目发布就可以了,会自动同步到容器内)
1 | [root@Mir test]# pwd |
访问 47.99.154.107:9090/test
由于默认页面在 tomcat\webapps\ROOT目录下,自建项目为tomcat\webapps\test,访问时需要加上/test
发布自己的镜像
发布到DockerHub
- 地址https://hub.docker.com/注册自己的账号
- 确定该账号可以登陆
- 查看login命令帮助
1 | [root@Mir tomcatlogs]# docker login --help |
4.登录
1 | [root@Mir tomcatlogs]# docker login -u miryyj |
5.提交镜像,docker push
1 | docker tag命令 |
1 | 注意要push到DockerHub的镜像需要标记为:dockerhub用户名/镜像名:[tag] |
发布到阿里云镜像服务
- 进入阿里云容器镜像服务,创建个人实例
2.创建命名空间
3.创建镜像仓库
4.可查看基本信息以及操作指南
5.根据指南docker push
总结
Docker网络
理解Docker0
清空所有docker环境
1 | 删除所有容器 |
测试
1 | 用ip addr查看网络信息 |
问题:docker如何处理容器网络访问的?
1 | 启动一个Tomcat容器 |
Linux能不能ping通容器内部?
1 | [root@Mir ~]# ping 172.17.0.2 |
原理
- 每启动一个docker容器,docker就会给容器分配一个ip,只要安装了docker,就会有一个docker0网卡,与物理网卡桥接,使用evth-pair技术
再次测试ip addr
1 | [root@Mir ~]# ip addr |
再启动一个容器测试
1 | [root@Mir ~]# docker run -d -P --name tomcat02 tomcat |
发现创建的容器带来的网卡都是成对的
evth-pair 就是一对的虚拟设备接口,都是成对出现,一端连着协议,一端彼此相连
因为这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备的
OpenStac,Docker容器之间的连接,OVS的连接,都是使用evth-pair技术
测试容器之间能够ping通
1 | 在tomcat02(172.17.0.3)内ping tomcat01(172.17.0.2) |
网络模型图
tomcat01和tomcat02公用一个路由器docker0
所有的容器在不指定网络的情况下,都是通过docker0路由的,docker会给容器分配一个默认的可用IP
由/16可知该网络地址前16位为网络地址,后16位为主机地址,一个docker可以分配大约255*255的私有ip
总结
docker使用的是Linux的桥接,宿主机中是一个docker容器的网桥docker0
- docker中所有的网络接口都是虚拟的,转发效率高,相当于内网传输
- 只要容器删除,则对应一对网桥就被删除
–link
当项目重启时,ip换掉了,希望可以通过名字来访问容器
1 | 在tomcat01中直接ping tomcat02名字 |
解决方法
1 | 新建容器tomcat03,使用--link将tomcat02与tomcat网络相连 |
引入:docker network命令
1 | 查询帮助 |
其实tomcat03就是在其容器内部配置了tomcat02的信息
1 | 查看tomcat03的hosts配置 |
但是现在已经不建议使用–link了,我们需要的是自定义网络,不使用docker0
docker0的问题:不支持容器名连接访问
自定义网络
查看所有docker网络
1 | [root@Mir ~]# docker network ls |
网络模式
- bridge(默认):桥接,在docker上搭桥
- none:不配置网络
- host:和宿主机共享网络
- container:容器网络互通(用得少,局限性大)
测试
1 | 清空docker网络环境,删除所有容器 |
1 | 通常直接使用第一种启动方式,实际上省略了默认的参数 --net bridge |
自定义网络创建完成
1 | [root@Mir ~]# docker network inspect mynet |
测试
1 | 创建两个容器tomcat01 tomcat02 |
我们自定义的网络,docker已经帮我们维护好了对应的关系,建议使用这种方式
好处:假如希望搭建MySQL集群或者Redis集群,可以分别搭建一个MySQL网络和Redis网络,不同的集群使用不同的网络,而网络之间是隔离的,保证集群的安全(但二者之间也可以打通)
网络连通
docker network connect 命令
1 | 查看命令帮助 |
此小节网络结构背景图:
测试打通tomcat03到mynet
1 | [root@Mir ~]# docker network connect mynet tomcat03 |
连通之后直接把tomcat03加入mynet网络,一个容器,两个IP,同时处于mynet和docker0网络下
测试能否直接ping通:
1 | [root@Mir ~]# docker exec -it tomcat01 ping tomcat03 |
总结:docker内要跨网络连接别人,就必须要使用docker network connect 连通
实战:部署Redis集群
未完待续……