learn.lianglianglee.com/专栏/深入浅出 Docker 技术栈实践课(完)/07 自动化部署分布式容器云平台实践.md.html
2022-05-11 18:57:05 +08:00

791 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>07 自动化部署分布式容器云平台实践.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/01 导读Docker 核心技术预览.md.html">01 导读Docker 核心技术预览.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/02 Docker 命令行实践.md.html">02 Docker 命令行实践.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/03 基于 Docker 的 DevOps 实践.md.html">03 基于 Docker 的 DevOps 实践.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/04 容器云平台的构建实践.md.html">04 容器云平台的构建实践.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/05 容器网络机制和多主机网络实践.md.html">05 容器网络机制和多主机网络实践.md.html</a>
</li>
<li>
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/06 Docker 日志机制与监控实践.md.html">06 Docker 日志机制与监控实践.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/深入浅出 Docker 技术栈实践课(完)/07 自动化部署分布式容器云平台实践.md.html">07 自动化部署分布式容器云平台实践.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>07 自动化部署分布式容器云平台实践</h1>
<h3>概述</h3>
<p>当前云计算场景中部署一套 Kubernetes 集群系统是最常见的容器需求。在初期阶段,大量的部署经验都依赖于前人设计实现的自动化部署工具之上,比如 Ansible。但是为什么这样的自动化工具并不能彻底解决所有 Kubernetes 集群的安装问题呢,主要的矛盾在于版本的升级更新动作在分布式系统的部署过程中,由于步骤复杂,无法提供统一的自动化框架来支持。</p>
<p>Ansible 需要撰写大量的有状态的情况来覆盖各种可能发生的部署阶段并做出判断。这种二次判断的操作对于 Ansible 这种自动化工具是无法适应的。Ansible 这样的工具期望行为一致性,如果发生可能发生的情况,将无法有效的保证后续的步奏能有效的安装。通过本文分享的 Kubernetes 社区中提供的安装套件可以帮助大家结合实践现在适合自己的部署分布式容器云平台的方法和工具链。</p>
<h3>Kubernetes Operationskops</h3>
<h4>生产级别 k8s 安装、升级和管理</h4>
<p>Ansible 部署 k8s 需要投入很多精力来维护集群知识的 roles 和 inventory在日常分布式系统中会带来很多不确定的异常很难维护。所以社区提供了 kops期望能像 kubectl 一样来管理集群部署的问题。目前实现了 AWS 的支持GCE 支持属于 Beta 阶段vSphere 处于 alpha 阶段,其他平台属于计划中。对于中国区的 AWS可以选用 cn-north-1 可用区来支持。</p>
<p><img src="assets/761bd110-ce66-11e7-891f-b774435a5fbc" alt="enter image description here" /></p>
<p>1、配置 AWS 信息</p>
<pre><code class="language-sh">AWS Access Key ID [None]:
AWS Secret Access Key [None]:
Default region name [None]:
Default output format [None]:
</code></pre>
<blockquote>
<p>注意需要声明可用区信息</p>
<pre><code class="language-shell">export AWS_REGION=$(aws configure get region)
</code></pre>
</blockquote>
<p>2、DNS 配置</p>
<p>因为工作区没有 AWS 的 Route53 支持,我们通过使用 gossip 技术可以绕过去这个限制。</p>
<p>3、集群状态存储</p>
<p>创建独立的 S3 区来存储集群安装状态。</p>
<pre><code class="language-shell">aws s3api create-bucket --bucket prefix-example-com-state-store --create-bucket-configuration LocationConstraint=$AWS_REGION
</code></pre>
<p>4、创建第一个 k8s 集群</p>
<p>在中国区执行安装的时候,会遇到网络不稳定的情况,使用如下的环境声明可以缓解此类问题:</p>
<pre><code class="language-shell">## Setup vars
KUBERNETES_VERSION=$(curl -fsSL --retry 5 &quot;https://dl.k8s.io/release/stable.txt&quot;)
KOPS_VERSION=$(curl -fsSL --retry 5 &quot;https://api.github.com/repos/kubernetes/kops/releases/latest&quot; | grep 'tag_name' | cut -d\&quot; -f4)
ASSET_BUCKET=&quot;some-asset-bucket&quot;
ASSET_PREFIX=&quot;&quot;
# Please note that this filename of cni asset may change with kubernetes version
CNI_FILENAME=cni-0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff.tar.gz
export KOPS_BASE_URL=https://s3.cn-north-1.amazonaws.com.cn/$ASSET_BUCKET/kops/$KOPS_VERSION/
export CNI_VERSION_URL=https://s3.cn-north-1.amazonaws.com.cn/$ASSET_BUCKET/kubernetes/network-plugins/$CNI_FILENAME
## Download assets
KUBERNETES_ASSETS=(
network-plugins/$CNI_FILENAME
release/$KUBERNETES_VERSION/bin/linux/amd64/kube-apiserver.tar
release/$KUBERNETES_VERSION/bin/linux/amd64/kube-controller-manager.tar
release/$KUBERNETES_VERSION/bin/linux/amd64/kube-proxy.tar
release/$KUBERNETES_VERSION/bin/linux/amd64/kube-scheduler.tar
release/$KUBERNETES_VERSION/bin/linux/amd64/kubectl
release/$KUBERNETES_VERSION/bin/linux/amd64/kubelet
)
for asset in &quot;${KUBERNETES_ASSETS[@]}&quot;; do
dir=&quot;kubernetes/$(dirname &quot;$asset&quot;)&quot;
mkdir -p &quot;$dir&quot;
url=&quot;https://storage.googleapis.com/kubernetes-release/$asset&quot;
wget -P &quot;$dir&quot; &quot;$url&quot;
[ &quot;${asset##*.}&quot; != &quot;gz&quot; ] &amp;&amp; wget -P &quot;$dir&quot; &quot;$url.sha1&quot;
[ &quot;${asset##*.}&quot; == &quot;tar&quot; ] &amp;&amp; wget -P &quot;$dir&quot; &quot;${url%.tar}.docker_tag&quot;
done
KOPS_ASSETS=(
&quot;images/protokube.tar.gz&quot;
&quot;linux/amd64/nodeup&quot;
&quot;linux/amd64/utils.tar.gz&quot;
)
for asset in &quot;${KOPS_ASSETS[@]}&quot;; do
kops_path=&quot;kops/$KOPS_VERSION/$asset&quot;
dir=&quot;$(dirname &quot;$kops_path&quot;)&quot;
mkdir -p &quot;$dir&quot;
url=&quot;https://kubeupv2.s3.amazonaws.com/kops/$KOPS_VERSION/$asset&quot;
wget -P &quot;$dir&quot; &quot;$url&quot;
wget -P &quot;$dir&quot; &quot;$url.sha1&quot;
done
## Upload assets
aws s3api create-bucket --bucket $ASSET_BUCKET --create-bucket-configuration LocationConstraint=$AWS_REGION
for dir in &quot;kubernetes&quot; &quot;kops&quot;; do
aws s3 sync --acl public-read &quot;$dir&quot; &quot;s3://$ASSET_BUCKET/$ASSET_PREFIX$dir&quot;
done
</code></pre>
<p>创建集群的时候加上参数:</p>
<pre><code class="language-shell"> --kubernetes-version https://s3.cn-north-1.amazonaws.com.cn/$ASSET_BUCKET/kubernetes/release/$KUBERNETES_VERSION
</code></pre>
<p>另外,还有一些镜像是托管在 gcr.io 中的,比如<code>pause-amd64</code> <code>dns</code>等。需要自行下载并提交部署到所有机器上才能做到离线安装。这里有一个技巧是通过自建的 <strong>Dockerfile</strong> 中加上</p>
<pre><code class="language-bash">FROM gcr.io/google_containers/pause-amd64
</code></pre>
<p>一行,并通过 Docker Cloud 自动构建的功能,把 pause-amd64 这样的镜像同步到 docker hub 中,方便国内的 AWS 主机可以下载使用。</p>
<h3>kubeadm——官方安装 k8s 集群命令行工具</h3>
<p>kubeadm 主要的目的就为简化部署集群的难度提供一键式指令如kubeadm init 和 kubeadm join 让用户在安装集群的过程中获得平滑的用户体验。</p>
<p><img src="assets/aeb84f30-ce66-11e7-891f-b774435a5fbc" alt="enter image description here" /></p>
<h4><code>kubeadm init</code></h4>
<p>初始化的过程被严格定义成多个阶段来分步骤跟踪集群的状态。有些参数必须需要调优:</p>
<ul>
<li>
<p>--apiserver-advertise-address 这个地址是用来让 API Server 来通告其他集群组件的 IP 地址。</p>
</li>
<li>
<p>--apiserver-bind-port 这个端口是 API Server 的端口默认是6443。</p>
</li>
<li>
<p>--apiserver-cert-extra-sans 附加的主机名字或地址,并加入到证书中。例如:</p>
<p><code>--apiserver-cert-extra-sans=kubernetes.example.com,kube.example.com,10.100.245.1</code></p>
</li>
<li>
<p>--cert-dir 证书地址,默认在 /etc/kubernetes/pki。</p>
</li>
<li>
<p>--config kubeadm 的配置文件。</p>
</li>
<li>
<p>--dry-run 这个参数告诉 kubeadm 不要执行,只是显示执行步骤。</p>
</li>
<li>
<p>--feature-gates 通过键值对来激活 alpha/experimental 的特性。</p>
</li>
<li>
<p>--kubernetes-version 集群初始化版本号。</p>
</li>
<li>
<p>--node-name 主机名称。</p>
</li>
<li>
<p>--pod-network-cidr 选择 pod 的网络网段。</p>
</li>
<li>
<p>--service-cidr 服务 IP 地址网段。</p>
</li>
<li>
<p>--service-dns-domain 服务域名,默认 cluster.local。</p>
</li>
<li>
<p>--skip-preflight-checks 默认 kubeadm 运行一系列事前检查来确认系统的有效性。</p>
</li>
<li>
<p>--skip-token-print 去掉默认打印 token 的行为。</p>
</li>
<li>
<p><code>--token</code> 指定 token 的字符串。</p>
</li>
<li>
<p>--token-ttl 配置 token 的过期时间默认24个小时。</p>
</li>
</ul>
<h4>kubeadm join</h4>
<p>两种连接方式:</p>
<ul>
<li>
<p>通过共享 token 和 ip 地址和 root CA key 来加入集群。</p>
<p><code>kubeadm join --discovery-token abcdef.1234567890abcdef --discovery-token-ca-cert-hash sha256:1234..cdef 1.2.3.4:6443</code></p>
</li>
<li>
<p>使用配置文件</p>
</li>
</ul>
<p><code>kubeadm join --discovery-file path/to/file.conf</code></p>
<h4><code>kubeadm config</code></h4>
<p>kubeadm v1.8.0+ 将自动创建 ConfigMap 提供kubeadm init 需要的所有参数。</p>
<h4><code>kubeadm reset</code></h4>
<p>取消 kubeadm init 或者 kubeadm join 对集群做的改动。</p>
<h4><code>kubeadm token</code></h4>
<p>管理集群需要的 token。</p>
<p>还有kubeadm 可以配置使用其他 docker runtime比如 cri-o 容器引擎。</p>
<pre><code class="language-shell">$ cat &gt; /etc/systemd/system/kubelet.service.d/20-cri.conf &lt;&lt;EOF
Environment=&quot;KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=$RUNTIME_ENDPOINT --feature-gates=AllAlpha=true&quot;
EOF
$ systemctl daemon-reload
</code></pre>
<p>通过初始化后,就可以调用 cri-o 引擎了。</p>
<h4>kubeadm 配置自定义镜像</h4>
<p>默认kubeadm 会拉取 gcr.io/google_containers 下的镜像。必须通过配置文件覆盖默认的镜像仓库的地址。</p>
<ul>
<li>imageRepository 去掉。gcr.io/google_containers 的值。</li>
<li>unifiedControlPlaneImage 提供面板镜像。</li>
<li>etcd.image 是 etcd 的镜像。</li>
</ul>
<h4>kubeadm 支持云端集成</h4>
<p>通过指定--cloud-provider 参数可以实现云端 k8s 集群的部署。比如阿里云就实现了<a href="https://github.com/AliyunContainerService/alicloud-controller-manager">一套 cloud provider</a> 帮助用户在阿里云一键部署一套集群。从当前社区的热度来看k8s 社区重点专注在kubeadm的扩展第三方的 cloud provider 可以自行实现功能kubeadm 可以通过参数的方式调用阿里云的基础组件。</p>
<h3>总结</h3>
<p>从 Ansible 自动化工具开始K8S 集群作为典型的分布式集群系统安装范本,社区在不断的优化用户体验。我们期望集群能够自举的完成系统级配置,并且通过 kubeadm 的方式帮助用户简单的、平滑的升级集群。实现这个 kubeadm可以帮助任意系统管理员不在为分布式系统的安装犯愁只需要一行命令就可以完成集群的搭建。所有生产级别的经验都被固化在 kubeadm 的代码中,我们通过参数加以调优,实现集群的生产级别的部署工作。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/深入浅出 Docker 技术栈实践课(完)/06 Docker 日志机制与监控实践.md.html">上一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997a4189ee3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>