使用Ansible部署Ruby应用程序





5.00/5 (1投票)
了解如何为您的Ruby应用程序实现配置脚本
引言
如今,您可以在几秒钟内启动并运行您自己的专用服务器。一旦启动,您真的会花费几个小时来配置它以满足您的应用程序需求吗?您真的想对每个新服务器重复相同的步骤吗?在本文中,我将向您介绍使用Ansible(一个简单的IT自动化工具包)和Ubuntu 14.04 LTS服务器作为操作系统来进行自动化安装的想法。
背景
您需要对ansible文件的语法有基本的了解。如果您还没有使用过Ansible,我建议您查看一些介绍性文章,例如https://docs.ansible.org.cn/ansible/intro.html,或者查看类似这个的幻灯片。
烹饪
我们需要部署以下组件:Ruby、带有Passenger的Web服务器、您自己的应用程序。为了演示目的,我们将安装一个众所周知的入门程序Devise
Ruby切换器 Chruby
我过去常常选择RMV,但最近,我成了chruby的粉丝——它轻量级、易于理解并且确实有效——可在Github上找到。
在研究安装说明后,我们使用Ansible自动化手动安装步骤,并获得一个奖励:可重用的配方来安装ch_ruby
。
---
- name: Ruby | Check if chruby is present
shell: test -x /usr/local/bin/chruby-exec
when: ansible_system == "Linux"
ignore_errors: yes
register: chruby_present
tags: ruby
- name: Ruby | Download chruby distribution
get_url: url="http://github.com/postmodern/chruby/archive/v{{ chruby_version }}.tar.gz"
dest="/tmp/chruby-{{ chruby_version }}.tar.gz"
when: chruby_present|failed
tags: ruby
- name: Ruby | unpack chruby
command: tar xf "/tmp/chruby-{{ chruby_version }}.tar.gz"
chdir="/tmp"
when: chruby_present|failed
tags: ruby
- name: Ruby | chruby install target
command: make install
chdir="/tmp/chruby-{{ chruby_version }}"
become: yes
when: chruby_present|failed
tags: ruby
- name: Ruby | autoload script
template: src="{{role_dir}}/templates/ch_ruby.sh.j2" dest=/etc/profile.d/chruby.sh
become: yes
tags: ruby
Ruby 安装
Ruby-install是来自同一作者的第二个工具包。目前,当我需要安装特定的Ruby版本时,这种方法是我的首选。该工具可在Github上找到。
一旦我们研究了ruby-install
的设置说明,我们就可以使用一系列ansible步骤来自动化这些步骤
---
- name: Ruby | Check if ruby install is present
shell: test -x /usr/local/bin/ruby-install
when: ansible_system == "Linux"
ignore_errors: yes
register: rubyinstall_present
tags: ruby
- name: Ruby | Ruby install | package dependencies
apt: pkg={{ item }} state=present force="yes" update_cache="yes"
when: ansible_system == "Linux"
with_items:
- build-essential
- libffi-dev
- libgdbm-dev
- libncurses5-dev
- libreadline-dev
- libreadline6-dev
- libtinfo-dev
- libyaml-dev
become: yes
tags: ruby
- name: Ruby | Download rubyinstall
get_url: url=http://github.com/postmodern/ruby-install/archive/v{{ ruby_install_version }}.tar.gz
dest=/tmp/ruby-install-{{ ruby_install_version }}.tar.gz
when: rubyinstall_present | failed
tags: ruby
- name: Ruby | Unpack ruby-install
command: tar xf /tmp/ruby-install-{{ ruby_install_version }}.tar.gz
chdir=/tmp
when: rubyinstall_present | failed
tags: ruby
- name: Ruby | Run ruby-install install target
command: make install
chdir=/tmp/ruby-install-{{ ruby_install_version }}
when: rubyinstall_present | failed
become: yes
tags: ruby
- name: Ruby | Download list of rubies available
command: ruby-install
when: rubyinstall_present | failed
become: yes
tags: ruby
Ruby
现在是时候安装Ruby了。这里有一个小评论:如果您部署在共享服务器上,您很可能希望能够拥有多个ruby版本并在它们之间切换。另一方面,如果您将应用程序部署到专用主机上,通常我也会用相同的ruby版本替换默认的系统ruby。
使用上述工具,Ruby安装配方紧凑而清晰
---
- name: Ruby | Find out if ruby_version is already installed
stat: path={{rubies_location}}/ruby-{{ruby_version}}
register: ruby_version_present
tags: ruby
- name: Ruby | Install ruby_version if necessary
command: '/usr/local/bin/ruby-install ruby {{ruby_version}}'
when: not ruby_version_present.stat.exists
become: yes
tags: ruby
- debug: var="ruby_install_setsystem"
- name: Ruby | Update SYSTEM ruby_version if necessary
command: '/usr/local/bin/ruby-install --system ruby {{ruby_version}}'
when: option_ruby_install_setsystem
become: yes
tags: ruby
Web服务器和Passenger
感谢Phusion Passenger团队,他们出色地为大多数流行的平台和配置提供了预构建的二进制文件,请访问此链接。这使我们能够跳过从源代码编译phusion passengers、重新编译Web服务器等的步骤,并使用预构建的二进制文件。
从历史上看,我更喜欢Nginx而不是经典的Apache,因此我们将安装带有passenger的预构建Nginx
---
- name: Nginx | Check if is present
command: test -x /usr/sbin/nginx
when: ansible_os_family == "Debian"
ignore_errors: yes
register: nginx_present
tags: nginx
- name: Passenger | Add GPG key to apt keyring
apt_key: keyserver=keyserver.ubuntu.com id=561F9B9CAC40B2F7
when: ansible_os_family == "Debian" and nginx_present|failed
tags: passenger
become: yes
- name: Passenger | Install needed packages
apt: state=present pkg="{{item}}"
with_items:
- apt-transport-https
- ca-certificates
when: ansible_os_family == "Debian" and nginx_present|failed
become: yes
tags: passenger
- name: Passenger | Add nginx extras repository
apt_repository: repo="deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main" state=present
when: ansible_os_family == "Debian" and nginx_present|failed
tags: passenger
become: yes
- name: Ruby | Install Nginx extra and Phusion Passenger
apt: state=present update_cache=yes pkg="{{item}}"
when: ansible_os_family == "Debian" and nginx_present|failed
with_items:
- nginx-extras
- passenger
become: yes
tags: passenger
- name: Nginx | Create sites available/enabled directories
file: path={{item}} state=directory mode=0755
with_items:
- /etc/nginx/sites-available
- /etc/nginx/sites-enabled
when: ansible_os_family == "Debian" and nginx_present|failed
tags:
- nginx
- passenger
become: yes
- name: Nginx | Configure include sites-enabled
lineinfile: dest=/etc/nginx/nginx.conf regexp=".*sites-enabled.*"
line=" include /etc/nginx/sites-enabled/*;" insertbefore="}" state=present
tags:
- nginx
- passenger
when: ansible_os_family == "Debian" and nginx_present|failed
become: yes
- name: Nginx | Disable default site
file: path=/etc/nginx/sites-enabled/default state=absent
tags:
- nginx
- passenger
when: ansible_os_family == "Debian" and nginx_present|failed
become: yes
- name: Nginx | Uncomment server_names_hash_bucket_size
lineinfile: dest=/etc/nginx/nginx.conf
regexp="^(\s*)#\s*server_names_hash_bucket_size"
line="\1server_names_hash_bucket_size 64;" backrefs=yes
become: yes
when: ansible_os_family == "Debian" and nginx_present|failed
tags:
- nginx
- passenger
- name: Nginx | Set ruby to system one
lineinfile: dest=/etc/nginx/nginx.conf regexp="^(\s*)#\s*passenger_ruby"
line="passenger_ruby /usr/local/bin/ruby;" backrefs=yes
become: yes
when: ansible_os_family == "Debian" and nginx_present|failed
tags:
- nginx
- passenger
- name: Nginx | Set ruby to system one
lineinfile: dest=/etc/nginx/nginx.conf regexp="^(\s*)#\s*passenger_root"
line="passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;"
backrefs=yes
become: yes
when: ansible_os_family == "Debian" and nginx_present|failed
tags:
- nginx
- passenger
- name: Nginx | Reload
service: name=nginx state=reloaded
when: ansible_os_family == "Debian" and nginx_present|failed
tags:
- nginx
- passenger
become: yes
上面安装脚本的一些评论,这可能需要在您自己的场景中进行更改
- 名为“创建sites available/enabled目录”的任务以及下一个任务——它实际上为VHosts配置实现了类似Apache的“sites-available / sites-enabled”文件夹结构。如果您更喜欢默认设置 - 请将其注释掉。
- 使用以下命令指定ruby的位置给passenger
lineinfile: dest=/etc/nginx/nginx.conf regexp="^(\s*)#\s*passenger_ruby" line="passenger_ruby /usr/local/bin/ruby;" backrefs=yes
如您所见,上面的替换假定使用了系统ruby。您可能希望在此处指定不同的Ruby路径。
这两个任务的目标是获取nginx.conf并设置两个参数:passenger_root
和passenger_ruby
,如上述注释中的原始说明所示。
##
# Uncomment it if you installed passenger or passenger-enterprise
##
passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /usr/local/bin/ruby;
如何验证您是否正确安装了nginx和passenger?
执行这些命令并验证设置
sudo /usr/bin/passenger-config validate-install
What would you like to validate?
Use <space> to select.
If the menu doesn't display correctly, press '!'
‣ ⬢ Passenger itself
⬡ Apache
-------------------------------------------------------------------------
* Checking whether this Passenger install is in PATH... ✓
* Checking whether there are no other Passenger installations... ✓
Everything looks good. :-)
以及/usr/sbin/passenger-memory-stats - 您应该同时看到 - Nginx 和 passenger 进程。
sudo /usr/sbin/passenger-memory-stats
Version: 5.0.26
Date : 2016-03-18 11:17:57 +0200
------------- Apache processes -------------
*** WARNING: The Apache executable cannot be found.
Please set the APXS2 environment variable to your 'apxs2' executable's filename,
or set the HTTPD environment variable to your 'httpd' or 'apache2' executable's filename.
--------- Nginx processes ----------
PID PPID VMSize Private Name
------------------------------------
8768 9991 138.1 MB 1.1 MB nginx: worker process
8769 9991 137.8 MB 0.9 MB nginx: worker process
8770 9991 137.8 MB 0.9 MB nginx: worker process
8771 9991 137.8 MB 0.9 MB nginx: worker process
9991 1 137.8 MB 0.9 MB nginx: master process /usr/sbin/nginx
### Processes: 5
### Total private dirty RSS: 4.68 MB
---- Passenger processes -----
PID VMSize Private Name
------------------------------
8742 436.3 MB 1.0 MB Passenger watchdog
8745 982.9 MB 2.0 MB Passenger core
8756 444.5 MB 1.1 MB Passenger ust-router
8806 387.1 MB 69.3 MB Passenger RubyApp: /var/www/public (production)
### Processes: 4
### Total private dirty RSS: 73.47 MB
slavko@ERM:/etc/nginx$
应用程序本身的设置
让我们定义应用程序参数:特别是:构建gem所需的操作系统软件包、应用程序密钥 - 用于对密码进行哈希处理、应用程序环境参数、数据库连接详细信息。
app_dependencies:
- libsqlite3-dev
- libmysqlclient-dev
- libpq-dev
- git
- nodejs
- npm
app_short_name: app
app_env: production
app_domain: domain.local
app_secret: 82d58d3dfb91238b495a311eb8539edf5064784f1d58994679db8363ec241c745bef0b446bfe44d66cbf91a2f4e497d8f6b1ef1656e3f405b0d263a9617ac75e
app_repository: https://github.com/RailsApps/rails-devise.git
# app_repository_keyname: id_rsa_app
app_base_dir: /var/www
app_www_root: "{{app_base_dir}}/public"
app_env_vars:
- {name: SECRET_KEY_BASE, value: "{{app_secret}}" }
- {name: DATABASE_URL, value: "postgres://{{app_db_user}}:
{{app_db_password}}@{{app_db_host}}/{{app_db_name}}"}
- {name: RAILS_ENV, value: "{{app_env}}" }
- {name: DOMAIN_NAME, value: "{{app_domain}}" }
app_db_host: localhost
app_db_user: app_user
app_db_password: app_password
app_db_name: app_database
app_directories:
- "{{app_base_dir}}"
以及应用程序配置脚本本身,它分为几个阶段:操作系统软件包依赖项、Gem依赖项(对于devise,它是sqlite3)、检出源代码、修补Gem文件,以便ruby版本与主机上安装的版本匹配 + 引入用于uglifyjs的生产gem(这是当前应用程序的特定内容)、bundle install、修补数据库配置、资产编译、数据库迁移、生成nginx站点配置、重启Web服务器。
---
- name: APP STUB | Dependencies
apt: pkg={{ item }} state=present force="yes" update_cache="yes"
when: ansible_system == "Linux"
with_items: "{{app_dependencies}}"
become: yes
tags: app_stub
- name: APP STUB | Install gem dependencies
shell: "gem install --no-rdoc --no-ri {{item}}"
with_items:
- sqlite3
become: yes
tags: app_stub
- name: APP STUB | Re-create base app directory
file: path={{app_base_dir}} state=absent
become: yes
tags: app_stub
- name: APP STUB | Create directories
file: path={{item}} state=directory mode=0755
owner={{ansible_user_id}} group={{ansible_user_id}}
with_items: "{{app_directories}}"
become: yes
tags: app_stub
- name: APP STUB | Checkout app without key
git: repo="{{app_repository}}" dest="{{app_base_dir}}"
accept_hostkey="yes" force="yes"
when: app_repository_keyname is not defined
tags: app_stub
- name: APP STUB | Install global rails gem
shell: gem install --no-rdoc --no-ri rails
become: yes
tags: app_stub
- name: APP STUB | Eliminate ruby req
lineinfile: dest="{{app_base_dir}}/Gemfile" regexp="^(\s*)*ruby"
line="ruby '{{ruby_version}}'"
tags: app_stub
- name: APP STUB | gem therubyracer - uglifyjs
lineinfile: dest="{{app_base_dir}}/Gemfile" regexp="^(\s*)*gem 'therubyracer'"
line="gem 'therubyracer', :platforms => :ruby" insertafter="^group :production do"
tags: app_stub
- name: APP STUB | gem execjs - uglifyjs
lineinfile: dest="{{app_base_dir}}/Gemfile" regexp="^(\s*)*gem 'execjs'"
line="gem 'execjs'" insertafter="^group :production do"
tags: app_stub
- name: APP STUB | gem pg
lineinfile: dest="{{app_base_dir}}/Gemfile" regexp="^(\s*)*gem 'pg'"
line="gem 'pg'" insertafter="^group :production do"
tags: app_stub
- name: APP STUB | Run bundle install --path .bundle/gems --binstubs .bundle/bin
shell: bundle install --path .bundle/gems --binstubs .bundle/bin
args:
chdir: "{{app_base_dir}}"
tags: app_stub
- name: APP STUB | database.yml
template: src="{{root_dir}}/templates/app/database.yml.j2"
dest="{{app_base_dir}}/config/database.yml"
become: yes
tags: app_stub
- name: APP STUB | Precompile assets
shell: bundle exec rake assets:precompile
args:
chdir: "{{app_base_dir}}"
environment:
RAILS_ENV: "{{app_env}}"
DATABASE_URL: "postgres://{{app_db_user}}:{{app_db_password}}@{{app_db_host}}/{{app_db_name}}"
SECRET_KEY_BASE: "{{app_secret}}"
DOMAIN_NAME: "{{app_domain}}"
tags: app_stub
- name: APP STUB | DB Migrate
shell: bundle exec rake db:migrate
args:
chdir: "{{app_base_dir}}"
environment:
RAILS_ENV: "{{app_env}}"
DATABASE_URL: "postgres://{{app_db_user}}:{{app_db_password}}@{{app_db_host}}/{{app_db_name}}"
SECRET_KEY_BASE: "{{app_secret}}"
DOMAIN_NAME: "{{app_domain}}"
tags: app_stub
- name: APP STUB | Nginx conf
template: src="{{root_dir}}/templates/nginx_app.conf.j2" dest="/etc/nginx/sites-enabled/{{app_short_name}}.conf"
become: yes
tags: app_stub
- name: Nginx | Reload
service: name=nginx state=reloaded
become: yes
tags: app_stub
特别是,我们使用实际连接详细信息修补数据库config/database.yml
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
production:
url: <%= ENV['DATABASE_URL'] %>
并且我们修补 Nginx 应用程序站点配置,以便使用passenger_env_var
指令向ruby应用程序提供应用程序环境变量。
server {
listen 80 default_server;
passenger_enabled on;
{% for envvar in app_env_vars %}
passenger_env_var {{ envvar.name }} "{{ envvar.value }}";
{% endfor %}
passenger_app_env {{app_env}};
root {{app_www_root}};
}
运行代码
让我们执行配置并进行测试,为了演示目的 - 我们将使用本地 postgres 作为数据库。
---
- hosts: www
vars:
- root_dir: ..
roles:
- {
role: "sa-postgres",
option_create_app_user: true
}
- {
role: "sa-ruby",
ruby_install_setsystem: true,
ruby_version: 2.3.0,
option_install_sampleapp: true,
option_install_nginx_passenger: true
}
一旦应用程序配置过程完成
TASK: [sa-ruby | Nginx | Reload] **********************************************
changed: [192.168.0.17] => {"changed": true, "name": "nginx", "state": "started"}
PLAY RECAP ********************************************************************
192.168.0.17 : ok=55 changed=46 unreachable=0 failed=0
Play run took 23 minutes
因此,根据网络速度,您已安装了您的应用程序。
让我们通过IP地址进行检查
关注点
现在您已经了解了部署Ruby应用程序的另一种方法。