├── .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 | [](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 | [](https://twitter.com/lamw)
29 |
30 | ## :eyeglasses: Overview
31 |
32 | This repository contains the necessary code to build a [](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 |
83 |
84 | ## Deployment Options Appliance
85 |
86 | 
87 |
88 | 
89 |
90 | 
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 |
97 |
98 |
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 |
--------------------------------------------------------------------------------