docker学习浅谈
前言:前段时间看到K8s这玩意儿,听说可以取代docker,docker经常听,但又不知道具体是什么实现原理。
不想看概念,直接操作?点这里:确定吗?
文章目录
1.什么是docker
官方:
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。Docker 容器通过 Docker 镜像来创建。容器与镜像的关系类似于面向对象编程中的对象与类。
大自然中有一个养殖场。(主机系统,如windows、linux等)
docker可以理解为养殖场,里面有很多不同种类的动物。(docker服务:守护进程)
那么,养殖场的环境,也就是大自然的环境,即生态系统,生活着动物。(docker与服务器系统共用内核)
环境:水、天空、陆地。(容器中的 环境)
动物:鱼、鸟、猪。(容器中的 服务)
围笼:鱼塘、猪圈。(容器的 隔离性)
鸡为什么是鸡:界、门、纲、目、科、属、种,中的种。(镜像)
生态系统:不同种类的动物通过大自然空间进行共生、捕食、竞争。(容器间的 通信)
2.了解docker几个要点
2.1docker服务
一个容器引擎,可以通过镜像(image)生产相应的容器(container)。
比如我们创建虚拟机,需要用到一个工具作为承载,在工具上面创建虚拟机,docker就像virtual box、virtual vwarm等工具。
2.2镜像
一个只读的文件,用于创建出可执行的容器。
就像windows镜像文件,我们需要通过该镜像刷入/解压到某个存储空间,windows系统才能被操作。
例子:mysql镜像、nginx镜像等等。
镜像从何而来?镜像仓库(他建)、自己创建。
2.3容器
一个由镜像创建出来的虚拟环境服务,可以理解成有指定功能的虚拟机,拥有特定的环境,以及特定的服务。
体积小:容器的环境,仅仅是只够运行该容器所拥有的服务的环境,尽量去除多余的环境,以防资源浪费。
开放性:能够在所有主流Linux版本、Windows以及包括VM、裸机服务器和云在内的任何基础设施上运行。
可靠性:Docker赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。
2.4镜像与容器
镜像与容器的关系,就像java中类与实例对象的关系。
镜像可以生产多个容器,只要主机端口不同即可。就像类可以实例化成多个实例对象,只要对象的引用不同即可。
2.5通信
四种方式:
- 通过容器ip访问
- 通过宿主ip:port访问
- 通过–link参数建立连接(不推荐,不研究)
- 自定义(推荐)
后续4.5再拓展
2.6镜像仓库
集中存放镜像的地方。方便与后续的镜像拉取与上传,便于对镜像的集中管理。
- 公共中央仓库:如官方的Docker Hub。
- 私有仓库:可以是私人的、公司的、企业的。
3.浅浅了解一下原理
3.1组成架构
Docker是一个C/S结构的应用程序。主要包括客户端、服务端(守护进程)、命令行接口。
客户端:Docker Client,用户与Docker进行交互的主要方式,用户通过客户端发送服务命令给守护进程。
守护进程:Docker Daemon,接收客户端的命令,进行管理对象,对象包括镜像、容器、网络等。
命令行接口:Command Line Interface,将用户的命令转化为Restful Api形式,发送给守护进程。
3.2启动原理
通过主机系统创建Docker Daemon守护进程,以守护进程作为引擎,读取镜像并构建对应的容器、执行容器。
Docker容器技术和虚拟机技术都属于虚拟机技术,网络上也常有人将它们进行对比,下面也根据它们的区别来促进我们对Docker的理解。
Docker与虚拟机的启动结构图解:
Docker结构:
英文 解释 Infrastructure/Server 基础设施,可以理解成硬件设备,承载操作系统的设备。如个人电脑、服务器等 Host OS 主机操作系统,如Windows、Linux、Macos等 Docker Daemon Docker守护进程,属于操作系统的一个服务进程,用于管理容器。 bins/libs 镜像依赖集,为精简而生,是容器的必要依赖;容器不必要的依赖不会集合于此 App 拥有业务逻辑的应用。容器中的app是相互隔离的(以后再提及namespace)
虚拟机结构:
英文 解释 Infrastructure/Server 基础设施,可以理解成硬件设备,承载操作系统的设备。如个人电脑、服务器等 Host OS 主机机器,即操作系统,如Windows、Linux、Macos等 Hypervisor 虚拟监视器vmm,是用来建立与执行虚拟机器的软件,如Vbox,VMware等 Guest OS 客体机器,即虚拟机,虚拟系统 bins/libs 虚拟机系统的依赖集,以及应用所必需的依赖集。 App 应用程序。其隔离性是以虚拟机系统为粒度。
细心发现,Docker结构与虚拟机结构中,后者多了一层客机系统Guest OS(即虚拟机)。正是因为这一层,相对于Docker,虚拟机在启动方面(慢)、空间占用方面(大)、资源占用(多)方面产生了劣势。
相同点:
- 基于基础设施的主机机器。
- 基于虚拟化技术。
不同点:
区别 虚拟机 Docker 资源占用 总是会包含完整的系统,开销大 需要什么环境提供什么环境,不占多余资源 依赖集 应用需要在虚拟系统的基础上运行 容器共用主机系统的内核,供容器的应用所需的依赖 隔离粒度 系统之间 容器之间 稳定性 不存在服务进程间的致命影响 容器有一定权限访问系统内核,存在一定的系统威胁
3.3LXC与AUFS(待详解,另写)
Dockers=LXC+AUFS[^3]
LXC:namespace、cgroup ------LXC负责资源管理
AUFS:Advanced UnionFS ------AUFS负责镜像管理
3.4Docker容器语言(待考证,另写)
go语言,偶然从某本书得知go语言有粒度小于线程的运行行为,叫做协程。协程独立栈,共用堆。不知道是否因为这,Docker在容器隔离方面和利用资源有着一定的优势。(待考证)
4.动手
4.1安装docker
安装前,先卸载,以免以前安装过,造成依赖冲突:
apt-get remove docker docker-engine docker.io containerd runc
①习惯性更新索引:
sudo apt-get update
②安装以下依赖,使得apt可以使用https链接:
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
③添加Docker官方的GPG密钥:
ubuntu使用如下:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
debian\deepin使用如下:
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
④设置软件源仓库:
ubuntu使用如下:
add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
debian/deepin使用如下:
add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/debian $(lsb_release -cs) stable"
如果报错:
could not find a distribution template for Deepin/apricot
就手动修改软件源地址:
sudo vim /etc/apt/sources.list #添加以下 deb [arch=amd64] https://download.docker.com/linux/debian stretch stable
⑤更新索引:
sudo apt-get update
⑥安装docker
sudo apt-get install docker-ce docker-ce-cli containerd.io
docker-ce:引擎
docker-ce-cli:命令行,将用户的命令转化为Restful Api形式,发送给守护进程。
containerd.io:守护进程
查看版本:
docker version
开机自启:
systemctl enable docker
⑦更改镜像仓库位置,提速:
sudo vim /etc/docker/daemon.json #添加以下: { "registry-mirrors": ["https://registry.docker-cn.com"] }
重启:
service docker restart
4.2基本指令
------------------------------程序员常用----------------------------------
docker search xxx 搜索xxx镜像
docker pull xxx:version 从镜像库拉取容器镜像,如果不加version,将会拉取最新latest版本
docker ps 查看正在运行的容器列表(可以看到容器ID,所映射的端口号等等)
docker ps -a 查看所有的容器(不管是否运行都能看到)
docker start/stop CONTAINER ID 开始/停止容器(CONTAINER ID 是容器的ID)
docker rm CONTAINER ID 删除容器
docker kill CONTAINER ID 直接关闭容器
-------------------------------运维大佬常用--------------------------------
docker build -t [tag]:[version] [path] 构建镜像
docker run -itd -p [hostPort]:[containerPort] --name [containerName] [tag]:[version] 创建容器
docker network create [yourNetName] 自定义网络
docker network inspect [yourNetName] 查看自定义网络信息
docker logs -f [containerName] 查看容器日志
docker inspect 查看容器信息
-------------------------------操作docker服务-----------------------------
systemctl start/stop docker 运行/停止 docker 服务
systemctl enable docker 使 docker 开机自启
4.3运行容器
小建议:为避免根路径存储空间不足,建议将docker数据存储位置改成磁盘空间大的位置。如何修改?点击直达
下面以Mysql服务作为例子:
①拉取镜像:
docker pull mysql
②查看镜像,检查是否已下载:
docker images
③运行容器:
docker run -itd --name mysql_t -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql
④查看运行状态:
docker ps
若启动失败,通过
docker ps
看不到该容器的数据,所以我们使用docker ps -a
查看所有容器若容器已经确定被创建,那可以使用
docker logs -f [容器命名]
查看日志,根据实际情况修复
⑤测试容器服务:
检查主机端口是否被映射
sudo netstat -ntulp | grep 3306
使用navicat测试连接:
如果连接失败,很有可能是mysql配置root无法远程登录,解决方法:参考以下第⑥点。
⑥进入容器环境:docker exec -it [容器名] bash
docker exec -it mysql_t bash
配置root可以远程登录:
mysql -uroot; > use mysql; > select host,user from user; > update user set host = '%' where user = 'root'; > flush privileges;
随即可使用navicat登录成功。
运行容器总结:
docker search [镜像名称] #搜索镜像
docker pull [镜像名称]:[版本号] #拉取镜像
docker images #查看本地镜像
docker run -itd --name [容器名] -p [主机port]:[容器内port] [镜像名称] #通过镜像生产容器。-itd后台运行 --name容器名,其他参数自查
docker ps #查看正在运行的容器,查看所有容器需要加参数 -a
dicker logs -t [容器名] #查看容器日志
docker exec -it [容器名] bash #进入容器环境
4.4制作项目镜像
以简单的springboot项目为例。
方式一:使用Dockerfile
①使用maven打包好项目:如springboot-demo.jar
正常情况下打包项目时,注意maven加打包插件:
②将jar包丢进服务器,并同目录下创建Dockerfile文件
touch Dockerfile
③自定义Dockerfile参数:
vim Dockerfile
#容器基础环境 FROM java:8 #容器内,即项目的工作空间 WORKDIR /root/local/ #将本地jar包丢进容器内 COPY springboot-demo.jar /root/local/demo.jar #暴露容器端口,即jar项目的请求端口 EXPOSE 6688 #容器内,执行指令 ENTRYPOINT ["java", "-jar", "/root/local/demo.jar"]
④构建镜像
docker build -t springboot-demo:2.0 . # 注意最后的点'.',代表当前目录 # -t 即 --tag 标签的意思
⑤运行镜像创建容器myfirstDemo
docker run -itd --name myfirstDemo -p 6688:6688 springboot-demo:2.0
⑥查看日志,检查是否启动成功
docker logs -f myfirstDemo
⑦访问容器
//TODO 出打包镜像参数详情文档
方式二:使用maven插件直接生成镜像
较为复杂,在此引用网上比较易懂的教程:使用 Docker 部署 Spring Boot
需要注意一点便是试用maven构建镜像的环境,也是需要安装docker服务的,不然会出现类似错误:
Failed to execute goal com.spotify:docker-maven-plugin:0.4.12:build (default-cli) on project springboot-demo: Exception caught
4.5容器间通信
查看各个容器的ip:
docker inspect -f '{{.Name}} ->>>>>>>>> {{.NetworkSettings.IPAddress }} :::::::: {{.NetworkSettings.Ports}}' $(docker ps -aq)
#/mysql_t ->>>>>>>>>>>>>>>>> \t172.17.0.3 :::::::: map[3306/tcp:[{0.0.0.0 3306}]]
容器名称 容器ip 容器服务的端口:主机映射端口
先记住以下信息:
方式一:通过容器ip访问
①现在我们有两个容器了,一个是myfirstDemo,一个是mysql_t。
②myfirstDemo项目需要连接mysql
通过上述
docker inspect
指令,我们可以找出mysql_t的ip信息:172.17.0.3:3306那么可以在配置文件中进行配置:
③重新打包springboot项目并生成镜像,然后运行容器:如步骤4.4制作项目镜像
④测试:
方式二:通过宿主ip:port访问
①宿主ip可以理解成容器中的网关,即172.17.0.1。
②通过
docker inspect
指令,我们可以找出mysql_t的端口3306是映射到主机的3306端口那么可以在配置文件中进行配置:
③重新打包springboot项目并生成镜像,然后运行容器:如步骤4.4制作项目镜像
④测试:
小结:方式一和方式二其实原理一样的,方式一主要是直接请求容器,方式二主要是通过主机作为网关请求容器。如果要从中选择一种通信方式,那当然是选择后者,因为通过网关可以无视容器自身ip的变化,只需要关心端口映射。
4.6自定义网络
刚开始了解自定义网络的时候,都会有一个疑惑,为什么不直接用上述的通过宿主ip:port访问的方式就好了,何必多此一举自定义网络?那现在先摁住这个疑惑,进行学习。点击直接看答案
①创建桥段
docker network create xxxItemNet
②查看docker网络
docker network ls
③创建容器时用
--network [defind_name]
注明使用的桥段docker run -d --name xxxItemModules_1 -p 6677:6688 --network xxxItemNet springboot-demo:4.0
docker run -d --name mysql_3 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 --network xxxItemNet centos/mysql-57-centos7
④查看网络
docker network inspect xxxItemNet
⑤进入容器xxxItemModules_1,请求另一容器mysql_3
docker exec -it xxxItemModules_1 bash
加强测试:
创建多几个容器
docker run -d --name xxxItemModules_2 --network xxxItemNet springboot-demo:4.0
docker run -d --name xxxItemModules4 --network xxxItemNet springboot-demo:4.0
docker network create mynet docker run -d --name xxxItemModules5 -p 9999:6688 --network mynet springboot-demo:4.0
细心的靓仔会发现上面有一些指令没有配置参数-p进行映射到主机端口,这就看大家的业务需求了。
现在我们已经有了四个容器:
容器名称 网段 备注 xxxItemModules_1 xxxItemNet 有主机端口映射 xxxItemModules_2 xxxItemNet 没有主机端口映射 xxxItemModules4 xxxItemNet 注意容器名没有下划线 xxxItemModules5 mynet 新网段 mysql_3 xxxItemNet 这是mysql 答:使用自定义网络,只需要记住容器的名称或者自定义网络别名,就可以无视自身ip,也不需要通过主机作为网关转发,容器间相互请求只需要通过容器名称或者自定义网络别名,这就很方便。并且如果是多个集群,那可以通过自定义网段来区分不同的集群,使得整个网络格局清晰起来。
4.7自定义文件存储路径
由于根路径/空间小,如果docker数据占满了空间,会导致服务器出现各种问题,所以建议将默认路径改为磁盘空间比较大的路径。
#查看默认路径(默认路径一般是/var/lib/docker/)
docker info |grep -i root
#停止docker服务
systemctl stop docker
#使用增量备份指令rsync,将docker原有数据迁移至指定文件夹:rsync -参数 源 目标
rsync -avzP /var/lib/docker/ /data/docker
#修改docker配置文件/etc/docker/daemon.json
vim /etc/docker/daemon.json
#添加
{
"data-root": "/data/docker"
}
#重启docker
systemctl restart docker
#再次查看数据存储路径
docker info |grep -i root
#删除原路径下的数据
rm -rf /var/lib/docker/
4.8自定义网络实验(附加)
于本文4.7中,我们创建了6个容器。
docker run -d --name xxxItemModules_1 -p 6677:6688 --network xxxItemNet springboot-demo:4.0
docker run -d --name xxxItemModules_2 --network xxxItemNet springboot-demo:4.0
docker run -d --name xxxItemModules4 --network xxxItemNet springboot-demo:4.0
docker run -d --name xxxItemModules5 -p 9999:6688 --network mynet springboot-demo:4.0
docker run -d --name mysql_3 -e MYSQL_ROOT_PASSWORD=123456 --network xxxItemNet centos/mysql-57-centos7
docker run -d --name mysql4 -e MYSQL_ROOT_PASSWORD=123456 --network xxxItemNet centos/mysql-57-centos7
容器名称 | 网段 | 备注 |
---|---|---|
xxxItemModules_1 | xxxItemNet | 有主机端口映射 |
xxxItemModules_2 | xxxItemNet | 没有主机端口映射 |
xxxItemModules4 | xxxItemNet | 没有主机端口映射,容器名没有下划线 |
xxxItemModules5 | mynet | 新网段 |
mysql_3 | xxxItemNet | 这是mysql,容器名有下划线哇 |
mysql4 | xxxItemNet | 这是mysql,容器名无下划线哇 |
实验一:容器名称存在下划线
①进入容器xxxItemModules_1:
docker exec -it xxxItemModules_1 bash
②ping容器xxxItemModules_2
ping xxxItemModules_2
root@d366df81aedf:~/local# ping xxxItemModules_2
③请求容器xxxItemModules_2的接口
curl http://xxxItemModules_2:6688/demo/getWorld
root@d366df81aedf:~/local# curl http://xxxItemModules_2:6688/demo/getWorld
报错了,打开日志看看
docker logs -f xxxItemModules_2
④请求容器xxxItemModules_4的接口
curl http://xxxItemModules4:6688/demo/getData
root@d366df81aedf:~/local# curl http://xxxItemModules4:6688/demo/getData
实验结果:容器别名也是容器的网络名,虽然可以ping通,但如果存在下划线,那将会出现请求出错的情况。
实验二:创建容器时不映射端口,可以容器间相互请求吗?
#如:docker run -d --name xxxItemModules4 --network xxxItemNet springboot-demo:4.0
①进入容器xxxItemModules_1:
docker exec -it xxxItemModules_1 bash
②请求容器xxxItemModules4的接口
curl http://172.20.0.5:6688/demo/getData
root@d366df81aedf:~/local# curl http://172.20.0.5:6688/demo/getData
实验结果:创建容器时,不映射端口,容器之间是可以网段内通过ip进行数据交互的。
实验三:不同网段,容器之间数据可以互通?
①进入容器xxxItemModules_1:
docker exec -it xxxItemModules_1 bash
②请求容器xxxItemModules5:
ping xxxItemModules5
实验结果:不同网段,容器之间数据不可以可以互通(-p不是暴露端口的意思,暴露端口的操作已经在Dockerfile进行了)
实验四:项目连接数据库,数据库容器命名没有下划线(即域名/网络名称没有下划线),是可以的。
①新建一个容器spring1,项目配置如下:
docker run -d --name spring1 --network xxxItemNet sp:10
②查看容器spring1的启动日志:
docker logs spring1
实验结果:项目连接数据库,数据库容器命名没有下划线(即域名/网络名称没有下划线),是可以的。
实验五:项目连接数据库,需要注意容器名称(即域名/网络名称)的下划线吗?
①新建一个容器spring2,项目配置如下:
docker run -d --name spring2 --network xxxItemNet sp:11
②查看容器spring2的启动日志:
docker logs spring2
③不是很相信成功了,那请求一次数据看看
进入容器xxxItemModules_1:
docker exec -it xxxItemModules_1 bash
请求容器spring2的接口
curl http://spring2:6688/demo/getData
root@d366df81aedf:~/local# curl http://spring2:6688/demo/getData
数据不对劲,去看看日志:
docker logs spring2
实验结果:项目连接数据库,不需要注意容器名称(即域名/网络名称)的下划线,也能连接
5.1结语
学习docker是为了更容易上手k8s。
docker的使用远不止上述那么简单,主要还是对容器中服务的数据存储路径的熟悉和项目路径的可读写权限分配(就比如项目中的日志路径,一般指定在主机路径,方便查找),其次就是容器间数据交互的通信搭建。
5.2结语2
有没有一种可能,其实不用那么麻烦总是使用docker xxx
指令去搞上面的所有操作。^__=
有的,有很多docker面板,自己百度。
上面不使用面板,只不过是为了让读者更熟悉docker的指令操作,一定程度上加强对docker原理的理解。
author:孰知宇某
date:2022-04-24 23:22:54
blog:https://blog.csdn.net/weixin_43548748
参考文献:
[1] 苦逼运维.Docker容器 https://www.cnblogs.com/diantong/p/11498649.html
[2]水浅之.deepin安装docker https://blog.csdn.net/weixin_38331049/article/details/120026337
[3]架构师-尼恩.Docker原理 https://blog.csdn.net/crazymakercircle/article/details/120747767
[4]百度.百度搜索引擎 https://www.baidu.com