Changeset - 2935e65f230e
[Not reviewed]
0 1 0
Branko Majic (branko) - 8 years ago 2015-10-27 16:29:10
branko@majic.rs
MAR-18: Added more information on how the PHP apps are executed to the usage instructions (and where the files are served from). Added usage instructions for deploying a WSGI appliction (Django Wiki in this case).
1 file changed with 350 insertions and 3 deletions:
0 comments (0 inline, 0 general)
docs/usage.rst
Show inline comments
 
@@ -1114,6 +1114,8 @@ we'll be using for the PHP part:
 
* The role is designed to execute every application via dedicated user and
 
  group. The user/group name is automatically derived from the FQDN of website,
 
  for example ``web-tbg_example_com``.
 
* PHP applications are executed via FastCGI, using the ``php5-fpm`` package.
 
* Static content (non-PHP) is served directly by *Nginx*.
 
* For administrative purposes you can use a separate user that you have created
 
  before (for example via the ``common`` role). This user will get added to
 
  application's group.
 
@@ -1123,6 +1125,11 @@ we'll be using for the PHP part:
 
  application's group. In other words, all directories will have ``SGID`` bit
 
  set-up, allowing you to create files/directories that will have their group
 
  automatically set to the group of the parent directory.
 
* Files are served (both by *Nginx* and *php5-fpm*) from sub-directory called
 
  ``htdocs`` (located in website directory). For example
 
  ``/var/www/tbg.example.com/htdocs/``. Normally, this can be a symlink to some
 
  other sub-directory within the website directory (useful for having multiple
 
  versions for easier downgrades etc).
 
* Combination of admin user membership in application group, ``SGID``
 
  permission, and the way ownership of sub-directories is set-up usually means
 
  that the administrator will be capable of managing application files, and
 
@@ -1260,9 +1267,7 @@ we'll be using for the PHP part:
 

	
 
      - name: Deploy database configuration file for TBG
 
        copy: src="b2db.yml" dest="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/core/config/b2db.yml"
 
              mode=640 group=web-tbg_example_com
 
        become: yes
 
        become_user: admin
 
              mode=640 owner=admin group=web-tbg_example_com
 

	
 
      - name: Deploy expect script for installing TBG
 
        copy: src="tbg_expect_install" dest="/var/www/tbg.example.com/tbg_expect_install" mode=750
 
@@ -1322,3 +1327,345 @@ we'll be using for the PHP part:
 
8. At this point TBG has been installed, and you should be able to open the URL
 
   https://tbg.example.com/ (or http://tbg.example.com/) and log-in into
 
   *The Bug Genie* with username ``administrator`` and password ``admin``.
 

	
 

	
 
Deploying a WSGI application (Django Wiki)
 
------------------------------------------
 

	
 
Next thing up will be to deploy a WSGI Python application - our wiki website.
 

	
 
Similar to the PHP application deployment, we will use a couple of roles to make
 
it easier to deploy it in a standardised manner, and we will not have any kind
 
of parameters for configuring the application role.
 

	
 
Most of the notes on how the application is deployed in case of ``php_website``
 
role also stand for the ``wsgi_website`` role, but we will reitarte and clarify
 
them a bit just to be on the safe side:
 

	
 
* The role is designed to execute every application via dedicated user and
 
  group. The user/group name is automatically derived from the FQDN of website,
 
  for example ``web-wiki_example_com``.
 
* WSGI applications are executed via *Gunicorn*. The WSGI server listens on a
 
  Unix socket, making the socket accessible by *Nginx*.
 
* Static content is served directly by *Nginx*.
 
* For administrative purposes you can use a separate user that you have created
 
  before (for example via the ``common`` role). This user will get added to
 
  application's group. The administrator will also be able to switch to website
 
  virtual environment using the ``workon`` command from ``virtualenv-wrapper``
 
  package.
 
* Each web application gets distinct sub-directory under ``/var/www``, named
 
  after the FQDN. All sub-directories created under there are created with
 
  ``2750`` permissions, with ownership set to admin user, and group set to the
 
  application's group. In other words, all directories will have ``SGID`` bit
 
  set-up, allowing you to create files/directories that will have their group
 
  automatically set to the group of the parent directory.
 
* Each WSGI website gets a dedicated virtual environment, stored in the
 
  sub-directory ``virtualenv`` of the website directory, for example
 
  ``/var/www/wiki.example.com/virtualenv``.
 
* Static files are served from sub-directory ``htdocs`` in the website
 
  directory, for example ``/var/www/wiki.example.com/htdocs/``.
 
* The base directory where your website/application code should be at is
 
  expected to be in sub-directory ``code`` in the website directory, for example
 
  ``/var/www/wiki.example.com/code/``.
 
* Combination of admin user membership in application group, ``SGID``
 
  permission, and the way ownership of sub-directories is set-up usually means
 
  that the administrator will be capable of managing application files, and
 
  application can be granted write permissions to a *minimum* of necessary
 
  files.
 

	
 
  .. warning::
 
     Just keep in mind that some file-management commands, like ``mv``, do *not*
 
     respect the ``SGID`` bit. In fact, I would recommend using ``cp`` when you
 
     deploy new files to the directory instead (don't simply move them from your
 
     home).
 

	
 
1. Set-up the necessary directories first::
 

	
 
     mkdir -p ~/mysite/roles/wiki/{tasks,meta,files}/
 

	
 
2. Set-up some role dependencies, reusing the common role infrastructure.
 

	
 
   :file:`~/mysite/roles/wiki/meta/main.yml`::
 

	
 
      ---
 

	
 
      dependencies:
 
         - role: wsgi_website
 
           admin: admin
 
           fqdn: wiki.example.com
 
           # Most of these packages are needed for building Python packages.
 
           packages:
 
              - build-essential
 
              - python-dev
 
              - libjpeg-dev
 
              - libzip-dev
 
              - libtiff-dev
 
              - libfreetype6-dev
 
              - liblcms2-dev
 
              - libwebp-dev
 
              - libopenjpeg-dev
 
              - libmariadb-client-lgpl-dev
 
              - libmariadb-client-lgpl-dev-compat
 
           static_locations:
 
              - /static/
 
           uid: 2001
 
           virtualenv_packages:
 
              - pillow
 
              - wiki
 
              - MySQL-python
 
           wsgi_application: wiki_example_com.wsgi:application
 
         - role: database
 
           db_name: wiki
 
           db_password: wiki
 

	
 
3. Let's create a dedicated private key/certificate pair for the wiki website:
 

	
 
   1. Create new template for ``certtool``:
 

	
 
      :file:`~/mysite/tls/wiki.example.com_https.cfg`::
 

	
 
         organization = "Example Inc."
 
         country = SE
 
         cn = "Exampe Inc. Wiki"
 
         expiration_days = 365
 
         dns_name = "wiki.example.com"
 
         tls_www_server
 
         signing_key
 
         encryption_key
 

	
 
   2. Create the keys and certificates for the application::
 

	
 
        certtool --sec-param normal --generate-privkey --outfile ~/mysite/tls/wiki.example.com_https.key
 
        certtool --generate-certificate --load-ca-privkey ~/mysite/tls/ca.key --load-ca-certificate ~/mysite/tls/ca.pem --template ~/mysite/tls/wiki.example.com_https.cfg --load-privkey ~/mysite/tls/wiki.example.com_https.key --outfile ~/mysite/tls/wiki.example.com_https.pem
 

	
 
4. At this point we have exhausted what we can do with the built-in roles. Time
 
   to add some custom tasks.
 

	
 
   :file:`~/mysite/roles/wiki/tasks/main.yml`::
 

	
 
      ---
 

	
 
      - name: Set-up symbolic link for mysql_config for building MySQL-python
 
        file: src="/usr/bin/mariadb_config" dest="/usr/bin/mysql_config"
 
              state="link"
 

	
 
      - name: Create Django project directory
 
        file: dest="/var/www/wiki.example.com/code" state=directory
 
              owner=admin group=web-wiki_example_com
 
              mode=2750
 

	
 
      - name: Start Django project for the Wiki website
 
        command: /var/www/wiki.example.com/virtualenv/bin/exec django-admin.py startproject wiki_example_com /var/www/wiki.example.com/code
 
                 chdir=/var/www/wiki.example.com
 
                 creates=/var/www/wiki.example.com/code/wiki_example_com
 
        become: yes
 
        become_user: admin
 

	
 
      - name: Deploy settings for wiki website
 
        copy: src="{{ item }}" dest="/var/www/wiki.example.com/code/wiki_example_com/{{ item }}"
 
              mode=640 owner=admin group=web-wiki_example_com
 
        with_items:
 
           - settings.py
 
           - urls.py
 
        notify:
 
           - Restart website wiki.example.com
 

	
 
      - name: Deploy project database and deploy static files
 
        django_manage: command="{{ item }}"
 
                       app_path="/var/www/wiki.example.com/code/"
 
                       virtualenv="/var/www/wiki.example.com/virtualenv/"
 
        become: yes
 
        become_user: admin
 
        with_items:
 
           - syncdb
 
           - migrate
 
           - collectstatic
 

	
 
      - name: Deploy the superadmin creation script
 
        copy: src="create_superadmin.py" dest="/var/www/wiki.example.com/code/create_superadmin.py"
 
              owner=admin group=web-wiki_example_com mode=750
 

	
 
      - name: Create initial superuser
 
        command: /var/www/wiki.example.com/virtualenv/bin/exec ./create_superadmin.py
 
                 chdir=/var/www/wiki.example.com/code/
 
        become: yes
 
        become_user: admin
 
        register: wiki_superuser
 
        changed_when: wiki_superuser.stdout == "Created superuser."
 

	
 
5. There is a couple of files that we are deploying through the above
 
   tasks. Let's create them as well.
 

	
 
   :file:`~/mysite/roles/wiki/files/settings.py`::
 

	
 
      """
 
      Django settings for wiki_example_com project.
 

	
 
      For more information on this file, see
 
      https://docs.djangoproject.com/en/1.6/topics/settings/
 

	
 
      For the full list of settings and their values, see
 
      https://docs.djangoproject.com/en/1.6/ref/settings/
 
      """
 

	
 
      # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 
      import os
 
      BASE_DIR = os.path.dirname(os.path.dirname(__file__))
 

	
 

	
 
      # Quick-start development settings - unsuitable for production
 
      # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
 

	
 
      # SECURITY WARNING: keep the secret key used in production secret!
 
      SECRET_KEY = 'l^q+t$7h$ebls)v34+w9m9v4$n+^(9guxqntu&#cc4m&lfd-6_'
 

	
 
      # SECURITY WARNING: don't run with debug turned on in production!
 
      DEBUG = False
 

	
 
      TEMPLATE_DEBUG = False
 

	
 
      ALLOWED_HOSTS = ["wiki.example.com", "localhost"]
 

	
 

	
 
      # Application definition
 

	
 
      INSTALLED_APPS = (
 
          'django.contrib.admin',
 
          'django.contrib.auth',
 
          'django.contrib.contenttypes',
 
          'django.contrib.sessions',
 
          'django.contrib.messages',
 
          'django.contrib.staticfiles',
 
          'django.contrib.sites',
 
          'django.contrib.humanize',
 
          'django_nyt',
 
          'mptt',
 
          'sekizai',
 
          'sorl.thumbnail',
 
          'wiki',
 
          'wiki.plugins.attachments',
 
          'wiki.plugins.notifications',
 
          'wiki.plugins.images',
 
          'wiki.plugins.macros',
 
          'south',
 
      )
 

	
 
      MIDDLEWARE_CLASSES = (
 
          'django.contrib.sessions.middleware.SessionMiddleware',
 
          'django.middleware.common.CommonMiddleware',
 
          'django.middleware.csrf.CsrfViewMiddleware',
 
          'django.contrib.auth.middleware.AuthenticationMiddleware',
 
          'django.contrib.messages.middleware.MessageMiddleware',
 
          'django.middleware.clickjacking.XFrameOptionsMiddleware',
 
      )
 

	
 
      ROOT_URLCONF = 'wiki_example_com.urls'
 

	
 
      WSGI_APPLICATION = 'wiki_example_com.wsgi.application'
 

	
 

	
 
      # Database
 
      # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
 

	
 
      DATABASES = {
 
          'default': {
 
              'ENGINE': 'django.db.backends.mysql',
 
              'NAME': 'wiki',
 
              'USER': 'wiki',
 
              'PASSWORD': 'wiki',
 
              'HOST': '127.0.0.1',
 
              'PORT': '3306',
 
          }
 
      }
 

	
 
      # Internationalization
 
      # https://docs.djangoproject.com/en/1.6/topics/i18n/
 

	
 
      LANGUAGE_CODE = 'en-us'
 

	
 
      TIME_ZONE = 'Europe/Stockholm'
 

	
 
      USE_I18N = True
 

	
 
      USE_L10N = True
 

	
 
      USE_TZ = True
 

	
 

	
 
      # Static files (CSS, JavaScript, Images)
 
      # https://docs.djangoproject.com/en/1.6/howto/static-files/
 

	
 
      STATIC_URL = '/static/'
 

	
 
      STATIC_ROOT = '/var/www/wiki.example.com/htdocs/static'
 

	
 
      from django.conf import settings
 

	
 
      TEMPLATE_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + (
 
          "django.core.context_processors.debug",
 
          "django.core.context_processors.request",
 
          "sekizai.context_processors.sekizai",
 
      )
 

	
 
      SITE_ID=1
 

	
 
   :file:`~/mysite/roles/wiki/files/urls.py`::
 

	
 
      from django.conf.urls import patterns, include, url
 
      from wiki.urls import get_pattern as get_wiki_pattern
 
      from django_nyt.urls import get_pattern as get_nyt_pattern
 

	
 
      from django.contrib import admin
 
      admin.autodiscover()
 

	
 
      urlpatterns = patterns('',
 
          # Examples:
 
          # url(r'^$', 'wiki_example_com.views.home', name='home'),
 
          # url(r'^blog/', include('blog.urls')),
 

	
 
          url(r'^admin/', include(admin.site.urls)),
 
      )
 

	
 
      urlpatterns += patterns('',
 
          (r'^notifications/', get_nyt_pattern()),
 
          (r'', get_wiki_pattern())
 
      )
 

	
 
   :file:`~/mysite/roles/wiki/files/create_superadmin.py`::
 

	
 
      #!/usr/bin/env python
 

	
 
      import os
 
      os.environ['DJANGO_SETTINGS_MODULE']='wiki_example_com.settings'
 
      from django.conf import settings
 
      from django.contrib.auth.models import User
 

	
 
      User.objects.all()
 
      if len(User.objects.filter(username="admin")) == 0:
 
          User.objects.create_superuser('admin', 'john.doe@example.com', 'admin')
 
          print("Created superuser.")
 

	
 
6. Time to add the new role to our web server.
 

	
 
   :file:`~/mysite/playbooks/web.yml`::
 

	
 
      ---
 
      - hosts: web
 
        remote_user: ansible
 
        sudo: yes
 
        roles:
 
          - common
 
          - ldap_client
 
          - mail_forwarder
 
          - web_server
 
          - database_server
 
          - tbg
 
          - wiki
 

	
 
7. Apply the changes::
 

	
 
     ansible-playbook playbooks/site.yml
 

	
 
8. At this point Django Wiki has been installed, and you should be able to open
 
   the URL https://wiki.example.com/ (or http://wiki.example.com/) and log-in
 
   into *Django Wiki* with username ``admin`` and password ``admin``.
0 comments (0 inline, 0 general)