понеділок, 21 листопада 2016 р.

Удобный доступ к контейнеру Docker и другие вкусности :)

Сегодня пару коротких заметок о alias которые добавляют удобства для меня как администратора системы.

Первый alias
Так как я часто работаю с контейнерами Docker, и каждый раз для подключения к контейнеру писать docker attach мне лень, по этому я добавил себе в .profile
# Docker shell
alias dsh='function target(){ CONTAINER=$(docker inspect --format {{.State.Pid}} $1); sudo nsenter --target $CONTAINER --mount --uts --ipc --net --pid; }; target'
Теперь что бы получить "root" доступ к контейнеру, достаточно выполнить команду dsh

Второй alias
Вся суть этой команды, это показать актуальный лог файл игрового сервера. При перезапуске сервер формирует новый лог файл "logs/`date +%Y%m%d.%H%M%S`.log" и как обычно, лень как двигатель прогресса 😜 привела до написания этого alias, который как обычно добавил себе в .profile
# 7 Days to Die tools
alias tail7='grc -c conf.7dtd tail -f $(GAMELOG="/mnt/data/games/7DaysToDie/steam/games/7dtd/logs" && ls -tc ${GAMELOG} | echo "$GAMELOG/$(head -n1)")'
Дополнительная "плюшка" этого alias, это то что он еще раскрашивает лог файл.
Так как я иногда играя в игру, очень боюсь там собак, то я сделал себе такой вот "чит", перед тем как я подключаюсь к игре, я подключаюсь к серверу и запускаю эту команду и когда появляются собаки, консоль начинает "бибикать" 😎
Для раскраски лога, я использую утилиту Generic Colouriser, для которой я написал простенький конфигурационный файл conf.7dtd который нужно положить в ~/.grc/
# Spawned any zombies
regexp=.+Spawned.+|.+AIDirector.+
colours=yellow 
-
# Spawned Zombie Dog
regexp=.+Spawned.+Dog.+
colours=red beep
-
# Player info
regexp=.+Player.+
colours=green
-
regexp=.+ERR.+|.+EXC.+
colours=red
-
regexp=.+WRN.+|Warning.+|.+UNET.+
colours=magenta
-
regexp=(\d{4}\-\d{2}\-\d{2}).(\d{2}:\d{2}:\d{2})
colours=black, blue, blue bold
count=once
-
# Time: 6.00m FPS: 37.77 Heap: 250.0MB Max: 250.0MB
regexp=Time\: (\d{1,4}\.\d\dm) FPS\: (\d{1,3}\.\d\d) Heap\: (\d{1,4}\.\dMB) Max\: (\d{1,4}\.\dMB)
colours=default bold, blue bold, yellow bold, green bold, red bold
count=once
-
regexp=Ply\: (\d{1,2}) Zom\: (\d{1,2}) Ent\: (\d{1,2}) \((\d{1,2})\) Items\: (\d{1,2})
colours=default bold, green bold, red bold, yellow bold, yellow bold, blue bold
count=once
Вот так теперь радужно выглядит лог 😐

Программное формирования изображения на Go

Понадобилось мне при разработке мода на 7 Days to Die программно формировать изображение круга в зависимости от количества дней в игре. Так как я давно порывался посмотреть на Go, решил я использовать его для данной задачи. Да и подходит он для такого микро-сервиса в самый раз.
Сделать веб сервер на Go просто :) для этого используем стандартную библиотеку "net/http"
func main() {
  flag.Parse()
  log.Println("Starting tiny webserver for 7DTD moon texture...")
  log.Println("Document root path: ", *root)
  http.HandleFunc("/moon", moonHandler)
  log.Println("Listening on port: ", *port)
  if err := http.ListenAndServe(":"+strconv.Itoa(*port), nil); err != nil {
    log.Fatal("ERROR Listen and Serve: ", err)
  }
}
Дальше, формируем изображение круга с использование библиотеки "image/draw",
func moonHandler(w http.ResponseWriter, r *http.Request) {
  d := getDay(r)
  /*** Create colored image ***/
  dst := image.NewRGBA(image.Rect(0, 0, cXY.X+cR+1, cXY.Y+cR+1))
  draw.Draw(dst, dst.Bounds(), &circle{cXY, cR}, image.ZP, draw.Src)
  writeImg(w, dst)
}
и отдаем ее как картинку "image/png"
func writeImg(w http.ResponseWriter, img image.Image) {
  buffer := new(bytes.Buffer)
  if err := png.Encode(buffer, img); err != nil {
    log.Println("Unable to encode image: ", err)
    http.Error(w, err.Error(), http.StatusNotFound)
    return
  }

  w.Header().Set("Content-Type", "image/png")
  w.Header().Set("Content-Length", strconv.Itoa(len(buffer.Bytes())))

  if _, err := w.Write(buffer.Bytes()); err != nil {
    log.Println("Unable to write image: ", err)
    http.Error(w, err.Error(), http.StatusNotFound)
    return
  }
}
А теперь самое интересное, как же сделать эту картинку :)
Для этого описываем структуру:
type circle struct {
  P image.Point
  R int
}
И определяем методы которые описываю данную структуру в разрезе "image.Image"
func (c *circle) ColorModel() color.Model {
  return color.AlphaModel
}
func (c *circle) Bounds() image.Rectangle {
  return image.Rect(c.P.X-c.R, c.P.Y-c.R, c.P.X+c.R, c.P.Y+c.R)
}
func (c *circle) At(x, y int) color.Color {
  xx, yy, rr := float64(x-c.P.X), float64(y-c.P.Y), float64(c.R)
  if xx*xx+yy*yy < rr*rr {
    return color.Alpha{255}
  }
  return color.Alpha{0}
}
Все это прекрасно работает, но мне нужно было получить картинку с определенной прозрачностью, по этому пришлось немного изменить код функции "At" и структуры "circle". Добавляем поля для цвета и типа изображения (сплошная заливка или градиентная)
type circle struct {
  P image.Point
  R int
  C color.NRGBA
  S bool
}
... и теперь сама процедура отрисовки круга:
func (c *circle) At(x, y int) color.Color {
  xx, yy, rr := float64(x-c.P.X), float64(y-c.P.Y), float64(c.R)
  if xx*xx+yy*yy < rr*rr {
    xr := math.Abs(float64(c.P.X - x))
    yr := math.Abs(float64(c.P.Y - y))
    ar := uint8((1 - math.Sqrt(xr*xr+yr*yr)/float64(c.R)) * float64(c.C.A))
    if c.S {
      ar = c.C.A
    }
    return color.NRGBA{c.C.R, c.C.G, c.C.B, ar}
  }
  return color.Alpha{0}
}
В итоге, мы можем получить два вида кружкок :) задав параметры при обращении к веб серверу.
Градиентная заливка для 7 дня Сплошная заливка для 7 дня Градиентная заливка для 5-6 дня Сплошная заливка для 5-6 дня


Конечный результат как это все выглядит в игре :)

четвер, 6 жовтня 2016 р.

Автодополнение для Docker

Всем доброго времени суток :)
Уже несколько месяце я полностью переполз на Manjaro Linux (по сути ArchLinux для домохозяек :)) на рабочем ПК, и что бы продолжить эксперименты,  установил я недавно на него Docker. Все хорошо, вот только беда, на моем домашнем сервере было настроено автодополнение команд. Собственно и займемся исправлением данной "не удобности".
Для начало нужно установить пакет bash-completion
$ sudo pacman -S bash-completion
Дальше настраиваем само авто дополнение. Для этого идем на https://github.com/docker/docker/blob/master/contrib/completion/bash/docker и скачиваем файл.
Настроить автодополнение можно двумя способами - глобально или только для себя.
Для глобальной настройки нужно положить файл в /etc/bash_completion.d
$ sudo curl -L https://raw.githubusercontent.com/docker/docker/master/contrib/completion/bash/docker > /etc/bash_completion.d/docker 
Только для себя. Не забываем прописать в ~/.profile выполнение этого файла
$ sudo curl -L https://raw.githubusercontent.com/docker/docker/master/contrib/completion/bash/docker > ~/.docker-completion.sh
$ vim ~/.profile
... cut ...
. ~/.docker-completion.sh
... cut ...

пʼятниця, 11 грудня 2015 р.

Тригеры в MySQL

Пришлось как то мне заниматься "свадебкой" - подружить Asterisk с UTM5 (биллинговая система). После нескольких безсонных ночей, мне все таки удалось кое как заставить работать эти системы вместе, но был один существенный недостаток в этой связке. Хотя Asterisk и отдавал данные в UTM5, но обратной связи он не имел с биллинговой системой. Что было очень неудобно, так как приходилось фактически вести две конфигурации, после того как в UTM5 например добавлялся новый абонент, его нужно было переносить руками в конфигурацию Asterisk… Что в конечном итоге могло создать кучу проблем и постоянно нужно было следить за тем что бы конфигурации были одинаковы. Так как базы оба продукта хранили в MySQL, то первое что мне пришло в голову, это использовать тригеры для синхронизации конфигураций. Конечно настроить Asterisk что бы он работал с базой, было еще той задачей, так как я раньше не видел это чудо, только слышал, что есть такой продукт.
Сообственно об этом я расскажу как то в другой раз, думаю это не последняя статья на телефонную тематику у меня :)
Немножко посмотрел документацию по MySQL и приступил, что тут может быть сложного то… сделать три тригера, на вставку, обновление и удаление записи в таблице:
-- Insert peer --
CREATE TRIGGER tel_add AFTER INSERT ON tel_numbers 
  FOR EACH ROW BEGIN 
    INSERT INTO tel_peers SET 
    id = NEW.id, name = NEW.login, 
    callerid = NEW.tel_number, md5secret = MD5(CONCAT(NEW.login,':',NEW.allowed_cid,':',NEW.password)); 
  END;

-- Update peer --
CREATE TRIGGER tel_update AFTER UPDATE ON tel_numbers 
  FOR EACH ROW BEGIN 
    UPDATE tel_peers SET 
    name = NEW.login, 
    callerid = NEW.tel_number, 
    md5secret = MD5(CONCAT(NEW.login,':',NEW.allowed_cid,':',NEW.password)) 
    WHERE id = NEW.id; 
  END;

-- Delete peer --
CREATE TRIGGER tel_del AFTER DELETE ON tel_numbers 
  FOR EACH ROW BEGIN 
    DELETE FROM tel_peers WHERE id = NEW.id; 
  END;
Но как оказалось, UTM5 не удаляет данные когда в GUI нажимаешь кнопку удалить, в таблице есть отдельное поле с признаком того что запись удалена (я так понимаю для того что бы была возможность просмотреть старые записи). Пришлось мне переделывать тригер на обновление, а на удаление просто выбросить.
-- Update peer --
CREATE TRIGGER tel_update AFTER UPDATE ON tel_numbers 
  FOR EACH ROW 
  BEGIN
    IF (OLD.is_deleted = 0 AND NEW.is_deleted = 1) THEN 
      BEGIN
        DELETE FROM tel_peers WHERE id = NEW.id;
      END;
    ELSE 
      BEGIN
        UPDATE tel_peers SET 
        name = NEW.login, 
        callerid = NEW.tel_number, 
        md5secret = MD5(CONCAT(NEW.login,':',NEW.allowed_cid,':',NEW.password)) 
        WHERE id = NEW.id;
      END;
    END IF;
  END;
Конечно это все наверное можно как то более изящно переписать, но так как я не сильно большой специалист в SQL, для решения моей задачи достаточно :)

Массовая обработка файлов

Случилась у меня беда… мой верный Nexus 4 погиб от руки-ноги или еще какой то части тела, в общем раздавили его… Пришлось мне вытащить свой старый iPhone 4. Он конечно тоже не в идеальном состоянии, но хотя бы можно звонить :)
Даа… к чему я это все, блог то как бы про UNIX… В общем решил я залить на него парочку рингтонов, то как их делать я не буду рассказывать, но вот как заменить расширения файлов с m4a на m4r я и расскажу. Хотя рассказывать тут нечего, три команды в одной строке :)
find . -name '*.m4a' | sed  's: :\\\ :g;p;s:m4a:m4r:g;' | xargs -L2 mv 
Все таки какая мощная штука SED, каждый раз что то новое для себя нахожу в его использовании… :)

вівторок, 24 листопада 2015 р.

Windows 10 | Настройка VPN

Настраивал сегодня подключение у себя на Windows 10 к VPN серверу. И не смог найти галочку в настройке подключения - "Не использовать как шлюз по умолчанию".
То есть, теперь если я подключаюсь к VPN то весь трафик направляется через это соединение, и понятно интернет пропадает :( что не очень то приятно, потому как иногда нужно и что то подсмотреть в сети.
Долго рылся в настройках, но ничего не смог найти... но как оказалось, "ларчик просто открывался". Для этого нужно в консоли PowerShell, которую желательно запускать от Администратора, выполнить всего одну команду если вы знаете название своего VPN подключения, или две если не знаете :)
Get-VpnConnection
Name                  : VPN Name
ServerAddress         : vpn.server.ua
AllUserConnection     : False
...
Set-VpnConnection -Name "VPN Name" -SplitTunneling $True
Вот и все, теперь при подключении к VPN и доступ к интернет работает и к локальной сети VPN.

середа, 22 липня 2015 р.

Создаем игровой сервере 7 Days to Die. Немножко автоматизации.


И снова добрый день/вечер/ночь и тд. всем кто читает данный блог.
Потихоньку я осваиваю основы Docker и вникаю в его возможности и функционал. Тяжелый это труд все это описывать, проще все сделать быстренько за какой то часик, а потом тратишь несколько дней на оформление этого все в читабельный вид, что бы можно это все опубликовать. Ну что же продолжим... это было небольшое лирическое отступление :) что бы выровнять первый абзац с картинкой :)
Продолжаем наши эксперименты с Docker. Сегодня поупражняемся с созданием Dockerfile. Подопытным "кроликом" будет у нас все тот же 7 Days to Die.
Несколько слов о новых образах, которые теперь доступны через на официальном "Hub Docker".
~> docker search sovicua
NAME             DESCRIPTION           STARS     OFFICIAL   AUTOMATED
sovicua/debian   Debian Jessie (8.0)   0
Все они построены на базе Debian Jessie. На текущий момент есть два образа с тегами "jessie" и "steamcmd". Собственно первый это чистый Debian с установленными дополнительными пакетами и настроенной временной зоной. Данные настройки были описаны в Установка timezone в базовом образе Docker. Второй собран на базе первого с установленными библиотеками и steamcmd.
Итак, создаем файл конфигурации для создания образа контейнера.
~> mkdir docker/steamcmd/
~> cd docker/steamcmd/
~/d/steamcmd> vim Dockerfile
FROM sovicua/debian:jessie
MAINTAINER Viktor M. Sytnyk <sovicua sovic.org.ua>
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get install -y wget \
 && echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/2/Debian_8.0/ /' >> /etc/apt/sources.list.d/fish.list \
 && wget http://download.opensuse.org/repositories/shells:fish:release:2/Debian_8.0/Release.key \
 && apt-key add - < Release.key \
 && apt-get update && apt-get upgrade -y \
 && apt-get install -y lib32gcc1 lib32stdc++6 fish && apt-get clean \
 && useradd -m -c "SteamCMD User" -s /usr/bin/fish steam
WORKDIR /home/steam/steamcmd
RUN wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz \
 && tar -xvzf steamcmd_linux.tar.gz && rm steamcmd_linux.tar.gz \
 && chown -R steam:steam /home/steam
CMD /usr/bin/fish
Как вы можете видеть, shell по умолчанию я использую Fish Shell, хотя это не существенно, но понравился он мне :)
Создаем образ:
~/d/steamcmd> docker build -t sovicua/debian:steamcmd .
После этого можно выгрузить образ на Docker Hub:
~/d/steamcmd> docker push sovicua/debian:steamcmd
Теперь у нас есть готовый образ с установленным steamcmd, что позволит нам в дальнейшем быстро развернуть любую игру которая поддерживает установку с использованием данной утилиты. Теперь переходим к созданию образа с установленным сервером 7 Days to Die.
Для начала создадим файл скрипта с помощью которого будем устанавливать, а также обновлять саму игру.
~> mkdir docker/games/7dtd
~> cd docker/games/7dtd
~/d/steamcmd> vim 7dtd.steamcmd

login steam_user steam_password
force_install_dir /home/steam/games/7dtd
app_update 294420 validate
quit
Где, steam_user и steam_password это имя пользователя и пароль на учетной записи которого куплена эта игра.
Теперь создадим собственно сам файл конфигурации создания образа.
~/d/steamcmd> vim Dockerfile
FROM sovicua/debian:steamcmd
MAINTAINER Viktor M. Sytnyk <sovicua@sovic.org.ua>
WORKDIR /home/steam/steamcmd
COPY 7dtd.steamcmd /home/steam/steamcmd/
CMD /usr/bin/fish
Честно говоря, можно было бы и не делать данный образ, для этих целей можно бы использовать предыдущий образ, создав в нем скрипт установки и обновления игры, но раз уж автоматизируем то делаем все типа ка надо :) Изначально я планировал что бы при создании образа, сразу инсталлирована игра, но так как у меня настроен Steam Guard, то он запрашивал у меня код, так как это типа новая машина которая хочет получить доступ к моей учетной записи.
В Dockerfile была команда на запуск скрипта:
RUN ./steamcmd.sh +runscript 7dtd.steamcmd
Победить это, что бы инсталляция останавливалась при запросе кода, у меня так и не получилось. :( По этому инсталляцию придется выполнять руками уже в контейнере.
После этого создаем сам образ как обычно.
~/d/g/7dtd> docker build -t sovicua/games:7dtd
Вот что у нас получилось после всех этих процедур:
~/d/7dtd> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
sovicua/games   7dtd                c1c3539bbe2b        4 hours ago         594 MB
sovicua/debian  steamcmd            72ebdb1d894c        4 hours ago         594 MB
sovicua/debian  jessie              8ca03adc0398        27 hours ago        478.9 MB
Теперь мы можем быстренько создать контейнер:
~/d/7dtd> docker run --name="7dtd" --net="host" --user="steam" -i -t sovicua/games:7dtd
Выполняем инсталляцию игры, не забываем указать имя пользователя и пароль, если это не сделали раньше.
~/steamcmd> ./steamcmd.sh +runscript 7dtd.steamcmd
После инсталляции правим файл конфигурации сервера serverconfig.xml как вам удобно, и можно запускать сервер.
~/steamcmd> cd ../games/7dtd
~/g/7dtd> vim serverconfig.xml
Из параметров которые я у себя изменил, это установил пароль на доступ по telnet, и небольшие настройки самого сервера, такие как тройной лут и длина суток 2 часа реального времени. И еще пару параметров.
<property name="ServerName"             value="iHome Games Server" />
<property name="ServerPassword"         value="" />
<property name="ServerMaxPlayerCount"   value="4" />
<property name="ServerDescription"      value="iHome 7 Days to Die server" />
<property name="ServerWebsiteURL"       value="home.sovic.org.ua" />
...
<property name="DayNightLength"         value="120" />
<property name="DayLightLength"         value="18" />
...
<property name="DropOnDeath"            value="2" />
<property name="DropOnQuit"             value="0" />
...
<property name="CraftTimer"             value="0" />
<property name="LootTimer"              value="0" />
...
<property name="EnemySpawnMode"         value="4" />
...
<property name="LootAbundance"          value="300" />
<property name="LootRespawnDays"        value="30" />
Дополнительно, можно установить мод на сервер, который добавит пару дополнительных команд и самое вкусное что мне нравиться это возможность просмотра карты через веб. Очень помогает в поиске пещерок :) так как они редко сейчас встречаются, а на игровой карте искать не удобно, пока ищешь могут и "ушатать" :)
~/g/7dtd> wget http://illy.bz/fi/7dtd/server_fixes.tar.gz
~/g/7dtd> tar -xzvf server_fixes.tar.gz
Пример работы данного мода можно посмотреть у меня на сервере iHome 7 Days to Die server.
Ну вот и все, запускаем сервер:
~/g/7dtd> ./startserver.sh -configfile=serverconfig.xml
Если кто забыл, то напоминаю, что бы выйти с контейнера без его остановки, используем комбинацию клавиш Ctrl+p Ctrl+q.
На этом все, приятной игры. Если кто захочет поиграть вместе у меня на сервера, пишите.
Заходите на мой блог - UNIX и не только...