Implement pypiserver and pypi proxy cache

This patch implements nginx as a reverse proxy for python
packages. The initial query will be to a local deployment
of pypiserver in order to serve any locally built packages,
but if the package is not available locally it will retry
the query against pypi and cache the response.

Depends-On: Id20a43fed833d53ca0f147f517deafba6587352d
Change-Id: Ic4fd64f4dc82121a65088f3d7f4ae53f373df608
Implements: blueprint python-build-install-simplification
Signed-off-by: Jesse Pretorius <jesse.pretorius@rackspace.co.uk>
This commit is contained in:
Jesse Pretorius 2017-11-21 08:21:29 +00:00
parent 956a942e5b
commit 6320c00217
10 changed files with 194 additions and 5 deletions

View File

@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
## Verbosity Options
debug: False
## Cap the maximum number of threads / workers when a user value is unspecified.
repo_nginx_threads_max: 16
repo_nginx_threads: "{{ [[ansible_processor_vcpus|default(2) // 2, 1] | max, repo_nginx_threads_max] | min }}"
@ -20,9 +23,10 @@ repo_nginx_threads: "{{ [[ansible_processor_vcpus|default(2) // 2, 1] | max, rep
## APT Cache Options
cache_timeout: 600
# Set the package install state for distribution packages
# Set the package install state for distribution and pip packages
# Options are 'present' and 'latest'
repo_server_package_state: "latest"
repo_server_pip_package_state: "latest"
repo_worker_connections: 1024
repo_server_name: openstack-slushee
@ -61,3 +65,42 @@ repo_pkg_cache_group: apt-cacher-ng
# Set the log directory
repo_service_log_dir: /var/log/apt-cacher-ng
# Required packages to install on the host
repo_requires_pip_packages:
- virtualenv
- virtualenv-tools
# Set the list of packages for the pypiserver
repo_pypiserver_pip_packages:
- "pypiserver==1.2.0"
# Set the path to place all built python wheels
# This is used by pypiserver to serve them
repo_pypiserver_package_path: "{{ repo_service_home_folder }}/repo/python_packages"
# Path to the pypiserver python virtualenv binaries
repo_pypiserver_bin: "/openstack/venvs/pypiserver-1.2.0/bin"
# Path to the pypiserver working directory
repo_pypiserver_working_dir: "{{ repo_service_home_folder }}/pypiserver"
# pypiserver service start options
repo_pypiserver_start_options: >-
-i localhost
-p 8080
--log-file /var/log/pypiserver/pypiserver.log
--disable-fallback
{{ (debug | bool) | ternary('-vv', '-v') }}
{{ repo_pypiserver_package_path }}
# config override var for systemd init file
repo_pypiserver_init_overrides: {}
# Set the options for the nginx proxy_cache_path directive.
# The proxy cache is used for data downloaded from pypi.
# The default is set to cache up to 1G worth of packages
# for up to 1 month
repo_nginx_proxy_cache_path: >-
/var/lib/nginx/pypi levels=1:2 keys_zone=pypi:16m inactive=1M max_size=1G

View File

@ -79,3 +79,14 @@
retries: 5
delay: 2
- name: reload pypiserver
service:
name: "pypiserver"
enabled: yes
state: restarted
daemon_reload: "{{ (ansible_service_mgr == 'systemd') | ternary('yes', omit) }}"
register: _restart
until: _restart | success
retries: 5
delay: 2

View File

@ -38,3 +38,4 @@ dependencies:
- role: apt_package_pinning
when:
- ansible_pkg_mgr == 'apt'
- role: pip_install

View File

@ -0,0 +1,8 @@
---
features:
- |
The repo server now implements nginx as a reverse proxy for python
packages sourced from pypi. The initial query will be to a local
deployment of pypiserver in order to serve any locally built packages,
but if the package is not available locally it will retry
the query against pypi and cache the response.

View File

@ -13,15 +13,42 @@
# See the License for the specific language governing permissions and
# limitations under the License.
- name: Install repo server packages
- name: Install distro packages
package:
name: "{{ repo_server_distro_packages }}"
state: "{{ repo_server_package_state }}"
update_cache: "{{ (ansible_pkg_mgr == 'apt') | ternary('yes', omit) }}"
cache_valid_time: "{{ (ansible_pkg_mgr == 'apt') | ternary(cache_timeout, omit) }}"
register: install_packages
until: install_packages|success
until: install_packages | success
retries: 5
delay: 5
tags:
- repo-packages
- name: Install required pip packages
pip:
name: "{{ repo_requires_pip_packages }}"
state: "{{ repo_server_pip_package_state }}"
extra_args: >-
{{ (pip_install_upper_constraints is defined) | ternary('--constraint ' + pip_install_upper_constraints | default(''),'') }}
{{ pip_install_options | default('') }}
register: install_packages
until: install_packages | success
retries: 5
delay: 2
- name: Install pip packages
pip:
name: "{{ repo_pypiserver_pip_packages }}"
state: "{{ repo_server_pip_package_state }}"
virtualenv: "{{ repo_pypiserver_bin | dirname }}"
virtualenv_site_packages: "no"
extra_args: >-
{{ (pip_install_upper_constraints is defined) | ternary('--constraint ' + pip_install_upper_constraints | default(''),'') }}
{{ pip_install_options | default('') }}
register: install_packages
until: install_packages | success
retries: 5
delay: 2
notify:
- reload pypiserver

View File

@ -39,6 +39,8 @@
dest: "/etc/rsyncd.conf"
- src: "openstack-slushee.vhost.j2"
dest: "/etc/nginx/sites-available/openstack-slushee.vhost"
- src: "nginx-pypi.conf.j2"
dest: "/etc/nginx/conf.d/pypi.conf"
notify:
- reload nginx
@ -64,3 +66,16 @@
dest: "{{ systemd_utils_prefix }}/system/git.socket"
notify:
- reload git socket
- name: Place the pypiserver systemd init script
config_template:
src: "pypiserver-systemd-init.j2"
dest: "/etc/systemd/system/pypiserver.service"
mode: "0644"
owner: "root"
group: "root"
config_overrides: "{{ repo_pypiserver_init_overrides }}"
config_type: "ini"
notify:
- reload pypiserver

View File

@ -62,6 +62,10 @@
- path: "{{ repo_service_home_folder }}/repo/venvs"
- path: "/var/log/nginx"
mode: "0775"
- path: "/var/log/pypiserver"
mode: "0775"
- path: "{{ repo_pypiserver_working_dir }}"
mode: "0775"
- name: Drop repo pre/post command script
template:
@ -96,6 +100,8 @@
- path: "/var/log/lsyncd"
- path: "/etc/nginx/sites-enabled/default"
state: "absent"
- path: "/etc/nginx/conf.d"
- path: "/etc/nginx/sites-available"
- path: "/etc/nginx/sites-enabled"
- path: "{{ repo_pypiserver_package_path }}"

View File

@ -0,0 +1,12 @@
# {{ ansible_managed }}
proxy_cache_path {{ repo_nginx_proxy_cache_path }};
upstream pypiserver {
server localhost:8080;
}
upstream pypi {
server pypi.python.org:443;
keepalive 16;
}

View File

@ -6,6 +6,40 @@ server {
access_log /var/log/nginx/{{ repo_server_name }}.access.log gzip buffer=32k;
error_log /var/log/nginx/{{ repo_server_name }}.error.log notice;
# Allow cached content to be used even when the upstream source is not available.
proxy_cache pypi;
proxy_cache_key $uri;
proxy_cache_lock on;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_http_version 1.1;
proxy_set_header Host $host:$server_port;
proxy_set_header Connection "";
proxy_set_header Accept-Encoding "";
# Rewrite any http redirects to use relative to proxy
proxy_redirect ~https?://pypi.python.org(.*) $1;
# Fallback mechanism from:
# http://linuxplayer.org/2013/06/nginx-try-files-on-multiple-named-location-or-server
location @pypi {
proxy_set_header Host pypi.python.org;
proxy_pass https://pypi;
}
location /simple {
proxy_intercept_errors on;
proxy_pass http://pypiserver;
error_page 404 = @pypi;
}
location /packages {
proxy_intercept_errors on;
proxy_pass http://pypiserver;
error_page 404 = @pypi;
}
location / {
root {{ repo_service_home_folder }}/repo/;
autoindex on;

View File

@ -0,0 +1,32 @@
# {{ ansible_managed }}
[Unit]
Description=pypiserver
After=network.target
[Service]
Type=simple
User={{ repo_service_user_name }}
Group={{ repo_service_group_name }}
ExecStart={{ repo_pypiserver_bin }}/pypi-server {{ repo_pypiserver_start_options }}
ExecStop=/bin/kill -TERM $MAINPID
WorkingDirectory={{ repo_pypiserver_working_dir }}
# Give a reasonable amount of time for the server to start up/shut down
TimeoutSec=120
TimeoutStartSec=3
Restart=on-failure
RestartSec=2
# This creates a specific slice which all services will operate from
# The accounting options give us the ability to see resource usage through
# the `systemd-cgtop` command.
Slice=pypiserver.slice
CPUAccounting=true
BlockIOAccounting=true
MemoryAccounting=false
TasksAccounting=true
[Install]
WantedBy=multi-user.target