подключаемся к серверу и ставим Ansible
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible
возвращаемся на локальную машину, и создаем директорию ansible, все дальнейшие действия должны выполняться в этой директории
cоздаем host файл, в котором прописываем, группу и ip сервера
[web]
46.101.243.109
создаем файл конфигурации rails.yml, в этом файле будут описаны переменные и задачи которые Ansible должен выполнить на сервере.
---
# Указываем группу прописанную в hosts, дальнейшие действия будут выполнены для
# всех ip входящих в эту группу
- hosts: web
# Указываем от имени какого пользователя подключаться к серверу для выполнения задач
remote_user: root
# Блок переменных используемых в задачах и шаблонах
vars:
project_name: rails_app
project_path: /srv/{{ project_name }}
# Блок задач. Задачи входящие в этот блок будут выполнены по порядку
tasks:
Ниже описаны переменные и задачи для каждого действия по настройки сервера. В итоге должен получится один файл с блоками vars и tasks в которых содержаться все нужные переменные и задачи
vars:
user_name: develop
user_group: admin
tasks:
- name: Create 'dev' user with {{ user_group }} group
user:
name: '{{ user_name }}'
comment: Developer
shell: /bin/bash
group: '{{ user_group }}'
- name: Set authorized key took from file for '{{ user_name }}' user
authorized_key:
user: '{{ user_name }}'
state: present
key: "{{ lookup('file', './id_rsa.pub') }}"
- name: Allow 'admin' group to have passwordless sudo
lineinfile:
dest: /etc/sudoers
state: present
regexp: '^%admin'
line: '%admin ALL=(ALL) NOPASSWD: ALL'
validate: 'visudo -cf %s'
создаем файл id_rsa.pub в котором содержиться публичный ключ.
tasks:
- name: Install system libs
apt: pkg={{ item }} state=installed update-cache=yes
with_items:
- build-essential
- git-core
- libssl-dev
- libreadline-dev
- zlib1g-dev
- nodejs
vars:
ruby_version: 2.4.4
tasks:
- block:
- name: Install rbenv
git:
repo: https://github.com/rbenv/rbenv.git
dest: ~/.rbenv
- name: Set rbenv init and path
lineinfile:
dest: ~/.bashrc
state: present
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
with_items:
- { regexp: '^PATH.+rbenv', line: 'PATH="$HOME/.rbenv/bin:$PATH"' }
- { regexp: '^eval.+rbenv', line: 'eval "$(rbenv init -)"' }
- name: Install rbenv vars plugin
git:
repo: https://github.com/rbenv/rbenv-vars.git
dest: ~/.rbenv/plugins/rbenv-vars
- name: Install ruby-build
git:
repo: https://github.com/rbenv/ruby-build.git
dest: ~/.rbenv/plugins/ruby-build
- name: Check ruby {{ ruby_version }} installed
shell: ~/.rbenv/bin/rbenv versions --bare | grep {{ ruby_version }}
register: ruby_installed
ignore_errors: yes
- name: Install ruby {{ ruby_version }}
command: ~/.rbenv/bin/rbenv install {{ ruby_version }} -v
when: ruby_installed is failed
- name: Set global ruby is {{ ruby_version }}
shell: ~/.rbenv/bin/rbenv global {{ ruby_version }}
- name: Create gemrc file
file:
path: ~/.gemrc
state: touch
- name: Config gem
lineinfile:
dest: ~/.gemrc
state: present
regexp: '^gem.*no-document'
line: 'gem: --no-document'
- name: Check bundler installed
shell: ~/.rbenv/bin/rbenv exec gem list | grep bundler | cat
register: bundler_installed
- name: Install bundler
command: ~/.rbenv/bin/rbenv exec gem install bundler
when: bundler_installed.stdout == ''
become: yes
become_user: '{{ user_name }}'
vars:
postgres_version: 9.6
tasks:
- name: Add postgres repository
apt_repository:
repo: deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main
state: present
- name: Import postgres repository signing key
apt_key:
url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
state: present
- name: Install postgresql libs
apt: pkg={{ item }} state=installed update-cache=yes
with_items:
- postgresql-{{ postgres_version }}
- libpq-dev
- python-psycopg2
- name: Add postgres user
become: true
become_user: postgres
postgresql_user:
name: '{{ project_name }}'
password: "secret"
role_attr_flags: CREATEDB,NOSUPERUSER
- name: Create {{ project_name }}_production database
become: true
become_user: postgres
postgresql_db:
name: '{{ project_name }}_production'
- name: Restart postgres after configure
service:
name: postgresql
state: restarted
tasks:
- name: Install nginx
apt: pkg=nginx state=installed update_cache=true
- name: Set nginx config
template:
src: ./templates/nginx.j2
dest: /etc/nginx/sites-enabled/{{ project_name }}
- name: Restart nginx after configure
service:
name: nginx
state: restarted
создаем шаблон templates/nginx.j2, для конфига nginx
upstream app {
# Path to Puma SOCK file, as defined previously
server unix://srv/{{ project_name }}/shared/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
server_name servername;
root {{ project_path }}/current/public;
try_files $uri/index.html $uri @app;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
vars:
secret_key_base: "{{ lookup('password', '/dev/null length=128 chars=ascii_letters,digits') }}"
core_count: 1
tasks:
- name: Create project directories
file:
path: '{{ item }}'
state: directory
owner: '{{ user_name }}'
group: '{{ user_group }}'
mode: g+rx,u+rwx
with_items:
- '{{ project_path }}'
- '{{ project_path }}/releases'
- '{{ project_path }}/shared/log'
- '{{ project_path }}/shared/config'
- '{{ project_path }}/shared/pids'
- '{{ project_path }}/shared/sockets'
- '{{ project_path }}/shared/tmp/cache'
- '{{ project_path }}/shared/vendor/bundle'
- '{{ project_path }}/shared/public/assets'
- block:
- name: Create database.yml
template:
src: ./templates/database.yml.j2
dest: '{{ project_path }}/shared/config/database.yml'
- name: Check secrets.yml present
stat:
path: '{{ project_path }}/shared/config/secrets.yml'
register: secrets_yml
- name: Create secrets.yml
template:
src: ./templates/secrets.yml.j2
dest: '{{ project_path }}/shared/config/secrets.yml'
when: secrets_yml.stat.exists != true
- name: Create puma config
template:
src: ./templates/puma.rb.j2
dest: '{{ project_path }}/shared/config/puma.rb'
- name: Add repository host to known hosts
shell: "ssh-keyscan -H {{ repository_host }} >> ~/.ssh/known_hosts"
become: yes
become_user: '{{ user_name }}'
создаем шаблон templates/database.yml.j2
production:
adapter: postgresql
database: {{ project_name }}_production
pool: 25
timeout: 5000
host: localhost
username: {{ project_name }}
password: secret
создаем шаблон templates/secrets.yml.j2
production:
secret_key_base: {{ secret_key_base }}
создаем шаблон templates/puma.rb.j2
# Change to match your CPU core count
workers {{ core_count }}
# Min and Max threads per worker
threads 1, 5
app_dir = "{{ project_path }}"
shared_dir = "#{app_dir}/shared"
# Default to production
rails_env = ENV['RAILS_ENV'] || "production"
environment rails_env
# Set up socket location
bind "unix://#{shared_dir}/sockets/puma.sock"
# Logging
stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true
# Set master PID and state locations
pidfile "#{shared_dir}/pids/puma.pid"
state_path "#{shared_dir}/pids/puma.state"
activate_control_app
on_worker_boot do
require "active_record"
ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/current/config/database.yml")[rails_env])
end
tasks:
- name: Create puma service
template:
src: ./templates/puma.service.j2
dest: /etc/systemd/system/{{ project_name }}_puma.service
mode: 0644
notify:
- reload systemctl
создаем шаблон templates/puma.service.j2
[Unit]
Description=Puma web server
After=network.target
[Service]
Type=simple
User={{ user_name }}
Environment=RAILS_ENV=production
WorkingDirectory={{ project_path }}/current
ExecStart=/home/{{ user_name }}/.rbenv/bin/rbenv exec bundle exec puma -C {{ project_path }}/shared/config/puma.rb
ExecStop=/home/{{ user_name }}/.rbenv/bin/rbenv exec bundle exec pumactl -S {{ project_path }}/shared/tmp/pids/puma.state stop
TimeoutSec=15
Restart=always
[Install]
WantedBy=multi-user.target
отдельным блоков в rails.yml добавляем:
handlers:
- name: reload systemctl
command: systemctl daemon-reload
этот блок должен быть на одном уровне с vars, tasks