Docker 圖形化界面工具 Portainer 優(yōu)秀實(shí)踐
為了方便管理docker容器,本片文章筆者就來推薦一個(gè)筆者最常用的docker圖形化管理工具——Portainer。
一、安裝Portainer
1. 編寫docker-compose文件
Portainer部署的步驟比較簡(jiǎn)單,我們還是以docker-compose文件的形式完成Portainer的安裝,首先我們需要需要編寫的名為portainer.yml的文件,內(nèi)容如下:
version: "3"
services:
portainer:
image: portainer/portainer:latest
container_name: portainer
ports:
- "9000:9000"
volumes:
- /app/portainer/data:/data
- /var/run/docker.sock:/var/run/docker.sock
完成后,我們將這個(gè)文件上傳到我們的服務(wù)器即。
2. 初始化并配置容器
將配置文件上傳之后我們就可以嘗試啟動(dòng)了,為了方便查看服務(wù)是否可以正常啟動(dòng),我們還先以前臺(tái)運(yùn)行的方式啟動(dòng),到達(dá)portainer.yml文件位置,鍵入前臺(tái)啟動(dòng)命令
docker-compose -f portainer.yml up
當(dāng)控制臺(tái)沒有輸出錯(cuò)誤,并顯示正常啟動(dòng)后,我們分開服務(wù)器的9000端口,瀏覽器輸入服務(wù)器ip:9000,j即可進(jìn)入Portainer初始化界面,它會(huì)要求我們配置用戶名密碼:
完成賬戶密碼初始化之后,我們點(diǎn)擊local進(jìn)行服務(wù)器本地容器管理
此時(shí)我們就會(huì)看到local中docker的基本信息了
在步入local,我們就可以按需管理容器、鏡像、網(wǎng)絡(luò)等配置信息。我們不妨點(diǎn)擊containers查看我們服務(wù)器中當(dāng)前運(yùn)行的docker容器。
可以看到我們docker中容器的運(yùn)行情況,由于portainer涉及操作很多,筆者這里就不一一演示了,感興趣的讀者可以自行查閱portainer官方文檔https://www.portainer.io/take-5
到上述步驟我們已經(jīng)可以確定portainer安裝基本成功了,我們可以回到服務(wù)器將portainer后臺(tái)啟動(dòng),正式使用portainer了。
docker-compose -f portainer.yml up -d
二、基于Portainer演示redis-sentinel部署
1. 詳解redis應(yīng)用架構(gòu)
讀完上述的介紹可能讀者們對(duì)Portainer還沒有一個(gè)比較清晰的了解,所以筆者在這里就基于一個(gè)redis-sentinel的部署介紹一下Portainer的日常操作步驟。
首先我們來介紹一下本次部署架構(gòu),我們會(huì)基于一臺(tái)服務(wù)器完成一主二從+三個(gè)哨兵的部署架構(gòu),最終效果是:
- 當(dāng)主節(jié)點(diǎn)更新數(shù)據(jù)之后,從節(jié)點(diǎn)數(shù)據(jù)也會(huì)進(jìn)行同步。
- 當(dāng)我們將主節(jié)點(diǎn)手動(dòng)停止之后,哨兵就會(huì)選舉出新的master繼續(xù)進(jìn)行工作。
2. 安裝步驟
(1) 主從復(fù)制部署
首先我們先進(jìn)性主從結(jié)構(gòu)的部署,我們還是基于docker-compose創(chuàng)建一個(gè)名為redis-cluster.yml的文件配置一下主從信息,配置內(nèi)容如下,筆者已將配置的含義都一一注釋,讀者可自行參考修改。需要注意以下兩點(diǎn):
安全起見,建議讀者盡可能不要使用6379作為對(duì)外暴露的端口號(hào),就算使用6379也盡可能設(shè)置一個(gè)安全的密碼,避免被人下挖礦程序。
當(dāng)讀者復(fù)制下面的文件在服務(wù)器啟動(dòng)時(shí)可能會(huì)報(bào)各種語(yǔ)法錯(cuò)誤,所以筆者推薦的yaml格式化在地址,讀者完成配置后可以將配置粘貼到這個(gè)網(wǎng)站完成自動(dòng)格式化。,這樣可以避免啟動(dòng)時(shí)報(bào)一些沒必要的錯(cuò)誤YAML格式化/美化
version: '3'
services:
# 主節(jié)點(diǎn)
master:
image: redis
# 主節(jié)點(diǎn)名稱
container_name: redis-master
# 設(shè)置redis登錄密碼、從節(jié)點(diǎn)連接主節(jié)點(diǎn)的密碼
command: redis-server --requirepass xxxx--masterauth xxxx
ports:
# 對(duì)外暴露端口號(hào)為16379
- 16379:6379
# 從節(jié)點(diǎn)
slave1:
image: redis
container_name: redis-slave-1
ports:
# 對(duì)外暴露端口號(hào)為16380
- 16380:6379
# 啟動(dòng)redis 從屬于容器名為 redis-master的redis,端口號(hào)為容器端口號(hào)而不是對(duì)外映射端口號(hào),設(shè)置連接密碼,連接主節(jié)點(diǎn)的密碼
command: redis-server --slaveof redis-master 6379 --requirepass xxxx--masterauth xxxxx
# 從節(jié)點(diǎn)2
slave2:
image: redis
container_name: redis-slave-2
ports:
- 16381:6379
command: redis-server --slaveof redis-master 6379 --requirepass xxxxx --masterauth xxxxx
完成后我們即可將配置上傳到服務(wù)器并啟動(dòng):
docker-compose -f redis-cluster.yml up
啟動(dòng)完成后就可以在portainer中看到一主二從出現(xiàn)在containers列表中
以master為例,我們不妨點(diǎn)擊log查看master節(jié)點(diǎn)的運(yùn)行情況:
從輸出可以看到master沒有輸出任何錯(cuò)誤,說明master正常運(yùn)行了。
在確認(rèn)每個(gè)容器都能正常啟動(dòng)之后,我們不妨嘗試在master節(jié)點(diǎn)寫入一些數(shù)據(jù)測(cè)試一下主從節(jié)點(diǎn)同步功能是否正常。 在沒有portainer之前,我們進(jìn)入docker容器內(nèi)容用的都是使用docker exec -it 容器id bash/sh命令,有了portainer之后,我們進(jìn)入容器的操作就變得非常簡(jiǎn)單了。
回到容器列表,點(diǎn)擊redis-master進(jìn)入容器管理界面:
點(diǎn)擊console,進(jìn)入容器終端連接界面:
點(diǎn)擊connect連接進(jìn)入容器:
然后我們就進(jìn)入的容器內(nèi)部,嘗試使用redis-cli認(rèn)證并設(shè)置一個(gè)kv值。
完成上述步驟后,我們到達(dá)從節(jié)點(diǎn)的容器內(nèi)部查閱數(shù)據(jù)可以發(fā)現(xiàn),主節(jié)點(diǎn)數(shù)據(jù)確實(shí)同步過來了,自此我們主從復(fù)制部署基本完成。
(2) 創(chuàng)建redis-sentinel專用網(wǎng)絡(luò)驅(qū)動(dòng)
為了確保redis-sentinel可以統(tǒng)一管理且和其他容器隔離,我們?cè)诓渴餾entinel之前需要基于Portainer創(chuàng)建一個(gè)自定義的brige網(wǎng)絡(luò)。
什么是brige網(wǎng)絡(luò)呢?其實(shí)docker容器中有以下幾種網(wǎng)絡(luò)驅(qū)動(dòng)類型,不同的網(wǎng)絡(luò)驅(qū)動(dòng)類型可以使得容器和宿主機(jī)有著不同的網(wǎng)絡(luò)關(guān)聯(lián):
- host: 使用docker宿主機(jī)網(wǎng)絡(luò)
- bridge: 該網(wǎng)絡(luò)支持在同一個(gè)宿主機(jī)上的各個(gè)容器實(shí)例之間的通信。bridge網(wǎng)絡(luò)是一個(gè)獨(dú)立的網(wǎng)絡(luò)空間,在網(wǎng)絡(luò)空間內(nèi)部的各個(gè)容器實(shí)例能夠直接通信。
- none: 是一個(gè)完全隔離的自治網(wǎng)絡(luò),甚至與Docker宿主機(jī)的網(wǎng)絡(luò)都不通,必須手工配置網(wǎng)卡后才能夠使用。加入到該網(wǎng)絡(luò)的容器實(shí)例,往往要在后續(xù)設(shè)置中加入到其他的第三方網(wǎng)絡(luò)。
- overlay:該類型的網(wǎng)絡(luò)適用于Docker宿主機(jī)集群中的各個(gè)獨(dú)立的容器實(shí)例之間通信。
- macvlan:該類型的網(wǎng)絡(luò)適用于容器實(shí)例需要與宿主機(jī)的MAC地址直接通信,無需端口映射,也無需NAT,容器實(shí)例的eth0直接與宿主機(jī)的物理網(wǎng)卡通信。容器實(shí)例可以被賦予公共IP,并從宿主機(jī)外部直接訪問。
所以我們這里就希望創(chuàng)建一個(gè)名為redis-sentinel的bridge網(wǎng)絡(luò)將主從和哨兵節(jié)點(diǎn)關(guān)聯(lián)起來,并且和docker中的其他容器隔離開:
所以我們按照點(diǎn)擊network,選擇add network進(jìn)行自定義網(wǎng)絡(luò)創(chuàng)建:
輸入名稱為redis-sentinel,其余默認(rèn),然后直接點(diǎn)擊:
然后直接點(diǎn)擊create the network:
然后我們就可以在network列表中看到我們配置的network,自此網(wǎng)絡(luò)redis專用橋接網(wǎng)絡(luò)配置完成。
重點(diǎn)來了,我們的redis主從節(jié)點(diǎn)現(xiàn)在都處于默認(rèn)的網(wǎng)絡(luò)驅(qū)動(dòng)中,我們必須手動(dòng)將其配置到redis-sentinel網(wǎng)絡(luò)中,當(dāng)然有了portainer,這種操作也不會(huì)很難。 我們只需要點(diǎn)擊容器列表,找到我們的主動(dòng)節(jié)點(diǎn)容器,然后分別進(jìn)入他們的管理列表最下方,找到network選項(xiàng),在network列表中找到redis-sentinel選擇join network即可。
(3) 創(chuàng)建哨兵
接下來就可以配置哨兵節(jié)點(diǎn)了,首先自然是創(chuàng)建一個(gè)名為redis-sentinel.yml的docker-compose文件配置3個(gè)哨兵:
version: '3'
services:
sentinel1:
image: redis
# 容器名稱
container_name: redis-sentinel-1
ports:
# 端口映射
- 26379:26379
# 啟動(dòng)redis哨兵
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
# 哨兵1的sentinel.conf和宿主文件位置映射
- /app/cloud/redis/sentinel/sentinel1.conf:/usr/local/etc/redis/sentinel.conf
sentinel2:
image: redis
container_name: redis-sentinel-2
ports:
- 26380:26379
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- /app/cloud/redis/sentinel/sentinel2.conf:/usr/local/etc/redis/sentinel.conf
sentinel3:
image: redis
container_name: redis-sentinel-3
ports:
- 26381:26379
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- /app/cloud/redis/sentinel/sentinel3.conf:/usr/local/etc/redis/sentinel.conf
# 重點(diǎn),將3個(gè)哨兵加入到redis-sentinel和主從節(jié)點(diǎn)建立聯(lián)系
networks:
default:
external:
name: redis-sentinel
從配置文件中可以看出筆者將哨兵文件和宿主文件進(jìn)行關(guān)聯(lián),所以我們需要按照上面配置在app/cloud/redis/sentinel/文件夾下創(chuàng)建3個(gè)哨兵的配置文件sentinel.conf。
哨兵1的配置文件如下,其余哨兵配置同理,只需按需修改ip和端口號(hào)即可:
port 26379
dir /tmp
# master節(jié)點(diǎn)在bridge網(wǎng)絡(luò)中的ip值
sentinel monitor redis-master 172.20.0.2 6379 2
# master節(jié)點(diǎn)密碼
sentinel auth-pass redis-master xxxxx
sentinel down-after-milliseconds redis-master 30000
sentinel parallel-syncs redis-master 1
sentinel failover-timeout redis-master 180000
sentinel deny-scripts-reconfig yes
可以看到筆者將master節(jié)點(diǎn)命名為redis-master,然后網(wǎng)絡(luò)配置為172.20.0.2這個(gè)值是從哪里來的呢?很簡(jiǎn)單,我們點(diǎn)擊redis-master即redis主節(jié)點(diǎn)那個(gè)容器管理界面,到達(dá)最下方,就可以在ip address一欄看到master節(jié)點(diǎn)的容器ip地址,因?yàn)樯诒?jié)點(diǎn)和主從節(jié)點(diǎn)都處于redis-sentinel這個(gè)網(wǎng)絡(luò)中,所以170.20.0.x這個(gè)網(wǎng)絡(luò)是互通的,在bridge模式下配置這個(gè)ip地址是完全沒有問題的。
完成上述步驟后我們就可以將哨兵啟動(dòng):
docker-compose -f redis-sentinel.yml up
此時(shí)我們點(diǎn)擊任意一個(gè)哨兵節(jié)點(diǎn)都可以看到,哨兵的狀態(tài)信息,自此哨兵也部署完成了。
(4) 測(cè)試可用性
此時(shí)我們就可以測(cè)試哨兵是否正常工作了,我們的測(cè)試用例很簡(jiǎn)單,我們將master關(guān)閉掉,查看哨兵是否會(huì)選舉出新的master頂上。
在測(cè)試前我們首先進(jìn)入master容器輸入info replication查看master容器中的redis是否為主節(jié)點(diǎn):
再查看另外兩個(gè)幾點(diǎn)是否身份是否確屬?gòu)墓?jié)點(diǎn)。
從節(jié)點(diǎn)1控制臺(tái)輸出,確實(shí)屬于從節(jié)點(diǎn):
從節(jié)點(diǎn)2控制臺(tái)信息:
我們通過portainer圖形界面將redis-master關(guān)閉。
點(diǎn)入任意一個(gè)哨兵日志,可以看到它監(jiān)控到主節(jié)點(diǎn)下線,并快速選舉出一個(gè)新的節(jié)點(diǎn)作為master上線:
我們根據(jù)ip地址172.20.0.3定位到是slave2這個(gè)redis,我們進(jìn)入容器查看其身份,確實(shí)變?yōu)閙aster。
自此我們的redis-sentinel部署完成了。
三、基于java應(yīng)用操作容器內(nèi)部的redis實(shí)例
為了保證docker環(huán)境下部署的完整性,接下來我們就希望本地的web應(yīng)用可以通過Redis Sentinel架構(gòu)操作redis。
在Sentinel架構(gòu)下我們操作redis集群可直接通過sentinel節(jié)點(diǎn)操作master,通過訪問sentinel獲取可用的master地址,然后就可以把操作提交到master中。
1. 修改哨兵配置文件
由于操作master節(jié)點(diǎn)需要經(jīng)歷: 向sentinel詢問master地址->根據(jù)sentinel返回信息操作master,這就意味的哨兵返回的master地址信息必須是可訪問的。而我們上文中的sentinel.conf配置的都是docker容器的內(nèi)網(wǎng)地址,我們的web服務(wù)是外網(wǎng)訪問,是無法和內(nèi)網(wǎng)容器連接的。
所以我們必須修改上文關(guān)于哨兵的配置文件sentinel1.conf、sentinel2.conf、sentinel3.conf。
以sentinel1.conf為例,將redis-master的ip地址改為docker配置映射的外網(wǎng)ip,確保返回的master地址信息,對(duì)我們本地服務(wù)是可以訪問的。
port 26379
dir /tmp
# master節(jié)點(diǎn)ip
sentinel monitor redis-master 外網(wǎng)ip 16379 2
# master節(jié)點(diǎn)密碼
sentinel auth-pass redis-master 密碼
sentinel down-after-milliseconds redis-master 30000
sentinel parallel-syncs redis-master 1
sentinel failover-timeout redis-master 180000
sentinel deny-scripts-reconfig yes
完成后到portainer中重啟哨兵:
2. Spring Boot應(yīng)用集成依賴
我們引入Spring Boot關(guān)于Redis的腳手架以及l(fā)ettuce連接池。這里擴(kuò)展一下我們?yōu)槭裁刺砑觢ettuce連接池的依賴,Lettuce 和 Jedis 的都是連接Redis Server的客戶端程序。但是Jedis在實(shí)現(xiàn)上是直連redis server,多線程環(huán)境下非線程安全,除非使用連接池,為每個(gè)Jedis實(shí)例增加物理連接。而Lettuce基于Netty的連接實(shí)例,可以在多個(gè)線程間并發(fā)訪問,且線程安全,滿足多線程環(huán)境下的并發(fā)訪問,同時(shí)它是可伸縮的設(shè)計(jì),一個(gè)連接實(shí)例不夠的情況也可以按需增加連接實(shí)例。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 緩存連接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
3. 注冊(cè)中心配置redis參數(shù)
引入redis-starter配置之后,我們就可以配置Redis了。大部分讀者配置這些參數(shù)可能都是直接cv網(wǎng)上配置進(jìn)行修改,其實(shí)在spring boot項(xiàng)目中,由于starter的自動(dòng)裝配機(jī)制,我們完全可以通過源碼得出需要配置的內(nèi)容。
從上文依賴中已經(jīng)表明筆者需要用到lettuce 操作Redis客戶端,所以我們可以到spring boot自動(dòng)配置包中找到關(guān)于lettuce pool的配置類LettuceConnectionConfiguration。
在LettuceConnectionConfiguration找到關(guān)于客戶端連接配置的Bean方法,可以看到其配置獲取順序是:sentinel(哨兵)->cluster(集群)->Standalone(單機(jī))
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public LettuceConnectionFactory redisConnectionFactory(ClientResources clientResources)
throws UnknownHostException {
....略
//創(chuàng)建連接工廠
return createLettuceConnectionFactory(clientConfig);
}
private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
//有sentinel則創(chuàng)建sentinel配置工廠
if (getSentinelConfig() != null) {
returnnew LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
}
//有cluster配置則取cluster創(chuàng)建cluster連接工廠
if (getClusterConfiguration() != null) {
returnnew LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
}
//默認(rèn)配置
returnnew LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}
我們不如getSentinelConfig()方法細(xì)節(jié)中即可定位到sentinel配置的對(duì)象RedisSentinelConfiguration,然后就可以根據(jù)每個(gè)成員變量的調(diào)用定位到該配置文件文件的配置格式:
以master為例,通過對(duì)RedisSentinelConfiguration全局搜索master的引用處定位到了下面這段代碼,由此可知redis配置中的master與配置文件中的spring.redis.sentinel.master相關(guān)聯(lián)。
private static Map<String, Object> asMap(String master, Set<String> sentinelHostAndPorts) {
........略
Map<String, Object> map = new HashMap<>();
//spring.redis.sentinel.master 決定master的值
map.put(REDIS_SENTINEL_MASTER_CONFIG_PROPERTY, master);
........略
return map;
}
經(jīng)過分析總結(jié)得出下面這段配置,筆者這里為了測(cè)試方便就配置了一個(gè)哨兵,需要注意的是如果哨兵和主從節(jié)點(diǎn)有密碼則配置中必須添加關(guān)于password的配置。
spring:
redis:
# master有密碼則需要配置master認(rèn)證面膜
password: xxxxx
sentinel:
master: redis-master
# 多個(gè)節(jié)點(diǎn)用,分開,例如節(jié)點(diǎn)1ip:26379,節(jié)點(diǎn)2ip:26379
nodes: 節(jié)點(diǎn)ip:26379
password: xxxx
# lettuce連接池配置信息
lettuce:
pool:
max-idle: 10
max-active: 20
min-idle: 5
max-wait: 10000ms
4. 編碼測(cè)試
我們?cè)贑ontroller中注入RedisTemplate 。
@Autowired
private RedisTemplate redisTemplate;
然后編寫寫入和讀取的類,接下來就可以將服務(wù)啟動(dòng)測(cè)試了,這里筆者為了測(cè)試為了能夠快速測(cè)試可用性,編寫了一個(gè)簡(jiǎn)單的RedisStrDto 對(duì)象用于存儲(chǔ)string類型。
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class RedisStrDto {
String key;
String value;
}
然后在controller中編寫存取兩個(gè)接口。
@PostMapping("setKey")
public ResultData<String> setKey(@RequestBody RedisStrDto redisStrDto) {
redisTemplate.opsForValue().set(redisStrDto.getKey(),redisStrDto.getValue());
return ResultData.success("success");
}
@GetMapping("getKey/{key}")
public ResultData<Object> getKey(@PathVariable(value = "key") String key) {
return ResultData.success(redisTemplate.opsForValue().get(key));
}
然后我們就可以基于自己的測(cè)試工具進(jìn)行請(qǐng)求測(cè)試了,首先先通過setKey插入數(shù)據(jù)到redis中。
為了查看數(shù)據(jù)是否成功插入,這里筆者直接通過接口查詢,可以看到上一個(gè)接口插入的數(shù)據(jù)確實(shí)返回了,由此可知服務(wù)集成redis成功。
小結(jié)
本文以一個(gè)比較簡(jiǎn)單哨兵架構(gòu)的部署和應(yīng)用服務(wù)接入演示了一下如何基于docker portainer管理和操作docker容器,希望對(duì)你有幫助。