Непрерывная интеграция и развертывание docker в gitlab ci

Введение

Просто настроить gitlab за nginx в режиме proxy_pass не представляет никакой сложности. Трудности возникают именно тогда, когда вы хотите использовать встроенный Container Registry. Я несколько раз подходил к этой задаче и каждый раз отступал, либо отказываясь от registry, либо от nginx proxy.

Без прокси неудобно, так как чаще всего, если у вас есть какая-та инфраструктура, а не одиночный сервер, смотреть напрямую в интернет у вас будет какой-то шлюз, а не сам сервис. Так удобно разграничивать доступ, контролировать и перенаправлять подключения, следить за сертификатами, балансировать нагрузку и т.д. В общем, я использую его всегда, когда это возможно и оправдано (почти всегда).

С registry не получалось корректно настроить работу за прокси. Я перепробовал все советы, что только нашел в интернете. Перечитал всю документацию, но так ничего не получилось. Команда docker login успешно отрабатывала на сервере, а вот запушить образ в реджистри gitlab никак не получалось. Получал ошибку.

Проблему я в итоге решил, когда залез во внутренности gitlab и разобрался, как там все устроено. Когда все понял, придумал рабочее решение, которым с вами делюсь.

Restoring Backups

GitLab also defines a rake task to restore a backup.

Before performing a restore make sure the container is stopped and removed to avoid container name conflicts.

docker stop registry gitlab && docker rm registry gitlab

Execute the rake task to restore a backup. Make sure you run the container in interactive mode .

docker run --name gitlab -it --rm  \
    sameersbn/gitlab:10.5.5 app:rake gitlab:backup:restore

The list of all available backups will be displayed in reverse chronological order. Select the backup you want to restore and continue.

To avoid user interaction in the restore operation, specify the timestamp of the backup using the argument to the rake task.

docker run --name gitlab -it --rm  \
    sameersbn/gitlab:10.5.5 app:rake gitlab:backup:restore BACKUP=1417624827

Upgrading from an existing GitLab installation

If you want enable this feature for an existing instance of GitLab you need to do the following steps.

Step 1: Update the docker image.

docker pull sameersbn/gitlab:10.5.5

Step 2: Stop and remove the currently running image

docker stop gitlab && docker rm gitlab

Step 3: Create a backup

docker run --name gitlab -it --rm  \
    sameersbn/gitlab:x.x.x app:rake gitlab:backup:create
  • Step 4: Create a certs folder
    Create an authentication certificate with .

  • Step 5: Create an registry instance

docker run --name registry -d \
--restart=always \
-v /srv/gitlab/shared/registry:/registry \
-v ./certs:/certs \
--env 'REGISTRY_LOG_LEVEL=info' \
--env 'REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/registry' \
--env 'REGISTRY_AUTH_TOKEN_REALM=http://gitlab.example.com/jwt/auth' \
--env 'REGISTRY_AUTH_TOKEN_SERVICE=container_registry' \
--env 'REGISTRY_AUTH_TOKEN_ISSUER=gitlab-issuer' \
--env 'REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE=/certs/registry-auth.crt' \
--env 'REGISTRY_STORAGE_DELETE_ENABLED=true' \
registry:2.4.1

Step 6: Start the image

docker run --name gitlab -d  \
-v /srv/gitlab/certs:/certs \
--env 'SSL_REGISTRY_CERT_PATH=/certs/registry.crt' \
--env 'SSL_REGISTRY_KEY_PATH=/certs/registry.key' \
--env 'GITLAB_REGISTRY_ENABLED=true' \
--env 'GITLAB_REGISTRY_HOST=registry.gitlab.example.com' \
--env 'GITLAB_REGISTRY_API_URL=http://registry:5000/' \
--env 'GITLAB_REGISTRY_CERT_PATH=/certs/registry-auth.crt' \
--env 'GITLAB_REGISTRY_KEY_PATH=/certs/registry-auth.key' \
--link registry:registry
sameersbn/gitlab:10.5.5

Delete images

You can delete images from your Container Registry in multiple ways.

cautionDeleting images is a destructive action and can’t be undone. To restore
a deleted image, you must rebuild and re-upload it.

noteAdministrators should review how to

the deleted images.

Delete images from within GitLab

To delete images from within GitLab:

  1. Navigate to your project’s or group’s Packages & Registries > Container Registry.
  2. From the Container Registry page, you can select what you want to delete,
    by either:

    • Deleting the entire repository, and all the tags it contains, by clicking
      the redTrash icon.
    • Navigating to the repository, and deleting tags individually or in bulk
      by clicking the redTrash icon next to the tag you want
      to delete.
  3. In the dialog box, click Remove tag.

Delete images using the API

If you want to automate the process of deleting images, GitLab provides an API. For more
information, see the following endpoints:

Delete images using GitLab CI/CD

cautionGitLab CI/CD doesn’t provide a built-in way to remove your images, but this example
uses a third-party tool called reg
that talks to the GitLab Registry API. You are responsible for your own actions.
For assistance with this tool, see
the issue queue for reg.

The following example defines two stages: , and . The
job builds the Docker image for the branch, and the
job deletes it. The executable is downloaded and used to
remove the image matching the
predefined CI/CD variable.

To use this example, change the variable to match your needs:

noteYou can download the latest release from
the releases page, then update
the code example by changing the and variables
defined in the job.

Change the default proxy headers

By default, when you specify Omnibus GitLab will set a few
NGINX proxy headers that are assumed to be sane in most environments.

For example, Omnibus GitLab will set:

if you have specified schema in the .

However, if you have a situation where your GitLab is in a more complex setup
like behind a reverse proxy, you will need to tweak the proxy headers in order
to avoid errors like or
.

This can be achieved by overriding the default headers, eg. specify
in :

 nginx'proxy_set_headers' = {
  "X-Forwarded-Proto" => "http",
  "CUSTOM_HEADER" => "VALUE"
 }

This way you can specify any header supported by NGINX you require.

Change visibility of the Container Registry

By default, the Container Registry is visible to everyone with access to the project.
You can, however, change the visibility of the Container Registry for a project.

See the
for more details about the permissions that this setting grants to users.

  1. Go to your project’s Settings > General page.
  2. Expand the section Visibility, project features, permissions.
  3. Under Container Registry, select an option from the dropdown:

    • Everyone With Access (Default): The Container Registry is visible to everyone with access
      to the project. If the project is public, the Container Registry is also public. If the project
      is internal or private, the Container Registry is also internal or private.

    • Only Project Members: The Container Registry is visible only to project members with
      Reporter role or higher. This is similar to the behavior of a private project with Container
      Registry visibility set to Everyone With Access.

  4. Select Save changes.

Configuring GitLab and Registry to run on separate nodes

By default, package assumes that both services are running on the same node.
In order to get GitLab and Registry to run on a separate nodes, separate configuration
is necessary for Registry and GitLab.

Configuring Registry

Below you will find configuration options you should set in ,
for Registry to run separately from GitLab:

  • , default . Needs to be reachable by web server (or LB).
  • , default . Specifies the endpoint to use to perform authentication, usually the GitLab URL.
    This endpoint needs to be reachable by user.
  • , . A random piece of data used to sign state that may be stored with the client to protect against tampering.
  • , default . Contents of the key that GitLab uses to sign the tokens. They key gets created on the Registry server, but it won’t be used there.
  • , default . This is the path where contents will be written to disk.
  • , default . Contents of the certificate that GitLab uses to sign the tokens.
  • , default . Path to certificate. This is the path where
    contents will be written to disk.
  • , default . Configure whether health checks on the configured storage driver are enabled.
  • , . This setting needs to be set the same between Registry and GitLab.

Configuring GitLab

Below you will find configuration options you should set in ,
for GitLab to run separately from Registry:

  • , must be set to . This setting will
    signal to GitLab that it should allow Registry API requests.
  • , default . This is the Registry URL used internally that users do not need to interact with, with scheme.
  • , eg. . Registry endpoint without the scheme, the address that gets shown to the end user.
  • . Registry endpoint port, visible to the end user.
  • must match the issuer in the Registry configuration.
  • , path to the key that matches the certificate on the
    Registry side.
  • , contents of the key that GitLab uses to sign the tokens.

Change the visibility of the Container Registry

This controls who can view the Container Registry.

Attribute Type Required Description
integer/string yes The ID or accessible by the authenticated user.
string no The desired visibility of the Container Registry. One of (default), , or .

Descriptions of the possible values for :

  • enabled (Default): The Container Registry is visible to everyone with access to the project.
    If the project is public, the Container Registry is also public. If the project is internal or
    private, the Container Registry is also internal or private.

  • private: The Container Registry is visible only to project members with Reporter role or
    higher. This is similar to the behavior of a private project with Container Registry visibility set
    to enabled.

  • disabled: The Container Registry is disabled.

See the
for more details about the permissions that this setting grants to users.

Example response:

Общие параметры обратного прокси Nginx

Обслуживание контента через HTTPS в наши дни стало стандартом. В этом разделе мы приведем пример конфигурации обратного прокси https Nginx, включая рекомендуемые параметры прокси-сервера Nginx и заголовки.

  location/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version  1.1;
    proxy_cache_bypass  $http_upgrade;

    proxy_set_header Upgrade           $http_upgrade;
    proxy_set_header Connection        "upgrade";
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host  $host;
    proxy_set_header X-Forwarded-Port  $server_port;
  }

  • proxy_http_version 1.1- Определяет версию протокола HTTP для прокси, по умолчанию она установлена на 1.0. Для веб-сокетов и соединений keepalive вам необходимо использовать версию 1.1.
  • proxy_cache_bypass $http_upgrade – Устанавливает условия, при которых ответ не будет взят из кэша.
  • Upgrade $http_upgrade и Connection “upgrade”- эти поля заголовка обязательны, если ваше приложение использует веб-сокеты.
  • Host $host- Переменная $host содержит в следующем порядке приоритета имя хоста из строки запроса или имя хоста из поля заголовка запроса «Host», или имя сервера, соответствующее запросу.
  • X-Real-IP $remote_addr – Перенаправляет удаленный IP-адрес реального посетителя на прокси-сервер.
  • X-Forwarded-For $proxy_add_x_forwarded_for – список, содержащий IP-адреса каждого сервера, через который был проксирован клиент.
  • X-Forwarded-Proto $scheme – При использовании внутри блока HTTPS каждое перенаправление HTTP с прокси-сервера будет переписано в HTTPS.
  • X-Forwarded-Host $host – Определяет исходный хост, запрошенный клиентом.
  • X-Forwarded-Port $server_port – Определяет исходный порт, запрошенный клиентом.

Если у вас нет действующего сертификата SSL/TLS, вы можете использовать  бесплатный SSL-сертификат Let’s Encrypt на вашем Ubuntu 18.04, CentOS 7 или Debian.

Enabling/Disabling nginx_status

By default you will have an NGINX health-check endpoint configured at to monitor your NGINX server status.

The following information will be displayed

  • Active connections – Open connections in total.
  • 3 figures are shown.
    • All accepted connections.
    • All handled connections.
    • Total number of handled requests.
  • Reading: NGINX reads request headers
  • Writing: NGINX reads request bodies, processes requests, or writes responses to a client
  • Waiting: Keep-alive connections. This number depends on the keepalive-timeout.

Configuration options

Edit :

If you don’t find this service useful for your current infrastructure you can disable it with:

Make sure you run for the changes to take effect.

Warning

To ensure that user uploads are accessible your NGINX user (usually )
should be added to the group. This can be done using the following command:

Service-specific NGINX settings

Users can configure NGINX settings differently for different services via
. Settings for the GitLab Rails application can be configured using the
keys. There are similar keys for other services like
, and . All the configurations
available for are also available for these settings and
share the same default values as GitLab NGINX.

If modifying via , users have to configure NGINX setting for each
service separately. Settings given via WILL NOT be replicated to
service specific NGINX configuration (as or
, etc.). For example, to configure HTTP to HTTPS
redirection for GitLab, Mattermost and Registry, the following settings should
be added to

noteModifying NGINX configuration should be done with care as incorrect
or incompatible configuration may yield to unavailability of service.

Основы работы с Docker Engine

Базовые принципы:

Docker представляет из себя дополнительный уровень абстракции; систему, автоматизирующую виртуализацию на уровне операционной системы.

Основные преимущества использования Docker:

  • Изолированность отдельных кластеров друг от друга;
  • Один и тот же Docker-контейнер может работать без изменений на множестве различных машин (с разными конфигурациями);
  • Docker оптимизирован для развертывания приложений (application-centric);
  • Автоматизация сборки приложений;
  • Повторное использование одних и тех же компонентов;
  • Open Source реестр готовых контейнеров (Docker Hub);
  • Единый API для автоматизации пользовательской настройки контейнера и его разворачивания.

Далее расмотрим основные команды, которые нам понадобятся для создания кластера:

$ docker run

По сути основная команда, выполняющая запуск нового контейнера.

Основные параметры:

  1. —name: UUID идентификатор: уникальное имя контейнера;
  2. —volume (-v): том, связанный с контейнером: задается в форме абсолютного пути к директории;
  3. —env (-e): переменная окружения: позволяет дополнительного настраивать запускаемый контейнер;
  4. —publish (-p): настройка определенных портов, необходимых для работы контейнера (например, 80 для http, 443 для https).

$ docker rm container-name

Удаление определенного контейнера.

Внимание: прежде, чем удалить контейнер, его необходимо остановить (docker stop)!

Более подробно разобраться в работе каждой команды вы можете в официальной документации

В этой статье я обратил внимание только на основные команды, необходимые для успешного начала работы с Docker

Конкретные примеры использования вы увидите в этой статье немного далее.

Docker, GitLab, бесплатные SSL-сертификаты и другие плюшки современной веб-разработки +33

  • 15.12.16 12:31


Kyborg2011

#317636

Хабрахабр


Tutorial

15600

Системы управления версиями, Open source, Системное программирование, Разработка под Linux, Разработка веб-сайтов

И снова здравствуйте! Почти пять лет уже не писал здесь новых статей, хотя, если честно, всегда знал, что рано или поздно начну это делать снова. Не знаю как вам, а мне все таки это дело всегда казалось довольно увлекательным.

Начинать написание нового материала после столь продолжительного отдыха от этого дела — труднее всего. Но раз цель поставлена — надо идти до конца. Начну немного издалека.

Всю свою сознательную жизнь основным родом моей деятельности была и остается по сей день веб-разработка. Именно поэтому, сознаюсь сразу, что данный материал стоит воспринимать именно как попытка построения Docker-кластера от системного администратора любителя, но никак не профессионала. В этой статье я не могу претендовать на экспертное мнение в кластеризации и даже, более того, сам же хочу проверить достоверность собственного опыта.

Под хабракатом вы найдете Quick Start по использованию Docker на уровне, необходимом для решения конкретных задач, обозначенных ниже, без углубления в «дебри» виртуализации и прочих сопутствующих тем. Если вы до сих пор хотите начать успешно использовать эту современную технологию, тем самым значительно упростив целый ряд процессов: от разработки веб-продуктов и до разворачивания и переноса оных под какое-либо современное оборудование — прошу под кат!

Доставка

Тесты пройдены, опубликуем наш образ в Docker Registry.

Логин/пароль можно передать с использованием .

Теперь образ отображается на GitLab.

И другие разработчики могут скачать его из Docker Registry. На вкладке Environments все наши окружения доступны для просмотра:

Выводы

В этой серии статей рассмотрены общие подходы к непрерывной интеграции. Автоматизация сборки, тестирования и доставки вашего приложения на платформах InterSystems возможна и легко реализуема.

Применение технологий контейнеризации поможет оптимизировать процессы разработки и развёртывания приложений. Устранение несоответствий между окружениями позволяет упростить тестирование и отладку. Оркестрация позволяет создавать масштабируемые приложения.

Container Registry visibility permissions

The ability to view the Container Registry and pull images is controlled by the Container Registry’s
visibility permissions. You can change this through the
or the .
Other permissions
such as updating the Container Registry, pushing or deleting images, and so on are not affected by
this setting. However, disabling the Container Registry disables all Container Registry operations.

    Anonymous(Everyone on internet) Guest Reporter, Developer, Maintainer, Owner
Public project with Container Registry visibility set to Everyone With Access (UI) or (API) View Container Registry and pull images Yes Yes Yes
Public project with Container Registry visibility set to Only Project Members (UI) or (API) View Container Registry and pull images No No Yes
Internal project with Container Registry visibility set to Everyone With Access (UI) or (API) View Container Registry and pull images No Yes Yes
Internal project with Container Registry visibility set to Only Project Members (UI) or (API) View Container Registry and pull images No No Yes
Private project with Container Registry visibility set to Everyone With Access (UI) or (API) View Container Registry and pull images No No Yes
Private project with Container Registry visibility set to Only Project Members (UI) or (API) View Container Registry and pull images No No Yes
Any project with Container Registry All operations on Container Registry No No No

Этап 4. Завершаем настройку docker-compose.yml для nginx и certbot

В добавляем

restart: unless-stopped
# Перезапустит контейнер в непредвиденных ситуациях
command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''
# Перезапустит nginx контейнер каждые 6 часов и подгрузит новые сертификаты, если есть

В добавляем

restart: unless-stopped
entrypoint:  "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
# Проверяет каждые 12 часов, нужны ли новые сертификаты

Результат

version: '3.7'

services:
  nginx:
    container_name: nginx
    image: nginx:latest
    restart: unless-stopped
    networks:
      nginx_net:
    volumes:
      - ./data/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    ports:
      - 80:80
      - 443:443
    command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''

  certbot:
    container_name: certbot
    image: certbot/certbot
    restart: unless-stopped #+++
    networks:
      nginx_net:
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

networks:
  nginx_net:
    name: nginx_net

List registry repositories

Within a project

Get a list of registry repositories in a project.

Attribute Type Required Description
integer/string yes The ID or accessible by the authenticated user.
boolean no If the parameter is included as true, each repository includes an array of in the response.
boolean no If the parameter is included as true, each repository includes in the response (Introduced in GitLab 13.1).

Example response:

Within a group

Get a list of registry repositories in a group.

Attribute Type Required Description
integer/string yes The ID or accessible by the authenticated user.
boolean no If the parameter is included as true, each repository includes an array of in the response.
boolean no If the parameter is included as true, each repository includes in the response (Introduced in GitLab 13.1).

Example response:

Настройка виртуальных хостов

Проблема: определенная сложности реализации кластера с использованием виртуальных хостов в различных контейнерах заключается в том, что один порт может «прослушиваться» только одним контейнером (настраивается через —publish). Получается, что по-умолчанию мы можем создать только один контейнер, который будет отвечать на запросы к серверу через порт 80 и/или 443 (http и https протоколы, соответственно).

Решение: в принципе, довольно очевидно использовать для решения этой проблемы Reverse Proxy, инкапсулированный в один контейнер, который и будет «прослушивать» порты 80 и 443. Функционал данного контейнера будет заключаться в автоматическом перенаправлении запросов, в соответствии с используемыми виртуальными хостами.

Такой контейнер существует в открытом доступе в Docker Hub — nginx-proxy.

Кроме решения проблемы с виртуальными хостами, он по-умолчанию поддерживает работу с SSL-сертификатами, что позволяет развернуть поддержку защищенного HTTPS-доступа к сайту.

Прежде, чем запустить данный Reverse Proxy контейнер, давайте получим SSL-сертификаты для доменов, которые мы хотим использовать в качестве виртуальных хостов.

Получение бесплатного SSL-сертификата

Для получения SSL-сертификата будем использовать бесплатный сервис letsencrypt. Для этого на предыдущих этапах мы уже установили утилиту certbot. Я не буду останавливаться на подробностях использования этой утилиты (это все есть в официальной документации).

Приведу лишь готовую команду для автоматического получения бесплатного SSL-сертификата для вашего домена:

— данные параметры необходимы для того, чтобы certbot с одной стороны отработал в фоновом режиме, а с другой — сгенерировал сертификат без конкретной привязки к определенному веб-серверу.

В результате успешного выполнения данной команды будут сгенерированы два файла:

Для корректной работы nginx-proxy нам необходимо поместить все файлы сертификатов в одну директорию, при этом используя для каждого доменного имени по два файла в формате: yourdomain.com.crt (файл сертификата) и yourdomain.com.key (приватный ключ).

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

На расширение .pem не стоит обращать особого внимания — суть файлов от этого не меняется.

Аналогичным образом мы можем получить сертификаты для любых, принадлежащих нам, доменных имен и далее использовать их в качестве виртуальных хостов. Единственное требование заключается в том, что A-записи этих доменных имен должны быть направленны на внешний IP-адрес сервера, на котором вы и выполняете

Сгенерировав сертификаты для каждого домена, мы готовы к запуску nginx-proxy контейнера.

Рассмотрим эту команду более подробно:

  1. — привязываем к контейнеру порты 80 и 443. При чем 80-ый порт сервера отвечает 80-ому порту внутри контейнера и аналогично с портом 443. В формате PORT:PORT2 выполняется создание соответствий между реальным портом всей машины и портами внутри отдельного виртуального контейнера.
  2. — первый volume, необходимый для настройки данного контейнера. Здесь мы выполняем связь стандартной директории /etc/nginx/certs внутри самого контейнера с директорией, в которую мы вручную поместили символьные ссылки на файлы сертификатов и приватных ключей для наших доменов (на предыдущем этапе).
  3. — идентификатор контейнера внутри Docker Hub. Docker Engine автоматически скачает изображение данного контейнера, если оно еще не было загруженно ранее.

Вот и все — первый контейнер запущен! И этим контейнером является Reverse Proxy, через который мы сможем далее задать любому контейнеру-приложению VIRTUAL_HOST.

Упростите ваш рабочий процесс

Работа с GitLab Container Registry проста и безопасна. Вот несколько примеров того, как использование GitLab Container Registry может упростить процесс разработки и развертывания ПО:

  • Проводите сборку образов Docker с помощью GitLab CI с последующим их хранением в GitLab Container Registry.
  • Привязывайте образы к веткам, тегам исходного кода или используйте любой другой способ, подходящий для вашего процесса разработки. Созданные образы можно быстро и легко сохранить на GitLab.
  • Используйте собственные образы сборок, хранящиеся в вашем реестре, для тестирования приложений.
  • Пусть остальные участники команды также участвуют в разработке образов. Это не потребует от них дополнительных усилий, так как используется тот же рабочий процесс, к которому они привыкли. GitLab CI позволяет проводить автоматическую сборку образов, унаследованных от ваших, что в свою очередь позволяет с легкостью добавлять фиксы и новые фичи в базовый образ, используемый вашей командой.
  • Настройте CaaS на использование образов напрямую из GitLab Container Registry, получив таким образом процесс непрерывного развертывания кода. Это позволит проводить автоматическое развертывание приложений в облако (Docker Cloud, Docker Swarm, Kubernetes и т. п.) каждый раз, когда вы собираете или тестируете образ.

Подводя итоги

GitLab Container Registry — последнее на данный момент дополнение к встроенному набору инструментов для цикла разработки ПО GitLab. Это дополнение доступно начиная с версии GitLab 8.8. С использованием этой функциональности проводить тестирование и развертывание образов Docker стало гораздо проще. GitLab Container Registry идет в комплекте с GitLab CE и GitLab EE без какой-либо доплаты и устанавливается поверх той же инфраструктуры, что настроена у вас для GitLab.

Container Registry доступен на GitLab.com, он абсолютно бесплатен, и вы можете начать пользоваться им прямо сейчас!

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Техноарена
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: