Cách cấu hình một Continuous Integration Testing Environment với Docker và Docker Compose trên Ubuntu 16.04

2 năm trước

Cách cấu hình một Continuous Integration Testing Environment với Docker và Docker Compose trên Ubuntu 16.04

Mở đầu

Continuous integration (CI) chỉ hoạt động các nhà phát triển tích hợp code càng thường xuyên càng tốt và các lệnh xác thực được test trước và sau khi được nhập vào một kho chứa công cộng bởi một automated build.

CI tăng tốc quá trình phát triển và tối thiểu lỗi xảy ra, nhưng nó không dễ để cài đặt vì các automated build chạy trong những môi trường khác nhau, nơi quá trình cài đặt run-time dependency và cấu hình các dịch vụ bên ngoài có thể khác so với trong môi trường phát triển ứng dụng của bạn.

Docker là một nền tảng Containerization nhằm đơn giản hóa các vấn đề về tiêu chuẩn môi trường để việc triển khai các ứng dụng có thể được chuẩn hóa. Đối với các nhà phát triển, Docker cho phép bạn mô phỏng môi trường sản xuất trên máy cục bộ bằng cách chạy thành phần ứng dụng trong các local container. Những container có thể dễ dàng sử dụng Docker Compose một cách độc lập với các ứng dụng và hệ điều hành cơ bản.

Yêu cầu

 

Bước 1 — Tạo ứng dụng "Hello World"

 

Trong bước này, chúng ta sẽ tạo ra một ứng dụng Python đơn giản như một ví dụ về các loại ứng dụng mà bạn có thể thử nghiệm với thiết lập này.

Tạo một thư mục mới để chứa ứng dụng cần tạo:

cd
mkdir hello_word
cd hello_word

Mở file app.py với nano:

nano app.py

Thêm vào đoạn code:

from flask import Flask
from redis import Redis
app = Flask(__name__)
redis = Redis(host="redis")
@app.route("/")
def hello():
visits = redis.incr('counter')
html = 

Lưu và thoát.

app.py là một ứng dụng web được phát triển dựa trên Flask và kết nối đến một Redis data service. Dòng visits = redis.incr('counter') tăng số lần truy cập và giũ nguyên giá trị của nó trong Redis. Cuối cùng, thông báo Hello World cùng số lượng truy cập được trả lại qua một thông điệp HTML.

Ứng dụng của ta bao gồm hai dependency, Flask và Redis, và bạn phải nhập chúng trước khi triển khai ứng dụng.

Mở một fie mới:

nano requirements.txt

Thêm các dòng sau:

requirements.txt
Flask
Redis

Sau khi xong, lưu và thoát file.

 

Bước 2 — Dockerize ứng dụng "Hello World"

 

Dùng một file tên  Dockerfile để định nghĩa các bước cần thiết cho việc build Docker image cho một ứng dụng.

Mở một file mới:

nano Dockerfile 
  •  

Thêm vào các dòng:

Dockerfile
FROM python:2.7
WORKDIR /app
ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
ADD app.py /app/app.py
EXPOSE 80
CMD ["python", "app.py"]

Giờ hãy phân tích một chút về đoạn code:

  • FROM python:2.7: cho biết image của ứng dụng "Hello World" được build từ image python:2.7 
  • WORKDIR /app: đặt working directory( thư mục làm việc) là /app
  • ADD requirements.txt /app/requirements.txt: thêm file requirements.txt đến Docker image
  • RUN pip install -r requirements.txt: Cài đặt dependence pip 
  • ADD app.py /app/app.py: thêm mã nguồn của ứng dụng vào Docker image.
  • EXPOSE 80: đặt cổng truy cập vào ứng dụng là cổng 80.
  • CMD ["python", "app.py"]: Lệnh chạy ứng dụng.

Lưu và thoát khỏi file.

Về Dependency

Giờ sẽ đến phần phức tạp hơn một chút. Ứnng dụng của ta yêu cầu Redis như một dịch vụ bên ngoài. Đây là loại dependency khá khó khăn để thiết lập nhiều lần một cách giống hệt nhau trong một môi trường Linux truyền thống, nhưng với Docker Compose chúng ta có thể thiết lập nó một cách dễ dàng.

Tạo file docker-compose.yml để bắt đầu sử dụng Docker Compose.

nano docker-compose.yml

Thêm vào các dòng sau:

docker-compose.yml
web:
build: .
dockerfile: Dockerfile
links:
- redis
ports:
- "80:80"
redis:
image: redis

File Docker Compose này cho biết cách cách khởi động "Hello World" trong hai Docker container như sau.

  • web sử dụng thư mục của build , và build ứng dụng Python của chúng ta từ file Dockerfile. Đây là một local Docker image được tạo chỉ dành cho ứng dụng "Hello Word". Nó định nghĩa một liên kết đến redis container để có truy cập đến địa chỉ IP của redis container. Nó cũng làm cho cổng 80 có thể được truy cập một cách công cộng thông qua public IP của server.

  • redis được thực thi từ một public Docker image, redis.

 

Bước 3 — Triển khai ứng dụng

Trong bước này, ta sẽ triển khai ứng dụng, và đến cuối nó sẽ được có thể truy cập qua Internet. Tùy mục đích, bạn có thể coi đây là môi trường phát triển, dàn dựng, hoặc sản xuất, vì bạn có thể triển khai các ứng dụng cùng một cách nhiều lần.

Các file docker-compose.yml và Dockerfile cho phép triển khai tự động các môi trường với lênh:

docker-compose -f ~/hello_world/docker-compose.yml build
docker-compose -f ~/hello_world/docker-compose.yml up -d

Lệnh đầu tiên build  local image ủa ứng dụng từ file Dockerfile. Lệnh thứ hai khởi động các container web và redis trong daemon mode (-d),như đã định nghĩa trong file docker-compose.yml .

Kiểm tra lại với lệnh:

docker ps

Bạn sẽ thấy hai container, helloworld_web_1 và helloworld_redis_1 được hiển thị.

Giờ hãy kiểm tra xem ứng dụng đã hoạt động hay chưa với lệnh. \

Lấy đia chỉ IP của helloworld_web_1 bằng lệnh:

WEB_APP_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' helloworld_web_1)
echo $WEB_APP_IP

Kiểm tra kết quả của lệnh trên bằng cách truy cập ứng dụng:

curl http://${WEB_APP_IP}:80

Bạn sẽ thu được:

Output
Hello World!Visits: 2

 

Cách tùy chỉnh ứng dụng

Chìa khóa để thiết lập ứng dụng riêng của bạn là để đưa ứng dụng của bạn vào Docker container riêng của mình, và chạy các dependency trong các container của chúng. Sau đó, bạn có thể xác định mối quan hệ giữa các container với Docker Compose.

Bước 4 — Tạo đoạn Script để kiểm tra

 

Tạo file mới:

nano test.sh

Thêm đoạn code sau:

test.sh
sleep 5
if curl web | grep -q 'Visits: '; then
echo "Tests passed!"
exit 0
else
echo "Tests failed!"
exit 1
fi

Bước 5 — Tạo môi trường kiểm thử

 

Đầu tiên ta cần Dockerize mã script kiểm thử vừa tạo bằng một file Dockerfile mới:

nano Dockerfile.test

Thêm vào đoạn sau:

Dockerfile.test
FROM ubuntu:xenial
RUN apt-get update && apt-get install -yq curl && apt-get clean
WORKDIR /app
ADD test.sh /app/test.sh
CMD ["bash", "test.sh"]

Dockerfile.test mở rộng image ubuntu:xenial để cài curl dependency, thêm tests.sh đến hệ thống file image, và định nghĩa lệnh CMD cho phép chạy mã kiểm thử với Bash.

Bước tiếp theo là liên kết testing container tới ứng dụng "Hello World" .

Mở một file mới:

nano docker-compose.test.yml

Thêm vào các dòng:

docker-compose.test.yml
sut:
build: .
dockerfile: Dockerfile.test
links:
- web
web:
build: .
dockerfile: Dockerfile
links:
- redis
redis:
image: redis

Phần thứ hai của tập tin Docker Compose triển khai các ứng dụng web chính và redis dependency của nó trong cùng một cách như các tập tin docker-compose.yml trước. Đây là phần của tập tin xác định  web container và redis container. Sự khác biệt duy nhất là web container không còn thấy cổng 80, do đó ứng dụng sẽ không được cung cấp qua Internet công cộng trong thời gian thử nghiệm. Vì vậy, dễ thấy rằng ta đang xây dựng các ứng dụng và dependency của nó chính xác theo cùng một cách như việc triển khai trực tiếp.

File docker-compose.test.yml cũng định nghĩa một sut (system under tests) container có trách nhiệm triển khai các kiểm tra tích hợp. Nó xác định thư mục hiện tại giống như thư mục build cùng với file Dockerfile.test và liên kết tới web container để địa chỉ IP của redis container có thể truy cập được thông qua test.sh script.

Cách tùy biến ứng dụng

Chú ý rằng docker-compose.test.yml có thẻ bao gồm nhiều thiết bị ngoài cùng nhiều test container. Docker có khả năng chạy tất cả các dependency này trên một host vì tất cả container chia sẻ cùng một hệ điều hành.

Nếu có nhiều bài kiểm tra cần thực hiện trên ứng dụng, bạn có thể tạo thêm các Dockerfile cho chúng, giống như cách ta tạo file Dockerfile.test bên trên.

Sau đó bạn có thể thêm các container bên dưới sut container trong file docker-compose.test.yml tham chiếu đến các file Dockerfile được thêm vào.

 

Bước 6 — Kiểm tra ứng dụng "Hello World"

Sau tất cả các bước trên, giờ ta có thể kiểm tra ứng dụng một cách tự động với Docker bằng lệnh sau:

docker-compose -f ~/hello_world/docker-compose.test.yml -p ci build

Lệnh này build local image cần thiết cho docker-compose.test.yml. Chú ý rằng -f trỏ đến docker-compose.test.yml và -p là tên project.

Giờ hãy khởi động môi trường kiểm thử mới tạo:

docker-compose -f ~/hello_world/docker-compose.test.yml -p ci up -d
OutputCreating ci_redis_1 Creating ci_web_1 Creating ci_sut_1

Kiểm tra output của sut container bằng lệnh:

docker logs -f ci_sut_1
Output
 % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 42 100 42 0 0 3902 0 --:--:-- --:--:-- --:--:-- 4200
Tests passed!

Cuối cùng kiểm tra lệnh exit của sut container :

docker wait ci_sut_1
Output
0

Sau lệnh này, giá trị của $? sẽ là 0 nếu bài test thành công. Nếu không thì quá trình test đã thất bại.

Các công cụ CI khác có thể nhân bản mã nguồn và triển khai các lệnh này để xem quá trình test có thành công không bằng bit sau cùng của quá trình chạy mà không quan tâm đến runtime dependencies hay cấu hình các thiết bị ngoại vi.

 

Tổng kết

Nhờ có Docker và Docker Compose, ta có thể build ứng dụng một cách tự động (Dockerfile),triển khai môi trường địa phương (docker-compose.yml),build test image (Dockerfile.test),và chạy (integration) kiểm thử (docker-compose.test.yml) cho bát cứ ứng dụng nào.

Ưu điêm của cách này là

  • Tự động: cách một công cụ thực thi docker-compose.test.yml độc lập với ứng dụng cần kiểm thử.
  • Nhỏ gọn: hàng trăm thiết bị ngoại vi có thể được triển khai trên một host riêng lẻ
  • Đa dụng: Việc kiểm thử có thể thực hiện trên mọi hệ thống hỗ trợ Docker.
  • Bất biến: các bài test được thông quá trên local machine cũng được thông qua trên công cụ CI.