mirror of
https://github.com/puppetmaster/typhoon.git
synced 2025-08-02 13:21:34 +02:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
00b61a26c0 | |||
1efe39d6bc | |||
ec46bc13ae | |||
d48f88cfd6 | |||
6ef326a872 | |||
64435adbc3 | |||
140e869278 | |||
082dedbdbd | |||
a2609c14c0 | |||
564c0160bf | |||
5b2275872c | |||
2faacc6a50 | |||
056bd8a059 | |||
6a574d4a01 | |||
b29a6cd1cd | |||
a97bbf7128 | |||
dc3ff174ea | |||
fc018ffa28 | |||
bac968d3eb | |||
40bd338eab | |||
e5975cf9c7 | |||
e19517d3df | |||
f04411377f | |||
cafc58c610 | |||
88ba9bd273 | |||
29ed9eaa33 | |||
b303c0e986 | |||
097dcdf47e | |||
efff7497eb | |||
6070ffb449 | |||
2d33b9abe2 | |||
da596e06bb | |||
833c92b2bf | |||
386dc49a58 | |||
4df6bb81a8 | |||
75f4826097 |
33
.github/ISSUE_TEMPLATE.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!-- Fill in either the 'Bug' or 'Feature Request' section -->
|
||||||
|
|
||||||
|
## Bug
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
|
||||||
|
* Platform: bare-metal, google-cloud, digital-ocean
|
||||||
|
* OS: container-linux, fedora-cloud
|
||||||
|
* Terraform: `terraform version`
|
||||||
|
* Plugins: Provider plugin versions
|
||||||
|
* Ref: Git SHA (if applicable)
|
||||||
|
|
||||||
|
### Problem
|
||||||
|
|
||||||
|
Describe the problem.
|
||||||
|
|
||||||
|
### Desired Behavior
|
||||||
|
|
||||||
|
Describe the goal.
|
||||||
|
|
||||||
|
### Steps to Reproduce
|
||||||
|
|
||||||
|
Provide clear steps to reproduce the issue unless already covered.
|
||||||
|
|
||||||
|
## Feature Request
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
Describe the feature and what problem it solves.
|
||||||
|
|
||||||
|
### Tradeoffs
|
||||||
|
|
||||||
|
What are the pros and cons of this feature? How will it be exercised and maintained?
|
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
High level description of the change.
|
||||||
|
|
||||||
|
* Specific change
|
||||||
|
* Specific change
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Describe your work to validate the change works.
|
||||||
|
|
||||||
|
rel: issue number (if applicable)
|
5
CONTRIBUTING.md
Normal file
5
CONTRIBUTING.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Developer Certificate of Origin
|
||||||
|
|
||||||
|
By contributing, you agree to the Linux Foundation's Developer Certificate of Origin ([DOC](DCO)). The DCO is a statement that you, the contributor, have the legal right to make your contribution and understand the contribution will be distributed as part of this project.
|
37
DCO
Normal file
37
DCO
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
1 Letterman Drive
|
||||||
|
Suite D4700
|
||||||
|
San Francisco, CA, 94129
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
23
LICENSE
Normal file
23
LICENSE
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Typhoon Authors
|
||||||
|
Copyright (c) 2017 Dalton Hubble
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
125
README.md
Normal file
125
README.md
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# Typhoon <img align="right" src="https://storage.googleapis.com/dghubble/spin.png">
|
||||||
|
|
||||||
|
Typhoon is a minimal and free Kubernetes distribution.
|
||||||
|
|
||||||
|
* Minimal, stable base Kubernetes distribution
|
||||||
|
* Declarative infrastructure and configuration
|
||||||
|
* [Free](#social-contract) (freedom and cost) and privacy-respecting
|
||||||
|
* Practical for labs, datacenters, and clouds
|
||||||
|
|
||||||
|
Typhoon distributes upstream Kubernetes, architectural conventions, and cluster addons, much like a GNU/Linux distribution provides the Linux kernel and userspace components.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Kubernetes v1.7.3 (upstream, via [kubernetes-incubator/bootkube](https://github.com/kubernetes-incubator/bootkube))
|
||||||
|
* Self-hosted control plane, single or multi master, workloads isolated to workers
|
||||||
|
* On-cluster etcd with TLS, [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/)-enabled
|
||||||
|
* Ready for Ingress, Metrics, Dashboards, and other optional [addons](https://typhoon.psdn.io/addons/overview/)
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
Typhoon provides a Terraform Module for each supported operating system and platform.
|
||||||
|
|
||||||
|
| Platform | Operating System | Terraform Module |
|
||||||
|
|---------------|------------------|------------------|
|
||||||
|
| Bare-Metal | Container Linux | [bare-metal/container-linux/kubernetes](bare-metal/container-linux/kubernetes) |
|
||||||
|
| Digital Ocean | Container Linux | [digital-ocean/container-linux/kubernetes](digital-ocean/container-linux/kubernetes) |
|
||||||
|
| Google Cloud | Container Linux | [google-cloud/container-linux/kubernetes](google-cloud/container-linux/kubernetes) |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
* [Docs](https://typhoon.psdn.io)
|
||||||
|
* [Concepts](https://typhoon.psdn.io/concepts/)
|
||||||
|
* [Bare-Metal](https://typhoon.psdn.io/bare-metal/)
|
||||||
|
* [Digital Ocean](https://typhoon.psdn.io/digital-ocean/)
|
||||||
|
* [Google-Cloud](https://typhoon.psdn.io/google-cloud/)
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Define a Kubernetes cluster by using the Terraform module for your chosen platform and operating system. Here's a minimal example:
|
||||||
|
|
||||||
|
```tf
|
||||||
|
module "google-cloud-yavin" {
|
||||||
|
source = "git::https://github.com/poseidon/typhoon//google-cloud/container-linux/kubernetes"
|
||||||
|
|
||||||
|
# Google Cloud
|
||||||
|
zone = "us-central1-c"
|
||||||
|
dns_zone = "example.com"
|
||||||
|
dns_zone_name = "example-zone"
|
||||||
|
os_image = "coreos-stable-1465-6-0-v20170817"
|
||||||
|
|
||||||
|
cluster_name = "yavin"
|
||||||
|
controller_count = 1
|
||||||
|
worker_count = 2
|
||||||
|
ssh_authorized_key = "ssh-rsa AAAAB3Nz..."
|
||||||
|
|
||||||
|
# output assets dir
|
||||||
|
asset_dir = "/home/user/.secrets/clusters/yavin"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Fetch modules, plan the changes to be made, and apply the changes.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform get --update
|
||||||
|
$ terraform plan
|
||||||
|
Plan: 37 to add, 0 to change, 0 to destroy.
|
||||||
|
$ terraform apply
|
||||||
|
Apply complete! Resources: 37 added, 0 changed, 0 destroyed.
|
||||||
|
```
|
||||||
|
|
||||||
|
In 5-10 minutes (varies by platform), the cluster will be ready. This Google Cloud example creates a `yavin.example.com` DNS record to resolve to a network load balancer across controller nodes.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ KUBECONFIG=/home/user/.secrets/clusters/yavin/auth/kubeconfig
|
||||||
|
$ kubectl get nodes
|
||||||
|
NAME STATUS AGE VERSION
|
||||||
|
yavin-controller-1682.c.example-com.internal Ready 6m v1.7.3+coreos.0
|
||||||
|
yavin-worker-jrbf.c.example-com.internal Ready 5m v1.7.3+coreos.0
|
||||||
|
yavin-worker-mzdm.c.example-com.internal Ready 5m v1.7.3+coreos.0
|
||||||
|
```
|
||||||
|
|
||||||
|
List the pods.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get pods --all-namespaces
|
||||||
|
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||||
|
kube-system etcd-operator-3329263108-f443m 1/1 Running 1 6m
|
||||||
|
kube-system kube-apiserver-zppls 1/1 Running 0 6m
|
||||||
|
kube-system kube-controller-manager-3271970485-gh9kt 1/1 Running 0 6m
|
||||||
|
kube-system kube-controller-manager-3271970485-h90v8 1/1 Running 1 6m
|
||||||
|
kube-system kube-dns-1187388186-zj5dl 3/3 Running 0 6m
|
||||||
|
kube-system kube-etcd-0000 1/1 Running 0 5m
|
||||||
|
kube-system kube-etcd-network-checkpointer-crznb 1/1 Running 0 6m
|
||||||
|
kube-system kube-flannel-1cs8z 2/2 Running 0 6m
|
||||||
|
kube-system kube-flannel-d1l5b 2/2 Running 0 6m
|
||||||
|
kube-system kube-flannel-sp9ps 2/2 Running 0 6m
|
||||||
|
kube-system kube-proxy-117v6 1/1 Running 0 6m
|
||||||
|
kube-system kube-proxy-9886n 1/1 Running 0 6m
|
||||||
|
kube-system kube-proxy-njn47 1/1 Running 0 6m
|
||||||
|
kube-system kube-scheduler-3895335239-5x87r 1/1 Running 0 6m
|
||||||
|
kube-system kube-scheduler-3895335239-bzrrt 1/1 Running 1 6m
|
||||||
|
kube-system pod-checkpointer-l6lrt 1/1 Running 0 6m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
Typhoon is strict about minimalism, maturity, and scope. These are not in scope:
|
||||||
|
|
||||||
|
* In-place Kubernetes Upgrades
|
||||||
|
* Adding every possible option
|
||||||
|
* Openstack or Mesos platforms
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Typhoon powers the author's cloud and colocation clusters. The project has evolved through operational experience and Kubernetes changes. Typhoon is shared under a free license to allow others to use the work freely and contribute to its upkeep.
|
||||||
|
|
||||||
|
Typhoon addresses real world needs, which you may share. It is honest about limitations or areas that aren't mature yet. It avoids buzzword bingo and hype. It does not aim to be the one-solution-fits-all distro. An ecosystem of free (or enterprise) Kubernetes distros is healthy.
|
||||||
|
|
||||||
|
## Social Contract
|
||||||
|
|
||||||
|
Typhoon is not a product, trial, or free-tier. It is not run by a company, does not offer support or services, and does not accept or make any money. It is not associated with any operating system or platform vendor.
|
||||||
|
|
||||||
|
Typhoon clusters will contain only [free](https://www.debian.org/intro/free) components. Cluster components will not collect data on users without their permission.
|
||||||
|
|
||||||
|
*Disclosure: The author works for CoreOS and previously wrote Matchbox and original Tectonic for bare-metal and AWS. This project is not associated with CoreOS.*
|
56
addons/cluo/update-agent.yaml
Normal file
56
addons/cluo/update-agent.yaml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: container-linux-update-agent
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxUnavailable: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: container-linux-update-agent
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: update-agent
|
||||||
|
image: quay.io/coreos/container-linux-update-operator:v0.3.1
|
||||||
|
command:
|
||||||
|
- "/bin/update-agent"
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/run/dbus
|
||||||
|
name: var-run-dbus
|
||||||
|
- mountPath: /etc/coreos
|
||||||
|
name: etc-coreos
|
||||||
|
- mountPath: /usr/share/coreos
|
||||||
|
name: usr-share-coreos
|
||||||
|
- mountPath: /etc/os-release
|
||||||
|
name: etc-os-release
|
||||||
|
env:
|
||||||
|
# read by update-agent as the node name to manage reboots for
|
||||||
|
- name: UPDATE_AGENT_NODE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
tolerations:
|
||||||
|
- key: node-role.kubernetes.io/master
|
||||||
|
operator: Exists
|
||||||
|
effect: NoSchedule
|
||||||
|
volumes:
|
||||||
|
- name: var-run-dbus
|
||||||
|
hostPath:
|
||||||
|
path: /var/run/dbus
|
||||||
|
- name: etc-coreos
|
||||||
|
hostPath:
|
||||||
|
path: /etc/coreos
|
||||||
|
- name: usr-share-coreos
|
||||||
|
hostPath:
|
||||||
|
path: /usr/share/coreos
|
||||||
|
- name: etc-os-release
|
||||||
|
hostPath:
|
||||||
|
path: /etc/os-release
|
23
addons/cluo/update-operator.yaml
Normal file
23
addons/cluo/update-operator.yaml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: container-linux-update-operator
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: container-linux-update-operator
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: update-operator
|
||||||
|
image: quay.io/coreos/container-linux-update-operator:v0.3.1
|
||||||
|
command:
|
||||||
|
- "/bin/update-operator"
|
||||||
|
- "--analytics=false"
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
32
addons/dashboard/deployment.yaml
Normal file
32
addons/dashboard/deployment.yaml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: kubernetes-dashboard
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
name: kubernetes-dashboard
|
||||||
|
phase: prod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: kubernetes-dashboard
|
||||||
|
image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.6.1
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 9090
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 300Mi
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 100Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 9090
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 30
|
15
addons/dashboard/service.yaml
Normal file
15
addons/dashboard/service.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: kubernetes-dashboard
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
name: kubernetes-dashboard
|
||||||
|
phase: prod
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
protocol: TCP
|
||||||
|
port: 80
|
||||||
|
targetPort: 9090
|
63
addons/heapster/deployment.yaml
Normal file
63
addons/heapster/deployment.yaml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: heapster
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
k8s-app: heapster
|
||||||
|
kubernetes.io/cluster-service: "true"
|
||||||
|
version: v1.4.0
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
k8s-app: heapster
|
||||||
|
version: v1.4.0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-app: heapster
|
||||||
|
version: v1.4.0
|
||||||
|
annotations:
|
||||||
|
scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: heapster
|
||||||
|
image: gcr.io/google_containers/heapster-amd64:v1.4.0
|
||||||
|
command:
|
||||||
|
- /heapster
|
||||||
|
- --source=kubernetes.summary_api:''
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8082
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 180
|
||||||
|
timeoutSeconds: 5
|
||||||
|
- name: heapster-nanny
|
||||||
|
image: gcr.io/google_containers/addon-resizer:2.0
|
||||||
|
command:
|
||||||
|
- /pod_nanny
|
||||||
|
- --cpu=80m
|
||||||
|
- --extra-cpu=0.5m
|
||||||
|
- --memory=140Mi
|
||||||
|
- --extra-memory=4Mi
|
||||||
|
- --deployment=heapster-v1.4.0
|
||||||
|
- --container=heapster
|
||||||
|
- --poll-period=300000
|
||||||
|
env:
|
||||||
|
- name: MY_POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: MY_POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 90Mi
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 90Mi
|
15
addons/heapster/service.yaml
Normal file
15
addons/heapster/service.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: heapster
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
kubernetes.io/cluster-service: "true"
|
||||||
|
kubernetes.io/name: "Heapster"
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
k8s-app: heapster
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8082
|
14
bare-metal/container-linux/kubernetes/bootkube.tf
Normal file
14
bare-metal/container-linux/kubernetes/bootkube.tf
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Self-hosted Kubernetes assets (kubeconfig, manifests)
|
||||||
|
module "bootkube" {
|
||||||
|
source = "git::https://github.com/poseidon/bootkube-terraform.git?ref=5ffbfec46dc05721eaf9d15c3c9bbedefaead1bc"
|
||||||
|
|
||||||
|
cluster_name = "${var.cluster_name}"
|
||||||
|
api_servers = ["${var.k8s_domain_name}"]
|
||||||
|
etcd_servers = ["${var.controller_domains}"]
|
||||||
|
asset_dir = "${var.asset_dir}"
|
||||||
|
networking = "${var.networking}"
|
||||||
|
network_mtu = "${var.network_mtu}"
|
||||||
|
pod_cidr = "${var.pod_cidr}"
|
||||||
|
service_cidr = "${var.service_cidr}"
|
||||||
|
experimental_self_hosted_etcd = "${var.experimental_self_hosted_etcd}"
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
systemd:
|
||||||
|
units:
|
||||||
|
- name: installer.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Requires=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/opt/installer
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
storage:
|
||||||
|
files:
|
||||||
|
- path: /opt/installer
|
||||||
|
filesystem: root
|
||||||
|
mode: 0500
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
#!/bin/bash -ex
|
||||||
|
curl --retry 10 "${ignition_endpoint}?{{.request.raw_query}}&os=installed" -o ignition.json
|
||||||
|
coreos-install \
|
||||||
|
-d ${install_disk} \
|
||||||
|
-C ${container_linux_channel} \
|
||||||
|
-V ${container_linux_version} \
|
||||||
|
-o "${container_linux_oem}" \
|
||||||
|
${baseurl_flag} \
|
||||||
|
-i ignition.json
|
||||||
|
udevadm settle
|
||||||
|
systemctl reboot
|
||||||
|
passwd:
|
||||||
|
users:
|
||||||
|
- name: core
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- {{.ssh_authorized_key}}
|
178
bare-metal/container-linux/kubernetes/cl/controller.yaml.tmpl
Normal file
178
bare-metal/container-linux/kubernetes/cl/controller.yaml.tmpl
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
---
|
||||||
|
systemd:
|
||||||
|
units:
|
||||||
|
{{ if eq .etcd_on_host "true" }}
|
||||||
|
- name: etcd-member.service
|
||||||
|
enable: true
|
||||||
|
dropins:
|
||||||
|
- name: 40-etcd-cluster.conf
|
||||||
|
contents: |
|
||||||
|
[Service]
|
||||||
|
Environment="ETCD_IMAGE_TAG=v3.2.0"
|
||||||
|
Environment="ETCD_NAME={{.etcd_name}}"
|
||||||
|
Environment="ETCD_ADVERTISE_CLIENT_URLS=https://{{.domain_name}}:2379"
|
||||||
|
Environment="ETCD_INITIAL_ADVERTISE_PEER_URLS=https://{{.domain_name}}:2380"
|
||||||
|
Environment="ETCD_LISTEN_CLIENT_URLS=https://0.0.0.0:2379"
|
||||||
|
Environment="ETCD_LISTEN_PEER_URLS=https://0.0.0.0:2380"
|
||||||
|
Environment="ETCD_INITIAL_CLUSTER={{.etcd_initial_cluster}}"
|
||||||
|
Environment="ETCD_STRICT_RECONFIG_CHECK=true"
|
||||||
|
Environment="ETCD_SSL_DIR=/etc/ssl/etcd"
|
||||||
|
Environment="ETCD_TRUSTED_CA_FILE=/etc/ssl/certs/etcd/server-ca.crt"
|
||||||
|
Environment="ETCD_CERT_FILE=/etc/ssl/certs/etcd/server.crt"
|
||||||
|
Environment="ETCD_KEY_FILE=/etc/ssl/certs/etcd/server.key"
|
||||||
|
Environment="ETCD_CLIENT_CERT_AUTH=true"
|
||||||
|
Environment="ETCD_PEER_TRUSTED_CA_FILE=/etc/ssl/certs/etcd/peer-ca.crt"
|
||||||
|
Environment="ETCD_PEER_CERT_FILE=/etc/ssl/certs/etcd/peer.crt"
|
||||||
|
Environment="ETCD_PEER_KEY_FILE=/etc/ssl/certs/etcd/peer.key"
|
||||||
|
Environment="ETCD_PEER_CLIENT_CERT_AUTH=true"
|
||||||
|
{{ end }}
|
||||||
|
- name: docker.service
|
||||||
|
enable: true
|
||||||
|
- name: locksmithd.service
|
||||||
|
mask: true
|
||||||
|
- name: kubelet.path
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Watch for kubeconfig
|
||||||
|
[Path]
|
||||||
|
PathExists=/etc/kubernetes/kubeconfig
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- name: wait-for-dns.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Wait for DNS entries
|
||||||
|
Wants=systemd-resolved.service
|
||||||
|
Before=kubelet.service
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/bin/sh -c 'while ! /usr/bin/grep '^[^#[:space:]]' /etc/resolv.conf > /dev/null; do sleep 1; done'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=kubelet.service
|
||||||
|
- name: kubelet.service
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Kubelet via Hyperkube ACI
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=/etc/kubernetes/kubelet.env
|
||||||
|
Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
|
||||||
|
--volume=resolv,kind=host,source=/etc/resolv.conf \
|
||||||
|
--mount volume=resolv,target=/etc/resolv.conf \
|
||||||
|
--volume var-lib-cni,kind=host,source=/var/lib/cni \
|
||||||
|
--mount volume=var-lib-cni,target=/var/lib/cni \
|
||||||
|
--volume var-log,kind=host,source=/var/log \
|
||||||
|
--mount volume=var-log,target=/var/log"
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/cni/net.d
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/checkpoint-secrets
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/inactive-manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /var/lib/cni
|
||||||
|
ExecStartPre=/usr/bin/bash -c "grep 'certificate-authority-data' /etc/kubernetes/kubeconfig | awk '{print $2}' | base64 -d > /etc/kubernetes/ca.crt"
|
||||||
|
ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
ExecStart=/usr/lib/coreos/kubelet-wrapper \
|
||||||
|
--kubeconfig=/etc/kubernetes/kubeconfig \
|
||||||
|
--require-kubeconfig \
|
||||||
|
--client-ca-file=/etc/kubernetes/ca.crt \
|
||||||
|
--anonymous-auth=false \
|
||||||
|
--cni-conf-dir=/etc/kubernetes/cni/net.d \
|
||||||
|
--network-plugin=cni \
|
||||||
|
--lock-file=/var/run/lock/kubelet.lock \
|
||||||
|
--exit-on-lock-contention \
|
||||||
|
--pod-manifest-path=/etc/kubernetes/manifests \
|
||||||
|
--allow-privileged \
|
||||||
|
--hostname-override={{.domain_name}} \
|
||||||
|
--node-labels=node-role.kubernetes.io/master \
|
||||||
|
--register-with-taints=node-role.kubernetes.io/master=:NoSchedule \
|
||||||
|
--cluster_dns={{.k8s_dns_service_ip}} \
|
||||||
|
--cluster_domain=cluster.local
|
||||||
|
ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- name: bootkube.service
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Bootstrap a Kubernetes control plane with a temp api-server
|
||||||
|
ConditionPathExists=!/opt/bootkube/init_bootkube.done
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
WorkingDirectory=/opt/bootkube
|
||||||
|
ExecStart=/opt/bootkube/bootkube-start
|
||||||
|
ExecStartPost=/bin/touch /opt/bootkube/init_bootkube.done
|
||||||
|
storage:
|
||||||
|
{{ if index . "pxe" }}
|
||||||
|
disks:
|
||||||
|
- device: /dev/sda
|
||||||
|
wipe_table: true
|
||||||
|
partitions:
|
||||||
|
- label: ROOT
|
||||||
|
filesystems:
|
||||||
|
- name: root
|
||||||
|
mount:
|
||||||
|
device: "/dev/sda1"
|
||||||
|
format: "ext4"
|
||||||
|
create:
|
||||||
|
force: true
|
||||||
|
options:
|
||||||
|
- "-LROOT"
|
||||||
|
{{end}}
|
||||||
|
files:
|
||||||
|
- path: /etc/kubernetes/kubelet.env
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
|
- path: /etc/hostname
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline:
|
||||||
|
{{.domain_name}}
|
||||||
|
- path: /etc/sysctl.d/max-user-watches.conf
|
||||||
|
filesystem: root
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
fs.inotify.max_user_watches=16184
|
||||||
|
- path: /opt/bootkube/bootkube-start
|
||||||
|
filesystem: root
|
||||||
|
mode: 0544
|
||||||
|
user:
|
||||||
|
id: 500
|
||||||
|
group:
|
||||||
|
id: 500
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
#!/bin/bash
|
||||||
|
# Wrapper for bootkube start
|
||||||
|
set -e
|
||||||
|
# Move experimental manifests
|
||||||
|
[ -d /opt/bootkube/assets/manifests-* ] && mv /opt/bootkube/assets/manifests-*/* /opt/bootkube/assets/manifests && rm -rf /opt/bootkube/assets/manifests-*
|
||||||
|
[ -d /opt/bootkube/assets/experimental/manifests ] && mv /opt/bootkube/assets/experimental/manifests/* /opt/bootkube/assets/manifests && rm -r /opt/bootkube/assets/experimental/manifests
|
||||||
|
[ -d /opt/bootkube/assets/experimental/bootstrap-manifests ] && mv /opt/bootkube/assets/experimental/bootstrap-manifests/* /opt/bootkube/assets/bootstrap-manifests && rm -r /opt/bootkube/assets/experimental/bootstrap-manifests
|
||||||
|
BOOTKUBE_ACI="${BOOTKUBE_ACI:-quay.io/coreos/bootkube}"
|
||||||
|
BOOTKUBE_VERSION="${BOOTKUBE_VERSION:-v0.6.1}"
|
||||||
|
BOOTKUBE_ASSETS="${BOOTKUBE_ASSETS:-/opt/bootkube/assets}"
|
||||||
|
exec /usr/bin/rkt run \
|
||||||
|
--trust-keys-from-https \
|
||||||
|
--volume assets,kind=host,source=$BOOTKUBE_ASSETS \
|
||||||
|
--mount volume=assets,target=/assets \
|
||||||
|
--volume bootstrap,kind=host,source=/etc/kubernetes \
|
||||||
|
--mount volume=bootstrap,target=/etc/kubernetes \
|
||||||
|
$RKT_OPTS \
|
||||||
|
${BOOTKUBE_ACI}:${BOOTKUBE_VERSION} \
|
||||||
|
--net=host \
|
||||||
|
--dns=host \
|
||||||
|
--exec=/bootkube -- start --asset-dir=/assets "$@"
|
||||||
|
passwd:
|
||||||
|
users:
|
||||||
|
- name: core
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- {{.ssh_authorized_key}}
|
||||||
|
|
112
bare-metal/container-linux/kubernetes/cl/worker.yaml.tmpl
Normal file
112
bare-metal/container-linux/kubernetes/cl/worker.yaml.tmpl
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
systemd:
|
||||||
|
units:
|
||||||
|
- name: docker.service
|
||||||
|
enable: true
|
||||||
|
- name: locksmithd.service
|
||||||
|
mask: true
|
||||||
|
- name: kubelet.path
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Watch for kubeconfig
|
||||||
|
[Path]
|
||||||
|
PathExists=/etc/kubernetes/kubeconfig
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- name: wait-for-dns.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Wait for DNS entries
|
||||||
|
Wants=systemd-resolved.service
|
||||||
|
Before=kubelet.service
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/bin/sh -c 'while ! /usr/bin/grep '^[^#[:space:]]' /etc/resolv.conf > /dev/null; do sleep 1; done'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=kubelet.service
|
||||||
|
- name: kubelet.service
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Kubelet via Hyperkube ACI
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=/etc/kubernetes/kubelet.env
|
||||||
|
Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
|
||||||
|
--volume=resolv,kind=host,source=/etc/resolv.conf \
|
||||||
|
--mount volume=resolv,target=/etc/resolv.conf \
|
||||||
|
--volume var-lib-cni,kind=host,source=/var/lib/cni \
|
||||||
|
--mount volume=var-lib-cni,target=/var/lib/cni \
|
||||||
|
--volume var-log,kind=host,source=/var/log \
|
||||||
|
--mount volume=var-log,target=/var/log"
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/cni/net.d
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/checkpoint-secrets
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/inactive-manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /var/lib/cni
|
||||||
|
ExecStartPre=/usr/bin/bash -c "grep 'certificate-authority-data' /etc/kubernetes/kubeconfig | awk '{print $2}' | base64 -d > /etc/kubernetes/ca.crt"
|
||||||
|
ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
ExecStart=/usr/lib/coreos/kubelet-wrapper \
|
||||||
|
--kubeconfig=/etc/kubernetes/kubeconfig \
|
||||||
|
--require-kubeconfig \
|
||||||
|
--client-ca-file=/etc/kubernetes/ca.crt \
|
||||||
|
--anonymous-auth=false \
|
||||||
|
--cni-conf-dir=/etc/kubernetes/cni/net.d \
|
||||||
|
--network-plugin=cni \
|
||||||
|
--lock-file=/var/run/lock/kubelet.lock \
|
||||||
|
--exit-on-lock-contention \
|
||||||
|
--pod-manifest-path=/etc/kubernetes/manifests \
|
||||||
|
--allow-privileged \
|
||||||
|
--hostname-override={{.domain_name}} \
|
||||||
|
--node-labels=node-role.kubernetes.io/node \
|
||||||
|
--cluster_dns={{.k8s_dns_service_ip}} \
|
||||||
|
--cluster_domain=cluster.local
|
||||||
|
ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
storage:
|
||||||
|
{{ if index . "pxe" }}
|
||||||
|
disks:
|
||||||
|
- device: /dev/sda
|
||||||
|
wipe_table: true
|
||||||
|
partitions:
|
||||||
|
- label: ROOT
|
||||||
|
filesystems:
|
||||||
|
- name: root
|
||||||
|
mount:
|
||||||
|
device: "/dev/sda1"
|
||||||
|
format: "ext4"
|
||||||
|
create:
|
||||||
|
force: true
|
||||||
|
options:
|
||||||
|
- "-LROOT"
|
||||||
|
{{end}}
|
||||||
|
files:
|
||||||
|
- path: /etc/kubernetes/kubelet.env
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
|
- path: /etc/hostname
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline:
|
||||||
|
{{.domain_name}}
|
||||||
|
- path: /etc/sysctl.d/max-user-watches.conf
|
||||||
|
filesystem: root
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
fs.inotify.max_user_watches=16184
|
||||||
|
passwd:
|
||||||
|
users:
|
||||||
|
- name: core
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- {{.ssh_authorized_key}}
|
||||||
|
|
53
bare-metal/container-linux/kubernetes/groups.tf
Normal file
53
bare-metal/container-linux/kubernetes/groups.tf
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Install Container Linux to disk
|
||||||
|
resource "matchbox_group" "container-linux-install" {
|
||||||
|
count = "${length(var.controller_names) + length(var.worker_names)}"
|
||||||
|
|
||||||
|
name = "${format("container-linux-install-%s", element(concat(var.controller_names, var.worker_names), count.index))}"
|
||||||
|
profile = "${var.cached_install == "true" ? matchbox_profile.cached-container-linux-install.name : matchbox_profile.container-linux-install.name}"
|
||||||
|
|
||||||
|
selector {
|
||||||
|
mac = "${element(concat(var.controller_macs, var.worker_macs), count.index)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
ssh_authorized_key = "${var.ssh_authorized_key}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "matchbox_group" "controller" {
|
||||||
|
count = "${length(var.controller_names)}"
|
||||||
|
name = "${format("%s-%s", var.cluster_name, element(var.controller_names, count.index))}"
|
||||||
|
profile = "${matchbox_profile.controller.name}"
|
||||||
|
|
||||||
|
selector {
|
||||||
|
mac = "${element(var.controller_macs, count.index)}"
|
||||||
|
os = "installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
domain_name = "${element(var.controller_domains, count.index)}"
|
||||||
|
etcd_name = "${element(var.controller_names, count.index)}"
|
||||||
|
etcd_initial_cluster = "${join(",", formatlist("%s=https://%s:2380", var.controller_names, var.controller_domains))}"
|
||||||
|
etcd_on_host = "${var.experimental_self_hosted_etcd ? "false" : "true"}"
|
||||||
|
k8s_dns_service_ip = "${module.bootkube.kube_dns_service_ip}"
|
||||||
|
ssh_authorized_key = "${var.ssh_authorized_key}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "matchbox_group" "worker" {
|
||||||
|
count = "${length(var.worker_names)}"
|
||||||
|
name = "${format("%s-%s", var.cluster_name, element(var.worker_names, count.index))}"
|
||||||
|
profile = "${matchbox_profile.worker.name}"
|
||||||
|
|
||||||
|
selector {
|
||||||
|
mac = "${element(var.worker_macs, count.index)}"
|
||||||
|
os = "installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
domain_name = "${element(var.worker_domains, count.index)}"
|
||||||
|
etcd_on_host = "${var.experimental_self_hosted_etcd ? "false" : "true"}"
|
||||||
|
k8s_dns_service_ip = "${module.bootkube.kube_dns_service_ip}"
|
||||||
|
ssh_authorized_key = "${var.ssh_authorized_key}"
|
||||||
|
}
|
||||||
|
}
|
3
bare-metal/container-linux/kubernetes/outputs.tf
Normal file
3
bare-metal/container-linux/kubernetes/outputs.tf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
output "kubeconfig" {
|
||||||
|
value = "${module.bootkube.kubeconfig}"
|
||||||
|
}
|
80
bare-metal/container-linux/kubernetes/profiles.tf
Normal file
80
bare-metal/container-linux/kubernetes/profiles.tf
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Container Linux Install profile (from release.core-os.net)
|
||||||
|
resource "matchbox_profile" "container-linux-install" {
|
||||||
|
name = "container-linux-install"
|
||||||
|
kernel = "http://${var.container_linux_channel}.release.core-os.net/amd64-usr/${var.container_linux_version}/coreos_production_pxe.vmlinuz"
|
||||||
|
|
||||||
|
initrd = [
|
||||||
|
"http://${var.container_linux_channel}.release.core-os.net/amd64-usr/${var.container_linux_version}/coreos_production_pxe_image.cpio.gz",
|
||||||
|
]
|
||||||
|
|
||||||
|
args = [
|
||||||
|
"coreos.config.url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||||
|
"coreos.first_boot=yes",
|
||||||
|
"console=tty0",
|
||||||
|
"console=ttyS0",
|
||||||
|
]
|
||||||
|
|
||||||
|
container_linux_config = "${data.template_file.container-linux-install-config.rendered}"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "template_file" "container-linux-install-config" {
|
||||||
|
template = "${file("${path.module}/cl/container-linux-install.yaml.tmpl")}"
|
||||||
|
|
||||||
|
vars {
|
||||||
|
container_linux_channel = "${var.container_linux_channel}"
|
||||||
|
container_linux_version = "${var.container_linux_version}"
|
||||||
|
ignition_endpoint = "${format("%s/ignition", var.matchbox_http_endpoint)}"
|
||||||
|
install_disk = "${var.install_disk}"
|
||||||
|
container_linux_oem = "${var.container_linux_oem}"
|
||||||
|
|
||||||
|
# only cached-container-linux profile adds -b baseurl
|
||||||
|
baseurl_flag = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container Linux Install profile (from matchbox /assets cache)
|
||||||
|
// Note: Admin must have downloaded container_linux_version into matchbox assets.
|
||||||
|
resource "matchbox_profile" "cached-container-linux-install" {
|
||||||
|
name = "cached-container-linux-install"
|
||||||
|
kernel = "/assets/coreos/${var.container_linux_version}/coreos_production_pxe.vmlinuz"
|
||||||
|
|
||||||
|
initrd = [
|
||||||
|
"/assets/coreos/${var.container_linux_version}/coreos_production_pxe_image.cpio.gz",
|
||||||
|
]
|
||||||
|
|
||||||
|
args = [
|
||||||
|
"coreos.config.url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||||
|
"coreos.first_boot=yes",
|
||||||
|
"console=tty0",
|
||||||
|
"console=ttyS0",
|
||||||
|
]
|
||||||
|
|
||||||
|
container_linux_config = "${data.template_file.cached-container-linux-install-config.rendered}"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "template_file" "cached-container-linux-install-config" {
|
||||||
|
template = "${file("${path.module}/cl/container-linux-install.yaml.tmpl")}"
|
||||||
|
|
||||||
|
vars {
|
||||||
|
container_linux_channel = "${var.container_linux_channel}"
|
||||||
|
container_linux_version = "${var.container_linux_version}"
|
||||||
|
ignition_endpoint = "${format("%s/ignition", var.matchbox_http_endpoint)}"
|
||||||
|
install_disk = "${var.install_disk}"
|
||||||
|
container_linux_oem = "${var.container_linux_oem}"
|
||||||
|
|
||||||
|
# profile uses -b baseurl to install from matchbox cache
|
||||||
|
baseurl_flag = "-b ${var.matchbox_http_endpoint}/assets/coreos"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kubernetes Controller profile
|
||||||
|
resource "matchbox_profile" "controller" {
|
||||||
|
name = "controller"
|
||||||
|
container_linux_config = "${file("${path.module}/cl/controller.yaml.tmpl")}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kubernetes Worker profile
|
||||||
|
resource "matchbox_profile" "worker" {
|
||||||
|
name = "worker"
|
||||||
|
container_linux_config = "${file("${path.module}/cl/worker.yaml.tmpl")}"
|
||||||
|
}
|
96
bare-metal/container-linux/kubernetes/ssh.tf
Normal file
96
bare-metal/container-linux/kubernetes/ssh.tf
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Secure copy etcd TLS assets and kubeconfig to all nodes. Activates kubelet.service
|
||||||
|
resource "null_resource" "copy-secrets" {
|
||||||
|
count = "${length(var.controller_names) + length(var.worker_names)}"
|
||||||
|
|
||||||
|
connection {
|
||||||
|
type = "ssh"
|
||||||
|
host = "${element(concat(var.controller_domains, var.worker_domains), count.index)}"
|
||||||
|
user = "core"
|
||||||
|
timeout = "60m"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.kubeconfig}"
|
||||||
|
destination = "$HOME/kubeconfig"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_ca_cert}"
|
||||||
|
destination = "$HOME/etcd-client-ca.crt"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_client_cert}"
|
||||||
|
destination = "$HOME/etcd-client.crt"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_client_key}"
|
||||||
|
destination = "$HOME/etcd-client.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_server_cert}"
|
||||||
|
destination = "$HOME/etcd-server.crt"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_server_key}"
|
||||||
|
destination = "$HOME/etcd-server.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_peer_cert}"
|
||||||
|
destination = "$HOME/etcd-peer.crt"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
content = "${module.bootkube.etcd_peer_key}"
|
||||||
|
destination = "$HOME/etcd-peer.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "remote-exec" {
|
||||||
|
inline = [
|
||||||
|
"sudo mkdir -p /etc/ssl/etcd/etcd",
|
||||||
|
"sudo mv etcd-client* /etc/ssl/etcd/",
|
||||||
|
"sudo cp /etc/ssl/etcd/etcd-client-ca.crt /etc/ssl/etcd/etcd/server-ca.crt",
|
||||||
|
"sudo mv etcd-server.crt /etc/ssl/etcd/etcd/server.crt",
|
||||||
|
"sudo mv etcd-server.key /etc/ssl/etcd/etcd/server.key",
|
||||||
|
"sudo cp /etc/ssl/etcd/etcd-client-ca.crt /etc/ssl/etcd/etcd/peer-ca.crt",
|
||||||
|
"sudo mv etcd-peer.crt /etc/ssl/etcd/etcd/peer.crt",
|
||||||
|
"sudo mv etcd-peer.key /etc/ssl/etcd/etcd/peer.key",
|
||||||
|
"sudo chown -R etcd:etcd /etc/ssl/etcd",
|
||||||
|
"sudo chmod -R 500 /etc/ssl/etcd",
|
||||||
|
"sudo mv /home/core/kubeconfig /etc/kubernetes/kubeconfig",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Secure copy bootkube assets to ONE controller and start bootkube to perform
|
||||||
|
# one-time self-hosted cluster bootstrapping.
|
||||||
|
resource "null_resource" "bootkube-start" {
|
||||||
|
# Without depends_on, this remote-exec may start before the kubeconfig copy.
|
||||||
|
# Terraform only does one task at a time, so it would try to bootstrap
|
||||||
|
# Kubernetes and Tectonic while no Kubelets are running. Ensure all nodes
|
||||||
|
# receive a kubeconfig before proceeding with bootkube.
|
||||||
|
depends_on = ["null_resource.copy-secrets"]
|
||||||
|
|
||||||
|
connection {
|
||||||
|
type = "ssh"
|
||||||
|
host = "${element(var.controller_domains, 0)}"
|
||||||
|
user = "core"
|
||||||
|
timeout = "60m"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
source = "${var.asset_dir}"
|
||||||
|
destination = "$HOME/assets"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "remote-exec" {
|
||||||
|
inline = [
|
||||||
|
"sudo mv /home/core/assets /opt/bootkube",
|
||||||
|
"sudo systemctl start bootkube",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
116
bare-metal/container-linux/kubernetes/variables.tf
Normal file
116
bare-metal/container-linux/kubernetes/variables.tf
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
variable "matchbox_http_endpoint" {
|
||||||
|
type = "string"
|
||||||
|
description = "Matchbox HTTP read-only endpoint (e.g. http://matchbox.example.com:8080)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "container_linux_channel" {
|
||||||
|
type = "string"
|
||||||
|
description = "Container Linux channel corresponding to the container_linux_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "container_linux_version" {
|
||||||
|
type = "string"
|
||||||
|
description = "Container Linux version of the kernel/initrd to PXE or the image to install"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_name" {
|
||||||
|
type = "string"
|
||||||
|
description = "Cluster name"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_authorized_key" {
|
||||||
|
type = "string"
|
||||||
|
description = "SSH public key to set as an authorized_key on machines"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Machines
|
||||||
|
# Terraform's crude "type system" does properly support lists of maps so we do this.
|
||||||
|
|
||||||
|
variable "controller_names" {
|
||||||
|
type = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "controller_macs" {
|
||||||
|
type = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "controller_domains" {
|
||||||
|
type = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_names" {
|
||||||
|
type = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_macs" {
|
||||||
|
type = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_domains" {
|
||||||
|
type = "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
# bootkube assets
|
||||||
|
|
||||||
|
variable "k8s_domain_name" {
|
||||||
|
description = "Controller DNS name which resolves to a controller instance. Workers and kubeconfig's will communicate with this endpoint (e.g. cluster.example.com)"
|
||||||
|
type = "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "asset_dir" {
|
||||||
|
description = "Path to a directory where generated assets should be placed (contains secrets)"
|
||||||
|
type = "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "networking" {
|
||||||
|
description = "Choice of networking provider (flannel or calico)"
|
||||||
|
type = "string"
|
||||||
|
default = "flannel"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "network_mtu" {
|
||||||
|
description = "CNI interface MTU (applies to calico only)"
|
||||||
|
type = "string"
|
||||||
|
default = "1480"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pod_cidr" {
|
||||||
|
description = "CIDR IP range to assign Kubernetes pods"
|
||||||
|
type = "string"
|
||||||
|
default = "10.2.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "service_cidr" {
|
||||||
|
description = <<EOD
|
||||||
|
CIDR IP range to assign Kubernetes services.
|
||||||
|
The 1st IP will be reserved for kube_apiserver, the 10th IP will be reserved for kube-dns, the 15th IP will be reserved for self-hosted etcd, and the 200th IP will be reserved for bootstrap self-hosted etcd.
|
||||||
|
EOD
|
||||||
|
|
||||||
|
type = "string"
|
||||||
|
default = "10.3.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
# optional
|
||||||
|
|
||||||
|
variable "cached_install" {
|
||||||
|
type = "string"
|
||||||
|
default = "false"
|
||||||
|
description = "Whether Container Linux should PXE boot and install from matchbox /assets cache. Note that the admin must have downloaded the container_linux_version into matchbox assets."
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "install_disk" {
|
||||||
|
type = "string"
|
||||||
|
default = "/dev/sda"
|
||||||
|
description = "Disk device to which the install profiles should install Container Linux (e.g. /dev/sda)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "container_linux_oem" {
|
||||||
|
type = "string"
|
||||||
|
default = ""
|
||||||
|
description = "Specify an OEM image id to use as base for the installation (e.g. ami, vmware_raw, xen) or leave blank for the default image"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "experimental_self_hosted_etcd" {
|
||||||
|
default = "false"
|
||||||
|
description = "Create self-hosted etcd cluster as pods on Kubernetes, instead of on-hosts"
|
||||||
|
}
|
@ -92,7 +92,7 @@ storage:
|
|||||||
contents:
|
contents:
|
||||||
inline: |
|
inline: |
|
||||||
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
KUBELET_IMAGE_TAG=v1.6.7_coreos.0
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
- path: /etc/hostname
|
- path: /etc/hostname
|
||||||
filesystem: root
|
filesystem: root
|
||||||
mode: 0644
|
mode: 0644
|
@ -1,11 +1,13 @@
|
|||||||
# Self-hosted Kubernetes assets (kubeconfig, manifests)
|
# Self-hosted Kubernetes assets (kubeconfig, manifests)
|
||||||
module "bootkube" {
|
module "bootkube" {
|
||||||
source = "git::https://github.com/dghubble/bootkube-terraform.git?ref=v0.5.1"
|
source = "git::https://github.com/poseidon/bootkube-terraform.git?ref=5ffbfec46dc05721eaf9d15c3c9bbedefaead1bc"
|
||||||
|
|
||||||
cluster_name = "${var.cluster_name}"
|
cluster_name = "${var.cluster_name}"
|
||||||
api_servers = ["${var.k8s_domain_name}"]
|
api_servers = ["${format("%s.%s", var.cluster_name, var.dns_zone)}"]
|
||||||
etcd_servers = ["http://127.0.0.1:2379"]
|
etcd_servers = ["http://127.0.0.1:2379"]
|
||||||
asset_dir = "${var.asset_dir}"
|
asset_dir = "${var.asset_dir}"
|
||||||
|
networking = "${var.networking}"
|
||||||
|
network_mtu = 1440
|
||||||
pod_cidr = "${var.pod_cidr}"
|
pod_cidr = "${var.pod_cidr}"
|
||||||
service_cidr = "${var.service_cidr}"
|
service_cidr = "${var.service_cidr}"
|
||||||
experimental_self_hosted_etcd = "true"
|
experimental_self_hosted_etcd = "true"
|
143
digital-ocean/container-linux/kubernetes/cl/controller.yaml.tmpl
Normal file
143
digital-ocean/container-linux/kubernetes/cl/controller.yaml.tmpl
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
systemd:
|
||||||
|
units:
|
||||||
|
- name: docker.service
|
||||||
|
enable: true
|
||||||
|
- name: locksmithd.service
|
||||||
|
mask: true
|
||||||
|
- name: wait-for-dns.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Wait for DNS entries
|
||||||
|
Wants=systemd-resolved.service
|
||||||
|
Before=kubelet.service
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/bin/sh -c 'while ! /usr/bin/grep '^[^#[:space:]]' /etc/resolv.conf > /dev/null; do sleep 1; done'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=kubelet.service
|
||||||
|
- name: kubelet.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Kubelet via Hyperkube ACI
|
||||||
|
Requires=coreos-metadata.service
|
||||||
|
After=coreos-metadata.service
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=/etc/kubernetes/kubelet.env
|
||||||
|
EnvironmentFile=/run/metadata/coreos
|
||||||
|
Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
|
||||||
|
--volume=resolv,kind=host,source=/etc/resolv.conf \
|
||||||
|
--mount volume=resolv,target=/etc/resolv.conf \
|
||||||
|
--volume var-lib-cni,kind=host,source=/var/lib/cni \
|
||||||
|
--mount volume=var-lib-cni,target=/var/lib/cni \
|
||||||
|
--volume var-log,kind=host,source=/var/log \
|
||||||
|
--mount volume=var-log,target=/var/log"
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/cni/net.d
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/checkpoint-secrets
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/inactive-manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /var/lib/cni
|
||||||
|
ExecStartPre=/usr/bin/bash -c "grep 'certificate-authority-data' /etc/kubernetes/kubeconfig | awk '{print $2}' | base64 -d > /etc/kubernetes/ca.crt"
|
||||||
|
ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
ExecStart=/usr/lib/coreos/kubelet-wrapper \
|
||||||
|
--kubeconfig=/etc/kubernetes/kubeconfig \
|
||||||
|
--require-kubeconfig \
|
||||||
|
--client-ca-file=/etc/kubernetes/ca.crt \
|
||||||
|
--anonymous-auth=false \
|
||||||
|
--cni-conf-dir=/etc/kubernetes/cni/net.d \
|
||||||
|
--network-plugin=cni \
|
||||||
|
--lock-file=/var/run/lock/kubelet.lock \
|
||||||
|
--exit-on-lock-contention \
|
||||||
|
--hostname-override=$${COREOS_DIGITALOCEAN_IPV4_PRIVATE_0} \
|
||||||
|
--pod-manifest-path=/etc/kubernetes/manifests \
|
||||||
|
--allow-privileged \
|
||||||
|
--node-labels=node-role.kubernetes.io/master \
|
||||||
|
--register-with-taints=node-role.kubernetes.io/master=:NoSchedule \
|
||||||
|
--cluster_dns=${k8s_dns_service_ip} \
|
||||||
|
--cluster_domain=cluster.local
|
||||||
|
ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- name: bootkube.service
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Bootstrap a Kubernetes cluster
|
||||||
|
ConditionPathExists=!/opt/bootkube/init_bootkube.done
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
WorkingDirectory=/opt/bootkube
|
||||||
|
ExecStart=/opt/bootkube/bootkube-start
|
||||||
|
ExecStartPost=/bin/touch /opt/bootkube/init_bootkube.done
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
storage:
|
||||||
|
files:
|
||||||
|
- path: /etc/kubernetes/kubeconfig
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Config
|
||||||
|
clusters:
|
||||||
|
- name: local
|
||||||
|
cluster:
|
||||||
|
server: ${kubeconfig_server}
|
||||||
|
certificate-authority-data: ${kubeconfig_ca_cert}
|
||||||
|
users:
|
||||||
|
- name: kubelet
|
||||||
|
user:
|
||||||
|
client-certificate-data: ${kubeconfig_kubelet_cert}
|
||||||
|
client-key-data: ${kubeconfig_kubelet_key}
|
||||||
|
contexts:
|
||||||
|
- context:
|
||||||
|
cluster: local
|
||||||
|
user: kubelet
|
||||||
|
- path: /etc/kubernetes/kubelet.env
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
|
- path: /etc/sysctl.d/max-user-watches.conf
|
||||||
|
filesystem: root
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
fs.inotify.max_user_watches=16184
|
||||||
|
- path: /opt/bootkube/bootkube-start
|
||||||
|
filesystem: root
|
||||||
|
mode: 0544
|
||||||
|
user:
|
||||||
|
id: 500
|
||||||
|
group:
|
||||||
|
id: 500
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
#!/bin/bash
|
||||||
|
# Wrapper for bootkube start
|
||||||
|
set -e
|
||||||
|
# Move experimental manifests
|
||||||
|
[ -d /opt/bootkube/assets/manifests-* ] && mv /opt/bootkube/assets/manifests-*/* /opt/bootkube/assets/manifests && rm -rf /opt/bootkube/assets/manifests-*
|
||||||
|
[ -d /opt/bootkube/assets/experimental/manifests ] && mv /opt/bootkube/assets/experimental/manifests/* /opt/bootkube/assets/manifests && rm -r /opt/bootkube/assets/experimental/manifests
|
||||||
|
[ -d /opt/bootkube/assets/experimental/bootstrap-manifests ] && mv /opt/bootkube/assets/experimental/bootstrap-manifests/* /opt/bootkube/assets/bootstrap-manifests && rm -r /opt/bootkube/assets/experimental/bootstrap-manifests
|
||||||
|
BOOTKUBE_ACI="$${BOOTKUBE_ACI:-quay.io/coreos/bootkube}"
|
||||||
|
BOOTKUBE_VERSION="$${BOOTKUBE_VERSION:-v0.6.1}"
|
||||||
|
BOOTKUBE_ASSETS="$${BOOTKUBE_ASSETS:-/opt/bootkube/assets}"
|
||||||
|
exec /usr/bin/rkt run \
|
||||||
|
--trust-keys-from-https \
|
||||||
|
--volume assets,kind=host,source=$${BOOTKUBE_ASSETS} \
|
||||||
|
--mount volume=assets,target=/assets \
|
||||||
|
--volume bootstrap,kind=host,source=/etc/kubernetes \
|
||||||
|
--mount volume=bootstrap,target=/etc/kubernetes \
|
||||||
|
$${RKT_OPTS} \
|
||||||
|
$${BOOTKUBE_ACI}:$${BOOTKUBE_VERSION} \
|
||||||
|
--net=host \
|
||||||
|
--dns=host \
|
||||||
|
--exec=/bootkube -- start --asset-dir=/assets "$@"
|
126
digital-ocean/container-linux/kubernetes/cl/worker.yaml.tmpl
Normal file
126
digital-ocean/container-linux/kubernetes/cl/worker.yaml.tmpl
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
systemd:
|
||||||
|
units:
|
||||||
|
- name: docker.service
|
||||||
|
enable: true
|
||||||
|
- name: locksmithd.service
|
||||||
|
mask: true
|
||||||
|
- name: wait-for-dns.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Wait for DNS entries
|
||||||
|
Wants=systemd-resolved.service
|
||||||
|
Before=kubelet.service
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/bin/sh -c 'while ! /usr/bin/grep '^[^#[:space:]]' /etc/resolv.conf > /dev/null; do sleep 1; done'
|
||||||
|
[Install]
|
||||||
|
RequiredBy=kubelet.service
|
||||||
|
- name: kubelet.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Kubelet via Hyperkube ACI
|
||||||
|
Requires=coreos-metadata.service
|
||||||
|
After=coreos-metadata.service
|
||||||
|
[Service]
|
||||||
|
EnvironmentFile=/etc/kubernetes/kubelet.env
|
||||||
|
EnvironmentFile=/run/metadata/coreos
|
||||||
|
Environment="RKT_RUN_ARGS=--uuid-file-save=/var/run/kubelet-pod.uuid \
|
||||||
|
--volume=resolv,kind=host,source=/etc/resolv.conf \
|
||||||
|
--mount volume=resolv,target=/etc/resolv.conf \
|
||||||
|
--volume var-lib-cni,kind=host,source=/var/lib/cni \
|
||||||
|
--mount volume=var-lib-cni,target=/var/lib/cni \
|
||||||
|
--volume var-log,kind=host,source=/var/log \
|
||||||
|
--mount volume=var-log,target=/var/log"
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/cni/net.d
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/checkpoint-secrets
|
||||||
|
ExecStartPre=/bin/mkdir -p /etc/kubernetes/inactive-manifests
|
||||||
|
ExecStartPre=/bin/mkdir -p /var/lib/cni
|
||||||
|
ExecStartPre=/usr/bin/bash -c "grep 'certificate-authority-data' /etc/kubernetes/kubeconfig | awk '{print $2}' | base64 -d > /etc/kubernetes/ca.crt"
|
||||||
|
ExecStartPre=-/usr/bin/rkt rm --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
ExecStart=/usr/lib/coreos/kubelet-wrapper \
|
||||||
|
--kubeconfig=/etc/kubernetes/kubeconfig \
|
||||||
|
--require-kubeconfig \
|
||||||
|
--client-ca-file=/etc/kubernetes/ca.crt \
|
||||||
|
--anonymous-auth=false \
|
||||||
|
--cni-conf-dir=/etc/kubernetes/cni/net.d \
|
||||||
|
--network-plugin=cni \
|
||||||
|
--lock-file=/var/run/lock/kubelet.lock \
|
||||||
|
--exit-on-lock-contention \
|
||||||
|
--hostname-override=$${COREOS_DIGITALOCEAN_IPV4_PRIVATE_0} \
|
||||||
|
--pod-manifest-path=/etc/kubernetes/manifests \
|
||||||
|
--allow-privileged \
|
||||||
|
--node-labels=node-role.kubernetes.io/node \
|
||||||
|
--cluster_dns=${k8s_dns_service_ip} \
|
||||||
|
--cluster_domain=cluster.local
|
||||||
|
ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- name: delete-node.service
|
||||||
|
enable: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Waiting to delete Kubernetes node on shutdown
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/bin/true
|
||||||
|
ExecStop=/etc/kubernetes/delete-node
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
storage:
|
||||||
|
files:
|
||||||
|
- path: /etc/kubernetes/kubeconfig
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Config
|
||||||
|
clusters:
|
||||||
|
- name: local
|
||||||
|
cluster:
|
||||||
|
server: ${kubeconfig_server}
|
||||||
|
certificate-authority-data: ${kubeconfig_ca_cert}
|
||||||
|
users:
|
||||||
|
- name: kubelet
|
||||||
|
user:
|
||||||
|
client-certificate-data: ${kubeconfig_kubelet_cert}
|
||||||
|
client-key-data: ${kubeconfig_kubelet_key}
|
||||||
|
contexts:
|
||||||
|
- context:
|
||||||
|
cluster: local
|
||||||
|
user: kubelet
|
||||||
|
- path: /etc/kubernetes/kubelet.env
|
||||||
|
filesystem: root
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
|
- path: /etc/sysctl.d/max-user-watches.conf
|
||||||
|
filesystem: root
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
fs.inotify.max_user_watches=16184
|
||||||
|
- path: /etc/kubernetes/delete-node
|
||||||
|
filesystem: root
|
||||||
|
mode: 0744
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
exec /usr/bin/rkt run \
|
||||||
|
--trust-keys-from-https \
|
||||||
|
--volume config,kind=host,source=/etc/kubernetes \
|
||||||
|
--mount volume=config,target=/etc/kubernetes \
|
||||||
|
quay.io/coreos/hyperkube:v1.7.3_coreos.0 \
|
||||||
|
--net=host \
|
||||||
|
--dns=host \
|
||||||
|
--exec=/kubectl -- --kubeconfig=/etc/kubernetes/kubeconfig delete node $(hostname)
|
61
digital-ocean/container-linux/kubernetes/controllers.tf
Normal file
61
digital-ocean/container-linux/kubernetes/controllers.tf
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Controller Instance DNS records
|
||||||
|
resource "digitalocean_record" "controllers" {
|
||||||
|
count = "${var.controller_count}"
|
||||||
|
|
||||||
|
# DNS zone where record should be created
|
||||||
|
domain = "${var.dns_zone}"
|
||||||
|
|
||||||
|
# DNS record (will be prepended to domain)
|
||||||
|
name = "${var.cluster_name}"
|
||||||
|
type = "A"
|
||||||
|
ttl = 300
|
||||||
|
|
||||||
|
# IPv4 addresses of controllers
|
||||||
|
value = "${element(digitalocean_droplet.controllers.*.ipv4_address, count.index)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Controller droplet instances
|
||||||
|
resource "digitalocean_droplet" "controllers" {
|
||||||
|
count = "${var.controller_count}"
|
||||||
|
|
||||||
|
name = "${var.cluster_name}-controller-${count.index}"
|
||||||
|
region = "${var.region}"
|
||||||
|
|
||||||
|
image = "${var.image}"
|
||||||
|
size = "${var.controller_type}"
|
||||||
|
|
||||||
|
# network
|
||||||
|
ipv6 = true
|
||||||
|
private_networking = true
|
||||||
|
|
||||||
|
user_data = "${data.ct_config.controller_ign.rendered}"
|
||||||
|
ssh_keys = "${var.ssh_fingerprints}"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"${digitalocean_tag.controllers.id}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tag to label controllers
|
||||||
|
resource "digitalocean_tag" "controllers" {
|
||||||
|
name = "${var.cluster_name}-controller"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Controller Container Linux Config
|
||||||
|
data "template_file" "controller_config" {
|
||||||
|
template = "${file("${path.module}/cl/controller.yaml.tmpl")}"
|
||||||
|
|
||||||
|
vars = {
|
||||||
|
k8s_dns_service_ip = "${cidrhost(var.service_cidr, 10)}"
|
||||||
|
k8s_etcd_service_ip = "${cidrhost(var.service_cidr, 15)}"
|
||||||
|
kubeconfig_ca_cert = "${module.bootkube.ca_cert}"
|
||||||
|
kubeconfig_kubelet_cert = "${module.bootkube.kubelet_cert}"
|
||||||
|
kubeconfig_kubelet_key = "${module.bootkube.kubelet_key}"
|
||||||
|
kubeconfig_server = "${module.bootkube.server}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "ct_config" "controller_ign" {
|
||||||
|
content = "${data.template_file.controller_config.rendered}"
|
||||||
|
pretty_print = false
|
||||||
|
}
|
52
digital-ocean/container-linux/kubernetes/network.tf
Normal file
52
digital-ocean/container-linux/kubernetes/network.tf
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
resource "digitalocean_firewall" "rules" {
|
||||||
|
name = "${var.cluster_name}"
|
||||||
|
|
||||||
|
tags = ["${var.cluster_name}-controller", "${var.cluster_name}-worker"]
|
||||||
|
|
||||||
|
# allow ssh, http/https ingress, and peer-to-peer traffic
|
||||||
|
inbound_rule = [
|
||||||
|
{
|
||||||
|
protocol = "tcp"
|
||||||
|
port_range = "22"
|
||||||
|
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol = "tcp"
|
||||||
|
port_range = "80"
|
||||||
|
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol = "tcp"
|
||||||
|
port_range = "443"
|
||||||
|
source_addresses = ["0.0.0.0/0", "::/0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol = "udp"
|
||||||
|
port_range = "all"
|
||||||
|
source_tags = ["${digitalocean_tag.controllers.name}", "${digitalocean_tag.workers.name}"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol = "tcp"
|
||||||
|
port_range = "all"
|
||||||
|
source_tags = ["${digitalocean_tag.controllers.name}", "${digitalocean_tag.workers.name}"]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# allow all outbound traffic
|
||||||
|
outbound_rule = [
|
||||||
|
{
|
||||||
|
protocol = "icmp"
|
||||||
|
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol = "udp"
|
||||||
|
port_range = "all"
|
||||||
|
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol = "tcp"
|
||||||
|
port_range = "all"
|
||||||
|
destination_addresses = ["0.0.0.0/0", "::/0"]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
23
digital-ocean/container-linux/kubernetes/outputs.tf
Normal file
23
digital-ocean/container-linux/kubernetes/outputs.tf
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
output "controllers_dns" {
|
||||||
|
value = "${digitalocean_record.controllers.0.fqdn}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "workers_dns" {
|
||||||
|
value = "${digitalocean_record.workers.0.fqdn}"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "controllers_ipv4" {
|
||||||
|
value = ["${digitalocean_droplet.controllers.*.ipv4_address}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "controllers_ipv6" {
|
||||||
|
value = ["${digitalocean_droplet.controllers.*.ipv6_address}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "workers_ipv4" {
|
||||||
|
value = ["${digitalocean_droplet.workers.*.ipv4_address}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "workers_ipv6" {
|
||||||
|
value = ["${digitalocean_droplet.workers.*.ipv6_address}"]
|
||||||
|
}
|
24
digital-ocean/container-linux/kubernetes/ssh.tf
Normal file
24
digital-ocean/container-linux/kubernetes/ssh.tf
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Secure copy bootkube assets to ONE controller and start bootkube to perform
|
||||||
|
# one-time self-hosted cluster bootstrapping.
|
||||||
|
resource "null_resource" "bootkube-start" {
|
||||||
|
depends_on = ["module.bootkube", "digitalocean_droplet.controllers"]
|
||||||
|
|
||||||
|
connection {
|
||||||
|
type = "ssh"
|
||||||
|
host = "${digitalocean_droplet.controllers.0.ipv4_address}"
|
||||||
|
user = "core"
|
||||||
|
timeout = "15m"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "file" {
|
||||||
|
source = "${var.asset_dir}"
|
||||||
|
destination = "$HOME/assets"
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "remote-exec" {
|
||||||
|
inline = [
|
||||||
|
"sudo mv /home/core/assets /opt/bootkube",
|
||||||
|
"sudo systemctl start bootkube",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
78
digital-ocean/container-linux/kubernetes/variables.tf
Normal file
78
digital-ocean/container-linux/kubernetes/variables.tf
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
variable "cluster_name" {
|
||||||
|
type = "string"
|
||||||
|
description = "Unique cluster name"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "region" {
|
||||||
|
type = "string"
|
||||||
|
description = "Digital Ocean region (e.g. nyc1, sfo2, fra1, tor1)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "dns_zone" {
|
||||||
|
type = "string"
|
||||||
|
description = "Digital Ocean domain (i.e. DNS zone) (e.g. do.example.com)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "image" {
|
||||||
|
type = "string"
|
||||||
|
default = "coreos-stable"
|
||||||
|
description = "OS image from which to initialize the disk (e.g. coreos-stable)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "controller_count" {
|
||||||
|
type = "string"
|
||||||
|
default = "1"
|
||||||
|
description = "Number of controllers"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "controller_type" {
|
||||||
|
type = "string"
|
||||||
|
default = "2gb"
|
||||||
|
description = "Digital Ocean droplet size (e.g. 2gb (min), 4gb, 8gb)."
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_count" {
|
||||||
|
type = "string"
|
||||||
|
default = "1"
|
||||||
|
description = "Number of workers"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_type" {
|
||||||
|
type = "string"
|
||||||
|
default = "512mb"
|
||||||
|
description = "Digital Ocean droplet size (e.g. 512mb, 1gb, 2gb, 4gb)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_fingerprints" {
|
||||||
|
type = "list"
|
||||||
|
description = "SSH public key fingerprints. (e.g. see `ssh-add -l -E md5`)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# bootkube assets
|
||||||
|
|
||||||
|
variable "asset_dir" {
|
||||||
|
description = "Path to a directory where generated assets should be placed (contains secrets)"
|
||||||
|
type = "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "networking" {
|
||||||
|
description = "Choice of networking provider (flannel or calico)"
|
||||||
|
type = "string"
|
||||||
|
default = "flannel"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "pod_cidr" {
|
||||||
|
description = "CIDR IP range to assign Kubernetes pods"
|
||||||
|
type = "string"
|
||||||
|
default = "10.2.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "service_cidr" {
|
||||||
|
description = <<EOD
|
||||||
|
CIDR IP range to assign Kubernetes services.
|
||||||
|
The 1st IP will be reserved for kube_apiserver, the 10th IP will be reserved for kube-dns, the 15th IP will be reserved for self-hosted etcd, and the 200th IP will be reserved for bootstrap self-hosted etcd.
|
||||||
|
EOD
|
||||||
|
|
||||||
|
type = "string"
|
||||||
|
default = "10.3.0.0/16"
|
||||||
|
}
|
58
digital-ocean/container-linux/kubernetes/workers.tf
Normal file
58
digital-ocean/container-linux/kubernetes/workers.tf
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Worker DNS records
|
||||||
|
resource "digitalocean_record" "workers" {
|
||||||
|
count = "${var.worker_count}"
|
||||||
|
|
||||||
|
# DNS zone where record should be created
|
||||||
|
domain = "${var.dns_zone}"
|
||||||
|
|
||||||
|
name = "${var.cluster_name}-workers"
|
||||||
|
type = "A"
|
||||||
|
ttl = 300
|
||||||
|
value = "${element(digitalocean_droplet.workers.*.ipv4_address, count.index)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Worker droplet instances
|
||||||
|
resource "digitalocean_droplet" "workers" {
|
||||||
|
count = "${var.worker_count}"
|
||||||
|
|
||||||
|
name = "${var.cluster_name}-worker-${count.index}"
|
||||||
|
region = "${var.region}"
|
||||||
|
|
||||||
|
image = "${var.image}"
|
||||||
|
size = "${var.worker_type}"
|
||||||
|
|
||||||
|
# network
|
||||||
|
ipv6 = true
|
||||||
|
private_networking = true
|
||||||
|
|
||||||
|
user_data = "${data.ct_config.worker_ign.rendered}"
|
||||||
|
ssh_keys = "${var.ssh_fingerprints}"
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
"${digitalocean_tag.workers.id}",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tag to label workers
|
||||||
|
resource "digitalocean_tag" "workers" {
|
||||||
|
name = "${var.cluster_name}-worker"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Worker Container Linux Config
|
||||||
|
data "template_file" "worker_config" {
|
||||||
|
template = "${file("${path.module}/cl/worker.yaml.tmpl")}"
|
||||||
|
|
||||||
|
vars = {
|
||||||
|
k8s_dns_service_ip = "${cidrhost(var.service_cidr, 10)}"
|
||||||
|
k8s_etcd_service_ip = "${cidrhost(var.service_cidr, 15)}"
|
||||||
|
kubeconfig_ca_cert = "${module.bootkube.ca_cert}"
|
||||||
|
kubeconfig_kubelet_cert = "${module.bootkube.kubelet_cert}"
|
||||||
|
kubeconfig_kubelet_key = "${module.bootkube.kubelet_key}"
|
||||||
|
kubeconfig_server = "${module.bootkube.server}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "ct_config" "worker_ign" {
|
||||||
|
content = "${data.template_file.worker_config.rendered}"
|
||||||
|
pretty_print = false
|
||||||
|
}
|
4
docs/addons/calico.md
Normal file
4
docs/addons/calico.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Calico Policy
|
||||||
|
|
||||||
|
!!! bug "In Progress"
|
||||||
|
These docs haven't been moved over yet.
|
29
docs/addons/cluo.md
Normal file
29
docs/addons/cluo.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Container Linux Update Operator
|
||||||
|
|
||||||
|
The [Container Linux Update Operator](https://github.com/coreos/container-linux-update-operator) (i.e. CLUO) coordinates reboots of auto-updating Container Linux nodes so that one node reboots at a time and nodes are drained before reboot. CLUO enables the auto-update behavior Container Linux clusters are known for, but does so in a Kubernetes native way.
|
||||||
|
|
||||||
|
## Create
|
||||||
|
|
||||||
|
Create the `update-operator` deployment and `update-agent` DaemonSet.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl apply -f addons/cluo -R
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
`update-agent` runs as a DaemonSet and annotates a node when `update-engine.service` indiates an update has been installed and a reboot is needed. It also adds additional labels and annotations to nodes.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get nodes --show-labels
|
||||||
|
...
|
||||||
|
container-linux-update.v1.coreos.com/group=stable
|
||||||
|
container-linux-update.v1.coreos.com/version=1465.6.0
|
||||||
|
```
|
||||||
|
|
||||||
|
`update-operator` ensures one node reboots at a time and that pods are drained prior to reboot.
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
CLUO replaces `locksmithd` reboot coordination. The `update_engine` systemd unit on hosts still performs the Container Linux update check, download, and install to the inactive partition.
|
||||||
|
|
||||||
|
|
24
docs/addons/dashboard.md
Normal file
24
docs/addons/dashboard.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Kubernetes Dashboard
|
||||||
|
|
||||||
|
The Kubernetes [Dashboard](https://github.com/kubernetes/dashboard) provides a web UI to manage a Kubernetes cluster for those who prefer an alternative to `kubectl`.
|
||||||
|
|
||||||
|
## Create
|
||||||
|
|
||||||
|
Create the dashboard deployment and service.
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl apply -f addons/dashboard -R
|
||||||
|
```
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
Use `kubectl` to authenticate to the apiserver and create a local port forward to the remote port on the dashboard pod.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl get pods -n kube-system
|
||||||
|
kubectl port-forward POD [LOCAL_PORT:]REMOTE_PORT
|
||||||
|
kubectl port-forward kubernetes-dashboard-id 9090 -n kube-system
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
If you'd like to expose the Dashboard via Ingress and add authentication, use a suitable OAuth2 proxy sidecar and pick your favorite OAuth2 provider.
|
19
docs/addons/heapster.md
Normal file
19
docs/addons/heapster.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Heapster
|
||||||
|
|
||||||
|
[Heapster](https://kubernetes.io/docs/user-guide/monitoring/) collects data from apiservers and kubelets and exposes it through a REST API. This API powers the `kubectl top` command and Kubernetes dashbard graphs.
|
||||||
|
|
||||||
|
## Create
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl apply -f addons/heapster -R
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Allow heapster to run for a few minutes, then check CPU and memory usage.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl top node
|
||||||
|
kubectl top pod
|
||||||
|
```
|
||||||
|
|
4
docs/addons/ingress.md
Normal file
4
docs/addons/ingress.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ingress Controller
|
||||||
|
|
||||||
|
!!! bug "In Progress"
|
||||||
|
These docs haven't been moved over yet.
|
12
docs/addons/overview.md
Normal file
12
docs/addons/overview.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Addons
|
||||||
|
|
||||||
|
Every Typhoon cluster is verified to work well with several post-install addons.
|
||||||
|
|
||||||
|
* Nginx [Ingress Controller](ingress.md)
|
||||||
|
* Calico [Network Policy](calico.md)
|
||||||
|
* [Heapster](heapster.md)
|
||||||
|
* Kubernetes [Dashboard](dashboard.md)
|
||||||
|
* [CLUO](cluo.md) (Container Linux only)
|
||||||
|
* Prometheus
|
||||||
|
* Grafana
|
||||||
|
|
6
docs/advanced/customization.md
Normal file
6
docs/advanced/customization.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Customization
|
||||||
|
|
||||||
|
To customize clusters in ways that aren't supported by input variables, fork the repo and make changes to the Terraform module. Stay tuned for improvements to this strategy since it is beneficial to stay close to this upstream.
|
||||||
|
|
||||||
|
To customize lower-level Kubernetes control plane bootstrapping, see the [poseidon/bootkube-terraform](https://github.com/poseidon/bootkube-terraform) Terraform module.
|
||||||
|
|
364
docs/bare-metal.md
Normal file
364
docs/bare-metal.md
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
# Bare-Metal
|
||||||
|
|
||||||
|
In this tutorial, we'll network boot and provison a Kubernetes v1.7.3 cluster on bare-metal.
|
||||||
|
|
||||||
|
First, we'll deploy a [Matchbox](https://github.com/coreos/matchbox) service and setup a network boot environment. Then, we'll declare a Kubernetes cluster in Terraform using the Typhoon Terraform module and power on machines. On PXE boot, machines will install Container Linux to disk, reboot into the disk install, and provision themselves as Kubernetes controllers or workers.
|
||||||
|
|
||||||
|
Controllers are provisioned as etcd peers and run `etcd-member` (etcd3) and `kubelet`. Workers are provisioned to run a `kubelet`. A one-time [bootkube](https://github.com/kubernetes-incubator/bootkube) bootstrap schedules an `apiserver`, `scheduler`, `controller-manager`, and `kube-dns` on controllers and runs `kube-proxy` and `flannel` on each node. A generated `kubeconfig` provides `kubectl` access to the cluster.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Machines with 2GB RAM, 30GB disk, PXE-enabled NIC, IPMI
|
||||||
|
* PXE-enabled [network boot](https://coreos.com/matchbox/docs/latest/network-setup.html) environment
|
||||||
|
* Matchbox v0.6+ deployment with API enabled
|
||||||
|
* Matchbox credentials `client.crt`, `client.key`, `ca.crt`
|
||||||
|
* Terraform v0.9.2+ and [terraform-provider-matchbox](https://github.com/coreos/terraform-provider-matchbox) installed locally
|
||||||
|
|
||||||
|
## Machines
|
||||||
|
|
||||||
|
Collect a MAC address from each machine. For machines with multiple PXE-enabled NICs, pick one of the MAC addresses. MAC addresses will be used to match machines to profiles during network boot.
|
||||||
|
|
||||||
|
* 52:54:00:a1:9c:ae (node1)
|
||||||
|
* 52:54:00:b2:2f:86 (node2)
|
||||||
|
* 52:54:00:c3:61:77 (node3)
|
||||||
|
|
||||||
|
Configure each machine to boot from the disk [^1] through IPMI or the BIOS menu.
|
||||||
|
|
||||||
|
|
||||||
|
[^1]: Configuring "diskless" workers that always PXE boot is possible, but not in the scope of this tutorial.
|
||||||
|
|
||||||
|
```
|
||||||
|
ipmitool -H node1 -U USER -P PASS chassis bootdev disk options=persistent
|
||||||
|
```
|
||||||
|
|
||||||
|
During provisioning, you'll explicitly set the boot device to `pxe` for the next boot only. Machines will install (overwrite) the operting system to disk on PXE boot and reboot into the disk install.
|
||||||
|
|
||||||
|
!!! tip ""
|
||||||
|
Ask your hardware vendor to provide MACs and preconfigure IPMI, if possible. With it, you can rack new servers, `terraform apply` with new info, and power on machines that network boot and provision into clusters.
|
||||||
|
|
||||||
|
## DNS
|
||||||
|
|
||||||
|
Create a DNS A (or AAAA) record for each node's default interface. Create a record that resolves to each controller node (or re-use the node record if there's one controller).
|
||||||
|
|
||||||
|
* node1.example.com (node1)
|
||||||
|
* node2.example.com (node2)
|
||||||
|
* node3.example.com (node3)
|
||||||
|
* myk8s.example.com (node1)
|
||||||
|
|
||||||
|
Cluster nodes will be configured to refer to the control plane and themselves by these fully qualified names and they'll be used in generated TLS certificates.
|
||||||
|
|
||||||
|
## Matchbox
|
||||||
|
|
||||||
|
Matchbox is an open-source app that matches network-booted bare-metal machines (based on labels like MAC, UUID, etc.) to profiles to automate cluster provisioning.
|
||||||
|
|
||||||
|
Install Matchbox on a Kubernetes cluster or dedicated server.
|
||||||
|
|
||||||
|
* Installing on [Kubernetes](https://coreos.com/matchbox/docs/latest/deployment.html#kubernetes) (recommended)
|
||||||
|
* Installing on a [server](https://coreos.com/matchbox/docs/latest/deployment.html#download)
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
Deploy Matchbox as service that can be accessed by all of your bare-metal machines globally. This provides a single endpoint to use Terraform to manage bare-metal clusters at different sites. Typhoon will never include secrets in provisioning user-data so you may even deploy matchbox publicly.
|
||||||
|
|
||||||
|
Matchbox provides a TLS client-authenticated API that clients, like Terraform, can use to manage machine matching and profiles. Think of it like a cloud provider API, but for creating bare-metal instances.
|
||||||
|
|
||||||
|
[Generate TLS](https://coreos.com/matchbox/docs/latest/deployment.html#generate-tls-certificates) client credentials. Save the `ca.crt`, `client.crt`, and `client.key` where they can be referenced in Terraform configs.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mv ca.crt client.crt client.key ~/.config/matchbox/
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the matchbox read-only HTTP endpoints are accessible (port is configurable).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ curl http://matchbox.example.com:8080
|
||||||
|
matchbox
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify your TLS client certificate and key can be used to access the Matchbox API (port is configurable).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ openssl s_client -connect matchbox.example.com:8081 \
|
||||||
|
-CAfile ~/.config/matchbox/ca.crt \
|
||||||
|
-cert ~/.config/matchbox/client.crt \
|
||||||
|
-key ~/.config/matchbox/client.key
|
||||||
|
```
|
||||||
|
|
||||||
|
## PXE Environment
|
||||||
|
|
||||||
|
Create a iPXE-enabled network boot environment. Configure PXE clients to chainload [iPXE](http://ipxe.org/cmd) and instruct iPXE clients to chainload from your Matchbox service's `/boot.ipxe` endpoint.
|
||||||
|
|
||||||
|
For networks already supporting iPXE clients, you can add a `default.ipxe` config.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# /var/www/html/ipxe/default.ipxe
|
||||||
|
chain http://matchbox.foo:8080/boot.ipxe
|
||||||
|
```
|
||||||
|
|
||||||
|
For networks with Ubiquiti Routers, you can [configure the router](TODO) itself to chainload machines to iPXE and Matchbox.
|
||||||
|
|
||||||
|
For a small lab, you may wish to checkout the [quay.io/coreos/dnsmasq](https://quay.io/repository/coreos/dnsmasq) container image and [copy-paste examples](https://github.com/coreos/matchbox/blob/master/Documentation/network-setup.md#coreosdnsmasq).
|
||||||
|
|
||||||
|
Read about the [many ways](https://coreos.com/matchbox/docs/latest/network-setup.html) to setup a compliant iPXE-enabled network. There is quite a bit of flexibility:
|
||||||
|
|
||||||
|
* Continue using existing DHCP, TFTP, or DNS services
|
||||||
|
* Configure specific machines, subnets, or architectures to chainload from Matchbox
|
||||||
|
* Place Matchbox behind a menu entry (timeout and default to Matchbox)
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
TFTP chainloding to modern boot firmware, like iPXE, avoids issues with old NICs and allows faster transfer protocols like HTTP to be used.
|
||||||
|
|
||||||
|
## Terraform Setup
|
||||||
|
|
||||||
|
Install [Terraform](https://www.terraform.io/downloads.html) v0.9.2+ on your system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform version
|
||||||
|
Terraform v0.10.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the [terraform-provider-matchbox](https://github.com/coreos/terraform-provider-matchbox) plugin binary for your system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
wget https://github.com/coreos/terraform-provider-matchbox/releases/download/v0.2.2/terraform-provider-matchbox-v0.2.2-linux-amd64.tar.gz
|
||||||
|
tar xzf terraform-provider-matchbox-v0.2.2-linux-amd64.tar.gz
|
||||||
|
sudo mv terraform-provider-matchbox-v0.2.2-linux-amd64/terraform-provider-matchbox /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the plugin to your `~/.terraformrc`.
|
||||||
|
|
||||||
|
```
|
||||||
|
providers {
|
||||||
|
ct = "/usr/local/bin/terraform-provider-matchbox"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Read [concepts](concepts.md) to learn about Terraform, modules, and organizing resources. Change to your infrastructure repository (e.g. `infra`).
|
||||||
|
|
||||||
|
```
|
||||||
|
cd infra/clusters
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provider
|
||||||
|
|
||||||
|
Configure the Matchbox provider to use your Matchbox API endpoint and client certificate.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
provider "matchbox" {
|
||||||
|
endpoint = "matchbox.example.com:8081"
|
||||||
|
client_cert = "${file("~/.config/matchbox/client.crt")}"
|
||||||
|
client_key = "${file("~/.config/matchbox/client.key")}"
|
||||||
|
ca = "${file("~/.config/matchbox/ca.crt")}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cluster
|
||||||
|
|
||||||
|
Define a Kubernetes cluster using the module `bare-metal/container-linux/kubernetes`.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
module "bare-metal-mercury" {
|
||||||
|
source = "git::https://github.com/poseidon/typhoon//bare-metal/container-linux/kubernetes"
|
||||||
|
|
||||||
|
# install
|
||||||
|
matchbox_http_endpoint = "http://matchbox.example.com"
|
||||||
|
container_linux_channel = "stable"
|
||||||
|
container_linux_version = "1465.6.0"
|
||||||
|
ssh_authorized_key = "ssh-rsa AAAAB3Nz..."
|
||||||
|
|
||||||
|
# cluster
|
||||||
|
cluster_name = "mercury"
|
||||||
|
k8s_domain_name = "node1.example.com"
|
||||||
|
|
||||||
|
# machines
|
||||||
|
controller_names = ["node1"]
|
||||||
|
controller_macs = ["52:54:00:a1:9c:ae"]
|
||||||
|
controller_domains = ["node1.example.com"]
|
||||||
|
worker_names = [
|
||||||
|
"node2",
|
||||||
|
"node3",
|
||||||
|
]
|
||||||
|
worker_macs = [
|
||||||
|
"52:54:00:b2:2f:86",
|
||||||
|
"52:54:00:c3:61:77",
|
||||||
|
]
|
||||||
|
worker_domains = [
|
||||||
|
"node2.example.com",
|
||||||
|
"node3.example.com",
|
||||||
|
]
|
||||||
|
|
||||||
|
# output assets dir
|
||||||
|
asset_dir = "/home/user/.secrets/clusters/mercury"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference the [variables docs](#variables) or the [variables.tf](https://github.com/poseidon/typhoon/blob/master/bare-metal/container-linux/kubernetes/variables.tf) source.
|
||||||
|
|
||||||
|
## ssh-agent
|
||||||
|
|
||||||
|
Initial bootstrapping requires `bootkube.service` be started on one controller node. Terraform uses `ssh-agent` to automate this step. Add your SSH private key to `ssh-agent`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh-add ~/.ssh/id_rsa
|
||||||
|
ssh-add -L
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
`terrafrom apply` will hang connecting to a controller if `ssh-agent` does not contain the SSH key.
|
||||||
|
|
||||||
|
## Apply
|
||||||
|
|
||||||
|
Initialize the config directory if this is the first use with Terraform.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
terraform init
|
||||||
|
```
|
||||||
|
|
||||||
|
Get or update Terraform modules.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform get # downloads missing modules
|
||||||
|
$ terraform get --update # updates all modules
|
||||||
|
Get: git::https://github.com/poseidon/typhoon (update)
|
||||||
|
Get: git::https://github.com/poseidon/bootkube-terraform.git?ref=v0.6.1 (update)
|
||||||
|
```
|
||||||
|
|
||||||
|
Plan the resources to be created.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform plan
|
||||||
|
Plan: 55 to add, 0 to change, 0 to destroy.
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply the changes. Terraform will generate bootkube assets to `asset_dir` and create Matchbox profiles (e.g. controller, worker) and matching rules via the Matchbox API.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
module.bare-metal-mercury.null_resource.copy-secrets.0: Provisioning with 'file'...
|
||||||
|
module.bare-metal-mercury.null_resource.copy-secrets.2: Provisioning with 'file'...
|
||||||
|
module.bare-metal-mercury.null_resource.copy-secrets.1: Provisioning with 'file'...
|
||||||
|
module.bare-metal-mercury.null_resource.copy-secrets.0: Still creating... (10s elapsed)
|
||||||
|
module.bare-metal-mercury.null_resource.copy-secrets.2: Still creating... (10s elapsed)
|
||||||
|
module.bare-metal-mercury.null_resource.copy-secrets.1: Still creating... (10s elapsed)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply will then loop until it can successfully copy credentials to each machine and start the one-time Kubernetes bootstrap service. Proceed to the next step while this loops.
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
You may see `terraform apply` fail to `copy-secrets` if it connects before the disk install has completed. Run terraform apply until it reconciles successfully.
|
||||||
|
|
||||||
|
### Power
|
||||||
|
|
||||||
|
Power on each machine with the boot device set to `pxe` for the next boot only.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ipmitool -H node1.example.com -U USER -P PASS chassis bootdev pxe
|
||||||
|
ipmitool -H node1.example.com -U USER -P PASS power on
|
||||||
|
```
|
||||||
|
|
||||||
|
Machines will network boot, install Container Linux to disk, reboot into the disk install, and provision themselves as controllers or workers.
|
||||||
|
|
||||||
|
!!! tip ""
|
||||||
|
If this is the first test of your PXE-enabled network boot environment, watch the SOL console of a machine to spot any misconfigurations.
|
||||||
|
|
||||||
|
### Bootstrap
|
||||||
|
|
||||||
|
Wait for the `bootkube-start` step to finish bootstrapping the Kubernetes control plane. This may take 5-15 minutes depending on your network.
|
||||||
|
|
||||||
|
```
|
||||||
|
module.bare-metal-mercury.null_resource.bootkube-start: Still creating... (6m10s elapsed)
|
||||||
|
module.bare-metal-mercury.null_resource.bootkube-start: Still creating... (6m20s elapsed)
|
||||||
|
module.bare-metal-mercury.null_resource.bootkube-start: Still creating... (6m30s elapsed)
|
||||||
|
module.bare-metal-mercury.null_resource.bootkube-start: Still creating... (6m40s elapsed)
|
||||||
|
module.bare-metal-mercury.null_resource.bootkube-start: Creation complete (ID: 5441741360626669024)
|
||||||
|
|
||||||
|
Apply complete! Resources: 55 added, 0 changed, 0 destroyed.
|
||||||
|
```
|
||||||
|
|
||||||
|
To watch the bootstrap process in detail, SSH to the first controller and journal the logs.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ssh node1.example.com
|
||||||
|
$ journalctl -f -u bootkube
|
||||||
|
bootkube[5]: Pod Status: pod-checkpointer Running
|
||||||
|
bootkube[5]: Pod Status: kube-apiserver Running
|
||||||
|
bootkube[5]: Pod Status: kube-scheduler Running
|
||||||
|
bootkube[5]: Pod Status: kube-controller-manager Running
|
||||||
|
bootkube[5]: All self-hosted control plane components successfully started
|
||||||
|
bootkube[5]: Tearing down temporary bootstrap control plane...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
[Install kubectl](https://coreos.com/kubernetes/docs/latest/configure-kubectl.html) on your system. Use the generated `kubeconfig` credentials to access the Kubernetes cluster and list nodes.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ KUBECONFIG=/home/user/.secrets/clusters/mercury/auth/kubeconfig
|
||||||
|
$ kubectl get nodes
|
||||||
|
NAME STATUS AGE VERSION
|
||||||
|
node1.example.com Ready 11m v1.7.3+coreos.0
|
||||||
|
node2.example.com Ready 11m v1.7.3+coreos.0
|
||||||
|
node3.example.com Ready 11m v1.7.3+coreos.0
|
||||||
|
```
|
||||||
|
|
||||||
|
List the pods.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get pods --all-namespaces
|
||||||
|
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||||
|
kube-system kube-apiserver-7336w 1/1 Running 0 11m
|
||||||
|
kube-system kube-controller-manager-3271970485-b9chx 1/1 Running 0 11m
|
||||||
|
kube-system kube-controller-manager-3271970485-v30js 1/1 Running 1 11m
|
||||||
|
kube-system kube-dns-1187388186-mx9rt 3/3 Running 0 11m
|
||||||
|
kube-system kube-etcd-network-checkpointer-q24f7 1/1 Running 0 11m
|
||||||
|
kube-system kube-flannel-6qp7f 2/2 Running 1 11m
|
||||||
|
kube-system kube-flannel-gnjrm 2/2 Running 0 11m
|
||||||
|
kube-system kube-flannel-llbgt 2/2 Running 0 11m
|
||||||
|
kube-system kube-proxy-50sd4 1/1 Running 0 11m
|
||||||
|
kube-system kube-proxy-bczhp 1/1 Running 0 11m
|
||||||
|
kube-system kube-proxy-mp2fw 1/1 Running 0 11m
|
||||||
|
kube-system kube-scheduler-3895335239-fd3l7 1/1 Running 1 11m
|
||||||
|
kube-system kube-scheduler-3895335239-hfjv0 1/1 Running 0 11m
|
||||||
|
kube-system pod-checkpointer-wf65d 1/1 Running 0 11m
|
||||||
|
kube-system pod-checkpointer-wf65d-node1.example.com 1/1 Running 0 11m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Going Further
|
||||||
|
|
||||||
|
Learn about [version pinning](concepts.md#versioning), maintenance, and [addons](addons/overview.md).
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
On Container Linux clusters, install the `container-linux-update-operator` addon to coordinate reboots and drains when nodes auto-update. Otherwise, updates may not be applied until the next reboot.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
| Name | Description | Example |
|
||||||
|
|:-----|:------------|:--------|
|
||||||
|
| matchbox_http_endpoint | Matchbox HTTP read-only endpoint | http://matchbox.example.com:8080 |
|
||||||
|
| container_linux_channel | Container Linux channel | stable, beta, alpha |
|
||||||
|
| container_linux_version | Container Linux version of the kernel/initrd to PXE and the image to install | 1465.6.0 |
|
||||||
|
| cluster_name | Cluster name | mercury |
|
||||||
|
| k8s_domain_name | FQDN resolving to the controller(s) nodes. Workers and kubectl will communicate with this endpoint | "myk8s.example.com" |
|
||||||
|
| ssh_authorized_key | SSH public key for ~/.ssh/authorized_keys | "ssh-rsa AAAAB3Nz..." |
|
||||||
|
| controller_names | Ordered list of controller short names | ["node1"] |
|
||||||
|
| controller_macs | Ordered list of controller identifying MAC addresses | ["52:54:00:a1:9c:ae"] |
|
||||||
|
| controller_domains | Ordered list of controller FQDNs | ["node1.example.com"] |
|
||||||
|
| worker_names | Ordered list of worker short names | ["node2", "node3"] |
|
||||||
|
| worker_macs | Ordered list of worker identifying MAC addresses | ["52:54:00:b2:2f:86", "52:54:00:c3:61:77"] |
|
||||||
|
| worker_domains | Ordered list of worker FQDNs | ["node2.example.com", "node3.example.com"] |
|
||||||
|
| asset_dir | Path to a directory where generated assets should be placed (contains secrets) | "/home/user/.secrets/clusters/mercury" |
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
| Name | Description | Default | Example |
|
||||||
|
|:-----|:------------|:--------|:--------|
|
||||||
|
| cached_install | Whether machines should PXE boot from the Matchbox `/assets` cache. Admin MUST have downloaded Container Linux images into the cache to use this | false | true |
|
||||||
|
| install_disk | Disk device where Container Linux should be installed | "/dev/sda" | "/dev/sdb" |
|
||||||
|
| container_linux_oem | Specify alternative OEM image ids for the disk install | "" | "vmware_raw", "xen" |
|
||||||
|
| experimental_self_hosted_etcd | Self-host etcd as pods on Kubernetes (not recommended) | false | true |
|
||||||
|
| networking | Choice of networking provider | "flannel" | "flannel" or "calico" |
|
||||||
|
| network_mtu | CNI interface MTU (calico-only) | 1480 | - |
|
||||||
|
| pod_cidr | CIDR range to assign to Kubernetes pods | "10.2.0.0/16" | "10.22.0.0/16" |
|
||||||
|
| service_cidr | CIDR range to assgin to Kubernetes services | "10.3.0.0/16" | "10.3.0.0/24" |
|
||||||
|
|
114
docs/concepts.md
Normal file
114
docs/concepts.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# Concepts
|
||||||
|
|
||||||
|
Let's cover the concepts you'll need to get started.
|
||||||
|
|
||||||
|
## Kubernetes
|
||||||
|
|
||||||
|
[Kubernetes](https://kubernetes.io/) is an open-source cluster system for deploying, scaling, and managing containerized applications across a pool of compute nodes (bare-metal, droplets, instances).
|
||||||
|
|
||||||
|
#### Nodes
|
||||||
|
|
||||||
|
Cluster nodes provision themselves from a declarative configuration upfront. Nodes run a `kubelet` service and register themselves with the control plane to join the higher order cluster.
|
||||||
|
|
||||||
|
#### Controllers
|
||||||
|
|
||||||
|
Controller nodes are scheduled to run the Kubernetes `apiserver`, `scheduler`, `controller-manager`, `kube-dns`, and `kube-proxy`. A fully qualified domain name (e.g. cluster_name.domain.com) resolving to a network load balancer or round-robin DNS (depends on platform) is used to refer to the control plane.
|
||||||
|
|
||||||
|
#### Workers
|
||||||
|
|
||||||
|
Worker nodes register with the control plane and run application workloads. Workers, like all nodes, run `kube-proxy` and `flannel` pods.
|
||||||
|
|
||||||
|
## Terraform
|
||||||
|
|
||||||
|
Terraform config files declare *resources* that Terraform should manage. Resources include infrastructure components created through a *provider* API (e.g. Compute instances, DNS records) or local assets like TLS certificates and config files.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
# Declare an instance
|
||||||
|
resource "google_compute_instance" "pet" {
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `terraform` tool parses configs, reconciles the desired state with actual state, and updates resources to reach desired state.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform plan
|
||||||
|
Plan: 4 to add, 0 to change, 0 to destroy.
|
||||||
|
$ terraform apply
|
||||||
|
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
|
||||||
|
```
|
||||||
|
|
||||||
|
With Typhoon, you'll be able to manage clusters with Terraform.
|
||||||
|
|
||||||
|
### Modules
|
||||||
|
|
||||||
|
Terraform [modules](https://www.terraform.io/docs/modules/usage.html) allow a collection of resources to be configured and managed together. Typhoon provides a Kubernetes cluster Terraform *module* for each [supported](/#modules) platform and operating system.
|
||||||
|
|
||||||
|
Clusters are declared in Terraform by referencing the module.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
module "google-cloud-yavin" {
|
||||||
|
source = "git::https://github.com/poseidon/typhoon//google-cloud/container-linux/kubernetes"
|
||||||
|
cluster_name = "yavin"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
Modules are updated regularly, set the version to a [release tag](https://github.com/poseidon/typhoon/releases) or [commit](https://github.com/poseidon/typhoon/commits/master) hash.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
...
|
||||||
|
source = "git:https://github.com/poseidon/typhoon//google-cloud/container-linux/kubernetes?ref=v1.7.3"
|
||||||
|
```
|
||||||
|
|
||||||
|
Module versioning ensures `terraform get --update` only fetches the desired version, so plan and apply don't change cluster resources, unless the version is altered.
|
||||||
|
|
||||||
|
### Organize
|
||||||
|
|
||||||
|
Maintain Terraform configs for "live" infrastructure in a versioned repository. Seek to organize configs to reflect resources that should be managed together in a `terraform apply` invocation.
|
||||||
|
|
||||||
|
You may choose to organize resources all together, by team, by project, or some other scheme. Here's an example that manages three clusters together:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
.git/
|
||||||
|
infra/
|
||||||
|
└── terraform
|
||||||
|
└── clusters
|
||||||
|
├── bare-metal-tungsten.tf
|
||||||
|
├── google-cloud-yavin.tf
|
||||||
|
├── digital-ocean-nemo.tf
|
||||||
|
├── providers.tf
|
||||||
|
├── terraform.tfvars
|
||||||
|
└── remote-backend.tf
|
||||||
|
```
|
||||||
|
|
||||||
|
By convention, `providers.tf` registers provider APIs, `terraform.tfvars` stores shared values, and state is written to a remote backend.
|
||||||
|
|
||||||
|
### State
|
||||||
|
|
||||||
|
Terraform syncs its state with provider APIs to plan changes to reconcile to the desired state. By default, Terraform writes state data (including secrets!) to a `terraform.tfstate` file. **At a minimum**, add a `.gitignore` file (or equivalent) to prevent state from being committed to your infrastructure repository.
|
||||||
|
|
||||||
|
```
|
||||||
|
# .gitignore
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.backup
|
||||||
|
.terraform/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remote Backend
|
||||||
|
|
||||||
|
Later, you may wish to checkout Terraform [remote backends](https://www.terraform.io/intro/getting-started/remote.html) which store state in a remote bucket like Google Storage or S3.
|
||||||
|
|
||||||
|
```
|
||||||
|
terraform {
|
||||||
|
backend "gcs" {
|
||||||
|
credentials = "/path/to/credentials.json"
|
||||||
|
project = "project-id"
|
||||||
|
bucket = "bucket-id"
|
||||||
|
path = "metal.tfstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
249
docs/digital-ocean.md
Normal file
249
docs/digital-ocean.md
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
# Digital Ocean
|
||||||
|
|
||||||
|
In this tutorial, we'll create a Kubernetes v1.7.3 cluster on Digital Ocean.
|
||||||
|
|
||||||
|
We'll declare a Kubernetes cluster in Terraform using the Typhoon Terraform module. On apply, firewall rules, DNS records, tags, and droplets for Kubernetes controllers and workers will be created.
|
||||||
|
|
||||||
|
Controllers and workers are provisioned to run a `kubelet`. A one-time [bootkube](https://github.com/kubernetes-incubator/bootkube) bootstrap schedules `etcd`, `apiserver`, `scheduler`, `controller-manager`, and `kube-dns` on controllers and runs `kube-proxy` and `flannel` on each node. A generated `kubeconfig` provides `kubectl` access to the cluster.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Digital Ocean Account and Token
|
||||||
|
* Digital Ocean Domain (registered Domain Name or delegated subdomain)
|
||||||
|
* Terraform v0.10.1+ and [terraform-provider-ct](https://github.com/coreos/terraform-provider-ct) installed locally
|
||||||
|
|
||||||
|
## Terraform Setup
|
||||||
|
|
||||||
|
Install [Terraform](https://www.terraform.io/downloads.html) v0.10.1+ on your system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform version
|
||||||
|
Terraform v0.10.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the [terraform-provider-ct](https://github.com/coreos/terraform-provider-ct) plugin binary for your system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
wget https://github.com/coreos/terraform-provider-ct/releases/download/v0.2.0/terraform-provider-ct-v0.2.0-linux-amd64.tar.gz
|
||||||
|
tar xzf terraform-provider-ct-v0.2.0-linux-amd64.tar.gz
|
||||||
|
sudo mv terraform-provider-ct-v0.2.0-linux-amd64/terraform-provider-ct /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the plugin to your `~/.terraformrc`.
|
||||||
|
|
||||||
|
```
|
||||||
|
providers {
|
||||||
|
ct = "/usr/local/bin/terraform-provider-ct"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Read [concepts](concepts.md) to learn about Terraform, modules, and organizing resources. Change to your infrastructure repository (e.g. `infra`).
|
||||||
|
|
||||||
|
```
|
||||||
|
cd infra/clusters
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provider
|
||||||
|
|
||||||
|
Login to [DigitalOcean](https://cloud.digitalocean.com) or create an [account](https://cloud.digitalocean.com/registrations/new), if you don't have one.
|
||||||
|
|
||||||
|
Generate a Personal Access Token with read/write scope from the [API tab](https://cloud.digitalocean.com/settings/api/tokens). Write the token to a file that can be referenced in configs.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p ~/.config/digital-ocean
|
||||||
|
echo "TOKEN" > ~/.config/digital-ocean/token
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure the DigitalOcean provider to use your token in a `providers.tf` file.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
provider "digitalocean" {
|
||||||
|
token = "${chomp(file("~/.config/digital-ocean/token"))}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cluster
|
||||||
|
|
||||||
|
Define a Kubernetes cluster using the module `digital-ocean/container-linux/kubernetes`.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
module "digital-ocean-nemo" {
|
||||||
|
source = "git::https://github.com/poseidon/typhoon//digital-ocean/container-linux/kubernetes"
|
||||||
|
|
||||||
|
region = "nyc3"
|
||||||
|
dns_zone = "digital-ocean.example.com"
|
||||||
|
|
||||||
|
cluster_name = "nemo"
|
||||||
|
image = "coreos-stable"
|
||||||
|
controller_count = 1
|
||||||
|
controller_type = "2gb"
|
||||||
|
worker_count = 2
|
||||||
|
worker_type = "512mb"
|
||||||
|
ssh_fingerprints = ["d7:9d:79:ae:56:32:73:79:95:88:e3:a2:ab:5d:45:e7"]
|
||||||
|
|
||||||
|
# output assets dir
|
||||||
|
asset_dir = "/home/user/.secrets/clusters/nemo"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference the [variables docs](#variables) or the [variables.tf](https://github.com/poseidon/typhoon/blob/master/digital-ocean/container-linux/kubernetes/variables.tf) source.
|
||||||
|
|
||||||
|
## ssh-agent
|
||||||
|
|
||||||
|
Initial bootstrapping requires `bootkube.service` be started on one controller node. Terraform uses `ssh-agent` to automate this step. Add your SSH private key to `ssh-agent`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh-add ~/.ssh/id_rsa
|
||||||
|
ssh-add -L
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
`terrafrom apply` will hang connecting to a controller if `ssh-agent` does not contain the SSH key.
|
||||||
|
|
||||||
|
## Apply
|
||||||
|
|
||||||
|
Initialize the config directory if this is the first use with Terraform.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
terraform init
|
||||||
|
```
|
||||||
|
|
||||||
|
Get or update Terraform modules.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform get # downloads missing modules
|
||||||
|
$ terraform get --update # updates all modules
|
||||||
|
Get: git::https://github.com/poseidon/typhoon (update)
|
||||||
|
Get: git::https://github.com/poseidon/bootkube-terraform.git?ref=v0.6.1 (update)
|
||||||
|
```
|
||||||
|
|
||||||
|
Plan the resources to be created.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform plan
|
||||||
|
Plan: 54 to add, 0 to change, 0 to destroy.
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply the changes to create the cluster.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform apply
|
||||||
|
module.digital-ocean-nemo.null_resource.bootkube-start: Still creating... (30s elapsed)
|
||||||
|
module.digital-ocean-nemo.null_resource.bootkube-start: Provisioning with 'remote-exec'...
|
||||||
|
...
|
||||||
|
module.digital-ocean-nemo.null_resource.bootkube-start: Still creating... (6m20s elapsed)
|
||||||
|
module.digital-ocean-nemo.null_resource.bootkube-start: Creation complete (ID: 7599298447329218468)
|
||||||
|
|
||||||
|
Apply complete! Resources: 54 added, 0 changed, 0 destroyed.
|
||||||
|
```
|
||||||
|
|
||||||
|
In 5-10 minutes, the Kubernetes cluster will be ready.
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
[Install kubectl](https://coreos.com/kubernetes/docs/latest/configure-kubectl.html) on your system. Use the generated `kubeconfig` credentials to access the Kubernetes cluster and list nodes.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ KUBECONFIG=/home/user/.secrets/clusters/nemo/auth/kubeconfig
|
||||||
|
$ kubectl get nodes
|
||||||
|
NAME STATUS AGE VERSION
|
||||||
|
10.132.110.130 Ready 10m v1.7.3+coreos.0
|
||||||
|
10.132.115.81 Ready 10m v1.7.3+coreos.0
|
||||||
|
10.132.124.107 Ready 10m v1.7.3+coreos.0
|
||||||
|
```
|
||||||
|
|
||||||
|
List the pods.
|
||||||
|
|
||||||
|
```
|
||||||
|
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||||
|
kube-system etcd-operator-3329263108-sgsbl 1/1 Running 1 11m
|
||||||
|
kube-system kube-apiserver-n10qr 1/1 Running 0 11m
|
||||||
|
kube-system kube-controller-manager-3271970485-37gtw 1/1 Running 1 11m
|
||||||
|
kube-system kube-controller-manager-3271970485-p52t5 1/1 Running 0 11m
|
||||||
|
kube-system kube-dns-1187388186-ld1j7 3/3 Running 0 11m
|
||||||
|
kube-system kube-etcd-0000 1/1 Running 0 9m
|
||||||
|
kube-system kube-etcd-network-checkpointer-n9xsk 1/1 Running 0 11m
|
||||||
|
kube-system kube-flannel-1cq1v 2/2 Running 0 11m
|
||||||
|
kube-system kube-flannel-hq9t0 2/2 Running 1 11m
|
||||||
|
kube-system kube-flannel-v0g9w 2/2 Running 0 11m
|
||||||
|
kube-system kube-proxy-6kxjf 1/1 Running 0 11m
|
||||||
|
kube-system kube-proxy-fh3td 1/1 Running 0 11m
|
||||||
|
kube-system kube-proxy-k35rc 1/1 Running 0 11m
|
||||||
|
kube-system kube-scheduler-3895335239-2bc4c 1/1 Running 0 11m
|
||||||
|
kube-system kube-scheduler-3895335239-b7q47 1/1 Running 1 11m
|
||||||
|
kube-system pod-checkpointer-pr1lq 1/1 Running 0 11m
|
||||||
|
kube-system pod-checkpointer-pr1lq-10.132.115.81 1/1 Running 0 10m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Going Further
|
||||||
|
|
||||||
|
Learn about [version pinning](concepts.md#versioning), maintenance, and [addons](addons/overview.md).
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
On Container Linux clusters, install the `container-linux-update-operator` addon to coordinate reboots and drains when nodes auto-update. Otherwise, updates may not be applied until the next reboot.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
| Name | Description | Example |
|
||||||
|
|:-----|:------------|:--------|
|
||||||
|
| cluster_name | Unique cluster name (prepended to dns_zone) | nemo |
|
||||||
|
| region | Digital Ocean region | nyc1, sfo2, fra1, tor1 |
|
||||||
|
| dns_zone | Digital Ocean domain (i.e. DNS zone) | do.example.com |
|
||||||
|
| ssh_fingerprints | SSH public key fingerprints | ["d7:9d..."] |
|
||||||
|
| asset_dir | Path to a directory where generated assets should be placed (contains secrets) | /home/user/.secrets/nemo |
|
||||||
|
|
||||||
|
#### DNS Zone
|
||||||
|
|
||||||
|
Clusters create DNS A records `${cluster_name}.${dns_zone}` to resolve to controller droplets (round robin). This FQDN is used by workers and `kubectl` to access the apiserver. In this example, the cluster's apiserver would be accessible at `nemo.do.example.com`.
|
||||||
|
|
||||||
|
You'll need a registered domain name or subdomain registered in Digital Ocean Domains (i.e. DNS zones). You can set this up once and create many clusters with unqiue names.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
resource "digitalocean_domain" "zone-for-clusters" {
|
||||||
|
name = "do.example.com"
|
||||||
|
# Digital Ocean oddly requires an IP here. You may have to delete the A record it makes. :(
|
||||||
|
ip_address = "8.8.8.8"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip ""
|
||||||
|
If you have an existing domain name with a zone file elsewhere, just carve out a subdomain that can be managed on DigitalOcean (e.g. do.mydomain.com) and [update nameservers](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-host-name-with-digitalocean).
|
||||||
|
|
||||||
|
#### SSH Fingerprints
|
||||||
|
|
||||||
|
DigitalOcean droplets are created with your SSH public key "fingerprint" (i.e. MD5 hash) to allow access. If your SSH public key is at `~/.ssh/id_rsa`, find the fingerprint with,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -lf ~/.ssh/id_rsa.pub | awk '{print $2}'
|
||||||
|
d7:9d:79:ae:56:32:73:79:95:88:e3:a2:ab:5d:45:e7
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use `ssh-agent` (e.g. Yubikey for SSH), find the fingerprint with,
|
||||||
|
|
||||||
|
```
|
||||||
|
ssh-add -l -E md5
|
||||||
|
2048 MD5:d7:9d:79:ae:56:32:73:79:95:88:e3:a2:ab:5d:45:e7 cardno:000603633110 (RSA)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you uploaded an SSH key to DigitalOcean (not required), find the fingerprint under Settings -> Security. Finally, if you don't have an SSH key, [create one now](https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/).
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
| Name | Description | Default | Example |
|
||||||
|
|:-----|:------------|:--------|:--------|
|
||||||
|
| image | OS image for droplets | "coreos-stable" | coreos-stable, coreos-beta, coreos-alpha |
|
||||||
|
| controller_count | Number of controllers (i.e. masters) | 1 | 1 |
|
||||||
|
| controller_type | Digital Ocean droplet size | 2gb | 2gb (min), 4gb, 8gb |
|
||||||
|
| worker_count | Number of workers | 1 | 3 |
|
||||||
|
| worker_type | Digital Ocean droplet size | 512mb | 512mb, 1gb, 2gb, 4gb |
|
||||||
|
| networking | Choice of networking provider | "flannel" | "flannel" |
|
||||||
|
| pod_cidr | CIDR range to assign to Kubernetes pods | "10.2.0.0/16" | "10.22.0.0/16" |
|
||||||
|
| service_cidr | CIDR range to assgin to Kubernetes services | "10.3.0.0/16" | "10.3.0.0/24" |
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Do not choose a `controller_type` smaller than `2gb`. The `1gb` droplet is not sufficient for running a controller and bootstrapping will fail.
|
||||||
|
|
||||||
|
!!! bug
|
||||||
|
Digital Ocean firewalls do not yet support the IP tunneling (IP in IP) protocol used by Calico. You can try using "calico" for `networking`, but it will only work if the cloud firewall is removed (unsafe).
|
30
docs/faq.md
Normal file
30
docs/faq.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# FAQ
|
||||||
|
|
||||||
|
## Terraform?
|
||||||
|
|
||||||
|
Typhoon provides a Terraform Module for each supported operating system and platform. Terraform is considered a *format* detail, much like a Linux distro might provide images in the qcow2 or ISO format. It is a mechanism for sharing Typhoon in a way that works for many users.
|
||||||
|
|
||||||
|
Formats rise and evolve. Typhoon may choose to adapt the format over time (with lots of forewarning). However, the authors' have built several Kubernetes "distros" before and learned from mistakes - Terraform modules are the right format for now.
|
||||||
|
|
||||||
|
## Self-hosted etcd
|
||||||
|
|
||||||
|
Typhoon clusters on cloud providers run etcd as "self-hosted" pods, managed by the [etcd-operator](https://github.com/coreos/etcd-operator). By contrast, Typhoon bare-metal runs an etcd peer as a systemd `etcd-member.service` on each controller (i.e. on-host).
|
||||||
|
|
||||||
|
In practice, self-hosted etcd has proven to be *ok*, but not ideal. Running the apiserver's etcd atop Kubernetes itself is inherently complex, but works suitably in most cases. It can be opaque to debug if complex edge cases with upstream Kubernetes bugs arise.
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
Typhoon clusters and their defaults power the maintainers' clusters. The edge cases are sufficiently rare that self-hosted etcd is not a pressing issue, but cloud clusters may switch back to on-host etcd in the future.
|
||||||
|
|
||||||
|
## Operating Systems
|
||||||
|
|
||||||
|
Only Container Linux is supported currently. This just due to operational familiarity, rather than intentional exclusion. It's important that another operating system be added, to reduce the risk of making narrowly-scoped design decisions.
|
||||||
|
|
||||||
|
Fedora Cloud will likely be next.
|
||||||
|
|
||||||
|
## Maintainers
|
||||||
|
|
||||||
|
Typhoon clusters are Kubernetes configurations the maintainers use in real-world, production clusters.
|
||||||
|
|
||||||
|
* Maintainers must personally operate a bare-metal and cloud provider cluster and strive to exercise it in real-world scenarios
|
||||||
|
|
||||||
|
We merge features that are along the "blessed path". We minimize options to reduce complexity and matrix size. We remove outdated materials to reduce sprawl. "Skate where the puck is going", but also "wait until the fit is right". No is temporary, yes is forever.
|
242
docs/google-cloud.md
Normal file
242
docs/google-cloud.md
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
# Google Cloud
|
||||||
|
|
||||||
|
In this tutorial, we'll create a Kubernetes v1.7.3 cluster on Google Compute Engine (not GKE).
|
||||||
|
|
||||||
|
We'll declare a Kubernetes cluster in Terraform using the Typhoon Terraform module. On apply, a network, firewall rules, managed instance groups of Kubernetes controllers and workers, network load balancers for controllers and workers, and health checks will be created.
|
||||||
|
|
||||||
|
Controllers and workers are provisioned to run a `kubelet`. A one-time [bootkube](https://github.com/kubernetes-incubator/bootkube) bootstrap schedules `etcd`, `apiserver`, `scheduler`, `controller-manager`, and `kube-dns` on controllers and runs `kube-proxy` and `flannel` on each node. A generated `kubeconfig` provides `kubectl` access to the cluster.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Google Cloud Account and Service Account
|
||||||
|
* Google Cloud DNS Zone (registered Domain Name or delegated subdomain)
|
||||||
|
* Terraform v0.9.2+ and [terraform-provider-ct](https://github.com/coreos/terraform-provider-ct) installed locally
|
||||||
|
|
||||||
|
## Terraform Setup
|
||||||
|
|
||||||
|
Install [Terraform](https://www.terraform.io/downloads.html) v0.9.2+ on your system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform version
|
||||||
|
Terraform v0.10.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the [terraform-provider-ct](https://github.com/coreos/terraform-provider-ct) plugin binary for your system.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
wget https://github.com/coreos/terraform-provider-ct/releases/download/v0.2.0/terraform-provider-ct-v0.2.0-linux-amd64.tar.gz
|
||||||
|
tar xzf terraform-provider-ct-v0.2.0-linux-amd64.tar.gz
|
||||||
|
sudo mv terraform-provider-ct-v0.2.0-linux-amd64/terraform-provider-ct /usr/local/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the plugin to your `~/.terraformrc`.
|
||||||
|
|
||||||
|
```
|
||||||
|
providers {
|
||||||
|
ct = "/usr/local/bin/terraform-provider-ct"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Read [concepts](concepts.md) to learn about Terraform, modules, and organizing resources. Change to your infrastructure repository (e.g. `infra`).
|
||||||
|
|
||||||
|
```
|
||||||
|
cd infra/clusters
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provider
|
||||||
|
|
||||||
|
Login to your Google Console [API Manager](https://console.cloud.google.com/apis/dashboard) and select a project, or [signup](https://cloud.google.com/free/) if you don't have an account.
|
||||||
|
|
||||||
|
Select "Credentials", and create service account key credentials. Choose the "Compute Engine default service account" and save the JSON private key to a file that can be referenced in configs.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mv ~/Downloads/project-id-43048204.json ~/.config/google-cloud/terraform.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure the Google Cloud provider to use your service account key, project-id, and region in a `providers.tf` file.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
provider "google" {
|
||||||
|
credentials = "${file("~/.config/google-cloud/terraform.json")}"
|
||||||
|
project = "project-id"
|
||||||
|
region = "us-central1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional configuration options are described in the `google` provider [docs](https://www.terraform.io/docs/providers/google/index.html).
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
A project may contain multiple clusters if you wish. Regions are listed in [docs](https://cloud.google.com/compute/docs/regions-zones/regions-zones) or with `gcloud compute regions list`.
|
||||||
|
|
||||||
|
## Cluster
|
||||||
|
|
||||||
|
Define a Kubernetes cluster using the module `google-cloud/container-linux/kubernetes`.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
module "google-cloud-yavin" {
|
||||||
|
source = "git::https://github.com/poseidon/typhoon//google-cloud/container-linux/kubernetes"
|
||||||
|
|
||||||
|
# Google Cloud
|
||||||
|
zone = "us-central1-c"
|
||||||
|
dns_zone = "example.com"
|
||||||
|
dns_zone_name = "example-zone"
|
||||||
|
os_image = "coreos-stable-1465-6-0-v20170817"
|
||||||
|
|
||||||
|
cluster_name = "yavin"
|
||||||
|
controller_count = 1
|
||||||
|
worker_count = 2
|
||||||
|
ssh_authorized_key = "ssh-rsa AAAAB3Nz..."
|
||||||
|
|
||||||
|
# output assets dir
|
||||||
|
asset_dir = "/home/user/.secrets/clusters/yavin"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reference the [variables docs](#variables) or the [variables.tf](https://github.com/poseidon/typhoon/blob/master/google-cloud/container-linux/kubernetes/variables.tf) source.
|
||||||
|
|
||||||
|
## ssh-agent
|
||||||
|
|
||||||
|
Initial bootstrapping requires `bootkube.service` be started on one controller node. Terraform uses `ssh-agent` to automate this step. Add your SSH private key to `ssh-agent`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh-add ~/.ssh/id_rsa
|
||||||
|
ssh-add -L
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
`terrafrom apply` will hang connecting to a controller if `ssh-agent` does not contain the SSH key.
|
||||||
|
|
||||||
|
## Apply
|
||||||
|
|
||||||
|
Initialize the config directory if this is the first use with Terraform.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
terraform init
|
||||||
|
```
|
||||||
|
|
||||||
|
Get or update Terraform modules.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform get # downloads missing modules
|
||||||
|
$ terraform get --update # updates all modules
|
||||||
|
Get: git::https://github.com/poseidon/typhoon (update)
|
||||||
|
Get: git::https://github.com/poseidon/bootkube-terraform.git?ref=v0.6.1 (update)
|
||||||
|
```
|
||||||
|
|
||||||
|
Plan the resources to be created.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform plan
|
||||||
|
Plan: 64 to add, 0 to change, 0 to destroy.
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply the changes to create the cluster.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform apply
|
||||||
|
module.google-cloud-yavin.null_resource.bootkube-start: Still creating... (10s elapsed)
|
||||||
|
...
|
||||||
|
|
||||||
|
module.google-cloud-yavin.null_resource.bootkube-start: Still creating... (8m30s elapsed)
|
||||||
|
module.google-cloud-yavin.null_resource.bootkube-start: Still creating... (8m40s elapsed)
|
||||||
|
module.google-cloud-yavin.null_resource.bootkube-start: Creation complete (ID: 5768638456220583358)
|
||||||
|
|
||||||
|
Apply complete! Resources: 64 added, 0 changed, 0 destroyed.
|
||||||
|
```
|
||||||
|
|
||||||
|
In 5-10 minutes, the Kubernetes cluster will be ready.
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
[Install kubectl](https://coreos.com/kubernetes/docs/latest/configure-kubectl.html) on your system. Use the generated `kubeconfig` credentials to access the Kubernetes cluster and list nodes.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ KUBECONFIG=/home/user/.secrets/clusters/yavin/auth/kubeconfig
|
||||||
|
$ kubectl get nodes
|
||||||
|
NAME STATUS AGE VERSION
|
||||||
|
yavin-controller-1682.c.example-com.internal Ready 6m v1.7.3+coreos.0
|
||||||
|
yavin-worker-jrbf.c.example-com.internal Ready 5m v1.7.3+coreos.0
|
||||||
|
yavin-worker-mzdm.c.example-com.internal Ready 5m v1.7.3+coreos.0
|
||||||
|
```
|
||||||
|
|
||||||
|
List the pods.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get pods --all-namespaces
|
||||||
|
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||||
|
kube-system etcd-operator-3329263108-f443m 1/1 Running 1 6m
|
||||||
|
kube-system kube-apiserver-zppls 1/1 Running 0 6m
|
||||||
|
kube-system kube-controller-manager-3271970485-gh9kt 1/1 Running 0 6m
|
||||||
|
kube-system kube-controller-manager-3271970485-h90v8 1/1 Running 1 6m
|
||||||
|
kube-system kube-dns-1187388186-zj5dl 3/3 Running 0 6m
|
||||||
|
kube-system kube-etcd-0000 1/1 Running 0 5m
|
||||||
|
kube-system kube-etcd-network-checkpointer-crznb 1/1 Running 0 6m
|
||||||
|
kube-system kube-flannel-1cs8z 2/2 Running 0 6m
|
||||||
|
kube-system kube-flannel-d1l5b 2/2 Running 0 6m
|
||||||
|
kube-system kube-flannel-sp9ps 2/2 Running 0 6m
|
||||||
|
kube-system kube-proxy-117v6 1/1 Running 0 6m
|
||||||
|
kube-system kube-proxy-9886n 1/1 Running 0 6m
|
||||||
|
kube-system kube-proxy-njn47 1/1 Running 0 6m
|
||||||
|
kube-system kube-scheduler-3895335239-5x87r 1/1 Running 0 6m
|
||||||
|
kube-system kube-scheduler-3895335239-bzrrt 1/1 Running 1 6m
|
||||||
|
kube-system pod-checkpointer-l6lrt 1/1 Running 0 6m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Going Further
|
||||||
|
|
||||||
|
Learn about [version pinning](concepts.md#versioning), maintenance, and [addons](addons/overview.md).
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
On Container Linux clusters, install the `container-linux-update-operator` addon to coordinate reboots and drains when nodes auto-update. Otherwise, updates may not be applied until the next reboot.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
| Name | Description | Example |
|
||||||
|
|:-----|:------------|:--------|
|
||||||
|
| cluster_name | Unique cluster name (prepended to dns_zone) | "yavin" |
|
||||||
|
| zone | Google Cloud zone | "us-central1-f" |
|
||||||
|
| dns_zone | Google Cloud DNS zone | "google-cloud.example.com" |
|
||||||
|
| dns_zone_name | Google Cloud DNS zone name | "example-zone" |
|
||||||
|
| ssh_authorized_key | SSH public key for ~/.ssh_authorized_keys | "ssh-rsa AAAAB3NZ..." |
|
||||||
|
| os_image | OS image for compute instances | "coreos-stable-1465-6-0-v20170817" |
|
||||||
|
| asset_dir | Path to a directory where generated assets should be placed (contains secrets) | "/home/user/.secrets/clusters/yavin" |
|
||||||
|
|
||||||
|
Check the list of valid [zones](https://cloud.google.com/compute/docs/regions-zones/regions-zones) and list Container Linux [images](https://cloud.google.com/compute/docs/images) with `gcloud compute images list | grep coreos`.
|
||||||
|
|
||||||
|
#### DNS Zone
|
||||||
|
|
||||||
|
Clusters create a DNS A record `${cluster_name}.${dns_zone}` to resolve a network load balancer backed by controller instances. This FQDN is used by workers and `kubectl` to access the apiserver. In this example, the cluster's apiserver would be accessible at `yavin.google-cloud.example.com`.
|
||||||
|
|
||||||
|
You'll need a registered domain name or subdomain registered in a Google Cloud DNS zone. You can set this up once and create many clusters with unqiue names.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
resource "google_dns_managed_zone" "zone-for-clusters" {
|
||||||
|
dns_name = "google-cloud.example.com."
|
||||||
|
name = "example-zone"
|
||||||
|
description = "Production DNS zone"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip ""
|
||||||
|
If you have an existing domain name with a zone file elsewhere, just carve out a subdomain that can be managed on Google Cloud (e.g. google-cloud.mydomain.com) and [update nameservers](https://cloud.google.com/dns/update-name-servers).
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
| Name | Description | Default | Example |
|
||||||
|
|:-----|:------------|:--------|:--------|
|
||||||
|
| machine_type | Machine type for compute instances | "n1-standard-1" | See below |
|
||||||
|
| controller_count | Number of controllers (i.e. masters) | 1 | 1 |
|
||||||
|
| worker_count | Number of workers | 1 | 3 |
|
||||||
|
| worker_preemptible | If enabled, Compute Engine will terminate controllers randomly within 24 hours | false | true |
|
||||||
|
| networking | Choice of networking provider | "flannel" | "flannel" or "calico" |
|
||||||
|
| pod_cidr | CIDR range to assign to Kubernetes pods | "10.2.0.0/16" | "10.22.0.0/16" |
|
||||||
|
| service_cidr | CIDR range to assgin to Kubernetes services | "10.3.0.0/16" | "10.3.0.0/24" |
|
||||||
|
|
||||||
|
Check the list of valid [machine types](https://cloud.google.com/compute/docs/machine-types).
|
||||||
|
|
||||||
|
#### Preemption
|
||||||
|
|
||||||
|
Add `worker_premeptible = "true"` to allow worker nodes to be [preempted](https://cloud.google.com/compute/docs/instances/preemptible) at random, but pay [significantly](https://cloud.google.com/compute/pricing) less. Clusters tolerate stopping instances fairly well (reschedules pods, but cannot drain) and preemption provides a nice reward for running fault-tolerant cluster systems.`
|
||||||
|
|
BIN
docs/img/favicon.ico
Normal file
BIN
docs/img/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
BIN
docs/img/spin.png
Normal file
BIN
docs/img/spin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
1
docs/img/spin.svg
Normal file
1
docs/img/spin.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" ?><svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Merged"><g><path d="M91.532,51.789c0.988-1.976,0.187-4.379-1.789-5.367L65.272,34.186c-2.099-2.028-4.599-3.642-7.369-4.712l22.729-7.576 c-0.698-2.096-2.964-3.229-5.059-2.53l-25.924,8.641c-3.048,0.048-5.946,0.719-8.575,1.891L51.789,8.468 c-1.976-0.988-4.379-0.187-5.367,1.789L34.186,34.728c-2.028,2.099-3.642,4.599-4.712,7.369l-7.576-22.729 c-2.096,0.698-3.229,2.964-2.53,5.059l8.641,25.924c0.048,3.048,0.719,5.946,1.891,8.575L8.468,48.211 c-0.988,1.976-0.187,4.379,1.789,5.367l24.471,12.236c2.099,2.028,4.599,3.642,7.369,4.712l-22.729,7.576 c0.698,2.096,2.964,3.229,5.059,2.53l25.924-8.641c3.048-0.048,5.946-0.719,8.575-1.891L48.211,91.532 c1.976,0.988,4.379,0.187,5.367-1.789l12.236-24.471c2.028-2.099,3.642-4.599,4.712-7.369l7.576,22.729 c2.096-0.698,3.229-2.964,2.53-5.06l-8.641-25.924c-0.048-3.048-0.719-5.946-1.891-8.575L91.532,51.789z M50,68 c-9.925,0-18-8.075-18-18s8.075-18,18-18s18,8.075,18,18S59.925,68,50,68z"/><path d="M50,38c-6.617,0-12,5.383-12,12s5.383,12,12,12s12-5.383,12-12S56.617,38,50,38z M50,58c-4.411,0-8-3.589-8-8s3.589-8,8-8 s8,3.589,8,8S54.411,58,50,58z"/></g></g></svg>
|
After Width: | Height: | Size: 1.3 KiB |
118
docs/index.md
Normal file
118
docs/index.md
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# Typhoon <img align="right" src="https://storage.googleapis.com/dghubble/spin.png">
|
||||||
|
|
||||||
|
Typhoon is a minimal and free Kubernetes distribution.
|
||||||
|
|
||||||
|
* Minimal, stable base Kubernetes distribution
|
||||||
|
* Declarative infrastructure and configuration
|
||||||
|
* [Free](#social-contract) (freedom and cost) and privacy-respecting
|
||||||
|
* Practical for labs, datacenters, and clouds
|
||||||
|
|
||||||
|
Typhoon distributes upstream Kubernetes, architectural conventions, and cluster addons, much like a GNU/Linux distribution provides the Linux kernel and userspace components.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Kubernetes v1.7.3 (upstream, via [kubernetes-incubator/bootkube](https://github.com/kubernetes-incubator/bootkube))
|
||||||
|
* Single or multi-master, workloads isolated on workers, [flannel](https://github.com/coreos/flannel) or [Calico](https://www.projectcalico.org/) networking (with BGP peering)
|
||||||
|
* On-cluster etcd with TLS, [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/)-enabled, [network policy](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
|
||||||
|
* Ready for Ingress, Dashboards, Metrics and other optional [addons](addons/overview.md)
|
||||||
|
* Provided via Terraform Modules
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
Typhoon provides a Terraform Module for each supported operating system and platform.
|
||||||
|
|
||||||
|
| Platform | Operating System | Terraform Module | Status |
|
||||||
|
|---------------|------------------|------------------|--------|
|
||||||
|
| Bare-Metal | Container Linux | [bare-metal/container-linux/kubernetes](bare-metal.md) | production |
|
||||||
|
| Digital Ocean | Container Linux | [digital-ocean/container-linux/kubernetes](digital-ocean.md) | beta |
|
||||||
|
| Google Cloud | Container Linux | [google-cloud/container-linux/kubernetes](google-cloud.md) | production |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
* [Concepts](concepts.md)
|
||||||
|
* Tutorials
|
||||||
|
* [Bare-Metal](bare-metal.md)
|
||||||
|
* [Digital Ocean](digital-ocean.md)
|
||||||
|
* [Google-Cloud](google-cloud.md)
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Define a Kubernetes cluster by using the Terraform module for your chosen platform and operating system. Here's a minimal example.
|
||||||
|
|
||||||
|
```tf
|
||||||
|
module "google-cloud-yavin" {
|
||||||
|
source = "git::https://github.com/poseidon/typhoon//google-cloud/container-linux/kubernetes"
|
||||||
|
|
||||||
|
# Google Cloud
|
||||||
|
zone = "us-central1-c"
|
||||||
|
dns_zone = "example.com"
|
||||||
|
dns_zone_name = "example-zone"
|
||||||
|
os_image = "coreos-stable-1465-6-0-v20170817"
|
||||||
|
|
||||||
|
cluster_name = "yavin"
|
||||||
|
controller_count = 1
|
||||||
|
worker_count = 2
|
||||||
|
ssh_authorized_key = "ssh-rsa AAAAB3Nz..."
|
||||||
|
|
||||||
|
# output assets dir
|
||||||
|
asset_dir = "/home/user/.secrets/clusters/yavin"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Fetch modules, plan the changes to be made, and apply the changes.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ terraform get --update
|
||||||
|
$ terraform plan
|
||||||
|
Plan: 64 to add, 0 to change, 0 to destroy.
|
||||||
|
$ terraform apply
|
||||||
|
Apply complete! Resources: 64 added, 0 changed, 0 destroyed.
|
||||||
|
```
|
||||||
|
|
||||||
|
In 5-10 minutes (varies by platform), the cluster will be ready. This Google Cloud example creates a `yavin.example.com` DNS record to resolve to a network load balancer across controller nodes.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ KUBECONFIG=/home/user/.secrets/clusters/yavin/auth/kubeconfig
|
||||||
|
$ kubectl get nodes
|
||||||
|
NAME STATUS AGE VERSION
|
||||||
|
yavin-controller-1682.c.example-com.internal Ready 6m v1.7.3+coreos.0
|
||||||
|
yavin-worker-jrbf.c.example-com.internal Ready 5m v1.7.3+coreos.0
|
||||||
|
yavin-worker-mzdm.c.example-com.internal Ready 5m v1.7.3+coreos.0
|
||||||
|
```
|
||||||
|
|
||||||
|
List the pods.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl get pods --all-namespaces
|
||||||
|
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||||
|
kube-system etcd-operator-3329263108-f443m 1/1 Running 1 6m
|
||||||
|
kube-system kube-apiserver-zppls 1/1 Running 0 6m
|
||||||
|
kube-system kube-controller-manager-3271970485-gh9kt 1/1 Running 0 6m
|
||||||
|
kube-system kube-controller-manager-3271970485-h90v8 1/1 Running 1 6m
|
||||||
|
kube-system kube-dns-1187388186-zj5dl 3/3 Running 0 6m
|
||||||
|
kube-system kube-etcd-0000 1/1 Running 0 5m
|
||||||
|
kube-system kube-etcd-network-checkpointer-crznb 1/1 Running 0 6m
|
||||||
|
kube-system kube-flannel-1cs8z 2/2 Running 0 6m
|
||||||
|
kube-system kube-flannel-d1l5b 2/2 Running 0 6m
|
||||||
|
kube-system kube-flannel-sp9ps 2/2 Running 0 6m
|
||||||
|
kube-system kube-proxy-117v6 1/1 Running 0 6m
|
||||||
|
kube-system kube-proxy-9886n 1/1 Running 0 6m
|
||||||
|
kube-system kube-proxy-njn47 1/1 Running 0 6m
|
||||||
|
kube-system kube-scheduler-3895335239-5x87r 1/1 Running 0 6m
|
||||||
|
kube-system kube-scheduler-3895335239-bzrrt 1/1 Running 1 6m
|
||||||
|
kube-system pod-checkpointer-l6lrt 1/1 Running 0 6m
|
||||||
|
```
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Typhoon powers the author's cloud and colocation clusters. The project has evolved through operational experience and Kubernetes changes. Typhoon is shared under a free license to allow others to use the work freely and contribute to its upkeep.
|
||||||
|
|
||||||
|
Typhoon addresses real world needs, which you may share. It is honest about limitations or areas that aren't mature yet. It avoids buzzword bingo and hype. It does not aim to be the one-solution-fits-all distro. An ecosystem of free (or enterprise) Kubernetes distros is healthy.
|
||||||
|
|
||||||
|
## Social Contract
|
||||||
|
|
||||||
|
Typhoon is not a product, trial, or free-tier. It is not run by a company, does not offer support or services, and does not accept or make any money. It is not associated with any operating system or platform vendor.
|
||||||
|
|
||||||
|
Typhoon clusters will contain only [free](https://www.debian.org/intro/free) components. Cluster components will not collect data on users without their permission.
|
||||||
|
|
||||||
|
*Disclosure: The author works for CoreOS and previously wrote Matchbox and original Tectonic for bare-metal and AWS. This project is not associated with CoreOS.*
|
@ -101,7 +101,7 @@ storage:
|
|||||||
contents:
|
contents:
|
||||||
inline: |
|
inline: |
|
||||||
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
KUBELET_IMAGE_TAG=v1.6.7_coreos.0
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
- path: /etc/sysctl.d/max-user-watches.conf
|
- path: /etc/sysctl.d/max-user-watches.conf
|
||||||
filesystem: root
|
filesystem: root
|
||||||
contents:
|
contents:
|
||||||
@ -120,10 +120,11 @@ storage:
|
|||||||
# Wrapper for bootkube start
|
# Wrapper for bootkube start
|
||||||
set -e
|
set -e
|
||||||
# Move experimental manifests
|
# Move experimental manifests
|
||||||
|
[ -d /opt/bootkube/assets/manifests-* ] && mv /opt/bootkube/assets/manifests-*/* /opt/bootkube/assets/manifests && rm -rf /opt/bootkube/assets/manifests-*
|
||||||
[ -d /opt/bootkube/assets/experimental/manifests ] && mv /opt/bootkube/assets/experimental/manifests/* /opt/bootkube/assets/manifests && rm -r /opt/bootkube/assets/experimental/manifests
|
[ -d /opt/bootkube/assets/experimental/manifests ] && mv /opt/bootkube/assets/experimental/manifests/* /opt/bootkube/assets/manifests && rm -r /opt/bootkube/assets/experimental/manifests
|
||||||
[ -d /opt/bootkube/assets/experimental/bootstrap-manifests ] && mv /opt/bootkube/assets/experimental/bootstrap-manifests/* /opt/bootkube/assets/bootstrap-manifests && rm -r /opt/bootkube/assets/experimental/bootstrap-manifests
|
[ -d /opt/bootkube/assets/experimental/bootstrap-manifests ] && mv /opt/bootkube/assets/experimental/bootstrap-manifests/* /opt/bootkube/assets/bootstrap-manifests && rm -r /opt/bootkube/assets/experimental/bootstrap-manifests
|
||||||
BOOTKUBE_ACI="$${BOOTKUBE_ACI:-quay.io/coreos/bootkube}"
|
BOOTKUBE_ACI="$${BOOTKUBE_ACI:-quay.io/coreos/bootkube}"
|
||||||
BOOTKUBE_VERSION="$${BOOTKUBE_VERSION:-v0.5.1}"
|
BOOTKUBE_VERSION="$${BOOTKUBE_VERSION:-v0.6.1}"
|
||||||
BOOTKUBE_ASSETS="$${BOOTKUBE_ASSETS:-/opt/bootkube/assets}"
|
BOOTKUBE_ASSETS="$${BOOTKUBE_ASSETS:-/opt/bootkube/assets}"
|
||||||
exec /usr/bin/rkt run \
|
exec /usr/bin/rkt run \
|
||||||
--trust-keys-from-https \
|
--trust-keys-from-https \
|
@ -16,9 +16,9 @@ resource "google_compute_instance_group_manager" "controllers" {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
# bootkube-controller Container Linux config
|
# Controller Container Linux Config
|
||||||
data "template_file" "controller_config" {
|
data "template_file" "controller_config" {
|
||||||
template = "${file("${path.module}/cl/bootkube-controller.yaml.tmpl")}"
|
template = "${file("${path.module}/cl/controller.yaml.tmpl")}"
|
||||||
|
|
||||||
vars = {
|
vars = {
|
||||||
k8s_dns_service_ip = "${cidrhost(var.service_cidr, 10)}"
|
k8s_dns_service_ip = "${cidrhost(var.service_cidr, 10)}"
|
||||||
@ -38,7 +38,7 @@ data "ct_config" "controller_ign" {
|
|||||||
|
|
||||||
resource "google_compute_instance_template" "controller" {
|
resource "google_compute_instance_template" "controller" {
|
||||||
name_prefix = "${var.cluster_name}-controller-"
|
name_prefix = "${var.cluster_name}-controller-"
|
||||||
description = "bootkube-controller Instance template"
|
description = "Controller Instance template"
|
||||||
machine_type = "${var.machine_type}"
|
machine_type = "${var.machine_type}"
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
@ -50,9 +50,6 @@ resource "google_compute_instance_template" "controller" {
|
|||||||
preemptible = "${var.preemptible}"
|
preemptible = "${var.preemptible}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# QUIRK: Undocumented field defaults to true if not set
|
|
||||||
automatic_restart = "${var.preemptible ? false : true}"
|
|
||||||
|
|
||||||
disk {
|
disk {
|
||||||
auto_delete = true
|
auto_delete = true
|
||||||
boot = true
|
boot = true
|
@ -1,16 +1,14 @@
|
|||||||
# DNS record set to the network load balancer over controllers
|
# Controller Network Load balancer DNS record
|
||||||
resource "google_dns_record_set" "k8s_dns" {
|
resource "google_dns_record_set" "controllers" {
|
||||||
# Managed DNS Zone name
|
# DNS Zone name where record should be created
|
||||||
managed_zone = "${var.dns_base_zone_name}"
|
managed_zone = "${var.dns_zone_name}"
|
||||||
|
|
||||||
# Name of the DNS record
|
|
||||||
#name = "${format("%s.%s.", var.cluster_name, var.dns_base_zone)}"
|
|
||||||
name = "${var.k8s_domain_name}."
|
|
||||||
|
|
||||||
|
# DNS record
|
||||||
|
name = "${format("%s.%s.", var.cluster_name, var.dns_zone)}"
|
||||||
type = "A"
|
type = "A"
|
||||||
ttl = 300
|
ttl = 300
|
||||||
|
|
||||||
# compute instance public IP
|
# IPv4 address of controllers' network load balancer
|
||||||
rrdatas = ["${google_compute_address.controllers-ip.address}"]
|
rrdatas = ["${google_compute_address.controllers-ip.address}"]
|
||||||
}
|
}
|
||||||
|
|
@ -13,21 +13,16 @@ variable "network" {
|
|||||||
description = "Name of the network to attach to the compute instance interfaces"
|
description = "Name of the network to attach to the compute instance interfaces"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "dns_base_zone" {
|
variable "dns_zone" {
|
||||||
type = "string"
|
type = "string"
|
||||||
description = "Google Cloud DNS Zone value to create etcd/k8s subdomains (e.g. dghubble.io)"
|
description = "Google Cloud DNS Zone value to create etcd/k8s subdomains (e.g. dghubble.io)"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "dns_base_zone_name" {
|
variable "dns_zone_name" {
|
||||||
type = "string"
|
type = "string"
|
||||||
description = "Google Cloud DNS Zone name to create etcd/k8s subdomains (e.g. dghubble-io)"
|
description = "Google Cloud DNS Zone name to create etcd/k8s subdomains (e.g. dghubble-io)"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "k8s_domain_name" {
|
|
||||||
type = "string"
|
|
||||||
description = "Controller DNS name which resolves to the controller instance. Kubectl and workers use TLS client credentials to communicate via this endpoint."
|
|
||||||
}
|
|
||||||
|
|
||||||
# instances
|
# instances
|
||||||
|
|
||||||
variable "count" {
|
variable "count" {
|
||||||
@ -64,6 +59,12 @@ variable "preemptible" {
|
|||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
|
|
||||||
|
variable "networking" {
|
||||||
|
description = "Choice of networking provider (flannel or calico)"
|
||||||
|
type = "string"
|
||||||
|
default = "flannel"
|
||||||
|
}
|
||||||
|
|
||||||
variable "service_cidr" {
|
variable "service_cidr" {
|
||||||
description = <<EOD
|
description = <<EOD
|
||||||
CIDR IP range to assign Kubernetes services.
|
CIDR IP range to assign Kubernetes services.
|
14
google-cloud/container-linux/kubernetes/bootkube.tf
Normal file
14
google-cloud/container-linux/kubernetes/bootkube.tf
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Self-hosted Kubernetes assets (kubeconfig, manifests)
|
||||||
|
module "bootkube" {
|
||||||
|
source = "git::https://github.com/poseidon/bootkube-terraform.git?ref=5ffbfec46dc05721eaf9d15c3c9bbedefaead1bc"
|
||||||
|
|
||||||
|
cluster_name = "${var.cluster_name}"
|
||||||
|
api_servers = ["${format("%s.%s", var.cluster_name, var.dns_zone)}"]
|
||||||
|
etcd_servers = ["http://127.0.0.1:2379"]
|
||||||
|
asset_dir = "${var.asset_dir}"
|
||||||
|
networking = "${var.networking}"
|
||||||
|
network_mtu = 1440
|
||||||
|
pod_cidr = "${var.pod_cidr}"
|
||||||
|
service_cidr = "${var.service_cidr}"
|
||||||
|
experimental_self_hosted_etcd = "true"
|
||||||
|
}
|
@ -1,20 +1,20 @@
|
|||||||
module "controllers" {
|
module "controllers" {
|
||||||
source = "../gce-bootkube-controller"
|
source = "../controllers"
|
||||||
cluster_name = "${var.cluster_name}"
|
cluster_name = "${var.cluster_name}"
|
||||||
ssh_authorized_key = "${var.ssh_authorized_key}"
|
ssh_authorized_key = "${var.ssh_authorized_key}"
|
||||||
|
|
||||||
# GCE
|
# GCE
|
||||||
network = "${google_compute_network.network.name}"
|
network = "${google_compute_network.network.name}"
|
||||||
count = "${var.controller_count}"
|
count = "${var.controller_count}"
|
||||||
dns_base_zone = "${var.dns_base_zone}"
|
zone = "${var.zone}"
|
||||||
dns_base_zone_name = "${var.dns_base_zone_name}"
|
dns_zone = "${var.dns_zone}"
|
||||||
k8s_domain_name = "${var.k8s_domain_name}"
|
dns_zone_name = "${var.dns_zone_name}"
|
||||||
zone = "${var.zone}"
|
machine_type = "${var.machine_type}"
|
||||||
machine_type = "${var.machine_type}"
|
os_image = "${var.os_image}"
|
||||||
os_image = "${var.os_image}"
|
preemptible = "${var.controller_preemptible}"
|
||||||
preemptible = "${var.controller_preemptible}"
|
|
||||||
|
|
||||||
# configuration
|
# configuration
|
||||||
|
networking = "${var.networking}"
|
||||||
service_cidr = "${var.service_cidr}"
|
service_cidr = "${var.service_cidr}"
|
||||||
kubeconfig_ca_cert = "${module.bootkube.ca_cert}"
|
kubeconfig_ca_cert = "${module.bootkube.ca_cert}"
|
||||||
kubeconfig_kubelet_cert = "${module.bootkube.kubelet_cert}"
|
kubeconfig_kubelet_cert = "${module.bootkube.kubelet_cert}"
|
||||||
@ -23,7 +23,7 @@ module "controllers" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module "workers" {
|
module "workers" {
|
||||||
source = "../gce-bootkube-worker"
|
source = "../workers"
|
||||||
cluster_name = "${var.cluster_name}"
|
cluster_name = "${var.cluster_name}"
|
||||||
ssh_authorized_key = "${var.ssh_authorized_key}"
|
ssh_authorized_key = "${var.ssh_authorized_key}"
|
||||||
|
|
@ -44,3 +44,23 @@ resource "google_compute_firewall" "allow-internal" {
|
|||||||
|
|
||||||
source_ranges = ["10.0.0.0/8"]
|
source_ranges = ["10.0.0.0/8"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Calico BGP and IPIP
|
||||||
|
# https://docs.projectcalico.org/v2.5/reference/public-cloud/gce
|
||||||
|
resource "google_compute_firewall" "allow-calico" {
|
||||||
|
count = "${var.networking == "calico" ? 1 : 0}"
|
||||||
|
|
||||||
|
name = "${var.cluster_name}-allow-calico"
|
||||||
|
network = "${google_compute_network.network.name}"
|
||||||
|
|
||||||
|
allow {
|
||||||
|
protocol = "tcp"
|
||||||
|
ports = ["179"]
|
||||||
|
}
|
||||||
|
|
||||||
|
allow {
|
||||||
|
protocol = "ipip"
|
||||||
|
}
|
||||||
|
|
||||||
|
source_ranges = ["10.0.0.0/8"]
|
||||||
|
}
|
@ -6,7 +6,7 @@ resource "null_resource" "bootkube-start" {
|
|||||||
# TODO: SSH to a controller's IP instead of waiting on DNS resolution
|
# TODO: SSH to a controller's IP instead of waiting on DNS resolution
|
||||||
connection {
|
connection {
|
||||||
type = "ssh"
|
type = "ssh"
|
||||||
host = "${var.k8s_domain_name}"
|
host = "${format("%s.%s", var.cluster_name, var.dns_zone)}"
|
||||||
user = "core"
|
user = "core"
|
||||||
timeout = "15m"
|
timeout = "15m"
|
||||||
}
|
}
|
@ -3,46 +3,41 @@ variable "cluster_name" {
|
|||||||
description = "Cluster name"
|
description = "Cluster name"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "ssh_authorized_key" {
|
|
||||||
type = "string"
|
|
||||||
description = "SSH public key for logging in as user 'core'"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "dns_base_zone" {
|
|
||||||
type = "string"
|
|
||||||
description = "Google Cloud DNS Zone value to create etcd/k8s subdomains (e.g. dghubble.io)"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "dns_base_zone_name" {
|
|
||||||
type = "string"
|
|
||||||
description = "Google Cloud DNS Zone name to create etcd/k8s subdomains (e.g. dghubble-io)"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "k8s_domain_name" {
|
|
||||||
type = "string"
|
|
||||||
description = "Controller DNS name which resolves to the controller instance. Kubectl and workers use TLS client credentials to communicate via this endpoint."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "zone" {
|
variable "zone" {
|
||||||
type = "string"
|
type = "string"
|
||||||
description = "Google zone that compute instances should be created in (e.g. gcloud compute zones list)"
|
description = "Google Cloud zone (e.g. us-central1-f, see `gcloud compute zones list`)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "dns_zone" {
|
||||||
|
type = "string"
|
||||||
|
description = "Google Cloud DNS Zone (e.g. google-cloud.dghubble.io)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "dns_zone_name" {
|
||||||
|
type = "string"
|
||||||
|
description = "Google Cloud DNS Zone name (e.g. google-cloud-prod-zone)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_authorized_key" {
|
||||||
|
type = "string"
|
||||||
|
description = "SSH public key for user 'core'"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "machine_type" {
|
variable "machine_type" {
|
||||||
type = "string"
|
type = "string"
|
||||||
default = "n1-standard-1"
|
default = "n1-standard-1"
|
||||||
description = "Machine type for compute instances (e.g. gcloud compute machine-types list)"
|
description = "Machine type for compute instances (see `gcloud compute machine-types list`)"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "os_image" {
|
variable "os_image" {
|
||||||
type = "string"
|
type = "string"
|
||||||
description = "OS image from which to initialize the disk (e.g. gcloud compute images list)"
|
description = "OS image from which to initialize the disk (see `gcloud compute images list`)"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "controller_count" {
|
variable "controller_count" {
|
||||||
type = "string"
|
type = "string"
|
||||||
default = "1"
|
default = "1"
|
||||||
description = "Number of workers"
|
description = "Number of controllers"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "worker_count" {
|
variable "worker_count" {
|
||||||
@ -70,6 +65,12 @@ variable "asset_dir" {
|
|||||||
type = "string"
|
type = "string"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "networking" {
|
||||||
|
description = "Choice of networking provider (flannel or calico)"
|
||||||
|
type = "string"
|
||||||
|
default = "flannel"
|
||||||
|
}
|
||||||
|
|
||||||
variable "pod_cidr" {
|
variable "pod_cidr" {
|
||||||
description = "CIDR IP range to assign Kubernetes pods"
|
description = "CIDR IP range to assign Kubernetes pods"
|
||||||
type = "string"
|
type = "string"
|
@ -99,7 +99,7 @@ storage:
|
|||||||
contents:
|
contents:
|
||||||
inline: |
|
inline: |
|
||||||
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
KUBELET_IMAGE_URL=quay.io/coreos/hyperkube
|
||||||
KUBELET_IMAGE_TAG=v1.6.7_coreos.0
|
KUBELET_IMAGE_TAG=v1.7.3_coreos.0
|
||||||
- path: /etc/sysctl.d/max-user-watches.conf
|
- path: /etc/sysctl.d/max-user-watches.conf
|
||||||
filesystem: root
|
filesystem: root
|
||||||
contents:
|
contents:
|
||||||
@ -116,7 +116,7 @@ storage:
|
|||||||
--trust-keys-from-https \
|
--trust-keys-from-https \
|
||||||
--volume config,kind=host,source=/etc/kubernetes \
|
--volume config,kind=host,source=/etc/kubernetes \
|
||||||
--mount volume=config,target=/etc/kubernetes \
|
--mount volume=config,target=/etc/kubernetes \
|
||||||
quay.io/coreos/hyperkube:v1.6.7_coreos.0 \
|
quay.io/coreos/hyperkube:v1.7.3_coreos.0 \
|
||||||
--net=host \
|
--net=host \
|
||||||
--dns=host \
|
--dns=host \
|
||||||
--exec=/kubectl -- --kubeconfig=/etc/kubernetes/kubeconfig delete node $(hostname)
|
--exec=/kubectl -- --kubeconfig=/etc/kubernetes/kubeconfig delete node $(hostname)
|
@ -16,9 +16,9 @@ resource "google_compute_instance_group_manager" "workers" {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
# bootkube-worker Container Linux config
|
# Worker Container Linux Config
|
||||||
data "template_file" "worker_config" {
|
data "template_file" "worker_config" {
|
||||||
template = "${file("${path.module}/cl/bootkube-worker.yaml.tmpl")}"
|
template = "${file("${path.module}/cl/worker.yaml.tmpl")}"
|
||||||
|
|
||||||
vars = {
|
vars = {
|
||||||
k8s_dns_service_ip = "${cidrhost(var.service_cidr, 10)}"
|
k8s_dns_service_ip = "${cidrhost(var.service_cidr, 10)}"
|
||||||
@ -38,7 +38,7 @@ data "ct_config" "worker_ign" {
|
|||||||
|
|
||||||
resource "google_compute_instance_template" "worker" {
|
resource "google_compute_instance_template" "worker" {
|
||||||
name_prefix = "${var.cluster_name}-worker-"
|
name_prefix = "${var.cluster_name}-worker-"
|
||||||
description = "bootkube-worker Instance template"
|
description = "Worker Instance template"
|
||||||
machine_type = "${var.machine_type}"
|
machine_type = "${var.machine_type}"
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
@ -50,9 +50,6 @@ resource "google_compute_instance_template" "worker" {
|
|||||||
preemptible = "${var.preemptible}"
|
preemptible = "${var.preemptible}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# QUIRK: Undocumented field defaults to true if not set
|
|
||||||
automatic_restart = "${var.preemptible ? false : true}"
|
|
||||||
|
|
||||||
disk {
|
disk {
|
||||||
auto_delete = true
|
auto_delete = true
|
||||||
boot = true
|
boot = true
|
55
mkdocs.yml
Normal file
55
mkdocs.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
site_name: Typhoon
|
||||||
|
theme: material
|
||||||
|
site_favicon: 'img/favicon.ico'
|
||||||
|
repo_name: 'poseidon/typhoon'
|
||||||
|
repo_url: 'https://github.com/poseidon/typhoon'
|
||||||
|
extra:
|
||||||
|
palette:
|
||||||
|
primary: 'blue'
|
||||||
|
accent: 'light blue'
|
||||||
|
logo: 'img/spin.png'
|
||||||
|
font:
|
||||||
|
text: 'Roboto Slab'
|
||||||
|
code: 'Roboto Mono'
|
||||||
|
social:
|
||||||
|
- type: 'github'
|
||||||
|
link: 'https://github.com/poseidon'
|
||||||
|
- type: 'twitter'
|
||||||
|
link: 'https://twitter.com/typhoon8s'
|
||||||
|
google_analytics:
|
||||||
|
- 'UA-38995133-6'
|
||||||
|
- 'auto'
|
||||||
|
markdown_extensions:
|
||||||
|
- admonition
|
||||||
|
- codehilite
|
||||||
|
- footnotes
|
||||||
|
- toc(permalink=true)
|
||||||
|
- pymdownx.arithmatex
|
||||||
|
- pymdownx.betterem(smart_enable=all)
|
||||||
|
- pymdownx.caret
|
||||||
|
- pymdownx.critic
|
||||||
|
- pymdownx.emoji:
|
||||||
|
emoji_generator: !!python/name:pymdownx.emoji.to_svg
|
||||||
|
- pymdownx.inlinehilite
|
||||||
|
- pymdownx.magiclink
|
||||||
|
- pymdownx.mark
|
||||||
|
- pymdownx.smartsymbols
|
||||||
|
- pymdownx.superfences
|
||||||
|
- pymdownx.tasklist(custom_checkbox=true)
|
||||||
|
- pymdownx.tilde
|
||||||
|
pages:
|
||||||
|
- Home: 'index.md'
|
||||||
|
- 'Concepts': 'concepts.md'
|
||||||
|
- 'Bare-Metal': 'bare-metal.md'
|
||||||
|
- 'Digital Ocean': 'digital-ocean.md'
|
||||||
|
- 'Google Cloud': 'google-cloud.md'
|
||||||
|
- 'Addons':
|
||||||
|
- 'Overview': 'addons/overview.md'
|
||||||
|
- 'Ingress Controller': 'addons/ingress.md'
|
||||||
|
- 'Network Policy': 'addons/calico.md'
|
||||||
|
- 'Heapster': 'addons/heapster.md'
|
||||||
|
- 'Dashboard': 'addons/dashboard.md'
|
||||||
|
- 'CLUO': 'addons/cluo.md'
|
||||||
|
- 'FAQ': 'faq.md'
|
||||||
|
- 'Advanced':
|
||||||
|
- 'Customization': 'advanced/customization.md'
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mkdocs==0.16.3
|
||||||
|
mkdocs-material==1.8.0
|
||||||
|
pygments==2.2.0
|
||||||
|
pymdown-extensions==3.5
|
Reference in New Issue
Block a user