RHEL 8 Beta研讨会:构建实时Web应用程序

RHEL 8 Beta为开发人员提供了许多新功能,其中的一些功能可能会分页显示,但是,在实践中学习新事物总是更好的做法,因此我们建议您参加一个研讨会以实际创建基于Red Hat Enterprise Linux 8 Beta的应用程序基础结构。



我们将Python(Django和PostgreSQL的组合,一个用于创建应用程序的相当常见的软件包)组合在一起,并配置RHEL 8 Beta与它们一起使用。 然后添加更多(未分类)成分。

测试环境将发生变化,因为研究自动化功能,使用容器并尝试使用多台服务器的环境非常有趣。 要开始使用新项目,您可以先手动创建一个简单的小型原型-通过这种方式,您可以查看实际发生的情况以及如何进行交互,然后进行自动化并创建更复杂的配置。 今天是创建这样一个原型的故事。

让我们从部署RHEL 8 Beta VM虚拟机映像开始。 您可以从头开始安装虚拟机,也可以使用Beta订阅提供的KVM来宾映像。 使用来宾映像时,您将需要配置虚拟CD,其中将包含用于云初始化(cloud-init)的元数据和用户数据。 您不需要对磁盘结构或可用软件包做任何特殊的事情,任何配置都可以。

让我们更详细地了解整个过程。

安装Django


使用最新版本的Django,您将需要具有Python 3.5或更高版本的虚拟环境(virtualenv)。 在Beta的注释中,您可以看到Python 3.6可用,让我们检查一下这是否成立:

[cloud-user@8beta1 ~]$ python -bash: python: command not found [cloud-user@8beta1 ~]$ python3 -bash: python3: command not found 

红帽积极地将Python用作RHEL中的系统工具包,那么为什么会得到这个结果?

事实是,许多使用Python的开发人员仍在考虑从Python 2切换到Python 3,而Python 3本身正在积极开发中,并且越来越多的新版本不断出现。 因此,为了满足稳定的系统工具的需求,并同时为用户提供访问各种新版本的Python的权限,系统Python已转移到新的程序包中,并提供了安装Python 2.7和3.6的功能。 有关更改及其执行原因的更多信息,请参见Langdon White博客文章

因此,为了使用Python,您只需要安装两个软件包,而python3-pip将作为依赖项上拉。

 sudo yum install python36 python3-virtualenv 

为什么不按照Langdon的建议使用直接模块调用,而不安装pip3? 考虑到即将到来的自动化,众所周知Ansible将需要安装pip,因为pip模块不支持带有自定义pip可执行文件的虚拟环境(virtualenvs)。

使用可使用的python3解释器,您可以继续Django安装过程,并获得与其他组件一起使用的工作系统。 网络为实现提供了许多选择。 这里提供了一个版本,但是用户可以使用自己的流程。

默认情况下,将使用Yum安装RHEL 8中可用的PostgreSQL和Nginx版本。

 sudo yum install nginx postgresql-server 

PostgreSQL需要psycopg2,但仅在virtualenv环境中可用,因此我们将使用pip3以及Django和Gunicorn进行安装。 但是首先我们需要配置virtualenv。

关于选择合适的位置安装Django项目的争论一直很多,但是如果有疑问,您可以随时使用Linux Filesystem Hierarchy Standard。 FHS特别指出/ srv用于:“存储主机特定的数据-系统提供的数据,例如,来自Web服务器的数据和脚本,存储在FTP服务器上的数据以及控制系统的存储库版本(2004年在FHS-2.3中引入)。”

这只是我们的情况,因此我们将所需的所有内容都放到了/ srv中,该文件由我们的应用程序用户(云用户)拥有。

 sudo mkdir /srv/djangoapp sudo chown cloud-user:cloud-user /srv/djangoapp cd /srv/djangoapp virtualenv django source django/bin/activate pip3 install django gunicorn psycopg2 ./django-admin startproject djangoapp /srv/djangoapp 

设置PostgreSQL和Django很简单:创建数据库,创建用户,配置权限。 首次设置PostgreSQL时要牢记一点-postgresql-setup脚本,该脚本与postgresql-server软件包一起安装。 该脚本可帮助您执行与管理数据库集群有关的基本任务,例如初始化集群或更新过程。 要在RHEL系统上配置PostgreSQL的新实例,我们需要运行以下命令:

 sudo /usr/bin/postgresql-setup -initdb 

之后,您可以使用systemd启动PostgreSQL,创建数据库并在Django中配置项目。 请记住,在对客户端身份验证配置文件(通常为pg_hba.conf)进行更改之后,请重新启动PostgreSQL,以为应用程序用户配置密码存储。 如果遇到其他困难,请确保在pg_hba.conf文件中更改IPv4和IPv6设置。

 systemctl enable -now postgresql sudo -u postgres psql postgres=# create database djangoapp; postgres=# create user djangouser with password 'qwer4321'; postgres=# alter role djangouser set client_encoding to 'utf8'; postgres=# alter role djangouser set default_transaction_isolation to 'read committed'; postgres=# alter role djangouser set timezone to 'utc'; postgres=# grant all on DATABASE djangoapp to djangouser; postgres=# \q 

在文件/var/lib/pgsql/data/pg_hba.conf中:

 # IPv4 local connections: host all all 0.0.0.0/0 md5 # IPv6 local connections: host all all ::1/128 md5 

在文件/srv/djangoapp/settings.py中:

 # Database DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': '{{ db_name }}', 'USER': '{{ db_user }}', 'PASSWORD': '{{ db_password }}', 'HOST': '{{ db_host }}', } } 

在项目中配置settings.py文件并配置数据库配置之后,可以启动开发服务器以确保一切正常。 启动开发服务器后,最好创建一个admin用户来测试与数据库的连接。

 ./manage.py runserver 0.0.0.0:8000 ./manage.py createsuperuser 

WSGI? 围


开发服务器对于测试很有用,但是要运行该应用程序,必须为Web服务器网关接口(WSGI)配置适当的服务器和代理。 有几种常见的捆绑包,例如,带有uWSGI的Apache HTTPD或带有Gunicorn的Nginx。

Web服务器网关接口的目标是将请求从Web服务器重定向到Python Web框架。 WSGI是使用CGI机制时的糟糕历史,如今,无论使用什么Web服务器或Python框架,WSGI实际上都是标准。 尽管分布广泛,但是在使用这些框架时仍然存在许多细微差别,并且有很多选择。 在这种情况下,我们将尝试通过套接字在Gunicorn和Nginx之间建立交互。

由于这两个组件都安装在同一服务器上,因此我们将尝试使用UNIX套接字而不是网络套接字。 由于通信仍然需要套接字,因此让我们再尝试一步,并通过systemd为Gunicorn配置套接字激活。

创建套接字激活的服务的过程非常简单。 首先,创建一个包含ListenStream指令的单元文件,该文件指向将要创建UNIX套接字的点,然后是该服务的单元文件,其中Requires指令将指向该套接字单元文件。 然后,在服务的单元文件中,仅保留从虚拟环境调用Gunicorn并为UNIX套接字和Django应用程序创建WSGI绑定。

这是可以作为基础的单位文件的一些示例。 首先,配置套接字。

 [Unit] Description=Gunicorn WSGI socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target 

现在,您需要配置Gunicorn守护程序。

 [Unit] Description=Gunicorn daemon Requires=gunicorn.socket After=network.target [Service] User=cloud-user Group=cloud-user WorkingDirectory=/srv/djangoapp ExecStart=/srv/djangoapp/django/bin/gunicorn \ —access-logfile - \ —workers 3 \ —bind unix:gunicorn.sock djangoapp.wsgi [Install] WantedBy=multi-user.target 

对于Nginx,只需创建一个代理配置文件并设置一个目录即可存储静态内容(如果使用它)。 在RHEL中,Nginx配置文件为/etc/nginx/conf.d。 您可以在其中将以下示例复制到文件/etc/nginx/conf.d/default.conf中,然后启动服务。 确保根据您的主机名指定server_name。

 server { listen 80; server_name 8beta1.example.com; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /srv/djangoapp; } location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://unix:/run/gunicorn.sock; } } 

使用systemd运行Gunicorn和Nginx套接字,即可开始测试。

网关错误?


如果在浏览器中输入地址,则很可能会收到502 Bad Gateway错误。 这可能是由于UNIX套接字的权限配置不正确,或者是由于SELinux中与访问控制有关的更复杂的问题。

在nginx错误日志中,您可以看到如下一行:

 2018/12/18 15:38:03 [crit] 12734#0: *3 connect() to unix:/run/gunicorn.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.122.1, server: 8beta1.example.com, request: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", host: "8beta1.example.com" 

如果我们直接测试Gunicorn,我们将得到一个空答案。

 curl —unix-socket /run/gunicorn.sock 8beta1.example.com 

让我们看看为什么会这样。 如果您打开日志,那么很可能我们会发现问题与SELinux有关。 由于我们正在运行一个尚未为其创建自己的策略的守护程序,因此将其标记为init_t。 让我们在实践中测试该理论。

 sudo setenforce 0 

所有这些都会引起批评和流血的眼泪,但这只是调试原型。 我们仅关闭检查以确保这是问题所在,然后将所有内容返回到他们的位置。

通过在浏览器中刷新页面或重新启动curl命令,您可以看到Django测试页面。

因此,确保一切正常,并且没有其他权限问题,我们重新启用SELinux。

 sudo setenforce 1 

由于目前还没有真正的Django应用程序,因此也没有关于audit2allow和基于警报使用sepolgen创建策略的讨论,因为还没有关于Gunicorn可能要访问的内容以及应该拒绝哪些访问的完整说明。 因此,有必要使SELinux保持工作状态以保护系统,同时允许应用程序启动并在审核日志中保留消息,以便随后可以基于它们创建真实的策略。

指定允许的域


并不是每个人都听说过SELinux中允许的域,但是其中没有新内容。 许多人甚至与他们合作,却自己没有意识到。 根据审核消息创建策略时,创建的策略是允许的域。 让我们尝试创建最简单的宽松策略。

要为Gunicorn创建一个特定的允许域,您需要一个特定的策略,并且还需要标记相应的文件。 此外,还需要工具来组合新策略。

 sudo yum install selinux-policy-devel 

解析的域机制是识别问题的好工具,特别是在涉及自定义应用程序或没有创建策略的应用程序时。 在这种情况下,Gunicorn允许的域策略将尽可能简单-声明主要类型(gunicorn_t),声明将用于标记多个可执行文件的类型(gunicorn_exec_t),然后为系统配置转换以正确标记正在运行的进程。 最后一行在加载时将策略设置为默认启用。

gunicorn.te:
 policy_module(gunicorn, 1.0) type gunicorn_t; type gunicorn_exec_t; init_daemon_domain(gunicorn_t, gunicorn_exec_t) permissive gunicorn_t; 

您可以编译此策略文件并将其添加到系统中。

 make -f /usr/share/selinux/devel/Makefile sudo semodule -i gunicorn.pp sudo semanage permissive -a gunicorn_t sudo semodule -l | grep permissive 

让我们检查SELinux是否阻止除了未知守护程序正在访问的内容以外的其他任何内容。

 sudo ausearch -m AVC type=AVC msg=audit(1545315977.237:1273): avc: denied { write } for pid=19400 comm="nginx" name="gunicorn.sock" dev="tmpfs" ino=52977 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file permissive=0 

SELinux阻止Nginx将数据写入Gunicorn使用的UNIX套接字。 通常,在这种情况下,政客开始变化,但未来还有其他任务。 您还可以通过将域设置从限制域转换为权限域来更改域设置。 现在将httpd_t移到权限域。 这将为Nginx提供必要的访问权限,并且我们将能够继续进行进一步的调试工作。

 sudo semanage permissive -a httpd_t 

因此,当有可能保留SELinux保护(实际上,您不应该让SELinux处于限制模式)并且加载了权限域时,您需要找出确切需要标记为gunicorn_exec_t的内容,以便所有功能都能按预期工作。 让我们尝试访问该网站以查看有关访问限制的新消息。

 sudo ausearch -m AVC -c gunicorn 

您会看到很多包含“ comm =” gunicorn”的消息,它们对/ srv / djangoapp中的文件执行各种操作,因此显然,这只是您应标记的命令之一。

但是此外,还会出现如下消息:

 type=AVC msg=audit(1545320700.070:1542): avc: denied { execute } for pid=20704 comm="(gunicorn)" name="python3.6" dev="vda3" ino=8515706 scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0 

如果您查看gunicorn服务的状态或运行ps命令,则不会显示任何正在运行的进程。 Gunicorn似乎正在尝试在我们的virtualenv环境中访问Python解释器,可能正在运行工作脚本(工作人员)。 现在,让我们标记这两个可执行文件,看看是否可以打开Django测试页。

 chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6 

您将需要重新启动gunicorn服务,以便可以选择一个新标签。 您可以立即重新启动它,也可以停止服务,并在浏览器中打开站点时让套接字启动它。 确保进程使用ps获得正确的标签。

 ps -efZ | grep gunicorn 

请记住,稍后再创建普通的SELinux策略!

如果现在查看AVC消息,则最后一条消息包含与应用程序相关的所有内容的permissive = 1,其余部分包含permissive = 0。 如果您了解实际应用程序需要哪种访问方式,则可以快速找到解决此类问题的最佳方法。 但是在那之前,最好对系统进行保护,并通过Django项目获得清晰,可用的审核。

 sudo ausearch -m AVC 

原来!


一个带有Nginx和Gunicorn WSGI前端的工作Django项目出现了。 我们从RHEL 8 Beta存储库配置了Python 3和PostgreSQL 10。 现在,您可以继续并创建(或只是部署)Django应用程序,或者探索RHEL 8 Beta中的其他可用工具,以自动执行调整过程,提高性能,甚至容器化此配置。

Source: https://habr.com/ru/post/zh-CN447540/


All Articles