예산에 맞춰 ARM을 개발하는 방법

Raspberry Pis, GitLab 및 여가 시간을 기반으로하는 Kubernetes 클러스터를 사용합니다.

침실에서 전체 컴퓨터 클러스터를 개발하는 것은 이국적이거나 복잡한 일처럼 보일 수 있습니다. 그러나 놀랍도록 다재다능한 Raspberry Pi 플랫폼을 사용하면 관심있는 땜장이라면 누구나 클러스터 자체와 같은 건물을 합리적인 예산으로 쉽게 플레이 할 수 있습니다! 모든 개발자는 버튼 클릭만으로 AWS 또는 Azure에서 일부 노드를 시작할 수 있지만, 자신의 물리적 클러스터를 개발하는 것은 그 자체로 만족 스러우며 다른 방법으로는 결코 할 수없는 것을 배울 수 있습니다. 이 쿡북이 끝나면 작지만 상당히 빠르고 안정적인 arm64 기반 Kubernetes 클러스터가 빌드 및 배포 플랫폼으로 사용하기 위해 GitLab과 쌍을 이루어 실제 클러스터를 사용할 수 있습니다.

Raspberry Pis는 간단하고 저렴하지만 실제 OS를 실행하는 실제 컴퓨터이므로 이상적인 땜질 플랫폼입니다. 다른 '실제'컴퓨터와의 주요 차이점 중 하나는 CPU 아키텍처이지만 곧 변경 될 수 있습니다. 인텔과 x86은 수년 동안 서버 및 데스크탑 시장을 장악 해 왔지만 이러한 환경이 곧 바뀔 것이라는 것을 암시하는 몇 가지 주요 움직임이 있습니다. Amazon AWS 2 세대 arm 기반 64 비트 CPU 인스턴스 유형을 출시 했으며 Canonical 은 arm 64 비트를 지원하는 Ubuntu 20을 출시 했으며 Apple 곧 출시 될 Mac 컴퓨터 용 ARM 기반 Apple 실리콘을 발표했습니다 . Raspberry Pis는 아마도 지금 ARM64에 대한 실제 경험을 얻을 수있는 가장 저렴하고 쉬운 방법을 제공 할 것입니다!

ARM 주변의 움직임은 내가 누워 있던 두 개의 Raspberry Pi로 흥미로운 일을하는 것에 대한 나의 관심을 불러 일으켰습니다. 그렇게하는 동안 모든 것이 작동하도록 노력하는 동안 몇 가지 문제가 발생했습니다.이 이야기를 문서화하려고했습니다. 저는 6 개의 Raspberry Pi를 사용하여 Kubernetes 개발 클러스터를 구성했으며,이를 웹 애플리케이션 개발 워크 플로에 통합했습니다. 이는 제가 만든 앱이 프로덕션에 출시되기 전에 제가 만든 앱을 테스트하는 개인 개발 파이프 라인의 중요한 부분을 수행 할 것입니다. 이 작품을 만들기 위해 모인 다양한 주제와 도구를 만질 것입니다. 상당히 관련되어 있으므로 사용되는 아키텍처 나 소프트웨어에 대한 심층 분석이 없습니다. 이것을 요리 책으로 사용하여 내가 만든 설정을 복제 할 수 있습니다. 일부 단계는 축약되어 있으며 명령 줄, 셸 사용 및 파일 편집과 함께 Ubuntu Server를 사용하는 데 대한 기본 지식이 있다고 가정합니다.

사용 된 재료

내 실험 설정에는 다음 하드웨어가 있습니다. 다른 자체 제작 Kubernetes 클러스터와 마찬가지로 상용 하드웨어를 사용합니다. 엔터프라이즈 급 보안이나 장애 복구없이 클라우드 제공 업체와 비교할 때 얼마나 많은 성능을 구매할 수 있는지 알아내는 것이 특히 흥미 롭습니다.

필수 하드웨어 목록 :

  • 6x Raspberry Pi 4, 4GB : 링크
  • OS 용 6x USB3 64GB 썸 드라이브 : 링크
  • 5x USB3 128GB 썸 드라이브 (데이터 볼륨 용) : 링크
  • 8 포트 기가비트 네트워크 스위치 : 링크
  • 6x 1ft Cat6 이더넷 케이블 : 링크
  • 1x micro-SD 카드 32GB, 설정시에만 사용 : 링크
  • 6x USB-C 전원 공급 장치 : 링크
  • 클러스터 케이스 : 링크
  • 옵션 : 방열판 : 링크

Pis에 전원을 공급하기 위해 6 개의 USB-C 벽면 어댑터를 사용하는 가장 저렴한 옵션을 선택했습니다. 이것은 미적으로 만족 스럽지는 않지만 케이블이 있는 USB 허브 를 사용하는 것보다 저렴 하거나 6 개의 PoE ( Power-over-Ethernet) HAT 만큼 편리합니다 .

클러스터를 구성하는 것은 매우 간단하며 몇 시간이 걸립니다. 비슷한 설정을 모아 놓은 인터넷 게시물 이 많이 있으므로 자세한 내용은 다루지 않겠습니다. 완료되면 매우 튼튼하고 작업하기 쉽습니다. 책장에 넣을 준비가되면 작은 책상 팬을 사용하여 바람을 불어 넣으십시오. 저는 5V USB 케이블에 연결된 두 개의 예비 케이스 팬을 사용합니다.이 팬은 충분한 공기 만 불어 넣고 완전히 조용합니다. 이런 식으로 내 Pis는 섭씨 50도 이상으로 거의 뜨거워지지 않습니다.

내 집에서 만든 파이 클러스터

이 설정의 일회성 비용은 USD 594.85입니다. 초기에는 비용이 많이 드는 것처럼 보이지만 클라우드 제공 업체의 클러스터를 사용하는 경우 빠르게 수익을 얻을 수 있습니다. 이 요리 책의 뒷부분에서 개요를 만들 것입니다.

이것을 합치 자.

  • 1 부 : USB에 Ubuntu 20 LTS 64 비트 설치
  • 2 부 : Kubernetes 설치
  • 파트 3 : Rook 및 Ceph를 사용하여 영구 볼륨 설정
  • 파트 4 : 서비스에 액세스하도록 MetalLB 설정
  • 5 부 : GitLab으로 워크 플로 구성
  • Part 6 : 비교 및 ​​결론

1 부 : USB에 Ubuntu 20 LTS 64 비트 설치

마이크로 SD 카드보다 훨씬 빠르기 때문에 각 Pi가 USB에서 부팅되기를 원합니다. 이 블로그 는 USB 드라이브를 사용하면 성능이 엄청나게 향상된다는 것을 보여줍니다. 또한 갑작스런 재부팅으로 데이터 손상 가능성이 훨씬 적습니다.이 문제는 제가 사용중인 스택의 참신함을 고려할 때 자주 발생할 수 있습니다. 저는 SSD 드라이브를 사용하지 않습니다. 일반 고속 USB 3 썸 드라이브가 더 저렴하고 제 경우에도 충분히 성능이 좋습니다.

이 작업을 수행하기 위해 Raspberry Pi 포럼 에서이 스레드를 따랐 습니다.

  1. Raspbian OS Lite 32 비트 ( 링크 )로 micro-SD 카드를 플래시하고 Ubuntu Server 20.04 LTS 64 비트 사전 설치된 이미지 ( 링크 )가있는 모든 OS 썸 드라이브를 플래시합니다 . balenaEtcher를 사용하여이 작업을 수행합니다 ( 링크 ).
  2. 마이크로 SD 카드 만 사용하여 Pi를 부팅하고 부트 로더를 업데이트합니다. 링크
  3. 재부팅하지 않고 OS 드라이브를 연결하고 OS 부팅 파티션을 찾아 마운트합니다.

$ sudo fdisk -l
Disk /dev/sda: 59.77 GiB, 64160400896 bytes, 125313283 sectors
Disk model: Flash Drive
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x87c6153d
Device     Boot  Start       End   Sectors  Size Id Type
/dev/sda1  *      2048    526335    524288  256M  c W95 FAT32 (LBA)
/dev/sda2       526336 125313249 124786914 59.5G 83 Linux

$ sudo mkdir -p /mount/data1
$ sudo mount /dev/sda1 /mount/data1

$ cd /mount/data1
$ sudo su
$ zcat vmlinuz > vmlinux

[pi4]
max_framebuffers=2
dtoverlay=vc4-fkms-v3d
boot_delay
kernel=vmlinux
initramfs initrd.img followkernel

$ sudo su
$ cd /boot/firmware
$ curl https://gist.githubusercontent.com/remcohendriks/6ceb8e39396aabf25db0a5322445ec8b/raw > auto_decompress_kernel

DPkg::Post-Invoke {"/bin/bash /boot/firmware/auto_decompress_kernel"; };

$ chmod +x 999_decompress_rpi_kernel

모든 파이에 대해 헹구고 반복하십시오. 매번 micro-SD 카드를 재사용 할 수 있으며 매번 사용 후 다시 플래시 할 필요가 없습니다.

다음 : Kubernetes 설치

2 부 : Kubernetes 설치

Kubernetes를 시작하고 실행하려면 상당한 사전 작업이 필요하며 이는 모든 단일 노드에 대해 반복하는 것이 지루할 수 있습니다. 따라서 Ansible과 같은 자동화 도구를 사용하여 이러한 단계에서 시간을 절약하십시오.

내가 사용했던 내 클러스터를 설정하는 게시물을. 원하는대로 몇 가지 지침을 추가하고 변경했습니다.

  1. 노드에 대한 고정 IP 주소를 설정합니다. 나중에 디버깅 할 때 유용합니다. 내 Pis에 대해 전체 IP 범위 192.168.3.0/24를 설정했으며 나중에 실행할 서비스에 대해 설정했습니다. /etc/netplan/00-installer-config.yaml다음 스 니펫을 추가 , 추가 및 변경하고 sudo netplan apply.

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0: # eth0 is the gigabit ethernet adapter.
      dhcp4: no
      addresses: [192.168.3.10/24] # I use 10-15 for my nodes.
      gateway4: 192.168.3.1 # change to your router's IP.
      nameservers:
        # change nameservers as you like:
        addresses: [192.168.3.1,8.8.8.8]
      dhcp6: no

$ sudo apt install -y docker.io
$ sudo systemctl enable --now docker
$ sudo usermod -aG docker ubuntu

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
256ab8fe8778: Pull complete
Digest: sha256:7f0a9f93b4aa3022c3a4c147a449bf11e0941a1fd0bf4a8e6c9408b2600777c5
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
[...]

{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
  • cgroup_enable=cpuset
  • cgroup_enable=memory
  • cgroup_memory=1
  • swapaccount=1

net.ifnames=0 dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 swapaccount=1

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

5. 노드에 호스트 이름을 할당합니다.이 이름은 Kubernetes에서 인식 가능한 이름으로 사용됩니다. 나는 kubernetes-master-1마스터, kubernetes-worker-[x]노드에 사용합니다. 다음 명령을 사용하여이를 수행하십시오.

$ sudo hostnamectl set-hostname kubernetes-worker-1

$ docker info
Client:
[...]
Logging Driver: json-file
Cgroup Driver: systemd
Plugins:
[...]

$ sudo apt-get update && sudo apt-get install -y apt-transport-https curl
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
$ sudo apt-get update
$ sudo apt-get install -y kubelet kubeadm kubectl

$ sudo apt-mark hold kubelet kubeadm kubectl

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.3.10:6443 --token 17b49p.jpram6b1rpj579w4     --discovery-token-ca-cert-hash sha256:ee70e44ea9f07285b10dee9c72c3ef56a93bb002eba9eb145d48666958f49801

$ kubectl get nodes
NAME                  STATUS   ROLES    AGE    VERSION
kubernetes-master-1   Ready    master   11m2s  v1.18.5

$ curl -sSL https://raw.githubusercontent.com/coreos/flannel/v0.12.0/Documentation/kube-flannel.yml | kubectl apply -f -

$ kubectl get po -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE
coredns-f9fd979d6-vb8hq           1/1     Running   0          2m31s
coredns-f9fd979d6-wtkcv           1/1     Running   0          2m31s
etcd-k8s-m-1                      1/1     Running   0          2m38s
kube-apiserver-k8s-m-1            1/1     Running   0          2m38s
kube-controller-manager-k8s-m-1   1/1     Running   0          2m38s
kube-flannel-ds-arm64-sncmx       1/1     Running   0          54s
kube-proxy-7fx9t                  1/1     Running   0          2m31s
kube-scheduler-k8s-m-1            1/1     Running   0          2m38s

$ sudo kubeadm join 192.168.3.10:6443 --token 17b49p.jpram6b1rpj579w4     --discovery-token-ca-cert-hash sha256:ee70e44ea9f07285b10dee9c72c3ef56a93bb002eba9eb145d48666958f49801

$ kubectl get po -n kube-system
NAME                              READY   STATUS    RESTARTS   AGE
coredns-f9fd979d6-vb8hq           1/1     Running   0          15m
coredns-f9fd979d6-wtkcv           1/1     Running   0          15m
etcd-k8s-m-1                      1/1     Running   0          15m
kube-apiserver-k8s-m-1            1/1     Running   0          15m
kube-controller-manager-k8s-m-1   1/1     Running   0          15m
kube-flannel-ds-arm64-s96gz       1/1     Running   0          63s
kube-flannel-ds-arm64-sncmx       1/1     Running   0          14m
kube-proxy-7fx9t                  1/1     Running   0          15m
kube-proxy-t864p                  1/1     Running   0          63s
kube-scheduler-k8s-m-1            1/1     Running   0          15m

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.3/aio/deploy/recommended.yaml

$ kubectl get po -n kubernetes-dashboard
NAME                                         READY   STATUS    RESTARTS   AGE
dashboard-metrics-scraper-7b59f7d4df-xjgvz   1/1     Running   0          68s
kubernetes-dashboard-5dbf55bd9d-qb8lr        1/1     Running   0          69s

파일 dashboard-sa.yaml을 만들고 다음과 같이 적용하십시오 kubectl apply -f dashboard-sa.yaml.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

$ kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')

대시 보드에 액세스하려면 별도의 터미널 창에서 실행하십시오.

$ kubectl proxy

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
로그인 화면, Kubernetes 문서의 이미지

토큰을 선택하고 액세스 토큰을 붙여넣고 로그인을 누르십시오. 이제 Kubernetes 대시 보드에 로그인되었습니다.

13. 마지막 단계로 대시 보드에 단순화 된 리소스 사용 통계가 표시되기를 원합니다. 이 작업은 pod에서 CPU 및 메모리 사용량을 수집하는 시스템 인 metrics-server, 그리고 Auto Scaling 정책과 같이 변경시 조치를 취하는 도구에 의해 수행됩니다. 먼저 구성을 다운로드하십시오.

$ curl -LO https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.7/components.yaml

- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --metric-resolution=30s

hostNetwork: true

$ kubectl apply -f components.yaml

$ kubectl top nodes
NAME                  CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
kubernetes-master-1   659m         16%    2172Mi          58%
kubernetes-worker-1   612m         15%    2872Mi          77%
kubernetes-worker-2   523m         13%    2685Mi          72%
kubernetes-worker-3   871m         21%    2834Mi          76%
kubernetes-worker-4   505m         12%    2402Mi          65%
kubernetes-worker-5   593m         14%    2432Mi          65%

다음 : Rook 및 Ceph를 사용하여 영구 볼륨 설정

파트 3 : Rook 및 Ceph를 사용하여 영구 볼륨 설정

이제 클러스터가 설정 및 실행되었으므로 기본 Pi Kubernetes 설정에서 소프트웨어를 호스팅 할 수 있습니다. 실제로 Kubernetes의 대부분의 프로덕션 소프트웨어 구성은 데이터베이스, 단순 파일 스토리지 등에 사용하기 위해 영구 볼륨을 사용하여 데이터를 저장합니다. 저와 같고 프로덕션 환경을 가능한 한 가깝게 시뮬레이션하려면 이것이 필요합니다. AWS, Azure 및 GCP와 같은 클라우드 솔루션은 즉시 사용 가능한 영구 볼륨을 제공하며 가용성과 성능에 크게 신경 쓰지 않고 즉시 사용할 수 있습니다. 제 경우에는 Persistent Volumes를 작동시키기 위해 Ceph 와 함께 Rook 을 사용 합니다.

설정을 위해 저는 상당히 큰 128GB 드라이브를 사용합니다. Ceph 클러스터는 가용성 목적으로 노드간에 데이터 복제를 허용하기 때문입니다. 또한 일반적으로 볼륨을 과도하게 프로비저닝합니다. 총 640GB를 사용할 수 있지만 30GB 볼륨을 구성하면 가용성을 보장하기 위해 90GB가 프로비저닝됩니다. 이는 노드 장애시 유용합니다. 그러나 사용 가능한 640GB보다 훨씬 더 많이 할당하여 Ceph 클러스터를 오버 프로비저닝 할 수 있습니다. 볼륨을 채우면 클러스터가 불안정해질 수 있습니다.

Ceph로 Rook을 설정하는 것은 간단하지만 arm64에서 작동하려면 몇 가지 구성 변경이 필요합니다. Rook with Ceph 빠른 시작 설명서를 사용합니다 .

  1. 데이터 저장 용도로 예약 된 USB 썸 드라이브를 연결합니다. 이것은 각 작업자 노드의 두 번째 USB3 포트로 이동합니다. 다음을 입력하여 운영 체제에서 감지되는지 확인하십시오.

$ sudo lsblk -f
NAME   FSTYPE   LABEL       UUID                                 FSAVAIL FSUSE% MOUNTPOINT
[...]
sda
├─sda1 vfat     system-boot B726-57E2                             105.7M    58% /boot/firmware
└─sda2 ext4     writable    483efb12-d682-4daf-9b34-6e2f774b56f7   52.3G     7% /
sdb
├─sdb1 vfat     EFI         67E3-17ED
└─sdb2 vfat     UNTITLED    C29D-16F3

$ sudo sgdisk --zap-all /dev/sdb

$ sudo lsblk -f
NAME   FSTYPE   LABEL       UUID                                 FSAVAIL FSUSE% MOUNTPOINT
[...]
sda
├─sda1 vfat     system-boot B726-57E2                             105.7M    58% /boot/firmware
└─sda2 ext4     writable    483efb12-d682-4daf-9b34-6e2f774b56f7   52.3G     7% /
sdb

$ git clone --single-branch --branch release-1.4 https://github.com/rook/rook.git
$ cd rook/cluster/examples/kubernetes/ceph

ROOK_CSI_ALLOW_UNSUPPORTED_VERSION: "true"

ROOK_CSI_CEPH_IMAGE: "raspbernetes/ceph-csi:v3.1.0-arm64"
ROOK_CSI_REGISTRAR_IMAGE: "raspbernetes/csi-node-driver-registrar:1.3.0"
ROOK_CSI_RESIZER_IMAGE: "raspbernetes/csi-external-resizer:0.5.0"
ROOK_CSI_PROVISIONER_IMAGE: "raspbernetes/csi-external-provisioner:1.6.0"
ROOK_CSI_SNAPSHOTTER_IMAGE: "raspbernetes/csi-external-snapshotter:2.1.1"
ROOK_CSI_ATTACHER_IMAGE: "raspbernetes/csi-external-attacher:2.2.0"

$ kubectl create -f common.yaml
$ kubectl create -f operator.yaml

## verify the rook-ceph-operator is in the `Running` state before proceeding
$ kubectl -n rook-ceph get pod
NAME                                  READY   STATUS    RESTARTS   AGE
rook-ceph-operator-775d4b6c5f-pf79c   1/1     Running   0          2m46s
rook-discover-kgssc                   1/1     Running   0          45s

$ kubectl create -f cluster.yaml
## to verify:
$ kubectl -n rook-ceph get pod

4. 블록 풀 및 스토리지 클래스를 설정하고 기본값으로 설정합니다. 로부터 ceph디렉토리 입력 :

$ kubectl apply -f csi/rbd/storageclass.yaml

$ kubectl patch storageclass rook-ceph-block -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

5. Rook with Ceph 문서의 예제를 사용하여 설정을 테스트합니다. 에서 cluster/examples/kubernetes폴더를 열고 mysql.yaml, 당신의 편집기로 위치를 wordpress-mysql배포합니다. 아래 spec.template.spec.containers의 변경 image: mysql:5.6image: mariadb. 불행히도 mysql은 아직 arm64를 지원하지 않습니다.

마찬가지로 wordpress.yaml에서 wordpress배포 이미지를에서 wordpress:4.6.1-apache로 변경합니다 wordpress:5-apache.

그런 다음 구성을 적용하십시오.

$ kubectl create -f mysql.yaml
$ kubectl create -f wordpress.yaml

$ kubectl get pvc
NAME             STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
mysql-pv-claim   Bound     pvc-95402dbc-efc0-11e6-bc9a-0cc47a3459ee   20Gi       RWO           1m
wp-pv-claim      Bound     pvc-39e43169-efc1-11e6-bc9a-0cc47a3459ee   20Gi       RWO           1m

이제 Pi 클러스터가 영구 볼륨과 함께 사용하도록 설정되었습니다.

더 자세한 구성 옵션을 찾고 있다면 게시물에서 더 많은 정보를 다룹니다.

다음 : 서비스에 액세스하도록 MetalLB 설정

파트 4 : 서비스에 액세스하도록 MetalLB 설정

이제 Pi 클러스터가 설정되고 데이터 저장소에 영구 볼륨을 사용할 수 있으므로 로컬 네트워크의 다른 컴퓨터에서 클러스터에서 실행중인 서비스에 액세스하려고합니다. 현재 모든 구성된 서비스는 Pis 자체에서만 액세스 할 수있는 cluster-ip를 얻습니다. 이전 단계에서 실행중인 WordPress 예제가 여전히있는 경우 다음을 사용하여 확인할 수 있습니다.

$ kubectl get svc wordpress
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
wordpress   LoadBalancer   10.99.152.63   <pending>     80:30763/TCP   4m

MetalLB는 서비스를 .NET Framework로 구성 할 때 클라우드 공급 업체가 수행하는 것처럼 자동으로 생성되는 '가상'로드 밸런서 역할을합니다 type: LoadBalancer. MetalLB를 사용하면 물리적 외부로드 밸런서가 프로비저닝되지 않지만 사실상 클러스터 자체 내에서 수행됩니다. arm64에서 작동하며 개발 Pi 클러스터에 적합합니다.

MetalLB는 또한 내 UniFi 보안 게이트웨이 라우터와 통신하는 BGP를 사용하여로드 밸런싱 기능을 지원합니다. 구성하고 사용해 보는 것은 매우 흥미롭지 만 개발 요구 사항에 맞게 고성능 환경을 구성하는 데는 관심이 없습니다.

MetalLB 설정은 쉽고 구성이 거의 필요하지 않습니다. 자세한 블로그 게시물의 스 니펫을 사용 합니다.

이 설정에 대한 유일한 요구 사항은 Pis의 네트워크에 예비 할 작은 범위의 IP 주소가 있다는 것입니다. 제 경우에는 192.168.3.100 — 192.168.3.199.

  1. MetalLB Kubernetes 매니페스트를 적용하십시오. arm64와 함께 사용할 준비가되었으며 변경할 필요가 없습니다.

$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
$ kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.3.100-192.168.3.199 # change this to your own range

$ kubectl apply -f metallb-config.yaml

$ kubectl get svc wordpress
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
wordpress   LoadBalancer   10.99.152.63   192.168.3.100   80:30763/TCP   5m
글쎄, 그것은 예기치 않게하기 쉬웠다.

다음 : GitLab으로 워크 플로 구성

5 부 : GitLab으로 워크 플로 구성

내 클러스터와 도구가 개발 클러스터로 사용할 준비가 된 시점에 도달했습니다. 요약하면 다음이 필요합니다.

  • USB에서 Ubuntu Server 20.04 LTS 부팅을 실행하는 Pis 6 개 (마스터 1 개와 작업자 5 개로 구성된 Kubernetes 클러스터 포함)
  • 메트릭 서버가있는 Kubernetes 대시 보드
  • 다른 USB 썸 드라이브를 사용하는 Rook 및 Ceph의 영구 볼륨
  • 서비스에 네트워크 IP 주소를 할당하기위한 MetalLB
  • 저장소를 호스팅하는 GitLab (비공개 또는 공개)

이 가이드에서는 풀 스택 JavaScript 웹 앱을 예로 사용합니다. 이것은 NodeJS API를 백엔드로, Angular.IO 앱을 프론트 엔드로 구성합니다. 둘 다 고유 한 도커 이미지를 가지고 있으며 파이프 라인에서 구축하고 있습니다.

이전에 일반적인 파이프 라인은 다음과 같습니다.

  • 이미지 아티팩트 빌드, 'develop'태그, 이미지 레지스트리로 푸시
  • 개발 클러스터에 'develop'Kubernetes 구성 배포
  • 개발 클러스터에서 소프트웨어를 검사하십시오. 만족하면 계속 파이프 라인
  • 'develop'이미지에 'production'태그 추가, 이미지 레지스트리로 푸시
  • 프로덕션 클러스터에 '프로덕션'Kubernetes 구성 배포

무료 GitLab 러너는 크로스 아키텍처 빌드를 지원할 수 있지만 buildx 기능을 사용하는 것은 실험적 이며 이미지 빌드에 상당한 시간이 소요될 수 있으며, 이는 프리 티어에서 제한적 이므로이를 탐색하지 않습니다 . arm64 쿼드 코어가있는 Pi가 6 개 있는데 대신 사용하는 것이 어떻습니까?

따라서 파이프 라인의 변경은 간단합니다.

  • arm64 이미지 아티팩트를 빌드하고 'arm64-develop'태그를 지정하고 이미지 레지스트리로 푸시합니다.
  • 개발 클러스터에 'develop'Kubernetes 구성 배포
  • 개발 클러스터에서 소프트웨어를 검사하십시오. 만족하면 파이프 라인을 계속하십시오.
  • x86 이미지 아티팩트 빌드, 'x86-production'태그, 이미지 레지스트리로 푸시
  • 프로덕션 클러스터에 '프로덕션'Kubernetes 구성 배포

이 작업을 수행하려면 Pi 클러스터에서 gitlab-runner 및 docker-in-docker를 구성하고 설치해야합니다. 전자의 경우 공식 Helm 차트를 수정하고 사용합니다 . 단계는 다음과 같습니다.

  1. Helm을 설치합니다 (아직 설치하지 않은 경우). 운영 체제에 적합한 방법을 선택 하려면 설명서 를 참조하십시오. 간단하고 보편적 인 방법은 로컬 설치입니다.

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh

$ curl -s https://gist.githubusercontent.com/remcohendriks/339594095369db210429fdcb3cdf4434/raw -o values.yaml
실수로 클러스터에 부담을주지 않도록 동시성을 10에서 2로 줄입니다. 특히 Pi 클러스터에 대한 작업을 예약하기 위해 러너에 'arm64'를 태그합니다. 태그없는 파이프 라인을 비활성화하여 x86에서 실행되는 '일반적인'파이프 라인 작업을 유지합니다. Docker-in-docker 실행을 허용하려면 컨테이너 권한을 활성화합니다.

자체 GitLab 서버를 실행하는 경우 19 행에서 등록 URL을 변경해야합니다.

나는 특별히이 시점에서 캐싱을 설정하지 않는다. 몇 가지 방법을 시도해 보았고 도커 계층 캐싱에 대해 작동하는 GitLab 실행기 옆에 공유 docker-in-docker 서비스를 사용하여 캐싱을 선택했습니다. 파이프 라인 단계간에 아티팩트 캐싱을 원하는 경우 클러스터에서 Minio 를 사용하여 S3 캐시를 설정할 수 있습니다.

다른 모든 구성 옵션은 GitLab 실행기 구성 페이지를 참조하세요 .

4. Helm과 함께 gitlab-runner를 설치합니다. 먼저 GitLab 저장소를 추가합니다.

$ helm repo add gitlab https://charts.gitlab.io

$ kubectl create ns gitlab-runner 
$ helm install --namespace gitlab-runner gitlab-runner -f values.yaml gitlab/gitlab-runner
두 개의 비공개 그룹 러너 설정, 하나는 arm64 용, 다른 하나는 x86 용 (여기서는 arm64라고 함)

4. 실행자가 Kubernetes 리소스를 관리 할 수 ​​있도록 서비스 계정을 설정합니다. 이미지를 빌드하는 데 필요하지는 않지만 클러스터에서 리소스를 설정하는 데에도 러너를 사용합니다. 에서 values.yaml파일 서비스 계정 이름이 라인 (271)에 gitlab-sa, 아직 존재하지 않는다. 다음을 적용하여 역할을 추가하십시오.

$ kubectl apply -f https://gist.githubusercontent.com/remcohendriks/c6529d3b5d14be2cf7d41f7881808f72/raw

# Don't do this in production:
$ kubectl apply -f https://gist.githubusercontent.com/remcohendriks/b7423c888879af05bb8debb722dd39a3/raw

내 프런트 엔드 Angular.IO 앱에서 노드 엉덩이 를 종속성으로 사용합니다. GitHub 에서 릴리스 아티팩트 페이지 를 찾아 보면 x86 용으로 사전 빌드 된 바인딩이 있지만 arm64 용은 아닙니다. x86 컴퓨터에 node-sass를 설치할 때 올바른 바인딩을 자동으로 감지하고 다운로드합니다. 목록에없는 아키텍처에서는 node-gyp를 사용하여 자체 바인딩을 빌드합니다 . 이것의 장점은 운영 체제에 Python 및 일반 빌드 도구가 설치되어 있으면 자동으로 빌드된다는 것입니다 (예 : Ubuntu의 경우 빌드 필수 , 알파인의 경우 빌드 기반 ). 이것의 나쁜 점은 구축 하는 데 영원히 걸린다는 입니다. 아래는 빌드 로그의 일부입니다.

Step 5/10 : RUN npm ci
---> Running in 1a2cb8ad725a
[...]
> node-sass@4.12.0 install /app/node_modules/node-sass
> node scripts/install.js
Downloading binary from https://github.com/sass/node-sass/releases/download/v4.12.0/linux-arm64-72_binding.node
Cannot download "https://github.com/sass/node-sass/releases/download/v4.12.0/linux-arm64-72_binding.node":
HTTP error 404 Not Found
[...]
> node-sass@4.12.0 postinstall /app/node_modules/node-sass
> node scripts/build.js
Building: /usr/local/bin/node /app/node_modules/node-gyp/bin/node-gyp.js rebuild --verbose --libsass_ext= --libsass_cflags= --libsass_ldflags= --libsass_library=
[... 800 lines of gyp build log ...]
gyp info ok
Installed to /app/node_modules/node-sass/vendor/linux-arm64-72/binding.node
[...]
added 1986 packages in 737.857s

Step 5/10 : RUN npm ci
---> Running in 6124215968d3
[...]
> node scripts/install.js
Downloading binary from https://github.com/sass/node-sass/releases/download/v4.12.0/linux_musl-x64-72_binding.node
Download complete
Binary saved to /app/node_modules/node-sass/vendor/linux_musl-x64-72/binding.node
Caching binary to /root/.npm/_cacache/node-sass/4.12.0/linux_musl-x64-72_binding.node
[...]
added 1986 packages in 18.086s
그것은 한 잔 이상의 커피입니다.

필자는 Pi 클러스터에서 작동하는 빌드 단계에서 도커 레이어 캐싱이 절실히 필요합니다.

불행히도 GitLab 의 문제 게시물 은 오래되고 길며 분산 실행기에서는 실제로 해결되지 않았습니다. 의 방향 공식 가이드는 정확하지만, 캐시는 각 실행 후에서의 포드와 함께 삭제됩니다 때문에 어느 분산 주자 작동하지 않습니다. 문제 게시물 중간에 실행 가능한 솔루션이 언급되어 있으며 클러스터의 gitlab-runner와 함께 별도의 docker-in-docker 서비스를 사용합니다.

기본적으로 Docker 이미지는 영구적으로 실행되는 docker-in-docker 서비스에 의해 빌드되며 캐시로 사용하기 위해 영구 저장소로 지원됩니다. 각 파이프 라인 작업은 docker-in-docker 서비스를 호스트로 사용하여 컴퓨팅 리소스를 공유하는 동시에 여러 작업을 병렬로 실행할 수 있습니다.

docker-in-docker 서비스를 설정하려면 다음 요점을 적용하십시오 .

$ kubectl apply -f https://gist.githubusercontent.com/remcohendriks/abb6bee55952837f33debe13882b7cf2/raw/

이제 러너와 캐시가 설정되었습니다. 다음 단계에서는 캐시 서비스의 속도 업그레이드와 함께 작동하는지 확인합니다.

6. arm64 빌드 작업을 설정하고 실행되는지 확인합니다. 앞서 언급했듯이 빌드 할 이미지가 두 개 있습니다. 하나는 백엔드 용이고 다른 하나는 프런트 엔드 용입니다. 간결함을 위해 두 가지 중 가장 복잡한 프런트 엔드에 대한 빌드 작업으로 만 시연합니다.

.gitlab-ci.yaml파일 (간결성을 위해 단계를 생략) :

image: docker:19.03.12
services:
- docker:19.03.12-dind
stages:
- build-dev
build-app-dev:
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
stage: build-dev
  variables:
    DOCKER_HOST: tcp://dind:2375 # dind = service in k8s
  script:
    - docker build --build-arg CONFIGURATION=dev -t $CI_REGISTRY_IMAGE/app:arm64-develop ./app
    - docker push $CI_REGISTRY_IMAGE/app:arm64-develop
  tags:
    - arm64

$ docker build --build-arg CONFIGURATION=dev -t $CI_REGISTRY_IMAGE/app:arm64-develop ./app
Step 1/11 : FROM node:12-alpine AS build
---> 137cb187b393
Step 2/11 : ARG CONFIGURATION=development
---> Using cache
---> 0b993343edde
Step 3/11 : RUN apk add python-dev build-base
---> Using cache
---> c98ae4b33fea
Step 4/11 : WORKDIR /app
---> Using cache
---> d90bda790614
Step 5/11 : COPY package*.json ./
---> Using cache
---> c28772974aba
Step 6/11 : RUN npm ci
---> Using cache
---> 4884a88c4d1c
Step 7/11 : COPY . .
---> 28ac0277648b
Much better.

다음 : 비교 및 ​​결론

Part 6 : 비교 및 ​​결론

이 설정이 내 일상 워크 플로에 얼마나 잘 통합되는지 알아보기 위해 몇 주 동안 여러 앱을 동시에 실행하는 데 사용했습니다. 성능을 비교하기 위해 arm64 또는 x86 인스턴스를 사용하여 두 개의 AWS EKS 클러스터에 대해 Pi 클러스터를 벤치마킹했습니다. 여기서 목표는 경제성을 보는 것이었기 때문에 고정 된 가격으로 구입할 수있는 하드웨어에 집중했습니다. 유일한 추가 제약은 하드웨어에 4GB 이상의 메모리가 있어야한다는 것입니다. 그렇지 않으면 클러스터를 제가 살펴본 사용 사례에 사용할 수 없습니다.

기준

를 사용하여 클러스터의 성능을 sysbench테스트하고 환경의 CPU, 메모리 및 I / O 성능을 테스트합니다. .NET의 단일 포드에서 실행됩니다 ubuntu:latest. 베어 메탈을 실행하지 않고 하나의 노드에서 작동하는 간단한 테스트입니다. 전체 클러스터의 성능을 반영하지는 않지만 Kubernetes 클러스터에서 작동하도록 구성된 Pi에서 얻을 수있는 것을 보여줍니다.

테스트의 클러스터 :

  • Pi 4 4GB를 사용하여이 쿡북에 내장 된 내 Pi 클러스터
  • m6g.medium arm64 인스턴스, 1 개의 vCPU 및 4GB 메모리가있는 AWS EKS 클러스터
  • t3.medium x86 인스턴스, 2 개의 vCPU 및 4GB 메모리가있는 AWS EKS 클러스터

vCPU 수는 클러스터마다 다르지만 고정 된 4GB 메모리 제약 조건으로 예산 조건을 시뮬레이션하고 싶습니다. m6g.medium 및 t3.medium은 모두 매월 USD 31 및 USD 33의 비용이 드는 최소 실행 가능한 선택입니다 (주문형, EBS 제외, eu-west-1).

임시 쉘을 실행하려면 다음을 수행하십시오.

$ kubectl run my-shell --rm -i --tty --image ubuntu:latest -- bash

$ apt update && apt install -y sysbench

# CPU benchmark:
$ sysbench cpu run --threads=4 --time=60 --cpu-max-prime=20000
# Memory benchmark:
$ sysbench memory run --memory-block-size=1M --memory-total-size=100G --time=60 --memory-oper=read
$ sysbench memory run --memory-block-size=1M --memory-total-size=100G --time=60 --memory-oper=write
# I/O benchmark:
$ sysbench --test=fileio --file-total-size=100M --file-extra-flags=direct --file-num=10 prepare
$ sysbench fileio run --file-num=10 --file-total-size=100M --file-test-mode=rndrw --file-extra-flags=direct --time=60

+--------------------+---------+------------+-----------+
|                    |  Pi 4   | m6g.medium | t3.medium |
+--------------------+---------+------------+-----------+
| CPU events/s       | 1973.60 |    1063.70 |    658.89 |
| Memory read MiB/s  | 4781.64 |   27168.50 |  20818.73 |
| Memory write MiB/s | 3203.49 |   12367.71 |  15289.32 |
| IO read MiB/s      |    3.89 |      23.40 |     19.63 |
| IO write MiB/s     |    2.59 |      15.60 |     13.08 |
+--------------------+---------+------------+-----------+

이 비교에서 가장 큰 차이점은 하드웨어의 품질입니다. 내 Pi 클러스터는 저렴하고 전력 효율적인 구성 요소로 만들어졌지만 AWS 인스턴스는 엔터프라이즈 급입니다. 이것은 특히 하드 디스크의 경우에 해당되며 저렴한 USB 썸 드라이브는 AWS의 고속 SSD EBS 드라이브보다 최소 5 배 느립니다.

CPU 성능에 만족합니다. 비록 그것은 t3.medium의 2 배의 코어와 m6g.medium의 4 배의 코어를 가지고 있지만; 거의 두 배 더 빠릅니다. 예산 클러스터에는 나쁘지 않습니다.

비용

상용 클라우드 공급자에서 실행하는 경우 개발 클러스터를 소유하는 데 비용이 많이들 수 있습니다. 쿠 버네 티스를 탐색하고 가끔 땜질하는 경우, 연중 무휴 24 시간 가용성을 갖춘 설정은 내가 가진 요구에 비해 상당히 비쌉니다. 내 Pi 클러스터의 실행 비용을 AWS의 예산 제안과 비교합니다. 비교 세부 사항 :

  • eu-west-1 지역을 기반으로하는 AWS 요금을 사용합니다.
  • AWS EKS의 고정 요금은 클러스터 당 시간당 0.10 USD이지만 전용 인스턴스가 클러스터 마스터로 작동 할 필요는 없습니다.
  • 작업자 노드 당 루트 볼륨으로 64GB의 EBS 스토리지를 추가하지만 요청시 작동하므로 영구 볼륨에 대한 사전 스토리지는 없습니다. 스냅 샷이 없습니다.
  • 탄력적 IP 또는로드 밸런서가 없습니다. GitLab 설정에서 이미지를 빌드하는 데 필요하지 않습니다.
  • 데이터 전송 비용이 없습니다. 이것은 무시해도 좋습니다.
  • 다른 AWS 관련 EKS 기능이 활성화되지 않았습니다.
  • 주문형 가격.

5x Linux on m6g.medium @ 100% usage/mo, on-demand: USD 140.95
5x 64 GB EBS general-purpose SSD (gp2) per month:  USD  32.00
1x AWS EKS cluster @ USD 0.10/hr, per month:       USD  73.20
Total cost of services:                            USD 246.15

기타 관찰

일부는 사전 빌드 된 바이너리가 없기 때문에 arm64와 함께 작동하도록 패키지 종속성을 변경하는 것은 번거 롭습니다. 일반적인 수정은 gcc 또는 Python과 같은 필수 컴파일러를 추가하는 것입니다. 그러나 하나의 Dockerfile에서 x86 및 arm64 이미지를 모두 빌드한다는 것은 한 플랫폼 또는 다른 플랫폼에서 불필요하다는 것을 의미합니다. Dockerfile을 플랫폼 버전으로 분리하면 작동 할 수 있지만 유지 관리가 중복됩니다.

많은 종속성이있는 이미지를 빌드하는 동안 노드가 응답하지 NotReady않고 kubectl get nodes명령 처럼 표시됩니다 . ssh를 사용하여 연결할 수 없었고 15 분 동안 기다렸다가 다시 반응하는 것을 확인했습니다. 걱정할 것이 없습니다.

더 심각한 상황을 테스트하기 위해 작업자 노드 중 하나에서 전원 플러그를 뽑았습니다. 위에서 설명한 상황과 마찬가지로 NotReady다시 연결할 때까지 보고되었습니다 . 이미지 빌드 작업이 예상대로 실패하여 수동으로 다시 시작해야합니다. 약 5 분 후 모든 것이 다시 작동했습니다.

결론 및 다음 단계

이 Pi Kubernetes 클러스터가 어떻게 작동했는지에 대해 매우 만족합니다. 가장 큰 장점은이 설정이 얼마나 신뢰할 수 있는지였습니다. 클러스터가 가동되고 실행되면 긴 개발 세션 동안 확실하게 사용할 수 있으므로 클러스터가 실패 할 것이라는 걱정없이 클러스터에서 아티팩트를 멈출 수 있습니다. 이 문서를 작성하고 몇 주 동안 적극적으로 사용하면서이 클러스터를 여러 번 구축하는 과정을 거쳤으므로 작업하고 놀 수있는 훌륭한 도구로 추천 할 준비가되었습니다.

Kubernetes를 설치하고 리소스를 설정 한 후 다음으로 시도 할 흥미로운 작업이 많이 있습니다. 이 요리 책에서 다루지 않은 몇 가지 주제 :

Craig Dennis 님의 사진 , 출처 : Pexels

검토해 주신 Shabaz Sultan에게 감사드립니다.

Suggested posts

데이터 포인트가 얼마나 극단적입니까?

특이 치 및 모델 선택

데이터 포인트가 얼마나 극단적입니까?

이상치 및 모델 선택 회귀를 실행할 수있는 것은 하나이지만 올바른 모델과 올바른 데이터를 선택할 수 있다는 것은 또 다른 문제입니다. 곡선의 맨 끝에있는 데이터 포인트가 실수로 여분의 제로 (인간 오류)를 기록한 사람 또는 블랙 스완 이벤트 (드물지만 중요한 이벤트)에서 가져온 것임을 어떻게 알 수 있습니까? 회귀 모델에 유지하면서 여전히 작동하는 예측을 가질 수 있습니까? 이 기사에서 알아 보자.

Express.js 시작하기

Express.js 시작하기

Express는 웹 및 모바일 앱을 만드는 경험을 즐겁게 만드는 기능 세트가 포함 된 Node.js 프레임 워크입니다.

Related posts

"실용적인 프로그래머"의 5 가지 필수 사항

역대 베스트셀러 코딩 북의 요점

"실용적인 프로그래머"의 5 가지 필수 사항

Pragmatic Programmer는 1999 년에 처음 출판되었으며 이후 역대 최고의 프로그래밍 책으로 선정되었습니다. 저자 Andy Hunt와 David Thomas는 Agile Manifesto의 원저자 중 하나였으며 몇 가지 심각한 자격을 가지고 있습니다.

대규모 GraphQL 쿼리 공격으로부터 보호

공격자가 공개적으로 사용 가능한 GraphQL 인터페이스를 사용하여 사이트를 스크랩하거나 서비스 거부 공격을 실행하는 방법에 대해 알아보십시오. 이들은 4 가지 방법 중 하나로이를 수행 할 수 있습니다. 단일 대형 쿼리를 신중하게 구성하여 실행하고, 관련 데이터를 가져올 수있는 병렬 쿼리를 많이 작성하고, 일괄 요청을 사용하여 많은 쿼리를 연속적으로 실행하고, 마지막으로 많은 요청을 보냅니다.

기술 인터뷰의 사회적 구성 요소

코딩 문제는 스트레스가 많지만 스트레스에 대한 당신의 반응은 당신의 기술적 능력보다 더 크게 말합니다.

기술 인터뷰의 사회적 구성 요소

기술 업계의 직책을 위해 인터뷰 할 때 일반적으로 제안을 고려하기 전에 최소한 3 차례의 인터뷰를 거치게됩니다. 라운드는 일반적으로 다음과 같습니다. 그렇게 생각하면 잘못된 것입니다.

훌륭한 개발자의 3 가지 행동 특성

훌륭한 개발자의 3 가지 행동 특성

훌륭한 개발자를 만드는 비 기술적 인 것들 나는이 기사를 작성하는 것을 한동안 미루고 있습니다. 나는 그것을 작성할 자격이 있다고 생각하지 못했습니다. 오늘은 쓸 때라고 생각했습니다.