docker基础
Docker
前言
Docker 是一个开源的应用容器引擎,Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化,解决了运行环境和配置问题的软件容器, 方便做持续集成并有助于整体发布的容器虚拟化技术,达到应用程式跨平台间的无缝接轨运作。
优点:
快速,一致地交付应用程序:Docker 允许开发人员使用您提供的应用程序或服务的本地容器在标准化环境中工作,从而简化了开发的生命周期。
响应式部署和扩展:Docker 的可移植性和轻量级的特性,还可以使您轻松地完成动态管理的工作负担,并根据业务需求指示,实时扩展或拆除应用程序和服务。
在同一硬件上运行更多工作负载:它为基于虚拟机管理程序的虚拟机提供了可行、经济、高效的替代方案,不像虚拟机一样将整个操作系统进行镜像打包,它只基于一个操作系统核心。
Docker架构
Docker包含三个主要要素:
- 镜像(Image):相当于是一个 root 文件系统,比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
- 容器(Container):镜像和容器的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。
- 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像,可分为公开仓库(Public)和私有仓库(Private)两种形式;最大的公开仓库是 Docker Hub(https://hub.docker.com/),但是国内一般使用阿里云镜像仓库(https://cr.console.aliyun.com/)和腾讯软件源(http://mirrors.tencent.com/#/index)。
大致结构:
名词解释:
- Docker 客户端(Client):Docker 客户端通过命令行或者其他工具使用 Docker SDK和Docker 的守护进程通信。
- Docker 主机(Host):一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
- Docker Registry:Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库;一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。
- Docker Machine:Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。
安装
首先卸载旧版本(直接执行命令):
1
2
3
4
5
6
7
8yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine安装gcc相关依赖:
yum -y install gcc
和yum -y install gcc-c++
通过Docker的仓库进行安装:
- 安装需要的软件包:
yum install -y yum-utils
- 设置稳定的镜像仓库(阿里云):
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
- 更新yum软件包索引:
yum makecache fast
- 安装docker引擎:
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
- 安装需要的软件包:
启动Docker:
systemctl start docker
运行hello-world:
docker run hello-world
卸载:
systemctl stop docker
yum remove docker-ce docker-ce-cli containerd.io
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
配置镜像加速器:
进入阿里云官网
进入容器镜像服务产品
右侧镜像工具中的镜像加速器
复制加速器地址
执行命令:
创建文件:
mkdir -p /etc/docker
写入文件:
1
2
3
4
5 tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://自己id.mirror.aliyuncs.com"]
}
EOF重启守护进程:
systemctl daemon-reload
重启docker服务:
systemctl restart docker
基础相关命令
帮助启动类命令
- 启动docker:
systemctl start docker
- 停止docker:
systemctl stop docker
- 重启docker:
systemctl restart docker
- 查看docker状态:
systemctl status docker
- 开机启动:
systemctl enable docker
- 查看docker概要信息:
docker info
- 查看docker总体帮助文档:
docker --help
- 查看docker命令帮助文档:
docker 具体命令 --help
镜像相关命令
列出本地主机上的镜像:
docker images
- 列出选项说明:
- REPOSITORY:表示镜像的仓库源
- TAG:镜像的标签版本号
- IMAGE ID:镜像ID
- CREATED:镜像创建时间
- SIZE:镜像大小
- 命令参数说明:
- -a :列出本地所有的镜像(含历史映像层)
- -q :只显示镜像ID
- 列出选项说明:
去镜像中查找某个镜像:
docker search [OPTIONS] 镜像名字
- 列出选项说明:
- name:镜像名称
- description:镜像描述
- stars:点赞数
- official:是否是官方的
- automated:是否自动构建的
- OPTIONS说明:
- -limit n:只列出N个镜像,默认25个
- 列出选项说明:
下载镜像:
docker pull 镜像名字[:TAG]
( 没有TAG就是最新版)查看镜像/容器/数据卷所占的空间:
docker system df
删除镜像:
- 删除单个:
docker rmi -f 镜像ID
- 删除多个:
docker rmi -f 镜像名1:TAG 镜像名2:TAG
- 删除全部:
docker rmi -f $(docker images -qa)
- 删除单个:
提交容器副本创建一个新的镜像:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
- OPTIONS说明:
- -a:提交的镜像作者;
- -c:使用Dockerfile指令来创建镜像;
- -m:提交时的说明文字;
- -p:在commit时,将容器暂停。
- 例子:将centos7基础镜像添加vim:
- 在centos7容器中安装vim:
yum install vim
- 提交新镜像:
docker commit -m "add vim cmd" -a="wht" f5d07dbc0e7c myCentos:centos7
- 在centos7容器中安装vim:
- OPTIONS说明:
容器命令
新建+启动容器:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS说明(常用):
- –name=”容器新名字”:为容器指定一个名称
- -d:后台运行容器并返回容器ID,也即启动守护式容器(后台运行)
- -i:以交互模式运行容器,通常与 -t 同时使用
- -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用
- -P:随机端口映射,大写P
- -p:指定端口映射,小写p
案例使用centos7容器:
docker run -it centos:centos7 /bin/bash
创建一个新的容器但不启动它:
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
- OPTIONS说明(同run)
- 例子:
docker create --name myrunoob nginx:latest
列出当前所有正在运行的容器:
docker ps [OPTIONS]
- OPTIONS说明:
- -a:列出当前所有正在运行的容器+历史上运行过的
- -l:显示最近创建的容器
- -n:显示最近n个创建的容器
- -q:静默模式,只显示容器编号
- OPTIONS说明:
退出容器:
- exit:exit退出,容器停止
- ctrl+p+q:ctrl+p+q退出,容器不停止
启动已经停止的容器:
docker start 容器ID或者容器名
重启容器:
docker restart 容器ID或者容器名
停止容器:
docker stop 容器ID或者容器名
强制停止容器:
docker kill 容器ID或容器名
删除已停止的容器:
docker rm 容器ID
查看容器日志:
docker logs 容器ID
查看容器内运行的进程:
docker top 容器ID
查看容器内部细节:
docker inspect 容器ID
重新进入容器:
docker exec -it 容器ID bashShell
- 案例(重新用命令行形式进入容器):
docker exec -it a2b057419f1f / bin/ bash
- 案例(重新用命令行形式进入容器):
docker attach 容器ID
两者区别:
exec 是在容器中打开新的终端,并且可以启动新的进程 用exit退出,不会导致容器的停止。
attach 直接进入容器启动命令的终端,不会启动新的进程 用exit退出,会导致容器的停止。
从容器内拷贝文件到主机上:
docker cp 容器ID:容器内路径 目的主机路径
导入和导出:
- 导出:
docker export 容器ID > 文件名.ta
- 导出:
cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
- 导出:
检查容器里文件结构的更改:
docker diff [OPTIONS] CONTAINER
- OPTIONS说明:
- -L:保持源目标中的链接
- OPTIONS说明:
更多命令见菜鸟教程
发布本地镜像
发布大致结构:
发布到阿里云
进入阿里云容器镜像服务控制台(https://cr.console.aliyun.com/cn-hangzhou/instances)
进入个人实例:
创建命名空间:
添加镜像仓库
执行命令将镜像推送到阿里云
- 登录校验:
docker login --username=自己账号 registry.cn-hangzhou.aliyuncs.com
- 标记本地镜像,将其归入阿里仓库
docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/dockertest-demo/centos7:[镜像版本号]
- 推送到阿里云
docker push registry.cn-hangzhou.aliyuncs.com/dockertest-demo/centos7:[镜像版本号]
- 登录校验:
拉取:
docker pull registry.cn-hangzhou.aliyuncs.com/dockertest-demo/centos7:[镜像版本号]
发布私有库
要搭建自己的私有库,必须通过docker官方的registry来创建。
首先拉取registry镜像:
docker pull registry
运行该私有库:
docker run -d -p 5000:5000 -v /wht/myregistry/:/tmp/registry --privileged=true registry
查询私有库中有什么镜像:
curl -XGET http://192.168.111.162:5000/v2/_catalog
标记镜像:
docker tag mycentos:centos7 192.168.227.113:5000/mycentos:1.2
修改配置文件使之支持http:
重启docker:
systemctl restart docker
(注意还需要重新运行私服库)推送私有库:
docker push 192.168.227.113:5000/mycentos:1.2
容器数据卷
卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性,设计目的就是数据的持久化。
例如刚才运行的私有库命令:docker run -d -p 5000:5000 -v 主机目录:容器内目录 --privileged=true registry
,这样实现了容器内数据与主机中的同步。
命令:
docker run -it -v /宿主机目录:/容器内目录:权限 容器名
- 权限说明:
- rw:默认值可读可写
- ro:容器实例内部被限制,只能读取不能写
数据卷继承:
docker run -it --privileged=true --volumes-from 父类容器名 容器名
特点:
- 数据卷可在容器之间共享或重用
- 数据卷中的更改可以直接实时生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
常用软件安装
安装Tomcat
查找tomcat镜像:
docker search tomcat
从远程仓库拉取镜像:
docker pull tomcat
查看是否有拉取到的tomcat:
docker images
使用tomcat镜像创建容器实例:
docker run -d -p 8080:8080 --name t1 tomcat
由于新版tomcat中的webapps中是空的所以需要把webapps.dist目录换成webapps(也完全可以下载8版本:
docker pull billygoo/tomcat8-jdk8
)- 进入tomcat容器:
docker exec -it tomcat版本号 /bin/bash
- 将webapps删除:
rm -r webapps
- 把webapps.dist目录换成webapps:
mv webapps.dist webapps
- 进入tomcat容器:
输入url访问:
安装完成!
安装mysql
查找mysql镜像:
docker search mysql
从远程仓库拉取镜像:
docker pull mysql:8.0.18
查看是否有拉取到的mysql:
docker images
查看linux是否已经启动了mysql:
ps -ef | grep mysql
如果已经启动了停止:
service mysql stop
(开启是service mysql start
)使用镜像运行容器:
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0.18
查看容器id:
docker ps
进入容器:
docker exec -it 容器id /bin/bash
进入容器后进入mysql:
mysql -uroot -p
进入后还需要改docker中mysql的字符集:
SHOW VARIABLES LIKE 'character%'
(稍后在配置文件中修改)退出关闭容器:
docker stop 容器id
重新开启容器(挂上数据卷):
1
2
3
4
5docker run -d -p 3306:3306 --privileged=true
-v /wht/mysql/log:/var/log/mysql
-v /wht/mysql/data:/var/lib/mysql
-v /wht/mysql/conf:/etc/mysql/conf.d
-e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:8.0.18安装完成!
安装redis
查找redis镜像:
docker search redis
从远程仓库拉取镜像:
docker pull redis:6.2.1
查看是否有拉取到的redis:
docker images
新建本机备份目录:
/opt/docker_app/redis
并将linux上的redis.conf复制到该备份目录
修改配置文件:
- 关闭后台启动:
daemonize no
(因为docker会-d将其后台运行) - 开启持久化:
appendonly yes
- 关闭后台启动:
停止本机的redis:
systemctl stop redis
创建并启动容器(添加数据卷):
1
docker run -p 6379:6379 --name myredis --privileged=true -v /opt/docker_app/redis/redis.conf:/etc/redis/redis.conf -v /opt/docker_app/redis/data:/data -d redis:6.2.1 redis-server /etc/redis/redis.conf
进入容器:
docker exec -it 运行着Rediis服务的容器ID redis-cli -a密码
安装完成!
其余更多环境安装详见菜鸟教程或者dockerhub官网(https://hub.docker.com/)中
MySql主从复制
创建主mysql服务器容器
新建主服务器容器实例端口号3307:
1
2
3
4
5
6docker run -p 3307:3306 --name mysql-master \
-v /mydata/mysql-master/log:/var/log/mysql \
-v /mydata/mysql-master/data:/var/lib/mysql \
-v /mydata/mysql-master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7进入/mydata/mysql-master/conf目录下新建my.cnf
配置my.cnf:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=101
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062修改完配置后重启master实例:
docker restart mysql-master
进入mysql-master容器:
docker exec -it mysql-master /bin/bash
进入mysql:
mysql -uroot -proot
master容器实例内创建数据同步用户:
1
2CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';创建从mysql服务器容器
新建从服务器容器实例3308:
1
2
3
4
5
6docker run -p 3308:3306 --name mysql-slave \
-v /mydata/mysql-slave/log:/var/log/mysql \
-v /mydata/mysql-slave/data:/var/lib/mysql \
-v /mydata/mysql-slave/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7进入/mydata/mysql-slave/conf目录下新建my.cnf
进行配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1修改完配置后重启slave实例:
docker restart mysql-slave
在主数据库中查看主从同步状态:
show master status;
进入mysql-slave容器:
docker exec -it mysql-slave /bin/bash
进入数据库:
mysql -uroot -proot
在从数据库中配置主从复制:
1
change master to master_host='宿主机ip', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;
主从复制命令参数说明:
- master_host:主数据库的IP地址;
- master_port:主数据库的运行端口;
- master_user:在主数据库创建的用于同步数据的用户账号;
- master_password:在主数据库创建的用于同步数据的用户密码;
- master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
- master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
- master_connect_retry:连接失败重试的时间间隔,单位为秒。
在从数据库中查看主从同步状态:
show slave status \G;
在从数据库中开启主从同步:
start slave
如果主数据库插入数据,从数据库能够看到说明插入成功
安装redis集群
当进行分布式存储时,会存在在哪台服务器上存取数据的问题,一般业界有三种落地实现方案
- 哈希取余分区
公式为hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪一个节点上。
优点:
使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化。
致性哈希算法分区
提出一致性Hash解决方案。 目的是当服务器个数发生变动时, 尽量减少影响客户端到服务器的映射关系
三大步骤:
算法构建一致性哈希环:
一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0,2^32-1],通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间
服务器IP节点映射:
将集群中各个IP节点映射到环上的某一个位置。将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置
key落到服务器的落键规则:
我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),将这个key使用相同的函数Hash计算出哈希值并确定此数据在环上的位置,从此位置沿环顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
优点:
有较强的容错性和扩展性
缺点:
Hash环的数据倾斜问题,一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题
哈希槽分区
为了解决上述方案的数据倾斜问题,出现了哈希槽。
解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系。
实现:
Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。
开始搭建集群
集群结构:
实现步骤:
启动6个redis容器:
1
2
3
4
5
6
7
8
9
10
11docker 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进入redis-node-1并为6台机器构建集群关系:
docker exec -it redis-node-1 /bin/bash
构建主从关系(每当主机挂了从机会补上,之后主机回来就会变成从机):
1
redis-cli --cluster create 192.168.227.130:6381 192.168.227.130:6382 192.168.227.130:6383 192.168.227.130:6384 192.168.227.130:6385 192.168.227.130:6386 --cluster-replicas 1
注意:
进入docker容器后才能执行一下命令,且注意自己的真实IP地址
TIP:
–cluster-replicas 1 表示为每个master创建一个slave节点
进入redis:
redis-cli -p 6381 -c
(-c会进行路由优化,将数据放在指定的槽位)查看集群状态:
cluster info 或者 cluster nodes
查看集群信息:
redis-cli --cluster check 真实ip地址:6381
主从扩容
新建6387、6388两个节点
1
2
3docker 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进入6387容器实例内部:
docker exec -it redis-node-7 /bin/bash
将新增的6387节点(空槽号)作为master节点加入原集群:
1
redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
检查集群情况第1次:
redis-cli --cluster check 真实ip地址:6381
重新分派槽号:
redis-cli --cluster reshard IP地址:6381
为主节点6387分配从节点6388:
redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID
主从缩容
目的:6387和6388下线
将6388删除 从集群中将4号从节点6388删除:
1
redis-cli --cluster del-node ip:从机端口 从机6388节点ID
查看集群信息:
redis-cli --cluster check ip:6382
将6387的槽号清空,重新分配,本例将清出来的槽号都给6381:
redis-cli --cluster reshard ip:6381
将6387删除:
redis-cli --cluster del-node ip:端口 6387节点ID