單機(jī)下用Redis二進(jìn)制程序包搭建Redis集群的案例很多,用docker在單節(jié)點上搭集群的也很多撼短,但是在k8s下單節(jié)點搭集群的就很少了平匈,有的只是掛載一個臨時目錄,數(shù)據(jù)無法持久化,pod銷毀后吧碾,數(shù)據(jù)就沒了凰盔。在k8s環(huán)境下測試機(jī)又不夠的情況下使用Redis集群就不太方便了,本文就是筆者的根據(jù)自身需要實踐出來的倦春,期間也找了很多網(wǎng)上資料户敬,最后自己綜合琢磨出的解決方案,由于對docker和k8s不是很精通睁本,可能有其他更簡單的方案尿庐,歡迎大家交流。
一呢堰、所需的背景知識
1抄瑟、對docker,k8s有一定使用經(jīng)驗:會編寫yaml文件枉疼,知道如何排查pod不能running的問題
2皮假、在單機(jī)環(huán)境下搭建過Redis集群
3、如果以上的知識都不知道也沒關(guān)系骂维,本文盡量保證你按照步驟執(zhí)行不出錯惹资。但是k8s環(huán)境你必須得有,基本的linux命令知識得有
二航闺、軟件版本
k8s v1.10.2
docker 18.03.1-ce, build 9ee9f40
Redis5.0.5
三褪测、準(zhǔn)備工作
docker pull redis:5.0.5
mkdir redis-cluster
cd redis-cluster/
mkdir data
創(chuàng)建多個節(jié)點的數(shù)據(jù)存儲目錄,避免Redis實例啟動時配置文件沖突導(dǎo)致無法啟動潦刃,在單臺物理機(jī)上搭建過Redis集群的應(yīng)該知道侮措。建好6個節(jié)點的目錄,后面會用到乖杠,這里的目錄要注意權(quán)限問題萝毛,k8s啟動的pod需要讀寫這里的文件夾
for port in `seq 7001 7006`; do \
mkdir -p ./${port}/
done
創(chuàng)建完成后目錄結(jié)構(gòu)
四、編輯yaml文件
創(chuàng)建k8s的yaml文件
回到redis-cluster目錄下
1滑黔、創(chuàng)建ConfigMap
先創(chuàng)建redis.conf配置文件
vi redis-cm.yaml
將以下內(nèi)容貼到文件中笆包,保存退出
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-conf
data:
redis.conf: |
appendonly yes
cluster-enabled yes
cluster-config-file /var/lib/redis/nodes.conf
cluster-node-timeout 5000
dir /var/lib/redis
port 6379
2环揽、創(chuàng)建statefulset
vi redis-statefulset.yaml
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: redis-app
spec:
serviceName: "redis-service"
replicas: 6
template:
metadata:
labels:
app: redis
appCluster: redis-cluster
spec:
nodeSelector:
node: mfc # 這里需要根據(jù)自己的k8s節(jié)點情況修改,本案例需修改成node-222
terminationGracePeriodSeconds: 20
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname
containers:
- name: redis
image: "redis:latest"
imagePullPolicy: IfNotPresent #默認(rèn)情況是會根據(jù)配置文件中的鏡像地址去拉取鏡像庵佣,配置為本地有鏡像不拉取遠(yuǎn)程歉胶,避免你的環(huán)境不能訪問外網(wǎng),拉取鏡像失敗
command:
- "redis-server"
args:
- "/etc/redis/redis.conf"
- "--protected-mode"
- "no"
resources:
requests:
cpu: "100m"
memory: "100Mi"
ports:
- name: redis
containerPort: 6379
protocol: "TCP"
- name: cluster
containerPort: 16379
protocol: "TCP"
volumeMounts:
- name: "redis-conf"
mountPath: "/etc/redis"
- name: "redis-data"
mountPath: "/var/lib/redis"
volumes:
- name: "redis-conf"
configMap:
name: "redis-conf"
items:
- key: "redis.conf"
path: "redis.conf"
volumeClaimTemplates: #可看作pvc的模板
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
3巴粪、創(chuàng)建pv的yaml文件
vi pv1.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
name: redis-pv-volume1
labels:
type: local
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /home/bboss/redis-cluster/data/7001/
/home/bboss/redis-cluster/data/7001/此目錄為前面我們創(chuàng)建的redis數(shù)據(jù)存儲的6個目錄
將此文件復(fù)制5份通今,依次命名為pv2.yaml 到pv6.yaml
在對應(yīng)的文件中將
name: redis-pv-volume1
path: /home/bboss/redis-cluster/data/7001/
這2處修改為對應(yīng)文件編號的序號,例如pv2.yaml修改為
name: redis-pv-volume2
path: /home/bboss/redis-cluster/data/7002/
五肛根、開始搭建集群
1辫塌、創(chuàng)建cm和pv如下圖
km create -f redis-cm.yaml
km create -f pv1.yaml
2、創(chuàng)建statefulset
確定當(dāng)前宿主機(jī)在k8s集群中的label
kubectl get nodes --show-labels
如圖派哲,我當(dāng)前部署的機(jī)器ip是222對應(yīng)的node標(biāo)簽名為node-222
將redis-statefulset.yaml文件中node: mfc 修改為 node: node-222臼氨,稍后啟動redis的pod是都會選擇在node-222這個節(jié)點的機(jī)器上啟動,這正是我們需要的芭届,修改正確后
km create -f redis-statefulset.yaml
[注:我這里km是做了配置储矩,km相當(dāng)于是kubectl -n mfc-namespace 這是指定k8s的namespace的,我的namespace名是mfc-namespace褂乍,kubectl命令不指定-n 默認(rèn)是使用default為名的namespace]
kubectl get pods -n mfc-namespace | grep redis
查看6個redis節(jié)點是否都啟動完成
查看一下pod對應(yīng)的ip持隧,下一步配置集群需要知道各節(jié)點的ip,這ip是k8s分配的
kubectl get pods -o wide -n mfc-namespace | grep redis
驗證一下pvc是否都bound了
kubectl get pvc -o wide -n mfc-namespace
3逃片、初始化Redis集群
啟動一個獨立的redis
docker run -it redis:latest bash
redis-cli --cluster create 10.254.79.20:6379 10.254.79.21:6379 10.254.79.22:6379 10.254.79.23:6379 10.254.79.24:6379 10.254.79.25:6379 --cluster-replicas 1
4屡拨、驗證Redis集群
日志顯示集群已經(jīng)初始化好幢哨,slots也分配完成承耿,驗證一下集群
用redis-cli隨便連接某個redis節(jié)點
/usr/local/bin/redis-cli -c -h 10.254.79.20 -p 6379
搭建完成了,下面來看看本地存儲的數(shù)據(jù)
隨便進(jìn)到某個目錄下看看,如下圖redis集群的數(shù)據(jù)已經(jīng)落到物理機(jī)上了缕棵。
驗證一下pod重啟后集群數(shù)據(jù)是否還在
看ip還是原來的ip性锭,這就是k8s定義statefulset的作用
通過剛才啟動的獨立redis再次連接集群看數(shù)據(jù)還在不在赠潦,如下圖數(shù)據(jù)都還在
5、創(chuàng)建headlessService
到這里我們的Redis集群已經(jīng)是完全可用的了草冈,但是我們的應(yīng)用要使用該集群時配置ip不好記她奥,這時就需要用到k8s中的Service
vi redis-headlessService.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-service
labels:
app: redis
spec:
ports:
- name: redis-port
port: 6379
clusterIP: None
selector:
app: redis
appCluster: redis-cluster
執(zhí)行 km create -f redis-headlessService.yaml 創(chuàng)建服務(wù)
查看服務(wù)
另外用k8s啟一個有nslookup或者ping工具的pod,驗證一下服務(wù)名是否可以訪問
k8s是自帶dns功能的怎棱,k8s會根據(jù)serviceName生成對應(yīng)的域名
如上圖哩俭,可以看到節(jié)點1 (10.254.79.20)的完整域名是redis-app-0.redis-service.mfc-namespace.svc.cluster.local,在k8s中同一個namespace的服務(wù)只需要前面的子域名即可拳恋,后面的會默認(rèn)補齊凡资,本例中的serviceName是redis-app-0.redis-service,這是一個節(jié)點的服務(wù)名。對于整個集群的serviceName是redis-service
用剛才的docker啟動的redis驗證一下域名是否可以連接,如下圖用域名連接失敗隙赁,因為我們這個是用docker啟動的不在k8s中垦藏,用pod的ip是可以連接的,因為pod的ip訪問是走docker的網(wǎng)絡(luò)伞访。因為服務(wù)名是k8s做的路由掂骏,所以必須在k8s集群中注冊才可以使用服務(wù)名互相訪問。serviceName其實是在操作系統(tǒng)中做了一個轉(zhuǎn)發(fā)的動作厚掷,通過iptales做策略實現(xiàn)的弟灼。
用redis集群中的任意一個節(jié)點驗證,域名是可以訪問的
六冒黑、總結(jié)
網(wǎng)上大部分文章都是類似的方案田绑,使用pv,pvc做存儲抡爹,有的pv是綁定的nfs掩驱,我也試過nfs碰到問題沒解決只能想其他辦法,但是我理解的是如果6個redis節(jié)點的配置文件nodes.conf和數(shù)據(jù)文件都在共享一個目錄豁延,那還是會有沖突昙篙,導(dǎo)致redis只能啟動一個節(jié)點腊状,而其他節(jié)點無法啟動诱咏,除非能把文件命名定義成不同節(jié)點對應(yīng)不同的文件名,因為沒搭成功基于nfs的pvc存儲缴挖,不知道是不是每個節(jié)點一個隔離的pvc袋狞,所以無法知道以后會再實踐。所以本例主要是通過定義不同的pv把目錄隔離開映屋,但是定義StatefulSet時不好指定pvc苟鸯,看到有文章用到volumeClaimTemplates(可看作pvc的模板)傳輸門于是問題就應(yīng)然而解了,StatefulSet在創(chuàng)建pod時是前一個創(chuàng)建成功才繼續(xù)下一個是有順序的,同時創(chuàng)建pvc也是有順序的棚点,每個pvc綁定一個pv早处,這樣6個節(jié)點的數(shù)據(jù)文件就自動綁定到各自的目錄下了。
另外redis集群初始化不需要Ruby了瘫析,避免了安裝ruby的麻煩砌梆。
七、參考資料
[https://www.cnblogs.com/tylerzhou/p/11027559.html]
[https://v1-12.docs.kubernetes.io/zh/docs/tasks/run-application/force-delete-stateful-set-pod/]
[https://juejin.im/post/5d206b1e5188252f275fdc95]
[https://www.cnblogs.com/breezey/p/6582082.html]
[https://juejin.im/post/5c989ff2f265da60f206ffe4#heading-7]
[http://www.reibang.com/p/a5172b0eeae4]
https://www.cnblogs.com/xiaochangwei/p/7993065.html