371 lines
14 KiB
Markdown
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. |