roles/homeassistant: Deploy container with Podman

Installing Home Assistant in a Python virtualenv is rather tedious,
especially on non-x86 machines.  The main issue is Python packages that
include native extensions, as many of these do not have binary wheels
available for aarch64, etc. on PyPI.  Thus, to install these, they have
to be built from source, which then requires the appropriate development
packages to be installed.  Additionally, compiling native code on a
Raspberry Pi is excruciatingly slow.  I have considered various ways of
mitigating this, but all would require a substantial time investment,
both up front and ongoing, making them rather pointless.  Eventually, I
settled on just deploying the official Home Assistant container image
with Podman.

Although Podman includes a tool for generating systemd service unit
files for running containers, I ended up creating my own for several
reasons.  First and foremost, the generated unit files configure the
containers to run as *root*, but I wanted to run Home Assistant as an
unprivileged user.  Unfortunately, I could not seem to get the container
to work when dropping privileges using the `User` directive of the unit.
Fortunately, `podman` has `--uidmap` and `--gidmap` arguments, which I
was able to use to map UID/GID 0 in the container to the *homeassistant*
user on the host.  Another drawback of the generated unit files is that
they specify a "forking" type service, which is not really necessary.
Podman/conmon supports the systemd notify protocol, but the generator
has not been updated to make use of that yet.

Recent versions of Home Assistant are more strict with respect to how
reverse proxies are handled.  In order to use one, it must be explicitly
listed in the configuration file.  Therefore, the *homeassistant*
Ansible role will now create a stub `configuration.yaml`, based on the
one generated by Home Assistant itslf when it starts for the first time
on a new machine, that includes the appropriate configuration for a
reverse proxy running on the same machine.  The stub configuration will
not overwrite an existing configuration file, so it is only useful when
deploying Home Assistant for the first time on a new machine.

Overall, although I think a 300+ MB container image is ridiculous,
deploying Home Assistant this way should make it a lot easier to manage,
especially when updating.
jenkins-master
Dustin 2021-07-12 16:14:57 -05:00
parent 4aa3cdddd9
commit 288b050a33
18 changed files with 109 additions and 108 deletions

View File

@ -1,7 +1,8 @@
- hosts: home-assistant - hosts: home-assistant
roles: roles:
- apache - apache
- homeassistant - role: homeassistant
tags: homeassistant
- role: mosquitto - role: mosquitto
tags: mosquitto tags: mosquitto
tasks: tasks:

View File

@ -0,0 +1 @@
homeassistant_image_name: '{{ homeassistant_default_image_name }}'

View File

@ -1,3 +0,0 @@
#!/bin/sh
exec /usr/local/homeassistant/bin/hass

View File

@ -1,13 +0,0 @@
# vim: set ft=systemd :
[Unit]
Description=Home Assistant
[Service]
Type=simple
Environment=TMPDIR=/var/lib/homeassistant/tmp
ExecStart=/usr/local/bin/hass
User=homeassistant
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,4 @@
server_host: '::1'
trusted_proxies:
- '::1'
use_x_forwarded_for: true

View File

@ -0,0 +1 @@
[]

View File

@ -0,0 +1,14 @@
# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:
http: !include http.yaml
# Text to speech
tts:
- platform: google_translate
group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

View File

@ -0,0 +1 @@
{}

View File

@ -3,11 +3,12 @@
with_first_found: with_first_found:
- '{{ ansible_architecture }}.yml' - '{{ ansible_architecture }}.yml'
- defaults.yml - defaults.yml
tags:
- always
- name: ensure system dependencies are installed - name: ensure podman is installed
package: package:
name: >- name: '{{ homeassistant_podman_packages }}'
{{ homeassistant_common_system_deps + homeassistant_arch_system_deps }}
state: present state: present
tags: tags:
- install - install
@ -17,62 +18,73 @@
name: homeassistant name: homeassistant
system: true system: true
home: /var/lib/homeassistant home: /var/lib/homeassistant
createhome: false
register: homeassistant_user
tags:
- user
- name: ensure homeassistant tmp dir exists - name: ensure homeassistant home directory exists
file: file:
path: /var/lib/homeassistant/tmp path: /var/lib/homeassistant
mode: '0700'
owner: homeassistant owner: homeassistant
group: homeassistant group: homeassistant
state: directory
- name: ensure homeassistant install dir exists
file:
path: /usr/local/homeassistant
mode: '0755' mode: '0755'
owner: homeassistant
group: homeassistant
state: directory state: directory
- name: ensure homeassistant is installed tags:
environment: - datadir
TMPDIR: /var/lib/homeassistant/tmp
become: true
become_user: homeassistant
pip:
name: homeassistant
extra_args: >-
--prefer-binary
virtualenv: /usr/local/homeassistant
virtualenv_command: '/usr/bin/python3 -m venv'
- name: ensure selinux file context map is correct for home assistant dir - name: ensure stub home assistant configuration is set
sefcontext:
ftype: a
setype: bin_t
target: /usr/local/homeassistant/bin(/.*)?
state: present
notify: relabel home assistant dir
- name: ensure homeassistant entry point is installed
copy: copy:
src: hass.sh src: '{{ item }}'
dest: /usr/local/bin/hass dest: /var/lib/homeassistant/{{ item|basename }}
setype: bin_t owner: homeassistant
mode: '0755' group: homeassistant
mode: '0644'
force: false
with_fileglob:
- 'stubs/*.yaml'
tags:
- config
- name: ensure home assistant proxy settings are configured
copy:
src: http.yaml
dest: /var/lib/homeassistant/http.yaml
owner: homeassistant
group: homeassistant
mode: '0644'
notify: notify:
- restart homeassistant - restart homeassistant
tags:
- config
- name: ensure homeassistant container image is available
podman_image:
name: ghcr.io/home-assistant/{{ homeassistant_image_name }}
tag: stable
state: present
notify:
- restart homeassistant
tags:
- container-image
- container
- name: ensure homeassistant systemd unit is installed - name: ensure homeassistant systemd unit is installed
copy: template:
src: homeassistant.service src: homeassistant.service.j2
dest: /etc/systemd/system/homeassistant.service dest: /etc/systemd/system/homeassistant.service
mode: '0644' mode: '0644'
notify: notify:
- reload systemd - reload systemd
- restart homeassistant - restart homeassistant
tags:
- service
- systemd
- name: ensure homeassistant starts at boot - name: ensure homeassistant starts at boot
service: service:
name: homeassistant name: homeassistant
enabled: true enabled: true
tags:
- service
- name: ensure apache is configured to proxy for homeassistant - name: ensure apache is configured to proxy for homeassistant
template: template:
@ -81,8 +93,13 @@
mode: '0644' mode: '0644'
notify: notify:
- restart httpd - restart httpd
tags:
- apache
- name: ensure selinux allows apache to proxy - name: ensure selinux allows apache to proxy
seboolean: seboolean:
name: httpd_can_network_connect name: httpd_can_network_connect
state: true state: true
persistent: true persistent: true
tags:
- selinux
- apache

View File

@ -1,37 +0,0 @@
homeassistant:
# Name of the location where Home Assistant is running
name: Home
# Location required to calculate the time the sun rises and sets
latitude: 38.9568
longitude: -94.6832
# Impacts weather/sunrise data (altitude above sea level in meters)
elevation: 0
# metric for Metric, imperial for Imperial
unit_system: imperial
# Pick yours from here: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
time_zone: America/Chicago
# Customization file
customize: !include customize.yaml
# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:
# Show the introduction message on startup.
introduction:
# Uncomment this if you are using SSL/TLS, running in Docker container, etc.
# http:
# base_url: example.duckdns.org:8123
http:
server_host: '::1'
# Sensors
sensor: !include sensors.yaml
# Text to speech
tts:
- platform: google
group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml

View File

@ -0,0 +1,24 @@
# vim: set ft=systemd :
[Unit]
Description=Home Assistant
[Service]
Type=notify
NotifyAccess=all
ExecStartPre=-/usr/bin/podman container rm --ignore -f homeassistant
ExecStart=/usr/bin/podman run \
--sdnotify=conmon --cgroups=no-conmon \
--rm \
--network=host \
--name homeassistant \
-v /var/lib/homeassistant:/config:Z \
--uidmap 0:{{ homeassistant_user.uid }}:1 \
--gidmap 0:{{ homeassistant_user.group }}:1 \
--uidmap 1:4000000:65536 \
--gidmap 1:4000000:65536 \
ghcr.io/home-assistant/{{ homeassistant_image_name }}:stable
ProtectSystem=full
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1 @@
homeassistant_default_image_name: aarch64-homeassistant

View File

@ -1,12 +1 @@
# These are required to build Python packages that do not have wheels homeassistant_default_image_name: armhf-homeassistant
# on pypi.org for armv7hl
homeassistant_arch_system_deps:
- gcc
- gcc-c++
- libffi-devel
- libopenzwave-devel
- libudev-devel
- make
- openssl-devel
- python3-devel
- which

View File

@ -1 +1 @@
homeassistant_arch_system_deps: [] homeassistant_default_image_name: home-assistant

View File

@ -1,2 +1,3 @@
homeassistant_common_system_deps: homeassistant_podman_packages:
- python3-pip - podman
- catatonit