摘要:計(jì)劃通過(guò)解決持久化的問(wèn)題通過(guò)帶起個(gè)實(shí)例,它們將有穩(wěn)定的主機(jī)名線上一個(gè)部署單元是個(gè)實(shí)例通過(guò)和注入配置文件和敏感信息因?yàn)榫€上系統(tǒng)的特性,我們底層的實(shí)例是不需要順序啟動(dòng)或停止的,將采用創(chuàng)建集群參考文章,快速創(chuàng)建一個(gè)集群。
緣起
線上有一個(gè) redis 集群,因?yàn)楫?dāng)時(shí) redis 自帶的集群還不成熟,而且我們項(xiàng)目上的需求和應(yīng)用的場(chǎng)景比較簡(jiǎn)單,redis 集群是通過(guò) twemproxy + redis 進(jìn)行搭建的。這個(gè)集群其實(shí)有很多的不足
單節(jié)點(diǎn)雖然設(shè)置了持久化,但是沒(méi)有使用主從模式,沒(méi)有哨兵 sentinel 負(fù)責(zé)主從切換
twemproxy 沒(méi)有 HA,存在單點(diǎn)故障問(wèn)題
集群的伸縮時(shí)(添加節(jié)點(diǎn),刪除節(jié)點(diǎn)),集群中的數(shù)據(jù)不能自動(dòng)平衡
如果需求放在現(xiàn)在,可以使用 reids 3.x 以后自帶的集群特性,另外也可以選用 codis 這類開(kāi)源方案。
正好最近在研究和實(shí)踐 kubernetes,打算嘗試將線上的這個(gè)集群遷移到 kubernetes,畢竟 kubernetes 能夠保證集群的實(shí)際狀態(tài)與用戶的期望一致,特別是線上的環(huán)境是可能出現(xiàn)主機(jī)重啟,多個(gè) redis 實(shí)例宕掉的情況,利用 kubernetes 就能提高集群的可用性。
初步分析了一下,要遷移線上這個(gè)集群,需要使用 statefulset 來(lái)實(shí)現(xiàn),因?yàn)檫@里面
每個(gè) redis 實(shí)例需要持久化,線上都是持久化到自己主機(jī)的某個(gè)目錄,每個(gè)實(shí)例和持久化目錄是緊密耦合的
twemproxy 的配置文件又和每個(gè) redis 實(shí)例的 IP 是緊耦合的,要求 redis 的服務(wù)暴露在穩(wěn)定的地址和端口
于是有了下面的實(shí)驗(yàn)。計(jì)劃
通過(guò) pv/pvc 解決 redis 持久化的問(wèn)題
通過(guò) statefulset 帶起 N 個(gè)實(shí)例,它們將有穩(wěn)定的主機(jī)名(線上一個(gè)部署單元是 108 個(gè) redis 實(shí)例)
通過(guò) configmap 和 secret 注入配置文件和敏感信息
因?yàn)榫€上系統(tǒng)的特性,我們底層的 redis 實(shí)例是不需要順序啟動(dòng)或停止的,podManagementPolicy 將采用 Parallel
創(chuàng)建 kubernetes 集群參考 setting-up-a-kubernetes-cluster-with-vagrant 文章,快速創(chuàng)建一個(gè) kubernetes 集群。實(shí)際上,因?yàn)槲以诠臼褂?windows 操作系統(tǒng),實(shí)際使用的 Vagrantfile 我做了少量的修改。
創(chuàng)建 pv/pvc簡(jiǎn)單起見(jiàn),本次實(shí)驗(yàn)的目的主要是為了驗(yàn)證想法,所以簡(jiǎn)單地使用基于 nfs 的 PV 和 PVC。首先在 kubernetes 的集群的節(jié)點(diǎn)中搭建 nfs 服務(wù)。
# 每個(gè)節(jié)點(diǎn) yum -y install nfs-server nfs-utils rpcbind # 選 node1 提供服務(wù) systemctl enable nfs rpcbind systemctl start nfs rpcbind # 其他節(jié)點(diǎn)開(kāi)啟 systemctl enable rpcbind systemctl start rpcbind # node1 配置 nfs mkdir /root/data vi /etc/exports /root/data 172.17.8.0/24(rw,sync,no_root_squash) # node1 重啟服務(wù),使配置生效 systemctl restart nfs # node1 檢驗(yàn) showmount -e localhost /root/data 172.17.8.0/24 # nodex 檢驗(yàn) mount -t nfs 172.17.8.101:/root/data /mnt
然后創(chuàng)建 pv/pvc
# create pv apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs spec: capacity: storage: 2Gi accessModes: - ReadWriteMany nfs: server: 172.17.8.101 path: "/root/data" # create pvc kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc-nfs spec: accessModes: - ReadWriteMany resources: requests: storage: 2Gi創(chuàng)建 redis 鏡像
本來(lái)沒(méi)想自定義 redis 鏡像,打算直接使用 hub 上的 redis 鏡像,然后用 configmap 注入 redis 的配置 redis.conf,但是只能使用同一個(gè) configmap,這樣 pod 中 redis 持久化的位置會(huì)是同一個(gè)位置,不是期望的。后來(lái)想到可以讓 redis.conf 中的 dir 和 hostname 關(guān)聯(lián),利用每個(gè) pod 的 hostname 不同來(lái)實(shí)現(xiàn)持久化到不同的位置上,按照這個(gè)想法做了2個(gè)實(shí)驗(yàn)
通過(guò) spec 里通過(guò) inti-container 執(zhí)行個(gè) shell 來(lái)修改注入的 redis.conf
通過(guò) sepc.lifecycle 通過(guò) poststart 執(zhí)行個(gè) shell 來(lái)修改注入的 redis.conf
這兩個(gè)想法都沒(méi)有實(shí)驗(yàn)成功。于是打算還是自定義一個(gè) redis 鏡像吧,畢竟這樣會(huì)通用很多。
參考文章 https://www.kubernetes.org.cn... 以及 文章中提到的 https://github.com/kubernetes... 。很受啟發(fā),但是相對(duì)我的實(shí)驗(yàn)?zāi)繕?biāo)都比較復(fù)雜,我只需要一個(gè)簡(jiǎn)單的 redis 鏡像,于是做了一番改造:具體的內(nèi)容放在了 https://github.com/arashicage...
這里主要講一下 run.sh,腳本里通過(guò) statefulset 中 pod 的 hostname 是穩(wěn)定的特定,將其用在了持久化目錄配置里
if [[ ! -e /data/$(hostname) ]]; then echo "Redis data dir doesn"t exist, data won"t be persistent!" mkdir -p /data/$(hostname) fi echo dir /data/$(hostname) >> /usr/local/etc/redis/redis.conf redis-server /usr/local/etc/redis/redis.conf --protected-mode no創(chuàng)建 statefulset
--- apiVersion: v1 kind: Service metadata: name: svc-redis labels: app: redis spec: ports: - port: 6379 name: redis clusterIP: None selector: app: redis --- apiVersion: apps/v1 kind: StatefulSet metadata: name: stateful-redis spec: podManagementPolicy: Parallel serviceName: "redis" replicas: 4 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: arashicage/redis-glibc-slim ports: - containerPort: 6379 name: redis volumeMounts: - name: nfs mountPath: "/data" volumes: - name: nfs persistentVolumeClaim: claimName: pvc-nfs
將上面的清單提交到 kubernetes 集群,等待其創(chuàng)建完成并驗(yàn)證(圖如果看不清,拖到新的標(biāo)簽頁(yè)里看大圖)
然后可以進(jìn) shell 里看看
檢查一下 nfs 目錄,statefulset 成功創(chuàng)建了各個(gè) pod 使用的持久化目錄
[root@node1 ~]# ll /root/data total 0 drwxr-xr-x. 2 root root 28 Apr 17 14:38 stateful-redis-0 drwxr-xr-x. 2 root root 28 Apr 17 14:38 stateful-redis-1 drwxr-xr-x. 2 root root 28 Apr 17 14:38 stateful-redis-2 drwxr-xr-x. 2 root root 28 Apr 17 14:38 stateful-redis-3測(cè)試 redis pod
查看 stateful-redis-x 的 ip 并用 redis-cli 連接測(cè)試
創(chuàng)建 twemproxy 的服務(wù)這一步,因?yàn)?twemproxy 是無(wú)狀態(tài)的打算創(chuàng)建一個(gè) deployment 和一個(gè) service,在 hub 上找了一下,拉取數(shù)量比較多的都從 twemproxy 都從外部的 etcd 中通過(guò) confd 來(lái)獲取 twemproxy 的配置,(我第一次聽(tīng)說(shuō) confd 是從我青云的一個(gè)朋友哪里,他們?cè)?confd 上做了些改造,很不錯(cuò)的軟件),想法很不錯(cuò),但是對(duì)于我目前的實(shí)驗(yàn)加大了難度,我還是找一個(gè)純粹點(diǎn)的 twemproxy 吧。最后選擇了 zapier/twemproxy ,不過(guò)也是4年前的了,使用的 twemproxy 是v0.3.0,最目前最新 v0.4.1 支持 Authentication,而且是用在 aws 云上的,影響實(shí)驗(yàn),本想需要改造一下(去掉了 python 相關(guān)的,去掉了 memcached 相關(guān)的)。后來(lái)找到一個(gè) fblgit/twemproxy-nutcracker 比較貼合自己的需求,但是這個(gè)鏡像也是有問(wèn)題的(Dockerfile 里的 chmod 755 實(shí)際上沒(méi)起作用,運(yùn)行的時(shí)候報(bào) Permission deny,https://github.com/moby/moby/... 另外這個(gè)鏡像將 nutcracker 的配置文件和二進(jìn)制文件都放在了一起 /scripts,我這需要在運(yùn)行的時(shí)候掛載或在 kubernetes 中通過(guò)configmap 注入,也修改了配置文件的位置)。修改后是這樣的
# ref https://hub.docker.com/r/zapier/twemproxy/~/dockerfile/ # ref https://hub.docker.com/r/jgoodall/twemproxy/~/dockerfile/ # ref https://hub.docker.com/r/fblgit/twemproxy-nutcracker/~/dockerfile/ FROM ubuntu:16.04 MAINTAINER [email protected] ENV DEBIAN_FRONTEND=noninteractive ENV VERSION=v0.4.1 RUN apt-get update && DEBIAN_FRONTEND=noninteractive && apt-get install -qy gcc autoconf make libtool binutils wget RUN cd /root && wget https://github.com/twitter/twemproxy/archive/${VERSION}.tar.gz && tar zxf ${VERSION}.tar.gz && cd twemproxy-* && autoreconf -fvi && ./configure --prefix=/usr && make -j4 && make install ADD start.sh /start.sh RUN chmod 755 /start.sh CMD ["/start.sh"]
將文件上傳到 github,通過(guò) hub.docker 的自動(dòng)構(gòu)建,最后拉取下來(lái)進(jìn)行了測(cè)試:
# /root/config/ 包含了 nutcracker.yml 文件,內(nèi)容見(jiàn)后面 docker run -it --name xxx -d -v /root/config/:/usr/local/etc/nutcracker/ docker.io/arashicage/twemproxy:0.4.1
查找容器的 IP 并檢測(cè)服務(wù)是否可用
# 查找 ip docker inspect xxx |grep IPAddress 172.33.96.3 # 檢測(cè) nutcracker 服務(wù) curl 172.33.96.3:22222 {"service":"nutcracker", "source":"34a2f6582378", "version":"0.4.1", "uptime":61, "timestamp":1524019442, "total_connections":2, "curr_connections":1, "alpha": {"client_eof":0, "client_err":0, "client_connections":1, "server_ejects":0, "forward_error":0, "fragments":0, "server0": {"server_eof":0, "server_err":0, "server_timedout":0, "server_connections":0, "server_ejected_at":0, "requests":0, "request_bytes":0, "responses":0, "response_bytes":0, "in_queue":0, "in_queue_bytes":0, "out_queue":0, "out_queue_bytes":0},"server1": {"server_eof":0, "server_err":0, "server_timedout":0, "server_connections":0, "server_ejected_at":0, "requests":0, "request_bytes":0, "responses":0, "response_bytes":0, "in_queue":0, "in_queue_bytes":0, "out_queue":0, "out_queue_bytes":0},"server2": {"server_eof":0, "server_err":0, "server_timedout":0, "server_connections":0, "server_ejected_at":0, "requests":0, "request_bytes":0, "responses":0, "response_bytes":0, "in_queue":0, "in_queue_bytes":0, "out_queue":0, "out_queue_bytes":0},"server3": {"server_eof":0, "server_err":0, "server_timedout":0, "server_connections":0, "server_ejected_at":0, "requests":0, "request_bytes":0, "responses":0, "response_bytes":0, "in_queue":0, "in_queue_bytes":0, "out_queue":0, "out_queue_bytes":0}}}
上面這個(gè)鏡像 nutcracker 的配置文件(參考 nutcracker)路徑是 /usr/local/etc/nutcracker/nutcracker.yml,通過(guò) configmap 來(lái)注入
# nutcracker.yml alpha: listen: 0.0.0.0:22121 hash: fnv1a_64 hash_tag: "{}" distribution: ketama auto_eject_hosts: false timeout: 400 redis: true redis_auth: foobar servers: - stateful-redis-0:6379:1 server0 - stateful-redis-1:6379:1 server1 - stateful-redis-2:6379:1 server2 - stateful-redis-3:6379:1 server3
2018-05-03 補(bǔ)充:上面的 nutcracker.yml 中,應(yīng)當(dāng)使用 svc-redis.stateful-redis-0 的形式。
創(chuàng)建 configmap
mv nutcracker.yml /root/config kubectl create configmap twemproxy-config --from-file=config/ # 結(jié)果 key=nutcracker.yml val=文件內(nèi)容
然后,創(chuàng)建 deployment
--- kind: Service apiVersion: v1 metadata: name: svc-twemproxy spec: selector: app: twemproxy ports: - name: proxy protocol: TCP port: 22121 targetPort: 22121 - name: state protocol: TCP port: 22122 targetPort: 22122 --- apiVersion: apps/v1 kind: Deployment metadata: name: twemproxy-deployment labels: app: twemproxy spec: replicas: 2 selector: matchLabels: app: twemproxy template: metadata: labels: app: twemproxy spec: containers: - name: twemproxy image: arashicage/twemproxy:0.4.1 ports: - containerPort: 22121 name: proxy - containerPort: 22122 name: state volumeMounts: - name: config-volume mountPath: "/usr/local/etc/nutcracker" volumes: - name: config-volume configMap: name: twemproxy-config items: - key: nutcracker.yml path: nutcracker.yml測(cè)試 twemproxy 到 stateful-redis-x
沒(méi)通啊,根據(jù)以往的經(jīng)驗(yàn),說(shuō)明 nutcracker 不能連到后面的 redis 實(shí)例(可能 redis 宕掉,可能主機(jī)宕掉,但現(xiàn)在情況不是這樣),估計(jì)是 nutcracker Pod 的不能通過(guò) stateful-redis-x 解析到正確的地址,驗(yàn)證一下(從 dashboard 的exec 進(jìn)去):
root@twemproxy-deployment-545c7dcbfd-k2h52:/# ping stateful-redis-0 bash: ping: command not found
可惜鏡像里缺少 ping,nlslookup 等實(shí)用工具。只好通過(guò)其他方式了:
# 先拉個(gè) busybox docker pull busybox # 再查一下 twemproxy pod 的容器 id(在stateful-redis-0 的節(jié)點(diǎn)上查,根據(jù) pod 名稱判斷,找 pause 的 id) docker ps -a # 找到 df4af96008ed # 啟動(dòng) busybox 連入 pause 的網(wǎng)絡(luò)空間 docker run -it --name busybox --rm --network:container:df4af96008ed busybox # ping 主機(jī)名不同,ping ip 是通的,ping svc-redis 也是通的 / # ping stateful-redis-0 ping: bad address "stateful-redis-0" / # ping 172.33.57.3 PING 172.33.57.3 (172.33.57.3): 56 data bytes 64 bytes from 172.33.57.3: seq=0 ttl=62 time=1.274 ms / # ping svc-redis PING svc-redis (172.33.57.2): 56 data bytes 64 bytes from 172.33.57.2: seq=0 ttl=62 time=0.965 ms
也就是說(shuō),這里 nutcracker.yml 里不能直接使用 statefulset 的主機(jī)名,因?yàn)闊o(wú)法進(jìn)行域名解析(ping ip 或 svc-redis 能通是因?yàn)?dns 的緣故)。要解決這個(gè)問(wèn)題,需要修改 nutcracker.yml 將它改為 ip 地址。雖然statefulset 的 ip 地址是不變的,但是顯式的設(shè)定感覺(jué)還是不夠通用,回頭通過(guò) confd 來(lái)解決吧。
configmap 修改為
# nutcracker.yml alpha: listen: 0.0.0.0:22121 hash: fnv1a_64 hash_tag: "{}" distribution: ketama auto_eject_hosts: false timeout: 400 redis: true redis_auth: foobar servers: - 172.33.57.3:6379:1 server0 - 172.33.57.2:6379:1 server1 - 172.33.96.2:6379:1 server2 - 172.33.92.4:6379:1 server3
重建 configmap,deployment,svc,檢驗(yàn)
[root@node1 ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE stateful-redis-0 1/1 Running 0 20h 172.33.57.3 node2 stateful-redis-1 1/1 Running 0 22h 172.33.57.2 node2 stateful-redis-2 1/1 Running 0 22h 172.33.96.2 node1 stateful-redis-3 1/1 Running 0 22h 172.33.92.4 node3 twemproxy-deployment-545c7dcbfd-5k2xh 1/1 Running 0 35s 172.33.92.3 node3 twemproxy-deployment-545c7dcbfd-r7d6h 1/1 Running 0 35s 172.33.96.4 node1 [root@node1 ~]# [root@node1 ~]# [root@node1 ~]# [root@node1 ~]# redis-cli -h 172.33.92.3 -p 22121 -a foobar 172.33.92.3:22121> set a b OK 172.33.92.3:22121> exit [root@node1 ~]# redis-cli -h 172.33.96.4 -p 22121 -a foobar 172.33.96.4:22121> get a "b" 172.33.96.4:22121> [root@node1 ~]# kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.254.0.1無(wú)頭服務(wù) headless service443/TCP 1d svc-redis ClusterIP None 6379/TCP 23h app=redis svc-twemproxy ClusterIP 10.254.68.39 22121/TCP,22122/TCP 4m app=twemproxy [root@node1 ~]# redis-cli -h 10.254.68.39 -p 22121 -a foobar 10.254.68.39:22121> get a "b" 10.254.68.39:22121> # twemproxy 也自帶 HA 了,通過(guò) 服務(wù)也能訪問(wèn)。服務(wù)隨便宕還能自愈,厲害了。
有時(shí)不需要或不想要負(fù)載均衡,以及多帶帶的 Service IP。 遇到這種情況,可以通過(guò)指定 Cluster IP(spec.clusterIP)的值為 "None" 來(lái)創(chuàng)建 Headless Service。 這個(gè)選項(xiàng)允許開(kāi)發(fā)人員自由地尋找他們想要的方式,從而降低與 Kubernetes 系統(tǒng)的耦合性。 應(yīng)用仍然可以使用一種自注冊(cè)的模式和適配器,對(duì)其它需要發(fā)現(xiàn)機(jī)制的系統(tǒng)能夠很容易地基于這個(gè) API 來(lái)構(gòu)建。 對(duì)這類 Service 并不會(huì)分配 Cluster IP,kube-proxy 不會(huì)處理它們,而且平臺(tái)也不會(huì)為它們進(jìn)行負(fù)載均衡和路由。 DNS 如何實(shí)現(xiàn)自動(dòng)配置,依賴于 Service 是否定義了 selector。 有 selector 創(chuàng)建 Endpoints; 無(wú) selector 不會(huì) Endpoints 對(duì)象。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/32663.html
摘要:集群中的每個(gè)成員,無(wú)論是主副本還是次級(jí)副本,都管理哈希槽的一個(gè)子集。在由三個(gè)主節(jié)點(diǎn)組成的最小的集群中,每個(gè)主節(jié)點(diǎn)都有一個(gè)從屬節(jié)點(diǎn)為了至少能保證最低程度的故障轉(zhuǎn)移,每個(gè)主節(jié)點(diǎn)分配一個(gè)范圍在至之間的哈希槽。 showImg(https://segmentfault.com/img/remote/1460000018405753?w=2350&h=1000); 介 紹 Redis(REmo...
摘要:使用導(dǎo)出端口,使用掛載數(shù)據(jù)卷。清理應(yīng)用使用一鍵清理應(yīng)用總結(jié)已經(jīng)實(shí)現(xiàn)了容器擴(kuò)容自動(dòng)擋更直觀的控制容器啟動(dòng)順序及依賴。從部署到編排,單字面理解,看起來(lái)能夠維護(hù)的容器量都增長(zhǎng)了。推薦應(yīng)用包括多個(gè)服務(wù),推薦部署方式就是。前言 容器化,云原生越演越烈,新概念非常之多。信息爆炸的同時(shí),帶來(lái)層層迷霧。我嘗試從擴(kuò)容出發(fā)理解其脈路,經(jīng)過(guò)實(shí)踐探索,整理形成一個(gè)入門教程,包括下面四篇文章。 容器化實(shí)踐之路-從d...
摘要:部署環(huán)境及架構(gòu)操作系統(tǒng)版本版本版本服務(wù)器信息在詳細(xì)介紹部署集群前,先給大家展示下集群的邏輯架構(gòu)。其他操作更新刪除查看刪除除此之外,你可以刪除,如刪除上的格式為服務(wù)名字,不必關(guān)心從哪個(gè)上刪除了。 本文通過(guò)實(shí)際操作來(lái)演示Kubernetes的使用,因?yàn)榄h(huán)境有限,集群部署在本地3個(gè)ubuntu上,主要包括如下內(nèi)容: 部署環(huán)境介紹,以及Kubernetes集群邏輯架構(gòu) 安裝部署Open v...
閱讀 1689·2021-11-23 09:51
閱讀 2701·2021-11-22 09:34
閱讀 1335·2021-10-14 09:43
閱讀 3676·2021-09-08 09:36
閱讀 3220·2019-08-30 12:57
閱讀 2042·2019-08-30 12:44
閱讀 2532·2019-08-29 17:15
閱讀 3026·2019-08-29 16:08