Docker基础教程
Posted IT飞牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker基础教程相关的知识,希望对你有一定的参考价值。
Docker基础教程
非常好的免费教程:狂神说视频教程地址
Docker是Go语言开发的;开源的;
Docker文档超级详细
文档地址:https://docs.docker.com/
仓库地址:https://hub.docker.com/
2016年发布第一版;
Docker能干嘛
比较DOcker和虚拟机技术的不同:
- 传统虚拟机,虚拟出一条贱贱;运行一个完整的操作系统,然年后在这个系统上安装运行软件
- 容器的应用直接运行在书主机的内容,容器是没有自己的内核的,也没有虚拟机虚拟我们的硬件,所以就请变了
- 每个容器间是互相隔离,每个容器内都有一个属于自己的文件系统,互不影响;
DevOps
应用更快速的交付和部署
传统:已退帮助文档,安装程序
Docker:打报警想法不测试,一键运行
更便捷的升级和扩缩容
使用Docker之后,我们部署应用就和搭积木一样
项目打包为一个镜像,扩展 服务器A, 服务器B
更简单的系统运维
在容器化之后,我们的开发,测试环境都是高度一致的
更高效的计算资源利用
Docker是内核级别的虚拟化,可以在一个物理机上可以运行很多的容器实例!服务器的性能可以被压榨到极致;
只要学不死,就往死力学;
Docker安装
Docker的基本组成
镜像(image):docker静香就好比是一个模板,可以通过这个末班赖床吉安容器服务,tomcat镜像>run>tomcat01容器(提供服务器),通过这个奖项可以创建多个容器(最终服务器运行或者项目运行就是在容器中的)
容器(conrainer):Docker利用容器技术,独立运行一个或者一个组应用,ton故宫镜像赖床建的。
启动 停止 清除
仓库(respository):
安装Docker
参考官方文档:https://docs.docker.com/engine/install/ubuntu/
设置docker镜像仓地址的时候可以改用淘宝镜像
1. 安装/升级Docker客户端 镜像加速器
推荐安装1.10.0以上版本的Docker客户端,参考文档docker-ce
2. 配置镜像加速器
针对Docker客户端版本大于 1.10.0
的用户
您可以通过修改daemon配置文件 /etc/docker/daemon.json
来使用加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
"registry-mirrors": ["https://j4z1a6if.mirror.aliyuncs.com"]
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker run运行原理图
底层原理
Docker是怎么工作的?
Docker是一个Client-Server结构的系统,Docker的守护进程运行在主机上,通过Socker从客户端访问;
DockerServer接收到Docker-Client的指令,就会执行这个命令;
Docker为什么快
-
Docker有更少的抽象层
-
docker利用的是书主机的内核,vm需要是guest os
-
所以说,新建一个容器的时候,docker不需要想虚拟机一样重新加载一个操作系统内核,避免引导,避免引导,虚拟机是加载guest os,分钟级别的,二docker是利用宿主机的操作系统吗,省略了这个复杂的过程,秒级!
之后学习完所有的命令,在回过头来看这段评论,就会很清晰!
Docker的常用命令
帮助命令
docker version
docker info //镜像和容器的数量
docker --help
帮助文档的地址:https://docs.docker.com/engine/reference/run/
镜像命令
docker images:查看本地所有的镜像
-a, --all 显示所有镜像
--digests
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print images using a Go template
--no-trunc Don't truncate output
-q, --quiet Only show image IDs
docker search:搜索命令
docker search mysql --filter=stars=3000
docker pull:下载镜像
docker pull 镜像名[:tag]
using default image latest 如果不写tag,默认是latest
指定版本下载:docker pull mysql:5.7
docker pull采用分层下载,好处是在下载同一个镜像的不同版本时,相同的层不用重复下载,极大的加速了下载的速度,减小下载镜像尺寸;
docker rmi 镜像名:删除时也只删除当前版本的文件
docker rmi -f 镜像id
docker rmi -f 镜像id1 镜像id2 镜像id3
docker rmi -f $(docker images -aq) 删除全部镜像
容器命令
有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
新建容器并启动
docker run [可选参数] image
--name 容器名字 comcat01 tomcat02,用来区分容器
-d 后台运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用)
-p 容器端口
容器端口
-P 随机指定端口
docker run -it centos /bin/bash 进入运行中的容器
exit 退出运行中的容器
查看运行中的容器
docker ps 查看运行中的容器
docker ps -a 查看所有容器
docker ps -n=2 查看最近创建的容器
删除容器
docker rm 容器id 删除指定容器
docker rm 容器id 容器id 删除指定多个容器
docker rm -f $(docker ps -aq) 删除全部容器
-f 强制操作
启动和停止容器的操作
docker start 容器id
docker restart 容器id
docker stop 容器id
docker kill 容器id
常用其他命令
后台启动容器
docker run -d centos 后台运行
查看日志
docker logs -f -t --tail 10 容器id 查看指定容器的10条日志
启动容器,并且自动打印日志
docker run -d centos /bin/bash -c "while true;do echo itren;sleep 1;done"
--tf 显示日志
--tail 显示条数
查看容器中进程信息
docker top 容器id
查看容器元信息
docker inspect 容器id
进入当前正在运行的容器
docker exec -it 容器it /bin/bash //方式1:进入容器后,开启新的终端
docker attach 容器id //方式2:进入容器正在执行的终端
作业
安装nginx
1、搜索镜像 docker search
2、拉取镜像 docker pull nginx
3、启动镜像 docker run -d --name ngixn01 -p 3344:80 nginx
4、查看启动的容器
端口暴露的概念
whereis nginx 查看nginx查看服务器目录
*/usr/share/nginx/html/index.html
路径是docker nginx虚拟主机中默认的代码路径;
思考问题:我们每次改nginx配置文件,都需要进入容器内部?我要是可以自容器外部提供一个映射路径,达到在容器修改文件名,容器内部就可以自动修改;
作业:docker来装一个tomcat
docker search tomcat --filter=stars=3000 搜索tomcat镜像
docker run -it --rm tomcat:9.0 启动后,用完就删(测试时用)
docker run -d -p 3355:8080 --name tomcat01 tomcat 启动容器
docker exec -it 73be /bin/bash 进入容器
tomcat的默认代码目录是/usr/local/tomcat/webapps
,
cp -r webapps.dist/* webapps 复制目录
-r 递归
思考问题,我们以后要部署项目,如果每次都要进入容器是不是十分麻烦?我要是可以在容器外部提供一个映射路径,webapps,我们在外部部署项目,就可以秀爱容器内部对应的文件;
作业:部署es+kibana
es 暴露的端口很多
es 十分的耗内存
es 的数据一般需要放置到安全目录!挂载
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:8.3.2
下载并运行
启动之后,发现整个linux非常卡,1.xG 1核2G
停止整个docker
docker stats 查看状态
测试下es是否成功了; curl localhost:9200
赶紧关闭,增加内存的限制
docker run -d --name elasticsearch03 --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xms512m" elasticsearch:8.3.2
可视化
portainer(先用这个)
Docker图形化界面管理工具!提供一个后端面板供我们操作!
docker search Portainer 搜索
docker run -d -p 8088:9000 \\
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
访问测试:
ip:8088
选择本地
登录后的界面
Rancher
commit镜像
docker commit 提交容器成为一个新的副本
docker commit -m ="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
实战测试
1、启动tomcat
docker run -it -p 8080:8080 tomcat
2、进入容器
docker exec -it 1892 /bin/bash
3、如果webapps目录文件为空,则拷贝webapps.dist内容到webapps中
cp -r webapps.dist/* webapps
4、退出容器
exit
5、提交镜像
docker commit -a="it飞牛" -m="add webapps app" 1892 tomcat02:1.0
6、再次查看生产的镜像
docker images
可以看到出现了tomcat02,以后我们就可以使用修改过后的镜像
运行成功后,页面如下:
如果想要保存当前同期的状态,我们可以使用commit,类似git commit
到了这里才算是入门的Docker!认真的吸收学习!
容器数据卷
docker的理念回顾
将应用和环境打包成一个镜像!
数据?如果输都在容器中,那么我们容器删除,数据就会丢失! **需求:数据可视持久化 **
MySql,容器删了,删库跑路!需求:MySql数据可以存储在本地!
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!
总结一句话:容器的持久化合同步操作!容器间也可以数据共享的!
使用数据卷
方式一:直接食用命令来挂载 -v
1、创建容器,并映射挂载
docker run -it -v /home/ceshi:/home centos /bin/bash
将容器中的home目录挂载到系统/home/ceshi目录上
2、查看容器信息
docker inspect 0e71
3、在容器内/home中新建文件,会自动同步到容器外的/home/ceshi 目录
在容器外/home/ceshi 目录创建文件也会同步到容器内的/home目录中。实现容器内外挂载目录完全同步;
容器内
容器外
实战:安装MySql
推荐一个mysql可视化操作软件:SQLyog
1、获取镜像
docker pull mysql:5.7
2、运行容器(配置musql密码:123123)
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=123123 \\
--name mysql01 \\
mysql:5.7
-d 后台运行
-v 挂载
-e 环境配置
3、启动容器信息
docker ps 看到mysql01已经启动
4、本地连接mysql是否启动成功(3310端口记得开放)
5、新建一个数据库first,外部/home/mysql/data中文件同步更新
mysql删除后,外部
/home/mysql
中的所有文件都会保留;这就实现了容器数据持久化功能;
具名和匿名挂载
上面还有一个指定路径挂载
匿名挂载
-v 容器内路径!
docker run -d -P --name nginx01 -v /etc/nginx nginx
不指定主机地址,-v后面直接写容器内路径
查看所有卷的情况
docker volume ls
这里发现,这种就是匿名挂载,我们在-v只写了容器内的路径
具名挂载(-v后面不加/开头):
docker run -d -P --name nginx03 -v juming-nginx:/etc/nginx nginx
查看所有卷的情况
docker volume ls
我们通过具名瓜子啊可以方便的找我们的一个卷,大多数情况在使用的具名挂载(推荐),尽量不要使用匿名挂载;
通过 -v 卷名:容器内路径
查看一下这个卷
所有的docker容器内的卷,没有指定目录的情况下都在/var/lib/docker/volumes/xxx/_data
查看卷信息
拓展:
通过 -v 容器内路径,ro、rw 改变读写权限
ro readonly:只读。只能通过宿主机来操作,容器内部是无法操作!
rw readwrite:可读可写
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
DockerFile
Dockerfile介绍
dockerfile使用来构建docker镜像的文件!命令参数脚本!
构建步骤:
- 编写一个dockerfile文件
- docker build构建成为一个镜像
- docker run运行镜像
- docker push发布镜像(DockerHub、阿里云镜像仓库)
例如我们看下centos在docker仓库上的镜像包长什么样:
打开centos对应的源码地址,也就是一个基础的dockerfile配置文件,里面配置了一些基本的包,好多功能时没有的,需要启动完镜像后,自己进入容器中自行创建;
常见场景是:centos+jdk+mysql+tomcat+redis这样的容器能力,这个时候就需要手动去安装centos意外的能力;
dockerfile指定容器卷名称
dockerfile就是用来构建docker镜像的构建文件!命令脚本!先体验下
通过这个脚本可以生成镜像,竟像是一层一层的,脚本一个个的命令,每个命令都是一层
1、在/home/中新建 docker-test-volume 目录,这里统一存放挂载的具名卷
2、新建一个dockerfile文件,代码如下:
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bash
3、docker build -f dockerfile -t itfeiniu/centos:1.0 .
4、执行完成后,可以看到已经新建一个镜像
5、运行镜像 itfeiniu/centos
docker run -it 镜像id /bin/bash
这个卷和外部一定是同步的,保存在默认的外部卷存储目录。
6、进入容器中volume01目录,新建一个container.txt文件
cd volume01
touch container.txt
7、退出容器,查看容器信息
docker inspect 容器id
可以看到外部挂载目录的具体地址
8、打开目录可以看到在容器内部创建的container.txt文件
这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!
假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!
DockerFile构建过程
基础知识
-
每个保留关键字(指令)都是必须是大写字母
-
执行从上到下顺序执行
-
#表示注释
-
每一个指令都会创建提交一个新的镜像层,并提交!
dockerfile是面向开发的,我们以后要发布项目,做镜像,就要编写dockerfile文件,这个文件十分简单!
Docker镜像逐渐成为企业交付的标准,必须要掌握;
Dockerfile:构建文件,定义了一切的步骤,源代码
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的和产品!
Docker容器:容器就是竞相运行起来提供服务器!
DockerFile的指令
FROM #基础镜像包,一切从这里开始构建 MAINTAINER #镜像是谁写的,姓名+邮箱 RUN #镜像构建的时候,需要运行的命令 ADD #步骤。tomcat镜像,这个tomcat压缩包!添加内容 WORKDIR #镜像的工作目录 VOLUME #挂载的目录 EXPOSE #保留端口配置 CMD #指定这个容器启动的时候需要运行的命令,只有最后一个会生效,可被替换; ENTRYPOINT #指定这个容器启动的时候需要运行的命令,可以追加命令 ONBUILD #当构建一个被继承,DockerFile这个时候就会运行ONBUILD的指定,触发指令; COPY #类似ADD,将我们文件拷贝到镜像中 ENV #构建的时候设置环境变量!
实战测试
Docker Hub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行的构建
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \\
org.label-schema.schema-version="1.0" \\
org.label-schema.name="CentOS Base Image" \\
org.label-schema.vendor="CentOS" \\
org.label-schema.license="GPLv2" \\
org.label-schema.build-date="20201113" \\
org.opencontainers.image.title="CentOS Base Image" \\
org.opencontainers.image.vendor="CentOS" \\
org.opencontainers.image.licenses="GPL-2.0-only" \\
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]
创建一个自己的centos
#编写dockerfile的文件:dockerfile-centos
FROM centos
MAINTAINER itfeiniu<wjb2007_nice@126.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH #指定进入容器后的工作目录
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash
#打包
docker build -f dockerfile-centos -t mycentos:1.0 .
#运行
docker run -it mycentos:1.0
运行后进入容器,可以看到当前工作目录就是`/usr/local`
这里在
yum -y install vim
时,可能会下载不下来,需要切换国内yum源;
docker history 查看镜像构建过程
使用docker history [镜像名|镜像id]查看镜像构建过程。
我们平时看到一个镜像,可以通过history命令来学习下怎么做的。
CMD和ENTRYPOINT区别
CMD #指定这个容器启动的时候需要运行的命令,只有最后一个会生效,可被替换;
ENTRYPOINT #指定这个容器启动的时候需要运行的命令,可以追加命令
创建一个dockerfile文件:dockerfile-cmd-test
FROM centos
CMD ["ls","-a"]
1、构建
docker build -f dockerfile-cmd-test -t cmdtest:1.0 .
2、执行
docker run 镜像id
执行完后,控制台打印出根目录所有目录,如下:
3、如果在docker run 3306的时候加上-l,并不会把-l追加成 ls -a -l命令来执行,因为CMD不支持追加,而是会用命令行中参数替换CMD中参数。所以页面报错:
4、使用ENTRYPOINT命令参数,docker-cmd-test代码修改如下:
FROM centos
ENTRYPOINT ["ls","-a"]
docker build -f dockerfile-cmd-test -t cmdtest02 .
docker run 镜像id -l
这样会将-l追加都命令后面,打印如下:
实战:Tomcat镜像
- 准备镜像文件,tomcat压缩包,jdk的压缩包;
下载下面两个包:
- 编写dockerfile文件,官方命名:
Dockerfile
touch readme.txt
vim
#Dockerfile代码如下:
FROM centos
MAINTAINER itfeiniu<wjb2007_nice@126.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u333-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.22.tar.gz /usr/local/
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_333
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /url/local/apache-tomcat-9.0.22/bin/logs/catalina.out
- 构建镜像
docker build -t diytomcat .
- 运行
docker run -d -p 9090:8080 --name itfeiniutomcat \\
-v /home/itfeiniu/build/tomcat/test:/usr/local/apache-tomcat-9.0.22/webapps/test \\
-v /home/itfeiniu/build/tomcat/tomcatlogs:/usr/local/apache-tomcat-9.0.22/logs \\
diytomcat
这里设置了两个数据卷挂载目录;
访问ip:9090路径,可以看到页面:
-
发布项目
由于设置了目录挂载,我们不需要进入容器发布项目,只要到容器外部的挂载目录发布项目源码就行;
#进入test目录
cd test
mkdir WEB.INF
cd WEB.INF
#创建web.xml,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
</web-app>
#创建index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>itfeiniu</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("--my test web logs--");
%>
</body>
</html>
然后访问 ip:9090/test/,就能正常看到页面
发布自己的镜像
DockerHub
- 地址 https://hub.docker.com 注册自己的账号!
- 确定这个账号可以登录
- 在我们服务器上提交自己的镜像
root@iZuf6ecwnj4s314sstbr31Z:~# docker login -h
Flag shorthand -h has been deprecated, please use --help
Usage: docker login [OPTIONS] [SERVER]
Log in to a Docker registry.
If no server is specified, the default is defined by the daemon.
Options:
-p, --password string Password
--password-stdin Take the password from stdin
-u, --username string Username
- 登录完成完毕后,就可以提交镜像
docker login -u 用户名
- docker push推送镜像到在线仓库
docker images #找到自己需要push的镜像包
docker push 镜像名:tag #直接push可能会出现重名,导致push失败
#如果push失败,采用tag重命名后,再push,如下
docker tag 4f02 6feel/tomcat:1.0
docker push 6feel/tomcat:1.0
push后结果如下:
root@iZuf6ecwnj4s314sstbr31Z:~# docker push 6feel/tomcat:1.0
The push refers to repository [docker.io/6feel/tomcat]
d1fb32b900f6: Pushed
576f6c7477f2: Pushed
2fac4279b55f: Pushed
74ddd0ec08fa: Mounted from library/centos
1.0: digest: sha256:6fb9136b3dcae5fb009938196d3893919a96107e56d442228dab10e507665d24 size: 1161
#提交的时候也是按照镜像的层级来进行提交的!
推送成功后,进入hubDocker仓库能看到刚推送的镜像
阿里云镜像服务
- 进入阿里云,搜素容器镜像服务,进入个人版
-
创建命名空间
-
创建镜像仓库
创建时选择本地仓库。
创建成功后,自动跳转到仓库的新本信息页面,里面有详细镜像推送和拉取教程。
基于前面我们已经打包6feel/tomcat镜像包,这里我们可以直接执行这
docker login --username=阿里云用户名 registry.cn-shanghai.aliyuncs.com
$ docker tag [ImageId] registry.cn-shanghai.aliyuncs.com/bilibili-feiniu/feiniu-test001:[镜像版本号]
$ docker push registry.cn-shanghai.aliyuncs.com/bilibili-feiniu/feiniu-test001:[镜像版本号]
推送成功后,控制台显示:
进入阿里云镜像版本,可以看到推送的镜像
以后,可以将自己的网站源码以及运行网站所需要的所有环境都push到远程。使用时只需要简单pull下来之后,就能把项目跑起来。
小结
数据卷容器
数据卷容器就是定义父容器,可以使用这个容器作为基础容器创建其他容器,其他容器挂载卷会指向父容器,一旦父容器有新增,子容器中的挂在卷也同步新增文件;
启动3个容器,通过--volumes-from创建数据卷容器;
docker run -it --name docker01 itfeiniu/centos 数据卷容器
docker run -it --name docker02 --volumes-from docker01 itfeiniu/centos
docker run -it --name docker03 --volumes-from docker01 itfeiniu/centos
可以看到在docker01容器volume01中创建的docker01文件,在docker02中的volume01目录中也同步出现了;如果docker01容器删除,不会删除docker02、docker03的数据;这里是一个拷贝的概念;
多个mysql同步数据!
docker run -d -p 3310:3306 \\
-v /etc/mysql/conf.d \\
-v /var/lib/mysql \\
-e MYSQL_ROOT_PASSWORD=123123 \\
--name mysql01 \\
mysql:5.7
docker run -d -p 3311:3306 \\
-e MYSQL_ROOT_PASSWORD=123123 \\
--name mysql02 \\
--volumes-from mysql01 \\
mysql:5.7
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止;
但是一旦你挂载到了本地,这个时候,本地的数据是不会删除的!
Docker网络
需要彻底理解Docker网络,才能学习下面的企业实战、Docker Compose、Ci流水线等,否则很难看懂。
#在学习Docker网络之前首先清空镜像和容器。
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)
理解Docker0
查看当前ip地址
ip addr
三个网络
问题,docker是如何处理容器网络访问的?
# 启动一个tomcat服务,创建容器1
docker run -d -P --name tomcat01 tomcat
#查看容器的内部网络地址 ip addr . 发现容器启动的时候会得到一个eth。查看容器ip也可使用docker inspect tomcat01。
可以看到容器的ip是127.17.0.2,尝试ping 127.17.0.2
,也执行成功了!
所有linux和内部的容器之间的网络是相通的。
上传容器对应创建的网卡如下图:
如果我们再次创建一个容器
docker run -d -P --name tomcat02 tomcat
#系统会再次创建一组网卡,网卡成对出现:426+427,428+429,
426是linux中的网卡,427是容器中的网卡,两个网卡通过evth-pair技术做了绑定就能进行网络连接,实现通信。
新建的这个容器2ip是172.17.0.3,如果我们进入容器2内部,去ping 容器1的ip,也是可以ping通的。
docker exec -it tomcat02 /bin/bash #进入容器2
apt-get update #刷新源列表
apt-get install inetutils-ping #安装ping命令
ping 172.17.0.2
#也可以这么执行
#返回到容器外部,执行下面代码:
docker exec -it tomcat ping 172.17.0.2
原理
- 我们每启动一个docker容器,docker就会给容器分配一个ip,并新建一组网卡,网卡成对出现。我们只要安装了docker,就会有一个网卡docker0桥接模式,使用的技术是evth-pair技术!
- 再启动一个容器测试,发现有多个了对网卡!
我们发现这个容器带拉网咖,都是一对对的
evth-pair就是一队的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
正因为有这个特性,evth-pair充当一个桥梁,连接各种虚拟网络设备的
OpenStac,Docker容器之间的链接,OVS的链接,都是使用evth-pair技术
容器和容器之间是可以ping通的!
小结:
Docker使用的是Linux的桥接,宿主机中是一个Docker容器的网桥docker0。
Docker中的所有的网络接口都是虚拟的,虚拟的转发效率高!(内网传递文件)
只要容器删除,对应的一对网桥就没了!
思考一个场景,我们编写了一个微服务,database url=ip;项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以按照名字来进行访问容器?
容器互联–link
docker exec -it tomcat02 ping tomcat01 #这样是联不通的
#下面我们使用--link来创建tomcat03
docker run -d -P --name tomcat03 --link tomcat02 tomcat
#用tomcat02 ping tomcat03:失败
#用tomcat03 ping tomcat02:成功
#首先安装ping命令
docker exec -it tomcat03 apt-get update
docker exec -it tomcat03 apt-get install inetutils-ping
#ping成功
docker exec -it tomcat03 ping tomcat02
查看网络
docker network ls
docker network --help
docker network inspect bridge
# 之前我们是用的tomcat03连接的tomcat02,下面我们看下tomcat03的网络信息,是如何连接tomcat02的。
docker inspect tomcat03
# 可以看到在HostConfig配置中,存在Links配置项,我们知道Host是配置本地域名跳转用的,到这里大致可以猜测到可能是在tomcat03中配置了Host文件,类似 172.17.0.3 -> tomcat02,这样的映射记录。
#为了验证,我们直接查看下tomcat03中的hosts文件
docker exec -it tomcat03 cat /etc/hosts
# 可以看到果然是有这样的配置
到这里我们知道ping tomcat03其实即使配置了tomcat03容器中的hosts文件,做了域名转发;那么刚刚在tomcat02中ping到tomcat03失败,也就可以解决。我们只要修改tomcat02中的host的文件,新增一条指向tomcat03的域名跳转。
#打开tomcat02中的hosts文件(vim如果没有安装的话,需要先安装)
docker exec -it tomcat02 vim /etc/hosts
#vim打开host之后,新增下面这条记录
172.17.0.4 tomcat03 f40398e290e3
#然后在执行ping命令,就通了
docker exec -it tomcat02 ping tomcat03
本质探究,–link其实就是在容器中新增一条hosts域名跳转
我们现在玩docker已经不建议使用–link
自定义网络,不适用docker0!
docker0问题:他不支持容器名连接访问!
进阶网络访问方式推荐是下面的自定义网络。
自定义网络
查看所有的docker网络
网络模式
bridge:桥接 docker(默认)
none:不配置网络
host:和宿主机共享网络
container:容器网络连通(用得好,局限很大)
测试
#开始前,清空所有容器。
docker rm -f $(docker ps -aq)
#查看下网络
ip addr
可以看到,这时只剩下lo、eth0、docker0三个网卡,其他evth-pair网卡都已经被删除。
#我们直接启动的命令--net bridge,而这个就是docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 --net bridge tomcat
#docker0特点,默认,域名不能访问 ,--link可以打通阻塞
#我们可以自定义一个网络
#--driver bridge
#--subnet 192.168.0.0/16
#--gateway 192.168.0.1
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
#下面我们通过mynet来创建容器
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat
#查看mynet关联信息,可以看到containers中已经新增了两个容器关联
#我们尝试在tomcat-net-01中ping向tomcat-net-02,是可以ping通的
docker exec -it tomcat-net-01 ping 192.168.0.3
直接ping 容器名也是通的
docker exec -it tomcat-net-01 ping tomcat-net-02
现在不适用--link也可以ping名字了!非常方便
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络!
好处!
假如我们以后有很多集群,有redis集群和mysql集群,可以分配两个完全不同的网络集群,互不干扰。
redis:不同的集群使用不同的网络,保证集群是安全和健康的
mysql:不同的集群使用不同的网络,保证集群是安全和健康的
网络连通
到这里,我们已经有两个网络,一个是默认的docker,和刚刚创建的mynet。
#在docker0下创建两个容器tomcat01、tomcat02。
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat02 tomcat
#如果我们想要跨网络访问,是ping不通的,例如在tomcat01 ping
向 tomcat-net-01
docker exec -it tomcat01 ping tomcat-net-01
#这是可能连通的
需要使用network connect命令,实现网络和容器的连通
# 连通tomcat01 -》 mynet
# 连通之后就是将 tomcat01 放到了mynet 网络下
# 这个其实就是,一个容器两个ip地址!最典型的案例就是:阿里云服务,公网ip,私网ip
docker network connect mynet tomcat01 #连通mynet网络和tomcat01容器
docker network inspect mynet #查看mynet网络
# 连通之后,可以看到mynet网络中已经有tomcat01这个容器的配置
# 到这里,我们已经可以在tomcat01中ping通tomcat-net-01+tomcat-net-02
root@iZuf6ecwnj4s314sstbr31Z:~# docker exec -it tomcat01 ping tomcat-net-01
PING tomcat-net-01 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: icmp_seq=0 ttl=64 time=0.164 ms
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.094 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.087 ms
^C--- tomcat-net-01 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.087/0.115/0.164/0.035 ms
root@iZuf6ecwnj4s314sstbr31Z:~# docker exec -it tomcat01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3): 56 data bytes
64 bytes from 192.168.0.3: icmp_seq=0 ttl=64 time=0.352 ms
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.111 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.079 ms
64 bytes from 192.168.0.3: icmp_seq=3 ttl=64 time=0.091 ms
结论:假设要瓜网络操作别人,就需要使用docker network connect连通!
实战:部署Redis集群
# 创建网卡
docker network create redis --subnet 172.38.0.0/16
创建完查看下网卡信息
root@iZuf6ecwnj4s314sstbr31Z:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
5d83611f8b61 bridge bridge local
7f559704cf2a redis bridge local
通过以下脚本创建六个Redis 的配置信息:
for port in $(seq 1 6); \\
do \\
mkdir -p /mydata/redis/node-$port/conf
touch /mydata/redis/node-$port/conf/redis.conf
cat << EOF >/mydata/redis/node-$port/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1$port
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
执行完脚本,我看到6个节点已经创建
下面启动6个Redis容器,设置对应的容器数据卷挂载,
#第1个Redis容器
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \\
-v /mydata/redis/node-1/data:/data \\
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第2个Redis容器
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \\
-v /mydata/redis/node-2/data:/data \\
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第3个Redis容器
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \\
-v /mydata/redis/node-3/data:/data \\
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第4个Redis容器
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \\
-v /mydata/redis/node-4/data:/data \\
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第5个Redis容器
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \\
-v /mydata/redis/node-5/data:/data \\
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第6个Redis容器
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \\
-v /mydata/redis/node-6/data:/data \\
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
或者通过脚本一次性启动6个Redis容器:
for port in $(seq 1 6); \\
do
docker run -p 637$port:6379 -p 1637$port:16379 --name redis-$port \\
-v /mydata/redis/node-$port/data:/data \\
-v /mydata/redis/node-$port/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.1$port redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \\
done
启动完成后,进入redis-1
docker exec -it redis-1 /bin/sh
进入redis-1容器后,创建集群,将1/2/3/4/5/6个redis容器都串联起来,高可用,负载均衡
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6
379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
连接成功,下面就可以操作集群了
#进入集群命令行模式
redis-cli -c
#查看集群信息
cluster info
#查看集群节点
cluster nodes
这里我们看出主从关系:
-
主:redis-2 -> 从:redis-6
-
主:redis-3 -> 从:redis-4
-
主:redis-1 -> 从:redis-5
设置一个值
set a b
#redis-3设置了a,那么redis-4中也会有a的备份
然后退出集群,去停止redis-3容器
# ctrl+p+q
docker stop redis-3
停止了redis-3之后,再次进入集群取值a,正常从机redis-4会返回b值;linux上Docker安装gogs私服亲测(详解)