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