docker是一种基于GO语言实现的容器虚拟化技术,能够保证系统平滑移植,即软件带环境安装,将代码和环境打包成一个镜像文件,消除环境不一致问题
容器和虚拟机的区别:
传统虚拟机基于安装在主操作系统上的虚拟机管理系统,如VirtualBox、VMVare等,在虚拟机上安装从操作系统,然后部署各种应用软件
虚拟机缺点:
容器不是模拟一个完整的操作系统,运行于主操作系统之上,容器之间隔离,只需要软件工作所需的库和设置,更加轻量高效
Docker容器化优点:
docker官网:https://www.docker.com/
docker仓库:https://hub.docker.com/
docker的基本组成:
1.image(镜像)
镜像是一个只读的模版,通过镜像可以创建容器,同一个镜像可以创建多个容器。docker镜像相当于Java类
模版,而容器相当于创建出来的对象实例
UnionFS(联合文件系统),是一种分层、轻量级、高性能的文件系统,支持对文件系统的修改作为一次提交来
一层层的叠加,同时将不同的目录挂载到同一个虚拟文件系统之下。
联合文件系统是Docker镜像的基础,使得镜像可以分层进行继承,基于基础镜像制作出各种应用镜像
2.container(容器)
容器是用镜像创建的实例,可以看成一个Linux环境,包括root用户权限、进程空间、用户空间、网络空间和
运行的应用程序
3.repository(仓库)
集中存放镜像的位置
从docker仓库拉取相应的镜像到本地,将镜像构建成容器运行
docker需要安装在64位、版本大于3.8的Linux系统之上,查看Linux版本
uname -r
安装指南:https://docs.docker.com/engine/install/centos/
1.卸载旧版本docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2.安装gcc
yum -y install gcc
yum -y install gcc-c++
3.安装设置存储库
sudo yum install -y yum-utils
//添加阿里云仓库
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
4.更新yum软件包索引
yum makecache fast
5.安装docker引擎
sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
6.启动docker
sudo systemctl start docker
ps -ef | grep docker
docker version
7.Hello world测试
sudo docker run hello-world
8.配置阿里云镜像加速器
登录阿里云,点击容器镜像服务
systemctl start docker //启动docker
systemctl stop docker //关闭docker
systemctl restart docker //重启docker
systemctl status docker //查看docker状态
systemctl enable docker //开机自动启动docker
docker info //查看docker信息
docker 具体命令 --help //查看帮助文档
docker images //列出本地镜像
docker images -a //列出本地所有镜像(含历史版本)
docker images -q //只显示镜像id
repository:仓库源
tag:镜像的标签(版本)
image id:镜像id
created:镜像创建时间
size:镜像大小
注意:
同一个仓库源可以有多个tag版本,使用repository:tag
来表示不同版本的镜像,如果不指定版本,默认使用最新版本latest
docker search 镜像名称 //在远程仓库中查找镜像
docker search --limit N 镜像名称 //在远程仓库中查找指定数量的镜像(默认为25个)
name:镜像名称
description:镜像介绍
starts:点赞数
official:是否是官方的
automated:是否是自动构建的
docker pull 镜像名称[:版本号] //从远程仓库拉取镜像(默认最新版)
docker system df //查看镜像、容器、数据卷所占空间情况
docker rmi 镜像名称/id [镜像名称/id]//删除镜像(多个)
docker rmi -f 镜像名称/id //强制删除镜像
docker rmi -f ${docker images -qa} //删除所有镜像
docker run [options] image //运行镜像
options:
--name 指定容器名称
-d 后台运行容器(守护线程)
-i 以交互模式运行容器,通常与-t同时使用
-t 启动交互容器,给容器分配一个伪终端等待交互
-P 随机端口映射(大写P)
-p 指定端口映射(小写p)
直接运行没有任何显示:
交互模式运行 docker run -it ubuntu /bin/bash,可以输入一些核心命令,有些命令
无法使用(容器只包括Linux核心功能),exit退出终端
docker ps [options] //列出当前正在运行的容器
options:
-a 列出所有容器
-l 最新创建的容器
-n 最近n个创建的容器
-q 静默模式,只显示容器编号
exit //exit退出时,容器停止
ctrl + p + q //退出,容器不停止
docker restart 容器名称/id //重启容器
docker stop 容器名称/id //停止运行的容器
docker start 容器名称/id //启动停止的容器
docker kill 容器名称/id //强制停止容器
docker rm 容器名称/id //删除容器
docker rm -f 容器名称/id //强制删除容器
docker rm -f $(docker ps -aq) //删除所有容器
docker logs 容器名称/id //查看容器日志
docker top 容器名称/id //查看容器内进程
docker inspect 容器名称/id //查看容器内部信息
docker exec -it 容器名称/id redis-cli //以交互模式进入运行中的容器(退出终端不会导致容器停止)
docker attach 容器名称/id //以交互模式进入运行中的容器(退出终端会导致容器停止)
docker cp 容器名称/id:容器内路径 目的主机路径 //将容器中的文件拷贝到主机上
docker cp 目的主机路径 容器名称/id:容器内路径 //将主机上的文件拷贝到容器中
docker export 容器名称/id > 文件名.tar //导出容器
cat 文件名.tar | docker import - 用户名/镜像名:镜像版本号 //导入镜像
给Ubuntu镜像添加vim:
进入Ubuntu容器备份下载源文件:
docker exec -it ubuntu /bin/bash
cp /etc/apt/sources.list /etc/apt/sources.list.bak
创建sources.list
文件,修改下载源
# deb cdrom:[Ubuntu 16.04 LTS _Xenial Xerus_ - Release amd64 (20160420.1)]/ xenial main restricted
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse
docker cp sources.list ubuntu:/etc/apt //覆盖下载源信息
安装vim:
apt-get update //更新包管理工具
apt-get install libtinfo5 //vim相关依赖
apt-get install libpython3.5 //vim相关依赖
apt-get install vim //下载vim
如果在更新包管理工具时报错没有公钥无法验证签名:
GPG error: http://archive.canonical.com/ubuntu xenial InRelease: The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY 40976EAF437D05B5 NO_PUBKEY 3B4FE6ACC0B21F32
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 公钥(3B4FE6ACC0B21F32)
测试vim:
vim 1.txt //编写txt文件(内容任意)
提交新的Ubuntu镜像:
docker commit -m="描述信息" -a="作者" 容器名称/id 镜像名称[:标签]
docker hub注册账号:https://registry.hub.docker.com/signup
docker login //登录
docker tag 镜像名称 仓库名称/镜像名称:版本号 //打标签
docker rmi -f 仓库名称/镜像名称:版本号 //删除标签
docker push 镜像名称:版本号 //推送到docker hub
测试:
由于docker hub外网访问较慢,可以将本地镜像推送到阿里云:
创建命名空间、镜像仓库
测试:
搭建docker私有库
docker pull registry //拉取registry镜像
docker run -d -p 5000:5000 -v /registry:/tmp/registry --privileged=true registry //运行registry镜像
//需要开启http支持
vim /etc/docker/daemon.json 添加一行 "insecure-registries":["ip:port"]
docker tag qingsongxyz/ubuntu localhost:5000/ubuntu:v1.0 //打标签
docker push localhost:5000/ubuntu:v1.0 //推送到私有库
curl -XGET http://127.0.0.1:5000/v2/_catalog //查看私有库镜像
测试:
数据卷就是目录文件,存在于一个或多个目录中,由docker挂在到容器,用于数据持久化和共享数据,完全独立于容器之外,容器删除时不能一起删除
在centos7中挂载目录默认为不安全行为,禁止该行为,可以使用--privileged=true
开启,扩大容器权限,使得容器内的root权限拥有主机的root权限
docker run -it --privileged=true -v /本机绝对路径:/容器内目录 镜像名
搭载数据卷后,容器内指定目录文件和本机指定目录文件实时同步(双向):
停止容器后,主机对指定目录的更新,在容器启动后会全部同步
docker inspect 容器名称/id //查看容器挂载数据卷
默认搭载数据卷后主机和容器支持双向读写共享
docker run -it --privileged=true -v /本机绝对路径:/容器内目录[:rw] 镜像名
docker run -it --privileged=true -v /本机绝对路径:/容器内目录[:ro] 镜像名 //容器只能读不能写
容器中指定目录不能创建文件,不能对文件进行写操作:
主机目录更新依然能同步,但容器内只能进行读操作:
//继承父容器 搭载数据卷相同
docker run -it --privileged=true --volumes-from 父容器名称/id 子容器名称/id
任何一处修改,主机、父容器、子容器进行同步(父容器停止不会影响子容器):
docker search tomcat --limit 5 //搜索tomcat
docker pull tomcat //拉取tomcat
docker run -d -p 8080:8080 --name tomcat tomcat //启动tomcat
浏览器访问:
docker search mysql //搜索mysql
docker pull mysql:8.0.23 //拉取mysql
docker run -p 3306:3306 --name mysql
--privileged=true
-v /docker/registry/mysql/log:/var/log/mysql \ //挂载日志目录
-v /docker/registry/mysql/data:/var/lib/mysql \ //挂载数据目录
-v /docker/registry/mysql/conf:/etc/mysql/conf.d \ //挂载配置目录
-e MYSQL_ROOT_PASSWORD=密码 -d mysql:8.0.23 //运行
----------------------------------------------------------------
创建配置文件my.cnf,修改编码:
[client]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
docker restart mysql //重启mysql
docker exec -it mysql /bin/bash //进入mysql容器
MySQL8以上版本的账户加密方式是caching_sha2_password
,Navicat不支持这种账户加密方式
use mysql;
select host,user,plugin from user;
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '密码'; //修改为原始加密方式
没有中文乱码
docker run -p 3306:3306 --name mysql-master --privileged=true \
-v /docker/registry/mysql/mysql-master/log:/var/log/mysql \
-v /docker/registry/mysql/mysql-master/data:/var/lib/mysql \
-v /docker/registry/mysql/mysql-master/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=密码 -d mysql:8.0.23 //运行主库3306
主库 my.cnf:
[client]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
##设置server id 唯一
server_id=101
##指定不需要同步的数据库名称
binlog-ignore-db=mysql
##开启binlog, 指定名称
log-bin=mysql-master-bin
##设置binlog 使用内存大小
binlog-cache-size=1M
##binlog 过期时间
expire_logs_days=7
##忽略主从复制中的错误,避免从库复制中断 1062错误指主键重复 1032错误指主从不一致
slave_skip_errors=1062
------------------------------------------------------------------------------
docker run -p 3307:3306 --name mysql-slave --privileged=true \
-v /docker/registry/mysql/mysql-slave/log:/var/log/mysql \
-v /docker/registry/mysql/mysql-slave/data:/var/lib/mysql \
-v /docker/registry/mysql/mysql-slave/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=密码 -d mysql:8.0.23 //运行从库3307
从库 my.cnf:
[client]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
##设置server id 唯一
server_id=102
##指定不需要同步的数据库名称
binlog-ignore-db=mysql
##开启binlog, 指定名称
log-bin=mysql-slave-bin
##设置binlog 使用内存大小
binlog-cache-size=1M
##binlog 过期时间
expire_logs_days=7
##忽略主从复制中的错误,避免从库复制中断 1062错误指主键重复 1032错误指主从不一致
slave_skip_errors=1062
##relay_log中继日志
relay_log=mysql-slave-relay-bin
##slave将复制事件写入自己的binlog
log_slave_updates=1
##slave只读(具有super权限用户除外)
read_only=1
##super超级管理员只读
super_read_only=1
查看主库ip:
docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql-master
查看主库binlog信息:
show master status;
change master to master_host='172.17.0.2', master_user='root', master_password='密码', master_port=3306, master_log_file='mysql-master-bin.000001', master_log_pos=156, master_connect_retry=30; //从库配置主从复制
start slave; //开启主从复制
show slave status\G; //显示主从复制信息
主库查看binlog:
从库配置主从复制:
查看主从复制信息:
注意:
主库和从库都需要配置账户加密方式为mysql_native_password
,以便Navicat连接
测试:
主库建库建表,从库同步:
主库插入数据:
从库同步:
从库执行写操作:
准备一个redis.conf配置文件,进行一下修改:
docker run -p 6379:6379 --name redis6379 --privileged=true
-v /docker/registry/redis/conf/redis.conf:/etc/redis/redis.conf //挂载redis配置文件
-v /docker/registry/redis/data/:/data //挂载redis数据目录
-d redis:7.0.0 redis-server /etc/redis/redis.conf //运行redis
docker exec -it redis6379 redis-cli //进入redis容器
创建redis-cluster.tmpl文件(用于创建redis.conf):
#节点端口
port ${PORT}
#开放端口访问
bind 0.0.0.0
#设置密码
requirepass 密码
masterauth 密码
#关闭保护模式
protected-mode no
#不以守护进程 后台运行
daemonize no
#开启aof持久化
appendonly yes
#启用集群
cluster-enabled yes
#集群配置文件
cluster-config-file nodes.conf
#连接超时时间
cluster-node-timeout 15000
#集群各节点IP地址
cluster-announce-ip 外网IP
#集群节点映射端口
cluster-announce-port ${PORT}
#集群通信端口 节点端口+10000
cluster-announce-bus-port 1${PORT}
for port in `seq 7000 7005`; do \
mkdir -p redis${port}/conf \
&& PORT=${port} envsubst < redis-cluster.tmpl > redis${port}/conf/redis.conf \
&& mkdir -p redis${port}/data;\
done
**注意:**云服务器需要配置安全组规则,添加所有端口 eg.(7000和17000 …)
for port in $(seq 7000 7005); do \
docker run -d --name redis${port} -p ${port}:${port} -p 1${port}:1${port}
--privileged=true
-v /docker/registry/redis/rediscluster/redis${port}/conf/redis.conf:/etc/redis/redis.conf
-v /docker/registry/redis/redis-cluster/redis${port}/data:/data
redis:7.0.0 redis-server /etc/redis/redis.conf;
done
//主节点为7000、7001、7002 每个主节点有一个从节点 所属从节点随机分配
redis-cli -a 密码 --cluster create --cluster-replicas 1 外网IP:7000 外网IP:7001 外网IP:7002 外网IP:7003 外网IP:7004 外网IP:7005
redis-cli -c -a root -p 7000
cluster info //查看集群状态
cluster nodes //查看节点信息
当前主从信息:
节点 | 槽位 | 从节点 |
---|---|---|
7000 | 0 - 5460 | 7004 |
7001 | 5461 - 10922 | 7005 |
7002 | 10923 - 16383 | 7003 |
redis-cli -a 密码 --cluster check 外网IP:7000 //查看节点存储key数量和主从配置信息
设置key获取key时,如果该key所在的槽位不在当前节点上会重定向到对应的节点
主从切换:
当主节点宕机后,对应的从节点会变成主节点
停止redis7000容器让其宕机,节点7004变成主节点
启动redis7000容器使其恢复后,变为从节点
集群扩缩容:
添加redis节点:
创建配置文件:
port 7006
#开放端口访问
bind 0.0.0.0
#设置密码
requirepass 密码
masterauth 密码
protected-mode no
daemonize no
appendonly yes
#打开集群
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-announce-ip 外网IP
cluster-announce-port 7006
#集群通信端口
cluster-announce-bus-port 17006
运行容器redis7006:
docker run -d --name redis7006 -p 7006:7006 -p 17006:17006 --privileged=true
-v /docker/registry/redis/redis-cluster/redis7006/conf/redis.conf:/etc/redis/redis.conf
-v /docker/registry/redis/redis-cluster/redis7006/data:/data
redis:7.0.0 redis-server /etc/redis/redis.conf
将redis7006节点加入集群:
docker exec -it redis7006 bash //进入新创建的容器redis7006
redis-cli -a 密码 --cluster add-node 外网IP:7006(自己) 外网IP:7000(集群中的一个主节点) //将自己加入集群
redis-cli -a 密码 --cluster check 外网IP:7006 //查看集群信息
重新分配槽位:
redis-cli -a 密码 --cluster reshard 外网IP:7000 //重新分配槽位
redis-cli -a 密码 --cluster check 外网IP:7006 //查看集群信息
重新分配成本太高,三个节点各自匀出来一部分槽位分给redis7006节点
给redis7006添加从节点redis7007:
docker run -d --name redis7007 -p 7007:7007 -p 17007:17007 --privileged=true
-v /docker/registry/redis/redis-cluster/redis7007/conf/redis.conf:/etc/redis/redis.conf
-v /docker/registry/redis/redis-cluster/redis7007/data:/data
redis:7.0.0 redis-server /etc/redis/redis.conf
添加为redis7006的从节点
redis-cli -a 密码 --cluster add-node 外网IP:7007 外网IP:7006 --cluster-slave --cluster-master-id 主节点id
redis-cli -a 密码 -c -p 7007
cluster nodes //集群节点信息
减少redis节点:
先删除从节点redis7007:
redis-cli -a 密码 --cluster del-node 外网IP:7007 节点id
redis-cli -a 密码 --cluster check 外网:7000
将redis7006的槽位重新分配给redis7000
redis-cli -a 密码 --cluster reshard 外网IP:7000 //重新分配槽位
redis-cli -a 密码 --cluster check 外网IP:7006 //查看集群信息
删除节点redis7006:
redis-cli -a 密码 --cluster del-node 外网IP:7007 节点id
redis-cli -a 密码 --cluster check 外网:7000
chmod -R 777 elasticsearch //修改文件夹授权 所有用户均可读写、执行
elasticsearch.yml:
cluster.name: "docker-cluster"
network.host: 0.0.0.0
#设置允许跨域请求访问
http.cors.enabled: true
http.cors.allow-origin: "*"
# 开启账户密码验证
http.cors.allow-headers: Authorization,X-Requested-With,Content-Length,Content-Type
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
docker run --name elasticsearch --net es-net -p 9200:9200 -p 9300:9300
-e "discovery.type=single-node"
-e ES_JAVA_OPTS="-Xms64m -Xmx256m" //设置最大可用内存256m
--privileged=true
-v /docker/registry/elasticsearch/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
-v /docker/registry/elasticsearch/data:/usr/share/elasticsearch/data
-v /docker/registry/elasticsearch/logs:/usr/share/elasticsearch/logs
-v /docker/registry/elasticsearch/plugins:/usr/share/elasticsearch/plugins
-d elasticsearch:7.9.2 //运行elasticsearch
docker exec -it elasticsearch bash
bin/elasticsearch-setup-passwords interactive //设置elasticsearch密码
浏览器访问9200端口:
安装ik分词器:
官网下载对应版本ik分词器zip:https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v5.6.3
unzip 解压到plugins目录下
docker restart elasticsearch //重启elasticsearch容器
docker logs elasticsearch
docker exec -it elasticsearch bash //进入容器
bin/elasticsearch-plugin list //查看加载的插件
安装elasticsearch-head插件:
docker run --name elasticsearch-head -p 9100:9100 -d mobz/elasticsearch-head:5
docker cp elasticsearch-head:/usr/src/app/_site/vendor.js ./vendor.js
# 修改6886、7574行( application/x-www-form-urlencoded 改成 application/json;charset=UTF-8 )
sed -i 's#application/x-www-form-urlencoded#application/json;charset=UTF-8#g' vendor.js
docker rm -f -d elasticsearch-head
docker run --name elasticsearch-head --net es-net -p 9100:9100 --privileged=true -v /docker/registry/elasticsearch-head/vendor.js:/usr/src/app/_site/vendor.js -d mobz/elasticsearch-head:5
运行sql脚本:
CREATE DATABASE nacos_config;
USE nacos_config;
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info */
/******************************************/
CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) DEFAULT NULL,
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL,
`c_use` varchar(64) DEFAULT NULL,
`effect` varchar(64) DEFAULT NULL,
`type` varchar(64) DEFAULT NULL,
`c_schema` text,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_aggr */
/******************************************/
CREATE TABLE `config_info_aggr` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) NOT NULL COMMENT 'group_id',
`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
`content` longtext NOT NULL COMMENT '内容',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_beta */
/******************************************/
CREATE TABLE `config_info_beta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_tag */
/******************************************/
CREATE TABLE `config_info_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_tags_relation */
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = group_capacity */
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = his_config_info */
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(64) unsigned NOT NULL,
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`data_id` varchar(255) NOT NULL,
`group_id` varchar(128) NOT NULL,
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL,
`md5` varchar(32) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
`gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
`src_user` text,
`src_ip` varchar(20) DEFAULT NULL,
`op_type` char(10) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = tenant_capacity */
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE users (
username varchar(50) NOT NULL PRIMARY KEY,
password varchar(500) NOT NULL,
enabled boolean NOT NULL
);
CREATE TABLE roles (
username varchar(50) NOT NULL,
role varchar(50) NOT NULL
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
编写application.properties:
# spring
server.servlet.contextPath=${SERVER_SERVLET_CONTEXTPATH:/nacos}
server.contextPath=/nacos
server.port=8848
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://IP:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=用户名
db.password=密码
db.pool.config.connectionTimeout=30000
db.pool.config.validationTimeout=10000
db.pool.config.maximumPoolSize=20
db.pool.config.minimumIdle=2
docker run --name nacos -p 8848:8848 \
--privileged=true \
-e JVM_XMS=64m \
-e JVM_XMX=64m \
-e MODE=standalone \
-e PREFER_HOST_MODE=外网IP \
-v /docker/registry/nacos/logs:/home/nacos/logs \
-v /docker/registry/nacos/conf/application.properties:/home/nacos/conf/application.properties \
-d nacos/nacos-server:1.1.4
浏览器访问8848端口,默认用户名密码均为nacos
:
修改用户名密码:
修改nacos_config数据库user表
默认用户名nacos,密码$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu
(Bcrypt加密,明
文为nacos),直接修改即可,密码需要进行Bcrypt加密
Bcrypt加密网站:https://www.jisuan.mobi/p163u3BN66Hm6JWx.html
注意:
如果连接mysql8需要添加相应的驱动,进入容器创建/plugins/mysql
目录,放在其中
下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java
重启容器即可
编写file.conf:
service {
#transaction service group mapping
vgroup_mapping.my_test_tx_group = "qingsongxyz" # 随便起一个名称 作为事务组名称
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#disable seata
disableGlobalTransaction = false
}
## transaction log store, only used in seata-server
store {
## store mode: file、db
mode = "file"
## file store property
file {
## store location dir
dir = "sessionStore"
}
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql"
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "mysql"
password = "mysql"
}
}
编写registry.conf:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos" # 将seata-server注册到nacos中
nacos {
application = "seata-server"
serverAddr = "外网IP:8848" # nacos IP端口
namespace = ""
cluster = "default"
username = "nacos" # 用户名
password = "nacos" # 密码
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
docker run --name seata-server \
-p 8091:8091 -p 7091:7091 \
-e SEATA_IP=外网IP \
-v /docker/registry/seata/file.conf:/seata-server/resources/file.conf \
-v /docker/registry/seata/registry.conf:/seata-server/resources/registry.conf \
-d seataio/seata-server:1.0.0 //运行seata-server1.0.0(选择对应的版本)
docker exec -it seata-server sh //进入容器seata-server
docker cp mysql-connector-java-8.0.30.jar seata-server:/seata-server/libs //复制文件到容器
**注:**云服务器需要配置安全组规则开启8091、7091端口
默认Connector/J 5.1.30连接mysql5版本,如果连接mysql8版本需要使用Connector/J 8.0.30
下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java
在此使用mysql8版本,删除Connector/J 5.1.30以免出错
mysql创建seata数据库和所需表
Seata1.0.0版本Sql脚本:https://github.com/seata/seata/blob/1.0.0/script/server/db/mysql.sql
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
修改file.conf:
service {
#transaction service group mapping
vgroup_mapping.my_test_tx_group = "qingsongxyz"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#disable seata
disableGlobalTransaction = false
}
## transaction log store, only used in seata-server
store {
## store mode: file、db
mode = "db" # 存储到数据库
## file store property
file {
## store location dir
dir = "sessionStore"
}
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "druid"
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql"
driver-class-name = "com.mysql.cj.jdbc.Driver" # mysql8版本驱动
url = "jdbc:mysql://外网IP:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false"
user = "用户名"
password = "密码"
}
}
docker restart seata-server //重启容器
docker logs seata-server //查看容器日志
查看nacos注册seata-server成功
docker仓库:https://registry.hub.docker.com/r/bladex/sentinel-dashboard
docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard:1.7.0
docker run --name sentinel -d -p 8858:8858 -d
-v /root/docker/sentinel/log/:/Users/sczyh30/logs/csp //挂在日志目录
-e sentinel.dashboard.auth.username=用户名
-e sentinel.dashboard.auth.password=密码
bladex/sentinel-dashboard:1.7.0
使用原始密码(sentinel/sentinel)登录失败:
查看日志文件:
Dockerfile是用来构建docker镜像的文本文件,是由一条条构建镜像所需的指令和参数组成的脚本
官网:https://docs.docker.com/engine/reference/builder/
Dockerfile是独立于docker之外的文件:
Dockerfile需要遵循以下规则:
Docker执行Dockerfile步骤:
1.docker基于基础镜像运行一个容器
2.执行一条指令对容器作出修改
3.执行docker commit操作提交一个镜像层
4.然后基于提交的镜像运行一个容器
5.执行dockerfile的下一条指令直到所有指令都执行完
FROM:
指定当前镜像基于哪一个基础镜像,Dockerfile第一条语句必须是FROM
MAINTAINER:
表示镜像维护者的姓名和邮箱地址
RUN:
容器构建时(docker build)需要运行的命令,有两种格式shell和exec
EXPOSE:
表示当前容器对外暴露的端口
WORKDIR:
指定创建容器后,终端登录后默认工作目录
USER:
指定该镜像由哪个用户执行,默认为root
ENV:
在构建镜像过程中设置环境变量
VOLUME:
指定容器数据卷,用于持久化数据
ADD:
将宿主机目录下的文件拷贝进镜像且会自动处理URL和tar压缩包
COPE:
CMD:
指定容器运行时(docker run)执行的操作,Dockerfile可以有多个CMD指令,但只有最后一个生效,CMD会被
之后的参数替换
ENTRYPOINT:
指定容器运行时执行的操作,类似CMD,但是不会被docker run后面的参数覆盖,而是将这些参数传递给
ENTRYPOINT指令指定的程序
ENTRYPOINT可以和CMD一起使用,此时CMD将给ENTRYPOINT传递参数,
Dockerfile构建镜像步骤:
1.编写Dockerfile
2.docker build命令构建镜像
3.docker run运行镜像实例
centosplus:在centos镜像的基础上下载vim编辑器、ifconfig命令、jdk1.8环境
下载jdk压缩包,编写Dockerfile:
FROM centos:7
MAINTAINER qingsongxyz<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
##安装vim
RUN yum -y install vim
##安装ifconfig
RUN yum -y install net-tools
##安装jdk8和lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
##添加jdk压缩包
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java
##配置java环境变量
ENV JAVA_HOME=/usr/local/java/jdk1.8.0_171
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo 'success...'
CMD /bin/bash
docker build -t 镜像名称[版本号] . //在含有Dockerfile的目录执行build命令
仓库名、标签都是
的镜像,称为虚悬镜像(dangling image),这些镜像是出现错误产生的,占据宿主
机空间需要进行删除
docker image ls -f dangling=true //查看虚悬镜像
docker image prune //删除所有虚悬镜像
创建Springboot项目,编写控制器:
@RestController
public class HelloController {
@Value("${server.port}")
private String serverPort;
@GetMapping("/hello")
public String helloWorld(){
return "Hello World! serverPort=" + serverPort;
}
}
POM.xml:
<build>
<finalName>${project.artifactId}finalName>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<executions>
<execution>
<phase>packagephase>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
<configuration>
<includeSystemScope>trueincludeSystemScope>
<mainClass>com.qingsongxyz.HelloWorldApplicationmainClass>
configuration>
plugin>
plugins>
build>
将jar包上传到服务器,编写Dockerfile:
FROM java:8
VOLUME /tmp
ADD HelloWorld.jar HelloWorld.jar
RUN bash -c 'touch HelloWorld.jar'
ENTRYPOINT ["java", "-jar", "HelloWorld.jar"]
EXPOSE 8080
构建镜像:
运行镜像:
访问测试:
docker网络用于容器间的互联通信以及端口映射,在容器IP变动时可以通过服务名进行网络通信
启动docker后,会产何时能一个名为docker0
的虚拟网桥
eth0:表示主机的以太网卡
lo:表示主机的回环地址,是本机用来测试的网络地址
docker network creat 网络名称//创建网络
docker network inspect 网络名称//展示网络信息
docker network ls //显示所有网络
docker rm 网络名称 //删除网络
网络模式 | 说明 |
---|---|
bridge(默认) | 为每一个容器分配IP,将容器连接到名为docker0 的虚拟网桥 |
host | 容器不会虚拟出自己的网卡、配置IP,而是使用宿主机的IP和端口 |
none | 容器有独立的Network namespace,但没有配置任何网络设置 |
container | 和指定的容器共享IP和端口 |
bridge:
宿主机有一个网桥docker0,docker0上有一系列的虚拟接口veth,每个容器实例内部有一块网卡eth0
docker0上面的每一个veth和容器实例内部的eth0一一匹配。宿主机和容器、容器与容器之间都需要通过网桥
docker0进行通信
测试docker0中的veth和容器中eth0一一匹配:
宿主机查看网络配置:
容器内部查看网络配置:
host:
docker run -d -p 8080:8080 --network host --name tomcat tomcat //无需指定端口映射
docker run -d --network host --name tomcat tomcat //正确
主机模式容器内网络配置和宿主机一致
使用默认端口8080访问:
none:
docker run -it --name alpine --network none alpine /bin/sh //none模式运行
只有回环地址
container:
docker run -it --name alpine1 alpine /bin/sh //运行容器alpine1
docker run -it --name alpine2 --network container:alpine1 alpine /bin/sh //container模式运行容器alpine2
alpine2和alpine1一个eth0
alpine1容器关闭,alpine2中网络配置消失:
**问题:**docker容器内部ip可能发生变化
// 运行两个容器
docker run -it --name alpine1 alpine /bin/sh
docker run -it --name alpine2 alpine /bin/sh
// 查看两个容器分配的ip
docker inspect alpine1 | tail -n 20
docker inspect alpine2 | tail -n 20
删除容器alpine2(模拟容器宕机),另外创建一个新的容器:
docker rm -f alpine2 // 删除容器alpine2
docker run -it --name alpine3 alpine /bin/sh //创建新的容器alpine3
docker inspect alpine3 | tail -n 20 //查看alpine3分配IP
总结:
容器当宕机后,后续创建的容器会继续使用其IP地址。对于固定IP地址进行通信,可能不会是同一个容器
没有使用自定义网络,无法使用容器名称进行通信:
使用自定义网络后,可以使用容器名称进行通信:
Docker Compose负责对Docker容器集群进行快速编排,需要定义一个docker-compose.yml
,声明多个容器
之间的调用关系,一条命令运行、停止多个容器
修改微服务helloworld:
导入依赖:
<dependency>
<groupId>io.springfoxgroupId>
<artifactId>springfox-boot-starterartifactId>
<version>3.0.0version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.3.4version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.4version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.80version>
dependency>
配置文件yaml(redis、mysql使用docker 容器名称连接):
server:
port: 8080
# 应用名称
spring:
application:
name: HelloWorld
# swagger和springboot整合问题
mvc:
pathmatch:
matching-strategy: ant_path_matcher
# 数据源
datasource:
username: root
password: 密码
url: jdbc:mysql://helloworld_mysql:3306/test
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
aop-patterns: com.qingsongxyz.* # 配置Spring监控
filters: 'stat,wall'
stat-view-servlet:
enabled: true # 打开监控统计功能
login-username: admin
login-password: admin
reset-enable: true
web-stat-filter:
enabled: true # Web关联监控配置
filter:
stat:
enabled: true # 开启sql监控
wall:
enabled: true # 开启防火墙
db-type: mysql
config:
delete-allow: false
drop-table-allow: false
# 单点redis配置
redis:
host: helloworld_redis
port: 6379
password: 密码
client-type: lettuce
# mybatis-plus
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 配置日志
type-aliases-package: com.qingsongxyz.pojo
mapper-locations: classpath*:mapper/**/*.xml
# redis日志
logging:
level:
io:
letture:
core: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
swagger配置文件:
@Configuration
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.OAS_30)
.groupName("开发1组")
.select()
.apis(RequestHandlerSelectors.basePackage("com.qingsongxyz.controller"))
.build()
.apiInfo(createApiInfo())
.enable(true);
}
@Bean
public ApiInfo createApiInfo() {
return new ApiInfo("qingsongxyz Swagger",
"qingsongxyz Api Documentation",
"3.0",
"xxx",
new Contact("qingsongxyz", "xxx", "[email protected]"),
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
Redis配置文件:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//方法已过时
//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
mybatis-plus配置文件:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//分表
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//阻止恶意的全表更新删除
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
}
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
this.setFieldValByName("deleted", 0, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
}
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("`user`")
@ApiModel(description = "用户实体类")
public class User implements Serializable {
@ApiModelProperty("用户id")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("性别")
private String gender;
@ApiModelProperty("逻辑删除字段")
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
@ApiModelProperty("创建时间")
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@ApiModelProperty("修改时间")
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@ApiModelProperty("版本号")
@Version
private Long version;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "响应实体类")
public class CommonResult {
private String code;
private Object data;
private String message;
public static CommonResult ok(){
return new CommonResult("200", null, null);
}
public static CommonResult ok(String message){
return new CommonResult("200", null, message);
}
public static CommonResult ok(Object data, String message){
return new CommonResult("200", data, message);
}
public static CommonResult failure(String code, String message){
return new CommonResult(code, null, message);
}
public static CommonResult failure(String code, List<ObjectError> error){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("参数校验失败");
for (ObjectError objectError : error) {
stringBuilder.append(",");
stringBuilder.append(objectError.getDefaultMessage());
}
return new CommonResult(code, null, stringBuilder.toString());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户实体传输类")
public class UserDTO {
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("性别")
private String gender;
@ApiModelProperty("逻辑删除字段")
private Integer deleted;
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
@ApiModelProperty("修改时间")
private LocalDateTime updateTime;
@ApiModelProperty("版本号")
private Long version;
}
UserMapper:
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
UserService:
public interface UserService extends IService<User> {
int addUser(User user);
UserDTO findUserById(long id);
}
UserServiceImpl:
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
private static final String USER_KEY = "user:";
@Override
public int addUser(User user) {
log.info("user:{}", user);
//1.插入mysql数据库
int insert = userMapper.insert(user);
if (insert > 0) {
//查询出刚添加的用户信息
User u = userMapper.selectById(user.getId());
//转换为userDTO
UserDTO userDTO = new UserDTO();
BeanUtil.copyProperties(u, userDTO, false);
Map<String, Object> map = BeanUtil.beanToMap(userDTO, new HashMap(), CopyOptions.create()
.ignoreNullValue()
.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())
);
//2.如果mysql插入成功,写入redis缓存
stringRedisTemplate.opsForHash().putAll(USER_KEY + user.getId(), map);
//设置过期时间1分钟
stringRedisTemplate.expire(USER_KEY + user.getId(), 1, TimeUnit.MINUTES);
}
return insert;
}
@Override
public UserDTO findUserById(long id) {
UserDTO result = new UserDTO();
//1.根据id从redis中获取用户信息
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(USER_KEY + id);
log.info("redis:entries={}", entries);
if (ObjectUtil.isEmpty(entries)) {
//2.如果缓存中没有,查询mysql
User user = userMapper.selectById(id);
log.info("mysql:user={}", user);
//3.将mysql中查询到的用户信息进行缓存
if (!ObjectUtil.isEmpty(user)) {
//转换为userDTO
BeanUtil.copyProperties(user, result, CopyOptions.create());
Map<String, Object> map = BeanUtil.beanToMap(result, new HashMap(), CopyOptions.create()
.ignoreNullValue()
.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())
);
//4.存入redis
stringRedisTemplate.opsForHash().putAll(USER_KEY + id, map);
//设置过期时间1分钟
stringRedisTemplate.expire(USER_KEY + user.getId(), 1, TimeUnit.MINUTES);
}
} else {
//转换为userDTO
result = BeanUtil.fillBeanWithMap(entries, result, true);
}
log.info("result:{}", result);
return result;
}
}
UserController:
@RestController
@Slf4j
@Api(tags = "用户类测试")
public class UserController {
@Resource
private UserService userService;
@ApiOperation("测试添加用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query", dataTypeClass = String.class),
@ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query", dataTypeClass = String.class),
@ApiImplicitParam(name = "gender", value = "性别", required = true, paramType = "query", dataTypeClass = String.class)
})
@ApiResponses({
@ApiResponse(code = 400, message = "参数有误"),
@ApiResponse(code = 401, message = "没有认证"),
@ApiResponse(code = 403, message = "没有权限")
})
@PostMapping("/user/u")
public CommonResult addUser(User user) {
int insert = userService.addUser(user);
if (insert > 0) {
return CommonResult.ok("添加用户成功!");
} else {
return CommonResult.failure("500", "添加用户失败!!!");
}
}
@ApiOperation("测试通过id查询用户信息")
@ApiImplicitParam(name = "id", value = "用户id", required = true, paramType = "path", dataTypeClass = Long.class)
@ApiResponses({
@ApiResponse(code = 400, message = "参数有误"),
@ApiResponse(code = 401, message = "没有认证"),
@ApiResponse(code = 403, message = "没有权限")
})
@GetMapping("/user/{id}")
public CommonResult findUserById(@PathVariable("id") long id) {
UserDTO userDTO = userService.findUserById(id);
if(ObjectUtil.isEmpty(userDTO))
{
return CommonResult.failure("500","通过id获取用户信息失败!!!");
}else {
return CommonResult.ok(userDTO,"通过id获取用户信息成功!");
}
}
}
version: "3.9"
services:
helloworld: # 服务名称
image: helloworld # 镜像名称
container_name: helloworld # 容器名称
ports: # 端口映射
- "8080:8080"
volumes: # 挂载数据卷
- /root/helloworld/app:/data
networks: # 连接网络
- helloworld_net
depends_on: # 依赖于redis、mysql服务,在它们运行后才运行
- redis
- mysql
redis:
image: redis:7.0.0
container_name: helloworld_redis
ports:
- "6379:6379"
networks:
- helloworld_net
volumes:
- /root/helloworld/redis/conf/redis.conf:/etc/redis/redis.conf
- /root/helloworld/redis/data/:/data
command: redis-server /etc/redis/redis.conf # 指定配置文件运行
mysql:
image: mysql:8.0.23
container_name: helloworld_mysql
ports:
- "3306:3306"
environment: # 配置root密码, 数据库
MYSQL_ROOT_PASSWORD: '密码'
MYSQL_ALLOW_EMPTY_PASSWORD: 'no'
MYSQL_DATABASE: 'test'
volumes:
- /root/helloworld/mysql/init:/docker-entrypoint-initdb.d
- /root/helloworld/mysql/data:/var/lib/mysql
- /root/helloworld/mysql/conf:/etc/my.cnf
networks:
- helloworld_net
command: --default-authentication-plugin=mysql_native_password # 外部访问
networks:
helloworld_net: # 创建网络
docker compose config -q //检查yaml文件是否存在语法错误
docker compose up -d //后台运行
docker compose down //停止所有服务,并删除所有相关容器
进入mysql容器内部建表:
USE test;
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户id',
`username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
`password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`gender` char(2) NOT NULL COMMENT '性别',
`deleted` int unsigned NOT NULL COMMENT '逻辑删除字段',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '修改时间',
`version` bigint NOT NULL DEFAULT '1' COMMENT '版本号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
外部访问8080端口,/swagger-ui路径:
portainer是一款轻量级的应用,提供图形化界面,方便管理Docker环境
官网:https://www.portainer.io/
拉取镜像,portainer/portainer已经弃用了,推荐使用portainer/portainer-ce
官方镜像:https://registry.hub.docker.com/r/portainer/portainer-ce
汉化版镜像:https://registry.hub.docker.com/r/6053537/portainer-ce
下面使用汉化版镜像测试:
docker pull 6053537/portainer-ce //拉取镜像
docker run -d --restart=always
--name="portainer" -p 9000:9000
-v /var/run/docker.sock:/var/run/docker.sock
-v portainer_data:/data
6053537/portainer-ce
[--restart=always:docker重启portainer也会重启保证该容器一直运行进行监控]
浏览器访问9000端口:
测试运行nginx容器:
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
CIG:CAdvisor(监控收集) + Influxdb(存储数据) + Granfana(图标展示)
CAdvisor是一个容器资源监控工具,包括内存、CPU、网络IO、磁盘IO,提供页面查看容器实时状态,默认
存储2分钟的数据,且只针对单机,支持Influxdb(推荐)、Redis、Kafka、ElasticSearch等软件对数据进行存
储
Influxdb:用Go语言编写的分布式时序、事件和指标数据库,适合存储CAdvisor中时序相关的数据
Granfana:数据监控分析可视化平台,支持多种数据源配置、丰富的插件、图标和报警
CAdvisor官网:https://github.com/google/cadvisor
Influxdb官网:https://www.influxdata.com/
Granfana官网:https://grafana.com/
docker-compose安装CIG:
version: '3.9'
volumes:
grafana_data: {}
services:
influxdb:
image: tutum/influxdb
container_name: influxdb
restart: always
environment:
- PRE_CREATE_DB=cadvisor # 预先创建数据库cadvisor
ports:
- "8083:8083"
- "8086:8086"
volumes:
- ./data/influxdb:/data
cadvisor:
image: google/cadvisor
container_name: 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
container_name: grafana
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数据库名称
- INFLUXDB_USER=root # influxdb数据库用户名
- INFLUXDB_PASS=root # influxdb数据库密码
docker compose up -d
浏览器访问8080端口:
浏览器访问8083端口:
浏览器访问3000端口:
使用admin/admin登录