Docker实战指南
一、Docker基础
1、为什么是Docker?
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。
1、Docker解决了什么问题
- 快速交付和部署(镜像与容器)
- 资源的高效利用和隔离(高密度部署)
- 轻松的迁移和扩展(一次封装,到处运行)
2、Docker使用步骤
- 安装Docker(安装应用商店)
- 寻找/下载镜像(寻找/下载app)
- 启动容器(启动应用)
- 移植(别的手机安装应用商店,继续以前流程)
容器化对比虚拟机
对比属性 | Container | VM |
---|---|---|
隔离性 | 基于进程隔离 | 提供资源的完全隔离 |
启动时间 | 秒级 | 分钟级 |
内核 | 共用宿主机内核 | 使用独立内核 |
占用资源 | MB级 | GB级 |
系统支持容量 | 单机支持上千个容器 | 一般几十个 |
2、核心概念
1、Docker 镜像
Docker镜像类似于虚拟机镜像,可以将他理解为一个只读的模板。
2、Docker 容器
Docker容器类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。容器是从镜像创建来的。容器可以启动、停止、删除,容器之间彼此隔离,互不可见。
可以把容器看做是一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子
3、Docker 仓库
Docker仓库是存储镜像的仓库。可以有Public(公有仓库)和Private(私有仓库)。最大的公有仓库是Docker Hub
Docker | 面向对象 |
---|---|
镜像 | 类 |
容器 | 对象 |
3、安装启动
1、阿里云安装
yum install docker
systemctl enable docker
systemctl start docker
docker -v
docker info
2、标准方式安装
#移除旧版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
#安装Docker其他依赖
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
#设置Docker yum源
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
#安装Docker CE
yum install -y docker-ce docker-ce-cli containerd.io
#安装指定版本的Docker
yum list docker-ce --showduplicates | sort -r
yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
#启动Docker
systemctl enable docker
systemctl start docker
二、Docker快速入门
我们以使用Docker的方式安装MySQL为例,快速体会Docker的基本操作
1、镜像操作
1、下载镜像 docker pull mysql
#我们应该先去Docker Hub寻找我们感兴趣的镜像
docker pull image_name[:Tag]
#1、tag一般为镜像的版本,不指定默认下载latest版本
#2、默认镜像是从Docker Hub下载,国内比较慢,推荐使用自己的阿里云加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://82m9ar63.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
- 以上下载过程看出,镜像是分层的(Layer)。c499e6d256d6是当前层的唯一id(完整的id为256bit,64个十六进制字符组成)。
- 不同的镜像如果有相同的层,本地只会存储一份,减小了存储空间
- 严格说,下载镜像需要制定指定仓库名称,不过从Docker Hub下载的可以忽略前缀。如
- docker pull **docker.io/library/**mysql:5.7
2、列出镜像 docker images
docker images
REPOSITORY:来源于哪个仓库
TAG:镜像版本标签信息
IMAGE ID :镜像唯一id
CREATED:镜像最后更新时间
SIZE:镜像大小
3、查看详情 docker inspect
docker inspect image_name[:tag]
docker inspect image_id
4、删除镜像 docker rmi
docker rmi image_name[:tag] 或者 docker rmi image_id
5、理解镜像
1、疑问一:
Docker每一个容器都是一个完整的这个应用的运行环境。所有应用最起码的基本环境是linux
SB微服务做成镜像。基本环境就是java环境,
如果我下载了100个镜像,启动了100个容器。100个linux在运行?
UFS:特点;git。增量记录文件改变。分层系统。需要经常读写变化的,只在变化层。
2、容器操作
1、启动容器
#docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 常用选项
-d 守护态运行
-p Publish a container's port(s) to the host ( container和host端口映射)
-i 以交互模式运行容器,通常与 -t 同时使用
-t 为容器重新分配一个伪输入终端,通常与 -i 同时使用
--name="nginx-lb" 为容器指定一个名称
-v, --volume value Bind mount a volume (default [])
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7
#查看所有运行中的容器
docker ps
#查看所有容器
docker ps -a
#docker create 和 docker run的区别
docker create -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7
#create新建容器,run新建并启动。create相当于 run -d设置
2、进入容器
#docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
#Run a command in a running container
docker exec -it hello-mysql /bin/bash
#尝试连接MySQL
mysql -uroot -p123456
#扩展
docker exec -d hello-mysql touch /hello.txt
docker exec -it hello-mysql mysql -uroot -p
#删除
docker rm container_id/container_name
#查看运行日志
docker logs container_id/container_name
3、其他命令
5、可视化
- Portainer(先用这个)
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
- Rancher(CI/CD再用这个)
#安装rancher-server
docker run --name rancher-server -p 8000:8080 -v /etc/localtime:/etc/localtime:ro -d rancher/server
#安装agent
docker run --rm --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v1.2.11 http://39.101.191.131:8000/v1/scripts/D3DBD43F263109BB881F:1577750400000:7M0yBzCw4XSxJklD7TpysYIpI
三、Docker数据管理
使用Docker过程中,往往需要对数据进行持久化,或者多个容器之间进行数据共享,这就需要我们熟悉容器的数据管理操作
容器中管理数据主要的两种方式:
- 数据卷(Data Volumes):容器内数据直接映射到本地主机环境
- 数据卷容器(Data Volumes Containers):使用特定容器维护数据
1、数据卷
将主机操作系统目录直接映射进容器,类似于Linux中的mount。数据卷很好的解耦了容器应用和数据。对数据卷内数据的操作(无论是在本地主机还是容器内),都会立即生效。
docker run -d -p 3307:3306 -v /my/custom:/etc/mysql/conf.d \
-v /my/own/datadir:/var/lib/mysql --restart always --name mysql01 mysql:5.7
-v hostDir:containerDir
:hostDir不存在会自动创建,默认权限是rw,也可以改为ro,这样容器内部就无法对数据卷的数据进行修改了。
docker run -d -p 3307:3306 -v /my/custom:/etc/mysql/conf.d:ro \
-v /my/own/datadir:/var/lib/mysql --restart always --name mysql01 mysql:5.7
#数据卷创建
docker volume create [OPTIONS] [VOLUME]
docker volume create hello
docker run -d -p 3307:3306 -v hello:/etc/mysql/conf.d \
-v /my/own/datadir:/var/lib/mysql --restart always --name mysql01 mysql:5.7
#数据卷列表
docker volume ls
#数据卷详情
docker volume inspect hello
[
{
"CreatedAt": "2020-04-08T06:43:08Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/hello/_data",
"Name": "hello",
"Options": {},
"Scope": "local"
}
]
#docker cp 用于容器与主机之间的数据拷贝
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
-
SRC_PATH
指定为一个文件DEST_PATH
不存在:文件名为DEST_PATH
,内容为SRC的内容DEST_PATH
不存在并且以/
结尾:报错DEST_PATH
存在并且是文件:目标文件内容被替换为SRC_PATH的文件内容。DEST_PATH
存在并且是目录:文件复制到目录内,文件名为SRC_PATH指定的名字
-
SRC_PATH
指定为一个目录-
DEST_PATH
不存在:DEST_PATH
创建文件夹,复制源文件夹内的所有内容 -
DEST_PATH
存在是文件:报错 -
DEST_PATH
存在是目录SRC_PATH
不以/.
结束:源文件夹复制到目标里面SRC_PATH
以/.
结束:源文件夹里面的内容复制到目标里面
自动创建文件夹不会做递归。把父文件夹做好
-
2、数据卷容器
如果需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器也是一个容器,但他专门用来提供数据卷供其他容器挂载。
docker run -it -v /idata --name idata ubuntu
docker run -it --volumes-from idata --name d01 ubuntu
docker run -it --volumes-from idata --name d02 ubuntu
#使用--volumes-from参数所挂载的数据卷容器本身不需要保持在运行状态
3、数据卷的用法细节
docker run -d -P --name nginx nginx
#方便修改nginx的配置和html页面等
#容器中 /usr/share/nginx/html
#容器中 /etc/nginx
##参照一下各种写法
# hostPath:containerPath
docker run -d -P --name nginx -v /opt/nginx/html:/usr/share/nginx/html nginx;
#为什么看到的是403而不是index页面内容。原来容器里面变为空了?主机的内容复制到容器中,导致,容器同步到了主机的空文件夹。
#特别是配置问津,必须提前保证我们主机挂载的目录里面提前有内容。
docker run -d -P --name nginx -v /opt/nginx:/etc/nginx nginx
#为什么docker ps看不到;出错了。open() "/etc/nginx/nginx.conf" failed (2: No such file or directory) nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
#我就想自定义挂载路径,并且把他们挂载出来?
# 1、复制容器的文件
docker cp
1、启动一个nginx,先不挂载。
2、docker cp把运行中的nginx的内容复制出来
Usage: docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
docker cp nginx:/usr/share/nginx/html /opt/hahanginx
nginx:/usr/share/nginx/html/. 把html里面的内容复制到 /opt/hahanginx
nginx:/usr/share/nginx/html 把html文件夹复制到 /opt/hahanginx
完整过程
mkdir /opt/nginx
docker cp nginx:/usr/share/nginx/html /opt/nginx 复制html
docker cp nginx:/etc/nginx/. /opt/nginx/conf
docker run -d -P --name nginx -v /opt/nginx/conf:/etc/nginx -v /opt/nginx/html:/usr/share/nginx/html nginx
# 匿名卷方式volume,挂载任意目录
docker run -d -P --name nginx02 -v 容器路径
eg:
docker run -d -P --name nginx02 -v /usr/share/nginx/html -v /etc/nginx nginx
想要知道到底迎合到主机的哪里了?
docker inspect nginx02.
"Mounts": [
{
"Type": "volume",
"Name": "9f4f8ac4c9cd9cb77df7a5386a5b68520c98ea1bb28e26b6593255c1d0ff9cdf",
"Source": "/var/lib/docker/volumes/9f4f8ac4c9cd9cb77df7a5386a5b68520c98ea1bb28e26b6593255c1d0ff9cdf/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "68afef2bf4979e2c9369f6eb68f8a94adc61e63d3a1fb3dad7469e243442cf32",
"Source": "/var/lib/docker/volumes/68afef2bf4979e2c9369f6eb68f8a94adc61e63d3a1fb3dad7469e243442cf32/_data",
"Destination": "/etc/nginx",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
#匿名挂载的缺点?不好维护。 docker volume可以维护他们
docker volume ls
#具名挂载。有卷。如果有可视化界面。我们就知道卷是干嘛,就不乱删除了。
#创建好的卷,会自动的和容器中需要挂载的这个文件夹做好同步。
docker volume create 卷名字, 创建一个卷。/var/lib/docker/volumes/自定义的卷名/_data
docker run -d -P --name nginx03 -v nginxconf:/etc/nginx -v nginxhtml:/usr/share/nginx/html nginx
最佳实战。
怎么判断挂载的是卷名而不是本机目录名
不以/开始就是卷名,以/开始就是主机名。
#改变文件的读写权限
#ro:readonly rw:readwrite; 指定容器对我们这个挂载出来的内容的读写权限
docker run -d -P --name nginx04 -v nginx04conf:/etc/nginx:ro -v nginx04html:/usr/share/nginx/html:rw nginx
ro:效果
root@c721b01b2752:/etc/nginx# echo 123 > nginx.conf
bash: nginx.conf: Read-only file system
容器内不能再对ro指定的所有进行修改了。
四、Docker容器互联
1、端口映射
docker create -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7
-p HostPort:ContainerPort | IP:HostPort:ContainerPort | HostPort:ContainerPort
- 查看端口映射
- docker port hello-mysql
2、容器互联
--link name:alias
,name连接容器的名称,alias连接的别名
场景:我们无需暴露mysql的情况下,让web应用使用mysql;
docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
docker run -d --link mysql01:mysql --name tomcat tomcat:7
docker exec -it tomcat bash
cat /etc/hosts
第一次作业
1、部署Tomcat
docker pull tomcat:jdk8-openjdk-slim
#官方 docker exec -it
docker run -it --rm -p 8080:8080 tomcat:9.0
-it :交互模式
--rm:Automatically remove the container when it exits; 容器启动成功并且推出以后容器就自动移除;控制台ctrl-c自动删除。测试情况。dry-run.。容器就没有了
docker stop fe546673ee19 11e476bb37b6 #批量
docker ps -aq #找到所有容器的id
##默认删除的方式
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)
##一步到位的
docker rm -f $(docker ps -aq)
docker run -d -p 8080:8080 --name tomcat -v /idata/tomcat/webapp:/usr/local/tomcat/webapps tomcat:9.0
以后在/idata/tomcat/webapp里面部署项目,war
镜像:
后缀:
-slim:瘦身的。
alpine:更瘦的。基础系统2.5
历史?
docker的所有镜像。最基础的镜像是什么?最起码是一个系统(小linux)
centos:debain;alpine。xxx
下载镜像(tomcat.,redis),优先推荐大家用 alpine后缀的。
2、Es+Kibana
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" \
-v /usr/share/elasticsearch/data \
-v /usr/share/elasticsearch/config \
-v /usr/share/elasticsearch/plugins \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
elasticsearch:7.6.2
# es能设置的环境变量,这里都能写。容器的名字必须唯一
docker stats 7dc #查看容器的cpu,内存,网络情况
# 必须考虑的
# 1、端口暴露问题
# 2、数据卷的挂载
# --net
解决的问题:
1、吃内存
2、数据卷的挂载?
es自己的所有数据都在 容器的/usr/share/elasticsearch
容器里的结构 bin config data jdk lib logs modules plugins
-v 容器里面的需要挂载的路径
#启动Kibana。kibana和es都安装在了一个一个机器
docker run -d --name kibana -p 5601:5601 -e ELASTICSEARCH_HOSTS="http://172.16.224.124:9200" kibana:7.6.2
#kibana怎么知道es在哪里。
#es地址,千万别127.0.0.1
3、RabbitMQ
# rabbitmq:3不带管理控制台的。rabbitmq:management
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 -v /idata/rabbitmq:/var/lib/rabbitmq rabbitmq:management
五、实战练习
1、安装MySQL
docker run -p 3306:3306 --name mysql \
-v /idata/mysql/log:/var/log/mysql \
-v /idata/mysql/data:/var/lib/mysql \
-v /idata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
docker exec -it mysql mysql -uroot -proot
#配置MySQL主从复制
#创建master
docker run -p 3306:3306 --name mysql-master \
-v /idata/mysql/log:/var/log/mysql \
-v /idata/mysql/data:/var/lib/mysql \
-v /idata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
##配置文件
vim /idata/mysql/conf/my.cnf
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve
server_id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=icoding_test
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
#创建Slaver,和master一致,配置文件修改如下即可
server_id=2
log-bin=mysql-bin
read-only=1
binlog-do-db=icoding_test
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
#配置matser
1、添加用来同步的用户
GRANT REPLICATION SLAVE ON *.* to 'backup'@'%' identified by '123456';
2、查看master状态
show master status\G;
#配置slaver
1、change master to master_host='mysql-master',master_user='backup',master_password='123456',master_log_file='mysql-bin.000003',master_log_pos=0,master_port=3306;
2、start slave;
3、show slave status\G;
2、安装Redis
mkdir -p /idata/redis/conf
touch /idata/redis/conf/redis.conf
docker run -p 6379:6379 --name redis -v /idata/redis/data:/data \
-v /idata/redis/conf:/etc/redis \
-d redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
redis 自描述文件:
https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf
#安装redis-cluster;3主3从方式,从为了同步备份,主进行slot数据分片
##单机情况下可以这样做。
## cluster-announce-ip 192.168.56.10 为自己服务器的ip
for port in $(seq 7001 7006); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port ${port}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.26.65.131
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
appendonly yes
EOF
docker run -p ${port}:${port} -p 1${port}:1${port} --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
done
docker stop $(docker ps -a |grep redis-700 | awk '{ print $1}')
docker rm $(docker ps -a |grep redis-700 | awk '{ print $1}')
##建立集群
docker exec -it redis-7001 /bin/sh
redis-cli --cluster create 172.26.65.131:7001 172.26.65.131:7002 172.26.65.131:7003 172.26.65.131:7004 172.26.65.131:7005 172.26.65.131:7006 --cluster-replicas 1
redis-cli -c -h 192.168.56.10 -p 7006 # -c方式连接
cluster info ,cluster node;测试
#模拟网络分离的
docker network create redis --subnet 172.38.0.0/16
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 192.168.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker exec -it redis-1 /bin/sh
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
3、安装ElasticSearch
mkdir -p /idata/es/config
mkdir -p /idata/es/data
echo "http.host: 0.0.0.0" >> /idata/es/config/elasticsearch.yml
chmod -R 777 /idata/es/ #保证权限
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /idata/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /idata/es/data:/usr/share/elasticsearch/data \
-v /idata/es/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.0.10:9200 -p 5601:5601 \
-d kibana:7.4.2
#安装Es集群
所有之前先运行:sysctl -w vm.max_map_count=262144
我们只是测试,所以临时修改。永久修改使用下面
#防止 JVM 报错
echo vm.max_map_count=262144 >> /etc/sysctl.conf
sysctl -p
##############3-master##########################
for port in $(seq 1 3); \
do \
mkdir -p /mydata/elasticsearch/master-${port}/config
mkdir -p /mydata/elasticsearch/master-${port}/data
chmod -R 777 /mydata/elasticsearch/master-${port}
cat << EOF >/mydata/elasticsearch/master-${port}/config/elasticsearch.yml
cluster.name: my-es #集群的名称,同一个集群该值必须设置成相同的
node.name: es-master-${port} #该节点的名字
node.master: true #该节点有机会成为 master 节点
node.data: false #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0 #所有 http 均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时 ping 连接的超时时间
discovery.seed_hosts: ["172.18.12.21:9301", "172.18.12.22:9302", "172.18.12.23:9303"] #设置集群中的 Master 节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置;
cluster.initial_master_nodes: ["172.18.12.21"] #新集群初始时的候选主节点,es7 的新增配置
EOF
docker run --name elasticsearch-node-${port} \ -p 920${port}:920${port} -p 930${port}:930${port} \ --network=mynet --ip 172.18.12.2${port} \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v
/mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/el
asticsearch.yml \ -v /mydata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2
done
#######################3-node#######################
for port in $(seq 4 6); \
do \
mkdir -p /mydata/elasticsearch/node-${port}/config
mkdir -p /mydata/elasticsearch/node-${port}/data
chmod -R 777 /mydata/elasticsearch/node-${port}
cat << EOF >/mydata/elasticsearch/node-${port}/config/elasticsearch.yml
cluster.name: my-es #集群的名称,同一个集群该值必须设置成相同的
node.name: es-node-${port} #该节点的名字
node.master: false #该节点有机会成为 master 节点
node.data: true #该节点可以存储数据
network.host: 0.0.0.0
#network.publish_host: 192.168.56.10 #互相通信 ip,要设置为本机可被外界访问的 ip,否则
无法通信
http.host: 0.0.0.0 #所有 http 均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
#discovery.zen.minimum_master_nodes: 2 #设置这个参数来保证集群中的节点可以知道其
它 N 个有 master 资格的节点。官方推荐(N/2)+1
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时 ping 连接的超时时间
discovery.seed_hosts: ["172.18.12.21:9301", "172.18.12.22:9302", "172.18.12.23:9303"] #设置集群中的 Master 节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7
的新增配置
cluster.initial_master_nodes: ["172.18.12.21"] #新集群初始时的候选主节点,es7 的新增配置
EOF
docker run --name elasticsearch-node-${port} \ -p 920${port}:920${port} -p 930${port}:930${port} \ --network=mynet --ip 172.18.12.2${port} \ -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v
/mydata/elasticsearch/node-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/ela
sticsearch.yml \ -v /mydata/elasticsearch/node-${port}/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/node-${port}/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2
done
##检查
_nodes/process?pretty 查看节点状况
_cluster/stats?pretty 查看集群状态
_cluster/health?pretty 查看集群健康状况
_cat/nodes 查看各个节点信息
- discovery.seed_hosts=es01,es03 - cluster.initial_master_nodes=es01,es02,es03 #还可以只开放一个实例端口.其他的都用集群内访问就可以啦。
#也可以使用自定义Docker网络的方式
docker network create --driver bridge --subnet 172.21.0.0/16 --gateway 172.21.0.1 mynet
#运行容器的时候,使用 --ip --network指定网络
4、安装Nginx
-
随便启动一个 nginx 实例, 只是为了复制出配置
docker run -p 80:80 --name nginx -d nginx:1.10
-
将容器内的配置文件拷贝到当前目录:
- mkdir -p /idata/nginx/conf
- docker container cp nginx:/etc/nginx/. /idata/nginx/conf
-
创建新的 nginx; 执行以下命令
docker run -p 80:80 --name nginx
-v /idata/nginx/html:/usr/share/nginx/html
-v /idata/nginx/logs:/var/log/nginx
-v /idata/nginx/conf:/etc/nginx
-d nginx:1.10 -
给 nginx 的 html 下面放的所有资源可以直接访问;
5、安装RabbitMQ
#简单安装
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p
25672:25672 -p 15671:15671 -p 15672:15672 -v /idata/rabbitmq:/var/lib/rabbitmq rabbitmq:management
4369, 25672 (Erlang发现&集群端口)
5672, 5671 (AMQP端口)
15672 (web管理后台端口)
61613, 61614 (STOMP协议端口)
1883, 8883 (MQTT协议端口)
https://www.rabbitmq.com/networking.html
#安装RabbitMQ镜像集群
#三台机器分别运行
mkdir -p /idata/rabbitmq
vi /idata/rabbitmq/hosts
192.168.0.48 rabbit1 rabbit1
192.168.0.47 rabbit2 rabbit2
192.168.0.46 rabbit3 rabbit3
#注意修改每个-h 参数,修改为正确的hostname
docker run -d --name rabbitmq -h rabbit1 -v /idata/rabbitmq:/var/lib/rabbitmq -v /idata/rabbitmq/hosts:/etc/hosts -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 -e RABBITMQ_ERLANG_COOKIE='icodingedu' rabbitmq:management
#上面这种方式匹配主机名适用于完全分布式情况,单机模拟集群,大家觉得还有更好的方式吗?(请使用自定义网络的方式)
#RABBITMQ_ERLANG_COOKIE 节点认证作用,部署集成时 需要同步该值
#节点加入集群
#初始化第一个节点
docker exec -it rabbitmq /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
Exit
#设置集群内的其他节点
docker exec -it rabbitmq /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbit1 #master的ip
rabbitmqctl start_app
exit
#实现镜像集群
#进入主节点进行操作
docker exec -it rabbitmq bash
rabbitmqctl set_policy -p / ha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
可以使用 rabbitmqctl list_policies -p /;查看vhost/下面的所有policy
在cluster中任意节点启用策略,策略会自动同步到集群节点
rabbitmqctl set_policy -p / ha-all "^" '{"ha-mode":"all"}'
策略模式 all 即复制到所有节点,包含新增节点,策略正则表达式为 “^” 表示所有匹配所有队列名称。“^hello”表示只匹配名为hello开始的队列
六、Docker进阶
1、Docker仓库(私有仓库)
1、阿里云
开通阿里云容器镜像服务
#阿里云下载镜像,需要填写前缀
docker pull registry.cn-shenzhen.aliyuncs.com/ali/mysql:[镜像版本号]
ali:命名空间
mysql:镜像仓库
- 创建命名空间
- 创建镜像仓库
2、Harbor
2、网络高级
#安装Es+kibana;kibana得指定es地址,
#sb-book-crud-service mysql
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
Linux虚拟网络技术。
Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。
原理:
1、每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。桥接网卡
2、没启动一个容器linux主机多了一个虚拟网卡。
3、docker run -d -P --name tomcat --net bridge tomcat:8
#1、docker0网络的特点。,
默认
域名访问不通
--link 域名通了,但是删了又不行
#2、可以让容器创建的时候使用自定义网络
1、自定义创建的默认default "bridge"
2、自定义创建一个网络网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
所有东西实时维护好,直接域名ping同
3、理解网络。
docker network connect [OPTIONS] NETWORK CONTAINER
跨网络连接别人就用
docker network connect mynet tomcat
效果:
1、自定义网络,默认都可以用主机名访问通
2、跨网络连接别人就用 docker network connect mynet tomcat
1、容器启动,指定容器ip。 docker run --ip 192.168.0.3 --net 自定义网络
2、网络。docker network create --subnet 指定子网范围 --driver bridge 所有东西实时维护好,直接域名ping同
3、docker compose。
1、网络的创建过程
Docker创建一个容器的时候,会具体执行以下操作:
- 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中
- 本地主机一段的虚拟接口连接到默认的docker0网桥或者指定网桥上,并具有一个以veth开头的唯一名字
- 容器一段的虚拟接口将放到新创建的容器中,并修改名字为eth0。这个接口只在容器的命名空间可见
- 从网桥的可用地址段中获取一个空闲的地址分配给容器的eth0(如:172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(如:172.17.42.1/16)
完成以上,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。
2、网络模式
网络模式 | 配置 | 说明 |
---|---|---|
bridge模式 | –net=bridge | 默认值,在Docker网桥docker0上为容器创建新的网络栈 |
none模式 | –net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
container模式 | –net=container:name/id | 容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。 |
host模式 | –net=host | 容器和宿主机共享Network namespace |
用户自定义 | –net=自定义网络 | 用户自己使用network相关命令定义网络, 创建容器的时候可以指定为自己定义的网络 |
3、实验测试网络互连
- 大家重点理解veth pair技术
- –link是容器在默认网络模式下,可以互相使用容器名ping通的
- 如果容器创建时使用自定义网络,不使用–link也是可以ping通相互的容器名
- –link 生产一般不用,我们可以使用自定义网络的方式
- docker network connect
4、部署一个集群(redis)
3、Dockerfile
Dockerfile指南:https://docs.docker.com/engine/reference/builder/
事情:nginx。tomcat。mysql。镜像从哪里来?
我们自己如何做一个镜像。微服务。SpringBoot。上云部署。最方便是Docker。
微服务打包成镜像。任何装了Docker。都可以下载使用。
应用–>Dockerfile—>打包成镜像—>上传到仓库(公有仓库,私有仓库)—>下载镜像—>启动运行。
移植:扩充服务器。
4、如何得到一个镜像
1、自己做的Dockefile
https://docs.docker.com/engine/reference/builder/
#nginx Dockerfile的示例
#alpine:3.11超迷你的linux 4mb;
#alpine:3.11 linux全目录 基于宿主机linux的一个统一接口。cpu,mem。主机。
FROM alpine:3.11
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
#环境
ENV NGINX_VERSION 1.18.0
ENV NJS_VERSION 0.4.0
ENV PKG_RELEASE 1
RUN set -x \
# create nginx user/group first, to be consistent throughout docker variants
&& addgroup -g 101 -S nginx \
&& adduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx \
&& apkArch="$(cat /etc/apk/arch)" \
&& nginxPackages=" \
nginx=${NGINX_VERSION}-r${PKG_RELEASE} \
nginx-module-xslt=${NGINX_VERSION}-r${PKG_RELEASE} \
nginx-module-geoip=${NGINX_VERSION}-r${PKG_RELEASE} \
nginx-module-image-filter=${NGINX_VERSION}-r${PKG_RELEASE} \
nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${PKG_RELEASE} \
" \
&& case "$apkArch" in \
x86_64) \
# arches officially built by upstream
set -x \
&& KEY_SHA512="e7fa8303923d9b95db37a77ad46c68fd4755ff935d0a534d26eba83de193c76166c68bfe7f65471bf8881004ef4aa6df3e34689c305662750c0172fca5d8552a *stdin" \
&& apk add --no-cache --virtual .cert-deps \
openssl \
&& wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \
&& if [ "$(openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -text -noout | openssl sha512 -r)" = "$KEY_SHA512" ]; then \
echo "key verification succeeded!"; \
mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \
else \
echo "key verification failed!"; \
exit 1; \
fi \
&& apk del .cert-deps \
&& apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published packaging sources
set -x \
&& tempDir="$(mktemp -d)" \
&& chown nobody:nobody $tempDir \
&& apk add --no-cache --virtual .build-deps \
gcc \
libc-dev \
make \
openssl-dev \
pcre-dev \
zlib-dev \
linux-headers \
libxslt-dev \
gd-dev \
geoip-dev \
perl-dev \
libedit-dev \
mercurial \
bash \
alpine-sdk \
findutils \
&& su nobody -s /bin/sh -c " \
export HOME=${tempDir} \
&& cd ${tempDir} \
&& hg clone https://hg.nginx.org/pkg-oss \
&& cd pkg-oss \
&& hg up -r 474 \
&& cd alpine \
&& make all \
&& apk index -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \
&& abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \
" \
&& cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \
&& apk del .build-deps \
&& apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \
;; \
esac \
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
&& if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \
&& if [ -n "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \
&& if [ -n "/etc/apk/keys/nginx_signing.rsa.pub" ]; then rm -f /etc/apk/keys/nginx_signing.rsa.pub; fi \
# Bring in gettext so we can get `envsubst`, then throw
# the rest away. To do this, we need to install `gettext`
# then move `envsubst` out of the way so `gettext` can
# be deleted completely, then move `envsubst` back.
&& apk add --no-cache --virtual .gettext gettext \
&& mv /usr/bin/envsubst /tmp/ \
\
&& runDeps="$( \
scanelf --needed --nobanner /tmp/envsubst \
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
| sort -u \
| xargs -r apk info --installed \
| sort -u \
)" \
&& apk add --no-cache $runDeps \
&& apk del .gettext \
&& mv /tmp/envsubst /usr/local/bin/ \
# Bring in tzdata so users could set the timezones through the environment
# variables
&& apk add --no-cache tzdata \
# Bring in curl and ca-certificates to make registering on DNS SD easier
&& apk add --no-cache curl ca-certificates \
# forward request and error logs to docker log collector
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
# make default server listen on ipv6
&& sed -i -E 's,listen 80;,listen 80;\n listen [::]:80;,' \
/etc/nginx/conf.d/default.conf
EXPOSE 80
STOPSIGNAL SIGTERM
CMD ["nginx", "-g", "daemon off;"]
# Dockerfile复原了我们在linux上安装nginx的过程。
2、别人给我们准备好的文件。xxx.tar。可以通过硬盘传输使用。
1、创建项目dockerfile
2、上传项目到服务器。
3、进入项目,构建镜像到本地仓库;
docker build -t nginx:1.0 -f ./Dockerfile .
别忘了最后的小数点。- docker images 查看镜像
- docker exec -it 容器id /bin/bash;进入容器,修改容器
- docker commit -a “icoding” -m “nginxxx” 容器id mynginx:2.0
- docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
- OPTIONS说明:
- -a :提交的镜像作者;
- -c :使用Dockerfile指令来创建镜像;
- -m :提交时的说明文字;
- -p :在commit时,将容器暂停。
- docker login : 登陆到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub
- docker login -u 用户名 -p 密码
- docker logout : 登出一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub
4、推送镜像到docker hub
- 标记镜像,docker tag local-image:tagname username/new-repo:tagname
- 上传镜像,docker push username/new-repo:tagname
5、保存镜像,加载镜像
- 可以保存镜像为tar,使用u盘等设备复制到任意docker主机,再次加载镜像
- 保存:docker save spring-boot-docker -o /home/spring-boot-docker.tar
- 加载:docker load -i spring-boot-docker.tar
6、阿里云操作
- 登录阿里云,密码就是开通镜像仓库时 的密码
- docker login --username=icoding registry.cn-hangzhou.aliyuncs.com
- 拉取镜像
- docker pull registry.cn-hangzhou.aliyuncs.com/icoding/i-nginx:v1.0
- 推送镜像
- docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/icoding/icoding-nginx:v2
- docker push registry.cn-hangzhou.aliyuncs.com/icoding/icoding-nginx:v2
2、Dockerfile详解
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。
一般而言,Dockerfile可以分为四部分
基础镜像信息
维护者信息
镜像操作指令
启动时执行指令
指令 | 说明 |
---|---|
FROM | 指定基础镜像 |
MAINTAINER | 指定维护者信息,已经过时,可以使用LABEL maintainer=xxx 来替代 |
RUN | 运行命令 v |
CMD | 指定启动容器时默认的命令 v |
LABEL | 指定生成镜像的元数据标签信息 v |
EXPOSE | 声明镜像内服务监听的端口 v |
ENV | 指定环境变量,可以在docker run的时候使用-e改变 v |
ADD | 复制指定的src路径下的内容到容器中的dest路径下,src可以为url会自动下载,可以为tar文件,会自动解压 |
COPY | 复制本地主机的src路径下的内容到镜像中的dest路径下,但不会自动解压等 |
ENTRYPOINT | 指定镜像的默认入口.运行命令 v |
VOLUME | 创建数据卷挂载点 |
USER | 指定运行容器时的用户名或UID |
WORKDIR | 配置工作目录,为后续的RUN、CMD、ENTRYPOINT指令配置工作目录 |
ARG | 指定镜像内使用的参数(如版本号信息等),可以在build的时候,使用–build-args改变 v |
OBBUILD | 配置当创建的镜像作为其他镜像的基础镜像是,所指定的创建操作指令 |
STOPSIGNAL | 容器退出的信号值 |
HEALTHCHECK | 健康检查 |
SHELL | 指定使用shell时的默认shell类型 |
1、RUN、CMD、ENTRYPOINT区别
- RUN 执行命令并创建新的镜像层RUN 经常用于安装软件包(在构建镜像时运行的)。
- CMD 设置容器启动后默认执行的命令及其参数但 CMD 能够被
docker run
后面跟的命令行参数替换。- ENTRYPOINT 配置容器启动时运行的命令。
- 以上命令都可以使用shell或者exec方式执行
CMD ["executable","param1","param2"]
(exec form, this is the preferred form)CMD ["param1","param2"]
(as default parameters to ENTRYPOINT)CMD command param1 param2
(shell form)
2、shell和exec方式
shell 是 /bin/sh -c 的方式,
exec [“/bin/sh”,“-c”,command] 的方式== shell方式
eg:shell方式
ENV name icoding
ENTRYPOINT echo "Hello, $name"
#输出 Hello icoding
ENTRYPOINT ["/bin/echo", "Hello, $name"]
#输出 Hello $name
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
#输出 Hello icoding
##建议:CMD 和 ENTRYPOINT 推荐使用 Exec 格式因为指令可读性更强更容易理解。RUN 则两种格式都可以。exec的时候要获取环境变量等值,就用/bin/sh -c 即可。
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
3、实战Dockerfile
#1、ping命令小工具。
编写Dockerfile-->打包镜像--->根据这个镜像启动容器使用容器的功能
docker build -t hello .
-t:tag标签。镜像名字
.:在当前目录下工作
默认.这个目录下就得有一个 Dockerfile
FROM alpine
CMD ping baidu.com
#2、文件名不是Dockerfile。用-f 指定
docker build -f Dockerfile2 -t hello:v1.0 .
docker run -d --name hello hello;
#3、默认构建出来的镜像,放到了我们的本地镜像仓库。
1)、登陆到dockerhub
2)、dockehub国外,就算能Push,非常慢。阿里云加速的是下载。
#4、把这个镜像发布到Docker hub
1)、docker login -u icodingallen;登陆到docker hub
2)、docker push nginx:v1.0;推送过去。
太慢了
#5、搭建私有的镜像仓库。使用阿里云的镜像仓库(免费)。
1)、docker login --username=lyt_goodtogread@163.com registry.cn-hangzhou.aliyuncs.com
为了团队自治。只能看到自己的仓库的所有镜像。namespace;
创建镜像仓库。保存镜像各种版本,book: 1 2 3 4 5
docker tag hello:v1.0 registry.cn-hangzhou.aliyuncs.com/icodingdocker/hello:v1.3
docker push registry.cn-hangzhou.aliyuncs.com/icodingdocker/hello:v1.3
docker.io/libarary/hello:latest
registry.cn-hangzhou.aliyuncs.com/icodingdocker/hello:v1.0
镜像—》打包–》上传。
5、Dockerfile区分一些易混淆的指令
1、USER 执行cmd等之类命令的使用那个用户
alpine sudo gosu
FROM centos
RUN groupadd -r abc && useradd -r -g abc aaa
USER aaa
CMD whoami
# CMD 就是容器启动以后要执行的命令
2、ARG、ENV
- The
ARG
instruction defines a variable that users can pass at build-time to the builder with thedocker build
:arg在构建期间。docker build - The environment variables set using
ENV
will persist when a container is run from the resulting image. You can view the values usingdocker inspect
, and change them usingdocker run --env =
. env在运行是可以用到的
FROM alpine
ARG bbb haha
ENV abc 666
CMD echo $bbb
容器运行的时候arg的东西拿不到
CMD echo $abc
容器运行的时候env的能拿到
FROM alpine
ARG bbb haha
ENV abc=$bbb
CMD echo $abc
ARG指定的值,在镜像构建的后面位置,构建期间都可以使用到。
改变这些值行不行
docker build -f Dockerfile-arg-env -t file-arg --build-arg bbb=888 --build-arg abc=777 .
结果888
# 构建时 bbb=888 abc=777 ,cmd打印888.原因 构建时不能改变env
# 运行时
\ | ARG | ENV |
---|---|---|
build时 | √。–build-arg改变 构建参数(ARG声明) | 生效,能不能改(不能) |
运行时 | 不生效 | 生效。能改 -e abc |
最佳实战:
ARG:定义一些版本信息
FROM alpine
ARG version=1
RUN yum install nginx:$version
Env:运行时的环境变量。
ENV env = --spring.profile.active=prod
-e修改。sb java -jar xxx.jar $env
3、ADD和COPY
#构建了一个SpringBoot镜像。 xxx.jar
/opt
docker build -t hello .
ADD:将当前目录下的内容放到镜像里面一起打包。
COPY:将当前目录下的内容放到镜像里面一起打包。
ADD ["<src1>","<src2>","<dest>"] dest:容器里面的目录
可以指定很多种路径地址。自动下载,解压复制。
FROM alpine
ADD hello.tar /opt/hello
COPY hello.tar /opt/world/
CMD echo "1234"
这个东西构建的镜像为什么docker run -d 不行。因为容器运行的是ech 1234;
没有一个守护进程一直运行。
CMD ping baidu.com
4、VOLUME和WORKDIR
VOLUME:指定容器需要挂载的卷
WORKDIR:工作目录。
1、以后的其他命令在这个目录里面运行
2、exec进去都默认来到了 WORKDIR 指定的目录。sh
docker run -it --rm file-volume sh
WORKDIR
WORKDIR /root == RUN cd /root
挂载麻烦。自动挂载。
FROM alpine
WORKDIR /opt/a
VOLUME /opt/b
COPY hello.txt .
ADD hello.tar /opt/b
CMD whoami
volume声明的挂载目录,即使容器运行的时候,不用-v进行挂载。docker也会自动的进行匿名挂载。
nginx:
5、RUN、CMD、ENTRYPOINT
相同点:运行命令
不同点:
RUN:在构建镜像的时候运行的命令
CMD、ENTRYPOINT:在容器启动运行的命令
测试RUN;
#想构建一个具有git功能的镜像。
FROM centos
RUN yum install -y git
WORKDIR /opt/data
RUN git clone https://gitee.com/lanoox/luject.git
VOLUME /opt/data
CMD echo "git clone success"
#CMD 容器运行的时候CMD的命令才执行。
相同的镜像layer层发生变化,只有这层变化。
- -RUN指令的所有命令都在镜像docker build期间就执行
- CMD和ENTRYPOINT在容器启动时运行
- CMD 容器运行的时候CMD的命令才执行。docker run -it --rm file-run bash 能进容器中
- 替换为ENTRYPOINT。虽然指令运行了。但是 docker run -it --rm file-run bash 。最后的bash没有进去,失效了。
无论是CMD还是ENTRYPOINT还是RUN
RUN
(shell form, the command is run in a shell, which by default is/bin/sh -c
on Linux orcmd /S /C
on Windows) RUN yum install -y git;/bin/sh -c
可以动态获取一些变量RUN ["executable", "param1", "param2"]
(exec form);无法动态获取一些变量
FROM centos
ARG soft=git
RUN ["yum install","-y","$soft"] #这是错误的。因为非`/bin/sh -c`方式,用不到前面声明的ARG,ENV
WORKDIR /opt/data
RUN git clone https://gitee.com/lanoox/luject.git
VOLUME /opt/data
ENTRYPOINT echo "git clone success"
# bash-c 和 数组方式的区别, 修改后的;安装正确。
FROM centos
ARG soft=git
RUN ["/bin/sh","-c","yum install -y $soft"]
#-c command:后免是完整命令
WORKDIR /opt/data
RUN git clone https://gitee.com/lanoox/luject.git
VOLUME /opt/data
ENTRYPOINT echo "git clone success"
RUN、CMD、ENTRYPOINT都支持一下两种方式
The exec form, which is the preferred form:
ENTRYPOINT ["executable", "param1", "param2"]
The shell form:
ENTRYPOINT command param1 param2
总结:
-
如果运行命令是。[]方式,默认不是bash -c就无法用变化,普通的方式RUN yum -install -y $soft可以使用变量。
-
CMD、ENTRYPOINT的最佳实战
- Dockerfile文件必须至少有一个
CMD
或者ENTRYPOINT
命令. ENTRYPOINT
用来定义容器如何运行.CMD
应该被用来作为给ENTRYPOINT
传递。默认参数的一种方式CMD
将会被覆盖,容器运行时指定参数的时候.ENTRYPOINT
命令不会被覆盖。- CMD多个只会有一个生效。
ENTRYPOINT
:不被传入的指令覆盖,但是多个也只有一个生效。
为什么我的指令Dockerfile写CMD的时候,docker run -it --rm file-run bash可以进控制台,而
ENTRYPOINT
不行?运行效果:
- Dockerfile是CMD;没打印git clone success。但是bash生效。
- Dockerfile 是 ENTRYPOINT;打印了git clone success但是没有进容器(bash没生效)
- Dockerfile文件必须至少有一个
-
混写。CMD +
ENTRYPOINT
FROM centos
ARG soft=git
RUN ["/bin/bash","-c","yum install -y $soft"]
WORKDIR /opt/data
RUN git clone https://gitee.com/lanoox/luject.git
VOLUME /opt/data
CMD ["nginx"] #CMD给entrypoint提供参数
ENTRYPOINT ["yum","install","-y"]
运行:
docker run -it --rm file-run maven
FROM centos
ARG soft=git
RUN ["/bin/bash","-c","yum install -y $soft"]
WORKDIR /opt/data
RUN git clone https://gitee.com/lanoox/luject.git
VOLUME /opt/data
CMD ["","","",""] #可以放空
ENTRYPOINT ["/bin/sh","-c","yum install -y"]
#容器启动就是yum install -y "";自己启动命令加上git
CMD 后面有N参数。传了参数替换N个还是最后一个?
docker run -it --rm file-run maven CMD ["maven"] 对
docker run -it --rm file-run maven CMD [“”,“”,"maven"] 错
目的:构建镜像。SpringBoot编写的微服务,怎么做镜像?
1、运行的业务的目标环境?SB服务。java环境。FROM
2、怎么启动业务的。得到jar包,java -jar xxx.jar --spring.profile.active=prod --server.port=8080;决定镜像的ENTRYPOINT怎么写。docker exec?docker run(第一次容器启动要执行)?
docker exec haha -it bash【和entrypoint,cmd没啥关系】;
3、业务运行的时候需要啥?java。jar。jar包就想办法放进镜像中。COPY 。ADD
4、业务的那些数据是需要做持久化。VOLUME怎么写。
FROM java:8
#服务器只有dockerfile和jar在一起
COPY *.jar /app.jar
#即使运行没有-v,也会匿名挂载
VOLUME ["/logs"]
CMD ["--server.port=8080"]
EXPOSE ["8080"]
ENTRYPOINT ["java","-jar","/app.jar"]
4、idea快速整合使用
作为一个了解。
1、 Docker开启远程访问
#修改该Docker服务文件
vi /lib/systemd/system/docker.service
#修改ExecStart这行,将原来注释,加上这个命令
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
#重新加载配置文件
systemctl daemon-reload
#重启服务
systemctl restart docker.service
#查看端口是否开启
netstat -nlpt #如果找不到netstat命令,可进行安装。yum install net-tools
#直接curl看是否生效
curl http://127.0.0.1:2375/info
2、IDEA安装Docker插件
3、IDEA配置docker
4、也可以整合自己的私有镜像仓库
可以提供非常方便的部署功能
docker build -t hello-java . && docker run -p 88:8080 -v /logs:/logs --name hello-666 hello-java
Dockerfile解耦应用的开发与部署。
maven插件再把部署流程一配置就不解耦。,
应用。代码变了改应用。部署只改Dockerfile。
maven。代码部署放在了一个完整的生命周期。运维人员想要改网络部署等配置,还得改pom。CICD只需要一个Dockerfile。
5、docker-maven-plugin
市面上docker-maven-plugin太多,我们推荐一个 docker-maven-plugin,照着文档使用就行。不管使用哪一种docker插件来构造image,都比不上直接用Dockerfile编写简单,而且可复用,不用学习不同插件不同的构造规则。
最好给自己的Docker服务器配置阿里云镜像加速,否则经常导致镜像下载不来的情况
fabric8io的有两大功能:
- 构造并推送Docker镜像
- 启动和停止Docker容器
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.33.0</version>
<!--全局配置-->
<configuration>
<!--这一部分是为了实现对远程docker容器的控制-->
<!--docker主机地址,用于完成docker各项功能,注意是tcp不是http!-->
<dockerHost>tcp://公网IP:2376</dockerHost>
<!--docker远程访问所需证书地址,如果docker远程主机没有启用TLS验证则不需要配证书-->
<certPath>${project.basedir}/docker/ssh</certPath>
<!--这一部分是为了实现docker镜像的构建和推送-->
<!--registry地址,用于推送,拉取镜像,我这里用的是阿里的registry-->
<registry>registry.cn-shenzhen.aliyuncs.com</registry>
<!--认证配置,用于私有registry认证,如果忘记了可以去阿里的registry查看-->
<authConfig>
<push>
<username>这里填registry的用户名</username>
<password>这里填registry的密码</password>
</push>
</authConfig>
<!--镜像相关配置,支持多镜像-->
<images>
<!-- 单个镜像配置 -->
<image>
<!--镜像名(含版本号)-->
<name>命名空间/仓库名称:镜像版本号</name>
<!--别名:用于容器命名和在docker-compose.yml文件只能找到对应名字的配置-->
<alias>${project.name}</alias>
<!--镜像build相关配置-->
<build>
<!--使用dockerFile文件-->
<dockerFile>${project.basedir}/docker/${project.name}</dockerFile>
</build>
<!--配置docker-compose文件-->
<external>
<type>compose</type>
<basedir>${project.basedir}/docker</basedir>
<composeFile>docker-compose.yml</composeFile>
</external>
<!--容器run相关配置-->
<run>
<!--配置运行时容器命名策略为:别名,如果不指定则默认为none,即使用随机分配名称-->
<namingStrategy>alias</namingStrategy>
</run>
</image>
</images>
</configuration>
</plugin>
maven指令 | 功能 |
---|---|
docker:start | 创建和启动容器 |
docker:stop | 停止并销毁容器 |
docker:build | 构建镜像 |
docker:watch | 自动进行重建和重启 |
docker:push | 将镜像推送到registry |
docker:remove | 从本地docker主机删除镜像 |
docker:logs | 显示容器日志 |
docker:source | 将docker build archive附加到Maven项目 |
docker:save | 将镜像保存到文件 |
docker:volume-create | 创建卷以在容器之间共享数据 |
docker:volume-remove | 删除创建的卷 |
#打包到容器运行的完整流程
mvn clean package docker:stop docker:remove docker:build docker:run
#不是每一次运行都要推送镜像,如果要的话可以单独调用docker:push
mvn docker:push
#示例配置
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.33.0</version>
<configuration>
<dockerHost>tcp://111.229.61.54:2375</dockerHost>
<registry>registry.cn-shenzhen.aliyuncs.com</registry>
<authConfig>
<push>
<username>icodingallen</username>
<password>icoding123</password>
</push>
</authConfig>
<images>
<image>
<name>icoding/icoding-docker:v1.2</name>
<build>
<!--使用dockerFile文件 <dockerFile>${project.basedir}/docker/${project.name}</dockerFile>-->
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
</build>
<run>
<ports>
<port>8080:8080</port>
</ports>
</run>
</image>
</images>
</configuration>
</plugin>
FROM java:8
LABEL maintainer=icoding
ADD target/*.jar /app.jar
EXPOSE 8080
CMD ["java","-jar","app.jar"]
@RestController
public class HelloController {
@Value("${project.version}")
String ver;
@Value("${project.name}")
String name;
@GetMapping("/")
public String hello(){
return "Hello Docker!"+ver+"=>"+name;
}
}
#获取maven pom对象的相关属性值。在springboot中获取如下
project.version=@project.version@
project.name=@project.name@
#在pom文件中直接使用${project.xxxx},按照对象属性关系获取即可
七、Docker Compose
https://docs.docker.com/compose/
Dockerfile。商城。拆分很多微服务。10微服务
购物车微服务+Redis+MySQL = 一套服务(自动化的全套部署,全套下线)。交给Docker Compose【编排】。一个整套的application会有非常多的service
一次性的将所有相关联的服务自动部署。
前面我们使用 Docker 的时候,定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知。批量的维护整套应用
使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具
Compose的三步最佳实战:
- 定义自己应用的
Dockerfile
文件。为了在anywhere进行构建。 - 使用
docker-compose.yml
定义我们服务。这一整套服务可以一起在一个隔离环境运行. - 运行
docker-compose up
就可以启动整个app.
总结:批量服务编排。docker-compose.yml
声明服务的规则。
单机环境。
1、安装
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose version
2、体验
1、创建一个测试应用
#1、Create a directory for the project:
mkdir composetest
cd composetest
#2、Create a file called app.py in your project directory and paste this in:
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
#3、Create another file called requirements.txt in your project directory and paste this in:
flask
redis
2、创建Dockerfile
#In your project directory, create a file named Dockerfile and paste the following:
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]
3、在Compose file里定义服务
#Create a file called docker-compose.yml in your project directory and paste the following:
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
4、Compose构建和运行应用
docker-compose up -d
#访问 http://localhost:5000/ 测试效果
5、效果
自己打包—》dockerfile–》结合docker-compose
1、创建网络
默认的网络:1998ee0450ee composetest_default bridge local
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
#默认两个服务都用一个网络,是contextName_default;web里面可以使用redis的hostname访问
#cache = redis.Redis(host='redis', port=6379)
#默认每一个启动的容器 contextName_serviceName_1
docker network inspect composetest_default
#效果:这个网络里面有。两个容器
#docker service ls #查看当前的服务。docker集群下,我们能看到所有的服务 容器的名_num。代表第几个副本。
集群状态下;compose更帅。我们可以直接定义服务在多个机器都有副本。
2、构建每个镜像
3、创建两个容器在同一网络,我们可以使用hostname访问
docker-compose帮我们自定的部署了pyweb,redis,并且pyweb能访问redis。pyweb的5000浏览器也能访问
整个docker:
1、dockerfile
2、docker-compose
3、docker二次开发,如何更换底层存储驱动,如何自定义docker的网络。。。
详情:https://docs.docker.com/compose/compose-file/#reference-and-guidelines
#语法
三层
version: ""
services: #定义很多服务
服务1:
#当前服务配置
服务2:
#当前服务配置
#服务要用的网络、卷,等其他全局规则,定义
volumes:
networks:
configs:
secrets:
networks:
outside:
external: true #外面创建好了,docker-compose不自动创建
如果自定义用了网络。卷等,都必须声明
version: "3"
services:
java-app:
build: .
ports:
- "9000:8080"
depends_on:
- mysql
command: java -jar /app.jar --spring.datasource.url=jdbc:mysql://mysql:3306/wps?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
networks:
- app-network
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 123456
networks:
- app-network
networks:
app-network: {}
docker-compose up -d
docker-compose up -d --build #再触发一下手动构建
随便网上一个项目。dockerfile+docker-compose 一键搞定
mysql dockerfile-->run 网上的项目拉下来,把的sql文件导入数据。
docker-compose替代了dockerfile。完全可以(镜像存在)自定义的镜像 dockerfile+docker-compose。
整个docker-compose up/down/rm 单机情况下的编排
app-redis。用同一个网络。
集群情况。官方默认docker-swarm
docker 5个。 2主(HA) 3从(工作节点)
5、概念
工程、服务、容器
- Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container)
- Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例
6、实战-使用Compose构建一个wordpress应用
https://docs.docker.com/compose/wordpress/
八、Docker Swarm
https://docs.docker.com/swarm/overview/
1、架构
2、进阶
1、Swarm集群(2manager-3worker)
https://docs.docker.com/engine/swarm/ swarm入门操作
In Docker 1.12 and higher;Swarm已经内置了,无需安装
8容器–>1个服务。 redis有8个实例。
对象–>类。
raft:大多,manager节点存活。worker不参与集群一致性。
2-manager >1台;生产环境最少3 manager;炸一个集群完美工作。
在所有docker主机上,选定一个主节点。运行docker swarm init --advertise-addr ip地址
,初始化一个manager
docker swarm init \
--advertise-addr 192.168.121.142
--listen-addr 192.168.121.142
#关于advertise-addr和listen-addr这两个参数,前者用来指定其他节点连接m0时的地址,后者指定承载swarm流量的IP和端口
以后告别docker run
#1、docker-compose up 使用compose启动一个服务。docker-compose是单机下的玩具
#2、集群:docker service (使用docker管理服务。)容器升级为服务。
# docker service:管理集群里面的所有服务
# docker stack:管理集群的app stack;一个完整的应用有超多服务,就是一个服务栈
#扩容;
docker service create --name hello-nginx -p 8888:80 --replicas 3 nginx
docker service update --replicas 3 hello-nginx
#升级。滚动升级
docker service rollback hello-nginx; 当前+之前 来回回滚
#灰度发布,迭代。
docker service update --image nginx:1.18.0-alpine --update-parallelism 1 --update-delay 10s hello-nginx
docker service create
#为什么每一个机器8888都能访问。
剩下节点使用docker swarm join即可;
在主节点
docker swarm join-token manager
#可以生成Manager节点的join信息,再选中一个节点,运行命令就可以作为manager加入
docker node ls #查看节点信息
2、基本概念
1.Swarm
集群的管理和编排是使用嵌入docker引擎的SwarmKit,可以在docker初始化时启动swarm模式或者加入已存在的swarm
2.Node
一个节点是docker引擎集群的一个实例。您还可以将其视为Docker节点。您可以在单个物理计算机或云服务器上运行一个或多个节点,但生产群集部署通常包括分布在多个物理和云计算机上的Docker节点。
要将应用程序部署到swarm,请将服务定义提交给 管理器节点。管理器节点将称为任务的工作单元分派 给工作节点。
Manager节点还执行维护所需群集状态所需的编排和集群管理功能。Manager节点选择单个领导者来执行编排任务。
工作节点接收并执行从管理器节点分派的任务。默认情况下,管理器节点还将服务作为工作节点运行,但您可以将它们配置为仅运行管理器任务并且是仅管理器节点。代理程序在每个工作程序节点上运行,并报告分配给它的任务。工作节点向管理器节点通知其分配的任务的当前状态,以便管理器可以维持每个工作者的期望状态。
3.Service
一个服务是任务的定义,管理机或工作节点上执行。它是群体系统的中心结构,是用户与群体交互的主要根源。创建服务时,你需要指定要使用的容器镜像。
4.Task
任务是在docekr容器中执行的命令,Manager节点根据指定数量的任务副本分配任务给worker节点
------------------------------------------使用方法-------------------------------------
docker swarm:集群管理,子命令有init, ``join``, leave, update。(docker swarm --help查看帮助)
docker service:服务创建,子命令有create, inspect, update, remove, tasks。(docker service--help查看帮助)
docker node:节点管理,子命令有accept, promote, demote, inspect, update, tasks, ``ls``, ``rm``。(docker node --help查看帮助)
3、Swarm的工作模式
1. Node
2.Service
3.任务与调度
4.服务副本与全局服务
3、测试
在Swarm中部署服务(nginx为例)
#1、可以创建网络
# docker network create -d overlay nginx_net
#2、部署service
docker service create --replicas 1 --name my_nginx -p 88:80 nginx
# 就创建了一个具有一个副本(--replicas 1 )的nginx服务,使用镜像nginx
docker service ls
docker service inspect --pretty my_nginx
docker service ps my_nginx
#3、扩容
docker service scale my_nginx=4
docker service ps my_nginx
#4、模拟宕机查看效果
systemctl stop docker
docker node ls
docker service ps my_nginx
#5、Swarm 动态缩容服务(scale)
docker service scale my_nginx=1
#6、update扩缩容
docker service update --replicas 3 my_nginx
#7、update镜像升级
docker service update --image nginx:latest my_nginx
#8、服务移除
docker service rm my_nginx
#1、创建名为tomcat-net的覆盖网络(Overlay Netowork),这是个二层网络,处于该网络下的docker容器,即使宿主机不一样,也能相互访问:
docker network create -d overlay tomcat-net
#2、创建名为tomcat的服务,使用了刚才创建的覆盖网络:
docker service create --name tomcat \
--network tomcat-net \
-p 8080:8080 \
--replicas 2 \
tomcat:7.0.96-jdk8-openjdk
#3、执行命令docker service ls查看当前所有服务:
#4、执行命令docker service ps tomcat查看名为tomcat的服务,可见三个容器分别部署在m0、m2、w1机器上:
#5、执行命令docker service inspect --pretty tomcat查看名为tomcat的服务的详细信息(去掉--pretty可以看到更完整的):
#6、访问三个节点的tomcat,都能访问
#7、扩缩容
docker service scale tomcat=5
#8、滚动更新
docker service update \
--image tomcat:9.0.24-jdk11-openjdk \
--update-parallelism 1 \
--update-delay 10s tomcat
#update-parallelism:每次更新的容器数量,这里设置为1,表示每一个容器升级成功后才去升级下一个;
#update-delay:每一批升级成功后,升级下一批之前的等待时间,这里表示升级一个容器后等10秒再升级下一个;
#在升级过程中执行命令docker service ps tomcat查看服务,可以看到新版本容器逐个启动的过程:
#9、移除服务
docker service rm tomcat
服务模式
- 服务模式一共有两种:Ingress和Host,如果不指定,则默认的是Ingress;
- Ingress模式下,到达Swarm任何节点的8080端口的流量,都会映射到任何服务副本的内部80端口,就算该节点上没有tomcat服务副本也会映射;
- Host模式下,仅在运行有容器副本的机器上开放端口,使用Host模式的命令如下:
docker service create --name tomcat \
--network tomcat-net \
--publish published=8080,target=8080,mode=host \
--replicas 3 \
tomcat:7.0.96-jdk8-openjdk
默认swarm集群的所有部署,(3副本)。
manager节点负责调度,真正会在其他节点执行(创建副本)【默认worker优先,基本随机】。我们不知道我们的容器最终会发配给那个服务器。指定服务器,给他部署上去(label)。
1、ssd硬盘 ,数据存储,mysql。合适作为数据节点,存储节点
2、cpu厉害,适合作为计算,大数据,数据分析框架,就应该优先调度到cpu厉害的。hadoop存储之类的,就应该调度到ssd磁盘。
Drain a node:排空节点。把节点里面的容器驱逐出去。
In earlier steps of the tutorial, all the nodes have been running with ACTIVE availability. The swarm manager can assign tasks to any ACTIVE node, so up to now all nodes have been available to receive tasks. ACTIVE的节点,manager就可以分配任务进行部署。
docker node update --availability drain worker1
#这台机器用service部署产生的所有容器,都被驱逐出去了。当前docker就没有service的容器。但是集群为了保证副本数量会在其他机器拉起。
docker node update --availability pause worker1 #暂停接受给我的任务。
--availability active
#100 最好的状态,20台计算型的 。40台存储型的。30台冗余备份的
docker service hadoop --->调度到 40台存储型的;Label选择
# routing mesh;路由网
docker service create --name nginx -p 8080:80 --replicas 3 nginx
只要service是暴露端口的方式,全服务器都能访问。
#真正的nginx容器的ip
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
32: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP
link/ether 02:42:0a:00:00:12 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.18/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever
34: eth1@if35: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
#集群状态下,每个主机都会多两个网络
43c75b51441f docker_gwbridge bridge local
o7b9pfkdt2ti ingress overlay swarm
ingress: "Subnet": "10.0.0.0/24", "Gateway": "10.0.0.1"
docker_gwbridge:"Subnet": "172.18.0.0/16"
集群状态下,service创建的容器默认加入两个网络。 docker_gwbridge 、 ingress
4、Docker Stack和Docker Compose区别
- Docker stack会忽略了“构建”指令,无法使用stack命令构建新镜像,它是需要镜像是预先已经构建好的。 所以docker-compose更适合于开发场景;
- Docker Compose是一个Python项目,在内部,它使用Docker API规范来操作容器。所以需要安装Docker -compose,以便与Docker一起在您的计算机上使用;
- Docker Stack功能包含在Docker引擎中。你不需要安装额外的包来使用它,docker stacks 只是swarm mode的一部分。
- Docker stack不支持基于第2版写的docker-compose.yml ,也就是version版本至少为3。然而Docker Compose对版本为2和3的 文件仍然可以处理;
- docker stack把docker compose的所有工作都做完了,因此docker stack将占主导地位。同时,对于大多数用户来说,切换到使用docker stack既不困难,也不需要太多的开销。如果您是Docker新手,或正在选择用于新项目的技术,请使用docker stack。
5、Docker Stack常用命令
命令 | 描述 |
---|---|
docker stack deploy | 部署新的堆栈或更新现有堆栈 |
docker stack ls | 列出现有堆栈 |
docker stack ps | 列出堆栈中的任务 |
docker stack rm | 删除一个或多个堆栈 |
docker stack services | 列出堆栈中的服务 |
推荐实验: https://blog.csdn.net/huangjun0210/article/details/86502021
6、swarm网络细节
在 Swarm Service 中有三个重要的网络概念:
- Overlay networks 管理 Swarm 中 Docker 守护进程间的通信。你可以将服务附加到一个或多个已存在的
overlay
网络上,使得服务与服务之间能够通信。 - ingress network 是一个特殊的
overlay
网络,用于服务节点间的负载均衡。当任何 Swarm 节点在发布的端口上接收到请求时,它将该请求交给一个名为IPVS
的模块。IPVS
跟踪参与该服务的所有IP地址,选择其中的一个,并通过ingress
网络将请求路由到它。
初始化或加入 Swarm 集群时会自动创建ingress
网络,大多数情况下,用户不需要自定义配置,但是 docker 17.05 和更高版本允许你自定义。 - docker_gwbridge是一种桥接网络,将
overlay
网络(包括ingress
网络)连接到一个单独的 Docker 守护进程的物理网络。默认情况下,服务正在运行的每个容器都连接到本地 Docker 守护进程主机的docker_gwbridge
网络。
docker_gwbridge
网络在初始化或加入 Swarm 时自动创建。大多数情况下,用户不需要自定义配置,但是 Docker 允许自定义。
名称 | 类型 | 注释 |
---|---|---|
docker_gwbridge | bridge | none |
ingress | overlay | none |
custom-network | overlay | none |
- docker_gwbridge和ingress是swarm自动创建的,当用户执行了docker swarm init/connect之后。
- docker_gwbridge是bridge类型的负责本机container和主机直接的连接
- ingress负责service在多个主机container之间的路由。
- custom-network是用户自己创建的overlay网络,通常我们都需要创建自己的network并把service挂在上面。
ingress网络。vip(虚拟ip模式)
https://docs.docker.com/engine/swarm/ingress/
查看网络的基本环境
yum install bridge-utils -y ##以后就可以brctl show 查看网关
iptables -nL -t nat ##查看转发规则
ln -s /var/run/docker/netns /var/run/netns ## 看容器创建的两个Net Namespace
ip netns ##查看Net Namespace
ip netns exec xxx ip add ##查看mynet网络命名空间下的网卡情况。
ip netns exec xxx brctl show ##查看mynet网络空间下网桥挂载情况可以看出veth0挂到了br0网桥上。
Docker Compose安装ES集群实战:
https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docker.html
Docker swarm 中的LB分为两种情况:
- Ingress Load Balancing:暴露端口方式的负载均衡。产生虚拟ip。转发链。
- Internal Load Balancing:内部的负载均衡。通过service的名字也可以访问吗?
自定义Service
-
the port where the swarm makes the service available outside the swarm
- docker service -p 8080:80
-
an overlay network for the service to connect to other services in the swarm
- docker service --network ?;同一个网络的跨机通讯
-
CPU and memory limits and reservations
-
docker service create
--limit-cpu decimal Limit CPUs --limit-memory bytes Limit Memory
-
-
a rolling update policy
- docker service create --update-delay 10s --update-parllelism 2
-
the number of replicas of the image to run in the swarm
- docker service create --replicas 3 --name nginx nginx:1.19
Replicated and global services
调整service以什么方式运行
--mode string
Service mode (replicated or global) (default "replicated")
docker service create --mode replicated --name mytom tomcat:7 默认的
docker service create --mode global --name haha alpine ping baidu.com
#场景?日志收集
每一个节点有自己的日志收集器,过滤。把所有日志最终再传给日志中心
服务监控,状态性能。
docker service create --name myredis --network myswarm-net redis
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
93: eth0@if94: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP
link/ether 02:42:0a:00:01:03 brd ff:ff:ff:ff:ff:ff
inet 10.0.1.3/24 brd 10.0.1.255 scope global eth0
valid_lft forever preferred_lft forever
95: eth1@if96: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.4/16 brd 172.18.255.255 scope global eth1;都是本机的
valid_lft forever preferred_lft forever
"Endpoint": {
"Spec": {
"Mode": "vip"
},
"VirtualIPs": [
{
"NetworkID": "xm5bv9m1yb5q5kfwl5sgbed0j",
"Addr": "10.0.1.2/24" #集群访问redis拿这个可以用
}
]
}
ping 容器的集群ip(10.0.1.3),service的集群vip(10.0.1.2),serviceName都通
3redis副本以后;容器ip 10.0.1.8、10.0.1.3、10.0.1.9。
无论多少个副本。一个service虚拟ip是固定的。负载均衡的方式。
建立redis集群。一个虚拟ip。sb应用和redis集群只要在同一个网络。直接写10.0.1.2,serviceName
docker service create --name mynettomcat --network myswarm-net tomcat
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
93: eth0@if94: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 02:42:0a:00:01:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.0.1.6/24 brd 10.0.1.255 scope global eth0
valid_lft forever preferred_lft forever
95: eth1@if96: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 172.18.0.4/16 brd 172.18.255.255 scope global eth1
valid_lft forever preferred_lft forever
docker service inspect mynettomcat
"Endpoint": {
"Spec": {
"Mode": "vip"
},
"VirtualIPs": [
{
"NetworkID": "xm5bv9m1yb5q5kfwl5sgbed0j",
"Addr": "10.0.1.5/24"
}
]
}
10.0.1.0/24
#1、他们两个能互相访问吗?有哪些方式。ip?serviceName?
#2、vip,dnsrr
docker service update --endpoint-mode dnsrr myredis
# docker inspect service就没vip了想要负载均衡。只能serviceName;
外部端口暴露负载均衡ingress。内部集群负载。vip。dnsrr
1、给docker集群部署服务的两种方式
docker service create xxxxx
2、使用compose文件
docker-compose up -d -c wordpress-compose.yaml;
效果:单机部署。自己产生默认的网络
docker stack deploy wordpress-compose.yaml;#集群部署,compose里面的所有服务会被发配到集群的各个地方
#写好docker-compose文件。运维人员部署即可。
version: '3.1'
services:
wordpress:
image: wordpress
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- wordpress:/var/www/html
db:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- db:/var/lib/mysql
volumes:
wordpress:
db:
#扩展。
集群下的数据一致。
redis,mysql等这些集群。数据本身挂载到本机(没有任何问题)。高可用,就是做mysql的集群。设置好集群内的数据同步,这是mysql集群技术解决的问题。而不是数据卷挂载解决的问题。
应用的资源文件。才应该考虑数据卷的集群情况下,如果本机挂载就不一致了。自己文件系统。
nfs;/abc ---> 远程的:/haha
Dockerfile
VOLUME ["/sss"] -->映射到docker /var/lib/docker/volumes
7、Docker Secret与Config
1、Secret
生产环境下,为了安全,我们不能把各项目的配置密码写入到配置文件
我们可以引入docker的secret方式保护密码。
场景
- 用户名密码
- SSH Key
- TLS认证
- 任何别人不想看到的数据
1、创建一个密码secret
2、用完就删
3、哪个服务想要使用只要暴露给他即可。
1、如何声明
#文本模式
printf 123456 | docker secret create my_secret -
echo "adminadmin" | docker secret create my-pwd -
#文件模式
docker secret create my_secret ./pwd
#Secret会基于raft在master主机之间同步。
2、如何使用
secret可以分配给一个service,就可以使用,secret在容器内部看起来是个文件,实际上在内存中
#1、普通情况
docker service create --name mysql --secret mysql_pwd -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_pwd -p 3306:3306 mysql:5.7
#2、compose文件
version: "3"
services:
mysql:
image: mysql:5.7
ports:
- 3306:3306
secrets:
- mysql_pwd
enviroment:
- MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_pwd
原理:我们使用–secret mysql_pwd以后,secret密码文件就会被解码保存到容器内部的/run/secrets/secretname中。这样我们就可以在容器中任意使用。但是外部无感知。
不管怎么使用,secret最好提前创建好。到时候声明使用就行。其实是在内存中的。整个集群manager是利用raft同步的
2、config
1、如何声明
docker config create [OPTIONS] CONFIG file|-
docker config create redis.conf redis.conf
# 使用文件方式创建
docker service create --config redis.conf --name redis redis
#最终会被映射到容器的根目录。以后修改容器的启动命令即可
#配置中心
#部署100个redis副本。到100个服务器。他们配置文件都一样
#config内容base64编码,是可以inspect出来的。
secret文件是在容器中 /run/secrets/xxxx 暴露的
config默认是在根目录暴露的。
2、如何使用
#指定位置
docker service create --name redis \
--config source=redis-conf,target=/etc/redis/redis.conf,mode=0400 redis:3.0.6
#2、compose文件
version: "3"
services:
mysql:
image: redis
ports:
- 6379:6379
config:
- mysql_pwd
enviroment:
- MYSQL_ROOT_PASSWORD_FILE: /
https://docs.docker.com/compose/compose-file/#configs
3、补充Label的使用
# 我们讨论了 Service 部署的两种模式:global mode 和 replicated mode。无论采用 global mode 还是 replicated mode,副本运行在哪些节点都是由 Swarm 决定的,作为用户我们有没有可能精细控制 Service 的运行位置呢?
答:能,使用 label
逻辑分两步:
1、为每个 node 定义 label。
2、设置 service 运行在指定 label 的 node 上。
docker node update --label-add env=test 节点1
docker node update --label-add env=prod swarm-worker2
docker service create \
--constraint node.labels.env==test \
--replicas 3 \
--name my_web \
--publish 8080:80 \
httpd
#更新 service,将其迁移到生产环境:
docker service update --constraint-rm node.labels.env==test my_web
docker service update --constraint-add node.labels.env==prod my_web
8、扩展阅读
1、vxlan
https://blog.csdn.net/tony_vip/article/details/100097245
2、lvs
https://blog.csdn.net/tony_vip/article/details/104224374
9、其他填坑
Service Mode:服务模式。 Endpoint Mode:端点模式
1、集群的所有操作必须在manager节点执行
2、机器数量决定的业务体量
3、service就是一个业务,真正是以容器的方式运行在docker机器。服务副本指容器数量
打日志的时候,加上机器名就行。