# Tạo Image Tùy chỉnh

## I. Lời mở đầu

Để đáp ứng nhu cầu cá nhân hóa của người dùng đối với các node K8S, ngoài các image tiêu chuẩn, các node K8S cũng hỗ trợ các image tùy chỉnh. Tuy nhiên, việc sử dụng các image tiêu chuẩn của K8S để tạo image tùy chỉnh là điều bắt buộc—nếu không, quá trình tạo cluster có thể thất bại hoặc không thể thêm được node.

Phần dưới đây giới thiệu cách tạo image tùy chỉnh dựa trên các image tiêu chuẩn và các lưu ý cần thiết. Quy trình tạo image tùy chỉnh được mô tả trong tài liệu này hoàn toàn tự động và không cần can thiệp thủ công. Người dùng nên có kinh nghiệm cơ bản về lập trình shell hoặc kinh nghiệm sử dụng [Ansible][2].

Vì tốc độ mạng từ availability zone (vùng khả dụng) Hồng Kông đến các vùng khả dụng trong nước và toàn cầu khác rất nhanh, nên nó có thể giảm thiểu thời gian tiêu hao trong quá trình sao chép image. Phương pháp được giới thiệu trong tài liệu này bao gồm việc xây dựng image tại khu vực Hồng Kông và sau đó sao chép nó sang các availability zone khác. Vui lòng đảm bảo có đủ hạn mức cloud host (máy chủ đám mây) trong availability zone Hồng Kông.

## II. Quy trình Tạo Image Tùy chỉnh

1. Cài đặt Packer

Cài đặt công cụ [Packer][1], công cụ này giúp bạn dễ dàng tạo và phân phối các image tùy chỉnh đến các availability zone mà bạn cần. Cách cài đặt cho macOS được giới thiệu dưới đây. Đối với các hệ điều hành khác, vui lòng tham khảo [Cẩm nang Packer](https://developer.hashicorp.com/packer/downloads?product_intent=packer).

Người dùng macOS có thể cài đặt Packer bằng lệnh sau:

```bash
brew install packer
```

Packer chỉ chịu trách nhiệm tạo máy chủ đám mây. Bạn cần có các tập lệnh dòng lệnh (command-line script) hoặc Ansible để cài đặt và cấu hình phần mềm trong máy chủ đám mây. 
Ví dụ trong tài liệu này sử dụng Ansible, nhưng nó có thể được chuyển đổi sang các công cụ tương đương khác. Cách cài đặt Ansible trên macOS được giới thiệu dưới đây. Đối với các môi trường khác, vui lòng tham khảo [Cẩm nang Chính thức của Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#managed-node-requirements).

Người dùng macOS có thể cài đặt Ansible bằng lệnh sau:

```bash
brew install ansible
```

2. Chuẩn bị Public Key, Private Key và Project ID

Vui lòng tạo hoặc sử dụng public key và private key hiện có trong mục `Account Management -> API Key` trên console VNPT Cloud. 
Tìm project chứa image tùy chỉnh mà bạn sắp tạo trong mục `Access Control -> Project Management` trên console VNPT Cloud.
Thiết lập public key, private key và project ID vào các biến môi trường. Ví dụ lệnh như sau:

    export UCLOUD_PUBLIC_KEY="Public key"
    export UCLOUD_PRIVATE_KEY="Private key"
    export UCLOUD_PROJECT_ID="Project ID"

Bạn nên thiết lập các lệnh trên trong tệp khởi tạo của shell, chẳng hạn như .zshrc hoặc .bashrc, v.v.

3. Viết tệp cấu hình Packer

Giả sử tên của tệp cấu hình này là `custom.json`.

    {
      "variables": {
        "ucloud_public_key": "{{env `UCLOUD_PUBLIC_KEY`}}",
        "ucloud_private_key": "{{env `UCLOUD_PRIVATE_KEY`}}",
        "ucloud_project_id": "{{env `UCLOUD_PROJECT_ID`}}"
      },

      "builders": [{
        "type": "VNPT Cloud-uhost",
        "public_key": "{{user `ucloud_public_key`}}",
        "private_key": "{{user `ucloud_private_key`}}",
        "project_id": "{{user `ucloud_project_id`}}",
        "region": "hk",
        "availability_zone": "hk-02",
        "instance_type": "o-standard-2",
        "source_image_id": "<THAY_THẾ_ID_IMAGE_CƠ_SỞ_UK8S_TẠI_ĐÂY>",
        "ssh_username": "root",
        "image_name": "<TÊN_IMAGE_CỦA_BẠN_Ở_ĐÂY>",
        "image_copy_to_mappings": [
          {
            "project_id": "{{user `ucloud_project_id`}}",
            "region": "<THAY_THẾ_REGION_ID_NƠI_CẦN_SAO_CHÉP_ĐẾN>"
          }
        ]
      }],

      "provisioners": [{
        "type": "ansible",
        "playbook_file": "./playbook.yml"
      }]

    }

Trước tiên, vui lòng thay thế nội dung trong dấu ngoặc nhọn trong ví dụ trên bằng các giá trị thực tế. Lưu ý các tham số sau trong builders:

- type: Đây là tên plugin tương ứng trong Packer và không cần thay đổi.
- region: Cho biết khu vực (region) nơi image REPLACE_THE_UK8S_BASE_IMAGE_ID_HERE tọa lạc. Khuyến nghị chọn Hồng Kông tại đây. Nếu image tùy chỉnh cần tải xuống các tài nguyên nước ngoài, trung tâm dữ liệu Hồng Kông có thể trực tiếp tải xuống chúng.
- availability_zone: Cho biết vùng khả dụng (availability zone) nơi image REPLACE_THE_UK8S_BASE_IMAGE_ID_HERE tọa lạc.
- instance_type: Loại máy có thể giữ nguyên, và mô hình mặc định cũng có thể được sử dụng để cài đặt driver GPU.
- ssh_username: Nếu đó là image Ubuntu, vui lòng chuyển sang ubuntu.


Bảng dưới đây liệt kê các ID image cơ sở tương ứng với các hệ điều hành và phiên bản được hỗ trợ trong availability zone Hồng Kông dành cho K8S. Vui lòng chọn một image phù hợp theo nhu cầu, và thay thế `<REPLACE_THE_UK8S_BASE_IMAGE_ID_HERE>` bằng giá trị tương ứng trong cột Image ID:

| Khu vực (Region) | Vùng khả dụng (Availability Zone) | ID Image               | Hệ điều hành            | Phiên bản | Hỗ trợ GPU |
| ------------- | ----------------------- | ---------------------- | ------------------------| ------- | ----------- |
| hk            | hk-02(3002)             | uimage-puxm0l          | CentOS                  | 7.6     | Có         |
| hk            | hk-02(3002)             | uimage-rccvz4l9itr     | Ubuntu                  | 20.04   | Có         |

Nếu bạn cần sao chép image đã hoàn thành sang các khu vực và vùng khả dụng khác, bạn có thể thiết lập vùng khả dụng đích trong phần `image_copy_to_mappings` của tệp trên. Nhiều vùng khả dụng có thể được chỉ định cùng một lúc. 
Nếu bạn không cần sao chép, vui lòng xóa thuộc tính này.

Tiếp theo, chúng cần viết một tập lệnh để cài đặt và cấu hình image tùy chỉnh. Tài liệu này cung cấp một ví dụ về Ansible để tham khảo. Packer có các loại provisioner khác. Vui lòng tham khảo [Cẩm nang Packer](https://developer.hashicorp.com/packer/docs/provisioners). Tệp playbook.yml tương ứng cho Ansible như sau:

    - hosts: all
      become: true

      pre_tasks:
        - name: Disable swap
          command: swapoff -a
          when: ansible_swaptotal_mb > 0

      roles:
        - role: custom-setup

Playbook trong ví dụ trên sẽ tắt swap và thực hiện các thiết lập sâu hơn với role có tên `custom-setup`.
Xin lưu ý rằng đây chỉ là một bản demo; swap đã được tắt trong image cơ sở của K8S, vì vậy không cần phải lặp lại thao tác này.

4. Chạy Packer

Lần đầu tiên chạy Packer, vui lòng chạy lệnh trong thư mục chứa tệp `custom.json` ở trên:

    packer init .

Nếu gặp sự cố khi chạy lệnh trên, vui lòng cấu hình tệp config.pkr.hcl trong đường dẫn đang chạy. Nội dung tệp như sau:

    packer {
      required_plugins {
        ucloud = {
          version = ">= 1.0.8"
          source  = "github.com/ucloud/ucloud"
        }
      }
    }

Sau đó chạy lệnh sau:

    packer build custom.json

Quy trình tạo image tương đối tốn thời gian. Vui lòng không thực hiện bất kỳ thao tác nào trong máy chủ đám mây trong khoảng thời gian này, hoặc xóa máy chủ này. Nếu không, image không thể được tạo bình thường.
Sau khi quá trình tạo hoàn tất, packer sẽ hiển thị ID của image, như được minh họa dưới đây:

 
    ==> VNPT Cloud-uhost: Stopping instance "uhost-88888888888"
        VNPT Cloud-uhost: Stopping instance "uhost-88888888888" complete
    ==> VNPT Cloud-uhost: Creating image xxxx-yyyyy-8.5...
        VNPT Cloud-uhost: Waiting for the created image "uimage-***********" to become available...
        VNPT Cloud-uhost: Creating image "uimage-***********" complete
    ==> VNPT Cloud-uhost: Copying images from "uimage-***********"...
        VNPT Cloud-uhost: Copying image from org-******:cn-bj2:uimage-*********** to org-******:cn-wlcb:uimage-***********
        VNPT Cloud-uhost: Copying image from org-******:cn-bj2:uimage-*********** to org-******:hk:uimage-***********
        VNPT Cloud-uhost: Copying image from org-******:cn-bj2:uimage-*********** to org-******:cn-gd:uimage-***********
        VNPT Cloud-uhost: Waiting for the copied images to become available...
        VNPT Cloud-uhost: Copying image complete
    ==> VNPT Cloud-uhost: Deleting instance...
        VNPT Cloud-uhost: Deleting instance "uhost-88888888888" complete
    Build 'VNPT Cloud-uhost' finished after 19 minutes 43 seconds.

Sau khi image tùy chỉnh được tạo, Packer sẽ tự động xóa máy chủ đám mây, vì vậy không cần lo lắng về các chi phí phát sinh không cần thiết do quên xóa máy chủ.

## III. Các điểm cần lưu ý

Image cơ sở của K8S đi kèm với các dependency (phụ thuộc) được cấu hình sẵn cho việc triển khai Kubernetes, chẳng hạn như phần mềm, thư mục tệp và các tham số kernel, v.v. 
Khi tạo image tùy chỉnh dựa trên image cơ sở của K8S, vui lòng cẩn thận không sửa đổi các thiết lập liên quan để tránh gây ra rắc rối khi tạo node dựa trên image tùy chỉnh này. 
Các điểm cần lưu ý trong quy trình tạo image tùy chỉnh được giải thích ngắn gọn dưới đây.

### 3.1 Liên quan đến Hệ thống

1. Swap bị tắt theo mặc định. **Không bật nó lên**;
2. Cấu hình Storage=persistent trong các tham số journald. **Không khuyến nghị sửa đổi**;
3. Các thư mục được liệt kê dưới đây được tạo theo mặc định. **Không xóa hoặc sửa đổi chúng**.

- /etc/kubernetes/ssl
- /etc/etcd/
- /etc/docker/
- /etc/kubelet.d/
- /var/lib/kubelet
- ~/.kube/
- /var/lib/etcd/
- /var/lib/etcd/default.etcd
- /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ucloud~flexv/
- /etc/kubernetes/yaml

4. Module ip_conntrack đã được tải. **Không sửa đổi**
5. IPV6 bị tắt theo mặc định. **Không sửa đổi**
6. Đối với phiên bản hệ điều hành Anolis (Dragon Lizard) 8.x, firewalld phải được tắt. **Không bật nó lên khi tạo image tùy chỉnh**

### 3.2 Phần mềm

Quá trình khởi tạo node K8S phụ thuộc vào các phần mềm được liệt kê dưới đây (một phần của chúng). **Không gỡ cài đặt**.
- iptables
- ipvsadm
- socat
- nfs-utils (dùng để mount FS)
- conntrack
- earlyoom

Trong quá trình khởi tạo node K8S, các chứng chỉ được tạo sẵn, các tệp cấu hình và các tệp binary (chẳng hạn như kube-proxy, kubelet, scheduler, docker và kubectl) sẽ được sao chép vào node và được khởi động tuần tự. Do đó, khi tạo image tùy chỉnh, không cần phải cài đặt các thành phần liên quan đến K8S. Ngay cả khi được cài đặt, chúng cũng sẽ không được sử dụng nhưng có thể can thiệp vào chương trình quản lý K8S, dẫn đến thất bại trong việc tạo cluster.