29
loading...
This website collects cookies to deliver better user experience
CI/CD is a method to frequently deliver apps to customers by introducing automation into the stages of app development. The main concepts attributed to CI/CD are continuous integration, continuous delivery, and continuous deployment. CI/CD is a solution to the problems integrating new code can cause for development and operations teams (AKA "integration hell").
source: What is CI/CD?
# docker-compose.yaml
version: "3.3"
services:
mysqldb:
image: "mysql"
ports:
- "3306:3306"
volumes:
- data:/data/db
- ./env/mysql.env:/env/mysql.env
env_file:
- ./env/mysql.env
app:
build: {the path where your Dockerfile is}
restart: always
ports:
- "8000:8000"
volumes:
- ./env/.env:/env/.env
env_file:
- ./env/.env
links:
- mysqldb
depends_on:
- mysqldb
volumes:
data:
image: "mysql"
, you can pull MySQL image from docker hub, and you don't have to install MySQL in your EC2 machine. If you want to use a specific version of it, image: "mysql:{version}"
.mysqldb:
image: "mysql"
ports:
- "3306:3306"
volumes:
- data:/data/db
- ./env/mysql.env:/env/mysql.env
env_file:
- ./env/mysql.env
...
volumes:
data:
build:
is pointing the directory where Dockerfile is.app:
build: ./
restart: always
ports:
- "8000:8000"
volumes:
- ./env/.env:/env/.env
env_file:
- ./env/.env
links:
- mysqldb
depends_on:
- mysqldb
# Dockerfile
FROM python:3.9
WORKDIR /
ENV DOCKERIZE_VERSION v0.2.0
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
COPY ./requirements.txt /requirements.txt
RUN pip install --upgrade pip
RUN pip install --no-cache-dir --upgrade -r /requirements.txt
COPY . .
RUN touch env/.env
RUN touch env/mysql.env
RUN chmod +x docker-entrypoint.sh
ENTRYPOINT ./docker-entrypoint.sh
EXPOSE 8000
CMD ["python", "app/main.py"]
# docker-compose.yaml
...
volumes:
...
- ./env/mysql.env:/env/mysql.env
...
volumes:
- ./env/.env:/env/.env
...
# Dockerfile
...
RUN touch env/.env # creates env file in container
RUN touch env/mysql.env # creates env file in container
...
Note that I'm trying to create development environment. Bind Mounts are not meant to be used in production.
# env/.env
JWT_ALGORITHM=****
JWT_SECRET_KEY=****
SQLALCHEMY_DATABASE_URL=mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@mysqldb:3306/{MYSQL_DATABASE}
# env/mysql.env
MYSQL_USER=****
MYSQL_PASSWORD=****
MYSQL_ROOT_PASSWORD=****
MYSQL_DATABASE=****
depends_on: mysqldb
in docker-compose.yaml, it automatically build a network for the containers. Therefore we can define database url using the name defined in the docker-compose.yaml(mysqldb
).# docker-entrypoint.sh
dockerize -wait tcp://mysqldb:3306 -timeout 20s
echo "Start server"
alembic upgrade head
python /app/main.py
New repository secret
, you can define environment variables which are using during github actions.set up a workflow your self
and create Github workflow.# .github/workflows/main.yml
name: CI/CD Docker
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
Integration:
timeout-minutes: 3
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Create env file
env:
JWT_ALGORITHM: ${{ secrets.JWT_ALGORITHM }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
SQLALCHEMY_DATABASE_URL: ${{ secrets.SQLALCHEMY_DATABASE_URL }}
MYSQL_USER: ${{ secrets.MYSQL_USER }}
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }}
run: |
mkdir env
touch ./env/.env
echo JWT_ALGORITHM="$JWT_ALGORITHM" >> ./env/.env
echo JWT_SECRET_KEY="$JWT_SECRET_KEY" >> ./env/.env
echo SQLALCHEMY_DATABASE_URL="$SQLALCHEMY_DATABASE_URL" >> ./env/.env
ls -a
cat env/.env
touch ./env/mysql.env
echo MYSQL_USER="$MYSQL_USER" >> ./env/mysql.env
echo MYSQL_PASSWORD="$MYSQL_PASSWORD" >> ./env/mysql.env
echo MYSQL_ROOT_PASSWORD="$MYSQL_ROOT_PASSWORD" >> ./env/mysql.env
echo MYSQL_DATABASE="$MYSQL_DATABASE" >> ./env/mysql.env
ls -a
cat env/mysql.env
shell: bash
- name: Start containers
run: docker-compose up -d
Deployment:
needs: Integration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Git pull
env:
AWS_EC2_PEM: ${{ secrets.AWS_EC2_PEM }}
AWS_EC2_PUBLIC_IP: ${{ secrets.AWS_EC2_PUBLIC_IP }}
AWS_EC2_USERNAME: ${{ secrets.AWS_EC2_USERNAME }}
run: |
pwd
echo "$AWS_EC2_PEM" > private_key && chmod 600 private_key
ssh -o StrictHostKeyChecking=no -i private_key ${AWS_EC2_USERNAME}@${AWS_EC2_PUBLIC_IP} '
cd {/path/to/your/project/directory} &&
git checkout main &&
git fetch --all &&
git reset --hard origin/main &&
git pull origin main &&
docker-compose up -d --build
'
FROM python:3.9
WORKDIR /
COPY ./requirements.txt /requirements.txt
RUN pip install --no-cache-dir --upgrade -r /requirements.txt
COPY . .
EXPOSE 8000
RUN touch .env
CMD ["python", "app/main.py"]
repo
, workflow
, and packages
.New self-hosted runner
../run.sh
command, let's configure workflow file.name: CI/CD Docker
on:
push:
branches:
- main
env:
DOCKER_IMAGE: ghcr.io/ninahwang/cicd_2 # this should be lower case !
VERSION: ${{ github.sha }}
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out source code
uses: actions/checkout@v2
- name: Set up docker buildx
id: buildx
uses: docker/setup-buildx-action@v1
- name: Cache docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ env.VERSION }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ env.DOCKER_IMAGE }}:${{ env.VERSION }}
deploy:
needs: build
name: Deploy
runs-on: [self-hosted]
steps:
- name: Login to ghcr
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.TOKEN }}
- name: Create .env file
env:
JWT_ALGORITHM: ${{ secrets.JWT_ALGORITHM }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
SQLALCHEMY_DATABASE_URL: ${{ secrets.SQLALCHEMY_DATABASE_URL }}
run: |
touch .env
echo JWT_ALGORITHM="$JWT_ALGORITHM" >> .env
echo JWT_SECRET_KEY="$JWT_SECRET_KEY" >> .env
echo SQLALCHEMY_DATABASE_URL="$SQLALCHEMY_DATABASE_URL" >> .env
shell: bash
- name: Docker run
run: |
docker ps -q --filter "name=cicd_2" | grep -q . && docker stop cicd_2 && docker rm -fv cicd_2
docker run -p 8000:8000 -d -v "$(pwd)/.env:/.env" --restart always --name cicd_2 ${{ env.DOCKER_IMAGE }}:${{ env.VERSION }}
run.sh
.