그럴듯한 자동화

프로젝트를 하다 보면, 깃에서 파일을 수정하고 Docker 이미지를 다시 만들고, 빌드한 이미지로 컨테이너를 실행하여 재배포해야할 때가 많다. 팀 프로젝트를 하면서 원래 있던 자동화 스크립트를 짜집기해서 쓰려다가 이번에 변수를 선언해서 제대로 만들어 놓는게 좋겠다 싶었다.

실무에서는 Jira, Jenkins 등으로 체계적인 자동화와 버전 관리가 되어있겠지만 팀원이 4명인 프로젝트에서는 이정도면 되겠다고 생각했다.

현재 프로젝트에 필요한 컨테이너는 DB 컨테이너와 노드 컨테이너이고, docker 안에서 연결되어야 한다. 학교에서 인스턴스를 하나만 주어서, 한 폴더 안에 데이터베이스와 서버 파일이 모두 들어가 있어야 했다. 나는 database-app으로 데이터베이스 파일을 분리했다.

Database 배포 자동화하기

MySQL Dockerfile 만들기

같은 디렉토리에 데이터베이스 구축 sql 파일을 넣고, Dockerfile을 작성한다.

1
2
3
4
5
FROM mysql:5.7
LABEL <AUTHOR> <github.com/GITHUB-NAME>
ENV MYSQL_DATABASE=<DB-NAME> \
MYSQL_ROOT_PASSWORD=<DB-PASSWORD>
COPY ./<SQL-FILENAME>.sql /docker-entrypoint-initdb.d/

Shell Script 만들어서 자동화하기

이제 같은 폴더에 스크립트 파일을 작성한다.

build_n_run.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Build N Run DB Container
# 2020. 09. 18 Zini

docker_username=""
db_image_name=""
db_container_name=""
db_password=""
version=""
port=3306 # Default MySQL Port: 3306

echo "## Automation docker-database build and run ##"

# remove container
echo "=> Remove previous container..."
docker rm -f ${db_container_name}

# remove image
echo "=> Remove previous image..."
docker rmi -f ${docker_username}/${db_image_name}:${version}

# new-build/re-build docker image
echo "=> Build new image..."
docker build --tag ${docker_username}/${db_image_name}:${version} .

# Run container
echo "=> Run container..."
docker run -d -p ${port}:${port} -e MYSQL_ROOT_PASSWORD=${db_password} --name ${db_container_name} ${docker_username}/${db_image_name}:${version} --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

이전에 컨테이너와 이미지가 있다면 삭제하고 재빌드 후 컨테이너를 실행한다. 이제 이 스크립트로 위에 변수만 바꿔주면서 써먹을 수 있다.

Node 배포 자동화하기

Node.js Dockerfile 만들기

이제 프로젝트 최상위 폴더에 서버(노드)에 대한 Dockerfile을 작성한다.

1
2
3
4
5
6
7
8
9
FROM node:10
RUN npm install -g yarn; npm install forever -g;
COPY package.json /src/package.json
RUN cd /src; yarn install;
COPY . /src
EXPOSE 3000
WORKDIR /src

CMD yarn start

foreveryarn 패키지를 먼저 설치하고 패키지를 세팅한다. 예전에는 npm을 썼지만 속도, 성능 면에서 yarn을 사용하려 하고 있다.

package.json의 일부를 보면,

1
2
3
4
5
6
7
8
9
10
{
"name": "Homes",
"version": "0.1",
"description": "Homes App",
"main": "app.js",
"scripts": {
"start": "forever app.js",
"test": "echo \"Error: no test specified\" && exit 1",
"build": ""
}, ...

이렇게 startforever app.js를 설정해뒀다.

Shell Script 만들기

같은 최상위 폴더에 build_n_run.sh을 작성한다. 이 스크립트는 미리 만들어둔 데이터베이스 컨테이너와 연결되어 있는 노드 컨테이너 배포를 자동화한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Build N Run Container with DB Container
# 2020. 09. 18 Zini

### You MUST RUN Database Container First ###

docker_username=""
container_name=""
image_name=""
db_container_name=""
version=""
host_port=3000
virtual_port=3000

echo "## Automation docker build and run ##"

# remove container
echo "=> Remove previous container..."
docker rm -f ${container_name}

# remove image
echo "=> Remove previous image..."
docker rmi -f ${docker_username}/${image_name}:${version}

# new-build/re-build docker image
echo "=> Build new image..."
docker build --tag ${docker_username}/${image_name}:${version} .

# Run container connected to existing database container
echo "=> Run container..."
docker run -t -d --name ${container_name} -p ${host_port}:${virtual_port} --link ${db_container_name}:db -e DATABASE_HOST=db ${docker_username}/${image_name}:${version}

데이터베이스 컨테이너와 연결할 필요가 없을 경우 마지막 docker run 부분만 조금 수정하면 된다. 데이터베이스 컨테이너 자동화와 거의 같은데, 연결하는 부분만 조금 다르다.

그럴듯한 자동화를 마치며

직접 작성한 스크립트 파일을 실행하기 위해서는 리눅스에서 실행 권한을 주고 실행해야 한다.

1
chmod +x build_n_run.sh

두 개의 파일에 모두 실행 권한을 주고 실행하면 된다.

사실 이 과정도 너무 귀찮아서, git 업데이트와 실행 권한 설정을 모두 하는 쉘 스크립트를 또 작성했다.

1
2
3
4
rm -rf <app-name>
git clone https://github.com/zinirun/<app-name>
chmod +x <app-name>/database-app/build_n_run.sh
chmod +x <app-name>/build_n_run.sh

이제 업데이트할 때도 명령어 칠 생각에 두려워하지 않아도 된다.