Базовое решение


Добро пожаловать, участник Highload Cup 2017!
Это туториал по созданию с нуля простейшего решения задачи. Пройдя все шаги, ты сможешь отправить первое решение и получить первые результаты.
В нашем примере мы сделаем сервер на Go и Docker-контейнер для его запуска.

1. Создание веб-сервера

Для примера напишем веб-сервер на Go, который будет на все запросы отвечать "Hello, I'm Web Server!".

Для этого создадим файл main.go, в котором напишем:

package main

import "net/http"

func DumbHandler(writer http.ResponseWriter, request *http.Request) {
    writer.Write([]byte("Hello, I'm Web Server!"))
}

func main () {
    http.HandleFunc("/", DumbHandler)
    http.ListenAndServe(":80", nil)
}
Этого вполне достаточно для нашего простого решения.

2. Сборка и запуск Docker-контейнера

Теперь скомпилируем, соберем и запустим наш сервер. Для этого не нужно ничего устанавливать, кроме Docker.

Чтобы создать свой Docker-container, нам нужен файл с названием Dockerfile. Создадим его в папке, в которой находится main.go. В Dockerfile находятся директивы, которые выполняются при сборке контейнера. Он, фактически, описывает состояние системы перед запуском.

Любой Dockerfile можно наследовать от другого Dockerfile, и в нашем случае мы отнаследуемся от файла, который описывает базовую CentOS 7. Нам надо скачать и установить Go, скомпилировать наш main.go и запустить сервер. Сделать это можно так:

# Наследуемся от CentOS 7
FROM centos:7

# Выбираем рабочую папку
WORKDIR /root

# Устанавливаем wget и скачиваем Go
RUN yum install -y wget && \
    wget https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz

# Устанавливаем Go, создаем workspace и папку проекта
RUN tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz && \
    mkdir go && mkdir go/src && mkdir go/bin && mkdir go/pkg && \
    mkdir go/src/dumb

# Задаем переменные окружения для работы Go
ENV PATH=${PATH}:/usr/local/go/bin GOROOT=/usr/local/go GOPATH=/root/go

# Копируем наш исходный main.go внутрь контейнера, в папку go/src/dumb
ADD main.go go/src/dumb

# Компилируем и устанавливаем наш сервер
RUN go build dumb && go install dumb

# Открываем 80-й порт наружу
EXPOSE 80

# Запускаем наш сервер
CMD ./go/bin/dumb
Запуск сервера должен происходить не на этапе сборки, а на этапе запуска контейнера, поэтому мы используем директиву CMD. Теперь мы можем собрать контейнер с помощью Docker. Находясь в папке с Dockerfile, надо запустить в консоли:
$ docker build -t dumb .
-t dumb добавляет контейнеру тэг, по которому в дальнейшем мы будем к нему обращаться.

Контейнер соберется, и выведет в консоль все, что выдадут команды из Dockerfile при выполнении. Теперь убедимся, что все работает правильно, запустив контейнер локально. Нам надо связать порт в контейнере с портом на реальной машине.

$ docker run --rm -p 8080:80 -t dumb 
-p 8080:80 перенаправит запросы к 8080 порту нашей машины на 80 порт контейнера.
-t dumb - обращение к контейнеру по тэгу, который мы добавили при сборке.

Теперь, если зайти в браузер на localhost:8080, то мы увидим "Hello, I'm Web Server!". Именно то, что мы и хотели!

3. Отправка контейнера с решением в тестирующую систему

Теперь остался последний шаг - отправить пробное решение на проверку. Для этого надо залогиниться в Docker, проставить свой тэг и, собственно, отправить решение.

$ docker login stor.highloadcup.ru
Username: xxxhackerxxx@domain.com                             # мой email
Password: 12345678                                            # мой секретный ключ из личного кабинета
Login Succeeded                                               # на highloadcup.ru

$ docker tag dumb stor.highloadcup.ru/travels/disabled_cat    # тут disabled_cat - это мой личный
$ docker push stor.highloadcup.ru/travels/disabled_cat        # репозиторий со страницы с задачей
Теперь решение отправлено на проверку - заглянем на сайт и посмотрим результаты.

Наше решение одну за одной проходит стадии обстрела.

 

После окончания пробного обстрела мы видим его статистику.

 

 

Рейтинговый обстрел на пробном решении мы запускать, конечно, не будем. Его можно запускать только один раз за шесть часов. Резюмируем - теперь мы можем создавать решение, собирать контейнер с ним и отправлять его на проверку.

Это как раз все, что нужно, чтоб успешно начать! Далее можно добавлять в Dockerfile установку новых компонент (mysql, memcached, python, ruby...), реализовывать все типы запросов, улучшать производительность, балансировать нагрузку и все, что душе угодно!

Удачи!