├── .gitignore ├── README.md ├── build.sh ├── files ├── getOvfProperty.py ├── rc.local ├── setup-01-os.sh ├── setup-02-proxy.sh ├── setup-03-network.sh ├── setup-04-kubernetes.sh ├── setup-05-shell.sh └── setup.sh ├── http ├── packages_minimal.json └── photon-kickstart.json ├── k8s-app-bom.json ├── manual ├── add_ovf_properties.sh └── photon.xml.template ├── photon-builder.json ├── photon-version.json ├── photon.json └── scripts ├── photon-cleanup.sh ├── photon-containers.sh ├── photon-docker.sh └── photon-settings.sh /.gitignore: -------------------------------------------------------------------------------- 1 | packer_cache/ 2 | output-vmware-iso/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Appliance - VMware OVA 2 | 3 | [![Twitter Follow](https://img.shields.io/twitter/follow/vmw_rguske?style=social)](https://twitter.com/vmw_rguske) 4 | 5 | :pencil: [My Personal Blog](https://rguske.github.io) 6 | 7 | ⬇️ [Download Kubernetes-Appliance](https://drive.google.com/drive/folders/1U8SkVbKw2ijIperWoszrhV5l0BtsjyTL?usp=sharing) 8 | 9 | ## :book: Table of Content 10 | 11 | - [Kubernetes Appliance - VMware OVA](#kubernetes-appliance---vmware-ova) 12 | - [:book: Table of Content](#book-table-of-content) 13 | - [:raised\_hands: Credits](#raised_hands-credits) 14 | - [:eyeglasses: Overview](#eyeglasses-overview) 15 | - [:clipboard: Requirements](#clipboard-requirements) 16 | - [:man\_cook: Building the Appliance](#man_cook-building-the-appliance) 17 | - [Debugging](#debugging) 18 | - [Output directoy](#output-directoy) 19 | - [:computer: `zsh` installed](#computer-zsh-installed) 20 | - [Deployment Options Appliance](#deployment-options-appliance) 21 | - [Join an existing Kubernetes-Appliance](#join-an-existing-kubernetes-appliance) 22 | - [📋 Change Log](#-change-log) 23 | 24 | ## :raised_hands: Credits 25 | 26 | Credits goes out to [William Lam](https://twitter.com/lamw). The code basis for this project is based on the awesome VMware open-source project [VMware Event Broker Appliance](https://www.vmweventbroker.io). I reused the code and stripped it down to the necessary pieces and adjusted it for my needs. 27 | 28 | [![Twitter Follow](https://img.shields.io/twitter/follow/lamw?style=social)](https://twitter.com/lamw) 29 | 30 | ## :eyeglasses: Overview 31 | 32 | This repository contains the necessary code to build a [![Photon OS 4.0](https://img.shields.io/badge/Photon%20OS-4.0-orange)](https://vmware.github.io/photon/) based Kubernetes Appliance. 33 | 34 | The Appliance can be quickly deployed on vSphere for testing, development or learning purposes. Perhaps, it serves as the foundation for your project(s) 😉 35 | 36 | ## :clipboard: Requirements 37 | 38 | **CLI Tools:** 39 | 40 | - [VMware ovftool](https://www.vmware.com/support/developer/ovf/) 41 | - [Packer](https://learn.hashicorp.com/tutorials/packer/get-started-install-cli) 42 | - Run `packer init .` to download the [Packer plugin binaries](https://developer.hashicorp.com/packer/docs/commands/init) for vSphere. 43 | - [jq](https://github.com/stedolan/jq/wiki/Installation) 44 | - [PowerShell](https://github.com/PowerShell/PowerShell) - more optional 45 | 46 | **Network:** 47 | 48 | - DHCP enabled 49 | - SSH enabled (port 22) 50 | - No network restrictions between the build system (were `packer` is running on) and the VMware ESXi host 51 | - Packer will create an http server serving `http_directory` 52 | - Random port used within the range of 8000 and 9000 53 | 54 | **vSphere** 55 | 56 | - ESXi 6.7 or greater 57 | - Enable GuestIPHack on the ESXi host before building the appliance: `esxcli system settings advanced set -o /Net/GuestIPHack -i 1` 58 | 59 | ## :man_cook: Building the Appliance 60 | 61 | 1. Clone the repository: `git clone git@github.com:rguske/kubernetes-appliance.git` 62 | 2. Change into the directoy: `cd kubernetes-appliance` 63 | 3. Adjust the `photon-builder.json` file with the appropriate ESXi (build host) data (IP or FQDN, user, password, datastore, network) 64 | 4. Execute the `build.sh` script: `./build.sh` 65 | 66 | Optional: If you like to change the versions for e.g. Kubernetes or Antrea, modify those in the `k8s-app-bom.json` 67 | 68 | ### Debugging 69 | 70 | The SSH session initiated will be visible in the detail provided when `PACKER_LOG=1` environment variable is set within the `build.sh` script. 71 | 72 | Example: `PACKER_LOG=1 packer build -var "K8S_APP_VERSION=${K8S_APP_VERSION_FROM_BOM}" -var-file=photon-builder.json -var-file=photon-version.json photon.json`. 73 | 74 | ### Output directoy 75 | 76 | The finished `ova` file will be exported to the `output-vmware-iso` directory. 77 | 78 | ## :computer: `zsh` installed 79 | 80 | I'm a happy user of [zsh](https://www.zsh.org/) and [oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh) and therefore, when connecting to the appliance via `ssh`, you will use a "pimped" shell environment. 81 | 82 | rguske-zsh 83 | 84 | ## Deployment Options Appliance 85 | 86 | ![rguske-k8s-app-ova-1](https://user-images.githubusercontent.com/31652019/156720139-afd35002-2156-4f56-8bec-87ad2492fea5.png) 87 | 88 | ![rguske-k8s-app-ova-2](https://user-images.githubusercontent.com/31652019/156720147-79fe6870-a4a5-4b08-a38c-63361980c982.png) 89 | 90 | ![rguske-k8s-app-ova-3](https://user-images.githubusercontent.com/31652019/156720148-f4fb1dd0-1543-4322-a4f7-a807254b75bf.png) 91 | 92 | ## Join an existing Kubernetes-Appliance 93 | 94 | In version v0.3.0, the possibility was added to join an existing Kubernetes-Appliance as an additional node. 95 | 96 | screenshot_change_name 2 97 | 98 | screenshot_change_name 7 99 | 100 | 101 | ## 📋 Change Log 102 | 103 | - [2024-06-06] Updated versions to Kubernetes 1.29.2, Antrea to 1.15, etc. 104 | - [2022-03-14] Added option to join an existing K8s Appliance Node (v0.3.0) 105 | - [2022-03-10] Added `local-path-provisioner` (v0.2.1) 106 | - [2022-03-10] Updated to VMware PhotonOS v4 Rev.2 (v0.2.0) 107 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | set -euo pipefail 6 | 7 | K8S_BOM_FILE=k8s-app-bom.json 8 | 9 | if [ ! -e ${K8S_BOM_FILE} ]; then 10 | echo "Unable to locate k8s-app-bom.json in current directory which is required" 11 | exit 1 12 | fi 13 | 14 | if ! hash jq 2>/dev/null; then 15 | echo "jq utility is not installed on this system" 16 | exit 1 17 | fi 18 | 19 | rm -f output-iso/*.ova 20 | 21 | K8S_APP_VERSION_FROM_BOM=$(jq -r < ${K8S_BOM_FILE} '.appliance.version') 22 | 23 | echo "Building K8s Appliance OVA from ${K8S_APP_VERSION_FROM_BOM} ..." 24 | PACKER_LOG=1 packer build -var "K8S_APP_VERSION=${K8S_APP_VERSION_FROM_BOM}" -var-file=photon-builder.json -var-file=photon-version.json photon.json 25 | -------------------------------------------------------------------------------- /files/getOvfProperty.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | import sys 5 | from xml.dom.minidom import parseString 6 | 7 | ovfenv_cmd="/usr/bin/vmtoolsd --cmd 'info-get guestinfo.ovfEnv'" 8 | 9 | 10 | def debug(s): 11 | sys.stderr.write(s + " \n") # Syserr only get logged on the console logs 12 | sys.stderr.flush() 13 | 14 | 15 | def get_ovf_properties(): 16 | """ 17 | Return a dict of OVF properties in the ovfenv 18 | """ 19 | properties = {} 20 | xml_parts = subprocess.Popen(ovfenv_cmd, shell=True, 21 | stdout=subprocess.PIPE).stdout.read() 22 | try: 23 | raw_data = parseString(xml_parts) 24 | except xml.parsers.expat.ExpatError as err: 25 | debug(e) 26 | sys.exit(1) 27 | for property in raw_data.getElementsByTagName('Property'): 28 | key, value = [ property.attributes['oe:key'].value, 29 | property.attributes['oe:value'].value ] 30 | properties[key] = value 31 | return properties 32 | 33 | 34 | def main(argv): 35 | if len(argv) is not 1: 36 | debug('usage: getOvfProperty.py /var/log/bootstrap.log 9 | fi 10 | -------------------------------------------------------------------------------- /files/setup-01-os.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | # OS Specific Settings where ordering does not matter 6 | 7 | set -euo pipefail 8 | 9 | systemctl enable sshd 10 | systemctl start sshd 11 | 12 | echo -e "\e[92mConfiguring OS Root password ..." > /dev/console 13 | echo "root:${ROOT_PASSWORD}" | /usr/sbin/chpasswd 14 | 15 | echo -e "\e[92mConfiguring IP Tables for Antrea ..." > /dev/console 16 | iptables -A INPUT -i gw0 -j ACCEPT 17 | iptables-save > /etc/systemd/scripts/ip4save -------------------------------------------------------------------------------- /files/setup-02-proxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | # Setup Network Proxy for both OS and Docker 6 | 7 | set -euo pipefail 8 | 9 | if [ -n "${HTTP_PROXY}" ] || [ -n "${HTTPS_PROXY}" ]; then 10 | PROXY_CONF=/etc/sysconfig/proxy 11 | CONTAINERD_CONF=/usr/lib/systemd/system/containerd.service 12 | 13 | echo -e "\e[92mConfiguring Proxy ..." > /dev/console 14 | echo "PROXY_ENABLED=\"yes\"" > ${PROXY_CONF} 15 | YES_CREDS=0 16 | if [ -n "${PROXY_USERNAME}" ] && [ -n "${PROXY_PASSWORD}" ]; then 17 | YES_CREDS=1 18 | fi 19 | 20 | if [ ! -z "${NO_PROXY}" ]; then 21 | echo "NO_PROXY=\"${NO_PROXY}\"" >> ${PROXY_CONF} 22 | sed -i "/^\[Install\]/i Environment=NO_PROXY=${NO_PROXY}" ${CONTAINERD_CONF} 23 | fi 24 | 25 | if [ ! -z "${HTTP_PROXY}" ]; then 26 | a=($(printf '%s\n' "${HTTP_PROXY//\:\/\//$'\n'}")) 27 | if [ ${#a[*]} -eq 2 ]; then 28 | HTTP_PROXY_PROTOCOL=${a[0]} 29 | HTTP_PROXY_SERVER_PORT=${a[1]} 30 | if [ $YES_CREDS -eq 1 ]; then 31 | HTTP_PROXY_URL="${HTTP_PROXY_PROTOCOL}://${PROXY_USERNAME}:${PROXY_PASSWORD}@${HTTP_PROXY_SERVER_PORT}" 32 | else 33 | HTTP_PROXY_URL="${HTTP_PROXY_PROTOCOL}://${HTTP_PROXY_SERVER_PORT}" 34 | fi 35 | echo "HTTP_PROXY='${HTTP_PROXY_URL}'" >> ${PROXY_CONF} 36 | sed -i "/^\[Install\]/i Environment=HTTP_PROXY=${HTTP_PROXY_URL}" ${CONTAINERD_CONF} 37 | else 38 | echo -e "\e[91mInvalid HTTP Proxy URL supplied" > /dev/console 39 | fi 40 | fi 41 | 42 | if [ ! -z "${HTTPS_PROXY}" ]; then 43 | a=($(printf '%s\n' "${HTTPS_PROXY//\:\/\//$'\n'}")) 44 | if [ ${#a[*]} -eq 2 ]; then 45 | HTTPS_PROXY_PROTOCOL=${a[0]} 46 | HTTPS_PROXY_SERVER_PORT=${a[1]} 47 | if [ $YES_CREDS -eq 1 ]; then 48 | HTTPS_PROXY_URL="${HTTPS_PROXY_PROTOCOL}://${PROXY_USERNAME}:${PROXY_PASSWORD}@${HTTPS_PROXY_SERVER_PORT}" 49 | else 50 | HTTPS_PROXY_URL="${HTTPS_PROXY_PROTOCOL}://${HTTPS_PROXY_SERVER_PORT}" 51 | fi 52 | echo "HTTPS_PROXY='${HTTPS_PROXY_URL}'" >> ${PROXY_CONF} 53 | sed -i "/^\[Install\]/i Environment=HTTPS_PROXY=${HTTPS_PROXY_URL}" ${CONTAINERD_CONF} 54 | else 55 | echo -e "\e[91mInvalid HTTPS Proxy URL supplied" > /dev/console 56 | fi 57 | fi 58 | fi 59 | -------------------------------------------------------------------------------- /files/setup-03-network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | # Setup Networking 6 | 7 | set -euo pipefail 8 | 9 | echo -e "\e[92mConfiguring Static IP Address ..." > /dev/console 10 | 11 | NETWORK_CONFIG_FILE=$(ls /etc/systemd/network | grep .network) 12 | cat > /etc/systemd/network/${NETWORK_CONFIG_FILE} << __CUSTOMIZE_PHOTON__ 13 | [Match] 14 | Name=e* 15 | 16 | [Network] 17 | Address=${IP_ADDRESS}/${NETMASK} 18 | Gateway=${GATEWAY} 19 | DNS=${DNS_SERVER} 20 | Domain=${DNS_DOMAIN} 21 | __CUSTOMIZE_PHOTON__ 22 | 23 | echo -e "\e[92mConfiguring NTP ..." > /dev/console 24 | cat > /etc/systemd/timesyncd.conf << __CUSTOMIZE_PHOTON__ 25 | 26 | [Match] 27 | Name=e* 28 | 29 | [Time] 30 | NTP=${NTP_SERVER} 31 | __CUSTOMIZE_PHOTON__ 32 | 33 | echo -e "\e[92mConfiguring hostname ..." > /dev/console 34 | echo "${IP_ADDRESS} ${HOSTNAME}" >> /etc/hosts 35 | hostnamectl set-hostname ${HOSTNAME} 36 | 37 | echo -e "\e[92mRestarting Network ..." > /dev/console 38 | systemctl restart systemd-networkd 39 | 40 | echo -e "\e[92mRestarting Timesync ..." > /dev/console 41 | systemctl restart systemd-timesyncd 42 | -------------------------------------------------------------------------------- /files/setup-04-kubernetes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | # Setup Containerd and Kubernetes 6 | 7 | set -euo pipefail 8 | 9 | K8S_BOM_FILE=/root/config/k8s-app-bom.json 10 | 11 | echo -e "\e[92mStarting Containerd ..." > /dev/console 12 | systemctl daemon-reload 13 | systemctl enable containerd 14 | systemctl start containerd 15 | 16 | echo -e "\e[92mDisabling/Stopping IP Tables ..." > /dev/console 17 | systemctl stop iptables 18 | systemctl disable iptables 19 | 20 | # Customize the POD CIDR Network if provided or else default to 172.16.0.0/16 21 | if [ -z "${POD_NETWORK_CIDR}" ]; then 22 | POD_NETWORK_CIDR="172.16.0.0/16" 23 | fi 24 | 25 | # Start kubelet.service 26 | systemctl enable kubelet.service 27 | 28 | # Setup k8s 29 | echo -e "\e[92mSetting up k8s ..." > /dev/console 30 | K8S_VERSION=$(jq -r < ${K8S_BOM_FILE} '.["kubernetes"].gitRepoTag') 31 | cat > /root/config/kubeconfig.yml << __EOF__ 32 | apiVersion: kubeadm.k8s.io/v1beta3 33 | kind: InitConfiguration 34 | --- 35 | apiVersion: kubeadm.k8s.io/v1beta3 36 | kind: ClusterConfiguration 37 | kubernetesVersion: ${K8S_VERSION} 38 | networking: 39 | podSubnet: ${POD_NETWORK_CIDR} 40 | __EOF__ 41 | 42 | echo -e "\e[92mDeloying kubeadm ..." > /dev/console 43 | HOME=/root 44 | kubeadm init --ignore-preflight-errors SystemVerification --skip-token-print --config /root/config/kubeconfig.yml 45 | 46 | mkdir -p $HOME/.kube 47 | cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 48 | chown $(id -u):$(id -g) $HOME/.kube/config 49 | kubectl taint nodes --all node-role.kubernetes.io/control-plane- 50 | 51 | echo -e "\e[92mDeloying Antrea ..." > /dev/console 52 | kubectl --kubeconfig /root/.kube/config apply -f /root/download/antrea.yml 53 | 54 | echo -e "\e[92mStarting k8s ..." > /dev/console 55 | systemctl enable kubelet.service 56 | 57 | while [[ $(systemctl is-active kubelet.service) == "inactive" ]] 58 | do 59 | echo -e "\e[92mk8s service is still inactive, sleeping for 10secs" > /dev/console 60 | sleep 10 61 | done 62 | 63 | echo -e "\e[92mInstalling Local-Path-Storage ..." > /dev/console 64 | 65 | CSI_VERSION=$(jq -r < ${K8S_BOM_FILE} '.["csi"].gitRepoTag') 66 | 67 | # Download local-path-provisioner config file 68 | cd /root/config 69 | curl -L https://raw.githubusercontent.com/rancher/local-path-provisioner/${CSI_VERSION}/deploy/local-path-storage.yaml -o local-path-storage.yaml 70 | 71 | # Apply local-path-provisioner file to k8s 72 | kubectl apply -f local-path-storage.yaml 73 | 74 | # Set default K8s Storageclass 75 | kubectl patch sc local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' 76 | 77 | # Join as a new Node 78 | if [ -n "${NODE_FQDN}" ]; then 79 | echo -e "\e[92mJoining an existing Control Plane Node ..." > /dev/console 80 | 81 | # Create token on Master Node 82 | NODE_TOKEN=$(sshpass -p ${NODE_PASSWORD} ssh -o 'StrictHostKeyChecking no' root@${NODE_FQDN} kubeadm token create) 83 | DISCOVERY_TOKEN=$(sshpass -p ${NODE_PASSWORD} ssh -o 'StrictHostKeyChecking no' root@${NODE_FQDN} openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //') 84 | 85 | # Reset kubeadm config on the node 86 | kubeadm reset --force 87 | 88 | # Join Node 89 | kubeadm join ${NODE_FQDN}:6443 --token ${NODE_TOKEN} --discovery-token-ca-cert-hash sha256:${DISCOVERY_TOKEN} --ignore-preflight-errors=All 90 | fi 91 | 92 | # End of Script 93 | -------------------------------------------------------------------------------- /files/setup-05-shell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | # Shell enhancements 6 | 7 | set -euo pipefail 8 | 9 | echo -e "\e[92mInstalling Shell Environment ..." > /dev/console 10 | 11 | # Install ZSH 12 | tdnf install -y zsh 13 | 14 | # Unattended oh-my-zsh installation 15 | sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended 16 | 17 | # Install Powerlin Fonts 18 | git clone https://github.com/powerline/fonts.git --depth=1 19 | cd fonts 20 | ./install.sh 21 | cd .. 22 | rm -rf fonts 23 | 24 | # Install Powerlevel9k ZSH appearance 25 | git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k 26 | 27 | # Add Powerlevel9k Theme as well as some appearance optimizations to .zshrc 28 | sed -i '11i ZSH_THEME="powerlevel9k/powerlevel9k"' ~/.zshrc 29 | sed -i '12i POWERLEVEL9K_SHORTEN_DIR_LENGTH=3' ~/.zshrc 30 | sed -i '13i POWERLEVEL9K_SHORTEN_DELIMITER=””' ~/.zshrc 31 | sed -i '14i POWERLEVEL9K_PROMPT_ON_NEWLINE=true' ~/.zshrc 32 | sed -i '15i POWERLEVEL9K_SHORTEN_STRATEGY=truncate_from_right' ~/.zshrc 33 | sed -i '16i POWERLEVEL9K_TIME_BACKGROUND=blue' ~/.zshrc 34 | sed -i '17i POWERLEVEL9K_DATE_BACKGROUND=cyan' ~/.zshrc 35 | sed -i '18i POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(kubecontext time date)' ~/.zshrc 36 | sed -i '19i POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(user dir vcs)' ~/.zshrc 37 | 38 | # Delete old Theme in .zshrc 39 | sed -i "20d" ~/.zshrc 40 | 41 | # Setting aliases in .zshrc 42 | sed -i -e '$aalias k=kubectl' ~/.zshrc 43 | sed -i -e '$aalias c=clear' ~/.zshrc 44 | sed -i -e '$aalias w="watch -n1"' ~/.zshrc 45 | 46 | # Add Syntax Highlighting to ZSH 47 | git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting 48 | sed -i "81d" ~/.zshrc 49 | sed -i '83i plugins=(git zsh-syntax-highlighting)' ~/.zshrc 50 | 51 | # ZSH Autocompletion for kubectl 52 | sed -i -e '$aif [ /usr/bin/kubectl ]; then source <(kubectl completion zsh); fi' ~/.zshrc 53 | 54 | echo -e "\e[92mInstalling Helm ..." > /dev/console 55 | # Install Helm 56 | curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 57 | chmod 700 get_helm.sh 58 | ./get_helm.sh 59 | rm get_helm.sh 60 | 61 | # Changing the default shell from bash to zsh 62 | chsh -s $(which zsh) 63 | -------------------------------------------------------------------------------- /files/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | set -euo pipefail 6 | 7 | # Extract all OVF Properties 8 | APPLIANCE_DEBUG=$(/root/setup/getOvfProperty.py "guestinfo.debug") 9 | HOSTNAME=$(/root/setup/getOvfProperty.py "guestinfo.hostname") 10 | IP_ADDRESS=$(/root/setup/getOvfProperty.py "guestinfo.ipaddress") 11 | NETMASK=$(/root/setup/getOvfProperty.py "guestinfo.netmask" | awk -F ' ' '{print $1}') 12 | GATEWAY=$(/root/setup/getOvfProperty.py "guestinfo.gateway") 13 | DNS_SERVER=$(/root/setup/getOvfProperty.py "guestinfo.dns") 14 | DNS_DOMAIN=$(/root/setup/getOvfProperty.py "guestinfo.domain") 15 | NTP_SERVER=$(/root/setup/getOvfProperty.py "guestinfo.ntp") 16 | NODE_FQDN=$(/root/setup/getOvfProperty.py "guestinfo.node_fqdn") 17 | NODE_PASSWORD=$(/root/setup/getOvfProperty.py "guestinfo.node_password") 18 | HTTP_PROXY=$(/root/setup/getOvfProperty.py "guestinfo.http_proxy") 19 | HTTPS_PROXY=$(/root/setup/getOvfProperty.py "guestinfo.https_proxy") 20 | PROXY_USERNAME=$(/root/setup/getOvfProperty.py "guestinfo.proxy_username") 21 | PROXY_PASSWORD=$(/root/setup/getOvfProperty.py "guestinfo.proxy_password") 22 | NO_PROXY=$(/root/setup/getOvfProperty.py "guestinfo.no_proxy") 23 | ROOT_PASSWORD=$(/root/setup/getOvfProperty.py "guestinfo.root_password") 24 | POD_NETWORK_CIDR=$(/root/setup/getOvfProperty.py "guestinfo.pod_network_cidr") 25 | export KUBECONFIG="/root/.kube/config" 26 | 27 | if [ -e /root/ran_customization ]; then 28 | exit 29 | else 30 | K8S_APP_LOG_FILE=/var/log/bootstrap.log 31 | if [ ${APPLIANCE_DEBUG} == "True" ]; then 32 | K8S_APP_LOG_FILE=/var/log/bootstrap-debug.log 33 | set -x 34 | exec 2>> ${K8S_APP_LOG_FILE} 35 | echo 36 | echo "### WARNING -- DEBUG LOG CONTAINS ALL EXECUTED COMMANDS WHICH INCLUDES CREDENTIALS -- WARNING ###" 37 | echo "### WARNING -- PLEASE REMOVE CREDENTIALS BEFORE SHARING LOG -- WARNING ###" 38 | echo 39 | fi 40 | 41 | echo -e "\e[92mStarting Customization ..." > /dev/console 42 | 43 | echo -e "\e[92mStarting OS Configuration ..." > /dev/console 44 | . /root/setup/setup-01-os.sh 45 | 46 | echo -e "\e[92mStarting Network Proxy Configuration ..." > /dev/console 47 | . /root/setup/setup-02-proxy.sh 48 | 49 | echo -e "\e[92mStarting Network Configuration ..." > /dev/console 50 | . /root/setup/setup-03-network.sh 51 | 52 | echo -e "\e[92mStarting Kubernetes Configuration ..." > /dev/console 53 | . /root/setup/setup-04-kubernetes.sh 54 | 55 | echo -e "\e[92mStarting Shell Configuration ..." > /dev/console 56 | . /root/setup/setup-05-shell.sh 57 | 58 | echo -e "\e[92mCustomization Completed ..." > /dev/console 59 | 60 | # Clear guestinfo.ovfEnv 61 | vmtoolsd --cmd "info-set guestinfo.ovfEnv NULL" 62 | 63 | # Ensure we don't run customization again 64 | touch /root/ran_customization 65 | fi -------------------------------------------------------------------------------- /http/packages_minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "minimal", 4 | "linux-esx", 5 | "initramfs" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /http/photon-kickstart.json: -------------------------------------------------------------------------------- 1 | { 2 | "hostname": "k8s-app", 3 | "password": 4 | { 5 | "crypted": false, 6 | "text": "VMware1!" 7 | }, 8 | "disk": "/dev/sda", 9 | "packagelist_file": "packages_minimal.json", 10 | "install_linux_esx": true, 11 | "postinstall": [ 12 | "#!/bin/sh", 13 | "sed -i 's/PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config", 14 | "sed -i 's/MaxAuthTries.*/MaxAuthTries 10/g' /etc/ssh/sshd_config", 15 | "systemctl restart sshd.service", 16 | "chage -I -1 -m 0 -M 99999 -E -1 root" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /k8s-app-bom.json: -------------------------------------------------------------------------------- 1 | { 2 | "appliance": { 3 | "version": "v1.29.2" 4 | }, 5 | "antrea": { 6 | "gitRepoTag": "v1.15.0", 7 | "containers": [{ 8 | "name": "projects.registry.vmware.com/antrea/antrea-agent-ubuntu", 9 | "version": "v1.15.0" 10 | }] 11 | }, 12 | "containerd": { 13 | "version": "1.7.13" 14 | }, 15 | "csi": { 16 | "gitRepoTag": "v0.0.26", 17 | "containers": [{ 18 | "name": "rancher/local-path-provisioner", 19 | "version": "v0.0.26" 20 | }, 21 | { 22 | "name": "busybox", 23 | "version": "latest" 24 | } 25 | ] 26 | }, 27 | "kubernetes": { 28 | "gitRepoTag": "v1.29.2", 29 | "containers": [{ 30 | "name": "registry.k8s.io/kube-apiserver", 31 | "version": "v1.29.2" 32 | }, 33 | { 34 | "name": "registry.k8s.io/kube-controller-manager", 35 | "version": "v1.29.2" 36 | }, 37 | { 38 | "name": "registry.k8s.io/kube-scheduler", 39 | "version": "v1.29.2" 40 | }, 41 | { 42 | "name": "registry.k8s.io/kube-proxy", 43 | "version": "v1.29.2" 44 | }, 45 | { 46 | "name": "registry.k8s.io/pause", 47 | "version": "3.9" 48 | }, 49 | { 50 | "name": "registry.k8s.io/etcd", 51 | "version": "3.5.10-0" 52 | }, 53 | { 54 | "name": "registry.k8s.io/coredns/coredns", 55 | "version": "v1.11.1" 56 | } 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /manual/add_ovf_properties.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | OUTPUT_PATH="../output-vmware-iso" 6 | OVF_PATH=$(find ${OUTPUT_PATH} -type f -iname ${K8S_APPLIANCE_NAME}.ovf -exec dirname "{}" \;) 7 | 8 | # Move ovf files in to a subdirectory of OUTPUT_PATH if not already 9 | if [ "${OUTPUT_PATH}" = "${OVF_PATH}" ]; then 10 | mkdir ${OUTPUT_PATH}/${K8S_APPLIANCE_NAME} 11 | mv ${OUTPUT_PATH}/*.* ${OUTPUT_PATH}/${K8S_APPLIANCE_NAME} 12 | OVF_PATH=${OUTPUT_PATH}/${K8S_APPLIANCE_NAME} 13 | fi 14 | 15 | rm -f ${OVF_PATH}/${K8S_APPLIANCE_NAME}.mf 16 | 17 | sed "s/{{VERSION}}/${K8S_APP_VERSION}/g" ${K8S_APP_OVF_TEMPLATE} > photon.xml 18 | 19 | if [ "$(uname)" == "Darwin" ]; then 20 | sed -i .bak1 's///g' ${OVF_PATH}/${K8S_APPLIANCE_NAME}.ovf 21 | sed -i .bak2 "/ <\/vmw:BootOrderSection>/ r photon.xml" ${OVF_PATH}/${K8S_APPLIANCE_NAME}.ovf 22 | sed -i .bak3 '/^ //g' ${OVF_PATH}/${K8S_APPLIANCE_NAME}.ovf 27 | sed -i "/ <\/vmw:BootOrderSection>/ r photon.xml" ${OVF_PATH}/${K8S_APPLIANCE_NAME}.ovf 28 | sed -i '/^ 2 | Information about the installed software 3 | Kubernetes Appliance 4 | rguske.github.io 5 | {{VERSION}} 6 | https://github.com/rguske/kubernetes-appliance 7 | Networking 8 | 9 | 10 | Hostname (FQDN) of system 11 | 12 | 13 | 14 | IP Address of the system 15 | 16 | 17 | 18 | Network Prefix 19 | 20 | 21 | 22 | Gateway of the system 23 | 24 | 25 | 26 | DNS Servers (space separated) 27 | 28 | 29 | 30 | DNS Domain 31 | 32 | 33 | 34 | NTP Servers (space separated) 35 | 36 | Join as a new Worker Node 37 | 38 | 39 | Enter the FQDN or IP of the existing Control Plane Node. Example: "k8s-app-1.jarvis.tanzu" 40 | 41 | 42 | 43 | Root Password of the existing Control Plane Node 44 | 45 | Proxy Settings (optional) 46 | 47 | 48 | Enter HTTP Proxy URL followed by the port. Example: "http://proxy.provider.com:3128" 49 | 50 | 51 | 52 | Enter HTTPS Proxy URL followed by the port. Example: "https://proxy.provider.com:3128" 53 | 54 | 55 | 56 | Username for the Proxy Server 57 | 58 | 59 | 60 | Password for the Proxy User 61 | 62 | 63 | 64 | No Proxy for e.g. your internal domain suffix. Adding the appliance IP address is recommended. Comma separated (localhost, 127.0.0.1, domain.local) 65 | 66 | OS Credentials 67 | 68 | 69 | Password to login in as root. Please use a secure password 70 | 71 | zAdvanced 72 | 73 | 74 | Enable Debugging 75 | 76 | 77 | 78 | Customize POD CIDR Network (Default 10.10.0.0/16) 79 | 80 | -------------------------------------------------------------------------------- /photon-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "builder_host": "##esxi-host-ip-address##", 3 | "builder_host_username": "root", 4 | "builder_host_password": "VMware1!", 5 | "builder_host_datastore": "vsanDatastore", 6 | "builder_host_portgroup": "VM Network" 7 | } 8 | -------------------------------------------------------------------------------- /photon-version.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Photon Build for Kubernetes Appliance", 3 | "vm_name": "Kubernetes_Appliance", 4 | "iso_checksum": "5af288017d0d1198dd6bd02ad40120eb", 5 | "iso_url": "https://packages.vmware.com/photon/4.0/Rev2/iso/photon-4.0-c001795b8.iso", 6 | "numvcpus": "2", 7 | "ramsize": "4096", 8 | "guest_username": "root", 9 | "guest_password": "VMware1!" 10 | } 11 | -------------------------------------------------------------------------------- /photon.json: -------------------------------------------------------------------------------- 1 | { 2 | "min_packer_version": "1.6.3", 3 | "variables": { 4 | "k8s_app_ovf_template": "photon.xml.template", 5 | "ovftool_deploy_vcenter": "10.10.60.10", 6 | "ovftool_deploy_vcenter_username": "administrator@jarvis.lab", 7 | "ovftool_deploy_vcenter_password": "VMware1!" 8 | }, 9 | "builders": [ 10 | { 11 | "type": "vmware-iso", 12 | "vm_name": "{{ user `vm_name` }}", 13 | "guest_os_type": "Other", 14 | "version": "17", 15 | "disk_size": "25600", 16 | "boot_command": [ 17 | "", 18 | "vmlinuz initrd=initrd.img root=/dev/ram0 loglevel=3 ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/photon-kickstart.json photon.media=cdrom insecure_installation=1", 19 | "" 20 | ], 21 | "boot_wait": "10s", 22 | "headless": false, 23 | "vnc_over_websocket": true, 24 | "insecure_connection": true, 25 | "iso_url": "{{ user `iso_url` }}", 26 | "iso_checksum": "{{ user `iso_checksum` }}", 27 | "http_directory": "http", 28 | "remote_type": "esx5", 29 | "remote_host": "{{ user `builder_host` }}", 30 | "remote_datastore": "{{ user `builder_host_datastore` }}", 31 | "remote_username": "{{ user `builder_host_username` }}", 32 | "remote_password": "{{ user `builder_host_password` }}", 33 | "ssh_username": "{{ user `guest_username` }}", 34 | "ssh_password": "{{ user `guest_password` }}", 35 | "ssh_port": 22, 36 | "format": "ovf", 37 | "shutdown_command": "/sbin/shutdown -h now", 38 | "shutdown_timeout": "1000s", 39 | "vmx_data": { 40 | "numvcpus": "{{ user `numvcpus` }}", 41 | "memsize": "{{ user `ramsize` }}", 42 | "ethernet0.networkName": "{{ user `builder_host_portgroup` }}", 43 | "ethernet0.present": "TRUE", 44 | "ethernet0.startConnected": "TRUE", 45 | "ethernet0.virtualDev": "vmxnet3", 46 | "ethernet0.addressType": "generated", 47 | "ethernet0.wakeOnPcktRcv": "FALSE", 48 | "annotation": "Version: {{ user `K8S_APP_VERSION` }}" 49 | } 50 | } 51 | ], 52 | "provisioners": [ 53 | { 54 | "type": "shell", 55 | "inline": ["mkdir -p /root/config"] 56 | }, 57 | { 58 | "type": "file", 59 | "source": "k8s-app-bom.json", 60 | "destination": "/root/config/k8s-app-bom.json" 61 | }, 62 | { 63 | "type": "shell", 64 | "environment_vars": [ 65 | "K8S_APP_VERSION={{ user `K8S_APP_VERSION` }}" 66 | ], 67 | "expect_disconnect" : true, 68 | "scripts": [ 69 | "scripts/photon-settings.sh", 70 | "scripts/photon-docker.sh" 71 | ] 72 | }, 73 | { 74 | "type": "shell", 75 | "environment_vars": [ 76 | "K8S_APP_VERSION={{ user `K8S_APP_VERSION` }}" 77 | ], 78 | "pause_before": "20s", 79 | "scripts": [ 80 | "scripts/photon-containers.sh", 81 | "scripts/photon-cleanup.sh" 82 | ] 83 | }, 84 | { 85 | "type": "file", 86 | "source": "files/rc.local", 87 | "destination": "/etc/rc.d/rc.local" 88 | }, 89 | { 90 | "type": "file", 91 | "source": "files/getOvfProperty.py", 92 | "destination": "/root/setup/getOvfProperty.py" 93 | }, 94 | { 95 | "type": "file", 96 | "source": "files/setup.sh", 97 | "destination": "/root/setup/setup.sh" 98 | }, 99 | { 100 | "type": "file", 101 | "source": "files/setup-01-os.sh", 102 | "destination": "/root/setup/setup-01-os.sh" 103 | }, 104 | { 105 | "type": "file", 106 | "source": "files/setup-02-proxy.sh", 107 | "destination": "/root/setup/setup-02-proxy.sh" 108 | }, 109 | { 110 | "type": "file", 111 | "source": "files/setup-03-network.sh", 112 | "destination": "/root/setup/setup-03-network.sh" 113 | }, 114 | { 115 | "type": "file", 116 | "source": "files/setup-04-kubernetes.sh", 117 | "destination": "/root/setup/setup-04-kubernetes.sh" 118 | }, 119 | { 120 | "type": "file", 121 | "source": "files/setup-05-shell.sh", 122 | "destination": "/root/setup/setup-05-shell.sh" 123 | } 124 | ], 125 | "post-processors": [ 126 | { 127 | "type": "shell-local", 128 | "environment_vars": ["K8S_APP_VERSION={{ user `K8S_APP_VERSION` }}", "K8S_APPLIANCE_NAME={{ user `vm_name` }}", "FINAL_K8S_APPLIANCE_NAME={{ user `vm_name` }}_{{user `K8S_APP_VERSION`}}", "K8S_APP_OVF_TEMPLATE={{ user `k8s_app_ovf_template` }}"], 129 | "inline": [ 130 | "cd manual", 131 | "./add_ovf_properties.sh" 132 | ] 133 | } 134 | ] 135 | } 136 | -------------------------------------------------------------------------------- /scripts/photon-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | ## 6 | ## cleanup everything we can to make the OVA as small as possible 7 | ## 8 | 9 | # Clear tdnf cache 10 | echo '> Clearing tdnf cache...' 11 | tdnf clean all 12 | 13 | 14 | # Cleanup log files 15 | echo '> Removing Log files...' 16 | cat /dev/null > /var/log/wtmp 2>/dev/null 17 | logrotate -f /etc/logrotate.conf 2>/dev/null 18 | find /var/log -type f -delete 19 | rm -rf /var/log/journal/* 20 | rm -f /var/lib/dhcp/* 21 | 22 | # Zero out the free space to save space in the final image, blocking 'til 23 | # written otherwise, the disk image won't be zeroed, and/or Packer will try to 24 | # kill the box while the disk is still full and that's bad. The dd will run 25 | # 'til failure, so (due to the 'set -e' above), ignore that failure. Also, 26 | # really make certain that both the zeros and the file removal really sync; the 27 | # extra sleep 1 and sync shouldn't be necessary, but...) 28 | echo '> Zeroing device to make space...' 29 | dd if=/dev/zero of=/EMPTY bs=1M || true; sync; sleep 1; sync 30 | rm -f /EMPTY; sync; sleep 1; sync 31 | 32 | echo '> Setting random root password...' 33 | RANDOM_PASSWORD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;) 34 | echo "root:${RANDOM_PASSWORD}" | /usr/sbin/chpasswd 35 | 36 | echo '> Clearing history ...' 37 | unset HISTFILE && history -c && rm -fr /root/.bash_history 38 | 39 | echo '> Done' 40 | -------------------------------------------------------------------------------- /scripts/photon-containers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | echo '> Pre-Downloading Kubeadm Docker Containers' 6 | 7 | K8S_BOM_FILE=/root/config/k8s-app-bom.json 8 | 9 | for component_name in $(jq '. | keys | .[]' ${K8S_BOM_FILE}); 10 | do 11 | HAS_CONTAINERS=$(jq ".$component_name | select(.containers != null)" ${K8S_BOM_FILE}) 12 | if [ "${HAS_CONTAINERS}" != "" ]; then 13 | for i in $(jq ".$component_name.containers | keys | .[]" ${K8S_BOM_FILE}); do 14 | value=$(jq -r ".$component_name.containers[$i]" ${K8S_BOM_FILE}); 15 | container_name=$(jq -r '.name' <<< "$value"); 16 | container_version=$(jq -r '.version' <<< "$value"); 17 | crictl pull "$container_name:$container_version" 18 | done 19 | fi 20 | done 21 | 22 | mkdir -p /root/download && cd /root/download 23 | cd .. 24 | 25 | echo '> Downloading Antrea...' 26 | ANTREA_VERSION=$(jq -r < ${K8S_BOM_FILE} '.["antrea"].gitRepoTag') 27 | ANTREA_CONTAINER_VERSION=$(jq -r < ${K8S_BOM_FILE} '.["antrea"].containers | .[] | select(.name | contains("antrea/antrea-ubuntu")).version') 28 | wget https://github.com/vmware-tanzu/antrea/releases/download/${ANTREA_VERSION}/antrea.yml -O /root/download/antrea.yml -------------------------------------------------------------------------------- /scripts/photon-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eux 2 | # Copyright 2019 VMware, Inc. All rights reserved. 3 | # SPDX-License-Identifier: BSD-2 4 | 5 | ## 6 | ## Enable ContainerD 7 | ## 8 | 9 | echo '> Enabling and Configuring Containerd...' 10 | 11 | # Setup & load required kernel modules 12 | cat < Kubernetes Appliance Settings...' 10 | 11 | K8S_BOM_FILE=/root/config/k8s-app-bom.json 12 | 13 | mkdir -p /root/download && cd /root/download 14 | 15 | echo '> Disable IPv6' 16 | echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf 17 | 18 | echo '> Applying latest Updates...' 19 | cd /etc/yum.repos.d/ 20 | sed -i 's/dl.bintray.com\/vmware/packages.vmware.com\/photon\/$releasever/g' photon.repo photon-updates.repo photon-extras.repo photon-debuginfo.repo 21 | tdnf -y update photon-repos 22 | tdnf clean all 23 | tdnf makecache 24 | tdnf -y update 25 | 26 | echo '> Installing Additional Packages...' 27 | tdnf install -y \ 28 | less \ 29 | logrotate \ 30 | curl \ 31 | wget \ 32 | git \ 33 | unzip \ 34 | awk \ 35 | tar \ 36 | jq \ 37 | parted \ 38 | apparmor-parser \ 39 | sshpass 40 | 41 | echo '> Downloading Containerd' 42 | CONTAINERD_VERSION=$(jq -r < ${K8S_BOM_FILE} '.["containerd"].version') 43 | curl -L https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz -o /root/download/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz 44 | tar -zxvf /root/download/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz -C /usr 45 | rm -f /root/download/containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz 46 | containerd config default > /etc/containerd/config.toml 47 | 48 | # Update default version of the pause container to the one from VEBA BOM 49 | PAUSE_CONTAINER_NAME="registry.k8s.io/pause" 50 | PAUSE_CONTAINER_VERSION=$(jq -r --arg PAUSE_CONTAINER_NAME ${PAUSE_CONTAINER_NAME} '.kubernetes.containers[] | select(.name == $PAUSE_CONTAINER_NAME) | .version' ${K8S_BOM_FILE}) 51 | sed -i "s#sandbox_image.*#sandbox_image = \"${PAUSE_CONTAINER_NAME}:${PAUSE_CONTAINER_VERSION}\"#g" /etc/containerd/config.toml 52 | 53 | cat > /usr/lib/systemd/system/containerd.service < Adding K8s Repo' 79 | K8S_PACKAGE_REPO_VERSION_FULL=$(jq -r < "${K8S_BOM_FILE}" '.["kubernetes"].gitRepoTag') 80 | K8S_PACKAGE_REPO_VERSION=${K8S_PACKAGE_REPO_VERSION_FULL%.*} 81 | cat > /etc/yum.repos.d/kubernetes.repo << EOF 82 | [kubernetes] 83 | name=Kubernetes 84 | baseurl=https://pkgs.k8s.io/core:/stable:/${K8S_PACKAGE_REPO_VERSION}/rpm/ 85 | enabled=1 86 | gpgcheck=1 87 | gpgkey=https://pkgs.k8s.io/core:/stable:/${K8S_PACKAGE_REPO_VERSION}/rpm/repodata/repomd.xml.key 88 | exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni 89 | EOF 90 | 91 | K8S_VERSION=$(jq -r < ${K8S_BOM_FILE} '.["kubernetes"].gitRepoTag' | sed 's/v//g') 92 | 93 | # Ensure kubelet is updated to the latest desired K8s version 94 | tdnf install -y kubelet-${K8S_VERSION} kubectl-${K8S_VERSION} kubeadm-${K8S_VERSION} 95 | 96 | echo '> Creating directory for setup scripts and configuration files' 97 | mkdir -p /root/setup 98 | 99 | echo '> Creating tools.conf to prioritize eth0 interface...' 100 | cat > /etc/vmware-tools/tools.conf << EOF 101 | [guestinfo] 102 | primary-nics=eth0 103 | low-priority-nics=weave,docker0 104 | 105 | [guestinfo] 106 | exclude-nics=veth*,vxlan*,datapath 107 | EOF 108 | 109 | cat > /etc/k8s-app-release << EOF 110 | Version: ${K8S_APP_VERSION} 111 | EOF 112 | 113 | echo '> Enable contrackd log rotation...' 114 | cat > /etc/logrotate.d/contrackd << EOF 115 | /var/log/conntrackd*.log { 116 | missingok 117 | size 5M 118 | rotate 3 119 | maxage 7 120 | compress 121 | copytruncate 122 | } 123 | EOF 124 | 125 | echo '> Done' 126 | --------------------------------------------------------------------------------