Files
guides-and-docs/gitea-docker-cicd/README.md
2025-04-04 00:58:13 +00:00

371 lines
14 KiB
Markdown

# Gitea Docker CI/CD Workflow Setup
This guide outlines how to set up a local CI/CD workflow using Gitea running in Docker. The goals are:
* Use Gitea as the primary Git repository.
* Optionally mirror repositories to GitHub.
* Build Docker images for selected repositories (e.g., NextJS applications) using Gitea Runners.
* Host the built Docker images in a local Docker Registry.
* Automatically update running Docker containers when new images are available.
## Prerequisites
* Docker installed and running on your host machine.
* Docker Compose installed.
* A running Gitea instance, preferably managed via Docker Compose.
## Goal Architecture
1. **Developer:** Pushes code to a Gitea repository.
2. **Gitea:**
* (Optional/Manual) Mirrors the repository to GitHub.
* Triggers a Gitea Action workflow on push/tag.
3. **Gitea Runner (Docker):**
* Picks up the workflow job.
* Checks out the code.
* Builds a Docker image using Docker-in-Docker or by mounting the host's Docker socket.
* Pushes the built image to a Local Docker Registry.
4. **Local Docker Registry (Docker):** Stores the built application images.
5. **Watchtower (or similar tool - Docker):** Monitors the Local Docker Registry for new image versions and automatically updates the running application containers.
6. **Application Container (Docker):** Runs the application (e.g., NextJS app) using the image from the Local Docker Registry.
---
## Step 1: Set up Gitea <-> GitHub Synchronization (Mirroring)
Configure Gitea to push changes to a corresponding GitHub repository.
1. **Generate a GitHub Personal Access Token (PAT):**
* Go to GitHub Settings -> Developer settings -> Personal access tokens -> Tokens (classic) or Fine-grained tokens.
* Generate a token with `repo` scope (or specific repository write access). Copy the token securely.
2. **Configure Mirroring in Gitea:**
* Navigate to your Gitea repository.
* Go to `Settings` -> `Repository` -> `Mirror Settings`.
* Click `Add Push Mirror`.
* **Git Remote Repository URL:** Enter the HTTPS URL of your GitHub repo (e.g., `https://github.com/your-username/your-repo.git`).
* **Authorization:**
* **Username:** Your GitHub username.
* **Password/Token:** Paste the GitHub PAT.
* **Sync Interval:** Set the automatic sync frequency (e.g., `1h`, `8h`) or leave blank/`0` for manual sync only.
* Click `Add Mirror Repository`.
3. **Manual Push (Optional):** Click the "Synchronize Now" button next to the mirror entry to push immediately.
*Note: Gitea becomes the source of truth. Changes made directly on GitHub won't sync back automatically without a pull mirror setup.*
---
## Step 2: Set up a Local Docker Registry
This container stores the Docker images built by the runner.
1. **Add Registry Service to `docker-compose.yml`:**
```yaml
# In your docker-compose.yml
version: '3.7'
services:
# ... your other services (like Gitea) ...
registry:
image: registry:2
container_name: local-docker-registry
ports:
# Map to a host port, e.g., 5000. Change if needed.
- "5000:5000"
volumes:
# Persist registry data
- ./registry-data:/var/lib/registry
restart: always
networks:
# Use a common network if Gitea/Runner need to access it by name
- your_shared_network # Optional: Replace with your network name
volumes:
registry-data:
# ... other volumes ...
networks:
your_shared_network: # Optional: Define if not already defined
external: false # Or true if defined elsewhere
```
2. **Run the Registry:**
```bash
docker-compose -f <your-compose-file.yml> up -d registry
```
3. **Configure Docker Daemon for Insecure Registry:**
* Your Docker daemon needs to trust this HTTP registry. Edit `/etc/docker/daemon.json` (create if it doesn't exist):
```json
{
"insecure-registries" : ["YOUR_HOST_IP:5000"]
}
```
* **Important:** Replace `YOUR_HOST_IP` with the actual network IP address of the machine running the registry (e.g., `192.168.1.100`). **Do not use `localhost` or `127.0.0.1`** if the runner is in a separate container.
* Restart the Docker daemon:
```bash
sudo systemctl restart docker
# Or equivalent command for your OS (e.g., restart Docker Desktop)
```
* *Security Note: This uses HTTP and is insecure. For sensitive environments, configure TLS for the registry.*
---
## Step 3: Set up Gitea Runners
Runners execute Gitea Actions workflows.
1. **Enable Actions in Gitea:**
* Ensure Actions are enabled in your Gitea instance's `app.ini` configuration file:
```ini
[actions]
ENABLED = true
```
* Restart your Gitea container after changing `app.ini`.
2. **Get Runner Registration Token:**
* In Gitea, go to `Site Administration` -> `Runners` (for global runners) or Repository `Settings` -> `Actions` -> `Runners` (for repository-specific runners).
* Note the `Registration Token`.
3. **Add Runner Service to `docker-compose.yml`:**
* This example mounts the host's Docker socket.
```yaml
# In your docker-compose.yml
version: '3.7'
services:
# ... your gitea, registry services ...
gitea-runner:
image: gitea/act_runner:latest # Check for the latest official image tag
container_name: gitea-runner
environment:
# Get these from Gitea Admin/Repo Settings -> Actions -> Runners
GITEA_INSTANCE_URL: http://your_gitea_url:3000 # URL to your Gitea instance
GITEA_RUNNER_REGISTRATION_TOKEN: YOUR_REGISTRATION_TOKEN_HERE
GITEA_RUNNER_NAME: my-docker-runner # Optional name
# Define runner capabilities. 'docker' is used in the example workflow.
# Adjust labels as needed for your workflows.
GITEA_RUNNER_LABELS: docker:docker://host.docker.internal/var/run/docker.sock,ubuntu-latest:docker://node:18-bullseye
volumes:
# Mount Docker socket from host - allows runner to use host Docker
- /var/run/docker.sock:/var/run/docker.sock
# Persist runner config
- ./gitea-runner-data:/data
restart: always
networks:
- your_shared_network # Ensure it can reach Gitea and the Registry
volumes:
gitea-runner-data:
# ... other volumes ...
networks:
your_shared_network: # Define if not already defined
# ...
```
* **Replace:** `http://your_gitea_url:3000` with your Gitea instance URL and `YOUR_REGISTRATION_TOKEN_HERE` with the token from Gitea.
* **Labels:** Ensure the `GITEA_RUNNER_LABELS` match the `runs-on:` directive in your workflow files.
4. **Run the Runner:**
```bash
docker-compose -f <your-compose-file.yml> up -d gitea-runner
```
5. **Verify Registration:** Check the `Runners` section in Gitea (Admin or Repository Settings) to confirm the runner is registered and idle.
---
## Step 4: Create Gitea Actions Workflow for Building and Pushing
Define the build process for your application (e.g., NextJS).
1. **Create a `Dockerfile`:**
* Add a `Dockerfile` to the root of your application's repository. Example for NextJS (multi-stage):
```dockerfile
# Stage 1: Build the application
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# Example: Set build-time args if needed
# ARG NEXT_PUBLIC_API_URL
# ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build
# Stage 2: Production image
FROM node:18-alpine
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
COPY --from=builder /app/public ./public
# Automatically leverage output traces to reduce image size
# [https://nextjs.org/docs/advanced-features/output-file-tracing](https://nextjs.org/docs/advanced-features/output-file-tracing)
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Expose the port Next.js runs on
EXPOSE 3000
ENV PORT 3000
# Optional: Run as non-root user
# RUN addgroup --system --gid 1001 nodejs
# RUN adduser --system --uid 1001 nextjs
# USER nextjs
# Command to run the application
CMD ["node", "server.js"]
```
2. **Create the Workflow File:**
* In your repository, create `.gitea/workflows/build-push.yaml`:
```yaml
name: Build and Push Docker Image
on:
push:
branches:
- main # Trigger on pushes to the main branch
tags:
- 'v*.*.*' # Trigger on version tags like v1.0.0
jobs:
build-and-push:
# Use a runner label defined in your runner's environment variables
runs-on: docker
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Optional: Login step if your registry requires authentication
# - name: Log in to local registry
# uses: docker/login-action@v2
# with:
# registry: YOUR_HOST_IP:5000 # Use the same IP as in daemon.json
# username: ${{ secrets.REGISTRY_USERNAME }} # Store credentials in Gitea secrets
# password: ${{ secrets.REGISTRY_PASSWORD }} # Store credentials in Gitea secrets
- name: Determine Image Tag
id: meta
run: |
# Use Git tag if present, otherwise use 'latest' for default branch
if [[ ${GITHUB_REF} == refs/tags/* ]]; then
TAG=${GITHUB_REF#refs/tags/}
else
TAG=latest
fi
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "Using tag: ${TAG}"
- name: Build and Push Docker Image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
# IMPORTANT: Tag format is REGISTRY_HOST:PORT/IMAGE_NAME:TAG
tags: YOUR_HOST_IP:5000/your-app-image-name:${{ steps.meta.outputs.tag }}
# Optional: Add build arguments
# build-args: |
# ARG_NAME=value
cache-from: type=gha # Optional: Enable build cache
cache-to: type=gha,mode=max # Optional: Enable build cache
```
* **Replace:**
* `YOUR_HOST_IP:5000` with your registry's IP and port.
* `your-app-image-name` with your desired image name.
* **`runs-on: docker`**: Ensure this matches a label on your Gitea Runner.
* **Secrets:** If using registry authentication, store credentials (`REGISTRY_USERNAME`, `REGISTRY_PASSWORD`) in the Gitea repository's `Settings` -> `Secrets`.
3. **Commit and Push:** Add `.gitea/workflows/build-push.yaml` and your `Dockerfile` to the repository, commit, and push to Gitea.
4. **Monitor Workflow:** Check the `Actions` tab in your Gitea repository to view the workflow execution status.
---
## Step 5: Automatically Update Containers with Watchtower
Use Watchtower to monitor the local registry and redeploy containers when a new image is available.
1. **Add Application and Watchtower Services to `docker-compose.yml`:**
```yaml
# In your docker-compose.yml
version: '3.7'
services:
# ... your gitea, registry, runner services ...
# Your Application container (Example: NextJS)
my-app:
# IMPORTANT: Use the full image path from your local registry
image: YOUR_HOST_IP:5000/your-app-image-name:latest # Watchtower tracks this tag
container_name: my-app-container
ports:
- "8080:3000" # Map host port 8080 to container port 3000 (adjust as needed)
restart: always
networks:
- your_shared_network
labels:
# Required label for Watchtower to manage this container when using --label-enable
- "com.centurylinklabs.watchtower.enable=true"
# Watchtower service
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
volumes:
# Mount Docker socket to allow Watchtower to manage other containers
- /var/run/docker.sock:/var/run/docker.sock
# Optional: Mount Docker config if registry requires authentication
# Make sure docker login has been run on the host first
# - $HOME/.docker/config.json:/config.json
# Check every 5 minutes (300s), remove old images, only update containers with the specific label
command: --cleanup --label-enable --interval 300
restart: always
networks:
- your_shared_network # Needs network access if registry needs authentication
networks:
your_shared_network: # Define if not already defined
# ...
```
* **Replace:** `YOUR_HOST_IP:5000/your-app-image-name:latest` with your registry IP, port, image name, and the tag Watchtower should monitor (usually `latest`).
* **`image:` directive:** Ensure your application service (`my-app`) uses the full image path from your local registry.
* **`labels:`:** Add the `com.centurylinklabs.watchtower.enable=true` label to any container you want Watchtower to auto-update.
* **`command:`:** Use `--label-enable` for safety, so Watchtower only touches labeled containers. Adjust `--interval` as needed.
2. **Run Application and Watchtower:**
```bash
# Bring up the application and Watchtower (and others if not running)
docker-compose -f <your-compose-file.yml> up -d
```
---
## Summary Workflow
1. Push code (matching `on:` criteria in workflow) to Gitea.
2. Gitea triggers the Actions workflow (`build-push.yaml`).
3. Gitea Runner executes the job: checks out code, builds image, pushes tagged image (e.g., `:latest` or `:v1.x.x`) to the local registry (`YOUR_HOST_IP:5000`).
4. Watchtower (running via Docker) polls the registry.
5. If Watchtower detects a new image digest for the tag specified in your application container's `image:` directive (e.g., `:latest`), it pulls the new image and redeploys the application container using the updated image.
6. (Optional) Gitea pushes code changes to GitHub via the configured mirror.
You now have an automated build and deployment pipeline hosted locally with Gitea! Remember to replace all placeholders (like `YOUR_HOST_IP`, image names, network names, tokens) with your actual values.