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

Обновление etcd от 3.1 до 3.2

Процессы, списки действий и примечания по обновлению etcd с 3.1 до 3.2

В общем случае, обновление от etcd 3.1 до 3.2 может быть обновлением с нулевым временем простоя:

  • по одному, останавливайте процессы etcd v3.1 и заменяйте их процессами etcd v3.2
  • после запуска всех процессов v3.2, новые функции в v3.2 становятся доступны для кластера

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

Обновление списков проверки

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

При миграции с v2 без данных v3 сервер etcd v3.2+ аварийно завершается при восстановлении из снимка без файла v3 ETCD_DATA_DIR/member/snap/db. Это происходит после миграции с v2 без прежних данных v3 и предотвращает случайную потерю v3, например при перемещении db. После миграции на v3 etcd требует данные v3. Не обновляйтесь до более новых версий v3, пока сервер v3.0 не содержит данные v3.

Изменения, требующие пересборки, в 3.2.

Изменено стандартное значение snapshot-count

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

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

Изменена зависимость gRPC (>=3.2.10)

Выпуск 3.2.10 или более поздний теперь требует grpc/grpc-go v1.7.5 (<=3.2.9 требует v1.2.1).

Устаревшая grpclog.Logger

grpclog.Logger был устаревшим в пользу grpclog.LoggerV2 . clientv3.Logger теперь grpclog.LoggerV2.

Перед

import "github.com/coreos/etcd/clientv3"
clientv3.SetLogger(log.New(os.Stderr, "grpc: ", 0))

После

import "github.com/coreos/etcd/clientv3"
import "google.golang.org/grpc/grpclog"
clientv3.SetLogger(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))

// log.New above cannot be used (not implement grpclog.LoggerV2 interface)
Устаревшая grpc.ErrClientConnTimeout

Ранее, grpc.ErrClientConnTimeout ошибка возвращалась при таймаутах на подключении клиента. 3.2 теперь возвращает context.DeadlineExceeded (см. #8504 ).

Перед

// expect dial time-out on ipv4 blackhole
_, err := clientv3.New(clientv3.Config{
    Endpoints:   []string{"http://254.0.0.1:12345"},
    DialTimeout: 2 * time.Second
})
if err == grpc.ErrClientConnTimeout {
	// handle errors
}

После

_, err := clientv3.New(clientv3.Config{
    Endpoints:   []string{"http://254.0.0.1:12345"},
    DialTimeout: 2 * time.Second
})
if err == context.DeadlineExceeded {
	// handle errors
}

Изменены максимальные ограничения размера запроса (>=3.2.10)

Версии 3.2.10 и 3.2.11 позволяют настраивать ограничение размера запроса на сервере. >=3.2.12 позволяет задавать его и на сервере, и на клиенте. В предыдущих версиях (v3.2.10, v3.2.11) ответ клиенту был ограничен 4 MiB.

Серверные ограничения на запросы можно настроить с помощью флага --max-request-bytes:

# limits request size to 1.5 KiB
etcd --max-request-bytes 1536

# client writes exceeding 1.5 KiB will be rejected
etcdctl put foo [LARGE VALUE...]
# etcdserver: request is too large

Или настройте поле embed.Config.MaxRequestBytes:

import "github.com/coreos/etcd/embed"
import "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"

// limit requests to 5 MiB
cfg := embed.NewConfig()
cfg.MaxRequestBytes = 5 * 1024 * 1024

// client writes exceeding 5 MiB will be rejected
_, err := cli.Put(ctx, "foo", [LARGE VALUE...])
err == rpctypes.ErrRequestTooLarge

Если значение не задано, серверное ограничение по умолчанию равно 1.5 MiB.

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

# limits request size to 1 MiB
etcd --max-request-bytes 1048576
import "github.com/coreos/etcd/clientv3"

cli, _ := clientv3.New(clientv3.Config{
    Endpoints: []string{"127.0.0.1:2379"},
    MaxCallSendMsgSize: 2 * 1024 * 1024,
    MaxCallRecvMsgSize: 3 * 1024 * 1024,
})


// client writes exceeding "--max-request-bytes" will be rejected from etcd server
_, err := cli.Put(ctx, "foo", strings.Repeat("a", 1*1024*1024+5))
err == rpctypes.ErrRequestTooLarge


// client writes exceeding "MaxCallSendMsgSize" will be rejected from client-side
_, err = cli.Put(ctx, "foo", strings.Repeat("a", 5*1024*1024))
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max (5242890 vs. 2097152)"


// some writes under limits
for i := range []int{0,1,2,3,4} {
    _, err = cli.Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("a", 1*1024*1024-500))
    if err != nil {
        panic(err)
    }
}
// client reads exceeding "MaxCallRecvMsgSize" will be rejected from client-side
_, err = cli.Get(ctx, "foo", clientv3.WithPrefix())
err.Error() == "rpc error: code = ResourceExhausted desc = grpc: received message larger than max (5240509 vs. 3145728)"

Если значения не заданы, клиентское ограничение отправки по умолчанию равно 2 MiB (1.5 MiB + накладные байты gRPC), а получения — math.MaxInt32. Подробнее см. godoc clientv3 .

Изменены.raw оболочки клиентов gRPC

3.2.12 или более поздняя версия изменяет сигнатуры функций оболочки gRPC клиентского clientv3. Этот изменения были необходимы для поддержки пользовательских ограничений размера сообщений grpc.CallOption.

До и после

-func NewKVFromKVClient(remote pb.KVClient) KV {
+func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {

-func NewClusterFromClusterClient(remote pb.ClusterClient) Cluster {
+func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {

-func NewLeaseFromLeaseClient(remote pb.LeaseClient, keepAliveTimeout time.Duration) Lease {
+func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {

-func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient) Maintenance {
+func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {

-func NewWatchFromWatchClient(wc pb.WatchClient) Watcher {
+func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {

Изменена clientv3.Lease.TimeToLive API

Прежде, clientv3.Lease.TimeToLive API возвращал lease.ErrLeaseNotFound при несуществующем идентификаторе арены. 3.2 вместо этого возвращает TTL=-1 в ответе и не выдает ошибку (см. #7305 ).

Перед

// when leaseID does not exist
resp, err := TimeToLive(ctx, leaseID)
resp == nil
err == lease.ErrLeaseNotFound

После

// when leaseID does not exist
resp, err := TimeToLive(ctx, leaseID)
resp.TTL == -1
err == nil

Перемещен clientv3.NewFromConfigFile в clientv3.yaml.NewConfig

clientv3.NewFromConfigFile перенесен в yaml.NewConfig.

Перед

import "github.com/coreos/etcd/clientv3"
clientv3.NewFromConfigFile

После

import clientv3yaml "github.com/coreos/etcd/clientv3/yaml"
clientv3yaml.NewConfig

Изменение в --listen-peer-urls и --listen-client-urls

3.2 теперь отвергает доменные имена для --listen-peer-urls и --listen-client-urls (3.1 только выводит предупреждения), так как доменное имя недопустимо для привязки к сетевому интерфейсу. Убедитесь, что эти URL правильно форматированы как scheme://IP:port.

См. issue #6336 для более подробной информации.

Проверки для обновления сервера

Требования к обновлению

Для обновления существующего развёртывания etcd до 3.2 кластер должен быть версии 3.1 или выше. Если это раньше 3.1, рекомендуется обновить до 3.1 перед обновлением до 3.2.

Также, для обеспечения плавного обновления кластер должен быть здоровым. Проверьте состояние кластера с помощью команды etcdctl endpoint health перед продолжением.

Подготовка

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

Перед началом сделайте резервную копию данных etcd . Если что-то пойдет не так с обновлением, можно использовать эту резервную копию для понижения версии обратно к существующей версии etcd. Пожалуйста, примечание: команда snapshot выполняет только резервное копирование v3 данных. Для v2 данных см. резервное копирование v2 хранилища данных .

Смешанные версии

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

Ограничения

Примечание: если кластер содержит только данные версии 3 и нет данных версии 2, то он не подлежит этому ограничению.

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

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

Понижение версии

Если все участники были обновлены до v3.2, кластер будет обновлен до v3.2, и понижение версии из этого завершенного состояния невозможно. Если хотя бы один участник остается v3.1, кластер и его операции остаются “v3.1”, и из этого смешанного состояния кластера возможно вернуться к использованию etcd-бинарного файла версии v3.1 на всех участниках.

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

Процедура обновления

Этот пример показывает, как обновить кластер etcd, работающий локально, состоящий из участника 3-члена v3.1.

1. Проверьте требования к обновлению

Я здоров и работает ли кластер v3.1.x?

$ ETCDCTL_API=3 etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:2379 is healthy: successfully committed proposal: took = 6.600684ms
localhost:22379 is healthy: successfully committed proposal: took = 8.540064ms
localhost:32379 is healthy: successfully committed proposal: took = 8.763432ms

$ curl http://localhost:2379/version
{"etcdserver":"3.1.7","etcdcluster":"3.1.0"}

2. Остановите существующий процесс etcd

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

2017-04-27 14:13:31.491746 I | raft: c89feb932daef420 [term 3] received MsgTimeoutNow from 6d4f535bae3ab960 and starts an election to get leadership.
2017-04-27 14:13:31.491769 I | raft: c89feb932daef420 became candidate at term 4
2017-04-27 14:13:31.491788 I | raft: c89feb932daef420 received MsgVoteResp from c89feb932daef420 at term 4
2017-04-27 14:13:31.491797 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 6d4f535bae3ab960 at term 4
2017-04-27 14:13:31.491805 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 9eda174c7df8a033 at term 4
2017-04-27 14:13:31.491815 I | raft: raft.node: c89feb932daef420 lost leader 6d4f535bae3ab960 at term 4
2017-04-27 14:13:31.524084 I | raft: c89feb932daef420 received MsgVoteResp from 6d4f535bae3ab960 at term 4
2017-04-27 14:13:31.524108 I | raft: c89feb932daef420 [quorum:2] has received 2 MsgVoteResp votes and 0 vote rejections
2017-04-27 14:13:31.524123 I | raft: c89feb932daef420 became leader at term 4
2017-04-27 14:13:31.524136 I | raft: raft.node: c89feb932daef420 elected leader c89feb932daef420 at term 4
2017-04-27 14:13:31.592650 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream MsgApp v2 reader)
2017-04-27 14:13:31.592825 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message reader)
2017-04-27 14:13:31.693275 E | rafthttp: failed to dial 6d4f535bae3ab960 on stream Message (dial tcp [::1]:2380: getsockopt: connection refused)
2017-04-27 14:13:31.693289 I | rafthttp: peer 6d4f535bae3ab960 became inactive
2017-04-27 14:13:31.936678 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message writer)

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

$ etcdctl snapshot save backup.db

3. Замените встраиваемую версию etcd v3.2 и запустите новый процесс etcd

The новое v3.2 etcd будет публиковать свои данные в кластер:

2017-04-27 14:14:25.363225 I | etcdserver: published {Name:s1 ClientURLs:[http://localhost:2379]} to cluster a9ededbffcb1b1f1

Убедитесь, что с новым двоичным файлом etcd v3.2 каждый участник, а затем весь кластер становятся исправными:

$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:22379 is healthy: successfully committed proposal: took = 5.540129ms
localhost:32379 is healthy: successfully committed proposal: took = 7.321771ms
localhost:2379 is healthy: successfully committed proposal: took = 10.629901ms

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

2017-04-27 14:15:17.071804 W | etcdserver: member c89feb932daef420 has a higher version 3.2.0
2017-04-27 14:15:21.073110 W | etcdserver: the local etcd version 3.1.7 is not up-to-date
2017-04-27 14:15:21.073142 W | etcdserver: member 6d4f535bae3ab960 has a higher version 3.2.0
2017-04-27 14:15:21.073157 W | etcdserver: the local etcd version 3.1.7 is not up-to-date
2017-04-27 14:15:21.073164 W | etcdserver: member c89feb932daef420 has a higher version 3.2.0

4. Повторите шаг 2 до шага 3 для всех других участников

5. Завершить

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

2017-04-27 14:15:54.536901 N | etcdserver/membership: updated the cluster version from 3.1 to 3.2
2017-04-27 14:15:54.537035 I | etcdserver/api: enabled capabilities for version 3.2
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:2379 is healthy: successfully committed proposal: took = 2.312897ms
localhost:22379 is healthy: successfully committed proposal: took = 2.553476ms
localhost:32379 is healthy: successfully committed proposal: took = 2.517902ms