ProHoster > Blog > Administración > Probando novas ferramentas para crear e automatizar a implantación en Kubernetes
Probando novas ferramentas para crear e automatizar a implantación en Kubernetes
Ola! Recentemente, lanzáronse moitas ferramentas de automatización interesantes tanto para crear imaxes de Docker como para a súa implantación en Kubernetes. Neste sentido, decidín xogar con GitLab, estudar a fondo as súas capacidades e, por suposto, configurar o pipeline.
Este traballo inspirouse na páxina web kubernetes.io, que se xera a partir de códigos fonte automaticamente, e para cada solicitude de grupo enviada, o robot xera automaticamente unha versión de vista previa do sitio cos teus cambios e proporciona unha ligazón para ver.
Tentei crear un proceso similar desde cero, pero construído enteiramente con Gitlab CI e ferramentas gratuítas que estou afeito usar para implementar aplicacións en Kubernetes. Hoxe por fin falareivos máis sobre eles.
O artigo discutirá ferramentas como: Hugo, qbec, kaniko, git-crypt и GitLab CI coa creación de ambientes dinámicos.
Como exemplo do noso proxecto, tentaremos crear un sitio de publicación de documentación construído sobre Hugo. Hugo é un xerador de contido estático.
Para aqueles que non estean familiarizados cos xeradores estáticos, vouvos contar un pouco máis sobre eles. A diferenza dos motores de sitios web convencionais con base de datos e algo de PHP, que, cando o solicita un usuario, xeran páxinas sobre a marcha, os xeradores estáticos están deseñados un pouco diferente. Permítenche coller fontes, normalmente un conxunto de ficheiros en modelos de marcas e temas de Markdown, e compilalos nun sitio web completamente rematado.
É dicir, como resultado, recibirá unha estrutura de directorios e un conxunto de ficheiros HTML xerados, que simplemente pode cargar a calquera hospedaxe barata e obter un sitio web que funcione.
Podes instalar Hugo localmente e probalo:
Iniciando un novo sitio:
hugo new site docs.example.org
E ao mesmo tempo o repositorio git:
cd docs.example.org
git init
Ata agora, o noso sitio é prístino e para que apareza algo nel, primeiro necesitamos conectar un tema; un tema é só un conxunto de modelos e regras especificadas polas que se xera o noso sitio.
Para o tema que utilizaremos Aprender, que, na miña opinión, é perfectamente axeitado para un sitio de documentación.
Gustaríame prestar especial atención ao feito de que non necesitamos gardar os ficheiros do tema no repositorio do noso proxecto; no seu lugar, simplemente podemos conectalo usando submódulo git:
Así, o noso repositorio conterá só ficheiros directamente relacionados co noso proxecto, e o tema conectado permanecerá como unha ligazón a un repositorio específico e un commit nel, é dicir, sempre se pode extraer da fonte orixinal e non ter medo de cambios incompatibles.
Imos corrixir a configuración config.toml:
baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"
Xa nesta fase podes executar:
hugo server
E no enderezo http://localhost:1313/ Consulte o noso sitio web recén creado, todos os cambios realizados no directorio actualizan automaticamente a páxina aberta no navegador, moi cómodo!
Tentemos crear unha portada content/_index.md:
# My docs site
## Welcome to the docs!
You will be very smart :-)
Captura de pantalla da páxina recén creada
Para xerar un sitio, só tes que executar:
hugo
Contidos do directorio público/ e será o teu sitio web.
Si, por certo, engadímolo inmediatamente .gitignore:
echo /public > .gitignore
Non esquezas comprometer os nosos cambios:
git add .
git commit -m "New site created"
2. Preparando o Dockerfile
É hora de definir a estrutura do noso repositorio. Normalmente uso algo como:
dockerfiles/ — conteñen directorios con Dockerfiles e todo o necesario para construír as nosas imaxes Docker.
despregar/ — contén directorios para implementar as nosas aplicacións en Kubernetes
Así, crearemos o noso primeiro Dockerfile ao longo do camiño dockerfiles/website/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 podes ver, o Dockerfile contén dous DE, esta oportunidade chámase construción en varias etapas e permítelle excluír todo o que non sexa necesario da imaxe docker final.
Así, a imaxe final só conterá escurohttpd (servidor HTTP lixeiro) e público/ — o contido do noso sitio web xerado de forma estática.
Non esquezas comprometer os nosos cambios:
git add dockerfiles/website
git commit -m "Add Dockerfile for website"
3. Coñecendo kaniko
Como creador de imaxes docker, decidín usar kaniko, xa que o seu funcionamento non require un daemon docker, e a propia construción pódese realizar en calquera máquina e a caché pódese almacenar directamente no rexistro, eliminando así a necesidade de ter un almacenamento persistente completo.
Para crear a imaxe, só tes que executar o contedor con executor kaniko e pásalle o contexto de compilación actual; isto tamén se pode facer localmente, mediante docker:
Onde registry.gitlab.com/kvaps/docs.example.org/website — o nome da súa imaxe docker; despois da creación, lanzarase automaticamente no rexistro docker.
Parámetro --caché permítelle almacenar en caché capas no rexistro docker; para o exemplo dado, gardaranse registry.gitlab.com/kvaps/docs.example.org/website/cache, pero pode especificar outro camiño usando o parámetro --cache-repo.
Captura de pantalla de docker-registry
4. Coñecendo qbec
Qbec é unha ferramenta de implementación que che permite describir de forma declarativa os manifestos da túa aplicación e implementalos en Kubernetes. Usar Jsonnet como sintaxe principal permíteche simplificar moito a descrición das diferenzas en varios ambientes e tamén elimina case por completo a repetición de código.
Isto pode ser especialmente certo nos casos nos que precisa implantar unha aplicación en varios clústeres con diferentes parámetros e quere describilos declarativamente en Git.
Qbec tamén permítelle renderizar gráficos Helm pasándolles os parámetros necesarios e despois operalos do mesmo xeito que os manifestos habituais, incluíndo que pode aplicarlle varias mutacións, e isto, á súa vez, permítelle desfacerse da necesidade de use ChartMuseum. É dicir, pode almacenar e renderizar gráficos directamente desde git, onde pertencen.
Como dixen anteriormente, almacenaremos todas as implementacións no directorio despregar/:
mkdir deploy
cd deploy
Imos inicializar a nosa primeira aplicación:
qbec init website
cd website
Agora a estrutura da nosa aplicación ten o seguinte aspecto:
Aquí nos interesa principalmente espec.ambientes, qbec xa creou un ambiente predeterminado para nós e tomou o enderezo do servidor, así como o espazo de nomes do noso kubeconfig actual.
Agora ao implementar a defecto ambiente, qbec sempre se implementará só no clúster de Kubernetes especificado e no espazo de nomes especificado, é dicir, xa non terá que cambiar entre contextos e espazos de nomes para realizar unha implementación.
Se é necesario, sempre pode actualizar a configuración deste ficheiro.
Todos os teus ambientes descríbense en qbec.yaml, e no arquivo params.libsonnet, onde di onde obter os parámetros para eles.
A continuación vemos dous directorios:
compoñentes / — todos os manifestos da nosa aplicación almacenaranse aquí; pódense describir tanto en ficheiros jsonnet como en ficheiros yaml habituais
ambientes/ — aquí describiremos todas as variables (parámetros) dos nosos contornos.
Por defecto temos dous ficheiros:
ambientes/base.libsonnet - conterá parámetros comúns para todos os ambientes
ambientes/default.libsonnet — contén parámetros anulados para o ambiente defecto
imos abrir ambientes/base.libsonnet e engade alí parámetros para o noso primeiro compoñente:
Neste ficheiro describimos tres entidades de Kubernetes á vez, estas son: desenvolvemento, servizo и Ingreso. Se quixeramos, poderiamos poñelos en diferentes compoñentes, pero neste momento un será suficiente para nós.
sintaxe jsonnet é moi semellante ao json normal, en principio, o json normal xa é jsonnet válido, polo que ao principio pode ser máis doado usar servizos en liña como yaml2json para converter o teu yaml habitual en json ou, se os teus compoñentes non conteñen ningunha variable, pódense describir en forma de yaml normal.
Ao traballar con jsonnet Recomendo encarecidamente instalar un complemento para o teu editor
Por exemplo, hai un complemento para vim vim-jsonnet, que activa o resaltado de sintaxe e execútase automaticamente jsonnet fmt cada vez que garda (require jsonnet instalado).
Todo está listo, agora podemos comezar a implementar:
Para ver o que temos, imos correr:
qbec show default
Na saída, verá os manifestos yaml renderizados que se aplicarán ao clúster predeterminado.
Genial, agora aplica:
qbec apply default
Na saída sempre verá o que se fará no seu clúster, qbec pediralle que estea de acordo cos cambios escribindo y poderás confirmar as túas intencións.
A nosa aplicación está lista e implementada!
Se realizas cambios, sempre podes facer:
qbec diff default
para ver como afectarán estes cambios á implantación actual
Non esquezas comprometer os nosos cambios:
cd ../..
git add deploy/website
git commit -m "Add deploy for website"
5. Probando Gitlab-runner con Kubernetes-executor
Ata hai pouco só usaba regular gitlab-runner nunha máquina preparada previamente (contedor LXC) con shell ou docker-executor. Inicialmente, tiñamos varios destes corredores definidos globalmente no noso gitlab. Recolleron imaxes docker para todos os proxectos.
Pero como demostrou a práctica, esta opción non é a máis idónea, tanto a nivel práctico como de seguridade. É moito mellor e ideoloxicamente máis correcto dispor de corredores separados para cada proxecto, ou mesmo para cada ambiente.
Afortunadamente, isto non é un problema en absoluto, xa que agora implementarémolo gitlab-runner directamente como parte do noso proxecto directamente en Kubernetes.
Gitlab ofrece un gráfico de timón preparado para implementar gitlab-runner en Kubernetes. Así que todo o que tes que facer é descubrir token de rexistro para o noso proxecto en Configuración -> CI / CD -> Corredores e pásao ao timón:
yga8y-jdCusVDn_t4Wxc - token de rexistro para o teu proxecto.
rbac.create=true — proporciona ao corredor a cantidade necesaria de privilexios para poder crear pods para realizar as nosas tarefas usando kubernetes-executor.
Se todo está feito correctamente, deberías ver un corredor rexistrado na sección Runners, na configuración do teu proxecto.
Captura de pantalla do corredor engadido
É tan sinxelo? -Si, é así de sinxelo! Non hai máis problemas co rexistro de corredores manualmente, a partir de agora os corredores crearanse e destruiranse automaticamente.
6. Implementa gráficos Helm con QBEC
Xa que decidimos considerar gitlab-runner parte do noso proxecto, é hora de describilo no noso repositorio Git.
Poderiamos describilo como un compoñente separado , pero no futuro temos previsto despregar diferentes copias moi a miúdo, a diferenza gitlab-runner, que só se implementará unha vez por clúster de Kubernetes. Entón, imos inicializar unha aplicación separada para iso:
cd deploy
qbec init gitlab-runner
cd gitlab-runner
Esta vez non imos describir as entidades de Kubernetes manualmente, pero levaremos un gráfico de Helm preparado. Unha das vantaxes de qbec é a capacidade de renderizar gráficos Helm directamente desde un repositorio de Git.
Agora o directorio vendedor/gitlab-runner Temos un repositorio cun gráfico para gitlab-runner.
Do mesmo xeito, pode conectar outros repositorios, por exemplo, todo o repositorio con gráficos oficiais https://github.com/helm/charts
Imos describir o compoñente components/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,
}
)
O primeiro argumento para expandir HelmTemplate pasamos o camiño ao gráfico, entón parámetros.valores, que tomamos dos parámetros do entorno, despois vén o obxecto
Modelo de nome - título da publicación
espazo de nomes — espazo de nomes transferido a helm
este ficheiro — un parámetro obrigatorio que pasa a ruta ao ficheiro actual
detallada - mostra o comando modelo de timón con todos os argumentos ao renderizar o gráfico
Agora imos describir os parámetros do noso compoñente en ambientes/base.libsonnet:
Pero almacenar segredos en Git non é seguro, non é? Polo tanto, necesitamos cifralos correctamente.
Normalmente, por mor dunha variable, isto non sempre ten sentido. Podes transferir segredos a qbec e a través das variables de ambiente do seu sistema CI.
Pero vale a pena sinalar que tamén hai proxectos máis complexos que poden conter moitos máis segredos; transferilos todos a través de variables de ambiente será extremadamente difícil.
Ademais, neste caso non sería capaz de falarvos dunha ferramenta tan marabillosa como git-crypt.
git-crypt Tamén é conveniente porque permite gardar todo o historial de segredos, así como comparar, fusionar e resolver conflitos do mesmo xeito que estamos acostumados a facer no caso de Git.
Primeiro despois da instalación git-crypt necesitamos xerar claves para o noso repositorio:
git crypt init
Se tes unha clave PGP, podes engadirte inmediatamente como colaborador deste proxecto:
Deste xeito sempre podes descifrar este repositorio usando a túa clave privada.
Se non tes unha clave PGP e non a esperas, podes ir cara a outro lado e exportar a clave do proxecto:
git crypt export-key /path/to/keyfile
Así, calquera que teña un exportado ficheiro de chave poderá descifrar o seu repositorio.
É hora de establecer o noso primeiro segredo.
Permíteme lembrarche que aínda estamos no directorio deploy/gitlab-runner/, onde temos un directorio segredos/, imos cifrar todos os ficheiros nel, para iso crearemos un ficheiro segredos/.gitattributes co seguinte contido:
Como se pode ver no contido, todos os ficheiros están enmascarados * será conducido git-crypt, agás a maioría .gitattributes
Podemos comprobar isto executando:
git crypt status -e
A saída será unha lista de todos os ficheiros do repositorio para os que o cifrado está activado
Isto é todo, agora podemos confirmar os nosos cambios con seguridade:
cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"
Para bloquear un repositorio, só tes que executar:
git crypt lock
e inmediatamente todos os ficheiros cifrados converteranse en algo binario, será imposible lelos.
Para descifrar o repositorio, execute:
git crypt unlock
8. Crea unha imaxe de caixa de ferramentas
Unha imaxe da caixa de ferramentas é unha imaxe con todas as ferramentas que utilizaremos para implementar o noso proxecto. Usaráo o corredor de Gitlab para realizar tarefas típicas de implantación.
Todo é sinxelo aquí, imos crear un novo dockerfiles/toolbox/Dockerfile co seguinte contido:
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 podedes ver, nesta imaxe instalamos todas as utilidades que utilizamos para implantar a nosa aplicación. Non o necesitamos aquí a menos que kubectl, pero pode querer xogar con el durante a fase de configuración da canalización.
Ademais, para poder comunicarnos con Kubernetes e implementar nel, necesitamos configurar un rol para os pods xerados por gitlab-runner.
Para facelo, imos ao directorio con gitlab-runner:
cd deploy/gitlab-runner
e engade un novo compoñente components/rbac.jsonnet:
Teña en conta que usamos GIT_SUBMODULE_STRATEGY: normal para aqueles traballos nos que precisa inicializar explícitamente os submódulos antes da execución.
Creo que podemos chamar a isto unha versión con seguridade v0.0.1 e engade a etiqueta:
git tag v0.0.1
Engadiremos etiquetas sempre que necesitemos lanzar unha nova versión. As etiquetas das imaxes de Docker estarán ligadas ás etiquetas Git. Cada pulsación cunha nova etiqueta inicializará a creación de imaxes con esta etiqueta.
Fagámolo git push --tags, e vexamos o noso primeiro pipeline:
Captura de pantalla da primeira canalización
Paga a pena chamar a súa atención sobre o feito de que a montaxe por etiquetas é adecuada para crear imaxes docker, pero non é adecuada para implementar unha aplicación en Kubernetes. Dado que se poden asignar novas etiquetas a commits antigas, neste caso, a inicialización da canalización para elas levará á implantación da versión antiga.
Para resolver este problema, normalmente a creación de imaxes docker está ligada a etiquetas e a implantación da aplicación nunha rama mestre, na que se codifican as versións das imaxes recollidas. Aquí é onde podes inicializar a restauración cun simple reverso mestre- ramas.
10. Automatización do despregamento
Para que Gitlab-runner descifra os nosos segredos, necesitaremos exportar a clave do repositorio e engadila ás nosas variables de ambiente CI:
--root some/app — permite determinar o directorio dunha aplicación específica
--force:k8s-context __incluster__ - esta é unha variable máxica que di que o despregamento terá lugar no mesmo clúster no que se está a executar gtilab-runner. Isto é necesario porque, en caso contrario, qbec tentará atopar un servidor Kubernetes axeitado no teu kubeconfig
--agarda — obriga a qbec a esperar ata que os recursos que crea pasen ao estado Listo e só despois saia cun código de saída exitoso.
—si - simplemente desactiva o shell interactivo Estás seguro? cando se despregue.
E despois git push veremos como se implantaron as nosas aplicacións:
Captura de pantalla da segunda canalización
11. Artefactos e montaxe ao empurrar para dominar
Normalmente, os pasos descritos anteriormente son suficientes para crear e ofrecer case calquera microservizo, pero non queremos engadir unha etiqueta cada vez que necesitemos actualizar o sitio. Polo tanto, tomaremos unha ruta máis dinámica e configuraremos un despregamento de resumo na rama mestra.
A idea é sinxela: agora a imaxe do noso será reconstruído cada vez que presione mestree, a continuación, implementarase automaticamente en Kubernetes.
Imos actualizar estes dous traballos no noso .gitlab-ci.yml:
Teña en conta que engadimos un fío mestre к ref para postos de traballo build_website e agora usamos $CI_COMMIT_REF_NAME en vez de $CI_COMMIT_TAG, é dicir, estamos desvinculados das etiquetas en Git e agora empurraremos unha imaxe co nome da rama de commit que inicializou a canalización. Cómpre sinalar que isto tamén funcionará con etiquetas, o que nos permitirá gardar instantáneas dun sitio cunha versión específica no rexistro docker.
Cando o nome da etiqueta docker para unha nova versión do sitio non se pode modificar, aínda temos que describir os cambios en Kubernetes, se non, simplemente non volverá implementar a aplicación desde a nova imaxe, xa que non notará ningún cambio na imaxe. manifesto de implantación.
Opción —vm:ext-str digest="$DIGEST" para qbec: permítelle pasar unha variable externa a jsonnet. Queremos que se vuelva a implantar no clúster con cada versión da nosa aplicación. Xa non podemos usar o nome da etiqueta, que agora pode ser inmutable, xa que necesitamos estar ligados a unha versión específica da imaxe e activar a implantación cando esta cambie.
Aquí axudaranos a capacidade de Kaniko para gardar unha imaxe resumida nun ficheiro (opción --ficheiro-resumo)
Despois transferiremos este ficheiro e leremos no momento da implantación.
Actualicemos os parámetros do noso deploy/website/environments/base.libsonnet que agora quedará así:
Feito, agora calquera compromiso mestre inicializa a compilación da imaxe docker para , e despois implementalo en Kubernetes.
Non esquezas comprometer os nosos cambios:
git add .
git commit -m "Configure dynamic build"
Comprobaremos máis tarde git push deberíamos ver algo así:
Captura de pantalla da canalización para master
En principio, non necesitamos volver a implantar gitlab-runner con cada empuxe, a menos que, por suposto, nada cambiou na súa configuración, imos corrixilo en .gitlab-ci.yml:
É hora de diversificar a nosa carteira con ambientes dinámicos.
Primeiro, imos actualizar o traballo build_website no noso .gitlab-ci.yml, quitándolle o bloque só, o que obrigará a Gitlab a activalo en calquera commit en calquera rama:
Lanzaranse ao ser enviados a calquera rama excepto ao mestre e despregarán a versión de vista previa do sitio.
Vemos unha nova opción para qbec: --app-tag — permítelle etiquetar as versións implantadas da aplicación e traballar só con esta etiqueta; ao crear e destruír recursos en Kubernetes, qbec só funcionará con eles.
Deste xeito non podemos crear un ambiente separado para cada revisión, senón simplemente reutilizar o mesmo.
Aquí tamén usamos qbec aplicar revisión, en vez de qbec aplica por defecto - este é exactamente o momento no que tentaremos describir as diferenzas para os nosos ambientes (revisión e predeterminado):
Engadimos revisar ambiente en deploy/website/qbec.yaml
Despois declararémolo en deploy/website/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
E anote os parámetros personalizados para iso deploy/website/environments/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',
},
},
}
Vexamos tamén máis de cerca jobu stop_review, activarase cando se elimine a rama e para que gitlab non intente facer a compra, úsase GIT_STRATEGY: ningunha, máis tarde clonamos mestre-ramificar e eliminar a revisión a través dela.
É un pouco confuso, pero aínda non atopei un xeito máis bonito.
Unha opción alternativa sería implementar cada revisión nun espazo de nomes de hotel, que sempre pode ser demolido por completo.
Non esquezas comprometer os nosos cambios:
git add .
git commit -m "Enable automatic review"
git push, git checkout -b proba, proba de orixe git push, comproba:
Captura de pantalla dos contornos creados en Gitlab
Todo funciona? - xenial, elimina a nosa rama de proba: git checkout master, git push origin :test, comprobamos que os traballos de eliminación do ambiente funcionaron sen erros.
Aquí gustaríame aclarar inmediatamente que calquera desenvolvedor nun proxecto pode crear ramas, tamén pode cambiar .gitlab-ci.yml arquivo e acceder a variables secretas.
Polo tanto, recoméndase encarecidamente permitir o seu uso só para ramas protexidas, por exemplo en mestreou cree un conxunto separado de variables para cada ambiente.
13. Revisar aplicacións
Revisar aplicacións Esta é unha función de GitLab que che permite engadir un botón para cada ficheiro do repositorio para visualizalo rapidamente nun ambiente despregado.
Para que aparezan estes botóns, cómpre crear un ficheiro .gitlab/route-map.yml e describe todas as transformacións de camiños nel; no noso caso será moi sinxelo: