今天讓咱們試着在k8s裏部署一個redis集羣,瞭解更多k8s的細節和特性。node
環境:minikube v0.30 (kubernetes 1.10)python
注:redis-cluster相關的背景知識和細節在此不作贅述,能夠參考以前的文章稍做回顧git
本質上來講,在k8s上部署一個redis集羣和部署一個普通應用沒有什麼太大的區別,但須要注意下面幾個問題:github
REDIS是一個有狀態應用redis
這是部署redis集羣時咱們最須要注意的問題,當咱們把redis以pod的形式部署在k8s中時,每一個pod裏緩存的數據都是不同的,並且pod的IP是會隨時變化,這時候若是使用普通的deployment和service來部署redis-cluster就會出現不少問題,所以須要改用StatefulSet + Headless Service來解決ubuntu
數據持久化api
redis雖然是基於內存的緩存,但仍是須要依賴於磁盤進行數據的持久化,以便服務出現問題重啓時能夠恢復已經緩存的數據。在集羣中,咱們須要使用共享文件系統 + PV(持久卷)的方式來讓整個集羣中的全部pod均可以共享同一份持久化儲存緩存
在開始以前先來詳細介紹一下幾個概念和原理ruby
簡單的說,Headless Service就是沒有指定Cluster IP的Service,相應的,在k8s的dns映射裏,Headless Service的解析結果不是一個Cluster IP,而是它所關聯的全部Pod的IP列表bash
StatefulSet
是k8s中專門用於解決有狀態應用部署的一種資源,總的來講能夠認爲它是Deployment/RC
的一個變種,它有如下幾個特性:
StatefulSet管理的每一個Pod都有惟一的文檔/網絡標識,而且按照數字規律生成,而不是像Deployment中那樣名稱和IP都是隨機的(好比StatefulSet名字爲redis,那麼pod名就是redis-0, redis-1 ...)
StatefulSet中ReplicaSet的啓停順序是嚴格受控的,操做第N個pod必定要等前N-1個執行完才能夠
StatefulSet中的Pod採用穩定的持久化儲存,而且對應的PV不會隨着Pod的刪除而被銷燬
另外須要說明的是,StatefulSet必需要配合Headless Service使用,它會在Headless Service提供的DNS映射上再加一層,最終造成精確到每一個pod的域名映射,格式以下:
$(podname).$(headless service name)
複製代碼
有了這個映射,就能夠在配置集羣時使用域名替代IP,實現有狀態應用集羣的管理
藉助StatefulSet和Headless Service,集羣的部署方案設計以下(圖片來自參考文章):
配置步驟大概羅列以下:
因爲使用的是minikube的單node環境,爲了簡化複雜度,此次先不配置PV和PVC,直接經過普通Volume的方式來掛載數據
先建立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
複製代碼
而後kubectl create configmap redis-conf --from-file=redis.conf
來建立ConfigMap
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
複製代碼
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: redis-app
spec:
serviceName: "redis-service"
replicas: 6
template:
metadata:
labels:
app: redis
appCluster: redis-cluster
spec:
terminationGracePeriodSeconds: 20
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname
containers:
- name: redis
image: "registry.cn-qingdao.aliyuncs.com/gold-faas/gold-redis:1.0"
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"
- name: "redis-data"
emptyDir: {}
複製代碼
StatefulSet建立完畢後,能夠看到6個pod已經啓動了,但這時候整個redis集羣尚未初始化,須要使用官方提供的redis-trib
工具。
咱們固然能夠在任意一個redis節點上運行對應的工具來初始化整個集羣,但這麼作顯然有些不太合適,咱們但願每一個節點的職責儘量地單一,因此最好單獨起一個pod來運行整個集羣的管理工具。
在這裏須要先介紹一下redis-trib
,它是官方提供的redis-cluster管理工具,能夠實現redis集羣的建立、更新等功能,在早期的redis版本中,它是以源碼包裏redis-trib.rb
這個ruby腳本的方式來運做的(pip上也能夠拉到python版本,但我運行失敗),如今(我使用的5.0.3
)已經被官方集成進redis-cli
中。
開始初始化集羣,首先在k8s上建立一個ubuntu的pod,用來做爲管理節點:
kubectl run -i --tty redis-cluster-manager --image=ubuntu --restart=Never /bin/bash
複製代碼
進入pod內部先安裝一些工具,包括wget
,dnsutils
,而後下載和安裝redis:
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
tar -xvzf redis-5.0.3.tar.gz
cd redis-5.0.3.tar.gz && make
複製代碼
編譯完畢後redis-cli
會被放置在src
目錄下,把它放進/usr/local/bin
中方便後續操做
接下來要獲取已經建立好的6個節點的host ip,能夠經過nslookup
結合StatefulSet的域名規則來查找,舉個例子,要查找redis-app-0
這個pod的ip,運行以下命令:
root@redis-cluster-manager:/# nslookup redis-app-0.redis-service
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: redis-app-0.redis-service.gold.svc.cluster.local
Address: 172.17.0.10
複製代碼
172.17.0.10
就是對應的ip。此次部署咱們使用0,1,2做爲Master節點;3,4,5做爲Slave節點,先運行下面的命令來初始化集羣的Master節點:
redis-cli --cluster create 172.17.0.10:6379 172.17.0.11:6379 172.17.0.12:6379
複製代碼
而後給他們分別附加對應的Slave節點,這裏的cluster-master-id
在上一步建立的時候會給出:
redis-cli --cluster add-node 172.17.0.13:6379 172.17.0.10:6379 --cluster-slave --cluster-master-id adf443a4d33c4db2c0d4669d61915ae6faa96b46
複製代碼
redis-cli --cluster add-node 172.17.0.14:6379 172.17.0.11:6379 --cluster-slave --cluster-master-id 6e5adcb56a871a3d78343a38fcdec67be7ae98f8
複製代碼
redis-cli --cluster add-node 172.17.0.16:6379 172.17.0.12:6379 --cluster-slave --cluster-master-id c061e37c5052c22f056fff2a014a9f63c3f47ca0
複製代碼
集羣初始化後,隨意進入一個節點檢查一下集羣信息:
至此,集羣初始化完畢,咱們進入一個節點來試試,注意在集羣模式下redis-cli
必須加上-c
參數纔可以訪問其餘節點上的數據:
如今進入redis集羣中的任意一個節點均可以直接進行操做了,可是爲了可以對集羣其餘的服務提供訪問,還須要創建一個service來實現服務發現和負載均衡(注意這裏的service和咱們以前建立的headless service不是一個東西)
yaml文件以下:
apiVersion: v1
kind: Service
metadata:
name: gold-redis
labels:
app: redis
spec:
ports:
- name: redis-port
protocol: "TCP"
port: 6379
targetPort: 6379
selector:
app: redis
appCluster: redis-cluster
複製代碼
部署完作個測試:
很nice,到這裏全部的工做就完畢了~