Initial commit
This commit is contained in:
32
.editorconfig
Normal file
32
.editorconfig
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# All files
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# Bash, shell, sh
|
||||||
|
[*.{bash,sh}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
# JSON
|
||||||
|
[*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
# YAML
|
||||||
|
[*.{yaml,yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
|
||||||
|
# Markdown
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Diffteam
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
530
README.md
Normal file
530
README.md
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
# Clear Docker Registry v2
|
||||||
|
|
||||||
|
A comprehensive automation toolkit for managing and maintaining Docker registries. This project provides two powerful scripts for cleaning up unused container images and optimizing registry storage.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Docker registries can accumulate many image versions over time, consuming significant disk space. This project offers automated solutions to:
|
||||||
|
|
||||||
|
1. **Clean up old image tags** in remote registries
|
||||||
|
2. **Run garbage collection** on local Docker registry containers
|
||||||
|
3. **Free up storage space** by removing unused layers and manifests
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
### 1. `clear-registry`
|
||||||
|
|
||||||
|
**Purpose**: Delete old image tags from a Docker registry while preserving recent versions.
|
||||||
|
|
||||||
|
This script helps manage image versioning by automatically removing old tags, keeping only the latest N versions plus the `latest` tag.
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
- Automatically detects and installs required dependencies
|
||||||
|
- Supports multiple Linux distributions (Debian, Ubuntu, Alpine, Arch, CentOS, Fedora, openSUSE)
|
||||||
|
- Preserves the `latest` tag automatically
|
||||||
|
- Provides detailed progress output with visual indicators
|
||||||
|
- Works with any Docker-compatible registry
|
||||||
|
|
||||||
|
#### Requirements
|
||||||
|
|
||||||
|
- **skopeo** - Tool for working with remote registries
|
||||||
|
- **jq** - JSON query processor
|
||||||
|
- **grep** - Text pattern matching
|
||||||
|
- **sort** - Text sorting
|
||||||
|
- **head** - Display file/input head
|
||||||
|
|
||||||
|
The script automatically installs missing dependencies if your system's package manager is detected.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./clear-registry <repository> [number_of_tags]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments:**
|
||||||
|
- `<repository>` (required) - Docker repository URL or name
|
||||||
|
- Examples: `docker.io/library/alpine`, `myregistry.local:5000/myapp`
|
||||||
|
- `[number_of_tags]` (optional) - Number of recent tags to keep (default: 3)
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Keep 3 most recent tags of alpine image
|
||||||
|
./clear-registry docker.io/library/alpine
|
||||||
|
|
||||||
|
# Keep 5 most recent tags from private registry
|
||||||
|
./clear-registry myregistry.local:5000/myapp 5
|
||||||
|
|
||||||
|
# Keep 10 versions of specific image
|
||||||
|
./clear-registry registry.example.com/project/image 10
|
||||||
|
```
|
||||||
|
|
||||||
|
#### How It Works
|
||||||
|
|
||||||
|
1. Validates input arguments
|
||||||
|
2. Checks and installs required utilities if needed
|
||||||
|
3. Fetches all tags from the specified repository using skopeo
|
||||||
|
4. Calculates which tags to delete (keeping N newest + latest)
|
||||||
|
5. Deletes old tags one by one with confirmation
|
||||||
|
6. Reports completion with summary
|
||||||
|
|
||||||
|
#### Output Example
|
||||||
|
|
||||||
|
```
|
||||||
|
🔍 Fetching list of tags for docker.io/library/alpine...
|
||||||
|
📦 Will keep 3 recent tags (excluding latest)
|
||||||
|
📊 Total tags: 10 (including latest)
|
||||||
|
🗑️ Will be deleted: 6 tags
|
||||||
|
|
||||||
|
➜ Deleting: v1.0.0
|
||||||
|
✓ Successfully deleted
|
||||||
|
➜ Deleting: v1.1.0
|
||||||
|
✓ Successfully deleted
|
||||||
|
...
|
||||||
|
✅ Done!
|
||||||
|
📝 Kept: 3 recent tags and latest
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error Handling
|
||||||
|
|
||||||
|
- **Repository not specified**: Shows usage help and exits
|
||||||
|
- **Repository unreachable**: Reports connection error
|
||||||
|
- **Empty repository**: Informs user and exits safely
|
||||||
|
- **Invalid tag count**: Validates numeric input and provides error message
|
||||||
|
- **Missing dependencies**: Attempts automatic installation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `collect-registry`
|
||||||
|
|
||||||
|
**Purpose**: Run garbage collection on a Docker registry container to free up disk space.
|
||||||
|
|
||||||
|
Docker registry containers accumulate unused layers and manifests. This script safely runs the built-in garbage collection tool and restarts the registry.
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
- Comprehensive container validation (exists and running)
|
||||||
|
- Automatic configuration file detection with fallback search
|
||||||
|
- Interactive configuration selection
|
||||||
|
- Color-coded output for easy reading
|
||||||
|
- Container status verification after completion
|
||||||
|
- Support for custom configuration paths
|
||||||
|
- Handles permission requirements gracefully
|
||||||
|
|
||||||
|
#### Requirements
|
||||||
|
|
||||||
|
- **Docker** - Docker daemon must be running
|
||||||
|
- **Access** - Sufficient permissions to run `docker exec` and `docker restart`
|
||||||
|
- **Registry container** - Must have `registry garbage-collect` command available
|
||||||
|
- **Configuration file** - Must be accessible inside the container
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./collect-registry <container_name> [config_path]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments:**
|
||||||
|
- `<container_name>` (required) - Name or ID of Docker registry container
|
||||||
|
- `[config_path]` (optional) - Path to configuration file inside container
|
||||||
|
|
||||||
|
#### Default Configuration Paths
|
||||||
|
|
||||||
|
The script searches for configuration in this order:
|
||||||
|
1. User-specified path (if provided)
|
||||||
|
2. `/etc/docker/registry/config.yml`
|
||||||
|
3. `/etc/distribution/config.yml`
|
||||||
|
4. Extended search through entire container (if allowed)
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run GC on container named 'registry'
|
||||||
|
./collect-registry registry
|
||||||
|
|
||||||
|
# Specify custom configuration path
|
||||||
|
./collect-registry my-registry-container /etc/distribution/config.yml
|
||||||
|
|
||||||
|
# Use container ID from docker ps
|
||||||
|
./collect-registry $(docker ps -qf 'name=registry')
|
||||||
|
|
||||||
|
# Custom path inside container
|
||||||
|
./collect-registry registry /custom/path/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### How It Works
|
||||||
|
|
||||||
|
1. **Validation Phase:**
|
||||||
|
- Verifies container name argument provided
|
||||||
|
- Checks container exists
|
||||||
|
- Verifies container is running
|
||||||
|
- Reports status and available containers if errors occur
|
||||||
|
|
||||||
|
2. **Configuration Detection Phase:**
|
||||||
|
- Uses provided path if specified
|
||||||
|
- Searches standard registry paths
|
||||||
|
- Offers extended filesystem search if needed
|
||||||
|
- Allows manual path entry if multiple options found
|
||||||
|
|
||||||
|
3. **Garbage Collection Phase:**
|
||||||
|
- Executes `registry garbage-collect` command
|
||||||
|
- Handles execution errors with troubleshooting hints
|
||||||
|
|
||||||
|
4. **Restart Phase:**
|
||||||
|
- Safely restarts Docker container
|
||||||
|
- Verifies restart success
|
||||||
|
|
||||||
|
5. **Verification Phase:**
|
||||||
|
- Shows container status
|
||||||
|
- Reports configuration file used
|
||||||
|
|
||||||
|
#### Output Example
|
||||||
|
|
||||||
|
```
|
||||||
|
[Search] Looking for configuration file in container...
|
||||||
|
[OK] Configuration file found: /etc/docker/registry/config.yml
|
||||||
|
[OK] All checks passed
|
||||||
|
[Info] Using config: /etc/docker/registry/config.yml
|
||||||
|
[Start] Running garbage collector in container 'registry'...
|
||||||
|
[OK] Garbage collector executed successfully
|
||||||
|
[Restart] Restarting container...
|
||||||
|
[OK] Container successfully restarted
|
||||||
|
[OK] DONE! Garbage collector completed, container restarted
|
||||||
|
[Status] Current container status:
|
||||||
|
registry Up 2 seconds Up 2 seconds
|
||||||
|
[Info] Configuration file used: /etc/docker/registry/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Color-Coded Output
|
||||||
|
|
||||||
|
- 🟢 **GREEN [OK]** - Successful operations
|
||||||
|
- 🔴 **RED [ERR]** - Errors requiring attention
|
||||||
|
- 🟡 **YELLOW [Warning/List]** - Warnings or information lists
|
||||||
|
- 🔵 **BLUE [Hint]** - Helpful suggestions
|
||||||
|
|
||||||
|
#### Error Handling
|
||||||
|
|
||||||
|
- **Container not specified**: Shows usage help
|
||||||
|
- **Container not found**: Lists available containers
|
||||||
|
- **Container not running**: Suggests starting the container
|
||||||
|
- **Configuration not found**: Offers extended search option
|
||||||
|
- **Garbage collection error**: Provides troubleshooting hints
|
||||||
|
- **Restart failure**: Reports error with next steps
|
||||||
|
|
||||||
|
#### Permission Issues
|
||||||
|
|
||||||
|
The script uses `sudo` for package installation but **NOT** for Docker operations. If you get permission errors:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add user to docker group
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
|
||||||
|
# Apply new permissions
|
||||||
|
newgrp docker
|
||||||
|
|
||||||
|
# Or run with sudo
|
||||||
|
sudo ./collect-registry registry
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
|
||||||
|
1. Clone or download the scripts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/ClearDockerRegistry_v2
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Make scripts executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x clear-registry collect-registry
|
||||||
|
```
|
||||||
|
|
||||||
|
3. (Optional) Add to PATH:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp clear-registry collect-registry /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Requirements
|
||||||
|
|
||||||
|
- Linux/macOS/WSL environment
|
||||||
|
- Bash shell (version 4+)
|
||||||
|
- Docker installed and running (for `collect-registry`)
|
||||||
|
- `sudo` access for package installation (first run only)
|
||||||
|
|
||||||
|
### Supported Package Managers
|
||||||
|
|
||||||
|
- **apt/apt-get** - Debian, Ubuntu, Linux Mint
|
||||||
|
- **pacman** - Arch Linux, Manjaro
|
||||||
|
- **apk** - Alpine Linux
|
||||||
|
- **yum** - CentOS 7, RHEL 7
|
||||||
|
- **dnf** - Fedora, CentOS 8+, RHEL 8+
|
||||||
|
- **zypper** - openSUSE
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Scenario 1: Clean Up Public Registry
|
||||||
|
|
||||||
|
Clean up old Alpine image versions, keeping only 3 most recent:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./clear-registry docker.io/library/alpine 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 2: Maintain Private Registry Images
|
||||||
|
|
||||||
|
Clean up multiple images in a private registry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Keep 5 versions of web app
|
||||||
|
./clear-registry myregistry.local:5000/myapp/web 5
|
||||||
|
|
||||||
|
# Keep 10 versions of database
|
||||||
|
./clear-registry myregistry.local:5000/myapp/db 10
|
||||||
|
|
||||||
|
# Keep 2 versions of worker (aggressive cleanup)
|
||||||
|
./clear-registry myregistry.local:5000/myapp/worker 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 3: Run Registry Garbage Collection
|
||||||
|
|
||||||
|
After cleaning images, run garbage collection on the registry container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find registry container
|
||||||
|
docker ps | grep registry
|
||||||
|
|
||||||
|
# Run garbage collection and restart
|
||||||
|
./collect-registry my-docker-registry
|
||||||
|
|
||||||
|
# Or specify config path if non-standard
|
||||||
|
./collect-registry my-docker-registry /etc/registry/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 4: Automated Cleanup (Cron Job)
|
||||||
|
|
||||||
|
Add both scripts to cron for periodic maintenance:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Crontab entry - run at 2:00 AM daily
|
||||||
|
0 2 * * * /usr/local/bin/clear-registry docker.io/library/myapp 5
|
||||||
|
5 2 * * * /usr/local/bin/collect-registry my-docker-registry
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### clear-registry Issues
|
||||||
|
|
||||||
|
#### Error: "Could not determine package manager"
|
||||||
|
|
||||||
|
**Cause**: System uses an unsupported package manager.
|
||||||
|
|
||||||
|
**Solution**: Install dependencies manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install required packages (Ubuntu example)
|
||||||
|
sudo apt install -y skopeo jq coreutils
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error: "Could not fetch tag list or repository is empty"
|
||||||
|
|
||||||
|
**Cause**:
|
||||||
|
- Repository URL is incorrect
|
||||||
|
- Registry is unreachable
|
||||||
|
- Repository doesn't exist
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Verify URL: `skopeo list-tags docker://your-repo`
|
||||||
|
- Check registry connectivity: `curl -I https://registry-url/v2/`
|
||||||
|
|
||||||
|
#### Error: "permission denied" during deletion
|
||||||
|
|
||||||
|
**Cause**: Insufficient permissions to delete tags.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- Verify registry credentials if using private registry
|
||||||
|
- Check registry permissions for your account
|
||||||
|
|
||||||
|
### collect-registry Issues
|
||||||
|
|
||||||
|
#### Error: "container not found"
|
||||||
|
|
||||||
|
**Cause**: Container name doesn't match.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# List all containers
|
||||||
|
docker ps -a | grep registry
|
||||||
|
|
||||||
|
# Use correct container name
|
||||||
|
./collect-registry correct_name
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error: "container not running"
|
||||||
|
|
||||||
|
**Cause**: Registry container is stopped.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Start container
|
||||||
|
docker start registry_name
|
||||||
|
|
||||||
|
# Then run script
|
||||||
|
./collect-registry registry_name
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error: "Could not find configuration file"
|
||||||
|
|
||||||
|
**Cause**: Registry config not in standard locations.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
- When prompted, select "Yes" for extended search
|
||||||
|
- If that fails, find it manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find config inside container
|
||||||
|
docker exec registry_name find / -name "config.yml" 2>/dev/null
|
||||||
|
|
||||||
|
# Run script with explicit path
|
||||||
|
./collect-registry registry_name /path/to/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error: "Error executing garbage collector"
|
||||||
|
|
||||||
|
**Causes**:
|
||||||
|
- Insufficient container permissions
|
||||||
|
- Corrupted registry database
|
||||||
|
- Registry version incompatibility
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Check registry logs
|
||||||
|
docker logs registry_name | tail -100
|
||||||
|
|
||||||
|
# Verify registry health
|
||||||
|
docker exec registry_name registry --version
|
||||||
|
|
||||||
|
# Try manual garbage collection
|
||||||
|
docker exec registry_name registry garbage-collect /etc/docker/registry/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Error: "Error restarting container"
|
||||||
|
|
||||||
|
**Cause**: Docker daemon issues.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Restart Docker daemon
|
||||||
|
sudo systemctl restart docker
|
||||||
|
|
||||||
|
# Manually restart container
|
||||||
|
docker restart registry_name
|
||||||
|
|
||||||
|
# Check container status
|
||||||
|
docker ps | grep registry_name
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### clear-registry
|
||||||
|
|
||||||
|
- **Speed**: Depends on registry size and network connectivity
|
||||||
|
- **Risk**: Deletion is permanent; deleted tags cannot be recovered
|
||||||
|
- **Recommendation**: Start with a higher keep count (e.g., 10) and adjust
|
||||||
|
|
||||||
|
### collect-registry
|
||||||
|
|
||||||
|
- **Duration**: Garbage collection time depends on registry size
|
||||||
|
- Small registries: 1-5 minutes
|
||||||
|
- Medium registries (10-100GB): 5-30 minutes
|
||||||
|
- Large registries (>100GB): 30+ minutes
|
||||||
|
- **Registry Availability**: Registry will be unavailable during restart (~10-30 seconds)
|
||||||
|
- **Disk Space**: GC may require temporary space equal to max blob size
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Backup First**: Always back up registry data before running GC
|
||||||
|
|
||||||
|
2. **Test on Small Registries**: Verify scripts work with non-critical registries first
|
||||||
|
|
||||||
|
3. **Schedule Off-Peak**: Run garbage collection during low-traffic periods
|
||||||
|
|
||||||
|
4. **Start Conservative**: Keep more tags initially, adjust based on storage trends
|
||||||
|
|
||||||
|
5. **Monitor Logs**: Check container logs after GC completion
|
||||||
|
|
||||||
|
6. **Version Control**: Keep builds/tags with proper versioning scheme to avoid confusion
|
||||||
|
|
||||||
|
7. **Document Changes**: Log which images were cleaned and when
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
|
Permission is granted for personal and commercial use, modification, and distribution.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues or questions:
|
||||||
|
1. Check the Troubleshooting section above
|
||||||
|
2. Review script output for specific error messages
|
||||||
|
3. Check Docker and registry logs for additional context
|
||||||
|
4. Verify all prerequisites are installed and configured
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
### Version 2.0
|
||||||
|
- Complete rewrite with improved error handling
|
||||||
|
- Added configuration validation and auto-detection
|
||||||
|
- Support for multiple Linux distributions
|
||||||
|
- Enhanced output with color coding and progress indicators
|
||||||
|
- Extended configuration search capability
|
||||||
|
- Better permission and error messaging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Commands
|
||||||
|
|
||||||
|
Useful Docker registry commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all running containers
|
||||||
|
docker ps
|
||||||
|
|
||||||
|
# View registry logs
|
||||||
|
docker logs registry_name
|
||||||
|
|
||||||
|
# Get container details
|
||||||
|
docker inspect registry_name
|
||||||
|
|
||||||
|
# Verify registry health
|
||||||
|
curl https://registry-url/v2/
|
||||||
|
|
||||||
|
# List all images/tags in registry (using skopeo)
|
||||||
|
skopeo list-tags docker://registry/image
|
||||||
|
|
||||||
|
# Manual garbage collection
|
||||||
|
docker exec registry_name registry garbage-collect /etc/docker/registry/config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: March 2026
|
||||||
159
clear-registry
Normal file
159
clear-registry
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Function to check and install required utilities
|
||||||
|
check_and_install_deps() {
|
||||||
|
local deps=("skopeo" "jq" "grep" "sort" "head")
|
||||||
|
local missing_deps=()
|
||||||
|
|
||||||
|
# Check availability of each utility
|
||||||
|
for dep in "${deps[@]}"; do
|
||||||
|
if ! command -v "$dep" &> /dev/null; then
|
||||||
|
missing_deps+=("$dep")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If all utilities are installed, exit
|
||||||
|
if [ ${#missing_deps[@]} -eq 0 ]; then
|
||||||
|
echo "✓ All required utilities are installed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "❌ The following utilities are missing: ${missing_deps[*]}"
|
||||||
|
echo "🔍 Attempting to install..."
|
||||||
|
|
||||||
|
# Determine package manager and install missing utilities
|
||||||
|
if command -v apt &> /dev/null; then
|
||||||
|
# Debian/Ubuntu
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y "${missing_deps[@]}"
|
||||||
|
elif command -v apt-get &> /dev/null; then
|
||||||
|
# Debian/Ubuntu (alternative)
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y "${missing_deps[@]}"
|
||||||
|
elif command -v pacman &> /dev/null; then
|
||||||
|
# Arch Linux
|
||||||
|
for dep in "${missing_deps[@]}"; do
|
||||||
|
case $dep in
|
||||||
|
"skopeo")
|
||||||
|
sudo pacman -S --noconfirm skopeo
|
||||||
|
;;
|
||||||
|
"jq")
|
||||||
|
sudo pacman -S --noconfirm jq
|
||||||
|
;;
|
||||||
|
"grep"|"sort"|"head")
|
||||||
|
# These utilities are usually part of coreutils
|
||||||
|
sudo pacman -S --noconfirm coreutils
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
elif command -v apk &> /dev/null; then
|
||||||
|
# Alpine Linux
|
||||||
|
sudo apk add "${missing_deps[@]}"
|
||||||
|
elif command -v yum &> /dev/null; then
|
||||||
|
# RHEL/CentOS 7
|
||||||
|
sudo yum install -y epel-release
|
||||||
|
sudo yum install -y "${missing_deps[@]}"
|
||||||
|
elif command -v dnf &> /dev/null; then
|
||||||
|
# Fedora/RHEL 8+
|
||||||
|
sudo dnf install -y "${missing_deps[@]}"
|
||||||
|
elif command -v zypper &> /dev/null; then
|
||||||
|
# openSUSE
|
||||||
|
sudo zypper install -y "${missing_deps[@]}"
|
||||||
|
else
|
||||||
|
echo "❌ Could not determine package manager. Install utilities manually: ${missing_deps[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if utilities were installed successfully
|
||||||
|
local failed_deps=()
|
||||||
|
for dep in "${missing_deps[@]}"; do
|
||||||
|
if ! command -v "$dep" &> /dev/null; then
|
||||||
|
failed_deps+=("$dep")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#failed_deps[@]} -gt 0 ]; then
|
||||||
|
echo "❌ Failed to install: ${failed_deps[*]}"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✓ All missing utilities were successfully installed."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to display help
|
||||||
|
show_help() {
|
||||||
|
echo "Usage: $0 <repository> [number_of_tags]"
|
||||||
|
echo ""
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " <repository> - Docker repository URL or name (required)"
|
||||||
|
echo " [number_of_tags] - number of recent tags to keep (default: 3)"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 docker.io/library/alpine"
|
||||||
|
echo " $0 myregistry.local:5000/myapp 5"
|
||||||
|
echo " $0 registry.example.com/project/image 10"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for the first argument
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "❌ Error: repository not specified"
|
||||||
|
echo ""
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check and install dependencies
|
||||||
|
check_and_install_deps
|
||||||
|
|
||||||
|
# Set the number of tags to keep
|
||||||
|
KEEP_TAGS=${2:-3}
|
||||||
|
|
||||||
|
# Check that the number of tags is a positive number
|
||||||
|
if ! [[ "$KEEP_TAGS" =~ ^[0-9]+$ ]] || [ "$KEEP_TAGS" -lt 1 ]; then
|
||||||
|
echo "❌ Error: number of tags must be a positive number"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🔍 Fetching list of tags for $1..."
|
||||||
|
echo "📦 Will keep $KEEP_TAGS recent tags (excluding latest)"
|
||||||
|
|
||||||
|
# Fetch all tags
|
||||||
|
allTags=$(skopeo list-tags docker://"$1" 2>/dev/null | jq -r '.Tags[]')
|
||||||
|
|
||||||
|
if [ -z "$allTags" ]; then
|
||||||
|
echo "❌ Error: could not fetch tag list or repository is empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Count total number of tags
|
||||||
|
totalTags=$(echo "$allTags" | wc -l)
|
||||||
|
|
||||||
|
# Determine how many tags to delete
|
||||||
|
tagsToDelete=$((totalTags - KEEP_TAGS - 1)) # -1 for latest
|
||||||
|
|
||||||
|
if [ "$tagsToDelete" -le 0 ]; then
|
||||||
|
echo "✓ Total tags: $totalTags (including latest)"
|
||||||
|
echo "✓ Nothing to delete, keeping all $totalTags tags"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get tags to delete (exclude latest and keep KEEP_TAGS recent ones)
|
||||||
|
oldTags=$(echo "$allTags" | grep -v "latest" | sort | head -n -"$KEEP_TAGS")
|
||||||
|
|
||||||
|
echo "📊 Total tags: $totalTags (including latest)"
|
||||||
|
echo "🗑️ Will be deleted: $(echo "$oldTags" | wc -l) tags"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Delete old tags
|
||||||
|
for tag in $oldTags; do
|
||||||
|
echo " ➜ Deleting: $tag"
|
||||||
|
if skopeo delete docker://"$1:$tag" 2>/dev/null; then
|
||||||
|
echo " ✓ Successfully deleted"
|
||||||
|
else
|
||||||
|
echo " ❌ Error deleting $tag"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Done!"
|
||||||
|
echo "📝 Kept: $KEEP_TAGS recent tags and latest"
|
||||||
199
collect-registry
Normal file
199
collect-registry
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Function to display help
|
||||||
|
show_help() {
|
||||||
|
echo "=== Usage: $0 <container_name> [config_path] ==="
|
||||||
|
echo ""
|
||||||
|
echo "Description:"
|
||||||
|
echo " Runs garbage collector in Docker registry container and restarts it"
|
||||||
|
echo ""
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " <container_name> - name or ID of Docker registry container (required)"
|
||||||
|
echo " [config_path] - path to configuration file inside container (optional)"
|
||||||
|
echo ""
|
||||||
|
echo "If config path is not specified, script automatically searches in:"
|
||||||
|
echo " - /etc/docker/registry/config.yml"
|
||||||
|
echo " - /etc/distribution/config.yml"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 registry"
|
||||||
|
echo " $0 my-registry-container"
|
||||||
|
echo " $0 registry /etc/distribution/config.yml"
|
||||||
|
echo " $0 \$(docker ps -qf 'name=registry') /custom/path/config.yml"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to find configuration file
|
||||||
|
find_config() {
|
||||||
|
local container="$1"
|
||||||
|
local config_paths=(
|
||||||
|
"/etc/docker/registry/config.yml"
|
||||||
|
"/etc/distribution/config.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add custom path if specified
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
config_paths=("$2" "${config_paths[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[Search] Looking for configuration file in container..."
|
||||||
|
|
||||||
|
for path in "${config_paths[@]}"; do
|
||||||
|
if docker exec "$container" test -f "$path" 2>/dev/null; then
|
||||||
|
echo " [OK] Configuration file found: ${path}"
|
||||||
|
CONFIG_PATH="$path"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo " [-] Not found: ${path}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to find all possible configs
|
||||||
|
find_all_configs() {
|
||||||
|
local container="$1"
|
||||||
|
echo "[Search] Performing extended search for configuration files..."
|
||||||
|
|
||||||
|
# Look for all .yml and .yaml files that could be registry configs
|
||||||
|
local configs=$(docker exec "$container" find / -type f \( -name "config.yml" -o -name "config.yaml" \) 2>/dev/null | grep -E "(registry|distribution|docker)" | head -10)
|
||||||
|
|
||||||
|
if [ -n "$configs" ]; then
|
||||||
|
echo " [OK] Possible configuration files found:"
|
||||||
|
echo "$configs" | nl -w2 -s') '
|
||||||
|
|
||||||
|
# If exactly one file is found, offer it
|
||||||
|
local count=$(echo "$configs" | wc -l)
|
||||||
|
if [ "$count" -eq 1 ]; then
|
||||||
|
echo "[Question] Configuration file found. Use it?"
|
||||||
|
read -p "Use $(echo "$configs" | tr -d '\n')? (y/n) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
CONFIG_PATH=$(echo "$configs" | head -1)
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If multiple files or user refused
|
||||||
|
echo "[Input] Please specify the path to the configuration file manually:"
|
||||||
|
read -p "Path to config: " user_path
|
||||||
|
if [ -n "$user_path" ]; then
|
||||||
|
if docker exec "$container" test -f "$user_path" 2>/dev/null; then
|
||||||
|
CONFIG_PATH="$user_path"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo " [ERR] File not found: ${user_path}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Check for the first argument
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo -e "${RED}[ERR] Error: container name not specified${NC}"
|
||||||
|
echo ""
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CONTAINER_NAME="$1"
|
||||||
|
CONFIG_PATH="" # Will be determined later
|
||||||
|
|
||||||
|
# Check if container exists (in any state)
|
||||||
|
if ! docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
echo -e "${RED}[ERR] Error: container '${CONTAINER_NAME}' not found${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}[List] Available containers:${NC}"
|
||||||
|
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | head -10
|
||||||
|
echo ""
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if container is running
|
||||||
|
CONTAINER_STATUS=$(docker inspect --format='{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ "$CONTAINER_STATUS" != "running" ]; then
|
||||||
|
echo -e "${RED}[ERR] Error: container '${CONTAINER_NAME}' not running${NC}"
|
||||||
|
echo -e "${YELLOW}[Status] Current status: ${CONTAINER_STATUS}${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}[Hint] You can start the container with the command:${NC}"
|
||||||
|
echo " docker start $CONTAINER_NAME"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine path to configuration file
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
# If path is specified explicitly, check it
|
||||||
|
echo "[Search] Checking specified path: ${2}"
|
||||||
|
if docker exec "$CONTAINER_NAME" test -f "$2" 2>/dev/null; then
|
||||||
|
CONFIG_PATH="$2"
|
||||||
|
echo " [OK] Configuration file found"
|
||||||
|
else
|
||||||
|
echo -e "${RED} [ERR] Specified file not found: ${2}${NC}"
|
||||||
|
echo -e "${YELLOW}[Warning] Trying to find automatically...${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If path is not determined, try to find automatically
|
||||||
|
if [ -z "$CONFIG_PATH" ]; then
|
||||||
|
if ! find_config "$CONTAINER_NAME" "$2"; then
|
||||||
|
echo -e "${YELLOW}[Warning] Could not find configuration file in standard paths${NC}"
|
||||||
|
|
||||||
|
# Offer extended search
|
||||||
|
read -p "[Search] Perform extended search for configuration files? (y/n) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
if ! find_all_configs "$CONTAINER_NAME"; then
|
||||||
|
echo -e "${RED}[ERR] Error: Could not find configuration file${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}[ERR] Operation canceled${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK] All checks passed${NC}"
|
||||||
|
echo "[Info] Using config: ${CONFIG_PATH}"
|
||||||
|
echo "[Start] Running garbage collector in container '${CONTAINER_NAME}'..."
|
||||||
|
|
||||||
|
# Execute garbage collector
|
||||||
|
if docker exec "$CONTAINER_NAME" registry garbage-collect "$CONFIG_PATH"; then
|
||||||
|
echo -e "${GREEN}[OK] Garbage collector executed successfully${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}[ERR] Error executing garbage collector${NC}"
|
||||||
|
echo -e "${YELLOW}[Possible causes]:${NC}"
|
||||||
|
echo " - Insufficient permissions in container"
|
||||||
|
echo " - Incorrect path to configuration file"
|
||||||
|
echo " - Registry issues"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
echo "[Restart] Restarting container..."
|
||||||
|
if docker restart "$CONTAINER_NAME"; then
|
||||||
|
echo -e "${GREEN}[OK] Container successfully restarted${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}[ERR] Error restarting container${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK] DONE! Garbage collector completed, container restarted${NC}"
|
||||||
|
|
||||||
|
# Show container status
|
||||||
|
echo "[Status] Current container status:"
|
||||||
|
docker ps --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.RunningFor}}" | tail -n +2
|
||||||
|
|
||||||
|
# Show configuration file used
|
||||||
|
echo "[Info] Configuration file used: ${CONFIG_PATH}"
|
||||||
Reference in New Issue
Block a user