hitcounter

Создание docker container с tengine webserver(русская версия)

  • April 19, 2018
  • Yuriy Medvedev

Что такое 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