docker从入门到熟练
一、为什么要用docker
1.jar+环境一起打包,直接运行镜像即可
2.每个容器间相互隔离,每个容器都有一个属于自己的文件系统,互不影响
3.通过隔离机制,可以将服务器内存利用到极致
4.容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟硬件(docker和虚拟机都是虚拟化技术,docker非常轻巧(最核心的环境+jdk+mysql...),虚拟机技术缺点:1、资源占用多 2、冗余步骤多 3、启动很慢)
二、docker优点
1、应用更快速的交付和部署:打包镜像发布测试,一键运行。
2、更快捷的升级和扩容。
3、更简单的系统运维:容器化之后,开发、测试环境都是高度一致的
4、更高效的计算机资源利用:docker是内核级别的虚拟化,服务器的性能可以被压榨到机制
三、安装docker
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 http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
4.安装docker
先更新软件包索引:yum makecache fast
安装:yum install docker-ce docker-ce-cli containerd.io
5.启动docker
systemctl start docker
6.查看是否安装成功
docker version
7.测试hell-world
docker run hello-world
8.查看镜像
docker images
9.卸载docker
-yum remove docker-ce docker-ce-cli containerd.io
-
rm -rf /var/lib/docker
- rm -rf /var/lib/containerd
10.配置阿里云镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{ "registry-mirrors": ["https://"] } EOF
sudo systemctl daemon-reload
sudosystemctl restart docker
四、docker工作原理
docker是一个client-server结构的系统,docker的守护进程运行在主机上,通过socket从客户端访问,dockerserver接收到docker-client的指令,就会去执行
docker为什么比vm快?
-docker的抽象层比vm更少
-docker利用的是宿主机的内核(秒级),vm需要加载Guest OS(分钟级别)。
五、docker常用命令
帮助命令
docker version 显示docker的版本信息
docker info 显示docker的系统信息,包括容器和镜像
docker [] --help 帮助命令
镜像命令
docker images[] 查看所有本地主机上的镜像
-a 显示所有的镜像
-q 只显示镜像id
-aq 显示所有镜像的id
docker search 搜索镜像
--filter=STARS=3000 搜索条件为STARTS3000以上的
docker pull 镜像名:版本号 指定tag下载,不使用latest(分层下载)
docker rmi -f 镜像id
docker rmi -f $(docker images -aq) 删除所有docker镜像
容器命令
docker run [] image
--name="name" 指定容器名字启动
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容 (交互运行要指定控制台 /bin/bash)
-p
-p ip:主机端口:容器端口
-p 主机端口:容器端口
-p 容器端口
容器端口
-P 随机指定端口
docker ps 查看正在运行的容器
docker ps -a 查看所有的容器(包含历史运行过的容器)
docker ps -a -n=1显示出最近创建的1个容器
docker ps -aq显示所有容器的id
exit:直接退出容器
ctrl+p+q:容器不停止退出
docker rm 容器id,不能删除正在运行的容器,要加-f才行
docker rm -f $(docker ps -aq)删除所有的容器
docker ps -a -q|xargs docker rm 删除所有的容器
docker start 容器id 启动
docker restart 容器id 重启
docker stop 容器id 停止正在运行的
docker kill 容器id 强制停止当前容器
docker常用的其他命令
docker run -d centos:容器停止了,docker容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
#nginx容器启动后发现自己没有提供服务,就会立刻停止
docker run -d centos /bin/sh -c "while true;do echo mj666;sleep 1;done"脚本运行
查看日志:docker logs -tf --tail (最近条数) 容器id
-t显示日志
-f显示时间戳
查看容器中的进程信息:docker top 容器id
查看容器的元数据:docker inspect 容器id
进入当前正在运行的容器:docker exec -it 容器id/(容器name) /bin/bash (进入容器后开启一个新的终端,可以在里面操作)
docker attach 容器id(进入容器正在执行的终端,不会启动新的进程)
从容器内拷贝文件到主机上:docker cp 容器id:容器内路径 目的主机路径(只要容器还在数据就在,不管是否是启动状态)
六、常用软件安装
Nginx安装:1.docker pull nginx
2.docker run -d --name nginx01 -p 9092:80 nginx
3.curl localhost:9092 查看
4.docker exec -it 容器id/(容器name) /bin/bash 进入容器之后whereis nginx 查看文件目录
Tomcat安装:1.docker pull nginx:9.0(docker run -it --rm tomcat:9.0(run命令会帮我们自动下载),一般用来测试用完就删除)
2.docker run -d --name tomcat01 -p 9093:8080 tomcat
3.docker exec -it tomcat01 /bin/bash 进入容器之后发现命令缺少,没有webapps,原因阿里云默认是下载最小的镜像,所有不必要的都剔除掉了。
4.cp -r(递归) webapps.dist/* webapps 递归拷贝webapps.dist下的内容到webapps目录下,发现可以访问到了
部署es+kibana:
es安装:
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2
启动之后直接卡死,docker stats 查看cpu的状态,原因是es十分耗内存
**docker run -d --name elasticsearch007 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64M -Xms512M" elasticsearch:7.6.2
kibana安装:连接es,后面再操作??????
七、可视化管理工具:
portainer:docker图形化管理工具
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
Rancher(CI/CD再使用):???
八、docker镜像
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,包含某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件,所有的应用,直接打包docker镜像就可以直接运行
镜像获得方式:远程仓库下载或者通过dockerfile制作
UnionFS是docker镜像的基础,docker镜像实际上由一层一层的文件系统组成。
docker镜像加载原理:
bootfs包含bootloader和kernel,linux刚启动之后会加载bootfs,bootfs是docker镜像的最底层,当boot加载完之后整个内核就在内存中了,此时内存的使用权就由bootfs转给内核,此时系统卸载掉bootfs
rootfs(基础镜像)就是各种操作系统的发行版,比如Ubuntu,Centos等。
docker的rootfs可以很小,只需要包含基本的命令,工具和程序,因为底层用的是Host的kernel。
分层理解:
tomcat为例,tomcat分层下载是6层,镜像是只读的,这时启动容器,一个新的可写层(容器层)被加载到镜像的顶部(commit把镜像层和修改过后的容器层打包为一个新的镜像层)。
commit镜像:
docker commit 提交容器成为一个新的副本
docker commit -m ="提交的描述信息" -a="作者" 容器id 目标镜像名:[版本号]
1.启动一个默认的tomcat
2.发现这个默认的tomcat没有webapps应用,这个是官方镜像的原因
3.这个时候我们进入容器拷贝webapps.dist下内容到webapps目录
4.将我们操作过的容器通过commit提交为一个新的镜像
九、docker容器数据卷
数据卷技术将容器内的目录,挂载到Linux上,实现数据的共享,数据相互同步。
作用:容器数据的持久化和同步操作,容器之间也可以共享数据
使用数据卷:
方式一:docker run -it -v /home/ceshi(宿主机目录):/home(容器目录) centos /bin/bash (cat查看文件信息)
-停止容器后
-宿主机上修改文件
-启动容器
-容器内的数据依旧是同步的
好处:只需要修改本地,容器内会自动同步
安装mysql:
docker run -d -p 9095:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=panghu --name mysql01 mysql:5.7
删除容器后,挂载到本地的数据卷没有丢失,即实现了容器数据持久化功能
**具名挂载和匿名挂载:
匿名挂载:docker run -d -P --name nginx01 -v /etc/nginx nginx
查看所有的volume的情况:docker volume ls
-v后面不指定宿主机目录,只指明容器内路径
![]()
具名挂载:docker run -d -P --name nginx02 -v juming-nginx(卷名):/etc/nginx nginx
docker volume ls
查看容器信息:docker inspect 容器name ,发现挂载的位置在/var/lib/docker/volumes/xxx(卷名)/_data(存放数据的目录)
通过具名挂载可以很方便的找到volume,大多数情况都采用具名挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径 #具名挂载
-v 宿主机路径:容器内路径 #指定路径挂载
ro:只读,容器内部无法操作,只能通过宿主机来操作
rw:可读可写
一旦设置了容器权限,容器对我们挂载出来的内容就有限定了
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
(pwd 指令可立刻得知您目前所在的工作目录的绝对路径名称)
十、dockerfile入门
dockerfile就是用来构建docker镜像的命令脚本文件
通过这个脚本可以生成镜像,镜像是一层一层的,脚本是一个一个的命令,一个命令构建一层
vim 构建并写好dockerfile文件后
![]()
通过docker build -f dockerfile1 -t /mj999/centos .
运行容器查看文件目录,发现volume01和volume02(匿名挂载),这个卷一定有一个同步的宿主机目录,在volume01下创建一个文件container.txt文件
通过查看容器信息docker inspect,发现了匿名挂载的目录位置跟刚才一样/var/lib/docker/volumes/xxx/_data
进入目录发现创建成功,数据挂载同步成功
创建自己的镜像时经常使用这种挂载数据卷方式,假设构建镜像时没有挂载卷,要手动挂载 -v 卷名:容器内路径名
十一、数据卷容器(用于容器之间的数据相互共享)(类似于拷贝文件)
创建第一个数据卷容器(父容器)
docker run -it --name docker01 mj999/centos
在volume01下创建一个文件docker01
(创建文件touch 创建目录mkdir删除一行命令ctrl+u)
创建第二个容器docker run -it --name docker02 --volumes-from docker01 mj999/centos(02挂载01数据卷容器)
进入volume01目录后发现docker01创建的内容docker02上面也有
![]()
删除docker01数据卷容器
![]()
发现docker02和docker03还是有数据
![]()
*多个mysql实现数据共享:
#docker run -d -p 9095:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=panghu --name mysql01 mysql:5.7
#
docker run -d -p 9095:3306 -e MYSQL_ROOT_PASSWORD=panghu --name mysql02 --volumes-from mysql01 mysql:5.7
结论:容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。一旦数据持久化到本地,本地的数据是不会被删除的。
十二、dockerfile
1)dockerfile是用来构建docker镜像文件的命令参数脚本,一行命令构建一层镜像
构建步骤:1.编写一个docker file 文件
2.docker build 构建成一个镜像
3.docker run 运行镜像
4.docker push 发布镜像(DockerHub、阿里云镜像仓库)
去dockerhub官网上查看dockerfile的编写
![]()
官方的很多镜像都是基础包,很多功能都没有,通常要搭建自己的镜像
*2)dockerfile构建过程
基础知识:1、每个保留关键字(指令)都必须是大写字母
2、执行从上到下顺序执行
3、#表示注释
4、每一个指令都会创建提交一个新的镜像层
dockerfile指令:
FROM #基础镜像,一切从这里开始构建
MAINTAINER #镜像的作者,姓名+邮箱
RUN #镜像构建的时候需要运行的命令
ADD #添加内容,例如制作tomcat镜像,添加tomcat压缩包
WORKDIR #镜像的工作目录
VOLUME #挂载的目录
EXPOSE #保留端口配置
CMD #指定这个容器启动的时候要运行的命令,一个cmd指令运行一个命令,只有最后一个cmd指令会生效,可以被替代
ENTRYPOINT #指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD #当构建一个被继承dockerfile,这个时候就会运行ONBUILD的指令,触发指令
COPY #类似ADD,将我们的文件拷贝到镜像中
ENV #构建的时候设置环境变量
3)创建一个文件目录,构建一个dockerfile制作镜像:
FROM centos
MAINTAINER mj666<18272046@163.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 mydockerfile -t mycentos:0.1 .
可以列出docker镜像的变更历史 docker history 镜像id(也可以用来查看官方的镜像制作历史)
4)cmd和entrypoint的区别:cmd追加命令会覆盖
entrypoint是直接追加
创建二个dockerfile制作镜像测试
cmd测试:
FROM centos
CMD ["ls","-a"]
entrypoint测试:
FROM centos
ENTRYPOINT ["ls","-a"]
制作完镜像之后分别使用run命令启动,没有什么区别,追加l命令后cmd的会报错,而entrypoint的正确执行了ll命令
5)实现tomcat镜像
1.准备镜像文件,tomcat的压缩包,jdk的压缩包
2.编写Dockerfile文件,官方命名,不需要再-f指定,build时自动寻找该文件
FROM centos
MAINTAINER mj999<1212@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u11-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.1.tar.gz /usr/local/
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.1
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.1
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.1/startup.sh && tail -F /usr/local/apache-tomcat-9.0.1/bin/logs/catalina.out
3.docker build -t 构建镜像 .
4.挂载数据卷启动
docker run -d -p 9096:8080 --name mjtomcat -v /home/mj999/build/tomcat/test:/usr/local/apache-tomcat-9.0.1/webapps/test -v /home/mj999/build/tomcat/logs:/usr/local/apache-tomcat-9.0.1/logs diytomcat
*启动失败通过docker logs 容器id查看原因
5.访问外网测试121.43.37.22:9096成功或者curl localhost:9096
6.发布项目,由于做了数据卷挂载,我们直接在本地编写项目就可以发布了。
-在 /home/mj999/build/tomcat/test目录下创建一个WEB-INF,去编辑一个web.xml文件
-在 /home/mj999/build/tomcat/test目录下创建一个jsp页面
#访问成功
#再看日志,打印成功
十三、发布镜像
**发布镜像到dockerhub
1.https://hub.docker.com/注册账号
2.登录账号
3.提交镜像docker push 到服务器上
4.docker tag 镜像id 作者/镜像名:版本号 (增加一个tag,又多了一个镜像,再重新push)
push成功
**push到阿里云仓库
1.登录阿里云打开镜像容器服务
2.创建个人实例
创建命名空间(最多创建3个)
创建镜像仓库
3.按照官网的操作指南操作
push成功
十四、docker网络
docker0
使用ip addr命令查看ip地址
lo:本机回环地址
eth0:阿里云内网地址
docker0:docker地址
![]()
启动一个tomcat:
docker run -d --name tomcat01 -P tomcat
查看容器ip
(容器中使用ip addr 报错:原因是tomcat版本问题
进入容器 apt update && apt install -y iproute2)
docker exec -it tomcat01 ip addr ,得到一个eth0@if131的ip地址,这是docker分配的。
直接在linux上ping容器内部,可以ping通。
**(1)linux可以ping通docker容器内部
原理:每启动一个docker容器,docker就会给容器分配一个ip,只要安装了docker就会有一个网卡docker0(桥接模式),使用的技术是veth-pair技术
在linux下执行ip addr,发现多了一个131,130
![]()
发现容器带的网卡,都是一对一对的。
veth-pair 就是一对的虚拟设备接口,我们都是成对出现的,一端连着协议,一端彼此相连
因为有这个特性,veth-pair可以充当桥梁,连接各种虚拟网络设备
OpenStac,docker容器之间的连接,ovs的连接,都是使用的veth-pair技术
(2)测试是否tomcat01和tomcat02之间是否能ping通,发现可以ping通
**容器和容器之间是可以进行通信的
结论:容器之间能通信是因为docker0作为共用路由器。(在容器和docker0分别创建一个虚拟网卡,通过vethpair进行连接,再通过docker0路由,容器之间就能通信了)
所有的容器不指定网络的情况下,都是docker0路由,docker0会给容器分配一个默认的可用ip。
docker使用的是linux的桥接,宿主机中是一个docker容器的网桥docker0(docker0和物理网卡是直连NAT)
docker中的所有网络接口都是虚拟的,虚拟的转发效率高。
只要容器删除,对应网桥一对就删除掉了。
(3)直接通过容器名ping,不能ping通
(容器缺少ping命令解决办法:apt-get update
apt install iputils-ping
apt install net-tools )
创建tomcat03用--link和tomcat02绑定:docker run -d -P --name tomcat03 --link tomcat02 tomcat
docker exec -it tomcat03 ping tomcat02
用3ping2发现能ping通
用2ping3发现不能ping通
结论:通过--link解决了网络连通问题,不使用ip,使用名字即可
(4)查看docker网络
发现了link连接
(5)通过查看host配置,--link操作就是增加了一个 172.17.0.3 tomcat02 4533e117ae50,所以能够通过名字ping通
--link已经不建议使用了。
*****自定义网络,不使用docker0,因为docker0不支持容器名连接访问。
查看docker的所有网络
网络模式:
bridge:桥接(默认,自己创建的网络也基本使用桥接)
none: 不配置网络
host: 和宿主机共享网络
container:容器网络连通(不常用)
测试:#docker run -d -P --name --net bridge tomcat(docker run -d -P --name tomcat 这是默认添加的,bridge就是docker0)
docker0:是默认的网络,域名不能访问,--link可以打通连接
自定义网络测试:docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
--driver 模式
--subnet 子网 192.168.0.2-192.168.255.254
--gateway 网关
查看刚才创建的网络
创建两个tomcat指定网络为mynet,再次查看mynet网络下多了2个容器
直接ping地址:能够ping通
直接ping名字:能够ping通
结论:自定义的网络docker已经帮我们维护好了对应关系
使用的好处:不同的集群使用不同的网络,保证集群是安全和健康的。
******网络连通(网卡之间不能打通,只能容器和网卡之间打通)
在docker0网络下创建2个tomcat容器,在mynet网络下创建2个容器,这个时候直接2个网络下的容器直接ping是ping不通的
这个时候就需要docker0网络下的容器连接到mynet网络
connect命令
测试打通tomcat03-mynet网络
连接之后发现容器直接加入进了mynet网络下,这就是一个容器两个ip地址
1和3之间直接可以ping通,4由于还没和mynet打通,所以不能ping通
结论:如果要跨网络操作他人,就需要使用docker network connect连通
十五、部署redis集群-3主3从
1.创建redis网卡
# docker network create redisnet --subnet 172.38.0.0/16(没有指定网络模式和网关)
2.通过脚本创建6个redis配置
for port in $(seq 1 6); \
do \
mkdir -p /redisdata/redis/node-${port}/conf
touch /redisdata/redis/node-${port}/conf/redis.conf
cat <<EOF>/redisdata/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
3.
脚本批量启动redis
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /redisdata/redis/node-${port}/data:/data \
-v /redisdata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redisnet --ip 172.38.0.1${port} redis:latest redis-server /etc/redis/redis.conf
一个一个启动redis
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /redisdata/redis/node-1/data:/data \
-v /redisdata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redisnet --ip 172.38.0.11 redis:latest redis-server /etc/redis/redis.conf
docker run -p 6372:6379 -p 16372:16379 --name redis-1 \
-v /redisdata/redis/node-2/data:/data \
-v /redisdata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redisnet --ip 172.38.0.13 redis:latest redis-server /etc/redis/redis.conf
6个节点全部启动完成
进入容器命令行
创建集群
# 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
连接集群
redis-cli -c
产看集群信息、节点信息
测试redis集群高可用:
往redis集群里面存一个a,值为b
关掉保存数据的redis节点
通过get命令取数据,发现从他的从机redis-4上拿到了数据,实现了redis集群的高可用
通过cluster nodes发现3号故障,master变成了4
docker搭建redis集群完成
十六、Springboot项目打包Docker镜像
1.构建springboot项目
2.打包应用-先clean再package
运行能访问
再命令黑窗运行测试一下,启动成功,这个时候这个打包就没有问题了
3.编写dockerfile
FROM java:8
COPY *.jar /app.jar
CMD["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT["java","-jar","/app.jar"]
4.构建镜像
docker build -t mj999 . 构建成功
5.发布运行
查看镜像
启动运行
docker run -d -P --name mjspringboot mj999
访问成功
curl localhost:49168/hello