Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

¡Hola! Recientemente, se han lanzado muchas herramientas de automatización interesantes tanto para crear imágenes de Docker como para implementarlas en Kubernetes. En este sentido, decidí probar GitLab, estudiar a fondo sus capacidades y, por supuesto, configurar el proceso.

Este trabajo fue inspirado en el sitio web. kubernetes.io, que se genera a partir de códigos fuente automáticamente, y para cada solicitud de grupo enviada, el robot genera automáticamente una versión preliminar del sitio con sus cambios y proporciona un enlace para su visualización.

Intenté crear un proceso similar desde cero, pero completamente basado en Gitlab CI y herramientas gratuitas que estoy acostumbrado a usar para implementar aplicaciones en Kubernetes. Hoy por fin os contaré más sobre ellos.

El artículo discutirá herramientas como:
Hugo, qbec, Kaniko, git-cripta и CI de GitLab con la creación de entornos dinámicos.

contenido

  1. Conoce a Hugo
  2. Preparando el archivo Docker
  3. Conociendo a Kaniko
  4. Conociendo qbec
  5. Probando Gitlab-runner con Kubernetes-executor
  6. Implementación de gráficos Helm con qbec
  7. Presentando git-crypt
  8. Crear una imagen de caja de herramientas
  9. Nuestro primer pipeline y montaje de imágenes por etiquetas.
  10. Automatización de la implementación
  11. Artefactos y ensamblaje al presionar para dominar.
  12. Entornos dinámicos
  13. Revisar aplicaciones

1. Conociendo a Hugo

Como ejemplo de nuestro proyecto, intentaremos crear un sitio de publicación de documentación basado en Hugo. Hugo es un generador de contenidos estáticos.

Para aquellos que no estén familiarizados con los generadores estáticos, les contaré un poco más sobre ellos. A diferencia de los motores de sitios web convencionales con una base de datos y algo de PHP, que, cuando lo solicita un usuario, generan páginas sobre la marcha, los generadores estáticos están diseñados de manera un poco diferente. Le permiten tomar fuentes, generalmente un conjunto de archivos en plantillas de temas y marcas de Markdown, y luego compilarlos en un sitio web completamente terminado.

Es decir, como resultado, recibirá una estructura de directorios y un conjunto de archivos HTML generados, que simplemente puede cargar en cualquier alojamiento económico y obtener un sitio web que funcione.

Puedes instalar Hugo localmente y probarlo:

Inicializando un nuevo sitio:

hugo new site docs.example.org

Y al mismo tiempo el repositorio de git:

cd docs.example.org
git init

Hasta ahora, nuestro sitio está impecable y para que aparezca algo en él, primero necesitamos conectar un tema; un tema es solo un conjunto de plantillas y reglas específicas mediante las cuales se genera nuestro sitio.

Para el tema que usaremos Aprender, que, en mi opinión, se adapta perfectamente a un sitio de documentación.

Me gustaría prestar especial atención al hecho de que no necesitamos guardar los archivos del tema en el repositorio de nuestro proyecto; en cambio, simplemente podemos conectarlo usando submódulo git:

git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn

Por lo tanto, nuestro repositorio contendrá solo archivos directamente relacionados con nuestro proyecto, y el tema conectado permanecerá como un enlace a un repositorio específico y una confirmación en él, es decir, siempre se puede extraer de la fuente original y no tener miedo. cambios incompatibles.

Corregimos la configuración. config.toml:

baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"

Ya en esta etapa puedes ejecutar:

hugo server

y en la dirección http://localhost:1313/ Consulte nuestro sitio web recién creado, todos los cambios realizados en el directorio actualizan automáticamente la página abierta en el navegador, ¡muy conveniente!

Intentemos crear una portada en contenido/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Captura de pantalla de la página recién creada

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

Para generar un sitio, simplemente ejecute:

hugo

Contenidos del directorio público/ y será tu sitio web.
Sí, por cierto, agreguémoslo inmediatamente a .gitignore:

echo /public > .gitignore

No olvides confirmar nuestros cambios:

git add .
git commit -m "New site created"

2. Preparando el archivo Docker

Es hora de definir la estructura de nuestro repositorio. Normalmente uso algo como:

.
├── deploy
│   ├── app1
│   └── app2
└── dockerfiles
    ├── image1
    └── image2

  • archivos acoplables/ — contienen directorios con Dockerfiles y todo lo necesario para crear nuestras imágenes de Docker.
  • desplegar/ — contiene directorios para implementar nuestras aplicaciones en Kubernetes

Por lo tanto, crearemos nuestro primer Dockerfile a lo largo de la ruta dockerfiles/sitio web/Dockerfile

FROM alpine:3.11 as builder
ARG HUGO_VERSION=0.62.0
RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-64bit.tar.gz | tar -xz -C /usr/local/bin
ADD . /src
RUN hugo -s /src

FROM alpine:3.11
RUN apk add --no-cache darkhttpd
COPY --from=builder /src/public /var/www
ENTRYPOINT [ "/usr/bin/darkhttpd" ]
CMD [ "/var/www" ]

Como puede ver, el Dockerfile contiene dos DESDE, esta oportunidad se llama construcción en varias etapas y le permite excluir todo lo innecesario de la imagen final de Docker.
Por lo tanto, la imagen final sólo contendrá oscurohttpd (servidor HTTP ligero) y público/ — el contenido de nuestro sitio web generado estáticamente.

No olvides confirmar nuestros cambios:

git add dockerfiles/website
git commit -m "Add Dockerfile for website"

3. Conociendo a Kaniko

Como creador de imágenes de Docker, decidí usar Kaniko, ya que su funcionamiento no requiere un demonio acoplable, y la compilación en sí se puede realizar en cualquier máquina y el caché se puede almacenar directamente en el registro, eliminando así la necesidad de tener un almacenamiento persistente completo.

Para construir la imagen, simplemente ejecute el contenedor con ejecutor kaniko y pásele el contexto de compilación actual; esto también se puede hacer localmente, a través de la ventana acoplable:

docker run -ti --rm 
  -v $PWD:/workspace 
  -v ~/.docker/config.json:/kaniko/.docker/config.json:ro 
  gcr.io/kaniko-project/executor:v0.15.0 
  --cache 
  --dockerfile=dockerfiles/website/Dockerfile 
  --destination=registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1

Donde registro.gitlab.com/kvaps/docs.example.org/website — el nombre de la imagen de la ventana acoplable; después de compilarla, se iniciará automáticamente en el registro de la ventana acoplable.

Parámetro --cache le permite almacenar en caché las capas en el registro de Docker; para el ejemplo dado, se guardarán en registro.gitlab.com/kvaps/docs.example.org/website/cache, pero puedes especificar otra ruta usando el parámetro --repositorio-cache.

Captura de pantalla de Docker-Registro

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

4. Conociendo qbec

qbec es una herramienta de implementación que le permite describir de forma declarativa los manifiestos de su aplicación e implementarlos en Kubernetes. El uso de Jsonnet como sintaxis principal le permite simplificar enormemente la descripción de las diferencias en múltiples entornos y también elimina casi por completo la repetición de código.

Esto puede ser especialmente cierto en los casos en los que necesita implementar una aplicación en varios clústeres con diferentes parámetros y desea describirlos de forma declarativa en Git.

Qbec también le permite representar gráficos de Helm pasándoles los parámetros necesarios y luego operarlos de la misma manera que los manifiestos regulares, incluso puede aplicarles varias mutaciones y esto, a su vez, le permite deshacerse de la necesidad de Utilice ChartMuseum. Es decir, puede almacenar y representar gráficos directamente desde git, donde pertenecen.

Como dije antes, almacenaremos todas las implementaciones en el directorio desplegar/:

mkdir deploy
cd deploy

Inicialicemos nuestra primera aplicación:

qbec init website
cd website

Ahora la estructura de nuestra aplicación se ve así:

.
├── components
├── environments
│   ├── base.libsonnet
│   └── default.libsonnet
├── params.libsonnet
└── qbec.yaml

veamos el archivo qbec.yaml:

apiVersion: qbec.io/v1alpha1
kind: App
metadata:
  name: website
spec:
  environments:
    default:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443
  vars: {}

Aquí nos interesa principalmente entornos específicos, qbec ya creó un entorno predeterminado para nosotros y tomó la dirección del servidor, así como el espacio de nombres de nuestro kubeconfig actual.
Ahora, al implementar en tu préstamo estudiantil entorno, qbec siempre se implementará solo en el clúster de Kubernetes especificado y en el espacio de nombres especificado, es decir, ya no tendrá que cambiar entre contextos y espacios de nombres para realizar una implementación.
Si es necesario, siempre puede actualizar la configuración de este archivo.

Todos sus entornos se describen en qbec.yaml, y en el archivo params.libsonnet, donde dice dónde obtener los parámetros para ellos.

A continuación vemos dos directorios:

  • componentes / — todos los manifiestos de nuestra aplicación se almacenarán aquí; se pueden describir tanto en archivos jsonnet como en archivos yaml normales
  • entornos / — aquí describiremos todas las variables (parámetros) de nuestros entornos.

Por defecto tenemos dos archivos:

  • entornos/base.libsonnet - contendrá parámetros comunes para todos los entornos
  • entornos/default.libsonnet — contiene parámetros anulados para el entorno tu préstamo estudiantil

vamos a abrir entornos/base.libsonnet y agregue parámetros para nuestro primer componente allí:

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1',
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

Creemos también nuestro primer componente. componentes/sitio web.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.website;

[
  {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      replicas: params.replicas,
      selector: {
        matchLabels: {
          app: params.name,
        },
      },
      template: {
        metadata: {
          labels: { app: params.name },
        },
        spec: {
          containers: [
            {
              name: 'darkhttpd',
              image: params.image,
              ports: [
                {
                  containerPort: params.containerPort,
                },
              ],
            },
          ],
          nodeSelector: params.nodeSelector,
          tolerations: params.tolerations,
          imagePullSecrets: [{ name: 'regsecret' }],
        },
      },
    },
  },
  {
    apiVersion: 'v1',
    kind: 'Service',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      selector: {
        app: params.name,
      },
      ports: [
        {
          port: params.servicePort,
          targetPort: params.containerPort,
        },
      ],
    },
  },
  {
    apiVersion: 'extensions/v1beta1',
    kind: 'Ingress',
    metadata: {
      annotations: {
        'kubernetes.io/ingress.class': params.ingressClass,
      },
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      rules: [
        {
          host: params.domain,
          http: {
            paths: [
              {
                backend: {
                  serviceName: params.name,
                  servicePort: params.servicePort,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

En este archivo describimos tres entidades de Kubernetes a la vez, estas son: Despliegue, Service и Ingreso. Si quisiéramos, podríamos ponerlos en diferentes componentes, pero en esta etapa uno será suficiente para nosotros.

sintaxis jsonnet es muy similar al json normal; en principio, el json normal ya es un jsonnet válido, por lo que al principio puede resultarle más fácil utilizar servicios en línea como yaml2json para convertir su yaml habitual en json o, si sus componentes no contienen ninguna variable, pueden describirse en forma de yaml normal.

Al trabajar con jsonnet Recomiendo encarecidamente instalar un complemento para su editor.

Por ejemplo, hay un complemento para vim. vim-jsonnet, que activa el resaltado de sintaxis y se ejecuta automáticamente jsonnetfmt cada vez que guarda (requiere jsonnet instalado).

Todo está listo, ahora podemos comenzar a implementar:

Para ver lo que tenemos, ejecutemos:

qbec show default

En la salida, verá manifiestos yaml renderizados que se aplicarán al clúster predeterminado.

Genial, ahora aplica:

qbec apply default

En la salida siempre verá lo que se hará en su clúster, qbec le pedirá que acepte los cambios escribiendo y podrás confirmar tus intenciones.

¡Nuestra aplicación está lista e implementada!

Si realiza cambios, siempre puede hacer:

qbec diff default

para ver cómo estos cambios afectarán la implementación actual

No olvides confirmar nuestros cambios:

cd ../..
git add deploy/website
git commit -m "Add deploy for website"

5. Probar Gitlab-runner con Kubernetes-executor

Hasta hace poco sólo usaba regular corredor de gitlab en una máquina preparada previamente (contenedor LXC) con shell o docker-executor. Inicialmente, teníamos varios de estos corredores definidos globalmente en nuestro gitlab. Recopilaron imágenes de Docker para todos los proyectos.

Pero como ha demostrado la práctica, esta opción no es la más ideal, tanto en términos de practicidad como de seguridad. Es mucho mejor e ideológicamente más correcto tener corredores separados desplegados para cada proyecto, o incluso para cada entorno.

Afortunadamente esto no supone ningún problema, ya que ahora implementaremos corredor de gitlab directamente como parte de nuestro proyecto directamente en Kubernetes.

Gitlab proporciona un gráfico de timón listo para implementar para implementar gitlab-runner en Kubernetes. Entonces todo lo que necesitas hacer es descubrir ficha de registro para nuestro proyecto en Configuración -> CI / CD -> Corredores y páselo al timón:

helm repo add gitlab https://charts.gitlab.io

helm install gitlab-runner 
  --set gitlabUrl=https://gitlab.com 
  --set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc 
  --set rbac.create=true 
  gitlab/gitlab-runner

Donde:

  • https://gitlab.com - la dirección de su servidor Gitlab.
  • yga8y-jdCusVDn_t4Wxc — token de registro para su proyecto.
  • rbac.create=verdadero — proporciona al corredor la cantidad necesaria de privilegios para poder crear pods para realizar nuestras tareas usando kubernetes-executor.

Si todo se hace correctamente deberías ver un corredor registrado en la sección Los corredores, en la configuración de su proyecto.

Captura de pantalla del corredor agregado

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

¿Es así de simple? - ¡Sí, es así de simple! No más problemas con el registro manual de corredores, de ahora en adelante los corredores se crearán y destruirán automáticamente.

6. Implementar gráficos de Helm con QBEC

Desde que decidimos considerar corredor de gitlab parte de nuestro proyecto, es hora de describirlo en nuestro repositorio Git.

Podríamos describirlo como un componente separado. página web del NDN Collective , pero en el futuro planeamos implementar copias diferentes página web del NDN Collective muy a menudo, a diferencia corredor de gitlab, que se implementará solo una vez por clúster de Kubernetes. Entonces, inicialicemos una aplicación separada para ello:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

Esta vez no describiremos las entidades de Kubernetes manualmente, sino que tomaremos un gráfico de Helm ya preparado. Una de las ventajas de qbec es la capacidad de representar gráficos Helm directamente desde un repositorio Git.

Conectémoslo usando el submódulo git:

git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner

Ahora el directorio proveedor/gitlab-runner Tenemos un repositorio con un gráfico para gitlab-runner.

De manera similar, puede conectar otros repositorios, por ejemplo, el repositorio completo con gráficos oficiales. https://github.com/helm/charts

Describamos el componente. componentes/gitlab-runner.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.gitlabRunner;

std.native('expandHelmTemplate')(
  '../vendor/gitlab-runner',
  params.values,
  {
    nameTemplate: params.name,
    namespace: env.namespace,
    thisFile: std.thisFile,
    verbose: true,
  }
)

El primer argumento para expandirHelmTemplate pasamos la ruta al gráfico, luego parámetros.valores, que tomamos de los parámetros del entorno, luego viene el objeto con

  • nombrePlantilla - Título de lanzamiento
  • espacio de nombres — espacio de nombres transferido al timón
  • Este archivo — un parámetro requerido que pasa la ruta al archivo actual
  • verboso - muestra el comando plantilla de timón con todos los argumentos al renderizar el gráfico

Ahora describamos los parámetros de nuestro componente en entornos/base.libsonnet:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
      },
    },
  },
}

Nota corredorRegistrationToken tomamos de un archivo externo secretos/base.libsonnet, vamos a crearlo:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Comprobemos si todo funciona:

qbec show default

Si todo está en orden, podemos eliminar nuestra versión implementada previamente a través de Helm:

helm uninstall gitlab-runner

y desplegarlo de la misma manera, pero a través de qbec:

qbec apply default

7. Introducción a git-crypt

git-cripta es una herramienta que le permite configurar un cifrado transparente para su repositorio.

Por el momento, nuestra estructura de directorios para gitlab-runner tiene este aspecto:

.
├── components
│   ├── gitlab-runner.jsonnet
├── environments
│   ├── base.libsonnet
│   └── default.libsonnet
├── params.libsonnet
├── qbec.yaml
├── secrets
│   └── base.libsonnet
└── vendor
    └── gitlab-runner (submodule)

Pero almacenar secretos en Git no es seguro, ¿verdad? Entonces necesitamos cifrarlos correctamente.

Generalmente, por el bien de una variable, esto no siempre tiene sentido. Puedes transferir secretos a qbec y a través de las variables de entorno de su sistema CI.
Pero vale la pena señalar que también hay proyectos más complejos que pueden contener muchos más secretos; transferirlos todos a través de variables de entorno será extremadamente difícil.

Además, en este caso no podría hablarles de una herramienta tan maravillosa como git-cripta.

git-cripta También es conveniente porque permite guardar todo el historial de secretos, así como comparar, fusionar y resolver conflictos de la misma forma que estamos acostumbrados a hacerlo en el caso de Git.

Lo primero después de la instalación git-cripta Necesitamos generar claves para nuestro repositorio:

git crypt init

Si tiene una clave PGP, puede agregarse inmediatamente como colaborador de este proyecto:

git-crypt add-gpg-user [email protected]

De esta manera siempre podrás descifrar este repositorio usando tu clave privada.

Si no tiene una clave PGP y no la espera, puede ir al otro lado y exportar la clave del proyecto:

git crypt export-key /path/to/keyfile

Así, cualquiera que tenga un producto exportado archivo de clave podrá descifrar su repositorio.

Es hora de establecer nuestro primer secreto.
Déjame recordarte que todavía estamos en el directorio. implementar/gitlab-runner/, donde tenemos un directorio misterios/, cifremos todos los archivos que contiene, para ello crearemos un archivo secretos/.gitattributes con el siguiente contenido:

* filter=git-crypt diff=git-crypt
.gitattributes !filter !diff

Como puede verse en el contenido, todos los archivos están enmascarados. * será conducido a través git-cripta, excepto la mayoría .gitattributes

Podemos comprobar esto ejecutando:

git crypt status -e

El resultado será una lista de todos los archivos en el repositorio para los cuales el cifrado está habilitado.

Eso es todo, ahora podemos confirmar nuestros cambios de forma segura:

cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"

Para bloquear un repositorio, simplemente ejecute:

git crypt lock

e inmediatamente todos los archivos cifrados se convertirán en algo binario, será imposible leerlos.
Para descifrar el repositorio, ejecute:

git crypt unlock

8. Crea una imagen de caja de herramientas.

Una imagen de caja de herramientas es una imagen con todas las herramientas que usaremos para implementar nuestro proyecto. Será utilizado por el ejecutor de Gitlab para realizar tareas de implementación típicas.

Aquí todo es simple, creemos uno nuevo. dockerfiles/caja de herramientas/Dockerfile con el siguiente contenido:

FROM alpine:3.11

RUN apk add --no-cache git git-crypt

RUN QBEC_VER=0.10.3 
 && wget -O- https://github.com/splunk/qbec/releases/download/v${QBEC_VER}/qbec-linux-amd64.tar.gz 
     | tar -C /tmp -xzf - 
 && mv /tmp/qbec /tmp/jsonnet-qbec /usr/local/bin/

RUN KUBECTL_VER=1.17.0 
 && wget -O /usr/local/bin/kubectl 
      https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/linux/amd64/kubectl 
 && chmod +x /usr/local/bin/kubectl

RUN HELM_VER=3.0.2 
 && wget -O- https://get.helm.sh/helm-v${HELM_VER}-linux-amd64.tar.gz 
     | tar -C /tmp -zxf - 
 && mv /tmp/linux-amd64/helm /usr/local/bin/helm

Como puedes ver, en esta imagen instalamos todas las utilidades que utilizamos para implementar nuestra aplicación. No lo necesitamos aquí a menos que kubectl, pero es posible que desees experimentar con él durante la fase de configuración de la canalización.

Además, para poder comunicarnos con Kubernetes e implementarlo, necesitamos configurar una función para los pods generados por gitlab-runner.

Para hacer esto, vayamos al directorio con gitlab-runner:

cd deploy/gitlab-runner

y agregar un nuevo componente componentes/rbac.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.rbac;

[
  {
    apiVersion: 'v1',
    kind: 'ServiceAccount',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
  },
  {
    apiVersion: 'rbac.authorization.k8s.io/v1',
    kind: 'Role',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
    rules: [
      {
        apiGroups: [
          '*',
        ],
        resources: [
          '*',
        ],
        verbs: [
          '*',
        ],
      },
    ],
  },
  {
    apiVersion: 'rbac.authorization.k8s.io/v1',
    kind: 'RoleBinding',
    metadata: {
      labels: {
        app: params.name,
      },
      name: params.name,
    },
    roleRef: {
      apiGroup: 'rbac.authorization.k8s.io',
      kind: 'Role',
      name: params.name,
    },
    subjects: [
      {
        kind: 'ServiceAccount',
        name: params.name,
        namespace: env.namespace,
      },
    ],
  },
]

También describiremos los nuevos parámetros en entornos/base.libsonnet, que ahora se ve así:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
        runners: {
          serviceAccountName: $.components.rbac.name,
          image: 'registry.gitlab.com/kvaps/docs.example.org/toolbox:v0.0.1',
        },
      },
    },
    rbac: {
      name: 'gitlab-runner-deploy',
    },
  },
}

Nota $.componentes.rbac.nombre se refiere a nombre para componente rbac

Veamos qué ha cambiado:

qbec diff default

y aplique nuestros cambios a Kubernetes:

qbec apply default

Además, no olvides enviar nuestros cambios a git:

cd ../..
git add dockerfiles/toolbox
git commit -m "Add Dockerfile for toolbox"
git add deploy/gitlab-runner
git commit -m "Configure gitlab-runner to use toolbox"

9. Nuestro primer pipeline y montaje de imágenes por etiquetas.

En la raíz del proyecto crearemos .gitlab-ci.yml con el siguiente contenido:

.build_docker_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug-v0.15.0
    entrypoint: [""]
  before_script:
    - echo "{"auths":{"$CI_REGISTRY":{"username":"$CI_REGISTRY_USER","password":"$CI_REGISTRY_PASSWORD"}}}" > /kaniko/.docker/config.json

build_toolbox:
  extends: .build_docker_image
  script:
    - /kaniko/executor --cache --context $CI_PROJECT_DIR/dockerfiles/toolbox --dockerfile $CI_PROJECT_DIR/dockerfiles/toolbox/Dockerfile --destination $CI_REGISTRY_IMAGE/toolbox:$CI_COMMIT_TAG
  only:
    refs:
      - tags

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_TAG
  only:
    refs:
      - tags

Tenga en cuenta que utilizamos GIT_SUBMODULE_STRATEGY: normal para aquellos trabajos en los que necesita inicializar explícitamente los submódulos antes de la ejecución.

No olvides confirmar nuestros cambios:

git add .gitlab-ci.yml
git commit -m "Automate docker build"

Creo que podemos llamar a esto con seguridad una versión. v0.0.1 y agrega la etiqueta:

git tag v0.0.1

Agregaremos etiquetas cada vez que necesitemos lanzar una nueva versión. Las etiquetas en las imágenes de Docker estarán vinculadas a etiquetas de Git. Cada inserción con una nueva etiqueta inicializará la compilación de imágenes con esta etiqueta.

Vamos a hacerlo git push --etiquetas, y veamos nuestra primera canalización:

Captura de pantalla del primer oleoducto

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

Vale la pena llamar su atención sobre el hecho de que el ensamblaje por etiquetas es adecuado para crear imágenes de Docker, pero no para implementar una aplicación en Kubernetes. Dado que se pueden asignar nuevas etiquetas a confirmaciones antiguas, en este caso, inicializar la canalización para ellas conducirá a la implementación de la versión anterior.

Para resolver este problema, normalmente la compilación de imágenes de la ventana acoplable está vinculada a etiquetas y la implementación de la aplicación en una rama. dominar, en el que las versiones de las imágenes recopiladas están codificadas. Aquí es donde puede inicializar la reversión con una simple reversión. dominar-sucursales.

10. Automatización del despliegue

Para que Gitlab-runner descifre nuestros secretos, necesitaremos exportar la clave del repositorio y agregarla a nuestras variables de entorno de CI:

git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echo

Guardaremos la línea resultante en Gitlab; para hacer esto, vayamos a la configuración de nuestro proyecto:
Configuración -> CI / CD -> Variables

Y creemos una nueva variable:

Tipo de Propiedad
Clave
Valor
Robusto
Enmascarado
Lo que hacemos

File
GITCRYPT_KEY
<your string>
true (durante el entrenamiento puedes false)
true
All environments

Captura de pantalla de la variable agregada

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

Ahora actualicemos nuestro .gitlab-ci.yml agregandole:

.deploy_qbec_app:
  stage: deploy
  only:
    refs:
      - master

deploy_gitlab_runner:
  extends: .deploy_qbec_app
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  before_script:
    - base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
  script:
    - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes

deploy_website:
  extends: .deploy_qbec_app
  script:
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes

Aquí hemos habilitado varias opciones nuevas para qbec:

  • --rootear alguna/aplicación — le permite determinar el directorio de una aplicación específica
  • --force:k8s-context __incluster__ - esta es una variable mágica que dice que la implementación ocurrirá en el mismo clúster en el que se ejecuta gtilab-runner. Esto es necesario porque, de lo contrario, qbec intentará encontrar un servidor Kubernetes adecuado en su kubeconfig.
  • --esperar — obliga a qbec a esperar hasta que los recursos que crea entren en el estado Listo y solo entonces salir con un código de salida exitoso.
  • -Sí - simplemente desactiva el shell interactivo ¿Estás seguro? cuando se despliega.

No olvides confirmar nuestros cambios:

git add .gitlab-ci.yml
git commit -m "Automate deploy"

Y despues git push veremos cómo se han desplegado nuestras aplicaciones:

Captura de pantalla del segundo oleoducto

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

11. Artefactos y ensamblaje al empujar para dominar

Normalmente, los pasos descritos anteriormente son suficientes para crear y ofrecer casi cualquier microservicio, pero no queremos agregar una etiqueta cada vez que necesitemos actualizar el sitio. Por lo tanto, tomaremos una ruta más dinámica y configuraremos una implementación de resumen en la rama maestra.

La idea es simple: ahora la imagen de nuestro página web del NDN Collective será reconstruido cada vez que entres dominary luego implementarlo automáticamente en Kubernetes.

Actualicemos estos dos trabajos en nuestro .gitlab-ci.yml:

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/
  only:
    refs:
      - master
      - tags

deploy_website:
  extends: .deploy_qbec_app
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

Tenga en cuenta que hemos añadido un hilo. dominar к refs para trabajos construir_sitio web y ahora usamos $CI_COMMIT_REF_NAME en lugar de $CI_COMMIT_TAG, es decir, nos desvinculamos de las etiquetas en Git y ahora enviaremos una imagen con el nombre de la rama de confirmación que inicializó la canalización. Vale la pena señalar que esto también funcionará con etiquetas, lo que nos permitirá guardar instantáneas de un sitio con una versión específica en el registro acoplable.

Cuando el nombre de la etiqueta acoplable para una nueva versión del sitio no se puede cambiar, aún tenemos que describir los cambios en Kubernetes; de lo contrario, simplemente no volverá a implementar la aplicación desde la nueva imagen, ya que no notará ningún cambio en el manifiesto de implementación.

Opcion —vm:ext-str digest=”$DIGEST” para qbec: le permite pasar una variable externa a jsonnet. Queremos que se vuelva a implementar en el clúster con cada lanzamiento de nuestra aplicación. Ya no podemos usar el nombre de la etiqueta, que ahora no se puede cambiar, ya que debemos estar vinculados a una versión específica de la imagen y activar la implementación cuando cambie.

Aquí nos ayudará la capacidad de Kaniko de guardar una imagen resumida en un archivo (opción --archivo-resumen)
Luego transferiremos este archivo y lo leeremos en el momento de la implementación.

Actualicemos los parámetros de nuestro implementar/sitio web/entornos/base.libsonnet que ahora se verá así:

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website@' + std.extVar('digest'),
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

Listo, ahora cualquier compromiso en dominar inicializa la compilación de la imagen de la ventana acoplable para página web del NDN Collective y luego implementarlo en Kubernetes.

No olvides confirmar nuestros cambios:

git add .
git commit -m "Configure dynamic build"

Lo comprobaremos más tarde git push deberíamos ver algo como esto:

Captura de pantalla de la tubería para master

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

En principio, no necesitamos volver a implementar gitlab-runner con cada pulsación, a menos, por supuesto, que nada haya cambiado en su configuración, arreglémoslo en .gitlab-ci.yml:

deploy_gitlab_runner:
  extends: .deploy_qbec_app
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  before_script:
    - base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
  script:
    - qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
  only:
    changes:
      - deploy/gitlab-runner/**/*

cambios le permitirá monitorear los cambios en implementar/gitlab-runner/ y activará nuestro trabajo sólo si hay alguno

No olvides confirmar nuestros cambios:

git add .gitlab-ci.yml
git commit -m "Reduce gitlab-runner deploy"

git push, eso es mejor:

Captura de pantalla del canal actualizado

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

12. Entornos dinámicos

Es hora de diversificar nuestra cartera con entornos dinámicos.

Primero, actualicemos el trabajo. construir_sitio web en nuestro .gitlab-ci.yml, quitando el bloque , solamente, lo que obligará a Gitlab a activarlo en cualquier confirmación de cualquier rama:

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/

Luego actualiza el trabajo implementar_sitio web, agrega un bloque allí entorno:

deploy_website:
  extends: .deploy_qbec_app
  environment:
    name: prod
    url: https://docs.example.org
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

Esto permitirá a Gitlab asociar el trabajo con pinchar entorno y mostrar el enlace correcto al mismo.

Ahora agreguemos dos trabajos más:

deploy_website:
  extends: .deploy_qbec_app
  environment:
    name: prod
    url: https://docs.example.org
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"

deploy_review:
  extends: .deploy_qbec_app
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: http://$CI_ENVIRONMENT_SLUG.docs.example.org
    on_stop: stop_review
  script:
    - DIGEST="$(cat artifacts/website.digest)"
    - qbec apply review --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
  only:
    refs:
    - branches
  except:
    refs:
      - master

stop_review:
  extends: .deploy_qbec_app
  environment:
    name: review/$CI_COMMIT_REF_NAME
    action: stop
  stage: deploy
  before_script:
    - git clone "$CI_REPOSITORY_URL" master
    - cd master
  script:
    - qbec delete review --root deploy/website --force:k8s-context __incluster__ --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
  variables:
    GIT_STRATEGY: none
  only:
    refs:
    - branches
  except:
    refs:
      - master
  when: manual

Se lanzarán al enviarlos a cualquier rama excepto a la maestra e implementarán la versión preliminar del sitio.

Vemos una nueva opción para qbec: --etiqueta de aplicación — le permite etiquetar las versiones implementadas de la aplicación y trabajar solo dentro de esta etiqueta; al crear y destruir recursos en Kubernetes, qbec operará solo con ellos.
De esta forma no podemos crear un entorno separado para cada revisión, sino simplemente reutilizar el mismo.

Aquí también utilizamos qbec aplicar revisiónen lugar de qbec aplica el valor predeterminado - este es exactamente el momento en el que intentaremos describir las diferencias para nuestros entornos (revisión y predeterminado):

Agregar una estrategia SEO para aparecer en las búsquedas de Google. ambiente en implementar/sitio web/qbec.yaml

spec:
  environments:
    review:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443

Entonces lo declararemos en implementar/sitio web/params.libsonnet:

local env = std.extVar('qbec.io/env');
local paramsMap = {
  _: import './environments/base.libsonnet',
  default: import './environments/default.libsonnet',
  review: import './environments/review.libsonnet',
};

if std.objectHas(paramsMap, env) then paramsMap[env] else error 'environment ' + env + ' not defined in ' + std.thisFile

Y escriba los parámetros personalizados para ello en implementar/sitio web/entornos/review.libsonnet:

// this file has the param overrides for the default environment
local base = import './base.libsonnet';
local slug = std.extVar('qbec.io/tag');
local subdomain = std.extVar('subdomain');

base {
  components+: {
    website+: {
      name: 'example-docs-' + slug,
      domain: subdomain + '.docs.example.org',
    },
  },
}

También echemos un vistazo más de cerca a jobu. parada_revisión, se activará cuando se elimine la rama y para que gitlab no intente realizar el checkout se utiliza GIT_STRATEGY: ninguno, luego clonamos dominar-bifurcar y eliminar reseña a través de él.
Es un poco confuso, pero todavía no he encontrado una manera más hermosa.
Una opción alternativa sería implementar cada reseña en el espacio de nombres de un hotel, que siempre se puede demoler por completo.

No olvides confirmar nuestros cambios:

git add .
git commit -m "Enable automatic review"

git push, git pago -b prueba, prueba de origen de git push, controlar:

Captura de pantalla de entornos creados en Gitlab

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

¿Todo funciona? - genial, elimina nuestra rama de prueba: GIT Checkout Master, origen de git push: prueba, comprobamos que los trabajos de eliminación del entorno funcionaron sin errores.

Aquí me gustaría aclarar de inmediato que cualquier desarrollador en un proyecto puede crear ramas, también puede cambiar .gitlab-ci.yml archivo y acceso a variables secretas.
Por lo tanto, se recomienda encarecidamente permitir su uso sólo en ramas protegidas, por ejemplo en dominar, o cree un conjunto separado de variables para cada entorno.

13. Revisar aplicaciones

Revisar aplicaciones Esta es una característica de GitLab que le permite agregar un botón para cada archivo en el repositorio para verlo rápidamente en un entorno implementado.

Para que aparezcan estos botones es necesario crear un archivo .gitlab/ruta-mapa.yml y describir todas las transformaciones de camino en él; en nuestro caso será muy simple:

# Indices
- source: /content/(.+?)_index.(md|html)/ 
  public: '1'

# Pages
- source: /content/(.+?).(md|html)/ 
  public: '1/'

No olvides confirmar nuestros cambios:

git add .gitlab/
git commit -m "Enable review apps"

git pushy comprobar:

Captura de pantalla del botón Revisar aplicación

Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

¡El trabajo esta terminado!

Fuentes del proyecto:

Gracias por su atención, espero que les haya gustado. Probar nuevas herramientas para crear y automatizar la implementación en Kubernetes

Fuente: habr.com

Añadir un comentario