使用 Python 编写 Slack 机器人,并将其部署到 Kubernetes,以便确认 Pod 的状态
因为想尝试一下ChatOps的内容,也想学习Python,所以我使用Python创建了一个Slack机器人,并部署到Kubernetes上,以便可以检查Pod的状态。虽然通常会用Hubot来创建机器人,但这次我使用了Python的slackbot模块来完成。
请参考以下链接
根据以下先人的信息作为参考。
-
- PythonのslackbotライブラリでSlackボットを作る
-
- Pythonを使ったSlackBotの作成方法
- Python3系でSlack Botの作成〜基礎的な対話を実装する
准备
请点击以下链接,添加Bots应用的Slack集成。确认API令牌并设置机器人的名称和图标等。
邀请并添加Bot到频道中。
Python是一种高级编程语言。
请查阅以下官方存储库和上方参考链接来了解如何使用Slackbot模块。
- https://github.com/lins05/slackbot
因为我想从Python访问kube-apiserver来获取Pod和Namespace的信息,所以我还使用了Python的Kubernetes客户端kubernetes模块。
- https://github.com/kubernetes-client/python
编写代码
创建下列三个文件→GitHub
python_slackbot
├── mybot.py
├── run.py
└── slackbot_settings.py
在slackbot_setting.py文件中,可以设置默认的回复消息和要加载的插件等配置。
API_TOKEN可以在这里作为API_TOKEN编写,但也可以通过环境变量SLACKBOT_API_TOKEN进行传递,所以这次我们通过环境变量进行传递。
如果要指定错误通知的接收用户,可以设置ERRORS_TO。请注意,如果用户不存在,则会导致启动错误。
DEFAULT_REPLY = "何言ってんだこいつ"
PLUGINS = ['mybot']
ERRORS_TO = 'sotoiwa'
run.py已按照以下方式编写。
from slackbot.bot import Bot
def main():
bot = Bot()
bot.run()
if __name__ == "__main__":
print('start slackbot')
main()
在slackbot_settings.py的PLUGINS选项中指定的mybot.py中编写自己的代码。使用@respond_to(‘反应字符串’)来编写被提及时的响应,使用@listen_to(‘反应字符串’)来编写对频道帖子的响应。反应字符串可以使用正则表达式进行指定。
import os
import re
from kubernetes import client, config
import prettytable
from slackbot.bot import respond_to
from slackbot.bot import listen_to
# こんにちはに応答する
@respond_to('hello', re.IGNORECASE)
@respond_to('こんにちは|こんにちわ')
def mention_hello(message):
message.reply('こんにちは!')
# kubernetesに反応する
@listen_to('kubernetes', re.IGNORECASE)
def listen_kubernetes(message):
message.reply('kubernetesかっこいい!')
message.react('+1')
# helmに反応する
@listen_to('helm', re.IGNORECASE)
def listen_helm(message):
message.reply('helmかっこいい!')
message.react('+1')
# get pod
@respond_to(r'^get\s+(po|pod|pods)\s+(-n|--namespace)\s+(.*)$')
def mention_get_po(message, arg2, arg3, namespace):
# kubernetes上で動いているかを環境変数から判断する
if os.getenv('KUBERNETES_SERVICE_HOST'):
# ServiceAccountの権限で実行する
config.load_incluster_config()
else:
# $HOME/.kube/config から読み込む
config.load_kube_config()
v1 = client.CoreV1Api()
ret = v1.list_namespaced_pod(namespace=namespace, watch=False)
table = prettytable.PrettyTable()
table.field_names = ['name', 'phase']
table.align['name'] = 'l'
table.align['phase'] = 'l'
for i in ret.items:
table.add_row([i.metadata.name,
i.status.phase
])
msg = '```\n' + table.get_string() + '\n```'
message.reply(msg)
# get ns
@respond_to(r'^get\s+(ns|namespace|namespaces)$')
def mention_get_ns(message, arg2):
# kubernetes上で動いているかを環境変数から判断する
if os.getenv('KUBERNETES_SERVICE_HOST'):
# ServiceAccountの権限で実行する
config.load_incluster_config()
else:
# $HOME/.kube/config から読み込む
config.load_kube_config()
v1 = client.CoreV1Api()
ret = v1.list_namespace(watch=False)
table = prettytable.PrettyTable()
table.field_names = ['name']
table.align['name'] = 'l'
for i in ret.items:
table.add_row([i.metadata.name])
msg = '```\n' + table.get_string() + '\n```'
message.reply(msg)
本地执行
如果在本地执行,则应从HOME/.kube/config读取认证信息到kube-apiserver,因此请使用kubectl config命令设置适当的上下文。
安装所需模块。
pip install kubernetes
pip install prettytable
pip install slackbot
导出API令牌。
export SLACKBOT_API_TOKEN=hogehoge
启动机器人。
python run.py
确认运转
你好,你好,对”hello”的提及有反应。
对于Kubernetes和Helm做出反应并做出相应。
在机器人上提及Namespace以确认。结果使用prettytable模块进行格式化。
提及机器人并检查Pod。需要指定命名空间。在以下示例中,由于Pod未运行,所以为空。
部署到Kubernetes
在Kubernetes上部署机器人。
形象塑造
创建一个 requirements.txt 文件和一个 Dockerfile 文件。
kubernetes
prettytable
slackbot
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY ./*.py ./
ENTRYPOINT [ "python", "./run.py" ]
文件的配置如下所示。
python_slackbot
├── Dockerfile
├── mybot.py
├── requirements.txt
├── run.py
└── slackbot_settings.py
构建形象。
docker build -t sotoiwa540/slackbot:1.0 .
docker push sotoiwa540/slackbot:1.0
部署到Kubernetes
如果在本地运行,将从HOME/.kube/config中读取认证信息,但如果在Kubernetes上作为Pod运行,则会根据执行Pod的ServiceAccount的权限进行认证,因此需要创建ClusterRole和ClusterRoleBinding。
这次我要创建一个具有列出Pod和Namespace权限的ClusterRole,并将ClusterRole绑定到默认Namespace下的默认ServiceAccount上。
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: slackbot
rules:
- apiGroups: [""]
resources:
- pods
- namespaces
verbs:
- list
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: slackbot
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: ClusterRole
name: slackbot
apiGroup: rbac.authorization.k8s.io
kubectl apply -f slackbot-clusterrole.yaml
kubectl apply -f slackbot-clusterrolebinding.yaml
创建API令牌的秘钥。
kubectl create secret generic slackbot-secret --from-literal=SLACKBOT_API_TOKEN=hogehoge
创建部署。
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
labels:
app: slackbot
name: slackbot
spec:
replicas: 1
selector:
matchLabels:
app: slackbot
strategy:
type: Recreate
template:
metadata:
labels:
app: slackbot
spec:
containers:
- name: slackbot
image: sotoiwa540/slackbot:1.0
imagePullPolicy: Always
env:
- name: SLACKBOT_API_TOKEN
valueFrom:
secretKeyRef:
key: SLACKBOT_API_TOKEN
name: slackbot-secret
kubectl apply -f slackbot-deployment.yaml
确认Pod已经运行。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
slackbot-5b68f5dddf-klmpw 1/1 Running 0 8s
$
检查工作是否正常
在微信里提到Bot以确认Pod的状态。
此外,由于本次仅仅观察了Pod的阶段,因此与直接运行kubectl命令时的STATUS列有所不同。此外,由于Slack发布的文字数量限制,如果Pod数量较多将导致显示混乱。
使用Python的Kubernetes Client库,需要分别创建处理每个提及操作的方法。如果想要编写复杂处理的编程,则可能更适合使用这种方法,但对于仅仅进行状态查询并整理结果的情况,创建每个处理方法并格式化结果非常不方便,因此我改为使用子进程执行kubectl命令。
- PythonのslackbotのPodからkubectlコマンドを実行させる