**Describe the solution you'd like**

We already can run clickhouse-server without configured ZooKeeper (the tables will start in read-only mode)
and with configured ZooKeeper, if it starts to be inaccessible at runtime (the tables will be read-only until the session is re-established).

Let's also make it possible to start up when ZooKeeper is unavailable.

**Additional context**

If that will be very hard to implement, we can consider other options:
- lazy attaching of the tables after sever start-up;
- a maintenance mode when the tables are not attached at start-up and require manual ATTACH queries.
void checkBrokenDisks();
    static bool removeSharedDetachedPart(DiskPtr disk, const String & path, const String & part_name, const String & table_uuid,
        const String & zookeeper_name, const String & replica_name, const String & zookeeper_path, ContextPtr local_context);
private:
    std::atomic_bool are_restoring_replica {false};
    
    ReplicatedMergeTreeQuorumAddedParts::PartitionIdToMaxBlock getMaxAddedBlocks() const;
    
    void clearOldPartsAndRemoveFromZK();
    friend class ReplicatedMergeTreeSink;
    friend class ReplicatedMergeTreePartCheckThread;
    friend class ReplicatedMergeTreeCleanupThread;
    friend class ReplicatedMergeTreeAlterThread;
    friend class ReplicatedMergeTreeRestartingThread;
    friend class ReplicatedMergeTreeMergeStrategyPicker;
    friend struct ReplicatedMergeTreeLogEntry;
    friend class ScopedPartitionMergeLock;
    friend class ReplicatedMergeTreeQueue;
    friend class PartMovesBetweenShardsOrchestrator;
    friend class MergeTreeData;
    friend class MergeFromLogEntryTask;
    friend class MutateFromLogEntryTask;
    friend class ReplicatedMergeMutateTaskBase;
    using MergeStrategyPicker = ReplicatedMergeTreeMergeStrategyPicker;
    using LogEntry = ReplicatedMergeTreeLogEntry;
    using LogEntryPtr = LogEntry::Ptr;
    using MergeTreeData::MutableDataPartPtr;
    zkutil::ZooKeeperPtr current_zookeeper;