Перейти к содержанию

1 - Аутентификационные руководства

Руководство по аутентификации и контролю доступа на основе ролей в etcd

1.1 - Аутентификация

Руководство по аутентификации кластера etcd

auth,user,role для аутентификации:

export ETCDCTL_API=3
ENDPOINTS=localhost:2379

etcdctl --endpoints=${ENDPOINTS} role add root
etcdctl --endpoints=${ENDPOINTS} role get root

etcdctl --endpoints=${ENDPOINTS} user add root
etcdctl --endpoints=${ENDPOINTS} user grant-role root root
etcdctl --endpoints=${ENDPOINTS} user get root

etcdctl --endpoints=${ENDPOINTS} role add role0
etcdctl --endpoints=${ENDPOINTS} role grant-permission role0 readwrite foo
etcdctl --endpoints=${ENDPOINTS} user add user0
etcdctl --endpoints=${ENDPOINTS} user grant-role user0 role0

etcdctl --endpoints=${ENDPOINTS} auth enable
# now all client requests go through auth

etcdctl --endpoints=${ENDPOINTS} --user=user0:123 put foo bar
etcdctl --endpoints=${ENDPOINTS} get foo
# permission denied, user name is empty because the request does not issue an authentication request
etcdctl --endpoints=${ENDPOINTS} --user=user0:123 get foo
# user0 can read the key foo
etcdctl --endpoints=${ENDPOINTS} --user=user0:123 get foo1

Примечание:

Это всего лишь заглушка, которую необходимо заполнить и обновить, добавив больше информации об аутентификации. Текст выше — всего лишь пример кода.

1.2 - Управление доступом на основе ролей

Базовое руководство по аутентификации и управлению доступом на основе ролей

Обзор

Аутентификация появилась в etcd 2.1. В API etcd v3 интерфейс API и пользовательский интерфейс аутентификации немного изменены для соответствия новой модели данных. Это руководство поможет настроить базовую аутентификацию и управление доступом на основе ролей в etcd v3.

Специальные пользователи и роли

Существует один специальный пользователь root и одна специальная роль root.

Пользователь root

Пользователя root с полным доступом к etcd необходимо создать до включения аутентификации. Он предназначен для административных задач: управления ролями и обычными пользователями. Пользователь root должен иметь роль root и может изменять любые данные в etcd.

Роль root

Роль root можно назначить любому пользователю, не только пользователю root. Пользователь с этой ролью имеет глобальный доступ на чтение и запись и право изменять конфигурацию аутентификации кластера. Роль root также предоставляет права для общего обслуживания кластера, включая изменение состава, дефрагментацию хранилища и создание снимков.

Работа с пользователями

Подкоманда user утилиты etcdctl выполняет все операции с учётными записями пользователей.

Список пользователей выводится командой:

$ etcdctl user list

Пользователь создаётся командой:

$ etcdctl user add myusername

При создании нового пользователя запрашивается пароль. Если указан параметр --interactive=false, пароль можно передать через стандартный ввод. Его также можно задать параметром --new-user-password.

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

$ etcdctl user add myusername --no-password

Такой пользователь может аутентифицироваться только по Common Name сертификата TLS .

Примечание

etcd не поддерживает аутентификацию с пустым паролем через --user username:. Например, пользователь с пустым паролем, созданный командой etcdctl user add anonymous:'', не может пройти аутентификацию по имени и паролю, а запросы вида etcdctl --user anonymous: get foo завершаются ошибкой user name is empty.

Роли назначаются и отзываются у пользователя командами:

$ etcdctl user grant-role myusername foo
$ etcdctl user revoke-role myusername bar

Параметры пользователя можно просмотреть командой:

$ etcdctl user get myusername

Пароль пользователя изменяется командой:

$ etcdctl user passwd myusername

При изменении снова запрашивается новый пароль. С параметром --interactive=false его можно передать через стандартный ввод.

Учётная запись удаляется командой:

$ etcdctl user delete myusername

Работа с ролями

Подкоманда role утилиты etcdctl управляет правами доступа отдельных ролей, назначаемых пользователям.

Список ролей выводится командой:

$ etcdctl role list

Новая роль создаётся командой:

$ etcdctl role add myrolename

У роли нет пароля; она лишь определяет набор прав доступа.

Роли предоставляется доступ к одному ключу или диапазону ключей.

Диапазон задаётся интервалом [start-key, end-key), где start-key должен лексикографически предшествовать end-key.

Можно предоставить доступ на чтение, запись или оба вида, как в следующих примерах:

# Give read access to a key /foo
$ etcdctl role grant-permission myrolename read /foo

# Give read access to keys with a prefix /foo/. The prefix is equal to the range [/foo/, /foo0)
$ etcdctl role grant-permission myrolename --prefix=true read /foo/

# Give write-only access to the key at /foo/bar
$ etcdctl role grant-permission myrolename write /foo/bar

# Give full access to keys in a range of [key1, key5)
$ etcdctl role grant-permission myrolename readwrite key1 key5

# Give full access to keys with a prefix /pub/
$ etcdctl role grant-permission myrolename --prefix=true readwrite /pub/

Выданные права можно в любой момент просмотреть у роли:

$ etcdctl role get myrolename

Права отзываются аналогичным образом:

$ etcdctl role revoke-permission myrolename /foo/bar

Роль целиком удаляется командой:

$ etcdctl role delete myrolename

Включение аутентификации

Ниже приведён минимальный порядок включения аутентификации. Администратор может настроить пользователей и роли до или после её включения.

Убедитесь, что пользователь root создан:

$ etcdctl user add root
Password of root:

Включите аутентификацию:

$ etcdctl auth enable

После этого etcd работает с включённой аутентификацией. Для её отключения используйте обратную команду:

$ etcdctl --user root:rootpw auth disable

Область защиты аутентификации

Аутентификация, включённая командой etcdctl auth enable, защищает операции V3 gRPC API (get, put, delete, watch и т. д.).

Конечные точки HTTP /metrics и /health обслуживаются отдельным обработчиком и не защищены аутентификацией V3 RBAC. Поэтому Prometheus и балансировщики нагрузки могут собирать метрики без аутентификации gRPC, тогда как данные ключей и значений остаются защищёнными.

Чтобы защитить эти конечные точки наблюдаемости:

  • включите mTLS с --cert-file, --key-file и --client-cert-auth;
  • либо привяжите метрики к частному интерфейсу через --listen-metrics-urls;
  • либо ограничьте доступ сетевыми политиками или правилами межсетевого экрана.

Аутентификация с помощью etcdctl

Для аутентификации etcdctl поддерживает флаг, аналогичный curl.

$ etcdctl --user user:password get foo

Пароль можно ввести по запросу:

$ etcdctl --user user get foo

Пароль также можно передать флагом командной строки --password:

$ etcdctl --user user --password password get foo

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

Использование Common Name сертификата TLS

Начиная с v3.2, если сервер etcd запущен с --client-cert-auth=true, поле Common Name (CN) клиентского сертификата TLS используется как пользователь etcd. В этом случае CN аутентифицирует пользователя, и пароль клиенту не нужен. Если одновременно 1. передан --client-cert-auth=true и клиент предоставляет CN и 2. клиент предоставляет имя пользователя и пароль, приоритет имеет аутентификация по имени и паролю. Эту возможность нельзя использовать с gRPC-proxy и gRPC-gateway. gRPC-proxy завершает TLS-соединение клиента, поэтому все клиенты используют сертификат прокси. gRPC-gateway внутренне использует TLS-соединение для преобразования запроса HTTP в запрос gRPC и имеет то же ограничение. Поэтому клиенты не могут правильно передать серверу свой CN. Если предоставленный сертификат содержит непустой CN, gRPC-proxy возвращает ошибку и останавливается.

Примечания о стойкости паролей

etcdctl и API etcd не требуют определённой длины пароля при создании пользователя или обновлении его пароля. Соблюдение таких требований должен обеспечивать администратор. Чтобы избежать рисков, связанных со стойкостью паролей, можно использовать аутентификацию по Common Name сертификата TLS и пользователей, созданных с параметром --no-password.

2 - Параметры конфигурации

Файлы конфигурации, флаги и переменные окружения etcd

etcd можно настроить следующими способами:

Предупреждение

Внимание: при сочетании разных способов конфигурации действуют следующие правила.

  • Флаги командной строки имеют приоритет над переменными окружения.
  • Если указан файл конфигурации, все флаги командной строки и переменные окружения игнорируются.

Флаги командной строки

Ниже флаги представлены в формате --flag-name DEFAULT_VALUE.

Из-за продолжающейся разработки приведённый ниже список флагов может быть неактуален. Последние доступные флаги можно получить командой etcd --help или в [справке etcd][].

Примечание

Примечание: сведения о новых, обновлённых и устаревших флагах v3.7 приведены в CHANGELOG-3.7.md .

Участник

--name 'default'
  Human-readable name for this member.
--data-dir '${name}.etcd'
  Path to the data directory.
--wal-dir ''
  Path to the dedicated wal directory.
--snapshot-count '10000'
  Number of committed transactions to trigger a snapshot to disk.
--heartbeat-interval '100'
  Time (in milliseconds) of a heartbeat interval.
--election-timeout '1000'
  Time (in milliseconds) for an election to timeout. See tuning documentation for details.
--initial-election-tick-advance 'true'
  Whether to fast-forward initial election ticks on boot for faster election.
--listen-peer-urls 'http://localhost:2380'
  List of URLs to listen on for peer traffic.
--listen-client-urls 'http://localhost:2379'
  List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.
--listen-client-http-urls ''
  List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.
--max-snapshots '5'
  Maximum number of snapshot files to retain (0 is unlimited).
--max-wals '5'
  Maximum number of wal files to retain (0 is unlimited).
--memory-mlock
  Enable to enforce etcd pages (in particular bbolt) to stay in RAM.
--quota-backend-bytes '0'
  Raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
--backend-bbolt-freelist-type 'map'
  BackendFreelistType specifies the type of freelist that boltdb backend uses(array and map are supported types).
--backend-batch-interval ''
  BackendBatchInterval is the maximum time before commit the backend transaction.
--backend-batch-limit '0'
  BackendBatchLimit is the maximum operations before commit the backend transaction.
--max-txn-ops '128'
  Maximum number of operations permitted in a transaction.
--max-request-bytes '1572864'
  Maximum client request size in bytes the server will accept.
--grpc-keepalive-min-time '5s'
  Minimum duration interval that a client should wait before pinging server.
--grpc-keepalive-interval '2h'
  Frequency duration of server-to-client ping to check if a connection is alive (0 to disable).
--grpc-keepalive-timeout '20s'
  Additional duration of wait before closing a non-responsive connection (0 to disable).
--socket-reuse-port 'false'
  Enable to set socket option SO_REUSEPORT on listeners allowing rebinding of a port already in use.
--socket-reuse-address 'false'
  Enable to set socket option SO_REUSEADDR on listeners allowing binding to an address in TIME_WAIT state.

Кластеризация

--initial-advertise-peer-urls 'http://localhost:2380'
  List of this member's peer URLs to advertise to the rest of the cluster.
--initial-cluster 'default=http://localhost:2380'
  Initial cluster configuration for bootstrapping.
--initial-cluster-state 'new'
  Initial cluster state ('new' or 'existing').
--initial-cluster-token 'etcd-cluster'
  Initial cluster token for the etcd cluster during bootstrap.
  Specifying this can protect you from unintended cross-cluster interaction when running multiple clusters.
--advertise-client-urls 'http://localhost:2379'
  List of this member's client URLs to advertise to the public.
  The client URLs advertised should be accessible to machines that talk to etcd cluster. etcd client libraries parse these URLs to connect to the cluster.
--discovery ''
  Discovery URL used to bootstrap the cluster.
--discovery-fallback 'proxy'
  Expected behavior ('exit' or 'proxy') when discovery services fails.
  "proxy" supports v2 API only.
--discovery-proxy ''
  HTTP proxy to use for traffic to discovery service.
--discovery-srv ''
  DNS srv domain used to bootstrap the cluster.
--discovery-srv-name ''
  Suffix to the dns srv name queried when bootstrapping.
--strict-reconfig-check 'true'
  Reject reconfiguration requests that would cause quorum loss.
--pre-vote 'true'
  Enable the raft Pre-Vote algorithm to prevent disruption when a node that has been partitioned away rejoins the cluster.
--auto-compaction-retention '0'
  Auto compaction retention length. 0 means disable auto compaction.
--auto-compaction-mode 'periodic'
  Interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
--enable-v2 'false'
  Accept etcd V2 client requests. Deprecated and to be decommissioned in v3.6.
--v2-deprecation 'not-yet'
  Phase of v2store deprecation. Allows to opt-in for higher compatibility mode.
  Supported values:
    'not-yet'                // Issues a warning if v2store have meaningful content (default in v3.5)
    'write-only'             // Custom v2 state is not allowed (default in v3.6 and v3.7)
    'write-only-skip-check'  // Custom v2 state is not supported and, if present, will be ignored (available in v3.5.32+, v3.6.13+, and v3.7.0+). Use this option at your own risk.
    'write-only-drop-data'   // Custom v2 state will get DELETED ! (planned default in v3.8)
    'gone'                   // v2store is not maintained any longer.

Безопасность

--cert-file ''
  Path to the client server TLS cert file.
--key-file ''
  Path to the client server TLS key file.
--client-cert-auth 'false'
  Enable client cert authentication.
  It's recommended to enable client cert authentication to prevent attacks from unauthenticated clients (e.g. CVE-2023-44487), especially when running etcd as a public service.
--client-crl-file ''
  Path to the client certificate revocation list file.
--client-cert-allowed-hostname ''
  Comma-separated list of SAN hostnames for client cert authentication.
--trusted-ca-file ''
  Path to the client server TLS trusted CA cert file.
  Note setting this parameter will also automatically enable client cert authentication no matter what value is set for `--client-cert-auth`.
--auto-tls 'false'
  Client TLS using generated certificates.
--peer-cert-file ''
  Path to the peer server TLS cert file.
--peer-key-file ''
  Path to the peer server TLS key file.
--peer-client-cert-auth 'false'
  Enable peer client cert authentication.
  It's recommended to enable peer client cert authentication to prevent attacks from unauthenticated forged peers (e.g. CVE-2023-44487).
--peer-trusted-ca-file ''
  Path to the peer server TLS trusted CA file.
--peer-cert-allowed-cn ''
  Comma-separated list of allowed CNs for inter-peer TLS authentication.
--peer-cert-allowed-hostname ''
  Comma-separated list of allowed SAN hostnames for inter-peer TLS authentication.
--peer-auto-tls 'false'
  Peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided.
--self-signed-cert-validity '1'
  The validity period of the client and peer certificates that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS, the unit is year, and the default is 1.
--peer-crl-file ''
  Path to the peer certificate revocation list file.
--cipher-suites ''
  Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).
--cors '*'
  Comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
--host-whitelist '*'
  Acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).
--tls-min-version 'TLS1.2'
  Minimum TLS version supported by etcd.
--tls-max-version ''
  Maximum TLS version supported by etcd (empty will be auto-populated by Go).

Аутентификация

--auth-token 'simple'
  Specify a v3 authentication token type and its options ('simple' or 'jwt').
--bcrypt-cost 10
  Specify the cost / strength of the bcrypt algorithm for hashing auth passwords. Valid values are between 4 and 31.
--auth-token-ttl 300
  Time (in seconds) of the auth-token-ttl.

Профилирование и мониторинг

--enable-pprof 'false'
  Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"
--metrics 'basic'
  Set level of detail for exported metrics, specify 'extensive' to include server side grpc histogram metrics.
--listen-metrics-urls ''
  List of URLs to listen on for the metrics and health endpoints.

Ведение журнала

--logger 'zap'
  Currently only supports 'zap' for structured logging.
--log-outputs 'default'
  Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.
--log-level 'info'
  Configures log level. Only supports debug, info, warn, error, panic, or fatal.
--log-format 'json'
  Configures log format. Only supports json, console.
--enable-log-rotation 'false'
  Enable log rotation of a single log-outputs file target.
--log-rotation-config-json '{"maxsize": 100, "maxage": 0, "maxbackups": 0, "localtime": false, "compress": false}'
  Configures log rotation if enabled with a JSON logger config. MaxSize(MB), MaxAge(days,0=no limit), MaxBackups(0=no limit), LocalTime(use computers local time), Compress(gzip)".
--warning-unary-request-duration '300ms'
  Set time duration after which a warning is logged if a unary request takes more than this duration.
Примечание

Примечание: в v3.7 несколько флагов --experimental-* стали стабильными или были переименованы. Обязательно замените устаревшие флаги перечисленными ниже стабильными эквивалентами.

Распределённая трассировка

--enable-distributed-tracing 'false'
  Enable distributed tracing.
--distributed-tracing-address 'localhost:4317'
  Distributed tracing collector address.
--distributed-tracing-service-name 'etcd'
  Distributed tracing service name, must be the same across all etcd instances.
--distributed-tracing-instance-id ''
  Distributed tracing instance ID, must be unique for each etcd instance.
--distributed-tracing-sampling-rate '0'
  Number of samples to collect per million spans for distributed tracing.

Прокси v2

Предупреждение

Примечание: флаги будут объявлены устаревшими в v3.6.

--proxy 'off'
  Proxy mode setting ('off', 'readonly' or 'on').
--proxy-failure-wait 5000
  Time (in milliseconds) an endpoint will be held in a failed state.
--proxy-refresh-interval 30000
  Time (in milliseconds) of the endpoints refresh interval.
--proxy-dial-timeout 1000
  Time (in milliseconds) for a dial to timeout.
--proxy-write-timeout 5000
  Time (in milliseconds) for a write to timeout.
--proxy-read-timeout 0
  Time (in milliseconds) for a read to timeout.

Возможности

--corrupt-check-time '0s'
  Duration of time between cluster corruption check passes.
--compact-hash-check-time '1m'
  Duration of time between leader checks followers compaction hashes.
--compaction-batch-limit 1000
  CompactionBatchLimit sets the maximum revisions deleted in each compaction batch.
--peer-skip-client-san-verification 'false'
  Skip verification of SAN field in client certificate for peer connections.
--watch-progress-notify-interval '10m'
  Duration of periodical watch progress notification.
--warning-apply-duration '100ms'
  Warning is generated if requests take more than this duration.
--bootstrap-defrag-threshold-megabytes
  Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.
--max-learners '1'
  Set the max number of learner members allowed in the cluster membership.
--compaction-sleep-interval
  Sets the sleep interval between each compaction batch.
--downgrade-check-time
  Duration of time between two downgrade status checks.
--snapshot-catchup-entries
  Number of entries for a slow follower to catch up after compacting the raft storage entries.

Флаги функций

--feature-gates=AllAlpha=true|false
  Enables or disables all alpha features. Default is false.
--feature-gates=AllBeta=true|false
  Enables or disables all beta features. Default is false.
--feature-gates=CompactHashCheck=true
  Enables leader to periodically check follower compaction hashes.
  Replaces: --experimental-compact-hash-check-enabled
--feature-gates=InitialCorruptCheck=true
  Enables corruption check before serving client/peer traffic.
  Replaces: --experimental-initial-corrupt-check
--feature-gates=LeaseCheckpoint=true
  ExperimentalEnableLeaseCheckpoint enables primary lessor to persist lease remainingTTL to prevent indefinite auto-renewal of long lived leases.
  Replaces: --experimental-enable-lease-checkpoint
--feature-gates=LeaseCheckpointPersist=true
  Enable persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled.
  Replaces: --experimental-enable-lease-checkpoint-persist
--feature-gates=SetMemberLocalAddr=true
  Allows setting a member’s local address.
--feature-gates=StopGRPCServiceOnDefrag=true
  Enable etcd gRPC service to stop serving client requests on defragmentation.
  Replaces: --experimental-stop-grpc-service-on-defrag
--feature-gates=TxnModeWriteWithSharedBuffer=true
  Enable the write transaction to use a shared buffer in its readonly check operations.
  Replaces: --experimental-txn-mode-write-with-shared-buffer

Небезопасные возможности

Предупреждение

Предупреждение: использование небезопасных возможностей может нарушить гарантии протокола консенсуса!

--force-new-cluster 'false'
  Force to create a new one-member cluster.
--unsafe-no-fsync 'false'
  Disables fsync, unsafe, will cause data loss.

Файл конфигурации

Файл конфигурации etcd представляет собой отображение YAML, ключами которого служат имена флагов командной строки, а значениями — значения флагов. Чтобы использовать файл, укажите его путь как значение флага --config-file или переменной окружения ETCD_CONFIG_FILE.

Пример см. в [образце etcd.conf.yml][].

Примечание

Поля длительности, такие как --grpc-keepalive-min-time, --grpc-keepalive-interval, --grpc-keepalive-timeout, --backend-batch-interval, --corrupt-check-time, --compact-hash-check-time, --compaction-sleep-interval, --watch-progress-notify-interval, --warning-apply-duration, --warning-unary-request-duration и --downgrade-check-time, при передаче как флаги командной строки принимают понятные человеку строки (например, 10m, 5s), однако в файле конфигурации допускаются только целые значения, представляющие наносекунды. Это известное ограничение стандартной библиотеки Go , где time.Duration десериализуется как обычное целое число.

Например, чтобы задать в файле конфигурации 10-минутный интервал уведомлений о ходе наблюдения:

# Correct: 10 minutes in nanoseconds
watch-progress-notify-interval: 600000000000

# Incorrect: will produce an unmarshal error
watch-progress-notify-interval: '10m'

3 - Модель транспортной безопасности

Защита передаваемых данных

etcd поддерживает автоматический TLS и аутентификацию по клиентским сертификатам как для связи клиентов с сервером, так и для связи одноранговых узлов (серверов друг с другом внутри кластера). Обратите внимание: по умолчанию etcd не включает аутентификацию на основе RBAC и аутентификацию на транспортном уровне, чтобы упростить начало работы с базой данных. Кроме того, изменение этого значения по умолчанию нарушило бы совместимость проекта, установленную с 2013 года. Кластер etcd без включённых функций безопасности может открыть свои данные любым клиентам.

Для начала подготовьте сертификат CA и подписанную пару ключей для одного участника. Рекомендуется создавать и подписывать новую пару ключей для каждого участника кластера.

Для удобства инструмент cfssl предоставляет простой интерфейс создания сертификатов; пример его использования приведён здесь . В качестве альтернативы воспользуйтесь руководством по созданию самоподписанных пар ключей .

Из-за продолжающейся разработки приведённый ниже список флагов может быть неактуален. Последние доступные флаги можно получить командой etcd --help или в [справке etcd][].

Базовая настройка

etcd принимает несколько относящихся к сертификатам параметров конфигурации в виде флагов командной строки или переменных окружения:

Связь клиента с сервером:

--cert-file=<path>: сертификат, используемый для соединений SSL/TLS с etcd. При заданном параметре advertise-client-urls может использовать схему HTTPS.

--key-file=<path>: ключ сертификата. Должен быть незашифрованным.

--client-cert-auth: если задан, etcd проверяет во всех входящих запросах HTTPS наличие клиентского сертификата, подписанного доверенным CA; запросы без действительного клиентского сертификата завершаются ошибкой. Если включена аутентификация , сертификат предоставляет учётные данные для имени пользователя из поля Common Name.

--trusted-ca-file=<path>: доверенный центр сертификации.

--auto-tls: использовать автоматически созданные самоподписанные сертификаты для соединений TLS с клиентами.

Связь одноранговых узлов (между серверами / в кластере):

Параметры одноранговых узлов работают так же, как параметры связи клиента с сервером:

--peer-cert-file=<path>: сертификат для соединений SSL/TLS между одноранговыми узлами. Используется как при прослушивании адреса однорангового узла, так и при отправке запросов другим узлам.

--peer-key-file=<path>: ключ сертификата. Должен быть незашифрованным.

--peer-client-cert-auth: если задан, etcd проверяет во всех входящих запросах одноранговых узлов кластера действительные клиентские сертификаты, подписанные указанным CA.

--peer-trusted-ca-file=<path>: доверенный центр сертификации.

--peer-auto-tls: использовать автоматически созданные самоподписанные сертификаты для соединений TLS между одноранговыми узлами.

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

Общие параметры:

--cipher-suites: разделённый запятыми список поддерживаемых наборов шифров TLS между сервером и клиентом, а также между одноранговыми узлами (пустой список автоматически заполняется Go).

--tls-min-version=<version> задаёт минимальную версию TLS, поддерживаемую etcd.

--tls-max-version=<version> задаёт максимальную версию TLS, поддерживаемую etcd. Если не задана, используется максимальная версия, поддерживаемая Go.

keyUsage и extendedKeyUsage сертификата TLS

При создании сертификатов X.509 для защиты транспорта etcd сертификаты должны содержать подходящие поля keyUsage и extendedKeyUsage в зависимости от своей роли. Для проверки сертификатов etcd использует библиотеки Go crypto/tls и crypto/x509, которые контролируют эти варианты использования во время рукопожатия TLS.

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

Роль сертификатаkeyUsageextendedKeyUsage
Сервер (клиент — сервер)digitalSignature, keyEnciphermentserverAuth
КлиентdigitalSignature, keyEnciphermentclientAuth
Одноранговый узел (сервер — сервер)digitalSignature, keyEnciphermentserverAuth, clientAuth

Примечания:

  • При включённом --peer-client-cert-auth сертификаты одноранговых узлов используются для взаимного TLS между участниками etcd и поэтому требуют как serverAuth, так и clientAuth.
  • Клиентские сертификаты, используемые с --client-cert-auth, должны содержать clientAuth.

Пример 1: транспортная безопасность между клиентом и сервером с HTTPS

Подготовьте сертификат CA (ca.crt) и подписанную пару ключей (server.crt, server.key).

Настроим простую транспортную безопасность HTTPS в etcd по шагам:

$ etcd --name infra0 --data-dir infra0 \
  --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
  --advertise-client-urls=https://127.0.0.1:2379 --listen-client-urls=https://127.0.0.1:2379

etcd должен успешно запуститься; конфигурацию можно проверить, обратившись к etcd по HTTPS:

$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

Команда должна показать успешное рукопожатие. Поскольку используются самоподписанные сертификаты и собственный центр сертификации, CA необходимо передать curl с параметром --cacert. Другой вариант — добавить сертификат CA в системный каталог доверенных сертификатов (обычно /etc/pki/tls/certs или /etc/ssl/certs).

Пользователям OSX 10.9+: curl 7.30.0 в OSX 10.9+ не распознаёт сертификаты, переданные в командной строке. Вместо этого импортируйте тестовый ca.crt непосредственно в связку ключей либо добавьте curl флаг -k, чтобы игнорировать ошибки. Для проверки без флага -k выполните open ./tests/fixtures/ca/ca.crt и следуйте указаниям. После тестирования удалите этот сертификат! Если известно обходное решение, сообщите о нём.

Пример 2: аутентификация клиента на сервере с клиентскими сертификатами HTTPS

К этому моменту клиент etcd умеет проверять подлинность сервера и обеспечивает транспортную безопасность. Клиентские сертификаты также позволяют предотвратить несанкционированный доступ к etcd.

Клиенты предъявляют серверу свои сертификаты, а сервер проверяет их подпись указанным CA и решает, обслуживать ли запрос.

Потребуются те же файлы, что и в первом примере, а также пара ключей клиента (client.crt, client.key), подписанная тем же центром сертификации.

$ etcd --name infra0 --data-dir infra0 \
  --client-cert-auth --trusted-ca-file=/path/to/ca.crt --cert-file=/path/to/server.crt --key-file=/path/to/server.key \
  --advertise-client-urls https://127.0.0.1:2379 --listen-client-urls https://127.0.0.1:2379

Теперь отправьте серверу тот же запрос, что и выше:

$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

Сервер должен отклонить запрос:

...
routines:SSL3_READ_BYTES:sslv3 alert bad certificate
...

Для успешного выполнения нужно передать серверу подписанный CA клиентский сертификат:

$ curl --cacert /path/to/ca.crt --cert /path/to/client.crt --key /path/to/client.key \
  -L https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

Вывод должен содержать:

...
SSLv3, TLS handshake, CERT verify (15):
...
TLS handshake, Finished (20)

А также ответ сервера:

{
    "action": "set",
    "node": {
        "createdIndex": 12,
        "key": "/foo",
        "modifiedIndex": 12,
        "value": "bar"
    }
}

Укажите наборы шифров, чтобы заблокировать слабые наборы шифров TLS .

Рукопожатие TLS завершается ошибкой, если приветствие клиента запрошено с недопустимыми наборами шифров.

Например:

$ etcd \
  --cert-file ./server.crt \
  --key-file ./server.key \
  --trusted-ca-file ./ca.crt \
  --cipher-suites TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

После этого клиентские запросы должны указывать один из заданных на сервере наборов шифров:

# valid cipher suite
$ curl \
  --cacert /path/to/ca.crt \
  --cert /path/to/client.crt \
  --key /path/to/client.key \
  -L [CLIENT-URL]/metrics \
  --ciphers ECDHE-RSA-AES128-GCM-SHA256

# request succeeds
etcd_server_version{server_version="3.2.22"} 1
...
# invalid cipher suite
$ curl \
  --cacert /path/to/ca.crt \
  --cert /path/to/client.crt \
  --key /path/to/client.key \
  -L [CLIENT-URL]/metrics \
  --ciphers ECDHE-RSA-DES-CBC3-SHA

# request fails with
(35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure

Пример 3: транспортная безопасность и клиентские сертификаты в кластере

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

Предположим, имеется ca.crt и два участника с собственными парами ключей (member1.crt и member1.key, member2.crt и member2.key), подписанными этим CA. Запустим etcd следующим образом:

DISCOVERY_URL=... # from https://discovery.etcd.io/new

# member1
$ etcd --name infra1 --data-dir infra1 \
  --peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member1.crt --peer-key-file=/path/to/member1.key \
  --initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
  --discovery ${DISCOVERY_URL}

# member2
$ etcd --name infra2 --data-dir infra2 \
  --peer-client-cert-auth --peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member2.crt --peer-key-file=/path/to/member2.key \
  --initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
  --discovery ${DISCOVERY_URL}

Участники etcd образуют кластер, а весь обмен между ними шифруется и аутентифицируется клиентскими сертификатами. Вывод etcd покажет, что адреса подключений используют HTTPS.

Пример 4: автоматическая транспортная безопасность с самоподписанными сертификатами

Предупреждение

При указании ClientAutoTLS и PeerAutoTLS срок действия автоматически созданных etcd клиентского сертификата и сертификата однорангового узла составляет только 1 год. Срок действия сертификата в годах можно задать флагом –self-signed-cert-validity.

Если требуется шифрование связи без аутентификации, etcd может шифровать сообщения автоматически созданными самоподписанными сертификатами. Это упрощает развёртывание, поскольку управлять сертификатами и ключами вне etcd не требуется. Настройте etcd на использование самоподписанных сертификатов для клиентских и одноранговых соединений флагами --auto-tls и --peer-auto-tls:

DISCOVERY_URL=... # from https://discovery.etcd.io/new

# member1
$ etcd --name infra1 --data-dir infra1 \
  --auto-tls --peer-auto-tls \
  --initial-advertise-peer-urls=https://10.0.1.10:2380 --listen-peer-urls=https://10.0.1.10:2380 \
  --discovery ${DISCOVERY_URL}

# member2
$ etcd --name infra2 --data-dir infra2 \
  --auto-tls --peer-auto-tls \
  --initial-advertise-peer-urls=https://10.0.1.11:2380 --listen-peer-urls=https://10.0.1.11:2380 \
  --discovery ${DISCOVERY_URL}

Самоподписанные сертификаты не подтверждают подлинность, поэтому curl вернёт ошибку:

curl: (60) SSL certificate problem: Invalid certificate chain

Чтобы отключить проверку цепочки сертификатов, вызовите curl с флагом -k:

$ curl -k https://127.0.0.1:2379/v2/keys/foo -Xput -d value=bar -v

Примечания по DNS SRV

Начиная с v3.1.0 (кроме v3.2.9), начальная инициализация через SRV аутентифицирует ServerName по корневому доменному имени из флага --discovery-srv. Для защиты от атак «человек посередине» с сертификатами требуется, чтобы сертификат содержал совпадающее корневое доменное имя в поле Subject Alternative Name (SAN). Например, etcd --discovery-srv=etcd.local аутентифицирует одноранговые узлы и клиентов, только если предоставленные сертификаты содержат корневой домен etcd.local в поле Subject Alternative Name (SAN)

Примечания для прокси etcd

Прокси etcd терминирует TLS своего клиента, если соединение защищено, а для связи с участниками etcd использует собственные ключ и сертификат прокси, заданные в --peer-key-file и --peer-cert-file.

Прокси связывается с участниками etcd как через --advertise-client-urls, так и через --advertise-peer-urls соответствующего участника. Он пересылает клиентские запросы на объявленные клиентские URL участников etcd и синхронизирует исходную конфигурацию кластера через объявленные URL их одноранговых узлов.

Когда для участника etcd включена аутентификация клиентов, администратор должен убедиться, что сертификат однорангового узла из параметра прокси --peer-cert-file действителен для этой аутентификации. При включённой аутентификации одноранговых узлов сертификат прокси также должен быть действителен для неё.

Примечания по аутентификации TLS

Начиная с v3.2.0 , сертификаты TLS перезагружаются при каждом клиентском соединении . Это позволяет заменять истекающие сертификаты без остановки серверов etcd, перезаписывая старые сертификаты новыми. Обновление сертификатов для каждого соединения не должно создавать значительных накладных расходов, однако в будущем его можно улучшить уровнем кэширования. Примеры тестов находятся здесь .

Начиная с v3.2.0 , сервер отклоняет входящие сертификаты одноранговых узлов с неверным IP в SAN . Если сертификат однорангового узла содержит IP-адреса в поле Subject Alternative Name (SAN), сервер аутентифицирует узел только при совпадении удалённого IP-адреса с одним из них. Это предотвращает присоединение к кластеру неавторизованных конечных точек. Например, CSR однорангового узла B (созданный с cfssl) имеет вид:

{
  "CN": "etcd peer",
  "hosts": [
    "*.example.default.svc",
    "*.example.default.svc.cluster.local",
    "10.138.0.27"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "CA",
      "ST": "San Francisco"
    }
  ]
}

при этом фактический IP-адрес узла B равен 10.138.0.2, а не 10.138.0.27. Когда B пытается присоединиться к кластеру, узел A отклоняет его с ошибкой x509: certificate is valid for 10.138.0.27, not 10.138.0.2, поскольку удалённый IP-адрес B не совпадает с адресом в поле Subject Alternative Name (SAN).

Начиная с v3.2.0 , при проверке SAN сервер разрешает TLS DNSNames . Если сертификат однорангового узла содержит в поле Subject Alternative Name (SAN) только DNS-имена без IP-адресов, сервер аутентифицирует узел лишь тогда, когда прямое разрешение этих DNS-имён (dig b.com) даёт IP, совпадающий с удалённым адресом. Например, CSR однорангового узла B (созданный с cfssl) имеет вид:

{
  "CN": "etcd peer",
  "hosts": [
    "b.com"
  ],

при этом удалённый IP-адрес узла B равен 10.138.0.2. Когда B пытается присоединиться к кластеру, узел A разрешает входящее имя b.com, получая список IP-адресов (например, командой dig b.com). Если список не содержит IP 10.138.0.2, A отклоняет B с ошибкой tls: 10.138.0.2 does not match any of DNSNames ["b.com"].

Начиная с v3.2.2 , при совпадении IP сервер принимает соединение без проверки записей DNS . Если сертификат однорангового узла содержит в поле Subject Alternative Name (SAN) IP-адреса и DNS-имена, а удалённый IP совпадает с одним из адресов, сервер принимает соединение без дальнейшей проверки DNS-имён. Например, CSR однорангового узла B (созданный с cfssl) имеет вид:

{
  "CN": "etcd peer",
  "hosts": [
    "invalid.domain",
    "10.138.0.2"
  ],

при этом удалённый IP-адрес узла B равен 10.138.0.2, а invalid.domain — недопустимое имя узла. Когда B пытается присоединиться к кластеру, узел A успешно аутентифицирует его, поскольку поле Subject Alternative Name (SAN) содержит действительный совпадающий IP-адрес. Подробнее см. issue#8206 .

Начиная с v3.2.5 , сервер поддерживает обратное разрешение шаблонных DNS SAN . Если сертификат однорангового узла содержит в поле Subject Alternative Name (SAN) только DNS-имена без IP-адресов, сервер сначала выполняет обратное разрешение удалённого IP и получает список соответствующих ему имён (например, командой nslookup IPADDR). Затем соединение принимается, если одно из имён совпадает с DNS-именем сертификата точно или по шаблону. Если совпадений нет, сервер выполняет прямое разрешение каждой записи DNS сертификата (например, разрешает example.default.svc для записи *.example.default.svc) и принимает соединение, только когда среди разрешённых адресов узла есть IP, совпадающий с удалённым IP однорангового узла. Например, CSR узла B (созданный с cfssl) имеет вид:

{
  "CN": "etcd peer",
  "hosts": [
    "*.example.default.svc",
    "*.example.default.svc.cluster.local"
  ],

при этом удалённый IP-адрес узла B равен 10.138.0.2. Когда B пытается присоединиться к кластеру, узел A выполняет обратное разрешение IP 10.138.0.2 и получает список имён узлов. Затем он точно или по шаблону сопоставляет их с DNS-именами в поле Subject Alternative Name (SAN) сертификата B. Если ни обратное, ни прямое разрешение не дало совпадений, возвращается ошибка "tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]. Подробнее см. issue#8268 .

В v3.3.0 добавлен флаг etcd --peer-cert-allowed-cn , поддерживающий аутентификацию соединений одноранговых узлов по CN (Common Name) . Начальная инициализация TLS в Kubernetes включает создание динамических сертификатов для участников etcd и других системных компонентов (например, сервера API, kubelet и т. д.). Отдельные CA для каждого компонента обеспечивают более строгий контроль доступа к кластеру etcd, но часто неудобны. При заданном флаге –peer-cert-allowed-cn узел может присоединиться только с совпадающим общим именем, даже если CA общий. Сопоставление является точным сравнением строки с полем Common Name (CN) сертификата; шаблоны и префиксы не поддерживаются. При фильтрации по имени узла с –peer-cert-allowed-hostname или –client-cert-allowed-hostname используется x509.Certificate.VerifyHostname() из Go, поддерживающий как точные имена, так и шаблонные записи (например, *.example.com). Например, каждый участник кластера из 3 узлов настраивается со следующими CSR (созданными с cfssl):

{
  "CN": "etcd.local",
  "hosts": [
    "m1.etcd.local",
    "127.0.0.1",
    "localhost"
  ],
{
  "CN": "etcd.local",
  "hosts": [
    "m2.etcd.local",
    "127.0.0.1",
    "localhost"
  ],
{
  "CN": "etcd.local",
  "hosts": [
    "m3.etcd.local",
    "127.0.0.1",
    "localhost"
  ],

При заданном --peer-cert-allowed-cn etcd.local аутентифицируются только одноранговые узлы с совпадающими общими именами. Узлы с другими CN в CSR или другим --peer-cert-allowed-cn отклоняются:

$ etcd --peer-cert-allowed-cn m1.etcd.local

I | embed: rejected connection from "127.0.0.1:48044" (error "CommonName authentication failed", ServerName "m1.etcd.local")
I | embed: rejected connection from "127.0.0.1:55702" (error "remote error: tls: bad certificate", ServerName "m3.etcd.local")

Каждый процесс следует запускать с параметром:

etcd --peer-cert-allowed-cn etcd.local

I | pkg/netutil: resolving m3.etcd.local:32380 to 127.0.0.1:32380
I | pkg/netutil: resolving m2.etcd.local:22380 to 127.0.0.1:22380
I | pkg/netutil: resolving m1.etcd.local:2380 to 127.0.0.1:2380
I | etcdserver: published {Name:m3 ClientURLs:[https://m3.etcd.local:32379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | etcdserver: published {Name:m1 ClientURLs:[https://m1.etcd.local:2379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | etcdserver: published {Name:m2 ClientURLs:[https://m2.etcd.local:22379]} to cluster 9db03f09b20de32b
I | embed: ready to serve client requests
I | embed: serving client requests on 127.0.0.1:32379
I | embed: serving client requests on 127.0.0.1:22379
I | embed: serving client requests on 127.0.0.1:2379

В v3.2.19 и v3.3.4 исправлена перезагрузка TLS, когда поле SAN сертификата содержит только IP-адреса без доменных имён . Например, участник настраивается со следующим CSR (созданным с cfssl):

{
  "CN": "etcd.local",
  "hosts": [
    "127.0.0.1"
  ],

В Go сервер вызывает (*tls.Config).GetCertificate для перезагрузки TLS тогда и только тогда, когда поле сервера (*tls.Config).Certificates непусто либо (*tls.ClientHelloInfo).ServerName непусто и содержит действительный SNI клиента. Ранее etcd всегда заполнял (*tls.Config).Certificates при первом клиентском рукопожатии TLS, делая поле непустым. Поэтому клиент всегда должен был передавать совпадающий SNI, чтобы пройти проверку TLS и вызвать (*tls.Config).GetCertificate для перезагрузки ресурсов TLS.

Однако сертификат, поле SAN которого не содержит доменных имён, а только IP-адреса , запрашивает *tls.ClientHelloInfo с пустым полем ServerName, поэтому при первом рукопожатии TLS перезагрузка не запускается. Это становится проблемой при замене истёкших сертификатов без остановки службы.

Теперь при первом клиентском рукопожатии TLS (*tls.Config).Certificates создаётся пустым, чтобы сначала вызвать (*tls.Config).GetCertificate, а затем заполнять остальные сертификаты при каждом новом соединении TLS, даже если SNI клиента пуст (например, сертификат содержит только IP-адреса).

Примечания по белому списку узлов

Флаг etcd --host-whitelist задаёт допустимые имена узлов из клиентских запросов HTTP. Политика происхождения клиента защищает незащищённые серверы etcd от атак “DNS Rebinding” . Любой веб-сайт может создать разрешённое DNS-имя и направить DNS на "localhost" или любой другой адрес. Тогда все конечные точки HTTP сервера etcd, слушающего "localhost", становятся доступны и уязвимы для атак повторной привязки DNS. Подробнее см. CVE-2018-5702 .

Политика происхождения клиента работает следующим образом:

  1. Если клиентское соединение защищено HTTPS, разрешаются любые имена узлов.
  2. Если клиентское соединение не защищено и "HostWhitelist" не пуст, разрешаются только запросы HTTP, поле Host которых указано в белом списке.

Для более строгого контроля политика происхождения клиента применяется независимо от того, включена ли аутентификация.

По умолчанию etcd --host-whitelist и embed.Config.HostWhitelist пусты, поэтому разрешены все имена узлов. Обратите внимание: при указании имён адреса обратной петли автоматически не добавляются. Чтобы разрешить интерфейсы обратной петли, внесите их в белый список вручную (например, "localhost", "127.0.0.1" и т. д.).

Часто задаваемые вопросы

При клиентской аутентификации TLS появляется ошибка рукопожатия SSLv3 alert handshake failure?

Пакет crypto/tls языка golang проверяет назначение открытого ключа сертификата перед его использованием. Чтобы применять открытый ключ сертификата для аутентификации клиента, при его создании нужно добавить clientAuth в Extended Key Usage.

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

Добавьте в openssl.cnf следующий раздел:

[ ssl_client ]
...
  extendedKeyUsage = clientAuth
...

При создании сертификата обязательно укажите его во флаге -extensions:

$ openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr

При аутентификации сертификата однорангового узла появляется “certificate is valid for 127.0.0.1, not $MY_IP”

Убедитесь, что сертификаты подписаны с Subject Name, содержащим общедоступный IP-адрес участника. Например, инструмент etcd-ca предоставляет параметр --ip= для команды new-cert.

Сертификат должен быть подписан для FQDN участника в Subject Name; для добавления IP-адреса используйте Subject Alternative Names (кратко — IP SAN). Инструмент etcd-ca предоставляет параметр --domain= для команды new-cert, а openssl также умеет это .

Шифрует ли etcd данные, хранящиеся на дисках?

Нет. etcd не шифрует данные ключей и значений, хранящиеся на дисках. Если данные etcd необходимо шифровать, доступны следующие варианты:

  • выполнять шифрование и расшифрование в клиентских приложениях
  • использовать функцию нижележащей системы хранения для шифрования сохранённых данных, например dm-crypt

При создании некоторых новых каталогов etcd задаёт права доступа 700, чтобы по возможности предотвратить непривилегированный доступ. Однако если пользователь уже создал каталог с собственными правами, etcd использует существующий каталог и записывает предупреждение, если права отличаются от 700.

4 - Руководство по кластеризации

Начальная инициализация кластера etcd: статическая, обнаружение etcd и обнаружение DNS

Обзор

При статическом запуске кластера etcd каждый участник должен знать других участников кластера. В некоторых случаях IP-адреса участников заранее неизвестны. Тогда кластер etcd можно инициализировать с помощью службы обнаружения.

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

В руководстве рассматриваются следующие механизмы начальной инициализации кластера etcd:

Каждый механизм будет использован для создания кластера etcd из трёх машин со следующими параметрами:

ИмяАдресИмя узла
infra010.0.1.10infra0.example.com
infra110.0.1.11infra1.example.com
infra210.0.1.12infra2.example.com

Статическая инициализация

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

ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE=new
--initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
--initial-cluster-state new

Обратите внимание: URL в initial-cluster — это объявленные URL одноранговых узлов, поэтому они должны совпадать со значением initial-advertise-peer-urls на соответствующих узлах.

При запуске нескольких кластеров (либо многократном создании и уничтожении одного кластера) с одинаковой конфигурацией для тестирования настоятельно рекомендуется задавать каждому кластеру уникальный initial-cluster-token. Тогда etcd создаёт уникальные идентификаторы кластера и участников, даже если остальные параметры полностью совпадают. Это защищает etcd от взаимодействия между кластерами, способного их повредить.

Для приёма клиентского трафика etcd слушает адреса listen-client-urls . Участник etcd объявляет URL из advertise-client-urls другим участникам, прокси и клиентам. Убедитесь, что advertise-client-urls доступны предполагаемым клиентам. Распространённая ошибка — указать в advertise-client-urls localhost или оставить значение по умолчанию, когда к etcd должны обращаться удалённые клиенты.

На каждой машине запустите etcd со следующими флагами:

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new
$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new
$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new

При последующих запусках etcd параметры командной строки, начинающиеся с --initial-cluster, игнорируются. После исходной инициализации переменные окружения или флаги командной строки можно удалить. Если конфигурацию потребуется изменить позднее (например, добавить участника в кластер или удалить его), обратитесь к руководству по динамической конфигурации .

TLS

etcd поддерживает шифрованную связь по протоколу TLS. Каналы TLS можно использовать как для шифрования внутреннего обмена между одноранговыми узлами кластера, так и для шифрования клиентского трафика. В этом разделе приведены примеры настройки кластера с TLS для одноранговых и клиентских соединений. Подробности о поддержке TLS в etcd см. в руководстве по безопасности .

Самоподписанные сертификаты

Кластер с самоподписанными сертификатами одновременно шифрует трафик и аутентифицирует соединения. Для запуска такого кластера каждый участник должен иметь уникальную пару ключей (member.crt, member.key), подписанную общим сертификатом CA кластера (ca.crt) для одноранговых и клиентских соединений. Сертификаты можно создать по примеру настройки TLS в etcd.

На каждой машине etcd запускается со следующими флагами:

$ etcd --name infra0 --initial-advertise-peer-urls https://10.0.1.10:2380 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --listen-client-urls https://10.0.1.10:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra0-client.crt --key-file=/path/to/infra0-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra0-peer.crt --peer-key-file=/path/to/infra0-peer.key
$ etcd --name infra1 --initial-advertise-peer-urls https://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls https://10.0.1.11:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra1-client.crt --key-file=/path/to/infra1-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra1-peer.crt --peer-key-file=/path/to/infra1-peer.key
$ etcd --name infra2 --initial-advertise-peer-urls https://10.0.1.12:2380 \
  --listen-peer-urls https://10.0.1.12:2380 \
  --listen-client-urls https://10.0.1.12:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra2-client.crt --key-file=/path/to/infra2-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra2-peer.crt --peer-key-file=/path/to/infra2-peer.key

Автоматические сертификаты

Если кластеру требуется шифрованная связь, но не нужна аутентификация соединений, etcd можно настроить на автоматическое создание ключей. При инициализации каждый участник создаёт собственный набор ключей на основе объявленных IP-адресов и имён узлов.

На каждой машине etcd запускается со следующими флагами:

$ etcd --name infra0 --initial-advertise-peer-urls https://10.0.1.10:2380 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --listen-client-urls https://10.0.1.10:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls
$ etcd --name infra1 --initial-advertise-peer-urls https://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls https://10.0.1.11:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls
$ etcd --name infra2 --initial-advertise-peer-urls https://10.0.1.12:2380 \
  --listen-peer-urls https://10.0.1.12:2380 \
  --listen-client-urls https://10.0.1.12:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls

Случаи ошибок

В следующем примере новый узел не включён в перечень узлов. Для нового кластера узел обязательно должен быть добавлен в список исходных участников.

$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380 \
  --initial-cluster-state new
etcd: infra1 not listed in the initial cluster config
exit 1

В этом примере узел (infra0) сопоставляется с адресом (127.0.0.1:2380), отличным от указанного для него в списке кластера (10.0.1.10:2380). Если узел должен слушать несколько адресов, все они обязательно должны быть отражены в директиве конфигурации “initial-cluster”.

$ etcd --name infra0 --initial-advertise-peer-urls http://127.0.0.1:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state=new
etcd: error setting up initial cluster: infra0 has different advertised URLs in the cluster and advertised peer URLs list
exit 1

Если одноранговый узел с другим набором параметров конфигурации попытается присоединиться к кластеру, etcd сообщит о несовпадении идентификатора кластера и завершит работу.

$ etcd --name infra3 --initial-advertise-peer-urls http://10.0.1.13:2380 \
  --listen-peer-urls http://10.0.1.13:2380 \
  --listen-client-urls http://10.0.1.13:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.13:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra3=http://10.0.1.13:2380 \
  --initial-cluster-state=new
etcd: conflicting cluster ID to the target cluster (c6ab534d07e8fcc4 != bc25ea2a74fb18b0). Exiting.
exit 1

Обнаружение

В некоторых случаях IP-адреса одноранговых узлов кластера заранее неизвестны. Такое часто происходит при использовании облачных провайдеров или DHCP в сети. Тогда вместо статической конфигурации для начальной инициализации нового кластера используется существующий кластер etcd. Этот процесс называется «обнаружением».

Для обнаружения доступны два метода:

  • служба обнаружения etcd
  • записи DNS SRV

Обнаружение etcd

Чтобы лучше понять конструкцию протокола службы обнаружения, рекомендуется прочитать документацию протокола.

Срок жизни URL обнаружения

URL обнаружения идентифицирует уникальный кластер etcd. Вместо повторного использования существующего URL каждый экземпляр etcd получает новый общий URL обнаружения для начальной инициализации нового кластера.

Кроме того, URL обнаружения следует использовать ТОЛЬКО для исходной инициализации кластера. Для изменения состава уже работающего кластера обратитесь к руководству по динамическому изменению конфигурации .

Пользовательская служба обнаружения etcd

Для обнаружения и начальной инициализации используется существующий кластер. При использовании частного кластера etcd создайте URL следующим образом:

$ curl -X PUT https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3

Задание ключа размера по этому URL создаёт URL обнаружения с ожидаемым размером кластера 3.

В этом случае используется URL https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83, а участники etcd при запуске регистрируются в каталоге https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83.

Для каждого участника должен быть задан уникальный флаг имени. Подходящим выбором может быть Hostname или machine-id. Иначе обнаружение завершится ошибкой из-за повторяющегося имени.

Теперь запустим etcd с соответствующими флагами для каждого участника:

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83

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

Общедоступная служба обнаружения etcd

Если существующего кластера нет, используйте общедоступную службу обнаружения на discovery.etcd.io. Чтобы создать частный URL обнаружения через конечную точку “new”, выполните команду:

$ curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

Будет создан кластер с исходным размером 3 участника. Если размер не указан, по умолчанию также используется 3.

ETCD_DISCOVERY=https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
--discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

Для каждого участника должен быть задан уникальный флаг имени, иначе обнаружение завершится ошибкой из-за повторяющихся имён. Подходящим выбором может быть Hostname или machine-id.

Теперь запустим etcd с соответствующими флагами для каждого участника:

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

Каждый участник зарегистрируется в службе обнаружения, а после регистрации всех участников кластер начнёт работу.

Чтобы etcd подключался к службе обнаружения через HTTP-прокси, задайте переменную окружения ETCD_DISCOVERY_PROXY.

Случаи ошибок и предупреждений

Ошибки сервера обнаружения
$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcd: error: the cluster doesn’t have a size configuration value in https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de/_config
exit 1
Предупреждения

Это безвредное предупреждение означает, что URL обнаружения будет проигнорирован на этой машине.

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcdserver: discovery token ignored since a cluster has already been initialized. Valid log found at /var/lib/etcd

Обнаружение DNS

В качестве механизма обнаружения можно использовать записи SRV DNS. Флаг --discovery-srv задаёт доменное имя DNS, в котором находятся записи SRV обнаружения. При значении --discovery-srv example.com записи DNS SRV ищутся в следующем порядке:

  • _etcd-server-ssl._tcp.example.com
  • _etcd-server._tcp.example.com

Если найдена _etcd-server-ssl._tcp.example.com, etcd попытается выполнить начальную инициализацию через TLS.

Чтобы клиенты могли обнаружить кластер etcd, следующие записи DNS SRV ищутся в указанном порядке:

  • _etcd-client._tcp.example.com
  • _etcd-client-ssl._tcp.example.com

Если найдена _etcd-client-ssl._tcp.example.com, клиенты попытаются связаться с кластером etcd через SSL/TLS.

Если etcd использует TLS, запись SRV обнаружения (например, example.com) вместе с именем узла должна входить в DNS SAN сертификата SSL, иначе кластеризация завершится ошибкой с сообщениями журнала наподобие следующего:

[...] rejected connection from "10.0.1.11:53162" (error "remote error: tls: bad certificate", ServerName "example.com")

Если etcd использует TLS без пользовательского центра сертификации, домен обнаружения (например, example.com) должен совпадать с доменом записи SRV (например, infra1.example.com). Это снижает риск атак с поддельными записями SRV, указывающими на другой домен: такой домен мог бы иметь действительный сертификат PKI, но контролироваться неизвестной третьей стороной.

Флаг -discovery-srv-name дополнительно задаёт суффикс имени SRV, запрашиваемого при обнаружении. Используйте его, чтобы различать несколько кластеров etcd в одном домене. Например, при значениях discovery-srv=example.com и -discovery-srv-name=foo выполняются следующие запросы DNS SRV:

  • _etcd-server-ssl-foo._tcp.example.com
  • _etcd-server-foo._tcp.example.com

Создание записей DNS SRV

$ dig +noall +answer SRV _etcd-server._tcp.example.com
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra0.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra1.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra2.example.com.
$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com.  300  IN  A  10.0.1.10
infra1.example.com.  300  IN  A  10.0.1.11
infra2.example.com.  300  IN  A  10.0.1.12

Начальная инициализация кластера etcd через DNS

Участники кластера etcd могут объявлять доменные имена или IP-адреса; при начальной инициализации разрешаются записи DNS A. Начиная с 3.2 (в 3.1 выводятся предупреждения), --listen-peer-urls и --listen-client-urls отклоняют доменное имя при привязке сетевого интерфейса.

Разрешённый адрес из --initial-advertise-peer-urls должен совпадать с одним из разрешённых адресов в целях SRV. Участник etcd считывает разрешённый адрес, чтобы определить, принадлежит ли он кластеру, заданному записями SRV.

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra0.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra0.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380
$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra1.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra1.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380
$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra2.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra2.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380

Кластер также можно инициализировать по IP-адресам вместо доменных имён:

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.10:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.10:2379 \
--listen-client-urls http://10.0.1.10:2379 \
--listen-peer-urls http://10.0.1.10:2380
$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.11:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.11:2379 \
--listen-client-urls http://10.0.1.11:2379 \
--listen-peer-urls http://10.0.1.11:2380
$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.12:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.12:2379 \
--listen-client-urls http://10.0.1.12:2379 \
--listen-peer-urls http://10.0.1.12:2380

Начиная с v3.1.0 (кроме v3.2.9), когда etcd --discovery-srv=example.com настроен с TLS, сервер аутентифицирует одноранговые узлы и клиентов только в том случае, если предоставленные сертификаты содержат корневой домен example.com в поле Subject Alternative Name (SAN). См. примечания по DNS SRV .

Шлюз

Шлюз etcd — простой TCP-прокси, пересылающий сетевые данные кластеру etcd. Подробнее см. в руководстве по шлюзу .

Прокси

При заданном флаге --proxy etcd работает в режиме прокси . Этот режим поддерживает только API etcd v2; поддержка API v3 не планируется. Вместо этого после выпуска etcd 3.0 для API v3 появится новый прокси с расширенными возможностями.

Для настройки кластера etcd с прокси API v2 прочитайте документ о кластеризации в выпуске etcd 2.3 .

5 - Запуск кластеров etcd в контейнерах

Запуск etcd в Docker со статической начальной инициализацией

В этом руководстве показано, как запустить etcd в Docker с помощью процесса статической начальной инициализации .

Docker

Чтобы предоставить клиентам вне узла Docker доступ к API etcd, используйте IP-адрес узла контейнера. Способ получения IP-адреса подробно описан в документации docker inspect . Также можно передать команде docker run флаг --net=host, чтобы не помещать контейнер в отдельный сетевой стек.

Запуск одного узла etcd

При настройке etcd используйте IP-адрес узла:

export NODE1=192.168.1.21

Настройте том Docker для хранения данных etcd:

docker volume create --name etcd-data
export DATA_DIR="etcd-data"

Запустите последнюю версию etcd (на момент написания — v3.7.0):

ETCD_VERSION=v3.7.0
REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=gcr.io/etcd-development/etcd

docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name node1 \
  --initial-advertise-peer-urls http://${NODE1}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${NODE1}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster node1=http://${NODE1}:2380

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

etcdctl --endpoints=http://${NODE1}:2379 member list

Запуск кластера etcd из 3 узлов

REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=gcr.io/etcd-development/etcd

# For each machine
ETCD_VERSION=v3.7.0
TOKEN=my-etcd-token
CLUSTER_STATE=new
NAME_1=etcd-node-0
NAME_2=etcd-node-1
NAME_3=etcd-node-2
HOST_1=10.20.30.1
HOST_2=10.20.30.2
HOST_3=10.20.30.3
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
DATA_DIR=/var/lib/etcd

# For node 1
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

# For node 2
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

# For node 3
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=${DATA_DIR}:/etcd-data \
  --name etcd ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd \
  --data-dir=/etcd-data --name ${THIS_NAME} \
  --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://0.0.0.0:2380 \
  --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://0.0.0.0:2379 \
  --initial-cluster ${CLUSTER} \
  --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

Чтобы использовать etcdctl с API версии 3:

docker exec etcd /usr/local/bin/etcdctl put foo bar

Физические серверы

При развёртывании кластера etcd из 3 узлов на физических серверах могут быть полезны примеры из репозитория baremetal .

Подключение тома с сертификатами

Контейнер выпуска etcd не содержит корневых сертификатов по умолчанию. Чтобы использовать HTTPS с сертификатами, которым доверяет корневой центр, например для обнаружения, подключите каталог сертификатов к контейнеру etcd:

ETCD_VERSION=v3.7.0
REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=docker://gcr.io/etcd-development/etcd

rkt run \
  --insecure-options=image \
  --volume etcd-ssl-certs-bundle,kind=host,source=/etc/ssl/certs/ca-certificates.crt \
  --mount volume=etcd-ssl-certs-bundle,target=/etc/ssl/certs/ca-certificates.crt \
  ${REGISTRY}:${ETCD_VERSION} -- --name my-name \
  --initial-advertise-peer-urls http://localhost:2380 --listen-peer-urls http://localhost:2380 \
  --advertise-client-urls http://localhost:2379 --listen-client-urls http://localhost:2379 \
  --discovery https://discovery.etcd.io/c11fbcdc16972e45253491a24fcf45e1
ETCD_VERSION=v3.7.0
REGISTRY=quay.io/coreos/etcd
# available from v3.2.5
REGISTRY=gcr.io/etcd-development/etcd

docker run \
  -p 2379:2379 \
  -p 2380:2380 \
  --volume=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt \
  ${REGISTRY}:${ETCD_VERSION} \
  /usr/local/bin/etcd --name my-name \
  --initial-advertise-peer-urls http://localhost:2380 --listen-peer-urls http://localhost:2380 \
  --advertise-client-urls http://localhost:2379 --listen-client-urls http://localhost:2379 \
  --discovery https://discovery.etcd.io/86a9ff6c8cb8b4c4544c1a2f88f8b801

6 - Запуск кластеров etcd как StatefulSet Kubernetes

Запуск etcd как StatefulSet Kubernetes

Ниже показано, как выполнить статическую начальную инициализацию в виде StatefulSet Kubernetes.

Пример манифеста

Этот манифест содержит службу и StatefulSet для развёртывания статического кластера etcd в Kubernetes.

Если скопировать содержимое манифеста в файл etcd.yaml, его можно применить к кластеру следующей командой.

$ kubectl apply --filename etcd.yaml

После применения дождитесь готовности подов.

$ kubectl get pods
NAME     READY   STATUS    RESTARTS   AGE
etcd-0   1/1     Running   0          24m
etcd-1   1/1     Running   0          24m
etcd-2   1/1     Running   0          24m

Используемый в примере контейнер содержит etcdctl, который можно вызывать непосредственно внутри подов.

$ kubectl exec -it etcd-0 -- etcdctl member list -wtable
+------------------+---------+--------+-------------------------+-------------------------+------------+
|        ID        | STATUS  |  NAME  |       PEER ADDRS        |      CLIENT ADDRS       | IS LEARNER |
+------------------+---------+--------+-------------------------+-------------------------+------------+
| 4f98c3545405a0b0 | started | etcd-2 | http://etcd-2.etcd:2380 | http://etcd-2.etcd:2379 |      false |
| a394e0ee91773643 | started | etcd-0 | http://etcd-0.etcd:2380 | http://etcd-0.etcd:2379 |      false |
| d10297b8d2f01265 | started | etcd-1 | http://etcd-1.etcd:2380 | http://etcd-1.etcd:2379 |      false |
+------------------+---------+--------+-------------------------+-------------------------+------------+

Для развёртывания с самоподписанным сертификатом найдите в закомментированной конфигурации разделы, начинающиеся с ## TLS, и раскомментируйте нужные значения. Дополнительные инструкции по созданию сертификата с помощью cert-manager приведены ниже.

# file: etcd.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: etcd
  namespace: default
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app: etcd
  ##
  ## Ideally we would use SRV records to do peer discovery for initialization.
  ## Unfortunately discovery will not work without logic to wait for these to
  ## populate in the container. This problem is relatively easy to overcome by
  ## making changes to prevent the etcd process from starting until the records
  ## have populated. The documentation on statefulsets briefly talk about it.
  ##   https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#stable-network-id
  publishNotReadyAddresses: true
  ##
  ## The naming scheme of the client and server ports match the scheme that etcd
  ## uses when doing discovery with SRV records.
  ports:
  - name: etcd-client
    port: 2379
  - name: etcd-server
    port: 2380
  - name: etcd-metrics
    port: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: default
  name: etcd
spec:
  ##
  ## The service name is being set to leverage the service headlessly.
  ## https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
  serviceName: etcd
  ##
  ## If you are increasing the replica count of an existing cluster, you should
  ## also update the --initial-cluster-state flag as noted further down in the
  ## container configuration.
  replicas: 3
  ##
  ## For initialization, the etcd pods must be available to eachother before
  ## they are "ready" for traffic. The "Parallel" policy makes this possible.
  podManagementPolicy: Parallel
  ##
  ## To ensure availability of the etcd cluster, the rolling update strategy
  ## is used. For availability, there must be at least 51% of the etcd nodes
  ## online at any given time.
  updateStrategy:
    type: RollingUpdate
  ##
  ## This is label query over pods that should match the replica count.
  ## It must match the pod template's labels. For more information, see the
  ## following documentation:
  ##   https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
  selector:
    matchLabels:
      app: etcd
  ##
  ## Pod configuration template.
  template:
    metadata:
      ##
      ## The labeling here is tied to the "matchLabels" of this StatefulSet and
      ## "affinity" configuration of the pod that will be created.
      ##
      ## This example's labeling scheme is fine for one etcd cluster per
      ## namespace, but should you desire multiple clusters per namespace, you
      ## will need to update the labeling schema to be unique per etcd cluster.
      labels:
        app: etcd
      annotations:
        ##
        ## This gets referenced in the etcd container's configuration as part of
        ## the DNS name. It must match the service name created for the etcd
        ## cluster. The choice to place it in an annotation instead of the env
        ## settings is because there should only be 1 service per etcd cluster.
        serviceName: etcd
    spec:
      ##
      ## Configuring the node affinity is necessary to prevent etcd servers from
      ## ending up on the same hardware together.
      ##
      ## See the scheduling documentation for more information about this:
      ##   https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity
      affinity:
        ## The podAntiAffinity is a set of rules for scheduling that describe
        ## when NOT to place a pod from this StatefulSet on a node.
        podAntiAffinity:
          ##
          ## When preparing to place the pod on a node, the scheduler will check
          ## for other pods matching the rules described by the labelSelector
          ## separated by the chosen topology key.
          requiredDuringSchedulingIgnoredDuringExecution:
          ## This label selector is looking for app=etcd
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - etcd
            ## This topology key denotes a common label used on nodes in the
            ## cluster. The podAntiAffinity configuration essentially states
            ## that if another pod has a label of app=etcd on the node, the
            ## scheduler should not place another pod on the node.
            ##   https://kubernetes.io/docs/reference/labels-annotations-taints/#kubernetesiohostname
            topologyKey: "kubernetes.io/hostname"
      ##
      ## Containers in the pod
      containers:
      ## This example only has this etcd container.
      - name: etcd
        image: quay.io/coreos/etcd:v3.7.0
        imagePullPolicy: IfNotPresent
        ports:
        - name: etcd-client
          containerPort: 2379
        - name: etcd-server
          containerPort: 2380
        - name: etcd-metrics
          containerPort: 8080
        ##
        ## These probes will fail over TLS for self-signed certificates, so etcd
        ## is configured to deliver metrics over port 8080 further down.
        ##
        ## As mentioned in the "Monitoring etcd" page, /readyz and /livez were
        ## added in v3.5.12. Prior to this, monitoring required extra tooling
        ## inside the container to make these probes work.
        ##
        ## The values in this readiness probe should be further validated, it
        ## is only an example configuration.
        readinessProbe:
          httpGet:
            path: /readyz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 30
        ## The values in this liveness probe should be further validated, it
        ## is only an example configuration.
        livenessProbe:
          httpGet:
            path: /livez
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        env:
        ##
        ## Environment variables defined here can be used by other parts of the
        ## container configuration. They are interpreted by Kubernetes, instead
        ## of in the container environment.
        ##
        ## These env vars pass along information about the pod.
        - name: K8S_NAMESPACE
          valueFrom:
            fieldRef:
             fieldPath: metadata.namespace
        - name: HOSTNAME
          valueFrom:
            fieldRef:
             fieldPath: metadata.name
        - name: SERVICE_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['serviceName']
        ##
        ## Configuring etcdctl inside the container to connect to the etcd node
        ## in the container reduces confusion when debugging.
        - name: ETCDCTL_ENDPOINTS
          value: $(HOSTNAME).$(SERVICE_NAME):2379
        ##
        ## TLS client configuration for etcdctl in the container.
        ## These files paths are part of the "etcd-client-certs" volume mount.
        # - name: ETCDCTL_KEY
        #   value: /etc/etcd/certs/client/tls.key
        # - name: ETCDCTL_CERT
        #   value: /etc/etcd/certs/client/tls.crt
        # - name: ETCDCTL_CACERT
        #   value: /etc/etcd/certs/client/ca.crt
        ##
        ## Use this URI_SCHEME value for non-TLS clusters.
        - name: URI_SCHEME
          value: "http"
        ## TLS: Use this URI_SCHEME for TLS clusters.
        # - name: URI_SCHEME
        # value: "https"
        ##
        ## If you're using a different container, the executable may be in a
        ## different location. This example uses the full path to help remove
        ## ambiguity to you, the reader.
        ## Often you can just use "etcd" instead of "/usr/local/bin/etcd" and it
        ## will work because the $PATH includes a directory containing "etcd".
        command:
        - /usr/local/bin/etcd
        ##
        ## Arguments used with the etcd command inside the container.
        args:
        ##
        ## Configure the name of the etcd server.
        - --name=$(HOSTNAME)
        ##
        ## Configure etcd to use the persistent storage configured below.
        - --data-dir=/data
        ##
        ## In this example we're consolidating the WAL into sharing space with
        ## the data directory. This is not ideal in production environments and
        ## should be placed in it's own volume.
        - --wal-dir=/data/wal
        ##
        ## URL configurations are parameterized here and you shouldn't need to
        ## do anything with these.
        - --listen-peer-urls=$(URI_SCHEME)://0.0.0.0:2380
        - --listen-client-urls=$(URI_SCHEME)://0.0.0.0:2379
        - --advertise-client-urls=$(URI_SCHEME)://$(HOSTNAME).$(SERVICE_NAME):2379
        ##
        ## This must be set to "new" for initial cluster bootstrapping. To scale
        ## the cluster up, this should be changed to "existing" when the replica
        ## count is increased. If set incorrectly, etcd makes an attempt to
        ## start but fail safely.
        - --initial-cluster-state=new
        ##
        ## Token used for cluster initialization. The recommendation for this is
        ## to use a unique token for every cluster. This example parameterized
        ## to be unique to the namespace, but if you are deploying multiple etcd
        ## clusters in the same namespace, you should do something extra to
        ## ensure uniqueness amongst clusters.
        - --initial-cluster-token=etcd-$(K8S_NAMESPACE)
        ##
        ## The initial cluster flag needs to be updated to match the number of
        ## replicas configured. When combined, these are a little hard to read.
        ## Here is what a single parameterized peer looks like:
        ##   etcd-0=$(URI_SCHEME)://etcd-0.$(SERVICE_NAME):2380
        - --initial-cluster=etcd-0=$(URI_SCHEME)://etcd-0.$(SERVICE_NAME):2380,etcd-1=$(URI_SCHEME)://etcd-1.$(SERVICE_NAME):2380,etcd-2=$(URI_SCHEME)://etcd-2.$(SERVICE_NAME):2380
        ##
        ## The peer urls flag should be fine as-is.
        - --initial-advertise-peer-urls=$(URI_SCHEME)://$(HOSTNAME).$(SERVICE_NAME):2380
        ##
        ## This avoids probe failure if you opt to configure TLS.
        - --listen-metrics-urls=http://0.0.0.0:8080
        ##
        ## These are some configurations you may want to consider enabling, but
        ## should look into further to identify what settings are best for you.
        # - --auto-compaction-mode=periodic
        # - --auto-compaction-retention=10m
        ##
        ## TLS client configuration for etcd, reusing the etcdctl env vars.
        # - --client-cert-auth
        # - --trusted-ca-file=$(ETCDCTL_CACERT)
        # - --cert-file=$(ETCDCTL_CERT)
        # - --key-file=$(ETCDCTL_KEY)
        ##
        ## TLS server configuration for etcdctl in the container.
        ## These files paths are part of the "etcd-server-certs" volume mount.
        # - --peer-client-cert-auth
        # - --peer-trusted-ca-file=/etc/etcd/certs/server/ca.crt
        # - --peer-cert-file=/etc/etcd/certs/server/tls.crt
        # - --peer-key-file=/etc/etcd/certs/server/tls.key
        ##
        ## This is the mount configuration.
        volumeMounts:
        - name: etcd-data
          mountPath: /data
        ##
        ## TLS client configuration for etcdctl
        # - name: etcd-client-tls
        #   mountPath: "/etc/etcd/certs/client"
        #   readOnly: true
        ##
        ## TLS server configuration
        # - name: etcd-server-tls
        #   mountPath: "/etc/etcd/certs/server"
        #   readOnly: true
      volumes:
      ##
      ## TLS client configuration
      # - name: etcd-client-tls
      #   secret:
      #     secretName: etcd-client-tls
      #     optional: false
      ##
      ## TLS server configuration
      # - name: etcd-server-tls
      #   secret:
      #     secretName: etcd-server-tls
      #     optional: false
  ##
  ## This StatefulSet will uses the volumeClaimTemplate field to create a PVC in
  ## the cluster for each replica. These PVCs can not be easily resized later.
  volumeClaimTemplates:
  - metadata:
      name: etcd-data
    spec:
      accessModes: ["ReadWriteOnce"]
      ##
      ## In some clusters, it is necessary to explicitly set the storage class.
      ## This example will end up using the default storage class.
      # storageClassName: ""
      resources:
        requests:
          storage: 1Gi

Создание сертификатов

В этом разделе Helm используется для установки оператора cert-manager .

После установки cert-manager в кластере можно создавать самоподписанные сертификаты. Созданные сертификаты помещаются в объект Secret, который можно подключить к контейнерам как файлы.

Команда Helm для установки cert-manager:

$ helm upgrade --install --create-namespace --namespace cert-manager cert-manager cert-manager --repo https://charts.jetstack.io --set crds.enabled=true

Пример конфигурации ClusterIssuer для создания самоподписанных сертификатов:

# file: issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}

Этот манифест создаёт объекты Certificate для клиентских и серверных сертификатов, ссылаясь на ClusterIssuer “selfsigned”. Поле dnsNames должно содержать исчерпывающий список действительных имён узлов для сертификатов, создаваемых cert-manager.

# file: certificates.yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: etcd-server
  namespace: default
spec:
  secretName: etcd-server-tls
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
  commonName: etcd
  dnsNames:
  - etcd
  - etcd.default
  - etcd.default.svc.cluster.local
  - etcd-0
  - etcd-0.etcd
  - etcd-0.etcd.default
  - etcd-0.etcd.default.svc
  - etcd-0.etcd.default.svc.cluster.local
  - etcd-1
  - etcd-1.etcd
  - etcd-1.etcd.default
  - etcd-1.etcd.default.svc
  - etcd-1.etcd.default.svc.cluster.local
  - etcd-2
  - etcd-2.etcd
  - etcd-2.etcd.default
  - etcd-2.etcd.default.svc
  - etcd-2.etcd.default.svc.cluster.local
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: etcd-client
  namespace: default
spec:
  secretName: etcd-client-tls
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
  commonName: etcd
  dnsNames:
  - etcd
  - etcd.default
  - etcd.default.svc.cluster.local
  - etcd-0
  - etcd-0.etcd
  - etcd-0.etcd.default
  - etcd-0.etcd.default.svc
  - etcd-0.etcd.default.svc.cluster.local
  - etcd-1
  - etcd-1.etcd
  - etcd-1.etcd.default
  - etcd-1.etcd.default.svc
  - etcd-1.etcd.default.svc.cluster.local
  - etcd-2
  - etcd-2.etcd
  - etcd-2.etcd.default
  - etcd-2.etcd.default.svc
  - etcd-2.etcd.default.svc.cluster.local

7 - Режимы отказа

Виды отказов и устойчивость etcd к ним

В крупных развёртываниях машин отказы неизбежны. Машина выходит из строя при неисправности оборудования или программного обеспечения. Несколько машин могут отказать одновременно из-за перебоя питания или проблем с сетью. Разные виды отказов также могут происходить одновременно; перечислить все возможные случаи практически невозможно.

В этом разделе описаны виды отказов и механизмы, позволяющие etcd сохранять работоспособность. Почти любой конкретный отказ можно отнести к одной из этих категорий. Чтобы подготовиться к редким или невосстановимым отказам , всегда создавайте резервные копии кластера etcd.

Отказ меньшинства последователей

Если отказало менее половины последователей, кластер etcd продолжает принимать запросы и работать без серьёзных нарушений. Например, отказ двух последователей не влияет на работу кластера etcd из пяти участников. Однако клиенты теряют соединение с отказавшими участниками. Клиентские библиотеки должны скрывать эти перебои при чтении, автоматически переподключаясь к другим участникам. Операторам следует ожидать роста нагрузки на оставшихся участников из-за переподключений.

Отказ лидера

При отказе лидера кластер etcd автоматически выбирает нового. Выборы начинаются не мгновенно: из-за модели обнаружения отказов по тайм-ауту для выбора нового лидера требуется примерно один тайм-аут выборов.

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

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

Новый лидер автоматически продлевает тайм-ауты всех аренд. Благодаря этому аренда не истекает раньше предоставленного TTL, даже если её выдал прежний лидер.

Отказ большинства

Если отказало большинство участников, кластер etcd прекращает работу и больше не может принимать записи.

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

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

Разделение сети

Разделение сети похоже на отказ меньшинства последователей или лидера. Оно делит кластер etcd на две части: одну с большинством участников и другую с меньшинством. Сторона большинства становится доступным кластером, а сторона меньшинства остаётся недоступной. В etcd не возникает «расщепления сознания» (split-brain), поскольку участники явно добавляются и удаляются, а каждое такое изменение утверждается текущим большинством.

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

После восстановления сети сторона меньшинства автоматически распознаёт лидера большинства и восстанавливает своё состояние.

Отказ во время начальной инициализации

Начальная инициализация кластера успешна только в том случае, если запустились все обязательные участники. При любом отказе во время инициализации удалите каталоги данных на всех участниках и заново инициализируйте кластер с новым cluster-token или токеном обнаружения.

Разумеется, неудачно инициализированный кластер можно восстанавливать так же, как работающий. Однако почти всегда это требует больше времени и ресурсов, чем повторная инициализация, поскольку сохранять в таком кластере ещё нечего.

8 - Восстановление после аварии

Средства создания снимков и восстановления etcd v3

etcd рассчитан на отказы машин. Кластер etcd автоматически восстанавливается после временных сбоев, например перезагрузки машины, и допускает до (N-1)/2 постоянных отказов в кластере из N участников. При постоянном отказе из-за неисправности оборудования или повреждения диска участник теряет доступ к кластеру. Если кластер навсегда теряет более (N-1)/2 участников, происходит катастрофический отказ с безвозвратной потерей кворума. Без кворума кластер не может достичь консенсуса и продолжать принимать обновления.

Для восстановления после катастрофического отказа etcd v3 предоставляет средства снимков и восстановления, позволяющие воссоздать кластер без потери данных ключей v3. Восстановление ключей v2 описано в руководстве администратора v2 .

Создание снимка пространства ключей

Для восстановления кластера сначала нужен снимок пространства ключей одного участника etcd. Его можно создать с работающего участника командой etcdctl snapshot save либо скопировать файл member/snap/db из каталога данных etcd. Следующая команда сохраняет пространство ключей, обслуживаемое $ENDPOINT, в файл snapshot.db:

$ ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db

Обратите внимание: снимок из файла member/snap/db может потерять ещё не записанные данные, находящиеся в каталоге wal (журнале предзаписи).

Состояние снимка

Чтобы узнать ревизию и хеш снимка, используйте команду etcdutl snapshot status:

$ etcdutl snapshot status snapshot.db -w table
+---------+----------+------------+------------+
|  HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| 7ef846e |   485261 |      11642 |      94 MB |
+---------+----------+------------+------------+

Восстановление кластера

Разница ревизий

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

Это особенно опасно для Kubernetes с etcd, где контроллеры и операторы могут использовать так называемые informers как локальные кэши, получающие уведомления об обновлениях через наблюдения. Восстановление старой ревизии может неправильно обновить кэши и вызвать непредсказуемое, несогласованное поведение контроллеров.

При известных потребителях API наблюдения, локальных кэшированных копиях данных etcd или использовании Kubernetes настоятельно рекомендуется восстанавливать снимок с описанным ниже «увеличением ревизии».

Восстановление из снимка

Для восстановления кластера достаточно одного файла снимка “db”. Команда etcdutl snapshot restore создаёт новые каталоги данных etcd; все участники должны восстанавливаться из одного снимка. При восстановлении перезаписывается часть метаданных снимка, в частности идентификаторы участника и кластера, поэтому участник теряет прежнюю идентичность. Это не позволяет новому участнику случайно присоединиться к существующему кластеру. Следовательно, восстановление из снимка обязательно должно запускать новый логический кластер.

Простое восстановление выполняется так:

$ etcdutl snapshot restore snapshot.db --data-dir output-dir

Проверка целостности

Целостность снимка можно проверить при восстановлении. Снимок, созданный etcdctl snapshot save, содержит хеш целостности, который проверяет etcdutl snapshot restore. У снимка, скопированного из каталога данных, хеша нет, и восстановить его можно только с --skip-hash-check.

Восстановление с увеличением ревизии

Чтобы после восстановления ревизии никогда не уменьшались, укажите --bump-revision. Параметр принимает 64 bit целое число ревизий, добавляемых к текущей ревизии снимка. Каждая запись в etcd увеличивает ревизию на один, поэтому для снимка недельной давности достаточно увеличения на 1'000'000'000, если etcd обрабатывает менее 1500 записей в секунду.

Для контроллеров Kubernetes важно также пометить все ревизии, включая добавленные, как компактизированные через --mark-compacted. Тогда все наблюдения завершаются, а etcd не отвечает на запросы о ревизиях после создания снимка, фактически инвалидируя кэши informer.

Полная команда выглядит так:

$ etcdutl snapshot restore snapshot.db --bump-revision 1000000000 --mark-compacted --data-dir output-dir

Восстановление с обновлённым составом

Участники кластера etcd хранятся в самом etcd и обслуживаются алгоритмом консенсуса Raft. После полной потери кворума можно изменить место и способ формирования нового кластера, например создать его из совершенно нового набора участников.

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

$ etcdutl snapshot restore snapshot.db \
  --name m1 \
  --data-dir m1.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380

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

В качестве альтернативы при запуске etcd можно указать --force-new-cluster, чтобы перезаписать состав кластера, сохранив данные приложения. Это настоятельно не рекомендуется: если другие участники прежнего кластера ещё работают, произойдёт аварийное завершение. Обязательно регулярно сохраняйте снимки.

Сквозной пример End-2-End

Получите снимок работающего кластера командой:

$ etcdctl snapshot save snapshot.db

В продолжение примера следующие команды создают новые каталоги данных etcd (m1.etcd, m2.etcd, m3.etcd) для кластера из трёх участников:

$ etcdutl snapshot restore snapshot.db \
  --name m1 \
  --data-dir m1_data_dir.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380
$ etcdutl snapshot restore snapshot.db \
  --name m2 \
  --data-dir m2_data_dir.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host2:2380
$ etcdutl snapshot restore snapshot.db \
  --name m3 \
  --data-dir m3_data_dir.etcd \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host3:2380

Затем запустите etcd с новыми каталогами данных:

$ etcd \
  --name m1 \
  --data-dir m1_data_dir.etcd \
  --listen-client-urls http://host1:2379 \
  --advertise-client-urls http://host1:2379 \
  --listen-peer-urls http://host1:2380 &
$ etcd \
  --name m2 \
  --data-dir m2_data_dir.etcd \
  --listen-client-urls http://host2:2379 \
  --advertise-client-urls http://host2:2379 \
  --listen-peer-urls http://host2:2380 &
$ etcd \
  --name m3 \
  --data-dir m3_data_dir.etcd \
  --listen-client-urls http://host3:2379 \
  --advertise-client-urls http://host3:2379 \
  --listen-peer-urls http://host3:2380 &

Теперь восстановленный кластер etcd должен быть доступен и обслуживать пространство ключей из снимка.

Начиная с etcd v3.6, снимок данных создаётся только с помощью etcdctl, а восстановление выполняется через etcdutl. Если --data-dir не указан, значение --data-dir по умолчанию — <name>.etcd, где <name> берётся из --name. Например, если --data-dir отсутствует, а участников зовут m1, m2 и m3, каталогами --data-dir будут m1.etcd, m2.etcd и m3.etcd.

9 - Шлюз etcd

Назначение, сценарии применения и настройка шлюза etcd

Что такое шлюз etcd

Шлюз etcd — простой TCP-прокси, пересылающий сетевые данные кластеру etcd. Шлюз не хранит состояния и работает прозрачно: он не анализирует клиентские запросы и не вмешивается в ответы кластера. Он не завершает TLS-соединения, не выполняет TLS-рукопожатия от имени клиентов и не проверяет защищённость соединения.

Шлюз поддерживает несколько конечных точек серверов etcd и использует простую циклическую политику. Он направляет трафик только доступным конечным точкам и скрывает от клиентов отказы. В будущем могут появиться другие политики повторных попыток, например взвешенная циклическая.

Когда следует использовать шлюз etcd

Каждому приложению для доступа к etcd сначала требуется адрес клиентской конечной точки кластера. Если несколько приложений на одном сервере обращаются к одному кластеру etcd, каждое из них всё равно должно знать объявленные клиентские конечные точки. При изменении конечных точек кластера списки, возможно, придётся обновить во всех приложениях. Такая массовая перенастройка трудоёмка и подвержена ошибкам.

Шлюз etcd решает эту проблему, предоставляя стабильную локальную конечную точку. В типичной конфигурации на каждой машине работает шлюз, слушающий локальный адрес, а все приложения etcd подключаются к нему. Поэтому при изменении кластера достаточно обновить конечные точки шлюза, а не каждого приложения.

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

Когда не следует использовать шлюз etcd

  • Повышение производительности

Шлюз не предназначен для повышения производительности кластера etcd. Он не кэширует данные, не объединяет наблюдения и не пакетирует запросы. Команда etcd разрабатывает кэширующий прокси для улучшения масштабируемости кластера.

  • Работа в системе управления кластером

Развитые системы управления кластерами, такие как Kubernetes, изначально поддерживают обнаружение служб. Приложения могут обращаться к кластеру etcd по DNS-имени или виртуальному IP-адресу, управляемому системой. Например, kube-proxy выполняет ту же роль, что и шлюз etcd.

Запуск шлюза etcd

Рассмотрим кластер etcd со следующими статическими конечными точками:

ИмяАдресИмя узлаПорт
infra010.0.1.10infra0.example.com2379
infra110.0.1.11infra1.example.com2379
infra210.0.1.12infra2.example.com2379

Запустите шлюз etcd с этими статическими конечными точками:

$ etcd gateway start --endpoints=infra0.example.com:2379,infra1.example.com:2379,infra2.example.com:2379
2016-08-16 11:21:18.867350 I | tcpproxy: ready to proxy client requests to [...]

При обнаружении служб через DNS рассмотрим следующие записи DNS SRV:

$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com.  300  IN  A  10.0.1.10
infra1.example.com.  300  IN  A  10.0.1.11
infra2.example.com.  300  IN  A  10.0.1.12

Запустите шлюз etcd, чтобы получить конечные точки из записей DNS SRV:

$ etcd gateway start --discovery-srv=example.com
2016-08-16 11:21:18.867350 I | tcpproxy: ready to proxy client requests to [...]

Флаги конфигурации

Кластер etcd

–endpoints

  • Разделённый запятыми список целевых серверов etcd, которым пересылаются клиентские соединения.
  • По умолчанию: 127.0.0.1:2379
  • Порт обязателен.
  • Недопустимый пример: https://127.0.0.1:2379 (шлюз не завершает TLS). Шлюз не проверяет схему HTTP и не анализирует запросы, а только пересылает их заданным конечным точкам.

–discovery-srv

  • Домен DNS для начальной загрузки конечных точек кластера через записи SRV.
  • По умолчанию: не задано

Сеть

–listen-addr

  • Интерфейс и порт для привязки и приёма клиентских запросов.
  • По умолчанию: 127.0.0.1:23790

–retry-delay

  • Задержка перед повторной попыткой подключения к недоступным конечным точкам.
  • По умолчанию: 1m0s
  • Недопустимый пример: “123” (ожидается единица времени)

Безопасность

–insecure-discovery

  • Принимать небезопасные или уязвимые для атак посредника записи SRV.
  • По умолчанию: false

–trusted-ca-file

  • Путь к клиентскому файлу центра сертификации TLS, с помощью которого кластер etcd проверяет конечные точки, полученные при обнаружении SRV. Он используется ТОЛЬКО для аутентификации обнаруженных конечных точек, а не для создания соединений передачи данных. Шлюз никогда не завершает TLS-соединения и не создаёт их от имени клиентов.
  • По умолчанию: не задано

10 - Прокси gRPC

Не сохраняющий состояние обратный прокси etcd, работающий на уровне gRPC

Прокси gRPC — это не сохраняющий состояние обратный прокси etcd, работающий на уровне gRPC (L7). Он предназначен для снижения общей вычислительной нагрузки на основной кластер etcd. Для горизонтального масштабирования прокси объединяет запросы API наблюдения и аренды. Для защиты кластера от злоупотребляющих клиентов он кэширует запросы диапазонов ключей.

Прокси gRPC поддерживает несколько конечных точек сервера etcd. При запуске прокси случайным образом выбирает одну конечную точку сервера etcd. Она обслуживает все запросы, пока прокси не обнаружит её отказ. Обнаружив отказ конечной точки, прокси gRPC переключается на другую доступную точку, скрывая сбой от клиентов. В будущем могут поддерживаться и другие политики повторных попыток, например взвешенный циклический выбор.

Масштабируемый API наблюдения

Прокси gRPC объединяет несколько клиентских наблюдателей (c-watchers) за одним ключом или диапазоном в единственного наблюдателя (s-watcher), подключённого к серверу etcd. Все события от s-watcher прокси рассылает своим c-watchers.

Если N клиентов наблюдают за одним ключом, один прокси gRPC может снизить нагрузку наблюдения на сервер etcd с N до 1. Для дальнейшего распределения серверной нагрузки можно развернуть несколько прокси gRPC.

В следующем примере три клиента наблюдают за ключом A. Прокси gRPC объединяет трёх наблюдателей, создавая одного наблюдателя, подключённого к серверу etcd.

            +-------------+
            | etcd server |
            +------+------+
                   ^ watch key A (s-watcher)
                   |
           +-------+-----+
           | gRPC proxy  | <-------+
           |             |         |
           ++-----+------+         |watch key A (c-watcher)
watch key A ^     ^ watch key A    |
(c-watcher) |     | (c-watcher)    |
    +-------+-+  ++--------+  +----+----+
    |  client |  |  client |  |  client |
    |         |  |         |  |         |
    +---------+  +---------+  +---------+

Ограничения

Чтобы эффективно объединить несколько клиентских наблюдателей в одного, прокси gRPC по возможности присоединяет новые c-watchers к существующему s-watcher. Такой объединённый s-watcher может быть не синхронизирован с сервером etcd из-за сетевых задержек или буферизованных недоставленных событий. Если ревизия наблюдения не указана, прокси gRPC не гарантирует, что c-watcher начнёт наблюдение с самой последней ревизии хранилища. Например, наблюдатель клиента, подключённого к серверу etcd с ревизией 1000, начнёт с ревизии 1000. Наблюдатель клиента, подключённого через прокси gRPC, может начать с ревизии 990.

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

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

Масштабируемый API аренды

Чтобы поддерживать аренды активными, клиент должен установить как минимум один поток gRPC к серверу etcd для отправки периодических сигналов активности. Если рабочая нагрузка etcd включает интенсивную работу с арендами, распределённую между множеством клиентов, эти потоки могут приводить к чрезмерной загрузке CPU. Чтобы сократить общее количество потоков в основном кластере, прокси поддерживает объединение потоков аренды.

Если N клиентов обновляют аренды, один прокси gRPC снижает потоковую нагрузку на сервер etcd с N до 1. В развёртывании можно использовать дополнительные прокси gRPC, чтобы распределить потоки между несколькими прокси.

В следующем примере три клиента обновляют три независимые аренды (L1, L2 и L3). Прокси gRPC объединяет три клиентских потока аренды (c-streams) в один поток поддержания аренды активной (s-stream), подключённый к серверу etcd. Прокси пересылает сигналы активности аренды с клиентской стороны из c-streams в s-stream, а затем возвращает ответы соответствующим c-streams.

          +-------------+
          | etcd server |
          +------+------+
                 ^
                 | heartbeat L1, L2, L3
                 | (s-stream)
                 v
         +-------+-----+
         | gRPC proxy  +<-----------+
         +---+------+--+            | heartbeat L3
             ^      ^               | (c-stream)
heartbeat L1 |      | heartbeat L2  |
(c-stream)   v      v (c-stream)    v
      +------+-+  +-+------+  +-----+--+
      | client |  | client |  | client |
      +--------+  +--------+  +--------+

Защита от злоупотребляющих клиентов

Прокси gRPC кэширует ответы на запросы, когда это не нарушает требований согласованности. Так сервер etcd можно защитить от злоупотребляющих клиентов с плотными циклами for.

Запуск прокси gRPC etcd

Рассмотрим кластер etcd со следующими статическими конечными точками:

ИмяАдресИмя узла
infra010.0.1.10infra0.example.com
infra110.0.1.11infra1.example.com
infra210.0.1.12infra2.example.com

Запустите прокси gRPC etcd для использования этих статических конечных точек следующей командой:

$ etcd grpc-proxy start --endpoints=infra0.example.com,infra1.example.com,infra2.example.com --listen-addr=127.0.0.1:2379

Прокси gRPC etcd запускается и слушает порт 2379. Он пересылает клиентские запросы одной из трёх указанных выше конечных точек.

Отправка запросов через прокси:

$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 put foo bar
OK
$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 get foo
foo
bar

Синхронизация клиентских конечных точек и разрешение имён

Прокси поддерживает регистрацию своих конечных точек для обнаружения путём записи в заданную пользователем конечную точку. Это решает две задачи. Во-первых, клиенты могут синхронизировать свои конечные точки с набором конечных точек прокси для высокой доступности. Во-вторых, прокси становится поставщиком конечных точек для разрешения имён gRPC в etcd.

Зарегистрируйте прокси, указав определённый пользователем префикс:

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23790 \
  --advertise-client-url=127.0.0.1:23790 \
  --resolver-prefix="___grpc_proxy_endpoint" \
  --resolver-ttl=60

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23791 \
  --advertise-client-url=127.0.0.1:23791 \
  --resolver-prefix="___grpc_proxy_endpoint" \
  --resolver-ttl=60

В списке участников прокси перечислит всех своих участников:

ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23790 member list --write-out table

+----+---------+--------------------------------+------------+-----------------+
| ID | STATUS  |              NAME              | PEER ADDRS |  CLIENT ADDRS   |
+----+---------+--------------------------------+------------+-----------------+
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23791 |
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23790 |
+----+---------+--------------------------------+------------+-----------------+

Это позволяет клиентам автоматически обнаруживать конечные точки прокси через Sync:

cli, err := clientv3.New(clientv3.Config{
    Endpoints: []string{"http://localhost:23790"},
})
if err != nil {
    log.Fatal(err)
}
defer cli.Close()

// fetch registered grpc-proxy endpoints
if err := cli.Sync(context.Background()); err != nil {
    log.Fatal(err)
}

Обратите внимание: если прокси настроен без префикса разрешения,

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23792 \
  --advertise-client-url=127.0.0.1:23792

API списка участников прокси grpc-proxy возвращает его собственный advertise-client-url:

ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23792 member list --write-out table

+----+---------+--------------------------------+------------+-----------------+
| ID | STATUS  |              NAME              | PEER ADDRS |  CLIENT ADDRS   |
+----+---------+--------------------------------+------------+-----------------+
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23792 |
+----+---------+--------------------------------+------------+-----------------+

Пространства имён

Предположим, приложению нужен полный контроль над всем пространством ключей, однако кластер etcd используется совместно с другими приложениями. Чтобы все приложения могли работать, не мешая друг другу, прокси может разделить пространство ключей etcd так, чтобы клиентам казалось, что они имеют доступ ко всему пространству. Если прокси передан флаг --namespace, все поступающие в него клиентские запросы преобразуются: к ключам добавляется заданный пользователем префикс. Обращения к кластеру etcd выполняются под этим префиксом, а прокси удаляет префикс из ответов; для клиента всё выглядит так, будто префикса нет.

Чтобы назначить прокси пространство имён, запустите его с --namespace:

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23790 \
  --namespace=my-prefix/

Теперь обращения к прокси прозрачно снабжаются префиксом в кластере etcd:

$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 put my-key abc
# OK
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 get my-key
# my-key
# abc
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:2379 get my-prefix/my-key
# my-prefix/my-key
# abc

Терминирование TLS

Терминируйте TLS защищённого кластера etcd с помощью прокси gRPC, обслуживающего незашифрованную локальную конечную точку.

Для проверки запустите одноузловой кластер etcd с клиентским https:

$ etcd --listen-client-urls https://localhost:2379 --advertise-client-urls https://localhost:2379 --cert-file=peer.crt --key-file=peer.key --trusted-ca-file=ca.crt --client-cert-auth

Убедитесь, что клиентский порт обслуживает https:

# fails
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:2379 endpoint status
# works
$ ETCDCTL_API=3 etcdctl --endpoints=https://localhost:2379 --cert=client.crt --key=client.key --cacert=ca.crt endpoint status

Затем запустите прокси gRPC на localhost:12379, подключив его к конечной точке etcd https://localhost:2379 с помощью клиентских сертификатов:

$ etcd grpc-proxy start --endpoints=https://localhost:2379 --listen-addr localhost:12379 --cert client.crt --key client.key --cacert=ca.crt --insecure-skip-tls-verify &

Наконец, проверьте терминирование TLS, записав ключ в прокси по http:

$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:12379 put abc def
# OK

Метрики и состояние

Прокси gRPC предоставляет конечные точки /health и Prometheus /metrics для участников etcd, заданных через --endpoints. В качестве альтернативы флагом --metrics-addr можно определить дополнительный URL, отвечающий на запросы к конечным точкам /metrics и /health.

$ etcd grpc-proxy start \
  --endpoints https://localhost:2379 \
  --metrics-addr https://0.0.0.0:4443 \
  --listen-addr 127.0.0.1:23790 \
  --key client.key \
  --key-file proxy-server.key \
  --cert client.crt \
  --cert-file proxy-server.crt \
  --cacert ca.pem \
  --trusted-ca-file proxy-ca.pem

Известная проблема

Основной интерфейс прокси обслуживает как HTTP2, так и HTTP/1.1. Если прокси настроен с TLS, как в примере выше, при обращении к слушающему интерфейсу клиентом наподобие cURL для получения /metrics или /health потребуется явно указать в запросе протокол HTTP/1.1. При использовании флага --metrics-addr вторичный интерфейс этого не требует.

 $ curl --cacert proxy-ca.pem --key proxy-client.key --cert proxy-client.crt https://127.0.0.1:23790/metrics --http1.1

11 - Рекомендации по оборудованию

Рекомендации по оборудованию для администрирования кластеров etcd

Для разработки и тестирования etcd обычно хорошо работает с ограниченными ресурсами; его часто запускают на ноутбуке или дешёвой облачной машине. Однако для правильной эксплуатации промышленных кластеров полезны рекомендации по оборудованию. Это не строгие правила, а хорошая отправная точка для надёжного развёртывания. Перед вводом в эксплуатацию всегда проверяйте систему с имитацией рабочей нагрузки.

CPU

Немногим развёртываниям etcd требуется большая вычислительная мощность. Типичному кластеру для стабильной работы достаточно от двух до четырёх ядер. Сильно нагруженные развёртывания, обслуживающие тысячи клиентов или десятки тысяч запросов в секунду, обычно ограничены CPU, поскольку etcd может отдавать запросы из памяти. Им, как правило, требуется от восьми до шестнадцати выделенных ядер.

Память

etcd потребляет относительно мало памяти, но производительность всё же зависит от её достаточного объёма. Сервер активно кэширует данные ключей и значений, а большую часть оставшейся памяти использует для отслеживания наблюдателей. Обычно достаточно 8GB. Для тяжёлых развёртываний с тысячами наблюдателей и миллионами ключей выделяйте от 16GB до 64GB в зависимости от нагрузки.

Диски

Быстрые диски — наиболее важный фактор производительности и стабильности etcd.

Медленный диск увеличивает задержку запросов и может нарушить стабильность кластера. Поскольку протокол консенсуса etcd требует постоянной записи метаданных в журнал, большинство участников должно записывать каждый запрос на диск. Кроме того, etcd инкрементно сохраняет контрольные точки состояния, чтобы усекать журнал. Если записи занимают слишком много времени, heartbeat может завершиться по тайм-ауту и вызвать выборы, подрывая стабильность. Проверить достаточную скорость диска можно инструментом вроде fio ; пример приведён здесь .

etcd очень чувствителен к задержке записи. Обычно требуется 50 последовательных IOPS, например от диска 7200 RPM. Для сильно нагруженных кластеров рекомендуется 500 последовательных IOPS, что обеспечивает типичный локальный SSD или высокопроизводительное виртуальное блочное устройство. Большинство облачных провайдеров публикуют параллельные, а не последовательные IOPS; опубликованное значение может быть в 10x раз выше последовательного. Фактические последовательные IOPS измеряйте с помощью diskbench или fio .

etcd требует умеренной пропускной способности диска, но более быстрый диск сокращает восстановление, когда отказавший участник догоняет кластер. Обычно 10MB/s позволяет восстановить 100MB данных за 15 секунд. Для крупных кластеров рекомендуется 100MB/s или больше, чтобы восстановить 1GB за 15 секунд.

По возможности используйте SSD для хранилища etcd. SSD обычно обеспечивает меньшую и более стабильную задержку записи, повышая стабильность и надёжность. Если используются вращающиеся диски, выбирайте самые быстрые, например 15,000 RPM. RAID 0 также эффективно увеличивает скорость как вращающихся дисков, так и SSD. При наличии как минимум трёх участников зеркалирование и варианты RAID с контролем чётности не нужны: согласованная репликация etcd уже обеспечивает высокую доступность.

Сеть

Кластеру etcd из нескольких участников нужна быстрая и надёжная сеть. Чтобы одновременно сохранять согласованность и устойчивость к разделению, в ненадёжной сети с разрывами разделов доступность будет низкой. Малая задержка ускоряет обмен между участниками, а высокая пропускная способность сокращает восстановление отказавшего участника. Для обычного развёртывания достаточно 1GbE; в крупном кластере сеть 10GbE уменьшает среднее время восстановления.

По возможности размещайте участников etcd в одном центре обработки данных, чтобы избежать дополнительной задержки и снизить вероятность разделения сети. Если требуется домен отказа в другом центре, выбирайте ближайший. Развёртывание между центрами также описано в документации по настройке .

Примеры аппаратных конфигураций

Ниже приведено несколько примеров для AWS и GCE. Несмотря на уже сказанное, необходимо ещё раз подчеркнуть: перед промышленной эксплуатацией администраторы должны проверить развёртывание etcd с имитацией нагрузки.

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

Малый кластер

Малый кластер обслуживает менее 100 клиентов и 200 запросов в секунду и хранит не более 100MB данных.

Пример нагрузки: кластер Kubernetes из 50 узлов

ПровайдерТипvCPUПамять (GB)Максимальные параллельные IOPSПропускная способность диска (MB/s)
AWSm4.large28360056.25
GCEn1-standard-2 + 50GB PD SSD27.5150025

Средний кластер

Средний кластер обслуживает менее 500 клиентов и 1,000 запросов в секунду и хранит не более 500MB данных.

Пример нагрузки: кластер Kubernetes из 250 узлов

ПровайдерТипvCPUПамять (GB)Максимальные параллельные IOPSПропускная способность диска (MB/s)
AWSm4.xlarge416600093.75
GCEn1-standard-4 + 150GB PD SSD415450075

Большой кластер

Большой кластер обслуживает менее 1,500 клиентов и 10,000 запросов в секунду и хранит не более 1GB данных.

Пример нагрузки: кластер Kubernetes из 1,000 узлов

ПровайдерТипvCPUПамять (GB)Максимальные параллельные IOPSПропускная способность диска (MB/s)
AWSm4.2xlarge8328000125
GCEn1-standard-8 + 250GB PD SSD8307500125

Кластер xLarge

Кластер xLarge обслуживает более 1,500 клиентов и более 10,000 запросов в секунду и хранит более 1GB данных.

Пример нагрузки: кластер Kubernetes из 3,000 узлов

ПровайдерТипvCPUПамять (GB)Максимальные параллельные IOPSПропускная способность диска (MB/s)
AWSm4.4xlarge166416,000250
GCEn1-standard-16 + 500GB PD SSD166015,000250

12 - Обслуживание

Руководство по периодическому обслуживанию кластера etcd

Обзор

Для сохранения надёжности кластер etcd нуждается в периодическом обслуживании. В зависимости от требований использующего etcd приложения такое обслуживание обычно можно автоматизировать и выполнять без простоя или существенного снижения производительности.

Все операции обслуживания etcd управляют ресурсами хранилища, занятыми пространством ключей etcd. Недостаточный контроль его размера предотвращается квотами дискового пространства: если у участника etcd заканчивается место, квота активирует аварийные сигналы для всего кластера и переводит систему в режим обслуживания с ограниченными операциями. Чтобы не исчерпать место для записи в пространство ключей, его историю необходимо компактизировать. Само дисковое пространство можно освободить дефрагментацией участников etcd. Наконец, регулярное резервное копирование состояния участника с помощью снимков позволяет восстановиться после непреднамеренной логической потери или повреждения данных, вызванных ошибкой эксплуатации.

Хранение журнала Raft

Параметр etcd --snapshot-count задаёт количество применённых записей Raft, которые хранятся в памяти до компактизации. Когда достигается значение --snapshot-count, сервер сначала сохраняет данные снимка на диск, а затем усекает старые записи. Если медленный последователь запрашивает журналы до уже компактизированного индекса, лидер отправляет снимок, вынуждая последователь перезаписать своё состояние.

Большее значение --snapshot-count удерживает больше записей Raft в памяти до создания снимка, тем самым вызывая повторяющееся повышенное потребление памяти . Поскольку лидер дольше хранит последние записи Raft, у медленного последователя больше времени, чтобы догнать его до создания снимка лидером. Выбор --snapshot-count — компромисс между повышенным потреблением памяти и лучшей доступностью медленных последователей.

Начиная с v3.2 значение --snapshot-count по умолчанию изменено с 10,000 на 100,000 .

С точки зрения производительности значение --snapshot-count больше 100,000 может снизить пропускную способность записи. Большое количество объектов в памяти способно замедлить фазу маркировки Go GC runtime.scanobject , а редкое освобождение памяти замедляет её выделение. Производительность зависит от рабочей нагрузки и системного окружения. Однако в целом слишком частая компактизация ухудшает доступность кластера и пропускную способность записи. Слишком редкая компактизация также вредна, поскольку создаёт чрезмерную нагрузку на сборщик мусора Go. Дополнительные результаты исследований приведены в материале Understanding Performance Aspects of etcd and Raft .

Компактизация истории: база данных Key-Value API v3

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

Пространство ключей можно компактизировать автоматически с помощью политики хранения истории etcd с временным окном либо вручную с помощью etcdctl. Метод etcdctl обеспечивает точное управление процессом компактизации, тогда как автоматическая компактизация подходит приложениям, которым история ключей нужна лишь в течение определённого времени.

Компактизация, инициированная etcdctl, выполняется следующим образом:

# compact up to revision 3
$ etcdctl compact 3

Ревизии, предшествующие ревизии компактизации, становятся недоступны:

$ etcdctl get --rev=2 somekey
Error:  rpc error: code = 11 desc = etcdserver: mvcc: required revision has been compacted

Автоматическая компактизация

Для автоматической компактизации пространства ключей в etcd можно задать параметры --auto-compaction-mode и --auto-compaction-retention. Существует два режима компактизации: periodic (по умолчанию) и revision.

Периодическая компактизация

Периодическая компактизация сохраняет ограниченное временным окном количество истории пространства ключей:

# keep one hour of history
$ etcd --auto-compaction-retention=1h

Значение срока хранения определяет, какой объём истории сохранять. Запись не будет компактизирована примерно до истечения указанного времени с момента создания. Это позволяет медленным наблюдателям успеть наверстать отставание в пределах окна хранения.

Если срок хранения превышает 1 час, etcd выполняет компактизацию каждый час, сохраняя полное окно хранения. Если срок хранения не превышает 1 часа, etcd выполняет компактизацию с интервалом, равным сроку хранения.

Например, с --auto-compaction-retention=10h etcd ждёт 10 часов до первой компактизации, а затем выполняет её каждый час:

0hr  (rev = 1)
1hr  (rev = 10)
...
8hr  (rev = 80)
9hr  (rev = 90)
10hr (rev = 100, Compact(1))
11hr (rev = 110, Compact(10))
...

Рекомендуемые значения зависят от сценария использования:

  • Частые обновления одних и тех же ключей: короткий период, например 1h или 30m
  • Редкие обновления: более длительный период, например 24h, 48h или 72h
  • Значение общего назначения по умолчанию: 10h

Компактизация по ревизиям

Компактизация по ревизиям сохраняет фиксированное количество ревизий:

# keep 1000 revisions
$ etcd --auto-compaction-mode=revision --auto-compaction-retention=1000

etcd выполняет проверку каждые 5 минут и компактизирует на ревизии "latest revision" - 1000. Например, если последняя ревизия равна 30000, компактизация выполняется на ревизии 29000.

Дефрагментация

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

Дефрагментация возвращает это дисковое пространство файловой системе. Она запускается отдельно для каждого участника, что позволяет избежать всплесков задержки во всём кластере.

Для дефрагментации участника etcd используйте команду etcdctl defrag:

$ etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
Предупреждение

Учтите, что дефрагментация работающего участника блокирует чтение и запись данных в системе на время перестроения его состояния

Предупреждение

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

Выполните дефрагментацию всех конечных точек кластера, связанных с конечной точкой по умолчанию:

$ etcdctl defrag --cluster
Finished defragmenting etcd member[http://127.0.0.1:2379]
Finished defragmenting etcd member[http://127.0.0.1:22379]
Finished defragmenting etcd member[http://127.0.0.1:32379]

Чтобы напрямую дефрагментировать каталог данных etcd при остановленном etcd, используйте команду:

etcdutl defrag --data-dir <path-to-etcd-data-dir>

Квота дискового пространства

Квота дискового пространства в etcd обеспечивает надёжную работу кластера. Без неё при чрезмерном росте пространства ключей производительность etcd может ухудшиться либо место в хранилище может полностью закончиться, что приведёт к непредсказуемому поведению кластера. Если база данных бэкенда пространства ключей любого участника превышает квоту, etcd активирует аварийный сигнал для всего кластера и переводит его в режим обслуживания, принимающий только операции чтения и удаления ключей. Возобновить нормальную работу кластер сможет лишь после освобождения достаточного места в пространстве ключей, дефрагментации базы данных бэкенда и сброса аварийного сигнала квоты.

По умолчанию etcd задаёт консервативную квоту дискового пространства, подходящую большинству приложений, однако в командной строке её можно указать в байтах:

# set a very small 16 MiB quota
$ etcd --quota-backend-bytes=$((16*1024*1024))

Квоту дискового пространства можно активировать с помощью цикла:

# fill keyspace
$ while [ 1 ]; do dd if=/dev/urandom bs=1024 count=1024  | ETCDCTL_API=3 etcdctl put key  || break; done
...
Error:  rpc error: code = 8 desc = etcdserver: mvcc: database space exceeded
# confirm quota space is exceeded
$ ETCDCTL_API=3 etcdctl --write-out=table endpoint status
+----------------+------------------+-----------+---------+-----------+-----------+------------+
|    ENDPOINT    |        ID        |  VERSION  | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+----------------+------------------+-----------+---------+-----------+-----------+------------+
| 127.0.0.1:2379 | bf9071f4639c75cc | 2.3.0+git | 18 MB   | true      |         2 |       3332 |
+----------------+------------------+-----------+---------+-----------+-----------+------------+
# confirm alarm is raised
$ ETCDCTL_API=3 etcdctl alarm list
memberID:13803658152347727308 alarm:NOSPACE

Удаление избыточных данных пространства ключей и дефрагментация базы данных бэкенда вернут кластер в пределы квоты:

# get current revision
$ rev=$(ETCDCTL_API=3 etcdctl --endpoints=:2379 endpoint status --write-out="json" | egrep -o '"revision":[0-9]*' | egrep -o '[0-9].*')
# compact away all old revisions
$ ETCDCTL_API=3 etcdctl compact $rev
compacted revision 1516
# defragment away excessive space
$ ETCDCTL_API=3 etcdctl defrag
Finished defragmenting etcd member[127.0.0.1:2379]
# disarm alarm
$ ETCDCTL_API=3 etcdctl alarm disarm
memberID:13803658152347727308 alarm:NOSPACE
# test puts are allowed again
$ ETCDCTL_API=3 etcdctl put newkey 123
OK

Метрика etcd_mvcc_db_total_size_in_use_in_bytes показывает фактическое использование базы данных после компактизации истории, а etcd_debugging_mvcc_db_total_size_in_bytes — размер базы данных с учётом свободного места, ожидающего дефрагментации. Вторая метрика увеличивается только тогда, когда первая приближается к ней; следовательно, когда обе метрики близки к квоте, необходимо компактизировать историю, чтобы избежать активации квоты дискового пространства.

Начиная с v3.4, etcd_debugging_mvcc_db_total_size_in_bytes переименована в etcd_mvcc_db_total_size_in_bytes.

Предупреждение

Для запроса Put/Txn/LeaseGrant можно получить ошибку ErrGRPCNoSpace, хотя запись в бэкенде окажется успешной. Это возможно потому, что etcd проверяет квоту дискового пространства на уровне API и на внутреннем уровне Apply, причём уровень Apply только активирует аварийный сигнал NOSPACE, не блокируя выполнение транзакции.

Резервное копирование снимка

Регулярное создание снимков кластера etcd обеспечивает долговечную резервную копию пространства ключей etcd. Благодаря периодическим снимкам базы данных бэкенда участника кластер etcd можно восстановить до момента времени с заведомо исправным состоянием.

Снимок создаётся с помощью etcdctl:

$ etcdctl snapshot save backup.db
$ etcdutl --write-out=table snapshot status backup.db
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| fe01cf57 |       10 |          7 | 2.1 MB     |
+----------+----------+------------+------------+

13 - Мониторинг etcd

Мониторинг etcd для проверки состояния системы и отладки кластера

Каждый сервер etcd предоставляет локальные сведения мониторинга через конечные точки HTTP на клиентском порту. Эти данные полезны для проверки состояния системы и отладки кластера.

Конечная точка отладки

Если задан --log-level=debug, сервер etcd экспортирует отладочные сведения по пути /debug на клиентском порту. Используйте --log-level=debug осторожно: он снижает производительность и включает подробное ведение журнала.

/debug/pprof — стандартная конечная точка профилирования среды выполнения Go. Она позволяет профилировать использование CPU, кучи, мьютексов и goroutine. В примере go tool pprof получает 10 функций, на которые etcd тратит больше всего времени:

$ go tool pprof http://localhost:2379/debug/pprof/profile
Fetching profile from http://localhost:2379/debug/pprof/profile
Please wait... (30s)
Saved profile in /home/etcd/pprof/pprof.etcd.localhost:2379.samples.cpu.001.pb.gz
Entering interactive mode (type "help" for commands)
(pprof) top10
310ms of 480ms total (64.58%)
Showing top 10 nodes out of 157 (cum >= 10ms)
    flat  flat%   sum%        cum   cum%
   130ms 27.08% 27.08%      130ms 27.08%  runtime.futex
    70ms 14.58% 41.67%       70ms 14.58%  syscall.Syscall
    20ms  4.17% 45.83%       20ms  4.17%  github.com/coreos/etcd/vendor/golang.org/x/net/http2/hpack.huffmanDecode
    20ms  4.17% 50.00%       30ms  6.25%  runtime.pcvalue
    20ms  4.17% 54.17%       50ms 10.42%  runtime.schedule
    10ms  2.08% 56.25%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/coreos/etcd/etcdserver.(*EtcdServer).AuthInfoFromCtx
    10ms  2.08% 58.33%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/coreos/etcd/etcdserver.(*EtcdServer).Lead
    10ms  2.08% 60.42%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/coreos/etcd/pkg/wait.(*timeList).Trigger
    10ms  2.08% 62.50%       10ms  2.08%  github.com/coreos/etcd/vendor/github.com/prometheus/client_golang/prometheus.(*MetricVec).hashLabelValues
    10ms  2.08% 64.58%       10ms  2.08%  github.com/coreos/etcd/vendor/golang.org/x/net/http2.(*Framer).WriteHeaders

Конечная точка /debug/requests показывает трассировки gRPC и статистику производительности в веб-браузере. Например, так выглядит запрос Range для ключа abc:

When	Elapsed (s)
2017/08/18 17:34:51.999317 	0.000244 	/etcdserverpb.KV/Range
17:34:51.999382 	 .    65 	... RPC: from 127.0.0.1:47204 deadline:4.999377747s
17:34:51.999395 	 .    13 	... recv: key:"abc"
17:34:51.999499 	 .   104 	... OK
17:34:51.999535 	 .    36 	... sent: header:<cluster_id:14841639068965178418 member_id:10276657743932975437 revision:15 raft_term:17 > kvs:<key:"abc" create_revision:6 mod_revision:14 version:9 value:"asda" > count:1

Конечная точка метрик

Каждый сервер etcd экспортирует метрики по пути /metrics на клиентском порту и, необязательно, по адресам из --listen-metrics-urls.

Метрики можно получить с помощью curl:

$ curl -L http://localhost:2379/metrics | grep -v debugging # ignore unstable debugging metrics

# HELP etcd_disk_backend_commit_duration_seconds The latency distributions of commit called by backend.
# TYPE etcd_disk_backend_commit_duration_seconds histogram
etcd_disk_backend_commit_duration_seconds_bucket{le="0.002"} 72756
etcd_disk_backend_commit_duration_seconds_bucket{le="0.004"} 401587
etcd_disk_backend_commit_duration_seconds_bucket{le="0.008"} 405979
etcd_disk_backend_commit_duration_seconds_bucket{le="0.016"} 406464
...

Проверка состояния

Начиная с v3.3.0, все адреса из --listen-metrics-urls отвечают не только через /metrics, но и через /health. Это полезно, когда стандартная конечная точка защищена взаимной клиентской аутентификацией TLS, но балансировщику нагрузки или службе мониторинга всё ещё нужен доступ к проверке состояния.

Начиная с v3.4 добавлены две конечные точки: /livez и /readyz.

  • /livez показывает, работает ли процесс или его необходимо перезапустить;
  • /readyz показывает, готов ли процесс обслуживать трафик.

Архитектура конечных точек описана в KEP .

Каждая конечная точка включает несколько отдельных проверок. Параметр verbose выводит подробности проверок и их состояние, например:

curl -k http://localhost:2379/readyz?verbose

Ответ будет выглядеть примерно так:

[+]data_corruption ok
[+]serializable_read ok
[+]linearizable_read ok
ok

API HTTP также позволяет исключать отдельные проверки, например:

curl -k http://localhost:2379/readyz?exclude=data_corruption

Prometheus

Самый простой способ получать и сохранять метрики etcd — запустить службу мониторинга Prometheus .

Сначала установите Prometheus:

PROMETHEUS_VERSION="2.0.0"
wget https://github.com/prometheus/prometheus/releases/download/v$PROMETHEUS_VERSION/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz -O /tmp/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz
tar -xvzf /tmp/prometheus-$PROMETHEUS_VERSION.linux-amd64.tar.gz --directory /tmp/ --strip-components=1
/tmp/prometheus -version

Настройте сборщик Prometheus на конечные точки кластера etcd:

cat > /tmp/test-etcd.yaml <<EOF
global:
  scrape_interval: 10s
scrape_configs:
  - job_name: test-etcd
    static_configs:
    - targets: ['10.240.0.32:2379','10.240.0.33:2379','10.240.0.34:2379']
EOF
cat /tmp/test-etcd.yaml

Запустите обработчик Prometheus:

nohup /tmp/prometheus \
    -config.file /tmp/test-etcd.yaml \
    -web.listen-address ":9090" \
    -storage.local.path "test-etcd.data" >> /tmp/test-etcd.log  2>&1 &

Теперь Prometheus будет собирать метрики etcd каждые 10 секунд.

Оповещения

Для кластеров etcd v3 доступен набор стандартных оповещений Prometheus.

Примечание

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

Grafana

Grafana имеет встроенную поддержку Prometheus; добавьте источник данных Prometheus:

Name:   test-etcd
Type:   Prometheus
Url:    http://localhost:9090
Access: proxy

Затем импортируйте стандартный шаблон панели etcd и настройте его. Например, если источник данных Prometheus называется my-etcd, поля datasource в JSON также должны иметь значение my-etcd.

Пример панели:

Распределённая трассировка

В v3.5 etcd добавлена распределённая трассировка с помощью OpenTelemetry .

Примечание

Эта возможность остаётся экспериментальной и может измениться в любое время.

Чтобы включить экспериментальную возможность, передайте серверу etcd --experimental-enable-distributed-tracing=true и флаг --experimental-distributed-tracing-sampling-rate=<number>, задающий число выборок на миллион спанов. Частота выборки по умолчанию — 0.

Настройте распределённую трассировку, запустив сервер etcd со следующими необязательными флагами:

  • --experimental-distributed-tracing-address - (необязательно) - “localhost:4317” - адрес сборщика трассировок.

  • --experimental-distributed-tracing-service-name - (необязательно) - “etcd” - имя службы распределённой трассировки, одинаковое для всех экземпляров etcd.

  • --experimental-distributed-tracing-instance-id - (необязательно) - идентификатор экземпляра; хотя параметр необязателен, его настоятельно рекомендуется задать уникальным для каждого экземпляра etcd.

Перед включением распределённой трассировки убедитесь, что конечная точка OpenTelemetry доступна. Если её адрес отличается от стандартного, переопределите его флагом --experimental-distributed-tracing-address. Варианты запуска OpenTelemetry описаны в документации сборщика .

Примечание

Как и любой сигнал наблюдаемости, эта возможность расходует ресурсы. По первым измерениям дополнительные затраты CPU составляют от 2% - 4%.

14 - Производительность

Понимание производительности: задержка и пропускная способность

Понимание производительности

etcd обеспечивает стабильную и устойчиво высокую производительность. Её определяют два фактора: задержка и пропускная способность. Задержка — время выполнения одной операции. Пропускная способность — общее число операций, завершённых за определённый период. Обычно средняя задержка растёт вместе с общей пропускной способностью, когда etcd принимает параллельные клиентские запросы. В типичной облачной среде, например на стандартной машине n-4 в Google Compute Engine (GCE) или сопоставимой машине AWS, кластер etcd из трёх участников при небольшой нагрузке завершает запрос менее чем за одну миллисекунду, а при высокой выполняет более 30,000 запросов в секунду.

Для репликации запросов между участниками и достижения соглашения etcd использует алгоритм консенсуса Raft. Производительность консенсуса, особенно задержку фиксации, ограничивают два физических фактора: задержка сетевого и дискового ввода-вывода. Минимальное время выполнения запроса etcd равно времени кругового обхода сети (RTT) между участниками плюс время, необходимое fdatasync для фиксации данных в постоянном хранилище. RTT внутри центра обработки данных может достигать нескольких сотен микросекунд. Типичный RTT в пределах США составляет около 50ms, а между континентами может доходить до 400ms. Типичная задержка fdatasync для вращающегося диска — около 10ms, а для SSD часто меньше 1ms. Чтобы увеличить пропускную способность, etcd объединяет несколько запросов в пакет и передаёт его Raft. Такая политика позволяет сохранять высокую пропускную способность под большой нагрузкой.

На общую производительность etcd влияют и другие подсистемы. Каждый сериализованный запрос etcd проходит через механизм хранения MVCC на основе boltdb, что обычно занимает десятки микросекунд. Периодически etcd создаёт инкрементный снимок недавно применённых запросов и объединяет его с предыдущим снимком на диске. Это может вызвать скачок задержки. На SSD проблема обычно незаметна, но на HDD наблюдаемая задержка может удвоиться. Выполняющаяся компактизация также влияет на производительность etcd. Обычно её влияние незначительно, поскольку компактизация распределена во времени и не конкурирует с обычными запросами за ресурсы. Система RPC gRPC предоставляет etcd чётко определённый расширяемый API, но добавляет задержку, особенно при локальном чтении.

Тесты производительности

Производительность etcd можно измерить инструментом командной строки benchmark , входящим в состав etcd.

В качестве базового примера рассмотрим кластер etcd из трёх участников со следующей аппаратной конфигурацией:

  • Google Cloud Compute Engine
  • 3 машины: 8 vCPU + 16GB памяти + 50GB SSD
  • 1 клиентская машина: 16 vCPU + 30GB памяти + 50GB SSD
  • Ubuntu 17.04
  • etcd 3.2.0, go 1.8.3

С такой конфигурацией etcd показывает примерно следующую производительность записи:

Число ключейРазмер ключа в байтахРазмер значения в байтахЧисло соединенийЧисло клиентовЦелевой сервер etcdСредний QPS записиСредняя задержка запросаСредний RSS сервера
10,000825611только лидер5831.6ms48 MB
100,00082561001000только лидер44,34122ms124MB
100,00082561001000все участники50,10420ms126MB

Примеры команд:

# write to leader
benchmark --endpoints=${HOST_1} --target-leader --conns=1 --clients=1 \
    put --key-size=8 --sequential-keys --total=10000 --val-size=256
benchmark --endpoints=${HOST_1} --target-leader  --conns=100 --clients=1000 \
    put --key-size=8 --sequential-keys --total=100000 --val-size=256

# write to all members
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 \
    put --key-size=8 --sequential-keys --total=100000 --val-size=256

Линеаризуемые запросы чтения проходят через кворум участников кластера, чтобы на основе консенсуса получить самые свежие данные. Сериализуемые запросы дешевле линеаризуемых, поскольку обслуживаются одним участником etcd, а не кворумом, но могут вернуть устаревшие данные. Производительность чтения etcd:

Число запросовРазмер ключа в байтахРазмер значения в байтахЧисло соединенийЧисло клиентовСогласованностьСредний QPS чтенияСредняя задержка запроса
10,000825611Линеаризуемая1,3530.7ms
10,000825611Сериализуемая2,9090.3ms
100,00082561001000Линеаризуемая141,5785.5ms
100,00082561001000Сериализуемая185,7582.2ms

Примеры команд:

# Single connection read requests
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 \
    range YOUR_KEY --consistency=l --total=10000
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=1 --clients=1 \
    range YOUR_KEY --consistency=s --total=10000

# Many concurrent read requests
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 \
    range YOUR_KEY --consistency=l --total=100000
benchmark --endpoints=${HOST_1},${HOST_2},${HOST_3} --conns=100 --clients=1000 \
    range YOUR_KEY --consistency=s --total=100000

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

15 - Устройство реконфигурации во время выполнения

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

Реконфигурация во время выполнения — одна из самых сложных и подверженных ошибкам возможностей распределённой системы, особенно основанной на консенсусе системы вроде etcd.

Ниже описано устройство команд реконфигурации etcd и способы решения связанных проблем.

Двухфазное изменение конфигурации сохраняет безопасность кластера

В etcd каждая реконфигурация во время выполнения в целях безопасности проходит две фазы . Например, чтобы добавить участника, сначала сообщите кластеру новую конфигурацию, а затем запустите участника.

Фаза 1 — сообщение кластеру новой конфигурации

Чтобы добавить участника в кластер etcd, вызовите API с запросом на его добавление. Это единственный способ добавить нового участника в существующий кластер. Вызов API возвращается после того, как кластер согласует изменение конфигурации.

Фаза 2 — запуск нового участника

Чтобы присоединить нового участника etcd к существующему кластеру, укажите правильный initial-cluster и задайте initial-cluster-state значение existing. При запуске участник сначала связывается с существующим кластером и проверяет, соответствует ли текущая конфигурация ожидаемой, заданной в initial-cluster. После успешного запуска нового участника кластер достигает ожидаемой конфигурации.

Разделение процесса на две отдельные фазы заставляет явно задавать изменения состава кластера. Это даёт больше гибкости и упрощает анализ. Например, попытка добавить в кластер etcd нового участника с тем же ID, что у существующего, немедленно завершится ошибкой на первой фазе и не повлияет на работающий кластер. Аналогичная защита предотвращает случайное добавление участников. Если новый участник etcd попытается присоединиться до принятия кластером изменения конфигурации, кластер его не примет.

Без явной процедуры управления составом etcd был бы уязвим для неожиданных изменений. Например, если etcd работает под управлением системы инициализации вроде systemd, после удаления через API состава systemd перезапустит etcd, и тот при запуске попытается снова войти в кластер. Если systemd настроен перезапускать etcd после сбоя, этот цикл будет повторяться при каждом удалении участника через API, что является неожиданным поведением.

Реконфигурация во время выполнения предполагается редкой операцией. Явная процедура под управлением пользователя обеспечивает безопасность конфигурации и позволяет кластеру стабильно работать под непосредственным контролем.

Безвозвратная потеря кворума требует нового кластера

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

Технически можно принудительно удалить отказавших участников из существующего кластера и восстановить его. Однако etcd не поддерживает этот способ, поскольку он обходит обычную фазу фиксации консенсусом и небезопасен. Если удаляемый участник на самом деле не отказал или принудительное удаление выполняется через разных участников одного кластера, получится расходящийся кластер с одинаковым clusterID. Это крайне опасно и впоследствии трудно диагностируется и исправляется.

При правильном развёртывании вероятность безвозвратной потери большинства очень мала. Но последствия достаточно серьёзны и требуют специальной подготовки. Перед вводом etcd в эксплуатацию настоятельно рекомендуется прочитать документацию по аварийному восстановлению и подготовиться к безвозвратной потере большинства.

Не используйте общедоступную службу обнаружения для реконфигурации

Общедоступную службу обнаружения следует применять только для начальной инициализации кластера. Для присоединения участника к существующему кластеру используйте API реконфигурации во время выполнения.

Служба обнаружения предназначена для начальной инициализации кластера etcd в облачной среде, когда IP-адреса всех участников заранее неизвестны. После успешной инициализации они становятся известны, и служба обнаружения технически больше не нужна.

Использование общедоступной службы обнаружения для реконфигурации может показаться удобным, поскольку она уже содержит всю конфигурацию кластера. Однако зависимость от неё создаёт проблемы:

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

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

  3. Общедоступной службе придётся хранить десятки тысяч конфигураций кластеров. Бэкенд нашей службы не готов к такой нагрузке.

Для поддержки реконфигурации лучше всего создать частную службу обнаружения.

16 - Динамическое изменение конфигурации

Поддержка поэтапного изменения конфигурации etcd во время работы

etcd поддерживает поэтапное изменение конфигурации во время работы, позволяя обновлять состав кластера без остановки.

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

Архитектура описана в документе о динамическом изменении конфигурации .

Сценарии реконфигурации

В этом разделе рассмотрены распространённые причины изменения конфигурации. Обычно это сочетания добавления и удаления участников, описанные в разделе Операции изменения конфигурации кластера .

Поочерёдная замена или обновление машин

Если несколько участников кластера необходимо переместить из-за запланированного обслуживания (обновления аппаратного обеспечения, простоя сети и т. д.), рекомендуется изменять их по одному.

Лидера можно безопасно удалить, но во время выборов возникнет краткий простой. Если кластер содержит более 50MB данных v2, рекомендуется перенести каталог данных участника .

Изменение размера кластера

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

Уменьшение размера может ускорить запись ценой отказоустойчивости. До фиксации запись реплицируется большинству участников; меньшее большинство подтверждает её быстрее.

Заменить сбойную машину

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

Для замены удалите участника , затем добавьте нового . Если кластер содержит более 50MB и каталог отказавшего участника доступен, рекомендуется перенести его .

Перезапуск кластера после потери большинства

При потере большинства или изменении IP-адресов всех узлов требуется ручное восстановление: создать новый кластер из старых данных , принудительно назначить одного участника лидером, затем по одному добавить новых участников .

Восстановить кластер после миноритарной ошибки

Потеря отдельного участника эквивалентна замене отказавшей машины; см. Замена отказавшей машины .

Операции перенастройки кластера

Для этих сценариев используются следующие операции.

Перед внесением любых изменений должно быть доступно простое большинство (кворум) участников etcd. Это в сущности то же самое требование для любого вида записи в etcd.

Все изменения в кластере должны выполняться последовательно:

  • Чтобы обновить peerURLs одного участника, выполните операцию обновления.
  • Чтобы заменить одного исправного участника, удалите старого и добавьте нового.
  • Для увеличения с 3 до 5 участников выполните две операции добавления.
  • Для уменьшения с 5 до 3 выполните две операции удаления.

Во всех примерах используется поставляемая с etcd утилита etcdctl. Чтобы изменить состав без etcdctl, используйте HTTP API участников v2 или gRPC API участников v3 .

Обновление участника

Обновление адресов клиентов для обмена

Для обновления адресов advertise клиентских URL-ов участника достаточно перезапустить этот участник с флагом (--advertise-client-urls) или переменной окружения (ETCD_ADVERTISE_CLIENT_URLS) обновленными клиентскими URL-ами. Перезапущенный участник сам опубликует обновленные URL-ы. Неправильно обновленный клиент (URL) не повлияет на состояние здоровья кластера etcd.

Обновление анонсируемых адресов пеерконтроллеров

Для обновления адресов пеер-членов участника, сначала явно обновите его с помощью команды member, а затем перезапустите участника. Дополнительное действие необходимо, так как обновление адресов пеер-членов изменяет конфигурацию кластера и может повлиять на состояние etcd кластера.

Для обновления объявляемых URL сначала найдите ID участника. Список выводится командой etcdctl:

$ etcdctl member list
6e3bd23ae5f1eae0: name=node2 peerURLs=http://localhost:23802 clientURLs=http://127.0.0.1:23792
924e2e83e93f2560: name=node3 peerURLs=http://localhost:23803 clientURLs=http://127.0.0.1:23793
a8266ecf031671f3: name=node1 peerURLs=http://localhost:23801 clientURLs=http://127.0.0.1:23791

Пример выполняет update участника с ID a8266ecf031671f3 и задаёт peerURLs http://10.0.1.10:2380:

$ etcdctl member update a8266ecf031671f3 --peer-urls=http://10.0.1.10:2380
Updated member with ID a8266ecf031671f3 in cluster

Удалить участника

Предположим, что идентификатор участника для удаления — a8266ecf031671f3. Используйте команду remove для выполнения удаления:

$ etcdctl member remove a8266ecf031671f3
Removed member a8266ecf031671f3 from cluster

Целевой участник остановит себя на этом этапе и выведет удаление в лог:

etcd: this member has been permanently removed from the cluster. Exiting.

Было бы безопасно удалить лидера, однако кластер будет неактивен до тех пор, пока не будет выбран новый лидер. Этот период составляет обычно сумму тайм-аута выборов и процесса голосования.

Добавление нового участника

Добавление участника является двухэтапным процессом:

  • Добавьте участника через HTTP API участников , gRPC API участников или etcdctl member add.
  • Запустите его с новой конфигурацией кластера и обновлённым списком участников (существующие + новый).

etcdctl добавляет участника по его имени и объявляемым URL однорангового узла :

$ etcdctl member add infra3 --peer-urls=http://10.0.1.13:2380
added member 9bf1b35fc7761a23 to cluster

ETCD_NAME="infra3"
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE=existing

etcdctl уведомил кластер о новом участнике и вывел переменные окружения, необходимые для успешного запуска. Теперь запустите новый процесс etcd с соответствующими флагами для нового участника:

$ export ETCD_NAME="infra3"
$ export ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
$ export ETCD_INITIAL_CLUSTER_STATE=existing
$ etcd --listen-client-urls http://10.0.1.13:2379 --advertise-client-urls http://10.0.1.13:2379 --listen-peer-urls http://10.0.1.13:2380 --initial-advertise-peer-urls http://10.0.1.13:2380 --data-dir %data_dir%

Новый участник будет функционировать как часть кластера и немедленно начнет синхронизироваться с остальными участниками кластера.

При добавлении нескольких участников настраивайте их по одному и проверяйте запуск. После добавления участника в кластер из 1 узла кластер не продвигается, пока новый участник не запустится: для консенсуса теперь нужно большинство из двух. Пауза длится от etcdctl member add до успешного соединения нового участника.

Добавление нового участника как обучающегося

Начиная с v3.4 etcd поддерживает добавление обучающегося участника без права голоса. Мотивация и дизайн можно найти в документе по дизайну . Чтобы сделать процесс добавления нового участника безопаснее, и снизить время простоя кластера при добавлении нового участника, рекомендуется добавлять новый участник в кластер как обучающийся до тех пор, пока он не синхронизируется. Это можно описать как трехступенчатый процесс:

  • Добавьте новый участник как обучающийся участника через gRPC members API или команду etcdctl member add --learner.

  • Запустите нового участника с новой конфигурацией и обновлённым списком участников (существующие + новый). Этот шаг не отличается от описанного выше.

  • Промотировать новый добавленный обучающийся до участника с правом голоса через gRPC members API или командой etcdctl member promote. сервер etcd проверяет запрос на промотацию для обеспечения его оперативной безопасности. Только после того как лог raft обучающегося участника захватит лог лидера, он может быть промовирован до участника с правом голоса. Если обучающийся участник не захватил лог лидера, запрос на промотацию участника завершится неудачей (см. раздел об ошибках при промотации участника для получения дополнительной информации). В этом случае пользователь должен подождать и повторить попытку позже.

В v3.4 сервер etcd ограничивает количество обучающихся участников, которые может иметь кластер, одним. Основное соображение заключается в ограничении дополнительной нагрузки на лидера при распространении данных от лидера к обучающемуся участнику.

Используйте etcdctl member add с флагом --learner для добавления нового участника в кластер как обучающегося участника.

$ etcdctl member add infra3 --peer-urls=http://10.0.1.13:2380 --learner
Member 9bf1b35fc7761a23 added to cluster a7ef944b95711739

ETCD_NAME="infra3"
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE=existing

После запуска нового процесса etcd для нового добавленного обучающегося участника используйте etcdctl member promote для повышения обучающегося участника до голосующего участника.

$ etcdctl member promote 9bf1b35fc7761a23
Member 9e29bbaa45d74461 promoted in cluster a7ef944b95711739

Случаи ошибок при добавлении участников

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

$ etcd --name infra3 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state existing
etcdserver: assign ids error: the member count is unequal
exit 1

В этом случае используйте другую адресацию (10.0.1.14:2380), отличную от той, которая была использована для присоединения к кластеру (10.0.1.13:2380):

$ etcd --name infra4 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra4=http://10.0.1.14:2380 \
  --initial-cluster-state existing
etcdserver: assign ids error: unmatched member while checking PeerURLs
exit 1

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

$ etcd
etcd: this member has been permanently removed from the cluster. Exiting.
exit 1

Ошибки при добавлении обучающегося участника

Нельзя добавить обучающегося участника в кластер, если кластер уже имеет 1 обучающегося участника (v3.4).

$ etcdctl member add infra4 --peer-urls=http://10.0.1.14:2380 --learner
Error: etcdserver: too many learner members in cluster

Ошибки при повышении обучающегося участника до участника

Обучающегося можно повысить до голосующего участника только после синхронизации с лидером.

$ etcdctl member promote 9bf1b35fc7761a23
Error: etcdserver: can only promote a learner member which is in sync with leader

Повышение участника, который не является обучающимся, завершится ошибкой.

$ etcdctl member promote 9bf1b35fc7761a23
Error: etcdserver: can only promote a learner member

Повышение отсутствующего в кластере участника завершится ошибкой.

$ etcdctl member promote 12345abcde
Error: etcdserver: member not found

Тщательный режим проверки строгой реконфигурации (-strict-reconfig-check)

Как описано выше, лучшая практика добавления новых участников заключается в настройке одного участника за раз и проверке того, что он стартует корректно, прежде чем добавлять новые участники. Этот пошаговый подход очень важен, так как если новый участник не настроен правильно (например, URL-адреса коллегиальных узлов неверны), кластер может потерять кворум. Потеря кворума происходит, так как новый участник учитывается в кворуме даже если он недоступен из других существующих участников. Также потеря кворума может произойти при проблемах с связностью или при операционных проблемах.

Для предотвращения проблемы etcd предоставляет -strict-reconfig-check. С этим параметром etcd отклоняет изменение конфигурации, если число запущенных участников станет меньше кворума нового состава.

По умолчанию включено.

17 - Поддерживаемые платформы

Поддержка распространённых архитектур и операционных систем в etcd

Уровни поддержки

etcd работает на разных платформах, однако предоставляемые гарантии зависят от уровня поддержки платформы:

  • Уровень 1: полностью поддерживается сопровождающими etcd ; гарантируется прохождение всех тестов, включая функциональные тесты и тесты надёжности.
  • Уровень 2: гарантируется прохождение интеграционных и сквозных тестов, но не обязательно функциональных тестов или тестов надёжности.
  • Уровень 3: гарантируется успешная сборка; тестирование может быть минимальным или отсутствовать, поэтому платформу следует считать нестабильной.

Текущая поддержка

В следующей таблице перечислены поддерживаемые платформы и соответствующие уровни поддержки etcd:

АрхитектураОперационная системаУровень поддержкиСопровождающие
AMD64Linux1сопровождающие etcd
ARM64Linux1сопровождающие etcd
AMD64Darwin3
ARM64Darwin3
AMD64Windows3
ppc64leLinux3
s390xLinux3

Платформы, отсутствующие в таблице, не поддерживаются.

Поддержка новой платформы

Хотите участвовать в etcd как «официальный» сопровождающий новой платформы? Помимо обязательства поддерживать платформу, необходимо настроить непрерывную интеграцию (CI) etcd, удовлетворяющую требованиям соответствующего уровня:

Непрерывная интеграция etcdУровень 1Уровень 2Уровень 3
Сборка проходит✓✓✓
Модульные тесты проходят✓✓
Интеграционные и сквозные тесты проходят✓✓
Тесты надёжности проходят✓

Пример настройки CI уровня 2 для ARM64 приведён в etcd PR #12928 .

Неподдерживаемые платформы

Чтобы сервер etcd не был случайно запущен на неподдерживаемой платформе, etcd выводит предупреждение и немедленно завершает работу, если переменная окружения ETCD_UNSUPPORTED_ARCH не содержит целевую архитектуру.

Предупреждение

32-bit системы В etcd существуют известные проблемы на 32-bit системах из-за ошибки в среде выполнения Go. Дополнительные сведения приведены в issue Go #599 и примечании об ошибке пакета atomic .

18 - Версионирование

Поддержка версионирования etcd

Данный документ описывает версии, поддерживаемые проектом etcd.

Версионирование сервиса и поддерживаемые версии

Версии etcd выражаются как x.y.z, где x — мажорная версия, y — минорная версия, а z — версия исправления, в соответствии с терминологией Semantic Versioning .
Новые минорные версии могут добавлять дополнительные функции в API.

Проект etcd поддерживает ветки релизов для текущей версии и предыдущего выпуска. Например, когда v3.5 является текущей версией, поддерживается v3.4. Когда выпускается v3.6, v3.4 прекращает поддержку.

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

Команда разработчиков Maintainers несёт ответственность за это решение.

Вы можете проверить версию работающего кластера etcd с помощью etcdctl:

etcdctl --endpoints=127.0.0.1:2379 endpoint status

API версионирование

Ответы v3 API не должны изменяться после выпуска 3.0.0, но со временем будут добавляться новые функции.

19 - Повреждение данных

Повреждение и восстановление данных etcd

В etcd встроено автоматическое обнаружение повреждения данных, предотвращающее расхождение состояния участников.

Включение обнаружения повреждения данных

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

  • Начальной проверки, включаемой флагом --experimental-initial-corrupt-check.
  • Периодической проверки:
    • Хеша сжатой ревизии, включаемой флагом --experimental-compact-hash-check-enabled.
    • Хеша последней ревизии, включаемой флагом --experimental-corrupt-check-time.

Начальная проверка выполняется при начальной инициализации участника etcd. Участник сравнивает своё постоянное состояние с другими участниками и завершает работу при несовпадении.

Обе периодические проверки выполняются лидером уже работающего кластера. Лидер сравнивает своё постоянное состояние с другими участниками и при несовпадении подаёт аварийный сигнал CORRUPT. Обе проверки решают одну задачу, однако для баланса между производительностью и временем обнаружения стоит включить обе.

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

Проверка хеша сжатой ревизии

Если проверка включена флагом --experimental-compact-hash-check-enabled, она выполняется раз в минуту. Период можно изменить флагом --experimental-compact-hash-check-time в формате: 1m — раз в минуту, 1h — раз в час. Проверка расширяет компактизацию вычислением контрольной суммы, которую можно сравнить между участниками кластера. Дополнительное сканирование базы данных не выполняется, поэтому стоимость проверки очень мала, но кластеру требуется регулярная компактизация.

Проверка хеша последней ревизии

Проверка включается флагом --experimental-corrupt-check-time; необходимо указать период выполнения в формате: 1m — раз в минуту, 1h — раз в час. Из-за высокой стоимости рекомендуется период в несколько часов. Для проверки вычисляется контрольная сумма путём сканирования всего содержимого etcd на заданной ревизии.

Восстановление повреждённого участника

Повреждённого участника можно восстановить тремя способами:

  • Очистить постоянное состояние участника
  • Заменить участника
  • Восстановить весь кластер

После восстановления повреждённого участника аварийный сигнал CORRUPT можно удалить.

Очистка постоянного состояния участника

Состояние участника можно очистить следующим образом:

  1. Остановите экземпляр etcd.
  2. Создайте резервную копию каталога данных etcd.
  3. Переместите подкаталог snap из каталога данных etcd.
  4. Запустите etcd с --initial-cluster-state=existing и списком участников кластера в --initial-cluster.

После этого участник etcd должен загрузить актуальный снимок у лидера.

Замена участника

Участника можно заменить следующим образом:

  1. Остановите экземпляр etcd.
  2. Создайте резервную копию каталога данных etcd.
  3. Удалите каталог данных.
  4. Удалите участника из кластера командой etcdctl member remove.
  5. Добавьте его обратно командой etcdctl member add.
  6. Запустите etcd с --initial-cluster-state=existing и списком участников кластера в --initial-cluster.

Восстановление всего кластера

Кластер можно восстановить, сохранив снимок текущего лидера и восстановив его на всех участниках. Выполните etcdctl snapshot save для лидера и следуйте процедуре восстановления кластера .