12 KiB
Kubernetes Walkthrough
This article will walk you through getting Plex-Meta-Manager [PMM] set up and running in Kubernetes. It will cover:
- Creating the Kubernetes CronJob
- Creating configuration files as Config Maps
- (Advanced) Creating dynamic configuration files with an Init Container
Prerequisites.
This walk through assumes you are familiar with Kubernetes concepts and have an exiting cluster to deploy into. If you do not, but are interested, minikube is a great place to start.
Creating the Kubernetes CronJob
When running PMM in Kubernetes, executing it as a CronJob gives us the ability to define a schedule for execution and have Kubernetes manage the rest.
Some parts of this to tweak to your needs:
- The namespace should be set to whatever you desire, in this example it runs in the
media
namespace. - The schedule, in this example it runs at 00:00 UTC. https://crontab.guru/ is a good site if you aren't sure on how to create a schedule.
apiVersion: batch/v1
kind: CronJob
metadata:
name: plex-media-manager
namespace: media
spec:
schedule: "0 0 * * *"
jobTemplate:
spec:
template:
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers:
- name: plex-media-manager
image: meisnate12/plex-meta-manager:latest
imagePullPolicy: IfNotPresent
args: [ "--run", "--read-only-config" ]
resources:
limits:
cpu: 100m
memory: 256Mi
requests:
cpu: 100m
memory: 125Mi
volumeMounts:
- name: config
mountPath: /config
- name: pmm-config
mountPath: /config/config.yml
subPath: config.yml
- name: movie-config
mountPath: /config/movies.yaml
subPath: movies.yaml
- name: tv-config
mountPath: /config/tv.yaml
subPath: tv.yaml
volumes:
- name: config
persistentVolumeClaim:
claimName: plex-media-manager
- configMap:
name: pmm-config
name: pmm-config
- configMap:
name: movie-config
name: movie-config
- configMap:
name: tv-config
name: tv-config
restartPolicy: OnFailure
This CronJob also requires
- A Persistent Volume Claim
- 3 Config Maps (see next section)
The Persistent Volume Claim (PVC) can be as simple as:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: plex-media-manager
name: plex-media-manager
namespace: media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 128Mi
Creating the Config Maps
In Kubernetes, configurations are managed via Config Maps. So we deploy the configurations for PMM as config maps. The minimum requirement is the PMM config, but the example here assumes you have a separate config for movies and tv shows.
PMM Config
Here's a config map for the config.yml
file for PMM. Note there are many placeholders that will need update based on
your environment and needs.
Follow the Trakt Attributes directions for generating the OAuth authorization values.
apiVersion: v1
data:
config.yml: |
libraries:
Movies:
metadata_path:
- file: config/movies.yaml
TV Shows:
metadata_path:
- file: config/tv.yaml
settings:
cache: true
cache_expiration: 60
asset_directory: config/assets
asset_folders: true
asset_depth: 0
create_asset_folders: false
dimensional_asset_rename: false
download_url_assets: false
show_missing_season_assets: false
sync_mode: append
minimum_items: 1
default_collection_order:
delete_below_minimum: true
delete_not_scheduled: false
run_again_delay: 2
missing_only_released: false
only_filter_missing: false
show_unmanaged: true
show_filtered: false
show_options: false
show_missing: true
show_missing_assets: true
save_report: true
tvdb_language: eng
ignore_ids:
ignore_imdb_ids:
playlist_sync_to_user: all
verify_ssl: true
plex:
url: http://PLEX_IP_HERE:32400
token: YOUR_TOKEN_HERE
timeout: 60
db_cache:
clean_bundles: false
empty_trash: false
optimize: false
tmdb:
apikey: YOUR_API_KEY_HERE
language: en
tautulli:
url: http://TAUTULLI_IP_HERE:8182
apikey: TAUTULLI_API_KEY_HERE
omdb:
apikey: OMDB_API_KEY
radarr:
url: http://RADARR_IP_HERE:7878
token: RADARR_TOKEN_HERE
add_missing: false
root_folder_path: /movies
monitor: false
availability: cinemas
quality_profile: HD - 720p/1080p
tag: pmm
add_existing: false
search: false
radarr_path:
plex_path:
sonarr:
url: http://SONARR_IP_HERE:8989
token: SONARR_TOKEN_HERE
add_missing: false
add_existing: false
root_folder_path: /tv
monitor: pilot
quality_profile: HD - 720p/1080p
language_profile: English
series_type: standard
season_folder: true
tag: pmm
search: true
cutoff_search: false
sonarr_path:
plex_path:
trakt:
client_id: YOUR_CLIENT_ID_HERE
client_secret: YOUR_CLIENT_SECRET_HERE
authorization:
access_token: YOUR_ACCESS_TOKEN_HERE
token_type: Bearer
expires_in: 7889237
refresh_token: YOUR_REFERSH_TOKEN_HERE
scope: public
created_at: 1642462048
kind: ConfigMap
metadata:
name: pmm-config
namespace: media
Movie Config Map
Config maps for collections (movies in this example) are more simple!
apiVersion: v1
data:
movies.yaml: |
collections:
Trakt Popular:
trakt_popular: 200
collection_order: custom
sync_mode: sync
sort_title: Traktpopular
summary: The most popular movies for all time.
radarr_add_missing: true
radarr_search: true
radarr_monitor: true
Tautulli Most Popular Movies:
sync_mode: sync
collection_order: custom
tautulli_watched:
list_days: 180
list_size: 10
list_minimum: 1
kind: ConfigMap
metadata:
name: movie-config
namespace: media
TV Config Map
apiVersion: v1
data:
tv.yaml: |
collections:
Most Popular:
smart_label: originally_available.desc
sync_mode: sync
imdb_list:
url: https://www.imdb.com/search/title/?title_type=tv_series,tv_miniseries
limit: 10
summary: The 10 most popular shows across the internet
sonarr_add_missing: true
sonarr_search: true
sonarr_monitor: pilot
Tautulli Most Popular:
sync_mode: sync
collection_order: custom
summary: The 10 most popular shows from Plex users
tautulli_popular:
list_days: 180
list_size: 10
kind: ConfigMap
metadata:
name: tv-config
namespace: media
Creating dynamic configuration files with an Init Container
IMDb search results may include results for media which has not yet been released, resulting in a collection that is incomplete. In order to solve for this you can replace a static config map with a config file that is (re)generated when the cronjob starts each time. This can be done by including an init container which renders a Jinja template to a file in the PVC.
Including the Init Container in the Cron Job
NOTE the environment value nameed JINJA_DEST_FILE
is the resulting name of the generated config file.
apiVersion: batch/v1
kind: CronJob
metadata:
name: plex-media-manager
namespace: media
spec:
schedule: "0 0 * * *"
jobTemplate:
spec:
template:
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
initContainers:
- name: render-dynamic-config
image: chrisjohnson00/jinja-init:v1.0.0
env:
# source and destination files
- name: JINJA_SRC_FILE
value: /config_src/tv.yaml
- name: JINJA_DEST_FILE
value: /config/tv.yaml
# let's be verbose
- name: VERBOSE
value: "1"
volumeMounts:
# configMap mount point
- name: tv-config-template
mountPath: /config_src
# target directory mount point; the final config file will be created here
- name: config
mountPath: /config
containers:
- name: plex-media-manager
image: meisnate12/plex-meta-manager:latest
imagePullPolicy: Always
args: [ "--run", "--read-only-config" ]
resources:
limits:
cpu: 100m
memory: 256Mi
requests:
cpu: 100m
memory: 125Mi
volumeMounts:
- name: config
mountPath: /config
- name: pmm-config
mountPath: /config/config.yml
subPath: config.yml
- name: movie-config
mountPath: /config/movies.yaml
subPath: movies.yaml
volumes:
- name: config
persistentVolumeClaim:
claimName: plex-media-manager
- configMap:
name: pmm-config
name: pmm-config
- configMap:
name: movie-config
name: movie-config
- configMap:
name: tv-config-jinja-template
name: tv-config-template
restartPolicy: OnFailure
Templatizing your configuration
This example will (re)generate the IMDb list URL and include the current date as the end date for the release_date
value.
https://www.imdb.com/search/title/?title_type=tv_series,tv_miniseries&release_date=1980-01-01,{{ now().strftime('%Y-%m-%d') }}
{{ now().strftime('%Y-%m-%d') }}
is the Jinja code, which when rendered will be replaced with the current date in
YYYY-MM-DD format. now()
is a special method defined in the Python code running in the init container to allow access
to the current date, so changing the output format is as simple as changing the string in strftime
to your desired
date/time format for your list source.
apiVersion: v1
data:
tv.yaml: |
collections:
Most Popular:
smart_label: originally_available.desc
sync_mode: sync
imdb_list:
url: https://www.imdb.com/search/title/?title_type=tv_series,tv_miniseries&release_date=1980-01-01,{{ now().strftime('%Y-%m-%d') }}
limit: 10
summary: The 10 most popular shows across the internet
sonarr_add_missing: true
sonarr_search: true
sonarr_monitor: pilot
Tautulli Most Popular:
sync_mode: sync
collection_order: custom
summary: The 10 most popular shows from Plex users
tautulli_popular:
list_days: 180
list_size: 10
kind: ConfigMap
metadata:
name: tv-config-jinja-template
namespace: media