Skip to content

Instantly share code, notes, and snippets.

@tonybolanyo
Last active August 26, 2019 17:38
Show Gist options
  • Save tonybolanyo/d6210fcd3b58f9216ec6ede36942895e to your computer and use it in GitHub Desktop.
Save tonybolanyo/d6210fcd3b58f9216ec6ede36942895e to your computer and use it in GitHub Desktop.
Deploy python+mysql in Ubuntu 18.04
  1. Create VPS with Ubuntu 18.04

  2. Update system

    sudo apt-get update && sudo apt-get upgrade -y
  3. Change SSH default port

    sudo vim /etc/ssh/sshd_config
  4. Change Port line and write file Port 22 ==> Port XXXX

  5. Reload service and test access

    sudo service ssh reload
  6. Install some needed packages

    sudo apt-get install -y mysql-server nginx build-essential zlib1g-dev libssl-dev \
         libbz2-dev libreadline-dev libsqlite3-dev python-setuptools circus libffi-dev \
         python3-dev libmysqlclient-dev
  7. Secure MySQL

    sudo mysql_secure_installation
  8. Create DB and user with all privileges on the new database

    sudo mysqladmin create database_name
    sudo mysql
    mysql> CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
    mysql> GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'localhost';
    mysql> FLUSH PRIVILEGES;
    mysql> exit
  9. Test user can connect

    mysql -u username -p
    mysql> SHOW GRANTS FOR 'abeduser'@'localhost';
    mysql> SHOW databases;
    mysql> USE database_name;
    mysql> CREATE TABLE dropme (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));
    mysql> INSERT INTO dropme (name) VALUES ('testing database');
    mysql> SELECT * FROM dropme;
    mysql> DROP TABLE dropme;
    mysql> exit
  10. Create user for application, disable password and add to web group

    sudo adduser appuser
    sudo passwd -l appuser
    sudo adduser appuser www-data
  11. Create a deploy key

    sudo -u appuser -i
    appuser:~$ ssh-keygen -t rsa -b 4096
    appuser:~$ cat ~/.ssh/id_rsa.pub
  12. Copy and paste de pubkey in Github deploy keys settings for the project

  13. Clone the repo

    appuser:~$ git clone [email protected]:tonybolanyo/reponame.git ~/app
  14. Install pyenv and pyenv-virtualenv and restart shell

    appuser:~$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
    appuser:~$ git clone https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
    appuser:~$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
    appuser:~$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
    appuser:~$ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.bashrc
    appuser:~$ echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
    appuser:~$ exec "$SHELL"
  15. Install python, create and activate virtual environment

    appuser:~$ pyenv install 3.7.4
    appuser:~$ pyenv virtualenv 3.7.4 env
    appuser:~$ pyenv activate env
  16. Upgrade pip and install requirements

appuser:~$ pip install --upgrade pip
appuser:~$ pip install -r app/requirements/prod.txt
  1. Export your env vars as you need, apply migrations and create your superuser
appuser:~$ export SECRET_KEY=<secret_key>
appuser:~$ export DB_NAME=appname
appuser:~$ export DB_USER=dbuser
appuser:~$ export DJANGO_SETTINGS_MODULE=app.settings_production

appuser:~$ cd app/src
appuser:~/app/src$ python manage.py migrate
appuser:~/app/src$ python manage.py collectstatic --noinput
appuser:~/app/src$ python manage.py createsuperuser
  1. Back to main user with sudo capabilities
appuser:~/app/src$ pyenv deactivate
appuser:~/app/src$ logout

Configure circus

  1. Create app config file
sudo vim /etc/circus/conf.d/appname.ini

Sample .ini content

[watcher:appname]
working_dir = /home/appname/app/src/
cmd = gunicorn
args = -w 1 -t 180 --pythonpath=. -b unix:/home/appname/appname.sock appname.wsgi
uid = appname
numprocesses = 1
autostart = true
send_hup = true
stdout_stream.class = FileStream
stdout_stream.filename = /home/appname/logs/gunicorn.stdout.log
stdout_stream.max_bytes = 10485760
stdout_stream.backup_count = 4
stderr_stream.class = FileStream
stderr_stream.filename = /home/appname/logs/gunicorn.stderr.log
stderr_stream.max_bytes = 10485760
stderr_stream.backup_count = 4
copy_env = true
virtualenv = /home/appname/.pyenv/versions/env
virtualenv_py_ver = 3.6

[env:appname]
DJANGO_SETTINGS_MODULE = appname.settings.prod
SECRET_KEY = <secret_key>
DB_NAME = <db_name>
DB_USER = <db_user>

NOTE: You can test circus easily using circusd /etc/circus/conf.d/appname.ini

Start circus service

sudo /etc/init.d/circusd start
  1. Create a site definition for ngnix
server {
	listen 80;
	server_name appname.com;

	access_log /home/appname/app/logs/nginx-access.log;
	error_log /home/appname/app/logs/nginx-error.log;

	root /home/appname/app/src/;

	client_max_body_size 10M;

	location /static {
		alias /home/appname/app/src/static;
	}

    location /media {
        alias /home/appname/app/src/media;
    }

	location / {
		include proxy_params;
		proxy_pass http://unix:/home/appname/appname.sock;
	}
}
  1. Create a symbolic link to activate site, test configuration and restart nginx service
sudo ln -s /etc/nginx/sites-available/wordplease /etc/nginx/sites-enabled/
sudo nginx -t
sudo service nginx restart
  1. Enable HTTPS using certbot:

Direct link: https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx Home: https://letsencrypt.org/

[watcher:appname]
working_dir = /home/appname/app/src/
cmd = gunicorn
args = -w 1 -t 180 --pythonpath=. -b unix:/home/appname/appname.sock appname.wsgi
uid = appname
numprocesses = 1
autostart = true
send_hup = true
stdout_stream.class = FileStream
stdout_stream.filename = /home/appname/app/logs/gunicorn.stdout.log
stdout_stream.max_bytes = 10485760
stdout_stream.backup_count = 4
stderr_stream.class = FileStream
stderr_stream.filename = /home/appname/app/logs/gunicorn.stderr.log
stderr_stream.max_bytes = 10485760
stderr_stream.backup_count = 4
copy_env = true
virtualenv = /home/appname/.pyenv/versions/env
virtualenv_py_ver = 3.7
[env:appname]
DJANGO_SETTINGS_MODULE = appname.settings.prod
SECRET_KEY = <secret_key>
DB_NAME = <db_name>
DB_USER = <db_user>
server {
listen 80;
server_name appname.com;
access_log /home/appname/app/logs/nginx-access.log;
error_log /home/appname/app/logs/nginx-error.log;
root /home/appname/app/src/;
client_max_body_size 10M;
location /static {
alias /home/appname/app/src/static;
}
location /media {
alias /home/appname/app/src/media;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/appname/appname.sock;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment