这里我用官方的一键脚本安装。
详细查看树莓派更换国内镜像源
curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
安装完成后,可以运行hello world看看
sudo docker run hello-world
Swarm 是 Docker 社区提供的原生支持 Docker 集群管理的工具。它可以把多个 Docker 主机组成的系统转换为单一的虚拟 Docker 主机,使得容器可以组成跨主机的子网网络。docker默认就带有Swarm。
我目前有4个树莓派,IP分别为
192.168.1.51 管理节点
192.168.1.52 管理节点
192.168.1.54 工作节点
192.168.1.55 管理节点
我在51上创建集群
docker swarm init --advertise-addr 192.168.1.51
创建完成后,可以通过 docker info 查看swarm的情况,
在51上输入
docker swarm join-token manager
获取加入swarm集群管理节点的token,进行复制
docker swarm join --token SWMTKN-1-4uetltgpm9a8qsg6egosgg5z4qw3nqyhipqdgse81fejb6xipr-39jb6lthdno9qxei6xv8cbjop 192.168.1.51:2377
注意: 不要照抄我的token
在51上输入
docker swarm join-token worker
获取加入swarm集群工作节点的token,进行复制
docker swarm join --token SWMTKN-1-4uetltgpm9a8qsg6egosgg5z4qw3nqyhipqdgse81fejb6xipr-e2ytxxf8lsizsb70hohablyns 192.168.1.51:2377
在任意一个管理节点输入
docker node ls
查看各个节点的情况
我使用.net core框架,C#语言编写测试代码,集群上运行一个Server,监听udp的12345端口,当收到客户端发来的数据,就给客户端返回自己的ID和接收到的数据。Socket控件我使用Bouyei.NetFactory。使用方法可以参考下我以前写的简单例子,《使用 Bouyei.NetFactory控件在树莓派上进行socket通信》
Server端代码
using Bouyei.NetFactoryCore;
using System;
using System.Text;
using System.Threading;
namespace Server
{
class Program
{
static string id = Guid.NewGuid().ToString();
static void Main(string[] args)
{
UdpDemo();
}
private static void UdpDemo()
{
int port = 12345;
INetServerProvider serverProvider = NetServerProvider.CreateProvider(256, 4, NetProviderType.Udp);
serverProvider.ReceivedOffsetHandler = GetReceivedSegmentHandler(serverProvider);
serverProvider.Start(port);
Thread.Sleep(Timeout.InfiniteTimeSpan);
serverProvider.Dispose();
}
private static OnReceivedSegmentHandler GetReceivedSegmentHandler(INetServerProvider serverProvider)
{
return new OnReceivedSegmentHandler((SegmentToken session) =>
{
string datas = Encoding.UTF8.GetString(session.Data.buffer, session.Data.offset, session.Data.size);
Console.WriteLine("from IP:"+ session.sToken.TokenIpEndPoint.ToString() +" Datas:"+ datas);
serverProvider.Send(new SegmentToken(session.sToken, Encoding.UTF8.GetBytes("id:"+id +" -> "+ Encoding.UTF8.GetString(session.Data.buffer, session.Data.offset, session.Data.size)+" "+ DateTime.Now)));
});
}
}
}
然后服务端添加Docker支持,相应的配置参考我另外的文章《树莓派上在Docker中跑.Net Core》
生成Docker镜像后,上传到私有仓库,方法可参考我的另外文章《docker 创建本地私有仓库》
客户端代码
客户端从PC机连接服务端,代码如下
using Bouyei.NetFactoryCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Client
{
class Program
{
static IList ips;
static int c = 1000*1000;
static INetClientProvider clientProvider = null;
static bool isConn;
static string currIP = "";
static DateTime latestTime = DateTime.Now;
static void Main(string[] args)
{
ips = new List();
ips.Add(new IpAddress()
{
IP = "192.168.1.51",
isOk = true
});
ips.Add(new IpAddress()
{
IP = "192.168.1.52",
isOk = true
});
ips.Add(new IpAddress()
{
IP = "192.168.1.54",
isOk = true
});
ips.Add(new IpAddress()
{
IP = "192.168.1.55",
isOk = true
});
clientProvider = NetClientProvider.CreateProvider(4096, 4, NetProviderType.Udp);
clientProvider.ReceivedOffsetHandler = new OnReceivedSegmentHandler((SegmentToken session) =>
{
latestTime = DateTime.Now;
Console.WriteLine("from server : " + Encoding.UTF8.GetString(session.Data.buffer, session.Data.offset,
session.Data.size));
});
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 5 * 1000;
timer.Elapsed += Timer_Elapsed;
timer.Start();
ConnectServer();
HandleSomething();
Thread.Sleep(Timeout.InfiniteTimeSpan);
}
///
/// 程序执行时间测试
///
/// 开始时间
/// 结束时间
/// 返回(秒)单位,比如: 0.00239秒
public static double ExecDateDiff(DateTime dateBegin, DateTime dateEnd)
{
TimeSpan ts1 = new TimeSpan(dateBegin.Ticks);
TimeSpan ts2 = new TimeSpan(dateEnd.Ticks);
TimeSpan ts3 = ts2.Subtract(ts1).Duration();
//你想转的格式
return ts3.TotalSeconds;
}
private static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (ExecDateDiff(latestTime, DateTime.Now) > 10)
{
isConn = false;
}
else
{
isConn = true;
}
if (!isConn)
{
var ip = ips.FirstOrDefault(x => x.IP.Contains(currIP));
ip.isOk = false;
ConnectServer();
}
}
static void ConnectServer()
{
int port = 12345;
var ipList = ips.Where(x => x.isOk).ToList();
Random rd = new Random();
int index = rd.Next(ipList.Count);
var ip = ipList[index];
if (ip!=null)
{
Console.WriteLine($"正在连接->{ip.IP}:{port}");
clientProvider.ConnectTo(port, ip.IP);
currIP = ip.IP;
}
else
{
foreach(var ip1 in ips)
{
ip1.isOk = true;
}
}
}
static void HandleSomething()
{
while (c > 0)
{
Console.WriteLine($"Send -> {c}");
clientProvider.Send(new SegmentOffset(Encoding.UTF8.GetBytes((c).ToString())));
c--;
Thread.Sleep(500);
}
}
}
class IpAddress
{
public string IP { get; set; }
public bool isOk { get; set; }
}
}
生成。
在leader的树莓派上,把Server从私有仓库拉取下来。
然后创建服务
docker service create --replicas 3 -p 12345:12345/udp 111.111.3.210:5000/bystdnetfactorydemo
我创建了一个Service,运行了3个副本的Server端程序,对外监听12345的udp端口。运行成功后,可以通过 docker service ls 查看服务
打开多个Client,程序启动后会自动连接服务端,并每隔500毫秒给服务端发送数据,docker Swarm会根据负载均衡,自动分配一个副本与客户端连接。下图中可以看到,我打开了3个Client端,服务端返回的ID是不一致的。
加入集群的节点,我们都可以使用其IP来访问集群,如果某个节点断掉或者故障了,可以使用其余节点进行连接集群,如果故障节点刚好运行副本,集群也会自动迁移,在其他节点上运行一个副本顶替。