這篇文章主要介紹了Docker 部署單機版 Pulsar 和叢集架構 Redis(開發神器)的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
一、前言:
現在網際網路的技術架構中,不斷出現各種各樣的中間件,例如 MQ、Redis、Zookeeper,這些中間件在部署的時候一般都是以主從架構或者叢集的架構來部署,公司一般都會在開發環境、測試環境和生産環境各部署一套。
當我們開發的時候,一般就會連着開發環境。但是呢,一般公司的開發環境都隻能在内網使用,當我們回家了,除非公司提供有 VPN,不然就沒辦法使用了。有時候我們是有VPN了,但是開發起來還是很不友善。例如我們現在的 MQ 中間件使用的是 Pulsar,但是 Pulsar 的 tenant 和 namespace 都是不能自動建立的,是以平時開發起來非常的不友善,隻能每次需要時都找一遍 DBA。
是以,一般我們都會在本地自己部署一套,但是自己部署有下面的兩個難點:
中間件有自己的實作語言,例如 RabbitMQ,當我們部署 RabbitMQ 時,首先要安裝好 Erlang 語言。部署叢集架構會耗費大量的系統資源,導緻本來資源就吃緊的筆記本更加卡頓,開發起來非常的不爽。
二、Docker:
Docker 可以完美地解決上面的問題,并且讓部署變得極其的簡單。下面以我自己用到的中間件為例子。
A、Pulsar:
上面提到,Pulsar 的租戶是不能自動建立的,命名空間也是不能自動建立的,如果我們要使用,隻能找到負責這塊的同僚幫忙建立了。而我最近做的功能:廣播中心和會員導出都使用到 Pulsar,但是不想麻煩同僚,而且我經常會在家裡幹活,是以
最後直接到官網上面,找了本地如何部署。
官網介紹了多種部署方式:有利用壓縮包的,Docker的,Kubernetes的。當然了,有 Docker 的部署方式,我們必須使用 Docker 的部署方式,反正拉取一個鏡像啟動一個容器就直接搞定了,相當的友善。
下面上指令:
docker run -it -d -p 6650:6650 -p 8080:8080 -v data -v conf --name=mypulsar apachepulsar/pulsar:2. bin/pulsar standalone
指令是非常的簡單:将 pulsar 的 6650和8080端口号開放出來,并綁定到主控端對應的端口号,這樣我們能直接通路主控端 ip:port 就能通路到容器。接着,将 Pulsar 的 data 和 conf 挂載到主控端中,這樣資料就不會丢失了。接着利用 pulsar standalone 指令啟動一個單機版的 Pulsar。
接着,不管我們是需要建立租戶或者命名空間,直接進入容器中建立即可。
進入容器:
docker exec -it mypulsar /bin/bash
關于tenant 和 namespace 的增删改查指令:
## 1 租戶
#檢視有哪些租戶(public 是系統預設的租戶)
pulsar-admin tenants list
##建立租戶
pulsar-admin tenants create my-tenant
#删除租戶
pulsar-admin tenants delete my-tenant
## 2 命名空間
#檢視指定租戶下邊的命名空間
pulsar-admin namespaces list my-tenant
#建立指定租戶命名空間
pulsar-admin namespaces create my-tenant/my-namespace
#删除指定租戶命名空間
pulsar-admin namespaces delete my-tenant/my-namespace
B、Redis:
Redis 一般我們生産的架構都是使用 Cluster 的,但是如果是自己部署一套叢集的 Redis,是相當的麻煩,以前我自己也寫過文章:Linux 部署 Redis 叢集
如果使用 Docker 的話,會非常的簡單。
1 自定義網絡
建立 Redis 的專屬網絡
Redis 叢集的各個節點公用一個專屬網絡,節點間就可以互相通路了,而避免了每個節點之間都要用 --link 去做網絡互通。
docker network create 指令建立的網絡預設是 bridge 模式。
[email protected] ~ % docker network create redis-net --subnet
5001355940f43474d59f5cb2d78e4e9eeb0a9827e53d8f9e5b55e7d3c5285a09
[email protected] ~ % docker network list
NETWORK ID NAME DRIVER SCOPE
4d88d473e947 bridge bridge local
79a915fafbb5 host host local
f56e362d3c68 none null local
5001355940f4 redis-net bridge local
[email protected] ~ %
1.2 檢視自定義網絡詳情
我們可以利用指令 docker network inspect redis-net 來檢視自定義網絡的詳情,可以看到現在網絡裡面是沒有容器的。
[email protected] mydata % docker network inspect redis-net
[
{
"Name": "redis-net",
"Id": "aed8340bbf8ab86cedc1d990eb7612854ba2b0bd4eae0f978ff95eadc3dbcf65",
"Created": "2020-10-22T08:46:",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": ""
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
2 開始部署
建立六個Redis節點的配置
for port in $(seq 1 6); \
do \
mkdir -p /Users/winfun/mydata/redis/node-${port}/conf
touch /Users/winfun/mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/Users/winfun/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.26.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
2.2 啟動容器
for port in $(seq 1 6); \
do \
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /Users/winfun/mydata/redis/node-${port}/data:/data \
-v /Users/winfun/mydata/redis/node-${port}/conf/ \
-d --net redis-net --ip 172.26.0.1${port} redis: redis-server /etc/redis/redis.conf
done
2.3 檢視啟動成功的6個容器
[email protected] mydata % docker ps | grep redis
ed5972e988e8 redis: "…" 11 seconds ago Up 10 seconds 0.0.0.0:6376->6379/tcp, 0.0.0.0:16376->16379/tcp redis-6
61cd467bc803 redis: "…" 12 seconds ago Up 11 seconds 0.0.0.0:6375->6379/tcp, 0.0.0.0:16375->16379/tcp redis-5
113943ba6586 redis: "…" 12 seconds ago Up 11 seconds 0.0.0.0:6374->6379/tcp, 0.0.0.0:16374->16379/tcp redis-4
5fc3c838851c redis: "…" 13 seconds ago Up 12 seconds 0.0.0.0:6373->6379/tcp, 0.0.0.0:16373->16379/tcp redis-3
f7d4430f752b redis: "…" 13 seconds ago Up 12 seconds 0.0.0.0:6372->6379/tcp, 0.0.0.0:16372->16379/tcp redis-2
bd3e4a593427 redis: "…" 14 seconds ago Up 13 seconds 0.0.0.0:6371->6379/tcp, 0.0.0.0:16371->16379/tcp redis-1
3 再檢視網絡
檢視網絡中的容器
我們上面啟動容器時,都指定使用 redis-net 網絡,是以我們可以先看看 redis-net 網絡的資訊:
我們可以看到 “Containers”包含了我們啟動的6個容器,也就是6個redis 節點。
[email protected] mydata % docker network inspect redis-net
[
{
"Name": "redis-net",
"Id": "aed8340bbf8ab86cedc1d990eb7612854ba2b0bd4eae0f978ff95eadc3dbcf65",
"Created": "2020-10-22T08:46:",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": ""
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"113943ba6586a4ac21d1c068b0535d5b4ef37da50141d648d30dab47eb47d3af": {
"Name": "redis-4",
"EndpointID": "3fe3b4655f39f90ee4daf384254d3f7548cddd19c384e0a26edb6a32545e5b30",
"MacAddress": "02:42:ac:1a:00:0e",
"IPv4Address": "",
"IPv6Address": ""
},
"5fc3c838851c0ca2f629457bc3551135567b4e9fb155943711e07a91ebe9827f": {
"Name": "redis-3",
"EndpointID": "edd826ca267714bea6bfddd8c5d6a5f3c71c50bd50381751ec40e9f8e8160dce",
"MacAddress": "02:42:ac:1a:00:0d",
"IPv4Address": "",
"IPv6Address": ""
},
"61cd467bc8030c4db9a4404b718c5c927869bed71609bec91e17ff0da705ae26": {
"Name": "redis-5",
"EndpointID": "7612c44ab2479ab62341eba2e30ab26f4c523ccbe1aa357fc8b7c17a368dba61",
"MacAddress": "02:42:ac:1a:00:0f",
"IPv4Address": "",
"IPv6Address": ""
},
"bd3e4a593427aab4750358330014422500755552c8b470f0fd7c1e88221db984": {
"Name": "redis-1",
"EndpointID": "400153b712859c5c17d99708586f30013bb28236ba0dead516cf3d01ea071909",
"MacAddress": "02:42:ac:1a:00:0b",
"IPv4Address": "",
"IPv6Address": ""
},
"ed5972e988e8301179249f6f9e82c8f9bb4ed801213fe49af9d3f31cbbe00db7": {
"Name": "redis-6",
"EndpointID": "b525b7bbdd0b0150f66b87d55e0a8f1208e113e7d1d421d1a0cca73dbb0c1e47",
"MacAddress": "02:42:ac:1a:00:10",
"IPv4Address": "",
"IPv6Address": ""
},
"f7d4430f752b5485c5a90f0dc6d1d9a826d782284b1badbd203c12353191bc57": {
"Name": "redis-2",
"EndpointID": "cbdc77cecda1c8d80f566bcc3113f37c1a7983190dbd7ac2e9a56f6b7e4fb21f",
"MacAddress": "02:42:ac:1a:00:0c",
"IPv4Address": "",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
檢視容器間是否能網絡互通
我們還可以嘗試利用 redis-1 來對 redis-2 執行 ping 指令,看看網絡是否可以互通:
[email protected] mydata % docker exec -it redis-1 ping redis-2
PING redis-2 (): 56 data bytes
64 bytes from : seq=0 ttl=64 time=0.136 ms
64 bytes from : seq=1 ttl=64 time=0.190 ms
64 bytes from : seq=2 ttl=64 time=0.483 ms
^C
--- redis-2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.136/0.269/0.483 ms
4 建立叢集
利用 redis-cli 指令建立叢集:
[email protected] conf % docker exec -it redis-1 /bin/bash
OCI runtime exec failed: exec failed: container_linux.go:349: starting container process caused "exec: \"/bin/bash\": stat /bin/bash: no such file or directory": unknown
# 隻能使用sh,redis 的鏡像沒有 bash
[email protected] mydata % docker exec -it redis-1 /bin/sh
/data # cd /usr/local/bin/
/usr/local/bin # redis-cli --cluster create 172.26.0.11:6379 :6379 17
2.26.0.13:6379 172.26.0.14:6379 172.26.0.15:6379 172.26.0.16:6379 --cluster-repl
icas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.26.0.15:6379 to 172.26.0.11:6379
Adding replica 172.26.0.16:6379 to :6379
Adding replica 172.26.0.14:6379 to :6379
M: 6de9e9eef91dbae773d8ee1d629c87e1e7e19b82 172.26.0.11:6379
slots:[0-5460] (5461 slots) master
M: 43e173849bed74f5bd389f9b272ecf0399ae448f :6379
slots:[5461-10922] (5462 slots) master
M: 1e504dc62b7ccc426d513983ca061d1657532fb6 :6379
slots:[10923-16383] (5461 slots) master
S: 92b95f18226903349fb860262d2fe6932d5a8dc2 172.26.0.14:6379
replicates 1e504dc62b7ccc426d513983ca061d1657532fb6
S: 7e5116ba9ee7bb70a68f4277efcbbbb3dcfd18af 172.26.0.15:6379
replicates 6de9e9eef91dbae773d8ee1d629c87e1e7e19b82
S: 203e3e33b9f4233b58028289d0ad2dd56e7dfe45 172.26.0.16:6379
replicates 43e173849bed74f5bd389f9b272ecf0399ae448f
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 172.26.0.11:6379)
M: 6de9e9eef91dbae773d8ee1d629c87e1e7e19b82 172.26.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 92b95f18226903349fb860262d2fe6932d5a8dc2 172.26.0.14:6379
slots: (0 slots) slave
replicates 1e504dc62b7ccc426d513983ca061d1657532fb6
S: 203e3e33b9f4233b58028289d0ad2dd56e7dfe45 172.26.0.16:6379
slots: (0 slots) slave
replicates 43e173849bed74f5bd389f9b272ecf0399ae448f
M: 1e504dc62b7ccc426d513983ca061d1657532fb6 :6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 7e5116ba9ee7bb70a68f4277efcbbbb3dcfd18af 172.26.0.15:6379
slots: (0 slots) slave
replicates 6de9e9eef91dbae773d8ee1d629c87e1e7e19b82
M: 43e173849bed74f5bd389f9b272ecf0399ae448f :6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
4.2 利用 redis-cli 連接配接目前節點,檢視叢集資訊:
/usr/local/bin # redis-cli -c
:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:91
cluster_stats_messages_pong_sent:95
cluster_stats_messages_sent:186
cluster_stats_messages_ping_received:90
cluster_stats_messages_pong_received:91
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:186
4.3 嘗試增加一個key
# 設定一個key,傳回提示這個key配置設定到槽[12539],對應的是節點 redis-3[1968.0.13]
:6379> set key hello
-> Redirected to slot [12539] located at :6379
OK
# 并且會将 redis-cli 切換到節點 redis-3[]
:6379>
5 測試
至此,應該可以說,我們已經成功利用 Docker 在本地部署了一套 Redis 叢集。
那麼接下來,我們會直接在代碼中測試這Redis 是否可以使用。
/**
* 測試Redis叢集
* @author winfun
* @date 2020/10/21 5:48 下午
**/
public class TestCluster {
public static void main(String[] args) throws Exception{
Set<HostAndPort> nodes = new HashSet<>(3);
(new HostAndPort("",6371));
(new HostAndPort("",6372));
(new HostAndPort("",6373));
JedisCluster cluster = new JedisCluster(nodes);
String value = ("key");
("get: key is key,value is "+value);
String result = ("key2","hello world");
("set: key is key2,result is "+result);
();
}
}
但是結果是不如意的,傳回的是一個異常:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5COhlDO4UGMjNzM4QTNlJWNiZDN1MjNwMDZ4MGZwczMx8CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
從這裡可以猜測到,估計是沒法連接配接到我們在 Docker 中部署的 Redis 叢集。
是以在下次調試的時候,我就看看 JedisCluster 拿到的叢集節點的資訊是怎麼樣的。
如下圖:
我們可以看到,即使我們給 JedisCluster 配置的寫的是本地 IP 和映射好的 Port。
但是沒想到,JedisCluster 自己又拿裡一遍叢集的中繼資料,此時候的節點的IP都是自定義網絡 redis-net 配置設定的子網了,主控端可能就走不通了(關于這個問題,我們可以考慮使用 host 類型的自定義網絡)。
6 應用也部署到自定義網絡中
那麼怎麼測試呢?
我下面将自己編寫一個簡單的 SpringBoot 項目,然後利用 Dockerfile 根據項目生成的 jar 包建構成一個鏡像,然後利用 Docker 部署起來,并且将部署後的容器加入到自定義網絡 redis-net 中,最後進行測試。
建立 SpringBoot 項目
配置如下:
6. pom.xml:
主要引入了 web 和 redis 的 starter。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
.2 項目的 application.properties :
server.port=8080
# 這裡我們也可以直接寫容器名,因為應用會部署到 Redis 叢集的同一個網絡中。
#spring.redis.cluster.nodes=redis-1:6379,redis-2:6379,redis-3:6379
spring.redis.cluster.nodes=172.26.0.11:6379,:6379,:6379
.3 Controller如下:
/**
* RedisCluster 測試
* @author winfun
* @date 2020/10/22 3:19 下午
**/
@RequestMapping("/redisCluster")
@RestController
public class RedisClusterController {
@Autowired
private StringRedisTemplate redisTemplate;
/***
* String:根據 key 擷取 value
* @author winfun
* @param key key
* @return {@link String }
**/
@GetMapping("/get/{key}")
public String get(@PathVariable("key") String key){
return ().get(key);
}
/***
* String:設定 key/value 對
* @author winfun
* @param key key
* @param value value
* @return {@link String }
**/
@GetMapping("/set/{key}/{value}")
public String set(@PathVariable("key") String key,@PathVariable("value") String value){
().set(key,value);
return "success";
}
}
将項目打包并生成鏡像
6. 将項目打包成 Jar 包
.2 生成鏡像
編寫 Dockerfile 檔案:
FROM java:8
MAINTAINER winfun
# jar 名稱為項目打包後的 jar
ADD redis-cluster-test-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar",""]
接着去到 Dockerfile 目前目錄,執行下面指令:
[email protected] redis-cluster-test % docker build -t winfun/rediscluster .
Sending build context to Docker daemon 25.84MB
Step 1/5 : FROM java:8
8: Pulling from library/java
5040bd298390: Pull complete
fce5728aad85: Pull complete
76610ec20bf5: Pull complete
60170fec2151: Pull complete
e98f73de8f0d: Pull complete
11f7af24ed9c: Pull complete
49e2d6393f32: Pull complete
bb9cdec9c7f3: Pull complete
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:8
---> d23bdf5b1b1b
Step 2/5 : MAINTAINER winfun
---> Running in a99086ed7e68
Removing intermediate container a99086ed7e68
---> f713578122fc
Step 3/5 : ADD redis-cluster-test-0.0.1-SNAPSHOT.jar
---> 12ca98d789b8
Step 4/5 : EXPOSE 8080
---> Running in 833a06f2dd32
Removing intermediate container 833a06f2dd32
---> 82f4e078510d
Step 5/5 : ENTRYPOINT ["java","-jar",""]
---> Running in 517a1ea7f138
Removing intermediate container 517a1ea7f138
---> ed8a66ef4eb9
Successfully built ed8a66ef4eb9
Successfully tagged winfun/rediscluster:latest
6.3 啟動容器,進行測試
6. 啟動容器,并将容器加入上面建立的自定義網絡 redis-net
建構後,我們可以利用
docker ps
指令看看我們的鏡像:
[email protected] ~ % docker images | grep rediscluster
winfun/rediscluster latest ed8a66ef4eb9 52 minutes ago 669MB
利用
docker run
指令運作此鏡像啟動一個容器:
[email protected] ~ % docker run -it -d -p 8787:8080 --name myrediscluster winfun/rediscluster
705998330f7e6941f5f96d187050d29c4a59f1b16348ebeb5ab0dbc6a1cd63e1
利用
docker network connect
将這個容器加入到上面的自定義網絡 redis-net 中:
winfu[email protected] ~ % docker network connect redis-net myrediscluster
當我們再檢視自定義網絡 redis-net 的詳情,我們可以在 Containers 中找到 myrediscluster 這個容器,并且還給這個容器配置設定了自定義網絡 redis-net 的一個 IP 位址。
如下圖:
6. 我們此時可以直接到浏覽器調用 RedisClusterController的接口:
設定一個 key/value:
根據key擷取value:
從上面可以看到,已經完全沒問題了。
但是呢,這樣我們每次測接口,都需要重新建構鏡像然後部署。
6.4 bridge 和 host 模式
我們都知道,上面我們建立的自定義網絡 redis-net 的模式是橋接模式,也就是 bridge。
他的最大特點是将 Docker 中容器的網絡和主控端隔離開來,容器的IP和主控端的IP是不通的,是以在上面利用 JedisCluster 來操作 Redis 叢集時,當 JedisCluster 擷取到叢集的節點資訊是 Docker 中容器的 IP 時,是通路不通的。
是以解決這個問題,其實我們可以利用 host 模式,它的原理其實就是容器共享主控端的網絡環境。這樣的話,JedisCluster 通路 Redis 叢集應該就沒問題了。
對,就是應該,因為我自己嘗試了很多遍 host 模式下的 Redis 叢集部署,部署和利用 redis-cli 指令操作都是沒問題的。但是,當利用 JedisCluster 通路叢集時,連叢集節點的資訊都沒拿到!!
是以需要大家自己去嘗試一下,具體可參考下面的文章:
https://www.cnblogs.com/niceyoo/p/13011626.html
7 最後
到此,我相信大家都體驗了一波 Docker 的強大。在開發時利用好,簡直就是開發的神器。可以在本地利用最少的系統資源,輕松地去搭建一套完整的開發環境,包括各種中間件。
到此這篇關于Docker 部署單機版 Pulsar 和叢集架構 Redis(開發神器)的文章就介紹到這了,更多相關Docker 部署Redis叢集内容請搜尋腳本之家以前的文章或繼續浏覽下面的相關文章希望大家以後多多支援腳本之家!