本篇是Docker学习笔记,基本上可以深入了解了Docker!
文章目录
Docker概述
Docker为什么会出现?
如何让你的项目带上环境在别人的电脑上也能运行?
如何避免超级麻烦的各种环境的配置?
如何跨平台开发?
Docker的出现解决了以上难题!
实现步骤:
- 本地开发项目
- 打包项目带上环境(镜像)
- 发布到Docker仓库:商店
- 别人下载我们发布的镜像
- 直接运行即可
Docker的思想:
Docker的图标是一个鲸鱼上面有很多集装箱,所以Docker的思想来源于集装箱。
隔离:打包装箱,每个箱子是互相隔离的。(不用担心端口冲突环境冲突等!)
Docker通过隔离机制,将服务器利用到机制。
Docker网址:
Docker历史
未记录
Docker能干嘛
容器化技术
- 容器内的应用直接运行在宿主机的内核上,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了。
- 每个容器间是相互隔离的,每个容器内部都有一个属于自己的文件系统,互不影响。
DevOps
DevOps即开发与运维
DevOps有以下特点:
1. 应用更快速的交付和部署
传统:一堆帮助文档,安装程序。
Docker:打包镜像发布测试,一键运行。
2. 更便捷的升级和扩缩容
使用了Docker之后,我们部署应用就和搭积木一样。
如果我们将一个项目打包为一个镜像,比如说服务器A上的项目出现问题了,我们可以在服务器上B直接运行镜像,进行水平扩展。
3. 更简单的系统运维系统
在容器化之后,我们的开发,测试环境都是高度一致的。
不会出现在我的电脑上能跑,在你的电脑上就不能用了的问题。
4. 更高效的计算资源利用
使用Docker可以实现1核2g的服务器 ,可以同时运行几十个tomcat!!
Docker是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例。
服务器的性能可以被发挥到极致!
Docker安装
Docker架构
镜像(image):
docker镜像就好比是一个模板,可以通过这个模板来创建容器服务。
比如:
tomcat镜像–>run—>tomcat01容器(提供服务器)
通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
容器(container):
Docker利用容器技术,独立运行一个或者一个组应用 ,通过镜像来创建容易。
容器基本命令包括:启动,停止,删除。
目前就可以把这个容器理解为就是一个简易的linux系统。
仓库(repository):
仓库就是存放镜像的地方,仓库分为公有仓库和私有仓库。
-
Docker Hub(默认是国外的)
-
像阿里云之类的大型企业都有容器服务器(配置镜像加速)
安装Docker
环境查看
[root@zhenghaoyun ~]$ uname -r #查看内核版本
3.10.0-1062.18.1.el7.x86_64
[root@zhenghaoyun ~]$ cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
安装
可以查看帮助文档
# 1.卸载旧版本
$ yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 2.需要的安装包
$ yum install -y yum-utils
# 3.设置镜像仓库
$ yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo # 默认是国外的!很慢!
$ yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 阿里云镜像 快
# 更新yum软件包索引
$ yum makecache fast
# 4.安装docker docker-ce 社区版 ee 企业版
$ yum install docker-ce docker-ce-cli containerd.io
# 5.启动docker
$ systemctl start docker
# 6.使用docker version查看是否安装成功
$ docker version
# 7.使用hello owrld测试
$ docker run hello-world
# 判断images是否下载成功,可以看到hello-world镜像。
$ docker images
卸载
# 卸载依赖
$ yum remove docker-ce docker-ce-cli containerd.io
# 删除资源
$ rm -rf /var/lib/docker
# /var/lib/docker docker的默认工作路径
阿里云镜像加速
登录阿里云找到容器服务
配置使用
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://owqdaahd.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker run流程
run的流程图
Docker底层原理
Docker是一个Client -Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问!
DockerServer接收到DockerClient的信息,就会执行这条命令。
Docker为什么比VM块?
- Docker有比虚拟机更少的抽象层
- Docker利用的是宿主机的内核,vm是需要Guest OS(需要再搭建一个环境)
所以说,新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免了引导过程,虚拟机是加载Guest OS,是分钟级别的。而Docker是利用宿主机的操作系统,省略了这个复杂的过程,是秒级的!
Docker命令
帮助命令
docker version # 显示docker版本信息
docker info # 显示docker的的系统信息,包括镜像和容器的数量
docker 命令 --help # 帮助命令
不会的时候就查帮助文档地址吧!
镜像命令
docker images查看本机所有镜像
[root@zhenghaoyun ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 4 months ago 13.3kB
名词解释:
- REPOSITORY 镜像的仓库源
- TAG 镜像的标签
- IMAGE ID 镜像的id
- CREATED 镜像的创建时间
- SIZE 镜像的大小
参数解释:
-a, --all # 列出所有镜像
-q, --quiet # 列出所有镜像id,一般-ap连用
[root@zhenghaoyun ~]$ docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 4 months ago 13.3kB
[root@zhenghaoyun ~]$ docker images -aq
bf756fb1ae65
docker search搜索镜像
可选项:通过**过滤
例:docker search mysql --filter=stars=3000
搜索出来的镜像就是STARS大于3000的
docker pull下载镜像
# 下载镜像 docker pull 镜像名[:tag]
[root@zhenghaoyun ~]$ docker pull mysql
Using default tag: latest # 如果不写[tag]默认就是latest
latest: Pulling from library/mysql # 分层下载. docker image核心 联合文件系统
5b54d594fba7: Pull complete
07e7d6a8a868: Pull complete
abd946892310: Pull complete
dd8f4d07efa5: Pull complete
076d396a6205: Pull complete
cf6b2b93048f: Pull complete
530904b4a8b7: Pull complete
fb1e55059a95: Pull complete
4bd29a0dcde8: Pull complete
b94a001c6ec7: Pull complete
cb77cbeb422b: Pull complete
2a35cdbd42cc: Pull complete
Digest: sha256:dc255ca50a42b3589197000b1f9bab2b4e010158d1a9f56c3db6ee145506f625 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
# 等价
docker pull mysql
docker pull docker.io/library/mysql:latest
# 指定版本下载
[root@zhenghaoyun ~]$ docker pull mysql:5.7
5.7: Pulling from library/mysql
5b54d594fba7: Already exists # 联合文件系统特性,已存在的直接复用 Already exists表示已经存在
07e7d6a8a868: Already exists
abd946892310: Already exists
dd8f4d07efa5: Already exists
076d396a6205: Already exists
cf6b2b93048f: Already exists
530904b4a8b7: Already exists
a37958cbebcf: Pull complete
04960017f638: Pull complete
e1285def0d2a: Pull complete
670cb3a9678e: Pull complete
Digest: sha256:e4d39b85118358ffef6adc5e8c7d00e49d20b25597e6ffdc994696f10e3dc8e2
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
docker rmi删除镜像
rmi中的i代表images
[root@zhenghaoyun ~]$ docker rmi -f 镜像id # 删除指定的镜像
[root@zhenghaoyun ~]$ docker rmi -f 镜像id 镜像id 镜像id # 删除多个容器
[root@zhenghaoyun ~]$ docker rmi -f $(docker images -aq) # 删除全部镜像
$()
中可以写命令,docker images -aq
是查出所有镜像的id,$(docker images -aq)
就可以得到所有id并作为docker rmi -f
命令要删除的对象。
容器命令
有了镜像后,通过镜像可以创建容器。
docker pull centos #下载一个centos镜像
docker run新建容器并启动
docker run [可选参数] image
# 参数说明
--name="Name" 容器名字 tomcat01 tomcat02 用来区分容器
-d 后台方式运行(nohup)
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口 (常用)
-p 容器端口
容器端口
-P 随机指定端口
测试
[root@zhenghaoyun ~]$ docker run -it centos /bin/bash
#进入容器
[root@2c235d59bdbb /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
#从容器中退回主机
[root@2c235d59bdbb /]# exit
exit
可以看到进入了centos容器
docker ps列出所有运行的容器
docker ps
docker ps # 列出当前正在运行的容器
-a # 列出当前正在运行的容器+历史运行过的容器
-n=? # 显示最近创建的容器
-q # 只显示容器的编号
退出容器
exit # 直接停止容器并退出
Ctrl + p + q # 容器不停止退出
删除容器
docker rm 容器id # 删除指定的容器,不能删除正在运行的容器
docker rm -f 容器id # 强制删除容器
docker rm -f $(docker ps -aq) # 递归删除所有的容器
docker ps -aq|xargs docker rm # 通过管道命令删除所有的容器
启动容器和停止容器
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止容器
docker kill 容器id # 强制停止容器
docker run -d 镜像名 # 后台启动
常见的坑:docker容器使用后台运行就必须要有一个前台进程,docker发现没有应用,就会自动停止!
如nginx容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了。
查看日志
docker logs -f -t --tail 10 容器id # 显示10条日志
-tf # 显示日志
--tail number # 要显示的日志条数
查看容器中进程信息
docker top 容器id
查看容器元数据
docker inspect 容器id
[root@zhenghaoyun ~]$ docker inspect be9f24ac2472
[
{
"Id": "be9f24ac24725cbcf7b0a0495c3d9590d3d158994e0a1029542675711908aacd",
"Created": "2020-05-26T09:41:19.843651451Z",
"Path": "/bin/bash",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 8235,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-05-26T09:41:20.356642351Z",
"FinishedAt": "0001-01-01T00:00:00Z"
...
进入当前正在运行的容器
# 通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
# 命令
docker exec -it 容器id bashShell
# 方式二
docker attach 容器id
正在执行的代码....
# docker exec # 进入容器后开启一个新的终端,可以在里面操作(常用)
# docker attach # 进入容器正在执行的终端,不会启动新的进程
测试
[root@zhenghaoyun ~]$ docker exec -it be9f24ac2472 /bin/bash
[root@be9f24ac2472 /]# exit #进入到了一个新的终端
exit
[root@zhenghaoyun ~]$ docker attach be9f24ac2472
[root@be9f24ac2472 /]# #进入容器正在执行的终端
从容器内拷贝文件到主机上
docker cp 容器id:/文件路径 /目标主机路径 # 拷贝文件到主机
# 拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现
小结
attach Attach to a running container #当前sheil卜attach住按指定您行谎你
build Build an image from a Dockerfile #通过Dockerfile定制镜像
commit Create a new image from a container changes #提交当前容器为新的镜像
cp Copy files/folders from the containers filesystem to the host path #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container #创建一个新的容器,同run,但不启动容器
diff Inspect changes on a containers filesystem #查看docker容器变化
events Get real time events from the server #从docker 服务获取容器实时事件
exec Run a command in an existing container #在已存在的容器上运行命令
export Stream the contents of a container as a tar archive #导出容器的内容流作为一个tar归档文件[对应import ]
history Show the history of an image #展示一个镜像形成历史
images List images #列出系统当前镜像
import Create a new filesystem image from the contents of a tarball #从tar包中的内容创建一个新的文件系统映僧[对应export]
info Display system-wide information #显示系统相关信息
inspect Return 1ow-leve1 information on a container #查看容器详细信息
ki11 Ki11 a running container # ki11 指定docker 容器
load Load an image from a tar archive #从一个tar包中加载-一个镜像[对应save]
login Register or Login to the docker registry server #注册或者登录一个docker 源服务器
logout Log out fromLa Docker registry server #从当前Docker registry 退出
1ogs Fetch the 1ogs of a container #输出当村容器日志信息
port Lookup the public- facing port which is NAT-ed to PRIVATE PORT #查看映射端口对应的容器内郵源端
pause Pause a11 processes within a container #暂停容器
ps List containers #列出容器列表
pull Pull an image or a repository from the docker registry server #从docker境像源原 务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server #推送指定镜像或者库镜像至docker源服务器
restart Restart a running container #重启运行的容器
rm Remove one or more containers #移除一个或者多个容器
rmi Remove one or more images #移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或-个强制删除]
run Run a command in a new container #创建一个新的容器并运行一个命令
save Save an image toa tar archive #保存一个镜像为一个tar包[对应load]
search Search for an image on the Docker Hub #在docker hub中搜索镜像
start Start a stopped containers #启动容器
stop stop a running containers #停止容器
tag Tag an image into a reposi tory #给源中镜像打标签
top Lookup the running processes of a container #查看容器中运行的进程信息
unpause Unpause a paused container #取消暂停容器
version Show the docker vers ion information #查看docker 版本号
wait B1ock until a container stops, then print its exit code # 截取容器停止时的退出状态值
练习
docker安装nginx
# 搜索镜像 search 建议去docker hub上搜索
# 下载镜像 pull
# 运行测试
[root@zhenghaoyun ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 602e111c06b6 3 weeks ago 127MB
centos latest 470671670cac 3 months ago 237MB
# -d 后台执行
# --name 给容器命名
# -p 宿主机端口:容器内部端口 (映射)
[root@zhenghaoyun ~]# docker run -d --name nginx01 -p 3344:80 nginx
83505a20805b0891e43e895097a7c84a1450a65b0d1b83e54880fafa7b8ef8a5
[root@zhenghaoyun ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83505a20805b nginx "nginx -g 'daemon of…" 9 seconds ago Up 8 seconds 0.0.0.0:3344->80/tcp nginx01
# 进入容器
[root@zhenghaoyun ~]# docker exec -it nginx01 /bin/bash
root@83505a20805b:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@83505a20805b:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@83505a20805b:/# cd /etc/nginx
root@83505a20805b:/etc/nginx# ls
conf.d fastcgi_params koi-utf koi-win mime.types modules nginx.conf scgi_params uwsgi_params win-utf
# 退出容器
root@83505a20805b:/etc/nginx# exit
exit
[root@zhenghaoyun ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83505a20805b nginx "nginx -g 'daemon of…" 8 minutes ago Up 8 minutes 0.0.0.0:3344->80/tcp nginx01
# 停止容器运行
[root@zhenghaoyun ~]# docker stop 83505a20805b
83505a20805b
如何理解端口暴露(宿主机端口:容器内部端口)?

即通过宿主机的3344端口可以访问到nginx01容器内部的80端口
问题:每次改动nginx配置文件,都需要进入容器内部,十分麻烦
解决:数据卷技术(后面会学习到)
docker安装tomcat
1.官方的使用方式
# 官方的使用
docker run -it --rm tomcat:9.0
# 我们之前的启动都是后台启动,停止了容器后,容器还是可以查到 而docker run -it --rm 一般用来测试,用完就删除了。
2.一般方式
# 下载再启动
docker pull tomcat
# 启动运行
docker run -d -p 80:8080 --name tomcat01 tomcat
# 下载的tomcat没有webapps,阿里云镜像默认是最小镜像,所有不必要的东西都会被剔除
# 测试访问没有问题,但是404,因为webapps中没有项目,需要把webapps.dist下的东西拷贝到webapps中
docker安装ES+Kibana
es 暴露的端口很多!
es 十分耗内存!
es 的数据一般要放置到安全的目录!使用挂载!
1.下载并启动es
$ docker network create somenetwork
$ docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.7.0
2.测试启动是否成功
$ curl localhost:9200
3.如果不成功,可能是内存不够,因为docker非常的耗内存。查看容器日志寻找错误原因。
$ docker logs -ft 8cb65d8a1627
# 错误原因
2020-05-27T10:13:57.587645924Z OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x000000008a660000, 1973026816, 0) failed; error='Cannot allocate memory' (errno=12)
2020-05-27T10:13:57.587716355Z #
2020-05-27T10:13:57.587722244Z # There is insufficient memory for the Java Runtime Environment to continue.
2020-05-27T10:13:57.587725840Z # Native memory allocation (mmap) failed to map 1973026816 bytes for committing reserved memory.
2020-05-27T10:13:57.587729419Z # An error report file with more information is saved as:
2020-05-27T10:13:57.587732980Z # /tmp/hs_err_pid1.log
4.删除容器,增加对内存的限制。
#先删除容器
[root@zhenghaoyun ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
67550d49d74e elasticsearch:7.7.0 "/tini -- /usr/local…" 4 minutes ago Exited (1) 4 minutes ago elasticsearch
[root@zhenghaoyun ~]# docker rm 67550d49d74e
67550d49d74e
#启动容器,使用 -e ES_JAVA_OPTS="-Xms64m -Xmx512m"增加了对内存的限制
[root@zhenghaoyun ~]# docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.7.0
1147b24344a98d01bb894d5b5cee7ab3867b7c8a0962fb778ae406de8e7aef31
#查看容器是否启动
[root@zhenghaoyun ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1147b24344a9 elasticsearch:7.7.0 "/tini -- /usr/local…" 5 seconds ago Up 4 seconds 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp elasticsearch
5.再次测试
[root@zhenghaoyun ~]# curl localhost:9200
{
"name" : "1147b24344a9",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "v4t6u-mdQ1iA5HvImqPn_Q",
"version" : {
"number" : "7.7.0",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "81a1e9eda8e6183f5237786246f6dced26a10eaf",
"build_date" : "2020-05-12T02:01:37.602180Z",
"build_snapshot" : false,
"lucene_version" : "8.5.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
问题:不可以使用kibana直接连接ES,因为他们是互相隔离的,那么如何解决呢?
Docker可视化
potainer是Docker图形化界面管理工具,提供了一个后台面板供我们操作。
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
访问测试: http://ip:8088/ (阿里云安全组需要开放8088端口)
Docker镜像详解
镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量以及配置文件等。
所有的应用,直接打包成docker镜像。就直接可以跑起来。
如何得到镜像:
- 从远程仓库下载
- 朋友拷贝给你
- 自己制作一个DockerFile
Docker镜像加载原理
UnionFS(联合文件系统)
UnionFs(联合文件系统):union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件下。
Union文件系统是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等等。
对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序就可以了(像我从docker上下载的centos只包含了一点点基本命令),因为底层直接用宿主机的内核,自己只需要提供rootfs就可以了,因此可见,对于不用的Linux发行版,bootfs基本是一致的,而rootfs会有差别,因此不同的发行版可以公用bootfs。
分层理解
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!
Docker为什么采用分层结构呢?
共享资源!
多个镜像从相同的基础镜像构建而来,那么宿主机只需在磁盘上保存一份基础镜像,同时内存中也只需加载一份基础镜像就可以为所有容器服务了,并且镜像的每一层都可以被共享。
特点
Docker镜像都是只读的,当容器启动时, 一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
镜像提交
# 提交容器成为一个新的副本
docker commit
# 命令和git基本类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
实战:做一个有webapps的tomcat镜像
-
启动一个默认的tomcat
-
发现这个默认的tomcat webapps下没有项目,官方的默认镜像下就是没有文件的
-
我们自己拷贝进去基本的文件,然后提交
-
将我们操作过的容器通过commit提交为新的镜像,我们以后就用我们修改过的镜像就可以,这就是我们一个修改过的镜像
- 以后就可以直接用这个镜像了
容器数据卷
什么是容器数据卷
问题:如果数据都在容器中,如果我们将容器删除,数据就会丢失,希望可以做到容器删除,数据不丢失,做到数据持久化。
容器之间可以有一个数据共享的技术,Docker容器中产生的数据,可以同步到本地。
这就是卷技术,说白了就是目录的挂载,将我们容器内的目录,挂载到Linux上面。
总结
- 容器的持久化和同步操作
- 容器间数据也是可以共享的
使用容器数据卷
docker run -it -v 主机目录:容器内目录
测试
[root@zhenghaoyun home]# mkdir test
[root@zhenghaoyun home]# docker run -it -v /home/test:/home centos /bin/bash
[root@d0686ec1c489 /]# cd /home
[root@d0686ec1c489 home]# ls
# 没有任何东西
# 新开一个终端,创建test.java文件
[root@zhenghaoyun home]# cd test
[root@zhenghaoyun test]# touch test.java
[root@zhenghaoyun test]#
# 发现容器内也有test.java了
[root@d0686ec1c489 home]# ls
test.java
使用docker inspect
查看挂载信息
[root@zhenghaoyun test]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d0686ec1c489 centos "/bin/bash" 4 minutes ago Up 4 minutes silly_kepler
[root@zhenghaoyun test]# docker inspect d0686ec1c489
慢慢翻可以看到以下信息:
可以看到成功绑定了
之后不管是修改宿主机内文件还是修改容器内文件,都会自动同步。
实战:安装MySQL
实现MySQL的数据持久化问题
# 获取镜像
[root@zhenghaoyun /]# docker pull mysql:5.7
# 运行容器,需要做数据挂载 . 安装启动mysql时,一定要有密码
# 官方运行实例
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动mysql
-d 后台运行
-p 映射端口
-v 卷挂载
-e 环境配置
--name 容器名字
[root@zhenghaoyun /]# 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
# 启动成功之后,在本地使用Navicat连接测试一下
# Navicat连接到服务器的3310 --- 3310与容器内的3306映射,连接成功
# 在本地测试创建一个数据库,查看一下我们映射的路径是否ok
# 成功
如果连接失败,查看下安全组是否开放端口。
当mysql容器删掉的时候,宿主机的数据库文件仍然存在,实现了数据持久化,数据不会丢失。
具名挂载和匿名挂载
匿名挂载
挂载的时候可以不指定主机目录
# 匿名挂载
# -v 容器内路径 -P 随机映射端口
[root@zhenghaoyun ~]# docker run -d -P --name nginx01 -v /etc/nginx nginx
a3aea776a59f7568950d5eb822e65b32a6f9cd42e2c985a58c31d4dfec3e00d4
# 查看所有卷的情况
[root@zhenghaoyun ~]# docker volume ls
DRIVER VOLUME NAME
local 3fbc02b34b7db4bca1558728d8edea592541e748f6f898464a465331000e7c04
# 这里发现,这种就是就是匿名挂载,我们在-v的时候,只写了内部路径,没有写容器外的路径
# 会自动创建一个名字由字符和数字组成的数据卷
# 通过docker volume ls可以查看数据卷列表
具名挂载
挂载的时候创建一个自定义名称的数据卷
# 将/etc/nginx nginx挂载到juming-nginx这个数据卷上
[root@zhenghaoyun ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
79a2190e4658af610cf76635980f7176e1daf443a43f612fb418ca559880891f
[root@zhenghaoyun ~]# docker volume ls
DRIVER VOLUME NAME
local juming-nginx
#查看下这个卷的具体信息
[root@zhenghaoyun ~]# docker volume inspect juming-nginx
[
{
"CreatedAt": "2020-05-28T16:25:24+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]
所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxx/_data
。
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况下我们使用具名挂载。
如何确定是具名挂载还是匿名挂载
- -v 容器内路径 # 匿名挂载
- -v 卷名:容器内路径 # 具名挂载
- -v /宿主机路径:容器内路径 # 指定路径挂载,一般是/开头的,指绝对路径
拓展
# 通过 -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
# 只要看到ro,就说明这个路径只能通过宿主机来操作,容器内无法操作
# 默认rw
初识DockerFile
DockerFile就是用来构建Docker镜像的构建文件,它其实就是一个命令脚本。
通过这个脚本可以生成镜像,镜像是一层一层的,脚本一个个的命令,每个命令都是一层。
编写DokcerFile
创建一个dockerfile文件,名字可以随意,建议还是dockerfile。
FROM centos
# 在容器中创建两个数据卷目录,这是匿名挂载,在宿主机上通过docker inspect 容器id 可以查看到这两个目录挂载在哪里。
VOLUME ["volume01","volume02"]
CMD echo "----end----"
CMD /bin/bash
通过DockerFile生成镜像
zhy/centos:1.0
为生成的镜像名:tag
docker build -f /home/docker-test-volume/dockerfile -t zhy/centos:1.0 .
通过这种方式就生成了一个镜像,通过这个镜像生成一个容器的时候,这个容器的挂载和我们再DockerFile文件内设置的是一样的。volume01和volume01匿名挂载的时候,挂载在/var/lib/docker/volumes/xxx/_data
。
启动自己的镜像
[root@zhenghaoyun docker-test-volume]# docker run -it zhy/centos
Unable to find image 'zhy/centos:latest' locally
^Z
[1]+ Stopped docker run -it zhy/centos
[root@zhenghaoyun docker-test-volume]# docker run -it e53d4f7b84cd
[root@cc887d7eecc0 /]# ls
bin home lost+found opt run sys var
dev lib media proc sbin tmp volume01
etc lib64 mnt root srv usr volume02
查看挂载目录
$ docker inpect 容器id
# 查看mounts项可以看到挂载信息
"Mounts": [
{
"Type": "volume",
"Name": "aab60727df7805eb76d78f0ddfdc0fa8cab7d16d5090d85ca0dcea28419742b8",
"Source": "/var/lib/docker/volumes/aab60727df7805eb76d78f0ddfdc0fa8cab7d16d5090d85ca0dcea28419742b8/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "a99afe0b7a935a8f0834e294df7e4b9506d9ba6c637d9b3f19a45fea1c9d448a",
"Source": "/var/lib/docker/volumes/a99afe0b7a935a8f0834e294df7e4b9506d9ba6c637d9b3f19a45fea1c9d448a/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
数据卷容器
如果用户需要在容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器,数据卷容器其实是一个普通的容器,专门用来提供数据卷供其它容器挂载。
启动三个容器
docker run -it --name=docker01 zhy/centos:1.0
docker run -it --name docker02 --volumes-from docker01 zhy/centos:1.0
docker run -it --name docker03 --volumes-from docker02 zhy/centos:1.0
分别进入docker01、docker02、docker03后发现都含有volumes01、volumes02这两个数据卷。
[root@8fff8e978165 /]# ls
bin home lost+found opt run sys var
dev lib media proc sbin tmp volume01
etc lib64 mnt root srv usr volume02
进入docker01的volume01后创建文件test.java
[root@zhenghaoyun _data]# docker attach docker01
[root@e73760697aea /]# ls
bin home lost+found opt run sys var
dev lib media proc sbin tmp volume01
etc lib64 mnt root srv usr volume02
[root@e73760697aea /]# cd volume01
[root@e73760697aea volume01]# ls
[root@e73760697aea volume01]# touch test.java
然后再进docker02和docker03发现docker01创建的文件同步到了docker02和docker03上
[root@zhenghaoyun _data]# docker attach docker02
[root@8fff8e978165 /]# cd volume01
[root@8fff8e978165 volume01]# ls
test.java
[root@8fff8e978165 volume01]# read escape sequence
[root@zhenghaoyun _data]# docker attach docker03
[root@26c6fd5d06e9 /]# cd volume01
[root@26c6fd5d06e9 volume01]# ls
test.java
删掉容器docker02后,在docker01的volume01中新增文件test2.java
测试结果发现docker03中也有test2.java
说明删掉了一个容器,剩余的容器仍然能实现数据共享!
因为docker的数据共享是基于拷贝机制的!
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止,但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的。
DockerFile
dockerfile 是用来构建docker镜像的文件,它是一个命令参数脚本。
构建步骤
- 编写一个dockerfile文件
- docker build 构建成为一个镜像
- docker run 运行镜像
- docker push 发布镜像(DockerHub,阿里云镜像仓库)
构建过程
Dockerfile基础知识
- 每个保留关键字(指令)都必须是大写字母
- 从上到下顺序执行
- #表示注释
- 每一个指令都会创建一个新的镜像层,并提交
dockerfile是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile文件,这个文件十分简单。
Docker镜像逐渐成为了企业交付的标准,必须要掌握。
指令
常用指令
FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的,姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤: tomcat镜像,这个tomcat压缩包,添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露端口配置
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令而CMD不可以追加
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行ONBUILD指令,触发指令
COPY # 类似ADD 将我们的文件拷贝到镜像中
ENV # 构建的时候设置环境变量
CMD和ENTRYPOINT的区别
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
测试CMD
# 编写dockerfile文件
[root@zhenghaoyun dockerfile]# vim dockerfile-cmd-test
FROM centos
CMD ["ls","-a"]
# 构建镜像
[root@zhenghaoyun dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest .
# run运行,发现我们的ls -a生效
[root@zhenghaoyun dockerfile]# docker run cmdtest
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
# 想追加一个命令 -l 发现报错 (ls -al)
[root@zhenghaoyun dockerfile]# docker run cmdtest -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
ERRO[0000] error waiting for container: context canceled
# CMD的情况下 -l 替换了CMD ["ls","-a"] -l不是命令所以就会报错
测试ENTRYPOINT
[root@zhenghaoyun dockerfile]# vim dockerfile-cmd-entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
[root@zhenghaoyun dockerfile]# docker build -f dockerfile-cmd-entrypoint -t entrypoint-test .
[root@zhenghaoyun dockerfile]# docker run entrypoint-test
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
# 我们的追加命令,是直接拼接在我们的ENTRYPOINT命令的后面
[root@zhenghaoyun dockerfile]# docker run entrypoint-test -l
total 56
drwxr-xr-x 1 root root 4096 May 17 02:13 .
drwxr-xr-x 1 root root 4096 May 17 02:13 ..
-rwxr-xr-x 1 root root 0 May 17 02:13 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 May 17 02:13 dev
drwxr-xr-x 1 root root 4096 May 17 02:13 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Jan 13 21:48 lost+found
Dockerfile中很多命令都十分的相似,我们需要了解他们的区别,最好的学习就是对比然后测试。
实战
FROM scratch
Dockerhub中99%的镜像都是从这个基础镜像过来的,然后根据需要的软件和配置来进行的构建。
创建一个自己的centOS
# 编写dockerfile文件
[root@zhenghaoyun ~]# vim mydockerfile-centos
[root@zhenghaoyun ~]# cat mydockerfile-centos
FROM centos
MAINTAINER zhy<123456@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD $MYPATH
CMD "----end----"
CMD /bin/bash
# 开始构建
[root@zhenghaoyun ~]# docker build -f mydockerfile-centos -t mycentos:0.1 .
Sending build context to Docker daemon 231.5MB
Step 1/10 : FROM centos
---> 470671670cac
Step 2/10 : MAINTAINER zhy<123456@qq.com>
---> Running in 81104dc1bf58
Removing intermediate container 81104dc1bf58
---> 5083be5d9e3d
...
Removing intermediate container 1dd935006a10
---> 863e1e9a99df
Successfully built 863e1e9a99df
Successfully tagged mycentos:0.1
# 查看我们自己制作的镜像
[root@zhenghaoyun ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 0.1 863e1e9a99df 29 seconds ago 321MB
zhy/centos 1.0 e53d4f7b84cd About an hour ago 237MB
mysql 5.7 a4fdfd462add 8 days ago 448MB
nginx latest 9beeba249f3e 13 days ago 127MB
elasticsearch 7.7.0 7ec4f35ab452 2 weeks ago 757MB
portainer/portainer latest 2869fc110bf7 2 months ago 78.6MB
centos latest 470671670cac 4 months ago 237MB
# 运行镜像
[root@zhenghaoyun ~]# docker run -it mycentos:0.1
# 查看工作目录
[root@222c4f170311 local]# pwd
/usr/local
通过docker history
查看镜像历史
[root@zhenghaoyun ~]# docker history mycentos:0.1
IMAGE CREATED CREATED BY SIZE COMMENT
863e1e9a99df 4 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
3630bf4d4b65 4 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "\"--… 0B
bd395ab3f210 4 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "$MYP… 0B
539ddae94271 4 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
a44756b81bb3 4 minutes ago /bin/sh -c yum -y install net-tools 24.1MB
9125de5f6a9b 4 minutes ago /bin/sh -c yum -y install vim 59.8MB
c44e45342f97 5 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
1b747b2ad94b 5 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
5083be5d9e3d 5 minutes ago /bin/sh -c #(nop) MAINTAINER zhy<123456@qq.… 0B
470671670cac 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 4 months ago /bin/sh -c #(nop) ADD file:aa54047c80ba30064… 237MB
制作一个tomcat镜像
# Dockerfile内容
FROM centos
MAINTAINER zhy<123456@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u152-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.5.24.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_152
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.24
ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.24
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-8.5.24/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.5.24/bin/logs/catalina.out
# 构建镜像
$ docker build -t diytomcat .
# 启动镜像
$ docker run -d -p 3030:8080 --name zhytomcat
-v /root/dockertest/test:/usr/local/apache-tomcat-8.5.24/webapps/test
-v /root/dockertest/tomcatlogs/:/usr/local/apache-tomcat-8.5.24/logs diytomcat
发布自己的镜像
发布到DockerHub
- 去DockerHub注册自己的账号
- 确定这个账号可以登录
- 在我们服务器上提交自己的镜像
# 登录dockerhub
[root@zhenghaoyun ~]# docker login -u zhenghaoyun
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
# 发布镜像前最好规范镜像名,如增加自己的姓名和版本号。
[root@zhenghaoyun ~]# docker tag diytomcat zhy/diytomcat:1.0
# push,自己发布的镜像尽量带上版本号
[root@zhenghaoyun ~]# docker push zhy/diytomcat:1.0
提交的时候也是按照镜像的层级来提交的。
发布到阿里云镜像
- 登录阿里云
- 找到容器镜像服务
- 创建命名空间
- 创建仓库
- 参考阿里云教程
- 推送示例
小结
Docker网络
理解Docker0
[root@zhenghaoyun ~]# ip addr
# 本机回环地址
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
# 阿里云内网地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:2c:49:f0 brd ff:ff:ff:ff:ff:ff
inet 172.17.109.162/20 brd 172.17.111.255 scope global dynamic eth0
valid_lft 311911158sec preferred_lft 311911158sec
# docker0地址
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:d2:e2:8f:bc brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
valid_lft forever preferred_lft forever
74: br-fc3b359394e5: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:9e:68:59:59 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.1/16 brd 172.19.255.255 scope global br-fc3b359394e5
valid_lft forever preferred_lft forever
问题:docker是如何处理容器网络访问的
我们每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡docker0 桥接模式,使用的技术是veth-pair 技术。
# 启动一个容器tomcat01
[root@zhenghaoyun ~]# docker run -d -P --name tomcat01 tomcat
8d31b16a8cc1361dbd2b9117de424063ee1831d6c717aee75f2f54ae3aa45cb8
# 查看容器内ip,注意到有个119: eth0@if120
[root@zhenghaoyun ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
119: eth0@if120: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 尝试在容器外ping tomcat01,会发现可以ping通
[root@zhenghaoyun ~]# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.057 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.054 ms
^Z
[1]+ Stopped ping 172.18.0.2
# 在容器外再次测试ip addr,会发现又多了一对网卡120: veth349e841@if119,和之前tomcat01内的是惊人的相似119: eth0@if120
[root@zhenghaoyun ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:2c:49:f0 brd ff:ff:ff:ff:ff:ff
inet 172.17.109.162/20 brd 172.17.111.255 scope global dynamic eth0
valid_lft 311910065sec preferred_lft 311910065sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:d2:e2:8f:bc brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
valid_lft forever preferred_lft forever
74: br-fc3b359394e5: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:9e:68:59:59 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.1/16 brd 172.19.255.255 scope global br-fc3b359394e5
valid_lft forever preferred_lft forever
120: veth349e841@if119: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether da:41:5f:db:1e:89 brd ff:ff:ff:ff:ff:ff link-netnsid 0
我们发现这个容器带来的网卡都是一对一对的
veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
正因为有了这个特性,veth-pair 充当一个桥梁,连接各种虚拟网络设备的
OpenStac ,Docker容器之间的连接,OVS的连接,都是使用veth-pair 技术
再来测试一下tomcat01和tomcat02是否可以ping通
root@zhenghaoyun ~]# docker exec -it tomcat02 ping 172.18.0.2
# 结论: 容器和容器之间是可以互相ping通的
结论
tomcat01和tomcat02是共用的一个路由器,docker0
所有的容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用ip。
Docker使用的是Linux的桥接,宿主机中是一个Docker容器的网桥docker0。
Docker中的所有的网络接口都是虚拟的,虚拟的转发效率高。(内网传递文件)
只要容器删除,对应的一对网桥就没有了。
–link
思考一个场景,我们编写了一个微服务,database url=ip:,项目不重启,数据库ip换掉了,我们希望可以处理这个问题,可以通过名字来进行访问容器。
# 通过名字无法ping
[root@zhenghaoyun ~]# docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known
# 如何可以解决呢
# 通过--link就可以解决网络连通问题
[root@zhenghaoyun ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
434fab3886a930372a75935b23abb2da4df07d1d860143c258769961e8b93e5a
[root@zhenghaoyun ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.054 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.074 ms
# 反向可以ping通吗? 不行,因为没有配置
[root@zhenghaoyun ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
查看tomcat03的hosts配置
# 查看hosts配置,在这里发现
[root@zhenghaoyun ~]# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 a8be6dc01d63
172.17.0.4 434fab3886a9
本质探究
–link就是我们在hosts配置中增加了一个172.17.0.3,tomcat02 a8be6dc01d63。
注意
我们现在用Docker已经不建议使用--link
了
我们需要使用更高级的方式:自定义网络,不使用docker0。
docker0问题:它不支持容器名连接访问。
自定义网络
查看所有的docker网络
[root@zhenghaoyun ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
e1e8a026e40f bridge bridge local
f2b909434e48 host host local
255e387a2a2e none null local
fc3b359394e5 somenetwork bridge local
网络模式
-
bridge:桥接docker(默认,自己创建也是用bridge模式)
-
none:不配置网络
-
host:和宿主机共享网络
-
container:容器网络连通(用得少,局限很大)
通常情况下,我们直接启动容器时,有个默认选项--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 范围 192.168.0.2~192.168.255.255
# --gateway 192.168.0.1
[root@zhenghaoyun ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
837a94ff18774c323b48011a41112fcf8e4a34b13f6b5557fbf3bdfe01675fba
[root@zhenghaoyun ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
e1e8a026e40f bridge bridge local
f2b909434e48 host host local
837a94ff1877 mynet bridge local
255e387a2a2e none null local
fc3b359394e5 somenetwork bridge local
[root@zhenghaoyun ~]# docker network inspect 837a94ff1877
...
"Config": [
{
# 看这里,和我们自定义的一样!
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
...
]
测试ping连接
# 再次测试ping连接
[root@zhenghaoyun ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.110 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.064 ms
# 现在不适用--link也可以ping名字了
[root@zhenghaoyun ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.036 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.065 ms
我们自定义的网络docker都已经帮我们维护好了对应的关系,推荐我们平时这样使用网络。
好处:
有两个集群分别是redis和mysql,不同的集群使用不同的网络,保证集群是安全和健康的。
网路连通
通过以上的步骤,我们现在有两个网段了,一个是docker0和mynet,docker0和mynet内的容器是不能网络连通的,如何去解决这一问题?
# 测试打通tomcat01 - mynet
[root@zhenghaoyun ~]# docker network connect mynet tomcat01
# 连通之后就是将tomcat01 放到了mynet网络下
# 一个容器两个ip地址
# 好比 阿里云服务:公网ip 私网ip
# tomcat01可以ping通
[root@zhenghaoyun ~]# docker exec -it tomcat01 ping tomcat-net-01
PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.070 ms
# tomcat02依旧打不通
[root@zhenghaoyun ~]# docker exec -it tomcat02 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known
结论
假设要跨网络操作别人,就需要使用docker network connect 连通。
实战:部署redis集群
场景:r-m2突然挂掉了
# 创建网卡
docker network create redis --subnet 172.38.0.0/16
# 通过脚本创建六个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
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; \
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
# 创建集群
[root@zhenghaoyun /]# docker exec -it redis-1 /bin/sh
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: bc3aff4049d9610330f51b58871769f8ac827c63 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 6af312198899e48ff01b1ee4a4158f7510e12d0b 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: c4f207bf3beb7ff1ac6142ab7b40166d57d34463 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: b2b335eb0306a36d23c9962b09f1ceed720200cc 172.38.0.14:6379
replicates c4f207bf3beb7ff1ac6142ab7b40166d57d34463
S: c5bbf3ef339568f7d63000855d42ef366e52aba7 172.38.0.15:6379
replicates bc3aff4049d9610330f51b58871769f8ac827c63
S: 5768864a0132bbc9139f16d1cf21b20868f66511 172.38.0.16:6379
replicates 6af312198899e48ff01b1ee4a4158f7510e12d0b
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: bc3aff4049d9610330f51b58871769f8ac827c63 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 6af312198899e48ff01b1ee4a4158f7510e12d0b 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: b2b335eb0306a36d23c9962b09f1ceed720200cc 172.38.0.14:6379
slots: (0 slots) slave
replicates c4f207bf3beb7ff1ac6142ab7b40166d57d34463
S: 5768864a0132bbc9139f16d1cf21b20868f66511 172.38.0.16:6379
slots: (0 slots) slave
replicates 6af312198899e48ff01b1ee4a4158f7510e12d0b
S: c5bbf3ef339568f7d63000855d42ef366e52aba7 172.38.0.15:6379
slots: (0 slots) slave
replicates bc3aff4049d9610330f51b58871769f8ac827c63
M: c4f207bf3beb7ff1ac6142ab7b40166d57d34463 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
至此redis集群就搭建完成了
SpringBoot微服务打包成Docker镜像
- 构建springboot项目
- 打包应用
- 编写dockerfile
- 构建镜像
- 发布运行
到这里我们就完全够用了Docker。
以后我们使用了Docker之后,给别人交付的就是一个镜像即可。
其他
如果有很多镜像,100个镜像,分别启动和维护,非常麻烦,所以真正的企业实战还需要以下知识:
- Docker Compose
- Docker Swarm
c4f207bf3beb7ff1ac6142ab7b40166d57d34463
S: c5bbf3ef339568f7d63000855d42ef366e52aba7 172.38.0.15:6379
replicates bc3aff4049d9610330f51b58871769f8ac827c63
S: 5768864a0132bbc9139f16d1cf21b20868f66511 172.38.0.16:6379
replicates 6af312198899e48ff01b1ee4a4158f7510e12d0b
Can I set the above configuration? (type ‘yes’ to accept): yes
Nodes configuration updated
Assign a different config epoch to each node
Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
…Performing Cluster Check (using node 172.38.0.11:6379)
M: bc3aff4049d9610330f51b58871769f8ac827c63 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 6af312198899e48ff01b1ee4a4158f7510e12d0b 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: b2b335eb0306a36d23c9962b09f1ceed720200cc 172.38.0.14:6379
slots: (0 slots) slave
replicates c4f207bf3beb7ff1ac6142ab7b40166d57d34463
S: 5768864a0132bbc9139f16d1cf21b20868f66511 172.38.0.16:6379
slots: (0 slots) slave
replicates 6af312198899e48ff01b1ee4a4158f7510e12d0b
S: c5bbf3ef339568f7d63000855d42ef366e52aba7 172.38.0.15:6379
slots: (0 slots) slave
replicates bc3aff4049d9610330f51b58871769f8ac827c63
M: c4f207bf3beb7ff1ac6142ab7b40166d57d34463 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.Check for open slots…
Check slots coverage…
[OK] All 16384 slots covered.
至此redis集群就搭建完成了
# SpringBoot微服务打包成Docker镜像
1. 构建springboot项目
2. 打包应用
3. 编写dockerfile
4. 构建镜像
5. 发布运行
到这里我们就完全够用了Docker。
以后我们使用了Docker之后,给别人交付的就是一个镜像即可。
# 其他
如果有很多镜像,100个镜像,分别启动和维护,非常麻烦,所以真正的企业实战还需要以下知识:
* Docker Compose
* Docker Swarm
* CI/CD Jenkins