狂神说基础入门,下
1.0 容器数据卷
1.1 什么是数据容器卷
docker的理念回顾
将应用的运行环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能过持久化的,
就好比,你安装了一个MySQL,结果把容器删除了,就相相当于删库跑路了。这个就很扯。
所以我们希望容器之间可以共享数据,docker容器产生的数据,如果不通过docker commint 生成新的镜像,使得数据作为一个镜像作为一部分保留下来,那么当容器删除之后,数据自然就没有的 了!这样在实际项目中是不合理的。
为了能保留数据在docker中,我们就可以使用数据卷!让数据卷挂载到我们本地,这样数据就不会因为容器被删除而导致数据丢失了。
作用
卷就好似目录或者文件,存在一个或者多个容器中,有docker挂载到容器,但不属于联合文件中系统,因此能够绕过,union file system ,提供一些用于持续存储或者共享数据的特性。
卷的作用就是数据的持久化,完全独立于容器的生存周期,因此docker不会在容器删除时删除其挂载的容器卷。
特性
1,数据卷可在容器之间共享数据重用数据。
2,卷中的更改可直接生效。
3,数据卷的更改不会包含在镜像的更新中
4,数据卷的生命周期一直持续到没有容器使用它为止
所以,总结就是一句话:就是容器的持久化,以及容器间的继承和数据共享
1.2 使用数据卷
方式一:容器中使用命令来添加
挂载
# 命令
docker run -it -v 宿主机绝对路径目录:容器内目录 镜像名
# 测试
[root@docker home]#ls
lighthouse
[root@docker home]#docker run -it -v /home/ceshi:/home centos /bin/bash
查看数据卷是否挂载成功docker inspect 容器id
[root@docker ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c203df3e3375 centos "/bin/bash" About a minute ago Up About a minute reverent_burnell
[root@docker ~]#docker inspect c203df3e3375
测试容器和宿主机之间数据共享:可以发现,在容器中,创建的会在宿主机中看到!
[root@c203df3e3375 home]# exit
exit
[root@docker ceshi]#
测试容器停止后,主机修改数据是否会同步!
- 停止容器 ,容器内使用 exit
- 在宿主机上修改文件,增加一些内容
- 启动刚才停止的容器
- 然后查看对应的文件,发现数据依旧同步!OK
使用docker安装MySQL
思考:MySQL数据持久化的问题
# 1 , 搜索镜像
[root@docker ceshi]#docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 14298 [OK]
mariadb MariaDB Server is a high ,,,,,,,/
# 2, 拉取镜像
[root@docker ceshi]#docker pull mysql:5.7
5.7: Pulling from library/mysql
72a69066d2fe: Pull complete
93619dbc5b36: Pull complete
99da31dd6142: Pull complete
626033c43d70: Pull complete
37d5d7efb64e: Pull complete
ac563158d721: Pull complete
d2ba16033dad: Pull complete
0ceb82207cd7: Pull complete
37f2405cae96: Pull complete
e2482e017e53: Pull complete
70deed891d42: Pull complete
Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
#3,启动容器 -e 环境变量!
#注意:MySQL的数据应该不丢失!先体验 !参考官方文档
[root@docker ceshi]# 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
2219011effe9f3981ea677c74fddee4a521eef55f354540602c48d239cb124a9
-d 后台运行
-p 端口映射
-v 挂载卷
-e 环境配置
--name 命名容器名字
#4,使用本地的sqlyog连接测试下3310端口连接成功了
#sqlyog 连接到服务器3310--3310 和容器内的3306端口映射,这个时候就可以连接上了!说明是部署成功的,
#5,查看本地的 /home/mysql 目录
[root@docker ceshi]#cd /home/mysql/data/
[root@docker data]#pwd
/home/mysql/data
[root@docker data]#ls
auto.cnf client-cert.pem ibdata1 ibtmp1 private_key.pem server-key.pem
ca-key.pem client-key.pem ib_logfile0 mysql public_key.pem sys
ca.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem
#6,删除MySQL容器测试下数据是否还在
[root@docker data]#docker rm -f mysql01
mysql01
#删除容器,然后发现远程连接失败!
#查看下挂载到本地的数据卷
[root@docker data]#ls
auto.cnf client-cert.pem ibdata1 ibtmp1 private_key.pem server-key.pem
ca-key.pem client-key.pem ib_logfile0 mysql public_key.pem sys
ca.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem
可以看到刚刚就建立的MySQL数据库在本地存储着,这就实现了数据持久化的功能
1.3 具名挂载和匿名挂载
#匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
#匿名挂载的缺点就是不好维护,通常使用命令 docker volume维护
docker volume ls
#具名挂载
-v 卷名:/容器内路径
[root@docker ~]#docker run -d -P --name nginx03 -v juming-nginx:/etc/nginx nginx
9365b963be03ef675f4ae98a1679d2726a71f3a590e3e6782eb2d9bb7fb17f61
[root@docker ~]#docker volume ls
DRIVER VOLUME NAME
local f494d5bb75a608beff1d2a6ace4ec5d679640cbfd1350ae03692a82e62f2
local juming-nginx #这个就显示具名挂载是成功的 ,有名字了
# -P 随机指定端口
#查看挂载的路径
[root@docker ~]#docker volume inspect juming-nginx
[
{
"CreatedAt": "2023-07-12T17:12:59+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/
下的
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况下都使用的是具名挂载
#怎么判断什么是卷名而不是本机目录名
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载
扩展
#改变文件的读写权限,一旦设置了容器的权限,容器对我们挂载的内容就会有限定了。
#ro : readonly 只能用宿主机来操作,容器内部是无法操作的
#rw : readwrite
#z指定容器对我们挂载出来的内容的读写权限
#eg:
docker run -d -P --name nginx02 -v nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v nginx:/etc/nginx:rw nginx
1.4 初识dockerfile
通过docker file 来添加(了解)
dockerfile 是用来构建docker镜像的构建文件,一些命令和参数构成的脚本,镜像是一层一层的,脚本的命令都是一层!
# 1,我们在宿主机/home 目录下新建一个 docker-test-volume文件夹(名字是可以随机的,但是建议是dockerfile)
[root@docker ~]#cd /home/
[root@docker home]#ls
ceshi lighthouse mysql
[root@docker home]#mkdir docker-test-volume
[root@docker home]#ls
ceshi docker-test-volume lighthouse mysql
[root@docker home]#pwd
/home
[root@docker home]#cd docker-test-volume/
[root@docker docker-test-volume]#ls
#2,编辑一个dockerfile文件
[root@docker docker-test-volume]#vim dockerfile1
[root@docker docker-test-volume]#cat dockerfile1 #编写的时候是不能添加注释的,这里做笔记添加了注释
FROM centos #以centos为基础
VOLUME ["volume01","volume02"] #卷名
CMD echo "--------end------" #命令执行完成之后输出一个内容
CMD /bin/bash #命令执行完成之后默认走bash
#3,构建镜像
[root@docker ~]#docker build -f /home/docker-test-volume/dockerfile1 -t kuangshen/centos:1.0 .
[+] Building 0.1s (5/5) FINISHED docker:default
=> [internal] load build definition from dockerfile1 0.0s
=> => transferring dockerfile: 131B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:latest 0.0s
=> [1/1] FROM docker.io/library/centos 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:8e613d76265da2f086a1cf24ba6af9aeb0058dc84f95aa36aabae7e161154c4c 0.0s
=> => naming to docker.io/kuangshen/centos:1.0
[root@docker ~]#docker images #查看镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 18 months ago 141MB
mysql 5.7 c20987f18b13 18 months ago 448MB
#4,查看镜像
kuangshen/centos 1.0 8e613d76265d 22 months ago 231MB
centos latest 5d0da3dc9764 22 months ago 231MB
#5,测试启动我们构建的镜像
这个卷和外部一定有一个同步的目录
查看卷挂载的路径是否同步出去了!
[root@docker ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0b0cc4867d65 8e613d76265d "/bin/bash" 8
,,,,,/
[root@docker ~]#docker inspect 0b0cc4867d65
测试一下刚才的文件同步出去了!
这种方式我们未来使用的非常多,因为我们通常会构建自己的镜像!
假设构建的镜像的时候没有挂载卷,要手动挂载 -v 卷名:容器内路径 !
[root@docker ~]#cd /var/lib/docker/volumes/34c1f9f1424ff8b1fad859f47a5f9f3f0487136bcf6635a5bfee776a68344f84/_data
[root@docker _data]#ls
container.txt
1.5 数据卷容器
#启动3个容器,通过我们刚才自己写的镜像启动
crtl+p+q 退出
[root@99c7d5781382 /]# [root@docker _data]#
进入到volume01 创建一个文件之后再volume 02 对应的文件中也可以看到,反之亦然。
启动一个docker03
[root@docker ~]#docker run -it --name docker03 --volumes-from docker01 kuangshen/centos:1.0
#测试,可以删除docker01 ,查看一下docker02和docker03是否还是可以访问这个文件夹
#因为是一个拷贝的概念,所以应该是可以访问的
多个数据库实现数据库的共享
[root@docker ~]#docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 --volume-from mysql:5.7 mysql:5.7
[root@docker ~]#docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volume-from mysql:5.7 mysql:5.7
#这个时候可以实现两个容器数据同步
结论:
容器之间配置信息的传递,数据卷容器之间的生命周期一直持续到没有容器使用为止。
一旦持久化到了本地,这个时候本地的数据是不会删除的。
2.0 dockerfile
dockerfile 是用来构建镜像的文件,命令参数脚本
构建步骤:
1,编写一个dockerfile文件
2,docker build 构建成为一个镜像
3,docker run 运行镜像
4,docker push发布镜像
查看下官方是怎么做的(以centos为例)
相关网址:https://hub.docker.com/_/centos
官方的部署镜像都是基础包很多功能都没有的,我通常是搭建自己的镜像1
官方既然可以制作镜像那我们也是可以制作的!
2.1 dockerfile构建过程
基础知识
1,每个保留关键字(指令)都必须是大写字母
2,执行从上到下的顺序执行
3, #
表示注释
4,每一个指令都会提交一个新的镜像层,并提交!
dockerfile 是面向开发的,我们以后发布项目,做镜像,就需要编写dockerfile文件,这个文件是十分简单的!
docker 镜像逐渐成为一个企业交付的标准,必须要掌握。
dockerfile:构建文件,定义了一切步骤,源代码
dockermages:通过dockerfile构建生成的镜像,最终发布和运行的产品。
docker容器:容器就是镜像运行起来提供服务器
2.2 dockerfile指令
以前都是使用别人的,现在我们知道这些指令之后就可以来练习自己写一个镜像了!
FROM #基础镜像,一切从这里开始构建 centos
MANINTAINER #镜像是谁写的,姓名+邮箱
RUN #镜像构建的时候运行的命令
ADD #步骤,Tomcat镜像,这个Tomcat压缩包!添加内容
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPORE #暴露端口配置
CMD #指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTERYPOINT #指定这个容器启动的时候要运行的命令,可以直接追加命令。
ONBUILD #当构建一个被继承 dockerfile 这个之后, ONBUILD 的指令。触发指令
COPY #类似ADD ,将我们文件拷贝到镜像中
ENV #构建的时候设置环境变量!
2.3 实战测试 1 centos
docker hub中99%的镜像都是从这个基础镜像过来的 FROM scrath ,然后配置需要的软件和配置进行构建
创建一个自己的centos
#1,编写dockerfile的文件
[root@docker dockerfile]#cat mydockerfile-centos
FROM centos:7
MAINTAINER kuangshen<24736743@qq.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
一开始按照视频的文档操作的,但是会出现报错,
百度查询是因为centos版本的问题,需要指定版本,不然拉取的镜像是最新的,现在的最新的镜像是centos8版本的。具体可以参考下这个文档有介绍https://www.codeleading.com/article/78936327038/
#2,通过这个文件构建镜像
对比,官方的和自己构建的镜像的区别。
使用官方默认的镜像进入到的是根目录(/),且很多命令都使用不了
查看您自己写的镜像会直接进入到设定的目录(usr/local),还有添加的命令也可以使用了。
可以通过列出本地进行的变更历史(以centos为例)
docker history 容器id
平时拿到一个镜像,可以研究下它是怎么做的了。
CMD 和 ENTERYPOINT 区别
CMD #指定这个容器启动的时候要运行的命令,只有最后一个会生效,可以被替代
ENTERYPOINT #指定这个容器启动的时候要运行的命令,可以追加命令
#编写dockerfile文件
[root@docker dockerfile]#vim dockerfile-cmd-test
[root@docker dockerfile]#cat !$
cat dockerfile-cmd-test
FROM centos
CMD ["ls","-l"]
#构建镜像
[root@docker dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest .
#run 运行,发现我们的ls -l 命令生效
[root@docker dockerfile]#docker run eef8b17feb1d
total 48
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 13 03:16 dev
drwxr-xr-x 1 root root 4096 Jul 13 03:16 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 4096 Sep 15 2021 lost+found
drwxr-xr-x 2 root root 4096 Nov 3 2020 media
drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt
,,,,,
#需要追加一个命令 -a 实现 ls -al的效果
[root@docker dockerfile]#docker run eef8b17feb1d -a
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "-a": executable file not found in $PATH: unknown.
ERRO[0000] error waiting for container:
#cmd的清理下 -l 替换了CMD ["ls","-l"] 命令,-l 不是命令,所以报错
#后面将所有的命令都写完整之后就可以执行命令了
[root@docker dockerfile]#docker run eef8b17feb1d ls -al
total 56
drwxr-xr-x 1 root root 4096 Jul 13 03:22 .
drwxr-xr-x 1 root root 4096 Jul 13 03:22 ..
-rwxr-xr-x 1 root root 0 Jul 13 03:22 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 13 03:22 dev
drwxr-xr-x 1 root root 4096 Jul 13 03:22 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 4096 Sep 15 2021 lost+found
drwxr-xr-x 2 root root 4096 Nov 3 2020 media
,,,,,,
测试 ENTERYPOINT
编辑个使用 ENTERYPOINT 的 dockerfile 文件
[root@docker dockerfile]#vim dockerfile-cmd-entrypoint
[root@docker dockerfile]#cat dockerfile-cmd-entrypoint
FROM centos
ENTRYPOINT ["ls","-a"]
[root@docker dockerfile]#docker build -f dockerfile-cmd-entrypoint -t entorypoint-test .
#run运行下这个镜像
[root@docker dockerfile]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
entorypoint-test latest b325f5b97233 22 months ago 231MB
,,,,,/
[root@docker dockerfile]#docker run b325f5b97233
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
,,,,,//
#发现可以直接追加命令,不写原生的命令时,不会报错
[root@docker dockerfile]#docker run b325f5b97233 -l
total 56
drwxr-xr-x 1 root root 4096 Jul 13 03:29 .
drwxr-xr-x 1 root root 4096 Jul 13 03:29 ..
-rwxr-xr-x 1 root root 0 Jul 13 03:29 .dockerenv
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 13 03:29 dev
drwxr-xr-x 1 root root 4096 Jul 13 03:29 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 4096 Sep 15 2021 lost+found
,,,,,/
dockerfile中很多命令都十分的相似,我们需要了解它们的区别。我们最好的学习就是对比它们然后测试效果
2.4 实战测试 2 Tomcat镜像
1,准备镜像文件 Tomcat压缩包 ,jdk 的压缩包!
[root@docker tomcat]#ls -l
total 151172
-rw-r--r-- 1 root root 11684406 Jul 13 11:54 apache-tomcat-9.0.78.tar.gz
-rw-r--r-- 1 root root 143111803 Jul 13 11:43 jdk-8u261-linux-x64.tar.gz
-rw-r--r-- 1 root root 0 Jul 13 12:04 readme.txt
[root@docker tomcat]#pwd
/home/kuangshen/bulid/tomcat
[root@docker tomcat]#
2,编写docker文件,官方命名Dockerfile
,bulid 会自动寻找这个文件,就不需要 -f 指定了。
[root@docker tomcat]#vim Dockerfile
[root@docker tomcat]#cat Dockerfile #注释为做笔记添加的,测试可以不添加注释
#这里因为要下载centos的命令,所以要指定一下版本 ,视频中没有添加版本,具体原因前面有说过,看实战1中有介绍,因为在这里又犯了同样的错误,所以再次记录下。
FROM centos:7
MAINTAINER kuangshen<24736743@qq.com>
#把宿主机当前上下文的read.txt拷贝到容器/usr/local/路径下
COPY readme.txt /usr/local/readme.txt
#把java与tomcat添加到容器中
ADD jdk-8u261-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.78.tar.gz /usr/local/
#安装vim编辑器
RUN yum -y install vim
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_261
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.78
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.78
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.22/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.22/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.78/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.78/logs/catalina.out
3,构建镜像
[root@docker tomcat]#docker build -t diytomcat .
查看下镜像就会发现有构建成功自己刚才制作的镜像了
[root@docker tomcat]#docker images
4,启动镜像
5,访问测试
6,发布项目(由于做了卷挂载,我们直接在本地编写项目即可)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello,kuangshen</title>
</head>
<body>
Hello World!<br/>
<%
System.out.print("----my test web logs-----");
%>
</body>
</html>
此段实验未成功访问编写的文件,视频链接:https://www.bilibili.com/video/BV1og4y1q7M4?p=30&vd_source=1dedd20010b1fbe27e026c9eb33734dc
卡在了图示位置;
应该是挂载文件的路径问题,但是没有检查到,要么就是dockerfile文件编写有些问题,或者是其他为止的问题,这里赶时间就先放一放,有时间在看看,现在也不怎么用tomcat。以后学习这块用到了再来研究下。
2.5 发布镜像
dockhub
1, 地址 https://hub.docker.com/
2,确定这个账号可以登录
3,在我们的服务器上提交自己的镜像
[root@docker tomcat]#
[root@docker tomcat]#docker login --help
Usage: docker login [OPTIONS] [SERVER]
Log in to a 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
[root@docker tomcat]#docker login -u qianbailan
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
4,登录完成之后就可以上传镜像了,就是一步一步docker push
[root@docker tomcat]#docker push qianbailan/mycentos:0.1
The push refers to repository [docker.io/qianbailan/mycentos]
7b99cf138015: Pushing [===========> ] 42.85MB/184.7MB
6e0e5518be0b: Pushing [========> ] 46.65MB/268.9MB
5f70bf18a086: Pushed
174f56854903: Pushing [=========> ] 36.96MB/203.9MB
#push 自己的镜像到自己的服务器上
[root@docker tomcat]#docker push diytomcat #直接push
Using default tag: latest
The push refers to repository [docker.io/library/diytomcat]
5f70bf18a086: Preparing
7a7fcc2859a6: Preparing
39c0f0c6bda4: Preparing
fd188aa8c969: Preparing
dae17c78a809: Preparing
174f56854903: Waiting
denied: requested access to the resource is denied #拒绝
#push出现的问题?
[root@docker tomcat]#docker push kuangshen/diytomcat:1.0
The push refers to repository [docker.io/kuangshen/diytomcat]
An image does not exist locally with the tag: kuangshen/diytomcat
##解决办法,添加一个标签(这里换一个镜像上传)
[root@docker tomcat]#docker tag d214babc2c22 qianbailan/mycentos:0.1
#然后测试就可以直接上传成功了。
[root@docker tomcat]#docker push qianbailan/mycentos:0.1
The push refers to repository [docker.io/qianbailan/mycentos]
7b99cf138015: Pushing [===========> ] 42.85MB/184.7MB
6e0e5518be0b: Pushing [========> ] 46.65MB/268.9MB
5f70bf18a086: Pushed
174f56854903: Pushing [=========> ] 36.96MB/203.9MB
提交的时候也是按照镜像的层级来进行提交的
5,退出账号
[root@docker tomcat]#docker logout
Removing login credentials for https://index.docker.io/v1/
阿里云镜像服务上
1,,登录阿里云:https://www.aliyun.com/
2,找到容器镜像服务
3,创建命名空间
4,创建容器镜像
选择本地仓库就好了
5,浏览阿里云,参考这个就ok了
示例:
测试
可以看到这个有镜像的版本
2.6 docker 流程小结
3.0 docker 网络
3.1 理解docker 0
准备工作:清空所有的容器,清除所有的镜像
docker rm -f $(docker ps -a -q) # 删除所有容器
docker rmi -f $(docker images -qa) # 删除全部镜像
三个网络
#问题,docker是如何处理容器网络访问的
![image-20230713202739043](https://typorabo.oss-cn-chengdu.aliyuncs.com/img_for_typora/image-20230713202739043.png)
启动tomcat01 #这里借用一些笔记,自己测试是也是一样的。
[root@kuangshen ~]# docker run -d -P --name tomcat01 tomcat
# 查看tomcat01的ip地址,docker会给每个容器都分配一个ip!
[root@kuangshen ~]# 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
122: eth0@if123: <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
# 思考,我们的linux服务器是否可以ping通容器内的tomcat ?
[root@kuangshen ~]# 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.070 ms # 可以ping通!
原理
1,我们每启动一个容器,docker就会给docker容器分配一个IP,我们只要安装了docker,就会有一个网卡docker0,桥接模式。使用的是evth_pair技术!
2,再次启动一个容器,会发现多了一对网卡。
我们发现这个容带来的网卡,都是一对的
#evth-pair 就是一对的虚拟设备接口,它们都是一对的,一段连着协议,一段彼此相连
#正因为有这个特性,ecth-pair充当一个桥梁,连接着各种虚拟网络设备的
#OpenStac,docker容器之间的连接。ovs的连接,都是使用ecth-pair技术
3,我们测试下,tomcat01和tomcat02 是否可以ping通
[root@kuangshen ~]# docker exec -it tomcat02 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.110 ms
# 结论:容器和容器之间是可以互相访问的。
网络模型图
结论:tomcat1和tomcat2共用一个路由器。是的,他们使用的一个,就是docker0。任何一个容器启动 默认都是docker0网络。 docker默认会给容器分配一个可用ip。
小结
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据 Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网 关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接 通信。
最多可以分配65535个
3.2 --link
思考一个问题,我们编写了一个微服务,database url=ip ,项目不重启,数据库IP换掉了,我们希望处理这个问题,可以用服务的名字来访问容器?
# 我们使用tomcat02,直接通过容器名ping tomcat01,不使用ip
[root@kuangshen ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known # 发现ping不通
# 我们再启动一个tomcat03,但是启动的时候连接tomcat02
[root@kuangshen ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
a3a4a17a2b707766ad4f2bb967ce1d94f658cd4cccef3bb8707395cdc71fa1e7
# 这个时候,我们就可以使用tomcat03 ping通tomcat02 了
[root@kuangshen ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.18.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.18.0.3): icmp_seq=1 ttl=64 time=0.093 ms
64 bytes from tomcat02 (172.18.0.3): icmp_seq=2 ttl=64 time=0.066 ms
# 再来测试,tomcat03 是否可以ping tomcat01 失败
[root@kuangshen ~]# docker exec -it tomcat03 ping tomcat01
ping: tomcat01: Name or service not known
# 再来测试,tomcat02 是否可以ping tomcat03 反向也ping不通
[root@kuangshen ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
思考,这个原理是什么呢?我们进入tomcat03中查看下host配置文件
[root@kuangshen ~]# 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.18.0.3 tomcat02 b80da266a3ad # 发现tomcat2直接被写在这里
172.18.0.4 a3a4a17a2b70
# 所以这里其实就是配置了一个 hosts 地址而已!
# 原因:--link的时候,直接把需要link的主机的域名和ip直接配置到了hosts文件中了。
–link早都过时了,我们不推荐使用!我们可以使用自定义网络的方式
3.3 自定义网络
基础命令查看
命令如下:
查看所有网络
[root@docker /]#docker network ls
NETWORK ID NAME DRIVER SCOPE
f05e1df218a3 bridge bridge local
8908b52ae00e host host local
596d3188502b none null local
所有网路模式
网络模式 | 配置 | 说明 |
---|---|---|
birdge模式 | –net=birdge | 默认值,在docker网桥docker0上为容器创建新的网络栈 |
none模式 | –net=none | 不配置网络,用户可以稍后进入容器 ,自行配置 |
container模式 | –net=container:name/id | 容器和另一个共享的network namespace。kubernetes 中pod就是多个容器共享一个network namespace 容器间连接,局限性较大。 |
hosts模式 | –net=host | 容器和宿主机共享network namespace |
用户自定义 | –net=自定义网络 | 用户可以自己使用network namespace |
测试
#我们直接启动的命令 --net bridge ,而这个就是我们的docker0
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat01 tomcat --net bridge tomcat
#docker0特点,默认,域名不能访问,--link可以打通连接!
#我们可以自定义一个网络!
#--driver bridge
#--subnet 192.168.0.0/16
#-gateway 192.168.0.1
[root@docker /]#docker network create --driver bridge --subnet 192.168.0.0/016 --gateway 192.168.0.1 mynet
81fd1d2b37bba8e4ec753b3d442c90919c49bfd700c2d45046146fb3f630ba1e
[root@docker /]#docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@docker /]#docker network ls
NETWORK ID NAME DRIVER SCOPE
f05e1df218a3 bridge bridge local
8908b52ae00e host host local
81fd1d2b37bb mynet bridge local
596d3188502b none null local
我们自己的网络就创建好了
[root@docker /]#docker run -d -P --name tomcat-net-01 --net mynet tomcat
f17ac5de08cd3bef347610bef12a6ffe630c449e47f7d762db24b374ffd7a496
[root@docker /]#docker run -d -P --name tomcat-net-02 --net mynet tomcat
9eaf4379f4b018e8e606f3b3e1cbae2bfea4a12280a45d1dbd7045561aa646b4
[root@docker /]#docker network inspect mynet
# 我们来测试ping容器名和ip试试,都可以ping通
[root@kuangshen ~]# 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.093 ms
[root@kuangshen ~]# 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.063 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64
time=0.066 ms
# 发现,我们自定义的网络docker都已经帮我们维护好了对应的关系
# 所以我们平时都可以这样使用网络,不使用--link效果一样,所有东西实时维护好,直接域名 ping通
3.4 网络连通
docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离: 大家公司项目部署的业务都非常多,假设我们有一个商城,我们会有订单业务(操作不同数据),会有 订单业务购物车业务(操作不同缓存)。如果在一个网络下,有的程序猿的恶意代码就不能防止了,所 以我们就在部署的时候网络隔离,创建两个桥接网卡,比如订单业务(里面的数据库,redis,mq,全 部业务 都在order-net网络下)其他业务在其他网络。 那关键的问题来了,如何让 tomcat-net-01 访问 tomcat1?
# 启动默认的容器,在docker0网络下
[root@kuangshen ~]# docker run -d -P --name tomcat01 tomcat
bcd122e0dcf6bf8c861eaa934911f98a5497a4954f3fde9575e496160bd23287
[root@kuangshen ~]# docker run -d -P --name tomcat02 tomcat
6183aaeca003a3e5a3549a37f9c1040551320ae358807b4aaad547a986afb887
结论:如果要跨网络操作别人,就需要使用 docker network connect [OPTIONS] NETWORK CONTAINER
连接
3.5 实战:部署一个Redis集群
#创建网卡
[root@docker /]#docker network create redis --subnet 172.38.0.0/16
04cc6fef5beadc9a699063242b798a195b703c399242170ccfb85633544a3984
[root@docker /]#docker network ls
NETWORK ID NAME DRIVER SCOPE
f05e1df218a3 bridge bridge local
8908b52ae00e host host local
81fd1d2b37bb mynet bridge local
596d3188502b none null local
04cc6fef5bea redis bridge local
#通过脚本创建6个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个容器
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
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
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
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
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
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
#进入关于Redis,注意这里使用 sh命令
[root@docker ~]#docker exec -it redis-1 /bin/sh
/data # ls
appendonly.aof dump.rdb nodes.conf
/data # cd nodes.conf
/bin/sh: cd: can't cd to nodes.conf: Not a directory
/data # ls
appendonly.aof dump.rdb nodes.conf
/data #
##查看和确定配置文件
[root@docker ~]#cd /mydata/
[root@docker mydata]#ls
redis
[root@docker mydata]#cd redis/
[root@docker redis]#ls
node-02 node-1 node-2 node-3 node-4 node-5 node-6
[root@docker redis]#cd node-1
[root@docker node-1]#ls
conf data
[root@docker node-1]#ls
conf data
[root@docker node-1]#cd conf/
[root@docker conf]#ls
redis.conf
[root@docker conf]#cat 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.11
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
[root@docker conf]#
#创建集群
[root@docker conf]#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 1
72.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: fab1c11958e0a5996b87e63487939bddf23ccf46 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 2859d5c2de72356f93b3c18a08003b31f613f428 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: 8433d56bd2a499618c2e3c0f74bf5a20247ad441 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: 11599ce1e7e4530ecc67463a72eadbeab82e12ee 172.38.0.14:6379
replicates 8433d56bd2a499618c2e3c0f74bf5a20247ad441
S: b717bf8aa2fa976134beaf922f523d856be3332e 172.38.0.15:6379
replicates fab1c11958e0a5996b87e63487939bddf23ccf46
S: de6c6d0e5066eb915da0c91610737056bf691b6b 172.38.0.16:6379
replicates 2859d5c2de72356f93b3c18a08003b31f613f428
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: fab1c11958e0a5996b87e63487939bddf23ccf46 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: b717bf8aa2fa976134beaf922f523d856be3332e 172.38.0.15:6379
slots: (0 slots) slave
replicates fab1c11958e0a5996b87e63487939bddf23ccf46
S: de6c6d0e5066eb915da0c91610737056bf691b6b 172.38.0.16:6379
slots: (0 slots) slave
replicates 2859d5c2de72356f93b3c18a08003b31f613f428
M: 8433d56bd2a499618c2e3c0f74bf5a20247ad441 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 2859d5c2de72356f93b3c18a08003b31f613f428 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 11599ce1e7e4530ecc67463a72eadbeab82e12ee 172.38.0.14:6379
slots: (0 slots) slave
replicates 8433d56bd2a499618c2e3c0f74bf5a20247ad441
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
/data #
#此时集群就已经创建好了,进入集群测试看看
/data # redis-cli -c #进入集群
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:182
cluster_stats_messages_pong_sent:187
cluster_stats_messages_sent:369
cluster_stats_messages_ping_received:182
cluster_stats_messages_pong_received:182
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:369
127.0.0.1:6379> cluster nodes #进入一个节点
b717bf8aa2fa976134beaf922f523d856be3332e 172.38.0.15:6379@16379 slave fab1c11958e0a5996b87e63487939bddf23ccf46 0 1689301626000 5 connected
fab1c11958e0a5996b87e63487939bddf23ccf46 172.38.0.11:6379@16379 myself,master - 0 1689301626000 1 connected 0-5460
de6c6d0e5066eb915da0c91610737056bf691b6b 172.38.0.16:6379@16379 slave 2859d5c2de72356f93b3c18a08003b31f613f428 0 1689301625000 6 connected
8433d56bd2a499618c2e3c0f74bf5a20247ad441 172.38.0.13:6379@16379 master - 0 1689301626562 3 connected 10923-16383
2859d5c2de72356f93b3c18a08003b31f613f428 172.38.0.12:6379@16379 master - 0 1689301625659 2 connected 5461-10922
11599ce1e7e4530ecc67463a72eadbeab82e12ee 172.38.0.14:6379@16379 slave 8433d56bd2a499618c2e3c0f74bf5a20247ad441 0 1689301624556 4 connected
127.0.0.1:6379> set a b #此已经进入了一个节点, set 一个值看看 ,发现是 `172.38.0.13:6379 ` 这个节点存储了数据。
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
172.38.0.13:6379>
#此时再打开一个会话,进入到宿主机的命令行界面,把172.38.0.13:6379 这个节点关闭掉
[root@docker ~]#docker stop redis-3 #关闭节点的服务
redis-3
[root@docker ~]# #此时主机redis-3已经是挂掉的
#转到集群的界面,重新get 一下 a
172.38.0.13:6379> get a #因为集群已经是挂掉的,所以退出了
Error: Operation timed out
/data # redis-cli -c #从新进入一个节点
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b" #发现此时已经是get到了 a ,在172.38.0.14:6379 这个节点get 到的,因为 14 是13的从机
172.38.0.14:6379>
#此时搭建Redis集群是完成的,高可用测试是正常可使用的。
4.0 IDEA整合Docker
4.1 创建镜像
1、使用 IDEA 构建一个 SpringBoot 项目
2、编写一个helloController
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello,kuangshen";
}
}
3、启动测试下,端口修改下,避免8080冲突!本地访问没问题就可以!
4、打jar包
有了 jar 包,我们就可以作镜像了! 记得测试一下jar包可以使用吗!
4.2 打包镜像
1、在项目下编写 Dockerfile 文件,将打包好的jar包拷贝到Dockerfile同级目录
FROM java:8
# 服务器只有dockerfile和jar在同级目录
COPY *.jar /app.jar
CMD ["--server.port=8080"]
# 指定容器内要暴露的端口
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
2、将Dockerfile 和 项目的 jar 包上传到linux服务器上,构建运行
[root@kuangshen idea]# pwd
/home/idea
[root@kuangshen idea]# ll
total 17228
-rw-r--r-- 1 root root 17634294 May 14 12:33 demo-0.0.1-SNAPSHOT.jar
-rw-r--r-- 1 root root 207 May 14 12:32 Dockerfile
# 构建镜像
docker build -t idea-ks .
# 查看镜像
docker images
# 运行
docker run -d -P --name idea-ks idea-ks
[root@kuangshen ~]# docker ps
CONTAINER ID IMAGE PORTS NAMES
2366c960ba99 idea-ks 0.0.0.0:32779->8080/tcp idea-ks1
# 测试访问
[root@kuangshen ~]# curl localhost:32779
[root@kuangshen ~]# curl localhost:32779/hello
4.3 IDEA安装插件
了解即可!以后CI/CD,就完全没必要这样做!
1、IDEA安装插件
2、配置docker连接集成
3、集成了docker插件就可以在IDEA中操作Docker内部的容器和镜像了,但是很鸡肋这个功能,对于我 们开发人员来说! 之后学习的CI/CD才是真正在企业中的王道