go.etcd.io/etcd@v3.3.27+incompatible/Documentation/upgrades/upgrade_3_3.md (about)

     1  ---
     2  title: Upgrade etcd from 3.2 to 3.3
     3  ---
     4  
     5  In the general case, upgrading from etcd 3.2 to 3.3 can be a zero-downtime, rolling upgrade:
     6   - one by one, stop the etcd v3.2 processes and replace them with etcd v3.3 processes
     7   - after running all v3.3 processes, new features in v3.3 are available to the cluster
     8  
     9  Before [starting an upgrade](#upgrade-procedure), read through the rest of this guide to prepare.
    10  
    11  ### Upgrade checklists
    12  
    13  **NOTE:** When [migrating from v2 with no v3 data](https://github.com/etcd-io/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
    14  
    15  Highlighted breaking changes in 3.3.
    16  
    17  #### Changed value type of `etcd --auto-compaction-retention` flag to `string`
    18  
    19  Changed `--auto-compaction-retention` flag to [accept string values](https://github.com/etcd-io/etcd/pull/8563) with [finer granularity](https://github.com/etcd-io/etcd/issues/8503). Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type. Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`. If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `--auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
    20  
    21  ```diff
    22  # etcd.config.yaml
    23  +auto-compaction-mode: periodic
    24  -auto-compaction-retention: 24
    25  +auto-compaction-retention: "24"
    26  +# Or
    27  +auto-compaction-retention: "24h"
    28  ```
    29  
    30  #### Changed `etcdserver.EtcdServer.ServerConfig` to `*etcdserver.EtcdServer.ServerConfig`
    31  
    32  `etcdserver.EtcdServer` has changed the type of its member field `*etcdserver.ServerConfig` to `etcdserver.ServerConfig`. And `etcdserver.NewServer` now takes `etcdserver.ServerConfig`, instead of `*etcdserver.ServerConfig`.
    33  
    34  Before and after (e.g. [k8s.io/kubernetes/test/e2e_node/services/etcd.go](https://github.com/kubernetes/kubernetes/blob/release-1.8/test/e2e_node/services/etcd.go#L50-L55))
    35  
    36  ```diff
    37  import "github.com/coreos/etcd/etcdserver"
    38  
    39  type EtcdServer struct {
    40  	*etcdserver.EtcdServer
    41  -	config *etcdserver.ServerConfig
    42  +	config etcdserver.ServerConfig
    43  }
    44  
    45  func NewEtcd(dataDir string) *EtcdServer {
    46  -	config := &etcdserver.ServerConfig{
    47  +	config := etcdserver.ServerConfig{
    48  		DataDir: dataDir,
    49          ...
    50  	}
    51  	return &EtcdServer{config: config}
    52  }
    53  
    54  func (e *EtcdServer) Start() error {
    55  	var err error
    56  	e.EtcdServer, err = etcdserver.NewServer(e.config)
    57      ...
    58  ```
    59  
    60  #### Added `embed.Config.LogOutput` struct
    61  
    62  **Note that this field has been renamed to `embed.Config.LogOutputs` in `[]string` type in v3.4. Please see [v3.4 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_4.md) for more details.**
    63  
    64  Field `LogOutput` is added to `embed.Config`:
    65  
    66  ```diff
    67  package embed
    68  
    69  type Config struct {
    70   	Debug bool `json:"debug"`
    71   	LogPkgLevels string `json:"log-package-levels"`
    72  +	LogOutput string `json:"log-output"`
    73   	...
    74  ```
    75  
    76  Before gRPC server warnings were logged in etcdserver.
    77  
    78  ```
    79  WARNING: 2017/11/02 11:35:51 grpc: addrConn.resetTransport failed to create client transport: connection error: desc = "transport: Error while dialing dial tcp: operation was canceled"; Reconnecting to {localhost:2379 <nil>}
    80  WARNING: 2017/11/02 11:35:51 grpc: addrConn.resetTransport failed to create client transport: connection error: desc = "transport: Error while dialing dial tcp: operation was canceled"; Reconnecting to {localhost:2379 <nil>}
    81  ```
    82  
    83  From v3.3, gRPC server logs are disabled by default.
    84  
    85  **Note that `embed.Config.SetupLogging` method has been deprecated in v3.4. Please see [v3.4 upgrade guide](https://github.com/etcd-io/etcd/blob/master/Documentation/upgrades/upgrade_3_4.md) for more details.**
    86  
    87  ```go
    88  import "github.com/coreos/etcd/embed"
    89  
    90  cfg := &embed.Config{Debug: false}
    91  cfg.SetupLogging()
    92  ```
    93  
    94  Set `embed.Config.Debug` field to `true` to enable gRPC server logs.
    95  
    96  #### Changed `/health` endpoint response
    97  
    98  Previously, `[endpoint]:[client-port]/health` returned manually marshaled JSON value. 3.3 now defines [`etcdhttp.Health`](https://godoc.org/github.com/coreos/etcd/etcdserver/api/etcdhttp#Health) struct.
    99  
   100  Note that in v3.3.0-rc.0, v3.3.0-rc.1, and v3.3.0-rc.2, `etcdhttp.Health` has boolean type `"health"` and `"errors"` fields. For backward compatibilities, we reverted `"health"` field to `string` type and removed `"errors"` field. Further health information will be provided in separate APIs.
   101  
   102  ```bash
   103  $ curl http://localhost:2379/health
   104  {"health":"true"}
   105  ```
   106  
   107  #### Changed gRPC gateway HTTP endpoints (replaced `/v3alpha` with `/v3beta`)
   108  
   109  Before
   110  
   111  ```bash
   112  curl -L http://localhost:2379/v3alpha/kv/put \
   113    -X POST -d '{"key": "Zm9v", "value": "YmFy"}'
   114  ```
   115  
   116  After
   117  
   118  ```bash
   119  curl -L http://localhost:2379/v3beta/kv/put \
   120    -X POST -d '{"key": "Zm9v", "value": "YmFy"}'
   121  ```
   122  
   123  Requests to `/v3alpha` endpoints will redirect to `/v3beta`, and `/v3alpha` will be removed in 3.4 release.
   124  
   125  #### Changed maximum request size limits
   126  
   127  3.3 now allows custom request size limits for both server and **client side**. In previous versions(v3.2.10, v3.2.11), client response size was limited to only 4 MiB.
   128  
   129  Server-side request limits can be configured with `--max-request-bytes` flag:
   130  
   131  ```bash
   132  # limits request size to 1.5 KiB
   133  etcd --max-request-bytes 1536
   134  
   135  # client writes exceeding 1.5 KiB will be rejected
   136  etcdctl put foo [LARGE VALUE...]
   137  # etcdserver: request is too large
   138  ```
   139  
   140  Or configure `embed.Config.MaxRequestBytes` field:
   141  
   142  ```go
   143  import "github.com/coreos/etcd/embed"
   144  import "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
   145  
   146  // limit requests to 5 MiB
   147  cfg := embed.NewConfig()
   148  cfg.MaxRequestBytes = 5 * 1024 * 1024
   149  
   150  // client writes exceeding 5 MiB will be rejected
   151  _, err := cli.Put(ctx, "foo", [LARGE VALUE...])
   152  err == rpctypes.ErrRequestTooLarge
   153  ```
   154  
   155  **If not specified, server-side limit defaults to 1.5 MiB**.
   156  
   157  Client-side request limits must be configured based on server-side limits.
   158  
   159  ```bash
   160  # limits request size to 1 MiB
   161  etcd --max-request-bytes 1048576
   162  ```
   163  
   164  ```go
   165  import "github.com/coreos/etcd/clientv3"
   166  
   167  cli, _ := clientv3.New(clientv3.Config{
   168      Endpoints: []string{"127.0.0.1:2379"},
   169      MaxCallSendMsgSize: 2 * 1024 * 1024,
   170      MaxCallRecvMsgSize: 3 * 1024 * 1024,
   171  })
   172  
   173  
   174  // client writes exceeding "--max-request-bytes" will be rejected from etcd server
   175  _, err := cli.Put(ctx, "foo", strings.Repeat("a", 1*1024*1024+5))
   176  err == rpctypes.ErrRequestTooLarge
   177  
   178  
   179  // client writes exceeding "MaxCallSendMsgSize" will be rejected from client-side
   180  _, err = cli.Put(ctx, "foo", strings.Repeat("a", 5*1024*1024))
   181  err.Error() == "rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max (5242890 vs. 2097152)"
   182  
   183  
   184  // some writes under limits
   185  for i := range []int{0,1,2,3,4} {
   186      _, err = cli.Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("a", 1*1024*1024-500))
   187      if err != nil {
   188          panic(err)
   189      }
   190  }
   191  // client reads exceeding "MaxCallRecvMsgSize" will be rejected from client-side
   192  _, err = cli.Get(ctx, "foo", clientv3.WithPrefix())
   193  err.Error() == "rpc error: code = ResourceExhausted desc = grpc: received message larger than max (5240509 vs. 3145728)"
   194  ```
   195  
   196  **If not specified, client-side send limit defaults to 2 MiB (1.5 MiB + gRPC overhead bytes) and receive limit to `math.MaxInt32`**. Please see [clientv3 godoc](https://godoc.org/github.com/coreos/etcd/clientv3#Config) for more detail.
   197  
   198  #### Changed raw gRPC client wrapper function signatures
   199  
   200  3.3 changes the function signatures of `clientv3` gRPC client wrapper. This change was needed to support [custom `grpc.CallOption` on message size limits](https://github.com/etcd-io/etcd/pull/9047).
   201  
   202  Before and after
   203  
   204  ```diff
   205  -func NewKVFromKVClient(remote pb.KVClient) KV {
   206  +func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
   207  
   208  -func NewClusterFromClusterClient(remote pb.ClusterClient) Cluster {
   209  +func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
   210  
   211  -func NewLeaseFromLeaseClient(remote pb.LeaseClient, keepAliveTimeout time.Duration) Lease {
   212  +func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {
   213  
   214  -func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient) Maintenance {
   215  +func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
   216  
   217  -func NewWatchFromWatchClient(wc pb.WatchClient) Watcher {
   218  +func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
   219  ```
   220  
   221  #### Changed clientv3 `Snapshot` API error type
   222  
   223  Previously, clientv3 `Snapshot` API returned raw [`grpc/*status.statusError`] type error. v3.3 now translates those errors to corresponding public error types, to be consistent with other APIs.
   224  
   225  Before
   226  
   227  ```go
   228  import "context"
   229  
   230  // reading snapshot with canceled context should error out
   231  ctx, cancel := context.WithCancel(context.Background())
   232  rc, _ := cli.Snapshot(ctx)
   233  cancel()
   234  _, err := io.Copy(f, rc)
   235  err.Error() == "rpc error: code = Canceled desc = context canceled"
   236  
   237  // reading snapshot with deadline exceeded should error out
   238  ctx, cancel = context.WithTimeout(context.Background(), time.Second)
   239  defer cancel()
   240  rc, _ = cli.Snapshot(ctx)
   241  time.Sleep(2 * time.Second)
   242  _, err = io.Copy(f, rc)
   243  err.Error() == "rpc error: code = DeadlineExceeded desc = context deadline exceeded"
   244  ```
   245  
   246  After
   247  
   248  ```go
   249  import "context"
   250  
   251  // reading snapshot with canceled context should error out
   252  ctx, cancel := context.WithCancel(context.Background())
   253  rc, _ := cli.Snapshot(ctx)
   254  cancel()
   255  _, err := io.Copy(f, rc)
   256  err == context.Canceled
   257  
   258  // reading snapshot with deadline exceeded should error out
   259  ctx, cancel = context.WithTimeout(context.Background(), time.Second)
   260  defer cancel()
   261  rc, _ = cli.Snapshot(ctx)
   262  time.Sleep(2 * time.Second)
   263  _, err = io.Copy(f, rc)
   264  err == context.DeadlineExceeded
   265  ```
   266  
   267  #### Changed `etcdctl lease timetolive` command output
   268  
   269  Previously, `lease timetolive LEASE_ID` command on expired lease prints `-1s` for remaining seconds. 3.3 now outputs clearer messages.
   270  
   271  Before
   272  
   273  
   274  ```bash
   275  lease 2d8257079fa1bc0c granted with TTL(0s), remaining(-1s)
   276  ```
   277  
   278  After
   279  
   280  ```bash
   281  lease 2d8257079fa1bc0c already expired
   282  ```
   283  
   284  #### Changed `golang.org/x/net/context` imports
   285  
   286  `clientv3` has deprecated `golang.org/x/net/context`. If a project vendors `golang.org/x/net/context` in other code (e.g. etcd generated protocol buffer code) and imports `github.com/coreos/etcd/clientv3`, it requires Go 1.9+ to compile.
   287  
   288  Before
   289  
   290  ```go
   291  import "golang.org/x/net/context"
   292  cli.Put(context.Background(), "f", "v")
   293  ```
   294  
   295  After
   296  
   297  ```go
   298  import "context"
   299  cli.Put(context.Background(), "f", "v")
   300  ```
   301  
   302  #### Changed gRPC dependency
   303  
   304  3.3 now requires [grpc/grpc-go](https://github.com/grpc/grpc-go/releases) `v1.7.5`.
   305  
   306  ##### Deprecated `grpclog.Logger`
   307  
   308  `grpclog.Logger` has been deprecated in favor of [`grpclog.LoggerV2`](https://github.com/grpc/grpc-go/blob/master/grpclog/loggerv2.go). `clientv3.Logger` is now `grpclog.LoggerV2`.
   309  
   310  Before
   311  
   312  ```go
   313  import "github.com/coreos/etcd/clientv3"
   314  clientv3.SetLogger(log.New(os.Stderr, "grpc: ", 0))
   315  ```
   316  
   317  After
   318  
   319  ```go
   320  import "github.com/coreos/etcd/clientv3"
   321  import "google.golang.org/grpc/grpclog"
   322  clientv3.SetLogger(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
   323  
   324  // log.New above cannot be used (not implement grpclog.LoggerV2 interface)
   325  ```
   326  
   327  ##### Deprecated `grpc.ErrClientConnTimeout`
   328  
   329  Previously, `grpc.ErrClientConnTimeout` error is returned on client dial time-outs. 3.3 instead returns `context.DeadlineExceeded` (see [#8504](https://github.com/etcd-io/etcd/issues/8504)).
   330  
   331  Before
   332  
   333  ```go
   334  // expect dial time-out on ipv4 blackhole
   335  _, err := clientv3.New(clientv3.Config{
   336      Endpoints:   []string{"http://254.0.0.1:12345"},
   337      DialTimeout: 2 * time.Second
   338  })
   339  if err == grpc.ErrClientConnTimeout {
   340  	// handle errors
   341  }
   342  ```
   343  
   344  After
   345  
   346  ```go
   347  _, err := clientv3.New(clientv3.Config{
   348      Endpoints:   []string{"http://254.0.0.1:12345"},
   349      DialTimeout: 2 * time.Second
   350  })
   351  if err == context.DeadlineExceeded {
   352  	// handle errors
   353  }
   354  ```
   355  
   356  #### Changed official container registry
   357  
   358  etcd now uses [`gcr.io/etcd-development/etcd`](https://gcr.io/etcd-development/etcd) as a primary container registry, and [`quay.io/coreos/etcd`](https://quay.io/coreos/etcd) as secondary.
   359  
   360  Before
   361  
   362  ```bash
   363  docker pull quay.io/coreos/etcd:v3.2.5
   364  ```
   365  
   366  After
   367  
   368  ```bash
   369  docker pull gcr.io/etcd-development/etcd:v3.3.0
   370  ```
   371  
   372  ### Upgrades to >= v3.3.14
   373  
   374  [v3.3.14](https://github.com/etcd-io/etcd/releases/tag/v3.3.14) had to include some features from 3.4, while trying to minimize the difference between client balancer implementation. This release fixes ["kube-apiserver 1.13.x refuses to work when first etcd-server is not available" (kubernetes#72102)](https://github.com/kubernetes/kubernetes/issues/72102).
   375  
   376  `grpc.ErrClientConnClosing` has been [deprecated in gRPC >= 1.10](https://github.com/grpc/grpc-go/pull/1854).
   377  
   378  ```diff
   379  import (
   380  +	"go.etcd.io/etcd/clientv3"
   381  
   382  	"google.golang.org/grpc"
   383  +	"google.golang.org/grpc/codes"
   384  +	"google.golang.org/grpc/status"
   385  )
   386  
   387  _, err := kvc.Get(ctx, "a")
   388  -if err == grpc.ErrClientConnClosing {
   389  +if clientv3.IsConnCanceled(err) {
   390  
   391  // or
   392  +s, ok := status.FromError(err)
   393  +if ok {
   394  +  if s.Code() == codes.Canceled
   395  ```
   396  
   397  [The new client balancer](https://github.com/etcd-io/etcd/blob/master/Documentation/learning/design-client.md) uses an asynchronous resolver to pass endpoints to the gRPC dial function. As a result, [v3.3.14](https://github.com/etcd-io/etcd/releases/tag/v3.3.14) or later requires `grpc.WithBlock` dial option to wait until the underlying connection is up.
   398  
   399  ```diff
   400  import (
   401  	"time"
   402  	"go.etcd.io/etcd/clientv3"
   403  +	"google.golang.org/grpc"
   404  )
   405  
   406  +// "grpc.WithBlock()" to block until the underlying connection is up
   407  ccfg := clientv3.Config{
   408    Endpoints:            []string{"localhost:2379"},
   409    DialTimeout:          time.Second,
   410  + DialOptions:          []grpc.DialOption{grpc.WithBlock()},
   411    DialKeepAliveTime:    time.Second,
   412    DialKeepAliveTimeout: 500 * time.Millisecond,
   413  }
   414  ```
   415  
   416  Please see [CHANGELOG](https://github.com/etcd-io/etcd/blob/master/CHANGELOG-3.3.md) for a full list of changes.
   417  
   418  ### Server upgrade checklists
   419  
   420  #### Upgrade requirements
   421  
   422  To upgrade an existing etcd deployment to 3.3, the running cluster must be 3.2 or greater. If it's before 3.2, please [upgrade to 3.2](upgrade_3_2.md) before upgrading to 3.3.
   423  
   424  Also, to ensure a smooth rolling upgrade, the running cluster must be healthy. Check the health of the cluster by using the `etcdctl endpoint health` command before proceeding.
   425  
   426  #### Preparation
   427  
   428  Before upgrading etcd, always test the services relying on etcd in a staging environment before deploying the upgrade to the production environment.
   429  
   430  Before beginning, [backup the etcd data](../op-guide/maintenance.md#snapshot-backup). Should something go wrong with the upgrade, it is possible to use this backup to [downgrade](#downgrade) back to existing etcd version. Please note that the `snapshot` command only backs up the v3 data. For v2 data, see [backing up v2 datastore](../v2/admin_guide.md#backing-up-the-datastore).
   431  
   432  #### Mixed versions
   433  
   434  While upgrading, an etcd cluster supports mixed versions of etcd members, and operates with the protocol of the lowest common version. The cluster is only considered upgraded once all of its members are upgraded to version 3.3. Internally, etcd members negotiate with each other to determine the overall cluster version, which controls the reported version and the supported features.
   435  
   436  #### Limitations
   437  
   438  Note: If the cluster only has v3 data and no v2 data, it is not subject to this limitation.
   439  
   440  If the cluster is serving a v2 data set larger than 50MB, each newly upgraded member may take up to two minutes to catch up with the existing cluster. Check the size of a recent snapshot to estimate the total data size. In other words, it is safest to wait for 2 minutes between upgrading each member.
   441  
   442  For a much larger total data size, 100MB or more , this one-time process might take even more time. Administrators of very large etcd clusters of this magnitude can feel free to contact the [etcd team][etcd-contact] before upgrading, and we'll be happy to provide advice on the procedure.
   443  
   444  #### Downgrade
   445  
   446  If all members have been upgraded to v3.3, the cluster will be upgraded to v3.3, and downgrade from this completed state is **not possible**. If any single member is still v3.2, however, the cluster and its operations remains "v3.2", and it is possible from this mixed cluster state to return to using a v3.2 etcd binary on all members.
   447  
   448  Please [backup the data directory](../op-guide/maintenance.md#snapshot-backup) of all etcd members to make downgrading the cluster possible even after it has been completely upgraded.
   449  
   450  ### Upgrade procedure
   451  
   452  This example shows how to upgrade a 3-member v3.2 ectd cluster running on a local machine.
   453  
   454  #### 1. Check upgrade requirements
   455  
   456  Is the cluster healthy and running v3.2.x?
   457  
   458  ```
   459  $ ETCDCTL_API=3 etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
   460  localhost:2379 is healthy: successfully committed proposal: took = 6.600684ms
   461  localhost:22379 is healthy: successfully committed proposal: took = 8.540064ms
   462  localhost:32379 is healthy: successfully committed proposal: took = 8.763432ms
   463  
   464  $ curl http://localhost:2379/version
   465  {"etcdserver":"3.2.7","etcdcluster":"3.2.0"}
   466  ```
   467  
   468  #### 2. Stop the existing etcd process
   469  
   470  When each etcd process is stopped, expected errors will be logged by other cluster members. This is normal since a cluster member connection has been (temporarily) broken:
   471  
   472  ```
   473  14:13:31.491746 I | raft: c89feb932daef420 [term 3] received MsgTimeoutNow from 6d4f535bae3ab960 and starts an election to get leadership.
   474  14:13:31.491769 I | raft: c89feb932daef420 became candidate at term 4
   475  14:13:31.491788 I | raft: c89feb932daef420 received MsgVoteResp from c89feb932daef420 at term 4
   476  14:13:31.491797 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 6d4f535bae3ab960 at term 4
   477  14:13:31.491805 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 9eda174c7df8a033 at term 4
   478  14:13:31.491815 I | raft: raft.node: c89feb932daef420 lost leader 6d4f535bae3ab960 at term 4
   479  14:13:31.524084 I | raft: c89feb932daef420 received MsgVoteResp from 6d4f535bae3ab960 at term 4
   480  14:13:31.524108 I | raft: c89feb932daef420 [quorum:2] has received 2 MsgVoteResp votes and 0 vote rejections
   481  14:13:31.524123 I | raft: c89feb932daef420 became leader at term 4
   482  14:13:31.524136 I | raft: raft.node: c89feb932daef420 elected leader c89feb932daef420 at term 4
   483  14:13:31.592650 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream MsgApp v2 reader)
   484  14:13:31.592825 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message reader)
   485  14:13:31.693275 E | rafthttp: failed to dial 6d4f535bae3ab960 on stream Message (dial tcp [::1]:2380: getsockopt: connection refused)
   486  14:13:31.693289 I | rafthttp: peer 6d4f535bae3ab960 became inactive
   487  14:13:31.936678 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message writer)
   488  ```
   489  
   490  It's a good idea at this point to [backup the etcd data](../op-guide/maintenance.md#snapshot-backup) to provide a downgrade path should any problems occur:
   491  
   492  ```
   493  $ etcdctl snapshot save backup.db
   494  ```
   495  
   496  #### 3. Drop-in etcd v3.3 binary and start the new etcd process
   497  
   498  The new v3.3 etcd will publish its information to the cluster:
   499  
   500  ```
   501  14:14:25.363225 I | etcdserver: published {Name:s1 ClientURLs:[http://localhost:2379]} to cluster a9ededbffcb1b1f1
   502  ```
   503  
   504  Verify that each member, and then the entire cluster, becomes healthy with the new v3.3 etcd binary:
   505  
   506  ```
   507  $ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
   508  localhost:22379 is healthy: successfully committed proposal: took = 5.540129ms
   509  localhost:32379 is healthy: successfully committed proposal: took = 7.321771ms
   510  localhost:2379 is healthy: successfully committed proposal: took = 10.629901ms
   511  ```
   512  
   513  Upgraded members will log warnings like the following until the entire cluster is upgraded. This is expected and will cease after all etcd cluster members are upgraded to v3.3:
   514  
   515  ```
   516  14:15:17.071804 W | etcdserver: member c89feb932daef420 has a higher version 3.3.0
   517  14:15:21.073110 W | etcdserver: the local etcd version 3.2.7 is not up-to-date
   518  14:15:21.073142 W | etcdserver: member 6d4f535bae3ab960 has a higher version 3.3.0
   519  14:15:21.073157 W | etcdserver: the local etcd version 3.2.7 is not up-to-date
   520  14:15:21.073164 W | etcdserver: member c89feb932daef420 has a higher version 3.3.0
   521  ```
   522  
   523  #### 4. Repeat step 2 to step 3 for all other members
   524  
   525  #### 5. Finish
   526  
   527  When all members are upgraded, the cluster will report upgrading to 3.3 successfully:
   528  
   529  ```
   530  14:15:54.536901 N | etcdserver/membership: updated the cluster version from 3.2 to 3.3
   531  14:15:54.537035 I | etcdserver/api: enabled capabilities for version 3.3
   532  ```
   533  
   534  ```
   535  $ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
   536  localhost:2379 is healthy: successfully committed proposal: took = 2.312897ms
   537  localhost:22379 is healthy: successfully committed proposal: took = 2.553476ms
   538  localhost:32379 is healthy: successfully committed proposal: took = 2.517902ms
   539  ```
   540  
   541  [etcd-contact]: https://groups.google.com/forum/#!forum/etcd-dev