Создание docker container с tengine webserver(русская версия)
Что такое tengine?
Tengine является форком nginx, разрабатываемым и поддерживаемым крупнейшими китайскими сайтами: taobao.com и tmall.com и, разумеется, применяется на серверах этих сайтов в production.
Полный список имеющихся на сегодня в tengine фич можно найти на странице http://tengine.taobao.org
Я вам расскажу как быстро собрать docker container с tengine на борту с помощью Packer без всяких проблем и очень быстро.
Вы сможете применить полученые знания для организации процесса Continuous delivery, packer прекрасная утилиа для использования в Continuous delivery(cd) или Continuous integration(ci) pipelines и как утилита для automation сборок любых образов.
Что такое Packer
Packer — это инструмент для создания одинаковых образов ОС для различных платформ из одного описания.
На текущий момент Packer поддерживает множество платформ и этот список постояно увеличивается!
Терминология в Packer
Templates: JSON файл содержащий сценарий сборки
Builders: Платформа для сборки будущего образа
Provisioners: Утилиты/скрипты для настройки будущего образа
Post-processors: Что необходимо сделать после сборки
Так же вы можете, ознакомится с моей прeзентацией с Pgday17 в Санкт-Петербурге.
Template как много в этом звуке
И так пришло время начать писать темплейт будущей сборки, для начала давайте опишем что у нас будет выступать в качестве builder. Так как мы будем собирать docker container в качестве builder у нас быдует выступать docker, а за основу мы возьмем образ alpine.
"builders":[{
"type": "docker",
"image": "alpine:3.5",
"export_path": "containers/tengine.tar",
"run_command": [
"-d",
"-i",
"-t",
"--name",
"tengine-{{timestamp}}",
"{{.Image}}",
"/bin/ash"
]
}],
В шаблоне мы должны указать: - тип билдера - исходный образ - команду запуска - Путь куда мы будем эспортировать готовый образ
Следующим шагом, нам необходимо указать provisioners
"provisioners":[
{
"destination": "/tmp/install.sh",
"source": "./scripts/install.sh",
"type": "file"
},
{
"type": "shell",
"inline":"apk add --no-cache bash"
},
{
"type": "shell",
"execute_command": "echo 'test' |/bin/bash -e '{{ .Path }}'",
"inline": [
"chmod +x /tmp/install.sh",
"/tmp/install.sh {{user `version`}}"
]
},
{
"destination": "/tmp",
"source": "./files",
"type": "file"
},
{
"type": "shell",
"inline": [
"mv /tmp/files/nginx.conf /etc/nginx/nginx.conf",
"mv /tmp/files/nginx.default.conf /etc/nginx/conf.d/default.conf"
]
}
]
}
Как вы видите, я использовал два типа провижинера первый это file, а второй shell. Задача провижинера file - это скопировать файл или папку с магины на которой мы производим сборку на удаленную машину, провижинер shell выполняет команду или скрипт на удаленном хосте. Все скрипты я храню в папке scripts, которая распологается в том же каталоге, что и наш шаблон, а файлы конфигураций я храню в папке files. Последние, что мы укажем это переменные
"variables": {
"version":"2.2.2"
},
Как переменную я указываю версию tengine.
Теперь давайте посмотрим, что содержится в первом скрипте install.sh, который я копирую на удаленный хост в папку tmp
#!/usr/bin/env bash
set -x
VERSION=$1
CONFIG="\
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_xslt_module=shared \
--with-http_image_filter_module=shared \
--with-http_geoip_module=shared \
--with-threads \
--with-http_slice_module \
--with-mail \
--with-mail_ssl_module \
--with-file-aio \
--with-http_v2_module \
--with-http_concat_module \
--with-http_sysguard_module \
--with-http_dyups_module \
"
addgroup -S nginx \
&& adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \
&& apk add --no-cache --virtual .build-deps \
gcc \
libc-dev \
make \
openssl-dev \
pcre-dev \
zlib-dev \
linux-headers \
curl \
gnupg \
libxslt-dev \
gd-dev \
geoip-dev;
curl -L "http://tengine.taobao.org/download/tengine-$VERSION.tar.gz" -o tengine.tar.gz \
&& mkdir -p /usr/src \
&& tar xfz tengine.tar.gz -C /usr/src \
&& rm tengine.tar.gz \
&& cd /usr/src/tengine-${VERSION}/ \
&& ./configure ${CONFIG} --with-debug \
&& make -j$(getconf _NPROCESSORS_ONLN) \
&& mv objs/nginx objs/nginx-debug \
&& ./configure ${CONFIG} \
&& make -j$(getconf _NPROCESSORS_ONLN) \
&& make install \
&& rm -rf /etc/nginx/html/ \
&& mkdir /etc/nginx/conf.d/ \
&& mkdir -p /usr/share/nginx/html/ \
&& install -m644 html/index.html /usr/share/nginx/html/ \
&& install -m644 html/50x.html /usr/share/nginx/html/ \
&& install -m755 objs/nginx-debug /usr/sbin/nginx-debug \
&& strip /usr/sbin/nginx* \
&& strip /etc/nginx/modules/*.so \
&& rm -rf /usr/src/tengine-${VERSION} \
&& apk add --no-cache --virtual .gettext gettext \
&& mv /usr/bin/envsubst /tmp/ \
\
&& runDeps="$( \
scanelf --needed --nobanner /usr/sbin/nginx /etc/nginx/modules/*.so /tmp/envsubst \
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
| sort -u \
| xargs -r apk info --installed \
| sort -u \
)" \
&& apk add --no-cache --virtual .nginx-rundeps $runDeps \
&& apk del .build-deps \
&& apk del .gettext \
&& mv /tmp/envsubst /usr/local/bin/\
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
Непосредственно, это сборка tengine, скрипт принимает как аргумент версию tengine, которыую мы указали выше как переменную. После того, как я скопировал скрипт установки, с помощью apk я устанавливаю в систему bash,
делаю исполняемый файл install.sh и запускаю сборку передвая переменную version на вход скрипта /tmp/install.sh {{user `version`}}
.
Далее я копирую файлы конфигурации tengine(nginx.conf), сам файл конфигурации такой же как и обычного nginx
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log error;
pid /var/run/nginx.pid;
#Specifies the value for maximum file descriptors that can be opened by this process.
worker_rlimit_nofile 51200;
events {
use epoll;
worker_connections 51200;
multi_accept on;
}
# load modules compiled as Dynamic Shared Object (DSO)
#
#dso {
# load ngx_http_fastcgi_module.so;
# load ngx_http_rewrite_module.so;
#}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# access_log /var/log/nginx/access.log main;
access_log off;
# Hide nginx version information
server_tokens off;
server_info off;
# Tell all agents to _only_ use HTTPS
# add_header Strict-Transport-Security "max-age=31536000";
# ssl_prefer_server_ciphers on;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
server_names_hash_bucket_size 128;
client_body_buffer_size 128k;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 50m;
client_header_timeout 15;
client_body_timeout 15;
send_timeout 12;
tcp_nodelay on;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
gzip_types
application/atom+xml
application/javascript
application/x-javascript
application/json
application/rss+xml
application/stylesheet
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
font/ttf
font/otf
image/svg+xml
image/x-icon
text/css
text/javascript
text/plain
text/xml
text/x-component;
#limit_conn_zone $binary_remote_addr zone=perip:10m;
##If enable limit_conn_zone,add "limit_conn perip 10;" to server section.
# limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
## add "limit_req zone=req_limit_per_ip burst=10 nodelay;" to server section.
include conf.d/*.conf;
}
На этом подготовка сборки завершена и теперь можем начать процесс сборки, для этого достаточно выполнить команду packer build template.json
После сбокри готовый контейнер будет помещен в директорию containers с именем tengine.tar
Для удобства, я использую docker-compose, создаем файл docker-compose.yml, примерное содержание может быть следующим:
version: '3'
services:
tengine:
build: tengine
tty: true
privileged: true
ports:
- '9090:80'
Дальше нам достаточно импортировать образ и запустить
docker import containers/tengine.tar tengine:latest
docker-compose up --build --remove-orphans -d
Кому лень писать можете сколнировать уже готовый репозиторий и собрать еще быстрее
git@github.com:pyToshka/packer-tengine-template.git
cd packer-tengine-template
./build.sh