├── README.md └── okdc-centos.sh /README.md: -------------------------------------------------------------------------------- 1 | # One-liner Kubernetes Deployment in China aka OKDC 2 | 3 | Deploy fully functional Kubernetes in China with a one-liner 4 | 5 | ## Why 6 | 7 | Automate the process, save some time, and stab GFW in the ass. 8 | 9 | ## What's Included 10 | 11 | Kubernetes, Etcd, Network layer (flannel, calico), GFW related workarounds. 12 | 13 | ## Usage 14 | 15 | Run on master: 16 | 17 | `curl -s https://raw.githubusercontent.com/kubeup/okdc/master/okdc-centos.sh|sh` 18 | 19 | Command for nodes will show up after master is done. 20 | 21 | If you want to install non-interactively, use 22 | 23 | `curl -s https://raw.githubusercontent.com/kubeup/okdc/master/okdc-centos.sh|NOINPUT=true sh` 24 | 25 | ## Supported OS 26 | 27 | - CentOS x86_64 6/7 28 | 29 | ## Supported Kubernetes Version 30 | 31 | - v1.6.2 32 | - v1.7.0 33 | 34 | ## Caveats 35 | 36 | You need a docker registry mirror address to install Calico (try Aliyun Accelerator). 37 | If you don't have that, use flannel instead. 38 | -------------------------------------------------------------------------------- /okdc-centos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # vim: noet ts=2 4 | 5 | set -e 6 | 7 | PREREQUISITES="uname rpm yum python" 8 | for cmd in $PREREQUISITES; do 9 | which $cmd >/dev/null || (echo "$cmd is required to run okdc" && exit 9) 10 | done 11 | 12 | VERSION=v1.4.1 13 | GPG_FILE=RPM-GPG-KEY-k8s 14 | ARCH=$(uname -m) 15 | OS_VERSION=$(rpm -q --queryformat '%{VERSION}' centos-release 2>/dev/null) 16 | MEM=$(cat /proc/meminfo |grep MemTotal|awk '{print $2}') 17 | ADMIN_CONF=/etc/kubernetes/admin.conf 18 | KUBELET_DROPLET=/etc/systemd/system/kubelet.service.d/99-kubelet-droplet.conf 19 | OKDC_BASE=https://raw.githubusercontent.com/kubeup/okdc/master 20 | 21 | 22 | # User tweakable vars 23 | NOINPUT=${NOINPUT} 24 | NETWORK=${NETWORK} # If it's user supplied, perform without prompt 25 | DEFAULT_NOINPUT_NETWORK=flannel 26 | REPO=${REPO:-https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el$OS_VERSION-$ARCH} 27 | REGISTRY_PREFIX=${REGISTRY_PREFIX:-registry.aliyuncs.com/archon} 28 | USER_DOCKER_MIRROR=$(python -c 'import json; d=json.load(open("/etc/docker/daemon.json")); print d.get("registry-mirrors",[])[0]' 2>/dev/null || true) 29 | DOCKER_MIRROR=${DOCKER_MIRROR:-${USER_DOCKER_MIRROR:-https://docker.mirrors.ustc.edu.cn}} 30 | K8S_VERSION=${K8S_VERSION:-v1.7.0} 31 | KUBEADM_VERSION=${KUBEADM_VERSION:-1.7.0} 32 | PAUSE_IMG=${PAUSE_IMG:-$REGISTRY_PREFIX/pause-amd64:3.0} 33 | HYPERKUBE_IMG=${HYPERKUBE_IMG:-$REGISTRY_PREFIX/hyperkube-amd64:$K8S_VERSION} 34 | ETCD_IMG=${ETCD_IMG:-$REGISTRY_PREFIX/etcd:3.0.17} 35 | KUBE_ALIYUN_IMG=${KUBE_ALIYUN_IMG:-registry.aliyuncs.com/kubeup/kube-aliyun} 36 | POD_IP_RANGE=${POD_IP_RANGE:-10.244.0.0/16} 37 | APISERVER_ADVERTISE_IP=${APISERVER_ADVERTISE_IP} 38 | TOKEN=${TOKEN:-$(python -c 'import random,string as s;t=lambda l:"".join(random.choice(s.ascii_lowercase + s.digits) for _ in range(l));print t(6)+"."+t(16)')} 39 | 40 | # Only required for node mode 41 | MASTER=${MASTER} 42 | 43 | readtty() { 44 | for varname; do true; done 45 | if [ -n "$NOINPUT" ]; then 46 | declare -g $varname=$NOINPUT_DEFAULT 47 | return 0 48 | fi 49 | 50 | read "$@" 1.5G && requires a docker mirror)" 159 | echo "3) Calico with kubernetes datastore (mem>1.5G && requires a docker mirror)" 160 | echo "4) Skip " 161 | readtty -n1 -p "Choose one to install: " INPUT 162 | case $INPUT in 163 | 1) 164 | install_flannel 165 | ;; 166 | 2) 167 | install_calico_with_etcd 168 | ;; 169 | 3) 170 | install_calico_with_kdd 171 | ;; 172 | 4) 173 | echo -e "\nSkipped." 174 | ;; 175 | *) 176 | echo -e "\nHuh??" 177 | continue 178 | esac 179 | break 180 | done 181 | echo 182 | return 0 183 | } 184 | 185 | setup_aliyun() { 186 | #readtty -n 1 -p "Deploy kube-aliyun as well? (to enable SLB, Routes and Volumes support) (Y/n)? " ENABLE_KUBE_ALIYUN 187 | #[ -z $ENABLE_KUBE_ALIYUN ] && ENABLE_KUBE_ALIYUN=y 188 | # 189 | #if [ "$ENABLE_KUBE_ALIYUN" = "y" ]; then 190 | # [ -n "$ALIYUN_ACCESS_KEY" ] && KEY_DEFAULT="(default: $ALIYUN_ACCESS_KEY)" 191 | # readtty -p "Aliyun Access Key?$KEY_DEFAULT " INPUT 192 | # ALIYUN_ACCESS_KEY=${INPUT:-$ALIYUN_ACCESS_KEY} 193 | # [ -z "$ALIYUN_ACCESS_KEY" ] && echo "Can't proceed without it" && exit 2 194 | # 195 | # unset KEY_DEFAULT 196 | # [ -n "$ALIYUN_ACCESS_KEY_SECRET" ] && KEY_DEFAULT="(default: $ALIYUN_ACCESS_KEY_SECRET)" 197 | # readtty -p "Aliyun Access Key Secret?$KEY_DEFAULT " INPUT 198 | # ALIYUN_ACCESS_KEY_SECRET=${INPUT:-$ALIYUN_ACCESS_KEY_SECRET} 199 | # [ -z "$ALIYUN_ACCESS_KEY_SECRET" ] && echo "Can't proceed without it" && exit 2 200 | #fi 201 | echo 202 | } 203 | 204 | update_yum() { 205 | # Update yum repo 206 | cat >/etc/pki/rpm-gpg/$GPG_FILE <<-END 207 | -----BEGIN PGP PUBLIC KEY BLOCK----- 208 | Version: GnuPG v1 209 | 210 | mQENBFWKtqgBCADmKQWYQF9YoPxLEQZ5XA6DFVg9ZHG4HIuehsSJETMPQ+W9K5c5 211 | Us5assCZBjG/k5i62SmWb09eHtWsbbEgexURBWJ7IxA8kM3kpTo7bx+LqySDsSC3 212 | /8JRkiyibVV0dDNv/EzRQsGDxmk5Xl8SbQJ/C2ECSUT2ok225f079m2VJsUGHG+5 213 | RpyHHgoMaRNedYP8ksYBPSD6sA3Xqpsh/0cF4sm8QtmsxkBmCCIjBa0B0LybDtdX 214 | XIq5kPJsIrC2zvERIPm1ez/9FyGmZKEFnBGeFC45z5U//pHdB1z03dYKGrKdDpID 215 | 17kNbC5wl24k/IeYyTY9IutMXvuNbVSXaVtRABEBAAG0Okdvb2dsZSBDbG91ZCBQ 216 | YWNrYWdlcyBSUE0gU2lnbmluZyBLZXkgPGdjLXRlYW1AZ29vZ2xlLmNvbT6JATgE 217 | EwECACIFAlWKtqgCGy8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPCcOUw+ 218 | G6jV+QwH/0wRH+XovIwLGfkg6kYLEvNPvOIYNQWnrT6zZ+XcV47WkJ+i5SR+QpUI 219 | udMSWVf4nkv+XVHruxydafRIeocaXY0E8EuIHGBSB2KR3HxG6JbgUiWlCVRNt4Qd 220 | 6udC6Ep7maKEIpO40M8UHRuKrp4iLGIhPm3ELGO6uc8rks8qOBMH4ozU+3PB9a0b 221 | GnPBEsZdOBI1phyftLyyuEvG8PeUYD+uzSx8jp9xbMg66gQRMP9XGzcCkD+b8w1o 222 | 7v3J3juKKpgvx5Lqwvwv2ywqn/Wr5d5OBCHEw8KtU/tfxycz/oo6XUIshgEbS/+P 223 | 6yKDuYhRp6qxrYXjmAszIT25cftb4d4= 224 | =/PbX 225 | -----END PGP PUBLIC KEY BLOCK----- 226 | END 227 | 228 | cat >/etc/yum.repos.d/k8s.repo <<-END 229 | [kubernetes] 230 | name=Kubernetes Repo 231 | baseurl=$REPO 232 | enabled=1 233 | gpgkey=file:///etc/pki/rpm-gpg/$GPG_FILE 234 | gpgcheck=1 235 | END 236 | 237 | # Install stuff 238 | yum updateinfo 239 | yum install -y kubectl kubernetes-cni docker kubelet "kubeadm-$KUBEADM_VERSION" 240 | } 241 | 242 | update_kubelet() { 243 | # Kubelet droplet 244 | mkdir -p $(dirname $KUBELET_DROPLET) 245 | cat >$KUBELET_DROPLET <<-END 246 | [Unit] 247 | Wants=flexv.service 248 | After=flexv.service 249 | [Service] 250 | Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni" 251 | Environment="KUBELET_EXTRA_ARGS=--pod-infra-container-image=$PAUSE_IMG --cgroup-driver=systemd" 252 | END 253 | chmod +x $KUBELET_DROPLET 254 | } 255 | 256 | patch_kubelet() { 257 | # Due to an issue of kubeadm, we need to switch to cni network after kubeadm is done. #43815 258 | sed -i "/kubenet/d" $KUBELET_DROPLET 259 | systemctl daemon-reload 260 | } 261 | 262 | restart_kubelet() { 263 | systemctl restart kubelet 264 | } 265 | 266 | set_accelerator() { 267 | if [ -n "$DOCKER_MIRROR" ]; then 268 | NOINPUT_DEFAULT="${DOCKER_MIRROR}" readtty -p "Docker registry mirror, ex. Aliyun accelerator? (default: $DOCKER_MIRROR) " INPUT 269 | [ -n "$INPUT" ] && DOCKER_MIRROR=$INPUT 270 | else 271 | NOINPUT_DEFAULT="" readtty -p "Docker registry mirror, ex. Aliyun accelerator? (empty to skip) " DOCKER_MIRROR 272 | fi 273 | 274 | # Docker accelerator 275 | if [ -n "$DOCKER_MIRROR" ] && [ "$DOCKER_MIRROR" != "$USER_DOCKER_MIRROR" ]; then 276 | mkdir -p /etc/docker 277 | cat >/etc/docker/daemon.json <<-END 278 | { 279 | "registry-mirrors": ["$DOCKER_MIRROR"] 280 | } 281 | END 282 | fi 283 | 284 | true 285 | } 286 | 287 | run_kubeadm() { 288 | # Kubeadm config 289 | if [ ! -f /tmp/kubeadm.conf ]; then 290 | cat >/tmp/kubeadm.conf <<-END 291 | apiVersion: kubeadm.k8s.io/v1alpha1 292 | kind: MasterConfiguration 293 | api: 294 | advertiseAddress: $APISERVER_ADVERTISE_IP 295 | networking: 296 | podSubnet: $POD_IP_RANGE 297 | kubernetesVersion: $K8S_VERSION 298 | token: $TOKEN 299 | END 300 | fi 301 | 302 | KUBE_HYPERKUBE_IMAGE=$HYPERKUBE_IMG KUBE_ETCD_IMAGE=$ETCD_IMG KUBE_REPO_PREFIX=$REGISTRY_PREFIX kubeadm init --skip-preflight-checks --config /tmp/kubeadm.conf |tee /tmp/kubeadm.log 303 | MASTER_IP=$( grep "kubeadm join" /tmp/kubeadm.log|awk '{print $5}' ) 304 | } 305 | 306 | run_kubeadm_node() { 307 | KUBE_HYPERKUBE_IMAGE=$HYPERKUBE_IMG KUBE_REPO_PREFIX=$REGISTRY_PREFIX kubeadm join --token $TOKEN $MASTER 308 | } 309 | 310 | enable_services() { 311 | # Disable SELinux 312 | setenforce 0 || true 313 | 314 | # Enable services 315 | systemctl daemon-reload 316 | systemctl enable docker && systemctl start docker 317 | systemctl enable kubelet && systemctl start kubelet 318 | } 319 | 320 | show_node_cmd() { 321 | [ -z "$MASTER_IP" ] && exit 3 322 | [ -n "$DOCKER_MIRROR" ] && TMP_MIRROR=" DOCKER_MIRROR=$DOCKER_MIRROR" 323 | [ -n "$NOINPUT" ] && TMP_NOINPUT=" NOINPUT=$NOINPUT" 324 | echo 325 | echo Run the following command on your nodes to join the cluster 326 | echo 327 | echo "curl -s $OKDC_BASE/okdc-centos.sh|TOKEN=$TOKEN MASTER=$MASTER_IP$TMP_MIRROR$TMP_NOINPUT sh" 328 | } 329 | 330 | check_env() { 331 | if [ "$(id -u)" != "0" ]; then 332 | echo "This script must be run as root" 1>&2 333 | exit 1 334 | fi 335 | } 336 | 337 | update_bridge() { 338 | grep "^net.bridge.bridge-nf-call-arptables" /etc/sysctl.conf >>/dev/null || echo "net.bridge.bridge-nf-call-arptables = 1" >> /etc/sysctl.conf 339 | grep "^net.bridge.bridge-nf-call-iptables" /etc/sysctl.conf >>/dev/null || echo "net.bridge.bridge-nf-call-iptables = 1" >> /etc/sysctl.conf 340 | grep "^net.bridge.bridge-nf-call-ip6tables" /etc/sysctl.conf >>/dev/null || echo "net.bridge.bridge-nf-call-ip6tables = 1" >> /etc/sysctl.conf 341 | sysctl -p >>/dev/null 342 | } 343 | 344 | check_node_prerequisite() { 345 | [ -z "$MASTER" ] && echo "MASTER is required but not defined" && exit 4 346 | [ -z "$TOKEN" ] && echo "TOKEN is required but not defined" && exit 4 347 | true 348 | } 349 | 350 | detect_advertise_ip() { 351 | if [ -z "$APISERVER_ADVERTISE_IP" ]; then 352 | ips=$(ip -4 -o addr show|grep eth|awk '{print $4}') 353 | for i in $ips; do 354 | ret=$(echo $i|grep -E '^(192\.168|10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.)[^ /]+' -o) 355 | [ -n "$ret" ] && APISERVER_ADVERTISE_IP="$ret" && break 356 | done 357 | fi 358 | 359 | if [ -z "$APISERVER_ADVERTISE_IP" ]; then 360 | echo "Failed to detect private ip. Will let kubeadm decide which ip to advertise." 361 | else 362 | echo "Using $APISERVER_ADVERTISE_IP as advertise ip" 363 | fi 364 | true 365 | } 366 | 367 | run_master() { 368 | intro 369 | 370 | check_env 371 | pause 372 | 373 | update_yum 374 | detect_advertise_ip 375 | set_accelerator 376 | update_kubelet 377 | enable_services 378 | update_bridge 379 | run_kubeadm 380 | patch_kubelet 381 | restart_kubelet 382 | install_network 383 | 384 | show_node_cmd 385 | echo "Done" 386 | } 387 | 388 | run_node() { 389 | intro 390 | 391 | check_env 392 | check_node_prerequisite 393 | pause 394 | 395 | update_yum 396 | set_accelerator 397 | update_kubelet 398 | patch_kubelet 399 | enable_services 400 | update_bridge 401 | run_kubeadm_node 402 | 403 | echo "Done" 404 | } 405 | 406 | if [ -n "$MASTER" ]; then 407 | run_node 408 | else 409 | run_master 410 | fi 411 | 412 | 413 | --------------------------------------------------------------------------------