Integración de Gitea con Docker Registry
Muchas empresas dependen de terceros para el versionado de su código, para hacer pruebas sobre el mismo y para crear contenedores docker. Para reducir esta dependencia, he decidido mostrar cómo mantengo mis proyectos en mis propios entornos, obteniendo mayor autonomía, estabilidad y seguridad en mi forma de trabajar.
Programas
Gitea
Gitea es una excelente alternativa a Github, pudiendo mantener tantos repositorios como se quiera y teniendo prácticamente todas las funcionalidades de Github. Con esto tendremos una solución para mantener nuestro código y versionado del mismo en nuestra infraestructura.
Gitea Act Runner
Gitea Act Runner es un servicio que permite correr Gitea actions, para tener pipelines parecidos a los que se tienen en Github. Este servicio puede correrse en otro servidor, para separar el servidor CI/CD del servidor que tiene el versionado del código. A continuación muestro un docker compose que permite correr Gitea junto a Gitea Act Runner
1services:
2 server:
3 image: docker.io/gitea/gitea:latest
4 container_name: gitea
5 environment:
6 - USER_UID=1000
7 - USER_GID=1000
8 - ROOT_URL=https://gitea.gabrielcachadina.com/
9 - GITEA__SERVICE__REGISTER_MANUAL_CONFIRM=true
10 restart: always
11 networks:
12 - gitea
13 volumes:
14 - ./Gitea:/data
15 - /etc/timezone:/etc/timezone:ro
16 - /etc/localtime:/etc/localtime:ro
17 ports:
18 - "3000:3000"
19 - "222:22"
20
21 runner:
22 image: docker.io/gitea/act_runner:nightly
23 environment:
24 GITEA_INSTANCE_URL: "https://gitea.gabrielcachadina.com"
25 GITEA_RUNNER_REGISTRATION_TOKEN: "{{ gitea_runner }}"
26 GITEA_RUNNER_NAME: "Gitea Runner Localhost"
27 volumes:
28 - ./GiteaRunner/data:/data
29 - /var/run/docker.sock:/var/run/docker.sock
30 networks:
31 - gitea
32 restart: always
33
34networks:
35 gitea:
36 external: falseSe deberá sustituir el token de Gitea y deberá añadirse el runner dentro del panel de administrador de Gitea.

Docker Registry
Docker Registry permite almacenar contenedores Docker de los servicios que creemos, en mi caso yo almaceno los contenedores en el mismo servidor que corre Gitea, aunque este servicio podría desplegarse en un servidor independiente. Para correr este servicio se podría usar el siguiente docker-compose:
1---
2services:
3 registry:
4 image: registry:latest
5 ports:
6 - "5000:5000"
7 environment:
8 REGISTRY_AUTH: htpasswd
9 REGISTRY_AUTH_HTPASSWD_REALM: Registry
10 REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
11 REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /registry-data
12 volumes:
13 - ./DockerRegistry/auth:/auth
14 - ./DockerRegistry/registry-data:/registry-data
15 restart:
16 alwaysPara generar la contraseña se deberá generar el archivo registry.password, de la siguiente forma:
1htpasswd -B -c /auth/registry.password "$DOCKERREGISTRY_USER" "$DOCKERREGISTRY_PASS"Aplicación Real
De forma de ejemplo aquí muestro un código de un microservicio que uso actualmente para retornar todos los datos geográficos de una pareja de coordenadas mediante una API. Este código pequeño solo contiene un añadido inusual, la carpeta .gitea/workflows.
Dentro de la carpeta tengo un workflow que, al actualizar el código, inicia una máquina Ubuntu, crea el contenedor de docker y lo sube a Docker Registry:
1name: Build and Push Docker Image (Go)
2
3on:
4 push:
5 branches:
6 - main
7
8jobs:
9 build-and-push:
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Manual Git clone from Gitea
14 run: |
15 git clone https://USERNAME:${{ secrets.GITEA_TOKEN }}@gitea.gabrielcachadina.com/GabrielCachadina/ms_gps_location_data.git
16 cd ms_gps_location_data
17 git checkout main
18
19 - name: Log in to Docker Registry
20 working-directory: ./ms_gps_location_data
21 run: |
22 echo "${{ secrets.DOCKER_PASSWORD }}" | docker login dockerregistry.gabrielcachadina.com -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
23
24 - name: Clean up Docker images
25 working-directory: ./ms_gps_location_data
26 run: |
27 docker image prune -f
28 docker image rm dockerregistry.gabrielcachadina.com/ms_gps_location_data:latest || true
29
30 - name: Build Docker image
31 working-directory: ./ms_gps_location_data
32 run: |
33 docker build --no-cache -t dockerregistry.gabrielcachadina.com/ms_gps_location_data:latest .
34
35 - name: Push Docker image
36 working-directory: ./ms_gps_location_data
37 run: docker push dockerregistry.gabrielcachadina.com/ms_gps_location_data:latestFinalmente, para correr este servicio en otros servidores, se podría hacer de forma manual, pero yo prefiero levantar los micro servicios mediante ansible, en concreto creo un rol para cada servicio, quedando este rol como:
1.
2├── defaults
3│ └── main.yml
4├── meta
5│ └── main.yml
6└── tasks
7 └── main.ymly el archivo tasks/main.yml:
1---
2################# Directory Config #################
3- name: Ensure application directory exists
4 file:
5 path: "{{ app_dest }}"
6 state: directory
7 owner: '1000'
8 group: '1000'
9 mode: '0755'
10
11- name: Ensure logs directory exists
12 file:
13 path: "{{ app_dest }}/logs"
14 state: directory
15 owner: '1000'
16 group: '1000'
17 mode: '0755'
18################# Save the image localy #################
19- name: Login to private Docker registry on controller
20 delegate_to: localhost
21 run_once: true
22 community.docker.docker_login:
23 registry_url: "dockerregistry.gabrielcachadina.com"
24 username: "{{ dockerregistry_user }}"
25 password: "{{ dockerregistry_pass }}"
26 become: false
27
28- name: Pull Docker image on controller (ensure it's present)
29 delegate_to: localhost
30 run_once: true
31 community.docker.docker_image:
32 name: "{{ image_name }}"
33 source: pull
34 force_source: true
35 become: false
36
37- name: Save Docker image on controller to temporary file
38 delegate_to: localhost
39 run_once: true
40 command: >
41 docker save {{ image_name }} -o /tmp/{{ container_name }}.tar
42 become: false
43
44- name: Save Docker image on controller to temporary file
45 delegate_to: localhost
46 run_once: true
47 command: >
48 docker save {{ image_name }} -o /tmp/{{ container_name }}.tar
49 become: false
50
51- name: Change the permissions for the tarball
52 delegate_to: localhost
53 run_once: true
54 file:
55 path: "/tmp/{{ container_name }}.tar"
56 mode: "0644"
57 become: false
58
59################# Push and Compile #################
60
61- name: Remove old container
62 community.docker.docker_container:
63 name: "{{ container_name }}"
64 state: absent
65 force_kill: true
66
67- name: Remove existing image from remote
68 community.docker.docker_image:
69 name: "{{ image_name }}"
70 state: absent
71
72- name: Copy Docker image tarball to remote host
73 copy:
74 src: "/tmp/{{ container_name }}.tar"
75 dest: "/tmp/{{ container_name }}.tar"
76 mode: '0644'
77
78- name: Load Docker image from tarball
79 community.docker.docker_image:
80 name: "{{ image_name }}"
81 load_path: "/tmp/{{ container_name }}.tar"
82 source: load
83 force_source: true
84
85- name: Remove temporary tar file from remote
86 file:
87 path: "/tmp/{{ container_name }}.tar"
88 state: absent
89
90
91- name: Start the container
92 community.docker.docker_container:
93 name: "{{ container_name }}"
94 image: "{{ image_name }}"
95 state: started
96 restart_policy: "{{ restart_policy }}"
97 privileged: yes
98 network_mode: host
99 env:
100 TZ: Europe/Madrid
101 volumes:
102 - "{{ app_dest }}/logs:/app/logs"
103 ports:
104 - "22003:22003"
105
106################# Clean from Localhost #################
107
108- name: Remove temporary tar file from controller (cleanup)
109 delegate_to: localhost
110 run_once: true
111 file:
112 path: "/tmp/{{ container_name }}.tar"
113 state: absent
114 become: false
115
116- name: Remove Docker image from controller (cleanup)
117 delegate_to: localhost
118 run_once: true
119 community.docker.docker_image:
120 name: "{{ image_name }}"
121 state: absent
122 force_absent: true
123 become: falseComo nota final, remarcar que estos servicios mostrados están todos tras un reverse proxy usando NGINX, para conseguir seguir HTTPS en los mismos y así evitar problemas de seguridad. Además de esto los puertos que usan los servicios están deshabilitados dentro del firewall de los servicios.