CodeArtifact: Kho Package Nội Bộ Cho Build

K
Kai··4 min read

Build ở Part II kéo pytest từ Internet mỗi lần chạy. Với app thật, một build có thể tải hàng trăm package từ PyPI hay npm — và mỗi nguồn ngoài là một rủi ro: package bị gỡ, registry sập, hay phiên bản đổi sau lưng. CodeArtifact giải việc đó: một kho package được AWS quản lý, vừa làm proxy cache cho registry công khai (build kéo qua đó), vừa lưu package riêng của bạn. Bài này (cũng là cả Part III) tạo kho, publish một package, cài lại từ kho, và nối vào CodeBuild.

Mục tiêu

Tạo domain và repository CodeArtifact, hiểu cơ chế external connection (proxy registry công khai) và auth token, publish rồi consume một package, và cho CodeBuild kéo package từ CodeArtifact.

Domain và repository

CodeArtifact có hai tầng. Domain là vùng chứa cấp cao, gom các repository và quản lý mã hóa/quyền dùng chung. Repository là nơi package thực sự nằm. Tạo cả hai:

$ aws codeartifact create-domain --domain awscicd \
    --query 'domain.[name,status]' --output text
awscicd Active

$ aws codeartifact create-repository --domain awscicd --repository demo-repo \
    --query 'repository.name' --output text
demo-repo

Repo vừa tạo còn trống. Nối nó tới PyPI công khai để nó proxy được package từ đó:

$ aws codeartifact associate-external-connection --domain awscicd \
    --repository demo-repo --external-connection public:pypi \
    --query 'repository.externalConnections[0].externalConnectionName' --output text
public:pypi

Giờ demo-repo vừa lưu package riêng, vừa kéo hộ package từ PyPI khi cần — package public đi qua nó được cache lại, nên lần sau không phải ra Internet và không lo nguồn gốc biến mất.

Auth token: cơ chế đăng nhập

CodeArtifact không dùng username/password riêng cho từng công cụ. Nó cấp một authorization token tạm thời (mặc định 12 giờ) lấy từ chính credential AWS của bạn, rồi cấu hình công cụ (pip/npm/twine) dùng token đó. Lệnh login làm trọn việc này. Đăng nhập cho twine (công cụ publish package Python):

$ aws codeartifact login --tool twine --domain awscicd --repository demo-repo
Successfully configured twine to use AWS CodeArtifact repository
  https://awscicd-111122223333.d.codeartifact.ap-southeast-1.amazonaws.com/pypi/demo-repo/
Login expires in 12 hours at 2026-05-26 01:01:27+07:00

Token hết hạn sau 12 giờ rồi phải login lại — giống credential tạm của service role ở bài 2, không có mật khẩu cố định để lộ.

Publish một package

Tạo một package Python tối thiểu rồi build:

$ cat pyproject.toml
[project]
name = "cicddemo-hello"
version = "0.1.0"
...
$ python3 -m build --wheel --sdist
Successfully built cicddemo_hello-0.1.0.tar.gz and cicddemo_hello-0.1.0-py3-none-any.whl

Upload lên CodeArtifact qua twine (đã login ở trên):

$ twine upload --repository codeartifact dist/*
Uploading cicddemo_hello-0.1.0-py3-none-any.whl
Uploading cicddemo_hello-0.1.0.tar.gz

Kiểm chứng package đã nằm trong repo:

$ aws codeartifact list-package-versions --domain awscicd --repository demo-repo \
    --format pypi --package cicddemo-hello \
    --query 'versions[].[version,status]' --output text
0.1.0   Published

Consume: cài lại từ CodeArtifact

Đăng nhập pip vào cùng repo rồi cài package vừa publish:

$ aws codeartifact login --tool pip --domain awscicd --repository demo-repo
Login expires in 12 hours ...

$ pip install cicddemo-hello
$ python -c "import cicddemo_hello; print(cicddemo_hello.hello())"
hello from CodeArtifact package

Package riêng vừa publish đã cài và chạy được, lấy từ CodeArtifact chứ không phải PyPI. Cùng repo đó cũng phục vụ package public (qua external connection), nên một lệnh pip install lấy được cả package riêng lẫn package công khai từ một nguồn duy nhất.

   pip / twine
      │  login → auth token tạm (12h, từ credential AWS)
      ▼
   ┌──────────── CodeArtifact: domain awscicd / repo demo-repo ───────────┐
   │   package riêng:  cicddemo-hello 0.1.0  (twine upload)                │
   │   external connection → public:pypi  (proxy + cache package công khai)│
   └───────────────────────────────────────────────────────────────────────┘

Nối vào CodeBuild

Để build kéo package từ CodeArtifact, làm hai việc. Một, cấp cho service role CodeBuild quyền lấy token và đọc repo:

{
  "Effect": "Allow",
  "Action": ["codeartifact:GetAuthorizationToken","codeartifact:GetRepositoryEndpoint","codeartifact:ReadFromRepository"],
  "Resource": "*"
},
{
  "Effect": "Allow", "Action": "sts:GetServiceBearerToken", "Resource": "*",
  "Condition": { "StringEquals": { "sts:AWSServiceName": "codeartifact.amazonaws.com" } }
}

Hai, thêm bước login vào phase install của buildspec.yml — build tự lấy token bằng role của nó, không cần khóa:

phases:
  install:
    commands:
      - aws codeartifact login --tool pip --domain awscicd --repository demo-repo
      - pip install -r requirements.txt   # giờ kéo từ CodeArtifact

Quyền sts:GetServiceBearerToken là mảnh dễ quên: token CodeArtifact thực chất là bearer token do STS cấp, nên thiếu quyền này thì login trong build sẽ lỗi dù đã có quyền codeartifact.

🧹 Dọn dẹp

$ aws codeartifact delete-repository --domain awscicd --repository demo-repo
$ aws codeartifact delete-domain --domain awscicd

CodeArtifact tính theo dung lượng lưu và request; vài package nhỏ gần như không tốn, nhưng vẫn nên xóa khi học xong.

Tổng kết

CodeArtifact là kho package được quản lý: domain gom các repository, repository lưu package, external connection (public:pypi, public:npmjs...) cho nó proxy và cache registry công khai. Xác thực bằng auth token tạm 12 giờ lấy từ credential AWS qua aws codeartifact login, không mật khẩu cố định. Ta đã publish một package Python rồi cài lại từ kho, và nối vào CodeBuild bằng cách cấp role quyền GetAuthorizationToken + sts:GetServiceBearerToken rồi login trong phase install. Kết quả: build kéo cả package riêng lẫn công khai từ một nguồn kiểm soát được.

Part III khép lại ở đây. Part IV là phần dài và quan trọng nhất: CodeDeploy đưa artifact lên EC2. Bài tới dựng nền — cài agent, tạo application và deployment group, viết appspec.yml, và chạy lần deploy in-place đầu tiên lên một EC2 instance thật.