Cách cấu hình một Continuous Integration Testing Environment với Docker và Docker Compose trên Ubuntu 16.04
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
- Một Ubuntu 16.04 server với một sudo non-root use. Tham khảo bài viết Thiết lập ban đầu cho server dùng Ubuntu 16.04
- Docker được cài theo Bước 1 và Bước 2 trong bài viết Cách Cài đặt và Sử dụng Docker trên Ubuntu 16.04
- Docker Compose, được cài theo Bước 1 của bài viết Cách cài đặt Docker Compose trên Ubuntu 16.04
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:
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:
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ừ imagepython:2.7
WORKDIR /app
: đặt working directory( thư mục làm việc) là/app
ADD requirements.txt /app/requirements.txt
: thêm filerequirements.txt
đến Docker imageRUN pip install -r requirements.txt
: Cài đặt dependencepip
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:
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ủabuild
, và build ứng dụng Python của chúng ta từ fileDockerfile
. Đâ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 đếnredis
container để có truy cập đến địa chỉ IP củaredis
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:
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:
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:
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:
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
% 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
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.