diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..913f6b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +app.env +db.env +dhparam.pem +group_vars/aws.yml +roles/monit/vars/mail.yml + +site.retry diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..d5dcc1b --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +inventory = hosts +hosts = production +remote_user = root diff --git a/group_vars/all b/group_vars/all new file mode 100644 index 0000000..4d73c36 --- /dev/null +++ b/group_vars/all @@ -0,0 +1 @@ +app_home: /opt/app/skyderby diff --git a/group_vars/aws.yml.example b/group_vars/aws.yml.example new file mode 100644 index 0000000..9c53152 --- /dev/null +++ b/group_vars/aws.yml.example @@ -0,0 +1,4 @@ +--- +aws_region: us-west-1 +aws_access_key_id: ... +aws_secret_access_key: ... diff --git a/hosts b/hosts new file mode 100644 index 0000000..4ac2ac4 --- /dev/null +++ b/hosts @@ -0,0 +1,2 @@ +[production] +skyderby.ru diff --git a/roles/app_backup/files/app_files_sync.sh b/roles/app_backup/files/app_files_sync.sh new file mode 100644 index 0000000..a64d416 --- /dev/null +++ b/roles/app_backup/files/app_files_sync.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +app_files=$(docker volume inspect app_system --format '{{ .Mountpoint }}') + +/urs/local/bin/aws s3 sync $app_files s3://app-files.skyderby.ru diff --git a/roles/app_backup/files/pg_base.sh b/roles/app_backup/files/pg_base.sh new file mode 100644 index 0000000..f4e8068 --- /dev/null +++ b/roles/app_backup/files/pg_base.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +wal_files=$(docker volume inspect pg_wal_archive --format '{{ .Mountpoint }}') + +cd /opt/app/skyderby + +# Perform backup and upload to s3 +docker-compose exec -T db /bin/bash -c "pg_basebackup -F tar -D - -X f --gzip -U rep" | \ +/usr/local/bin/aws s3 cp - s3://pg-backup.skyderby.ru/pg_backup.tar.gz + +# Cleanup old WAL files +find $wal_files -mtime +2 -delete diff --git a/roles/app_backup/files/wal_sync_to_aws.sh b/roles/app_backup/files/wal_sync_to_aws.sh new file mode 100644 index 0000000..0b55b06 --- /dev/null +++ b/roles/app_backup/files/wal_sync_to_aws.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +wal_files=$(docker volume inspect pg_wal_archive --format '{{ .Mountpoint }}') + +/usr/local/bin/aws s3 sync --delete $wal_files s3://wal-files.skyderby.ru/ diff --git a/roles/app_backup/tasks/main.yml b/roles/app_backup/tasks/main.yml new file mode 100644 index 0000000..9dc7ab9 --- /dev/null +++ b/roles/app_backup/tasks/main.yml @@ -0,0 +1,34 @@ +--- +- name: Copy cron scripts + copy: + src: "{{ item }}" + dest: /root/cron_scripts/ + mode: 500 + with_fileglob: + - ../files/* + tags: cron + +- name: Creates cron logs directory + file: path=/root/cron_logs state=directory + +- name: Cron job | Sync app files + cron: + name: Sync app files + minute: 0 + job: /root/cron_scripts/app_files_sync.sh + tags: cron + +- name: Cron job | Sync WAL logs + cron: + name: Sync WAL logs + minute: '*/10' + job: /root/cron_scripts/wal_sync_to_aws.sh + tags: cron + +- name: Cron job | PG Base Backup + cron: + name: Backup and upload pg base + minute: 0 + hour: 5 + job: /root/cron_scripts/pg_base.sh + tags: cron diff --git a/roles/application/files/app.env.example b/roles/application/files/app.env.example new file mode 100644 index 0000000..48f258e --- /dev/null +++ b/roles/application/files/app.env.example @@ -0,0 +1,26 @@ +RAILS_ENV=production + +DB_HOST=db +DB_NAME=your_db_name +DB_USERNAME=your_db_username +DB_PASSWORD=your_db_password + +MAILER_ASSET_HOST=https://skyderby.ru +MAILER_URL_HOST=skyderby.ru +SMTP_ADDRESS=smtp.example.com +SMTP_PORT=25 +SMTP_DOMAIN=skyderby.ru +SMTP_USER_NAME=example@skyderby.ru +SMTP_PASSWORD=secret + +WEB_CONCURRENCY=0 +RAILS_MAX_THREADS=5 +PORT=8000 + +REDIS_HOST='redis' +REDIS_PORT=6379 +REDIS_DB=12 + +MAPS_API_KEY=google_maps_api_key + +HONEYBADGER_API_KEY=honey_badger_api_key diff --git a/roles/application/files/db.env.example b/roles/application/files/db.env.example new file mode 100644 index 0000000..24b3ae0 --- /dev/null +++ b/roles/application/files/db.env.example @@ -0,0 +1,3 @@ +POSTGRES_DB=your_db_name +POSTGRES_USER=your_db_username +POSTGRES_PASSWORD=your_db_password diff --git a/roles/application/files/docker-compose.yml b/roles/application/files/docker-compose.yml new file mode 100644 index 0000000..7696397 --- /dev/null +++ b/roles/application/files/docker-compose.yml @@ -0,0 +1,68 @@ +version: '2' + +services: + db: + image: postgres:9.6.4 + env_file: db.env + ports: + - "5432:5432" + volumes: + - pg_data_96:/var/lib/postgresql/data + - pg_wal_archive:/wal_archive + - ./postgres/pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf + - ./postgres/postgresql.conf:/var/lib/postgresql/data/postgresql.conf + restart: always + + app: + image: skyderby/app:latest + env_file: app.env + volumes: + - /opt/app/public + - app_system:/opt/app/public/system + depends_on: + - db + - redis + ports: + - "8000:8000" + restart: always + + workers: + image: skyderby/app:latest + env_file: app.env + volumes: + - ./database.yml:/opt/app/config/database.yml:ro + - app_system:/opt/app/public/system + depends_on: + - db + - redis + command: "bundle exec sidekiq -q default -q mailers -c 5" + restart: always + + redis: + image: redis:3.0.5 + volumes: + - redis_data:/data + restart: always + + web: + image: nginx:stable + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/dhparam.pem:/etc/pki/nginx/dhparam.pem:ro + - /etc/letsencrypt:/etc/letsencrypt:ro + volumes_from: + - app:ro + ports: + - "80:80" + - "443:443" + restart: always + +volumes: + redis_data: + external: true + pg_data_96: + external: false + pg_wal_archive: + external: true + app_system: + external: true diff --git a/roles/application/files/nginx/nginx.conf b/roles/application/files/nginx/nginx.conf new file mode 100644 index 0000000..373a550 --- /dev/null +++ b/roles/application/files/nginx/nginx.conf @@ -0,0 +1,179 @@ +# This is example contains the bare mininum to get nginx going with +# unicorn servers. Generally these configuration settings +# are applicable to other HTTP application servers (and not just Ruby +# ones), so if you have one working well for proxying another app +# server, feel free to continue using it. +# +# The only setting we feel strongly about is the fail_timeout=0 +# directive in the "upstream" block. max_fails=0 also has the same +# effect as fail_timeout=0 for current versions of nginx and may be +# used in its place. +# +# Users are strongly encouraged to refer to nginx documentation for more +# details and search for other example configs. + +# you generally only need one nginx worker unless you're serving +# large amounts of static files which require blocking disk reads +worker_processes 1; + +# # drop privileges, root is needed on most systems for binding to port 80 +# # (or anything < 1024). Capability-based security may be available for +# # your system and worth checking out so you won't need to be root to +# # start nginx to bind on 80 +user nobody nogroup; # for systems with a "nogroup" +# user nobody nobody; # for systems with "nobody" as a group instead + +# Feel free to change all paths to suite your needs here, of course +pid /tmp/nginx.pid; +error_log /var/log/nginx/error.log; + +events { + worker_connections 1024; # increase if you have lots of clients + accept_mutex off; # "on" if nginx worker_processes > 1 + # use epoll; # enable for Linux 2.6+ + # use kqueue; # enable for FreeBSD, OSX +} + +http { + # nginx will find this file in the config directory set at nginx build time + include mime.types; + + # fallback in case we can't determine a type + default_type application/octet-stream; + + # click tracking! + access_log /var/log/nginx/access.log combined; + + # you generally want to serve static files with nginx since + # unicorn is not and will never be optimized for it + sendfile on; + + tcp_nopush on; # off may be better for *some* Comet/long-poll stuff + tcp_nodelay off; # on may be better for some Comet/long-poll stuff + + # we haven't checked to see if Rack::Deflate on the app server is + # faster or not than doing compression via nginx. It's easier + # to configure it all in one place here for static files and also + # to disable gzip for clients who don't get gzip/deflate right. + # There are other gzip settings that may be needed used to deal with + # bad clients out there, see http://wiki.nginx.org/NginxHttpGzipModule + gzip on; + gzip_http_version 1.0; + gzip_proxied any; + gzip_min_length 500; + gzip_disable "MSIE [1-6]\."; + gzip_types text/plain text/xml text/css + text/comma-separated-values + text/javascript application/x-javascript + application/atom+xml; + + # this can be any application server, not just unicorn + upstream app_server { + # fail_timeout=0 means we always retry an upstream even if it failed + # to return a good HTTP response (in case the unicorn master nukes a + # single worker for timing out). + + server app:8000 fail_timeout=0; + } + + server { + return 204; + } + + server { + server_name skyderby.ru; + listen 80; + return 301 https://skyderby.ru$request_uri; + } + + server { + listen 443 ssl default deferred http2; # for Linux + server_name skyderby.ru; + ssl_stapling on; + ssl on; + ssl_certificate /etc/letsencrypt/live/skyderby.ru/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/skyderby.ru/privkey.pem; + ssl_dhparam /etc/pki/nginx/dhparam.pem; + ssl_session_timeout 24h; + ssl_session_cache shared:SSL:2m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2; + ssl_prefer_server_ciphers on; + add_header Content-Security-Policy-Report-Only "default-src https:; script-src https: 'unsafe-eval' 'unsafe-inline'; style-src https: 'unsafe-inline'; img-src https: data:; font-src https: data:; report-uri /csp-report"; + + if ($http_user_agent ~* (AhrefsBot|SemrushBot|BaiduSpider|Jorgee|MJ12bot)) { + return 444; + } + + # If you have IPv6, you'll likely want to have two separate listeners. + # One on IPv4 only (the default), and another on IPv6 only instead + # of a single dual-stack listener. A dual-stack listener will make + # for ugly IPv4 addresses in $remote_addr (e.g ":ffff:10.0.0.1" + # instead of just "10.0.0.1") and potentially trigger bugs in + # some software. + # listen [::]:80 ipv6only=on; # deferred or accept_filter recommended + + client_max_body_size 10m; + + # ~2 seconds is often enough for most folks to parse HTML/CSS and + # retrieve needed images/icons/frames, connections are cheap in + # nginx so increasing this is generally safe... + keepalive_timeout 5; + + # path for static files + root /opt/app/public; + + # Prefer to serve static files directly from nginx to avoid unnecessary + # data copies from the application server. + # + # try_files directive appeared in in nginx 0.7.27 and has stabilized + # over time. Older versions of nginx (e.g. 0.6.x) requires + # "if (!-f $request_filename)" which was less efficient: + # http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127 + try_files $uri/index.html $uri.html $uri @app; + + location ^~ /assets/ { + gzip_static on; + expires max; + add_header Cache-Control public; + } + + location @app { + # an HTTP header important enough to have its own Wikipedia entry: + # http://en.wikipedia.org/wiki/X-Forwarded-For + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # enable this if you forward HTTPS traffic to unicorn, + # this helps Rack set the proper URL scheme for doing redirects: + # proxy_set_header X-Forwarded-Proto $scheme; + + # pass the Host: header from the client right along so redirects + # can be set properly within the Rack application + proxy_set_header Host $http_host; + + # we don't want nginx trying to do something clever with + # redirects, we set the Host: header above already. + proxy_redirect off; + + # It's also safe to set if you're using only serving fast clients + # with unicorn + nginx, but not slow clients. You normally want + # nginx to buffer responses to slow clients, even with Rails 3.1 + # streaming because otherwise a slow client can become a bottleneck + # of unicorn. + # + # The Rack application may also set "X-Accel-Buffering (yes|no)" + # in the response headers do disable/enable buffering on a + # per-response basis. + # proxy_buffering off; + + proxy_pass http://app_server; + } + + # Rails error pages + error_page 500 502 503 504 /500.html; + #location = /500.html { + # root /opt/app/public; + #} + } +} diff --git a/roles/application/files/postgres/pg_hba.conf b/roles/application/files/postgres/pg_hba.conf new file mode 100644 index 0000000..32b1793 --- /dev/null +++ b/roles/application/files/postgres/pg_hba.conf @@ -0,0 +1,6 @@ +#host all all 0.0.0.0/0 md5 +local all all trust +local replication rep trust +#host skyderby skyderby 172.18.0.0/0 md5 +host skyderby skyderby .skyderby_default md5 +host replication rep 45.32.239.89/32 md5 diff --git a/roles/application/files/postgres/postgresql.conf b/roles/application/files/postgres/postgresql.conf new file mode 100644 index 0000000..84ffeb7 --- /dev/null +++ b/roles/application/files/postgres/postgresql.conf @@ -0,0 +1,635 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, or use "pg_ctl reload". Some +# parameters, which are marked below, require a server shutdown and restart to +# take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: kB = kilobytes Time units: ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +max_connections = 20 # (change requires restart) +# Note: Increasing max_connections costs ~400 bytes of shared memory per +# connection slot, plus lock space (see max_locks_per_transaction). +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - Security and Authentication - + +#authentication_timeout = 1min # 1s-600s +#ssl = off # (change requires restart) +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers + # (change requires restart) +#ssl_prefer_server_ciphers = on # (change requires restart) +#ssl_ecdh_curve = 'prime256v1' # (change requires restart) +#ssl_cert_file = 'server.crt' # (change requires restart) +#ssl_key_file = 'server.key' # (change requires restart) +#ssl_ca_file = '' # (change requires restart) +#ssl_crl_file = '' # (change requires restart) +#password_encryption = on +#db_user_namespace = off +#row_security = on + +# GSSAPI using Kerberos +#krb_server_keyfile = '' +#krb_caseins_users = off + +# - TCP Keepalives - +# see "man 7 tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +shared_buffers = 256MB # min 128kB + # (change requires restart) +huge_pages = try # on, off, or try + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Note: Increasing max_prepared_transactions costs ~600 bytes of shared memory +# per transaction slot, plus lock space (see max_locks_per_transaction). +# It is not advisable to set max_prepared_transactions nonzero unless you +# actively intend to use prepared transactions. +work_mem = 24MB # min 64kB +maintenance_work_mem = 128MB # min 1MB +#autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem +#max_stack_depth = 2MB # min 100kB +dynamic_shared_memory_type = posix # the default is the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # use none to disable dynamic shared memory + +# - Disk - + +#temp_file_limit = -1 # limits per-session temp file space + # in kB, or -1 for no limit + +# - Kernel Resource Usage - + +#max_files_per_process = 1000 # min 25 + # (change requires restart) +shared_preload_libraries = 'pg_stat_statements' + +# pg_stat_statements +pg_stat_statements.max = 1000 +pg_stat_statements.track = top + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 10 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # 0-1000 max buffers written/round +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multipler on buffers scanned/round + +# - Asynchronous Behavior - + +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#max_worker_processes = 8 + + +#------------------------------------------------------------------------------ +# WRITE AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +wal_level = archive # minimal, archive, hot_standby, or logical + # (change requires restart) +fsync = on # turns forced synchronization on or off +synchronous_commit = off # synchronization level; + # off, local, remote_write, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_compression = off # enable compression of full-page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +wal_buffers = 16MB # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1h +max_wal_size = 2GB +min_wal_size = 1GB +checkpoint_completion_target = 0.7 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_warning = 30s # 0 disables + +# - Archiving - + +archive_mode = on # enables archiving; off, on, or always + # (change requires restart) +archive_command = 'test ! -f /wal_archive/%f && cp %p /wal_archive/%f' + # command to use to archive a logfile segment + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +archive_timeout = 900 # force a logfile segment switch after this + # number of seconds; 0 disables + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Server(s) - + +# Set these on the master and on any standby that will send replication data. + +max_wal_senders = 2 # max number of walsender processes + # (change requires restart) +wal_keep_segments = 64 # in logfile segments, 16MB each; 0 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables + +#max_replication_slots = 0 # max number of replication slots + # (change requires restart) +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Master Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # comma-separated list of application_name + # from standby(s); '*' = all +#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed + +# - Standby Servers - + +# These settings are ignored on a master server. + +#hot_standby = off # "on" allows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from master + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_bitmapscan = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +random_page_cost = 1.1 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +effective_cache_size = 1GB + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses + + +#------------------------------------------------------------------------------ +# ERROR REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, syslog, and eventlog, + # depending on platform. csvlog + # requires logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr and csvlog + # into log files. Required to be on for + # csvlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'pg_log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' + +# This is only relevant when logging to eventlog (win32): +#event_source = 'PostgreSQL' + +# - When to Log - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +log_min_duration_statement = 3000 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_checkpoints = off +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %p = process ID + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +log_timezone = 'UTC' + + +# - Process Title - + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# RUNTIME STATISTICS +#------------------------------------------------------------------------------ + +# - Query/Index Statistics Collector - + +#track_activities = on +track_counts = on +#track_io_timing = off +#track_functions = none # none, pl, all +#track_activity_query_size = 1024 # (change requires restart) +#stats_temp_directory = 'pg_stat_tmp' + + +# - Statistics Monitoring - + +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off +#log_statement_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM PARAMETERS +#------------------------------------------------------------------------------ + +autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +log_autovacuum_min_duration = 1000 # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +autovacuum_naptime = 1min # time between autovacuum runs +autovacuum_vacuum_threshold = 300 # min number of row updates before + # vacuum +autovacuum_analyze_threshold = 200 # min number of row updates before + # analyze +autovacuum_vacuum_scale_factor = 0.002 # fraction of table size before vacuum +autovacuum_analyze_scale_factor = 0.0005 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#search_path = '"$user", public' # schema names +#default_tablespace = '' # a tablespace name, '' uses the default +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_min_age = 50000000 +#vacuum_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_freeze_table_age = 150000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_fuzzy_search_limit = 0 +#gin_pending_list_limit = 4MB + +# - Locale and Formatting - + +datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +timezone = 'UTC' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 0 # min -15, max 3 +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +lc_messages = 'en_US.utf8' # locale for system error message + # strings +lc_monetary = 'en_US.utf8' # locale for monetary formatting +lc_numeric = 'en_US.utf8' # locale for number formatting +lc_time = 'en_US.utf8' # locale for time formatting + +# default configuration for text search +default_text_search_config = 'pg_catalog.english' + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#local_preload_libraries = '' +#session_preload_libraries = '' + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +# Note: Each lock table slot uses ~270 bytes of shared memory, and there are +# max_locks_per_transaction * (max_connections + max_prepared_transactions) +# lock table slots. +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) + + +#------------------------------------------------------------------------------ +# VERSION/PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#default_with_oids = off +#escape_string_warning = on +#lo_compat_privileges = off +#operator_precedence_warning = off +#quote_all_identifiers = off +#sql_inheritance = on +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. + +#include_dir = 'conf.d' # include files ending in '.conf' from + # directory 'conf.d' +#include_if_exists = 'exists.conf' # include file only if it exists +#include = 'special.conf' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/roles/application/handlers/main.yml b/roles/application/handlers/main.yml new file mode 100644 index 0000000..034fe79 --- /dev/null +++ b/roles/application/handlers/main.yml @@ -0,0 +1,15 @@ +--- +- name: Restart NGINX + shell: docker-compose restart web + args: + chdir: "{{ app_home }}" + +- name: Restart Postgres + shell: docker-compose restart db + args: + chdir: "{{ app_home }}" + +- name: Soft recreate containers + shell: docker-compose up -d + args: + chdir: "{{ app_home }}" diff --git a/roles/application/tasks/compose_configuration.yml b/roles/application/tasks/compose_configuration.yml new file mode 100644 index 0000000..d606f43 --- /dev/null +++ b/roles/application/tasks/compose_configuration.yml @@ -0,0 +1,10 @@ +--- +- name: Docker compose file + copy: + src: "{{ item }}" + dest: "{{ app_home }}" + with_items: + - app.env + - db.env + - docker-compose.yml + notify: Soft recreate containers diff --git a/roles/application/tasks/docker_volumes.yml b/roles/application/tasks/docker_volumes.yml new file mode 100644 index 0000000..556d722 --- /dev/null +++ b/roles/application/tasks/docker_volumes.yml @@ -0,0 +1,31 @@ +--- +- name: Docker volume | Check pg_wal_archive + shell: docker volume ls | grep pg_wal_archive + register: pg_wal_archive_check + changed_when: False + +- name: Docker volume | Create pg_wal_archive + shell: docker volume create pg_wal_archive + when: pg_wal_archive_check|failed + +- name: Docker volume | Set pg_wal_archive permissions + shell: 'chown -R 999:999 $(docker volume inspect pg_wal_archive --format "{{ .Mountpoint }}")' + when: pg_wal_archive_check|failed + +- name: Docker volume | Check app_system + shell: docker volume ls | grep app_system + register: app_system_check + changed_when: False + +- name: Docker volume | Create app_system + shell: docker volume create app_system + when: app_system_check|failed + +- name: Docker volume | Check redis_data + shell: docker volume ls | grep redis_data + register: redis_data_check + changed_when: False + +- name: Docker volume | Create redis_data + shell: docker volume create redis_data + when: redis_data_check|failed diff --git a/roles/application/tasks/main.yml b/roles/application/tasks/main.yml new file mode 100644 index 0000000..026816f --- /dev/null +++ b/roles/application/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- import_tasks: docker_volumes.yml +- import_tasks: nginx.yml +- import_tasks: postgres.yml +- import_tasks: compose_configuration.yml diff --git a/roles/application/tasks/nginx.yml b/roles/application/tasks/nginx.yml new file mode 100644 index 0000000..f7bdab2 --- /dev/null +++ b/roles/application/tasks/nginx.yml @@ -0,0 +1,16 @@ +- name: NGINX | directory + file: + path: "{{ app_home }}/nginx" + state: directory + +- name: NGINX | configuration + copy: + src: nginx/nginx.conf + dest: "{{ app_home }}/nginx/" + notify: Restart NGINX + +- name: NGINX | dhparam.pem + copy: + src: nginx/dhparam.pem + dest: "{{ app_home }}/nginx/" + notify: Restart NGINX diff --git a/roles/application/tasks/postgres.yml b/roles/application/tasks/postgres.yml new file mode 100644 index 0000000..d48c0a5 --- /dev/null +++ b/roles/application/tasks/postgres.yml @@ -0,0 +1,17 @@ +--- +- name: Postgres | directory + file: + path: "{{ app_home }}/postgres" + state: directory + +- name: Postgres | configuration + copy: + src: postgres/postgresql.conf + dest: "{{ app_home }}/postgres/" + notify: Restart Postgres + +- name: Postgres | pg_hba + copy: + src: postgres/pg_hba.conf + dest: "{{ app_home }}/postgres/" + notify: Restart Postgres diff --git a/roles/awscli/tasks/main.yml b/roles/awscli/tasks/main.yml new file mode 100644 index 0000000..e748c41 --- /dev/null +++ b/roles/awscli/tasks/main.yml @@ -0,0 +1,60 @@ +--- +- name: Include vars + include_vars: "{{ playbook_dir }}/group_vars/aws.yml" + tags: aws-cli + +- name: Install Python PIP + become: yes + apt: + pkg: python-pip + state: latest + tags: aws-cli + +- name: Install AWS CLI + become: yes + pip: + name: awscli + state: latest + tags: aws-cli + +- name: Set home directory of the user + set_fact: + home_dir: "/home/{{ aws_cli_user }}" + when: "not aws_cli_user == 'root'" + tags: aws-cli + +- name: Set home directory for root + set_fact: + home_dir: /root + when: "aws_cli_user == 'root'" + tags: aws-cli + +- name: Create the AWS config directory + become: yes + file: + path: "{{ home_dir }}/.aws" + state: directory + owner: "{{ aws_cli_user }}" + group: "{{ aws_cli_group }}" + mode: 0755 + tags: aws-cli + +- name: Copy AWS CLI config + become: yes + template: + src: aws_cli_config.j2 + dest: "{{ home_dir }}/.aws/config" + owner: "{{ aws_cli_user }}" + group: "{{ aws_cli_group }}" + mode: 0600 + tags: aws-cli + +- name: Copy AWS CLI credentials + become: yes + template: + src: aws_cli_credentials.j2 + dest: "{{ home_dir }}/.aws/credentials" + owner: "{{ aws_cli_user }}" + group: "{{ aws_cli_group }}" + mode: 0600 + tags: aws-cli diff --git a/roles/awscli/templates/aws_cli_config.j2 b/roles/awscli/templates/aws_cli_config.j2 new file mode 100644 index 0000000..b751a89 --- /dev/null +++ b/roles/awscli/templates/aws_cli_config.j2 @@ -0,0 +1,3 @@ +[default] +output = {{ output_format }} +region = {{ region }} diff --git a/roles/awscli/templates/aws_cli_credentials.j2 b/roles/awscli/templates/aws_cli_credentials.j2 new file mode 100644 index 0000000..5f7099e --- /dev/null +++ b/roles/awscli/templates/aws_cli_credentials.j2 @@ -0,0 +1,3 @@ +[default] +aws_access_key_id = {{ access_key_id }} +aws_secret_access_key = {{ secret_access_key }} diff --git a/roles/awscli/vars/main.yml b/roles/awscli/vars/main.yml new file mode 100644 index 0000000..ca87bb1 --- /dev/null +++ b/roles/awscli/vars/main.yml @@ -0,0 +1,8 @@ +--- +aws_cli_user: '{{ ansible_ssh_user }}' +aws_cli_group: '{{ ansible_ssh_user }}' + +output_format: 'json' +region: '{{ aws_region }}' +access_key_id: '{{ aws_access_key_id }}' +secret_access_key: '{{ aws_secret_access_key }}' diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml new file mode 100644 index 0000000..6ffcc04 --- /dev/null +++ b/roles/common/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- name: Update and upgrade apt packages + become: true + apt: + upgrade: yes + update_cache: yes + cache_valid_time: 86400 + +- name: Set timezone to UTC + timezone: + name: Etc/UTC + +- name: Set locale en_US.UTF-8 + locale_gen: + name: en_US.UTF-8 + state: present diff --git a/roles/docker-compose/tasks/main.yml b/roles/docker-compose/tasks/main.yml new file mode 100644 index 0000000..2a19f3a --- /dev/null +++ b/roles/docker-compose/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Check Docker compose bin file presence + stat: path=/usr/local/bin/docker-compose + register: dockercompose + +- name: Install docker compose + shell: curl -L https://github.com/docker/compose/releases/download/1.5.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose + when: dockercompose.stat.exists == false + +- name: Apply executable permission + file: path=/usr/local/bin/docker-compose mode="u+x,g+x" diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml new file mode 100644 index 0000000..b1f6d20 --- /dev/null +++ b/roles/docker/tasks/main.yml @@ -0,0 +1,61 @@ +--- +- name: Add Docker repository key + apt_key: + id: "{{ apt_key_sig }}" + keyserver: "{{ apt_key_url }}" + state: present + register: add_repository_key + ignore_errors: true + +- name: Alternative | Add Docker repository key + shell: "apt-key adv --fetch-keys {{ apt-key-url }}" + when: add_repository_key|failed + +- name: Install Docker dependencies + apt: + name: '{{ item }}' + state: present + with_items: + - apt-transport-https + - ca-certificates + - curl + - software-properties-common + +- name: Add Docker repository and update apt cache + apt_repository: + repo: "{{ apt_repository }}" + mode: '644' + update_cache: yes + state: present + +- name: Install (or update) docker package + apt: + name: 'docker-{{ docker_edition }}={{ docker_version }}~{{ docker_edition }}-0~{{ docker_distribution | lower }}' + state: present + update_cache: True + cache_valid_time: 86400 + +- name: Add specific users to 'docker' group + user: + name: '{{ item }}' + groups: 'docker' + append: True + with_items: '{{ docker_users }}' + when: item|d() + +- name: Change docker directory + lineinfile: + dest: /lib/systemd/system/docker.service + regexp: '^ExecStart' + line: 'ExecStart=/usr/bin/dockerd -g /opt/app/docker -H fd://' + register: directory_changed + +- name: Systemctl daemon-reload if directory changed + command: 'systemctl daemon-reload' + when: directory_changed|changed + +- name: Restart if directory changed + service: + name: docker.service + state: restarted + when: directory_changed|changed diff --git a/roles/docker/vars/main.yml b/roles/docker/vars/main.yml new file mode 100644 index 0000000..2fd6bad --- /dev/null +++ b/roles/docker/vars/main.yml @@ -0,0 +1,19 @@ +--- +# Place to get apt repository key +apt_key_url: "hkp://ha.pool.sks-keyservers.net" +# apt repository key signature +apt_key_sig: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 +# apt keyring file +keyring: "/etc/apt/trusted.gpg.d/docker.gpg" +# Name of the apt repository for Docker CE or EE +apt_repository: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution|lower }} {{ ansible_distribution_release|lower }} stable" + +# Version and edition +docker_edition: ce +docker_version: 17.12.0 +docker_distribution: '{{ ansible_local.core.distribution + if (ansible_local|d() and ansible_local.core|d() and + ansible_local.core.distribution|d()) + else ansible_distribution }}' + +docker_users: [root] diff --git a/roles/monit/files/system_monitoring b/roles/monit/files/system_monitoring new file mode 100644 index 0000000..61956e6 --- /dev/null +++ b/roles/monit/files/system_monitoring @@ -0,0 +1,14 @@ +check system skyderby.ru + if loadavg (5min) > 3 for 2 cycles then alert + if loadavg (15min) > 1 for 2 cycles then alert + if memory usage > 80% for 2 cycles then alert + if swap usage > 20% for 2 cycles then alert + if cpu usage (user) > 80% for 2 cycles then alert + if cpu usage (system) > 20% for 2 cycles then alert + if cpu usage (wait) > 20% for 2 cycles then alert + +check filesystem "root" with path /dev/vda1 + if space usage > 80% for 4 cycles then alert + +check filesystem "app" with path /opt/app + if space usage > 70% for 4 cycles then alert diff --git a/roles/monit/handlers/main.yml b/roles/monit/handlers/main.yml new file mode 100644 index 0000000..96e5333 --- /dev/null +++ b/roles/monit/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart monit + service: + name: monit + state: restarted diff --git a/roles/monit/tasks/main.yml b/roles/monit/tasks/main.yml new file mode 100644 index 0000000..1d19c81 --- /dev/null +++ b/roles/monit/tasks/main.yml @@ -0,0 +1,49 @@ +--- +- name: Monit | package + apt: + name: monit + update_cache: yes + cache_valid_time: 3600 + state: present + +- name: Monit | includes folder + file: + path: "{{ monit_includes_dir }}" + state: directory + mode: 0600 + +- name: Monit | lib folder + file: + path: "{{ monit_lib_folder }}" + state: directory + mode: 0600 + +- name: Monit | config + template: + src: monitrc.j2 + dest: "{{ monitrc_conf }}" + owner: root + group: root + mode: 0700 + notify: restart monit + +- name: Monit | Include mail vars + include_vars: mail.yml + +- name: Monit | Mail alerts + template: + src: mail.j2 + dest: "{{ monit_includes_dir }}/mail" + owner: root + group: root + mode: 0644 + notify: restart monit + +- name: Monit | Monitoring config + copy: + src: system_monitoring + dest: "{{ monit_includes_dir }}" + owner: root + group: root + mode: 0644 + notify: restart monit diff --git a/roles/monit/templates/mail.j2 b/roles/monit/templates/mail.j2 new file mode 100644 index 0000000..a685e08 --- /dev/null +++ b/roles/monit/templates/mail.j2 @@ -0,0 +1,10 @@ +# {{ ansible_managed }} + +set mailserver {{ monit_mailserver_host }} port {{ monit_mailserver_port }} + username "{{ monit_mailserver_user }}" password "{{ monit_mailserver_password }}" + using {{ monit_mailserver_ssl_version }} + with timeout {{ monit_mailserver_timeout | default(5) }} seconds + +set mail-format { from: {{ monit_mailserver_user }} } + +set alert {{ monit_alert_address }} but not on { instance } diff --git a/roles/monit/templates/monitrc.j2 b/roles/monit/templates/monitrc.j2 new file mode 100644 index 0000000..a31c813 --- /dev/null +++ b/roles/monit/templates/monitrc.j2 @@ -0,0 +1,6 @@ +# {{ ansible_managed }} + +set daemon {{ monit_cycle }} +set logfile {{ monit_log_destination }} + +include {{ monit_includes_dir }}/* diff --git a/roles/monit/vars/mail.yml.example b/roles/monit/vars/mail.yml.example new file mode 100644 index 0000000..a1574f3 --- /dev/null +++ b/roles/monit/vars/mail.yml.example @@ -0,0 +1,8 @@ +--- +monit_mailserver_host: smtp.example.com +monit_mailserver_port: 25 +monit_mailserver_user: info@example.com +monit_mailserver_password: secret +monit_mailserver_ssl_version: tlsv1 +monit_mailserver_timeout: 30 +monit_alert_address: destination@example.com diff --git a/roles/monit/vars/main.yml b/roles/monit/vars/main.yml new file mode 100644 index 0000000..1259acc --- /dev/null +++ b/roles/monit/vars/main.yml @@ -0,0 +1,8 @@ +--- +monitrc_conf: /etc/monit/monitrc +monit_includes_dir: /etc/monit/conf.d +monit_cycle: 120 +monit_log_destination: /var/log/monit.log +monit_lib_folder: /var/lib/monit +monit_state_file: "{{ monit_lib_folder }}/state" +monit_id_file: "{{ monit_lib_folder }}/id" diff --git a/rolling_update.yml b/rolling_update.yml new file mode 100644 index 0000000..2116677 --- /dev/null +++ b/rolling_update.yml @@ -0,0 +1,77 @@ +--- +- hosts: all + + pre_tasks: + - name: Copy maintenance setup + copy: + src: rolling_update/maintenance + dest: "{{ app_home }}" + + tasks: + - name: Pull latest app image + shell: docker pull skyderby/app:latest + + - name: Stop web-server + shell: docker-compose stop web + args: + chdir: "{{ app_home }}" + + - name: Remove maintenance web-server if present + shell: docker rm -f nginx-maintenance + ignore_errors: yes + + - name: Deploy maintenance web-server + shell: > + docker run -d + -p 80:80 -p 443:443 + --volume {{ app_home }}/maintenance/nginx.conf:/etc/nginx/nginx.conf:ro + --volume {{ app_home }}/maintenance/maintenance.html:/opt/app/maintenance.html:ro + --volume {{ app_home }}/ssl:/etc/pki/nginx:ro + --volume /etc/letsencrypt:/etc/letsencrypt:ro + --name nginx-maintenance + nginx:stable + args: + chdir: "{{ app_home }}" + + - name: Stop app and workers containers + shell: docker-compose stop app workers + args: + chdir: "{{ app_home }}" + + - name: Remove app and workers containers + shell: docker-compose rm -v -f app workers + args: + chdir: "{{ app_home }}" + + - name: Deploy new version + shell: docker-compose up --no-deps --force-recreate -d app workers + args: + chdir: "{{ app_home }}" + + - name: Wait app to start + shell: 'docker-compose exec -T app curl --fail http://127.0.0.1:8000/ping || exit 1' + register: result + until: result|success + retries: 30 + delay: 1 + changed_when: False + args: + chdir: "{{ app_home }}" + + - name: Remove maintenance web-server + shell: docker rm -f -v nginx-maintenance + + - name: Starting web-server + shell: docker-compose up -d --no-deps web + args: + chdir: "{{ app_home }}" + + - name: Clean old images + shell: docker rmi $(docker images --filter "dangling=true" -q --no-trunc) + ignore_errors: yes + + post_tasks: + - name: Cleanup maintenance setup files + file: + state: absent + path: "{{ app_home }}/maintenance" diff --git a/rolling_update/maintenance/maintenance.html b/rolling_update/maintenance/maintenance.html new file mode 100644 index 0000000..cc5b473 --- /dev/null +++ b/rolling_update/maintenance/maintenance.html @@ -0,0 +1,57 @@ + + + + Maintenance. We'll be back soon (503) + + + + +
+ + Skyderby logo + Created with Sketch. + + + + + +
+ +
+

#503 We're on maintenance and will be back soon.

+
+ + diff --git a/rolling_update/maintenance/nginx.conf b/rolling_update/maintenance/nginx.conf new file mode 100644 index 0000000..1b72809 --- /dev/null +++ b/rolling_update/maintenance/nginx.conf @@ -0,0 +1,40 @@ +worker_processes 1; +user nobody nogroup; + +events { + worker_connections 1024; + accept_mutex off; +} + +http { + server { + server_name skyderby.ru; + listen 80; + + return 301 https://$host$request_uri; + } + + server { + server_name skyderby.ru; + listen 443; + + ssl_stapling on; + ssl on; + ssl_certificate /etc/letsencrypt/live/skyderby.ru/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/skyderby.ru/privkey.pem; + ssl_dhparam /etc/pki/nginx/dhparam.pem; + ssl_session_timeout 24h; + ssl_session_cache shared:SSL:2m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2; + ssl_prefer_server_ciphers on; + add_header Strict-Transport-Security "max-age=31536000;"; + add_header Content-Security-Policy-Report-Only "default-src https:; script-src https: 'unsafe-eval' 'unsafe-inline'; style-src https: 'unsafe-inline'; img-src https: data:; font-src https: data:; report-uri /csp-report"; + + location / { + root /opt/app; + expires off; + try_files '' /maintenance.html =503; + } + } +} diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..4767f9d --- /dev/null +++ b/site.yml @@ -0,0 +1,10 @@ +--- +- hosts: production + roles: + - { role: common, tags: [ 'common'] } + - docker + - docker-compose + - awscli + - { role: monit, tags: [ 'monit' ] } + - app_backup + - { role: application, tags: [ 'application' ] }