Compare commits
No commits in common. "19487527f96539fce8d83c9260d0605b0472b36f" and "4cb9752c239df8689786bfd9daaf234d7e27c046" have entirely different histories.
19487527f9
...
4cb9752c23
@ -12,8 +12,7 @@ steps:
|
|||||||
# - apt update -y # not needed with custom image
|
# - apt update -y # not needed with custom image
|
||||||
# - apt install build-essential patchelf -y # not needed with custom image
|
# - apt install build-essential patchelf -y # not needed with custom image
|
||||||
# - pip install nuitka # not needed with custom image
|
# - pip install nuitka # not needed with custom image
|
||||||
# - python -m nuitka --onefile run.py --include-data-dir=./resources=resources --output-filename="ProxmoxDeploy${DRONE_TAG##v}" # not needed with new system with repo
|
- python -m nuitka --onefile run.py --include-data-dir=./resources=resources --output-filename="ProxmoxDeploy${DRONE_TAG##v}"
|
||||||
- python -m nuitka --onefile run.py --output-filename="ProxmoxDeploy${DRONE_TAG##v}"
|
|
||||||
- name: gitea_release
|
- name: gitea_release
|
||||||
image: plugins/gitea-release
|
image: plugins/gitea-release
|
||||||
settings:
|
settings:
|
||||||
|
63
README.md
63
README.md
@ -10,70 +10,13 @@ Proxmox Deploy is a little script to manage my HomeLab with JSON file.
|
|||||||
As my homelab was growing I realised that it was harder and harder to keep everything in sync and up to date.
|
As my homelab was growing I realised that it was harder and harder to keep everything in sync and up to date.
|
||||||
So I decided to create a script to manage my Proxmox homelab.
|
So I decided to create a script to manage my Proxmox homelab.
|
||||||
|
|
||||||
# How it works
|
# How to use it
|
||||||
The concept is simple, you have a Git repository with a the following structure:
|
Have a look at the resources folder to see how to use it.
|
||||||
```
|
|
||||||
.
|
|
||||||
├── config.json
|
|
||||||
├── lxc
|
|
||||||
│ ├── <id>
|
|
||||||
│ │ ├── config.json
|
|
||||||
│ │ ├── <your files>
|
|
||||||
│ │ └── <your folders>
|
|
||||||
│ └── <id>
|
|
||||||
│ ├── ...
|
|
||||||
│── qemu
|
|
||||||
│ ├── <id>
|
|
||||||
│ │ ├── config.json
|
|
||||||
│ │ ├── <your files>
|
|
||||||
│ │ └── <your folders>
|
|
||||||
│ └── <id>
|
|
||||||
│ ├── ...
|
|
||||||
│── scripts
|
|
||||||
│ ├── <your scripts>
|
|
||||||
│ └── ...
|
|
||||||
```
|
|
||||||
*See below for more information about the structure and differents files*
|
|
||||||
|
|
||||||
PDJ (Proxmox Deploy JSON) is a program that will read that repository files and execute the necessary commands to create/update your LXC/VM.
|
|
||||||
Now ideally you have some sort of Git actions like Drone/GitHub(/Gitea) Actions to run PDJ automatically when you push a change, which will result in your homelab being updated almost instantly.
|
|
||||||
|
|
||||||
If you don't, no big deal, you'll just have to manually clone and update your repo, then start PDJ *(you could use a crontab with regular intervals)*
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
## Download
|
|
||||||
Download the pre-compiled binaries from the release page or build it yourself.
|
|
||||||
|
|
||||||
## Build it yourself
|
|
||||||
```bash
|
|
||||||
# Build on Debian
|
|
||||||
git clone <url of this repo>
|
|
||||||
cd ProxmoxDeploy
|
|
||||||
apt update && apt install -y build-essential patchelf
|
|
||||||
pip install nuitka
|
|
||||||
pip install -r requirements.txt
|
|
||||||
python -m nuitka --onefile run.py --output-filename="ProxmoxDeploy"
|
|
||||||
```
|
|
||||||
*Also see the ``Dockerfile`` and ``.drone.yml`` for more information.*
|
|
||||||
|
|
||||||
## Run it
|
|
||||||
```bash
|
|
||||||
# Run it
|
|
||||||
./ProxmoxDeploy --repo /path/to/repo
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
## Configuration
|
|
||||||
### General
|
|
||||||
Before configuring your LXC and VM, you must decide how you'll run this program.
|
|
||||||
As it requires SSH for some actions, you have two options:
|
|
||||||
- Run it directly on the Proxmox VE host **(recommended)** *(refered as `local`)*
|
|
||||||
- No configuration needed
|
|
||||||
- You can use Git(ea)/Drone actions to run it automatically as soon as a change is pushed.
|
|
||||||
- Run it on another machine and connect via SSH
|
|
||||||
- You'll need to setup passwordless SSH connection between your machine and the Proxmox VE host.
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
### Proxmox VE
|
### Proxmox VE
|
||||||
The Proxmox VE configuration is located in the `config.json` file.
|
The Proxmox VE configuration is located in the `config.json` file.
|
||||||
```json
|
```json
|
||||||
|
10
resources/config.json
Normal file
10
resources/config.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"pve":{
|
||||||
|
"host": "192.168.11.99",
|
||||||
|
"user": "root",
|
||||||
|
"port": 22,
|
||||||
|
"local": false
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
}
|
||||||
|
}
|
49
resources/lxc/100/config.json
Normal file
49
resources/lxc/100/config.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"lxc_hostname": "traefik",
|
||||||
|
"os": {
|
||||||
|
"name": "alpine",
|
||||||
|
"release": "3.17"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"cpu": 2,
|
||||||
|
"memory": 1024,
|
||||||
|
"swap": 256,
|
||||||
|
"disk": 8,
|
||||||
|
"storage": "local-lvm"
|
||||||
|
},
|
||||||
|
"network": {
|
||||||
|
"bridge": "vmbr0",
|
||||||
|
"ipv4": "dhcp",
|
||||||
|
"ipv6": "auto",
|
||||||
|
"mac": "92:A6:71:77:8E:D8",
|
||||||
|
"gateway4": "",
|
||||||
|
"gateway6": "",
|
||||||
|
"vlan": ""
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"privileged": false,
|
||||||
|
"start_on_boot": false,
|
||||||
|
"startup_order": 2,
|
||||||
|
"password": "qwertz1234",
|
||||||
|
"tags": "2-proxy+auth"
|
||||||
|
},
|
||||||
|
"creation": {
|
||||||
|
"conditions": {
|
||||||
|
"programs": ["docker"],
|
||||||
|
"folders": ["/var/data/traefik", "/var/data/config/traefik"],
|
||||||
|
"files": ["/var/data/traefik/traefik.toml", "/var/data/config/traefikv2/docker-compose.yml"]
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "script",
|
||||||
|
"local_path": "global/scripts/install-docker.sh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "folder_copy",
|
||||||
|
"path": "data/",
|
||||||
|
"destination": "/var/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"deploy": {}
|
||||||
|
}
|
40
resources/lxc/100/data/config/traefikv2/docker-compose.yml
Normal file
40
resources/lxc/100/data/config/traefikv2/docker-compose.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: traefik:v2.9
|
||||||
|
env_file: /var/data/config/traefikv2/traefik.env
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "80:80" # http
|
||||||
|
- "443:443" # https
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- /var/data/config/traefikv2/dyn:/dyn
|
||||||
|
- /var/data/config/traefikv2/traefik.toml:/etc/traefik/traefik.toml
|
||||||
|
- /var/data/traefik/traefik.log:/traefik.log
|
||||||
|
- /var/data/traefik/access.log:/access.log
|
||||||
|
- /var/data/traefik/acme.json:/acme.json
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.api.rule=Host(`traefik.xyz.abc`)"
|
||||||
|
- "traefik.http.routers.api.entrypoints=https"
|
||||||
|
|
||||||
|
- "traefik.http.routers.api.service=api@internal"
|
||||||
|
- "traefik.http.services.dummy.loadbalancer.server.port=9999"
|
||||||
|
|
||||||
|
- "traefik.http.routers.api.tls=true"
|
||||||
|
- "traefik.http.routers.api.tls.domains[0].main=xyz.abc"
|
||||||
|
- "traefik.http.routers.api.tls.domains[0].sans=*.xyz.abc"
|
||||||
|
- "traefik.http.routers.api.tls.certresolver=cloudflare"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "2m"
|
||||||
|
max-file: "2"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
3
resources/lxc/100/data/config/traefikv2/traefik.env
Normal file
3
resources/lxc/100/data/config/traefikv2/traefik.env
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# CloudFlare example
|
||||||
|
CLOUDFLARE_EMAIL=me@xyz.abc
|
||||||
|
CLOUDFLARE_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
52
resources/lxc/100/data/config/traefikv2/traefik.toml
Normal file
52
resources/lxc/100/data/config/traefikv2/traefik.toml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
[global]
|
||||||
|
checkNewVersion = true
|
||||||
|
|
||||||
|
# Enable the Dashboard
|
||||||
|
[api]
|
||||||
|
dashboard = true
|
||||||
|
|
||||||
|
# Write out Traefik logs
|
||||||
|
[log]
|
||||||
|
level = "INFO"
|
||||||
|
filePath = "/traefik.log"
|
||||||
|
|
||||||
|
# [accessLog]
|
||||||
|
# filePath = "/access.log"
|
||||||
|
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
# Redirect to HTTPS (why wouldn't you?)
|
||||||
|
[entryPoints.http.http.redirections.entryPoint]
|
||||||
|
to = "https"
|
||||||
|
scheme = "https"
|
||||||
|
|
||||||
|
[entryPoints.http.forwardedHeaders]
|
||||||
|
trustedIPs = ["10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/16", "fc00::/7"]
|
||||||
|
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.http.tls]
|
||||||
|
certResolver = "cloudflare"
|
||||||
|
|
||||||
|
[entryPoints.https.forwardedHeaders]
|
||||||
|
trustedIPs = ["10.0.0.0/8", "172.16.0.0/16", "192.168.0.0/16", "fc00::/7"]
|
||||||
|
|
||||||
|
|
||||||
|
# Cloudflare
|
||||||
|
[certificatesResolvers.infomaniak.acme]
|
||||||
|
email = "me@xyz.abc"
|
||||||
|
storage = "acme.json"
|
||||||
|
[certificatesResolvers.infomaniak.acme.dnsChallenge]
|
||||||
|
provider = "cloudflare"
|
||||||
|
resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||||
|
|
||||||
|
# Docker Traefik provider
|
||||||
|
[providers.docker]
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
swarmMode = false
|
||||||
|
watch = true
|
||||||
|
exposedByDefault = false
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
directory = "/dyn"
|
||||||
|
watch = true
|
1
resources/lxc/100/data/traefikv2/acme.json
Normal file
1
resources/lxc/100/data/traefikv2/acme.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
0
resources/lxc/100/data/traefikv2/treafik.log
Normal file
0
resources/lxc/100/data/traefikv2/treafik.log
Normal file
0
resources/qemu/id/0000/config.json
Normal file
0
resources/qemu/id/0000/config.json
Normal file
54
resources/scripts/install-docker.sh
Normal file
54
resources/scripts/install-docker.sh
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if which docker >/dev/null 2>&1; then
|
||||||
|
echo "Docker is installed"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Docker is not installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if lsb_release -a 2>/dev/null | grep -q -E "Debian"; then
|
||||||
|
echo "Running Debian"
|
||||||
|
|
||||||
|
sudo apt-get remove docker docker-engine docker.io containerd runc -y
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
gnupg -y
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
echo \
|
||||||
|
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
|
||||||
|
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" |
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
|
||||||
|
|
||||||
|
elif lsb_release -a 2>/dev/null | grep -q -E "Ubuntu"; then
|
||||||
|
echo "Running Ubuntu"
|
||||||
|
|
||||||
|
sudo apt-get remove docker docker-engine docker.io containerd runc -y
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install ca-certificates curl gnupg -y
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
echo \
|
||||||
|
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||||
|
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" |
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
|
||||||
|
|
||||||
|
elif cat /etc/os-release 2>/dev/null | grep -q -i "alpine"; then
|
||||||
|
echo "Running Alpine"
|
||||||
|
apk add docker docker-compose
|
||||||
|
addgroup username docker
|
||||||
|
rc-update add docker default
|
||||||
|
service docker start
|
||||||
|
else
|
||||||
|
echo "Unknown distribution"
|
||||||
|
exit 1
|
||||||
|
fi
|
11
run.py
11
run.py
@ -1,18 +1,7 @@
|
|||||||
import argparse
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from src import main
|
from src import main
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
|
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.verbose:
|
|
||||||
logging.basicConfig(format='[%(levelname)s] : %(message)s', level=logging.DEBUG)
|
logging.basicConfig(format='[%(levelname)s] : %(message)s', level=logging.DEBUG)
|
||||||
else:
|
|
||||||
logging.basicConfig(format='[%(levelname)s] : %(message)s', level=logging.INFO)
|
|
||||||
|
|
||||||
main.run()
|
main.run()
|
||||||
|
@ -177,7 +177,7 @@ def run_command_on_pve(command: str, return_status_code: bool = False, exception
|
|||||||
logging.debug(f"Running command on PVE (ssh): {command}")
|
logging.debug(f"Running command on PVE (ssh): {command}")
|
||||||
|
|
||||||
# catch errors code
|
# catch errors code
|
||||||
command = subprocess.run(f'ssh {username}@{host} -p {port} "{command}"', shell=shell, capture_output=True,
|
command = subprocess.run(f'ssh -i {get_identity_file()} {username}@{host} -p {port} "{command}"', shell=shell, capture_output=True,
|
||||||
encoding="utf-8")
|
encoding="utf-8")
|
||||||
|
|
||||||
# If return code is not 0 and that exception_on_exit is True and return_status_code is False, throw an exception
|
# If return code is not 0 and that exception_on_exit is True and return_status_code is False, throw an exception
|
||||||
@ -414,7 +414,7 @@ def copy_file_to_pve(path: Path, destination: str):
|
|||||||
config = get_config()
|
config = get_config()
|
||||||
|
|
||||||
# copy the file to the PVE from the local machine
|
# copy the file to the PVE from the local machine
|
||||||
run_command_locally(command=f"scp {str(path)} {config['pve']['user']}@{config['pve']['host']}:{destination}",
|
run_command_locally(command=f"scp -i {get_identity_file()} {str(path)} {config['pve']['user']}@{config['pve']['host']}:{destination}",
|
||||||
return_status_code=True)
|
return_status_code=True)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user