# prod/fabfile.pyimportosfromfabric.contrib.filesimportsedfromfabric.apiimportenv,local,runfromfabric.apiimportenv# initialize the base directoryabs_dir_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# declare environment global variables# root userenv.user='root'# list of remote IP addressesenv.hosts=['<remote-server-ip>']# password for the remote serverenv.password='<remote-server-password>'# full name of the userenv.full_name_user='<your-name>'# user groupenv.user_group='deployers'# user for the above groupenv.user_name='deployer'# ssh key pathenv.ssh_keys_dir=os.path.join(abs_dir_path,'ssh-keys')
请注意内嵌注释。请务必将远程服务器的 IP 地址添加到env.hosts多变的。更新env.full_name_user以及。暂缓更新env.password;我们很快就会讨论这个问题。看看所有的env变量 - 它们可以根据您的系统设置完全自定义。
设置 SSH 密钥
添加以下代码到fabfile.py:
defstart_provision():""" Start server provisioning """# Create a new directory for a new remote serverenv.ssh_keys_name=os.path.join(env.ssh_keys_dir,env.host_string+'_prod_key')local('ssh-keygen -t rsa -b 2048 -f {0}'.format(env.ssh_keys_name))local('cp {0}{1}/authorized_keys'.format(env.ssh_keys_name+'.pub',env.ssh_keys_dir))# Prevent root SSHing into the remote serversed('/etc/ssh/sshd_config','^UsePAM yes','UsePAM no')sed('/etc/ssh/sshd_config','^PermitRootLogin yes','PermitRootLogin no')sed('/etc/ssh/sshd_config','^#PasswordAuthentication yes','PasswordAuthentication no')install_ansible_dependencies()create_deployer_group()create_deployer_user()upload_keys()set_selinux_permissive()run('service sshd reload')upgrade_server()
defcreate_deployer_group():""" Create a user group for all project developers """run('groupadd {}'.format(env.user_group))run('mv /etc/sudoers /etc/sudoers-backup')run('(cat /etc/sudoers-backup; echo "%'+env.user_group+' ALL=(ALL) ALL") > /etc/sudoers')run('chmod 440 /etc/sudoers')
defcreate_deployer_user():""" Create a user for the user group """run('adduser -c "{}" -m -g {}{}'.format(env.full_name_user,env.user_group,env.user_name))run('passwd {}'.format(env.user_name))run('usermod -a -G {}{}'.format(env.user_group,env.user_name))run('mkdir /home/{}/.ssh'.format(env.user_name))run('chown -R {} /home/{}/.ssh'.format(env.user_name,env.user_name))run('chgrp -R {} /home/{}/.ssh'.format(env.user_group,env.user_name))
这个功能——
将新用户添加到deployers用户组,我们在上一个函数中定义的
设置用于保存 SSH 密钥对的 SSH 目录,并授予组和用户访问该目录的权限
上传 SSH 密钥
defupload_keys():""" Upload the SSH public/private keys to the remote server via scp """scp_command='scp {}{}/authorized_keys {}@{}:~/.ssh'.format(env.ssh_keys_name+'.pub',env.ssh_keys_dir,env.user_name,env.host_string)local(scp_command)
在这里,我们-
将本地生成的SSH密钥上传到远程服务器,以便非root用户无需输入密码即可通过SSH登录
将新建的公钥和授权密钥复制到远程服务器ssh 密钥目录
安装 Ansible 依赖项
添加以下函数来安装 Ansible 的依赖包:
definstall_ansible_dependencies():""" Install the python-dnf module so that Ansible can communicate with Fedora's Package Manager """run('dnf install -y python-dnf')
请记住,这是特定于 Fedora Linux 发行版的,因为我们将使用地下城与勇士模块用于安装软件包,但它可能因发行版而异。
将 SELinux 设置为宽容模式
下一个功能集SELinux到宽容模式。这样做是为了克服任何潜在的 Nginx 502 Bad Gateway错误.
defset_selinux_permissive():""" Set SELinux to Permissive/Disabled Mode """# for permissiverun('sudo setenforce 0')
同样,这是特定于 Fedora Linux 发行版的。
升级服务器
最后,升级服务器:
defupgrade_server():""" Upgrade the server as a root user """run('dnf upgrade -y')# optional command (necessary for Fedora 25)run('dnf install -y python')run('reboot')
$ sshroot@<server-ip-address>
You are required to change your password immediately (root enforced)Changing password for root.(current)UNIX password:New password:Retype new password:
一定要更新env.password使用新密码。退出服务器并返回本地终端,然后执行Fabric:
$ fab-f./prod/fabfile.pystart_provision
如果一切顺利,将生成新的 SSH 密钥,并且系统会要求您创建密码(请务必执行此操作!):
Generating public/private rsa key pair.Enter passphrase (empty for no passphrase):Enter same passphrase again:
将运行许多任务。之后deployer创建用户后,系统将提示您为该用户添加密码 -
[104.236.66.172] out: Changing password for user deployer.
# App Nameapp_name:django_bootstrap# Deployer User and Groupsdeployer_user:deployerdeployer_group:deployers# SSH Keys Directoryssh_dir:<path-to-your-ssh-keys>
### Configure the server for the Django app##-include:01_server.yml-include:02_git.yml-include:03_postgres.yml-include:04_dependencies.yml-include:05_migrations.yml-include:06_nginx.yml-include:07_gunicorn.yml-include:08_systemd.yml# - include: 09_fix-502.yml
### Update the DNF package cache and install packages as a root user##-name:Install required packagesdnf:name={{item}} state=latestbecome:yeswith_items:-vim-fail2ban-python3-devel-python-virtualenv-python3-virtualenv-python-devel-gcc-libselinux-python-redhat-rpm-config-libtiff-devel-libjpeg-devel-libzip-devel-freetype-devel-lcms2-devel-libwebp-devel-tcl-devel-tk-devel-policycoreutils-devel
在这里,我们列出了所有需要安装的系统软件包。
02_git.yml
### Clone and pull the repo##-name:Set up git configurationdnf:name=git state=latestbecome:yes-name:Clone or pull the latest codegit:repo={{ code_repository_url }}dest={{ app_dir }}
### Set up and configure postgres##-name:Install and configure dbdnf:name={{item}} state=latestbecome:yeswith_items:-postgresql-server-postgresql-contrib-postgresql-devel-python-psycopg2-name:Run initdb commandraw:postgresql-setup initdbbecome:yes-name:Start and enable postgresservice:name=postgresql enabled=yes state=startedbecome:yes-name:Create databasepostgresql_db:name={{ app_name }}become_user:postgresbecome:yes-name:Configure a new postgresql userpostgresql_user:db={{ app_name }}name={{ db_user }}password={{ db_password }}priv=ALLrole_attr_flags=NOSUPERUSERbecome:yesbecome_user:postgresnotify:-restart postgres
更新组变量/全部以及剧本所需的数据库配置:
# DB Configurationdb_url:postgresql://{{deployer_user}}:{{db_password}}@localhost/{{app_name}}db_password:thisissomeseucrepassworddb_name:"{{app_name}}"db_user:"{{deployer_user}}"
### Set up all the dependencies in a virtualenv required by the Django app##-name:Create a virtualenv directoryfile:path={{ venv_dir }} state=directory-name:Install dependenciespip:requirements={{ app_dir }}/requirements.txtvirtualenv={{ venv_dir }}virtualenv_python=python3.5-name:Create the .env file for running ad-hoc python commands in our virtualenvtemplate:src=env.j2 dest={{ app_dir }}/.envbecome:yes
### Run db migrations and get all static files##-name:Make migrationsshell:".{{app_dir}}/.env;{{venv_python}}{{app_dir}}/manage.pymakemigrations"become:yes-name:Migrate databasedjango_manage:app_path={{ app_dir }}command=migratevirtualenv={{ venv_dir }}-name:Get all static filesdjango_manage:app_path={{ app_dir }}command=collectstaticvirtualenv={{ venv_dir }}become:yes
06_nginx.yml
### Configure nginx web server##-name:Set up nginx configdnf:name=nginx state=latestbecome:yes-name:Write nginx conf filetemplate:src=django_bootstrap.conf dest=/etc/nginx/conf.d/{{ app_name }}.confbecome:yesnotify:-restart nginx
添加以下变量组变量/全部:
# Remote Server Detailsserver_ip:<remote-server-ip>wsgi_server_port:8000
### Set up Gunicorn and configure systemd to execute gunicorn_start script##-name:Create a deploy directoryfile:path={{ deploy_dir }} state=directorybecome:yes-name:Create the gunicorn_start script for running our app from systemd servicetemplate:src=gunicorn_startdest={{ deploy_dir }}/gunicorn_startbecome:yes-name:Make the gunicorn_start script executableraw:cd {{ deploy_dir }}; chmod +x gunicorn_startbecome:yes
添加更多变量组变量/全部:
# Deploy Dir in App Directorydeploy_dir:'{{app_dir}}/deploy'# WSGI Varsdjango_wsgi_module:config.wsgidjango_settings_module:config.settings.productiondjango_secret_key:'changeme'database_url:'{{db_url}}'
添加Gunicorn_start模板:
#!/bin/bash### Define script variables# Name of the appNAME='{{ app_name }}'# Path to virtualenvVIRTUALENV='{{ venv_dir }}'# Django Project DirectoryDJANGODIR='{{ app_dir }}'# The user to run asUSER={{deployer_user}}# The group to run asGROUP={{deployer_group}}# Number of worker processes Gunicorn should spawnNUM_WORKERS=3# Settings file that Gunicorn should useDJANGO_SETTINGS_MODULE={{django_settings_module}}# WSGI module nameDJANGO_WSGI_MODULE={{django_wsgi_module}}### Activate virtualenv and create environment variablesecho"Starting $NAME as `whoami`"# Activate the virtual environmentcd$VIRTUALENVsourcebin/activate
cd$DJANGODIR# Defining the Environment VariablesexportDJANGO_SECRET_KEY='{{ django_secret_key }}'exportDATABASE_URL='{{ db_url }}'exportDJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULEexportPYTHONPATH=$DJANGODIR:$PYTHONPATH### Start Gunicornexecgunicorn${DJANGO_WSGI_MODULE}:application\--name$NAME\--workers$NUM_WORKERS\--user=$USER--group=$GROUP\--log-level=debug\--bind=127.0.0.1:8000
08_systemd.yml
### Set up systemd for executing gunicorn_start script##-name:write a systemd service filetemplate:src=django-bootstrap.servicedest=/etc/systemd/systembecome:yesnotify:-restart app-restart nginx