07 апреля, 2012

LAMP одной командой

Одна команда способна превратить Debian в систему, отвечающий потребностям среднестатистического php-быдлокодера:

apt-get install apache2 libapache2-mod-php5 php5-cli php5-curl php5-gd \
 php5-imap php5-mysql php5-sqlite php5-memcached php5-geoip memcached \
 mysql-server mysql-client mytop iftop iotop htop ntp

Всё это ещё и настраивать надо, впрочем кого это волнует ;)

27 марта, 2012

Часовые пояса в Java

В виду отмены перехода на зимнее время и обратно весьма актуальна проблема обновления временных зон. В Debian/Ubuntu проблема решается легко - обновлением пакета tzdata. К сожалению, часть приложений стоят особняком и данные для них приходится обновлять отдельно. В список «проблемных» программ попадают mysql со специальной утилитой и героиня поста - Java.

Для OpenJDK зоны вынесены в отдельный пакет tzdata-java и легко обновляются. Для Oracle (Sun) Java, которую требуют некоторые упёртые товарищи, придётся скачивать специальную утилиту - TZUpdater. Скачиваем, распаковываем, запускаем java -jar tzupdater.jar --update. После каждого изменения в часовых поясов утилита обновляется и её нужно снова скачивать.

Проверить корректность временных зон можно при помощи простой программы:

$ cat TestMSKtz.java 
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class TestMSKtz {
  public static void main(String[] args) {

    Calendar cal = new GregorianCalendar();
    System.out.printf("Local time: %04d-%02d-%02d %02d:%02d:%02d\n", cal.get(Calendar.YEAR),cal.get(Calendar.MONTH),cal.get(Calendar.DAY_OF_MONTH),cal.get(Calendar.HOUR_OF_DAY),cal.get(Calendar.MINUTE),cal.get(Calendar.SECOND));

    cal = new GregorianCalendar(TimeZone.getTimeZone("Europe/Moscow"));
    System.out.printf("Moscow time: %04d-%02d-%02d %02d:%02d:%02d\n", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));

    cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    System.out.printf("UTC time: %04d-%02d-%02d %02d:%02d:%02d\n", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));    
  }
}
$ javac TestMSKtz.java && java TestMSKtz
Local time: 2012-02-27 16:40:50
Moscow time: 2012-02-27 16:40:50
UTC time: 2012-02-27 12:40:50

После обновления зон нужно будет перезапустить java-приложения.

26 марта, 2012

yum: command not found

Не знаю, зачем продавать VPS с урезанной CentOS, наверное чтобы проще было впарить какую-нибудь панель. Маркетологи хуже политиков :(

Так или иначе сервер уже куплен, я обещал его настроит, а yum на сервере отсутствует. Немного погуглив составил рецепт для пятой версии:

rpm -Uvh http://mirror.centos.org/centos/5/os/x86_64/CentOS/python-elementtree-1.2.6-5.x86_64.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/python-iniparse-0.2.3-4.el5.noarch.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/python-sqlite-1.1.7-1.2.1.x86_64.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/python-urlgrabber-3.1.0-6.el5.noarch.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/yum-fastestmirror-1.1.16-21.el5.centos.noarch.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/yum-metadata-parser-1.1.2-3.el5.centos.x86_64.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/m2crypto-0.16-8.el5.x86_64.rpm \
http://mirror.centos.org/centos/5/os/x86_64/CentOS/yum-3.2.22-39.el5.centos.noarch.rpm

18 марта, 2012

GLPI: неправильная кодировка при обработке почты.

После начала использования GLPI в качестве Helpdesk всплыл весьма неприятный баг: часть обращений (тикетов) импортировались в неправильной кодировке. С первого взгляда «плохие» письма ничем не выделялись.

Тут нужно отметить, что мы используем Gmail-аккаунт. Только после отладки, стало понятно, что в «плохом» случае Gmail отдаёт тему письма в KOI8-R, а тело в UTF-8. В этой ситуации в коде GLPI происходит вызов iconv с параметрами ('KOI8-R' -> 'UTF-8').

Что же, воспроизведённый баг - половина патча. Приведённый ниже патч проработал у меня полтора месяца, проблем не выявлено.

--- inc/mailcollector.class.php.orig      2012-01-30 00:08:25.000000000 +0400
+++ inc/mailcollector.class.php   2012-01-30 01:29:35.000000000 +0400
@@ -955,6 +955,11 @@
                      $text = mb_convert_encoding($text, 'utf-8',$param->value);
                      $this->body_converted=true;
                   }
+                  if ((strtoupper($param->attribute)=='CHARSET')
+                      && function_exists('mb_convert_encoding')
+                      && strtoupper($param->value) == 'UTF-8') {
+                     $this->body_converted=true;
+                  }
                }
             }
             return $text;

P.S. Разработчикам отписал

15 марта, 2012

Генерация самоподписанного сертификата с openSSL

Переработка этой статьи, чтобы не искать её каждый раз, когда нужно сгенерировать сертификат.

Для начала сгенерируем ключ. OpenSSL не позволяет сгенерировать ключь, не защищенный паролем. Nginx не запускается с ключем, защищенным паролем. Чтобы устранить противоречия, сначала создаётся защищенный ключ, потом с него снимается защита.

# openssl genrsa -des3 -out server.key.encrypted 2048
 Generating RSA private key, 2048 bit long modulus
 ........+++
 ..................+++
 e is 65537 (0x10001)
 Enter pass phrase for server.key.encrypted:
 Verifying - Enter pass phrase for server.key.encrypted:
# openssl rsa -in server.key.encrypted -out server.key
 Enter pass phrase for server.key.encrypted:
 writing RSA key
# rm server.key.encrypted

Теперь имея ключ, можно создавать сертификат. Никаких особых хитростей, главное чтобы в CN был целевой адрес.

# openssl req -new -key server.key -out server.csr
 You are about to be asked to enter information that will be incorporated
 into your certificate request.
 What you are about to enter is what is called a Distinguished Name or a DN.
 There are quite a few fields but you can leave some blank
 For some fields there will be a default value,
 If you enter '.', the field will be left blank.
 -----
 Country Name (2 letter code) [AU]:RU
 State or Province Name (full name) [Some-State]:Moscow
 Locality Name (eg, city) []:Moscow
 Organization Name (eg, company) [Internet Widgits Pty Ltd]:Mind
 Organizational Unit Name (eg, section) []:
 Common Name (eg, YOUR name) []:example.ru
 Email Address []:

 Please enter the following 'extra' attributes
 to be sent with your certificate request
 A challenge password []:
 An optional company name []:
# openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
 Signature ok
 subject=/C=RU/ST=Moscow/L=Moscow/O=Mind/CN=example.ru
 Getting Private key

27 февраля, 2012

Базовая настройка MySQL

MySQL весьма популярная СУБД, на удивление, приемлемые настройки «из коробки» мне ни разу не попадались. Приведу наиболее важные параметры, которые стоит поменять сразу после установки.

Начнём с кодировки и метода сравнения строк, потому как latin1 плохой вариант, latin1_swedish_ci просто не вариант для кириллицы. Кодировка без вариантов utf8. Метод сравнения - utf8_general_ci, хороший вариант для русского и английского языков, для других случаев, возможно, стоит использовать utf8_unicode_ci.

character_set_client = utf8
character_set_server = utf8
collation_server = utf8_general_ci

Многие разработчики не указывать «движок» при создании таблицы, следующая опция позволит сделать выбор за них, естественно в сторону InnoDB.

default_storage_engine = InnoDB

По умолчанию все InnoDB таблицы хранятся в одном файле - ibdata1. Файл этот увеличивается, при росте объёма данных, но никогда не уменьшается. При значительном объёме БД это создаёт определённые сложности, например с помощью Xtrabackup нельзя будет скопировать отдельную БД, на InnoDB-таблицы не будут распространяться файловые квоты (привет разработчикам ISPmanager, надеюсь они уже поправили эту багу). Следующая опция заставит MySQL хранить каждую таблицу в отдельном файле:

innodb_file_per_table = 1
При при хранении объёмных blob'ов или текстов, max_allowed_packet должен быть выставлен на клиенте и сервере в значение превосходящее максимальный объём записи.
max_allowed_packet = 16M

На опции ниже так же нужно обратить внимание, если скорость выполнения запросов вызывает нарекания.

innodb_flush_log_at_trx_commit = 2

innodb_open_files = 300

innodb_buffer_pool_size = 128MB

query_cache_size = 10M
query_cache_limit = 1M

16 февраля, 2012

Exim frozen message

Спешка хороша... Все знают, когда она хороша, но всё равно просят сделать побыстрее. После переноса одного из внутренних ресурсов на новый хост от него перестал сыпаться спам не приходили информационные сообщения.

Беглый анализ показал, что сообщения сервер не покидали. Вывод mailq пестрел сообщениями вида «3h 5.1K 1RxZQT-0005q8-O8 <> *** frozen ***». В ответ на sendmail -q в лог exim сыпалось примерно то же самое: «2012-02-15 07:35:13 1RxEYG-0001lU-JE Message is frozen». При попытке отправить e-mail в лог падает сообщение «Mailing to remote domains not supported».

Тут всё становится ясно, exim заботливо настроен на доставку только локальной почты. Ситуацию исправляется можно с помощью команды dpkg-reconfigure exim4-config. После правильной настройки, свежая почта начинает доходить до адресата, но замёрзшая так и висит в очереди. Протолкнуть её можно не хитрой командой, tail выполняет роль дозатора:

mailq  | grep "*** frozen ***" | awk '{print $3}' | tail -10 | xargs exim -d -M