Kubernetes Init Container là gì?

Trong bài viết này ta sẽ tìm hiểu sâu hơn về Kubernetes Init Container. Ta sẽ tìm hiểu xem chúng là gì, hoạt động ra sao và lợi ích khi sử dụng chúng là gì nhé.
Bắt đầu thôi!
Init container là gì?
Các Kubernetes pod có thể chứa nhiều hơn một container. Các container trong một pod làm việc với nhau để cùng đạt được một mục tiêu chung.
Init Container là các container dùng để thực hiện các công việc khởi tạo cho các container chính trong pod. Nó đóng vai trò như là bước chuẩn bị cho phép ta thực hiện các công việc khởi tạo, cấu hình hoặc cài đặt dependency phải có trong ứng dụng của các container chính.

Ví dụ ta có một ứng dụng cần một secret để kết nối tới một API. Ta không thể hardcode secret đó vào ứng dụng hoặc dùng Kubernetes secret vì lý do bảo mật nào đó. Trong trường hợp này bạn có thể sử dụng init container để fetch secret từ secret management service như Vault hay AWS Secrets Manager và lưu nó vào thư mục trong pod mà container ứng dụng có thể truy cập được.

Bằng cách này khi ứng dụng chạy nó sẽ có quyền truy cập tới secret để kết nối tới API.
Để dễ hiểu thì các init container có thể đảm bảo các ứng dụng của bạn luôn luôn được khởi động và cấu hình chính xác trước khi chạy.
Init Containers hoạt động như thế nào?
Trước khi tìm hiểu sâu hơn về init container, ta sẽ xem qua cách chúng hoạt động trước:
kubeletchạy các init container theo thứ tự xuất hiện trong Pod spec, init container tiếp theo chỉ được chạy sau khi init container trước đó đã hoàn tất.Init Container chạy trước khi các container ứng dụng chính chạy
Nếu Pod restart thì mọi init container sẽ được chạy lại.
Trong vòng đời của pod, init container chạy để hoàn thành giai đoạn Pending.
Mặc dù các init container chia sẻ chung đặc điểm về container nhưng chúng không hỗ trợ vòng đời (lifecycle), livenessProbe, readinessProbe và startupProbe như các container chính.

Các trường hợp sử dụng Init Container
Các trường hợp sử dụng init container phụ thuộc vào nhu cầu cụ thể của từng ứng dụng. Ví dụ một trường hợp thường thấy đó là:
Load và cấu hình các dependencies: Các init container có thể load và cấu hình các dependencies cần thiết cho container chính trước khi chúng hoạt động.
Tạo Database Schema: Bạn có thể dùng init container để tạo database schema.
Warm Up Cache: Warm up cache ví dụ như preloading các dữ liệu thường xuyên cần dùng vào Redis cache.
Network Configuration: Xử lý các tác vụ như thiết lập cấu hình mạng network hoặc thiết lập kết nối tới các dịch vụ bên ngoài.
Git Clone: Clone các git repositories hoặc viết các file vào pod volume.
Security Checks: Thực hiện kiểm tra bảo mật như là quét lỗ hổng hoặc xác thực TLS certificate.
Truy cập các secrets: Truy cập các secret mà các container ứng dụng chính không thể, ví dụ như đọc các secret từ Vault.
Environment Setup: Xử lý các tác vụ như tạo các thư mục, apply quyền hoặc chạy các script tùy chỉnh để cài môi trường cho ứng dụng chính.
Đợi các service khác: Đợi một service nào đó khởi động trước khi khởi động ứng dụng chính.
Ứng dụng thực tế của Init Container
Init container được định nghĩa trong phần spec.initContainers của một Pod manifest. Nội dung định nghĩa tương tự như spec.containers. Ta có thể định nghĩa bao nhiêu container trong initContainers cũng được.
Hãy xem qua một ứng dụng thực tế. Ta cần triển khai một Nginx web server pod hiển thị Pod IP trên index page.
Một init container tên là
write-ipsẽ lấy pod IP sử dụng biếnMY_POD_IPlấy trạng thái từ Pod và lưu nó vào fileip.txttrong volume/web-contentđược gắn vào pod.Init container thứ hai tên là
create-htmlđọc pod IP từ/web-content/ip.txtchứa pod IP được tạo từ init container đầu tiên và lưu nội dung mới vào/web-content/index.html.Giờ container nginx mount
/usr/share/nginx/htmltới volume/web-contentnơi lưuindex.html.
Lưu file sau dưới tên init-container.yaml :
apiVersion: v1
kind: Pod
metadata:
name: web-server-pod
spec:
initContainers:
- name: write-ip
image: busybox
command: ["sh", "-c", "echo $MY_POD_IP > /web-content/ip.txt; echo 'Wrote the Pod IP to ip.txt'"]
env:
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: web-content
mountPath: /web-content
- name: create-html
image: busybox
command: ["sh", "-c", "echo 'Hello, World! Your Pod IP is: ' > /web-content/index.html; cat /web-content/ip.txt >> /web-content/index.html; echo 'Created index.html with the Pod IP'"]
volumeMounts:
- name: web-content
mountPath: /web-content
containers:
- name: web-container
image: nginx
volumeMounts:
- name: web-content
mountPath: /usr/share/nginx/html
volumes:
- name: web-content
emptyDir: {}
kubectl apply -f init-container.yaml
Kiểm tra khả dụng của pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web-server-pod 1/1 Running 0 22s
Ta có 3 container trong pod nhưng chỉ một là đang ở trạng thái RUNNING. Tại vì các init container chạy trước khi khởi động container chính, sau khi 2 init container tạo nginx HTML index page và exit thành công thì container chính sẽ chạy với page html tùy chỉnh.
Ta đã thêm 2 echo command trong quá trình chạy các init container. Ta có thể check log của các init container và xem liệu nó có chạy các command đó không:
$ kubectl logs web-server-pod -c write-ip
Wrote the Pod IP to ip.txt
$ kubectl logs web-server-pod -c create-html
Created index.html with the Pod IP
Giờ kiểm tra Nginx pod bằng cách truy cập Nginx pod qua port forwarding:
kubectl port-forward pod/web-server-pod 8080:80
Giờ nếu ta truy cập Nginx ta có thể thấy Nginx index page hiển thị message với nội dung kèm Pod IP như dưới đây.

Thêm tài nguyên CPU/RAM
Các init container yêu cầu CPU và RAM để chạy các tác vụ cụ thể. Giới hạn tài nguyên có thể được điều chỉnh dựa trên mức độ quan trọng của tác vụ.
Nếu có nhiều init container thì giá trị cao nhất được đặt cho bất kì init container nào được gọi là effective init request/limit tức là nếu bạn có một init container không đặt giới hạn CPU/RAM thì nó sẽ sử dụng hết effective init request/limit.
Ta có thể chỉ định giới hạn tài nguyên cho init container bằng cách như sau:
spec:
initContainers:
- name: init-container
image: init-container-image
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
Việc quản lý và điều chỉnh giới hạn tài nguyên nên dựa trên lượng tiêu thụ thực tế của các init container để tối ưu hóa phân bổ tài nguyên của cụm. Tuy nhiên ta cũng nên đảm bảo tổng tài nguyên yêu cầu từ init container và các container chính không vượt quá số lượng tài nguyên khả dụng của các node.
Thêm các volume
Các volume trong init container đóng vai trò quan trọng trong việc thực hiện setup dữ liệu, khởi tạo và chuẩn bị các tác vụ trước khi ứng dụng chính hoạt động. Ta có thể mount các volume trong init container tương tự như với các containe chính.
Ví dụ có những lúc ứng dụng cần truy cập tới dataset hoặc các file mà ta không muốn đưa chúng vào container image vì kích thước lớn. Trong trường hợp này một init container có thể được sử dụng để fetch và load các dataset vào shared volume và hơn nữa volume này có thể được dùng bởi container chính. Dưới đây là một ví dụ:
apiVersion: v1
kind: Pod
metadata:
name: volume-example-pod
spec:
initContainers:
- name: download-dataset
image: busybox
command: ["wget", "-O", "/data/dataset.zip", "https://example.com/dataset.zip"]
volumeMounts:
- name: data-volume
mountPath: /data
- name: unzip-dataset
image: busybox
command: ["unzip", "/data/dataset.zip", "-d", "/data"]
volumeMounts:
- name: data-volume
mountPath: /data
containers:
- name: main-app
image: main-app-image
volumeMounts:
- name: data-volume
mountPath: /app-data
volumes:
- name: data-volume
emptyDir: {}
Native Sidecar sử dụng các Init Container
Chức năng này dựa trên khái niệm persistent init container - nơi quá trình khởi tạo diễn ra trong init container và sử dụng thêm restartPolicy để cung cấp khả năng mới của sidecar container: chúng khởi động trước các container chính như các init container nhưng sẽ tiếp tục chạy trong suốt vòng đời của Pod.
Nói dễ hiểu thì để tao một init container như một sidecar, ta cần thêm restartPolicy: Always trong spec của nó. Đây là một optional, nếu restartPolicy không được đề cập thì nó sẽ được sử dụng như một init container cơ bản.
Lưu ý là restartPolicy trong init container chỉ khả dụng khi ta bật feature gate SidecarContainers. Tại thời điểm bài viết này thì nó vẫn đang trong giai đoạn alpha.
Một trường hợp thực tế có thể sử dụng:
Một Nginx web server với một container nginx chính lưu logs vào
/var/log/nginxvolume.Ta cần một native sidecar fluentd logging agent container đọc logs của nginx trong
/var/log/nginx.
Đây là YAML cho trường hợp trên. restartPolicy: Always được thêm vào logging-agent init container để chỉ địnhh nó là sidecar container:
apiVersion: v1
kind: Pod
metadata:
name: webserver-pod
spec:
initContainers:
- name: logging-agent
image: fluentd:latest
restartPolicy: Always
volumeMounts:
- name: nginx-logs
mountPath: /var/log/nginx
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
volumeMounts:
- name: nginx-logs
mountPath: /var/log/nginx
volumes:
- name: nginx-logs
emptyDir: {}
kubectl apply -f sidecar.yaml
Giờ ta có thể kiểm tra tình trạng pod, ta có thể thấy 2/2 container đang chạy. Một là native sidecar init container và một là Nginx container.

Các đặc điểm chính của sidecar:
Dedicated lifecycle: Native Sidecar containers có một vòng đời độc lập với vòng đời của các container chính trong Pod.
Nó không chặn quá trình kết thúc của Pod như các non-native sidecar container. Khi các init container thông thường bị lỗi nó sẽ thoát và Pod sẽ không thể khởi động tuy nhiên nếu sidecar container bị xóa hay lỗi không ảnh hưởng đến Pod và Pod vẫn sẽ chạy các container khác.
Lifecycle Handlers and Probes: Ta có thể thêm PostStart và PreStop handler và các probe (startup, readiness, liveness) để đảm bảo readiness của sidecar và readiness của pod.
Init Container YAML
Để lấy thông tin của toàn bộ các trường (field) được hỗ trợ ta có thể sử dụng command:
kubectl explain pod.spec.initContainers
spec:
initContainers:
- name: init-container
image: busybox:latest
command:
- "sh"
- "-c"
- "echo Initializing... && sleep 5"
imagePullPolicy: IfNotPresent
env:
- name: INIT_ENV_VAR
value: "init-value"
resources:
limits:
memory: "128Mi"
cpu: "500m"
requests:
memory: "64Mi"
cpu: "250m"
volumeMounts:
- name: init-container-volume
mountPath: /init-data
ports:
- containerPort: 80
securityContext:
runAsUser: 1000
runAsGroup: 1000
capabilities:
add: ["NET_ADMIN"]
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 20
startupProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'PostStart'"]
preStop:
exec:
command: ["/bin/sh", "-c", "echo 'PreStop'"]
restartPolicy: Always
volumes:
- name: init-container-volume
emptyDir: {}
Init Container Best Practices
Dưới đây là các best practices bạn nên áp dụng:
Đảm bảo các init container được thiết kế để làm các tác vụ cụ thể một cách nhanh chóng mà không sử dụng quá nhiều tài nguyên.
Nếu bạn có nhiều tác vụ khởi động, sử dụng nhiều init container cho từng tác vụ. Việc này sẽ giúp quá trình quản lý và troubleshoot chúng đơn giản hơn.
Các init container có thể fail nên hãy có kế hoạch trước. Thực hiện các retry, back-off strategy và clear error message để phân tích và giải quyết vấn đề hiệu quả.
Tận dụng pre-run và post-run hook để chạy các script tùy chỉnh hoặc command trong giai đoạn cụ thể trong vòng đời của container.
Bảo mật thông tin nhạy cảm sử dụng trong quá trình khởi tạo và tránh việc bị lộ.
Đảm bảo các init container có đủ tài nguyên cần thiết. Thiếu tài nguyên có thể dẫn đến các tác vụ khởi tạo bị fail hoặc bị delay.
Init Container vs Sidecar Container
Init Container thực hiện các tác vụ cần được hoàn thành trước khi container chính hoạt động trong khi đó Sidecar Container cung cấp tính năng bổ sung cho container chính.
Init Container không chia sẻ chung network và tài nguyên lưu trữ với container chính trong khi đó Sidecar Container chia sẻ chung chúng với container chính.
Init Container thực thi theo thứ tự và hoàn tất trước khi container chính chạy. Ngược lại Sidecar Container khởi động, chạy và thoát song song với container chính.
Init Container đảm bảo container chính hoạt động cùng các điều kiện tiên quyết trong khi Sidecar Container trực tiếp ảnh hưởng đến hành vi và chức năng của container chính.
Các Init Container được dùng để setup môi trường cho ứng dụng chính như là tải các file cấu hình hay khởi tạo shared volumes. Sidecar Container được dùng để log dữ liệu ra các hệ thống bên ngoài, thu thập metrics hay xử lý các chức năng liên quan đến bảo mật.
Kết luận
Trong bài viết này ta đã học về các init container và cách sử dụng chúng trong cụm Kubernetes. Ta đã đề cập đến các chức năng và lợi ích của chúng. Thêm vào đó ta cũng đã nói về cách chúng hoạt động và các trường hợp sử dụng chúng.



