云原生部署常见服务

在开发过程中,经常需要自己进行一些小小的测试,亦或是简单的验证一些功能,简单功能使用公司的业务库还好,若是公司的业务库小众,或者测试的功能有危险情况,一般都希望能在隔离环境测试,虚拟机对于此场景太重了,用 Docker 轻量的启动一些服务非常方便。

该博客并非科普向,因此不再赘述 Docker、容器、Kubernetes 基础知识。

镜像拉取

内网环境一般公司会自行搭建 Harbor 镜像仓库,在拉取时配置 secret 秘钥即可。公网环境由于网络等限制,一般需要配置镜像加速,阿里云中提供了方法:阿里云配置镜像加速

bash 复制代码
sudo mkdir -p /etc/docker
bash 复制代码
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://quype1o7.mirror.aliyuncs.com"]
}
EOF
bash 复制代码
sudo systemctl daemon-reload
bash 复制代码
sudo systemctl restart docker

有的时候,即使配置了这个也还是拉不下来,我一般都会去三方仓库(如:渡渡鸟镜像同步站)拉,找到自己想要的镜像后使用 docker pull 国内镜像下载:

bash 复制代码
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/fnproject/fn-java-fdk:jre11-1.0.211

拉下来后这个 tag 太长了,一般我会改成比较简单的 tag:

bash 复制代码
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/fnproject/fn-java-fdk:jre11-1.0.211 jre:11

有时我们内网下载不下镜像,希望在外网下载后导入到内网使用,使用如下命令导出为 tar 文件:

bash 复制代码
docker save jre:11 -o jre11.tar

注意我用的 jre:11 如果这里使用镜像ID,在内网导入后会丢失 tag 信息,内网导入:

bash 复制代码
docker load -i jre11.tar

其实部署服务时镜像的选择有很多知识,如果是想要绝对安全的环境(内部 curl、vim等指令均不存在),则使用只包含基础工具的镜像,像 Java 程序运行时其实只依赖 jre,如果使用 jdk 会导致最后的镜像体积很大,当然这也是一种权衡,有了 jdk 后可以定位一些问题。

Docker 运行 Mysql

数据库想必是开发中最常用的工具了,启动指令也非常简单:

bash 复制代码
docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -d mysql:5.7

这是个用完即丢的示例,因为这个指令并没有保存 mysql 的数据,非常适合测试,如果想要保存数据,则命令如下:

bash 复制代码
docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -v /data/mysql:/var/lib/mysql -d mysql:5.7

其中 -v /data/mysql:/var/lib/mysql 即把容器外的 /data/mysql 目录挂载到 容器内的 /var/lib/mysql 目录

注:不同版本的镜像数据目录、环境变量未必完全相同,使用时注意看文档。

Docker 运行 Redis

bash 复制代码
docker run --name redis -d -p 6379:6379 redis:5.0.8

依然是用完即丢的示例,并且没有设置账号和密码,这种都倾向于用完即弃,生产环境千万不要这样


像一些常见的还有 consul、kafka 等...

Kubernetes 部署 Redis-Cluster 集群

Redis-Cluster 和 主从哨兵集群的区别与优劣就不在此比对了,本文是以部署为主。

  1. 从三方仓库拉取 Redis7 镜像:
bash 复制代码
docker pull docker.1ms.run/library/redis:7
  1. 换一个简单的 tag
bash 复制代码
docker tag docker.1ms.run/library/redis:7 redis:7
  1. 为了让业务更可控(对于陌生的镜像我们连 redis.conf 在哪都不清楚),基于这个镜像改造我们自己的镜像,首先去 github-redis-release 中找到对应的版本,接着找到里面的 redis.conf 文件
  2. 在有 docker 环境的机器上组成如下的结构(dockerfile 自行创建):
bash 复制代码
└── redis
    ├── dockerfile
    └── redis.conf
  1. 编写 dockerfile
bash 复制代码
# 基于刚刚下载的镜像
FROM redis:7 as base

# 创建配置目录
RUN mkdir -p /usr/local/etc

# 复制本地的redis.conf到容器中
COPY redis.conf /usr/local/etc/redis.conf

# 可选:设置配置文件权限(如果需要)
RUN chmod 644 /usr/local/etc/redis.conf

# 暴露Redis默认端口
EXPOSE 6379

# 设置启动命令
CMD ["redis-server", "/usr/local/etc/redis.conf"]

将 dockerfile 构建成镜像:

bash 复制代码
docker build -t redis:7.4 -f dockerfile .
  1. 写 Kubernetes 声明文件,注意事项如下:
  • 第4行 redis-cluster 为命名空间的名字,不推荐修改,如果修改则需修改所有 namespace: redis-cluster 的地方
  • 第9行 redis-cluster-config 是 redis.conf 中的一些属性,通过 74-79 行挂载进了容器的 redis.conf 文件中,requirepass 是连该节点的密码, masterauth 是主备间的密码
  • 第31行与93行应保持一致
  • 第85行对应的字符串应取 "kubectl get sc" 查询返回结果中的第一列(Name)值,sc 是用来真正帮忙创建存储目录的,有些公司可能有固态和机械等多种盘,会根据应用情况选择合适的 sc
  • 第88行指定了 redis 存储的大小,根据使用量情况自行决定(redis的aof、rdb持久化机制),12行指定了 redis 工作目录为容器内的/data,82行指定了通过sc创建出来的路径的别名,64、65行将这个别名挂载到了容器的/data目录,不推荐修改这个目录,因为还涉及到15、18行等地方
yaml 复制代码
apiVersion: v1
kind: Namespace
metadata:
  name: redis-cluster
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-cluster-config
  namespace: redis-cluster
data:
  redis-config: |
    appendonly yes
    protected-mode no
    dir /data
    port 6379
    cluster-enabled yes
    cluster-config-file /data/nodes.conf
    cluster-node-timeout 5000
    masterauth Fusion@123
    requirepass Fusion@123
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
  namespace: redis-cluster
  labels:
    app.kubernetes.io/name: redis-cluster
spec:
  serviceName: redis-headless
  replicas: 6
  selector:
    matchLabels:
      app.kubernetes.io/name: redis-cluster
  template:
    metadata:
      labels:
        app.kubernetes.io/name: redis-cluster
    spec:
      containers:
        - name: redis
          image: 'redis:7.4'
          command:
            - "redis-server"
          args:
            - "/usr/local/etc/redis.conf"
            - "--protected-mode"
            - "no"
            - "--cluster-announce-ip"
            - "$(POD_IP)"
          env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          ports:
            - name: redis-6379
              containerPort: 6379
              protocol: TCP
          volumeMounts:
            - name: config
              mountPath: /usr/local/etc
            - name: pvc
              mountPath: /data
          resources:
            limits:
              cpu: '2'
              memory: 8Gi
            requests:
              cpu: 50m
              memory: 500Mi
      volumes:
        - name: config
          configMap:
            name: redis-cluster-config
            items:
              - key: redis-config
                path: redis.conf
  volumeClaimTemplates:
    - metadata:
        name: pvc
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: "managed-nfs-storage"
        resources:
          requests:
            storage: 3Gi
---
apiVersion: v1
kind: Service
metadata:
  name: redis-headless
  namespace: redis-cluster
  labels:
    app.kubernetes.io/name: redis-cluster
spec:
  ports:
    - name: redis-6379
      protocol: TCP
      port: 6379
      targetPort: 6379
  selector:
    app.kubernetes.io/name: redis-cluster
  clusterIP: None
  type: ClusterIP

对于这个声明文件你可能会有疑问,上一步建自己的镜像时我说为了安全,能自己控制 redis.conf,但我却没有暴露 redis.conf 反而直接在这个声明文件的 13-21 行之间配置了,其实我是把它声明为了 configmap,修改时觉得还好,就这样部署下来了。

  1. 接下来在 kubernetes 环境运行就好啦,其中 redis-cluster.yaml 是我自己的 yaml 文件名,注意要先把第 6 步创建出的镜像加载到 k8s 集群的各个节点上!
bash 复制代码
kubectl apply -f redis-cluster.yaml
  1. 此时系统中运行了 6 个 redis,但它们之间并没有组成集群,需要通过下方指令,注意事项如下:
  • redis-cluster-0 创建出来的1个pod的名字(命名规则是文件1第26行 + 副本序号,例如6副本则0-5)
  • -n redis-cluster 命名空间,如果文件1的第4行修改了这里也需要修改,注意这段中(-n redis-cluster)出现了2次,其实就是筛选这个命名空间下有特定标签(app.kubernetes.io/name=redis-cluster,文件1的第29行定义)的 pod 共有多少
  • -a Fusion@123 文件1 中第 21 行指定的密码
  • --cluster-replicas 1 副本数1,6 节点恰好 3 主 3 备
  • 后面的 podIP、6379等都不推荐修改,在文件1中通过环境变量等已经配置好了,而且命名空间、容器互相隔离,相互直接使用相同的端口啥的毫无影响
bash 复制代码
kubectl exec -it redis-cluster-0 -n redis-cluster -- redis-cli -a Fusion@123 --cluster create --cluster-replicas 1 $(kubectl get pods -n redis-cluster -l app.kubernetes.io/name=redis-cluster -o jsonpath='{range.items[*]}{.status.podIP}:6379 {end}')

执行后可能会让输入 yes,输入完成后会输出集群创建成功啥的,并展示每个节点分配的槽数(槽是redis-cluster集群中的概念,默认是均分,也有某节点性能特别好需要特殊划分的情况),可以通过创建出来的目录 (/mnt/nfs 之类的)进入后查看 node.conf(文件1第18行配置) 查看

  1. 那系统中该如何使用呢,以 Java 为例:
bash 复制代码
spring:
  redis:
    cluster:
      nodes: redis-cluster-0.redis-headless.redis-cluster.svc.cluster.local:6379,redis-cluster-1.redis-headless.redis-cluster.svc.cluster.local:6379,redis-cluster-2.redis-headless.redis-cluster.svc.cluster.local:6379,redis-cluster-3.redis-headless.redis-cluster.svc.cluster.local:6379,redis-cluster-4.redis-headless.redis-cluster.svc.cluster.local:6379,redis-cluster-5.redis-headless.redis-cluster.svc.cluster.local:6379
    lettuce:
      pool:
        min-idle: 30
        max-idle: 100
    password: Fusion@123

注意需要部署的 Java 应用和 Redis 在同一 Kubernetes 集群里哦,如果不在则需要通过其它手段(例如 NodePort) 来访问了~

这里只是以部署 Redis-Cluster 为例,其实部署有状态集群如数据库等也是一样的,需要理解的是 Service、StorageClass 等~

相关推荐
youyudehexie2 小时前
云原生与边缘计算融合驱动下一代互联网架构创新探索实践
云原生·架构·边缘计算
上海云盾第一敬业销售2 小时前
云原生时代的安全困境:容器化应用如何正确部署WAF防护?
安全·云原生
zhoupenghui1682 小时前
搭建VictoriaLogs集中式日志管理系统来解决 微服务 “请求跨越多个服务”时报错的全链路追踪与快速排查 问题
微服务·云原生·架构·链路追踪·victorialogs·logs explorer
晨旭缘2 小时前
kubectl、kubelet、kubeadm命令详解
docker·kubernetes·kubelet
janeysj2 小时前
docker-proxy实现原理
运维·docker·容器
小夏子_riotous2 小时前
Docker学习路径——5、容器数据卷
linux·运维·服务器·学习·docker·容器·云计算
Irissgwe2 小时前
redis之持久化
数据库·redis·缓存
eLIN TECE2 小时前
Redis重大版本整理(Redis2.6-Redis7.0)
java·数据库·redis
Deepincode2 小时前
Redis源码探究系列—双向链表(adlist)源码实现解析
redis