在上一篇笔记中,我们已经可以使用 k8s1.6 版本搭建一个基础的集群,在集群内部可以完成不同 node 之间的 pod 互通并且可以完成服务发现。但已经完成的这个集群仍然是通过不安全的 8080 端口进行的,并且除了最基本的 apiserver 和 controller-manager 之间以外,其他组件间通讯都没有认证措施。这一次,我准备在集群中加入 TLS 验证来加强集群的安全性,不过这次只加入认证并不考虑授权,同时加入 kubeconfig 配置文件的机制,将apiserver地址和一些客户端证书存放在 kubeconfig 配置文件中。再安装一个新的插件 Dashboard ,用于以web方式管理集群。
这次使用的 k8s 版本是比较新的 1.10.8 。第一个原因是已经决定使用 kubeconfig 来进行配置所以旧版本不是必须的,另一个原因是在使用 tls 证书时 1.6.0 版本出现了一些 bug,所以就决定直接将 k8s 更换成比较新的版本。到目前为止 k8s 仍在快速迭代,所以建议在使用的时候选择新一些的版本。
参考资料:
- 《Kubernetes权威指南》
- Kubernetes v1.8.x 全手動苦工安裝教學(TL;DR)
- 升级Dashboard
运行环境&软件版本:
- Ubuntu 16.04.4 LTS
- kubernetes v1.10.8
- etcd v3.1.12
- docker-ce 18.06.1-ce
- flannel v0.10.0
- coredns 1.0.6
- easyrsa3
- kubernetes-dashboard v1.8.3
角色规划:
主机名 | IP地址 | 角色 | CPU/内存 |
---|---|---|---|
u16-1 | 192.168.112.148 | master | 2核/2G |
u16-2 | 192.168.112.149 | node | 2核/2G |
u16-3 | 192.168.112.150 | node | 2核/2G |
上一篇笔记中关于 clusterIP 理解有些错误,clusterIP 是 service 使用的 IP 地址段,和 pod 使用的 ip 地址段不是同一个配置,并且应该无重合部分(与物理机IP段也不要有重合部分)。这次指定 clusterIP 端为 10.0.0.0/16 ,kube-dns 的 service 使用的 IP 地址为 10.0.0.10
环境准备
准备工作基本可以归纳为以下几步
- 下载需要的 kubernetes 和 etcd 的二进制版本并上传至服务器。(etcd 的版本选择可以从 k8s 源码包中 kubernetes/cluster/images/etcd/Makefile 文件的 REGISTRY_TAG? 字段找到)
- 在节点上禁用 swap 和 selinux。
- 配置防火墙放开各个组件使用的端口或者关闭防火墙管理软件。虽然不使用防火墙管理软件(ufw、Firewalld),但k8s仍然会操作 iptables 表用来实现访问 service 等功能。
-
设置以下内核参数,可以在 /etc/sysctl.conf 文件或者等效的其他文件内填写
net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1
部分发行版本的 linux 可能不默认加载 br_netfilter 模块导致无法加载后两条规则,如果缺少模块需要提前配置加载,这里不详细介绍过程了。
- 安装 docker-ce 并且配置在启动 docker 之后,修改 FORWARD 链的默认规则为 ACCEPT。具体可以参考上一篇笔记。
-
如果有内部 DNS,可以在内部 DNS 上配置各个节点间可以通过主机名互相解析。如果没有则需要配置 /etc/hosts 解析各个节点,像下面这样:
# 添加如下几行 192.168.112.148 u16-1 192.168.112.149 u16-2 192.168.112.150 u16-3
安装 Master 功能
安装 etcd 服务
$ tar xf etcd-v3.1.12-linux-amd64.tar.gz
# 把解压后的 etcd 和 etcdctl 复制到 /usr/bin 目录下
$ sudo cp etcd-v3.1.12-linux-amd64/etcd{,ctl} /usr/bin/
# 创建 etcd 工作路径和配置存放路径
$ sudo mkdir /var/lib/etcd/
$ sudo mkdir /etc/etcd
# 然后创建管理脚本 /lib/systemd/system/etcd.service 和配置文件 /etc/etcd/etcd.conf。内容如下
$ cat /lib/systemd/system/etcd.service
[Unit]
Description=Etcd Server
After=network.target
[Service]
Type=notify
WorkingDirectory=/var/lib/etcd
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/bin/etcd $ETCD_ARGS
[Install]
WantedBy=multi-user.target
$ cat /etc/etcd/etcd.conf
ETCD_ARGS="--listen-client-urls 'http://192.168.112.148:2379' --advertise-client-urls 'http://192.168.112.148:2379'"
# 启动并设置为开机自启动
$ sudo systemctl daemon-reload
$ sudo systemctl start etcd
$ sudo systemctl enable etcd
Created symlink from /etc/systemd/system/multi-user.target.wants/etcd.service to /lib/systemd/system/etcd.service.
# 完成后可以检查一下服务是否正常运行
systemctl status etcd
# 也可以使用 etcdctl 来检查 etcd 健康状况
$ etcdctl --endpoints http://192.168.112.148:2379 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://192.168.112.148:2379
cluster is healthy
安装 kube-apiserver 服务
# 解压二进制文件并将 kube-apiserver 复制到系统目录 /usr/bin
$ tar xf kubernetes-server-linux-amd64.tar.gz
$ sudo cp kubernetes/server/bin/kube-apiserver /usr/bin/
# 顺便把其他 master 的组件都复制到系统目录 /usr/bin
$ sudo cp kubernetes/server/bin/{kube-controller-manager,kube-scheduler,kubectl} /usr/bin/
# 创建日志存放目录和配置存放目录
$ sudo mkdir /var/log/kubernetes
$ sudo mkdir /etc/kubernetes
然后创建各个组件间认证的证书,因为这一次的目的是创建一个只通过 tls 证书认证组件而不做对应授权的集群,同时也没有开启 RBAC,所以这里就简化配置,只创建一组 CA 证书用于签名客户端和服务端证书、一组服务端证书用于 apiserver 、一组客户端证书用于其他组件。
# 因为这次不需要对证书配置过多的属性,所以还是使用上次使用的 easy-rsa 工具来创建证书并签名
$ curl -L -O https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz
$ tar xf easy-rsa.tar.gz
$ cd easy-rsa-master/easyrsa3/
# 初始化工具
$ ./easyrsa init-pki
# 创建根证书(CA)
$ ./easyrsa --batch --req-cn=kubernetes build-ca nopass
# 创建服务端证书和秘钥
$ ./easyrsa --subject-alt-name="IP:192.168.112.148,IP:10.0.0.1,IP:127.0.0.1,DNS:kubernetes.default" build-server-full server nopass
# 创建客户端证书和秘钥
$ ./easyrsa build-client-full client nopass
# 使用 kubectl 创建 kubeconfig 文件供客户端和其他组件使用
$ kubectl config set-cluster kubernetes \
> --certificate-authority=pki/ca.crt \
> --embed-certs=true \
> --server=https://192.168.112.148:6443 \
> --kubeconfig=./client.conf
Cluster "kubernetes" set.
$ kubectl config set-credentials client \
> --client-certificate=pki/issued/client.crt \
> --client-key=pki/private/client.key \
> --embed-certs=true \
> --kubeconfig=./client.conf
User "client" set.
$ kubectl config set-context client@kubernetes \
> --cluster=kubernetes \
> --user=client \
> --kubeconfig=./client.conf
Context "client@kubernetes" created.
$ kubectl config use-context client@kubernetes \
> --kubeconfig=./client.conf
Switched to context "client@kubernetes".
# 将生成的 kubeconfig 放在 kubectl 默认读取配置的位置并重命名成 config。
$ cp client.conf ~/.kube/config
# 将 kubeconfig 和证书放在配置文件的目录
$ sudo mkdir /etc/kubernetes/pki
$ sudo cp client.conf /etc/kubernetes/
$ sudo cp pki/ca.crt pki/issued/server.crt pki/private/server.key /etc/kubernetes/pki/
然后创建管理脚本 /lib/systemd/system/kube-apiserver.service 和配置文件 /etc/kubernetes/apiserver
$ cat /lib/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
After=etcd.service
Wants=etcd.service
[Service]
EnvironmentFile=/etc/kubernetes/apiserver
ExecStart=/usr/bin/kube-apiserver $KUBE_API_ARGS
Restart=on-failure
Type=notify
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
$ cat /etc/kubernetes/apiserver
KUBE_API_ARGS="--storage-backend=etcd3 --etcd-servers=http://192.168.112.148:2379 --insecure-port=0 --service-cluster-ip-range=10.0.0.0/16 --service-node-port-range=1-65535 --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota --logtostderr=false --log-dir=/var/log/kubernetes --v=2 --client-ca-file=/etc/kubernetes/pki/ca.crt --tls-private-key-file=/etc/kubernetes/pki/server.key --tls-cert-file=/etc/kubernetes/pki/server.crt"
# 启动并设置为开机自启动
$ sudo systemctl daemon-reload
$ sudo systemctl start kube-apiserver
$ sudo systemctl enable kube-apiserver
Created symlink from /etc/systemd/system/multi-user.target.wants/kube-apiserver.service to /lib/systemd/system/kube-apiserver.service.
# 此时 kube-apiserver 已经启动并且仅在 6443 端口监听。
$ sudo netstat -lnpt|grep apiserver
tcp6 0 0 :::6443 :::* LISTEN 8781/kube-apiserver
# kubectl 默认情况下链接的 API 地址是本地的 8080 端口,因此需要设置 ~/.kube/config 文件, kubectl 才能正确的找到 API 服务器地址。
$ kubectl cluster-info
Kubernetes master is running at https://192.168.112.148:6443
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
安装 kube-controller-manager 服务
上一步已经将需要的二进制可执行文件和证书秘钥放在相应的位置了,下面需要创建管理脚本 /lib/systemd/system/kube-controller-manager.service 和 配置文件 /etc/kubernetes/controller-manager
$ cat /lib/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
After=kube-apiserver.service
Requires=kube-apiserver.service
[Service]
EnvironmentFile=/etc/kubernetes/controller-manager
ExecStart=/usr/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_ARGS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
$ cat /etc/kubernetes/controller-manager
KUBE_CONTROLLER_MANAGER_ARGS="--kubeconfig=/etc/kubernetes/client.conf --service-account-private-key-file=/etc/kubernetes/pki/server.key --root-ca-file=/etc/kubernetes/pki/ca.crt --logtostderr=false --log-dir=/var/log/kubernetes --v=2"
kube-controller-manager 参数说明:
--kubeconfig :在 kubeconfig 文件中指定 apiserver 地址、服务端证书和客户端证书与秘钥
--service-account-private-key-file :指定服务端私钥。用于签署 serviceaccount 的 token。
--root-ca-file :指定 ca 跟证书文件。配置了此项后,ca证书将被包含在 serviceaccount 中,然后就可以使用 serviceaccount 认证 组件与apiserver 间的通讯。
启动并设置为开机自启动
$ sudo systemctl daemon-reload
$ sudo systemctl start kube-controller-manager
$ sudo systemctl enable kube-controller-manager
Created symlink from /etc/systemd/system/multi-user.target.wants/kube-controller-manager.service to /lib/systemd/system/kube-controller-manager.service.
安装 kube-scheduler 服务
二进制文件和 kubeconfig 文件已经就绪,下面需要创建管理脚本 /lib/systemd/system/kube-scheduler.service 和配置文件 /etc/kubernetes/scheduler
$ cat /lib/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler Server
After=kube-apiserver.service
Requires=kube-apiserver.service
[Service]
EnvironmentFile=/etc/kubernetes/scheduler
ExecStart=/usr/bin/kube-scheduler $KUBE_SCHEDULER_ARGS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
$ cat /etc/kubernetes/scheduler
KUBE_SCHEDULER_ARGS="--kubeconfig=/etc/kubernetes/client.conf --logtostderr=false --log-dir=/var/log/kubernetes --v=2"
# 启动并设置为开机自启动
$ sudo systemctl daemon-reload
$ sudo systemctl start kube-scheduler
$ sudo systemctl enable kube-scheduler
Created symlink from /etc/systemd/system/multi-user.target.wants/kube-scheduler.service to /lib/systemd/system/kube-scheduler.service.
安装 node 功能
在所有节点上操作安装如下组件。
安装 kubelet 和 kube-proxy
$ tar xf kubernetes-server-linux-amd64.tar.gz
# 将 kubelet 和 kube-proxy 的二进制文件复制到 /usr/bin 目录下
$ sudo cp kubernetes/server/bin/{kube-proxy,kubelet} /usr/bin/
# 创建 kubelet 的工作路径 /var/lib/kubelet 、配置文件的存放路径 /etc/kubernetes 和 日志路径 /var/log/kubernetes。master 节点上已经创建过的文件夹则不需要再次创建
$ sudo mkdir /var/lib/kubelet
$ sudo mkdir /var/log/kubernetes
$ sudo mkdir /etc/kubernetes
# 创建完配置文件夹后,还需要将 client.conf 文件复制到所有节点的这个文件夹下
# 下一步分别创建 kubelet 和 kube-proxy 的管理脚本与配置文件,内容如下
$ cat /lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet Server
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet $KUBELET_ARGS
Restart=on-failure
[Install]
WantedBy=mulit-user.target
$ cat /etc/kubernetes/kubelet
KUBELET_ARGS="--kubeconfig=/etc/kubernetes/client.conf --hostname-override=u16-1 --logtostderr=false --log-dir=/var/log/kubernetes --v=2"
$ cat /lib/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube-Proxy Server
After=networking.service
Requires=networking.service
[Service]
EnvironmentFile=/etc/kubernetes/proxy
ExecStart=/usr/bin/kube-proxy $KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
$ cat /etc/kubernetes/proxy
KUBE_PROXY_ARGS="--kubeconfig=/etc/kubernetes/client.conf --logtostderr=false --log-dir=/var/log/kubernetes --v=2"
# 启动并设置为开机自启动
$ sudo systemctl daemon-reload
$ sudo systemctl start kubelet kube-proxy
$ sudo systemctl enable kubelet kube-proxy
Created symlink from /etc/systemd/system/mulit-user.target.wants/kubelet.service to /lib/systemd/system/kubelet.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/kube-proxy.service to /lib/systemd/system/kube-proxy.service.
--hostname-override ,设置本节点显示的名字,需要与/etc/hosts 中的解析对应
安装 flanneld 网络插件
此部分与上一篇笔记基本相同
wget 下载 flannel 对应版本的二进制程序包,也可以在 github 上下载。
$ wget https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz
# 解压出需要的程序放在 /usr/bin 下
$ tar xf flannel-v0.10.0-linux-amd64.tar.gz
$ sudo cp flanneld /usr/bin/
$ sudo cp mk-docker-opts.sh /usr/bin/
创建 systemd 管理脚本 /lib/systemd/system/flanneld.service。内容如下:
$ cat /lib/systemd/system/flanneld.service
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
Before=docker.service
[Service]
EnvironmentFile=-/etc/kubernetes/flanneld
ExecStart=/usr/bin/flanneld ${FLANNEL_ETCD} ${FLANNEL_OPTIONS}
ExecStartPost=/usr/bin/mk-docker-opts.sh -d /run/flannel/docker
Type=notify
[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
创建 flanneld 的配置文件:
$ cat /etc/kubernetes/flanneld
FLANNEL_ETCD="-etcd-endpoints=http://192.168.112.148:2379"
FLANNEL_ETCD_KEY="/coreos.com/network"
同时还需要修改 /lib/systemd/system/docker.service 。在 After= 和 Requires= 后添加 flanneld.service 。添加环境变量文件 EnvironmentFile=-/run/flannel/docker ,同时在 ExecStart= 后面添加环境变量 DOCKER_OPTS , 比如:ExecStart=/usr/bin/dockerd -H fd:// $DOCKER_OPTS
flannel 网络插件使用 etcd 存储和同步信息,在启动 flanneld 之前首先主节点上配置 etcd 中设置初始的配置:
$ etcdctl --endpoints http://192.168.112.148:2379 set /coreos.com/network/config '{ "Network": "10.244.0.0/16" }'
{ "Network": "10.244.0.0/16" }
然后就可以启动 flanneld 了
$ sudo systemctl daemon-reload
$ sudo systemctl start flanneld && sudo systemctl enable flanneld
# 启动 flanneld 之后还需要重启 docker 和 kubelet
$ sudo systemctl restart docker kubelet
# 之后查看本机网卡信息,docker0 的 ip 地址已经变为指定的 ip 段。
解决基础镜像下载
这个版本 k8s 使用的基础镜像为 k8s.gcr.io/pause-amd64:3.1 ,因为一些原因不能直接下载到,所以可以通过在 dockerhub 上下载相同镜像然后重新设置标签的方式解决。
sudo docker image pull mirrorgooglecontainers/pause-amd64:3.1
sudo docker tag mirrorgooglecontainers/pause-amd64:3.1 k8s.gcr.io/pause-amd64:3.1
安装插件
安装 coredns 插件
尝试在节点间使用 tls 加密通信后 dns 插件能否安装成功。
# 源码包已经包含在我们下载的二进制安装包内了,先解压
$ cd kubernetes/
$ tar xf kubernetes-src.tar.gz
$ cd cluster/addons/dns
# 将需要修改的文件复制一份并作出修改,将模板改中的变量改为实际的值。和旧版本不同,这个版本将原来的单独的 svc、configmap、sa、deploy 都放进一个配置文件 kube-dns.yaml。同时还提供了更强大的 coredns 用来替代 kube-dns。这次尝试安装 coredns 插件
$ cp coredns.yaml.sed coredns.yaml
$ sed -i 's/$DNS_SERVER_IP/10.0.0.10/g' coredns.yaml
$ sed -i 's/$DNS_DOMAIN/cluster.local/g' coredns.yaml
# 因为这次我们不使用包括 rbac 在内的任何鉴权方式,所以可以手动删除掉这个配置文件中 kind 为 ClusterRole 和 ClusterRoleBinding 的配置段。然后使用 kubectl 创建配置文件对应的资源。
$ kubectl apply -f coredns.yaml
# 查看 dns 是否正常运行,与之前 kube-dns 存在三个容器在一个 POD 中不同, coredns 创建了1个容器在一个 POD 中,同时 replicas 设置为2,就是说有2个相同的 POD 为 DNS 服务提供负载均衡
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-77c989547b-7mk6s 1/1 Running 1 3m
coredns-77c989547b-b9g55 1/1 Running 1 2m
安装 Dashboard 插件
Dashboard 是 k8s 自带的 web UI 管理界面,可以通过图形化的方式查看集群状态以及对集群进行操作。
# k8s 也在源码包内提供了 Dashboard 的配置文件,路径为 kubernetes/cluster/addons/dashboard ,也就是说和 dns 插件所在的文件夹拥有同样一个上级目录。
$ cd ../dashboard/
$ ls -l
总用量 32
-rw-rw-r-- 1 sun sun 264 9月 13 23:17 dashboard-configmap.yaml
-rw-rw-r-- 1 sun sun 1754 9月 13 23:17 dashboard-controller.yaml
-rw-rw-r-- 1 sun sun 1353 9月 13 23:17 dashboard-rbac.yaml
-rw-rw-r-- 1 sun sun 551 9月 13 23:17 dashboard-secret.yaml
-rw-rw-r-- 1 sun sun 359 9月 27 14:04 dashboard-service.yaml
-rw-rw-r-- 1 sun sun 242 9月 13 23:17 MAINTAINERS.md
-rw-rw-r-- 1 sun sun 72 9月 13 23:17 OWNERS
-rw-rw-r-- 1 sun sun 400 9月 13 23:17 README.md
# dashboard 的 image 存放在 google 的镜像仓库,无法直接下载。所以这里还是先找到这个镜像的名字,然后在 dockerhub 上下载相同镜像并修改 tag 。
$ grep 'image:' dashboard-controller.yaml
image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
$ sudo docker image pull mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.8.3
$ sudo docker image tag mirrorgooglecontainers/kubernetes-dashboard-amd64:v1.8.3 k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
# 这里为了访问方便,修改一下 service 的配置文件,把 type 设置为 NodeProt 模式并设置一个固定的 nodePort
$ cat dashboard-service.yaml
apiVersion: v1
kind: Service
metadata:
name: kubernetes-dashboard
namespace: kube-system
labels:
k8s-app: kubernetes-dashboard
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
spec:
selector:
k8s-app: kubernetes-dashboard
ports:
- port: 443
targetPort: 8443
nodePort: 31000
type: NodePort
# 然后就可以使用这些配置文件安装 Dashboard 了
$ kubectl apply -f dashboard-configmap.yaml -f dashboard-secret.yaml -f dashboard-service.yaml
$ kubectl apply -f dashboard-controller.yaml
# 创建后查看 pod 的状态,ready 后就可以通过 https://NODEIP:31000 访问。
$ kubectl get pod -n kube-system|grep dashboard
kubernetes-dashboard-598d75cb96-fsrlm 1/1 Running 0 2m
# 访问 Dashboard 时会提示身份验证,因为没有配置权限,这时点击跳过可以进入 Dashboard 。或者可以通过 token 登录。 token 可以通过 kubectl 查看
$ kubectl get secrets -n kube-system |grep kubernetes-dashboard-token
kubernetes-dashboard-token-4s5p5 kubernetes.io/service-account-token 3 23m
$ kubectl describe secrets kubernetes-dashboard-token-4s5p5 -n kube-system|grep 'token:'
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC10b2tlbi00czVwNSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjRmYmFjMWUyLWMyMjctMTFlOC04NDk1LTAwMGMyOWEwMTU1NiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprdWJlcm5ldGVzLWRhc2hib2FyZCJ9.rMNq9DbbPQc7XYl7M0We1zAWQgIHk1hlt7AXMyoE2YKCf6YIjcW96o454KEuM6LFoWY7SDQL_PRMA3keoKhDfMJtOc2Y5HOeYaxxgqkD3xrEeShEPO3fWOvaYiZU7-hemlMo_avb-YAB5VUGVFQQLYB-1bjrcII7qJj18tJu3Wd2lmqXnxYEV62ZMRUUTprEmBoTdRGyzHa0_JwD37Sjkrb0VqCLQN7n59pbOGpSxvdw5jSLoMolOnWywzVV3Ji7rFjvqHz6z2WX8sYcgEFGShHLPU8pFJo9YLc_D6F0FsDjqPyNPfW0Fv3j0zu38OHC5z896ow9Z1PP3qap2PiFdQ
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。