Commit 2f0ace20 authored by Jerzy Szczepkowski's avatar Jerzy Szczepkowski

Merge pull request #12693 from mikedanese/remove-contrib

remove most of contrib as it's moving to a seperate repo
parents 8dcbebae 00cf4c9c
# Kubernetes Contrib is moving!
Checkout https://github.com/kubernetes/contrib for anything that is missing here!
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/contrib/README.md?pixel)]()
myinventory
inventory
*.swp
# Kubernetes Ansible
This playbook and set of roles set up a Kubernetes cluster onto machines. They
can be real hardware, VMs, things in a public cloud, etc. Anything that you can connect to via SSH.
## Before starting
* Record the IP address/hostname of which machine you want to be your master (only support a single master)
* Record the IP address/hostname of the machine you want to be your etcd server (often same as master, only one)
* Record the IP addresses/hostname of the machines you want to be your nodes. (the master can also be a node)
* Make sure your ansible running machine has ansible 1.9 and python-netaddr installed.
## Setup
### Configure inventory
Add the system information gathered above into the 'inventory' file, or create a new inventory file for the cluster.
### Configure Cluster options
Look though all of the options in `group_vars/all.yml` and
set the variables to reflect your needs. The options are described there
in full detail.
## Running the playbook
After going through the setup, run the setup script provided:
`$ ./setup.sh`
You may override the inventory file by doing:
`INVENTORY=myinventory ./setup.sh`
In general this will work on very recent Fedora, rawhide or F21. Future work to
support RHEL7, CentOS, and possible other distros should be forthcoming.
### Targeted runs
You can just setup certain parts instead of doing it all.
#### etcd
`$ ./setup.sh --tags=etcd`
#### Kubernetes master
`$ ./setup.sh --tags=masters`
#### kubernetes nodes
`$ ./setup.sh --tags=nodes`
### flannel
`$ ./setup.sh --tags=flannel`
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/contrib/ansible/README.md?pixel)]()
---
# This playbook deploys a kubernetes cluster
# with the default addons.
- hosts: all
gather_facts: false
sudo: yes
roles:
- pre-ansible
tags:
- pre-ansible
# Install etcd
- hosts: etcd
sudo: yes
roles:
- etcd
tags:
- etcd
# install flannel
- hosts:
- etcd
- masters
- nodes
sudo: yes
roles:
- flannel
tags:
- flannel
# install kube master services
- hosts: masters
sudo: yes
roles:
- master
tags:
- masters
# launch addons, like dns
- hosts: masters
sudo: yes
roles:
- kubernetes-addons
tags:
- addons
- dns
# install kubernetes on the nodes
- hosts: nodes
sudo: yes
roles:
- node
tags:
- nodes
# This value determines how kubernetes binaries, config files, and service
# files are loaded onto the target machines. The following are the only
# valid options:
#
# localBuild - requires make release to have been run to build local binaries
# packageManager - will install packages from your distribution using yum/dnf/apt
source_type: localBuild
# will be used as the Internal dns domain name if DNS is enabled. Services
# will be discoverable under <service-name>.<namespace>.svc.<domainname>, e.g.
# myservice.default.svc.cluster.local
cluster_name: cluster.local
# Account name of remote user. Ansible will use this user account to ssh into
# the managed machines. The user must be able to use sudo without asking
# for password unless ansible_sudo_pass is set
#ansible_ssh_user: root
# password for the ansible_ssh_user. If this is unset you will need to set up
# ssh keys so a password is not needed.
#ansible_ssh_pass: password
# If a password is needed to sudo to root that password must be set here
#ansible_sudo_pass: password
# A list of insecure registrys you night need to define
insecure_registrys:
# - "gcr.io"
# If you need a proxy for the docker daemon define these here
#http_proxy: "http://proxy.example.com:3128"
#https_proxy: "http://proxy.example.com:3128"
# Kubernetes internal network for services.
# Kubernetes services will get fake IP addresses from this range.
# This range must not conflict with anything in your infrastructure. These
# addresses do not need to be routable and must just be an unused block of space.
kube_service_addresses: 10.254.0.0/16
# Flannel internal network (optional). When flannel is used, it will assign IP
# addresses from this range to individual pods.
# This network must be unused in your network infrastructure!
flannel_subnet: 172.16.0.0
# Flannel internal network total size (optional). This is the prefix of the
# entire flannel overlay network. So the entirety of 172.16.0.0/12 must be
# unused in your environment.
flannel_prefix: 12
# Flannel internal network (optional). This is the size allocation that flannel
# will give to each node on your network. With these defaults you should have
# room for 4096 nodes with 254 pods per node.
flannel_host_prefix: 24
# Set to false to disable logging with elasticsearch
cluster_logging: true
# Turn to false to disable cluster monitoring with heapster and influxdb
cluster_monitoring: true
# Turn to false to disable the kube-ui addon for this cluster
kube-ui: false
# Turn this varable to 'false' to disable whole DNS configuration.
dns_setup: true
# How many replicas in the Replication Controller
dns_replicas: 1
# There are other variable in roles/kubernetes/defaults/main.yml but changing
# them comes with a much higher risk to your cluster. So proceed over there
# with caution.
[masters]
kube-master-test-[1:3].example.com
[etcd:children]
masters
[nodes]
kube-minion-test-[1:2].example.com
[masters]
kube-master-test-01.example.com
[etcd]
kube-master-test-01.example.com
[nodes]
kube-minion-test-01.example.com
kube-minion-test-02.example.com
[virt7-docker-common-candidate]
name=virt7-docker-common-candidate
baseurl=http://cbs.centos.org/repos/virt7-docker-common-candidate/x86_64/os/
enabled=0
gpgcheck=0
[virt7-testing]
name=virt7-testing
baseurl=http://cbs.centos.org/repos/virt7-testing/x86_64/os/
enabled=0
gpgcheck=0
---
- name: CentOS | Install Testing centos7 repo
copy: src=virt7-testing.repo dest=/etc/yum.repos.d/virt7-testing.repo
- name: CentOS | Install docker-common-candidate centos7 repo
copy: src=virt7-docker-common-candidate.repo dest=/etc/yum.repos.d/virt7-docker-common-candidate.repo
---
- name: Generic | Install Firewalld Python2 Package
action: "{{ ansible_pkg_mgr }}"
args:
name: python-firewall
state: latest
when: ansible_distribution_major_version|int >= 22
---
- name: Determine if Atomic
stat: path=/run/ostree-booted
register: s
changed_when: false
- name: Init the is_atomic fact
set_fact:
is_atomic: false
- name: Set the is_atomic fact
set_fact:
is_atomic: true
when: s.stat.exists
- name: Determine if has rpm
stat: path=/usr/bin/rpm
register: s
changed_when: false
- name: Init the has_rpm fact
set_fact:
has_rpm: false
- name: Set the has_rpm fact
set_fact:
has_rpm: true
when: s.stat.exists
- name: Init the has_firewalld fact
set_fact:
has_firewalld: false
- name: Init the has_iptables fact
set_fact:
has_iptables: false
# collect information about what packages are installed
- include: rpm.yml
when: has_rpm
- include: centos.yml
when: ansible_distribution == "CentOS"
- include: fedora-install.yml
when: not is_atomic and ansible_distribution == "Fedora"
---
- name: RPM | Determine if firewalld installed
command: "rpm -q firewalld"
register: s
changed_when: false
failed_when: false
- name: Set the has_firewalld fact
set_fact:
has_firewalld: true
when: s.rc == 0
- name: Determine if iptables-services installed
command: "rpm -q iptables-services"
register: s
changed_when: false
failed_when: false
- name: Set the has_iptables fact
set_fact:
has_iptables: true
when: s.rc == 0
no_proxy: "localhost,127.0.0.0/8,::1,/var/run/docker.sock"
\ No newline at end of file
---
- name: restart docker
service: name=docker state=restarted
---
- name: DEBIAN | Make sure this is stretch or sid, jessies does not have docker
fail: msg="Docker.io only available in sid and stretch, https://wiki.debian.org/Docker"
when: ansible_lsb.codename != "stretch" and ansible_lsb.codename != "sid"
- name: DEBIAN | Install Docker
action: "{{ ansible_pkg_mgr }}"
args:
name: docker.io
state: latest
---
- name: Generic | Install Docker
action: "{{ ansible_pkg_mgr }}"
args:
name: docker
state: latest
when: not is_atomic
---
- include: debian-install.yml
when: ansible_distribution == "Debian"
- include: generic-install.yml
when: ansible_distribution != "Debian"
- name: Set docker config file directory
set_fact:
docker_config_dir: "/etc/sysconfig"
- name: Override docker config file directory for Debian
set_fact:
docker_config_dir: "/etc/default"
when: ansible_distribution == "Debian"
- name: Verify docker config files exists
file: path={{ docker_config_dir }}/{{ item }} state=touch
changed_when: false
with_items:
- docker
- docker-network
- name: Turn down docker logging
lineinfile: dest={{ docker_config_dir }}/docker regexp=^OPTIONS= line=OPTIONS="--selinux-enabled --log-level=warn"
notify:
- restart docker
- name: Install http_proxy into docker-network
lineinfile: dest={{ docker_config_dir }}/docker-network regexp=^HTTP_PROXY= line=HTTP_PROXY="{{ http_proxy }}"
when: http_proxy is defined
notify:
- restart docker
- name: Install https_proxy into docker-network
lineinfile: dest={{ docker_config_dir }}/docker-network regexp=^HTTPS_PROXY= line=HTTPS_PROXY="{{ https_proxy }}"
when: https_proxy is defined
notify:
- restart docker
- name: Install no-proxy into docker-network
lineinfile: dest={{ docker_config_dir }}/docker-network regexp=^NO_PROXY= line=NO_PROXY="{{ no_proxy }}"
when: no_proxy is defined
notify:
- restart docker
- name: Add any insecure registrys to docker config
lineinfile: dest={{ docker_config_dir }}/docker regexp=^INSECURE_REGISTRY= line=INSECURE_REGISTRY='{% for reg in insecure_registrys %}--insecure-registry="{{ reg }}" {% endfor %}'
when: insecure_registrys is defined and insecure_registrys > 0
notify:
- restart docker
- name: Enable Docker
service: name=docker enabled=yes state=started
Role Name
=========
Configures an etcd cluster for an arbitrary number of hosts
Role Variables
--------------
TODO
Dependencies
------------
None
Example Playbook
----------------
- hosts: etcd
roles:
- { etcd }
License
-------
MIT
Author Information
------------------
Scott Dodson <sdodson@redhat.com>, Tim St. Clair <tstclair@redhat.com>
Adapted from https://github.com/retr0h/ansible-etcd. We
should at some point submit a PR to merge this with that module.
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/contrib/ansible/roles/etcd/README.md?pixel)]()
---
etcd_client_port: 2379
etcd_peer_port: 2380
etcd_peers_group: etcd
etcd_url_scheme: http
etcd_peer_url_scheme: http
etcd_conf_dir: /etc/etcd
etcd_script_dir: /usr/libexec/etcd
etcd_ca_file: "{{ etcd_conf_dir }}/ca.crt"
etcd_cert_file: "{{ etcd_conf_dir }}/server.crt"
etcd_key_file: "{{ etcd_conf_dir }}/server.key"
etcd_peer_ca_file: "{{ etcd_conf_dir }}/ca.crt"
etcd_peer_cert_file: "{{ etcd_conf_dir }}/peer.crt"
etcd_peer_key_file: "{{ etcd_conf_dir }}/peer.key"
etcd_initial_cluster_state: new
etcd_initial_cluster_token: etcd-k8-cluster
etcd_initial_advertise_peer_urls: "{{ etcd_peer_url_scheme }}://{{ ansible_fqdn }}:{{ etcd_peer_port }}"
etcd_listen_peer_urls: "{{ etcd_peer_url_scheme }}://{{ ansible_fqdn }}:{{ etcd_peer_port }}"
etcd_advertise_client_urls: "{{ etcd_url_scheme }}://{{ ansible_fqdn }}:{{ etcd_client_port }}"
etcd_listen_client_urls: "{{ etcd_url_scheme }}://{{ ansible_fqdn }}:{{ etcd_client_port }}"
etcd_data_dir: /var/lib/etcd
[Unit]
Description=Etcd Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/var/lib/etcd/
EnvironmentFile=-/etc/etcd/etcd.conf
User=etcd
ExecStart=/usr/bin/etcd
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
---
- name: reload systemd
command: systemctl --system daemon-reload
- name: restart etcd
service: name=etcd state=restarted
when: etcd_started.changed == false
- name: Save iptables rules
command: service iptables save
---
dependencies:
- { role: common }
---
- name: Open firewalld port for etcd
firewalld: port={{ item }}/tcp permanent=false state=enabled
# in case this is also a node where firewalld turned off
ignore_errors: yes
with_items:
- 2379
- 2380
- name: Save firewalld port for etcd
firewalld: port={{ item }}/tcp permanent=true state=enabled
# in case this is also a node where firewalld turned off
ignore_errors: yes
with_items:
- 2379
- 2380
---
- name: Get iptables rules
command: iptables -L
register: iptablesrules
always_run: yes
- name: Enable iptables at boot
service: name=iptables enabled=yes state=started
- name: Open etcd client port with iptables
command: /sbin/iptables -I INPUT 1 -p tcp --dport {{ item }} -j ACCEPT -m comment --comment "etcd"
when: "'etcd' not in iptablesrules.stdout"
notify:
- Save iptables rules
with_items:
- 2379
- 2380
---
- name: Install etcd
action: "{{ ansible_pkg_mgr }}"
args:
name: etcd
state: latest
notify:
- restart etcd
when: not is_atomic
- name: Write etcd config file
template: src=etcd.conf.j2 dest=/etc/etcd/etcd.conf
notify:
- restart etcd
- name: Write etcd systemd unit file for Debian
copy: src=etcd.service dest=/etc/systemd/system
notify:
- reload systemd
- restart etcd
when: ansible_distribution == "Debian"
- name: Enable etcd
service: name=etcd enabled=yes
- name: Start etcd
service: name=etcd state=started
register: etcd_started
- include: firewalld.yml
when: has_firewalld
- include: iptables.yml
when: not has_firewalld and has_iptables
{% macro initial_cluster() -%}
{% for host in groups[etcd_peers_group] -%}
{% if loop.last -%}
{{ hostvars[host]['ansible_hostname'] }}={{ etcd_peer_url_scheme }}://{{ hostvars[host]['ansible_fqdn'] }}:{{ etcd_peer_port }}
{%- else -%}
{{ hostvars[host]['ansible_hostname'] }}={{ etcd_peer_url_scheme }}://{{ hostvars[host]['ansible_fqdn'] }}:{{ etcd_peer_port }},
{%- endif -%}
{% endfor -%}
{% endmacro -%}
{% if groups[etcd_peers_group] and groups[etcd_peers_group] | length > 1 %}
ETCD_NAME={{ ansible_hostname }}
ETCD_LISTEN_PEER_URLS={{ etcd_listen_peer_urls }}
{% else %}
ETCD_NAME=default
{% endif %}
ETCD_DATA_DIR={{ etcd_data_dir }}
#ETCD_SNAPSHOT_COUNTER="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
ETCD_LISTEN_CLIENT_URLS={{ etcd_listen_client_urls }}
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
{% if groups[etcd_peers_group] and groups[etcd_peers_group] | length > 1 %}
#[cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS={{ etcd_initial_advertise_peer_urls }}
ETCD_INITIAL_CLUSTER={{ initial_cluster() }}
ETCD_INITIAL_CLUSTER_STATE={{ etcd_initial_cluster_state }}
ETCD_INITIAL_CLUSTER_TOKEN={{ etcd_initial_cluster_token }}
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY=""
{% endif %}
ETCD_ADVERTISE_CLIENT_URLS={{ etcd_advertise_client_urls }}
#[proxy]
#ETCD_PROXY="off"
#[security]
{% if etcd_url_scheme == 'https' -%}
ETCD_CA_FILE={{ etcd_ca_file }}
ETCD_CERT_FILE={{ etcd_cert_file }}
ETCD_KEY_FILE={{ etcd_key_file }}
{% endif -%}
{% if etcd_peer_url_scheme == 'https' -%}
ETCD_PEER_CA_FILE={{ etcd_peer_ca_file }}
ETCD_PEER_CERT_FILE={{ etcd_peer_cert_file }}
ETCD_PEER_KEY_FILE={{ etcd_peer_key_file }}
{% endif -%}
---
- name: restart flannel
service: name=flanneld state=restarted
notify:
- stop docker
- delete docker0
- start docker
when: inventory_hostname in groups['nodes']
- name: stop docker
service: name=docker state=stopped
- name: delete docker0
command: ip link delete docker0
ignore_errors: yes
- name: start docker
service: name=docker state=started
# This might fail if docker isn't installed yet
ignore_errors: yes
---
dependencies:
- { role: common }
---
- name: Install flannel
action: "{{ ansible_pkg_mgr }}"
args:
name: flannel
state: latest
when: not is_atomic
- name: Install Flannel config file
template: src=flanneld.j2 dest=/etc/sysconfig/flanneld
notify:
- restart flannel
- name: Launch Flannel
service: name=flanneld state=started enabled=yes
notify:
- restart flannel
---
- name: Set facts about etcdctl command
set_fact:
peers: "{% for hostname in groups['etcd'] %}http://{{ hostname }}:2379{% if not loop.last %},{% endif %}{% endfor %}"
conf_file: "/tmp/flannel-conf.json"
conf_loc: "/{{ cluster_name }}/network/config"
run_once: true
delegate_to: "{{ groups['etcd'][0] }}"
- name: Create flannel config file to go in etcd
template: src=flannel-conf.json.j2 dest={{ conf_file }}
run_once: true
delegate_to: "{{ groups['etcd'][0] }}"
- name: Load the flannel config file into etcd
shell: "/usr/bin/etcdctl --no-sync --peers={{ peers }} set {{ conf_loc }} < {{ conf_file }}"
run_once: true
delegate_to: "{{ groups['etcd'][0] }}"
- name: Clean up the flannel config file
file: path=/tmp/flannel-config.json state=absent
run_once: true
delegate_to: "{{ groups['etcd'][0] }}"
---
- include: config.yml
- include: client.yml
when: inventory_hostname in groups['masters'] + groups['nodes']
{ "Network": "{{ flannel_subnet }}/{{ flannel_prefix }}", "SubnetLen": {{ flannel_host_prefix }}, "Backend": { "Type": "vxlan" } }
# Flanneld configuration options
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD="{% for node in groups['etcd'] %}http://{{ node }}:2379{% if not loop.last %},{% endif %}{% endfor %}"
# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_KEY="/{{ cluster_name }}/network"
# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
kube_addons_dir: "{{ kube_config_dir }}/addons"
local_temp_addon_dir: /tmp/kubernetes/addons
#!/bin/bash
# Copyright 2014 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The business logic for whether a given object should be created
# was already enforced by salt, and /etc/kubernetes/addons is the
# managed result is of that. Start everything below that directory.
KUBECTL=${KUBECTL_BIN:-/usr/local/bin/kubectl}
ADDON_CHECK_INTERVAL_SEC=${TEST_ADDON_CHECK_INTERVAL_SEC:-600}
SYSTEM_NAMESPACE=kube-system
token_dir=${TOKEN_DIR:-/srv/kubernetes}
function create-kubeconfig-secret() {
local -r token=$1
local -r username=$2
local -r server=$3
local -r safe_username=$(tr -s ':_' '--' <<< "${username}")
# Make a kubeconfig file with the token.
if [[ ! -z "${CA_CERT:-}" ]]; then
# If the CA cert is available, put it into the secret rather than using
# insecure-skip-tls-verify.
read -r -d '' kubeconfig <<EOF
apiVersion: v1
kind: Config
users:
- name: ${username}
user:
token: ${token}
clusters:
- name: local
cluster:
server: ${server}
certificate-authority-data: ${CA_CERT}
contexts:
- context:
cluster: local
user: ${username}
namespace: ${SYSTEM_NAMESPACE}
name: service-account-context
current-context: service-account-context
EOF
else
read -r -d '' kubeconfig <<EOF
apiVersion: v1
kind: Config
users:
- name: ${username}
user:
token: ${token}
clusters:
- name: local
cluster:
server: ${server}
insecure-skip-tls-verify: true
contexts:
- context:
cluster: local
user: ${username}
namespace: ${SYSTEM_NAMESPACE}
name: service-account-context
current-context: service-account-context
EOF
fi
local -r kubeconfig_base64=$(echo "${kubeconfig}" | base64 -w0)
read -r -d '' secretyaml <<EOF
apiVersion: v1
data:
kubeconfig: ${kubeconfig_base64}
kind: Secret
metadata:
name: token-${safe_username}
type: Opaque
EOF
create-resource-from-string "${secretyaml}" 100 10 "Secret-for-token-for-user-${username}" "${SYSTEM_NAMESPACE}" &
}
# $1 filename of addon to start.
# $2 count of tries to start the addon.
# $3 delay in seconds between two consecutive tries
# $4 namespace
function start_addon() {
local -r addon_filename=$1;
local -r tries=$2;
local -r delay=$3;
local -r namespace=$4
create-resource-from-string "$(cat ${addon_filename})" "${tries}" "${delay}" "${addon_filename}" "${namespace}"
}
# $1 string with json or yaml.
# $2 count of tries to start the addon.
# $3 delay in seconds between two consecutive tries
# $4 name of this object to use when logging about it.
# $5 namespace for this object
function create-resource-from-string() {
local -r config_string=$1;
local tries=$2;
local -r delay=$3;
local -r config_name=$4;
local -r namespace=$5;
while [ ${tries} -gt 0 ]; do
echo "${config_string}" | ${KUBECTL} --namespace="${namespace}" create -f - && \
echo "== Successfully started ${config_name} in namespace ${namespace} at $(date -Is)" && \
return 0;
let tries=tries-1;
echo "== Failed to start ${config_name} in namespace ${namespace} at $(date -Is). ${tries} tries remaining. =="
sleep ${delay};
done
return 1;
}
# The business logic for whether a given object should be created
# was already enforced by salt, and /etc/kubernetes/addons is the
# managed result is of that. Start everything below that directory.
echo "== Kubernetes addon manager started at $(date -Is) with ADDON_CHECK_INTERVAL_SEC=${ADDON_CHECK_INTERVAL_SEC} =="
# Load the kube-env, which has all the environment variables we care
# about, in a flat yaml format.
kube_env_yaml="/var/cache/kubernetes-install/kube_env.yaml"
if [ ! -e "${kubelet_kubeconfig_file}" ]; then
eval $(python -c '''
import pipes,sys,yaml
for k,v in yaml.load(sys.stdin).iteritems():
print "readonly {var}={value}".format(var = k, value = pipes.quote(str(v)))
''' < "${kube_env_yaml}")
fi
# Create the namespace that will be used to host the cluster-level add-ons.
start_addon /etc/kubernetes/addons/namespace.yaml 100 10 "" &
# Wait for the default service account to be created in the kube-system namespace.
token_found=""
while [ -z "${token_found}" ]; do
sleep .5
token_found=$(${KUBECTL} get --namespace="${SYSTEM_NAMESPACE}" serviceaccount default -o template -t "{{with index .secrets 0}}{{.name}}{{end}}" || true)
done
echo "== default service account in the ${SYSTEM_NAMESPACE} namespace has token ${token_found} =="
# Generate secrets for "internal service accounts".
# TODO(etune): move to a completely yaml/object based
# workflow so that service accounts can be created
# at the same time as the services that use them.
# NOTE: needs to run as root to read this file.
# Read each line in the csv file of tokens.
# Expect errors when the script is started again.
# NOTE: secrets are created asynchronously, in background.
while read line; do
# Split each line into the token and username.
IFS=',' read -a parts <<< "${line}"
token=${parts[0]}
username=${parts[1]}
# DNS is special, since it's necessary for cluster bootstrapping.
if [[ "${username}" == "system:dns" ]] && [[ ! -z "${KUBERNETES_MASTER_NAME:-}" ]]; then
create-kubeconfig-secret "${token}" "${username}" "https://${KUBERNETES_MASTER_NAME}"
else
# Set the server to https://kubernetes. Pods/components that
# do not have DNS available will have to override the server.
create-kubeconfig-secret "${token}" "${username}" "https://kubernetes.default"
fi
done < "${token_dir}/known_tokens.csv"
# Create admission_control objects if defined before any other addon services. If the limits
# are defined in a namespace other than default, we should still create the limits for the
# default namespace.
for obj in $(find /etc/kubernetes/admission-controls \( -name \*.yaml -o -name \*.json \)); do
start_addon ${obj} 100 10 default &
echo "++ obj ${obj} is created ++"
done
# Check if the configuration has changed recently - in case the user
# created/updated/deleted the files on the master.
while true; do
start_sec=$(date +"%s")
#kube-addon-update.sh must be deployed in the same directory as this file
`dirname $0`/kube-addon-update.sh /etc/kubernetes/addons ${ADDON_CHECK_INTERVAL_SEC}
end_sec=$(date +"%s")
len_sec=$((${end_sec}-${start_sec}))
# subtract the time passed from the sleep time
if [[ ${len_sec} -lt ${ADDON_CHECK_INTERVAL_SEC} ]]; then
sleep_time=$((${ADDON_CHECK_INTERVAL_SEC}-${len_sec}))
sleep ${sleep_time}
fi
done
---
- name: reload and restart kube-addons
command: systemctl --system daemon-reload
notify:
- restart kube-addons
- name: restart kube-addons
service: name=kube-addons state=restarted
---
- name: LOGGING | Assures {{ kube_addons_dir }}/cluster-logging dir exists
file: path={{ kube_addons_dir }}/cluster-logging state=directory
- name: LOGGING | Download logging files from Kubernetes repo
get_url:
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/addons/fluentd-elasticsearch/{{ item }}
dest="{{ kube_addons_dir }}/cluster-logging/"
force=yes
with_items:
- es-controller.yaml
- es-service.yaml
- kibana-controller.yaml
- kibana-service.yaml
---
- name: MONITORING | Assures {{ kube_config_dir }}/addons/cluster-monitoring dir exists
file: path={{ kube_addons_dir }}/cluster-monitoring state=directory
- name: MONITORING | Download monitoring files from Kubernetes repo
get_url:
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/addons/cluster-monitoring/influxdb/{{ item }}
dest="{{ kube_addons_dir }}/cluster-monitoring/"
force=yes
with_items:
- grafana-service.yaml
- heapster-controller.yaml
- heapster-service.yaml
- influxdb-grafana-controller.yaml
- influxdb-service.yaml
---
- name: DNS | Assures {{ kube_addons_dir }}/dns dir exists
file: path={{ kube_addons_dir }}/dns state=directory
- name: DNS | Assures local dns addon dir exists
local_action: file
path={{ local_temp_addon_dir }}/dns
state=directory
sudo: no
- name: DNS | Download skydns-rc.yaml file from Kubernetes repo
local_action: get_url
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/addons/dns/skydns-rc.yaml.in
dest="{{ local_temp_addon_dir }}/dns/skydns-rc.yaml.j2"
force=yes
sudo: no
- name: DNS | Convert pillar vars to ansible vars for skydns-rc.yaml
local_action: replace
dest="{{ local_temp_addon_dir }}/dns/skydns-rc.yaml.j2"
regexp="pillar\[\'(\w*)\'\]"
replace="\1"
sudo: no
- name: DNS | Install Template from converted saltfile
template:
args:
src: "{{ local_temp_addon_dir }}/dns/skydns-rc.yaml.j2"
dest: "{{ kube_addons_dir }}/dns/skydns-rc.yaml"
mode: 0755
owner: root
group: root
- name: DNS | Download skydns-svc.yaml file from Kubernetes repo
local_action: get_url
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/addons/dns/skydns-svc.yaml.in
dest="{{ local_temp_addon_dir }}/dns/skydns-svc.yaml.j2"
force=yes
sudo: no
- name: DNS | Convert pillar vars to ansible vars for skydns-rc.yaml
local_action: replace
dest="{{ local_temp_addon_dir }}/dns/skydns-svc.yaml.j2"
regexp="pillar\[\'(\w*)\'\]"
replace="\1"
sudo: no
- name: DNS | Install Template from converted saltfile
template:
args:
src: "{{ local_temp_addon_dir }}/dns/skydns-svc.yaml.j2"
dest: "{{ kube_addons_dir }}/dns/skydns-svc.yaml"
mode: 0755
owner: root
group: root
- name: Set pyyaml package name
set_fact:
pyyaml_name: python-yaml
- name: Overwrite pyyaml package name for non-Debian
set_fact:
pyyaml_name: PyYAML
when: ansible_distribution != "Debian"
- name: Install PyYAML for non-debian
action: "{{ ansible_pkg_mgr }}"
args:
name: "{{ pyyaml_name }}"
state: latest
---
- name: KUBE-UI | Assures {{ kube_addons_dir }}/kube-ui dir exists
file: path={{ kube_addons_dir }}/kube-ui state=directory
- name: KUBE-UI | Download kube-ui files from Kubernetes repo
get_url:
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/addons/kube-ui/{{ item }}
dest="{{ kube_addons_dir }}/kube-ui/"
force=yes
with_items:
- kube-ui-rc.yaml
- kube-ui-svc.yaml
---
- name: Assures addons dir exists
file: path={{ kube_addons_dir }} state=directory
- include: generic-install.yml
when: not is_atomic
- name: Assures local addon dir exists
local_action: file
path={{ local_temp_addon_dir }}
state=directory
sudo: no
- name: Make sure the system services namespace exists
get_url:
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/saltbase/salt/kube-addons/namespace.yaml
dest="{{ kube_config_dir }}/addons/"
force=yes
- include: dns.yml
when: dns_setup
- include: cluster-monitoring.yml
when: cluster_monitoring
- include: cluster-logging.yml
when: cluster_logging
- include: kube-ui.yml
when: kube-ui
#- name: Get kube-addons script from Kubernetes
# get_url:
# url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/saltbase/salt/kube-addons/kube-addons.sh
# dest={{ kube_script_dir }}/kube-addons.sh mode=0755
# force=yes
#- name: Get kube-addon-update script from Kubernetes
# get_url:
# url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/saltbase/salt/kube-addons/kube-addon-update.sh
# dest={{ kube_script_dir }}/kube-addon-update.sh mode=0755
# force=yes
- name: HACK | copy local kube-addons.sh
copy: src=kube-addons.sh dest={{ kube_script_dir }}/kube-addons.sh mode=0755
- name: HACK | copy local kube-addon-update.sh
copy: src=kube-addon-update.sh dest={{ kube_script_dir }}/kube-addon-update.sh mode=0755
- name: Run kube-gen-token script to create {{ kube_token_dir }}/known_tokens.csv
command: "{{ kube_script_dir }}/kube-gen-token.sh {{ item }}"
environment:
TOKEN_DIR: "{{ kube_token_dir }}"
with_items:
- "system:dns"
- "system:monitoring"
- "system:logging"
register: gentoken
changed_when: "'Added' in gentoken.stdout"
notify:
- restart apiserver
- restart kube-addons
- name: Install kube-addons service
template: src=kube-addons.service.j2 dest=/etc/systemd/system/kube-addons.service
notify:
- reload and restart kube-addons
- name: Enable and start kube addons
service: name=kube-addons.service enabled=yes state=started
[Unit]
Description=Kubernetes Addon Object Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
[Service]
Environment="TOKEN_DIR={{ kube_token_dir }}"
Environment="KUBECTL_BIN=/usr/bin/kubectl"
Environment="KUBERNETES_MASTER_NAME={{ groups['masters'][0] }}"
ExecStart={{ kube_script_dir }}/kube-addons.sh
[Install]
WantedBy=multi-user.target
# The port that the Kubernetes apiserver component listens on.
kube_master_api_port: 443
# This directory is where all the additional scripts go
# that Kubernetes normally puts in /srv/kubernetes.
# This puts them in a sane location
kube_script_dir: /usr/libexec/kubernetes
# This directory is where all the additional config stuff goes
# the kubernetes normally puts in /srv/kubernets.
# This puts them in a sane location.
# Editting this value will almost surely break something. Don't
# change it. Things like the systemd scripts are hard coded to
# look in here. Don't do it.
kube_config_dir: /etc/kubernetes
# This is where all the cert scripts and certs will be located
kube_cert_dir: "{{ kube_config_dir }}/certs"
# This is where all of the bearer tokens will be stored
kube_token_dir: "{{ kube_config_dir }}/tokens"
# This is where you can drop yaml/json files and the kubelet will run those
# pods on startup
kube_manifest_dir: "{{ kube_config_dir }}/manifests"
# This is the group that the cert creation scripts chgrp the
# cert files to. Not really changeable...
kube_cert_group: kube-cert
# Internal DNS domain name.
# This domain must not be used in your network. Services will be discoverable
# under <service-name>.<namespace>.<domainname>, e.g.
# myservice.default.svc.cluster.local
dns_domain: "{{ cluster_name }}"
# IP address of the DNS server.
# Kubernetes will create a pod with several containers, serving as the DNS
# server and expose it under this IP address. The IP address must be from
# the range specified as kube_service_addresses. This magic will actually
# pick the 10th ip address in the kube_service_addresses range and use that.
dns_server: "{{ kube_service_addresses|ipaddr('net')|ipaddr(10)|ipaddr('address') }}"
#!/bin/bash
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
token_dir=${TOKEN_DIR:-/var/srv/kubernetes}
token_file="${token_dir}/known_tokens.csv"
create_accounts=($@)
touch "${token_file}"
for account in "${create_accounts[@]}"; do
if grep ",${account}," "${token_file}" ; then
continue
fi
token=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d "=+/" | dd bs=32 count=1 2>/dev/null)
echo "${token},${account},${account}" >> "${token_file}"
echo "${token}" > "${token_dir}/${account}.token"
echo "Added ${account}"
done
#!/bin/bash
# Copyright 2014 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
# Caller should set in the ev:
# MASTER_IP - this may be an ip or things like "_use_gce_external_ip_"
# MASTER_NAME - DNS name for the master
# DNS_DOMAIN - which will be passed to minions in --cluster-domain
# SERVICE_CLUSTER_IP_RANGE - where all service IPs are allocated
# Also the following will be respected
# CERT_DIR - where to place the finished certs
# CERT_GROUP - who the group owner of the cert files should be
cert_ip="${MASTER_IP:="${1}"}"
master_name="${MASTER_NAME:="kubernetes"}"
service_range="${SERVICE_CLUSTER_IP_RANGE:="10.0.0.0/16"}"
dns_domain="${DNS_DOMAIN:="cluster.local"}"
cert_dir="${CERT_DIR:-"/srv/kubernetes"}"
cert_group="${CERT_GROUP:="kube-cert"}"
# The following certificate pairs are created:
#
# - ca (the cluster's certificate authority)
# - server
# - kubelet
# - kubecfg (for kubectl)
#
# TODO(roberthbailey): Replace easyrsa with a simple Go program to generate
# the certs that we need.
# TODO: Add support for discovery on other providers?
if [ "$cert_ip" == "_use_gce_external_ip_" ]; then
cert_ip=$(curl -s -H Metadata-Flavor:Google http://metadata.google.internal./computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip)
fi
if [ "$cert_ip" == "_use_aws_external_ip_" ]; then
cert_ip=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4)
fi
if [ "$cert_ip" == "_use_azure_dns_name_" ]; then
cert_ip=$(uname -n | awk -F. '{ print $2 }').cloudapp.net
fi
tmpdir=$(mktemp -d --tmpdir kubernetes_cacert.XXXXXX)
trap 'rm -rf "${tmpdir}"' EXIT
cd "${tmpdir}"
# TODO: For now, this is a patched tool that makes subject-alt-name work, when
# the fix is upstream move back to the upstream easyrsa. This is cached in GCS
# but is originally taken from:
# https://github.com/brendandburns/easy-rsa/archive/master.tar.gz
#
# To update, do the following:
# curl -o easy-rsa.tar.gz https://github.com/brendandburns/easy-rsa/archive/master.tar.gz
# gsutil cp easy-rsa.tar.gz gs://kubernetes-release/easy-rsa/easy-rsa.tar.gz
# gsutil acl ch -R -g all:R gs://kubernetes-release/easy-rsa/easy-rsa.tar.gz
#
# Due to GCS caching of public objects, it may take time for this to be widely
# distributed.
# Calculate the first ip address in the service range
octets=($(echo "${service_range}" | sed -e 's|/.*||' -e 's/\./ /g'))
((octets[3]+=1))
service_ip=$(echo "${octets[*]}" | sed 's/ /./g')
# Determine appropriete subject alt names
sans="IP:${cert_ip},IP:${service_ip},DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.${dns_domain},DNS:${master_name}"
curl -L -O https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz > /dev/null 2>&1
tar xzf easy-rsa.tar.gz > /dev/null
cd easy-rsa-master/easyrsa3
(./easyrsa init-pki > /dev/null 2>&1
./easyrsa --batch "--req-cn=${cert_ip}@$(date +%s)" build-ca nopass > /dev/null 2>&1
./easyrsa --subject-alt-name="${sans}" build-server-full "${master_name}" nopass > /dev/null 2>&1
./easyrsa build-client-full kubelet nopass > /dev/null 2>&1
./easyrsa build-client-full kubecfg nopass > /dev/null 2>&1) || {
# If there was an error in the subshell, just die.
# TODO(roberthbailey): add better error handling here
echo "=== Failed to generate certificates: Aborting ==="
exit 2
}
mkdir -p "$cert_dir"
cp -p pki/ca.crt "${cert_dir}/ca.crt"
cp -p "pki/issued/${master_name}.crt" "${cert_dir}/server.crt" > /dev/null 2>&1
cp -p "pki/private/${master_name}.key" "${cert_dir}/server.key" > /dev/null 2>&1
cp -p pki/issued/kubecfg.crt "${cert_dir}/kubecfg.crt"
cp -p pki/private/kubecfg.key "${cert_dir}/kubecfg.key"
cp -p pki/issued/kubelet.crt "${cert_dir}/kubelet.crt"
cp -p pki/private/kubelet.key "${cert_dir}/kubelet.key"
CERTS=("ca.crt" "server.key" "server.crt" "kubelet.key" "kubelet.crt" "kubecfg.key" "kubecfg.crt")
for cert in "${CERTS[@]}"; do
chgrp "${cert_group}" "${cert_dir}/${cert}"
chmod 660 "${cert_dir}/${cert}"
done
---
- name: Fedora | Remove docker window manager on F20
yum: pkg=docker state=absent
when: ansible_distribution_major_version == "20"
---
- name: Install openssl for easy-rsa stuff
action: "{{ ansible_pkg_mgr }}"
args:
name: "{{ item }}"
state: latest
with_items:
- openssl
- curl
when: not is_atomic
#- name: Get create ca cert script from Kubernetes
# get_url:
# url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/saltbase/salt/generate-cert/make-ca-cert.sh
# dest={{ kube_script_dir }}/make-ca-cert.sh mode=0500
# force=yes
- name: HACK | overwrite make-ca-cert.sh from local copy
copy:
src=make-ca-cert.sh
dest={{ kube_script_dir }}
mode=0500
changed_when: false
# FIXME This only generates a cert for one master...
- name: Run create cert script on master
command:
"{{ kube_script_dir }}/make-ca-cert.sh"
args:
creates: "{{ kube_cert_dir }}/server.crt"
environment:
MASTER_IP: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"
MASTER_NAME: "{{ inventory_hostname }}"
DNS_DOMAIN: "{{ dns_domain }}"
SERVICE_CLUSTER_IP_RANGE: "{{ kube_service_addresses }}"
CERT_DIR: "{{ kube_cert_dir }}"
CERT_GROUP: "{{ kube_cert_group }}"
- name: Verify certificate permissions
file:
path={{ item }}
group={{ kube_cert_group }}
owner=kube
mode=0440
with_items:
- "{{ kube_cert_dir }}/ca.crt"
- "{{ kube_cert_dir }}/server.crt"
- "{{ kube_cert_dir }}/server.key"
- "{{ kube_cert_dir }}/kubecfg.crt"
- "{{ kube_cert_dir }}/kubecfg.key"
---
- name: Copy the token gen script
copy:
src=kube-gen-token.sh
dest={{ kube_script_dir }}
mode=u+x
- name: Generate tokens for master components
command: "{{ kube_script_dir }}/kube-gen-token.sh {{ item[0] }}-{{ item[1] }}"
environment:
TOKEN_DIR: "{{ kube_token_dir }}"
with_nested:
- [ "system:controller_manager", "system:scheduler", "system:kubectl" ]
- "{{ groups['masters'] }}"
register: gentoken
changed_when: "'Added' in gentoken.stdout"
notify:
- restart daemons
- name: Generate tokens for node components
command: "{{ kube_script_dir }}/kube-gen-token.sh {{ item[0] }}-{{ item[1] }}"
environment:
TOKEN_DIR: "{{ kube_token_dir }}"
with_nested:
- [ 'system:kubelet', 'system:proxy' ]
- "{{ groups['nodes'] }}"
register: gentoken
changed_when: "'Added' in gentoken.stdout"
notify:
- restart daemons
---
- include: fedora.yml
when: ansible_distribution == "Fedora"
- name: Update {{ kube_script_dir }} if this is atomic
set_fact:
kube_script_dir: "/usr/local/libexec/kubernretes"
when: is_atomic and kube_script_dir == "/usr/libexec/kubernetes"
- name: Create kubernetes config directory
file: path={{ kube_config_dir }} state=directory
- name: Create kubernetes script directory
file: path={{ kube_script_dir }} state=directory
- name: write the global config file
template: src=config.j2 dest={{ kube_config_dir }}/config
notify:
- restart daemons
- include: secrets.yml
tags:
secrets
---
- name: Create system kube-cert groups
group: name={{ kube_cert_group }} state=present system=yes
- name: Create system kube user
user:
name=kube
comment="Kubernetes user"
shell=/sbin/nologin
state=present
system=yes
groups={{ kube_cert_group }}
- name: make sure the certificate directory exits
file:
path={{ kube_cert_dir }}
state=directory
mode=o-rwx
group={{ kube_cert_group }}
- name: make sure the tokens directory exits
file:
path={{ kube_token_dir }}
state=directory
mode=o-rwx
group={{ kube_cert_group }}
- include: gen_certs.yml
when: inventory_hostname == groups['masters'][0]
- name: Read back the CA certificate
slurp:
src: "{{ kube_cert_dir }}/ca.crt"
register: ca_cert
run_once: true
delegate_to: "{{ groups['masters'][0] }}"
- name: Register the CA certificate as a fact so it can be used later
set_fact:
kube_ca_cert: "{{ ca_cert.content|b64decode }}"
- name: Place CA certificate everywhere
copy: content="{{ kube_ca_cert }}" dest="{{ kube_cert_dir }}/ca.crt"
notify:
- restart daemons
- include: gen_tokens.yml
when: inventory_hostname == groups['masters'][0]
###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
# kube-apiserver.service
# kube-controller-manager.service
# kube-scheduler.service
# kubelet.service
# kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"
# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"
# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=true"
# How the replication controller, scheduler, and proxy
KUBE_MASTER="--master=https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}"
---
- name: reload systemd
command: systemctl --system daemon-reload
notify:
- restart daemons
- name: restart daemons
command: /bin/true
notify:
- restart apiserver
- restart controller-manager
- restart scheduler
- name: restart apiserver
service: name=kube-apiserver state=restarted
- name: restart controller-manager
service: name=kube-controller-manager state=restarted
- name: restart scheduler
service: name=kube-scheduler state=restarted
- name: restart iptables
service: name=iptables state=restarted
---
dependencies:
- { role: common }
- { role: kubernetes }
---
- name: Open firewalld port for apiserver
firewalld: port={{ kube_master_api_port }}/tcp permanent=false state=enabled
# in case this is also a node with firewalld turned off
ignore_errors: yes
- name: Save firewalld port for apiserver
firewalld: port={{ kube_master_api_port }}/tcp permanent=true state=enabled
# in case this is also a node with firewalld turned off
ignore_errors: yes
---
- name: Get iptables rules
command: iptables -L
register: iptablesrules
always_run: yes
- name: Open apiserver port with iptables
command: /sbin/iptables -I INPUT 1 -p tcp --dport {{ kube_master_api_port }} -j ACCEPT -m comment --comment "kube-apiserver"
when: "'kube-apiserver' not in iptablesrules.stdout"
notify:
- restart iptables
- name: Save iptables rules
command: service iptables save
when: "'kube-apiserver' not in iptablesrules.stdout"
---
- name: Copy master binaries
copy:
src: ../../_output/local/go/bin/{{ item }}
dest: /usr/bin/
mode: 755
with_items:
- kube-apiserver
- kube-scheduler
- kube-controller-manager
- kubectl
notify: restart daemons
- name: Copy master service files
copy:
src: ../init/systemd/{{ item }}
dest: /etc/systemd/system/
mode: 644
with_items:
- kube-apiserver.service
- kube-scheduler.service
- kube-controller-manager.service
notify: reload systemd
- name: Copy systemd tmpfile for apiserver
copy:
src: ../init/systemd/tmpfiles.d/
dest: /etc/tmpfiles.d/
mode: 644
notify: reload systemd
---
- include: packageManagerInstall.yml
when: source_type == "packageManager"
tags:
- binary-update
- include: localBuildInstall.yml
when: source_type == "localBuild"
tags:
- binary-update
- name: write the config file for the api server
template: src=apiserver.j2 dest={{ kube_config_dir }}/apiserver
notify:
- restart apiserver
- name: Ensure that a token auth file exists (addons may populate it)
file: path={{ kube_token_dir }}/known_tokens.csv state=touch
changed_when: false
- name: add cap_net_bind_service to kube-apiserver
capabilities: path=/usr/bin/kube-apiserver capability=cap_net_bind_service=ep state=present
when: not is_atomic
- name: Enable apiserver
service: name=kube-apiserver enabled=yes state=started
- name: Get the master token values
slurp:
src: "{{ kube_token_dir }}/{{ item }}-{{ inventory_hostname }}.token"
with_items:
- "system:controller_manager"
- "system:scheduler"
- "system:kubectl"
register: tokens
delegate_to: "{{ groups['masters'][0] }}"
- name: Set token facts
set_fact:
controller_manager_token: "{{ tokens.results[0].content|b64decode }}"
scheduler_token: "{{ tokens.results[1].content|b64decode }}"
kubectl_token: "{{ tokens.results[2].content|b64decode }}"
- name: write the config file for the controller-manager
template: src=controller-manager.j2 dest={{ kube_config_dir }}/controller-manager
notify:
- restart controller-manager
- name: write the kubecfg (auth) file for controller-manager
template: src=controller-manager.kubeconfig.j2 dest={{ kube_config_dir }}/controller-manager.kubeconfig
notify:
- restart controller-manager
- name: Enable controller-manager
service: name=kube-controller-manager enabled=yes state=started
- name: write the config file for the scheduler
template: src=scheduler.j2 dest={{ kube_config_dir }}/scheduler
notify:
- restart scheduler
- name: write the kubecfg (auth) file for scheduler
template: src=scheduler.kubeconfig.j2 dest={{ kube_config_dir }}/scheduler.kubeconfig
notify:
- restart scheduler
- name: Enable scheduler
service: name=kube-scheduler enabled=yes state=started
- name: write the kubecfg (auth) file for kubectl
template: src=kubectl.kubeconfig.j2 dest={{ kube_config_dir }}/kubectl.kubeconfig
- include: firewalld.yml
when: has_firewalld
- include: iptables.yml
when: not has_firewalld and has_iptables
---
- name: Set fact for Atomic Host package install
set_fact:
did_install: true
when: is_atomic
- include: pkgMgrInstallers/centos-install.yml
when: ansible_distribution == "CentOS"
- name: Set fact saying we did CentOS package install
set_fact:
did_install: true
when: ansible_distribution == "CentOS"
- include: pkgMgrInstallers/fedora-install.yml
when: ansible_distribution == "Fedora" and ansible_distribution_release != "Rawhide"
- name: Set fact saying we did Fedora package install
set_fact:
did_install: true
when: ansible_distribution == "Fedora" and ansible_distribution_release != "Rawhide"
- include: pkgMgrInstallers/generic-install.yml
when: not did_install
---
- name: CentOS | Install kubernetes master
yum:
pkg=kubernetes-master
state=latest
enablerepo=virt7-docker-common-candidate
notify:
- restart daemons
- name: Fedora | Install kubernetes master
action: "{{ ansible_pkg_mgr }}"
args:
name: kubernetes-master
state: latest
enablerepo: "updates-testing"
notify:
- restart daemons
- name: Generic | Install kubernetes master
action: "{{ ansible_pkg_mgr }}"
args:
name: kubernetes-master
state: latest
notify:
- restart daemons
###
# kubernetes system config
#
# The following values are used to configure the kube-apiserver
#
# The address on the local server to listen to.
KUBE_API_ADDRESS="--insecure-bind-address=127.0.0.1"
# The port on the local server to listen on.
KUBE_API_PORT="--secure-port={{ kube_master_api_port }}"
# Port nodes listen on
# KUBELET_PORT="--kubelet-port=10250"
# Address range to use for services
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range={{ kube_service_addresses }}"
# Location of the etcd cluster
KUBE_ETCD_SERVERS="--etcd-servers={% for node in groups['etcd'] %}http://{{ node }}:2379{% if not loop.last %},{% endif %}{% endfor %}"
# default admission control policies
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"
# Add your own!
KUBE_API_ARGS="--tls-cert-file={{ kube_cert_dir }}/server.crt --tls-private-key-file={{ kube_cert_dir }}/server.key --client-ca-file={{ kube_cert_dir }}/ca.crt --token-auth-file={{ kube_token_dir }}/known_tokens.csv --service-account-key-file={{ kube_cert_dir }}/server.crt"
###
# The following values are used to configure the kubernetes controller-manager
# defaults from config and apiserver should be adequate
# Add your own!
KUBE_CONTROLLER_MANAGER_ARGS="--kubeconfig={{ kube_config_dir }}/controller-manager.kubeconfig --service-account-private-key-file={{ kube_cert_dir }}/server.key --root-ca-file={{ kube_cert_dir }}/ca.crt"
apiVersion: v1
kind: Config
current-context: controller-manager-to-{{ cluster_name }}
preferences: {}
clusters:
- cluster:
certificate-authority: {{ kube_cert_dir }}/ca.crt
server: https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}
name: {{ cluster_name }}
contexts:
- context:
cluster: {{ cluster_name }}
user: controller-manager
name: controller-manager-to-{{ cluster_name }}
users:
- name: controller-manager
user:
token: {{ controller_manager_token }}
apiVersion: v1
kind: Config
current-context: kubectl-to-{{ cluster_name }}
preferences: {}
clusters:
- cluster:
certificate-authority-data: {{ kube_ca_cert|b64encode }}
server: https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}
name: {{ cluster_name }}
contexts:
- context:
cluster: {{ cluster_name }}
user: kubectl
name: kubectl-to-{{ cluster_name }}
users:
- name: kubectl
user:
token: {{ kubectl_token }}
###
# kubernetes scheduler config
# default config should be adequate
# Add your own!
KUBE_SCHEDULER_ARGS="--kubeconfig={{ kube_config_dir }}/scheduler.kubeconfig"
apiVersion: v1
kind: Config
current-context: scheduler-to-{{ cluster_name }}
preferences: {}
clusters:
- cluster:
certificate-authority: {{ kube_cert_dir }}/ca.crt
server: https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}
name: {{ cluster_name }}
contexts:
- context:
cluster: {{ cluster_name }}
user: scheduler
name: scheduler-to-{{ cluster_name }}
users:
- name: scheduler
user:
token: {{ scheduler_token }}
---
- name: reload systemd
command: systemctl --system daemon-reload
notify:
- restart daemons
- name: restart daemons
command: /bin/true
notify:
- restart kubelet
- restart proxy
- name: restart kubelet
service: name=kubelet state=restarted
- name: restart proxy
service: name=kube-proxy state=restarted
- name: restart iptables
service: name=iptables state=restarted
---
dependencies:
- { role: common }
- { role: docker }
- { role: kubernetes }
---
# https://bugzilla.redhat.com/show_bug.cgi?id=1033606 and I think others say firewalld+docker == bad
- name: disable firewalld
service: name=firewalld enabled=no state=stopped
#- name: Open firewalld port for the kubelet
#firewalld: port=10250/tcp permanent=false state=enabled
#- name: Save firewalld port for the kubelet
#firewalld: port=10250/tcp permanent=true state=enabled
---
- name: Get iptables rules
command: iptables -L
register: iptablesrules
always_run: yes
- name: Enable iptables at boot
service: name=iptables enabled=yes state=started
- name: Open kubelet port with iptables
command: /sbin/iptables -I INPUT 1 -p tcp --dport 10250 -j ACCEPT -m comment --comment "kubelet"
when: "'kubelet' not in iptablesrules.stdout"
notify:
- restart iptables
- name: Save iptables rules
command: service iptables save
when: "'kubelet' not in iptablesrules.stdout"
---
- name: Copy node binaries
copy:
src: ../../_output/local/go/bin/{{ item }}
dest: /usr/bin/
mode: 755
with_items:
- kubelet
- kube-proxy
- kubectl
notify:
- restart daemons
- name: Copy node service files
copy:
src: ../init/systemd/{{ item }}
dest: /etc/systemd/system/
mode: 644
with_items:
- kube-proxy.service
- kubelet.service
notify: reload systemd
- name: Create the /var/lib/kubelet working directory
file:
path: /var/lib/kubelet
state: directory
---
- name: Set selinux permissive because tokens and selinux don't work together
selinux: state=permissive policy={{ ansible_selinux.type }}
when: ansible_selinux is defined and ansible_selinux.status == "enabled"
- include: packageManagerInstall.yml
when: source_type == "packageManager"
tags:
- binary-update
- include: localBuildInstall.yml
when: source_type == "localBuild"
tags:
- binary-update
- name: Make sure manifest directory exists
file: path={{ kube_manifest_dir }} state=directory
- name: Install fluentd pod into each node
get_url:
url=https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/cluster/saltbase/salt/fluentd-es/fluentd-es.yaml
dest="{{ kube_manifest_dir }}"
force=yes
when: cluster_logging
- name: Get the node token values
slurp:
src: "{{ kube_token_dir }}/{{ item }}-{{ inventory_hostname }}.token"
with_items:
- "system:kubelet"
- "system:proxy"
register: tokens
delegate_to: "{{ groups['masters'][0] }}"
- name: Set token facts
set_fact:
kubelet_token: "{{ tokens.results[0].content|b64decode }}"
proxy_token: "{{ tokens.results[1].content|b64decode }}"
- name: write the config files for kubelet
template: src=kubelet.j2 dest={{ kube_config_dir }}/kubelet
notify:
- restart kubelet
- name: write the kubecfg (auth) file for kubelet
template: src=kubelet.kubeconfig.j2 dest={{ kube_config_dir }}/kubelet.kubeconfig
notify:
- restart kubelet
- name: Enable kubelet
service: name=kubelet enabled=yes state=started
- name: write the config files for proxy
template: src=proxy.j2 dest={{ kube_config_dir }}/proxy
notify:
- restart proxy
- name: write the kubecfg (auth) file for kube-proxy
template: src=proxy.kubeconfig.j2 dest={{ kube_config_dir }}/proxy.kubeconfig
notify:
- restart proxy
- name: Enable proxy
service: name=kube-proxy enabled=yes state=started
- include: firewalld.yml
when: has_firewalld
- include: iptables.yml
when: not has_firewalld and has_iptables
---
- name: Set fact for Atomic Host package install
set_fact:
did_install: true
when: is_atomic
- include: pkgMgrInstallers/centos-install.yml
when: ansible_distribution == "CentOS"
- name: Set fact saying we did CentOS package install
set_fact:
did_install: true
when: ansible_distribution == "CentOS"
- include: pkgMgrInstallers/fedora-install.yml
when: ansible_distribution == "Fedora" and ansible_distribution_release != "Rawhide"
- name: Set fact saying we did Fedora package install
set_fact:
did_install: true
when: ansible_distribution == "Fedora" and ansible_distribution_release != "Rawhide"
- include: pkgMgrInstallers/generic-install.yml
when: not did_install
---
- name: CentOS | Install kubernetes node
yum:
pkg=kubernetes-node
state=latest
enablerepo=virt7-docker-common-candidate
notify:
- restart daemons
- name: Fedora | Install kubernetes node
action: "{{ ansible_pkg_mgr }}"
args:
name: kubernetes-node
state: latest
enablerepo: "updates-testing"
notify:
- restart daemons
- name: Generic | Install kubernetes node
action: "{{ ansible_pkg_mgr }}"
args:
name: kubernetes-node
state: latest
notify:
- restart daemons
###
# kubernetes kubelet (node) config
# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=0.0.0.0"
# The port for the info server to serve on
# KUBELET_PORT="--port=10250"
# You may leave this blank to use the actual hostname
KUBELET_HOSTNAME="--hostname-override={{ inventory_hostname }}"
# location of the api-server
KUBELET_API_SERVER="--api-servers=https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}"
# Add your own!
{% if dns_setup %}
KUBELET_ARGS="--cluster-dns={{ dns_server }} --cluster-domain={{ dns_domain }} --kubeconfig={{ kube_config_dir}}/kubelet.kubeconfig --config={{ kube_manifest_dir }}"
{% else %}
KUBELET_ARGS="--kubeconfig={{ kube_config_dir }}/kubelet.kubeconfig --config={{ kube_manifest_dir }}"
{% endif %}
apiVersion: v1
kind: Config
current-context: kubelet-to-{{ cluster_name }}
preferences: {}
clusters:
- cluster:
certificate-authority: {{ kube_cert_dir }}/ca.crt
server: https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}
name: {{ cluster_name }}
contexts:
- context:
cluster: {{ cluster_name }}
user: kubelet
name: kubelet-to-{{ cluster_name }}
users:
- name: kubelet
user:
token: {{ kubelet_token }}
###
# kubernetes proxy config
# default config should be adequate
# Add your own!
KUBE_PROXY_ARGS="--kubeconfig={{ kube_config_dir }}/proxy.kubeconfig"
apiVersion: v1
kind: Config
current-context: proxy-to-{{ cluster_name }}
preferences: {}
contexts:
- context:
cluster: {{ cluster_name }}
user: proxy
name: proxy-to-{{ cluster_name }}
clusters:
- cluster:
certificate-authority: {{ kube_cert_dir }}/ca.crt
server: https://{{ groups['masters'][0] }}:{{ kube_master_api_port }}
name: {{ cluster_name }}
users:
- name: proxy
user:
token: {{ proxy_token }}
---
- name: Install minimal packages
raw: dnf install -y {{ item }}
with_items:
- python # everyone need python2
- python-dnf # some versions of ansible (2.0) use dnf directly
- yum # some versions of ansible use yum
- libselinux-python
---
- name: Get os_version from /etc/os-release
raw: "grep '^VERSION_ID=' /etc/os-release | sed s'/VERSION_ID=//'"
register: os_version
- name: Get distro name from /etc/os-release
raw: "grep '^NAME=' /etc/os-release | sed s'/NAME=//'"
register: distro
- include: fedora-dnf.yml
when: os_version.stdout|int >= 22 and 'Fedora' in distro.stdout
#!/bin/bash
# Copyright 2015 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
inventory=${INVENTORY:-inventory}
ansible-playbook -i ${inventory} cluster.yml $@
## Vagrant deployer for Kubernetes Ansible
This deployer sets up a vagrant cluster and installs kubernetes with flannel on it.
## Before you start !
You will need a functioning vagrant provider. Currently supported are openstack, libvirt, and virtualbox.
## USAGE
In general all that should be needed it to run
```
vagrant up
```
If you export an env variable such as
```
export NUM_MINIONS=4
```
The system will create that number of nodes. Default is 2.
## Provider Specific Information
Vagrant tries to be intelligent and pick the first provider supported by your installation. If you want to specify a provider you can do so by running vagrant like so:
```
vagrant up --provider=openstack
```
### OpenStack
Make sure to install the openstack provider for vagrant.
```
vagrant plugin install vagrant-openstack-provider --plugin-version ">= 0.6.1"
```
NOTE This is a more up-to-date provider than the similar `vagrant-openstack-plugin`.
Also note that current (required) versions of `vagrant-openstack-provider` are not compatible with ruby 2.2.
https://github.com/ggiamarchi/vagrant-openstack-provider/pull/237
So make sure you get at least version 0.6.1.
To use the vagrant openstack provider you will need
- Copy `openstack_config.yml.example` to `openstack_config.yml`
- Edit `openstack_config.yml` to include your relevant details.
For vagrant (1.7.2) does not seem to ever want to pick openstack as the provider. So you will need to tell it to use openstack explicitly.
###### Libvirt
The libvirt vagrant provider is non-deterministic when launching VMs. This is a problem as we need ansible to only run after all of the VMs are running. To solve this when using libvirt one must
do the following
```
vagrant up --no-provision
vagrant provision
```
### VirtualBox
Nothing special with VirtualBox. Hopefully `vagrant up` just works.
## Random Information
If you just want to update the binaries on your systems (either pkgManager or localBuild) you can do so using the ansible binary-update tag. To do so with vagrant provision you would need to run
```
ANSIBLE_TAGS="binary-update" vagrant provision
```
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/contrib/ansible/vagrant/README.md?pixel)]()
# -*- mode: ruby -*-
# vi: set ft=ruby :
require "yaml"
### This is a new provider, different then cloudbau's.
### RUN: vagrant plugin uninstall vagrant-openstack-plugin"
### Then RUN: "vagrant plugin install vagrant-openstack-provider"
require 'vagrant-openstack-provider'
$num_nodes = (ENV['NUM_NODES'] || 2).to_i
ansible_tags = ENV['ANSIBLE_TAGS']
VAGRANTFILE_API_VERSION = "2"
# Openstack providers are best used with latest versions.
Vagrant.require_version ">= 1.7"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# By default, Vagrant 1.7+ automatically inserts a different
# insecure keypair for each new VM created. The easiest way
# to use the same keypair for all the machines is to disable
# this feature and rely on the legacy insecure key.
config.ssh.insert_key = false
# This explicitly sets the order that vagrant will use by default if no --provider given
config.vm.provider "openstack"
config.vm.provider "libvirt"
config.vm.provider "virtualbox"
def set_openstack(os, config, n)
# common config
config.vm.box = "dummy"
config.vm.box_url = "https://github.com/cloudbau/vagrant-openstack-plugin/raw/master/dummy.box"
# this crap is to make it not fail if the file doesn't exist (which is ok if we are using a different provisioner)
__filename = File.join(File.dirname(__FILE__), "openstack_config.yml")
if File.exist?(__filename)
_config = YAML.load(File.open(__filename, File::RDONLY).read)
else
_config = Hash.new("")
_config['security_group'] = []
end
config.ssh.username = "fedora"
config.ssh.private_key_path = "~/.ssh/id_rsa"
config.vm.boot_timeout = 60*10
### The below parameters need to be modified per your openstack instance.
os.username = _config['os_username']
os.password = _config['os_password']
os.tenant_name = _config['os_tenant']
os.keypair_name = _config['os_ssh_key_name']
os.openstack_auth_url = _config['os_auth_url']
os.region = _config['os_region_name']
os.floating_ip_pool = _config['os_floating_ip_pool']
os.flavor = _config['os_flavor']
os.image = _config['os_image']
os.security_groups = _config['os_security_groups']
os.server_name = n.vm.hostname
end
def set_vbox(vb, config)
config.vm.box = "chef/centos-7.0"
config.vm.network "private_network", type: "dhcp"
vb.gui = false
vb.memory = 2048
vb.cpus = 2
# Use faster paravirtualized networking
vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
end
def set_libvirt(lv, config)
config.vm.box = "kube-centos-7"
config.vm.box_url = "http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7.LibVirt.box"
lv.memory = 2048
lv.cpus = 2
lv.nested = true
lv.volume_cache = 'none'
end
def set_provider(n)
n.vm.provider :openstack do |os, override|
set_openstack(os, override, n)
end
n.vm.provider :virtualbox do |vb, override|
set_vbox(vb, override)
end
n.vm.provider :libvirt do |lv, override|
set_libvirt(lv, override)
end
end
config.vm.synced_folder ".", "/vagrant", disabled: true
nodes = Array.new()
$num_nodes.times do |i|
# multi vm config
name = "kube-node-#{i+1}"
nodes.push(name)
config.vm.define "#{name}" do |n|
n.vm.hostname = name
set_provider(n)
end
end
# This is how we create the ansible inventory, see it in .vagrant
# if you want to debug, run 'VAGRANT_LOG=info vagrant up'
# and you'll see exactly how the cluster comes up via ansible inv.
groups = {
"etcd" => ["kube-master"],
"masters" => ["kube-master"],
"nodes" => nodes,
"all_groups:children" => ["etcd","masters","nodes"]
}
config.vm.define "kube-master" do |n|
name = "kube-master"
n.vm.hostname = name
set_provider(n)
if ansible_tags.nil?
# This set up the vagrant hosts before we run the main playbook
# Today this just creates /etc/hosts so machines can talk via their
# 'internal' IPs instead of the openstack public ip.
n.vm.provision :ansible do |ansible|
ansible.groups = groups
ansible.playbook = "./vagrant-ansible.yml"
ansible.limit = "all" #otherwise the metadata wont be there for ipv4?
end
end
# This sets up both flannel and kube.
n.vm.provision :ansible do |ansible|
ansible.groups = groups
ansible.playbook = "../cluster.yml"
ansible.limit = "all" #otherwise the metadata wont be there for ipv4?
ansible.tags = ansible_tags
end
end
end
os_username: eparis
os_password: redhat
os_tenant: "RH US Business Group"
os_auth_url: "http://os1-public.osop.rhcloud.com:5000/v2.0"
os_region_name: "OS1Public"
os_ssh_key_name: "eparis"
os_flavor: "m1.small"
os_image: "Fedora 22 Cloud Base x86_64 (final)"
os_security_groups:
- "default"
#- some_other_group
os_floating_ip_pool: "os1_public"
os_username: eparis
os_password: password
os_tenant: "RH US Business Group"
os_auth_url: "http://os1-public.osop.rhcloud.com:5000/v2.0"
os_region_name: "OS1Public"
os_ssh_key_name: "eparis"
os_flavor: "m1.small"
os_image: "Fedora 22 Cloud Base x86_64 (final)"
os_security_groups:
- "default"
#- some_other_group
os_floating_ip_pool: "os1_public"
- hosts: all
sudo: yes
tasks:
- name: "Build hosts file"
lineinfile:
dest=/etc/hosts
regexp=".*{{ item }}$"
line="{{ hostvars[item].ansible_default_ipv4.address }} {{item}}"
state=present
when: hostvars[item].ansible_default_ipv4.address is defined
with_items: groups['all']
FROM busybox
MAINTAINER Muhammed Uluyol "uluyol@google.com"
ADD dc /diurnal
RUN chown root:users /diurnal && chmod 755 /diurnal
ENTRYPOINT ["/diurnal"]
.PHONY: build push vet test clean
TAG = 0.5
REPO = uluyol/kube-diurnal
BIN = dc
dc: dc.go time.go
CGO_ENABLED=0 godep go build -a -installsuffix cgo -o dc dc.go time.go
vet:
godep go vet .
test:
godep go test .
build: $(BIN)
docker build -t $(REPO):$(TAG) .
push:
docker push $(REPO):$(TAG)
clean:
rm -f $(BIN)
## Diurnal Controller
This controller manipulates the number of replicas maintained by a replication controller throughout the day based on a provided list of times of day (according to ISO 8601) and replica counts. It should be run under a replication controller that is in the same namespace as the replication controller that it is manipulating.
For example, to set the replica counts of the pods with the labels "tier=backend,track=canary" to 10 at noon UTC and 6 at midnight UTC, we can use `-labels tier=backend,track=canary -times 00:00Z,12:00Z -counts 6,10`. An example replication controller config can be found [here](example-diurnal-controller.yaml).
Instead of providing replica counts and times of day directly, you may use a script like the one below to generate them using mathematical functions.
```python
from math import *
import os
import sys
def _day_to_2pi(t):
return float(t) * 2 * pi / (24*3600)
def main(args):
if len(args) < 3:
print "Usage: %s sample_interval func" % (args[0],)
print "func should be a function of the variable t, where t will range from 0"
print "to 2pi over the course of the day"
sys.exit(1)
sampling_interval = int(args[1])
exec "def f(t): return " + args[2]
i = 0
times = []
counts = []
while i < 24*60*60:
hours = i / 3600
left = i - hours*3600
min = left / 60
sec = left - min*60
times.append("%dh%dm%ds" % (hours, min, sec))
count = int(round(f(_day_to_2pi(i))))
counts.append(str(count))
i += sampling_interval
print "-times %s -counts %s" % (",".join(times), ",".join(counts))
if __name__ == "__main__":
main(sys.argv)
```
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/contrib/diurnal/README.md?pixel)]()
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// An external diurnal controller for kubernetes. With this, it's possible to manage
// known replica counts that vary throughout the day.
package main
import (
"errors"
"flag"
"fmt"
"os"
"os/signal"
"sort"
"strconv"
"strings"
"syscall"
"time"
kclient "k8s.io/kubernetes/pkg/client"
"k8s.io/kubernetes/pkg/labels"
"github.com/golang/glog"
)
const dayPeriod = 24 * time.Hour
type timeCount struct {
time time.Duration
count int
}
func (tc timeCount) String() string {
h := tc.time / time.Hour
m := (tc.time % time.Hour) / time.Minute
s := (tc.time % time.Minute) / time.Second
if m == 0 && s == 0 {
return fmt.Sprintf("(%02dZ, %d)", h, tc.count)
} else if s == 0 {
return fmt.Sprintf("(%02d:%02dZ, %d)", h, m, tc.count)
}
return fmt.Sprintf("(%02d:%02d:%02dZ, %d)", h, m, s, tc.count)
}
type byTime []timeCount
func (tc byTime) Len() int { return len(tc) }
func (tc byTime) Swap(i, j int) { tc[i], tc[j] = tc[j], tc[i] }
func (tc byTime) Less(i, j int) bool { return tc[i].time < tc[j].time }
func timeMustParse(layout, s string) time.Time {
t, err := time.Parse(layout, s)
if err != nil {
panic(err)
}
return t
}
// first argument is a format string equivalent to HHMMSS. See time.Parse for details.
var epoch = timeMustParse("150405", "000000")
func parseTimeRelative(s string) (time.Duration, error) {
t, err := parseTimeISO8601(s)
if err != nil {
return 0, fmt.Errorf("unable to parse %s: %v", s, err)
}
return (t.Sub(epoch) + dayPeriod) % dayPeriod, nil
}
func parseTimeCounts(times string, counts string) ([]timeCount, error) {
ts := strings.Split(times, ",")
cs := strings.Split(counts, ",")
if len(ts) != len(cs) {
return nil, fmt.Errorf("provided %d times but %d replica counts", len(ts), len(cs))
}
var tc []timeCount
for i := range ts {
t, err := parseTimeRelative(ts[i])
if err != nil {
return nil, err
}
c, err := strconv.ParseInt(cs[i], 10, 64)
if c < 0 {
return nil, errors.New("counts must be non-negative")
}
if err != nil {
return nil, err
}
tc = append(tc, timeCount{t, int(c)})
}
sort.Sort(byTime(tc))
return tc, nil
}
type Scaler struct {
timeCounts []timeCount
selector labels.Selector
start time.Time
pos int
done chan struct{}
}
var posError = errors.New("could not find position")
func findPos(tc []timeCount, cur int, offset time.Duration) int {
first := true
for i := cur; i != cur || first; i = (i + 1) % len(tc) {
if tc[i].time > offset {
return i
}
first = false
}
return 0
}
func (s *Scaler) setCount(c int) {
glog.Infof("scaling to %d replicas", c)
rcList, err := client.ReplicationControllers(namespace).List(s.selector)
if err != nil {
glog.Errorf("could not get replication controllers: %v", err)
return
}
for _, rc := range rcList.Items {
rc.Spec.Replicas = c
if _, err = client.ReplicationControllers(namespace).Update(&rc); err != nil {
glog.Errorf("unable to scale replication controller: %v", err)
}
}
}
func (s *Scaler) timeOffset() time.Duration {
return time.Since(s.start) % dayPeriod
}
func (s *Scaler) curpos(offset time.Duration) int {
return findPos(s.timeCounts, s.pos, offset)
}
func (s *Scaler) scale() {
for {
select {
case <-s.done:
return
default:
offset := s.timeOffset()
s.pos = s.curpos(offset)
if s.timeCounts[s.pos].time < offset {
time.Sleep(dayPeriod - offset)
continue
}
time.Sleep(s.timeCounts[s.pos].time - offset)
s.setCount(s.timeCounts[s.pos].count)
}
}
}
func (s *Scaler) Start() error {
now := time.Now().UTC()
s.start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
if *startNow {
s.start = now
}
// set initial count
pos := s.curpos(s.timeOffset())
// add the len to avoid getting a negative index
pos = (pos - 1 + len(s.timeCounts)) % len(s.timeCounts)
s.setCount(s.timeCounts[pos].count)
s.done = make(chan struct{})
go s.scale()
return nil
}
func safeclose(c chan<- struct{}) (err error) {
defer func() {
if e := recover(); e != nil {
err = e.(error)
}
}()
close(c)
return nil
}
func (s *Scaler) Stop() error {
if err := safeclose(s.done); err != nil {
return errors.New("already stopped scaling")
}
return nil
}
var (
counts = flag.String("counts", "", "replica counts, must have at least one (csv)")
times = flag.String("times", "", "times to set replica counts relative to UTC following ISO 8601 (csv)")
userLabels = flag.String("labels", "", "replication controller labels, syntax should follow https://godoc.org/k8s.io/kubernetes/pkg/labels#Parse")
startNow = flag.Bool("now", false, "times are relative to now not 0:00 UTC (for demos)")
local = flag.Bool("local", false, "set to true if running on local machine not within cluster")
localPort = flag.Int("localport", 8001, "port that kubectl proxy is running on (local must be true)")
namespace string = os.Getenv("POD_NAMESPACE")
client *kclient.Client
)
const usageNotes = `
counts and times must both be set and be of equal length. Example usage:
diurnal -labels name=redis-slave -times 00:00:00Z,06:00:00Z -counts 3,9
diurnal -labels name=redis-slave -times 0600-0500,0900-0500,1700-0500,2200-0500 -counts 15,20,13,6
`
func usage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprint(os.Stderr, usageNotes)
}
func main() {
flag.Usage = usage
flag.Parse()
var (
cfg *kclient.Config
err error
)
if *local {
cfg = &kclient.Config{Host: fmt.Sprintf("http://localhost:%d", *localPort)}
} else {
cfg, err = kclient.InClusterConfig()
if err != nil {
glog.Errorf("failed to load config: %v", err)
flag.Usage()
os.Exit(1)
}
}
client, err = kclient.New(cfg)
selector, err := labels.Parse(*userLabels)
if err != nil {
glog.Fatal(err)
}
tc, err := parseTimeCounts(*times, *counts)
if err != nil {
glog.Fatal(err)
}
if namespace == "" {
glog.Fatal("POD_NAMESPACE is not set. Set to the namespace of the replication controller if running locally.")
}
scaler := Scaler{timeCounts: tc, selector: selector}
if err != nil {
glog.Fatal(err)
}
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGQUIT,
syscall.SIGTERM)
glog.Info("starting scaling")
if err := scaler.Start(); err != nil {
glog.Fatal(err)
}
<-sigChan
glog.Info("stopping scaling")
if err := scaler.Stop(); err != nil {
glog.Fatal(err)
}
}
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"testing"
"time"
)
func equalsTimeCounts(a, b []timeCount) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i].time != b[i].time || a[i].count != b[i].count {
return false
}
}
return true
}
func TestParseTimeCounts(t *testing.T) {
cases := []struct {
times string
counts string
out []timeCount
err bool
}{
{
"00:00:01Z,00:02Z,03:00Z,04:00Z", "1,4,1,8", []timeCount{
{time.Second, 1},
{2 * time.Minute, 4},
{3 * time.Hour, 1},
{4 * time.Hour, 8},
}, false,
},
{
"00:01Z,00:02Z,00:05Z,00:03Z", "1,2,3,4", []timeCount{
{1 * time.Minute, 1},
{2 * time.Minute, 2},
{3 * time.Minute, 4},
{5 * time.Minute, 3},
}, false,
},
{"00:00Z,00:01Z", "1,0", []timeCount{{0, 1}, {1 * time.Minute, 0}}, false},
{"00:00+00,00:01+00:00,01:00Z", "0,-1,0", nil, true},
{"-00:01Z,01:00Z", "0,1", nil, true},
{"00:00Z", "1,2,3", nil, true},
}
for i, test := range cases {
out, err := parseTimeCounts(test.times, test.counts)
if test.err && err == nil {
t.Errorf("case %d: expected error", i)
} else if !test.err && err != nil {
t.Errorf("case %d: unexpected error: %v", i, err)
}
if !test.err {
if !equalsTimeCounts(test.out, out) {
t.Errorf("case %d: expected timeCounts: %v got %v", i, test.out, out)
}
}
}
}
func TestFindPos(t *testing.T) {
cases := []struct {
tc []timeCount
cur int
offset time.Duration
expected int
}{
{[]timeCount{{0, 1}, {4, 0}}, 1, 1, 1},
{[]timeCount{{0, 1}, {4, 0}}, 0, 1, 1},
{[]timeCount{{0, 1}, {4, 0}}, 1, 70, 0},
{[]timeCount{{5, 1}, {100, 9000}, {4000, 2}, {10000, 4}}, 0, 0, 0},
{[]timeCount{{5, 1}, {100, 9000}, {4000, 2}, {10000, 4}}, 1, 5000, 3},
{[]timeCount{{5, 1}, {100, 9000}, {4000, 2}, {10000, 4}}, 2, 10000000, 0},
{[]timeCount{{5, 1}, {100, 9000}, {4000, 2}, {10000, 4}}, 0, 50, 1},
}
for i, test := range cases {
pos := findPos(test.tc, test.cur, test.offset)
if pos != test.expected {
t.Errorf("case %d: expected %d got %d", i, test.expected, pos)
}
}
}
apiVersion: v1
kind: ReplicationController
metadata:
labels:
name: diurnal-controller
name: diurnal-controller
spec:
replicas: 1
selector:
name: diurnal-controller
template:
metadata:
labels:
name: diurnal-controller
spec:
containers:
- args: ["-labels", "name=redis-slave", "-times", "00:00Z,00:02Z,01:00Z,02:30Z", "-counts", "3,7,6,9"]
resources:
limits:
cpu: 0.1
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: uluyol/kube-diurnal:0.5
name: diurnal-controller
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment