Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker 3 #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
docker-compose.yml
build_info.txt
prometheus/

## python specific
*.pyc
1 change: 1 addition & 0 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Micorservices
14 changes: 14 additions & 0 deletions src/comment/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM ruby:2.2
RUN apt-get update -qq && apt-get install -y build-essential

ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install
COPY . $APP_HOME

ENV COMMENT_DATABASE_HOST comment_db
ENV COMMENT_DATABASE comments
CMD ["puma"]

10 changes: 10 additions & 0 deletions src/comment/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
source 'https://rubygems.org'

gem 'sinatra', '~> 2.0.2'
gem 'bson_ext'
gem 'mongo'
gem 'puma'
gem 'prometheus-client'
gem "rack", '>= 2.0.6'
gem 'rufus-scheduler'
gem 'tzinfo-data'
50 changes: 50 additions & 0 deletions src/comment/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
GEM
remote: https://rubygems.org/
specs:
bson (4.3.0)
bson_ext (1.5.1)
et-orbi (1.1.6)
tzinfo
fugit (1.1.6)
et-orbi (~> 1.1, >= 1.1.6)
raabro (~> 1.1)
mongo (2.6.2)
bson (>= 4.3.0, < 5.0.0)
mustermann (1.0.3)
prometheus-client (0.8.0)
quantile (~> 0.2.1)
puma (3.12.0)
quantile (0.2.1)
raabro (1.1.6)
rack (2.0.6)
rack-protection (2.0.4)
rack
rufus-scheduler (3.5.2)
fugit (~> 1.1, >= 1.1.5)
sinatra (2.0.4)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.4)
tilt (~> 2.0)
thread_safe (0.3.6)
tilt (2.0.9)
tzinfo (1.2.5)
thread_safe (~> 0.1)
tzinfo-data (1.2018.7)
tzinfo (>= 1.0.0)

PLATFORMS
ruby

DEPENDENCIES
bson_ext
mongo
prometheus-client
puma
rack (>= 2.0.6)
rufus-scheduler
sinatra (~> 2.0.2)
tzinfo-data

BUNDLED WITH
1.17.2
1 change: 1 addition & 0 deletions src/comment/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.3
132 changes: 132 additions & 0 deletions src/comment/comment_app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
require 'sinatra'
require 'json/ext'
require 'uri'
require 'mongo'
require 'prometheus/client'
require 'rufus-scheduler'
require_relative 'helpers'

# Database connection info
COMMENT_DATABASE_HOST ||= ENV['COMMENT_DATABASE_HOST'] || '127.0.0.1'
COMMENT_DATABASE_PORT ||= ENV['COMMENT_DATABASE_PORT'] || '27017'
COMMENT_DATABASE ||= ENV['COMMENT_DATABASE'] || 'test'
DB_URL ||= "mongodb://#{COMMENT_DATABASE_HOST}:#{COMMENT_DATABASE_PORT}"

# App version and build info
if File.exist?('VERSION')
VERSION ||= File.read('VERSION').strip
else
VERSION ||= "version_missing"
end

if File.exist?('build_info.txt')
BUILD_INFO = File.readlines('build_info.txt')
else
BUILD_INFO = Array.new(2, "build_info_missing")
end

configure do
Mongo::Logger.logger.level = Logger::WARN
db = Mongo::Client.new(DB_URL, database: COMMENT_DATABASE,
heartbeat_frequency: 2)
set :mongo_db, db[:comments]
set :bind, '0.0.0.0'
set :server, :puma
set :logging, false
set :mylogger, Logger.new(STDOUT)
end

# Create and register metrics
prometheus = Prometheus::Client.registry
comment_health_gauge = Prometheus::Client::Gauge.new(
:comment_health,
'Health status of Comment service'
)
comment_health_db_gauge = Prometheus::Client::Gauge.new(
:comment_health_mongo_availability,
'Check if MongoDB is available to Comment'
)
comment_count = Prometheus::Client::Counter.new(
:comment_count,
'A counter of new comments'
)
prometheus.register(comment_health_gauge)
prometheus.register(comment_health_db_gauge)
prometheus.register(comment_count)

# Schedule health check function
scheduler = Rufus::Scheduler.new
scheduler.every '5s' do
check = JSON.parse(healthcheck_handler(DB_URL, VERSION))
set_health_gauge(comment_health_gauge, check['status'])
set_health_gauge(comment_health_db_gauge, check['dependent_services']['commentdb'])
end

before do
env['rack.logger'] = settings.mylogger # set custom logger
end

after do
request_id = env['HTTP_REQUEST_ID'] || 'null'
logger.info('service=comment | event=request | ' \
"path=#{env['REQUEST_PATH']}\n" \
"request_id=#{request_id} | " \
"remote_addr=#{env['REMOTE_ADDR']} | " \
"method= #{env['REQUEST_METHOD']} | " \
"response_status=#{response.status}")
end

# retrieve post's comments
get '/:id/comments' do
id = obj_id(params[:id])
begin
posts = settings.mongo_db.find(post_id: id.to_s).to_a.to_json
rescue StandardError => e
log_event('error', 'find_post_comments',
"Couldn't retrieve comments from DB. Reason: #{e.message}",
params)
halt 500
else
log_event('info', 'find_post_comments',
'Successfully retrieved post comments from DB', params)
posts
end
end

# add a new comment
post '/add_comment/?' do
begin
prms = { post_id: params['post_id'],
name: params['name'],
email: params['email'],
body: params['body'],
created_at: params['created_at'] }
rescue StandardError => e
log_event('error', 'add_comment',
"Bat input data. Reason: #{e.message}", prms)
end
db = settings.mongo_db
begin
result = db.insert_one post_id: params['post_id'], name: params['name'],
email: params['email'], body: params['body'],
created_at: params['created_at']
db.find(_id: result.inserted_id).to_a.first.to_json
rescue StandardError => e
log_event('error', 'add_comment',
"Failed to create a comment. Reason: #{e.message}", params)
halt 500
else
log_event('info', 'add_comment',
'Successfully created a new comment', params)
comment_count.increment
end
end

# health check endpoint
get '/healthcheck' do
healthcheck_handler(DB_URL, VERSION)
end

get '/*' do
halt 404, 'Page not found'
end
10 changes: 10 additions & 0 deletions src/comment/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require './comment_app'
require 'rack'
require 'prometheus/middleware/collector'
require 'prometheus/middleware/exporter'

use Rack::Deflater, if: ->(_, _, _, body) { body.any? && body[0].length > 512 }
use Prometheus::Middleware::Collector
use Prometheus::Middleware::Exporter

run Sinatra::Application
6 changes: 6 additions & 0 deletions src/comment/docker_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

echo `git show --format="%h" HEAD | head -1` > build_info.txt
echo `git rev-parse --abbrev-ref HEAD` >> build_info.txt

docker build -t $USER_NAME/comment .
75 changes: 75 additions & 0 deletions src/comment/helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
def obj_id(val)
begin
BSON::ObjectId.from_string(val)
rescue BSON::ObjectId::Invalid
nil
end
end

def document_by_id(id)
id = obj_id(id) if String === id
if id.nil?
{}.to_json
else
document = settings.mongo_db.find(_id: id).to_a.first
(document || {}).to_json
end
end

def healthcheck_handler(db_url, version)
begin
commentdb_test = Mongo::Client.new(db_url,
server_selection_timeout: 2)
commentdb_test.database_names
commentdb_test.close
rescue StandardError
commentdb_status = 0
else
commentdb_status = 1
end

status = commentdb_status
healthcheck = {
status: status,
dependent_services: {
commentdb: commentdb_status
},
version: version
}

healthcheck.to_json
end

def set_health_gauge(metric, value)
metric.set(
{
version: VERSION,
commit_hash: BUILD_INFO[0].strip,
branch: BUILD_INFO[1].strip
},
value
)
end

def log_event(type, name, message, params = '{}')
case type
when 'error'
logger.error('service=comment | ' \
"event=#{name} | " \
"request_id=#{request.env['HTTP_REQUEST_ID']}\n" \
"message=\'#{message}\'\n" \
"params: #{params.to_json}")
when 'info'
logger.info('service=comment | ' \
"event=#{name} | " \
"request_id=#{request.env['HTTP_REQUEST_ID']}\n" \
"message=\'#{message}\'\n" \
"params: #{params.to_json}")
when 'warning'
logger.warn('service=comment | ' \
"event=#{name} | " \
"request_id=#{request.env['HTTP_REQUEST_ID']}\n" \
"message=\'#{message}\'\n" \
"params: #{params.to_json}")
end
end
20 changes: 20 additions & 0 deletions src/post-py/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM python:3.6.0-alpine

WORKDIR /app
ADD . /app/

RUN apk update
RUN apk add gcc
RUN apk add musl-dev


RUN pip install --upgrade pip
RUN pip install wheel
RUN pip install -r /app/requirements.txt


ENV POST_DATABASE_HOST post_db
ENV POST_DATABASE posts

CMD ["python3", "post_app.py"]

1 change: 1 addition & 0 deletions src/post-py/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.2
6 changes: 6 additions & 0 deletions src/post-py/docker_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

echo `git show --format="%h" HEAD | head -1` > build_info.txt
echo `git rev-parse --abbrev-ref HEAD` >> build_info.txt

docker build -t $USER_NAME/post .
40 changes: 40 additions & 0 deletions src/post-py/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import structlog
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
from json import dumps
from flask import request


log = structlog.get_logger()


def http_healthcheck_handler(mongo_host, mongo_port, version):
postdb = MongoClient(mongo_host, int(mongo_port),
serverSelectionTimeoutMS=2000)
try:
postdb.admin.command('ismaster')
except ConnectionFailure:
postdb_status = 0
else:
postdb_status = 1

status = postdb_status
healthcheck = {
'status': status,
'dependent_services': {
'postdb': postdb_status
},
'version': version
}
return dumps(healthcheck)


def log_event(event_type, name, message, params={}):
request_id = request.headers['Request-Id'] \
if 'Request-Id' in request.headers else None
if event_type == 'info':
log.info(name, service='post', request_id=request_id,
message=message, params=params)
elif event_type == 'error':
log.error(name, service='post', request_id=request_id,
message=message, params=params)
Loading