你的时间吗CI/CD管道在开发测试期间,部署会阻碍您的发展吗?本文展示了一种更快的开发方式Spring Boot微服务使用裸机库伯内特斯在您自己的开发机器上运行的集群。

成功的秘诀
这是关于Ansible和Kubernetes系列的第四篇文章。在……里头遍熄灯号》中,我解释了如何在Windows中的Linux虚拟机上安装和运行Ansible。后续的帖子演示了如何使用Ansible在Ubuntu 20.04上运行本地Kubernetes集群。它在运行Linux的本地Linux和基于Windows的虚拟机上进行了测试。当您的devbox有一个单独的网络适配器供虚拟机专用时,最后提到的方法效果最佳。

本文继续讨论前一篇文章并在由一个控制平面和一个工作器组成的集群上进行测试。因此不需要运行HAProxy的前端代理,并在清单中将其注释掉。

该代码可从以下网站获取开源代码库.

什么时候停靠码头,什么时候不停靠码头
更快部署到本地基础架构的秘诀是削减不需要的内容。例如,一个人真的需要完全安装Docker来烘焙映像吗?是否应该将每次构建产生的图像推送到正式的Docker存储库中?是否需要CI/CD平台?

让我们先回答最后一个问题。Maven一开始就设想了持续集成和持续部署,应该能够取代Jenkins等CI/CD平台进行本地部署。现在,众所周知,所有Maven问题都可以通过改变依赖关系或添加插件来解决。我们不在jar-hell中,所以答案肯定是插件。这移转构建插件对示例Spring Boot就是这样做的微服务我们将部署:

<build>
    <plugins>
        <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>3.1.4</version>
            <configuration>
                <from>
                    <image>openjdk:11-jdk-slim</image>
                </from>
                <to>
                    <image>docker_repo:5000/rbuhrmann/hello-svc</image>
                    <tags>
                        <tag>latest10</tag>
                    </tags>
                </to>
                <allowInsecureRegistries>false</allowInsecureRegistries>
            </configuration>
        </plugin>
    </plugins>
</build>

这里我们可以看到Jib Maven插件是如何配置的,以便将图像烘焙并推送到私有Docker repo中。然而,插件也可以从命令行操纵。这个Ansible shell任务在一个或多个Spring Boot微服务上循环,并执行以下操作:

- name: Git checkouts
  ansible.builtin.git:
    repo: "{{ item.git_url }}"
    dest: "~/{{ item.name }}"
    version: "{{ item.git_branch }}"
  loop:
    "{{ apps }}"

****************

- name: Run JIB builds
      ansible.builtin.command: "mvn clean compile jib:buildTar -Dimage={{ item.name }}:{{ item.namespace }}"
      args:
        chdir: "~/{{ item.name }}/{{ item.jib_dir }}"
      loop:
        "{{ apps }}"

第一个任务克隆,而最后一个任务集成Docker映像。但是,它不会将映像推送到Docker报告。相反,它把它当成焦油球扔掉了。因此,我们正在从循环中移除Docker repo。由于我们的Kubernetes集群使用Containerd(Docker的衍生产品)作为其容器守护进程,因此我们只需要将tar球直接加载到container d中。事实证明这种应用程序是存在的。它叫做ctr并且可以从Ansible:

- name: Load images into containerd
  ansible.builtin.command: ctr -n=k8s.io images import jib-image.tar
  args:
    chdir: "/home/ansible/{{ item.name }}/{{ item.jib_dir }}/target"
  register: ctr_out
  become: true
  loop:
    "{{ apps }}"

到目前为止,任务一直在worker节点上执行。在worker节点上构建映像似乎很愚蠢,但请记住:

它涉及本地测试,很少需要一个以上的K8s工作人员——构建不会在多台机器上进行。
Jib构建的基础图像小于通常从Docker repo中提取的生成图像。这导致了更快的下载和疏忽的上传时间,因为映像是直接加载到worker节点的容器守护进程中的。
下载Git和Maven所花费的时间分摊到所有部署中,因此随着使用量的增加,所占的时间比例越来越小。
绕过CI/CD平台,例如詹金斯或者与其他应用程序共享的Git运行程序可以大大节省构建和部署时间。
我宣布,你是部署
到目前为止,我只显示了Ansible任务,但是没有显示所接收的变量声明。现在是将部分投入:

apps:
  - name: hello1
    git_url: https://github.com/jrb-s2c-github/spinnaker_tryout.git
    jib_dir: hello_svc
    image: s2c/hello_svc
    namespace: env1
    git_branch: kustomize
    application_properties:
      application.properties: |
        my_name: LocalKubeletEnv1
  - name: hello2
    git_url: https://github.com/jrb-s2c-github/spinnaker_tryout.git
    jib_dir: hello_svc
    image: s2c/hello_svc
    namespace: env2
    config_map_path:
    git_branch: kustomize
    application_properties:
      application.properties: |
        my_name: LocalKubeletEnv2

它涉及一系列Spring Boot微服务的DevOps特征,这些微服务负责克隆、集成、部署和编排。我们已经看到Ansible如何处理前三个问题。所有剩下的就是Ansible创建Kubernetes的任务deployments, services,以及application.properties ConfigMaps:

- name: Create k8s namespaces
  remote_user: ansible
  kubernetes.core.k8s:
    kubeconfig: /home/ansible/.kube/config
    name: "{{ item.namespace }}"
    api_version: v1
    kind: Namespace
    state: present
  loop:
    "{{ apps }}"


- name: Create application.property configmaps
  kubernetes.core.k8s:
    kubeconfig: /home/ansible/.kube/config
    namespace: "{{ item.namespace }}"
    state: present
    definition:
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: "{{ item.name }}-cm"
      data:
        "{{ item.application_properties }}"
  loop:
    "{{ apps }}"

- name: Create deployments
  kubernetes.core.k8s:
    kubeconfig: /home/ansible/.kube/config
    namespace: "{{ item.namespace }}"
    state: present
    definition:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        creationTimestamp: null
        labels:
          app: "{{ item.name }}"
        name: "{{ item.name }}"
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: "{{ item.name }}"
        strategy: { }
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: "{{ item.name }}"
          spec:
            containers:
              - image: "{{ item.name }}:{{ item.namespace }}"
                name: "{{ item.name }}"
                resources: { }
                imagePullPolicy: IfNotPresent
                volumeMounts:
                  - mountPath: /config
                    name: config
            volumes:
              - configMap:
                  items:
                    - key: application.properties
                      path: application.properties
                  name: "{{ item.name }}-cm"
                name: config
      status: { }
  loop:
    "{{ apps }}"

- name: Create services
  kubernetes.core.k8s:
    kubeconfig: /home/ansible/.kube/config
    namespace: "{{ item.namespace }}"
    state: present
    definition:
      apiVersion: v1
      kind: List
      items:
        - apiVersion: v1
          kind: Service
          metadata:
            creationTimestamp: null
            labels:
              app: "{{ item.name }}"
            name: "{{ item.name }}"
          spec:
            ports:
              - port: 80
                protocol: TCP
                targetPort: 8080
            selector:
              app: "{{ item.name }}"
            type: ClusterIP
          status:
            loadBalancer: {}
  loop:
    "{{ apps }}"

这些任务在控制平面上运行,并使用配置两个微服务的编排kubernetes.core.k8s可完成的任务。为了说明如何将同一应用程序的不同功能分支同时部署到不同的名称空间,使用了相同的图像。但是,每一个都在其中部署了不同的内容application.properties。还可以指定不同的Git分支。

应该注意的是,没有什么可以阻止我们将两个或更多微服务部署到单个名称空间中,以便为现代JavaScript前端提供后端服务。

这imagePullPolicy设置为"IfNotPresent"。因为ctr已经将映像直接部署到容器运行时,就不需要从Docker repo中提取映像。

入口路由
入口实例用于向集群外部的客户端公开来自多个名称空间的微服务。入口及其路由规则的声明在上面部分列出的输入声明中较低:

ingress:
  host: www.demo.io
  rules:
    - service: hello1
      namespace: env1
      ingress_path: /env1/hello
      service_path: /
    - service: hello2
      namespace: env2
      ingress_path: /env2/hello
      service_path: /

注意事项DNS名称应在您的控制之下,或者不能作为DNS条目输入到世界任何地方的DNS服务器上。在这种情况下,流量可能会从群集发送到该IP地址。

这service变量应该与输入声明上半部分中相关微服务的名称相匹配。这ingress路径是客户端应该用来访问服务和servicepath是应该路由到的Spring控制器的端点。

解释和执行上述声明的任务包括:

- name: Create ingress master
  kubernetes.core.k8s:
    kubeconfig: /home/ansible/.kube/config
    namespace: default
    state: present
    definition:
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        name: ingress-master
        annotations:
          nginx.org/mergeable-ingress-type: "master"
      spec:
        ingressClassName: nginx
        rules:
          - host: "{{ ingress.host }}"

- name: Create ingress minions
  kubernetes.core.k8s:
    kubeconfig: /home/ansible/.kube/config
    namespace: "{{ item.namespace }}"
    state: present
    definition:
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        annotations:
          nginx.ingress.kubernetes.io/rewrite-target: " {{ item.service_path }} "
          nginx.org/mergeable-ingress-type: "minion"
        name: "ingress-{{ item.namespace }}"
      spec:
        ingressClassName: nginx
        rules:
          - host: "{{ ingress.host }}"
            http:
              paths:
                - path: "{{ item.ingress_path }}"
                  pathType: Prefix
                  backend:
                    service:
                      name: "{{ item.service }}"
                      port:
                        number: 80
  loop:
    "{{ ingress.rules }}"

我们继续上一篇文章中的内容,使用Nginx入口控制器和MetalLB建立入口路由。再次使用Ansible循环结构来满足多种路由规则。在这种情况下,路由将从/env1/hello路由前进到env1命名空间中的Hello K8s服务,并从/env2/hello路由前进到env2命名空间中的Hello K8s服务。

使用Nginx可合并入口类型来实现到不同名称空间的路由。更多可以阅读这里但基本上,一个人会把Ingresses解释为主人或奴才之一。因此,多个实例组合在一起可以实现复杂的路由,如上所示。

入口路由可以并且很可能不同于Spring控制器的端点。这里的情况确实如此,需要第二个注释来将入口路由更改为控制器监听的端点:

nginx.ingress.kubernetes.io/rewrite-target: " {{ item.service_path }} "

这是样品控制器:

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from " + name;
    }

    @Value(value = "${my_name}")
    private String name;

}

因为my_name字段中定义的内容替换为application.properties微服务的每个实例都有不同的价值,因此我们预计每个K8S服务/部署都会发出不同的欢迎消息。在不同的入口路由中,我们发现情况确实如此:
在这里插入图片描述
秘密之类的
您的Git存储库可能需要令牌认证。对于这种情况,应该将整个git URL添加到可旋转拱顶:

apps:
  - name: mystery
    git_url: "{{ vault_git_url }}"
    jib_dir: harvester
    image: s2c/harvester
    namespace: env1
    git_branch: main
    application_properties:
      application.properties: |
        my_name: LocalKubeletEnv1

变量的内容vault_git_url在中加密all/vault.yaml并且可以使用以下内容进行编辑:

ansible-vault edit jetpack/group_vars/all/vault.yaml

输入保管库的密码并添加/编辑URL以包含您的身份验证令牌:

vault_git_url: https://AUTH TOKEN@github.com/jrb-s2c-github/demo.git

幕后发生的事情足以保证一整篇帖子。然而,简言之,group_vars是为库存组定义的,每个库存组的var和vaults都位于与该组同名的子目录中。“all”子文件夹充当此构造之外的所有其他托管服务器的总称。因此,我们库存的主组和工人组只需要“all”子目录即可使用同一个存储库。

因此,可以遵循相同的方法来加密应该添加到application.propertiesSpring Boot的。

结论
我们已经看到了如何通过绕过CI/CD期间使用的某些步骤和技术将Sprint Boot微服务更快地部署到本地基础架构。

可以采用多个名称空间来允许部署不同版本的微服务架构。当不同环境的秘密在发挥作用时,一些想法将不得不给出。本文的重点是本地环境和如何使用group的描述vars不同的环境有不同的秘密超出了讨论范围。这可能是未来文章的主题。

如果您需要帮助安装和运行钻机,请随时在LinkedIn上与我联系。感谢您的阅读!

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐