目录html
Docker已经上市不少年,不是什么新鲜事物了,不少企业或者开发同窗之前也很少很多有所接触,可是有实操经验的人很少,本系列教程主要偏重实战,尽可能讲干货,会根据本人理解去作阐述,具体官方概念能够查阅官方教程,由于本系列教程对前一章节有必定依赖,建议先学习前面章节内容。java
本系列教程导航:
Docker深刻浅出系列 | 容器初体验
Docker深刻浅出系列 | Image实战演练
Docker深刻浅出系列 | 单节点多容器网络通讯
Docker深刻浅出系列 | 容器数据持久化
Docker深刻浅出系列 | 单机Nginx+Springboot实战
Docker深刻浅出系列 | Docker Compose多容器实战node
教程目的:mysql
1.克隆credit-facility-service
做为后面部署演示使用,使用docker
分支linux
git clone https://github.com/EvanLeung08/credit-facility-service.git
2.虚拟机、centos和docker环境安装请查看第一章,本章默认已经安装好centos和docker
Docker深刻浅出系列 | 容器初体验nginx
简单来讲,Docker Swarm就是一个把多个物理主机或者虚拟机组成的集群上的容器群进行管理的容器编排工具,负责编排、调度和集群管理,由集群的活动由集群管理器控制,加入集群的机器称为节点,容许用户管理跨多个主机部署的多个容器。git
SwarmKit是可扩展分布式系统的节点发现、基于Raft的共识、任务调度、基于基元的编排工具包,该工具包使用Raft共识算法来协调和决策分布式系统。github
如下列出了Docker Swarm的一些关键术语:web
节点(Node): 在编排方面,节点是主机。 一个节点能够是单个主机中的多个VM。算法
管理节点(Manager Node): 此节点负责维护Swarm编排,它管理集群环境。
工做节点(Worker Node): 该节点负责执行管理节点定义的任务。 它将始终将其状态通知给Manager节点并提供分配给它的服务。
任务(Task): 任务包含一个Docker容器和在容器内运行的命令,它是swarm的原子调度单元,若是某一个任务奔溃,那么协调器将建立一个新的副本任务,该任务将生成一个新的容器
Docker Swarm和k8s都是目前的容器编排的主流技术,可是目前市场上大多数企业都是用k8s进行容器集群管理,k8s的背靠Google这棵大树,开源社区也很是活跃,随着这些年云厂商的迅速发展,k8s是将来趋势,我我的建议在真实项目仍是使用k8s进行容器编排和管理,不过这里是docker专场,我暂很少说,会在后面k8s专题去讲这一块内容。
假如没有Swarm这类多机容器管理技术,咱们很难对容器进行管理,而且容器之间没办法实现跨机器通讯。而Docker swarm可让用户轻松在多个机器上发布和管理应用,而且咱们不须要关注每一个容器实例具体落在哪个节点,swarm把咱们的应用以服务的形式暴露出去,并内置服务发现和负载均衡,让运行在多个节点上的容器集群感受就像只有一个应用在跑同样简单,能够轻松实现扩容和自动容错(一个swarm任务的容器奔溃会自动扩展一个新的容器
)。Swarm集群一般有几个工做程序节点和至少一个管理程序节点,负责高效地处理工做程序节点的资源并确保集群有效地运行,提升了应用可用性。
如下三个网络概念对于Swarm集群服务很重要:
Overlay - Overlay
网络管理参与集群的Docker守护程序之间的通讯。您可使用与独立容器的用户定义网络相同的方式建立Overlay
网络。您也能够将服务附加到一个或多个现有的Overlay
网络,以启用服务到服务的通讯。Overlay
网络是使用Overlay
网络驱动程序的Docker网络。
ingress - ingress
网络是一种特殊的Overlay
网络,可促进服务节点之间的负载均衡。当任何集群节点在已发布的端口上收到请求时,会将请求转交给名为IPVS的模块。 IPVS经过ingress
网络跟踪参与该服务的全部IP地址,选择其中一个并将请求发送给它。
当对节点进行swarm init
或swarm join
时,会自动建立ingress
网络。大多数用户不须要自定义其配置,可是Docker 17.05及更高版本容许您自定义。
docker_gwbridge - docker_gwbridge
是一个桥接网络,它将overlay
网络(包括ingress
网络)链接到单个Docker守护程序的物理网络。默认状况下,服务正在运行的每一个容器都链接到其本地Docker守护程序主机的docker_gwbridge
网络。
初始化或加入集群时会自动建立docker_gwbridge
网络。大多数用户不须要自定义其配置,可是Docker容许您自定义。
Docker Engine内有一个嵌入式DNS服务器,当Docker不以Swarm模式运行时,容器会使用这个服务器;而当Docker Engine以Swarm模式运行时,该服务器将用于任务。 它为bridge
,Overlay
或MACVLAN
网络中主机上的全部容器提供名称解析。 每一个容器将其查询请求转发到Docker引擎,后者依次检查该容器或服务是否与首先发送请求的容器在同一网络上。 若是是,它将在其内部键值存储中搜索与容器、任务或服务的名称匹配的IP(或虚拟IP)地址,并将其返回给发送请求的容器。
若是匹配的资源与生成请求的容器在同一网络内,则Docker引擎只会返回IP地址。 这样作的好处还在于,Docker主机仅存储属于该节点在其中具备容器或任务的网络的DNS条目。 这意味着它们将不会存储实际上与他们无关的信息,或者其余容器不须要知道的信息。
在上图中,有一个名为custom-net
的自定义网络。 网络上运行着两种服务:myservice和myclient。 myservice有两个与之关联的任务,而客户端只有一个。
客户端myclient
而后执行对myservice的curl请求,所以,它也在对DNS进行请求。 容器内置的解析器将查询转发到Docker引擎的DNS服务器。 而后,对myservice的请求将解析为10.0.0.2虚拟IP,转发回客户端,客户端就能够经过虚拟ip去访问容器。
建立服务后,Docker将自动启用此功能。 所以,建立服务后,它会当即在服务的网络上得到虚拟IP地址。 就像上文在服务发现部分中所说的那样,当请求服务时,所获得的DNS查询将转发到Docker引擎,该引擎进而返回服务的IP,即虚拟IP。 发送到该虚拟IP的流量将负载均衡到网络上该服务的全部正常运行的容器。 全部负载均衡均由Docker完成,由于只有一个入口点被分配给客户端(一个IP)。
默认状况下,不会激活负载均衡,当在建立或更新时使用–publish
标志公开服务时,集群中的每一个节点都会开始侦听已发布的端口,这意味着每一个节点均可以响应对映射到该端口的服务的请求。
当某个节点接收到一个请求,但该节点没有容器实例时,会发生什么? 从Docker 1.12(将Swarm模式集成到Docker Engine的同一版本)开始,就有一个名为Routing Mesh
的功能,该功能使用IP虚拟服务器(ipvs)和iptables来负载均衡第4层中的请求。基本上,ipvs实现了第4层 Linux内核上的负载均衡功能,该功能容许将对基于TCP / UDP
的服务的请求重定向到实际的后端(在这种状况下为容器)。 在Swarm的特定状况下,每一个节点都侦听暴露的端口,而后使用称为ingress
的特殊Overlay
网络将请求转发到暴露的服务的VIP(虚拟IP)。 仅当将外部流量传输到请求的服务时,才使用此Overlay
网络。 在这种状况,docker会使用与上文描述的相同的内部负载均衡策略。
在上图中,在appnet Overlay
网络上建立了具备两个副本的服务。 咱们能够看到该服务在三个节点上的8000
端口上公开,此时,发往应用程序的流量能够转发到任何节点。 假设在这种状况下,有一个外部负载均衡器,它刚好将请求转发到惟一没有该服务实例的节点, 该请求由IPVS在第三个节点上处理和转发,该IPVS使用ingress
网络并所以使用上述负载均衡方法将其重定向到该服务的集群上的其中一个真实运行的容器。
上图来自官方,很清晰展现了在swarm模式,manage节点和worker节点是怎么协做的,这里就不作细说了。
credit-facility-net
credit-facility-volume
,用于持久化Mysql容器数据由于我机器资源不够,我这里只是建立了三台虚拟机,manager节点也是能够部署服务的
这里用到Vagrant来管理虚拟机,若是不知道Vagrant是什么,请查看第一章内容。Vagrant指令能够查看Vagrant详细指令文档
1.在咱们的主机(你本身的电脑)建立一个文件夹swarm-centos7
,而后在目录下建立一个Vagrantfile
文件
Vagrant
boxes = [ { :name => "manager-node", :eth1 => "192.168.101.11", :mem => "1024", :cpu => "1" }, { :name => "worker01-node", :eth1 => "192.168.101.12", :mem => "1024", :cpu => "1" }, { :name => "worker02-node", :eth1 => "192.168.101.13", :mem => "1024", :cpu => "1" } ] Vagrant.configure(2) do |config| config.vm.box = "centos7" boxes.each do |opts| config.vm.define opts[:name] do |config| config.vm.hostname = opts[:name] config.vm.provider "vmware_fusion" do |v| v.vmx["memsize"] = opts[:mem] v.vmx["numvcpus"] = opts[:cpu] end config.vm.provider "virtualbox" do |v| v.customize ["modifyvm", :id, "--memory", opts[:mem]] v.customize ["modifyvm", :id, "--cpus", opts[:cpu]] v.customize ["modifyvm", :id, "--name", opts[:name]] end config.vm.network :public_network, ip: opts[:eth1] end end end
这里指定了三台虚拟机的配置,而且分别分配了一个静态ip 192.168.101.11
、192.168.101.12
和192.168.101.13
,而且循环建立虚拟机。这里指定了docker swarm
最低要求配置1个CPU、1G内存。
2.启动三台虚拟机,记得在启动过程选择你可用的网卡
evans-MacBook-Pro:swarm-centos7 evan$ vagrant up
当该命令执行完毕,会有三台虚拟机成功被初始化
由于后面须要用到ssh客户端工具,因此这里要把密码开放下
修改 manager 节点访问密码,用于后面上传文件使用,这里的密码是evan123
[root@manager-node local]# passwd Changing password for user root. New password: BAD PASSWORD: The password fails the dictionary check - it is too simplistic/systematic Retype new password: passwd: all authentication tokens updated successfully.
这里只演示了manager节点,另外两个worker节点也须要作一样的操做
须要在每一个节点安装Docker,请使用SSH客户端工具,分别登录3个虚拟机,先按如下步骤安装docker
1.卸载以前的docker配置,若有
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
2.安装服务器必要的依赖
sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
3.配置加速器,由于国内下载docker须要越过长城,会比较慢,这里我用到的是我本身的阿里云加速器,若是不知道怎么配置的,请查看第一章
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://6xh8u88j.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker
4.设置Docker仓库
sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
5.安装Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io
6.安装完毕后,分别进入manger节点和2个worker节点,启动docker服务
[root@manager-node ~]# systemctl start docker
7.验证docker,输入docker info
,验证下docker是否已经安装成功
这里须要先进入manager节点192.168.101.11
进行swarm初始化操做,
[root@manager-node credit-facility]# docker swarm init --advertise-addr=192.168.101.11 Swarm initialized: current node (tdtu8tl63zqim8jrbzf8l5bcn) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-61e18f81408f4ja2yrcn9l11y5x21okcx58d3f6gcptyydb0iz-9hquv710qpx8s4h88oy2fycei 192.168.101.11:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
这里会生成一串token,随后须要在worker节点使用该命令去加入到swarm集群中
这里咱们须要分别进去两个worker节点192.168.101.12
和 192.168.101.13
执行如下操做
docker swarm join --token SWMTKN-1-61e18f81408f4ja2yrcn9l11y5x21okcx58d3f6gcptyydb0iz-9hquv710qpx8s4h88oy2fycei 192.168.101.11:2377
这里表示当前节点加入swarm集群
只能在swarm manager节点查看当前全部节点信息
[root@manager-node credit-facility]# docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION tdtu8tl63zqim8jrbzf8l5bcn * manager-node Ready Active Leader 19.03.7 n8m358c7ta18206gzey7xsvw8 worker01-node Ready Active 19.03.7 pgzph6ye6xl1p9fz0hif191kn worker02-node Ready Active 19.03.7
这里能够看到,咱们的3个节点都已经成功加入到swarm集群,manager-node是咱们的swarm集群 leader
跟前一章同样,在集群三个节点的/usr/local
下,分别建一个credit-facitliy
目录
[root@worker01-node local]# mkdir credit-facility
# for docker-stack demo spring.datasource.url = jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicode=true&characterEncoding=utf8 #spring.datasource.url = jdbc:mysql://192.168.101.23:3301/db_credit_facility?useUnicode=true&characterEncoding=utf8 #配置数据库用户名 #spring.datasource.username = root spring.datasource.username = ${DB_USER} #配置数据库密码 #spring.datasource.password = evan123 spring.datasource.password = ${DB_PASSWORD}
配置文件我已经在项目里提早修改好了,你们直接用便可
mvn clean package
,打包后在star目录下evans-MacBook-Pro:target evan$ sftp root@192.168.101.11 root@192.168.101.11's password: Connected to root@192.168.101.11. sftp> put start-1.0.0-SNAPSHOT.jar /usr/local/credit-facility Uploading start-1.0.0-SNAPSHOT.jar to /usr/local/credit-facility/start-1.0.0-SNAPSHOT.jar start-1.0.0-SNAPSHOT.jar 100% 43MB 76.5MB/s 00:00 sftp>
evans-MacBook-Pro:target evan$ sftp root@192.168.101.12 root@192.168.101.11's password: Connected to root@192.168.101.12. sftp> put start-1.0.0-SNAPSHOT.jar /usr/local/credit-facility Uploading start-1.0.0-SNAPSHOT.jar to /usr/local/credit-facility/start-1.0.0-SNAPSHOT.jar start-1.0.0-SNAPSHOT.jar 100% 43MB 76.5MB/s 00:00 sftp>
evans-MacBook-Pro:target evan$ sftp root@192.168.101.13 root@192.168.101.11's password: Connected to root@192.168.101.13. sftp> put start-1.0.0-SNAPSHOT.jar /usr/local/credit-facility Uploading start-1.0.0-SNAPSHOT.jar to /usr/local/credit-facility/start-1.0.0-SNAPSHOT.jar start-1.0.0-SNAPSHOT.jar 100% 43MB 76.5MB/s 00:00 sftp>
/usr/local/credit-facility
目录下,建立Dockerfile[root@manager-node credit-facility]# cat Dockerfile FROM openjdk:8-jre-alpine MAINTAINER evan LABEL name="credit-facility" version="1.0" author="evan" COPY start-1.0.0-SNAPSHOT.jar credit-facility-service.jar CMD ["java","-jar","credit-facility-service.jar"]
该文件我已经提早放置在额度服务项目里,直接copy便可
/usr/local/credit-facility
目录下,执行如下命令建立额度服务镜像,这里只演示manager
节点,其余节点操做同样[root@manager-node credit-facility]# docker build -t credit-facility-image . Sending build context to Docker daemon 44.92MB Step 1/5 : FROM openjdk:8-jre-alpine ---> f7a292bbb70c Step 2/5 : MAINTAINER evan ---> Running in 50b0ae0125ef Removing intermediate container 50b0ae0125ef ---> b4231d681d22 Step 3/5 : LABEL name="credit-facility" version="1.0" author="evan" ---> Running in 4a6bb0ae9f12 Removing intermediate container 4a6bb0ae9f12 ---> ea441d121fc4 Step 4/5 : COPY start-1.0.0-SNAPSHOT.jar credit-facility-service.jar ---> 0bed9d9397f6 Step 5/5 : CMD ["java","-jar","credit-facility-service.jar"] ---> Running in 6bb0c14f1a85 Removing intermediate container 6bb0c14f1a85 ---> de2606eea641 Successfully built de2606eea641 Successfully tagged credit-facility-image:latest
建立完查看下每一个节点现有的镜像列表:
[root@worker01-node credit-facility]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE credit-facility-image latest 8dcef5954aaa 3 hours ago 130MB openjdk 8-jre-alpine f7a292bbb70c 10 months ago 84.9MB
从上面查询结果能够看到,咱们的额度服务镜像已经成功建立
由于咱们后面用到Nginx
服务,因此咱们须要提早把配置建立后,而后经过--mount
方式把配置文件覆盖Nginx
容器内的默认配置
在/usr/local/credit-facility
文件夹下,建立一个nginx
目录,并在nginx
目录下建一个nginx.conf
配置文件
[root@manager-node nginx]# cat nginx.conf user nginx; worker_processes 1; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; location / { proxy_pass http://balance; } } upstream balance{ server credit-facility-service:8080; } include /etc/nginx/conf.d/*.conf; }
我这里利用docker swam
内置DNS原理,我这里配置域名是额度服务名称credit-facility-service
,docker swarm
会自动帮咱们路由到对应的服务节点上
/usr/local/credit-facility
文件夹下,建立一个docker-stack.yml
用于建立和管理服务(注意,这里只须要manager节点建立便可,manager节点会把docker service
发布到到其余节点)[root@manager-node credit-facility]# cat docker-stack.yml version: '3' services: db: restart: always image: mysql build: context: /usr/local/credit-facility ports: - 3306:3306/tcp volumes: - "credit-facility-volume:/var/lib/mysql:rw" environment: - MYSQL_DATABASE=db_credit_facility - MYSQL_ROOT_PASSWORD=evan123 networks: - demo-overlay deploy: mode: global placement: constraints: - node.role == manager credit-facility-service: restart: always image: credit-facility-image build: context: /usr/local/credit-facility ports: - 8080:8080 environment: - DB_HOST=db - DB_PORT=3306 - DB_USER=root - DB_PASSWORD=evan123 - DB_NAME=db_credit_facility networks: - demo-overlay deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 1 delay: 10s nginx: restart: always depends_on: - db - credit-facility-service image: nginx ports: - "80:80" volumes: - /usr/local/credit-facility/nginx/nginx.conf:/etc/nginx/nginx.conf networks: - demo-overlay deploy: mode: global placement: constraints: - node.role == manager networks: demo-overlay: driver: overlay volumes: credit-facility-volume: {}
docker stack
去发布服务,能够将服务发布到集群各个节点上(docker compose
是用于单机部署,多机部署须要用到docker swarm stack
)[root@manager-node credit-facility]# docker stack deploy -c docker-stack.yml web Ignoring unsupported options: build, restart Ignoring deprecated options: container_name: Setting the container name is not supported. Creating network web_credit-facility-net Creating service web_nginx Creating service web_db Creating service web_credit-facility-service ...
[root@manager-node nginx]# docker service ls ID NAME MODE REPLICAS IMAGE PORTS 3qcjnj7n5dkk web_credit-facility-service replicated 3/3 credit-facility-image:latest *:8080->8080/tcp t5omqvum57ag web_db global 1/1 mysql:latest *:3306->3306/tcp w89fkne6fzcg web_nginx global 1/1 nginx:latest *:80->80/tcp
从上面结果能够看到,咱们发布的各个服务已经成功启动,额度服务的3个实例也成功发布到3个节点中
[root@manager-node nginx]# docker service ps web_credit-facility-service ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS pc32kfmfxke0 web_credit-facility-service.1 credit-facility-image:latest worker01-node Running Running 23 minutes ago 8v9efe61p5wb web_credit-facility-service.2 credit-facility-image:latest manager-node Running Running 23 minutes ago sg1wh95lxyca web_credit-facility-service.3 credit-facility-image:latest worker02-node Running Running 23 minutes ago
上面结果能够清晰看到,每个实例被发布到哪一个节点中
跟前面章节同样,由于额度服务对数据库有依赖,因此这里须要初始化额度服务用到的表,使用Mysql客户端,链接到数据库,把项目里resources/db
的表建立语句放入执行,我这里用Navicat去链接,数据库链接是192.168.101.11:3306
到这里,全部的配置已经完成了,咱们来一块儿核对下现有的文件有哪些,看看你们有没有遗漏的恶配置
在manager节点的credit-facility
文件夹下,应该有如下文件
[root@manager-node credit-facility]# ls docker-compose.yaml Dockerfile docker-stack.yml nginx start-1.0.0-SNAPSHOT.jar
在2个worker节点的credit-facility
文件夹下,应该有如下文件
worker-01节点
[root@worker01-node credit-facility]# ls Dockerfile start-1.0.0-SNAPSHOT.jar
worker-02节点
[root@worker02-node credit-facility]# ls Dockerfile start-1.0.0-SNAPSHOT.jar
192.168.101.11http://192.168.101.11/swagger-ui.html
能够访问到咱们的额度服务ip+8080
访问咱们的服务由于咱们额度服务有用到数据库,这里咱们实验下调用额度服务的接口,看是否能够成功入库
请求参数以下:
{ "registrationLimitCO": { "applicationId": "1111", "userId": 1111, "quotaLimit": 10000, "productCode": "tb", "expirationTime": "2030-01-01", "accountType": 1 } }
执行额度注册接口
执行结果:
从上面执行结果能够看到,咱们的接口已经成功处理咱们的请求,而且入库成功,有兴趣的同窗能够到数据库去查看相应记录
[root@manager-node credit-facility]# docker stack ls NAME SERVICES ORCHESTRATOR web 3 Swarm
docker-stack.yml
建立服务docker statck deploy -c docker-stack.yml web
[root@manager-node credit-facility]# docker service inspect web_db ... "Endpoint": { "Spec": { "Mode": "vip", "Ports": [ { "Protocol": "tcp", "TargetPort": 3306, "PublishedPort": 3306, "PublishMode": "ingress" } ] }, "Ports": [ { "Protocol": "tcp", "TargetPort": 3306, "PublishedPort": 3306, "PublishMode": "ingress" } ], "VirtualIPs": [ { "NetworkID": "5mmql4cfhoac6q3y67wm4x2uh", "Addr": "10.0.0.122/24" }, { "NetworkID": "xx3b6lki8n1nvkphffretc050", "Addr": "10.0.6.12/24" } ] } ...
docker service create --name my-nginx nginx
docker service ls
docker service logs my-nginx
ocker service inspect my-nginx
docker service ps my-nginx
docker service scale my-nginx=3 docker service ls docker service ps my-nginx
docker service rm my-nginx