Docker学习笔记:
Docker简介:
Docker是什么: 解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术;
容器与虚拟机比较:
1. 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
2. 内的应用进程直接运行于宿主的内核,容器内没有自己的内核且也没有进行硬件虚拟;因此容器要比传统虚拟机更为轻便;
3. 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源;
Docker安装:
1. yum安装gcc相关:
yum -y install gcc;
yum -y install gcc-c++;
2. 安装需要的软件包: yum install -y yum-utils;
3. 设置stable镜像仓库: yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo;
4. 更新yum软件包索引: yum makecache fast;
5. 安装Docker CE: yum -y install docker-ce docker-ce-cli containerd.io;
6. 启动Docker: systemctl start docker;
7. 测试:
docker version;
docker run hello-world;
Docker卸载 :
1. systemctl stop docker;
2. yum remove docker-ce docker-ce-cli containerd.io;
3. rm -rf /var/lib/docker;
4. rm -rf /var/lib/containerd;
Docker常用命令:
1. 帮助启动类命令:
1. 启动docker: systemctl start docker;
2. 停止docker: systemctl stop docker;
3. 重启docker: systemctl restart docker;
4. 查看docker状态: systemctl status docker;
5. 开机启动: systemctl enable docker;
6. 查看docker概要信息: docker info;
7. 查看docker总体帮助文档: docker --help;
8. 查看docker命令帮助文档: docker 具体命令 --help;
2. 镜像命令:
1. 查看所有镜像: docker images -aq (-a 列出本地所有镜像 -q 只显示镜像id);
2. 查找镜像: docker search 镜像名字 --limit 5 (--limit 限制搜索行数);
3. 拉取镜像: docker pull 镜像名字[:tag] (tag代表版本号可写可不写,不写就是最新版);
4. 查看镜像/容器/数据卷所占的空间: docker system df;
5. 删除镜像: docker rmi -f 镜像名1:TAG 镜像名2:TAG/镜像id1 镜像名id2
6. 删除全部镜像: docker rmi -f $(docker images -qa)
面试题:
谈谈docker虚悬镜像是什么?
答: 仓库名、标签都是<none>的镜像,俗称虚悬镜像dangling image;
3. 容器命令:
1. 新建+启动容器: docker run [options] image_name [command] [args] (options: --name="name" 为容器指定一个名称,-d 后台运行容器并返回容器ID,-i 以交互模式运行容器,-t 为容器重新分配一个伪输入终端,-P 随机端口映射,-p 指定端口映射);
2. 列出当前所有正在运行的容器: docker ps [options] (options: -a 列出当前所有正在运行的容器+历史上运行过的,-l 显示最近创建的容器,-n 显示最近n个创建的容器,-q 只显示容器编号);
3. 退出容器
1. run进去容器,exit退出,容器停止;
2. run进去容器,ctrl+p+q退出,容器不停止;
4. 启动已停止运行的容器: docker start 容器ID或者容器名;
5. 重启容器: docker restart 容器ID或者容器名;
6. 停止容器: docker stop 容器ID或者容器名;
7. 强制停止容器: docker kill 容器ID或容器名;
8. 删除已停止的容器: docker rm -f 容器ID;
9. 一次性删除多个容器实例: docker rm -f $(docker ps -aq);
10. 查看容器日志: docker logs 容器ID;
11. 查看容器内运行的进程: docker top 容器ID;
12. 查看容器内部细节: docker inspect 容器ID;
13. 进入正在运行的容器并以命令行交互:
1. docker exec -it 容器ID bashShell;
2. docker attach 容器ID;
3. 两个命令的区别:
1. attach 直接进入容器启动命令的终端,不会启动新的进程用exit退出,会导致容器的停止;
2. exec 是在容器中打开新的终端,并且可以启动新的进程用exit退出,不会导致容器的停止;
14. 从容器内拷贝文件到主机上: docker cp 容器ID:容器内路径 目的主机路径;
15. 导出容器: docker export 容器ID > 文件名.tar;
16. 导出容器: cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号;
Docker镜像:
1. Docker镜像是什么: 是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件;
2. 分层的镜像: 在pull镜像的过程中发现镜像是分层的;
3. UnionFS(联合文件系统)
1. UnionFS是什么: Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem);Union 文件系统是 Docker 镜像的基础;镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像;
2. UnionFS特点: 一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录;
4. Docker镜像加载原理:
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS
bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs
这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核;
当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs;
5. 为什么 Docker 镜像要采用这种分层结构呢: 镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用;
6. 重点理解:
1. Docker镜像层都是只读的,容器层是可写的;
2. 当容器启动时,一个新的可写层被加载到镜像的顶部;
3. 这一层通常被称作"容器层","容器层"之下的都叫"镜像层";
7. Docker镜像commit操作:
1. 作用: docker commit提交容器副本使之成为一个新的镜像;
2. 命令: docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名];
Docker容器数据卷:
1. 作用: 将docker容器内的数据保存进宿主机的磁盘中;
2. 用法: docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
3. 读写规则映射添加说明:
1. 读写(默认): docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:rw 镜像名;
2. 只读(容器实例内部被限制,只能读取不能写): docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名;
4. 卷的继承和共享: docker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu
Docker常规安装简介:
安装redis集群:
1. 3主3从redis集群配置
1. 新建6个docker容器redis实例:
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
命令解释:
docker run: 创建并运行docker容器实例
--name redis-node-6: 容器名字
--net host: 使用宿主机的IP和端口,默认
--privileged=true: 获取宿主机root用户权限
-v /data/redis/share/redis-node-6:/data: 容器卷,宿主机地址:docker内部地址
redis:6.0.8: redis镜像和版本号
--cluster-enabled yes: 开启redis集群
--appendonly yes: 开启持久化
--port 6386: redis端口号
2. 进入容器: docker exec -it redis-node-1 /bin/bash
3. 构建主从关系:
redis-cli --cluster create 192.168.111.147:6381 192.168.111.147:6382 192.168.111.147:6383 192.168.111.147:6384 192.168.111.147:6385 192.168.111.147:6386 --cluster-replicas 1
命令解释:
--cluster-replicas 1: 表示为每个master创建一个slave节点
4. 链接进入6381作为切入点,查看集群状态:
查看集群状态命令:
cluster info
cluster nodes
2. 主从扩容案例
1. 新建6387、6388两个节点:
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
2. 进入6387容器实例内部:
docker exec -it redis-node-7 /bin/bash
3. 将新增的6387节点(空槽号)作为master节点加入原集群:
redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
4. 重新分派槽号:
redis-cli --cluster reshard IP地址:端口号
5. 为主节点6387分配从节点6388:
redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID
3. 主从缩容案例:
1. 将6388删除:
redis-cli --cluster del-node ip:从机端口 从机6388节点ID
redis-cli --cluster del-node 192.168.111.147:6388 5d149074b7e57b802287d1797a874ed7a1a284a8
2. 将6387的槽号清空,重新分配:
redis-cli --cluster reshard 192.168.111.147:6381
3. 将6387删除:
redis-cli --cluster del-node ip:端口 6387节点ID
DockerFile:
1. DockerFile是什么: Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本
2. 构建三步骤:
1. 编写Dockerfile文件
2. docker build命令构建镜像
3. docker run依镜像运行容器实例
3. DockerFile构建过程解析
1. Dockerfile内容基础知识
1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2. 指令按照从上到下,顺序执行
3. #表示注释
4. 每条指令都会创建一个新的镜像层并对镜像进行提交
2. Docker执行Dockerfile的大致流程
1. docker从基础镜像运行一个容器
2. 执行一条指令并对容器作出修改
3. 执行类似docker commit的操作提交一个新的镜像层
4. docker再基于刚提交的镜像运行一个新容器
5. 执行dockerfile中的下一条指令直到所有指令都执行完成
4. DockerFile常用保留字指令
1. FROM: 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from
2. MAINTAINER: 镜像维护者的姓名和邮箱地址
3. RUN: 容器构建时需要运行的命令
1. RUN是在 docker build时运行
2. 两种格式:
1. shell格式: RUN yum -y install vim
2. exec格式: RUN ["可执行文件","参数1","参数2"]
4. EXPOSE: 当前容器对外暴露出的端口
5. WORKDIR: 指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
6. USER: 指定该镜像以什么样的用户去执行,如果都不指定,默认是root
7. ENV: 用来在构建镜像过程中设置环境变量
8. ADD: 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
9. COPY: 类似ADD,拷贝文件和目录到镜像中但不解压
10. VOLUME: 容器数据卷,用于数据保存和持久化工作
11. CMD: 指定容器启动后的要干的事情
1. Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
2. 它和前面RUN命令的区别: CMD是在 docker run 时运行,RUN是在 docker build 时运行
12. ENTRYPOINT: 也是用来指定一个容器启动时要运行的命令
1. 说明: 类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序
2. 优点: 在执行docker run的时候可以指定 ENTRYPOINT 运行所需的参数
3. 注意: 如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效
DockerFile案例:
1. 自定义镜像mycentosjava8
1. 编写: 准备编写Dockerfile文件
FROM centos
MAINTAINER zzyy<zzyybs@126.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
#解决安装vim报错
RUN cd /etc/yum.repos.d/
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
#ADD 是相对路径jar,把jdk-8u171-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
2. 构建 :docker build -t 新镜像名字:TAG .
3. 运行 :docker run -it 新镜像名字:TAG
2. 虚悬镜像:
1. 是什么: 仓库名、标签都是<none>的镜像,俗称dangling image
2. Dockerfile写一个:
FROM ubuntu
CMD echo 'action is success'
3. 查看: docker image ls -f dangling=true
4. 删除: docker image prune
Docker网络:
1. Docker网络是什么:
1. docker不启动,默认网络情况
1. ens33
2. lo
3. virbr0
2. docker启动后,网络情况
1. ens33
2. lo
3. virbr0
4. docker0
2. Docker网络常用基本命令:
1. All命令: docker network --help
2. 查看网络: docker network ls
3. 查看网络源数据: docker network inspect XXX网络名字
4. 创建网络: docker network create XXX网络名字
5. 删除网络: docker network rm XXX网络名字
3. Docker网络的作用:
1. 容器间的互联和通信以及端口映射
2. 容器IP变动时候可以通过服务名直接网络通信而不受到影响
4. 网络模式:
1. 总体介绍
1. bridge模式:使用--network bridge指定,默认使用docker0
2. host模式:使用--network host指定
3. none模式:使用--network none指定
4. container模式:使用--network container:NAME或者容器ID指定
2. 容器实例内默认网络IP生产规则 : docker容器内部的ip是有可能会发生改变的
3. 案例说明:
1. bridge :
1. bridge是什么: Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络;Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信;
2. 说明:
1. Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关.因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信.
2. docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0;在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址
3. 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配;
1. 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
2. 每个容器实例内部也有一块网卡,每个接口叫eth0;
3. docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配;
4. 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的;
2. host:
1. host是什么: 直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行 NAT 转换
2. 说明: 容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace;容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口
3. 代码 :
1. 警告: docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
1. 问题: docke启动时总是遇见标题中的警告
2. 解释: docker启动时指定--network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增
3. 解决: 解决的办法就是使用docker的其他网络模式,例如--network=bridge,这样就可以解决问题,或者直接无视
2. 正确: docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
3. none: 禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
4. container:
1. container是什么: 新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享;新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等;同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的;
2. 案例命令:
docker run -it --name alpine1 alpine /bin/sh
docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh
5. 自定义网络: 自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
Docker-compose容器编排:
1. Docker-compose容器编排是什么: Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
2. 能干嘛: 可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建
3. 安装步骤:
1. curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
2. chmod +x /usr/local/bin/docker-compose
3. docker-compose --version
4. 卸载步骤: sudo rm /user/local/bin/docker-compose
5. Compose核心概念:
1. 一文件: docker-compose.yml
2. 两要素:
1. 服务(service): 一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器
2. 工程(project): 由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
6. Compose使用的三个步骤:
1. 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
2. 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
3. 最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线
7. Compose常用命令:
1. docker-compose -h # 查看帮助
2. docker-compose up # 启动所有docker-compose服务
3. docker-compose up -d # 启动所有docker-compose服务并后台运行
4. docker-compose down # 停止并删除容器、网络、卷、镜像。
5. docker-compose exec yml里面的服务id # 进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash
6. docker-compose ps # 展示当前docker-compose编排过的运行的所有容器
7. docker-compose top # 展示当前docker-compose编排过的容器进程
8. docker-compose logs yml里面的服务id # 查看容器输出日志
9. docker-compose config # 检查配置
10. docker-compose config -q # 检查配置,有问题才有输出
11. docker-compose restart # 重启服务
12. docker-compose start # 启动服务
13. docker-compose stop # 停止服务
8. Compose编排微服务:
1. 编写docker-compose.yml文件
version: "3"
services:
microService:
image: zzyy_docker:1.6
container_name: ms01
ports:
- "6001:6001"
volumes:
- /app/microService:/data
networks:
- atguigu_net
depends_on:
- redis
- mysql
redis:
image: redis:6.0.8
ports:
- "6379:6379"
volumes:
- /app/redis/redis.conf:/etc/redis/redis.conf
- /app/redis/data:/data
networks:
- atguigu_net
command: redis-server /etc/redis/redis.conf
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'db2021'
MYSQL_USER: 'zzyy'
MYSQL_PASSWORD: 'zzyy123'
ports:
- "3306:3306"
volumes:
- /app/mysql/db:/var/lib/mysql
- /app/mysql/conf/my.cnf:/etc/my.cnf
- /app/mysql/init:/docker-entrypoint-initdb.d
networks:
- atguigu_net
command: --default-authentication-plugin=mysql_native_password #解决外部无法访问
networks:
atguigu_net:
2. 执行 docker-compose up
Docker轻量级可视化工具Portainer:
1. Portainer是什么: Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。
2. 安装: docker命令安装 docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
Docker容器监控之 CAdvisor + InfluxDB + Granfana:
3. 新建3件套组合的docker-compose.yml
version: '3.1'
volumes:
grafana_data: {}
services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
- PRE_CREATE_DB=cadvisor
ports:
- "8083:8083"
- "8086:8086"
volumes:
- ./data/influxdb:/data
cadvisor:
image: google/cadvisor
links:
- influxdb:influxsrv
command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
restart: always
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
grafana:
user: "104"
image: grafana/grafana
user: "104"
restart: always
links:
- influxdb:influxsrv
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- HTTP_USER=admin
- HTTP_PASS=admin
- INFLUXDB_HOST=influxsrv
- INFLUXDB_PORT=8086
- INFLUXDB_NAME=cadvisor
- INFLUXDB_USER=root
- INFLUXDB_PASS=root