摘要:計劃通過解決持久化的問題通過帶起個實(shí)例,它們將有穩(wěn)定的主機(jī)名線上一個部署單元是個實(shí)例通過和注入配置文件和敏感信息因為線上系統(tǒng)的特性,我們底層的實(shí)例是不需要順序啟動或停止的,將采用創(chuàng)建集群參考文章,快速創(chuàng)建一個集群。
緣起
線上有一個 redis 集群,因為當(dāng)時 redis 自帶的集群還不成熟,而且我們項目上的需求和應(yīng)用的場景比較簡單,redis 集群是通過 twemproxy + redis 進(jìn)行搭建的。這個集群其實(shí)有很多的不足
單節(jié)點(diǎn)雖然設(shè)置了持久化,但是沒有使用主從模式,沒有哨兵 sentinel 負(fù)責(zé)主從切換
twemproxy 沒有 HA,存在單點(diǎn)故障問題
集群的伸縮時(添加節(jié)點(diǎn),刪除節(jié)點(diǎn)),集群中的數(shù)據(jù)不能自動平衡
如果需求放在現(xiàn)在,可以使用 reids 3.x 以后自帶的集群特性,另外也可以選用 codis 這類開源方案。
正好最近在研究和實(shí)踐 kubernetes,打算嘗試將線上的這個集群遷移到 kubernetes,畢竟 kubernetes 能夠保證集群的實(shí)際狀態(tài)與用戶的期望一致,特別是線上的環(huán)境是可能出現(xiàn)主機(jī)重啟,多個 redis 實(shí)例宕掉的情況,利用 kubernetes 就能提高集群的可用性。
初步分析了一下,要遷移線上這個集群,需要使用 statefulset 來實(shí)現(xiàn),因為這里面
每個 redis 實(shí)例需要持久化,線上都是持久化到自己主機(jī)的某個目錄,每個實(shí)例和持久化目錄是緊密耦合的
twemproxy 的配置文件又和每個 redis 實(shí)例的 IP 是緊耦合的,要求 redis 的服務(wù)暴露在穩(wěn)定的地址和端口
于是有了下面的實(shí)驗。計劃
通過 pv/pvc 解決 redis 持久化的問題
通過 statefulset 帶起 N 個實(shí)例,它們將有穩(wěn)定的主機(jī)名(線上一個部署單元是 108 個 redis 實(shí)例)
通過 configmap 和 secret 注入配置文件和敏感信息
因為線上系統(tǒng)的特性,我們底層的 redis 實(shí)例是不需要順序啟動或停止的,podManagementPolicy 將采用 Parallel
創(chuàng)建 kubernetes 集群參考 setting-up-a-kubernetes-cluster-with-vagrant 文章,快速創(chuàng)建一個 kubernetes 集群。實(shí)際上,因為我在公司使用 windows 操作系統(tǒng),實(shí)際使用的 Vagrantfile 我做了少量的修改。
創(chuàng)建 pv/pvc簡單起見,本次實(shí)驗的目的主要是為了驗證想法,所以簡單地使用基于 nfs 的 PV 和 PVC。首先在 kubernetes 的集群的節(jié)點(diǎn)中搭建 nfs 服務(wù)。
# 每個節(jié)點(diǎn) yum -y install nfs-server nfs-utils rpcbind # 選 node1 提供服務(wù) systemctl enable nfs rpcbind systemctl start nfs rpcbind # 其他節(jié)點(diǎn)開啟 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 檢驗 showmount -e localhost /root/data 172.17.8.0/24 # nodex 檢驗 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 鏡像
本來沒想自定義 redis 鏡像,打算直接使用 hub 上的 redis 鏡像,然后用 configmap 注入 redis 的配置 redis.conf,但是只能使用同一個 configmap,這樣 pod 中 redis 持久化的位置會是同一個位置,不是期望的。后來想到可以讓 redis.conf 中的 dir 和 hostname 關(guān)聯(lián),利用每個 pod 的 hostname 不同來實(shí)現(xiàn)持久化到不同的位置上,按照這個想法做了2個實(shí)驗
通過 spec 里通過 inti-container 執(zhí)行個 shell 來修改注入的 redis.conf
通過 sepc.lifecycle 通過 poststart 執(zhí)行個 shell 來修改注入的 redis.conf
這兩個想法都沒有實(shí)驗成功。于是打算還是自定義一個 redis 鏡像吧,畢竟這樣會通用很多。
參考文章 https://www.kubernetes.org.cn... 以及 文章中提到的 https://github.com/kubernetes... 。很受啟發(fā),但是相對我的實(shí)驗?zāi)繕?biāo)都比較復(fù)雜,我只需要一個簡單的 redis 鏡像,于是做了一番改造:具體的內(nèi)容放在了 https://github.com/arashicage...
這里主要講一下 run.sh,腳本里通過 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)建完成并驗證(圖如果看不清,拖到新的標(biāo)簽頁里看大圖)
然后可以進(jìn) shell 里看看
檢查一下 nfs 目錄,statefulset 成功創(chuàng)建了各個 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測試 redis pod
查看 stateful-redis-x 的 ip 并用 redis-cli 連接測試
創(chuàng)建 twemproxy 的服務(wù)這一步,因為 twemproxy 是無狀態(tài)的打算創(chuàng)建一個 deployment 和一個 service,在 hub 上找了一下,拉取數(shù)量比較多的都從 twemproxy 都從外部的 etcd 中通過 confd 來獲取 twemproxy 的配置,(我第一次聽說 confd 是從我青云的一個朋友哪里,他們在 confd 上做了些改造,很不錯的軟件),想法很不錯,但是對于我目前的實(shí)驗加大了難度,我還是找一個純粹點(diǎn)的 twemproxy 吧。最后選擇了 zapier/twemproxy ,不過也是4年前的了,使用的 twemproxy 是v0.3.0,最目前最新 v0.4.1 支持 Authentication,而且是用在 aws 云上的,影響實(shí)驗,本想需要改造一下(去掉了 python 相關(guān)的,去掉了 memcached 相關(guān)的)。后來找到一個 fblgit/twemproxy-nutcracker 比較貼合自己的需求,但是這個鏡像也是有問題的(Dockerfile 里的 chmod 755 實(shí)際上沒起作用,運(yùn)行的時候報 Permission deny,https://github.com/moby/moby/... 另外這個鏡像將 nutcracker 的配置文件和二進(jìn)制文件都放在了一起 /scripts,我這需要在運(yùn)行的時候掛載或在 kubernetes 中通過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 arashicage@yeah.net 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,通過 hub.docker 的自動構(gòu)建,最后拉取下來進(jìn)行了測試:
# /root/config/ 包含了 nutcracker.yml 文件,內(nèi)容見后面 docker run -it --name xxx -d -v /root/config/:/usr/local/etc/nutcracker/ docker.io/arashicage/twemproxy:0.4.1
查找容器的 IP 并檢測服務(wù)是否可用
# 查找 ip docker inspect xxx |grep IPAddress 172.33.96.3 # 檢測 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}}}
上面這個鏡像 nutcracker 的配置文件(參考 nutcracker)路徑是 /usr/local/etc/nutcracker/nutcracker.yml,通過 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: - 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測試 twemproxy 到 stateful-redis-x
沒通啊,根據(jù)以往的經(jīng)驗,說明 nutcracker 不能連到后面的 redis 實(shí)例(可能 redis 宕掉,可能主機(jī)宕掉,但現(xiàn)在情況不是這樣),估計是 nutcracker Pod 的不能通過 stateful-redis-x 解析到正確的地址,驗證一下(從 dashboard 的exec 進(jìn)去):
root@twemproxy-deployment-545c7dcbfd-k2h52:/# ping stateful-redis-0 bash: ping: command not found
可惜鏡像里缺少 ping,nlslookup 等實(shí)用工具。只好通過其他方式了:
# 先拉個 busybox docker pull busybox # 再查一下 twemproxy pod 的容器 id(在stateful-redis-0 的節(jié)點(diǎn)上查,根據(jù) pod 名稱判斷,找 pause 的 id) docker ps -a # 找到 df4af96008ed # 啟動 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
也就是說,這里 nutcracker.yml 里不能直接使用 statefulset 的主機(jī)名,因為無法進(jìn)行域名解析(ping ip 或 svc-redis 能通是因為 dns 的緣故)。要解決這個問題,需要修改 nutcracker.yml 將它改為 ip 地址。雖然statefulset 的 ip 地址是不變的,但是顯式的設(shè)定感覺還是不夠通用,回頭通過 confd 來解決吧。
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,檢驗
[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ù) 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 了,通過 服務(wù)也能訪問。服務(wù)隨便宕還能自愈,厲害了。
有時不需要或不想要負(fù)載均衡,以及多帶帶的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 "None" 來創(chuàng)建 Headless Service。 這個選項允許開發(fā)人員自由地尋找他們想要的方式,從而降低與 Kubernetes 系統(tǒng)的耦合性。 應(yīng)用仍然可以使用一種自注冊的模式和適配器,對其它需要發(fā)現(xiàn)機(jī)制的系統(tǒng)能夠很容易地基于這個 API 來構(gòu)建。 對這類 Service 并不會分配 Cluster IP,kube-proxy 不會處理它們,而且平臺也不會為它們進(jìn)行負(fù)載均衡和路由。 DNS 如何實(shí)現(xiàn)自動配置,依賴于 Service 是否定義了 selector。 有 selector 創(chuàng)建 Endpoints; 無 selector 不會 Endpoints 對象。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/32663.html
摘要:集群中的每個成員,無論是主副本還是次級副本,都管理哈希槽的一個子集。在由三個主節(jié)點(diǎn)組成的最小的集群中,每個主節(jié)點(diǎn)都有一個從屬節(jié)點(diǎn)為了至少能保證最低程度的故障轉(zhuǎn)移,每個主節(jié)點(diǎn)分配一個范圍在至之間的哈希槽。 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ò)容自動擋更直觀的控制容器啟動順序及依賴。從部署到編排,單字面理解,看起來能夠維護(hù)的容器量都增長了。推薦應(yīng)用包括多個服務(wù),推薦部署方式就是。前言 容器化,云原生越演越烈,新概念非常之多。信息爆炸的同時,帶來層層迷霧。我嘗試從擴(kuò)容出發(fā)理解其脈路,經(jīng)過實(shí)踐探索,整理形成一個入門教程,包括下面四篇文章。 容器化實(shí)踐之路-從d...
摘要:部署環(huán)境及架構(gòu)操作系統(tǒng)版本版本版本服務(wù)器信息在詳細(xì)介紹部署集群前,先給大家展示下集群的邏輯架構(gòu)。其他操作更新刪除查看刪除除此之外,你可以刪除,如刪除上的格式為服務(wù)名字,不必關(guān)心從哪個上刪除了。 本文通過實(shí)際操作來演示Kubernetes的使用,因為環(huán)境有限,集群部署在本地3個ubuntu上,主要包括如下內(nèi)容: 部署環(huán)境介紹,以及Kubernetes集群邏輯架構(gòu) 安裝部署Open v...
閱讀 1773·2021-11-23 09:51
閱讀 2818·2021-11-22 09:34
閱讀 1419·2021-10-14 09:43
閱讀 3743·2021-09-08 09:36
閱讀 3275·2019-08-30 12:57
閱讀 2111·2019-08-30 12:44
閱讀 2591·2019-08-29 17:15
閱讀 3081·2019-08-29 16:08