推荐这里 阅读笔记 :Docker 笔记
B站视频地址:尚硅谷 Docker 阳哥
云原生Java 架构师的第一课K8s + Docker + KubeSphere + DevOps
Docker仓库:https://hub.docker.com/
编码开发微服务、上线部署容器化、时时刻刻要监控、devops。
Docker - 容器虚拟化技术,解决了运行环境和配置问题的软件容器。
环境配置相当麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。
一次镜像,处处运行。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
虚拟机与 Docker
Docker 运行在CentOS 7 (64-bit)上,要求系统为64位、Linux系统内核版本为 3.8以上。
Linux 配置Docker
参考:https://blog.csdn.net/m0_63099094/article/details/121178929
systemctl status docker 查看docker 服务状态 or ps -ef | grep docker
systemctl start docker 启动 docker
docker version / docker -v 查看版本
测试是否成功 docker run hello-world (本地没有这个景象,是从仓库拉取的)
配置阿里云镜像加速,这个看脑图吧。配置完后,在执行 docker run hello-world 就会很快了,(上图虽然没配置上面等了一段时间才打出来。)
镜像、容器、仓库。
先从本地拉镜像,本地没有,从仓库找。
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。
它也相当于是一个root文件系统。比如官方镜像 centos:7 就包含了完整的一套 centos:7 最小系统的 root 文件系统。
相当于容器的“源代码”,docker镜像文件类似于Java的类模板,而docker容器实例类似于java中new出来的实例对象。
1 从面向对象角度
Docker 利用容器(Container)独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台
2 从镜像容器角度
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
仓库(Repository)是集中存放镜像文件的场所。
类似于
Maven仓库,存放各种jar包的地方;
github仓库,存放各种git项目的地方;
Docker公司提供的官方registry被称为Docker Hub,存放各种镜像模板的地方。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub(https://hub.docker.com/),
存放了数量庞大的镜像供用户下载。国内的公开仓库包括阿里云、网易云等
systemctl status docker or ps -ef | grep docker 查看docker 服务状态
systemctl start docker 启动 docker
docker version 查看版本
docker run hello-world 启动 hello-world 镜像
镜像命令
docker images 列出本地主机上的镜像
docker search 某个XXX镜像名字 查找镜像(在 docker hub上)
docker search --limit 5 redis 查找点赞数前5 的redis 镜像
docker pull 某个XXX镜像名字 拉取镜像(从 docker hub 上)
docker pull redis:6.0.8 拉取 redis 6.0.8 版本的镜像
docker system df 查看镜像/容器/数据卷所占的空间
docker rmi 某个XXX镜像名字ID 删除镜像
docker rmi feb5d9fea6a5 删除 hello-world 镜像
docker rmi -f feb5d9fea6a5 强制删除镜像,当镜像在使用时候会删除不了
docker rmi -f ${docker images -qa} 删除全部, docker images -qa 是查处所有的镜像id
docker image ls -f dangling=true 查看所有的虚悬镜像
docker image prune 删词所有的虚悬镜像 docker rmi -f 就行
容器命令(容器的创建等更加详细的参考脑图)
docker run -it ubuntu /bin/bash 启动容器(前台交互式启动)
docker run -it --name=myu1 ubuntu bash 启动 ubuntu 容器,并且重名为 myu1
exit run -it 进去,退出容器容器停止。
Ctrl + P + Q run -it 进去,退出容器容器不停止
docker run -d redis:6.0.8 启动容器(后台守护式启动)
docker logs 容器ID 查看容器日志
docker exec -it 容器ID /bin/bash 进入容器,exit 退出,不会停止。
docker ps 查看所有正在运行的容器
docker ps -a 查看所有正在运行的+ 历史运行的容器
docker ps -l 查看最近创建的容器(正在运行+ 历史运行)
docker ps -n 2 查看最近2个创建的容器(正在运行+ 历史运行)
docker ps -q 查看所有正在运行的容器 id。
docker start 容器ID 或容器名 启动已停止运行的容器(docker ps -n 10 查看容器ID)
docker restart容器ID 或容器名 重启容器
docker stop容器ID 或容器名 停止容器
docker kill容器ID 或容器名 强制停止容器
docker rm 容器ID 删除容器(正在运行的无法删除,需stop后才可以)
docker rm -f 容器ID 强制删除容器
docker rm -f $(docker ps -a -q) 强制删除所有容器
docker top 容器ID 查看容器内运行的进程,进程号、端口号等。
docker inspect 容器ID 查看容器内部细节
docker cp 容器ID:容器内路径目的主机路径 从容器内拷贝文件到主机上
docker export 927e21b06561 > /root/exportUbutun.tar 导出容器到主机上。
cat exportUbutun.tar | docker import - cg/ubuntu:1.0 导入导出的容器cg 随便写,1.0 版本号随便写
docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名] docker commit -m=“vim cmd add ok” -a=“cg” 927e21b06561 cg/myubuntu:1.1
容器卷命令
docker inspect 803b5d9531c0 查看容器挂载
网络命令
docker network ls 查看 docker 网络列表
docker network create test_network 创建网络
docker network inspect test_network 查看网络源数据
docker network rm test_network 删除网络
docker inspect 容器名 | tail -n 20 查看容器网络
docker-compose
docker-compose --version 查看 docker-compose 版本
docker run -it ubuntu /bin/bash
i:interactive,t:tyy(伪终端)
进入了 ubuntu 系统,并且以终端形式打开的。
ps -ef 可以看到 ubuntu 系统的/bin/bash 已运行。
docker ps 查看所有正在运行的容器
可以看到IMAGE ubuntu 镜像正在运行,CONTAINER ID 容器ID,和上面 root@778248e34084 是一样的。
docker run -it --name=myu1 ubuntu bash,又启动了一个容器,这次NAMES 为 myu1,前面返回了容器ID 就是正常启动了。新开一个终端查看 docker ps。
进入了 ubuntu 命令行模式后,输入exit 即可退出,或者关闭 xshell选项卡也可以退出,容器停止。
Ctrl + P + Q退出容器不停止。
上面 run -it 都是前台交互式启动, 后台守护式启动 run -d,比如 redis。
docker logs 容器ID,查看日志,比如redis
docker exec -it acb16932c06b /bin/bash 进入容器。 一般用 -d 后台启动,然后 exec 进入对应容器实例。
docker cp 容器ID:容器内路径目的主机路径
docker export 927e21b06561 > /root/exportUbutun.tar 导出容器到主机上
cat exportUbutun.tar | docker import - cg/ubuntu:1.0 导入导出的容器,进入到这个容器 a.txt 还在。
docker commit 提交容器副本使之成为一个新的镜像
docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]
docker commit -m=“vim cmd add ok” -a=“cg” 927e21b06561 cg/myubuntu:1.1
https://cr.console.aliyun.com/cn-beijing/instances 阿里云容器镜像服务
创建命令空间,创建镜像仓库。在从本地推送镜像到阿里云。
docker login --username=username registry.cn-beijing.aliyuncs.com
docker tag ImageId registry.cn-beijing.aliyuncs.com/cgxin/myubuntu:1.1
docker push registry.cn-beijing.aliyuncs.com/cgxin/myubuntu:1.1
docker pull registry.cn-beijing.aliyuncs.com/cgxin/myubuntu:1.1
docker pull registry 拉取
docker run -d -p 5000:5000 -v /root/01Sofeware/10Docker/myregistry/:/tmp/registry --privileged=true registry 运行私有库 registry,这里-d 是后台守护运行,并且端口映射了。
原来什么也没有的 ubuntu,安装了 net-tools 在exit,下次进入就没有了。Ctrl + P + Q,然后将整个容器生成一个新的镜像,整个新的镜像是有 net-tools的。在 stop 原来的ubuntu,run -it 新的镜像即可。
docker commit -m=“ifconfig cmd add ok” -a=“cg” 4c6f298ddda8 cgubuntu:1.0
curl -XGET http://localhost:5000/v2/_catalog 验证私服库上有什么镜像。(localhost 是私服库的ip)
docker tag cgubuntu:1.0 localhost:5000/cgubuntu:1.0
docker 默认不允许 http 方式推送镜像,修改配置文件使之支持。
改完后如果不生效,重启 docker(systemctl restart docker)。
没配置成功啊,修改了配置,docker就起不来了。(docker 20.10.9 允许http 推送镜像…)
docker push localhost:5000/cgubuntu:1.0
必须要记住版本号,没有版本号,没法拉取。
然后 run -it,ifconfig 测试可行。
容器数据卷的方式完成数据的持久化,重要资料backup(备份)
将容器内的数据 备份 + 持久化 到本地主机目录。
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
docker run -it --privileged=true -v /root/01Sofeware/10Docker/host_data:/tmp/docker_data --name=u1 ubuntu
就会进入到 ubuntu 镜像创建的一个容器里,
有点 vue 中 v-model 双向绑定的意思。
docker inspect 803b5d9531c0 查看容器挂载信息。
上面默认 不加 是可读可写的。
加个 ro(read only),容器里只能读,不能写。 touch/vim a.txt 会报错。
docker run -it --privileged=true -v /root/01Sofeware/10Docker/host_data:/tmp/docker_data:ro --name=u1 ubuntu
docker run -it --privileged=true --volumes-from 父类 --name u2 ubuntu
docker run -it --privileged=true --volumes-from u1 --name u2 ubuntu
这个 u1 和 u2 docker ps 最后一列 name 可以看到。
继承过来了。
docker search tomcat
docker pull tomcat
docker images 查看 tomcat镜像是否下载成功
docker run -d -p 8081:8080 --name t1 tomcat
tomcat8 不需要修改上面的 webapps.dist 就可以访问猫首页。
docker search mysql
ducker pull mysql:8.0
docker run -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0
docker ps
docker exec -it 容器ID /bin/bash
mysql -uroot -p
上面这是简单版的,删除容器后数据就都没了。
实战版
docker run -d -p 3307:3306 --privileged=true
-v /cg/mysql/log:/var/log/mysql
-v /cg/mysql/data:/var/lib/mysql
-v /cg/mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:8.0
上面3个 -v 挂了三个容器数据卷。
在主机上cd /cg/mysql/conf,vim my.cnf,加入下面
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8
重启 mysql容器:
docker ps
docker restart mysql
这时候在删除 mysql容器,
docker rm -f mysql
然后重新
docker run -d -p 3307:3306 --privileged=true
-v /cg/mysql/log:/var/log/mysql
-v /cg/mysql/data:/var/lib/mysql
-v /cg/mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:8.0
可以看到,数据库、表、表数据等等都还在。
高级篇 Mysql主从复制★(亲测成功)
脑图用的 5.7,我这里用的8.0,版本这一个真是个大坑,因为你并没有去关注8.0 升级了什么。
主从复制:只会在连接成功后,从才会复制主的数据,从数据库不会去复制主之前的数据。
新建主服务器容器实例3307
docker run -p 3307:3306 --name mysql-master
-v /cg/mysql-master/log:/var/log/mysql
-v /cg/mysql-master/data:/var/lib/mysql
-v /cg/mysql-master/conf:/etc/mysql
-v /cg/mysql-master/mysql-files:/var/lib/mysql-files
-e MYSQL_ROOT_PASSWORD=root
-d mysql:8.0
cd /cg/mysql-master/conf
vim my.cnf 添加如下:
[mysqld]
server_id=101
binlog-ignore-db=mysql
log-bin=mall-mysql-bin
binlog_cache_size=1M
binlog_format=mixed
binlog_expire_logs_seconds=7
replica_skip_errors=1062
docker restart mysql-master
docker exec -it mysql-master /bin/bash docker ps
mysql -uroot -p (没有密码,如果有密码 mysql -uroot -proot,不知道为啥密码没生效)
CREATE USER ‘slave’@‘%’ IDENTIFIED BY ‘123456’;
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO ‘slave’@‘%’;
ALTER USER ‘slave’@‘%’ IDENTIFIED WITH mysql_native_password BY ‘123456’; (8.0需要,5.7不用了)
新建从服务器容器实例3308
docker run -p 3308:3306 --name mysql-slave
-v /cg/mysql-slave/log:/var/log/mysql
-v /cg/mysql-slave/data:/var/lib/mysql
-v /cg/mysql-slave/conf:/etc/mysql
-v /cg/mysql-slave/mysql-files:/var/lib/mysql-files
-e MYSQL_ROOT_PASSWORD=root
-d mysql:8.0
cd /cg/mysql-slave/conf
vim my.cnf 添加如下:
[mysqld]
server_id=102
binlog-ignore-db=mysql
log-bin=mall-mysql-slave1-bin
binlog_cache_size=1M
binlog_format=mixed
expire_logs_days=7
slave_skip_errors=1062
relay_log=mall-mysql-relay-bin
log_slave_updates=1
read_only=1
docker restart mysql-master
然后在主数据库中 show master status;
出现如上。
进入到从数据库:
docker exec -it mysql-slave /bin/bash
mysql -uroot -proot
在从数据库中:(连接之前开放端口,别忘了安全组配置)
change master to master_host=‘ip’, master_user=‘slave’, master_password=‘123456’, master_port=3307, master_log_file=‘mall-mysql-bin.000002’, master_log_pos=156, master_connect_retry=30; (master_log_file 和master_log_pos 要跟上图对应)
STOP SLAVE IO_THREAD FOR CHANNEL ‘’; (停止主从)
从机重启slave配置"stop slave;",“start slave;”,“show slave status \G;”,问题解决
在从数据库中查看主从同步状态:
show slave status \G;
在从数据库中开启主从同步,在查看主从状态
start slave;
show slave status \G; 下面Slave_IO_Running: Connecting 说明没成,看下面Bug 解决。
主从测试:
在主数据库中使用库,新建表,插入数据。从数据库中查看记录。
主数据库:create database db01; use db01; create table t1 (id int, name varcahr(20)); insert into t1 value(1, ‘cg’);
从数据库:use db01; select * from t1;
Bug:Mysql 8.0 采用新的身份验证插件。(caching_sha2_password), 原来的身份验证插件为(mysql_native_password)
从数据库使用 slave 这个用户。
在 主数据库修改:
ALTER USER ‘slave’@‘%’ IDENTIFIED WITH mysql_native_password BY ‘123456’;
终于连接成功了!
实战版
docker images
docker run -d -p 6380:6379 redis:6.0.8 (我已经装过redis了,这里端口用6380)
docker exec -it 容器ID /bin/bash
redis-cli
set k1 v1
get k1
一套丝滑走完。 但是删了也就凉凉了。
在主机配置好 redis.conf (redis 配置文件,假如在/app/redis/,具体配置可以看脑图)
docker run -p 6380:6379 --name myr3 --privileged=true
-v /app/redis/redis.conf:/etc/redis/redis.conf
-v /app/redis/data:/data
-d redis:6.0.8 redis-server /etc/redis/redis.conf (以自己配置的 redis.conf 启动)
docker exec -it myr3 /bin/bash
redis-cli
set k1 v1
get k1
测试文件生效,在 redis.conf 的 databases 16 是16个库(015),改成10个库(09)
docker restart myr3
docker exec -it myr3 /bin/bash
redis-cli
然后在 select 15 时候就会报错了(因为并没有16库了。)
高级篇★Redis 集群(3主3从)
大厂面试题:
1~2 亿条数据需要缓存,请问如何设计这个存储案例
单机单台100%不可能,肯定是分布式存储,用redis如何落地?
● 哈希取余分区,hash(key)/ 集群数量。缺点:集群扩缩,hash 取余会全部重新洗牌。
● 一致性哈希算法:hash环,节点映射。缺点:一致性哈希算法的数据倾斜问题。(服务器少不要用)
● 哈希槽分区:0 ~ 16383(2`14 - 1). (大厂都用)
搭建Redis 集群3主3从
启动6个redis 容器:
docker run -d --name redis-node-1 --net host --privileged=true -v /cg/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 /cg/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 /cg/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 /cg/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 /cg/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 /cg/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
构建主从关系:(如果不是一个主机的话,注意开放端口)
docker exec -it redis-node-1 /bin/bash
redis-cli --cluster create ip:6381 ip:6382 ip:6383 ip:6384 ip:6385 ip:6386 --cluster-replicas 1
hash slot 哈希槽,3个槽;0:0 - 5640、1:5461 - 10922、2:10923 - 16383
查看集群状态:
连接进入6381 作为切入点,查看集群状态、集群节点
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381
cluster info
cluster nodes
可以看到6381、6382、6383 是 master,6381的 slave 是6384,和下图一样。
向Redis 集群 set 值
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381
在 redis里 set 值,集群环境不要用单机进入,如下进入了 node 1(0-5640),超过了哈希槽位,就报错。
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381 -c 集群环境连接 redis
查看集群状态:
redis-cli --cluster check ip:6381
主从切换
6381 宕机后(被kill),6384 上位。
查看主从节点关系:
stop 6381,6384上位:
依旧可以 get 值。
6381 恢复后,6381 是 slave,想让6381 当 master的话,需要 stop 6384,然后在 start 6384
再查看集群状态:redis-cli --cluster check ip:6381
主从扩容(更详细看脑图)
3主3从变成4主4从。
新增主机:6387,新增从机:6388
3台master + 1台 = 4 台
分配的槽位:16384 / 4 = 4096
docker run -d --name redis-node-7 --net host --privileged=true -v /cg/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 /cg/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
将6387 作为 master 加入集群中:
redis-cli --cluster add-node ip:6387 ip:6381
检查集群
redis-cli --cluster check ip:6381
重新分派槽位
redis-cli --cluster reshard ip:6381
3台master + 1台 = 4 台,分配的槽位:16384 / 4 = 4096
6387的master id,all,yes。
在检查集群情况:
redis-cli --cluster check ip:6381
为主节点6387分配从节点6388
redis-cli --cluster add-node ip:6388 ip:6387 --cluster-slave --cluster-master-id d6e9e3b11b1fe8fc5f41f43e51f61d6bf32e3d60
检查集群
redis-cli --cluster check ip:6381
查看 key的存储情况:
主从缩容
4主4从 -> 3主3从,减少 6387 master 6388 slave
思路:先删除 6388 slave,清出来的槽号重新分配,在删除 6387 master,恢复3主3从。
检查集群
redis-cli --cluster check ip:6381
删除从机6388
redis-cli --cluster del-node ip:6388 b46cd3c1e82a3faff5a8ebc990d5cf2ecab67987
将6387的槽号清空,重新分配,本例将清出来的槽号都给6381。
重新分配槽号,全分给 6381了, 6382和 6383 还是 4096个槽位,6381 8192个槽位了。这里并没有匀分给之前的。
redis-cli --cluster reshard ip:6381
检查集群
redis-cli --cluster check ip:6381
删除主机6387
redis-cli --cluster del-node ip:6387 d6e9e3b11b1fe8fc5f41f43e51f61d6bf32e3d60
查看 9.3 Portainer 操作配置 nginx
总结:
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
● Dockerfile是软件的原材料
● Docker镜像是软件的交付品
● Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
● Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
● Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;
● Docker容器,容器是直接提供服务的。
Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
之前是拉取镜像ubuntu ,不含有 vim/ifconfig … 这些命令,安装后, docker commit 构建功能更加强大的 ubuntu++ 镜像。每次增加功能,随时变化都需要 docker commit 构建新的镜像。这样很不方便。
DockerFile 一次性给搞定。
某种镜像的增强,给个list清单,后续 需要加入任何功能,直接在list单子里面写好,相当于多次提交。 解决上面说的麻烦。 vim/ifconfig/tomcat/java8…
Dockerfile 构建镜像3步骤:编写 Dockerfile文件,docker build 构建镜像,docker run 镜像运行容器实例。
Dockerfile 底层就解析为 docker 命令。
例如 pig-ui的 Dockerfile:
● 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
● 指令按照从上到下,顺序执行。 # 表示注释
● 每条指令都会创建一个新的镜像层并对镜像进行提交
FROM 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是FROM
MAINTAINER 镜像维护者的姓名和邮箱地址
RUN 容器构建时需要运行的命令,RUN是在 docker build时运行。用 shell的命令即可。
EXPOSE 当前容器对外暴露出的端口
WORKDIR 指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
USER 指定该镜像以什么样的用户去执行,如果都不指定,默认是root
ENV 用来在构建镜像过程中设置环境变量
ADD 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
COPY
(docker cp) 类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
COPY src dest
COPY [“src”, “dest”]
VOLUME (-v) 容器数据卷,用于数据保存和持久化工作
CMD 指定容器启动后的要干的事情,CMD是在 docker run时运行,命令和 RUN 一样。Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换
ENTRYPOINT 也是用来指定一个容器启动时要运行的命令,类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
更多详细的,看脑图吧。特别是 ENTRYPOINT 和 CMD。
例如 Tomcat8的 Dockerfile,
https://github.com/docker-library/tomcat/blob/master/Dockerfile.template
上面命令写错了, -p 8081:8080
docker run -it -p 8081:8080 tomcat bash;
上面这样子访问猫网是进不去的。
删除上面这个容器。
docker run -it -p 8081:8080 tomcat
Ctrl + P +Q
docker ps
docker exec -it dfb736f73c4e bash
rm -r webapps
mv webapps.dist/ webapps/
即可成功访问。
docker run -it -p:8081:8080 tomcat
相当于
EXPOSE 8080
CMD [“catalina.sh”, “run”]
docker run -it -p:8081:8080 tomcat /bin/bash
相当于
EXPOSE 8080
CMD [“/bin/bash”, “run”]
这样子访问猫, localhost:8081 是进不去的。替换了 CMD。(亲测,真的进不去。)
docker 构建 nginx 镜像
nginx 一般要指定一个配置文件,
ENTRYPOINT [“nginx”, “-c”] 定参
CMD[“/etc/nginx/nginx.conf”] 变参 脑图下面的表格
Centos7拉取下来是什么也没有,现在让Centos7镜像具备vim + ifconfig + jdk8。
注意:用centos7,docker pull centos:7,centos8 会报错的。
首先 Docker pull centos; Dockerfile 如下:
#镜像(本地没有则拉取)
FROM centos:7
MAINTAINER [email protected]
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装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-8u11-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u11-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_11
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH J A V A H O M E / l i b / d t . j a r : JAVA_HOME/lib/dt.jar: JAVAHOME/lib/dt.jar:JAVA_HOME/lib/tools.jar: J R E H O M E / l i b : JRE_HOME/lib: JREHOME/lib:CLASSPATH
ENV PATH J A V A H O M E / b i n : JAVA_HOME/bin: JAVAHOME/bin:PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo “success--------------ok”
CMD /bin/bash
Dockerfile 构建镜像(需要在当前目录下)
docker build -t 新镜像名字:TAG . (注意,TAG 后面是空格 + .)
docker build -t centosjava8:1.5 .
出现 Successfully 即是成功,本地镜像没有centos:7,FROM centos:7 会从远程拉取了。
Run 且测试 vim、ifconfig、java环境
docker run -it centosjava8:1.5 bash
再加一个 lsof 命令
yum install lsof
Dockerfile 改成如下:
#镜像(本地没有则拉取)
FROM centos:7
MAINTAINER [email protected]
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装lsof
RUN yum -y install lsof
#安装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-8u11-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u11-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_11
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH J A V A H O M E / l i b / d t . j a r : JAVA_HOME/lib/dt.jar: JAVAHOME/lib/dt.jar:JAVA_HOME/lib/tools.jar: J R E H O M E / l i b : JRE_HOME/lib: JREHOME/lib:CLASSPATH
ENV PATH J A V A H O M E / b i n : JAVA_HOME/bin: JAVAHOME/bin:PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo “success--------------ok”
CMD /bin/bash
再次构建镜像,测试 lsof
docker build -t centosjava8:1.5 .
总结:
只要有Dockerfile 就可以一行命令,无限构建镜像。之前那种 docker commit 方式简直不行。
仓库名、标签都是的镜像,俗称dangling image
虚悬镜像没有任何价值,直接删了就行。
查看虚悬镜像:
docker image ls -f dangling=true
删除虚悬镜像:
docker image prune
它有镜像id,用docker rmi -f 镜像ID,也可以删除
随便建一个项目,打成一个 jar包。(可以看脑图建一个)
FROM java:8
MAINTAINER [email protected]
VOLUME /tmp
ADD docker_boot.jar cgxin_docker.jar
RUN bash -c ‘touch /cgxin_docker.jar’
ENTRYPOINT [“java”,“-jar”,“/cgxin_docker.jar”]
#暴露8080 端口作为微服务, jar包中 yml写的端口
EXPOSE 8080
将 Dockerfile 和 jar包放在同一目录下:
docker build -t cgxin_docker:1.0 .
docker run -d -p 8080:8080 cgxin_docker:1.0
访问成功!
容器间的互联和通信以及端口映射,容器IP变动时候可以通过服务名直接网络通信而不受到影响。
启动docker后,产生一个 docker0 的虚拟网桥。
安装完 docker 默认创建3个网络模式:
docker network ls
创建网络:docker network create test_network
查看网络源数据:docker network inspect test_network
删除网络:docker network rm test_network
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
docker run 的时候没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址
网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
● 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
● 每个容器实例内部也有一块网卡,每个接口叫eth0;
● docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
查看 bridge 网络的详细信息,可以通过 grep 获取名称项
docker network inspect bridge
docker network inspect bridge | grep name
两两匹配验证:
docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-jdk8
启动两个容器后,在主机 ip address,可以看到多了个2个ip
在启动一个 u1 容器,查看 ip address,发现也增加了一个 ip。 (网络模式用 bridge 时)
直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。
容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
docker启动时指定–network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,
并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。
解决的办法就是使用docker的其他网络模式,例如–network=bridge,这样就可以解决问题,或者直接无视。
docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8
可以看到并没有增加新的 ip,而是和主机共用一个。
没有设置-p的端口映射了,如何访问启动的tomcat83?
http://宿主机IP:8080/
此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。
真的牛,亲测可以访问的, 8080 8081 8082 都可以进猫网。
在none模式下,并不为Docker容器进行任何网络配置。
也就是说,这个Docker容器没有网卡、IP、路由等信息,只有一个lo
需要我们自己为Docker容器添加网卡、配置IP等。
禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
docker inpsect tomcat84 | tail -n 20 (进入到容器外查看)
docker exec -it tomcat84 bash (进入到容器里查看)
ip addr
这个不看了,有需要看脑图吧。演示案例没用 tomcat,说是很坑。用的其他镜像。
在阿里云上并没有 Driver 是 container 的。
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
还是用的 bridge 的 tomcat81 和 tomcat82
分别看一下 ip:
进入容器实例,互相 ping ip 和服务名。
自定义网络
自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
docker network create cgxin_network
docker network ls
docker run -d -p 8083:8080 --network cgxin_network --name tomcat83 billygoo/tomcat8-jdk8
docker run -d -p 8084:8080 --network cgxin_network --name tomcat84 billygoo/tomcat8-jdk8
分别进去,再 ping 域名试试。
查看ip:
结论:自定义网络本身就维护好了主机名和ip的对应关系(ip和域名都能通)
pig中的 network查看
可以看到项目中都会自己的 network,用来 ping 域名实现网络通信,而不是 ip。
启动2个ubuntu 容器(之前创建的没删,docker run -it --name u1 ubuntu bash)
docker start u1
docker start u2
查看容器网络:docker inspect u1 | tail -n 20
关闭 u2,新建一个 u3,查看网络IP变化
docker stop u2
docker run -it --name u3 ubuntu bash
docker inspect u3 | tail -n 20
总结:
这说明 docker 容器内部的 Ip 是有可能改变的。使用自定义网络,可以 ping 域名。
Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器。 容器启动需要有加载顺序,比如 jar包,需要先启动mysql + redis容器。
对 docker 容器集群的快速编排 (容器调用关系,一键启动,一键stop)
就像容器 aplicaitonContext.xml 对bean对象统计集中管理起来
java里有对象, docker 里有容器, docker-compose 就是管理容器。
下载官网:https://docs.docker.com/compose/install/ 选择 Linux,翻译成中文照着操作。
查看 docker-compose 版本:docker-compose --version
三步走:
● 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
● 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
● 最后,执行docker-compose up命令来启动并运行整个应用程序,完成一键部署上线
docker-compose -h 查看帮助
docker-compose up 启动所有docker-compose服务
docker-compose up -d 启动所有docker-compose服务并后台运行
docker-compose down 停止并删除容器、网络、卷、镜像。
docker-compose exec yml里面的服务id 进入容器实例内部 docker-compose exec
docker-compose yml文件中写的服务id /bin/bash
docker-compose ps 展示当前docker-compose编排过的运行的所有容器
docker-compose top 展示当前docker-compose编排过的容器进程
docker-compose logs yml里面的服务id 查看容器输出日志
docker-compose config 检查配置
docker-compose config -q 检查配置,有问题才有输出
docker-compose restart 重启服务
docker-compose start 启动服务
docker-compose stop 停止服务
改造 6.4 写的项目,添加上 mysql 和 redis,这就需要mysql 和 redis 环境。
Dockerfile 还是原来,没有改动。
docker build -t cgxin_docker:1.1 .
● 单独的mysql实例
● 单独的redis实例
● 单独的jar包
上面3个实例依次顺序启动成功。
缺点:
● 先后顺序要去固定,先 mysql + redis 才能再 jar 包;需要多个 run
● 容器间的启停或者宕机,有可能导致 ip地址对应的容器实例变化,映射出错,要么生产ip写死(可以但不推荐),要么通过微服务调用。
想了很久很久啊。还是不编排了,阿里云服务器上本来就装了redis 和 mysql 环境了,下面还要在搭一个镜像。需要先 stop 原来的,然后其这个容器,测试完了后,在 start 原来的那些。mysql 还好说, redis 那个配置文件都在另一份笔记上了。嗯,这里不测试了,就写写吧。
编写 docker-compose.yml
version: “3”
services:
microService:
image: cgxin_docker:1.1
container_name: cgxin_docker02
ports:
redis:
image: redis:6.0.8
ports:
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:
networks:
cgxin_net:
修改docker_boot 项目中的 yml
mysql 的密码, mysql 和 redis 域名是在 docker-compose.yml 中编排的。
构建镜像
进入到这个目录,用 Dockerfile构建镜像。(Dockerfile 还是之前,没有改,基础镜像java8 和 java -jar)
docker build -t cgxin_docker:1.2 .
编排项目
先docker-compose config -q检查配置,如果有错误就会输出。
docker compose up或者docker compose up -d
docker netword ls,可以看到自己创建了新的网络。
测试
先进入mysql 实例,建库建表
docker exec -it 容器实例id /bin/bash
mysql -uroot -p
create database db2021;
use db2021;
CREATE TABLE t_user
(
id
INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
username
VARCHAR(50) NOT NULL DEFAULT ‘’ COMMENT ‘用户名’,
password
VARCHAR(50) NOT NULL DEFAULT ‘’ COMMENT ‘密码’,
sex
TINYINT(4) NOT NULL DEFAULT ‘0’ COMMENT '性别 0=女 1=男 ',
deleted
TINYINT(4) UNSIGNED NOT NULL DEFAULT ‘0’ COMMENT ‘删除标志,默认0不删除,1删除’,
update_time
TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ‘更新时间’,
create_time
TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘创建时间’,
PRIMARY KEY (id
)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT=‘用户表’;
浏览器调用写的那些接口, redis 和 mysql 正常使用,即可成功。
要注意是用镜像创建的 redis 和 mysql,需要把自己之前安装的stop。
关停
Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。
官网:https://docs.portainer.io/v/ce-2.9/start/install/server/docker/linux
docker命令安装:–restart=always (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-ce:latest
http://ip:9000/
第一次登录需要创建admin。
可以手动启动容器。
Pull the Image
Deploy the container:部署容器。下面可以进行一个更多的设置。
部署后,浏览器访问 nginx,可以看到成功了。 Docker 是真牛啊!
CAdvisor监控收集 + InfluxDB存储数据 + Granfana展示图表
docker ps
docker stats
通过docker stats命令可以很方便的看到当前宿主机上所有容器的CPU,内存以及网络流量等数据,
一般小公司够用了。
但是,docker stats统计结果只能是当前宿主机的全部容器,数据资料是实时的,没有地方存储、没有健康指标过线预警等功能。
编写 docker-compose.yml
version: ‘3.1’
volumes:
grafana_data: {}
services:
influxdb:
image: tutum/influxdb:0.9
restart: always
environment:
cadvisor:
image: google/cadvisor
links:
grafana:
user: “104”
image: grafana/grafana
user: “104”
restart: always
links:
部署容器:
docker-compose config -q
docker-compose up
出现上面这个,才是正常的。
查看网络:
所以下面配置 Granfana 中的 panel 时,写的数据库用的域名InfluxDB。
CAdvisor:http://ip:8080/
往下拉动,有很多图形化监控。
cadvisor也有基础的图形展现功能,这里主要用它来作数据采集。
InfluxDB:http://ip:8083/
Granfana:http://ip:3000/
默认账号密码:admin / admin
配置数据源:
出现上面 Data Souce is working 即是成功。
配置面板 panel:
这样子,就配置完成了。