הצהרת בעיה
אתגר
ארגונים המפעילים יישומים בקונטיינרים ב-Kubernetes לעיתים קרובות צריכים ללכוד ולשמור את מצב הקונטיינרים הפועלים עבור:
- שחזור מאסון
- מיגרציה של יישומים
- ניפוי בעיות/פתרון בעיות
- שימור מצב
- שחזור סביבות
עם זאת, אין דרך פשוטה ומאוטומטית:
- ליצור נקודות בדיקה לקונטיינרים לפי דרישה
- לאחסן את נקודות הבדיקה בפורמט סטנדרטי
- להפוך אותן לנגישות בקלות בין אשכולות
- להפעיל נקודות בדיקה דרך ממשק סטנדרטי
מגבלות נוכחיות
- יצירת נקודות בדיקה ידנית דורשת גישה ישירה לאשכול
- אין פורמט אחסון סטנדרטי עבור נקודות בדיקה
- אינטגרציה מוגבלת עם רישומי קונטיינרים
- חוסר בגישה תכנותית עבור אוטומציה
- תיאום מורכב בין containerd למערכות אחסון
פתרון
שירות צד של Kubernetes ש:
- מחשף את פונקציית נקודות הבדיקה דרך API REST
- ממיר אוטומטית נקודות בדיקה לדימויים התואמים ל-OCI
- מאחסן דימויים ב-ECR להפצה קלה
- מתממשק עם תשתית Kubernetes הקיימת
- מספק ממשק סטנדרטי עבור אוטומציה
זה פותר את הבעיות המרכזיות על ידי:
- אוטומציה של תהליך נקודת הבדיקה
- תקן אחסון נקודת בדיקה
- הפוך את נקודות הבדיקה לניידות
- אפשר גישה תכנותית
- פשוט את השילוב עם זרימות העבודה הקיימות
משתמשים יעד:
- צוותי DevOps
- מהנדסי פלטפורמה
- מפתחי אפליקציות
- מהנדסי אמינות באתר (SREs)
איחסון נקודת בדיקת תיקיון מבוסס על Checkpoint/Restore In Userspace (CRIU) ומאפשר את יצירת העתקים עם סטטוס מצבי של קונטיינר פועל מבלי שהקונטיינר יודע שהוא נבדק. ההעתק של הקונטיינר ניתן לניתוח ושחזור בסביבת חול מרובות בלי שהקונטיינר המקורי יודע על כך. איחסון נקודת בדיקת תיקיון הוצג כתכונת אלפא ב-Kubernetes v1.25.
מאמר זה ידריך אותך כיצד ל- להפעיל קוד Golang שניתן לשימוש לקחת נקודת בדיקה של קונטיינר באמצעות API.
הקוד מקבל זיהוי פוד, ממשיך את זיהוי הקונטיינר מ-containerd כקלט, ואז משתמש בפקודת ctr
על מנת לקחת נקודת בדיקה של הקונטיינר הספציפי במרחב השמות k8s.io
של containerd:
- אשכול Kubernetes
- התקן את הכלי
ctr commandline
. אם ניתן להפעיל פקודות ctr על ה-kubelet או על צומת העבודה; אם לא, התקן או התאם את AMI כדי לכלול את ה-ctr. kubectl
מוגדר כך שיתקשר עם האשכול שלך- התקן את Docker מקומית
- גישה למחסן התוכניות (לדוגמה, Docker Hub, ECR)
- Helm (להתקנת Nginx Ingress Controller)
שלב 0: קוד ליצירת נקודת בקרה של מיכל באמצעות GO
צור קובץ בשם checkpoint_container.go
עם התוכן הבא:
package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces"
)
func init() {
log.SetOutput(os.Stdout)
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
}
func main() {
if len(os.Args) < 4 {
log.Fatal("Usage: checkpoint_container <pod_identifier> <ecr_repo> <aws_region>")
}
podID := os.Args[1]
ecrRepo := os.Args[2]
awsRegion := os.Args[3]
log.Printf("Starting checkpoint process for pod %s", podID)
containerID, err := getContainerIDFromPod(podID)
if err != nil {
log.Fatalf("Error getting container ID: %v", err)
}
err = processContainerCheckpoint(containerID, ecrRepo, awsRegion)
if err != nil {
log.Fatalf("Error processing container checkpoint: %v", err)
}
log.Printf("Successfully checkpointed container %s and pushed to ECR", containerID)
}
func getContainerIDFromPod(podID string) (string, error) {
log.Printf("Searching for container ID for pod %s", podID)
client, err := containerd.New("/run/containerd/containerd.sock")
if err != nil {
return "", fmt.Errorf("failed to connect to containerd: %v", err)
}
defer client.Close()
ctx := namespaces.WithNamespace(context.Background(), "k8s.io")
containers, err := client.Containers(ctx)
if err != nil {
return "", fmt.Errorf("failed to list containers: %v", err)
}
for _, container := range containers {
info, err := container.Info(ctx)
if err != nil {
continue
}
if strings.Contains(info.Labels["io.kubernetes.pod.uid"], podID) {
log.Printf("Found container ID %s for pod %s", container.ID(), podID)
return container.ID(), nil
}
}
return "", fmt.Errorf("container not found for pod %s", podID)
}
func processContainerCheckpoint(containerID, ecrRepo, region string) error {
log.Printf("Processing checkpoint for container %s", containerID)
checkpointPath, err := createCheckpoint(containerID)
if err != nil {
return err
}
defer os.RemoveAll(checkpointPath)
imageName, err := convertCheckpointToImage(checkpointPath, ecrRepo, containerID)
if err != nil {
return err
}
err = pushImageToECR(imageName, region)
if err != nil {
return err
}
return nil
}
func createCheckpoint(containerID string) (string, error) {
log.Printf("Creating checkpoint for container %s", containerID)
checkpointPath := "/tmp/checkpoint-" + containerID
cmd := exec.Command("ctr", "-n", "k8s.io", "tasks", "checkpoint", containerID, "--checkpoint-path", checkpointPath)
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("checkpoint command failed: %v, output: %s", err, output)
}
log.Printf("Checkpoint created at: %s", checkpointPath)
return checkpointPath, nil
}
func convertCheckpointToImage(checkpointPath, ecrRepo, containerID string) (string, error) {
log.Printf("Converting checkpoint to image for container %s", containerID)
imageName := ecrRepo + ":checkpoint-" + containerID
cmd := exec.Command("buildah", "from", "scratch")
containerId, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to create container: %v", err)
}
cmd = exec.Command("buildah", "copy", string(containerId), checkpointPath, "/")
err = cmd.Run()
if err != nil {
return "", fmt.Errorf("failed to copy checkpoint: %v", err)
}
cmd = exec.Command("buildah", "commit", string(containerId), imageName)
err = cmd.Run()
if err != nil {
return "", fmt.Errorf("failed to commit image: %v", err)
}
log.Printf("Created image: %s", imageName)
return imageName, nil
}
func pushImageToECR(imageName, region string) error {
log.Printf("Pushing image %s to ECR in region %s", imageName, region)
sess, err := session.NewSession(&aws.Config{
Region: aws.String(region),
})
if err != nil {
return fmt.Errorf("failed to create AWS session: %v", err)
}
svc := ecr.New(sess)
authToken, registryURL, err := getECRAuthorizationToken(svc)
if err != nil {
return err
}
err = loginToECR(authToken, registryURL)
if err != nil {
return err
}
cmd := exec.Command("podman", "push", imageName)
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to push image to ECR: %v", err)
}
log.Printf("Successfully pushed checkpoint image to ECR: %s", imageName)
return nil
}
func getECRAuthorizationToken(svc *ecr.ECR) (string, string, error) {
log.Print("Getting ECR authorization token")
output, err := svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
if err != nil {
return "", "", fmt.Errorf("failed to get ECR authorization token: %v", err)
}
authData := output.AuthorizationData[0]
log.Print("Successfully retrieved ECR authorization token")
return *authData.AuthorizationToken, *authData.ProxyEndpoint, nil
}
func loginToECR(authToken, registryURL string) error {
log.Printf("Logging in to ECR at %s", registryURL)
cmd := exec.Command("podman", "login", "--username", "AWS", "--password", authToken, registryURL)
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to login to ECR: %v", err)
}
log.Print("Successfully logged in to ECR")
return nil
}
שלב 1: אתחל את מודול ה-go
go mod init checkpoint_container
שנה את קובץ ה-go.mod
:
module checkpoint_container
go 1.23
require (
github.com/aws/aws-sdk-go v1.44.298
github.com/containerd/containerd v1.7.2
)
require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
github.com/pkg/errors v0.9.1 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
)
הפעל את הפקודה הבאה:
go mod tidy
שלב 2: בנה ופרסם תמונת Docker
צור קובץ Dockerfile
באותו ספרייה:
# Build stage
FROM golang:1.20 as builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o checkpoint_container
# Final stage
FROM amazonlinux:2
# Install necessary tools
RUN yum update -y && \
amazon-linux-extras install -y docker && \
yum install -y awscli containerd skopeo && \
yum clean all
# Copy the built Go binary
COPY --from=builder /app/checkpoint_container /usr/local/bin/checkpoint_container
EXPOSE 8080
ENTRYPOINT ["checkpoint_container"]
קובץ זה עושה את הבא:
- משתמש ב-
golang:1.20
כשלב הבנייה להדקת היישום שלך ב-GO. - משתמש ב-
amazonlinux:2
כתמונת בסיס סופית. - מתקין את AWS CLI, Docker (שכולל את containerd), ו-skopeo באמצעות yum ו-amazon-linux-extras.
- מעתיק את הבינארי של GO שהודקל משלב הבנייה.
docker build -t <your-docker-repo>/checkpoint-container:v1 .
docker push <your-docker-repo>/checkpoint-container:v1
החלף את <your-docker-repo>
במאגר ה-Docker האמיתי שלך.
שלב 3: החל משאבי ה-RBAC
צור קובץ בשם rbac.yaml
:
apiVersion v1
kind ServiceAccount
metadata
name checkpoint-sa
namespace default
---
apiVersion rbac.authorization.k8s.io/v1
kind Role
metadata
name checkpoint-role
namespace default
rules
apiGroups""
resources"pods"
verbs"get" "list"
---
apiVersion rbac.authorization.k8s.io/v1
kind RoleBinding
metadata
name checkpoint-rolebinding
namespace default
subjects
kind ServiceAccount
name checkpoint-sa
namespace default
roleRef
kind Role
name checkpoint-role
apiGroup rbac.authorization.k8s.io
החל משאבי ה-RBAC:
kubectl apply -f rbac.yaml
שלב 4: צור פריסת Kubernetes
צור קובץ בשם deployment.yaml
:
apiVersion apps/v1
kind Deployment
metadata
name main-app
namespace default
spec
replicas1
selector
matchLabels
app main-app
template
metadata
labels
app main-app
spec
serviceAccountName checkpoint-sa
containers
name main-app
image nginx latest # Replace with your main application image
name checkpoint-sidecar
image <your-docker-repo>/checkpoint-container v1
ports
containerPort8080
securityContext
privilegedtrue
volumeMounts
name containerd-socket
mountPath /run/containerd/containerd.sock
volumes
name containerd-socket
hostPath
path /run/containerd/containerd.sock
type Socket
החל את הפריסה:
kubectl apply -f deployment.yaml
בעוד deployment.yaml
, עדכן את הדברים הבאים:
image <your-docker-repo>/checkpoint-container v1
שלב 5: שירות Kubernetes
צור קובץ בשם service.yaml
:
apiVersion v1
kind Service
metadata
name checkpoint-service
namespace default
spec
selector
app main-app
ports
protocol TCP
port80
targetPort8080
החל את השירות:
kubectl apply -f service.yaml
שלב 6: התקן את בקר ה-Ngnix Ingress
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx
שלב 7: צור משאב Ingress
צור קובץ בשם ingress.yaml
:
apiVersion networking.k8s.io/v1
kind Ingress
metadata
name checkpoint-ingress
annotations
kubernetes.io/ingress.class nginx
nginx.ingress.kubernetes.io/ssl-redirect"false"
spec
rules
http
paths
path /checkpoint
pathType Prefix
backend
service
name checkpoint-service
port
number80
החל את ה-Ingress:
kubectl apply -f ingress.yaml
שלב 8: בדוק את ה-API
kubectl get services ingress-ngnix-contoller -n ingress-ngnix
curl -X POST http://<EXTERNAL-IP>/checkpoint \
-H "Content-Type: application/json" \
-d '{"podId": "your-pod-id", "ecrRepo": "your-ecr-repo", "awsRegion": "your-aws-region"}'
החלף את <EXTERNAL-IP>
עם ה-IP החיצוני האמיתי.
שיקולים נוספים
- אבטחה.
- יישם HTTPS על ידי התקנת תעודות TLS
- הוסף אימות ל-API
- ניטור. הגדר יומנים וניטור עבור ה-API ותהליך הצ'קפוינט.
- ניהול משאבים. הגדר בקשות ומגבלות משאבים עבור מיכל הצד.
- טיפול בשגיאות. יישם טיפול בשגיאות חזק באפליקציית ה-Go.
- בדיקות. בדוק ביסודיות את ההתקנה בסביבה שאינה ייצור לפני פריסתה לייצור.
- מסמכים. שמור תיעוד ברור על איך להשתמש ב-API של הצ'קפוינט.
סיכום
ההגדרה הזו משיקה את קונטיינר הצ'קפוינט כצד בשירות Kubernetes ומפיצה את הפונקציות שלו דרך API שניתן לגשת אליו מחוץ לקלאסטר. היא מציעה פתרון גמיש לניהול צ'קפוינטים של קונטיינרים בסביבת Kubernetes.
מאפיינים ספציפיים של AWS/EKS
שלב 7: התקנת בקר מאזן העומסים של AWS
במקום להשתמש בבקר Nginx Ingress, נשתמש בבקר מאזן העומסים של AWS. בקר זה ייצור וינהל ALBs עבור משאבי ה-Ingress שלנו.
1. הוסף את מאגר הצ'ארטים של EKS ל-Helm:
helm repo add eks https://aws.github.io/eks-charts
2. התקן את בקר מאזן העומסים של AWS:
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=<your-cluster-name> \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
החלף את <your-cluster-name>
בשם הקלאסטר שלך ב-EKS.
הערה: ודא שיש לך את ההרשאות IAM הנדרשות עבור בקר מאזן העומסים של AWS. תוכל למצוא את מדיניות ה-IAM המפורטת בתיעוד של AWS.
שלב 8: צור משאב Ingress
צור קובץ בשם ingress.yaml
:
apiVersion networking.k8s.io/v1
kind Ingress
metadata
name checkpoint-ingress
annotations
kubernetes.io/ingress.class alb
alb.ingress.kubernetes.io/scheme internet-facing
alb.ingress.kubernetes.io/target-type ip
spec
rules
http
paths
path /checkpoint
pathType Prefix
backend
service
name checkpoint-service
port
number80
החל את ה-Ingress:
kubectl apply -f ingress.yaml
שלב 9: בדוק את ה-API
1. קבל את שם ה-DNS של ה-ALB:
kubectl get ingress checkpoint-ingress
חפש את השדה ADDRESS, שיהיה שם ה-DNS של ה-ALB.
2. שלח בקשה לבדיקה:
curl -X POST http://<ALB-DNS-NAME>/checkpoint \
-H "Content-Type: application/json" \
-d '{"podId": "your-pod-id", "ecrRepo": "your-ecr-repo", "awsRegion": "your-aws-region"}'
החלף את <ALB-DNS-NAME>
בשם ה-DNS האמיתי של ה-ALB שלך מהשלב 1.
שיקולים נוספים עבור AWS ALB
1. קבוצות אבטחה. ה-ALB ייווצר עם קבוצת אבטחה באופן אוטומטי. ודאו שהיא מאפשרת תעבורה נכנסת על פורט 80 (וכן 443 אם הגדרתם HTTPS).
2. SSL/TLS: כדי לאפשר HTTPS, אתם יכולים להוסיף את ההערות הבאות ל-Ingress שלכם:
alb.ingress.kubernetes.io/listen-ports'[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/certificate-arn arn aws acm region account-id certificate/certificate-id
3. יומני גישה. הפעלו יומני גישה עבור ה-ALB שלכם על ידי הוספת הבאות:
alb.ingress.kubernetes.io/load-balancer-attributes access_logs.s3.enabled=true,access_logs.s3.bucket=your-log-bucket,access_logs.s3.prefix=your-log-prefix
4. אינטגרציית WAF. אם אתם רוצים להשתמש ב-AWS WAF עם ה-ALB שלכם, אתם יכולים להוסיף:
alb.ingress.kubernetes.io/waf-acl-id your-waf-web-acl-id
5. אימות. אתם יכולים להגדיר אימות באמצעות Amazon Cognito או OIDC על ידי שימוש בהערות המתאימות של ALB Ingress Controller.
שינויים אלו יגבירו את ה-Ingress שלכם באמצעות מאזן עומסים של AWS במקום Nginx. ה-ALB Ingress Controller יProvision וית конфיגור את ה-ALB באופן אוטומטי בהתבסס על משאב ה-Ingress שלכם.
סיכום
זכרו לוודא ש-cluster EKS שלכם יש הרשאות IAM הנדרשות כדי ליצור ולנהל ALBs. זה בדרך כלל כולל יצירת מדיניות IAM וחשבון שירות עם ההרשאות המתאימות.
הגדרה זו תשתמש כעת בפתרון האיזון העומסים המקורי של AWS, שמשתלב היטב עם שאר שירותי AWS ויכול להיות חסכוני יותר בסביבת AWS.
Source:
https://dzone.com/articles/container-checkpointing-kubernetes-api