Ir a la última, a veces no es baladí, ya que los cambios que se han producido pueden ser suficientes para optar por esta vía, si nuestro proyecto esta comenzando. Necesitaba comenzar a hacer testing, o mejor dicho CI o integración continua con Laravel, usando mi propio servidor Gitlab CE. Prefería tener Gitlab Runner en un VPS dedicado por seguridad y porque no decirlo comodidad. Pero también necesitaba que mi proyecto usará MySQL 8.0 y PHP 7.3 y aquí comenzaron los problemas
Contenidos
Gitlab y Gitlab Runner
Instalar Gitlab EE
Bien lo primero es instalar en un VPS Worker en mi caso prefería usar una Ubuntu Bionic 18.04 LTS porque después de 25 años, al final me he decantado por Ubuntu por que para un programador novel, puede ser infinitamente más sencillo esta distribución y sus actualizaciones que los horrores a los que nos tiene acostumbrado RedHat y sus forks, o Debian y su retardo con la modernidad.
Instalar Gitlab en Ubuntu Bionic es tan sencillo como seguir sus instrucciones. Lo importante es después es configurarlo para usarlo seguro y con certificado Let’s Encrypt
Instalar Gitlab Runner
Para instalar Gitlab Runner en otro VPS Entrepeneur opte por la misma distribución, y usar los repositorios oficiales de Gitlab, además de en lugar de usar la opción de instalar docker durante la instalación de Ubuntu Server, instalar docker desde el sistema oficial, ya que Ubuntu instala la versión con snap lo que no me gusta mucho para este tipo de paquetes.
Crear un proyecto Laravel de prueba
La mejor manera de volverse loco cuando uno es aprendiz del tema Gitlab+Docker, es no seguir a pie juntillas sus manuales, porque al final nos volvernos locos, y ni decir tiene, comenzar a buscar en Medium, y otros sitios de bloggers, pues muchas veces es copy & paste, y con errores garrafales.
Sí, hay que leer el manual de Gitlab + Gitlab Runner y si hay que tenerlos a mano, pero mejor te lo cuento que seguro que ahorras un montón de tiempo.Abdelkarim Mateos
Crear un repositorio vacio en nuestro Gitlab
Instalar Laravel
Yo prefiero usar laravel installer pero siente libre de usar el sistema que desees
abkrim@abkrim-nox$ laravel new laravel-test Crafting application... Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 80 installs, 0 updates, 0 removals - Installing doctrine/inflector (v1.3.0): Loading from cache - Installing doctrine/lexer (1.0.2): Loading from cache - Installing dragonmantank/cron-expression (v2.3.0): Loading from cache ... ... ... filp/whoops suggests installing whoops/soap (Formats errors as SOAP responses) sebastian/global-state suggests installing ext-uopz (*) phpunit/phpunit suggests installing phpunit/php-invoker (^2.0) Generating optimized autoload files > @php -r "file_exists('.env') || copy('.env.example', '.env');" > @php artisan key:generate --ansi Application key set successfully. > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: beyondcode/laravel-dump-server Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully. Application ready! Build something amazing. abkrim@abkrim-nox$
Usar la nueva instalación para usar el repositorio de Gitlab
cd ~/path/laravel-test git init git remote add origin git@gitlab.midominio.tld:root/laravel-test.git git add . git commit -m "Initial commit" git push -u origin master
Preparar nuestro proyecto para unas pruebas mínimas
Javascript
Necesitamos tener instalado NPM o Yarn, para actualizar los paquetes javascript en la raíz de nuestro proyecto.
abkrim@abkrim-nox $ npm install npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"}) added 1001 packages from 485 contributors and audited 17129 packages in 37.831s found 0 vulnerabilities
Database con MySQL 8.0
Necesitamos preparar nuestra aplicación para generar algún tipo de datos con MySQL, que es lo que queremos probar, así que que mejor que el scaffolding de autenticación de Laravel.
Editamos el fichero .env
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=my_database DB_USERNAME=myuser DB_PASSWORD=mypassword # Sustituye <> por el valor apropiado
Generamos el scaffolding
abkrim@abkrim-nox? php artisan make:auth Authentication scaffolding generated successfully. abkrim@abkrim-nox? php artisan migrate Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (0.05 seconds) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (0.05 seconds)
Configurando nuestro proyecto Gitlab CI
Obtención del token en nuestro Gitlab
En nuestro proyecto tendremos que ajustar nuestro runner para configurarlo y obtener el token.
Proyecto > Settings > CI / CD -> Runners -> Set up a specific Runner manually
Copiamos el token que ahora necesitaremos para configurar nuestro runner en la máquina de Gitlab Runner que hemos instalado.
Registrar el runner del proyecto
Ahora debemos ir a la consola de nuestro VPS Gitlab Runner y registrar nuestro runner. En principio con el comando debería bastar pero hay opciones que no consiguo en modo interactivo, (¿alguien se anima a comentar la solución?)
? sudo gitlab-runner register \ --non-interactive \ --url "https://gitlab.dominio.tld/" \ --registration-token "NuEsTrO_ToKeN_CaMbIaLo" \ --executor "docker" \ --docker-image alpine:latest \ --description "laravel-test" \ --tag-list "laravel,php,mysql,npm" \ --run-untagged="true" \ --locked="false" \ --access-level="not_protected" Runtime platform arch=amd64 os=linux pid=13854 revision=a987417a version=12.2.0 Running in system-mode. Registering runner... succeeded runner=bxAAbr3B Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Esto escribirá o añadirá el fichero /etc/gitlab-runner/config.toml donde se guarda la configuración de los dockers de gitlab runner
Vamos a editar este fichero manualmente para añadir alguna cosa que vimos muy provechosa del post de Oh Dear relativa a los procesos concurrentes
? sudo nano /etc/gitlab-runner/config.toml concurrent = 2 check_interval = 0 [session_server] session_timeout = 1800 [[runners]] name = "laravel-test" url = "https://gitlab.dominio.tld" token = "NuEsTrO_ToKeN_CaMbIaLo" executor = "docker" [runners.custom_build_dir] [runners.docker] tls_verify = false image = "alpine:latest" privileged = false disable_entrypoint_overwrite = false oom_kill_disable = false disable_cache = false volumes = ["/cache"] shm_size = 0 [runners.cache] [runners.cache.s3] [runners.cache.gcs]
Problema de resolución
Sin embargo esa configuración me dio problemas localizados en:fatal: unable to access 'https://gitlab-ci-token:[MASKED]@gitlab.castris.com/root/laravel-test.git/': Could not resolve host: gitlab.castris.comSe trata de modificar el fichero de configuración añadiendo en la sección [runners.dock] según obtuvimos en el control de problemas de Gitlab
dns_search = ["gitlab.castris.com"] extra_hosts = ["gitlab.castris.com:176.31.31.225"]En mi repo te dejo la versión definitiva del fichero config.toml, y se hago algún cambio o mejora alli estará. Si quieres conocer más sobre el formato del archivo TOM, puedes leer su documentación
Crear nuestro fichero .gitlab-ci.yml
Bueno hora de configurar nuestro proyecto para trabajar en el escenario que queremos.stages: - preparation - building - testing - security variables: MYSQL_ROOT_PASSWORD: root MYSQL_USER: myuser MYSQL_PASSWORD: mypassword MYSQL_DATABASE: mydatabase DB_HOST: mysql cache: key: $CI_BUILD_REF_NAM composer: stage: preparation services: - mysql:8.0 image: edbizarro/gitlab-ci-pipeline-php:7.3-alpine script: - php -v - composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts - cp .env.example .env - php artisan key:generate artifacts: paths: - vendor/ - .env expire_in: 5 days when: always cache: paths: - vendor/ yarn: stage: preparation image: edbizarro/gitlab-ci-pipeline-php:7.3-alpine script: - yarn --version - yarn install --pure-lockfile artifacts: paths: - node_modules/ expire_in: 1 days when: always cache: paths: - node_modules/ build-assets: stage: building image: edbizarro/gitlab-ci-pipeline-php:7.3-alpine # Download the artifacts for these jobs dependencies: - composer - yarn script: - yarn --version - yarn run production --progress false artifacts: paths: - public/css/ - public/js/ - public/fonts/ - public/mix-manifest.json expire_in: 1 days when: always db-seeding: stage: building services: - mysql:8.0 image: edbizarro/gitlab-ci-pipeline-php:7.3-alpine # Download the artifacts for these jobs dependencies: - composer - yarn script: - php artisan migrate:fresh --seed artifacts: paths: - ./storage/logs # for debugging expire_in: 1 days when: on_failure phpunit: stage: testing services: - mysql:8.0 image: edbizarro/gitlab-ci-pipeline-php:7.3-alpine # Download the artifacts for these jobs dependencies: - build-assets - composer - db-seeding script: - php -v - sudo cp /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.bak - echo "" | sudo tee /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini - ./vendor/phpunit/phpunit/phpunit --version - php -d short_open_tag=off ./vendor/phpunit/phpunit/phpunit -v --colors=never --stderr - sudo cp /usr/local/etc/php/conf.d/docker-php-ext-xdebug.bak /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini artifacts: paths: - ./storage/logs # for debugging expire_in: 1 days when: on_failure
Credenciales de MySQL en Laravel en modo CI
Para esto necesitaremos pasar en el fichero .env.example los valores de conexión a MySQL en modo CI. Es importante no usar root como usuario, y usar un nombre de base de datos por proyecto, si estamos trabajando distintos proyectos.DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=mydatabase DB_USERNAME=myuser DB_PASSWORD=mypasswordAtención al DB_HOST que produce mas de un quebradero de cabeza Y al mismo tiempo en la sección de la etapa de preparación, en el composer definimos su copia
cp .env.example .env
Explicación
- En la parte superior dividimos el proceso CI en varias etapas. Esto es bueno cuando trabajamos en equipo, y también para identificar potenciales problemas. No es necesario pero es una buena practica.
- Asignamos las variables necesarias para nuestra aplicación y su uso con MySQL
- Definimos la variable key para el cache
- Preparamos las distintas etapas para las que vamos a usar un repositorio bastante bueno para Laravel CI, dbizarro/gitlab-ci-pipeline-php con algunas modificaciones en cuanto al su recomendación de configuración y las de OnMyDear.app
- Preparamos el fichero .env.example, para ser usado como remisor de las variables de uso de MySQL
Probando la integración CI
Hacemos una pequeña modificación en cualquier fichero que no suponga un problemagit addSi acudimos al rato a nuestro proyecto en Gitlab, > Pipelines veremos el fallogit commit -m 'Primera prueba' git push
$ php artisan migrate:fresh --seed Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name does not resolve (SQL: SHOW FULL TABLES WHERE table_type = 'BASE TABLE') at /builds/root/laravel-test/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664 660| // If an exception occurs when attempting to run a query, we'll format the error 661| // message to include the bindings with SQL, which will make this exception a 662| // lot more helpful to the developer instead of just the database's errors. 663| catch (Exception $e) { > 664| throw new QueryException( 665| $query, $this->prepareBindings($bindings), $e 666| ); 667| } 668| Exception trace: 1 PDOException::("PDO::__construct(): php_network_getaddresses: getaddrinfo failed: Name does not resolve") /builds/root/laravel-test/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 2 PDO::__construct("mysql:host=mysql;port=3306;dbname=laravel", "root", "secret", []) /builds/root/laravel-test/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70 Please use the argument -v to see more details. Uploading artifacts... ./storage/logs: found 3 matching files Uploading artifacts to coordinator... ok id=346 responseStatus=201 Created token=TF3GSFJV ERROR: Job failed: exit code 1El error es debido a problemas de comunicación entre PHP y MySQL 8.0 que requieren un cambio en el arranque de mysql que permita el sistema antiguo de contraseñas en tanto y cuanto, PHP y multitud de librerias se pongan al día con el nuevo sistema de contraseñas de MySQl. Este cambio implica añadir la linea default-authentication-plugin = mysql_native_password al fichero de configuración de mysql. Como no me gustan dar muchas vueltas, y prefiero centrarme en usar las buenas herramientas de terceros, como el repositorio propuesto, la mejor opción que vi, era la de crear mi propia imagen de mysql basada en la oficial, pero con la modificación. Así que procedemos a crear el container y subirlo a nuestra cuenta de Docker.
Creación de un container de MySQL modificado
Docker Gitlab CI PHP, es un repositorio basado en imágenes oficiales, asi que no es complicado preparar nuestro container basado en una imagen oficial y modificada para tal efecto, en tanto y cuanto pase la tormenta del tema de la autenticación. Mi imagen creada esta en el Hub de Docker, abkrim /mysql8_legacy_password y en GitHub puedes ver su código que es muy simpleFROM mysql:8.0 MAINTAINER Abdelkarim Mateosmysqld_password.cnfCOPY mysqld_password.cnf /etc/mysql/conf.d/mysqld_password.cnf ENV MYSQL_ROOT_PASSWORD=123456
[mysqld] default-authentication-plugin = mysql_native_password collation-server = utf8mb4_general_ci character-set-server = utf8mb4
Construcción del repositorio en Gitub de nuestro container y enlazamos con Hub Docker
Creamos el repositorio en GitHUb y después lo enlazamos con nuestro repositorio dockerhub. Ahora sólo falta subir a nuestro repositorio de github, nuestro proyecto de Docker.$ mkdir mysql8-legacy-password $ cd $_ $ git init $ git remote add origin git@github.com:Dockerhub, connectará con Github, y cuando detecte un cambio comenzará un despliegue del container. Si no falla, en unos minutos (a veces puede tardar una hora, si hay muchos proyectos en cola) si no hay fallos, estará disponible y recibirás los avisos por correo electronico./mysql8-legacy-password.git $ git add . $ git commit -m "Initial commit" $ git push -u origin master
Testing en local
Antes es buena práctica comprobar que tu imagen es correcta. Asi que puede construirla de forma fácil condocker build --no-cache -t mysql8_legacy_password .
Actualización de nuestro .gitlab-ci.yml
La cosa es sencilla. Se trata de modificar todas las llamadas a la imagen oficial de mysql por nuestra imagen cambiando mysql:8.0 por {name: ‘abkrim/mysql8_legacy_password’, alias: ‘mysql’}sed -i -e "s/mysql\:8\.0/\{name\:\ \'abkrim\/mysql8\_legacy\_password\'\,\ alias\:\ \'mysql\'\}/g" .gitlab-ci.yml git add .gitlab-ci.yml git commit -m 'Update .gitlab-ci.yml' git pushPasado un rato, si todo es correcto veremos el resultado
Mostrar la información del CI en nuestro README.md
En Settings -> CI/CD -> General Pipelines se pueden obtener los códigos para que se visualice en nuestro repo o también en la página webToDo
- Mejorar y comprender el sistema de cache en docker
- Otros métodos para alterar imágenes o despliegues
Agradecimientos
Como siempre gracias Unsplash y a Charles 🇵🇭 por la imagen que sirve de portada, que edite gracias a Canva
Comparte este articulo en