ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

operator-sdk+ansible构建应用自动化交付

2022-04-22 09:32:47  阅读:231  来源: 互联网

标签:name -- yaml ansible mysql operator yml sdk


官方地址

环境说明

  • kubernetes v1.22.6+k0s (通过vCluster创建虚拟机集群,避免污染主集群。参考文档)
  • kubectl v1.22.8
  • operator-sdk v1.19.1
  • docker-ce 20.10.12
  • 阿里云容器镜像服务

测试应用架构

graph BT; MySQL --> Nacos; Gateway --> Nacos; Gateway --> Nginx; Postgres --> Gateway; Redis --> Gateway;

应用采用微服务架构:

  • Gateway代表了后端服务
  • 服务发现和配置采用Nacos
  • 业务数据使用Postgres
  • 缓存使用Redis
  • Portal代表前端服务

应用Operator初始化

# 假设应用名为app520
[root@k8stest ~]# mkdir app520-operator
[root@k8stest ~]# cd app520-operator/
# --plugins ansible创建基于ansible的目录、文件,--domain代表了组织域
[root@k8stest app520-operator]# operator-sdk init --domain example.com --plugins ansible
Writing kustomize manifests for you to edit...
Next: define a resource with:
$ operator-sdk create api
# 查看目录、文件
[root@k8stest app520-operator]# tree
.
├── config
│   ├── default
│   │   ├── kustomization.yaml
│   │   ├── manager_auth_proxy_patch.yaml
│   │   └── manager_config_patch.yaml
│   ├── manager
│   │   ├── controller_manager_config.yaml
│   │   ├── kustomization.yaml
│   │   └── manager.yaml
│   ├── manifests
│   │   └── kustomization.yaml
│   ├── prometheus
│   │   ├── kustomization.yaml
│   │   └── monitor.yaml
│   ├── rbac
│   │   ├── auth_proxy_client_clusterrole.yaml
│   │   ├── auth_proxy_role_binding.yaml
│   │   ├── auth_proxy_role.yaml
│   │   ├── auth_proxy_service.yaml
│   │   ├── kustomization.yaml
│   │   ├── leader_election_role_binding.yaml
│   │   ├── leader_election_role.yaml
│   │   ├── role_binding.yaml
│   │   ├── role.yaml
│   │   └── service_account.yaml
│   ├── scorecard
│   │   ├── bases
│   │   │   └── config.yaml
│   │   ├── kustomization.yaml
│   │   └── patches
│   │       ├── basic.config.yaml
│   │       └── olm.config.yaml
│   └── testing
│       ├── debug_logs_patch.yaml
│       ├── kustomization.yaml
│       ├── manager_image.yaml
│       └── pull_policy
│           ├── Always.yaml
│           ├── IfNotPresent.yaml
│           └── Never.yaml
├── Dockerfile
├── Makefile
├── molecule
│   ├── default
│   │   ├── converge.yml
│   │   ├── create.yml
│   │   ├── destroy.yml
│   │   ├── kustomize.yml
│   │   ├── molecule.yml
│   │   ├── prepare.yml
│   │   └── verify.yml
│   └── kind
│       ├── converge.yml
│       ├── create.yml
│       ├── destroy.yml
│       └── molecule.yml
├── playbooks
├── PROJECT
├── requirements.yml
├── roles
└── watches.yaml

16 directories, 45 files
[root@k8stest app520-operator]#
# 创建应用组件API
# --group 代表了组件类型,我们根据应用划分为:中间件、后端、前端,默认不支持多group,需要修改"app520-operator/PROJECT"文件,添加'multigroup: true'
# --kind 代表了具体的组件或服务
# --version 根据成熟度发布版本
# --generate-role 生成ansible中role模板

# 创建中间件组及各类型
[root@k8stest app520-operator]# operator-sdk create api --group middleware --version v1alpha1 --kind Mysql --generate-role
Writing kustomize manifests for you to edit...
[root@k8stest app520-operator]# operator-sdk create api --group middleware --version v1alpha1 --kind Postgres --generate-role
Writing kustomize manifests for you to edit...
[root@k8stest app520-operator]# operator-sdk create api --group middleware --version v1alpha1 --kind Redis --generate-role
Writing kustomize manifests for you to edit...
[root@k8stest app520-operator]# operator-sdk create api --group middleware --version v1alpha1 --kind Nacos --generate-role
Writing kustomize manifests for you to edit...
[root@k8stest app520-operator]#

# 创建后端组和其中一个服务类型Gateway
[root@k8stest app520-operator]# operator-sdk create api --group backend --version v1alpha1 --kind Gateway --generate-role
Writing kustomize manifests for you to edit...

# 创建前端组和其中一个服务类型Portal
[root@k8stest app520-operator]# operator-sdk create api --group frontend --version v1alpha1 --kind Portal --generate-role
Writing kustomize manifests for you to edit...
[root@k8stest app520-operator]#

配置服务role

# 查看role情况,
[root@k8stest app520-operator]# ls roles/
gateway  mysql  nacos  portal  postgres  redis
[root@k8stest app520-operator]# tree roles/portal/
roles/portal/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
└── vars
    └── main.yml

7 directories, 6 files
[root@k8stest app520-operator]#

# 主要修改的文件:
# defaults/main.yml 定义默认参数,类似于helm中的value
# tasks/main.yml 配置服务在kubernetes上的编排,类似helm中的template
# 下面以nacos为例,了解其构建过程
# nacos数据库使用mysql,所以先构建mysql
# mysql/defaults/main.yml (定义默认变量的过程)
---
# defaults file for Mysql
name: 'mysql'
image: 'mysql:5.7.36'
root_password: 'Root@2022'
database: 'nacos_config'
user: 'nacos'
password: 'Nacos@2022'
svctype: 'NodePort'
scname: 'nfs-storage'
pvcsize: '10Gi'
requests:
  cpu: '200m'
  mem: '500Mi'
limits:
  cpu: '300m'
  mem: '500Mi'
# mysql/tasks/main.yml (设置部署yaml模板)
	# mysql-cm 包含nacos初始化sql脚本
	# mysql-headless-svc mysql有状态服务dns解析
	# mysql-svc 创建用于nacos连接的mysql服务
	# mysql-sts mysql有状态服务
---
# tasks file for Mysql
- name: deploy mysql-cm
  kubernetes.core.k8s:
    definition:
      kind: ConfigMap
      apiVersion: v1
      metadata:
        name: '{{ name }}-initsql'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      data:
        init.sql: |
          use {{ database }};
          /******************************************/
          /*   数据库全名 = nacos_config   */
          /*   表名称 = config_info   */
          /******************************************/
          CREATE TABLE `config_info` (
            `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
            `data_id` varchar(255) NOT NULL COMMENT 'data_id',
            `group_id` varchar(255) DEFAULT NULL,
            `content` longtext NOT NULL COMMENT 'content',
            `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
            `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
            `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
            `src_user` text COMMENT 'source user',
            `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
            `app_name` varchar(128) DEFAULT NULL,
            `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
            `c_desc` varchar(256) DEFAULT NULL,
            `c_use` varchar(64) DEFAULT NULL,
            `effect` varchar(64) DEFAULT NULL,
            `type` varchar(64) DEFAULT NULL,
            `c_schema` text,
            PRIMARY KEY (`id`),
            UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
           ... ...(下方内容略)


- name: deploy mysql-headless-svc
  kubernetes.core.k8s:
    definition:
      apiVersion: v1
      kind: Service
      metadata:
        name: '{{ name }}-headless'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      spec:
        selector:
          app: '{{ name }}'
        type: ClusterIP
        clusterIP: None
        ports:
        - name: mysql
          port: 3306
          protocol: TCP

- name: deploy mysql-svc
  kubernetes.core.k8s:
    definition:
      apiVersion: v1
      kind: Service
      metadata:
        name: '{{ name }}'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      spec:
        selector:
          app: '{{ name }}'
        type: '{{ svctype }}'
        ports:
        - name: mysql
          port: 3306
          protocol: TCP

- name: deploy mysql-sts
  kubernetes.core.k8s:
    definition:
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: '{{ name }}'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      spec:
        selector:
          matchLabels:
            app: '{{ name }}'
        serviceName: '{{ name }}-headless'
        replicas: 1
        template:
          metadata:
            labels:
              app: '{{ name }}'
          spec:
            initContainers: # 如果存储卷为块存储需要删除/lost+found目录
            - name: initdata
              image: '{{ image }}'
              imagePullPolicy: IfNotPresent
              command:
                [
                  "sh",
                  "-c",
                  "if [ -e /data/lost+found ];then rm /data/lost+found -rf;fi",
                ]
              securityContext:
                privileged: true
              volumeMounts:
                - name: data
                  mountPath: /data
            containers:
            - name: '{{ name }}'
              image: '{{ image }}'
              env:
              - name: MYSQL_ROOT_PASSWORD
                value: '{{ root_password }}'
              - name: MYSQL_DATABASE
                value: '{{ database }}'
              - name: MYSQL_USER
                value: '{{ user }}'
              - name: MYSQL_PASSWORD
                value: '{{ password }}'
              args:
              - --character-set-server=utf8mb4
              - --collation-server=utf8mb4_unicode_ci
              ports:
              - containerPort: 3306
                name: mysql
              resources:
                requests:
                  cpu: '{{ requests.cpu }}'
                  memory: '{{ requests.mem }}'
                limits:
                  cpu: '{{ limits.cpu }}'
                  memory: '{{ limits.mem }}'
              livenessProbe:
                tcpSocket:
                  port: 3306
                initialDelaySeconds: 120
                timeoutSeconds: 5
                successThreshold: 1
                failureThreshold: 10
                periodSeconds: 10
              volumeMounts:
              - name: data
                mountPath: /var/lib/mysql
              - name: localtime
                mountPath: /etc/localtime
              - name: mysqlinit
                mountPath: /docker-entrypoint-initdb.d/init.sql
                subPath: init.sql
            volumes:
            - name: mysqlinit
              configMap:
                name: '{{ name }}-initsql'
            - name: localtime
              hostPath:
                path: /usr/share/zoneinfo/Asia/Shanghai
        volumeClaimTemplates:
        - metadata:
            name: data
            labels:
              app: '{{ name }}'
          spec:
            storageClassName: '{{ scname }}'
            accessModes: ["ReadWriteOnce"]
            resources:
              requests:
                storage: '{{ pvcsize }}'
# 下面是nacos的配置
# nacos/role/default/main.yml
---
# defaults file for Nacos
name: 'nacos'
image: 'nacos/nacos-server:2.0.3'
svctype: 'NodePort'
requests:
  cpu: '300m'
  mem: '2500Mi'
limits:
  cpu: '500m'
  mem: '2500Mi'

mysql_name: mysql
mysql_database: nacos
mysql_user: nacos
mysql_password: Nacos@2022

# nacos/role/task/main.yml
---
# tasks file for Nacos
- name: deploy nacos-headless-svc
  kubernetes.core.k8s:
    definition:
      apiVersion: v1
      kind: Service
      metadata:
        name: '{{ name }}-headless'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      spec:
        selector:
          app: '{{ name }}'
        type: ClusterIP
        clusterIP: None
        ports:
        - name: http
          port: 8848
          protocol: TCP

- name: deploy nacos-svc
  kubernetes.core.k8s:
    definition:
      apiVersion: v1
      kind: Service
      metadata:
        name: '{{ name }}'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      spec:
        selector:
          app: '{{ name }}'
        type: '{{ svctype }}'
        ports:
        - name: http
          port: 8848
          protocol: TCP

- name: deploy nacos-sts
  kubernetes.core.k8s:
    definition:
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: '{{ name }}'
        namespace: '{{ ansible_operator_meta.namespace }}'
        labels:
          app: '{{ name }}'
      spec:
        selector:
          matchLabels:
            app: '{{ name }}'
        serviceName: '{{ name }}-headless'
        replicas: 1
        template:
          metadata:
            labels:
              app: '{{ name }}'
          spec:
            initContainers:    # 在nacos启动前探测mysql服务是否就绪
            - name: check-mysql
              image: '{{ image }}'
              imagePullPolicy: IfNotPresent
              command:
                [
                  "sh",
                  "-c",
                  "until nc -zvw3 {{ mysql_name }} 3306; do echo waiting for mysql; sleep 2; done",
                ]
            containers:
              - name: '{{ name }}'
                image: '{{ image }}'
                env:
                - name: TZ
                  value: 'Asia/Shanghai'
                - name: PREFER_HOST_MODE
                  value: hostname
                - name: MODE
                  value: standalone
                - name: SPRING_DATASOURCE_PLATFORM
                  value: mysql
                - name: MYSQL_SERVICE_HOST
                  value: '{{ mysql_name }}'
                - name: MYSQL_SERVICE_DB_NAME
                  value: '{{ mysql_database }}'
                - name: MYSQL_SERVICE_PORT
                  value: "3306"
                - name: MYSQL_SERVICE_USER
                  value: '{{ mysql_user }}'
                - name: MYSQL_SERVICE_PASSWORD
                  value: '{{ mysql_password }}'
                - name: MYSQL_SERVICE_DB_PARAM
                  value: characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&&useSSL=false
                ports:
                - containerPort: 8848
                  name: http
                resources:
                requests:
                  cpu: '{{ requests.cpu }}'
                  memory: '{{ requests.mem }}'
                limits:
                  cpu: '{{ limits.cpu }}'
                  memory: '{{ limits.mem }}'
                livenessProbe:
                  tcpSocket:
                    port: 8848
                  initialDelaySeconds: 30
                  timeoutSeconds: 3
                  successThreshold: 1
                  failureThreshold: 3
                  periodSeconds: 10
                volumeMounts:
                  - name: localtime
                    mountPath: /etc/localtime
            volumes:
              - name: localtime
                hostPath:
                  path: /usr/share/zoneinfo/Asia/Shanghai

权限配置

默认生成的operator controller对原生kubernetes对象只有下面这些:

# config/rbac/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: manager-role
rules:
  ##
  ## Base operator rules
  ##
  - apiGroups:
      - ""
    resources:
      - secrets
      - pods
      - pods/exec
      - pods/log
    verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - apps
    resources:
      - deployments
      - daemonsets
      - replicasets
      - statefulsets
    verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch

需要添加我们使用的configmap、service和pvc等类型:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: manager-role
rules:
  ##
  ## Base operator rules
  ##
  - apiGroups:
      - ""
    resources:
      - secrets
      - pods
      - pods/exec
      - pods/log
      - services
      - services/finalizers
      - endpoints
      - persistentvolumeclaims
      - events
      - configmaps
      - serviceaccounts
      - namespaces
      - persistentvolumes
      - resourcequotas
    verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - apps
    resources:
      - deployments
      - daemonsets
      - replicasets
      - statefulsets
    verbs:
      - create
      - delete
      - get
      - list
      - patch
      - update
      - watch

构建镜像


未完待续

标签:name,--,yaml,ansible,mysql,operator,yml,sdk
来源: https://www.cnblogs.com/longtds/p/16177465.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有