Готовим образ Docker для статического сайта (nginx, ssl)

 Публичный пост
11 июля 2022  1771

Постановки задачи:

Имеется статический веб-сайт, никакой динамики, максимум js. Необходимо создать из него Docker-образ, затем загрузить его на Production-сервер и подключить SSL.

Вводные:

На сервере уже имеется рабочий nginx для других не-Docker сайтов.
В данной статье мы рассмотрим подключение сайта к субдомену (домен третьего уровня). Домен делегирован на Cloudflare. Да, там есть pages и он прекрасно справляется с serve статических сайтов, но данная статья описывает путь именно через Docker.
Начал изучать Docker и решил написать данную заметку, возможно пригодится в освоении другим специалистам.

  1. Добавляем A-запись, в данном случае endocker.endlessnights.ru, который будет смотреть на 185.255.132.54 .

  1. Создаем в каталоге проекта рядом с index.html файл Dockerfile со следующим содержимым:
FROM nginx
COPY ./ /data/www/ # копируем всё содержимое сайта в /data/www/
COPY ensite.conf /etc/nginx/conf.d/ensite.conf # копируем файл конфигурации nginx сайта
COPY nginx.conf /etc/nginx/nginx.conf # копируем файл конфигурации nginx по шаблону

Создаем файл конфигурации сайта endocker.conf:

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /data/www;
        index  index.html;
    }
}

Создаем конфигурационный файл для nginx по шаблону + + инклюдим ссылку на файл конфигурации nginx сайта:

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/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;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;
    include /etc/nginx/conf.d/ensite.conf;
}

Docker у меня работает на MBA M1 (arm), а сервер в облаке на linux, поэтому нам нужно изменить целевую платформу для Docker-образа на linux/amd64, для этого будем использовать Docker buildx:

docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 --push -t legeminus/en2 # legeminus/en2 - тег

В таком случае Docker-образ сразу запушится в Docker Hub.

После успешной сборки заходим на наш облачный сервер и загружаем наш образ для его дальнейшего запуска (Я использую TMUX, так как это учебная практика, но на production лучше воспользоваться Systemd):

docker pull legeminus/en3
docker run -p 800:80 legeminus/en3

Тут 80 порт в контейнере будем слушать 800 порт снаружи, то есть site:800 > docker.site:80 грубо говоря.

Готовим nginx на стороне Linux-сервера.
Данный сервис у меня уже запущен и поэтому запуск двух биндов на 80 порту невозможен.

Идем в /etc/nginx/sites-available/ и создаем новый файл без расширения endocker :

server {
    listen 80;
    server_name endocker.endlessnights.ru;
    location / {
        proxy_pass http://127.0.01:800;
}

Создаем символическую ссылку:

sudo ln -s /etc/nginx/sites-available/endocker /etc/nginx/sites-enabled/

Также, проверяем, что файл конфигурации nginx корректен и перезапускаем nginx:

sudo nginx -t
sudo service nginx restart

Наконец запускаем certbot для получения SSL:

certbot

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx

Which names would you like to activate HTTPS for?

Выбираем наше доменное имя, которое указывали во "внешнем" конфигурационном файле nginx нашего проекта, в данном случае endocker.endlessnights.ru .

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.


1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.

Здесь выбираем цифру 2, чтобы все запросы на http редиректились на https.

Итоговый результат файл конфигурации nginx endocker.nginx:

server_name endocker.endlessnights.ru;
    location / {
        proxy_pass http://127.0.01:800;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/endocker.endlessnights.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/endocker.endlessnights.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}server {
    if ($host = endocker.endlessnights.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name endocker.endlessnights.ru;
    return 404; # managed by Certbot

По итогу схема грубо говоря выглядит так:

docker.localhost:80 > localhost:800 + nginx (800>80>443) =example.com:443 (редирект с 80 на 443)

Сайт из Docker-контейнере размещен тут: https://endocker.endlessnights.ru
Основной сайт с идентичным содержимым тут: https://endlessnights.ru , только он на Cloudlfare Pages.

Можно полагать, что, если отсутствуют запущенный имеющийся nginx на сервере Linux, всё сервить можно и внутри Docker-контейнера.

Связанные посты
19 комментариев 👇
Вастрик Блогер, питонист, мизантроп 11 июля 2022

Плюсанул в карму. Хабр торт!

  Развернуть 1 комментарий

@vas3k, Да, я на Хабре как-то публиковал статью, оказалась не очень качественная и словил много минусов. Буду стараться здесь лучше :)

  Развернуть 1 комментарий
Nikita Galaiko founding software engineer 11 июля 2022

статья хорошая, но как будто из 2015

сегодня есть куча однокнопочных сервисов, на которых можно хостить статику (небольшую даже бесплатно)

например:

  • github pages
  • cloudflare pages
  • netlify
  • vercel
  • digital ocean app platform
  Развернуть 1 комментарий

@ngalaiko, Да, вы правы. Я как раз упомянул в статье Cloudflare Pages.

  Развернуть 1 комментарий

Если выкинуть из статьи ненужное пердольство с nginx/certbot и взять вместо этого Caddy, то получится намного более короткая статья

  Развернуть 1 комментарий

https://github.com/umputun/reproxy неплохой еще

  Развернуть 1 комментарий

Чтоб не быть голословным, вот как выглядит https reverse proxy используя caddy

example.com {
  reverse_proxy localhost:1245
}

Оно само сходит, получит https сертификат и будет его обновлять без вашего участия.

  Развернуть 1 комментарий

@Kirk, так сайт из докера все равно нджинксом сервится.

  Развернуть 1 комментарий

@dmitryv, ну так пусть и в докере будет кадди, отдавать статику там так же просто в одну строчку)

  Развернуть 1 комментарий

@Kirk, smtps оно тоже отдаст?

  Развернуть 1 комментарий

@dmitryv, нет, кадди только http обслуживает

  Развернуть 1 комментарий

@Kirk, ну штош, значит придётся пердолить серты в нджинксе

  Развернуть 1 комментарий

@dmitryv, зачем ваще в 2к22 smtp дрочильня?

  Развернуть 1 комментарий

@Kirk, а как ещё дижитал номаду завести электронную почту

  Развернуть 1 комментарий

Спасибо за статью, так держать! Но это решение для решенных еще вчера вопросов. Прокси уже несколько лет как не лежат в контейнере, и вынесены в другие слои выше, а докер сегодня это про скучную установку зависимостей, копирование файла и запуск скрипта/бинаря. Давайте про кубернетисы лучше!

  Развернуть 1 комментарий

@AndreiChenchik, Спасибо за отзыв. Будет про кубернетесы, будет!

  Развернуть 1 комментарий

Привет! Погляди https://github.com/TopTuK/pmi.landing - кажется, что чутка проще вышло.

Reverse proxy: https://github.com/nginx-proxy/nginx-proxy

Спасибо.

  Развернуть 1 комментарий

COPY ./ /data/www/ # копируем всё содержимое сайта в /data/www/

Dockerfile получается доступным для скачивания и прочие конфигурации

мне, кажется, лучше Dockerfile вытащить на уровень выше и папку с конфигами отдельно

  Развернуть 1 комментарий
Rail Hamdeew Программист 19 июля 2022

Еще бы неплохо добавить файл .dockerignore чтобы при сборке в образ не попали лишние файлы (например директория .git).

  Развернуть 1 комментарий

😎

Автор поста открыл его для большого интернета, но комментирование и движухи доступны только участникам Клуба

Что вообще здесь происходит?


Войти  или  Вступить в Клуб