add first doc
This commit is contained in:
371
gitea-docker-cicd/README.md
Normal file
371
gitea-docker-cicd/README.md
Normal file
@@ -0,0 +1,371 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user