macOS工作室

macOS工作室起步 我思考了不同的构建工作室的基础环境,例如虚拟化和容器化。我个人一直在不同的Linux环境和macOS环境中切换(有太多的设备和安装部署),每次起步初始化确实也花费了不少时间。例如我有机会能短暂使用 Apple ARM架构芯片M1 Pro ,但是很快又得切换回 Arch LinuxMacBook Pro 15” Late 2013

理想的工作方式是全面采用容器化技术( Docker Atlas )来构建,并结合 kind(本地docker模拟k8s集群) 模拟 Kubernetes Atlas ,这样在不同的工作环境中,只要采用合适的镜像( 从Dockerfile构建Docker镜像 )以及基础运行环境,就能够无缝切换。

Homebrew

要实现完整的可移植工作环境(和 Linux Atlas 对齐),在macOS环境中使用 Homebrew 来构建基础环境:

  • 安装 homebrew :

通过网络安装Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • 安装必要工具:

在macOS新系统必装的brew软件
brew update
brew install pssh vim tmux tree webp gawk gsed openconnect nginx
brew install --cask iterm2
brew install --cask docker
brew install golang
  • 切换python3版本:

切换macOS的python3版本到homebrew提供的版本
cd /opt/homebrew/bin
ln -s python3.11 python3
ln -s python3.11 python
ln -s pip3.11 pip3
ln -s pip3.11 pip

Python virtualenvSphinx文档

虚拟沙箱环境非常简单:

venv初始化
cd ~
python3 -m venv venv3
  • 激活:

激活venv
source venv3/bin/activate
  • 安装Sphinx 以及 rtd :

通过virtualenv的Python环境安装sphinx doc
pip install sphinx
pip install sphinx_rtd_theme
pip install sphinxnotes-strike

# 支持视频、YouTube和中文搜索,安装组件和配置
pip install sphinxcontrib-video
pip install sphinxcontrib-youtube
pip install jieba

Kubernetes Atlas 模拟

通过Homebrew安装Docker Desktop for macOS
brew install --cask docker
在macOS平台上安装kind
brew install kind kubectl
  • 3个管控节点,5个工作节点的集群,并且结合本地registry,采用 kind集群本地Registry 方法 kind-with-registry-macos.sh 脚本:

运行Registry适配kind集群(dev),macOS环境的Docker Desktop for macOS
#!/bin/sh
set -o errexit

CLUSTER_NAME='dev'
# create registry container unless it already exists
reg_name="${CLUSTER_NAME}-registry"
reg_port='5001'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
  docker run \
    -d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" \
    registry:2
fi

# create a cluster with the local registry enabled in containerd
cat <<EOF | kind create cluster --name ${CLUSTER_NAME} --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: worker
- role: worker
- role: worker
- role: worker
containerdConfigPatches:
- |-
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
    endpoint = ["http://${reg_name}:5000"]
EOF

# connect the registry to the cluster network if not already connected
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
  docker network connect "kind" "${reg_name}"
fi

# Document the local registry
# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: local-registry-hosting
  namespace: kube-public
data:
  localRegistryHosting.v1: |
    host: "localhost:${reg_port}"
    help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF
通过 kind_static_ips.sh 脚本设置kind集群每个node静态IP
#!/usr/bin/env bash
set -e

CLUSTER_NAME='dev'
reg_name="${CLUSTER_NAME}-registry"

# Workaround for https://github.com/kubernetes-sigs/kind/issues/2045
# all_nodes=$(kind get nodes --name "${CLUSTER_NAME}" | tr "\n" " ")
# 我将 registry 也加入了列表指定静态IP
all_nodes=$(kind get nodes --name "${CLUSTER_NAME}" | tr "\n" " ")${reg_name}
declare -A nodes_table
ip_template="{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}"

echo "Saving original IPs from nodes"

for node in ${all_nodes}; do
  #nodes_table["${node}"]=$(docker inspect -f "${ip_template}" "${node}")
  #registry有2个网络接口,这里采用了一个比较ugly的方法,过滤掉不属于kind网络的接口IP
  nodes_table["${node}"]=$(docker inspect -f "${ip_template}" "${node}" | sed 's/172.17.0.2//')
  echo "${node}: ${nodes_table["${node}"]}"
done

echo "Stopping all nodes and registry"
docker stop ${all_nodes} >/dev/null

echo "Re-creating network with user defined subnet"
subnet=$(docker network inspect -f "{{(index .IPAM.Config 0).Subnet}}" "kind")
echo "Subnet: ${subnet}"
gateway=$(docker network inspect -f "{{(index .IPAM.Config 0).Gateway}}" "kind")
echo "Gateway: ${gateway}"
docker network rm "kind" >/dev/null
docker network create --driver bridge --subnet ${subnet} --gateway ${gateway} "kind" >/dev/null

echo "Assigning static IPs to nodes"
for node in "${!nodes_table[@]}"; do
  docker network connect --ip ${nodes_table["${node}"]} "kind" "${node}"
  echo "Assigning IP ${nodes_table["${node}"]} to node ${node}"
done

echo "Starting all nodes and registry"
docker start ${all_nodes} >/dev/null

echo -n "Wait until all nodes are ready "

while :; do
  #[[ $(kubectl get nodes | grep Ready | wc -l) -eq ${#nodes_table[@]} ]] && break
  #需要启动的k8s节点比docker的容器列表少2 (registry和haproxy没有包含在k8s)
  pod_num=`expr ${#nodes_table[@]} - 2`
  [[ $(kubectl get nodes | grep Ready | wc -l) -eq ${pod_num} ]] && break
  echo -n "."
  sleep 5
done

echo