Виртуальное приватное облако: работа с CoreOS и RancherOS

в 9:22, , рубрики: coreos, docker, docker swarm, etcd, fleet, howto, linux, rancheros, VPC, Блог компании Селектел, виртуальное приватное облако, контейнеры, Настройка Linux, облачные сервисы, системное администрирование

CoreOS

Недавно мы добавили в сервис «Виртуальное приватное облако» новый образ с операционной системой RancherOS и обновили образ CoreOS.

Эти операционные системы будут интересны пользователям, которым необходим инструмент для простого управления большим количеством приложений в контейнерах и использования различных систем кластеризации контейнеров — Kubernetes, Docker Swarm, Apache Mesos и других.

CoreOS и RancherOS отличаются от большинства популярных дистрибутивов Linux: они содержат лишь минимальный набор программного обеспечения для запуска приложений внутри контейнеров. В этих системах отсутствуют пакетные менеджеры и интерпретаторы различных языков программирования (Python, Ruby и других.).

Ниже мы рассмотрим специфику CoreOS и RancherOS, разберём особенности конфигурации сервисов etcd и fleet, а также приведём пример настройки кластера Docker Swarm на базе RancherOS.

CoreOS

Дистрибутив CoreOS разработан специально для быстрого и массового развертывания большого количества пользовательских приложений в изолированных контейнерах.

CoreOS содержит предустановленные инструменты для удобного управления множеством нод и контейнеров в кластере:

  • etcd — распределенное хранилище данных, которое используется нодами для получения информации о доступных сервисах в кластере, конфигурационным и временным файлам;
  • Docker — основная платформа для доставки и запуска приложений внутри контейнеров;
  • rkt — альтернатива Docker, разработанная создателями CoreOS;
  • fleet — распределенная система инициализации, позволяющая управлять множеством systemd-инстансов на отдельных нодах кластера и производить запуск необходимых сервисов на менее загруженных нодах.

Конфигурация

Основной метод изменения системных параметров ― это описание необходимых настроек при помощи декларативного языка YAML в конфигурационном файле cloud-config.yml. Этот файл используется утилитой Cloud-init для настройки системы.

Обратите внимание, что в CoreOS используется собственная реализация Cloud-init, конфигурационные файлы которой лишь частично совместимы с настройками для более распространенной версии Launchpad-cloud-init, используемой в большинстве обычных дистрибутивов для конфигурации системы в различных облачных сервисах, в том числе и в сервисе «Виртуальное приватное облако».

Приведем пример файла cloud-config.yml:

#cloud-config

# Set hostname
hostname: "node01"

# Set ntp servers
write_files:
    - path: /etc/systemd/timesyncd.conf
      content: |
        [Time]
        NTP=0.ru.pool.ntp.org 1.ru.pool.ntp.org

coreos:
    units:
    # Configure static network on eth0 interface
    - name: iface-eth0.network
      runtime: true
      content: |
        [Match]
        Name=eth0

        [Network]
        DNS=188.93.16.19
	DNS=188.93.17.19
	DNS=109.234.159.91
        Address=10.11.12.13/24
        Gateway=10.11.12.1

    # Change standard SSH port
    - name: sshd.socket
      command: restart
      runtime: true
      content: |
        [Socket]
        ListenStream=2345
        FreeBind=true
        Accept=yes

При помощи заданных параметров утилита Cloud-init сменит хостнейм и стандартный порт 22 для сервиса SSH, настроит статическую сеть на интерфейсе eth0 и добавит ntp-серверы для синхронизации времени.

Подробнее о структуре конфигурационного файла cloud-config.yml можно прочитать в официальной документации.

При создании сервера из готового образа CoreOS в сервисе «Виртуальное приватное облако» вам не потребуется производить базовую настройку системы при помощи cloud-config.yml. Мы уже внесли необходимые изменения в образ, при помощи которых автоматически производится стандартная конфигурация сервера, в том числе настройка сетевых интерфейсов.

Доступ по SSH к серверу под управлением CoreOS изначально будет возможен только по ключу, указанному при создании сервера. Указанный ключ будет добавлен для пользователя core.

В дополнение к собственной версии cloud-init создатели CoreOS разработали утилиту Ignition. Этот инструмент дублирует возможности cloud-init и еще позволяет производить низкоуровневые настройки системы, — например, изменение таблиц разделов дисков и форматирование файловых систем. Это возможно благодаря тому, что Ignition начинает работать во время ранних этапов загрузки системы, в процессе запуска initramfs.

В Ignition для конфигурационных файлов используется формат JSON.

Ниже представлен пример файла, при помощи которого Ignition отформатирует корневой раздел в Btrfs, и настроит систему с параметрами, аналогичными примеру выше для Cloud-init:

{
    "ignition": { "version": "2.0.0" },
    "storage": {
        "filesystems": [{
            "mount": {
                "device": "/dev/disk/by-label/ROOT",
                "format": "btrfs",
                "create": {
                    "force": true,
                    "options": [ "--label=ROOT" ]
                }
            }
        }],
        "files": [{
            "filesystem": "root",
            "path": "/etc/systemd/timesyncd.conf",
            "mode": 420,
            "contents": { "source": "data:,%5BTime%5D%0ANTP=0.ru.pool.ntp.org%201.ru.pool.ntp.org%0A" }
            },
            {
            "filesystem": "root",
            "path": "/etc/hostname",
            "mode": 420,
            "contents": { "source": "data:,node01" }
            }
        ]
    },
    "networkd": {
        "units": [{
            "name": "iface-eth0.network",
            "contents": "[Match]nName=eth0nn[Network]nDNS=188.93.16.19nDNS=188.93.17.19nDNS=109.234.159.91nAddress=10.11.12.13/24nGateway=10.11.12.1"
        }]
    },
    "systemd": {
        "units": [{
            "name": "sshd.socket",
            "command": "restart",
            "runtime": true,
            "contents": "[Socket]nListenStream=2345nFreeBind=truenAccept=yes"
        }]
    }
}

Подробнее о всех возможностях Ignition можно узнать в официальной документации.

Пример настройки кластера CoreOS

Для этого примера нам потребуется заранее создать три сервера под управлением CoreOS в панели управления VPC со следующими параметрами:

CoreOS

После создания серверов потребуется discovery-url для сервиса etcd. Для этого мы можем воспользоваться публичным бесплатным сервисом discovery.etcd.io. Запустим на хосте node00 следующую команду:

core@node00 ~ $ curl -w "n" 'https://discovery.etcd.io/new?size=3'
https://discovery.etcd.io/ec42cfef0450bd8a99090ee3d3294493

Добавим полученный URL и дополнительные параметры etcd в файл /usr/share/oem/cloud-config.yml на каждом сервере:

coreos:
    etcd2:
        discovery: https://discovery.etcd.io/ec42cfef0450bd8a99090ee3d3294493
        advertise-client-urls: http://192.168.0.10:2379,http://192.168.0.10:4001
        initial-advertise-peer-urls: http://192.168.0.10:2380
        listen-client-urls: http://0.0.0.0:2379,http://0.0.0.0:4001
        listen-peer-urls: http://192.168.0.10:2380

    units:
        - name: etcd2.service
          command: start

        - name: fleet.service
          command: start

Параметры в разделе etcd2 обозначают URL-адреса в формате protocol://address:port, которые необходимы для анонсирования ноды и обмена данными с другими членами кластера.

Раздел units позволяет управлять юнитами systemd в системе; в нашем случае он запустит юниты etc2d.service и fleet.service.

Для серверов node01 и node02 вам будет необходимо добавить аналогичные изменения в cloud-config.yml, изменив ip-адрес с 192.168.0.10 на 192.168.0.11 для сервера node01, и на адрес 192.168.0.12 для сервера node02.

После добавления новых настроек запустим утилиту Cloud-init на всех трех серверах (либо можно просто перезапустить эти серверы):

core@node00 ~ $ sudo coreos-cloudinit --from-file /usr/share/oem/cloud-config.yml

Также проверим статус сервиса etcd:

core@node00 ~ $ etcdctl cluster-health
member 3aaf91fd1172594e is healthy: got healthy result from http://192.168.0.11:2379
member 8cf40790248e1dcd is healthy: got healthy result from http://192.168.0.12:2379
member 96b61f40c082cd0b is healthy: got healthy result from http://192.168.0.10:2379
cluster is healthy

Проверим доступность распределенного хранилища, добавим новую директорию (в терминологии etcd — ключ) со значением на сервере node00:

core@node00 ~ $ etcdctl set /common/cluster-name selectel-test-cluster
selectel-test-cluster

Затем убедимся, что наш новый ключ со значением доступен с любой другой ноды кластера:

core@node01 ~ $ etcdctl get common/cluster-name
selectel-test-cluster

Отлично, etcd функционирует, и мы можем приступать к использованию сервиса fleet.

В нашем примере мы будем использовать fleet для запуска Nginx в изолированном контейнере Docker. Для начала создадим systemd-юнит /etc/systemd/system/nginx.service:

[Unit]
Description=Nginx Test Service

[Service]
EnvironmentFile=/etc/environment
ExecStartPre=/usr/bin/docker pull nginx
ExecStart=/usr/bin/docker run --rm --name nginx -p 80:80 nginx
ExecStop=/usr/bin/docker kill nginx

[X-Fleet]
Conflicts=nginx.service

Параметр Conflicts в секции X-Fleet запрещает запускать два и более сервиса Nginx на одной ноде, что позволит «размазать» нагрузку на кластер и повысить доступность сервиса. Дополнительные параметры X-Fleet, которые можно использовать внутри юнитов systemd описаны в документации fleet.

Остальные секции в файле являются стандартными для systemd, вы можете ознакомиться с ними например в небольшой вводной инструкции от CoreOS.

Запустим новый юнит nginx.service в нашем кластере при помощи fleetctl:

core@node00 ~ $ fleetctl start /etc/systemd/system/nginx.service
Unit nginx.service inactive
Unit nginx.service launched on 1ad018e0.../192.168.0.11

После этого мы можем увидеть наличие Nginx в списке юнитов на любой ноде кластера:

core@node02 ~ $ fleetctl list-units
UNIT		    MACHINE				        ACTIVE	SUB
nginx.service	1ad018e0.../192.168.0.11	active	running

core@node02 ~ $ fleetctl list-unit-files
UNIT		    HASH	DSTATE		STATE		TARGET
nginx.service	 0c112c1	launched	launched	1ad018e0.../192.168.0.11

Как мы видим, контейнер с Nginx запустился на сервере с ip-адресом 192.168.0.11, которому соответствует сервер node01.

Мы можем отключить или полностью удалить сервер node01, после чего fleet автоматически перенесет Nginx на другую доступную ноду.

Etcd сообщит о недоступности члена при проверке статуса кластера:

core@node00 ~ $ etcdctl cluster-health
failed to check the health of member 3aaf91fd1172594e on http://192.168.0.11:2379: Get http://192.168.0.11:2379/health: dial tcp 192.168.0.11:2379: i/o timeout
failed to check the health of member 3aaf91fd1172594e on http://192.168.0.11:4001: Get http://192.168.0.11:4001/health: dial tcp 192.168.0.11:4001: i/o timeout
member 3aaf91fd1172594e is unreachable: [http://192.168.0.11:2379 http://192.168.0.11:4001] are all unreachable
member 8cf40790248e1dcd is healthy: got healthy result from http://192.168.0.12:2379
member 96b61f40c082cd0b is healthy: got healthy result from http://192.168.0.10:2379
cluster is healthy

Но как мы видим, статус сервиса Nginx в норме, он запустился на другой рабочей ноде:

core@node00 ~ $ fleetctl list-units
UNIT		    MACHINE				        ACTIVE	SUB
nginx.service	4a1ff11c.../192.168.0.10	active	running

Мы разобрали элементарный пример конфигурации кластера etcd + Docker + fleet на базе CoreOS. В следующем разделе мы рассмотрим другой дистрибутив — RancherOS, который основан на CoreOS и также предназначен для активного использования контейнеров.

RancherOS

Дистрибутив RancherOS представляет собой форк CoreOS. Отличительная особенность этой системы заключается в том, что в контейнерах запускаются не только пользовательские приложения, но и все системные сервисы. Более того, в RancherOS Docker имеет PID = 1, поэтому запускается сразу после ядра системы.

Операционная система содержит два Docker-инстанса; один из них является системным, в нём запускаются контейнеры udev, acpid, syslog, ntp и другие базовые сервисы, необходимые для работы системы. Системный Docker-инстанс заменяет собой традиционные системы инициализации (systemd, Upstart, SysV), присутствующих в обычных дистрибутивах Linux.

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

Такое разделение обеспечивает защиту системных контейнеров от некорректных действий пользователя.

RancherOS

Так как Docker-инстанс в RancherOS представляет собой обычное Docker-окружение, то мы можем использовать стандартный набор команд Docker для управления как пользовательскими так и системными контейнерами.

# проверка версии Docker
[rancher@rancher-os ~]$ docker --version
Docker version 1.12.1, build 23cf638

# установка нового контейнера из Docker Hub
[rancher@rancher-os ~]$ docker run -d nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
…

Status: Downloaded newer image for nginx:latest

# просмотр списка запущенных контейнеров
[rancher@rancher-os ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
2ec5126e8dd5        nginx               "nginx -g 'daemon off"   About a minute ago   Up About a minute   80/tcp, 443/tcp     sad_fermat

Для доступа к системным контейнерам необходимо использовать команду system-docker вместе с утилитой sudo.

Просмотреть список запущенных базовых сервисов операционной системы можно с помощью команды:

[rancher@rancher-os ~]$ sudo system-docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
a566d0c7cc4c        rancher/os-docker:1.12.1    "/usr/bin/user-docker"   8 minutes ago       Up 8 minutes                            docker
8697a04e90a4        rancher/os-console:v0.6.1   "/usr/bin/ros entrypo"   8 minutes ago       Up 8 minutes                            console
c8c337282aaa        rancher/os-base:v0.6.1      "/usr/bin/ros entrypo"   8 minutes ago       Up 8 minutes                            network
1e55244fc99c        rancher/os-base:v0.6.1      "/usr/bin/ros entrypo"   8 minutes ago       Up 8 minutes                            ntp
fd6a03fdb28f        rancher/os-udev:v0.6.1      "/usr/bin/ros entrypo"   8 minutes ago       Up 8 minutes                            udev
7e9e50c28651        rancher/os-acpid:v0.6.1     "/usr/bin/ros entrypo"   8 minutes ago       Up 8 minutes                            acpid
5fec48b060f2        rancher/os-syslog:v0.6.1    "/usr/bin/ros entrypo"   8 minutes ago       Up 8 minutes                            syslog

Перезапуск системного контейнера осуществляется так:

[rancher@rancher-os ~]$ sudo system-docker restart ntp

Конфигурация

Основным инструментом автоматической конфигурации системы является собственная имплементация cloud-init, который был разделен на две отдельные утилиты: cloud-init-execute и cloud-init-save. По аналогии с другими реализациями cloud-init, версия RancherOS использует декларативный язык YAML, однако cloud-config.yml для другого дистрибутива не будет совместим с RancherOS.

Все возможные директивы конфигурационного файла описаны в официальной документации docs.rancher.com/os.

Системные сервисы используют параметры, полученные из файлов, в описанной ниже последовательности (каждый последующий файл из указанного списка перезаписывает предыдущие параметры, если они совпадают):

  • /usr/share/ros/os-config.yml — настройки системы по умолчанию;
  • /usr/share/ros/oem/oem-config.yml — в нашем случае это файл, позволяющий производить автоматическую конфигурацию статических настроек сети, в соответствии с параметрами из панели управления VPC;
  • YAML-файлы в директории /var/lib/rancher/conf/cloud-config.d/;
  • /var/lib/rancher/conf/cloud-config.yml — файл, хранящий значения, установленные утилитой ros, о которой мы расскажем далее;
  • параметры ядра, начинающиеся с ключевого слова «rancher»;
  • /var/lib/rancher/conf/metadata — метаданные из используемого облачного сервиса, добавленные утилитой cloud-init-save.

Вы можете изменять конфигурацию системы при при помощи утилиты ros, после чего новые параметры будут отображаться в файле /var/lib/rancher/conf/cloud-config.yml:

[rancher@rancher-os ~]$ sudo ros config set rancher.network.dns.nameservers "['188.93.16.19','188.93.17.19', '109.234.159.91']"

[rancher@rancher-os ~]$ sudo cat /var/lib/rancher/conf/cloud-config.yml
rancher:
    network:
        dns:
            nameservers:
              - 188.93.16.19
              - 188.93.17.19
              - 109.234.159.91

Помимо изменения конфигурации системных сервисов утилита ros также используется для управления версиями операционной системы, версиями Docker, настройкой TLS и SELinux.

При использовании готового образа RancherOS в сервисе «Виртуальное приватное облако» вам нет необходимости настраивать базовые параметры системы (конфигурацию сетевых интерфейсов и т.п.)

После установки сервер будет доступен по выбранному вами IP-адресу в панели управления VPC. Вы сможете подключиться к серверу, используя протокол SSH, однако аутентификация изначально будет возможна только по ключу, указанному при создании сервера. Этот ключ будет добавлен для пользователя rancher.

Управление версиями

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

[rancher@rancher-os ~]$ sudo ros os version
v0.6.1

А для проверки всех доступных релизов:

[rancher@rancher-os ~]$ sudo ros os list
rancher/os:v0.4.0 remote
rancher/os:v0.4.1 remote
rancher/os:v0.4.2 remote
rancher/os:v0.4.3 remote
rancher/os:v0.4.4 remote
rancher/os:v0.4.5 remote
rancher/os:v0.5.0 remote
rancher/os:v0.6.0 remote
rancher/os:v0.6.1 remote

Вы можете установить последнюю стабильную версию системы при помощи команды sudo ros os upgrade, либо выбрать требуемую версию, указав параметр -i:

[rancher@rancher-os ~]$ sudo ros os upgrade -i rancher/os:v0.5.0

Так как указанная нами версия 0.5.0 старше, чем установленная 0.6.1, то вместо обновления системы произойдет даунгрейд

Управлять версией Docker не менее просто. Приведём несколько примеров:

#просмотр доступных версий
[rancher@rancher-os ~]$ sudo ros engine list
disabled docker-1.10.3
disabled docker-1.11.2
current  docker-1.12.1

#установка версии 1.11.2
[rancher@rancher-os ~]$ sudo ros engine switch docker-1.11.2
INFO[0000] Project [os]: Starting project
INFO[0000] [0/19] [docker]: Starting
…
INFO[0010] Recreating docker
INFO[0010] [1/19] [docker]: Started

Кластер Docker Swarm на RancherOS

В Docker 1.12 появились интегрированные инструменты для управления кластером контейнеров Docker Swarm, благодаря чему настроить готовый кластер стало гораздо проще.

В нашем примере мы будем использовать готовый образ RancherOS в сервисе «Виртуальное приватное облако». В нём используется Docker версии 1.12.1.

Нам понадобится создать три сервера со следующими параметрами:

pr-3225-02

Для начала нам необходимо инициализировать создание кластера Docker Swarm, для этого на ноде manager0 выполним следующую команду:

[rancher@manager0 ~]$ docker swarm init --advertise-addr 192.168.0.100
Swarm initialized: current node (8gf95qb6l61v5s6561qx5vfy6) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join 
        --token SWMTKN-1-0hlhela57gsvxzoaqol70n2b9wos6qlu3ukriry3pcxyb9j2k6-0n1if4hkvdvmrpbb7f3clx1yg 
        192.168.0.100:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Как вы можете заметить из появившейся подсказки, на нодах worker0 и worker1 вам необходимо выполнить команду:

docker swarm join 
    --token SWMTKN-1-0hlhela57gsvxzoaqol70n2b9wos6qlu3ukriry3pcxyb9j2k6-0n1if4hkvdvmrpbb7f3clx1yg 
    192.168.0.100:2377

Проверим статус нод кластера, для этого на manager0 запустим команду:

[rancher@manager0 ~]$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
366euocei01dw3ay66uwzb3pv    worker0   Ready   Active
67pt4fo5gi13cxkphtfzfbfbo    worker1   Ready   Active
8gf95qb6l61v5s6561qx5vfy6 *  manager0  Ready   Active        Leader

Docker Swarm готов к использованию!

По умолчанию новые сервисы будут запускаться на всех нодах кластера. Подкорректируем данные настройки таким образом, чтобы все сервисы запускались на нодах worker0 и worker1, а нода manager0 выполняла только управляющую роль.

Для внесения этих параметров изменим «доступность» ноды manager0:

[rancher@manager0 ~]$ docker node update --availability drain manager0

Запустим сервис с контейнером Nginx:

[rancher@manager0 ~]$ docker service create --name webserver --replicas 1 --publish 80:80 nginx

В ответ мы получим идентификатор сервиса:

av1qvj32mz8vwkwihf0lauiz8

Изначально количество реплик будет равно 0, а единственная задача, относящаяся к этому сервису, будет находится в статусе «Preparing»:

[rancher@manager0 ~]$ docker service ls
ID            NAME       REPLICAS  IMAGE  COMMAND
av1qvj32mz8v  webserver  0/1       nginx

[rancher@manager0 ~]$ docker service ps webserver
ID                         NAME         IMAGE  NODE     DESIRED STATE  CURRENT STATE                 ERROR
cjxkza3qzx5m73bgbls0r26m6  webserver.1  nginx  worker1  Running        Preparing about a minute ago

Через несколько секунд необходимый образ Nginx будет автоматически скачан из Docker Hub, после чего статус сервиса изменится на «Running»:

[rancher@manager0 ~]$ docker service ls
ID            NAME       REPLICAS  IMAGE  COMMAND
av1qvj32mz8v  webserver  1/1       nginx

[rancher@manager0 ~]$ docker service ps webserver
ID                         NAME         IMAGE  NODE     DESIRED STATE  CURRENT STATE          ERROR
cjxkza3qzx5m73bgbls0r26m6  webserver.1  nginx  worker1  Running        Running 8 minutes ago

Как мы видим, сервис запустился на ноде worker1 (значение в столбце «NODE»). Отключим эту ноду через панель управления VPC и снова проверим статус сервиса webserver:

[rancher@manager0 ~]$ docker service ps webserver
ID                         NAME             IMAGE  NODE     DESIRED STATE  CURRENT STATE             ERROR
5lwf4z54p74qtoopuilshw204  webserver.1      nginx  worker0  Running        Preparing 32 seconds ago
cjxkza3qzx5m73bgbls0r26m6   _ webserver.1  nginx  worker1  Shutdown       Running 16 minutes ago

Статус сервиса изменился на «Preparing», Docker Swarm пытается перенести сервис на другую рабочую ноду worker0, задержка связана с тем, что перед запуском на ноду worker0 должен быть добавлен образ Nginx из Docker Hub.

После автоматического добавления образа сервис webserver успешно запустится на ноде worker0:

[rancher@manager0 ~]$ docker service ps webserver
ID                         NAME             IMAGE  NODE     DESIRED STATE  CURRENT STATE               ERROR
5lwf4z54p74qtoopuilshw204  webserver.1      nginx  worker0  Running        Running about a minute ago
cjxkza3qzx5m73bgbls0r26m6   _ webserver.1  nginx  worker1  Shutdown       Running 19 minutes ago

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

Выполним команду (не забудьте включить ноду worker1 обратно):

[rancher@manager0 ~]$ docker service update --replicas 2 webserver

Сервис начнет реплицироваться на вторую ноду worker1:

[rancher@manager0 ~]$ docker service ps webserver
ID                         NAME             IMAGE  NODE     DESIRED STATE  CURRENT STATE            ERROR
5lwf4z54p74qtoopuilshw204  webserver.1      nginx  worker0  Running        Running 11 minutes ago
cjxkza3qzx5m73bgbls0r26m6   _ webserver.1  nginx  worker1  Shutdown       Complete 6 minutes ago
f3o7icke69zw83ag92ywol0dr  webserver.2      nginx  worker1  Running        Preparing 2 seconds ago

Заключение

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

Будем рады, если вы поделитесь собственным опытом использования CoreOS, RancherOS и подобных им операционных систем.

Автор: Селектел

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js